OpenJDK / valhalla / valhalla
changeset 56254:4be2cb65666f nestmates
[Nestmates] update hidden/nestmate/weak class prototype
line wrap: on
line diff
--- a/src/hotspot/share/oops/instanceKlass.cpp Wed Jun 26 22:16:31 2019 -0700 +++ b/src/hotspot/share/oops/instanceKlass.cpp Thu Aug 29 19:32:19 2019 -0700 @@ -2907,7 +2907,7 @@ *inner_is_member = true; } if (NULL == outer_klass) { - // It may be unsafe anonymous; try for that. + // It may be a local or anonymous class; try for that. int encl_method_class_idx = enclosing_method_class_index(); if (encl_method_class_idx != 0) { Klass* ok = i_cp->klass_at(encl_method_class_idx, CHECK_NULL);
--- a/src/hotspot/share/oops/method.hpp Wed Jun 26 22:16:31 2019 -0700 +++ b/src/hotspot/share/oops/method.hpp Thu Aug 29 19:32:19 2019 -0700 @@ -888,8 +888,9 @@ _flags = x ? (_flags | _dont_inline) : (_flags & ~_dont_inline); } - bool is_hidden() { - return (_flags & _hidden) != 0; + bool is_hidden() const { + // ## FIXME: should be set at class parsing time + return method_holder()->is_nonfindable() || (_flags & _hidden) != 0; } void set_hidden(bool x) { _flags = x ? (_flags | _hidden) : (_flags & ~_hidden);
--- a/src/hotspot/share/runtime/reflection.cpp Wed Jun 26 22:16:31 2019 -0700 +++ b/src/hotspot/share/runtime/reflection.cpp Thu Aug 29 19:32:19 2019 -0700 @@ -707,7 +707,7 @@ // Checks that the 'outer' klass has declared 'inner' as being an inner klass. If not, // throw an incompatible class change exception // If inner_is_member, require the inner to be a member of the outer. -// If !inner_is_member, require the inner to be a nonfindable or unsafe anonymous (a non-member). +// If !inner_is_member, require the inner to be a local or anonymous class (a non-member). // Caller is responsible for figuring out in advance which case must be true. void Reflection::check_for_inner_class(const InstanceKlass* outer, const InstanceKlass* inner, bool inner_is_member, TRAPS) {
--- a/src/java.base/share/classes/java/lang/Class.java Wed Jun 26 22:16:31 2019 -0700 +++ b/src/java.base/share/classes/java/lang/Class.java Thu Aug 29 19:32:19 2019 -0700 @@ -63,7 +63,6 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.stream.Stream; import java.util.stream.Collectors; import jdk.internal.HotSpotIntrinsicCandidate; @@ -129,10 +128,9 @@ * enclosed within the top-level class declaration. * * <p> Some methods of class {@code Class} expose some characteristics of - * a class defined via - * {@link java.lang.invoke.MethodHandles.Lookup#defineClass(byte[], MethodHandles.Lookup.ClassProperty...) - * Lookup::defineClass} API such as whether this class is a - * {@linkplain #isHiddenClass() hidden class}. + * hidden classes that can be defined by calling + * {@link java.lang.invoke.MethodHandles.Lookup#defineHiddenClass(byte[], boolean, MethodHandles.Lookup.ClassOptions...) + * Lookup::defineHiddenClass}. * * <p> The following example uses a {@code Class} object to print the * class name of an object: @@ -761,6 +759,9 @@ * by * <cite>The Java™ Language Specification</cite>. * + * <p> If this class object represents a {@linkplain #isHiddenClass() hidden class}, + * then the name is given by the JVM and it may not be a valid binary name. + * * <p> If this class object represents a primitive type or void, then the * name returned is a {@code String} equal to the Java language * keyword corresponding to the primitive type or void. @@ -1655,7 +1656,8 @@ /** * Returns {@code true} if and only if the underlying class - * is an anonymous class. + * is an anonymous class. An anonymous class is not a + * {@linkplain #isHiddenClass() hidden class}. * * @return {@code true} if and only if this class is an anonymous class. * @since 1.5 @@ -3922,10 +3924,11 @@ * {@code NestMembers} attribute (JVMS 4.7.29). * A {@code class} file of version 54.0 or lower does not use these * attributes. - * A class defined at runtime in a nest via - * {@link MethodHandles.Lookup#defineClass(byte[], MethodHandles.Lookup.ClassProperty[])} - * must not have the {@code NestHost} attribute and is not enumerated - * in the {@code NestMembers} attribute of the class file of the nest host. + * If this class was created by calling + * {@link MethodHandles.Lookup#defineHiddenClass(byte[], boolean, MethodHandles.Lookup.ClassOptions...) + * Lookup.defineHiddenClass} + * and added to the nest of the lookup object's lookup class, then + * this class has the same nest host as the lookup class. * * @return the nest host of this class or interface * @@ -4018,16 +4021,12 @@ * interface records itself as a member of that same nest. Any exceptions * that occur during this validation are rethrown by this method. * - * <p>This method does not return the - * {@linkplain MethodHandles.Lookup.ClassProperty#NESTMATE nest members} - * defined dynamically via - * {@link MethodHandles.Lookup#defineClass(byte[], MethodHandles.Lookup.ClassProperty...)}. - * * @apiNote - * Reflection API presents the static view of this class. The dynamic - * nestmates are not listed in the {@code NestMembers} attribute. - * We can revisit this in the future if there is a need to find - * all dynamically defined nest members. + * This method returns the nest members listed in the {@code NestMembers} + * attribute. The returned array does not include nest members created + * by calling + * {@link MethodHandles.Lookup#defineHiddenClass(byte[], boolean, MethodHandles.Lookup.ClassOptions...) + * Lookup.defineHiddenClass}. * * @return an array of all classes and interfaces in the same nest as * this class @@ -4045,7 +4044,7 @@ * * @since 11 * @see #getNestHost() - * @see MethodHandles.Lookup#defineClass(byte[], MethodHandles.Lookup.ClassProperty...) + * @see MethodHandles.Lookup#defineHiddenClass(byte[], boolean, MethodHandles.Lookup.ClassOptions...) */ @CallerSensitive public Class<?>[] getNestMembers() { @@ -4135,28 +4134,21 @@ /** * Returns {@code true} if this class is a hidden class. * - * <p> A <em>hidden class</em> is a class to which no constant pool entry - * (or symbolic reference derived therefrom) can refer. - * Loading, linking, and initializing of a hidden class are controlled solely - * by invocations on the {@link MethodHandles.Lookup#defineClass(byte[], MethodHandles.Lookup.ClassProperty...) - * MethodHandles.Lookup} object. - * - * <p> A hidden class does not have a {@linkplain #getCanonicalName() - * canonical name}. - * - * <p> If this class is hidden then it cannot be found via its name - * including {@link Class#forName(String) Class.forName(this.getName())}, + * <p> A <em>hidden class</em> is non-discoverable by class loaders + * by bytecode linkage. It cannot be found by + * {@link Class#forName(String) Class.forName(this.getName())}, * {@link ClassLoader#findLoadedClass}, * and {@link MethodHandles.Lookup#findClass(String) MethodHandles.Lookup.findClass}. * * <p> If this class is an array class and its component class is hidden, * then this array class is also hidden. * - * @return {@code true} if this class is hidden; otherwise {@code false}. + * <p> An anonymous class is not a hidden class. + * + * @return {@code true} if this class is a hidden class; otherwise {@code false}. * * @since 14 - * @see MethodHandles.Lookup.ClassProperty#HIDDEN - * @see MethodHandles.Lookup#defineClass(byte[], MethodHandles.Lookup.ClassProperty[]) + * @see MethodHandles.Lookup#defineHiddenClass(byte[], boolean, MethodHandles.Lookup.ClassOptions...) */ public boolean isHiddenClass() { return getName().indexOf('/') > -1;
--- a/src/java.base/share/classes/java/lang/StringConcatHelper.java Wed Jun 26 22:16:31 2019 -0700 +++ b/src/java.base/share/classes/java/lang/StringConcatHelper.java Thu Aug 29 19:32:19 2019 -0700 @@ -28,6 +28,10 @@ import jdk.internal.misc.Unsafe; import jdk.internal.vm.annotation.ForceInline; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + /** * Helper for string concatenation. These methods are mostly looked up with private lookups * from {@link java.lang.invoke.StringConcatFactory}, and used in {@link java.lang.invoke.MethodHandle} @@ -466,4 +470,13 @@ return String.COMPACT_STRINGS ? LATIN1 : UTF16; } + static MethodHandle lookupStatic(String name, MethodType methodType) { + try { + return MethodHandles.lookup().findStatic(StringConcatHelper.class, name, methodType); + } catch (NoSuchMethodException|IllegalAccessException e) { + throw new AssertionError(e); + } + } + + }
--- a/src/java.base/share/classes/java/lang/System.java Wed Jun 26 22:16:31 2019 -0700 +++ b/src/java.base/share/classes/java/lang/System.java Thu Aug 29 19:32:19 2019 -0700 @@ -35,6 +35,8 @@ import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.lang.annotation.Annotation; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; import java.lang.module.ModuleDescriptor; import java.lang.reflect.Constructor; import java.lang.reflect.Executable; @@ -2282,6 +2284,11 @@ public ProtectionDomain protectionDomain(Class<?> c) { return c.protectionDomain(); } + + public MethodHandle stringConcatHelper(String name, MethodType methodType) { + return StringConcatHelper.lookupStatic(name, methodType); + } + }); } }
--- a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java Wed Jun 26 22:16:31 2019 -0700 +++ b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java Thu Aug 29 19:32:19 2019 -0700 @@ -25,7 +25,6 @@ package java.lang.invoke; -import jdk.internal.misc.Unsafe; import jdk.internal.org.objectweb.asm.*; import sun.invoke.util.BytecodeDescriptor; import sun.security.action.GetPropertyAction; @@ -41,7 +40,7 @@ import java.util.PropertyPermission; import java.util.Set; -import static java.lang.invoke.MethodHandles.Lookup.*; +import static java.lang.invoke.MethodHandles.Lookup.ClassOptions.NESTMATE; import static jdk.internal.org.objectweb.asm.Opcodes.*; /** @@ -51,8 +50,6 @@ * @see LambdaMetafactory */ /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory { - private static final Unsafe UNSAFE = Unsafe.getUnsafe(); - private static final int CLASSFILE_VERSION = 52; private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE); private static final String JAVA_LANG_OBJECT = "java/lang/Object"; @@ -78,8 +75,6 @@ private static final String DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION = "(Ljava/lang/String;)V"; private static final String[] SER_HOSTILE_EXCEPTIONS = new String[] {NAME_NOT_SERIALIZABLE_EXCEPTION}; - private static final String DESCR_HIDDEN = "Ljdk/internal/vm/annotation/Hidden;"; - private static final String[] EMPTY_STRING_ARRAY = new String[0]; // Used to ensure that each spun class name is unique @@ -158,7 +153,7 @@ implMethodName = implInfo.getName(); implMethodDesc = implInfo.getMethodType().toMethodDescriptorString(); constructorType = invokedType.changeReturnType(Void.TYPE); - lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet(); + lambdaClassName = lambdaClassName(targetClass); cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); int parameterCount = invokedType.parameterCount(); if (parameterCount > 0) { @@ -173,6 +168,15 @@ } } + private static String lambdaClassName(Class<?> targetClass) { + String name = targetClass.getName(); + if (targetClass.isHiddenClass()) { + // use the original class name + name = name.replace('/', '_'); + } + return name.replace('.', '/') + "$$Lambda$" + counter.incrementAndGet(); + } + /** * Build the CallSite. Generate a class file which implements the functional * interface, define the class, if there are no parameters create an instance @@ -187,7 +191,7 @@ */ @Override CallSite buildCallSite() throws LambdaConversionException { - Lookup lookup = spinInnerClassAsLookup(); + Lookup lookup = spinInnerClass(); Class<?> innerClass = lookup.lookupClass(); assert innerClass.isHiddenClass() : innerClass.toString(); if (invokedType.parameterCount() == 0) { @@ -241,7 +245,7 @@ * @throws LambdaConversionException If properly formed functional interface * is not found */ - private Lookup spinInnerClassAsLookup() throws LambdaConversionException { + private Lookup spinInnerClass() throws LambdaConversionException { String[] interfaces; String samIntf = samBase.getName().replace('.', '/'); boolean accidentallySerializable = !isSerializable && Serializable.class.isAssignableFrom(samBase); @@ -276,7 +280,6 @@ // Forward the SAM method MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName, samMethodType.toMethodDescriptorString(), null, null); - mv.visitAnnotation(DESCR_HIDDEN, true); new ForwardingMethodGenerator(mv).generate(samMethodType); // Forward the bridges @@ -284,7 +287,6 @@ for (MethodType mt : additionalBridges) { mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName, mt.toMethodDescriptorString(), null, null); - mv.visitAnnotation(DESCR_HIDDEN, true); new ForwardingMethodGenerator(mv).generate(mt); } } @@ -312,8 +314,12 @@ // createDirectories may need it new PropertyPermission("user.dir", "read")); } - // this class is linked at the indy callsite. No need to be weak class - return caller.defineClassAsLookupNoCheck(classBytes, HIDDEN_NESTMATE); + try { + // this class is linked at the indy callsite; so define a hidden nestmate + return caller.defineHiddenClassAsLookup(classBytes, true, NESTMATE); + } catch (IllegalAccessException e) { + throw new LambdaConversionException("Exception defining lambda proxy class", e); + } } /**
--- a/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Wed Jun 26 22:16:31 2019 -0700 +++ b/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Thu Aug 29 19:32:19 2019 -0700 @@ -308,19 +308,12 @@ /** * Extract the MemberName of a newly-defined method. */ - private MemberName loadMethod(byte[] classFile, List<Object> classData) { - Class<?> invokerClass = loadAndInitializeInvokerClass(classFile, classData); + private MemberName loadMethod(byte[] classFile) { + Class<?> invokerClass = LOOKUP.defineClassAsLookup(className(), classFile, + HIDDEN_CLASS|WEAK_CLASS|ACCESS_VM_ANNOTATIONS, true, classDataValues()).lookupClass(); return resolveInvokerMember(invokerClass, invokerName, invokerType); } - /** - * Define a given class as anonymous class in the runtime system. - */ - private static Class<?> loadAndInitializeInvokerClass(byte[] classBytes, List<Object> classData) { - return LOOKUP.defineClassAsLookupNoCheck(classBytes, WEAK_HIDDEN_NESTMATE, classData) - .lookupClass(); - } - private static MemberName resolveInvokerMember(Class<?> invokerClass, String name, MethodType type) { MemberName member = new MemberName(invokerClass, name, type, REF_invokeStatic); try { @@ -761,7 +754,7 @@ if (pregenerated != null) return pregenerated; // pre-generated bytecode InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("MH", form, invokerType); - return g.loadMethod(g.generateCustomizedCodeBytes(), g.classDataValues()); + return g.loadMethod(g.generateCustomizedCodeBytes()); } /** Generates code to check that actual receiver and LambdaForm matches */ @@ -1816,7 +1809,7 @@ MethodType type = mt; // includes leading argument type = type.changeParameterType(0, MethodHandle.class); InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("LFI", name, type); - return g.loadMethod(g.generateLambdaFormInterpreterEntryPointBytes(), g.classDataValues()); + return g.loadMethod(g.generateLambdaFormInterpreterEntryPointBytes()); } private byte[] generateLambdaFormInterpreterEntryPointBytes() { @@ -1876,7 +1869,7 @@ MethodType invokerType = NamedFunction.INVOKER_METHOD_TYPE; String invokerName = "invoke_" + shortenSignature(basicTypeSignature(typeForm.erasedType())); InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("NFI", invokerName, invokerType); - return g.loadMethod(g.generateNamedFunctionInvokerImpl(typeForm), g.classDataValues()); + return g.loadMethod(g.generateNamedFunctionInvokerImpl(typeForm)); } private byte[] generateNamedFunctionInvokerImpl(MethodTypeForm typeForm) {
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Wed Jun 26 22:16:31 2019 -0700 +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Thu Aug 29 19:32:19 2019 -0700 @@ -54,7 +54,7 @@ import static java.lang.invoke.LambdaForm.*; import static java.lang.invoke.MethodHandleStatics.*; import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; -import static java.lang.invoke.MethodHandles.Lookup.WEAK_HIDDEN_NESTMATE; +import static java.lang.invoke.MethodHandleNatives.Constants.HIDDEN_CLASS; import static jdk.internal.org.objectweb.asm.Opcodes.*; /** @@ -1177,8 +1177,8 @@ * * @CSM must be public and exported if called by any module. */ - String name = targetClass.getNestHost().getName() + "$$InjectedInvoker"; - Lookup lookup = new Lookup(targetClass).defineClassAsLookupNoCheck(name, INJECTED_INVOKER_TEMPLATE, WEAK_HIDDEN_NESTMATE); + String name = targetClass.getName() + "$$InjectedInvoker"; + Lookup lookup = new Lookup(targetClass).defineClassAsLookup(name, INJECTED_INVOKER_TEMPLATE, HIDDEN_CLASS, true, null); Class<?> invokerClass = lookup.lookupClass(); assert checkInjectedInvoker(targetClass, invokerClass); return lookup.findStatic(invokerClass, "invoke_V", INVOKER_MT); @@ -1276,10 +1276,6 @@ "(Ljava/lang/invoke/MethodHandle;[Ljava/lang/Object;)Ljava/lang/Object;", null, null); - // Suppress invoker method in stack traces. - AnnotationVisitor av0 = mv.visitAnnotation(InvokerBytecodeGenerator.HIDDEN_SIG, true); - av0.visitEnd(); - mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1);
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java Wed Jun 26 22:16:31 2019 -0700 +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java Thu Aug 29 19:32:19 2019 -0700 @@ -142,7 +142,7 @@ REF_LIMIT = 10; /** - * Flags for Lookup.ClassProperty + * Flags for Lookup.ClassOptions */ static final int NESTMATE_CLASS = 0x00000001,
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Wed Jun 26 22:16:31 2019 -0700 +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Thu Aug 29 19:32:19 2019 -0700 @@ -29,6 +29,8 @@ import jdk.internal.access.SharedSecrets; import jdk.internal.module.IllegalAccessLogger; import jdk.internal.org.objectweb.asm.ClassReader; +import jdk.internal.org.objectweb.asm.ClassVisitor; +import jdk.internal.org.objectweb.asm.Opcodes; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; import jdk.internal.vm.annotation.ForceInline; @@ -58,7 +60,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static java.lang.invoke.MethodHandles.Lookup.ClassProperty.*; import static java.lang.invoke.MethodHandleImpl.Intrinsic; import static java.lang.invoke.MethodHandleNatives.Constants.*; import static java.lang.invoke.MethodHandleStatics.newIllegalArgumentException; @@ -550,8 +551,8 @@ * and the Core Reflection API * (as found on {@link java.lang.Class Class}). * <p> - * If a security manager is present, member and class lookups are subject to - * additional checks. + * If a security manager is present, member and class lookups are + * subject to additional checks. * From one to three calls are made to the security manager. * Any of these calls can refuse access by throwing a * {@link java.lang.SecurityException SecurityException}. @@ -595,6 +596,13 @@ * Therefore, the above rules presuppose a member or class that is public, * or else that is being accessed from a lookup class that has * rights to access the member or class. + * <p> + * If a security manager is present and the current lookup object does not have + * <a href="MethodHandles.Lookup.html#privacc">private access</a>, then + * {@link #defineClass(byte[]) defineClass} and + * {@link #defineHiddenClass(byte[], boolean, ClassOptions...) defineHiddenClass} + * call {@link SecurityManager#checkPermission smgr.checkPermission} + * with {@code RuntimePermission("defineClass")} is called. * * <h2><a id="callsens"></a>Caller sensitive methods</h2> * A small number of Java methods have a special property called caller sensitivity. @@ -822,8 +830,6 @@ * @param requestedLookupClass the desired lookup class for the new lookup object * @return a lookup object which reports the desired lookup class, or the same object * if there is no change - * @throws IllegalArgumentException if {@code requestedLookupClass} is - * a primitive type or array class * @throws NullPointerException if the argument is null * * @revised 9 @@ -831,10 +837,6 @@ */ public Lookup in(Class<?> requestedLookupClass) { Objects.requireNonNull(requestedLookupClass); - if (requestedLookupClass.isPrimitive()) - throw new IllegalArgumentException(requestedLookupClass + " is a primitive class"); - if (requestedLookupClass.isArray()) - throw new IllegalArgumentException(requestedLookupClass + " is an array class"); if (allowedModes == TRUSTED) // IMPL_LOOKUP can make any lookup at all return new Lookup(requestedLookupClass, FULL_POWER_MODES); @@ -910,16 +912,20 @@ * {@linkplain java.security.ProtectionDomain protection domain} as this lookup's * {@linkplain #lookupClass() lookup class}. * - * This method is equivalent to calling {@link #defineClass(byte[], ClassProperty[]) - * defineClass(bytes, new ClassProperty[0])}. + * <p> The {@linkplain #lookupModes() lookup modes} for this lookup must include + * {@link #PACKAGE PACKAGE} access as default (package) members will be + * accessible to the class. The {@code PACKAGE} lookup mode serves to authenticate + * that the lookup object was created by a caller in the runtime package (or derived + * from a lookup originally created by suitably privileged code to a target class in + * the runtime package). </p> * - * <p> The {@linkplain #lookupModes() lookup modes} for this lookup must - * have {@code PRIVATE} and {@code MODULE} access in order to add a new - * member in the module of this lookup class. + * <p> The {@code bytes} parameter is the class bytes of a valid class file (as defined + * by the <em>The Java Virtual Machine Specification</em>) with a class name in the + * same package as the lookup class. </p> * - * <p> The class bytes of a nestmate class must not contain - * the {@code NestHost} attribute nor the {@code NestMembers} attribute; - * otherwise {@code IllegalArgumentException} will be thrown. + * <p> This method does not run the class initializer. The class initializer may + * run at a later time, as detailed in section 12.4 of the <em>The Java Language + * Specification</em>. </p> * * <p> If there is a security manager, its {@code checkPermission} method is first called * to check {@code RuntimePermission("defineClass")}. </p> @@ -928,7 +934,7 @@ * @return the {@code Class} object for the class * @throws IllegalArgumentException the bytes are for a class in a different package * to the lookup class - * @throws IllegalAccessException if this lookup does not have {@code PRIVATE} and {@code MODULE} access + * @throws IllegalAccessException if this lookup does not have {@code PACKAGE} access * @throws LinkageError if the class is malformed ({@code ClassFormatError}), cannot be * verified ({@code VerifyError}), is already defined, or another linkage error occurs * @throws SecurityException if denied by the security manager @@ -940,271 +946,287 @@ * @see ClassLoader#defineClass(String,byte[],int,int,ProtectionDomain) */ public Class<?> defineClass(byte[] bytes) throws IllegalAccessException { - return defineClass(bytes, EMPTY_CLASS_PROPS); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new RuntimePermission("defineClass")); + if ((lookupModes() & PACKAGE) == 0) + throw new IllegalAccessException("Lookup does not have PACKAGE access"); + assert (lookupModes() & (MODULE|PUBLIC)) != 0; + + // parse class bytes to get class name (in internal form) + bytes = bytes.clone(); + String name; + try { + ClassReader reader = new ClassReader(bytes); + name = reader.getClassName(); + } catch (RuntimeException e) { + // ASM exceptions are poorly specified + ClassFormatError cfe = new ClassFormatError(); + cfe.initCause(e); + throw cfe; + } + + // get package and class name in binary form + String cn, pn; + int index = name.lastIndexOf('/'); + if (index == -1) { + cn = name; + pn = ""; + } else { + cn = name.replace('/', '.'); + pn = cn.substring(0, index); + } + if (!pn.equals(lookupClass.getPackageName())) { + throw new IllegalArgumentException("Class not in same package as lookup class"); + } + + return defineClass(cn, bytes, 0, false, null); + } + + private void checkDefineClassPermission() { + SecurityManager sm = System.getSecurityManager(); + if (sm == null) return; + if (allowedModes == TRUSTED) return; + + if (!hasPrivateAccess()) { + sm.checkPermission(new RuntimePermission("defineClass")); + } } /** - * Defines a class to the same class loader and in the same runtime package - * and {@linkplain java.security.ProtectionDomain protection domain} as - * this lookup's {@linkplain #lookupClass() lookup class}. - * The {@code props} parameter specifies the properties of the class. + * Defines a hidden class to the same class loader and in the same runtime package + * and {@linkplain java.security.ProtectionDomain protection domain} as this + * lookup's {@linkplain #lookupClass() lookup class}. + * The {@code options} parameter specifies the class options. * - * <p> A class can be defined with the following properties: + * <p> The hidden class is initialized if the {@code initialize} parameter is + * {@code true}. + * + * <p> + * A {@link Class#isHiddenClass() <em>hidden</em>} class, i.e. a class cannot be + * symbolically referenced in other classes, with the following properties: * <ul> - * <li>A {@linkplain ClassProperty#NESTMATE <em>nestmate</em>} of the lookup class, - * i.e. in the same {@linkplain Class#getNestHost nest} - * of the lookup class. The class will have access to the private members - * of all classes and interfaces in the same nest. - * </li> - * <li>A {@linkplain ClassProperty#HIDDEN <em>hidden</em>} class, - * i.e. a class cannot be named in other classes. - * A hidden class has the following properties: - * <ul> - * <li>Naming: - * The name of this class is derived from the name of - * the class in the class bytes so that the class name does not - * collide with other classes defined to the same class loader. - * <li>Class resolution: + * <li>Naming: + * The name of this class is determined by the JVM and the class name + * does not collide with other classes defined to the same class loader. + * The name returned by {@link Class#getName()} must be unique + * in the same package as this lookup class. + * It may not be a valid binary name. + * There is no two classes (whether ordinary or hidden) with the same + * defining loader have the same name. + * <li>Class resolution: * A hidden class is not registered with a globally defined name and * hence cannot be found by its class loader. * A hidden class cannot be named as a field type, a method parameter * type and a method return type. - * The name returned by {@link Class#getName()} is a unique name - * in the same package as this lookup class and - * is different from the name in the class bytes. - * <li>Class retransformation: + * <li>Class retransformation: * A hidden class is not {@linkplain java.lang.instrument.Instrumentation#isModifiableClass(Class) * modifiable} by Java agents or tool agents using * the <a href="{@docRoot}/../specs/jvmti.html">JVM Tool Interface</a>. - * <li>Reflective access: + * <li>Reflective access: * The {@linkplain java.lang.reflect.AccessibleObject#setAccessible(boolean) * accessible} flag of its {@link Field}, {@link Method}, * {@link Constructor} reflected objects of a hidden class cannot be set * to suppress access check. - * </ul> - * </li> - * <li>A {@linkplain ClassProperty#WEAK <em>weak</em>} class, - * i.e. a class may be unloaded independently with - * its defining class loader when it becomes - * <a href="../ref/package.html#reachability">reachable</a>, - * as if the defining class loader would only hold a - * {@linkplain java.lang.ref.WeakReference weak reference} of - * the class. - * A weak class is hidden. If the {@code WEAK} property is set, - * then it implies that {@code HIDDEN} property is also set.</li> * </ul> * + * <p> If {@code options} has {@link ClassOptions#NESTMATE}, then this method adds + * the hidden class to join the nest of the lookup class. + * The hidden class has the same {@linkplain Class#getNestHost nest host} + * as the lookup class and therefore it can access to the private members + * of all classes and interfaces in the same nest. + * + * <p> If {@code options} has {@link ClassOptions#WEAK}, then the hidden class is + * weakly reachable from its defining class loader and may be unloaded while + * its defining class loader is strongly reachable. + * * <p> The {@linkplain #lookupModes() lookup modes} for this lookup must * have {@code PRIVATE} and {@code MODULE} access in order to add a new * member in the module of this lookup class. * * <p> The {@code bytes} parameter is the class bytes of a valid class file * (as defined by the <em>The Java Virtual Machine Specification</em>) - * with a class name in the same package as the lookup class. - * - * <p> The class bytes of a nestmate class must not contain - * the {@code NestHost} attribute nor the {@code NestMembers} attribute; - * otherwise {@code IllegalArgumentException} will be thrown. - * - * <p> If there is a security manager, its {@code checkPermission} method is first called - * to check {@code RuntimePermission("defineClass")}. - * - * <p> This method does not run the class initializer. The class initializer may - * run at a later time, as detailed in section 12.4 of the <em>The Java Language - * Specification</em>. - * - * @apiNote An implementation of the Java Progamming Language may - * unload classes as specified in section 12.7 of the Java Language Specification. - * A class or interface may be unloaded if and only if - * its defining class loader may be reclaimed by the garbage collector. - * If the implementation supports class loading, a weak class - * may become weakly reachable while its defining class loader is - * strongly reachable. + * with a class name in the same package as the lookup class. In addition, + * <ul> + * <li> It must be a top-level class.</li> + * <li> It cannot be abstract.</li> + * <li> It cannot be an interface.</li> + * <li> It cannot be an enclosing class.</li> + * <li> It cannot be a superclass.</li> + * <li> It cannot be in any static nest membership, i.e. the class bytes + * must not contain the {@code NestHost}, {@code NestMembers}.</li> + * </ul> + * If any of the above checks is violated, {@code IllegalArgumentException} + * will be thrown. * * @param bytes the class bytes - * @param props {@linkplain ClassProperty class properties} - * @return the {@code Class} object for the class + * @param initialize if {@code true} the class will be initialized. + * See Section 12.4 of <em>The Java Language Specification</em>. + * @param options {@linkplain ClassOptions class options} + * @return the {@code Lookup} object on the hidden class * * @throws IllegalArgumentException the bytes are for a class in a different package - * to the lookup class; or if {@code NESTMATE} class - * property is specified and the bytes contain - * {@code NestHost} or {@code NestMembers} attribute + * to the lookup class; or the bytes contain + * {@code NestHost}, {@code NestMembers}, {@code InnerClasses} or + * {@code EnclosingMethod} attribute. * @throws IllegalAccessException if this lookup does not have {@code PRIVATE} and {@code MODULE} access * @throws LinkageError if the class is malformed ({@code ClassFormatError}), cannot be * verified ({@code VerifyError}), is already defined, * or another linkage error occurs - * @throws SecurityException if denied by the security manager + * @throws SecurityException if a security manager is present and it + * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a> * @throws NullPointerException if {@code bytes} is {@code null} * * @since 14 * @jls 12.7 Unloading of Classes and Interfaces */ - public Class<?> defineClass(byte[] bytes, ClassProperty... props) throws IllegalAccessException { + public Class<?> defineHiddenClass(byte[] bytes, boolean initialize, ClassOptions... options) + throws IllegalAccessException + { + checkDefineClassPermission(); + Objects.requireNonNull(bytes); - - // clone the properties before access - Set<ClassProperty> properties; - if (props == null || props.length == 0) { - properties = Set.of(); - } else { - properties = Set.of(props); - } if ((lookupModes() & (PRIVATE|MODULE)) != (PRIVATE|MODULE)){ throw new IllegalAccessException(this + " does not have PRIVATE or MODULE access"); } - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - sm.checkPermission(new RuntimePermission("defineClass")); + int flags = HIDDEN_CLASS; + if (options != null && options.length > 0) { + // clone options parameters + Set<ClassOptions> opts = Set.of(options); + for (ClassOptions cp : opts) { + flags |= cp.flag; + } } - bytes = bytes.clone(); - return defineClass(className(bytes), bytes, classPropertiesToFlags(properties), null, false); + ClassFileChecker cfc = new ClassFileChecker(bytes).validate(); + return defineClass(cfc.className(), bytes, flags, initialize, null); } /** - * Returns a {@code Lookup} on the class defined to the same class loader and - * in the same runtime package and - * {@linkplain java.security.ProtectionDomain protection domain} as this lookup's - * {@linkplain #lookupClass() lookup class} with its class initializer invoked. - * - * <p> This method is equivalent to calling {@link #defineClass(byte[], ClassProperty...) - * defineClass(bytes, props)} to define the class and then calling - * its class initializer. If the newly loaded class is a nestmate of this lookup class, - * this method returns a {@code Lookup} with {@link #PRIVATE} access; otherwise - * this method returns a {@code Lookup} with {@link #PACKAGE} access. - * - * <p> The {@linkplain #lookupModes() lookup modes} for this lookup must - * have {@code PRIVATE} and {@code MODULE} access in order to add a new - * member in the module of this lookup class. + * Defines a {@link Class#isHiddenClass() hidden} class to the same class loader + * and in the same runtime package and {@linkplain java.security.ProtectionDomain + * protection domain} as this lookup's {@linkplain #lookupClass() lookup class}. * - * <p> The class bytes of a nestmate class must not contain - * the {@code NestHost} attribute nor the {@code NestMembers} attribute; - * otherwise {@code IllegalArgumentException} will be thrown. - * - * <p> If there is a security manager, its {@code checkPermission} method is first called - * to check {@code RuntimePermission("defineClass")}. + * <p> This method returns a {@code Lookup} object with private access + * on the hidden class which is defined as if calling + * {@link #defineHiddenClass(byte[], boolean, ClassOptions...) defineHiddenClass}. * - * @apiNote - * This method ensures that the newly loaded class is initialized prior - * to the invocation of {@code MethodHandle} created from the lookup - * of the newly loaded class. - * - * @param bytes the class bytes - * @param props {@linkplain ClassProperty class properties} - * @return the {@code Lookup} object for the defined class + * @param bytes the class bytes + * @param initialize if {@code true} the class will be initialized. + * See Section 12.4 of <em>The Java Language Specification</em>. + * @param options {@linkplain ClassOptions class options} + * @return the {@code Lookup} object on the hidden class * * @throws IllegalArgumentException the bytes are for a class in a different package - * to the lookup class; or if {@code NESTMATE} class - * property is specified and the bytes contain - * {@code NestHost} or {@code NestMembers} attribute + * to the lookup class; or the bytes contain + * {@code NestHost}, {@code NestMembers}, {@code InnerClasses} or + * {@code EnclosingMethod} attribute. * @throws IllegalAccessException if this lookup does not have {@code PRIVATE} and {@code MODULE} access * @throws LinkageError if the class is malformed ({@code ClassFormatError}), cannot be * verified ({@code VerifyError}), is already defined, * or another linkage error occurs - * @throws SecurityException if denied by the security manager + * @throws SecurityException if a security manager is present and it + * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a> * @throws NullPointerException if {@code bytes} is {@code null} + * * @since 14 - * @see #defineClass(byte[], ClassProperty...) + * @jls 12.7 Unloading of Classes and Interfaces */ - public Lookup defineClassAsLookup(byte[] bytes, ClassProperty... props) throws IllegalAccessException { + public Lookup defineHiddenClassAsLookup(byte[] bytes, boolean initialize, ClassOptions... options) + throws IllegalAccessException + { + checkDefineClassPermission(); + Objects.requireNonNull(bytes); - - // clone the properties before access - Set<ClassProperty> properties; - if (props == null || props.length == 0) { - properties = Set.of(); - } else { - properties = Set.of(props); - } if ((lookupModes() & (PRIVATE|MODULE)) != (PRIVATE|MODULE)){ throw new IllegalAccessException(this + " does not have PRIVATE or MODULE access"); } - SecurityManager sm = System.getSecurityManager(); - if (sm != null) - sm.checkPermission(new RuntimePermission("defineClass")); - - return defineClassAsLookupNoCheck(bytes.clone(), classPropertiesToFlags(properties)); + int flags = HIDDEN_CLASS; + if (options != null && options.length > 0) { + // clone options parameters + Set<ClassOptions> opts = Set.of(options); + for (ClassOptions cp : opts) { + flags |= cp.flag; + } + } + bytes = bytes.clone(); + ClassFileChecker cfc = new ClassFileChecker(bytes).validate(); + return defineHiddenClassAsLookup(cfc.className(), bytes, initialize, flags); } + /** * Returns a {@code Lookup} on the class defined to the same class loader * and in the same runtime package * and {@linkplain java.security.ProtectionDomain protection domain} as * this lookup's {@linkplain #lookupClass() lookup class} with - * the given class properties and {@code classData}. + * the given class options and {@code classData}. * * <p> This method defines a class as if calling - * {@link #defineClass(byte[], ClassProperty...) defineClass(bytes, props)} - * and then initializes the class as if setting the {@code classData} - * in a private static unnamed field and then invoke {@code <clinit>} - * to complete the class initialization. + * {@link #defineHiddenClass(byte[], boolean, ClassOptions...) defineHiddenClass(bytes, true, options)}. + * Setting the {@code classData} behaves as if assigning it to + * a private static unnamed field in the class static initializer. * * <p> The {@linkplain #lookupModes() lookup modes} for this lookup must * have {@code PRIVATE} and {@code MODULE} access in order to add a new * member in the module of this lookup class. * - * <p> The class bytes of a nestmate class must not contain - * the {@code NestHost} attribute nor the {@code NestMembers} attribute; - * otherwise {@code IllegalArgumentException} will be thrown. - * - * <p> If there is a security manager, its {@code checkPermission} method is - * first called to check {@code RuntimePermission("defineClass")}. </p> - * * <p> * The {@link Lookup#classData(Class)} method can be used to retrieve * the {@code classData}. * * @param bytes the class bytes * @param classData pre-initialized class data - * @param props {@linkplain ClassProperty class properties} + * @param options {@linkplain ClassOptions class options} * @return the {@code Class} object for the class * * @throws IllegalArgumentException the bytes are for a class in a different package - * to the lookup class; or if {@code NESTMATE} class - * property is specified and the bytes contain - * {@code NestHost} or {@code NestMembers} attribute + * to the lookup class; or the bytes contain + * {@code NestHost}, {@code NestMembers}, {@code InnerClasses} or + * {@code EnclosingMethod} attribute. * @throws IllegalAccessException if this lookup does not have {@code PRIVATE} and {@code MODULE} access * @throws LinkageError if the class is malformed ({@code ClassFormatError}), cannot be * verified ({@code VerifyError}), is already defined, * or another linkage error occurs - * @throws SecurityException if denied by the security manager - * @throws NullPointerException if {@code bytes} or {@code classData} is {@code null} + * @throws SecurityException if a security manager is present and it + * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a> + * @throws NullPointerException if {@code bytes} is {@code null} * * @since 14 - * @jls 12.7 Unloading of Classes and Interfaces * @see #classData(Class) */ - public Lookup defineClassWithClassData(byte[] bytes, Object classData, ClassProperty... props) + public Lookup defineHiddenClassWithClassData(byte[] bytes, Object classData, ClassOptions... options) throws IllegalAccessException { + checkDefineClassPermission(); + Objects.requireNonNull(bytes); Objects.requireNonNull(classData); - - Set<ClassProperty> properties; - if (props == null || props.length == 0) { - properties = Set.of(); - } else { - properties = Set.of(props); - } if ((lookupModes() & (PRIVATE|MODULE)) != (PRIVATE|MODULE)){ throw new IllegalAccessException(this + " does not have PRIVATE or MODULE access"); } - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - sm.checkPermission(new RuntimePermission("defineClass")); + int flags = HIDDEN_CLASS; + if (options != null && options.length > 0) { + // clone options parameters + Set<ClassOptions> opts = Set.of(options); + for (ClassOptions cp : opts) { + flags |= cp.flag; + } } - return defineClassAsLookupNoCheck(bytes.clone(), classPropertiesToFlags(properties), classData); + bytes = bytes.clone(); + ClassFileChecker cfc = new ClassFileChecker(bytes).validate(); + return defineClassAsLookup(cfc.className(), bytes, flags, true, classData); } /** * Returns the class data associated with this lookup class. * If this lookup class was defined via - * {@link #defineClassWithClassData(byte[], Object, ClassProperty...) - * defineClassWithClassData(bytes, classData, properties)} + * {@link #defineHiddenClassWithClassData(byte[], Object, ClassOptions...) + * defineHiddenClassWithClassData(bytes, classData, options)} * then the supplied {@code classData} object is returned; otherwise, * {@code null}. * @@ -1220,7 +1242,7 @@ * class data as effective constants in private static final variables * in its class initializer. These can be method handles, lookup objects * and arbitrary user objects. For example, a class data is - * {@code List.of(o1, o2, o3....)} passed to {@link #defineClassWithClassData(byte[], Object, ClassProperty...)} + * {@code List.of(o1, o2, o3....)} passed to {@link #defineHiddenClassWithClassData(byte[], Object, ClassOptions...)} * where {@code <clinit>} can unpack it as follows: * * <pre>{@code @@ -1238,7 +1260,7 @@ * @return the class data if present; otherwise {@code null}. * @throws IllegalAccessException if this lookup does not have {@code PRIVATE} and {@code MODULE}access * @since 14 - * @see #defineClassWithClassData(byte[], Object, ClassProperty...) + * @see #defineHiddenClassWithClassData(byte[], Object, ClassOptions...) */ @SuppressWarnings("unchecked") public <T> T classData(Class<T> clazz) throws IllegalAccessException { @@ -1248,100 +1270,113 @@ return (T) MethodHandleNatives.classData(lookupClass); } - private static final ClassProperty[] EMPTY_CLASS_PROPS = new ClassProperty[0]; - - /* - * map the set of ClassProperty to VM flags - */ - private static int classPropertiesToFlags(Set<ClassProperty> props) { - if (props.isEmpty()) return 0; - - int flags = 0; - for (ClassProperty cp : props) { - flags |= cp.flag; - if (cp == WEAK) { - // weak class property implies hidden - flags |= HIDDEN.flag; + class ClassFileChecker extends ClassVisitor { + private final ClassReader reader; + private final String name; + private final int accessFlags; + private String exMsg; + + ClassFileChecker(byte[] bytes) { + super(Opcodes.ASM7); + try { + this.reader = new ClassReader(bytes); + this.name = reader.getClassName(); + this.accessFlags = reader.getAccess(); + if (Modifier.isInterface(accessFlags)) { + setIAE("can't define a hidden interface"); + } + if (Modifier.isAbstract(accessFlags)) { + setIAE("can't define an abstract hidden class"); + } + if (exMsg == null) { + reader.accept(this, ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES); + } + } catch (RuntimeException e) { + // ASM exceptions are poorly specified + ClassFormatError cfe = new ClassFormatError(); + cfe.initCause(e); + throw cfe; } } - return flags; - } - - /** - * Returns the class name of the given byte stream. - * - * @throws IllegalArgumentException if it is in a different package from this lookup class. - */ - private String className(byte[] bytes) { - // Can't use lambda during bootstrapping - String name; - try { - ClassReader reader = new ClassReader(bytes); - name = reader.getClassName(); - } catch (RuntimeException e) { - // ASM exceptions are poorly specified - ClassFormatError cfe = new ClassFormatError(); - cfe.initCause(e); - throw cfe; + + String className() { + return name.replace('/', '.'); + } + + @Override + public void visitNestHost(String nestHost) { + setIAE("can't define a hidden class with NestHost attribute"); + } + + @Override + public void visitNestMember(final String nestMember) { + setIAE("can't define a hidden class with NestMembers attribute"); + } + + @Override + public void visitOuterClass(String owner, String name, String desc) { + setIAE("can't define a hidden class with EnclosingMethod attribute"); + } - // get package and class name in binary form - String cn, pn; - int index = name.lastIndexOf('/'); - if (index == -1) { // unnamed package - cn = name; - pn = ""; - } else { - cn = name.replace('/', '.'); - pn = cn.substring(0, index); + + @Override + public void visitInnerClass(String cn, String outerName, String innerName, int access) { + if (name.equals(cn)) { + setIAE(name + " is a nested class"); + } + if (name.equals(outerName)) { + setIAE(name + " contains class members " + cn); + } + if (innerName == null) { + setIAE(name + " encloses an anonymous class " + cn); + } } - if (!pn.equals(lookupClass.getPackageName())) { - throw new IllegalArgumentException(cn + " not in same package as lookup class: " + lookupClass.getName()); + + void setIAE(String msg) { + if (exMsg == null) + exMsg = msg; } - return cn; + + ClassFileChecker validate() { + int index = name.lastIndexOf('/'); + String cn = className(); + String pn = (index == -1) ? "" : cn.substring(0, index); + if (!pn.equals(lookupClass.getPackageName())) { + throw newIllegalArgumentException(cn + " not in same package as lookup class: " + lookupClass.getName()); + } + if (exMsg != null) + throw newIllegalArgumentException(exMsg); + return this; + } } /* * Invoke class loader's defineClass method to define the class of * the given byte stream. */ - private Class<?> defineClass(String cn, byte[] bytes, int flags, Object classData, boolean initialize) { + private Class<?> defineClass(String name, byte[] bytes, int flags, boolean initialize, Object classData) { ClassLoader loader = lookupClass.getClassLoader(); ProtectionDomain pd = (loader != null) ? lookupClassProtectionDomain() : null; - // TODO: can't pass an illegal name for hidden class here. Append the name with '$` - // for now. VM set the external name. - String name = ((flags & HIDDEN_CLASS) != 0)? cn + "$$" : cn; Class<?> clazz = JLA.defineClass(loader, lookupClass, name, bytes, pd, initialize, flags, classData); - assert clazz.getClassLoader() == lookupClass.getClassLoader() - && clazz.getPackageName().equals(lookupClass.getPackageName()); - return clazz; } - // package-private - static final int HIDDEN_NESTMATE = NESTMATE_CLASS|HIDDEN_CLASS|ACCESS_VM_ANNOTATIONS; - static final int WEAK_HIDDEN_NESTMATE = WEAK_CLASS|HIDDEN_NESTMATE; - /* * Load and initialize the class of the given bytes. */ - Lookup defineClassAsLookupNoCheck(byte[] bytes, int flags) { - return defineClassAsLookupNoCheck(className(bytes), bytes, flags); + private Lookup defineHiddenClassAsLookup(String name, byte[] bytes, boolean initialize, int flags) { + Class<?> c = defineClass(name, bytes, flags, initialize, null); + return new Lookup(c, FULL_POWER_MODES); } /* - * Load and initialize the class of the given bytes and the given classData + * Load and initialize the class of the given bytes and the given classData. + * Called by InvokerBytecodeGenerator and BindCaller.makeInjectedInvoker */ - Lookup defineClassAsLookupNoCheck(byte[] bytes, int flags, Object classData) { - Class<?> c = defineClass(className(bytes), bytes, flags, classData, true); - int modes = (flags & NESTMATE_CLASS) != 0 ? FULL_POWER_MODES : (FULL_POWER_MODES & ~PRIVATE); - return new Lookup(c, modes); - } - - // called by BindCaller.makeInjectedInvoker - Lookup defineClassAsLookupNoCheck(String name, byte[] bytes, int flags) { - Class<?> c = defineClass(name, bytes, flags, null, true); - int modes = (flags & NESTMATE_CLASS) != 0 ? FULL_POWER_MODES : (FULL_POWER_MODES & ~PRIVATE); - return new Lookup(c, modes); + Lookup defineClassAsLookup(String name, byte[] bytes, int flags, boolean initialize, Object classData) { + assert (initialize || classData == null); // initialize must be true if classData is non-null + Class<?> c = defineClass(name, bytes, flags, initialize, classData); + return new Lookup(c, FULL_POWER_MODES); } private ProtectionDomain lookupClassProtectionDomain() { @@ -2920,14 +2955,12 @@ static ConcurrentHashMap<MemberName, DirectMethodHandle> LOOKASIDE_TABLE = new ConcurrentHashMap<>(); /** - * Class property representing the kind of classes defined by the - * {@link Lookup#defineClass(byte[], ClassProperty[]) Lookup::defineClass} method. + * Class options representing the kind of hidden classes defined by the + * {@link Lookup#defineHiddenClass(byte[], boolean, ClassOptions...)} method. * * @since 14 - * @see Lookup#defineClass(byte[], ClassProperty[]) - * @see Lookup#defineClassAsLookup(byte[], ClassProperty[]) */ - public enum ClassProperty { + public enum ClassOptions { /** * A nestmate is a class that is in the same {@linkplain Class#getNestHost nest} * of a lookup class. It has access to the private members of all @@ -2938,21 +2971,9 @@ NESTMATE(NESTMATE_CLASS), /** - * A hidden class is a class that cannot be symbolically referenced by other - * classes. A Java Virtual Machine implementation may hide - * the hidden frames from {@linkplain Throwable#getStackTrace() - * stack traces}. - * - * @see Class#isHiddenClass() - * @see StackWalker.Option#SHOW_HIDDEN_FRAMES - */ - HIDDEN(HIDDEN_CLASS), - - /** * A weak class is a class that may be unloaded independently with * its defining class loader when it becomes * <a href="../ref/package.html#reachability">reachable</a>. - * A weak class is {@linkplain #HIDDEN hidden}. * * @jls 12.7 Unloading of Classes and Interfaces */ @@ -2960,7 +2981,7 @@ /* the flag value is used by VM at define class time */ private final int flag; - ClassProperty(int flag) { + ClassOptions(int flag) { this.flag = flag; } }
--- a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java Wed Jun 26 22:16:31 2019 -0700 +++ b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java Thu Aug 29 19:32:19 2019 -0700 @@ -25,7 +25,8 @@ package java.lang.invoke; -import jdk.internal.misc.Unsafe; +import jdk.internal.access.JavaLangAccess; +import jdk.internal.access.SharedSecrets; import jdk.internal.misc.VM; import jdk.internal.org.objectweb.asm.ClassWriter; import jdk.internal.org.objectweb.asm.Label; @@ -42,7 +43,9 @@ import java.util.concurrent.ConcurrentMap; import java.util.function.Function; -import static java.lang.invoke.MethodHandles.Lookup.*; +import static java.lang.invoke.MethodHandles.lookup; +import static java.lang.invoke.MethodType.methodType; +import static java.lang.invoke.MethodHandleNatives.Constants.*; import static jdk.internal.org.objectweb.asm.Opcodes.*; /** @@ -134,6 +137,8 @@ */ private static final Strategy DEFAULT_STRATEGY = Strategy.MH_INLINE_SIZED_EXACT; + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + private enum Strategy { /** * Bytecode generator, calling into {@link java.lang.StringBuilder}. @@ -190,8 +195,6 @@ */ private static final ProxyClassesDumper DUMPER; - private static final Class<?> STRING_HELPER; - static { // In case we need to double-back onto the StringConcatFactory during this // static initialization, make sure we have the reasonable defaults to complete @@ -203,12 +206,6 @@ // DEBUG = false; // implied // DUMPER = null; // implied - try { - STRING_HELPER = Class.forName("java.lang.StringConcatHelper"); - } catch (Throwable e) { - throw new AssertionError(e); - } - final String strategy = VM.getSavedProperty("java.lang.invoke.stringConcat"); CACHE_ENABLE = Boolean.parseBoolean( @@ -724,8 +721,7 @@ However, there are two peculiarities: a) The generated class should stay within the same package as the - host class, to allow Unsafe.defineAnonymousClass access controls - to work properly. JDK may choose to fail with IllegalAccessException + host class. JDK may choose to fail with IllegalAccessException when accessing a VM anonymous class with non-privileged callers, see JDK-8058575. @@ -748,7 +744,9 @@ String pkgName = hostClass.getPackageName(); return (pkgName != null && !pkgName.isEmpty() ? pkgName.replace('.', '/') + "/" : "") + "Stubs$$StringConcat"; } else { - return hostClass.getName().replace('.', '/') + "$$StringConcat"; + String name = hostClass.isHiddenClass() ? hostClass.getName().replace('/', '_') + : hostClass.getName(); + return name.replace('.', '/') + "$$StringConcat"; } } case MH_SB_SIZED: @@ -820,7 +818,7 @@ * chain javac would otherwise emit. This strategy uses only the public API, * and comes as the baseline for the current JDK behavior. On other words, * this strategy moves the javac generated bytecode to runtime. The - * generated bytecode is loaded via Unsafe.defineAnonymousClass, but with + * generated bytecode is loaded via Lookup::defineClass, but with * the caller class coming from the BSM -- in other words, the protection * guarantees are inherited from the method where invokedynamic was * originally called. This means, among other things, that the bytecode is @@ -849,7 +847,6 @@ * private String API. */ private static final class BytecodeStringBuilderStrategy { - static final Unsafe UNSAFE = Unsafe.getUnsafe(); static final int CLASSFILE_VERSION = 52; static final String METHOD_NAME = "concat"; @@ -862,7 +859,7 @@ cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_PUBLIC + ACC_FINAL + ACC_SYNTHETIC, - className, // Unsafe.defineAnonymousClass would append an unique ID + className, null, "java/lang/Object", null @@ -1144,9 +1141,10 @@ byte[] classBytes = cw.toByteArray(); try { - Lookup innerClassLookup = lookup.defineClassAsLookupNoCheck(classBytes, HIDDEN_NESTMATE); + // may use @ForceInline; use internal defineClassAsLookup + Lookup innerClassLookup = lookup.defineClassAsLookup(className, classBytes, HIDDEN_CLASS|ACCESS_VM_ANNOTATIONS, true, null); Class<?> innerClass = innerClassLookup.lookupClass(); - dumpIfEnabled(innerClass.getName(), classBytes); + dumpIfEnabled(className, classBytes); return innerClassLookup.findStatic(innerClass, METHOD_NAME, args); } catch (Exception e) { dumpIfEnabled(className + "$$FAILED", classBytes); @@ -1270,8 +1268,8 @@ * computation on MethodHandle combinators. The computation is built with * public MethodHandle APIs, resolved from a public Lookup sequence, and * ends up calling the public StringBuilder API. Therefore, this strategy - * does not use any private API at all, even the Unsafe.defineAnonymousClass, - * since everything is handled under cover by java.lang.invoke APIs. + * does not use any private API at all since everything is handled under + * cover by java.lang.invoke APIs. * * <p><b>{@link Strategy#MH_SB_SIZED_EXACT}: "MethodHandles StringBuilder, * sized exactly".</b> @@ -1283,7 +1281,6 @@ * private String API. */ private static final class MethodHandleStringBuilderStrategy { - private MethodHandleStringBuilderStrategy() { // no instantiation } @@ -1461,6 +1458,8 @@ return sum; } + private static final Lookup MHSBS_LOOKUP = lookup(); + private static final ConcurrentMap<Integer, MethodHandle> SUMMERS; // This one is deliberately non-lambdified to optimize startup time: @@ -1474,9 +1473,9 @@ // unroll some initial sizes. Class<?>[] cls = new Class<?>[cnt]; Arrays.fill(cls, int.class); - return lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, cls); + return lookupStatic(MHSBS_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, cls); } else { - return lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, int.class, int[].class) + return lookupStatic(MHSBS_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, int.class, int[].class) .asCollector(int[].class, cnt - 1); } } @@ -1491,8 +1490,8 @@ STRING_LENGTH = lookupVirtual(publicLookup, String.class, "length", int.class); BUILDER_TO_STRING = lookupVirtual(publicLookup, StringBuilder.class, "toString", String.class); if (DEBUG) { - BUILDER_TO_STRING_CHECKED = lookupStatic(MethodHandles.Lookup.IMPL_LOOKUP, - MethodHandleStringBuilderStrategy.class, "toStringChecked", String.class, StringBuilder.class); + BUILDER_TO_STRING_CHECKED = lookupStatic(MHSBS_LOOKUP, MethodHandleStringBuilderStrategy.class, + "toStringChecked", String.class, StringBuilder.class); } else { BUILDER_TO_STRING_CHECKED = null; } @@ -1516,8 +1515,6 @@ * that requires porting if there are private JDK changes occur. */ private static final class MethodHandleInlineCopyStrategy { - static final Unsafe UNSAFE = Unsafe.getUnsafe(); - private MethodHandleInlineCopyStrategy() { // no instantiation } @@ -1736,8 +1733,9 @@ private static final Function<Class<?>, MethodHandle> PREPEND = new Function<>() { @Override public MethodHandle apply(Class<?> c) { - return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", long.class, long.class, byte[].class, - String.class, Wrapper.asPrimitiveType(c), String.class); + return JLA.stringConcatHelper("prepend", + methodType(long.class, long.class, byte[].class, + String.class, Wrapper.asPrimitiveType(c), String.class)); } }; @@ -1745,8 +1743,7 @@ private static final Function<Class<?>, MethodHandle> MIX = new Function<>() { @Override public MethodHandle apply(Class<?> c) { - return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mix", long.class, long.class, - Wrapper.asPrimitiveType(c)); + return JLA.stringConcatHelper("mix", methodType(long.class, long.class, Wrapper.asPrimitiveType(c))); } }; @@ -1759,7 +1756,7 @@ static { try { - MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", long.class); + MethodHandle initCoder = JLA.stringConcatHelper("initialCoder", methodType(long.class)); INITIAL_CODER = (long) initCoder.invoke(); } catch (Throwable e) { throw new AssertionError(e); @@ -1768,9 +1765,9 @@ PREPENDERS = new ConcurrentHashMap<>(); MIXERS = new ConcurrentHashMap<>(); - SIMPLE = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "simpleConcat", String.class, Object.class, Object.class); - NEW_STRING = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newString", String.class, byte[].class, long.class); - NEW_ARRAY = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newArray", byte[].class, long.class); + SIMPLE = JLA.stringConcatHelper("simpleConcat", methodType(String.class, Object.class, Object.class)); + NEW_STRING = JLA.stringConcatHelper("newString", methodType(String.class, byte[].class, long.class)); + NEW_ARRAY = JLA.stringConcatHelper( "newArray", methodType(byte[].class, long.class)); } } @@ -1784,7 +1781,7 @@ } private static final MethodHandle OBJECT_INSTANCE = - lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "stringOf", String.class, Object.class); + JLA.stringConcatHelper("stringOf", methodType(String.class, Object.class)); private static class FloatStringifiers { private static final MethodHandle FLOAT_INSTANCE =
--- a/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java Wed Jun 26 22:16:31 2019 -0700 +++ b/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java Thu Aug 29 19:32:19 2019 -0700 @@ -292,13 +292,26 @@ throw new IllegalCallerException(); // should not happen } + Module callerModule = caller.getModule(); + Module declaringModule = declaringClass.getModule(); + if (declaringClass.isHiddenClass()) { + if (callerModule == Object.class.getModule()) return true; + if (throwExceptionIfDenied) { + // not accessible + String msg = "Unable to make "; + if (this instanceof Field) + msg += "field "; + msg += this + " accessible"; + InaccessibleObjectException e = new InaccessibleObjectException(msg); + if (printStackTraceWhenAccessFails()) { + e.printStackTrace(System.err); + } + throw e; + } return false; } - Module callerModule = caller.getModule(); - Module declaringModule = declaringClass.getModule(); - if (callerModule == declaringModule) return true; if (callerModule == Object.class.getModule()) return true; if (!declaringModule.isNamed()) return true;
--- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java Wed Jun 26 22:16:31 2019 -0700 +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java Thu Aug 29 19:32:19 2019 -0700 @@ -26,6 +26,8 @@ package jdk.internal.access; import java.lang.annotation.Annotation; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; import java.lang.module.ModuleDescriptor; import java.lang.reflect.Executable; import java.lang.reflect.Method; @@ -324,4 +326,9 @@ * @param cause set t's cause to new value */ void setCause(Throwable t, Throwable cause); + + /** + * Get a method handle of string concat helper method + */ + MethodHandle stringConcatHelper(String name, MethodType methodType); }
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java Wed Jun 26 22:16:31 2019 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java Thu Aug 29 19:32:19 2019 -0700 @@ -25,7 +25,7 @@ package jdk.nashorn.internal.runtime; -import static jdk.internal.org.objectweb.asm.Opcodes.V1_7; +import static jdk.internal.org.objectweb.asm.Opcodes.*; import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS; import static jdk.nashorn.internal.codegen.CompilerConstants.CREATE_PROGRAM_FUNCTION; import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE; @@ -41,6 +41,7 @@ import java.io.PrintWriter; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup.ClassOptions; import java.lang.invoke.MethodType; import java.lang.invoke.SwitchPoint; import java.lang.ref.ReferenceQueue; @@ -67,7 +68,6 @@ import java.security.ProtectionDomain; import java.util.Collection; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; @@ -84,10 +84,12 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import javax.script.ScriptEngine; + import jdk.dynalink.DynamicLinker; import jdk.internal.org.objectweb.asm.ClassReader; import jdk.internal.org.objectweb.asm.ClassWriter; -import jdk.internal.org.objectweb.asm.Opcodes; +import jdk.internal.org.objectweb.asm.FieldVisitor; +import jdk.internal.org.objectweb.asm.MethodVisitor; import jdk.internal.org.objectweb.asm.util.CheckClassAdapter; import jdk.nashorn.api.scripting.ClassFilter; import jdk.nashorn.api.scripting.ScriptObjectMirror; @@ -108,7 +110,6 @@ import jdk.nashorn.internal.runtime.logging.Logger; import jdk.nashorn.internal.runtime.options.LoggingOption.LoggerInfo; import jdk.nashorn.internal.runtime.options.Options; -import jdk.internal.misc.Unsafe; /** * This class manages the global state of execution. Context is immutable. @@ -318,21 +319,38 @@ private final WeakValueCache<CodeSource, Class<?>> anonymousHostClasses = new WeakValueCache<>(); private static final class AnonymousContextCodeInstaller extends ContextCodeInstaller { - private static final Unsafe UNSAFE = Unsafe.getUnsafe(); private static final String ANONYMOUS_HOST_CLASS_NAME = Compiler.SCRIPTS_PACKAGE.replace('/', '.') + ".AnonymousHost"; private static final byte[] ANONYMOUS_HOST_CLASS_BYTES = getAnonymousHostClassBytes(); - private final Class<?> hostClass; + private final MethodHandles.Lookup hostLookup; private AnonymousContextCodeInstaller(final Context context, final CodeSource codeSource, final Class<?> hostClass) { super(context, codeSource); - this.hostClass = hostClass; + this.hostLookup = (MethodHandles.Lookup)staticFieldValue(hostClass, "LOOKUP"); + } + + private static Object staticFieldValue(Class<?> c, String name) { + try { + return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { + @Override + public Object run() throws Exception { + Field f = c.getDeclaredField(name); + return f.get(null); + } + }); + } catch (PrivilegedActionException e) { + throw new InternalError(e.getCause()); + } } @Override public Class<?> install(final String className, final byte[] bytecode) { - ANONYMOUS_INSTALLED_SCRIPT_COUNT.increment(); - return UNSAFE.defineAnonymousClass(hostClass, bytecode, null); + try { + ANONYMOUS_INSTALLED_SCRIPT_COUNT.increment(); + return hostLookup.defineHiddenClass(bytecode, false, ClassOptions.WEAK); + } catch (IllegalAccessException e) { + throw new InternalError(e); + } } @Override @@ -350,8 +368,27 @@ } private static byte[] getAnonymousHostClassBytes() { + // Workaround: define a host class in a non-exported package. + // This should be replaced when there is a mechanism to define + // a hidden class in a given package of a given module. final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); - cw.visit(V1_7, Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT, ANONYMOUS_HOST_CLASS_NAME.replace('.', '/'), null, "java/lang/Object", null); + final String cn = ANONYMOUS_HOST_CLASS_NAME.replace('.', '/'); + cw.visit(V13, ACC_PUBLIC|ACC_INTERFACE |ACC_ABSTRACT, cn, null, "java/lang/Object", null); + { + FieldVisitor fv = cw.visitField(ACC_PUBLIC|ACC_STATIC|ACC_FINAL, "LOOKUP", + "Ljava/lang/invoke/MethodHandles$Lookup;", null, null); + fv.visitEnd(); + } + { + MethodVisitor mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null); + mv.visitCode(); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/invoke/MethodHandles", "lookup", + "()Ljava/lang/invoke/MethodHandles$Lookup;", false); + mv.visitFieldInsn(PUTSTATIC, cn, "LOOKUP", "Ljava/lang/invoke/MethodHandles$Lookup;"); + mv.visitInsn(RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } cw.visitEnd(); return cw.toByteArray(); }
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptLoader.java Wed Jun 26 22:16:31 2019 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptLoader.java Thu Aug 29 19:32:19 2019 -0700 @@ -74,7 +74,8 @@ .requires("java.logging") .requires(NASHORN_MODULE.getName()) .requires(structMod.getName()) - .packages(Set.of(SCRIPTS_PKG)); + .packages(Set.of(SCRIPTS_PKG)) + .exports(SCRIPTS_PKG, Set.of(NASHORN_MODULE.getName())); if (Context.javaSqlFound) { builder.requires("java.sql");
--- a/src/jdk.unsupported/share/classes/sun/misc/Unsafe.java Wed Jun 26 22:16:31 2019 -0700 +++ b/src/jdk.unsupported/share/classes/sun/misc/Unsafe.java Thu Aug 29 19:32:19 2019 -0700 @@ -822,9 +822,8 @@ * <li>InterfaceMethodRef: (NYI) a method handle to invoke on that call site's arguments * </ul> * - * @deprecated Use the {@link java.lang.invoke.MethodHandles.Lookup#defineClass(byte[], MethodHandles.Lookup.ClassProperty...)} - * and {@link java.lang.invoke.MethodHandles.Lookup#defineClassWithClassData(byte[], Object, MethodHandles.Lookup.ClassProperty...)} - * methods instead. + * @deprecated Use the {@link java.lang.invoke.MethodHandles.Lookup#defineHiddenClass(byte[], boolean, MethodHandles.Lookup.ClassOptions...)} + * method. * * @param hostClass context for linkage, access control, protection domain, and class loader * @param data bytes of a class file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/jtreg/compiler/cha/ObjectHashCode.java Thu Aug 29 19:32:19 2019 -0700 @@ -0,0 +1,115 @@ +/* + * 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. + */ + +package compiler.cha; + +import static compiler.cha.StrengthReduceInterfaceCall.*; + +public class ObjectHashCode extends ATest<ObjectHashCode.I> { + public ObjectHashCode() { + super(I.class, C.class); + } + + interface J {} + interface I extends J {} + + static class C implements I {} + + interface K1 extends I {} + interface K2 extends I { int hashCode(); } // K2.hC() ABSTRACT + // interface K3 extends I { default int hashCode() { return CORRECT; } // K2.hC() DEFAULT + + static class D implements I { public int hashCode() { return super.hashCode(); }} + + static class DJ1 implements J {} + static class DJ2 implements J { public int hashCode() { return super.hashCode(); }} + + @Override + public Object test(I i) { + return ObjectHashCodeHelper.test(i); /* invokeinterface I.hashCode() */ + } + + @TestCase + public void testMono() { + // 0. Trigger compilation of a monomorphic call site + compile(monomophic()); // C1 <: C <: intf I <: intf J <: Object.hashCode() + assertCompiled(); + + // Dependency: none + + call(new C() { public int hashCode() { return super.hashCode(); }}); // Cn.hC <: C.hC <: intf I + assertCompiled(); + } + + @TestCase + public void testBi() { + // 0. Trigger compilation of a bimorphic call site + compile(bimorphic()); // C1 <: C <: intf I <: intf J <: Object.toString() + assertCompiled(); + + // Dependency: none + + call(new C() { public int hashCode() { return super.hashCode(); }}); // Cn.hC <: C.hC <: intf I + assertCompiled(); + } + + @TestCase + public void testMega() { + // 0. Trigger compilation of a megamorphic call site + compile(megamorphic()); // C1,C2,C3 <: C <: intf I <: intf J <: Object.hashCode() + assertCompiled(); + + // Dependency: none + + // No dependency - no invalidation + repeat(100, () -> call(new C(){})); // Cn <: C <: intf I + assertCompiled(); + + initialize(K1.class, // intf K1 <: intf I <: intf J + K2.class, // intf K2.hC ABSTRACT <: intf I <: intf J + DJ1.class, // DJ1 <: intf J + DJ2.class); // DJ2.hC <: intf J + assertCompiled(); + + initialize(D.class); // D.hC <: intf I <: intf J + assertCompiled(); + + call(new C() { public int hashCode() { return super.hashCode(); }}); // Cn.hC <: C.hC <: intf I + assertCompiled(); + } + + @Override + public void checkInvalidReceiver() { + shouldThrow(IncompatibleClassChangeError.class, () -> { + I o = (I) unsafeCastMH(I.class).invokeExact(new Object()); // unrelated + test(o); + }); + assertCompiled(); + + shouldThrow(IncompatibleClassChangeError.class, () -> { + I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J() {}); // super interface + test(j); + }); + assertCompiled(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/jtreg/compiler/cha/ObjectToString.java Thu Aug 29 19:32:19 2019 -0700 @@ -0,0 +1,118 @@ +/* + * 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. + */ + +package compiler.cha; + +import static compiler.cha.StrengthReduceInterfaceCall.*; + +public class ObjectToString extends ATest<ObjectToString.I> { + public ObjectToString() { + super(I.class, C.class); + } + + interface J { String toString(); } + interface I extends J {} + + static class C implements I {} + + interface K1 extends I {} + interface K2 extends I { String toString(); } // K2.tS() ABSTRACT + // interface K3 extends I { default String toString() { return "K3"; } // K2.tS() DEFAULT + + static class D implements I { public String toString() { return "D"; }} + + static class DJ1 implements J {} + static class DJ2 implements J { public String toString() { return "DJ2"; }} + + @Override + public Object test(I i) { return ObjectToStringHelper.test(i); /* invokeinterface I.toString() */ } + + @TestCase + public void testMono() { + // 0. Trigger compilation of a monomorphic call site + compile(monomophic()); // C1 <: C <: intf I <: intf J <: Object.toString() + assertCompiled(); + + // Dependency: none + + call(new C() { public String toString() { return "Cn"; }}); // Cn.tS <: C.tS <: intf I + assertCompiled(); + } + + @TestCase + public void testBi() { + // 0. Trigger compilation of a bimorphic call site + compile(bimorphic()); // C1 <: C <: intf I <: intf J <: Object.toString() + assertCompiled(); + + // Dependency: none + + call(new C() { public String toString() { return "Cn"; }}); // Cn.tS <: C.tS <: intf I + assertCompiled(); + } + + @TestCase + public void testMega() { + // 0. Trigger compilation of a megamorphic call site + compile(megamorphic()); // C1,C2,C3 <: C <: intf I <: intf J <: Object.toString() + assertCompiled(); + + // Dependency: none + // compiler.cha.StrengthReduceInterfaceCall$ObjectToString::test (5 bytes) + // @ 1 compiler.cha.StrengthReduceInterfaceCall$ObjectToStringHelper::test (7 bytes) inline (hot) + // @ 1 java.lang.Object::toString (36 bytes) virtual call + + // No dependency - no invalidation + repeat(100, () -> call(new C(){})); // Cn <: C <: intf I + assertCompiled(); + + initialize(K1.class, // intf K1 <: intf I <: intf J + K2.class, // intf K2.tS ABSTRACT <: intf I <: intf J + DJ1.class, // DJ1 <: intf J + DJ2.class); // DJ2.tS <: intf J + assertCompiled(); + + initialize(D.class); // D.tS <: intf I <: intf J + assertCompiled(); + + call(new C() { public String toString() { return "Cn"; }}); // Cn.tS <: C.tS <: intf I + assertCompiled(); + } + + @Override + public void checkInvalidReceiver() { + shouldThrow(IncompatibleClassChangeError.class, () -> { + I o = (I) unsafeCastMH(I.class).invokeExact(new Object()); // unrelated + test(o); + }); + assertCompiled(); + + shouldThrow(IncompatibleClassChangeError.class, () -> { + I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J() {}); // super interface + test(j); + }); + assertCompiled(); + } +} + +
--- a/test/hotspot/jtreg/compiler/cha/StrengthReduceInterfaceCall.java Wed Jun 26 22:16:31 2019 -0700 +++ b/test/hotspot/jtreg/compiler/cha/StrengthReduceInterfaceCall.java Thu Aug 29 19:32:19 2019 -0700 @@ -79,614 +79,6 @@ run(ThreeLevelDefaultHierarchy1.class); } - public static class ObjectToString extends ATest<ObjectToString.I> { - public ObjectToString() { super(I.class, C.class); } - - interface J { String toString(); } - interface I extends J {} - - static class C implements I {} - - interface K1 extends I {} - interface K2 extends I { String toString(); } // K2.tS() ABSTRACT - // interface K3 extends I { default String toString() { return "K3"; } // K2.tS() DEFAULT - - static class D implements I { public String toString() { return "D"; }} - - static class DJ1 implements J {} - static class DJ2 implements J { public String toString() { return "DJ2"; }} - - @Override - public Object test(I i) { return ObjectToStringHelper.test(i); /* invokeinterface I.toString() */ } - - @TestCase - public void testMono() { - // 0. Trigger compilation of a monomorphic call site - compile(monomophic()); // C1 <: C <: intf I <: intf J <: Object.toString() - assertCompiled(); - - // Dependency: none - - call(new C() { public String toString() { return "Cn"; }}); // Cn.tS <: C.tS <: intf I - assertCompiled(); - } - - @TestCase - public void testBi() { - // 0. Trigger compilation of a bimorphic call site - compile(bimorphic()); // C1 <: C <: intf I <: intf J <: Object.toString() - assertCompiled(); - - // Dependency: none - - call(new C() { public String toString() { return "Cn"; }}); // Cn.tS <: C.tS <: intf I - assertCompiled(); - } - - @TestCase - public void testMega() { - // 0. Trigger compilation of a megamorphic call site - compile(megamorphic()); // C1,C2,C3 <: C <: intf I <: intf J <: Object.toString() - assertCompiled(); - - // Dependency: none - // compiler.cha.StrengthReduceInterfaceCall$ObjectToString::test (5 bytes) - // @ 1 compiler.cha.StrengthReduceInterfaceCall$ObjectToStringHelper::test (7 bytes) inline (hot) - // @ 1 java.lang.Object::toString (36 bytes) virtual call - - // No dependency - no invalidation - repeat(100, () -> call(new C(){})); // Cn <: C <: intf I - assertCompiled(); - - initialize(K1.class, // intf K1 <: intf I <: intf J - K2.class, // intf K2.tS ABSTRACT <: intf I <: intf J - DJ1.class, // DJ1 <: intf J - DJ2.class); // DJ2.tS <: intf J - assertCompiled(); - - initialize(D.class); // D.tS <: intf I <: intf J - assertCompiled(); - - call(new C() { public String toString() { return "Cn"; }}); // Cn.tS <: C.tS <: intf I - assertCompiled(); - } - - @Override - public void checkInvalidReceiver() { - shouldThrow(IncompatibleClassChangeError.class, () -> { - I o = (I) unsafeCastMH(I.class).invokeExact(new Object()); // unrelated - test(o); - }); - assertCompiled(); - - shouldThrow(IncompatibleClassChangeError.class, () -> { - I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J() {}); // super interface - test(j); - }); - assertCompiled(); - } - } - - public static class ObjectHashCode extends ATest<ObjectHashCode.I> { - public ObjectHashCode() { super(I.class, C.class); } - - interface J {} - interface I extends J {} - - static class C implements I {} - - interface K1 extends I {} - interface K2 extends I { int hashCode(); } // K2.hC() ABSTRACT - // interface K3 extends I { default int hashCode() { return CORRECT; } // K2.hC() DEFAULT - - static class D implements I { public int hashCode() { return super.hashCode(); }} - - static class DJ1 implements J {} - static class DJ2 implements J { public int hashCode() { return super.hashCode(); }} - - @Override - public Object test(I i) { - return ObjectHashCodeHelper.test(i); /* invokeinterface I.hashCode() */ - } - - @TestCase - public void testMono() { - // 0. Trigger compilation of a monomorphic call site - compile(monomophic()); // C1 <: C <: intf I <: intf J <: Object.hashCode() - assertCompiled(); - - // Dependency: none - - call(new C() { public int hashCode() { return super.hashCode(); }}); // Cn.hC <: C.hC <: intf I - assertCompiled(); - } - - @TestCase - public void testBi() { - // 0. Trigger compilation of a bimorphic call site - compile(bimorphic()); // C1 <: C <: intf I <: intf J <: Object.toString() - assertCompiled(); - - // Dependency: none - - call(new C() { public int hashCode() { return super.hashCode(); }}); // Cn.hC <: C.hC <: intf I - assertCompiled(); - } - - @TestCase - public void testMega() { - // 0. Trigger compilation of a megamorphic call site - compile(megamorphic()); // C1,C2,C3 <: C <: intf I <: intf J <: Object.hashCode() - assertCompiled(); - - // Dependency: none - - // No dependency - no invalidation - repeat(100, () -> call(new C(){})); // Cn <: C <: intf I - assertCompiled(); - - initialize(K1.class, // intf K1 <: intf I <: intf J - K2.class, // intf K2.hC ABSTRACT <: intf I <: intf J - DJ1.class, // DJ1 <: intf J - DJ2.class); // DJ2.hC <: intf J - assertCompiled(); - - initialize(D.class); // D.hC <: intf I <: intf J - assertCompiled(); - - call(new C() { public int hashCode() { return super.hashCode(); }}); // Cn.hC <: C.hC <: intf I - assertCompiled(); - } - - @Override - public void checkInvalidReceiver() { - shouldThrow(IncompatibleClassChangeError.class, () -> { - I o = (I) unsafeCastMH(I.class).invokeExact(new Object()); // unrelated - test(o); - }); - assertCompiled(); - - shouldThrow(IncompatibleClassChangeError.class, () -> { - I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J() {}); // super interface - test(j); - }); - assertCompiled(); - } - } - - public static class TwoLevelHierarchyLinear extends ATest<TwoLevelHierarchyLinear.I> { - public TwoLevelHierarchyLinear() { super(I.class, C.class); } - - interface J { default Object m() { return WRONG; } } - - interface I extends J { Object m(); } - static class C implements I { public Object m() { return CORRECT; }} - - interface K1 extends I {} - interface K2 extends I { Object m(); } - interface K3 extends I { default Object m() { return WRONG; }} - - static class D implements I { public Object m() { return WRONG; }} - - static class DJ1 implements J {} - static class DJ2 implements J { public Object m() { return WRONG; }} - - @DontInline - public Object test(I i) { - return i.m(); - } - - @TestCase - public void testMega1() { - // 0. Trigger compilation of a megamorphic call site - compile(megamorphic()); // C1,C2,C3 <: C.m <: intf I.m ABSTRACT <: intf J.m ABSTRACT - assertCompiled(); - - // Dependency: type = unique_concrete_method, context = I, method = C.m - - checkInvalidReceiver(); // ensure proper type check is preserved - - // 1. No deoptimization/invalidation on not-yet-seen receiver - repeat(100, () -> call(new C(){})); // Cn <: C.m <: intf I.m ABSTRACT <: intf J.m DEFAULT - assertCompiled(); - - // 2. No dependency invalidation on class loading of unrelated classes: different context - initialize(K1.class, // intf K1 <: intf I.m ABSTRACT <: intf J.m DEFAULT - K2.class, // intf K2.m ABSTRACT <: intf I.m ABSTRACT <: intf J.m DEFAULT - DJ1.class, // DJ1 <: intf J.m DEFAULT - DJ2.class); // DJ2.m <: intf J.m DEFAULT - assertCompiled(); - - // 3. Dependency invalidation on D <: I - initialize(D.class); // D.m <: intf I.m ABSTRACT <: intf J.m DEFAULT - assertNotCompiled(); - - // 4. Recompilation: no inlining, no dependencies - compile(megamorphic()); - call(new C() { public Object m() { return CORRECT; }}); // Cn.m <: C.m <: intf I.m ABSTRACT <: intf J.m DEFAULT - assertCompiled(); - - checkInvalidReceiver(); // ensure proper type check on receiver is preserved - } - - @TestCase - public void testMega2() { - // 0. Trigger compilation of a megamorphic call site - compile(megamorphic()); // C1,C2,C3 <: C.m <: intf I.m ABSTRACT <: intf J.m DEFAULT - assertCompiled(); - - // Dependency: type = unique_concrete_method, context = I, method = C.m - - checkInvalidReceiver(); // ensure proper type check on receiver is preserved - - // 1. Dependency invalidation - initialize(K3.class); // intf K3.m DEFAULT <: intf I.m ABSTRACT <: intf J.m DEFAULT - assertNotCompiled(); - - // 2. Recompilation: still inlines - // FIXME: no default method support in CHA yet - compile(megamorphic()); - call(new K3() { public Object m() { return CORRECT; }}); // K3n.m <: intf K3.m DEFAULT <: intf I.m ABSTRACT <: intf J.m ABSTRACT - assertNotCompiled(); - - // 3. Recompilation: no inlining, no dependencies - compile(megamorphic()); - call(new K3() { public Object m() { return CORRECT; }}); // Kn.m <: intf K3.m DEFAULT <: intf I.m ABSTRACT <: intf J.m DEFAULT - assertCompiled(); - - checkInvalidReceiver(); // ensure proper type check on receiver is preserved - } - - @Override - public void checkInvalidReceiver() { - shouldThrow(IncompatibleClassChangeError.class, () -> { - I o = (I) unsafeCastMH(I.class).invokeExact(new Object()); // unrelated - test(o); - }); - assertCompiled(); - - shouldThrow(IncompatibleClassChangeError.class, () -> { - I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J() {}); // super interface - test(j); - }); - assertCompiled(); - } - } - - public static class ThreeLevelHierarchyLinear extends ATest<ThreeLevelHierarchyLinear.I> { - public ThreeLevelHierarchyLinear() { super(I.class, C.class); } - - interface J { Object m(); } - interface I extends J {} - - interface K1 extends I {} - interface K2 extends I { Object m(); } - interface K3 extends I { default Object m() { return WRONG; }} - - static class C implements I { public Object m() { return CORRECT; }} - - static class DI implements I { public Object m() { return WRONG; }} - static class DJ implements J { public Object m() { return WRONG; }} - - @DontInline - public Object test(I i) { - return i.m(); // I <: J.m ABSTRACT - } - - @TestCase - public void testMega1() { - // 0. Trigger compilation of a megamorphic call site - compile(megamorphic()); // C1,C2,C3 <: C.m <: intf I <: intf J.m ABSTRACT - assertCompiled(); - - // Dependency: type = unique_concrete_method, context = I, method = C.m - - checkInvalidReceiver(); // ensure proper type check on receiver is preserved - - // 1. No deoptimization/invalidation on not-yet-seen receiver - repeat(100, () -> call(new C(){})); // Cn <: C.m <: intf I - assertCompiled(); // No deopt on not-yet-seen receiver - - // 2. No dependency invalidation: different context - initialize(DJ.class, // DJ.m <: intf J.m ABSTRACT - K1.class, // intf K1 <: intf I <: intf J.m ABSTRACT - K2.class); // intf K2.m ABSTRACT <: intf I <: intf J.m ABSTRACT - assertCompiled(); - - // 3. Dependency invalidation: DI.m <: I - initialize(DI.class); // DI.m <: intf I <: intf J.m ABSTRACT - assertNotCompiled(); - - // 4. Recompilation w/o a dependency - compile(megamorphic()); - call(new C() { public Object m() { return CORRECT; }}); // Cn.m <: C.m <: intf I <: intf J.m ABSTRACT - assertCompiled(); // no dependency - - checkInvalidReceiver(); // ensure proper type check on receiver is preserved - } - - @TestCase - public void testMega2() { - compile(megamorphic()); // C1,C2,C3 <: C.m <: intf I <: intf J.m ABSTRACT - assertCompiled(); - - // Dependency: type = unique_concrete_method, context = I, method = C.m - - checkInvalidReceiver(); // ensure proper type check on receiver is preserved - - // Dependency invalidation - initialize(K3.class); // intf K3.m DEFAULT <: intf I; - assertNotCompiled(); // FIXME: default methods in sub-interfaces shouldn't be taken into account by CHA - - // Recompilation with a dependency - compile(megamorphic()); - assertCompiled(); - - // Dependency: type = unique_concrete_method, context = I, method = C.m - - checkInvalidReceiver(); // ensure proper type check on receiver is preserved - - call(new K3() { public Object m() { return CORRECT; }}); // Kn.m <: K3.m DEFAULT <: intf I <: intf J.m ABSTRACT - assertNotCompiled(); - - // Recompilation w/o a dependency - compile(megamorphic()); - // Dependency: none - checkInvalidReceiver(); // ensure proper type check on receiver is preserved - call(new C() { public Object m() { return CORRECT; }}); // Cn.m <: C.m <: intf I <: intf J.m ABSTRACT - assertCompiled(); - } - - @Override - public void checkInvalidReceiver() { - shouldThrow(IncompatibleClassChangeError.class, () -> { - I o = (I) unsafeCastMH(I.class).invokeExact(new Object()); // unrelated - test(o); - }); - assertCompiled(); - - shouldThrow(IncompatibleClassChangeError.class, () -> { - I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J() { public Object m() { return WRONG; }}); // super interface - test(j); - }); - assertCompiled(); - } - } - - public static class ThreeLevelHierarchyAbstractVsDefault extends ATest<ThreeLevelHierarchyAbstractVsDefault.I> { - public ThreeLevelHierarchyAbstractVsDefault() { super(I.class, C.class); } - - interface J1 { default Object m() { return WRONG; } } // intf J1.m DEFAULT - interface J2 extends J1 { Object m(); } // intf J2.m ABSTRACT <: intf J1 - interface I extends J1, J2 {} // intf I.m OVERPASS <: intf J1,J2 - - static class C implements I { public Object m() { return CORRECT; }} - - @DontInline - public Object test(I i) { - return i.m(); // intf I.m OVERPASS - } - - static class DI implements I { public Object m() { return WRONG; }} - - static class DJ11 implements J1 {} - static class DJ12 implements J1 { public Object m() { return WRONG; }} - - static class DJ2 implements J2 { public Object m() { return WRONG; }} - - interface K11 extends J1 {} - interface K12 extends J1 { Object m(); } - interface K13 extends J1 { default Object m() { return WRONG; }} - interface K21 extends J2 {} - interface K22 extends J2 { Object m(); } - interface K23 extends J2 { default Object m() { return WRONG; }} - - - public void testMega1() { - // 0. Trigger compilation of megamorphic call site - compile(megamorphic()); // C1,C2,C3 <: C.m <: intf I.m OVERPASS <: intf J2.m ABSTRACT <: intf J1.m DEFAULT - assertCompiled(); - - // Dependency: type = unique_concrete_method, context = I, method = C.m - - checkInvalidReceiver(); // ensure proper type check on receiver is preserved - - // 1. No deopt/invalidation on not-yet-seen receiver - repeat(100, () -> call(new C(){})); // Cn <: C.m <: intf I.m OVERPASS <: intf J2.m ABSTRACT <: intf J1.m DEFAULT - assertCompiled(); - - // 2. No dependency invalidation: different context - initialize(K11.class, K12.class, K13.class, - K21.class, K22.class, K23.class); - - // 3. Dependency invalidation: Cn.m <: C <: I - call(new C() { public Object m() { return CORRECT; }}); // Cn.m <: C.m <: intf I.m OVERPASS <: intf J2.m ABSTRACT <: intf J1.m DEFAULT - assertNotCompiled(); - - // 4. Recompilation w/o a dependency - compile(megamorphic()); - call(new C() { public Object m() { return CORRECT; }}); - assertCompiled(); // no inlining - - checkInvalidReceiver(); // ensure proper type check on receiver is preserved - } - - public void testMega2() { - // 0. Trigger compilation of a megamorphic call site - compile(megamorphic()); - assertCompiled(); - - // Dependency: type = unique_concrete_method, context = I, method = C.m - - checkInvalidReceiver(); // ensure proper type check on receiver is preserved - - // 1. No dependency invalidation: different context - initialize(DJ11.class, - DJ12.class, - DJ2.class); - assertCompiled(); - - // 2. Dependency invalidation: DI.m <: I - initialize(DI.class); - assertNotCompiled(); - - // 3. Recompilation w/o a dependency - compile(megamorphic()); - call(new C() { public Object m() { return CORRECT; }}); - assertCompiled(); // no inlining - - checkInvalidReceiver(); // ensure proper type check on receiver is preserved - } - - @Override - public void checkInvalidReceiver() { - shouldThrow(IncompatibleClassChangeError.class, () -> { - I o = (I) unsafeCastMH(I.class).invokeExact(new Object()); // unrelated - test(o); - }); - assertCompiled(); - - shouldThrow(IncompatibleClassChangeError.class, () -> { - I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J1() {}); // super interface - test(j); - }); - assertCompiled(); - - shouldThrow(IncompatibleClassChangeError.class, () -> { - I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J2() { public Object m() { return WRONG; }}); // super interface - test(j); - }); - assertCompiled(); - } - } - - public static class ThreeLevelDefaultHierarchy extends ATest<ThreeLevelDefaultHierarchy.I> { - public ThreeLevelDefaultHierarchy() { super(I.class, C.class); } - - interface J { default Object m() { return WRONG; }} - interface I extends J {} - - static class C implements I { public Object m() { return CORRECT; }} - - interface K1 extends I {} - interface K2 extends I { Object m(); } - interface K3 extends I { default Object m() { return WRONG; }} - - static class DI implements I { public Object m() { return WRONG; }} - static class DJ implements J { public Object m() { return WRONG; }} - - @DontInline - public Object test(I i) { - return i.m(); // no inlining since J.m is a default method - } - - @TestCase - public void testMega() { - // 0. Trigger compilation of a megamorphic call site - compile(megamorphic()); // C1,C2,C3 <: C.m <: intf I <: intf J.m ABSTRACT - assertCompiled(); - - // Dependency: none - - checkInvalidReceiver(); // ensure proper type check on receiver is preserved - - // 1. No deoptimization/invalidation on not-yet-seen receiver - repeat(100, () -> call(new C() {})); - assertCompiled(); - - // 2. No dependency and no inlining - initialize(DJ.class, // DJ.m <: intf J.m ABSTRACT - DI.class, // DI.m <: intf I <: intf J.m ABSTRACT - K1.class, // intf K1 <: intf I <: intf J.m ABSTRACT - K2.class); // intf K2.m ABSTRACT <: intf I <: intf J.m ABSTRACT - assertCompiled(); - } - - @Override - public void checkInvalidReceiver() { - shouldThrow(IncompatibleClassChangeError.class, () -> { - I o = (I) unsafeCastMH(I.class).invokeExact(new Object()); // unrelated - test(o); - }); - assertCompiled(); - - shouldThrow(IncompatibleClassChangeError.class, () -> { - I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J() {}); // super interface - test(j); - }); - assertCompiled(); - } - } - - public static class ThreeLevelDefaultHierarchy1 extends ATest<ThreeLevelDefaultHierarchy1.I> { - public ThreeLevelDefaultHierarchy1() { super(I.class, C.class); } - - interface J1 { Object m();} - interface J2 extends J1 { default Object m() { return WRONG; } } - interface I extends J1, J2 {} - - static class C implements I { public Object m() { return CORRECT; }} - - interface K1 extends I {} - interface K2 extends I { Object m(); } - interface K3 extends I { default Object m() { return WRONG; }} - - static class DI implements I { public Object m() { return WRONG; }} - static class DJ1 implements J1 { public Object m() { return WRONG; }} - static class DJ2 implements J2 { public Object m() { return WRONG; }} - - @DontInline - public Object test(I i) { - return i.m(); // no inlining since J.m is a default method - } - - @TestCase - public void testMega() { - // 0. Trigger compilation of a megamorphic call site - compile(megamorphic()); - assertCompiled(); - - // Dependency: none - - checkInvalidReceiver(); // ensure proper type check on receiver is preserved - - // 1. No deoptimization/invalidation on not-yet-seen receiver - repeat(100, () -> call(new C() {})); - assertCompiled(); - - // 2. No dependency, no inlining - // CHA doesn't support default methods yet. - initialize(DJ1.class, - DJ2.class, - DI.class, - K1.class, - K2.class, - K3.class); - assertCompiled(); - } - - @Override - public void checkInvalidReceiver() { - shouldThrow(IncompatibleClassChangeError.class, () -> { - I o = (I) unsafeCastMH(I.class).invokeExact(new Object()); // unrelated - test(o); - }); - assertCompiled(); - - shouldThrow(IncompatibleClassChangeError.class, () -> { - I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J1() { public Object m() { return WRONG; } }); // super interface - test(j); - }); - assertCompiled(); - - shouldThrow(IncompatibleClassChangeError.class, () -> { - I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J2() {}); // super interface - test(j); - }); - assertCompiled(); - } - } - /* =========================================================== */ interface Action {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/jtreg/compiler/cha/ThreeLevelDefaultHierarchy.java Thu Aug 29 19:32:19 2019 -0700 @@ -0,0 +1,88 @@ +/* + * 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. + */ + +package compiler.cha; + +import jdk.internal.vm.annotation.DontInline; +import static compiler.cha.StrengthReduceInterfaceCall.*; + +public class ThreeLevelDefaultHierarchy extends ATest<ThreeLevelDefaultHierarchy.I> { + public ThreeLevelDefaultHierarchy() { + super(I.class, C.class); + } + + interface J { default Object m() { return WRONG; }} + interface I extends J {} + + static class C implements I { public Object m() { return CORRECT; }} + + interface K1 extends I {} + interface K2 extends I { Object m(); } + interface K3 extends I { default Object m() { return WRONG; }} + + static class DI implements I { public Object m() { return WRONG; }} + static class DJ implements J { public Object m() { return WRONG; }} + + @DontInline + public Object test(I i) { + return i.m(); // no inlining since J.m is a default method + } + + @TestCase + public void testMega() { + // 0. Trigger compilation of a megamorphic call site + compile(megamorphic()); // C1,C2,C3 <: C.m <: intf I <: intf J.m ABSTRACT + assertCompiled(); + + // Dependency: none + + checkInvalidReceiver(); // ensure proper type check on receiver is preserved + + // 1. No deoptimization/invalidation on not-yet-seen receiver + repeat(100, () -> call(new C() {})); + assertCompiled(); + + // 2. No dependency and no inlining + initialize(DJ.class, // DJ.m <: intf J.m ABSTRACT + DI.class, // DI.m <: intf I <: intf J.m ABSTRACT + K1.class, // intf K1 <: intf I <: intf J.m ABSTRACT + K2.class); // intf K2.m ABSTRACT <: intf I <: intf J.m ABSTRACT + assertCompiled(); + } + + @Override + public void checkInvalidReceiver() { + shouldThrow(IncompatibleClassChangeError.class, () -> { + I o = (I) unsafeCastMH(I.class).invokeExact(new Object()); // unrelated + test(o); + }); + assertCompiled(); + + shouldThrow(IncompatibleClassChangeError.class, () -> { + I j = (I) unsafeCastMH(I.class).invokeExact((Object) new J() { + }); // super interface + test(j); + }); + assertCompiled(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/jtreg/compiler/cha/ThreeLevelDefaultHierarchy1.java Thu Aug 29 19:32:19 2019 -0700 @@ -0,0 +1,103 @@ +/* + * 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. + */ + +package compiler.cha; + +import jdk.internal.vm.annotation.DontInline; +import static compiler.cha.StrengthReduceInterfaceCall.*; + +public class ThreeLevelDefaultHierarchy1 extends ATest<ThreeLevelDefaultHierarchy1.I> { + public ThreeLevelDefaultHierarchy1() { + super(I.class, C.class); + } + + interface J1 { Object m();} + interface J2 extends J1 { default Object m() { return WRONG; } } + interface I extends J1, J2 {} + + static class C implements I { public Object m() { return CORRECT; }} + + interface K1 extends I {} + interface K2 extends I { Object m(); } + interface K3 extends I { default Object m() { return WRONG; }} + + static class DI implements I { public Object m() { return WRONG; }} + static class DJ1 implements J1 { public Object m() { return WRONG; }} + static class DJ2 implements J2 { public Object m() { return WRONG; }} + + @DontInline + public Object test(I i) { + return i.m(); // no inlining since J.m is a default method + } + + @TestCase + public void testMega() { + // 0. Trigger compilation of a megamorphic call site + compile(megamorphic()); + assertCompiled(); + + // Dependency: none + + checkInvalidReceiver(); // ensure proper type check on receiver is preserved + + // 1. No deoptimization/invalidation on not-yet-seen receiver + repeat(100, () -> call(new C() {})); + assertCompiled(); + + // 2. No dependency, no inlining + // CHA doesn't support default methods yet. + initialize(DJ1.class, + DJ2.class, + DI.class, + K1.class, + K2.class, + K3.class); + assertCompiled(); + } + + @Override + public void checkInvalidReceiver() { + shouldThrow(IncompatibleClassChangeError.class, () -> { + I o = (I) unsafeCastMH(I.class).invokeExact(new Object()); // unrelated + test(o); + }); + assertCompiled(); + + shouldThrow(IncompatibleClassChangeError.class, () -> { + I j = (I) unsafeCastMH(I.class).invokeExact((Object) new J1() { + public Object m() { + return WRONG; + } + }); // super interface + test(j); + }); + assertCompiled(); + + shouldThrow(IncompatibleClassChangeError.class, () -> { + I j = (I) unsafeCastMH(I.class).invokeExact((Object) new J2() { + }); // super interface + test(j); + }); + assertCompiled(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/jtreg/compiler/cha/ThreeLevelHierarchyAbstractVsDefault.java Thu Aug 29 19:32:19 2019 -0700 @@ -0,0 +1,141 @@ +/* + * 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. + */ + +package compiler.cha; + +import jdk.internal.vm.annotation.DontInline; +import static compiler.cha.StrengthReduceInterfaceCall.*; + +public class ThreeLevelHierarchyAbstractVsDefault extends ATest<ThreeLevelHierarchyAbstractVsDefault.I> { + public ThreeLevelHierarchyAbstractVsDefault() { + super(I.class, C.class); + } + + interface J1 { default Object m() { return WRONG; } } // intf J1.m DEFAULT + interface J2 extends J1 { Object m(); } // intf J2.m ABSTRACT <: intf J1 + interface I extends J1, J2 {} // intf I.m OVERPASS <: intf J1,J2 + + static class C implements I { public Object m() { return CORRECT; }} + + @DontInline + public Object test(I i) { + return i.m(); // intf I.m OVERPASS + } + + static class DI implements I { public Object m() { return WRONG; }} + + static class DJ11 implements J1 {} + static class DJ12 implements J1 { public Object m() { return WRONG; }} + + static class DJ2 implements J2 { public Object m() { return WRONG; }} + + interface K11 extends J1 {} + interface K12 extends J1 { Object m(); } + interface K13 extends J1 { default Object m() { return WRONG; }} + interface K21 extends J2 {} + interface K22 extends J2 { Object m(); } + interface K23 extends J2 { default Object m() { return WRONG; }} + + public void testMega1() { + // 0. Trigger compilation of megamorphic call site + compile(megamorphic()); // C1,C2,C3 <: C.m <: intf I.m OVERPASS <: intf J2.m ABSTRACT <: intf J1.m DEFAULT + assertCompiled(); + + // Dependency: type = unique_concrete_method, context = I, method = C.m + + checkInvalidReceiver(); // ensure proper type check on receiver is preserved + + // 1. No deopt/invalidation on not-yet-seen receiver + repeat(100, () -> call(new C(){})); // Cn <: C.m <: intf I.m OVERPASS <: intf J2.m ABSTRACT <: intf J1.m DEFAULT + assertCompiled(); + + // 2. No dependency invalidation: different context + initialize(K11.class, K12.class, K13.class, + K21.class, K22.class, K23.class); + + // 3. Dependency invalidation: Cn.m <: C <: I + call(new C() { public Object m() { return CORRECT; }}); // Cn.m <: C.m <: intf I.m OVERPASS <: intf J2.m ABSTRACT <: intf J1.m DEFAULT + assertNotCompiled(); + + // 4. Recompilation w/o a dependency + compile(megamorphic()); + call(new C() { public Object m() { return CORRECT; }}); + assertCompiled(); // no inlining + + checkInvalidReceiver(); // ensure proper type check on receiver is preserved + } + + public void testMega2() { + // 0. Trigger compilation of a megamorphic call site + compile(megamorphic()); + assertCompiled(); + + // Dependency: type = unique_concrete_method, context = I, method = C.m + + checkInvalidReceiver(); // ensure proper type check on receiver is preserved + + // 1. No dependency invalidation: different context + initialize(DJ11.class, + DJ12.class, + DJ2.class); + assertCompiled(); + + // 2. Dependency invalidation: DI.m <: I + initialize(DI.class); + assertNotCompiled(); + + // 3. Recompilation w/o a dependency + compile(megamorphic()); + call(new C() { public Object m() { return CORRECT; }}); + assertCompiled(); // no inlining + + checkInvalidReceiver(); // ensure proper type check on receiver is preserved + } + + @Override + public void checkInvalidReceiver() { + shouldThrow(IncompatibleClassChangeError.class, () -> { + I o = (I) unsafeCastMH(I.class).invokeExact(new Object()); // unrelated + test(o); + }); + assertCompiled(); + + shouldThrow(IncompatibleClassChangeError.class, () -> { + I j = (I) unsafeCastMH(I.class).invokeExact((Object) new J1() { + }); // super interface + test(j); + }); + assertCompiled(); + + shouldThrow(IncompatibleClassChangeError.class, () -> { + I j = (I) unsafeCastMH(I.class).invokeExact((Object) new J2() { + public Object m() { + return WRONG; + } + }); // super interface + test(j); + }); + assertCompiled(); + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/jtreg/compiler/cha/ThreeLevelHierarchyLinear.java Thu Aug 29 19:32:19 2019 -0700 @@ -0,0 +1,134 @@ +/* + * 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. + */ + +package compiler.cha; + +import jdk.internal.vm.annotation.DontInline; +import static compiler.cha.StrengthReduceInterfaceCall.*; + +public class ThreeLevelHierarchyLinear extends ATest<ThreeLevelHierarchyLinear.I> { + public ThreeLevelHierarchyLinear() { + super(I.class, C.class); + } + + interface J { Object m(); } + interface I extends J {} + + interface K1 extends I {} + interface K2 extends I { Object m(); } + interface K3 extends I { default Object m() { return WRONG; }} + + static class C implements I { public Object m() { return CORRECT; }} + + static class DI implements I { public Object m() { return WRONG; }} + static class DJ implements J { public Object m() { return WRONG; }} + + @DontInline + public Object test(I i) { + return i.m(); // I <: J.m ABSTRACT + } + + @TestCase + public void testMega1() { + // 0. Trigger compilation of a megamorphic call site + compile(megamorphic()); // C1,C2,C3 <: C.m <: intf I <: intf J.m ABSTRACT + assertCompiled(); + + // Dependency: type = unique_concrete_method, context = I, method = C.m + + checkInvalidReceiver(); // ensure proper type check on receiver is preserved + + // 1. No deoptimization/invalidation on not-yet-seen receiver + repeat(100, () -> call(new C(){})); // Cn <: C.m <: intf I + assertCompiled(); // No deopt on not-yet-seen receiver + + // 2. No dependency invalidation: different context + initialize(DJ.class, // DJ.m <: intf J.m ABSTRACT + K1.class, // intf K1 <: intf I <: intf J.m ABSTRACT + K2.class); // intf K2.m ABSTRACT <: intf I <: intf J.m ABSTRACT + assertCompiled(); + + // 3. Dependency invalidation: DI.m <: I + initialize(DI.class); // DI.m <: intf I <: intf J.m ABSTRACT + assertNotCompiled(); + + // 4. Recompilation w/o a dependency + compile(megamorphic()); + call(new C() { public Object m() { return CORRECT; }}); // Cn.m <: C.m <: intf I <: intf J.m ABSTRACT + + assertCompiled(); // no dependency + + checkInvalidReceiver(); // ensure proper type check on receiver is preserved + } + + @TestCase + public void testMega2() { + compile(megamorphic()); // C1,C2,C3 <: C.m <: intf I <: intf J.m ABSTRACT + assertCompiled(); + + // Dependency: type = unique_concrete_method, context = I, method = C.m + + checkInvalidReceiver(); // ensure proper type check on receiver is preserved + + // Dependency invalidation + initialize(K3.class); // intf K3.m DEFAULT <: intf I; + assertNotCompiled(); // FIXME: default methods in sub-interfaces shouldn't be taken into account by CHA + + // Recompilation with a dependency + compile(megamorphic()); + assertCompiled(); + + // Dependency: type = unique_concrete_method, context = I, method = C.m + + checkInvalidReceiver(); // ensure proper type check on receiver is preserved + + call(new K3() { public Object m() { return CORRECT; }}); // Kn.m <: K3.m DEFAULT <: intf I <: intf J.m ABSTRACT + assertNotCompiled(); + + // Recompilation w/o a dependency + compile(megamorphic()); + // Dependency: none + checkInvalidReceiver(); // ensure proper type check on receiver is preserved + call(new C() { public Object m() { return CORRECT; }}); // Cn.m <: C.m <: intf I <: intf J.m ABSTRACT + assertCompiled(); + } + + @Override + public void checkInvalidReceiver() { + shouldThrow(IncompatibleClassChangeError.class, () -> { + I o = (I) unsafeCastMH(I.class).invokeExact(new Object()); // unrelated + test(o); + }); + assertCompiled(); + + shouldThrow(IncompatibleClassChangeError.class, () -> { + I j = (I) unsafeCastMH(I.class).invokeExact((Object) new J() { + public Object m() { + return WRONG; + } + }); // super interface + test(j); + }); + assertCompiled(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/jtreg/compiler/cha/TwoLevelHierarchyLinear.java Thu Aug 29 19:32:19 2019 -0700 @@ -0,0 +1,129 @@ +/* + * 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. + */ + +package compiler.cha; + +import jdk.internal.vm.annotation.DontInline; +import static compiler.cha.StrengthReduceInterfaceCall.*; + +public class TwoLevelHierarchyLinear extends ATest<TwoLevelHierarchyLinear.I> { + public TwoLevelHierarchyLinear() { + super(I.class, C.class); + } + + interface J { default Object m() { return WRONG; } } + + interface I extends J { Object m(); } + static class C implements I { public Object m() { return CORRECT; }} + + interface K1 extends I {} + interface K2 extends I { Object m(); } + interface K3 extends I { default Object m() { return WRONG; }} + + static class D implements I { public Object m() { return WRONG; }} + + static class DJ1 implements J {} + static class DJ2 implements J { public Object m() { return WRONG; }} + + @DontInline + public Object test(I i) { + return i.m(); + } + + @TestCase + public void testMega1() { + // 0. Trigger compilation of a megamorphic call site + compile(megamorphic()); // C1,C2,C3 <: C.m <: intf I.m ABSTRACT <: intf J.m ABSTRACT + assertCompiled(); + + // Dependency: type = unique_concrete_method, context = I, method = C.m + + checkInvalidReceiver(); // ensure proper type check is preserved + + // 1. No deoptimization/invalidation on not-yet-seen receiver + repeat(100, () -> call(new C(){})); // Cn <: C.m <: intf I.m ABSTRACT <: intf J.m DEFAULT + assertCompiled(); + + // 2. No dependency invalidation on class loading of unrelated classes: different context + initialize(K1.class, // intf K1 <: intf I.m ABSTRACT <: intf J.m DEFAULT + K2.class, // intf K2.m ABSTRACT <: intf I.m ABSTRACT <: intf J.m DEFAULT + DJ1.class, // DJ1 <: intf J.m DEFAULT + DJ2.class); // DJ2.m <: intf J.m DEFAULT + assertCompiled(); + + // 3. Dependency invalidation on D <: I + initialize(D.class); // D.m <: intf I.m ABSTRACT <: intf J.m DEFAULT + assertNotCompiled(); + + // 4. Recompilation: no inlining, no dependencies + compile(megamorphic()); + call(new C() { public Object m() { return CORRECT; }}); // Cn.m <: C.m <: intf I.m ABSTRACT <: intf J.m DEFAULT + assertCompiled(); + + checkInvalidReceiver(); // ensure proper type check on receiver is preserved + } + + @TestCase + public void testMega2() { + // 0. Trigger compilation of a megamorphic call site + compile(megamorphic()); // C1,C2,C3 <: C.m <: intf I.m ABSTRACT <: intf J.m DEFAULT + assertCompiled(); + + // Dependency: type = unique_concrete_method, context = I, method = C.m + + checkInvalidReceiver(); // ensure proper type check on receiver is preserved + + // 1. Dependency invalidation + initialize(K3.class); // intf K3.m DEFAULT <: intf I.m ABSTRACT <: intf J.m DEFAULT + assertNotCompiled(); + + // 2. Recompilation: still inlines + // FIXME: no default method support in CHA yet + compile(megamorphic()); + call(new K3() { public Object m() { return CORRECT; }}); // K3n.m <: intf K3.m DEFAULT <: intf I.m ABSTRACT <: intf J.m ABSTRACT + assertNotCompiled(); + + // 3. Recompilation: no inlining, no dependencies + compile(megamorphic()); + call(new K3() { public Object m() { return CORRECT; }}); // Kn.m <: intf K3.m DEFAULT <: intf I.m ABSTRACT <: intf J.m DEFAULT + assertCompiled(); + + checkInvalidReceiver(); // ensure proper type check on receiver is preserved + } + + @Override + public void checkInvalidReceiver() { + shouldThrow(IncompatibleClassChangeError.class, () -> { + I o = (I) unsafeCastMH(I.class).invokeExact(new Object()); // unrelated + test(o); + }); + assertCompiled(); + + shouldThrow(IncompatibleClassChangeError.class, () -> { + I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J() {}); // super interface + test(j); + }); + assertCompiled(); + } +} +
--- a/test/hotspot/jtreg/runtime/Nestmates/membership/TestDynamicNestmateMembership.java Wed Jun 26 22:16:31 2019 -0700 +++ b/test/hotspot/jtreg/runtime/Nestmates/membership/TestDynamicNestmateMembership.java Thu Aug 29 19:32:19 2019 -0700 @@ -32,9 +32,8 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.Path; -import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; -import static java.lang.invoke.MethodHandles.Lookup.ClassProperty.*; +import static java.lang.invoke.MethodHandles.Lookup.ClassOptions.*; /* package */ class DynamicNestmate { } @@ -98,13 +97,13 @@ // Try to inject a class that is already part of another nest static void test_alreadyNestMember() { String name = "StaticHost$StaticMember"; - inject(name, ClassFormatError.class); + inject(name, IllegalArgumentException.class); } // Try to inject a class that is already a nest host static void test_alreadyNestHost() { String name = "StaticHost"; - inject(name, ClassFormatError.class); + inject(name, IllegalArgumentException.class); } // Try to inject a class that is in another package @@ -125,7 +124,7 @@ try { byte[] bytes = getBytesForClass(name); - Class<?> nestmate = lookup.defineClass(bytes, NESTMATE); + Class<?> nestmate = lookup.defineHiddenClass(bytes, false, NESTMATE); if (ex != null) { throw new RuntimeException(action + " was expected to throw " + ex.getSimpleName());
--- a/test/jdk/java/lang/StackWalker/VerifyStackTrace.java Wed Jun 26 22:16:31 2019 -0700 +++ b/test/jdk/java/lang/StackWalker/VerifyStackTrace.java Thu Aug 29 19:32:19 2019 -0700 @@ -202,7 +202,6 @@ // out before comparing. We also erase the hash-like names of // synthetic frames introduced by lambdas & method handles return produced.replaceAll(":[1-9][0-9]*\\)", ":00)") - .replaceAll("\\$\\$/0x", "/0x") .replaceAll("/0x[0-9a-f]+\\.run", "/xxxxxxxx.run") .replaceAll("/0x[0-9a-f]+\\.invoke", "/xxxxxxxx.invoke") // LFs may or may not be pre-generated, making frames differ
--- a/test/jdk/java/lang/invoke/defineClass/DefineClassTest.java Wed Jun 26 22:16:31 2019 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,210 +0,0 @@ -/* - * Copyright (c) 2018, 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 - * @library /test/lib - * @modules java.base/jdk.internal.org.objectweb.asm - * @build DefineClassTest - * @run testng/othervm DefineClassTest - */ - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodHandles.Lookup; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.stream.Stream; - -import jdk.internal.org.objectweb.asm.*; -import org.testng.annotations.Test; - -import static java.lang.invoke.MethodHandles.Lookup.ClassProperty.*; -import static java.lang.invoke.MethodHandles.Lookup.PRIVATE; -import static java.lang.invoke.MethodType.*; - -import static jdk.internal.org.objectweb.asm.Opcodes.*; -import static org.testng.Assert.*; - -public class DefineClassTest { - private static final byte[] bytes = classBytes("Injected"); - private static byte[] classBytes(String classname) { - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES); - MethodVisitor mv; - - cw.visit(V12, ACC_FINAL, classname, null, "java/lang/Object", null); - - { - mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); - mv.visitCode(); - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); - mv.visitInsn(RETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); - } - { - // access a private member of the nest host class - mv = cw.visitMethod(ACC_PUBLIC, "test", "(LDefineClassTest;)I", null, null); - mv.visitCode(); - mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, "DefineClassTest", "privMethod", "()I"); - mv.visitInsn(IRETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); - } - cw.visitEnd(); - - return cw.toByteArray(); - } - - private int privMethod() { return 1234; } - - @Test - public void defineNestMate() throws Throwable { - // define a nestmate - Lookup lookup = MethodHandles.lookup(); - Class<?> c = lookup.defineClass(bytes, NESTMATE); - assertTrue(c.getNestHost() == DefineClassTest.class); - assertTrue(c == Class.forName("Injected")); - - // invoke int test(DefineClassTest o) - int x = testInjectedClass(c); - assertTrue(x == privMethod()); - - // dynamic nestmate is not listed in the return array of getNestMembers - assertTrue(Stream.of(c.getNestHost().getNestMembers()).noneMatch(k -> k == c)); - assertTrue(c.isNestmateOf(DefineClassTest.class)); - } - - @Test - public void defineNestMateAsLookup() throws Throwable { - // define a nestmate - byte[] bytes = classBytes("Injected2"); - Lookup lookup = MethodHandles.lookup().defineClassAsLookup(bytes, NESTMATE); - Class<?> c = lookup.lookupClass(); - assertTrue(c.getNestHost() == DefineClassTest.class); - assertTrue(c == Class.forName("Injected2")); - - // invoke int test(DefineClassTest o) via MethodHandle - MethodHandle ctor = lookup.findConstructor(c, methodType(void.class)); - MethodHandle mh = lookup.findVirtual(c, "test", methodType(int.class, DefineClassTest.class)); - int x = (int)mh.bindTo(ctor.invoke()).invokeExact( this); - // int x =(int)mh.invoke(c.newInstance(), this); - assertTrue(x == privMethod()); - - // dynamic nestmate is not listed in the return array of getNestMembers - assertTrue(Stream.of(c.getNestHost().getNestMembers()).noneMatch(k -> k == c)); - assertTrue(c.isNestmateOf(DefineClassTest.class)); - } - - @Test - public void defineHiddenClass() throws Throwable { - // define a hidden class - Lookup lookup = MethodHandles.lookup(); - Class<?> c = lookup.defineClass(bytes, NESTMATE, HIDDEN); - System.out.println(c.getName()); - assertTrue(c.getNestHost() == DefineClassTest.class); - assertTrue(c.isHiddenClass()); - - // invoke int test(DefineClassTest o) - int x = testInjectedClass(c); - assertTrue(x == privMethod()); - - // dynamic nestmate is not listed in the return array of getNestMembers - assertTrue(Stream.of(c.getNestHost().getNestMembers()).noneMatch(k -> k == c)); - assertTrue(c.isNestmateOf(DefineClassTest.class)); - } - - @Test - public void defineHiddenClassAsLookup() throws Throwable { - // define a hidden class - Lookup lookup = MethodHandles.lookup().defineClassAsLookup(bytes, NESTMATE, HIDDEN); - Class<?> c = lookup.lookupClass(); - System.out.println(c.getName()); - assertTrue(c.getNestHost() == DefineClassTest.class); - assertTrue(c.isHiddenClass()); - - // invoke int test(DefineClassTest o) via MethodHandle - MethodHandle ctor = lookup.findConstructor(c, methodType(void.class)); - MethodHandle mh = lookup.findVirtual(c, "test", methodType(int.class, DefineClassTest.class)); - int x = (int)mh.bindTo(ctor.invoke()).invokeExact( this); - assertTrue(x == privMethod()); - - // dynamic nestmate is not listed in the return array of getNestMembers - assertTrue(Stream.of(c.getNestHost().getNestMembers()).noneMatch(k -> k == c)); - assertTrue(c.isNestmateOf(DefineClassTest.class)); - } - - @Test - public void defineWeakClass() throws Throwable { - // define a weak class - Class<?> c = MethodHandles.lookup().defineClass(bytes, WEAK); - System.out.println(c.getName()); - assertTrue(c.getNestHost() == c); - assertTrue(c.isHiddenClass()); - } - - @Test(expectedExceptions = IllegalAccessException.class) - public void definePackageAccessClass() throws Throwable { - Lookup lookup = MethodHandles.lookup().dropLookupMode(Lookup.PRIVATE); - Class<?> c = lookup.defineClass(bytes, HIDDEN); - } - - @Test(expectedExceptions = IllegalAccessException.class) - public void noPrivateLookupAccess() throws Throwable { - Lookup lookup = MethodHandles.lookup().dropLookupMode(Lookup.PRIVATE); - lookup.defineClass(bytes, NESTMATE); - } - - @Test(expectedExceptions = IllegalAccessException.class) - public void teleportToNestmate() throws Throwable { - Class<?> c = MethodHandles.lookup().defineClass(bytes, NESTMATE, HIDDEN); - assertTrue(c.getNestHost() == DefineClassTest.class); - assertTrue(c.isHiddenClass()); - - // Teleport to a nestmate - Lookup lookup = MethodHandles.lookup().in(c); - assertTrue((lookup.lookupModes() & PRIVATE) == 0); - // fail to define a nestmate - lookup.defineClass(bytes, NESTMATE, HIDDEN); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void notSamePackage() throws Throwable { - MethodHandles.lookup().defineClass(classBytes("p/Injected"), NESTMATE); - } - - /* - * invoke int test(DefineClassTest o) method defined in the injected class - */ - private int testInjectedClass(Class<?> c) throws Throwable { - try { - Method m = c.getMethod("test", DefineClassTest.class); - return (int) m.invoke(c.newInstance(), this); - } catch (InvocationTargetException e) { - throw e.getCause(); - } - } -}
--- a/test/jdk/java/lang/invoke/defineClass/DefineClassWithClassData.java Wed Jun 26 22:16:31 2019 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,230 +0,0 @@ -/* - * Copyright (c) 2018, 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 - * @library /test/lib - * @modules java.base/jdk.internal.org.objectweb.asm - * @build DefineClassWithClassData - * @run testng/othervm DefineClassWithClassData - */ - -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodHandles.Lookup; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.List; -import java.util.stream.Stream; - -import jdk.internal.org.objectweb.asm.*; -import org.testng.annotations.Test; - -import static java.lang.invoke.MethodHandles.Lookup.ClassProperty.*; -import static java.lang.invoke.MethodHandles.Lookup.PRIVATE; -import static jdk.internal.org.objectweb.asm.Opcodes.*; -import static org.testng.Assert.*; - -public class DefineClassWithClassData { - private static final byte[] T_CLASS_BYTES = ClassByteBuilder.classBytes("T"); - private static final byte[] T2_CLASS_BYTES = ClassByteBuilder.classBytes("T2"); - - private int privMethod() { return 1234; } - - /* - * invoke int test(DefineClassWithClassData o) method defined in the injected class - */ - private int testInjectedClass(Class<?> c) throws Throwable { - try { - Method m = c.getMethod("test", DefineClassWithClassData.class); - return (int) m.invoke(c.newInstance(), this); - } catch (InvocationTargetException e) { - throw e.getCause(); - } - } - - /* - * Returns the value of the static final "data" field in the injected class - */ - private Object injectedData(Class<?> c) throws Throwable { - return c.getDeclaredField("data").get(null); - } - - private static final List<String> classData = List.of("nestmate", "classdata"); - - @Test - public void defineNestMate() throws Throwable { - // define a nestmate - Lookup lookup = MethodHandles.lookup().defineClassWithClassData(T_CLASS_BYTES, classData, NESTMATE, HIDDEN); - Class<?> c = lookup.lookupClass(); - assertTrue(c.getNestHost() == DefineClassWithClassData.class); - assertEquals(classData, injectedData(c)); - - // invoke int test(DefineClassWithClassData o) - int x = testInjectedClass(c); - assertTrue(x == privMethod()); - - // dynamic nestmate is not listed in the return array of getNestMembers - assertTrue(Stream.of(c.getNestHost().getNestMembers()).noneMatch(k -> k == c)); - assertTrue(c.isNestmateOf(DefineClassWithClassData.class)); - } - - @Test - public void defineHiddenClass() throws Throwable { - // define a hidden class - Lookup lookup = MethodHandles.lookup().defineClassWithClassData(T_CLASS_BYTES, classData, NESTMATE, HIDDEN); - Class<?> c = lookup.lookupClass(); - assertTrue(c.getNestHost() == DefineClassWithClassData.class); - assertTrue(c.isHiddenClass()); - assertEquals(classData, injectedData(c)); - - // invoke int test(DefineClassWithClassData o) - int x = testInjectedClass(c); - assertTrue(x == privMethod()); - - // dynamic nestmate is not listed in the return array of getNestMembers - assertTrue(Stream.of(c.getNestHost().getNestMembers()).noneMatch(k -> k == c)); - assertTrue(c.isNestmateOf(DefineClassWithClassData.class)); - } - - @Test - public void defineWeakClass() throws Throwable { - // define a weak class - Lookup lookup = MethodHandles.lookup().defineClassWithClassData(T_CLASS_BYTES, classData, WEAK); - Class<?> c = lookup.lookupClass(); - assertTrue(c.getNestHost() == c); - assertTrue(c.isHiddenClass()); - } - - @Test(expectedExceptions = IllegalAccessException.class) - public void noPrivateLookupAccess() throws Throwable { - Lookup lookup = MethodHandles.lookup().dropLookupMode(Lookup.PRIVATE); - lookup.defineClassWithClassData(T2_CLASS_BYTES, classData, NESTMATE, HIDDEN); - } - - @Test(expectedExceptions = IllegalAccessException.class) - public void teleportToNestmate() throws Throwable { - Lookup lookup = MethodHandles.lookup() - .defineClassWithClassData(T_CLASS_BYTES, classData, NESTMATE, HIDDEN); - Class<?> c = lookup.lookupClass(); - assertTrue(c.getNestHost() == DefineClassWithClassData.class); - assertEquals(classData, injectedData(c)); - assertTrue(c.isHiddenClass()); - - // Teleport to a nestmate - Lookup lookup2 = MethodHandles.lookup().in(c); - assertTrue((lookup2.lookupModes() & PRIVATE) == 0); - // fail to define a nestmate - lookup2.defineClassWithClassData(T2_CLASS_BYTES, classData, NESTMATE, HIDDEN); - } - - static class ClassByteBuilder { - static final String OBJECT_CLS = "java/lang/Object"; - static final String STRING_CLS = "java/lang/String"; - static final String LIST_CLS = "java/util/List"; - static final String MH_CLS = "java/lang/invoke/MethodHandles"; - static final String LOOKUP_CLS = "java/lang/invoke/MethodHandles$Lookup"; - static final String LOOKUP_SIG = "Ljava/lang/invoke/MethodHandles$Lookup;"; - static final String LIST_SIG = "Ljava/util/List;"; - - static byte[] classBytes(String classname) { - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); - MethodVisitor mv; - FieldVisitor fv; - - String hostClassName = DefineClassWithClassData.class.getName(); - - cw.visit(V11, ACC_FINAL, classname, null, OBJECT_CLS, null); - { - fv = cw.visitField(ACC_STATIC | ACC_FINAL, "data", LIST_SIG, null, null); - fv.visitEnd(); - } - { - mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null); - mv.visitCode(); - - // set up try block - Label lTryBlockStart = new Label(); - Label lTryBlockEnd = new Label(); - Label lCatchBlockStart = new Label(); - Label lCatchBlockEnd = new Label(); - mv.visitTryCatchBlock(lTryBlockStart, lTryBlockEnd, lCatchBlockStart, "java/lang/IllegalAccessException"); - - mv.visitLabel(lTryBlockStart); - mv.visitMethodInsn(INVOKESTATIC, MH_CLS, "lookup", "()" + LOOKUP_SIG); - mv.visitLdcInsn(Type.getType(List.class)); - mv.visitMethodInsn(INVOKEVIRTUAL, LOOKUP_CLS, "classData", "(Ljava/lang/Class;)Ljava/lang/Object;"); - mv.visitTypeInsn(CHECKCAST, LIST_CLS); - mv.visitFieldInsn(PUTSTATIC, classname, "data", LIST_SIG); - mv.visitLabel(lTryBlockEnd); - mv.visitJumpInsn(GOTO, lCatchBlockEnd); - - mv.visitLabel(lCatchBlockStart); - mv.visitVarInsn(ASTORE, 0); - mv.visitTypeInsn(NEW, "java/lang/Error"); - mv.visitInsn(DUP); - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Error", "<init>", "(Ljava/lang/Throwable;)V"); - mv.visitInsn(ATHROW); - mv.visitLabel(lCatchBlockEnd); - mv.visitInsn(RETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); - } - - { - mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); - mv.visitCode(); - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKESPECIAL, OBJECT_CLS, "<init>", "()V"); - mv.visitInsn(RETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); - } - { - mv = cw.visitMethod(ACC_PUBLIC, "test", "(L" + hostClassName + ";)I", null, null); - mv.visitCode(); - mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, hostClassName, "privMethod", "()I"); - mv.visitInsn(IRETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); - } - - { - mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "printData", "()V", null, null); - mv.visitCode(); - mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); - mv.visitFieldInsn(GETSTATIC, classname, "data", LIST_SIG); - mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V"); - mv.visitInsn(RETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); - } - cw.visitEnd(); - return cw.toByteArray(); - } - } -} - -
--- a/test/jdk/java/lang/invoke/defineClass/DefineNonFindableClass.java Wed Jun 26 22:16:31 2019 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,131 +0,0 @@ -/* - * 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 - * @modules java.base/jdk.internal.misc - * @library /test/lib - * @build jdk.test.lib.JDKToolLauncher - * jdk.test.lib.process.ProcessTools - * jdk.test.lib.Utils - * @run main/othervm -Xverify:remote DefineNonFindableClass - */ - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodHandles.Lookup; -import static java.lang.invoke.MethodHandles.Lookup.ClassProperty.*; -import static java.lang.invoke.MethodHandles.Lookup.PRIVATE; -import java.nio.file.Path; -import java.nio.file.Paths; -import jdk.internal.misc.Unsafe; - -import jdk.test.lib.JDKToolLauncher; -import jdk.test.lib.process.ProcessTools; -import jdk.test.lib.Utils; - -/* package-private */ interface Test { - void test(); -} - - -public class DefineNonFindableClass { - - static final Class<?> klass = DefineNonFindableClass.class; - static final Path SRC_DIR = Paths.get(Utils.TEST_SRC, "nonFindable"); - static final Path CLASSES_DIR = Paths.get(Utils.TEST_CLASSES, "nonFindable"); - - static void compileSources(String sourceFile) throws Throwable { - JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("javac"); - launcher.addToolArg("-cp") - .addToolArg(Utils.TEST_CLASSES.toString()) - .addToolArg("-d") - .addToolArg(CLASSES_DIR.toString()) - .addToolArg(Paths.get(SRC_DIR.toString(), sourceFile).toString()); - - int exitCode = ProcessTools.executeCommand(launcher.getCommand()) - .getExitValue(); - if (exitCode != 0) { - throw new RuntimeException("Compilation of the test failed. " - + "Unexpected exit code: " + exitCode); - } - } - - static byte[] readClassFile(String classFileName) throws Exception { - File classFile = new File(CLASSES_DIR + File.separator + classFileName); - try (FileInputStream in = new FileInputStream(classFile); - ByteArrayOutputStream out = new ByteArrayOutputStream()) - { - int b; - while ((b = in.read()) != -1) { - out.write(b); - } - return out.toByteArray(); - } - } - - public static void main(String[] args) throws Throwable { - compileSources("NonFindable.java"); - Lookup lookup = MethodHandles.lookup(); - byte[] bytes = readClassFile("NonFindable.class"); - Class<?> c = lookup.defineClass(bytes, NESTMATE, HIDDEN); - Test t = (Test) c.newInstance(); - t.test(); - - - // Test that a nonFindable class cannot use its own name in a field - // signature. - compileSources("NonFindableField.java"); - Lookup lookup2 = MethodHandles.lookup(); - byte[] bytes2 = readClassFile("NonFindableField.class"); - try { - Class<?> c2 = lookup2.defineClass(bytes2, NESTMATE, HIDDEN); - throw new RuntimeException( - "Expected NoClassDefFoundError exception not thrown"); - } catch (java.lang.NoClassDefFoundError e) { - if (!e.getMessage().contains("NonFindableField")) { - throw new RuntimeException( - "Unexpected NoClassDefFoundError: " + e.getMessage()); - } - } - - // Test that a nonFindable class cannot use its own name in a method - // signature. - compileSources("NonFindableMethod.java"); - Lookup lookup3 = MethodHandles.lookup(); - byte[] bytes3 = readClassFile("NonFindableMethod.class"); - try { - Class<?> c3 = lookup3.defineClass(bytes3, NESTMATE, HIDDEN); - throw new RuntimeException( - "Expected NoClassDefFoundError exception not thrown"); - } catch (java.lang.NoClassDefFoundError e) { - if (!e.getMessage().contains("NonFindableMethod")) { - throw new RuntimeException( - "Unexpected NoClassDefFoundError: " + e.getMessage()); - } - } - } - -}
--- a/test/jdk/java/lang/invoke/defineClass/nonFindable/NonFindable.java Wed Jun 26 22:16:31 2019 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,70 +0,0 @@ -/* - * 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. - */ - -/* - * The classfile for this class will be loaded directly and used to define - * a non-findable class. - */ -public class NonFindable implements Test { - - NonFindable other = null; - - private String realTest() { - Object o = other; - NonFindable local = this; - local = other; - local = (NonFindable) o; - local = new NonFindable(); - - set_other(null); - - local = getThis(); - - set_other_maybe(new Object()); - set_other_maybe(this); - return "NonFindable"; - } - - private NonFindable getThis() { - return null; - } - - private void set_other(NonFindable t) { - other = t; - } - - private void set_other_maybe(Object o) { - if (o instanceof NonFindable) { - } - } - - public void test() { - String result = realTest(); - // Make sure that the Utf8 constant pool entry for "NonFindable" is okay. - if (!result.substring(0, 7).equals("NonFind") || - !result.substring(7).equals("able")) { - throw new RuntimeException("'NonFindable string is bad: " + result); - } - - } -}
--- a/test/jdk/java/lang/invoke/defineClass/nonFindable/NonFindableField.java Wed Jun 26 22:16:31 2019 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -/* - * 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. - */ - -/* - * The classfile for this class will be used to define a non-findable - * class. The load of this class will fail because non-findable classes - * cannot user their name in field signatures. - */ -public class NonFindableField implements Test { - - NonFindableField other = null; - - private void realTest() { - other = this; // field signature test - } - - public void test() { - realTest(); - } -}
--- a/test/jdk/java/lang/invoke/defineClass/nonFindable/NonFindableMethod.java Wed Jun 26 22:16:31 2019 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +0,0 @@ -/* - * 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. - */ - -/* - * The classfile for this class will be used to define a non-findable - * class. The load of this class will fail because non-findable classes - * cannot use their names in method signatures. - */ -public class NonFindableMethod implements Test { - - private void realTest() { - NonFindableMethod local = this; - set_other(local); // method signature test - } - - private void set_other(NonFindableMethod t) { - } - - public void test() { - realTest(); - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/invoke/defineHiddenClass/BasicTest.java Thu Aug 29 19:32:19 2019 -0700 @@ -0,0 +1,221 @@ +/* + * 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 + * @library /test/lib + * @build jdk.test.lib.Utils + * jdk.test.lib.compiler.CompilerUtils + * BasicTest + * @run testng/othervm BasicTest + */ + +import java.io.File; +import java.io.IOException; +import static java.lang.invoke.MethodHandles.Lookup.ClassOptions.*; +import static java.lang.invoke.MethodHandles.lookup; + +import java.lang.reflect.InaccessibleObjectException; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.stream.Stream; + +import jdk.test.lib.compiler.CompilerUtils; + +import jdk.test.lib.Utils; + +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; +import static org.testng.Assert.*; + +/* package-private */ interface HiddenTest { + void test(); +} + +public class BasicTest { + + private static final Path SRC_DIR = Paths.get(Utils.TEST_SRC, "src"); + private static final Path CLASSES_DIR = Paths.get("classes"); + private static final Path CLASSES_10_DIR = Paths.get("classes_10"); + + @BeforeTest + static void setup() throws IOException { + if (!CompilerUtils.compile(SRC_DIR, CLASSES_DIR, false, "-cp", Utils.TEST_CLASSES)) { + throw new RuntimeException("Compilation of the test failed"); + } + } + + static void compileSources(Path sourceFile, Path dest, String... options) throws IOException { + Stream<String> ops = Stream.of("-cp", Utils.TEST_CLASSES + File.pathSeparator + CLASSES_DIR); + if (options != null && options.length > 0) { + ops = Stream.concat(ops, Arrays.stream(options)); + } + if (!CompilerUtils.compile(sourceFile, dest, ops.toArray(String[]::new))) { + throw new RuntimeException("Compilation of the test failed: " + sourceFile); + } + } + + static byte[] readClassFile(String classFileName) throws IOException { + return Files.readAllBytes(CLASSES_DIR.resolve(classFileName)); + } + + static Class<HiddenTest> defineHiddenClass(String name) throws Exception { + byte[] bytes = readClassFile(name + ".class"); + return (Class<HiddenTest>)lookup().defineHiddenClass(bytes, false, NESTMATE); + } + + @Test + public void hiddenClass() throws Throwable { + HiddenTest t = defineHiddenClass("HiddenClass").newInstance(); + t.test(); + + Class<?> c = t.getClass(); + Class<?>[] intfs = c.getInterfaces(); + assertTrue(intfs.length == 1); + assertTrue(intfs[0] == HiddenTest.class); + + // test setAccessible + checkSetAccessible(c, "realTest"); + checkSetAccessible(c, "test"); + } + + public void checkSetAccessible(Class<?> c, String name, Class<?>... ptypes) throws Exception { + Method m = c.getDeclaredMethod(name, ptypes); + assertFalse(m.trySetAccessible()); + try { + m.setAccessible(true); + } catch (InaccessibleObjectException e) { + e.printStackTrace(); + } + } + + @Test + public void testLambda() throws Throwable { + HiddenTest t = defineHiddenClass("Lambda").newInstance(); + try { + t.test(); + } catch (Error e) { + if (!e.getMessage().equals("thrown by " + t.getClass().getName())) { + throw e; + } + } + } + + @Test + public void hiddenCantReflect() throws Throwable { + HiddenTest t = defineHiddenClass("HiddenCantReflect").newInstance(); + t.test(); + + Class<?> c = t.getClass(); + Class<?>[] intfs = c.getInterfaces(); + assertTrue(intfs.length == 1); + assertTrue(intfs[0] == HiddenTest.class); + + try { + // this will cause class loading of HiddenCantReflect and fail + c.getDeclaredMethods(); + } catch (NoClassDefFoundError e) { + Throwable x = e.getCause(); + if (x == null || !(x instanceof ClassNotFoundException && x.getMessage().contains("HiddenCantReflect"))) { + throw e; + } + } + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void hiddenInterface() throws Exception { + byte[] bytes = readClassFile("HiddenInterface.class"); + Class<?> c = lookup().defineHiddenClass(bytes, false); + } + + + @Test(expectedExceptions = IllegalArgumentException.class) + public void abstractHiddenClass() throws Exception { + byte[] bytes = readClassFile("AbstractClass.class"); + Class<?> c = lookup().defineHiddenClass(bytes, false); + } + + @Test(expectedExceptions = NoClassDefFoundError.class) + public void hiddenSuperClass() throws Exception { + byte[] bytes = readClassFile("HiddenSuper.class"); + Class<?> c = lookup().defineHiddenClass(bytes, false); + } + + @Test + public void hiddenNestmates() throws Throwable { + try { + byte[] bytes = readClassFile("Outer.class"); + lookup().defineHiddenClass(bytes, false); + } catch (IllegalArgumentException e) { + if (!e.getMessage().contains("NestMembers attribute")) throw e; + } + + try { + byte[] bytes = readClassFile("Outer$Inner.class"); + lookup().defineHiddenClass(bytes, false); + } catch (IllegalArgumentException e) { + if (!e.getMessage().contains("NestHost attribute")) throw e; + } + } + + @Test + public void hiddenNestedClass() throws Throwable { + // compile with --release 10 with no NestHost and NestMembers attribute + compileSources(SRC_DIR.resolve("Outer.java"), CLASSES_10_DIR, "--release", "10"); + try { + byte[] bytes = Files.readAllBytes(CLASSES_10_DIR.resolve("Outer.class")); + lookup().defineHiddenClass(bytes, false); + } catch (IllegalArgumentException e) { + if (!e.getMessage().contains("Outer$Inner")) throw e; + } + + try { + byte[] bytes = Files.readAllBytes(CLASSES_10_DIR.resolve("Outer$Inner.class")); + lookup().defineHiddenClass(bytes, false); + } catch (IllegalArgumentException e) { + if (!e.getMessage().contains("Outer$Inner")) throw e; + } + } + + @Test + public void hiddenAnonymous() throws Throwable { + // compile with --release 10 with no NestHost and NestMembers attribute + compileSources(SRC_DIR.resolve("EnclosingClass.java"), CLASSES_10_DIR, "--release", "10"); + try { + byte[] bytes = Files.readAllBytes(CLASSES_10_DIR.resolve("EnclosingClass.class")); + lookup().defineHiddenClass(bytes, false); + } catch (IllegalArgumentException e) { + if (!e.getMessage().contains("EnclosingClass$1")) throw e; + } + + try { + byte[] bytes = Files.readAllBytes(CLASSES_10_DIR.resolve("EnclosingClass$1.class")); + lookup().defineHiddenClass(bytes, false); + } catch (IllegalArgumentException e) { + if (!e.getMessage().contains("EnclosingMethod attribute")) throw e; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/invoke/defineHiddenClass/DefineClassWithClassData.java Thu Aug 29 19:32:19 2019 -0700 @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2018, 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 + * @library /test/lib + * @modules java.base/jdk.internal.org.objectweb.asm + * @build DefineClassWithClassData + * @run testng/othervm DefineClassWithClassData + */ + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; +import java.util.stream.Stream; + +import jdk.internal.org.objectweb.asm.*; +import org.testng.annotations.Test; + +import static java.lang.invoke.MethodHandles.Lookup.ClassOptions.*; +import static java.lang.invoke.MethodHandles.Lookup.PRIVATE; +import static jdk.internal.org.objectweb.asm.Opcodes.*; +import static org.testng.Assert.*; + +public class DefineClassWithClassData { + private static final byte[] T_CLASS_BYTES = ClassByteBuilder.classBytes("T"); + private static final byte[] T2_CLASS_BYTES = ClassByteBuilder.classBytes("T2"); + + private int privMethod() { return 1234; } + + /* + * invoke int test(DefineClassWithClassData o) method defined in the injected class + */ + private int testInjectedClass(Class<?> c) throws Throwable { + try { + Method m = c.getMethod("test", DefineClassWithClassData.class); + return (int) m.invoke(c.newInstance(), this); + } catch (InvocationTargetException e) { + throw e.getCause(); + } + } + + /* + * Returns the value of the static final "data" field in the injected class + */ + private Object injectedData(Class<?> c) throws Throwable { + return c.getDeclaredField("data").get(null); + } + + private static final List<String> classData = List.of("nestmate", "classdata"); + + @Test + public void defineNestMate() throws Throwable { + // define a nestmate + Lookup lookup = MethodHandles.lookup().defineHiddenClassWithClassData(T_CLASS_BYTES, classData, NESTMATE); + Class<?> c = lookup.lookupClass(); + assertTrue(c.getNestHost() == DefineClassWithClassData.class); + assertEquals(classData, injectedData(c)); + + // invoke int test(DefineClassWithClassData o) + int x = testInjectedClass(c); + assertTrue(x == privMethod()); + + // dynamic nestmate is not listed in the return array of getNestMembers + assertTrue(Stream.of(c.getNestHost().getNestMembers()).noneMatch(k -> k == c)); + assertTrue(c.isNestmateOf(DefineClassWithClassData.class)); + } + + @Test + public void defineHiddenClass() throws Throwable { + // define a hidden class + Lookup lookup = MethodHandles.lookup().defineHiddenClassWithClassData(T_CLASS_BYTES, classData, NESTMATE); + Class<?> c = lookup.lookupClass(); + assertTrue(c.getNestHost() == DefineClassWithClassData.class); + assertTrue(c.isHiddenClass()); + assertEquals(classData, injectedData(c)); + + // invoke int test(DefineClassWithClassData o) + int x = testInjectedClass(c); + assertTrue(x == privMethod()); + + // dynamic nestmate is not listed in the return array of getNestMembers + assertTrue(Stream.of(c.getNestHost().getNestMembers()).noneMatch(k -> k == c)); + assertTrue(c.isNestmateOf(DefineClassWithClassData.class)); + } + + @Test + public void defineWeakClass() throws Throwable { + // define a weak class + Lookup lookup = MethodHandles.lookup().defineHiddenClassWithClassData(T_CLASS_BYTES, classData, WEAK); + Class<?> c = lookup.lookupClass(); + assertTrue(c.getNestHost() == c); + assertTrue(c.isHiddenClass()); + } + + @Test(expectedExceptions = IllegalAccessException.class) + public void noPrivateLookupAccess() throws Throwable { + Lookup lookup = MethodHandles.lookup().dropLookupMode(Lookup.PRIVATE); + lookup.defineHiddenClassWithClassData(T2_CLASS_BYTES, classData, NESTMATE); + } + + @Test(expectedExceptions = IllegalAccessException.class) + public void teleportToNestmate() throws Throwable { + Lookup lookup = MethodHandles.lookup() + .defineHiddenClassWithClassData(T_CLASS_BYTES, classData, NESTMATE); + Class<?> c = lookup.lookupClass(); + assertTrue(c.getNestHost() == DefineClassWithClassData.class); + assertEquals(classData, injectedData(c)); + assertTrue(c.isHiddenClass()); + + // Teleport to a nestmate + Lookup lookup2 = MethodHandles.lookup().in(c); + assertTrue((lookup2.lookupModes() & PRIVATE) == 0); + // fail to define a nestmate + lookup2.defineHiddenClassWithClassData(T2_CLASS_BYTES, classData, NESTMATE); + } + + static class ClassByteBuilder { + static final String OBJECT_CLS = "java/lang/Object"; + static final String STRING_CLS = "java/lang/String"; + static final String LIST_CLS = "java/util/List"; + static final String MH_CLS = "java/lang/invoke/MethodHandles"; + static final String LOOKUP_CLS = "java/lang/invoke/MethodHandles$Lookup"; + static final String LOOKUP_SIG = "Ljava/lang/invoke/MethodHandles$Lookup;"; + static final String LIST_SIG = "Ljava/util/List;"; + + static byte[] classBytes(String classname) { + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); + MethodVisitor mv; + FieldVisitor fv; + + String hostClassName = DefineClassWithClassData.class.getName(); + + cw.visit(V11, ACC_FINAL, classname, null, OBJECT_CLS, null); + { + fv = cw.visitField(ACC_STATIC | ACC_FINAL, "data", LIST_SIG, null, null); + fv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null); + mv.visitCode(); + + // set up try block + Label lTryBlockStart = new Label(); + Label lTryBlockEnd = new Label(); + Label lCatchBlockStart = new Label(); + Label lCatchBlockEnd = new Label(); + mv.visitTryCatchBlock(lTryBlockStart, lTryBlockEnd, lCatchBlockStart, "java/lang/IllegalAccessException"); + + mv.visitLabel(lTryBlockStart); + mv.visitMethodInsn(INVOKESTATIC, MH_CLS, "lookup", "()" + LOOKUP_SIG, false); + mv.visitLdcInsn(Type.getType(List.class)); + mv.visitMethodInsn(INVOKEVIRTUAL, LOOKUP_CLS, "classData", "(Ljava/lang/Class;)Ljava/lang/Object;", false); + mv.visitTypeInsn(CHECKCAST, LIST_CLS); + mv.visitFieldInsn(PUTSTATIC, classname, "data", LIST_SIG); + mv.visitLabel(lTryBlockEnd); + mv.visitJumpInsn(GOTO, lCatchBlockEnd); + + mv.visitLabel(lCatchBlockStart); + mv.visitVarInsn(ASTORE, 0); + mv.visitTypeInsn(NEW, "java/lang/Error"); + mv.visitInsn(DUP); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Error", "<init>", "(Ljava/lang/Throwable;)V", false); + mv.visitInsn(ATHROW); + mv.visitLabel(lCatchBlockEnd); + mv.visitInsn(RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + + { + mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, OBJECT_CLS, "<init>", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "test", "(L" + hostClassName + ";)I", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, hostClassName, "privMethod", "()I", false); + mv.visitInsn(IRETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + + { + mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "printData", "()V", null, null); + mv.visitCode(); + mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + mv.visitFieldInsn(GETSTATIC, classname, "data", LIST_SIG); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + cw.visitEnd(); + return cw.toByteArray(); + } + } +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/invoke/defineHiddenClass/DefineHiddenClassTest.java Thu Aug 29 19:32:19 2019 -0700 @@ -0,0 +1,170 @@ +/* + * 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 + * @library /test/lib + * @modules java.base/jdk.internal.org.objectweb.asm + * @build DefineHiddenClassTest + * @run testng/othervm DefineHiddenClassTest + */ + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.stream.Stream; + +import jdk.internal.org.objectweb.asm.*; +import org.testng.annotations.Test; + +import static java.lang.invoke.MethodHandles.Lookup.ClassOptions.*; +import static java.lang.invoke.MethodHandles.Lookup.PRIVATE; +import static java.lang.invoke.MethodType.*; + +import static jdk.internal.org.objectweb.asm.Opcodes.*; +import static org.testng.Assert.*; + +public class DefineHiddenClassTest { + private static final byte[] bytes = classBytes("Injected"); + private static byte[] classBytes(String classname) { + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES); + MethodVisitor mv; + + cw.visit(V12, ACC_FINAL, classname, null, "java/lang/Object", null); + + { + mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); + mv.visitInsn(RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + { + // access a private member of the nest host class + mv = cw.visitMethod(ACC_PUBLIC, "test", "(LDefineHiddenClassTest;)I", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, "DefineHiddenClassTest", "privMethod", "()I"); + mv.visitInsn(IRETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + cw.visitEnd(); + + return cw.toByteArray(); + } + + private int privMethod() { return 1234; } + + @Test + public void defineHiddenClass() throws Throwable { + // define a hidden class + Lookup lookup = MethodHandles.lookup(); + Class<?> c = lookup.defineHiddenClass(bytes, false, NESTMATE); + System.out.println(c.getName()); + assertTrue(c.getNestHost() == DefineHiddenClassTest.class); + assertTrue(c.isHiddenClass()); + + // invoke int test(DefineHiddenClassTest o) + int x = testInjectedClass(c); + assertTrue(x == privMethod()); + + // dynamic nestmate is not listed in the return array of getNestMembers + assertTrue(Stream.of(c.getNestHost().getNestMembers()).noneMatch(k -> k == c)); + assertTrue(c.isNestmateOf(DefineHiddenClassTest.class)); + } + + @Test + public void defineHiddenClassAsLookup() throws Throwable { + // define a hidden class + Lookup lookup = MethodHandles.lookup().defineHiddenClassAsLookup(bytes, false, NESTMATE); + Class<?> c = lookup.lookupClass(); + assertTrue(c.getNestHost() == DefineHiddenClassTest.class); + assertTrue(c.isHiddenClass()); + + // invoke int test(DefineHiddenClassTest o) via MethodHandle + MethodHandle ctor = lookup.findConstructor(c, methodType(void.class)); + MethodHandle mh = lookup.findVirtual(c, "test", methodType(int.class, DefineHiddenClassTest.class)); + int x = (int)mh.bindTo(ctor.invoke()).invokeExact( this); + assertTrue(x == privMethod()); + + // dynamic nestmate is not listed in the return array of getNestMembers + assertTrue(Stream.of(c.getNestHost().getNestMembers()).noneMatch(k -> k == c)); + assertTrue(c.isNestmateOf(DefineHiddenClassTest.class)); + } + + @Test + public void defineWeakClass() throws Throwable { + // define a weak class + Class<?> c = MethodHandles.lookup().defineHiddenClass(bytes, false, WEAK); + assertTrue(c.getNestHost() == c); + assertTrue(c.isHiddenClass()); + } + + @Test(expectedExceptions = IllegalAccessException.class) + public void definePackageAccessClass() throws Throwable { + Lookup lookup = MethodHandles.lookup().dropLookupMode(Lookup.PRIVATE); + lookup.defineHiddenClass(bytes, false, NESTMATE); + } + + @Test(expectedExceptions = IllegalAccessException.class) + public void noPrivateLookupAccess() throws Throwable { + Lookup lookup = MethodHandles.lookup().dropLookupMode(Lookup.PRIVATE); + lookup.defineHiddenClass(bytes, false, NESTMATE); + } + + @Test(expectedExceptions = IllegalAccessException.class) + public void teleportToNestmate() throws Throwable { + Class<?> c = MethodHandles.lookup().defineHiddenClass(bytes, false, NESTMATE); + assertTrue(c.getNestHost() == DefineHiddenClassTest.class); + assertTrue(c.isHiddenClass()); + + // Teleport to a nestmate + Lookup lookup = MethodHandles.lookup().in(c); + assertTrue((lookup.lookupModes() & PRIVATE) == 0); + // fail to define a nestmate + lookup.defineHiddenClass(bytes, false, NESTMATE); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void notSamePackage() throws Throwable { + MethodHandles.lookup().defineHiddenClass(classBytes("p/Injected"), false, NESTMATE); + } + + /* + * invoke int test(DefineHiddenClassTest o) method defined in the injected class + */ + private int testInjectedClass(Class<?> c) throws Throwable { + try { + Method m = c.getMethod("test", DefineHiddenClassTest.class); + return (int) m.invoke(c.newInstance(), this); + } catch (InvocationTargetException e) { + throw e.getCause(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/invoke/defineHiddenClass/SelfRefField.java Thu Aug 29 19:32:19 2019 -0700 @@ -0,0 +1,40 @@ +/* + * 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. + */ + +/* + * The classfile for this class will be used to define a hidden class + * The load of this class will fail because hidden classes cannot + * use their name in field signatures. + */ +public class SelfRefField implements Test { + + SelfRefField other = null; + + private void realTest() { + other = this; // field signature test + } + + public void test() { + realTest(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/invoke/defineHiddenClass/SelfRefMethod.java Thu Aug 29 19:32:19 2019 -0700 @@ -0,0 +1,42 @@ +/* + * 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. + */ + +/* + * The classfile for this class will be used to define a hidden class + * The load of this class will fail because hidden classes cannot + * use their names in method signatures. + */ +public class SelfRefMethod implements Test { + + private void realTest() { + SelfRefMethod local = this; + set_other(local); // method signature test + } + + private void set_other(SelfRefMethod t) { + } + + public void test() { + realTest(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/invoke/defineHiddenClass/SelfReferenceDescriptor.java Thu Aug 29 19:32:19 2019 -0700 @@ -0,0 +1,109 @@ +/* + * 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 + * @library /test/lib + * @build jdk.test.lib.Utils + * jdk.test.lib.compiler.CompilerUtils + * SelfReferenceDescriptor + * @run main/othervm -Xverify:remote SelfReferenceDescriptor + */ + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import static java.lang.invoke.MethodHandles.Lookup.ClassOptions.*; +import static java.lang.invoke.MethodHandles.lookup; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.stream.Stream; + +import jdk.test.lib.compiler.CompilerUtils; + +import jdk.test.lib.Utils; + +/* package-private */ interface Test { + void test(); +} + +public class SelfReferenceDescriptor { + + private static final Path SRC_DIR = Paths.get(Utils.TEST_SRC); + private static final Path CLASSES_DIR = Paths.get("classes"); + + static void compileSources(Path sourceFile, String... options) throws IOException { + Stream<String> ops = Stream.of("-cp", Utils.TEST_CLASSES + File.pathSeparator + CLASSES_DIR); + if (options != null && options.length > 0) { + ops = Stream.concat(ops, Arrays.stream(options)); + } + if (!CompilerUtils.compile(sourceFile, CLASSES_DIR, ops.toArray(String[]::new))) { + throw new RuntimeException("Compilation of the test failed: " + sourceFile); + } + } + + static byte[] readClassFile(String classFileName) throws IOException { + File classFile = new File(CLASSES_DIR + File.separator + classFileName); + try (FileInputStream in = new FileInputStream(classFile); + ByteArrayOutputStream out = new ByteArrayOutputStream()) + { + int b; + while ((b = in.read()) != -1) { + out.write(b); + } + return out.toByteArray(); + } + } + + // Test that a nonFindable class cannot use its own name in a field + // signature. + public static void hiddenClassInFieldDescriptor() throws Exception { + compileSources(SRC_DIR.resolve("SelfRefField.java")); + byte[] bytes = readClassFile("SelfRefField.class"); + try { + Class<?> c = lookup().defineHiddenClass(bytes, false, NESTMATE); + } catch (NoClassDefFoundError e) { + if (!e.getMessage().contains("SelfRefField")) throw e; + } + } + + // Test that a nonFindable class cannot use its own name in a method + // signature. + public static void hiddenClassInMethodDescriptor() throws Exception { + compileSources(SRC_DIR.resolve("SelfRefMethod.java")); + byte[] bytes = readClassFile("SelfRefMethod.class"); + try { + Class<?> c = lookup().defineHiddenClass(bytes, false, NESTMATE); + } catch (NoClassDefFoundError e) { + if (!e.getMessage().contains("SelfRefMethod")) throw e; + } + } + + public static void main(String... args) throws Exception { + hiddenClassInMethodDescriptor(); + hiddenClassInFieldDescriptor(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/invoke/defineHiddenClass/src/AbstractClass.java Thu Aug 29 19:32:19 2019 -0700 @@ -0,0 +1,26 @@ +/* + * 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. + */ + +public abstract class AbstractClass { + abstract void test(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/invoke/defineHiddenClass/src/EnclosingClass.java Thu Aug 29 19:32:19 2019 -0700 @@ -0,0 +1,30 @@ +/* + * 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. + */ + +public class EnclosingClass { + public static void run() { + Runnable r = new Runnable() { + public void run() {} + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/invoke/defineHiddenClass/src/HiddenCantReflect.java Thu Aug 29 19:32:19 2019 -0700 @@ -0,0 +1,70 @@ +/* + * 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. + */ + +/* + * The classfile does not fail verification but would fail when + * getMethod + */ +public class HiddenCantReflect implements HiddenTest { + + HiddenCantReflect other = null; + + private String realTest() { + Object o = other; + HiddenCantReflect local = this; + local = other; + local = (HiddenCantReflect) o; + local = new HiddenCantReflect(); + + set_other(null); + + local = getThis(); + + set_other_maybe(new Object()); + set_other_maybe(this); + return "HiddenCantReflect"; + } + + private HiddenCantReflect getThis() { + return null; + } + + private void set_other(HiddenCantReflect t) { + other = t; + } + + private void set_other_maybe(Object o) { + if (o instanceof HiddenCantReflect) { + } + } + + public void test() { + String result = realTest(); + // Make sure that the Utf8 constant pool entry for "HiddenCantReflect" is okay. + if (!result.substring(0, 7).equals("HiddenC") || + !result.substring(7).equals("antReflect")) { + throw new RuntimeException("'HiddenCantReflect string is bad: " + result); + } + + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/invoke/defineHiddenClass/src/HiddenClass.java Thu Aug 29 19:32:19 2019 -0700 @@ -0,0 +1,58 @@ +/* + * 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. + */ + +/* + * The classfile for this class will be loaded directly and used to define + * a hidden class. + */ +public class HiddenClass implements HiddenTest { + + HiddenClass other = null; + + private String realTest() { + Object o = other; + HiddenClass local = this; + local = other; + local = (HiddenClass) o; + local = new HiddenClass(); + + set_other_maybe(new Object()); + set_other_maybe(this); + return "HiddenClass"; + } + + private void set_other_maybe(Object o) { + if (o instanceof HiddenClass) { + } + } + + public void test() { + String result = realTest(); + // Make sure that the Utf8 constant pool entry for "HiddenClass" is okay. + if (!result.substring(0, 7).equals("HiddenC") || + !result.substring(7).equals("lass")) { + throw new RuntimeException("'HiddenClass string is bad: " + result); + } + + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/invoke/defineHiddenClass/src/HiddenClassThrow.java Thu Aug 29 19:32:19 2019 -0700 @@ -0,0 +1,33 @@ +/* + * 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. + */ + +/* + * The classfile for this class will be loaded directly and used to define + * a non-findable class. + */ +public class HiddenClassThrow implements HiddenTest { + + public void test() { + throw new RuntimeException(this.getClass().getName()); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/invoke/defineHiddenClass/src/HiddenInterface.java Thu Aug 29 19:32:19 2019 -0700 @@ -0,0 +1,32 @@ +/* + * 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. + */ + +/* + * The classfile for this class will be used to define a non-findable + * interface. The load of this class will fail because non-findable classes + * cannot user their name in field signatures. + */ +public interface HiddenInterface { + default void test() { + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/invoke/defineHiddenClass/src/HiddenSuper.java Thu Aug 29 19:32:19 2019 -0700 @@ -0,0 +1,31 @@ +/* + * 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. + */ + +public class HiddenSuper extends HiddenClass implements HiddenTest { + private void realTest() { + } + + public void test() { + realTest(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/invoke/defineHiddenClass/src/Lambda.java Thu Aug 29 19:32:19 2019 -0700 @@ -0,0 +1,36 @@ +/* + * 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. + */ + +import java.util.function.Function; + +public class Lambda implements HiddenTest { + public void test() { + Function<Object, String> f = Object::toString; + String s = f.apply(this); + throw new Error("thrown by " + s); + } + + public String toString() { + return getClass().getName(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/invoke/defineHiddenClass/src/Outer.java Thu Aug 29 19:32:19 2019 -0700 @@ -0,0 +1,4 @@ +public class Outer { + public static class Inner { + } +}
--- a/test/langtools/jdk/javadoc/doclet/testSingletonLists/TestSingletonLists.java Wed Jun 26 22:16:31 2019 -0700 +++ b/test/langtools/jdk/javadoc/doclet/testSingletonLists/TestSingletonLists.java Thu Aug 29 19:32:19 2019 -0700 @@ -23,6 +23,7 @@ /* * @test + * @ignore 8227415 * @bug 8219998 8221991 * @summary Eliminate inherently singleton lists * @library /tools/lib ../../lib
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/micro/org/openjdk/bench/java/lang/invoke/LookupDefineClass.java Thu Aug 29 19:32:19 2019 -0700 @@ -0,0 +1,288 @@ +/* + * 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. + */ +package org.openjdk.bench.java.lang.invoke; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Field; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.concurrent.TimeUnit; + +import static java.lang.invoke.MethodHandles.Lookup.ClassOptions.*; + +public class LookupDefineClass { + private static final byte[] X_BYTECODE = new byte[]{ + (byte)0xCA, (byte)0xFE, (byte)0xBA, (byte)0xBE, 0x00, 0x00, 0x00, 0x38, 0x00, 0x10, 0x0A, 0x00, + 0x03, 0x00, 0x0C, 0x07, 0x00, 0x0D, 0x07, 0x00, 0x0E, 0x07, 0x00, 0x0F, + 0x01, 0x00, 0x06, 0x3C, 0x69, 0x6E, 0x69, 0x74, 0x3E, 0x01, 0x00, 0x03, + 0x28, 0x29, 0x56, 0x01, 0x00, 0x04, 0x43, 0x6F, 0x64, 0x65, 0x01, 0x00, + 0x0F, 0x4C, 0x69, 0x6E, 0x65, 0x4E, 0x75, 0x6D, 0x62, 0x65, 0x72, 0x54, + 0x61, 0x62, 0x6C, 0x65, 0x01, 0x00, 0x03, 0x72, 0x75, 0x6E, 0x01, 0x00, + 0x0A, 0x53, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6C, 0x65, 0x01, + 0x00, 0x08, 0x46, 0x6F, 0x6F, 0x2E, 0x6A, 0x61, 0x76, 0x61, 0x0C, 0x00, + 0x05, 0x00, 0x06, 0x01, 0x00, 0x07, 0x66, 0x6F, 0x6F, 0x2F, 0x46, 0x6F, + 0x6F, 0x01, 0x00, 0x10, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, + 0x67, 0x2F, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x01, 0x00, 0x12, 0x6A, + 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x52, 0x75, 0x6E, + 0x6E, 0x61, 0x62, 0x6C, 0x65, 0x00, 0x21, 0x00, 0x02, 0x00, 0x03, 0x00, + 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x05, 0x2A, (byte)0xB7, 0x00, 0x01, (byte)0xB1, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x06, 0x00, 0x01, 0x00, 0x07, + 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + (byte)0xB1, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x0B + }; + + /** + * Our own crippled classloader, that can only load a simple class over and over again. + */ + public static class XLoader extends URLClassLoader { + public XLoader() { + super("X-Loader", new URL[0], ClassLoader.getSystemClassLoader()); + } + + @Override + protected Class<?> findClass(final String name) throws ClassNotFoundException { + return defineClass(name, X_BYTECODE, 0, X_BYTECODE.length); + } + + public Class<?> install(final String name, byte[] bytes) { + return defineClass(name, bytes, 0, bytes.length); + } + } + + public static class Loader extends URLClassLoader { + public Loader(String name) { + super(name, new URL[0], ClassLoader.getSystemClassLoader()); + } + + public Class<?> install(final String name, byte[] bytes) { + return defineClass(name, bytes, 0, bytes.length); + } + } + + @State(Scope.Thread) + @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) + @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) + @Fork(3) + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public static class OneClassPerLoader { + ClassLoader loader; + @Benchmark + public Class<?> load() throws ClassNotFoundException { + return Class.forName("foo.Foo", false, loader); + } + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(LookupDefineClass.OneClassPerLoader.class.getSimpleName()) + // .addProfiler(ClassloaderProfiler.class) + // .addProfiler(CompilerProfiler.class) + .build(); + + new Runner(opt).run(); + } + + @Setup(Level.Invocation) + public void doSetup() { + loader = new XLoader(); + } + } + + private static final byte[] FOO_HOST_BYTES = new byte[]{ + (byte)0xCA, (byte)0xFE, (byte)0xBA, (byte)0xBE, 0x00, 0x00, 0x00, 0x33, 0x00, 0x12, 0x01, 0x00, + 0x11, 0x66, 0x6F, 0x6F, 0x2F, 0x41, 0x6E, 0x6F, 0x6E, 0x79, 0x6D, 0x6F, + 0x75, 0x73, 0x48, 0x6F, 0x73, 0x74, 0x07, 0x00, 0x01, 0x01, 0x00, 0x10, + 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x4F, 0x62, + 0x6A, 0x65, 0x63, 0x74, 0x07, 0x00, 0x03, 0x01, 0x00, 0x06, 0x4C, 0x4F, + 0x4F, 0x4B, 0x55, 0x50, 0x01, 0x00, 0x27, 0x4C, 0x6A, 0x61, 0x76, 0x61, + 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x69, 0x6E, 0x76, 0x6F, 0x6B, 0x65, + 0x2F, 0x4D, 0x65, 0x74, 0x68, 0x6F, 0x64, 0x48, 0x61, 0x6E, 0x64, 0x6C, + 0x65, 0x73, 0x24, 0x4C, 0x6F, 0x6F, 0x6B, 0x75, 0x70, 0x3B, 0x01, 0x00, + 0x08, 0x3C, 0x63, 0x6C, 0x69, 0x6E, 0x69, 0x74, 0x3E, 0x01, 0x00, 0x03, + 0x28, 0x29, 0x56, 0x01, 0x00, 0x1E, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, + 0x61, 0x6E, 0x67, 0x2F, 0x69, 0x6E, 0x76, 0x6F, 0x6B, 0x65, 0x2F, 0x4D, + 0x65, 0x74, 0x68, 0x6F, 0x64, 0x48, 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x73, + 0x07, 0x00, 0x09, 0x01, 0x00, 0x06, 0x6C, 0x6F, 0x6F, 0x6B, 0x75, 0x70, + 0x01, 0x00, 0x29, 0x28, 0x29, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, + 0x61, 0x6E, 0x67, 0x2F, 0x69, 0x6E, 0x76, 0x6F, 0x6B, 0x65, 0x2F, 0x4D, + 0x65, 0x74, 0x68, 0x6F, 0x64, 0x48, 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x73, + 0x24, 0x4C, 0x6F, 0x6F, 0x6B, 0x75, 0x70, 0x3B, 0x0C, 0x00, 0x0B, 0x00, + 0x0C, 0x0A, 0x00, 0x0A, 0x00, 0x0D, 0x0C, 0x00, 0x05, 0x00, 0x06, 0x09, + 0x00, 0x02, 0x00, 0x0F, 0x01, 0x00, 0x04, 0x43, 0x6F, 0x64, 0x65, 0x06, + 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x19, 0x00, + 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x07, 0x00, + 0x08, 0x00, 0x01, 0x00, 0x11, 0x00, 0x00, 0x00, 0x13, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, (byte)0xB8, 0x00, 0x0E, (byte)0xB3, 0x00, 0x10, (byte)0xB1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + private static MethodHandles.Lookup defineHostClass(Loader loader, String name, byte[] bytes) { + try { + Class<?> c = loader.install(name, bytes); + Field f = c.getDeclaredField("LOOKUP"); + return (MethodHandles.Lookup)f.get(null); + } catch (NoSuchFieldException|IllegalAccessException e) { + throw new InternalError(e); + } + } + + @State(Scope.Thread) + @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) + @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) + @Fork(3) + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public static class TwoClassPerLoader { + XLoader loader; + @Benchmark + public Class<?> load() throws ClassNotFoundException { + return Class.forName("foo.Foo", false, loader); + } + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(LookupDefineClass.TwoClassPerLoader.class.getSimpleName()) + // .addProfiler(ClassloaderProfiler.class) + // .addProfiler(CompilerProfiler.class) + .build(); + + new Runner(opt).run(); + } + + @Setup(Level.Invocation) + public void doSetup() { + loader = new XLoader(); + loader.install("foo.AnonymousHost", FOO_HOST_BYTES); + } + } + + + @State(Scope.Thread) + @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) + @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) + @Fork(3) + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public static class WeakClass { + private static final MethodHandles.Lookup HOST_LOOKUP = + defineHostClass(new Loader("weak-class-loader"), "foo.AnonymousHost", FOO_HOST_BYTES); + + @Benchmark + public Class<?> load() throws ClassNotFoundException { + try { + return HOST_LOOKUP.defineHiddenClass(X_BYTECODE, false, WEAK); + } catch (IllegalAccessException e) { + throw new InternalError(e); + } + } + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(LookupDefineClass.WeakClass.class.getSimpleName()) + // .addProfiler(ClassloaderProfiler.class) + // .addProfiler(CompilerProfiler.class) + .build(); + + new Runner(opt).run(); + } + } + + @State(Scope.Thread) + @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) + @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) + @Fork(3) + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public static class UnsafeAnonymousClass { + static final sun.misc.Unsafe unsafe = getUnsafe(); + + private static final MethodHandles.Lookup lookup = + defineHostClass(new Loader("anonymous-class-loader"),"foo.AnonymousHost", FOO_HOST_BYTES); + + @Benchmark + public Class<?> load() throws ClassNotFoundException { + return unsafe.defineAnonymousClass(lookup.lookupClass(), X_BYTECODE, null); + } + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(LookupDefineClass.UnsafeAnonymousClass.class.getSimpleName()) + // .addProfiler(ClassloaderProfiler.class) + // .addProfiler(CompilerProfiler.class) + .build(); + + new Runner(opt).run(); + } + } + + static sun.misc.Unsafe getUnsafe() { + try { + Field f = sun.misc.Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return (sun.misc.Unsafe)f.get(null); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @State(Scope.Thread) + @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) + @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) + @Fork(3) + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public static class HiddenClass { + private static final MethodHandles.Lookup HOST_LOOKUP = + defineHostClass(new Loader("hidden-class-loader"),"foo.AnonymousHost", FOO_HOST_BYTES); + + @Benchmark + public Class<?> load() throws ClassNotFoundException { + try { + return HOST_LOOKUP.defineHiddenClass(X_BYTECODE, false); + } catch (IllegalAccessException e) { + throw new InternalError(e); + } + } + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(LookupDefineClass.HiddenClass.class.getSimpleName()) + // .addProfiler(ClassloaderProfiler.class) + // .addProfiler(CompilerProfiler.class) + .build(); + + new Runner(opt).run(); + } + } +}