7199823: javac generates inner class that can't be verified
Reviewed-by: jjg, mcimadamore
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java Sat Feb 02 21:04:56 2013 +0000
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java Sun Feb 03 02:31:30 2013 +0000
@@ -570,10 +570,19 @@
* @param flags The class symbol's flags
* @param owner The class symbol's owner
*/
- ClassSymbol makeEmptyClass(long flags, ClassSymbol owner) {
+ JCClassDecl makeEmptyClass(long flags, ClassSymbol owner) {
+ return makeEmptyClass(flags, owner, null, true);
+ }
+
+ JCClassDecl makeEmptyClass(long flags, ClassSymbol owner, Name flatname,
+ boolean addToDefs) {
// Create class symbol.
ClassSymbol c = reader.defineClass(names.empty, owner);
- c.flatname = chk.localClassName(c);
+ if (flatname != null) {
+ c.flatname = flatname;
+ } else {
+ c.flatname = chk.localClassName(c);
+ }
c.sourcefile = owner.sourcefile;
c.completer = null;
c.members_field = new Scope(c);
@@ -597,9 +606,8 @@
cdef.type = c.type;
// Append class definition tree to owner's definitions.
- odef.defs = odef.defs.prepend(cdef);
-
- return c;
+ if (addToDefs) odef.defs = odef.defs.prepend(cdef);
+ return cdef;
}
/**************************************************************************
@@ -706,7 +714,7 @@
* and synthethise a class (with makeEmptyClass) if one is not available.
* However, there is a small possibility that an existing class will not
* be generated as expected if it is inside a conditional with a constant
- * expression. If that is found to be the case, create an empty class here.
+ * expression. If that is found to be the case, create an empty class tree here.
*/
private void checkAccessConstructorTags() {
for (List<ClassSymbol> l = accessConstrTags; l.nonEmpty(); l = l.tail) {
@@ -714,14 +722,10 @@
if (isTranslatedClassAvailable(c))
continue;
// Create class definition tree.
- JCClassDecl cdef = make.ClassDef(
- make.Modifiers(STATIC | SYNTHETIC), names.empty,
- List.<JCTypeParameter>nil(),
- null, List.<JCExpression>nil(), List.<JCTree>nil());
- cdef.sym = c;
- cdef.type = c.type;
- // add it to the list of classes to be generated
- translated.append(cdef);
+ JCClassDecl cdec = makeEmptyClass(STATIC | SYNTHETIC,
+ c.outermostClass(), c.flatname, false);
+ swapAccessConstructorTag(c, cdec.sym);
+ translated.append(cdec);
}
}
// where
@@ -735,6 +739,19 @@
return false;
}
+ void swapAccessConstructorTag(ClassSymbol oldCTag, ClassSymbol newCTag) {
+ for (MethodSymbol methodSymbol : accessConstrs.values()) {
+ Assert.check(methodSymbol.type.hasTag(METHOD));
+ MethodType oldMethodType =
+ (MethodType)methodSymbol.type;
+ if (oldMethodType.argtypes.head.tsym == oldCTag)
+ methodSymbol.type =
+ types.createMethodTypeWithParameters(oldMethodType,
+ oldMethodType.getParameterTypes().tail
+ .prepend(newCTag.erasure(types)));
+ }
+ }
+
/**************************************************************************
* Access methods
*************************************************************************/
@@ -1211,7 +1228,7 @@
"1");
ClassSymbol ctag = chk.compiled.get(flatname);
if (ctag == null)
- ctag = makeEmptyClass(STATIC | SYNTHETIC, topClass);
+ ctag = makeEmptyClass(STATIC | SYNTHETIC, topClass).sym;
// keep a record of all tags, to verify that all are generated as required
accessConstrTags = accessConstrTags.prepend(ctag);
return ctag;
@@ -1778,7 +1795,7 @@
if (e.sym.kind == TYP &&
e.sym.name == names.empty &&
(e.sym.flags() & INTERFACE) == 0) return (ClassSymbol) e.sym;
- return makeEmptyClass(STATIC | SYNTHETIC, clazz);
+ return makeEmptyClass(STATIC | SYNTHETIC, clazz).sym;
}
/** Return symbol for "class$" method. If there is no method definition
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/7199823/InnerClassCannotBeVerified.java Sun Feb 03 02:31:30 2013 +0000
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2012, 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 7199823
+ * @summary javac generates inner class that can't be verified
+ * @run main InnerClassCannotBeVerified
+ */
+
+import java.util.Arrays;
+import javax.tools.JavaFileObject;
+import java.net.URI;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.ToolProvider;
+import javax.tools.JavaCompiler;
+import com.sun.source.util.JavacTask;
+import com.sun.tools.classfile.ClassFile;
+import com.sun.tools.classfile.ConstantPoolException;
+import java.io.File;
+import java.io.IOException;
+
+public class InnerClassCannotBeVerified {
+
+ private static final String errorMessage =
+ "Compile error while compiling the following source:\n";
+
+ public static void main(String... args) throws Exception {
+ new InnerClassCannotBeVerified().run();
+ }
+
+ void run() throws Exception {
+ JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
+ JavaSource source = new JavaSource();
+ JavacTask ct = (JavacTask)comp.getTask(null, null, null,
+ null, null, Arrays.asList(source));
+ try {
+ if (!ct.call()) {
+ throw new AssertionError(errorMessage +
+ source.getCharContent(true));
+ }
+ } catch (Throwable ex) {
+ throw new AssertionError(errorMessage +
+ source.getCharContent(true));
+ }
+ check();
+ }
+
+ private void check() throws IOException, ConstantPoolException {
+ File file = new File("Test$1.class");
+ ClassFile classFile = ClassFile.read(file);
+ boolean inheritsFromObject =
+ classFile.getSuperclassName().equals("java/lang/Object");
+ boolean implementsNoInterface = classFile.interfaces.length == 0;
+ boolean noMethods = classFile.methods.length == 0;
+ if (!(inheritsFromObject &&
+ implementsNoInterface &&
+ noMethods)) {
+ throw new AssertionError("The inner classes reused as " +
+ "access constructor tag for this code must be empty");
+ }
+ }
+
+ class JavaSource extends SimpleJavaFileObject {
+
+ String internalSource =
+ "public class Test {\n" +
+ " private static class Foo {}\n" +
+ " public static void main(String[] args){ \n" +
+ " new Foo();\n" +
+ " if(false) {\n" +
+ " new Runnable() {\n" +
+ " @Override\n" +
+ " public void run() {\n" +
+ " System.out.println();\n" +
+ " }\n" +
+ " }.run();\n" +
+ " }\n" +
+ " }\n" +
+ "}";
+ public JavaSource() {
+ super(URI.create("Test.java"), JavaFileObject.Kind.SOURCE);
+ }
+
+ @Override
+ public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+ return internalSource;
+ }
+ }
+}