# HG changeset patch # User hseigel # Date 1561554392 14400 # Node ID d3a33953b9365edcf3b5fff93691261975b05e07 # Parent 8e0ae3830fca952269c786731c10af762bb93bdc 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 diff -r 8e0ae3830fca -r d3a33953b936 test/hotspot/jtreg/TEST.groups --- 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 \ diff -r 8e0ae3830fca -r d3a33953b936 test/hotspot/jtreg/runtime/InvocationTests/invokeinterface/Checker.java --- /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"; + } +} diff -r 8e0ae3830fca -r d3a33953b936 test/hotspot/jtreg/runtime/InvocationTests/invokeinterface/ClassGenerator.java --- /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 { + 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); + } +} diff -r 8e0ae3830fca -r d3a33953b936 test/hotspot/jtreg/runtime/InvocationTests/invokeinterface/Generator.java --- /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 classes = new HashMap(); + + // 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")); + } + } +} diff -r 8e0ae3830fca -r d3a33953b936 test/hotspot/jtreg/runtime/InvocationTests/invokeinterfaceTests.java --- /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 + } +} diff -r 8e0ae3830fca -r d3a33953b936 test/hotspot/jtreg/runtime/InvocationTests/invokespecial/Checker.java --- /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 (!"".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 "---"; + } +} diff -r 8e0ae3830fca -r d3a33953b936 test/hotspot/jtreg/runtime/InvocationTests/invokespecial/ClassGenerator.java --- /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 { + 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); + } +} diff -r 8e0ae3830fca -r d3a33953b936 test/hotspot/jtreg/runtime/InvocationTests/invokespecial/Generator.java --- /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? 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 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 + * + * invokespecial is used in certain special cases to invoke a method + * Specifically, invokespecial is used to invoke: + * - the instance initialization method, + * - 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, , 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/()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 are resolved. See Chapter 9 for a description of how methods + * are resolved. + * + * invokespecial first looks at the descriptor given in , 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 , or one of its subclasses. The interpreter + * searches the list of methods defined by the class named in , + * 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 . + * + * 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 + * 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 classes = new HashMap(); + + 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")); + } + } +} diff -r 8e0ae3830fca -r d3a33953b936 test/hotspot/jtreg/runtime/InvocationTests/invokespecialTests.java --- /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 + } +} diff -r 8e0ae3830fca -r d3a33953b936 test/hotspot/jtreg/runtime/InvocationTests/invokevirtual/Checker.java --- /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()); + } +} diff -r 8e0ae3830fca -r d3a33953b936 test/hotspot/jtreg/runtime/InvocationTests/invokevirtual/ClassGenerator.java --- /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 { + 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); + } +} diff -r 8e0ae3830fca -r d3a33953b936 test/hotspot/jtreg/runtime/InvocationTests/invokevirtual/Generator.java --- /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 classes = new HashMap(); + + // 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")); + } + } +} diff -r 8e0ae3830fca -r d3a33953b936 test/hotspot/jtreg/runtime/InvocationTests/invokevirtualTests.java --- /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 + } +} diff -r 8e0ae3830fca -r d3a33953b936 test/hotspot/jtreg/runtime/InvocationTests/shared/AbstractGenerator.java --- /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 params = new ArrayList(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 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 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); +} diff -r 8e0ae3830fca -r d3a33953b936 test/hotspot/jtreg/runtime/InvocationTests/shared/AccessCheck.java --- /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; + } +} diff -r 8e0ae3830fca -r d3a33953b936 test/hotspot/jtreg/runtime/InvocationTests/shared/AccessType.java --- /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; + } +}; diff -r 8e0ae3830fca -r d3a33953b936 test/hotspot/jtreg/runtime/InvocationTests/shared/ByteArrayClassLoader.java --- /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 classes; + + public ByteArrayClassLoader() { + classes = new HashMap(); + } + + public ByteArrayClassLoader(Map 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); + } + } +} diff -r 8e0ae3830fca -r d3a33953b936 test/hotspot/jtreg/runtime/InvocationTests/shared/Caller.java --- /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); + } + } +} diff -r 8e0ae3830fca -r d3a33953b936 test/hotspot/jtreg/runtime/InvocationTests/shared/Checker.java --- /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; + } +} diff -r 8e0ae3830fca -r d3a33953b936 test/hotspot/jtreg/runtime/InvocationTests/shared/ExecutorGenerator.java --- /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, "", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()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, "", "()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", "", "()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, "", "()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(); + } +} diff -r 8e0ae3830fca -r d3a33953b936 test/hotspot/jtreg/runtime/InvocationTests/shared/GenericClassGenerator.java --- /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 { + 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 + , "" + , "()V" + , null + , null + ); + + m.visitCode(); + m.visitVarInsn(ALOAD, 0); + m.visitMethodInsn( + INVOKESPECIAL + , getInternalName(parent) + , "" + , "()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 + , "" + , "(I)V" + , null + , null + ); + + // Add a call to parent constructor + m.visitCode(); + m.visitVarInsn(ALOAD, 0); + m.visitMethodInsn( + INVOKESPECIAL + , getInternalName(parentClassName) + , "" + , "()V" + ); + + // Add result reporting + String shortName = fullClassName.substring(fullClassName.lastIndexOf('.') + 1); + m.visitLdcInsn(shortName+"."); + 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; + } +} diff -r 8e0ae3830fca -r d3a33953b936 test/hotspot/jtreg/runtime/InvocationTests/shared/Utils.java --- /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 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("\\.", "/"); + } + +}