changeset 55981:d3a33953b936

8224137: Analyze and port invocation tests to jtreg and co-locate to jdk repo Summary: Add JTReg compatible main programs to run tests for various invoke* instructions Reviewed-by: lfoltan, coleenp
author hseigel
date Wed, 26 Jun 2019 09:06:32 -0400
parents 8e0ae3830fca
children e64383344f14
files test/hotspot/jtreg/TEST.groups test/hotspot/jtreg/runtime/InvocationTests/invokeinterface/Checker.java test/hotspot/jtreg/runtime/InvocationTests/invokeinterface/ClassGenerator.java test/hotspot/jtreg/runtime/InvocationTests/invokeinterface/Generator.java test/hotspot/jtreg/runtime/InvocationTests/invokeinterfaceTests.java test/hotspot/jtreg/runtime/InvocationTests/invokespecial/Checker.java test/hotspot/jtreg/runtime/InvocationTests/invokespecial/ClassGenerator.java test/hotspot/jtreg/runtime/InvocationTests/invokespecial/Generator.java test/hotspot/jtreg/runtime/InvocationTests/invokespecialTests.java test/hotspot/jtreg/runtime/InvocationTests/invokevirtual/Checker.java test/hotspot/jtreg/runtime/InvocationTests/invokevirtual/ClassGenerator.java test/hotspot/jtreg/runtime/InvocationTests/invokevirtual/Generator.java test/hotspot/jtreg/runtime/InvocationTests/invokevirtualTests.java test/hotspot/jtreg/runtime/InvocationTests/shared/AbstractGenerator.java test/hotspot/jtreg/runtime/InvocationTests/shared/AccessCheck.java test/hotspot/jtreg/runtime/InvocationTests/shared/AccessType.java test/hotspot/jtreg/runtime/InvocationTests/shared/ByteArrayClassLoader.java test/hotspot/jtreg/runtime/InvocationTests/shared/Caller.java test/hotspot/jtreg/runtime/InvocationTests/shared/Checker.java test/hotspot/jtreg/runtime/InvocationTests/shared/ExecutorGenerator.java test/hotspot/jtreg/runtime/InvocationTests/shared/GenericClassGenerator.java test/hotspot/jtreg/runtime/InvocationTests/shared/Utils.java
diffstat 22 files changed, 2925 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/test/hotspot/jtreg/TEST.groups	Wed Jun 26 05:49:59 2019 +0000
+++ b/test/hotspot/jtreg/TEST.groups	Wed Jun 26 09:06:32 2019 -0400
@@ -275,6 +275,7 @@
  -runtime/ErrorHandling/ErrorHandler.java \
  -runtime/ErrorHandling/TestHeapDumpOnOutOfMemoryError.java \
  -runtime/ErrorHandling/TimeoutInErrorHandlingTest.java \
+ -runtime/InvocationTests \
  -runtime/logging/MonitorMismatchTest.java \
  -runtime/memory/ReserveMemory.java \
  -runtime/memory/RunUnitTestsConcurrently.java \
@@ -384,6 +385,7 @@
   serviceability/ \
  -runtime/CommandLine/OptionsValidation/TestOptionsWithRanges.java \
  -runtime/CompressedOops/UseCompressedOops.java \
