changeset 1594:d686d8a7eb78

8008227: Mixing lambdas with anonymous classes leads to NPE thrown by compiler Summary: Disentangle cyclic dependency between static-ness of synthetic lambda method and static-ness of classes nested within lambdas Reviewed-by: jjg
author mcimadamore
date Thu, 21 Feb 2013 15:19:29 +0000
parents 267225edc1fe
children 3a39d123d33a
files src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java test/tools/javac/lambda/LambdaConv27.java
diffstat 2 files changed, 77 insertions(+), 40 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java	Wed Feb 20 15:47:14 2013 -0800
+++ b/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java	Thu Feb 21 15:19:29 2013 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -125,7 +125,8 @@
             MethodType type = new MethodType(List.of(syms.serializedLambdaType), syms.objectType,
                     List.<Type>nil(), syms.methodClass);
             deserMethodSym = makeSyntheticMethod(flags, names.deserializeLambda, type, kSym);
-            deserParamSym = new VarSymbol(FINAL, names.fromString("lambda"), syms.serializedLambdaType, deserMethodSym);
+            deserParamSym = new VarSymbol(FINAL, names.fromString("lambda"),
+                    syms.serializedLambdaType, deserMethodSym);
         }
 
         private void addMethod(JCTree decl) {
@@ -738,7 +739,8 @@
                 List<Type> refPTypes = tree.sym.type.getParameterTypes();
                 int refSize = refPTypes.size();
                 int samSize = samPTypes.size();
-                int last = localContext.needsVarArgsConversion() ? refSize - 1 : refSize;   // Last parameter to copy from referenced method
+                // Last parameter to copy from referenced method
+                int last = localContext.needsVarArgsConversion() ? refSize - 1 : refSize;
 
                 List<Type> l = refPTypes;
                 // Use parameter types of the referenced method, excluding final var args
@@ -763,7 +765,8 @@
                         null,
                         null);
                 bridgeDecl.sym = (MethodSymbol) localContext.bridgeSym;
-                bridgeDecl.type = localContext.bridgeSym.type = types.createMethodTypeWithParameters(samDesc, TreeInfo.types(params.toList()));
+                bridgeDecl.type = localContext.bridgeSym.type =
+                        types.createMethodTypeWithParameters(samDesc, TreeInfo.types(params.toList()));
 
                 //bridge method body generation - this can be either a method call or a
                 //new instance creation expression, depending on the member reference kind
@@ -803,7 +806,8 @@
 
             //create the method call expression
             JCExpression apply = make.Apply(List.<JCExpression>nil(), select,
-                    convertArgs(tree.sym, args.toList(), tree.varargsElement)).setType(tree.sym.erasure(types).getReturnType());
+                    convertArgs(tree.sym, args.toList(), tree.varargsElement)).
+                    setType(tree.sym.erasure(types).getReturnType());
 
             apply = transTypes.coerce(apply, localContext.generatedRefSig().getReturnType());
             setVarargsIfNeeded(apply, tree.varargsElement);
