changeset 1149:3721384cb546

Updated code generation strategy for extension methods: *) Added compiler flag -XDinlinedExtensionBodies to generate code attribute for extension methods. The code attribute will contain an invokestatic to the default implementation. Enabling this flag disables generation of the Defender method attribute/annotation. *) Updated javap to correctly detect ACC_DEFENDER flag on interface methods
author mcimadamore
date Thu, 27 Oct 2011 16:41:02 +0100
parents afd33b3093d3
children cf1816277dd1
files src/share/classes/com/sun/tools/classfile/AccessFlags.java src/share/classes/com/sun/tools/javac/comp/Lower.java src/share/classes/com/sun/tools/javac/comp/MemberEnter.java src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java src/share/classes/com/sun/tools/javac/jvm/Gen.java src/share/classes/com/sun/tools/javac/parser/JavacParser.java src/share/classes/com/sun/tools/javac/tree/TreeInfo.java test/tools/javac/defender/TestInlinedDefenderBody.java
diffstat 8 files changed, 157 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/tools/classfile/AccessFlags.java	Wed Oct 26 16:41:55 2011 +0100
+++ b/src/share/classes/com/sun/tools/classfile/AccessFlags.java	Thu Oct 27 16:41:02 2011 +0100
@@ -50,7 +50,7 @@
     public static final int ACC_TRANSIENT     = 0x0080; //               field
     public static final int ACC_VARARGS       = 0x0080; //                      method
     public static final int ACC_NATIVE        = 0x0100; //                      method
-    public static final int ACC_INTERFACE     = 0x0200; // class, inner
+    public static final int ACC_INTERFACE     = 0x0200; // class, inner,        method (defenders)
     public static final int ACC_ABSTRACT      = 0x0400; // class, inner,        method
     public static final int ACC_STRICT        = 0x0800; //                      method
     public static final int ACC_SYNTHETIC     = 0x1000; // class, inner, field, method
@@ -143,7 +143,7 @@
     private static final int[] methodFlags = {
         ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL,
         ACC_SYNCHRONIZED, ACC_BRIDGE, ACC_VARARGS, ACC_NATIVE, ACC_ABSTRACT,
-        ACC_STRICT, ACC_SYNTHETIC, ACC_MODULE
+        ACC_STRICT, ACC_SYNTHETIC, ACC_MODULE, ACC_INTERFACE
     };
 
     public Set<String> getMethodModifiers() {
@@ -236,7 +236,7 @@
         case ACC_NATIVE:
             return "ACC_NATIVE";
         case ACC_INTERFACE:
-            return "ACC_INTERFACE";
+            return (t == Kind.Class ? "ACC_INTERFACE" : "ACC_DEFENDER");
         case ACC_ABSTRACT:
             return "ACC_ABSTRACT";
         case ACC_STRICT:
--- a/src/share/classes/com/sun/tools/javac/comp/Lower.java	Wed Oct 26 16:41:55 2011 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/Lower.java	Thu Oct 27 16:41:02 2011 +0100
@@ -110,7 +110,9 @@
         Options options = Options.instance(context);
         debugLower = options.isSet("debuglower");
         pkginfoOpt = PkgInfo.get(options);
-        emitDefenderMethodAnnos = options.get("emitDefenderMethodAnnos") != null;
+        boolean inlinedExtensionBodies = options.isSet("inlinedExtensionBodies");
+        emitDefenderMethodAnnos = options.get("emitDefenderMethodAnnos") != null &&
+                !inlinedExtensionBodies;
     }
 
     /** The currently enclosing class.
--- a/src/share/classes/com/sun/tools/javac/comp/MemberEnter.java	Wed Oct 26 16:41:55 2011 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/MemberEnter.java	Thu Oct 27 16:41:02 2011 +0100
@@ -582,14 +582,13 @@
                             localEnv.info.scope.enterIfAbsent(typevar.type.tsym);
                         }
                         attr.attribStat(tree.body, localEnv);
-                        if (tree.body.stats.head.getTag() != JCTree.EXEC ||
-                            ((JCExpressionStatement)tree.body.stats.head).expr.getTag() != JCTree.APPLY) {
+                        JCMethodInvocation apply = TreeInfo.defenderMethodCall(tree.body.stats.head);
+                        if (apply == null) {
                             log.error(tree.body.stats.head, "bad.defender.method.body");
                             return defaultImpl = syms.errSymbol;
                         } else {
-                            JCTree defaultImplTree = ((JCExpressionStatement)tree.body.stats.head).expr;
-                            chk.checkType(defaultImplTree, localEnv, defaultImplTree.type, type.getReturnType());
-                            return defaultImpl = TreeInfo.symbol(((JCMethodInvocation)defaultImplTree).meth);
+                            chk.checkType(apply, localEnv, apply.type, type.getReturnType());
+                            return defaultImpl = TreeInfo.symbol(apply.meth);
                         }
                     }
                 }
--- a/src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java	Wed Oct 26 16:41:55 2011 +0100
+++ b/src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java	Thu Oct 27 16:41:02 2011 +0100
@@ -94,6 +94,9 @@
     /** Switch: describe the generated stackmap
      */
     boolean debugstackmap;
