Fri, 13 Dec 2013 14:13:03 +0000
8029721: javac crash for annotated parameter type of lambda in a field
Reviewed-by: rfield, jfranck
1.1 --- a/src/share/classes/com/sun/tools/javac/comp/Attr.java Thu Dec 12 19:19:07 2013 -0800 1.2 +++ b/src/share/classes/com/sun/tools/javac/comp/Attr.java Fri Dec 13 14:13:03 2013 +0000 1.3 @@ -2590,15 +2590,61 @@ 1.4 } 1.5 } 1.6 1.7 + /* Map to hold 'fake' clinit methods. If a lambda is used to initialize a 1.8 + * static field and that lambda has type annotations, these annotations will 1.9 + * also be stored at these fake clinit methods. 1.10 + * 1.11 + * LambdaToMethod also use fake clinit methods so they can be reused. 1.12 + * Also as LTM is a phase subsequent to attribution, the methods from 1.13 + * clinits can be safely removed by LTM to save memory. 1.14 + */ 1.15 + private Map<ClassSymbol, MethodSymbol> clinits = new HashMap<>(); 1.16 + 1.17 + public MethodSymbol removeClinit(ClassSymbol sym) { 1.18 + return clinits.remove(sym); 1.19 + } 1.20 + 1.21 + /* This method returns an environment to be used to attribute a lambda 1.22 + * expression. 1.23 + * 1.24 + * The owner of this environment is a method symbol. If the current owner 1.25 + * is not a method, for example if the lambda is used to initialize 1.26 + * a field, then if the field is: 1.27 + * 1.28 + * - an instance field, we use the first constructor. 1.29 + * - a static field, we create a fake clinit method. 1.30 + */ 1.31 private Env<AttrContext> lambdaEnv(JCLambda that, Env<AttrContext> env) { 1.32 Env<AttrContext> lambdaEnv; 1.33 Symbol owner = env.info.scope.owner; 1.34 if (owner.kind == VAR && owner.owner.kind == TYP) { 1.35 //field initializer 1.36 lambdaEnv = env.dup(that, env.info.dup(env.info.scope.dupUnshared())); 1.37 - lambdaEnv.info.scope.owner = 1.38 - new MethodSymbol((owner.flags() & STATIC) | BLOCK, names.empty, null, 1.39 - env.info.scope.owner); 1.40 + ClassSymbol enclClass = owner.enclClass(); 1.41 + /* if the field isn't static, then we can get the first constructor 1.42 + * and use it as the owner of the environment. This is what 1.43 + * LTM code is doing to look for type annotations so we are fine. 1.44 + */ 1.45 + if ((owner.flags() & STATIC) == 0) { 1.46 + for (Symbol s : enclClass.members_field.getElementsByName(names.init)) { 1.47 + lambdaEnv.info.scope.owner = s; 1.48 + break; 1.49 + } 1.50 + } else { 1.51 + /* if the field is static then we need to create a fake clinit 1.52 + * method, this method can later be reused by LTM. 1.53 + */ 1.54 + MethodSymbol clinit = clinits.get(enclClass); 1.55 + if (clinit == null) { 1.56 + Type clinitType = new MethodType(List.<Type>nil(), 1.57 + syms.voidType, List.<Type>nil(), syms.methodClass); 1.58 + clinit = new MethodSymbol(STATIC | SYNTHETIC | PRIVATE, 1.59 + names.clinit, clinitType, enclClass); 1.60 + clinit.params = List.<VarSymbol>nil(); 1.61 + clinits.put(enclClass, clinit); 1.62 + } 1.63 + lambdaEnv.info.scope.owner = clinit; 1.64 + } 1.65 } else { 1.66 lambdaEnv = env.dup(that, env.info.dup(env.info.scope.dup())); 1.67 }
2.1 --- a/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java Thu Dec 12 19:19:07 2013 -0800 2.2 +++ b/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java Fri Dec 13 14:13:03 2013 +0000 2.3 @@ -1474,12 +1474,27 @@ 2.4 private Symbol initSym(ClassSymbol csym, long flags) { 2.5 boolean isStatic = (flags & STATIC) != 0; 2.6 if (isStatic) { 2.7 - //static clinits are generated in Gen - so we need to fake them 2.8 - Symbol clinit = clinits.get(csym); 2.9 + /* static clinits are generated in Gen, so we need to use a fake 2.10 + * one. Attr creates a fake clinit method while attributing 2.11 + * lambda expressions used as initializers of static fields, so 2.12 + * let's use that one. 2.13 + */ 2.14 + MethodSymbol clinit = attr.removeClinit(csym); 2.15 + if (clinit != null) { 2.16 + clinits.put(csym, clinit); 2.17 + return clinit; 2.18 + } 2.19 + 2.20 + /* if no clinit is found at Attr, then let's try at clinits. 2.21 + */ 2.22 + clinit = (MethodSymbol)clinits.get(csym); 2.23 if (clinit == null) { 2.24 + /* no luck, let's create a new one 2.25 + */ 2.26 clinit = makePrivateSyntheticMethod(STATIC, 2.27 names.clinit, 2.28 - new MethodType(List.<Type>nil(), syms.voidType, List.<Type>nil(), syms.methodClass), 2.29 + new MethodType(List.<Type>nil(), syms.voidType, 2.30 + List.<Type>nil(), syms.methodClass), 2.31 csym); 2.32 clinits.put(csym, clinit); 2.33 }
3.1 --- a/test/tools/javac/annotations/typeAnnotations/newlocations/Lambda.java Thu Dec 12 19:19:07 2013 -0800 3.2 +++ b/test/tools/javac/annotations/typeAnnotations/newlocations/Lambda.java Fri Dec 13 14:13:03 2013 +0000 3.3 @@ -23,8 +23,9 @@ 3.4 3.5 /* 3.6 * @test 3.7 - * @bug 8008077 3.8 + * @bug 8008077 8029721 3.9 * @summary new type annotation location: lambda expressions 3.10 + * javac crash for annotated parameter type of lambda in a field 3.11 * @compile Lambda.java 3.12 * @author Werner Dietl 3.13 */ 3.14 @@ -57,6 +58,14 @@ 3.15 LambdaInt2 getLambda() { 3.16 return (@TA Object x, @TB Object y) -> { @TA Object l = null; System.out.println("We have: " + (@TB Object) x); }; 3.17 } 3.18 + 3.19 + java.util.function.IntUnaryOperator x = (@TA int y) -> 1; 3.20 + 3.21 + static java.util.function.IntUnaryOperator xx = (@TA int y) -> 1; 3.22 + 3.23 + java.util.function.IntUnaryOperator foo() { 3.24 + return (@TA int y) -> 2; 3.25 + } 3.26 } 3.27 3.28 @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
4.1 --- a/test/tools/javac/annotations/typeAnnotations/referenceinfos/Lambda.java Thu Dec 12 19:19:07 2013 -0800 4.2 +++ b/test/tools/javac/annotations/typeAnnotations/referenceinfos/Lambda.java Fri Dec 13 14:13:03 2013 +0000 4.3 @@ -23,8 +23,9 @@ 4.4 4.5 /* 4.6 * @test 4.7 - * @bug 8008077 4.8 + * @bug 8008077 8029721 4.9 * @summary Test population of reference info for lambda expressions 4.10 + * javac crash for annotated parameter type of lambda in a field 4.11 * @compile -g Driver.java ReferenceInfoUtil.java Lambda.java 4.12 * @run main Driver Lambda 4.13 * @author Werner Dietl 4.14 @@ -285,4 +286,24 @@ 4.15 " }" + 4.16 "}"; 4.17 } 4.18 + 4.19 + @TADescriptions({ 4.20 + @TADescription(annotation = "TA", type = METHOD_FORMAL_PARAMETER, 4.21 + paramIndex = 0)}) 4.22 + public String lambdaField1() { 4.23 + return 4.24 + "class Test {" + 4.25 + " java.util.function.IntUnaryOperator field = (@TA int y) -> 1;" + 4.26 + "}"; 4.27 + } 4.28 + 4.29 + @TADescriptions({ 4.30 + @TADescription(annotation = "TA", type = METHOD_FORMAL_PARAMETER, 4.31 + paramIndex = 0)}) 4.32 + public String lambdaField2() { 4.33 + return 4.34 + "class Test {" + 4.35 + " static java.util.function.IntUnaryOperator field = (@TA int y) -> 1;" + 4.36 + "}"; 4.37 + } 4.38 }
5.1 --- a/test/tools/javac/lambda/LambdaScope05.out Thu Dec 12 19:19:07 2013 -0800 5.2 +++ b/test/tools/javac/lambda/LambdaScope05.out Fri Dec 13 14:13:03 2013 +0000 5.3 @@ -1,5 +1,5 @@ 5.4 LambdaScope05.java:13:47: compiler.err.already.defined.in.clinit: kindname.variable, p, kindname.static.init, kindname.class, LambdaScope05 5.5 -LambdaScope05.java:14:40: compiler.err.already.defined.in.clinit: kindname.variable, p, kindname.instance.init, kindname.class, LambdaScope05 5.6 +LambdaScope05.java:14:40: compiler.err.already.defined: kindname.variable, p, kindname.constructor, LambdaScope05() 5.7 LambdaScope05.java:17:43: compiler.err.already.defined.in.clinit: kindname.variable, p, kindname.static.init, kindname.class, LambdaScope05 5.8 LambdaScope05.java:21:43: compiler.err.already.defined.in.clinit: kindname.variable, p, kindname.instance.init, kindname.class, LambdaScope05 5.9 LambdaScope05.java:25:43: compiler.err.already.defined: kindname.variable, p, kindname.method, m_static()