test/hotspot/jtreg/runtime/InvocationTests/invokeinterface/Generator.java
changeset 55497 d3a33953b936
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/InvocationTests/invokeinterface/Generator.java	Wed Jun 26 09:06:32 2019 -0400
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2009, 2019, 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.
+ *
+ */
+
+/*
+ * INVOKE_INTERFACE EXPECTED RESULTS
+ *
+ * Let C be the class of objectref. The actual method to be invoked is selected
+ * by the following lookup procedure:
+ *     - If C contains a declaration for an instance method with the same name
+ *     and descriptor as the resolved method, then this is the method to be
+ *     invoked, and the lookup procedure terminates.
+ *
+ *     - Otherwise, if C has a superclass, this same lookup procedure is
+ *     performed recursively using the direct superclass of C; the method to be
+ *     invoked is the result of the recursive invocation of this lookup
+ *     procedure.
+ *
+ * Otherwise, if the class of objectref does not implement the resolved
+ * interface, invokeinterface throws an IncompatibleClassChangeError?.
+ *
+ * Otherwise, if no method matching the resolved name and descriptor is
+ * selected, invokeinterface throws an AbstractMethodError?.
+ *
+ * Otherwise, if the selected method is not public, invokeinterface throws an
+ * IllegalAccessError. Note that it cannot be private because private methods
+ * are ignored when searching for an interface method.
+ *
+ * My translation:
+ *      1. Resolve compile-time class/method.
+ *      2. Look up runtime class C, if it contains a name/signature match,
+ *      and it is not private, invoke it.
+ *      3. If it does not, recursively lookup direct superclass of C.
+ *      4. If selected method is not public, throw IllegalAccessError
+ *
+ * InvokeInterface Results:
+ *    - A interface class, declares A.m
+ *    - A compile-time resolved class
+ *    - C runtime resolved class
+ *    - InvokeInterface will ALWAYS invoke C.m if C.m exists and is not private,
+ *    regardless of overriding or accessibility
+ *    - InvokeInterface will invoke a non-private B.m if C.m does not exist,
+ *    regardless of overriding or accessibility
+ *
+ * Note: assuming Interface is public
+ *
+ * TODO: member interfaces can be protected and private and have special hiding
+ * rules (JLS 8.5)
+ */
+
+package invokeinterface;
+
+import static jdk.internal.org.objectweb.asm.Opcodes.*;
+import shared.AbstractGenerator;
+import shared.AccessType;
+import shared.Utils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Generator extends AbstractGenerator {
+    public Generator(String[] args) {
+        super(args);
+    }
+
+    protected Checker getChecker(Class paramClass, Class targetClass) {
+        return new Checker(paramClass, targetClass);
+    }
+
+    public static void main (String[] args) throws Exception {
+        new Generator(args).run();
+    }
+
+    private void run() throws Exception {
+        // Specify package names
+        String pkg1 = "a.";
+        String pkg2 = "b.";
+        String pkg3 = "c.";
+        String pkgIntf = "i.";
+        String[] packages = new String[] { "", pkg1, pkg2, pkg3, pkgIntf };
+
+        int testNum = 0;
+        boolean isPassed = true;
+
+        // Hierarchy
+        // The following triples will be used during further
+        // hierarchy construction and will specify packages for A, B and C
+        String[][] packageSets = new String[][] {
+              {   "",   "",   "", ""}
+            , {   "",   "",   "", pkgIntf }
+
+            , {   "", pkg1, pkg1, "" }
+            , {   "", pkg1, pkg1, pkg1 }
+            , {   "", pkg1, pkg1, pkgIntf }
+
+            , {   "", pkg1, pkg2, "" }
+            , {   "", pkg1, pkg2, pkg1}
+            , {   "", pkg1, pkg2, pkg2}
+            , {   "", pkg1, pkg2, pkgIntf}
+
+            , { pkg1, pkg1, pkg1, pkg1 }
+            , { pkg1, pkg1, pkg1, pkgIntf }
+
+            , { pkg1, pkg1, pkg2, pkg1 }
+            , { pkg1, pkg1, pkg2, pkg2 }
+            , { pkg1, pkg1, pkg2, pkgIntf }
+
+            , { pkg1, pkg2, pkg1, pkg1 }
+            , { pkg1, pkg2, pkg1, pkg2 }
+            , { pkg1, pkg2, pkg1, pkgIntf }
+
+            , { pkg1, pkg2, pkg2, pkg1 }
+            , { pkg1, pkg2, pkg2, pkg2 }
+            , { pkg1, pkg2, pkg2, pkgIntf }
+        };
+
+        String [] header = new String[] {
+            String.format("%30s %68s %25s", "Method access modifiers", "Call site location", "Status")
+                , String.format("%5s  %-12s %-12s %-12s %-12s   %7s %7s %7s %7s %7s %7s %7s"
+                        , "  # "
+                        , "A.m()"
+                        , "B.m()"
+                        , "C.m()"
+                        , "I.m()"
+                        , "  C  "
+                        , "pkgC "
+                        , "  B  "
+                        , " pkgB"
+                        , "  A  "
+                        , "pkgA"
+                        , "Intf"
+                        )
+                , "--------------------------------------------------------------------------------------------------------------------"
+        };
+
+        for (String aHeader : header) {
+            System.out.println(aHeader);
+        }
+
+        for (String[] pkgSet : packageSets) {
+            String packageA = pkgSet[0];
+            String packageB = pkgSet[1];
+            String packageC = pkgSet[2];
+
+            String packageIntf = pkgSet[3];
+
+            String classNameA = packageA + "A";
+            String classNameB = packageB + "B";
+            String classNameC = packageC + "C";
+            String classNameIntf = packageIntf + "I";
+
+            // For all possible access modifier combinations
+            for (AccessType accessA : AccessType.values()) {
+                for (AccessType accessB : AccessType.values()) {
+                    for (AccessType accessC : AccessType.values()) {
+                        for (AccessType accessIntf : AccessType.values()) {
+
+                            if (accessIntf == AccessType.UNDEF) {
+                                continue;
+                            }
+
+                            for (int I = 0; I < 4; I++) {
+                                boolean isAbstractA = ((I & 1) != 0);
+                                boolean isAbstractB = ((I & 2) != 0);
+
+                                testNum++;
+
+                                Map<String, byte[]> classes = new HashMap<String, byte[]>();
+
+                                // TODO: add non-PUBLIC interfaces, then particular call sites will affect the results
+
+                                // Generate interface Intf
+                                classes.put(
+                                        classNameIntf
+                                        , new ClassGenerator( classNameIntf
+                                                            , "java.lang.Object"
+                                                            , ACC_ABSTRACT | ACC_INTERFACE | accessIntf.value())
+                                            .addTargetMethod(AccessType.PUBLIC)
+                                            .getClassFile()
+                                        );
+
+                                // Generate class A
+                                classes.put(
+                                        classNameA
+                                        , new ClassGenerator( classNameA
+                                                            , "java.lang.Object"
+                                                            , ACC_PUBLIC | ( isAbstractA ? ACC_ABSTRACT : 0))
+                                            .addTargetMethod(accessA)
+                                            .addCaller(classNameIntf)
+                                            .getClassFile()
+                                        );
+
+                                // Generate class B
+                                classes.put(
+                                        classNameB
+                                        , new ClassGenerator( classNameB
+                                                            , classNameA
+                                                            , ACC_PUBLIC | ( isAbstractB ? ACC_ABSTRACT : 0)
+                                                            , new String[] { Utils.getInternalName(classNameIntf) })
+                                            .addTargetMethod(accessB)
+                                            .addCaller(classNameIntf)
+                                            .getClassFile()
+                                        );
+
+                                // Generate class C
+                                classes.put( classNameC
+                                           , new ClassGenerator( classNameC, classNameB )
+                                               .addTargetMethod(accessC)
+                                               .addCaller(classNameIntf)
+                                               .getClassFile()
+                                           );
+
+                                // Generate package callers
+                                for (String pkg : packages) {
+                                    classes.put( pkg+"Caller"
+                                               , new ClassGenerator(pkg+"Caller")
+                                                   .addCaller(classNameIntf)
+                                                   .getClassFile()
+                                               );
+                                }
+
+                                String caseDescription =
+                                        String.format("%-12s %-12s %-12s %-12s| "
+                                            , (isAbstractA ? "! " : "  ") + classNameA + " " + accessA
+                                            , (isAbstractB ? "! " : "  ") + classNameB + " " + accessB
+                                            , classNameC + " " + accessC
+                                            , accessIntf + " " + classNameIntf
+                                            );
+
+                                String[] callSites = new String[] {
+                                        classNameC
+                                        , packageC+"Caller"
+                                        , classNameB
+                                        , packageB+"Caller"
+                                        , classNameA
+                                        , packageA+"Caller"
+                                        , packageIntf+"Caller"
+                                };
+
+                                boolean result = exec(classes, caseDescription, classNameIntf, classNameC, callSites);
+                                isPassed = isPassed && result;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        // Print footer
+
+        for (int i = header.length-1; i >= 0; i--) {
+            System.out.println(header[i]);
+        }
+
+        if (executeTests) {
+            System.out.printf("\nEXECUTION STATUS: %s\n", (isPassed? "PASSED" : "FAILED"));
+        }
+    }
+}