8022053: javac generates unverifiable initializer for nested subclass of local class
Reviewed-by: jjg, mcimadamore
--- 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());
+ }
+
+}