8136419: Type annotations in initializers and lambda bodies not written to class file
Reviewed-by: jlahoda
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java Thu Nov 12 05:59:47 2015 +0530
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java Thu Nov 12 06:13:14 2015 +0530
@@ -180,6 +180,14 @@
: metadata.getInitTypeAttributes();
}
+ public void setInitTypeAttributes(List<Attribute.TypeCompound> l) {
+ initedMetadata().setInitTypeAttributes(l);
+ }
+
+ public void setClassInitTypeAttributes(List<Attribute.TypeCompound> l) {
+ initedMetadata().setClassInitTypeAttributes(l);
+ }
+
public List<Attribute.Compound> getDeclarationAttributes() {
return (metadata == null)
? List.<Attribute.Compound>nil()
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java Thu Nov 12 05:59:47 2015 +0530
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java Thu Nov 12 06:13:14 2015 +0530
@@ -32,6 +32,7 @@
import com.sun.tools.javac.code.Attribute.Array;
import com.sun.tools.javac.code.Attribute.TypeCompound;
+import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.code.Symbol.TypeSymbol;
import com.sun.tools.javac.code.Type.ArrayType;
import com.sun.tools.javac.code.Type.CapturedType;
@@ -388,8 +389,21 @@
sym.getKind() == ElementKind.RESOURCE_VARIABLE ||
sym.getKind() == ElementKind.EXCEPTION_PARAMETER) {
// Make sure all type annotations from the symbol are also
- // on the owner.
- sym.owner.appendUniqueTypeAttributes(sym.getRawTypeAttributes());
+ // on the owner. If the owner is an initializer block, propagate
+ // to the type.
+ final long ownerFlags = sym.owner.flags();
+ if ((ownerFlags & Flags.BLOCK) != 0) {
+ // Store init and clinit type annotations with the ClassSymbol
+ // to allow output in Gen.normalizeDefs.
+ ClassSymbol cs = (ClassSymbol) sym.owner.owner;
+ if ((ownerFlags & Flags.STATIC) != 0) {
+ cs.appendClassInitTypeAttributes(typeAnnotations);
+ } else {
+ cs.appendInitTypeAttributes(typeAnnotations);
+ }
+ } else {
+ sym.owner.appendUniqueTypeAttributes(sym.getRawTypeAttributes());
+ }
}
}
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java Thu Nov 12 05:59:47 2015 +0530
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java Thu Nov 12 06:13:14 2015 +0530
@@ -55,12 +55,16 @@
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
import static com.sun.tools.javac.comp.LambdaToMethod.LambdaSymbolKind.*;
import static com.sun.tools.javac.code.Flags.*;
import static com.sun.tools.javac.code.Kinds.Kind.*;
import static com.sun.tools.javac.code.TypeTag.*;
import static com.sun.tools.javac.tree.JCTree.Tag.*;
+
+import javax.lang.model.element.ElementKind;
import javax.lang.model.type.TypeKind;
/**
@@ -268,21 +272,34 @@
MethodSymbol sym = localContext.translatedSym;
MethodType lambdaType = (MethodType) sym.type;
- {
+ { /* Type annotation management: Based on where the lambda features, type annotations that
+ are interior to it, may at this point be attached to the enclosing method, or the first
+ constructor in the class, or in the enclosing class symbol or in the field whose
+ initializer is the lambda. In any event, gather up the annotations that belong to the
+ lambda and attach it to the implementation method.
+ */
+
Symbol owner = localContext.owner;
- ListBuffer<Attribute.TypeCompound> ownerTypeAnnos = new ListBuffer<>();
- ListBuffer<Attribute.TypeCompound> lambdaTypeAnnos = new ListBuffer<>();
+ apportionTypeAnnotations(tree,
+ owner::getRawTypeAttributes,
+ owner::setTypeAttributes,
+ sym::setTypeAttributes);
+
- for (Attribute.TypeCompound tc : owner.getRawTypeAttributes()) {
- if (tc.position.onLambda == tree) {
- lambdaTypeAnnos.append(tc);
- } else {
- ownerTypeAnnos.append(tc);
- }
+ boolean init;
+ if ((init = (owner.name == names.init)) || owner.name == names.clinit) {
+ owner = owner.owner;
+ apportionTypeAnnotations(tree,
+ init ? owner::getInitTypeAttributes : owner::getClassInitTypeAttributes,
+ init ? owner::setInitTypeAttributes : owner::setClassInitTypeAttributes,
+ sym::appendUniqueTypeAttributes);
}
- if (lambdaTypeAnnos.nonEmpty()) {
- owner.setTypeAttributes(ownerTypeAnnos.toList());
- sym.setTypeAttributes(lambdaTypeAnnos.toList());
+ if (localContext.self != null && localContext.self.getKind() == ElementKind.FIELD) {
+ owner = localContext.self;
+ apportionTypeAnnotations(tree,
+ owner::getRawTypeAttributes,
+ owner::setTypeAttributes,
+ sym::appendUniqueTypeAttributes);
}
}
@@ -354,6 +371,29 @@
result = makeMetafactoryIndyCall(context, refKind, sym, indy_args);
}
+ // where
+ // Reassign type annotations from the source that should really belong to the lambda
+ private void apportionTypeAnnotations(JCLambda tree,
+ Supplier<List<Attribute.TypeCompound>> source,
+ Consumer<List<Attribute.TypeCompound>> owner,
+ Consumer<List<Attribute.TypeCompound>> lambda) {
+
+ ListBuffer<Attribute.TypeCompound> ownerTypeAnnos = new ListBuffer<>();
+ ListBuffer<Attribute.TypeCompound> lambdaTypeAnnos = new ListBuffer<>();
+
+ for (Attribute.TypeCompound tc : source.get()) {
+ if (tc.position.onLambda == tree) {
+ lambdaTypeAnnos.append(tc);
+ } else {
+ ownerTypeAnnos.append(tc);
+ }
+ }
+ if (lambdaTypeAnnos.nonEmpty()) {
+ owner.accept(ownerTypeAnnos.toList());
+ lambda.accept(lambdaTypeAnnos.toList());
+ }
+ }
+
private JCIdent makeThis(Type type, Symbol owner) {
VarSymbol _this = new VarSymbol(PARAMETER | FINAL | SYNTHETIC,
names._this,
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/annotations/typeAnnotations/classfile/InstanceInitializer.java Thu Nov 12 06:13:14 2015 +0530
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2015, 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.
+ */
+
+import java.lang.annotation.*;
+import java.util.ArrayList;
+
+import com.sun.tools.classfile.*;
+
+/*
+ * @test
+ * @bug 8136419
+ * @summary test that type annotations on entities in initializers are emitted to classfile
+ * @modules jdk.jdeps/com.sun.tools.classfile
+ */
+
+public class InstanceInitializer extends ClassfileTestHelper {
+ public static void main(String[] args) throws Exception {
+ new InstanceInitializer().run();
+ }
+
+ public void run() throws Exception {
+ expected_tinvisibles = 4;
+ expected_tvisibles = 0;
+
+ ClassFile cf = getClassFile("InstanceInitializer$Test.class");
+ test(cf);
+ for (Field f : cf.fields) {
+ test(cf, f);
+ }
+ for (Method m: cf.methods) {
+ test(cf, m, true);
+ }
+
+ countAnnotations();
+
+ if (errors > 0)
+ throw new Exception(errors + " errors found");
+ System.out.println("PASSED");
+ }
+
+ /*********************** Test class *************************/
+ static class Test {
+ @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
+ @interface T {}
+
+ {
+ @T String s = null;
+ Runnable r = () -> new ArrayList<@T String>();
+ }
+ @T String s = null;
+ Runnable r = () -> new ArrayList<@T String>();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/annotations/typeAnnotations/classfile/StaticInitializer.java Thu Nov 12 06:13:14 2015 +0530
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2015, 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.
+ */
+
+import java.lang.annotation.*;
+import java.util.ArrayList;
+
+import com.sun.tools.classfile.*;
+
+/*
+ * @test
+ * @bug 8136419
+ * @summary test that type annotations on entities in static initializers are emitted to classfile
+ * @modules jdk.jdeps/com.sun.tools.classfile
+ */
+
+public class StaticInitializer extends ClassfileTestHelper {
+ public static void main(String[] args) throws Exception {
+ new StaticInitializer().run();
+ }
+
+ public void run() throws Exception {
+ expected_tinvisibles = 4;
+ expected_tvisibles = 0;
+
+ ClassFile cf = getClassFile("StaticInitializer$Test.class");
+ test(cf);
+ for (Field f : cf.fields) {
+ test(cf, f);
+ }
+ for (Method m: cf.methods) {
+ test(cf, m, true);
+ }
+
+ countAnnotations();
+
+ if (errors > 0)
+ throw new Exception(errors + " errors found");
+ System.out.println("PASSED");
+ }
+
+ /*********************** Test class *************************/
+ static class Test {
+ @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
+ @interface T {}
+
+ static {
+ @T String s = null;
+ Runnable r = () -> new ArrayList<@T String>();
+ }
+ @T static String s = null;
+ static Runnable r = () -> new ArrayList<@T String>();
+ }
+}