@@ -817,7 +821,8 @@
         private JCExpression bridgeExpressionNew() {
             if (tree.kind == ReferenceKind.ARRAY_CTOR) {
                 //create the array creation expression
-                JCNewArray newArr = make.NewArray(make.Type(types.elemtype(tree.getQualifierExpression().type)),
+                JCNewArray newArr = make.NewArray(
+                        make.Type(types.elemtype(tree.getQualifierExpression().type)),
                         List.of(make.Ident(params.first())),
                         null);
                 newArr.type = tree.getQualifierExpression().type;
@@ -872,7 +877,8 @@
         Type mtype = types.erasure(tree.descriptorType);
         MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.type.tsym);
         List<Object> staticArgs = List.<Object>of(
-                new Pool.MethodHandle(ClassFile.REF_invokeInterface, types.findDescriptorSymbol(tree.type.tsym), types),
+                new Pool.MethodHandle(ClassFile.REF_invokeInterface,
+                    types.findDescriptorSymbol(tree.type.tsym), types),
                 new Pool.MethodHandle(refKind, refSym, types),
                 new MethodType(mtype.getParameterTypes(),
                         mtype.getReturnType(),
@@ -922,7 +928,8 @@
      * Generate an indy method call with given name, type and static bootstrap
      * arguments types
      */
-    private JCExpression makeIndyCall(DiagnosticPosition pos, Type site, Name bsmName, List<Object> staticArgs, MethodType indyType, List<JCExpression> indyArgs) {
+    private JCExpression makeIndyCall(DiagnosticPosition pos, Type site, Name bsmName,
+            List<Object> staticArgs, MethodType indyType, List<JCExpression> indyArgs) {
         int prevPos = make.pos;
         try {
             make.at(pos);
@@ -936,7 +943,9 @@
             DynamicMethodSymbol dynSym =
                     new DynamicMethodSymbol(names.lambda,
                                             syms.noSymbol,
-                                            bsm.isStatic() ? ClassFile.REF_invokeStatic : ClassFile.REF_invokeVirtual,
+                                            bsm.isStatic() ?
+                                                ClassFile.REF_invokeStatic :
+                                                ClassFile.REF_invokeVirtual,
                                             (MethodSymbol)bsm,
                                             indyType,
                                             staticArgs.toArray());
@@ -1057,26 +1066,19 @@
         @Override
         public void visitClassDef(JCClassDecl tree) {
             List<Frame> prevStack = frameStack;
-            Map<String, Integer> prevSerializableLambdaCount = serializableLambdaCounts;
+            Map<String, Integer> prevSerializableLambdaCount =
+                    serializableLambdaCounts;
             Map<ClassSymbol, Symbol> prevClinits = clinits;
             try {
                 serializableLambdaCounts = new HashMap<String, Integer>();
                 prevClinits = new HashMap<ClassSymbol, Symbol>();
                 if (directlyEnclosingLambda() != null) {
                     tree.sym.owner = owner();
-                    LambdaTranslationContext lambdaContext = (LambdaTranslationContext) contextMap.get(directlyEnclosingLambda());
-                    Type encl = lambdaContext.enclosingType();
-                    if (encl.hasTag(NONE)) {
-                        //if the translated lambda body occurs in a static context,
-                        //any class declaration within it must be made static
-                        //@@@TODO: What about nested classes within lambda?
-                        tree.sym.flags_field |= STATIC;
-                        ((ClassType) tree.sym.type).setEnclosingType(Type.noType);
-                    } else {
-                        //if the translated lambda body is in an instance context
-                        //the enclosing type of any class declaration within it
-                        //must be updated to point to the new enclosing type (if any)
-                        ((ClassType) tree.sym.type).setEnclosingType(encl);
+                    if (tree.sym.hasOuterInstance()) {
+                        //if a class is defined within a lambda, the lambda must capture
+                        //its enclosing instance (if any)
+                        ((LambdaTranslationContext) context())
+                                .addSymbol(tree.sym.type.getEnclosingType().tsym, CAPTURED_THIS);
                     }
                 }
                 frameStack = frameStack.prepend(new Frame(tree));
@@ -1087,11 +1089,6 @@
                 serializableLambdaCounts = prevSerializableLambdaCount;
                 clinits = prevClinits;
             }
-            if (!tree.sym.isStatic() && directlyEnclosingLambda() != null) {
-                // Any (non-static) class defined within a lambda is an implicit 'this' reference
-                // because its constructor will reference the enclosing class
-                ((LambdaTranslationContext) context()).addSymbol(tree.sym.type.getEnclosingType().tsym, CAPTURED_THIS);
-            }
         }
 
         @Override
@@ -1105,7 +1102,8 @@
                         if (localContext.tree.getTag() == LAMBDA) {
                             JCTree block = capturedDecl(localContext.depth, tree.sym);
                             if (block == null) break;
-                            ((LambdaTranslationContext)localContext).addSymbol(tree.sym, CAPTURED_VAR);
+                            ((LambdaTranslationContext)localContext)
+                                    .addSymbol(tree.sym, CAPTURED_VAR);
                         }
                         localContext = localContext.prev;
                     }
@@ -1118,7 +1116,8 @@
                             switch (block.getTag()) {
                                 case CLASSDEF:
                                     JCClassDecl cdecl = (JCClassDecl)block;
-                                    ((LambdaTranslationContext)localContext).addSymbol(cdecl.sym, CAPTURED_THIS);
+                                    ((LambdaTranslationContext)localContext)
+                                            .addSymbol(cdecl.sym, CAPTURED_THIS);
                                     break;
                                 default:
                                     Assert.error("bad block kind");
@@ -1165,7 +1164,8 @@
         @Override
         public void visitNewClass(JCNewClass tree) {
             if (lambdaNewClassFilter(context(), tree)) {
-                ((LambdaTranslationContext) context()).addSymbol(tree.type.getEnclosingType().tsym, CAPTURED_THIS);
+                ((LambdaTranslationContext) context())
+                        .addSymbol(tree.type.getEnclosingType().tsym, CAPTURED_THIS);
             }
             super.visitNewClass(tree);
         }
@@ -1278,7 +1278,8 @@
                         return ((JCMethodDecl)frameStack2.head.tree).sym;
                     case LAMBDA:
                         if (!skipLambda)
-                            return ((LambdaTranslationContext)contextMap.get(frameStack2.head.tree)).translatedSym;
+                            return ((LambdaTranslationContext)contextMap
+                                    .get(frameStack2.head.tree)).translatedSym;
                     default:
                         frameStack2 = frameStack2.tail;
                 }
@@ -1555,7 +1556,8 @@
                         return sym;  // self represented
                     case TYPE_VAR:
                         // Just erase the type var
-                        return new VarSymbol(sym.flags(), names.fromString(name), types.erasure(sym.type), sym.owner);
+                        return new VarSymbol(sym.flags(), names.fromString(name),
+                                types.erasure(sym.type), sym.owner);
                     default:
                         return makeSyntheticVar(FINAL, name, types.erasure(sym.type), translatedSym);
                 }
@@ -1633,7 +1635,8 @@
 
                 // If instance access isn't needed, make it static
                 // Interface methods much be public default methods, otherwise make it private
-                translatedSym.flags_field = SYNTHETIC | (needInstance? 0 : STATIC) | (inInterface? PUBLIC | DEFAULT : PRIVATE);
+                translatedSym.flags_field = SYNTHETIC | (needInstance? 0 : STATIC) |
+                        (inInterface? PUBLIC | DEFAULT : PRIVATE);
 
                 //compute synthetic params
                 ListBuffer<JCVariableDecl> params = ListBuffer.lb();
@@ -1655,12 +1658,6 @@
                         TreeInfo.types(syntheticParams));
             }
 
-            Type enclosingType() {
-                return owner.isStatic() ?
-                        Type.noType :
-                        owner.enclClass().type;
-            }
-
             Type generatedLambdaSig() {
                 return types.erasure(tree.descriptorType);
             }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/lambda/LambdaConv27.java	Thu Feb 21 15:19:29 2013 +0000
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8008227
+ * @summary Mixing lambdas with anonymous classes leads to NPE thrown by compiler
+ * @run main LambdaConv27
+ */
+public class LambdaConv27 {
+
+     public static void main(String[] args) {
+         SAM s = ()-> { SAM s2 = ()->{ new Object() { }; }; s2.m(); };
+         s.m();
+     }
+
+     interface SAM {
+         void m();
+     }
+}