8136419: Type annotations in initializers and lambda bodies not written to class file
authorsadayapalam
Thu, 12 Nov 2015 06:13:14 +0530
changeset 33712 fd1ce6d7ac63
parent 33711 2c47dffb2e18
child 33713 dc1d2632935c
8136419: Type annotations in initializers and lambda bodies not written to class file Reviewed-by: jlahoda
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java
langtools/test/tools/javac/annotations/typeAnnotations/classfile/InstanceInitializer.java
langtools/test/tools/javac/annotations/typeAnnotations/classfile/StaticInitializer.java
--- 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>();
+    }
+}