8022053: javac generates unverifiable initializer for nested subclass of local class
authorvromero
Fri, 16 Aug 2013 10:32:42 +0100
changeset 19501 9dac9369db2c
parent 19500 26d88d483764
child 19502 f30b3ef165ea
8022053: javac generates unverifiable initializer for nested subclass of local class Reviewed-by: jjg, mcimadamore
langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java
langtools/test/tools/javac/T8022053/UnverifiableInitForNestedLocalClassTest.java
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java	Thu Aug 15 17:24:35 2013 +0200
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java	Fri Aug 16 10:32:42 2013 +0100
@@ -356,22 +356,44 @@
         }
     }
 
+    ClassSymbol ownerToCopyFreeVarsFrom(ClassSymbol c) {
+        if (!c.isLocal()) {
+            return null;
+        }
+        Symbol currentOwner = c.owner;
+        while ((currentOwner.owner.kind & TYP) != 0 && currentOwner.isLocal()) {
+            currentOwner = currentOwner.owner;
+        }
+        if ((currentOwner.owner.kind & (VAR | MTH)) != 0 && c.isSubClass(currentOwner, types)) {
+            return (ClassSymbol)currentOwner;
+        }
+        return null;
+    }
+
     /** Return the variables accessed from within a local class, which
      *  are declared in the local class' owner.
      *  (in reverse order of first access).
      */
     List<VarSymbol> freevars(ClassSymbol c)  {
+        List<VarSymbol> fvs = freevarCache.get(c);
+        if (fvs != null) {
+            return fvs;
+        }
         if ((c.owner.kind & (VAR | MTH)) != 0) {
-            List<VarSymbol> fvs = freevarCache.get(c);
-            if (fvs == null) {
-                FreeVarCollector collector = new FreeVarCollector(c);
-                collector.scan(classDef(c));
-                fvs = collector.fvs;
-                freevarCache.put(c, fvs);
-            }
+            FreeVarCollector collector = new FreeVarCollector(c);
+            collector.scan(classDef(c));
+            fvs = collector.fvs;
+            freevarCache.put(c, fvs);
             return fvs;
         } else {
-            return List.nil();
+            ClassSymbol owner = ownerToCopyFreeVarsFrom(c);
+            if (owner != null) {
+                fvs = freevarCache.get(owner);
+                freevarCache.put(c, fvs);
+                return fvs;
+            } else {
+                return List.nil();
+            }
         }
     }
 
@@ -1046,7 +1068,7 @@
     boolean needsPrivateAccess(Symbol sym) {
         if ((sym.flags() & PRIVATE) == 0 || sym.owner == currentClass) {
             return false;
-        } else if (sym.name == names.init && (sym.owner.owner.kind & (VAR | MTH)) != 0) {
+        } else if (sym.name == names.init && sym.owner.isLocal()) {
             // private constructor in local class: relax protection
             sym.flags_field &= ~PRIVATE;
             return false;
@@ -2448,6 +2470,7 @@
         tree.name = Convert.shortName(currentClass.flatName());
 
         // Add this$n and free variables proxy definitions to class.
+
         for (List<JCVariableDecl> l = fvdefs; l.nonEmpty(); l = l.tail) {
             tree.defs = tree.defs.prepend(l.head);
             enterSynthetic(tree.pos(), l.head.sym, currentClass.members());
@@ -2670,8 +2693,7 @@
     //where
     private void visitMethodDefInternal(JCMethodDecl tree) {
         if (tree.name == names.init &&
-            (currentClass.isInner() ||
-             (currentClass.owner.kind & (VAR | MTH)) != 0)) {
+            (currentClass.isInner() || currentClass.isLocal())) {
             // We are seeing a constructor of an inner class.
             MethodSymbol m = tree.sym;
 
@@ -2818,7 +2840,7 @@
 
         // If created class is local, add free variables after
         // explicit constructor arguments.
-        if ((c.owner.kind & (VAR | MTH)) != 0) {
+        if (c.isLocal()) {
             tree.args = tree.args.appendList(loadFreevars(tree.pos(), freevars(c)));
         }
 
@@ -2837,7 +2859,7 @@
             if (tree.encl != null) {
                 thisArg = attr.makeNullCheck(translate(tree.encl));
                 thisArg.type = tree.encl.type;
-            } else if ((c.owner.kind & (MTH | VAR)) != 0) {
+            } else if (c.isLocal()) {
                 // local class
                 thisArg = makeThis(tree.pos(), c.type.getEnclosingType().tsym);
             } else {
@@ -2966,7 +2988,7 @@
             // If we are calling a constructor of a local class, add
             // free variables after explicit constructor arguments.
             ClassSymbol c = (ClassSymbol)constructor.owner;
-            if ((c.owner.kind & (VAR | MTH)) != 0) {
+            if (c.isLocal()) {
                 tree.args = tree.args.appendList(loadFreevars(tree.pos(), freevars(c)));
             }
 
@@ -2994,7 +3016,7 @@
                         makeNullCheck(translate(((JCFieldAccess) tree.meth).selected));
                     tree.meth = make.Ident(constructor);
                     ((JCIdent) tree.meth).name = methName;
-                } else if ((c.owner.kind & (MTH | VAR)) != 0 || methName == names._this){
+                } else if (c.isLocal() || methName == names._this){
                     // local class or this() call
                     thisArg = makeThis(tree.meth.pos(), c.type.getEnclosingType().tsym);
                 } else {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/T8022053/UnverifiableInitForNestedLocalClassTest.java	Fri Aug 16 10:32:42 2013 +0100
@@ -0,0 +1,73 @@
+/*
+ * 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 8022053
+ * @summary 8022053: javac generates unverifiable initializer for nested subclass of local class
+ * @run main UnverifiableInitForNestedLocalClassTest
+ */
+
+public class UnverifiableInitForNestedLocalClassTest {
+
+    public static void main(final String[] args) {
+        test("test");
+    }
+
+    static void test(final String arg) {
+        final String inlined = " inlined ";
+        class LocalClass {
+            String m() {
+                return "LocalClass " + arg + inlined;
+            }
+
+            class SubClass extends LocalClass {
+                @Override
+                String m() {
+                    return "SubClass " + arg + inlined;
+                }
+            }
+
+            class SubSubClass extends SubClass {
+                @Override
+                String m() {
+                    return "SubSubClass " + arg + inlined;
+                }
+            }
+
+            class AnotherLocal {
+                class AnotherSub extends LocalClass {
+                    @Override
+                    String m() {
+                        return "AnotherSub " + arg + inlined;
+                    }
+                }
+            }
+        }
+        System.out.println(new LocalClass().m());
+        System.out.println(new LocalClass().new SubClass().m());
+        System.out.println(new LocalClass().new SubSubClass().m());
+        System.out.println(new LocalClass().new AnotherLocal().new AnotherSub().m());
+    }
+
+}