--- /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"));
+ }
+ }
+}