+    
+    /** Generate code attribute for extension methods */
+    boolean inlinedExtensionBodies;
 
     /**
      * Target class version.
@@ -664,6 +667,7 @@
     int writeDefenderAttribute(MethodSymbol m) {
         if (!target.hasDefenderAttribute() ||
                 (m.flags() & DEFENDER) == 0 ||
+                inlinedExtensionBodies ||
                 m.getDefaultImpl() == null)
             return 0;
 
--- a/src/share/classes/com/sun/tools/javac/jvm/Gen.java	Wed Oct 26 16:41:55 2011 +0100
+++ b/src/share/classes/com/sun/tools/javac/jvm/Gen.java	Thu Oct 27 16:41:02 2011 +0100
@@ -77,6 +77,9 @@
 
     /** Set when Miranda method stubs are to be generated. */
     private final boolean generateIproxies;
+    
+    /** Generate code attribute for extension methods */
+    private boolean inlinedExtensionBodies;
 
     /** Format of stackmap tables to be generated. */
     private final Code.StackMapFormat stackMap;
@@ -126,6 +129,10 @@
         generateIproxies =
             target.requiresIproxy() ||
             options.isSet("miranda");
+        
+        inlinedExtensionBodies =
+                target.hasDefenderAttribute() &&
+                options.isSet("inlinedExtensionBodies");
 
         if (target.generateStackMapTable()) {
             // ignore cldc because we cannot have both stackmap formats
@@ -903,7 +910,7 @@
             }
 
             else if (tree.body != null &&
-                    (tree.sym.flags() & DEFENDER) == 0) {
+                    (inlinedExtensionBodies || (tree.sym.flags() & DEFENDER) == 0)) {
                 // Create a new code structure and initialize it.
                 int startpcCrt = initCode(tree, env, fatcode);
 
--- a/src/share/classes/com/sun/tools/javac/parser/JavacParser.java	Wed Oct 26 16:41:55 2011 +0100
+++ b/src/share/classes/com/sun/tools/javac/parser/JavacParser.java	Thu Oct 27 16:41:02 2011 +0100
@@ -2942,7 +2942,11 @@
                         meth = toP(F.at(pos).Select(meth, ident()));
                     }
                     JCMethodInvocation defaultImpl = toP(F.at(pos).Apply(args, meth, defenderMethodParams, false));
-                    body = toP(F.at(pos).Block(0, List.<JCStatement>of(toP(F.at(pos).Exec(defaultImpl)))));
+                    JCStatement inlined_body = toP(F.at(pos).Exec(defaultImpl));
+                    if (!isVoid) {
+                        inlined_body = toP(F.at(pos).Return(defaultImpl));
+                    }
+                    body = toP(F.at(pos).Block(0, List.<JCStatement>of(inlined_body)));
                     defaultValue = null;
                     mode = prevMode;
                 }
--- a/src/share/classes/com/sun/tools/javac/tree/TreeInfo.java	Wed Oct 26 16:41:55 2011 +0100
+++ b/src/share/classes/com/sun/tools/javac/tree/TreeInfo.java	Thu Oct 27 16:41:02 2011 +0100
@@ -207,6 +207,16 @@
         if (exec.expr.getTag() != JCTree.APPLY) return null;
         return (JCMethodInvocation)exec.expr;
     }