+ -runtime/InvocationTests \
  -runtime/Thread/TestThreadDumpMonitorContention.java \
  -:tier1_runtime \
  -:tier1_serviceability \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/InvocationTests/invokeinterface/Checker.java	Wed Jun 26 09:06:32 2019 -0400
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ *
+ */
+
+package invokeinterface;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+public class Checker extends shared.Checker {
+    private Class interfaceClass;
+
+    public Checker(Class interfaceClass, Class dynamicTargetClass) {
+        super(interfaceClass, dynamicTargetClass);
+
+        if (staticTargetClass.isInterface()) {
+            this.interfaceClass = staticTargetClass;
+        } else {
+            throw new RuntimeException("Static target class should be an interface.");
+        }
+    }
+
+    public String check (Class callerClass) {
+        // Check access rights to interface for caller
+        if (!checkAccess(interfaceClass, callerClass)) {
+            return "java.lang.IllegalAccessError";
+        }
+
+        // NSME is thrown when interface doesn't declare the method
+        if (getDeclaredMethod(interfaceClass) == null) {
+            return "java.lang.NoSuchMethodError";
+        }
+
+        // 9.1.5 Access to Interface Member Names
+        // "All interface members are implicitly public. They are
+        // accessible outside the package where the interface is
+        // declared if the interface is also declared public or
+        // protected, in accordance with the rules of 6.6."
+
+        // Search for method declaration in the hierarchy
+        Class klass = dynamicTargetClass;
+
+        while (klass != Object.class) {
+            Method method = getDeclaredMethod(klass);
+
+            if (method != null) {
+                int modifiers = method.getModifiers();
+
+                // Check whether obtained method is public and isn't abstract
+                if ( Modifier.isPublic(modifiers))
+                {
+                    if (Modifier.isAbstract(modifiers)) {
+                        return "java.lang.AbstractMethodError";
+                    } else {
+                        return String.format("%s.%s"
+                            , method.getDeclaringClass().getSimpleName()
+                            , methodName
+                            );
+                    }
+                } else {
+                    // IAE is thrown when located method isn't PUBLIC
+                    // or private.  Private methods are skipped when
+                    // looking for an interface method.
+                    if (!Modifier.isPrivate(modifiers)) {
+                        return "java.lang.IllegalAccessError";
+                    }
+                }
+            }
+
+            klass = klass.getSuperclass();
+        }
+
+        // No method declaration is found
+        return "java.lang.AbstractMethodError";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/InvocationTests/invokeinterface/ClassGenerator.java	Wed Jun 26 09:06:32 2019 -0400
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ *
+ */
+
+package invokeinterface;
+
+import jdk.internal.org.objectweb.asm.Opcodes;
+import shared.GenericClassGenerator;
+
+/*******************************************************************/
+class ClassGenerator extends GenericClassGenerator<ClassGenerator> {
+    public ClassGenerator(String fullClassName) {
+        super(fullClassName);
+    }
+
+    public ClassGenerator(String fullClassName, String parentClassName) {
+        super(fullClassName, parentClassName);
+    }
+
+    public ClassGenerator(String fullClassName, String parentClassName, int flags) {
+        super(fullClassName, parentClassName, flags);
+    }
+
+    public ClassGenerator(String fullClassName, String parentClassName, int flags, String[] implementedInterfaces) {
+        super(fullClassName, parentClassName, flags, implementedInterfaces);
+    }
+
+    // Add target method call site into current class
+    public ClassGenerator addCaller(String targetClass) {
+        return super.addCaller(targetClass, Opcodes.INVOKEINTERFACE);
+    }
+}
--- /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"));
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/InvocationTests/invokeinterfaceTests.java	Wed Jun 26 09:06:32 2019 -0400
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 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.
+ *
+ */
+
+/*
+ * @test
+ * @bug 8224137
+ * @summary Run invokeinterface invocation tests
+ * @library /test/lib
+ * @modules java.base/jdk.internal.org.objectweb.asm
+ *          java.base/jdk.internal.misc
+ * @compile shared/AbstractGenerator.java shared/AccessCheck.java shared/AccessType.java
+ *          shared/Caller.java shared/ExecutorGenerator.java shared/Utils.java
+ *          shared/ByteArrayClassLoader.java shared/Checker.java shared/GenericClassGenerator.java
+ * @compile invokeinterface/Checker.java invokeinterface/ClassGenerator.java
+ *          invokeinterface/Generator.java
+ *
+ * @run main/othervm/timeout=1800 invokeinterfaceTests
+ */
+
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.compiler.InMemoryJavaCompiler;
+
+public class invokeinterfaceTests {
+
+    public static void runTest(String classFileVersion, String option) throws Exception {
+        System.out.println("\ninvokeinterface invocation tests, option: " + option +
+                           ", class file version: " + classFileVersion);
+        ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(false, "-Xmx128M", option,
+            "invokeinterface.Generator", "--classfile_version=" + classFileVersion);
+        OutputAnalyzer output = new OutputAnalyzer(pb.start());
+        try {
+            output.shouldContain("EXECUTION STATUS: PASSED");
+            output.shouldHaveExitValue(0);
+        } catch (Throwable e) {
+            System.out.println(
+                "\nNote that an entry such as 'B.m/C.m' in the failure chart means that" +
+                " the test case failed because method B.m was invoked but the test " +
+                "expected method C.m to be invoked. Similarly, a result such as 'AME/C.m'" +
+                " means that an AbstractMethodError exception was thrown but the test" +
+                " case expected method C.m to be invoked.");
+            System.out.println(
+                "\nAlso note that passing --dump to invokeinterface.Generator will" +
+                " dump the generated classes (for debugging purposes).\n");
+
+            System.exit(1);
+        }
+    }
+
+    public static void main(String args[]) throws Throwable {
+        // Get current major class file version and test with it.
+        byte klassbuf[] = InMemoryJavaCompiler.compile("blah", "public class blah { }");
+        int major_version = klassbuf[6] << 8 | klassbuf[7];
+        runTest(String.valueOf(major_version), "-Xint");
+        runTest(String.valueOf(major_version), "-Xcomp");
+
+        // Test old class file version.
+        runTest("51", "-Xint"); // JDK-7
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/InvocationTests/invokespecial/Checker.java	Wed Jun 26 09:06:32 2019 -0400
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ *
+ */
+
+package invokespecial;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+public class Checker extends shared.Checker {
+
+    public Checker(Class staticTargetClass, Class dynamicTargetClass) {
+        super(staticTargetClass, dynamicTargetClass);
+    }
+
+    public String check (Class callerClass) {
+        /*
+         * If objectref is null, the invokespecial instruction throws a NullPointerException.
+         */
+        if (dynamicTargetClass == null) {
+            return "java.lang.NullPointerException";
+        }
+
+        /*
+         * TODO: find a citiation from spec for this case
+         */
+        Method resolvedMethod;
+        try {
+            // May throw VerifyError
+            resolvedMethod = getMethodInHierarchy(staticTargetClass);
+        } catch (Throwable e) {
+            return e.getClass().getName();
+        }
+
+        if (resolvedMethod == null) {
+            return "java.lang.NoSuchMethodError";
+        }
+
+       /*
+        * If:
+        *   - the resolved method is protected (4.7)
+        *   - it is a member of a superclass of the current class
+        *   - the method is not declared in the same run-time package (5.3) as the current class
+        * then:
+        *   the class of objectref must be either the current class or a subclass of the
+        * current class.
+        */
+
+        if (Modifier.isProtected(resolvedMethod.getModifiers())) {
+            Method methodInSuperclass = getMethodInHierarchy(resolvedMethod.getDeclaringClass().getSuperclass());
+
+            if (methodInSuperclass != null) {
+                String resolvedMethodPkg = getClassPackageName(resolvedMethod.getDeclaringClass());
+                String methodInSuperclassPkg = getClassPackageName(methodInSuperclass.getDeclaringClass());
+
+                if (!resolvedMethodPkg.equals(methodInSuperclassPkg)) {
+                    //TODO: clarify this
+//                    if (callerClass == methodInSuperclass.getDeclaringClass()) {
+//                        return "java.lang.IllegalAccessError";
+//                    }
+                }
+            }
+        }
+
+       /*
+        * The resolved method is selected for invocation unless all of
+        * the following conditions are true:
+        *     * TODO: The ACC_SUPER flag (see Table 4.1, "Class access and property
+        *       modifiers") is set for the current class.
+        *     * The class of the resolved method is a superclass of the
+        *       current class - assumed by construction procedure
+        *
+        *     * The resolved method is not an instance initialization method (3.9).
+        */
+        if (!"<init>".equals(methodName)) {
+           /*
+            * Let C be the direct superclass of the current class:
+            *    * If C contains a declaration for an instance method with the same
+            *      name and descriptor as the resolved method, then this method will be
+            *      invoked. 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, an AbstractMethodError is raised.
+            *      TODO: so far, sometimes NSME is thrown
+            */
+            Class klass = dynamicTargetClass.getSuperclass();
+
+            while (klass != Object.class) {
+                Method method = getDeclaredMethod(klass);
+
+                if (method != null) {
+                    /*
+                     * If the resolved method is a class (static) method, the
+                     * invokespecial instruction throws an IncompatibleClassChangeError.
+                     */
+                    if (Modifier.isStatic(method.getModifiers())) {
+                        return "java.lang.IncompatibleClassChangeError";
+                    }
+
+                    // Check access rights
+                    if ( checkAccess(method, callerClass)
+//                         && !(
+//                                 Modifier.isProtected(method.getModifiers())
+//                                 && (
+//                                     staticTargetClass.isAssignableFrom(callerClass)
+//                                     || getClassPackageName(staticTargetClass).equals(getClassPackageName(callerClass))
+//                                    )
+//
+//                            )
+                        )
+                    {
+                        return String.format("%s.%s"
+                                , method.getDeclaringClass().getSimpleName()
+                                , methodName
+                                );
+                    } else {
+                        // IAE is thrown when located method can't be accessed from the call site
+                        return "java.lang.IllegalAccessError";
+                    }
+                }
+
+                klass = klass.getSuperclass();
+            }
+
+            return "java.lang.AbstractMethodError";
+        } else {
+            // The resolved method is an instance initialization method (3.9).
+        }
+
+        // TODO: change
+        return "---";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/InvocationTests/invokespecial/ClassGenerator.java	Wed Jun 26 09:06:32 2019 -0400
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ *
+ */
+
+package invokespecial;
+
+import jdk.internal.org.objectweb.asm.Opcodes;
+import shared.GenericClassGenerator;
+
+/*******************************************************************/
+class ClassGenerator extends GenericClassGenerator<ClassGenerator> {
+    public ClassGenerator(String fullClassName, String parentClassName, int flags) {
+        super(fullClassName, parentClassName, flags);
+    }
+
+    // Add target method call site into current class
+    public ClassGenerator addCaller(String targetClass) {
+        return super.addCaller(targetClass, Opcodes.INVOKESPECIAL);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/InvocationTests/invokespecial/Generator.java	Wed Jun 26 09:06:32 2019 -0400
@@ -0,0 +1,452 @@
+/*
+ * 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.
+ *
+ */
+
+/*
+ * INVOKESPECIAL EXPECTED RESULTS
+ *
+ * From JVMS 3rd edition: invokespecial instruction:
+ *
+ * Invoke instance method; special handling for superclass, private, and instance
+ * initialization method invocations
+ *
+ * The named method is resolved (5.4.3.3). Finally, if the resolved method is
+ * protected (4.7), and it is a member of a superclass of the current class, and
+ * the method is not declared in the same run-time package (5.3) as the current
+ * class, then the class of objectref must be either the current class or a
+ * subclass of the current class.
+ *
+ * Next, the resolved method is selected for invocation unless all of the
+ * following conditions are true:
+ *     * The ACC_SUPER flag (see Table 4.1, "Class access and property modifiers") is set for the current class.
+ *     * The class of the resolved method is a superclass of the current class.
+ *     * The resolved method is not an instance initialization method (3.9).
+ *
+ * If the above conditions are true, the actual method to be invoked is selected
+ * by the following lookup procedure. Let C be the direct superclass of the
+ * current class:
+ *     * If C contains a declaration for an instance method with the same name and
+ *       descriptor as the resolved method, then this method will be invoked.
+ *       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, an AbstractMethodError? is raised.
+ *
+ * During resolution of the symbolic reference to the method, any of the
+ * exceptions pertaining to method resolution documented in Section 5.4.3.3 can be
+ * thrown.
+ *
+ * Otherwise, if the resolved method is an instance initialization method, and the
+ * class in which it is declared is not the class symbolically referenced by the
+ * instruction, a NoSuchMethodError? is thrown.
+ *
+ * Otherwise, if the resolved method is a class (static) method, the invokespecial
+ * instruction throws an IncompatibleClassChangeError?.
+ *
+ * Otherwise, if no method matching the resolved name and descriptor is selected,
+ * invokespecial throws an AbstractMethodError?.
+ *
+ * Otherwise, if the selected method is abstract, invokespecial throws an
+ * AbstractMethodError?.
+ *
+ * RUNTIME EXCEPTIONS
+ *
+ * Otherwise, if objectref is null, the invokespecial instruction throws a NullPointerException?.
+ *
+ * Otherwise, if the selected method is native and the code that implements the
+ * method cannot be bound, invokespecial throws an UnsatisfiedLinkError?.
+ *
+ * NOTES
+ *
+ * The difference between the invokespecial and the invokevirtual instructions is
+ * that invokevirtual invokes a method based on the class of the object. The
+ * invokespecial instruction is used to invoke instance initialization methods
+ * (3.9) as well as private methods and methods of a superclass of the current
+ * class.
+ *
+ * ACC_SUPER:
+ *
+ * The setting of the ACC_SUPER flag indicates which of two alternative semantics
+ * for its invokespecial instruction the Java virtual machine is to express; the
+ * ACC_SUPER flag exists for backward compatibility for code compiled by Sun's
+ * older compilers for the Java programming language. All new implementations of
+ * the Java virtual machine should implement the semantics for invokespecial
+ * documented in this specification. All new compilers to the instruction set of
+ * the Java virtual machine should set the ACC_SUPER flag. Sun's older compilers
+ * generated ClassFile? flags with ACC_SUPER unset. Sun's older Java virtual
+ * machine implementations ignore the flag if it is set.
+ *
+ * ACC_SUPER 0x0020 Treat superclass methods specially when invoked by the
+ * invokespecial instruction.
+ *
+ * My Translation:
+ *     1. compile-time resolved class B
+ *     2. A,B,C direct superclass relationships
+ *     3. If B.m is protected
+ *          - if the caller is in B
+ *                then runtime resolved class must be in B or C
+ *          - if the caller is in C
+ *                then runtime resolved class must be in C
+ *     TODO: otherwise what is thrown? <noWikiWord>AbstractMethodError?
+ *     4. If B.m is an instance initialization method,
+ *          invoke B.m
+ *     5. If backward compatible caller does not set ACC_SUPER,
+ *          invoke B.m
+ *     6. If B is not a superclass of the caller, e.g. A is caller, or unrelated X
+ *        is the caller, invoke B.m
+ *     7. Otherwise:
+ *        If superclass of caller contains name/sig match, use it
+ *        Else, recursively through that superclass
+ *     8. If none found, throw AbstractMethodError
+ *
+ * Note: there is NO mention of overriding or accessibility in determining
+ * resolved method, except for if the compile-time type is protected.
+ *
+ * Case 1: B.m is protected
+ *         Caller in A: if runtime resolved class in A.m, AbstractMethodError
+ *         Caller in B: if runtime resolved class in A.m, AbstractMethodError
+ * Case 2: B.m is an instance initialization method
+ *         Always invoke B.m
+ * Case 3: older javac, caller does not set ACC_SUPER
+ *         Always invoke B.m
+ * Case 4: A or X (not in hierarchy) calls invokespecial on B.m, invoke B.m
+ * Case 5: Caller in B:
+ *           if A.m exists, call it, else <noWikiWord>AbstractMethodError
+ *         Caller in C:
+ *           if B.m exists, call it
+ *           if B.m does not exist, and A.m exists, call it
+ */
+
+//   TODO: classes without ACC_SUPER attribute
+//   TODO: B.m is an instance initialization method
+
+/*
+ *   invokespecial <method-spec>
+ *
+ * invokespecial is used in certain special cases to invoke a method
+ * Specifically, invokespecial is used to invoke:
+ *      - the instance initialization method, <init>
+ *      - a private method of this
+ *      - a method in a superclass of this
+ *
+ * The main use of invokespecial is to invoke an object's instance
+ * initialization method, <init>, during the construction phase for a new object.
+ * For example, when you write in Java:
+ *
+ *      new StringBuffer()
+ *
+ * code like the following is generated:
+ *      new java/lang/StringBuffer         ; create a new StringBuffer
+ *      dup                                ; make an extra reference to the new instance
+ *                                         ; now call an instance initialization method
+ *      invokespecial java/lang/StringBuffer/<init>()V
+ *                                         ; stack now contains an initialized StringBuffer.
+ *
+ * invokespecial is also used by the Java language by the 'super' keyword to
+ * access a superclass's version of a method. For example, in the class:
+ *
+ *     class Example {
+ *         // override equals
+ *         public boolean equals(Object x) {
+ *              // call Object's version of equals
+ *              return super.equals(x);
+ *         }
+ *     }
+ *
+ * the 'super.equals(x)' expression is compiled to:
+ *
+ *     aload_0  ; push 'this' onto the stack
+ *     aload_1  ; push the first argument (i.e. x) onto the stack
+ *              ; now invoke Object's equals() method.
+ *     invokespecial java/lang/Object/equals(Ljava/lang/Object;)Z
+ *
+ * Finally, invokespecial is used to invoke a private method. Remember that
+ * private methods are only visible to other methods belonging the same class as
+ * the private method.
+ *
+ * Before performing the method invocation, the class and the method identified
+ * by <method-spec> are resolved. See Chapter 9 for a description of how methods
+ * are resolved.
+ *
+ * invokespecial first looks at the descriptor given in <method-spec>, and
+ * determines how many argument words the method takes (this may be zero). It
+ * pops these arguments off the operand stack. Next it pops objectref (a
+ * reference to an object) off the operand stack. objectref must be an instance
+ * of the class named in <method-spec>, or one of its subclasses. The interpreter
+ * searches the list of methods defined by the class named in <method-spec>,
+ * looking for a method called methodname whose descriptor is descriptor. This
+ * search is not based on the runtime type of objectref, but on the compile time
+ * type given in <method-spec>.
+ *
+ * Once a method has been located, invokespecial calls the method. First, if
+ * the method is marked as synchronized, the monitor associated with objectref is
+ * entered. Next, a new stack frame structure is established on the call stack.
+ * Then the arguments for the method (which were popped off the current method's
+ * operand stack) are placed in local variables of the new stack frame structure.
+ * arg1 is stored in local variable 1, arg2 is stored in local variable 2 and so
+ * on. objectref is stored in local variable 0 (the local variable used for the
+ * special Java variable this). Finally, execution continues at the first
+ *instruction in the bytecode of the new method.
+ *
+ * Methods marked as native are handled slightly differently. For native
+ * methods, the runtime system locates the platform-specific code for the method,
+ * loading it and linking it into the JVM if necessary. Then the native method
+ * code is executed with the arguments popped from the operand stack. The exact
+ * mechanism used to invoke native methods is implementation-specific.
+ *
+ * When the method called by invokespecial returns, any single (or double) word
+ * return result is placed on the operand stack of the current method. If the
+ * invoked method was marked as synchronized, the monitor associated with
+ * objectref is exited. Execution continues at the instruction that follows
+ * invokespecial in the bytecode.
+ *
+ * Notes
+ *
+ * 1. In Java Virtual Machine implementations prior to version JDK 1.02, this
+ * instruction was called invokenonvirtual, and was less restrictive than
+ * invokespecial - it wasn't limited to invoking only superclass, private or
+ * <init> methods. The class access flag ACC_SUPER (see Chapter 4) is used to
+ * indicate which semantics are used by a class. In older class files, the
+ * ACC_SUPER flag is unset. In all new classes, the ACC_SUPER flag should be set,
+ * indicating that the restrictions enforced by invokespecial are obeyed. (In
+ * practice, all the common uses of invokenonvirtual continue to be supported
+ * by invokespecial, so this change should have little impact on JVM users).
+ *
+ */
+
+package invokespecial;
+
+import static jdk.internal.org.objectweb.asm.Opcodes.*;
+import shared.AbstractGenerator;
+import shared.AccessType;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Generator extends AbstractGenerator {
+    public static void main (String[] args) throws Exception {
+        new Generator(args).run();
+    }
+    public Generator(String[] args) {
+        super(args);
+    }
+
+    protected Checker getChecker(Class paramClass, Class targetClass) {
+        return new Checker(paramClass, targetClass);
+    }
+
+    public void run() throws Exception {
+        // Specify package names
+        String pkg1 = "a.";
+        String pkg2 = "b.";
+        String[] packages = new String[] { "", pkg1, pkg2 };
+
+        boolean isPassed = true;
+
+        // HIERARCHIES
+        // The following triples will be used during further
+        // hierarchy construction and will specify packages for A, B and C
+        String[][] packageSets = new String[][] {
+              {   "",   "",   "" }
+            , {   "", pkg1, pkg1 }
+            , {   "", pkg1, pkg2 }
+            , { pkg1,   "", pkg1 }
+            , { pkg1,   "", pkg2 }
+            , { pkg1, pkg1,   "" }
+            , { pkg1, pkg2,   "" }
+            , { pkg1, pkg1, pkg1 }
+            , { pkg1, pkg1, pkg2 }
+            , { pkg1, pkg2, pkg1 }
+            , { pkg1, pkg2, pkg2 }
+        };
+
+        String [] header = new String[] {
+            String.format("%30s %35s", "Method access modifiers", "Call site location")
+                , String.format("%4s  %-10s %-10s %-10s   %7s %7s %7s %7s %7s %7s %7s"
+                        , "  # "
+                        , "A.m()"
+                        , "B.m()"
+                        , "C.m()"
+                        , "  A  "
+                        , "pkgA"
+                        , "  B  "
+                        , " pkgB"
+                        , "  C  "
+                        , "pkgC "
+                        , "  X  "
+                        )
+                , "-----------------------------------------------------------------------------------------------------------"
+        };
+
+        // Print header
+        for (String str : header) {
+            System.out.println(str);
+        }
+
+        // Iterate over all interesting package combinations
+        for (String[] pkgSet : packageSets) {
+            String packageA = pkgSet[0];
+            String packageB = pkgSet[1];
+            String packageC = pkgSet[2];
+
+            String classNameA = packageA + "A";
+            String classNameB = packageB + "B";
+            String classNameC = packageC + "C";
+
+            // For all possible access modifier combinations
+            for (AccessType accessFlagA : AccessType.values()) {
+                for (AccessType accessFlagB : AccessType.values()) {
+                    for (AccessType accessFlagC : AccessType.values()) {
+                        Map<String, byte[]> classes = new HashMap<String, byte[]>();
+
+                        String calleeClassName = classNameB;
+                        int classFlags = ACC_PUBLIC;
+
+                        // The following hierarhcy is created:
+                        //     c.C extends b.B extends a.A extends Object - base hierarchy
+                        //     X extends Object - external caller
+                        //     c.Caller, b.Caller, a.Caller extends Object - package callers
+
+                        // Generate result storage
+                        classes.put(
+                                "Result"
+                                , new ClassGenerator(
+                                    "Result"
+                                    , "java.lang.Object"
+                                    , ACC_PUBLIC
+                                    )
+                                .addField(
+                                    ACC_PUBLIC | ACC_STATIC
+                                    , "value"
+                                    , "java.lang.String"
+                                    )
+                                .getClassFile()
+                                );
+
+                        // Generate class A
+                        classes.put(
+                                classNameA
+                                , new ClassGenerator(
+                                    classNameA
+                                    , "java.lang.Object"
+                                    , classFlags
+                                    )
+                                .addTargetConstructor(accessFlagA)
+                                .addTargetMethod(accessFlagA)
+                                .addCaller(calleeClassName)
+                                .getClassFile()
+                                );
+
+                        // Generate class B
+                        classes.put(
+                                classNameB
+                                , new ClassGenerator(
+                                    classNameB
+                                    , classNameA
+                                    , classFlags
+                                    )
+                                .addTargetConstructor(accessFlagB)
+                                .addTargetMethod(accessFlagB)
+                                .addCaller(calleeClassName)
+                                .getClassFile()
+                                );
+
+                        // Generate class C
+                        classes.put(
+                                classNameC
+                                , new ClassGenerator(
+                                    classNameC
+                                    , classNameB
+                                    , classFlags
+                                    )
+                                .addTargetConstructor(accessFlagC)
+                                .addTargetMethod(accessFlagC)
+                                .addCaller(calleeClassName)
+                                .getClassFile()
+                                );
+
+                        // Generate class X
+                        String classNameX = "x.X";
+                        classes.put(
+                                classNameX
+                                , new ClassGenerator(
+                                    classNameX
+                                    , "java.lang.Object"
+                                    , classFlags
+                                    )
+                                .addTargetMethod(accessFlagC)
+                                .addCaller(calleeClassName)
+                                .getClassFile()
+                                );
+
+                        // Generate package callers
+                        for (String pkg : packages) {
+                            classes.put(
+                                    pkg+"Caller"
+                                    , new ClassGenerator(
+                                        pkg+"Caller"
+                                        , "java.lang.Object"
+                                        , classFlags
+                                        )
+                                    .addCaller(calleeClassName)
+                                    .getClassFile()
+                                    );
+                        }
+
+                        String[] callSites = new String[] {
+                                classNameA
+                                , packageA+"Caller"
+                                , classNameB
+                                , packageB+"Caller"
+                                , classNameC
+                                , packageC+"Caller"
+                                , classNameX
+                        };
+
+                        String caseDescription = String.format(
+                                    "%-10s %-10s %-10s| "
+                                    , classNameA + " " + accessFlagA
+                                    , classNameB + " " + accessFlagB
+                                    , classNameC + " " + accessFlagC
+                                    );
+
+                        boolean result = exec(classes, caseDescription, calleeClassName, 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"));
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/InvocationTests/invokespecialTests.java	Wed Jun 26 09:06:32 2019 -0400
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 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.
+ *
+ */
+
+/*
+ * @test
+ * @bug 8224137
+ * @summary Run invokespecial invocation tests
+ * @library /test/lib
+ * @modules java.base/jdk.internal.org.objectweb.asm
+ *          java.base/jdk.internal.misc
+ * @compile shared/AbstractGenerator.java shared/AccessCheck.java shared/AccessType.java
+ *          shared/Caller.java shared/ExecutorGenerator.java shared/Utils.java
+ *          shared/ByteArrayClassLoader.java shared/Checker.java shared/GenericClassGenerator.java
+ * @compile invokespecial/Checker.java invokespecial/ClassGenerator.java invokespecial/Generator.java
+ *
+ * @run main/othervm/timeout=1800 invokespecialTests
+ */
+
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.compiler.InMemoryJavaCompiler;
+
+public class invokespecialTests {
+
+    public static void runTest(String classFileVersion, String option) throws Exception {
+        System.out.println("\ninvokespecial invocation tests, option: " + option +
+                           ", class file version: " + classFileVersion);
+        ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(false, "-Xmx128M", option,
+            "invokespecial.Generator", "--classfile_version=" + classFileVersion);
+        OutputAnalyzer output = new OutputAnalyzer(pb.start());
+        try {
+            output.shouldContain("EXECUTION STATUS: PASSED");
+            output.shouldHaveExitValue(0);
+        } catch (Throwable e) {
+            System.out.println(
+                "\nNote that an entry such as 'B.m/C.m' in the failure chart means that" +
+                " the test case failed because method B.m was invoked but the test " +
+                "expected method C.m to be invoked. Similarly, a result such as 'AME/C.m'" +
+                " means that an AbstractMethodError exception was thrown but the test" +
+                " case expected method C.m to be invoked.");
+            System.out.println(
+                "\nAlso note that passing --dump to invokespecial.Generator will" +
+                " dump the generated classes (for debugging purposes).\n");
+            System.exit(1);
+        }
+    }
+
+    public static void main(String args[]) throws Throwable {
+        // Get current major class file version and test with it.
+        byte klassbuf[] = InMemoryJavaCompiler.compile("blah", "public class blah { }");
+        int major_version = klassbuf[6] << 8 | klassbuf[7];
+        runTest(String.valueOf(major_version), "-Xint");
+        runTest(String.valueOf(major_version), "-Xcomp");
+
+        // Test old class file version.
+        runTest("51", "-Xint"); // JDK-7
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/InvocationTests/invokevirtual/Checker.java	Wed Jun 26 09:06:32 2019 -0400
@@ -0,0 +1,145 @@
+/*
+ * 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.
+ *
+ */
+
+package invokevirtual;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+
+public class Checker extends shared.Checker {
+    public Checker(Class staticTargetClass, Class dynamicTargetClass) {
+        super(staticTargetClass, dynamicTargetClass);
+    }
+
+    public String check (Class callerClass) {
+        Method m;
+        try {
+            // May cause java.lang.VerifyError
+            m = getOverriddenMethod();
+        } catch (Throwable e) {
+            return e.getClass().getName();
+        }
+
+        // Check method accessibility (it's a static property, according to JLS #6.6: Access Control)
+        if (m != null) {
+            Method staticTargetMethod = getDeclaredMethod(staticTargetClass);
+
+            if (checkAccess(staticTargetMethod, callerClass)) {
+                // Can't invoke abstract method
+                if ( Modifier.isAbstract(m.getModifiers())) {
+                    return "java.lang.AbstractMethodError";
+                }
+
+                return String.format("%s.%s"
+                        , m.getDeclaringClass().getSimpleName()
+                        , methodName
+                        );
+            } else {
+                // if method isn't accessible, IllegalAccessError is thrown
+                return "java.lang.IllegalAccessError";
+            }
+        } else {
+            // if method == null, NoSuchMethodError is thrown
+            return "java.lang.NoSuchMethodError";
+        }
+    }
+
+    public Method getOverriddenMethod() {
+        return getOverriddenMethod(staticTargetClass, dynamicTargetClass);
+    }
+
+    public Method getOverriddenMethod(Class staticTarget, Class dynamicTarget) {
+        // Assertion #1. C is a subclass of A
+        if (!staticTarget.isAssignableFrom(dynamicTarget)) {
+            return null;
+        }
+
+        Method staticTargetMethod = getDeclaredMethod(staticTarget);
+        Method dynamicTargetMethod = getDeclaredMethod(dynamicTarget);
+
+        if (staticTarget.equals(dynamicTarget)) {
+            return staticTargetMethod;
+        }
+
+        // TODO: ? need to find out the right behavior
+        if (staticTargetMethod == null) {
+            return null;
+        }
+
+        // Dynamic target doesn't have desired method, so check it's superclass
+        if (dynamicTargetMethod == null) {
+            return getOverriddenMethod(staticTarget, dynamicTarget.getSuperclass());
+        } else {
+            // Private method can't override anything
+            if (Modifier.isPrivate(dynamicTargetMethod.getModifiers())) {
+                return getOverriddenMethod(staticTarget, dynamicTarget.getSuperclass());
+            }
+        }
+
+        // TODO: abstract methods
+
+        //Assertion #3.a: A.m2 is PUB || PROT || (PP && PKG(A) == PKG(C))
+        int staticTargetModifiers = staticTargetMethod.getModifiers();
+        {
+            boolean isPublic = Modifier.isPublic(staticTargetModifiers);
+            boolean isProtected = Modifier.isProtected(staticTargetModifiers);
+            boolean isPrivate = Modifier.isPrivate(staticTargetModifiers) ;
+            String staticTargetPkg = getClassPackageName(staticTarget);
+            String dynamicTargetPkg = getClassPackageName(dynamicTarget);
+
+            if ( isPublic || isProtected
+                 || ( !isPublic && !isProtected && !isPrivate
+                      && staticTargetPkg.equals(dynamicTargetPkg)
+                    ))
+            {
+                return dynamicTargetMethod;
+            }
+        }
+        // OR
+        //Assertion #3.b: exists m3: C.m1 != B.m3, A.m2 != B.m3, B.m3 overrides A.m2, C.m1 overrides B.m3
+        Class ancestor = dynamicTarget.getSuperclass();
+        while (ancestor != staticTarget) {
+            Method OverriddenM2 = getOverriddenMethod(staticTarget, ancestor);
+            Method m3 = getDeclaredMethod(ancestor);
+            Method m1 = getOverriddenMethod(ancestor, dynamicTarget);
+
+            if (m1 != null && m3 != null) {
+                if (m1.equals(dynamicTargetMethod) && m3.equals(OverriddenM2)) {
+                    return dynamicTargetMethod;
+                }
+            } else {
+                if (m1 == null && dynamicTargetMethod == null
+                    && m3 == null && OverriddenM2 == null)
+                {
+                    return null;
+                }
+            }
+
+            ancestor = ancestor.getSuperclass();
+        }
+
+        return getOverriddenMethod(staticTarget, dynamicTarget.getSuperclass());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/InvocationTests/invokevirtual/ClassGenerator.java	Wed Jun 26 09:06:32 2019 -0400
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ *
+ */
+
+package invokevirtual;
+
+import jdk.internal.org.objectweb.asm.Opcodes;
+import shared.GenericClassGenerator;
+
+/*******************************************************************/
+class ClassGenerator extends GenericClassGenerator<ClassGenerator> {
+    public ClassGenerator(String fullClassName) {
+        super(fullClassName);
+    }
+
+    public ClassGenerator(String fullClassName, String parentClassName) {
+        super(fullClassName, parentClassName);
+    }
+
+    public ClassGenerator(String fullClassName, String parentClassName, int flags) {
+        super(fullClassName, parentClassName, flags);
+    }
+
+    // Add target method call site into current class
+    public ClassGenerator addCaller(String targetClass) {
+        return super.addCaller(targetClass, Opcodes.INVOKEVIRTUAL);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/InvocationTests/invokevirtual/Generator.java	Wed Jun 26 09:06:32 2019 -0400
@@ -0,0 +1,194 @@
+/*
+ * 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.
+ *
+ */
+
+package invokevirtual;
+
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_ABSTRACT;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import shared.AbstractGenerator;
+import shared.AccessType;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Generator extends AbstractGenerator {
+    public Generator(String[] args) {
+        super(args);
+    }
+
+    public static void main (String[] args) throws Exception {
+        new Generator(args).run();
+    }
+
+    protected Checker getChecker(Class paramClass, Class targetClass) {
+        return new Checker(paramClass, targetClass);
+    }
+
+    private void run() throws Exception {
+        // Specify package names
+        String pkg1 = "a.";
+        String pkg2 = "b.";
+        String pkg3 = "c.";
+        String[] packages = new String[] { "", pkg1, pkg2, pkg3 };
+
+        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[][] {
+                {   "",   "",   "" }
+                , {   "", pkg1, pkg1 }
+                , {   "", pkg1, pkg2 }
+                , { pkg1, pkg1, pkg1 }
+                , { pkg1, pkg1, pkg2 }
+                , { pkg1, pkg2, pkg1 }
+                , { pkg1, pkg2, pkg2 }
+        };
+
+        String [] header = new String[] {
+                String.format("%30s %45s %20s", "Method access modifiers", "Call site location", "Status")
+                , String.format("%4s  %-12s %-12s %-12s   %7s %7s %7s %7s %7s %7s"
+                        , "  # "
+                        , "A.m()"
+                        , "B.m()"
+                        , "C.m()"
+                        , "  A  "
+                        , "pkgA "
+                        , "  B  "
+                        , " pkgB"
+                        , "  C  "
+                        , "pkgC "
+                        )
+                , "-------------------------------------------------------------------------------------------------"
+        };
+
+        for (String str : header) {
+            System.out.println(str);
+        }
+
+        for (String[] pkgSet : packageSets) {
+            String packageA = pkgSet[0];
+            String packageB = pkgSet[1];
+            String packageC = pkgSet[2];
+
+            String classNameA = packageA + "A";
+            String classNameB = packageB + "B";
+            String classNameC = packageC + "C";
+
+            String staticCallerParam = classNameA;
+
+            // For all possible access modifier combinations
+            for (AccessType accessA : AccessType.values()) {
+                for (AccessType accessB : AccessType.values()) {
+                    for (AccessType accessC : AccessType.values()) {
+
+                        if (accessA == AccessType.UNDEF) {
+                            continue;
+                        }
+
+                        for (int I = 0; I < 4; I++) {
+                            boolean isAbstractA = ((I & 1) != 0);
+                            boolean isAbstractB = ((I & 2) != 0);
+
+                            Map<String, byte[]> classes = new HashMap<String, byte[]>();
+
+                            // Generate class A
+                            classes.put(
+                                    classNameA
+                                    , new ClassGenerator( classNameA
+                                                        , "java.lang.Object"
+                                                        , ACC_PUBLIC | (isAbstractA ? ACC_ABSTRACT : 0))
+                                        .addTargetMethod( accessA
+                                                        , (isAbstractA ? ACC_ABSTRACT : 0))
+                                        .addCaller(staticCallerParam)
+                                        .getClassFile()
+                            );
+
+                            // Generate class B
+                            classes.put(
+                                    classNameB
+                                    , new ClassGenerator( classNameB
+                                                        , classNameA
+                                                        , ACC_PUBLIC | (isAbstractB ? ACC_ABSTRACT : 0))
+                                    .addTargetMethod( accessB
+                                                    , (isAbstractB ? ACC_ABSTRACT : 0))
+                                    .addCaller(staticCallerParam)
+                                    .getClassFile()
+                            );
+
+                            // Generate class C
+                            classes.put(
+                                    classNameC
+                                    , new ClassGenerator(classNameC, classNameB)
+                                        .addTargetMethod(accessC)
+                                        .addCaller(staticCallerParam)
+                                        .getClassFile()
+                            );
+
+                            // Generate package callers
+                            for (String pkg : packages) {
+                                classes.put(
+                                        pkg+"Caller"
+                                        , new ClassGenerator(pkg+"Caller")
+                                        .addCaller(staticCallerParam)
+                                        .getClassFile()
+                                );
+                            }
+
+                            String[] callSites = new String[] {
+                                    classNameA
+                                    , packageA+"Caller"
+                                    , classNameB
+                                    , packageB+"Caller"
+                                    , classNameC
+                                    , packageC+"Caller"
+                            };
+
+
+                            String caseDescription =
+                                    String.format("%-12s %-12s %-12s| "
+                                        , (isAbstractA ? "! " : "  ") + classNameA + " " + accessA
+                                        , (isAbstractB ? "! " : "  ") + classNameB + " " + accessB
+                                        , classNameC + " " + accessC
+                                    );
+
+                            boolean result = exec(classes, caseDescription, staticCallerParam, 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"));
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/InvocationTests/invokevirtualTests.java	Wed Jun 26 09:06:32 2019 -0400
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 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.
+ *
+ */
+
+/*
+ * @test
+ * @bug 8224137
+ * @summary Run invokevirtual invocation tests
+ * @library /test/lib
+ * @modules java.base/jdk.internal.org.objectweb.asm
+ *          java.base/jdk.internal.misc
+ * @compile shared/AbstractGenerator.java shared/AccessCheck.java shared/AccessType.java
+ *          shared/Caller.java shared/ExecutorGenerator.java shared/Utils.java
+ *          shared/ByteArrayClassLoader.java shared/Checker.java shared/GenericClassGenerator.java
+ * @compile invokevirtual/Checker.java invokevirtual/ClassGenerator.java invokevirtual/Generator.java
+ *
+ * @run main/othervm/timeout=1800 invokevirtualTests
+ */
+
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.compiler.InMemoryJavaCompiler;
+
+public class invokevirtualTests {
+
+    public static void runTest(String classFileVersion, String option) throws Exception {
+        System.out.println("\ninvokevirtual invocation tests, option: " + option +
+                           ", class file version: " + classFileVersion);
+        ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(false, "-Xmx128M", option,
+            "invokevirtual.Generator", "--classfile_version=" + classFileVersion);
+        OutputAnalyzer output = new OutputAnalyzer(pb.start());
+        try {
+            output.shouldContain("EXECUTION STATUS: PASSED");
+            output.shouldHaveExitValue(0);
+        } catch (Throwable e) {
+            System.out.println(
+                "\nNote that an entry such as 'B.m/C.m' in the failure chart means that" +
+                " the test case failed because method B.m was invoked but the test " +
+                "expected method C.m to be invoked. Similarly, a result such as 'AME/C.m'" +
+                " means that an AbstractMethodError exception was thrown but the test" +
+                " case expected method C.m to be invoked.");
+            System.out.println(
+                "\nAlso note that passing --dump to invokevirtual.Generator will" +
+                " dump the generated classes (for debugging purposes).\n");
+            System.exit(1);
+        }
+    }
+
+    public static void main(String args[]) throws Throwable {
+        // Get current major class file version and test with it.
+        byte klassbuf[] = InMemoryJavaCompiler.compile("blah", "public class blah { }");
+        int major_version = klassbuf[6] << 8 | klassbuf[7];
+        runTest(String.valueOf(major_version), "-Xint");
+// Uncomment the below test once JDK-8226588 is fixed
+//      runTest(String.valueOf(major_version), "-Xcomp");
+
+        // Test old class file version.
+        runTest("51", "-Xint"); // JDK-7
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/InvocationTests/shared/AbstractGenerator.java	Wed Jun 26 09:06:32 2019 -0400
@@ -0,0 +1,176 @@
+/*
+ * 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.
+ *
+ */
+
+package shared;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ *
+ */
+public abstract class AbstractGenerator {
+    protected final boolean dumpClasses;
+    protected final boolean executeTests;
+    private static int testNum = 0;
+
+    protected AbstractGenerator(String[] args) {
+        List<String> params = new ArrayList<String>(Arrays.asList(args));
+
+        if (params.contains("--help")) {
+            Utils.printHelp();
+            System.exit(0);
+        }
+
+        dumpClasses = params.contains("--dump");
+        executeTests = !params.contains("--noexecute");
+
+        params.remove("--dump");
+        params.remove("--noexecute");
+
+        Utils.init(params);
+    }
+
+    /*******************************************************************/
+    public static void writeToFile(File dir, Map<String, byte[]> classes) {
+        for (String name : classes.keySet()) {
+            try {
+                writeToFile(dir, name, classes.get(name));
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    /*******************************************************************/
+    public static void writeToFile(File dir, String fullName, byte[] classBytecode) {
+        if (!dir.isDirectory()) {
+            throw new RuntimeException("Invalid parameter: dir doesn't point to an existing directory");
+        }
+
+        File classFile =
+            new File(
+                    dir.getPath() + File.separator
+                    + fullName.replaceAll("\\.", File.separator)
+                    + ".class"
+                    );
+
+        classFile.getParentFile().mkdirs();
+
+        try {
+            FileOutputStream fos = new FileOutputStream(classFile);
+            try {
+                fos.write(classBytecode);
+            } finally {
+                fos.close();
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    protected boolean exec(Map<String, byte[]> classes, String description, String calleeClassName, String classNameC, String[] callSites) throws ClassNotFoundException {
+        boolean isPassed = true;
+
+        testNum++;
+
+        String caseDescription = String.format("%4d| %s", testNum, description);
+
+        // Create test executor for a single case
+        classes.put(
+                ExecutorGenerator.className
+                , new ExecutorGenerator(
+                        caseDescription
+                        , calleeClassName
+                        , classNameC
+                    ).generateExecutor(callSites)
+        );
+
+        // Dump generated set to disk, if needed
+        if (dumpClasses) {
+            File dir = new File("classes" + File.separator + String.format("%04d", testNum));
+            dir.mkdirs();
+            writeToFile(dir, classes);
+        }
+
+        ByteArrayClassLoader loader = new ByteArrayClassLoader(classes);
+
+        Class paramClass;
+        Class targetClass;
+        Checker checker;
+
+        try {
+            paramClass = loader.loadClass(calleeClassName);
+            targetClass = loader.loadClass(classNameC);
+
+            checker = getChecker(paramClass, targetClass);
+        } catch (Throwable e) {
+            String result = Checker.abbreviateResult(e.getClass().getName());
+
+            System.out.printf(caseDescription);
+
+            for (String site : callSites) {
+                System.out.printf(" %7s", result);
+            }
+
+            System.out.println("");
+
+            return true;
+        }
+
+        if (executeTests) {
+            // Check runtime behavior
+            Caller caller = new Caller(loader, checker, paramClass, targetClass);
+            boolean printedCaseDes = false;
+            for (String site : callSites) {
+                String callResult = caller.call(site);
+
+                if (!caller.isPassed()) {
+                    isPassed = false;
+                    if (!printedCaseDes) {
+                        System.out.printf(caseDescription);
+                        printedCaseDes = true;
+                    }
+                    System.out.printf(" %7s", callResult);
+                }
+            }
+            if (!caller.isPassed()) {
+                System.out.println(" |   FAILED");
+            }
+        } else {
+            for (String site : callSites) {
+                String result = checker.check(loader.loadClass(site));
+                System.out.printf(" %7s", Checker.abbreviateResult(result));
+            }
+        }
+
+        return isPassed;
+    }
+
+    protected abstract Checker getChecker(Class paramClass, Class targetClass);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/InvocationTests/shared/AccessCheck.java	Wed Jun 26 09:06:32 2019 -0400
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ *
+ */
+
+package shared;
+
+import static jdk.internal.org.objectweb.asm.Opcodes.*;
+
+/**
+ *
+ * @author vi158347
+ */
+public class AccessCheck {
+    public static boolean isAbstract(int access) {
+        return (access & ACC_ABSTRACT) != 0;
+    }
+
+    public static boolean isPublic(int access) {
+        return (access & ACC_PUBLIC) != 0;
+    }
+
+    public static boolean isProtected(int access) {
+        return (access & ACC_PROTECTED) != 0;
+    }
+
+    public static boolean isPackagePrivate(int access) {
+        return !isPublic(access) && !isProtected(access) && !isPrivate(access);
+    }
+
+    public static boolean isPrivate(int access) {
+        return (access & ACC_PRIVATE) != 0;
+    }
+
+    public static boolean isInterface(int access) {
+        return (access & ACC_INTERFACE) != 0;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/InvocationTests/shared/AccessType.java	Wed Jun 26 09:06:32 2019 -0400
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ *
+ */
+
+package shared;
+
+import static jdk.internal.org.objectweb.asm.Opcodes.*;
+
+public enum AccessType {
+      PUBLIC           ("PUB")   { public int value() { return ACC_PUBLIC; } }
+    , PROTECTED        ("PROT")  { public int value() { return ACC_PROTECTED; } }
+    , PACKAGE_PRIVATE  ("PP")    { public int value() { return 0; } }
+    , PRIVATE          ("PRIV")  { public int value() { return ACC_PRIVATE; } }
+    , UNDEF            ("UNDEF") { public int value() { return -1; } }
+    ;
+
+    private String name;
+
+    AccessType(String name) {
+        this.name = name;
+    }
+
+    public abstract int value();
+
+    public String toString() {
+        return name;
+    }
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/InvocationTests/shared/ByteArrayClassLoader.java	Wed Jun 26 09:06:32 2019 -0400
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ *
+ */
+
+package shared;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/*******************************************************************/
+// Class loader which has local class file storage in memory
+/*******************************************************************/
+
+public class ByteArrayClassLoader extends ClassLoader {
+    private Map<String, byte[]> classes;
+
+    public ByteArrayClassLoader() {
+        classes = new HashMap<String, byte[]>();
+    }
+
+    public ByteArrayClassLoader(Map<String, byte[]> classes) {
+        this.classes = classes;
+    }
+
+    public void appendClass(String name, byte[] classFile) {
+        classes.put(name, classFile);
+    }
+
+    public Class findClass (String name) throws ClassNotFoundException {
+        if (classes.containsKey(name)) {
+            byte[] classData = classes.get(name);
+            return defineClass(name, classData, 0, classData.length);
+        } else {
+            throw new ClassNotFoundException("Can't find requested class: " + name);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/InvocationTests/shared/Caller.java	Wed Jun 26 09:06:32 2019 -0400
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ *
+ */
+
+package shared;
+
+import java.lang.reflect.InvocationTargetException;
+
+/*******************************************************************/
+// Invoke different target method callers
+/*******************************************************************/
+
+public class Caller {
+    private ClassLoader loader;
+    private Class paramClass;
+    private Class targetClass;
+    private boolean passed = true;
+    private Checker checker;
+
+    public Caller(ClassLoader loader, Checker checker,
+                  Class paramClass, Class targetClass) {
+        this.loader = loader;
+        this.paramClass = paramClass;
+        this.targetClass = targetClass;
+        this.checker = checker;
+    }
+
+    public boolean isPassed() {
+        return passed;
+    }
+
+    public String call(String invoker) {
+        try {
+            Class clazz = loader.loadClass(invoker);
+
+            String expectedBehavior = checker.check(clazz);
+
+            String result = null;
+            Throwable exc = null;
+            try {
+                java.lang.reflect.Method m = clazz.getDeclaredMethod("call", paramClass);
+                result = (String) m.invoke(null, targetClass.newInstance());
+            } catch (InvocationTargetException e) {
+                exc = e.getCause();
+            } catch (Throwable e) {
+                exc = e;
+            }
+
+            if (result == null) {
+                if (exc != null) {
+                    result = exc.getClass().getName();
+                } else {
+                    result = "null";
+                }
+            }
+
+            if (!(result.equals(expectedBehavior) || "".equals(expectedBehavior)) ) {
+                passed = false;
+                result = String.format("%s/%s", result, expectedBehavior);
+            }
+
+            return Checker.abbreviateResult(result);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/InvocationTests/shared/Checker.java	Wed Jun 26 09:06:32 2019 -0400
@@ -0,0 +1,176 @@
+/*
+ * 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.
+ *
+ */
+
+package shared;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+public abstract class Checker {
+    protected Class staticTargetClass;
+    protected Class dynamicTargetClass;
+    protected String methodName;
+
+    public abstract String check (Class callSite);
+
+    public Checker(Class staticTargetClass, Class dynamicTargetClass) {
+        if (!staticTargetClass.isAssignableFrom(dynamicTargetClass)) {
+            throw new RuntimeException("Dynamic target class should be a subclass of the static target class.");
+        }
+
+        // **********************************************
+        // NB!!! All classes are assumed to be PUBLIC !!!
+        // **********************************************
+        Class klass = dynamicTargetClass;
+        while (klass != Object.class) {
+            if (!Modifier.isPublic(klass.getModifiers())) {
+                throw new AssertionError("Class "+klass.getName()+" isn't public.");
+            }
+
+            klass = klass.getSuperclass();
+        }
+
+        this.methodName = Utils.TARGET_METHOD_NAME;
+        this.staticTargetClass = staticTargetClass;
+        this.dynamicTargetClass = dynamicTargetClass;
+    }
+
+    protected Method getMethodInHierarchy (Class klass) {
+        return getMethodInHierarchy(klass, methodName);
+    }
+
+    protected Method getMethodInHierarchy (Class klass, String name) {
+        while (klass != null) {
+            Method method = getDeclaredMethod (klass, name);
+
+            if ( method != null) {
+// TODO: why doesn't this check work in VM?
+//                int modifiers = method.getModifiers();
+//
+//                if (Modifier.isPrivate(modifiers)) {
+//                    if (klass == initialClass) {
+//                        return method;
+//                    }
+//                } else {
+//                    return method;
+//                }
+                return method;
+            }
+            klass = klass.getSuperclass();
+        }
+
+        return null;
+    }
+
+    protected Method getMethod (Class klass) {
+        return getMethod (klass, methodName);
+    }
+
+    protected Method getDeclaredMethod (Class klass) {
+        return getDeclaredMethod (klass, methodName);
+    }
+
+    static protected Method getMethod (Class klass, String name) {
+        return findMethod (klass.getMethods(), name);
+    }
+
+    static protected Method getDeclaredMethod (Class klass, String name) {
+        return findMethod (klass.getDeclaredMethods(), name);
+    }
+
+    static protected Method findMethod (Method[] methods, String name) {
+        for (Method method : methods) {
+            if (name.equals(method.getName())) {
+                return method;
+            }
+        }
+
+        return null;
+    }
+
+    static public String getClassPackageName(Class klass) {
+        String name = klass.getName();
+        return getClassPackageName(name);
+    }
+
+    static public String getClassPackageName(String name) {
+        int lastDotIndex = name.lastIndexOf('.');
+        if (lastDotIndex > -1) {
+            return name.substring(0, lastDotIndex);
+        } else {
+            return "";
+        }
+    }
+
+    public static String abbreviateResult(String result) {
+        // Abbreviate exception names
+        result = result.replaceAll("java.lang.NullPointerException", "NPE");
+        result = result.replaceAll("java.lang.IllegalAccessError", "IAE");
+        result = result.replaceAll("java.lang.IllegalAccessException", "IAExc");
+        result = result.replaceAll("java.lang.NoSuchMethodError", "NSME");
+        result = result.replaceAll("java.lang.AbstractMethodError", "AME");
+        result = result.replaceAll("java.lang.IncompatibleClassChangeError", "ICCE");
+        result = result.replaceAll("java.lang.VerifyError", "VE");
+        result = result.replaceAll("java.lang.ClassFormatError", "CFE");
+
+        return result;
+    }
+
+    // Check access possibility from particular call site
+    protected boolean checkAccess(Class klass, Class callerClass) {
+        int modifiers = klass.getModifiers();
+
+        return checkAccess(modifiers, klass, callerClass);
+    }
+
+    protected boolean checkAccess(Method m, Class callerClass) {
+        int modifiers = m.getModifiers();
+        Class declaringClass = m.getDeclaringClass();
+
+        return checkAccess(modifiers, declaringClass, callerClass);
+    }
+
+    protected boolean checkAccess(int modifiers, Class klass, Class callerClass) {
+        if ( Modifier.isPublic(modifiers) ) {
+            return true;
+        } else if ( Modifier.isProtected(modifiers) ) {
+            if (klass.isAssignableFrom(callerClass)) {
+                return true;
+            } else if (getClassPackageName(klass).equals(getClassPackageName(callerClass))) {
+                return true;
+            }
+        } else if ( Modifier.isPrivate(modifiers)) {
+            if (klass == callerClass) {
+                return true;
+            }
+        } else if (getClassPackageName(klass).equals(getClassPackageName(callerClass))) {
+            return true;
+        } else {
+            // if method isn't accessible, IllegalAccessException is thrown
+            return false;
+        }
+
+        return false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/InvocationTests/shared/ExecutorGenerator.java	Wed Jun 26 09:06:32 2019 -0400
@@ -0,0 +1,259 @@
+/*
+ * 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.
+ *
+ */
+
+package shared;
+
+import static jdk.internal.org.objectweb.asm.ClassWriter.*;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import static jdk.internal.org.objectweb.asm.Opcodes.*;
+
+public class ExecutorGenerator {
+    public static final String className = Utils.getInternalName("Test");
+    private String caseDescription;
+    private String staticTargetName;
+    private String dynamicTargetName;
+
+    private String callerSignature;
+
+    public ExecutorGenerator(String caseDescription,
+                             String staticTargetName,
+                             String dynamicTargetName) {
+        this.caseDescription = caseDescription;
+        this.staticTargetName = Utils.getInternalName(staticTargetName);
+        this.dynamicTargetName = Utils.getInternalName(dynamicTargetName);
+        callerSignature = String.format("(L%s;)Ljava/lang/String;", this.staticTargetName);
+    }
+
+    public byte[] generateExecutor(String[] callSites) {
+        ClassWriter cw = new ClassWriter(COMPUTE_MAXS);
+
+        cw.visit(Utils.version, ACC_PUBLIC | (Utils.isACC_SUPER ? ACC_SUPER : 0), className, null, "java/lang/Object", null);
+
+        // Generate constructor
+        {
+            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+            mv.visitCode();
+            mv.visitVarInsn(ALOAD, 0);
+            mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+            mv.visitInsn(RETURN);
+            mv.visitEnd();
+            mv.visitMaxs(0, 0);
+        }
+
+        // public static void main(String[] args) {
+        //      new Test().run();
+        // }
+        {
+            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+            mv.visitCode();
+            mv.visitTypeInsn(NEW, className);
+            mv.visitInsn(DUP);
+            mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "()V");
+            mv.visitMethodInsn(INVOKEVIRTUAL, className, "run", "()V");
+            mv.visitInsn(RETURN);
+            mv.visitEnd();
+            mv.visitMaxs(0, 0);
+        }
+
+        //    private String indent(String result) {
+        //        while (result.length() < 8) {
+        //            result = " "+result;
+        //        }
+        //        return result;
+        //    }
+        {
+            MethodVisitor mv = cw.visitMethod(ACC_PRIVATE, "indent", "(Ljava/lang/String;)Ljava/lang/String;", null, null);
+            mv.visitCode();
+            Label l0 = new Label();
+            mv.visitLabel(l0);
+            mv.visitVarInsn(ALOAD, 1);
+            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "length", "()I");
+            mv.visitIntInsn(BIPUSH, 8);
+            Label l1 = new Label();
+            mv.visitJumpInsn(IF_ICMPGE, l1);
+            mv.visitTypeInsn(NEW, "java/lang/StringBuffer");
+            mv.visitInsn(DUP);
+            mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuffer", "<init>", "()V");
+            mv.visitLdcInsn(" ");
+            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
+            mv.visitVarInsn(ALOAD, 1);
+            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
+            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "toString", "()Ljava/lang/String;");
+            mv.visitVarInsn(ASTORE, 1);
+            mv.visitJumpInsn(GOTO, l0);
+            mv.visitLabel(l1);
+            mv.visitVarInsn(ALOAD, 1);
+            mv.visitInsn(ARETURN);
+            mv.visitEnd();
+            mv.visitMaxs(0, 0);
+        }
+
+        //private String abbr(String result) {
+        //      result = result.replaceAll("java.lang.NullPointerException", "NPE");
+        //      result = result.replaceAll("java.lang.IllegalAccessError", "IAE");
+        //      result = result.replaceAll("java.lang.IllegalAccessException", "IAExc");
+        //      result = result.replaceAll("java.lang.NoSuchMethodError", "NSME");
+        //      result = result.replaceAll("java.lang.AbstractMethodError", "AME");
+        //      result = result.replaceAll("java.lang.IncompatibleClassChangeError", "ICCE");
+        //
+        //      return result;
+        //}
+        {
+            MethodVisitor mv = cw.visitMethod(ACC_PRIVATE, "abbr", "(Ljava/lang/String;)Ljava/lang/String;", null, null);
+            mv.visitCode();
+            mv.visitVarInsn(ALOAD, 1);
+            mv.visitLdcInsn("java.lang.NullPointerException");
+            mv.visitLdcInsn("NPE");
+            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "replaceAll", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
+            mv.visitVarInsn(ASTORE, 1);
+            mv.visitVarInsn(ALOAD, 1);
+            mv.visitLdcInsn("java.lang.IllegalAccessError");
+            mv.visitLdcInsn("IAE");
+            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "replaceAll", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
+            mv.visitVarInsn(ASTORE, 1);
+            mv.visitVarInsn(ALOAD, 1);
+            mv.visitLdcInsn("java.lang.IllegalAccessException");
+            mv.visitLdcInsn("IAExc");
+            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "replaceAll", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
+            mv.visitVarInsn(ASTORE, 1);
+            mv.visitVarInsn(ALOAD, 1);
+            mv.visitLdcInsn("java.lang.NoSuchMethodError");
+            mv.visitLdcInsn("NSME");
+            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "replaceAll", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
+            mv.visitVarInsn(ASTORE, 1);
+            mv.visitVarInsn(ALOAD, 1);
+            mv.visitLdcInsn("java.lang.AbstractMethodError");
+            mv.visitLdcInsn("AME");
+            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "replaceAll", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
+            mv.visitVarInsn(ASTORE, 1);
+            mv.visitVarInsn(ALOAD, 1);
+            mv.visitLdcInsn("java.lang.IncompatibleClassChangeError");
+            mv.visitLdcInsn("ICCE");
+            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "replaceAll", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
+            mv.visitVarInsn(ASTORE, 1);
+            mv.visitVarInsn(ALOAD, 1);
+            mv.visitInsn(ARETURN);
+            mv.visitEnd();
+            mv.visitMaxs(0, 0);
+        }
+
+        // Generate execution method
+        //        public void run() {
+        //            System.out.print("2048| ! a.A PUB    ! b.B PP     a.C PROT    |");
+        //
+        //            C object = new C();
+        //
+        //            try {
+        //              System.out.print(indent(A.call(object)));
+        //            } catch (Throwable e) {
+        //              System.out.print(indent(abbr(e.getClass().getName())));
+        //            }
+        //
+        //            ...
+        //
+        //            System.out.println();
+        //        }
+        {
+            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "run", "()V", null, null);
+            mv.visitCode();
+
+            // Generate try/catch blocks
+            Label[][] tryCatchLabels = new Label[callSites.length][3];
+            for (int I = 0; I < tryCatchLabels.length; I++) {
+                Label[] labels = tryCatchLabels[I];
+                for (int K = 0; K < labels.length; K++) {
+                    labels[K] = new Label();
+                }
+
+                mv.visitTryCatchBlock(labels[0], labels[1], labels[2], "java/lang/Throwable");
+            }
+
+            // System.out.print("2048| ! a.A PUB    ! b.B PP     a.C PROT    |");
+            mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+            mv.visitLdcInsn(caseDescription);
+            mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "print", "(Ljava/lang/String;)V");
+
+            // C object = new C();
+            mv.visitTypeInsn(NEW, dynamicTargetName);
+            mv.visitInsn(DUP);
+            mv.visitMethodInsn(INVOKESPECIAL, dynamicTargetName, "<init>", "()V");
+            mv.visitVarInsn(ASTORE, 1);
+
+//            for (String site: callSites) {
+            // System.out.print(indent(A.call(object)));
+//                mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+//                mv.visitVarInsn(ALOAD, 0);
+//                mv.visitVarInsn(ALOAD, 1);
+//                mv.visitMethodInsn(INVOKESTATIC, AbstractGenerator.getInternalName(site), "call", callerSignature);
+//                mv.visitMethodInsn(INVOKESPECIAL, className, "indent", "(Ljava/lang/String;)Ljava/lang/String;");
+//                mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "print", "(Ljava/lang/String;)V");
+//        }
+
+            Label returnLabel = new Label();
+            for (int I = 0; I < callSites.length; I++) {
+                String site = callSites[I];
+                Label[] l = tryCatchLabels[I];
+
+                Label nextBlock = (I+1 < callSites.length ? tryCatchLabels[I+1][0] : returnLabel);
+
+                mv.visitLabel(l[0]);
+                mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+                mv.visitVarInsn(ALOAD, 0);
+                mv.visitVarInsn(ALOAD, 1);
+                mv.visitMethodInsn(INVOKESTATIC, Utils.getInternalName(site), "call", callerSignature);
+                mv.visitMethodInsn(INVOKESPECIAL, className, "indent", "(Ljava/lang/String;)Ljava/lang/String;");
+                mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "print", "(Ljava/lang/String;)V");
+                mv.visitLabel(l[1]);
+                mv.visitJumpInsn(GOTO, nextBlock);
+                mv.visitLabel(l[2]);
+                mv.visitVarInsn(ASTORE, 2);
+                mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+                mv.visitVarInsn(ALOAD, 0);
+                mv.visitVarInsn(ALOAD, 0);
+                mv.visitVarInsn(ALOAD, 2);
+                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;");
+                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getName", "()Ljava/lang/String;");
+                mv.visitMethodInsn(INVOKESPECIAL, className, "abbr", "(Ljava/lang/String;)Ljava/lang/String;");
+                mv.visitMethodInsn(INVOKESPECIAL, className, "indent", "(Ljava/lang/String;)Ljava/lang/String;");
+                mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "print", "(Ljava/lang/String;)V");
+            }
+            mv.visitLabel(returnLabel);
+
+            // System.out.println();
+            mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+            mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "()V");
+            mv.visitInsn(RETURN);
+
+            mv.visitEnd();
+            mv.visitMaxs(0, 0);
+        }
+
+        cw.visitEnd();
+
+        return cw.toByteArray();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/InvocationTests/shared/GenericClassGenerator.java	Wed Jun 26 09:06:32 2019 -0400
@@ -0,0 +1,255 @@
+/*
+ * 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.
+ *
+ */
+
+package shared;
+
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
+import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_MAXS;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import static jdk.internal.org.objectweb.asm.Opcodes.*;
+import static shared.AccessCheck.*;
+
+public class GenericClassGenerator<T extends GenericClassGenerator> {
+    private static final String targetMethodName = Utils.TARGET_METHOD_NAME;
+
+    private int flags = 0;
+    private ClassWriter writer;
+    private String fullClassName = null;
+    private String parentClassName = null;
+
+    /*******************************************************************/
+    public GenericClassGenerator(String fullClassName) {
+        this(fullClassName, "java/lang/Object");
+    }
+
+    /*******************************************************************/
+    public GenericClassGenerator(String fullClassName, String parentClassName ) {
+        this(fullClassName, parentClassName, ACC_PUBLIC);
+    }
+
+    /*******************************************************************/
+    public GenericClassGenerator(String fullClassName, String parentClassName, int flags) {
+        this(fullClassName, parentClassName, flags, new String[0]);
+    }
+
+    /*******************************************************************/
+    public GenericClassGenerator(String fullClassName, String parentClassName, int flags, String[] implementedInterfaces) {
+        writer = new ClassWriter(COMPUTE_FRAMES | COMPUTE_MAXS);
+
+        this.fullClassName = fullClassName;
+        this.flags = flags;
+
+        // Construct simple class
+        if (parentClassName != null) {
+            this.parentClassName = getInternalName(parentClassName);
+        } else {
+            this.parentClassName = "java/lang/Object";
+        }
+
+        String parent = this.parentClassName;
+        String name = getInternalName(fullClassName);
+
+        if (Utils.isACC_SUPER) {
+            flags = flags | ACC_SUPER;
+        }
+
+        writer.visit(Utils.version, flags, name, null, parent, implementedInterfaces);
+
+        // Add constructor
+        if ( !isInterface(flags) ) {
+            MethodVisitor m =
+                    writer.visitMethod(
+                            ACC_PUBLIC
+                            , "<init>"
+                            , "()V"
+                            , null
+                            , null
+                    );
+
+            m.visitCode();
+            m.visitVarInsn(ALOAD, 0);
+            m.visitMethodInsn(
+                      INVOKESPECIAL
+                    , getInternalName(parent)
+                    , "<init>"
+                    , "()V"
+            );
+            m.visitInsn(RETURN);
+            m.visitEnd();
+            m.visitMaxs(0,0);
+        }
+    }
+
+    /*******************************************************************/
+    protected static String getInternalName(String fullClassName) {
+        return fullClassName.replaceAll("\\.", "/");
+    }
+
+    /*******************************************************************/
+    public T addTargetConstructor(AccessType access) {
+        // AccessType.UNDEF means that the target method isn't defined, so do nothing
+        if (access == AccessType.UNDEF || isInterface(flags) ) {
+            return (T)this;
+        }
+
+        // Add target constructor
+        int methodAccessType = access.value();
+
+        MethodVisitor m =
+                writer.visitMethod(
+                        methodAccessType
+                        , "<init>"
+                        , "(I)V"
+                        , null
+                        , null
+                );
+
+        // Add a call to parent constructor
+        m.visitCode();
+        m.visitVarInsn(ALOAD, 0);
+        m.visitMethodInsn(
+                  INVOKESPECIAL
+                , getInternalName(parentClassName)
+                , "<init>"
+                , "()V"
+        );
+
+        // Add result reporting
+        String shortName = fullClassName.substring(fullClassName.lastIndexOf('.') + 1);
+        m.visitLdcInsn(shortName+".<init>");
+        m.visitFieldInsn(
+                  PUTSTATIC
+                , "Result"
+                , "value"
+                , "Ljava/lang/String;"
+        );
+
+        m.visitInsn(RETURN);
+        m.visitEnd();
+        m.visitMaxs(0,0);
+
+        return (T)this;
+
+    }
+
+    /*******************************************************************/
+    public T addTargetMethod(AccessType access) {
+        return addTargetMethod(access, 0);
+    }
+
+    /*******************************************************************/
+    public T addTargetMethod(AccessType access, int additionalFlags) {
+        // AccessType.UNDEF means that the target method isn't defined, so do nothing
+        if (access == AccessType.UNDEF) {
+            return (T)this;
+        }
+
+        // Add target method
+        int methodAccessType = access.value();
+        if ( isInterface(flags) || isAbstract(flags) ) {
+            methodAccessType |= ACC_ABSTRACT;
+        }
+
+        // Skip method declaration for abstract private case, which doesn't pass
+        // classfile verification stage
+        if ( isPrivate(methodAccessType) && isAbstract(methodAccessType) ) {
+            return (T)this;
+        }
+
+        MethodVisitor m =
+                writer.visitMethod(
+                        methodAccessType | additionalFlags
+                        , targetMethodName
+                        , "()Ljava/lang/String;"
+                        , null
+                        , null
+                );
+
+        // Don't generate body if the method is abstract
+        if ( (methodAccessType & ACC_ABSTRACT) == 0 ) {
+            String shortName = fullClassName.substring(fullClassName.lastIndexOf('.') + 1);
+
+            // Simply returns info about itself
+            m.visitCode();
+            m.visitLdcInsn(shortName+"."+targetMethodName);
+            m.visitInsn(ARETURN);
+            m.visitEnd();
+            m.visitMaxs(0,0);
+        }
+
+        return (T)this;
+    }
+
+    /*******************************************************************/
+    public T addField(int access, String name, String type) {
+        writer.visitField(
+                access
+                , name
+                , getInternalName(type)
+                , null
+                , null
+        )
+                .visitEnd();
+
+        return (T)this;
+    }
+
+    /*******************************************************************/
+    // Add target method call site into current class
+    public T addCaller(String targetClass, int callType) {
+        MethodVisitor m = writer.visitMethod(
+                ACC_PUBLIC | ACC_STATIC
+                , "call"
+                , String.format( "(L%s;)Ljava/lang/String;" , getInternalName(targetClass))
+                , null
+                , null
+        );
+
+        m.visitCode();
+        m.visitVarInsn(ALOAD, 0);
+        m.visitMethodInsn(
+                  callType
+                , getInternalName(targetClass)
+                , targetMethodName
+                , "()Ljava/lang/String;"
+        );
+        m.visitInsn(ARETURN);
+        m.visitEnd();
+        m.visitMaxs(0,0);
+
+        return (T)this;
+    }
+
+    /*******************************************************************/
+    public byte[] getClassFile() {
+        writer.visitEnd();
+        return writer.toByteArray();
+    }
+
+    /*******************************************************************/
+    public String getFullClassName() {
+        return fullClassName;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/InvocationTests/shared/Utils.java	Wed Jun 26 09:06:32 2019 -0400
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ *
+ */
+
+package shared;
+
+import java.util.List;
+
+/**
+ * Just a set of constants
+ */
+public class Utils {
+    public static final String TARGET_METHOD_NAME = "m";
+    public static int version = 50;
+
+    public static boolean isACC_SUPER = false;
+
+    public static void init(List<String> args) {
+        for (String param : args) {
+            String name = "classfile_version";
+            String pattern = "--"+name+"=";
+            if (param.startsWith(pattern)) {
+                String value = param.substring(pattern.length());
+                int majorVersion = 50;
+                int minorVersion = 0;
+
+                try {
+                    String[] versions = value.split(":");
+                    if (versions.length > 2) {
+                        throw new RuntimeException(String.format("Unknown %s value: %s", name, value));
+                    }
+
+                    try {
+                        majorVersion = Integer.parseInt(versions[0]);
+                        if (versions.length > 1) {
+                            minorVersion = Integer.parseInt(versions[1]);
+                        }
+                    } catch(Exception e) {
+                        throw new RuntimeException(String.format("Can't parse %s value: '%s'", name, value));
+                    }
+                } catch (Exception e) {
+                    System.out.println("ERROR: "+e.getMessage());
+                }
+
+                version = majorVersion + (minorVersion << 16);
+
+                System.out.printf("INFO: Class file version: major: %d; minor: %d\n", majorVersion, minorVersion);
+
+                if (majorVersion < 49 && !args.contains("--no_acc_super")) {
+                    isACC_SUPER = true;
+                    System.out.printf("INFO: Enabling ACC_SUPER flag for major: %d\nTo disable it, specify --no_acc_super option.\n", majorVersion, minorVersion);
+                }
+            } else if (param.equals("--no_acc_super")){
+                System.out.println("INFO: ACC_SUPER flag is disabled");
+                isACC_SUPER = false;
+            } else if (param.equals("--acc_super")){
+                isACC_SUPER = true;
+            } else {
+                System.out.println("ERROR: Unknown option: "+param);
+                printHelp();
+                System.exit(1);
+            }
+        }
+    }
+
+    public static void printHelp() {
+        System.out.println(
+                 "Supported parameters:\n"
+               + "\t--classfile_version=major_version[:minor_version]\n"
+               + "\t\t- specify class file version for generated classes\n"
+               + "\t--no_acc_super\n"
+               + "\t\t- don't add ACC_SUPER flag into generated classes\n"
+               + "\t--acc_super\n"
+               + "\t\t- force ACC_SUPER flag in generated classes\n"
+               + "\t--dump\n"
+               + "\t\t- dump generated classes\n"
+               + "\t--noexecute\n"
+               + "\t\t- print only expected results, don't execute tests\n"
+        );
+    }
+
+    /*******************************************************************/
+    public static String getInternalName(String s) {
+        return s.replaceAll("\\.", "/");
+    }
+
+}