+    
+    /** get the first statement (apply) of the synthetic defender method body */
+    public static JCMethodInvocation defenderMethodCall(JCTree tree) {
+        switch (tree.getTag()) {
+            case JCTree.EXEC: return defenderMethodCall(((JCExpressionStatement)tree).expr);
+            case JCTree.RETURN : return defenderMethodCall(((JCReturn)tree).expr);
+            case JCTree.APPLY : return (JCMethodInvocation)tree;
+            default: return null;
+        }
+    }
 
     /** Return true if a tree represents a diamond new expr. */
     public static boolean isDiamond(JCTree tree) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/defender/TestInlinedDefenderBody.java	Thu Oct 27 16:41:02 2011 +0100
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2011, 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
+ * @summary  check that code attributed for defender methods is correctly generated
+ * @compile -XDinlinedExtensionBodies TestInlinedDefenderBody.java
+ * @run main/othervm -Xverify:none TestInlinedDefenderBody
+ */
+
+import com.sun.tools.classfile.Attribute;
+import com.sun.tools.classfile.ClassFile;
+import com.sun.tools.classfile.ConstantPool.*;
+import com.sun.tools.classfile.Code_attribute;
+import com.sun.tools.classfile.Instruction;
+import com.sun.tools.classfile.Method;
+
+import com.sun.tools.classfile.Opcode;
+import java.io.*;
+
+public class TestInlinedDefenderBody {
+
+    interface TestInterface {
+        int no_defender(int i);
+        int defender(int i) default impl;
+    }
+
+    static int impl(TestInterface ti, int i) { return 0; }
+
+    static final String TARGET_CLASS_NAME = "TestInlinedDefenderBody";
+    static final String TARGET_NAME = "impl";
+    static final String TARGET_TYPE = "(LTestInlinedDefenderBody$TestInterface;I)I";
+    static final String SUBTEST_NAME = TestInterface.class.getName() + ".class";
+    static final String TEST_METHOD_NAME = "defender";
+
+    public static void main(String... args) throws Exception {
+        new TestInlinedDefenderBody().run();
+    }
+
+    public void run() throws Exception {
+        String workDir = System.getProperty("test.classes");
+        File compiledTest = new File(workDir, SUBTEST_NAME);
+        verifyInlinedDefenderBody(compiledTest);
+    }
+
+    void verifyInlinedDefenderBody(File f) {
+        System.err.println("verify: " + f);
+        try {
+            ClassFile cf = ClassFile.read(f);
+            Method testMethod = null;
+            Code_attribute codeAttr = null;
+            for (Method m : cf.methods) {
+                codeAttr = (Code_attribute)m.attributes.get(Attribute.Code);
+                String mname = m.getName(cf.constant_pool);
+                if (mname.equals(TEST_METHOD_NAME)) {
+                    testMethod = m;
+                    break;
+                } else {
+                    codeAttr = null;
+                }
+            }
+            if (testMethod == null) {
+                throw new Error("Test method not found");
+            }
+            if (codeAttr == null) {
+                throw new Error("Code attribute in test method not found");
+            }
+            
+            boolean found = false;
+            for (Instruction instr : codeAttr.getInstructions()) {
+                if (instr.getOpcode() == Opcode.INVOKESTATIC) {
+                    found = true;
+                    int pc_index = instr.getShort(1);
+                    CONSTANT_Methodref_info mref = (CONSTANT_Methodref_info)cf.constant_pool.get(pc_index);
+                    String className = mref.getClassName();
+                    String targetName = mref.getNameAndTypeInfo().getName();
+                    String targetType = mref.getNameAndTypeInfo().getType();
+
+                    if (!className.equals(TARGET_CLASS_NAME)) {
+                        throw new Error("unexpected class in defender method body " + className);
+                    }
+                    if (!targetName.equals(TARGET_NAME)) {
+                        throw new Error("unexpected method name in defender method body " + targetName);
+                    }
+                    if (!targetType.equals(TARGET_TYPE)) {
+                        throw new Error("unexpected method type in defender method body " + targetType);
+                    }
+                    break;
+                }
+            }
+            
+            if (!found) {
+                throw new Error("no invokestatic found in defender method body");
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new Error("error reading " + f +": " + e);
+        }
+    }
+}