< prev index next >
src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java
Print this page
rev 58565 : 8238358: Implementation of JEP 371: Hidden Classes
Reviewed-by: duke
Contributed-by: mandy.chung@oracle.com, lois.foltan@oracle.com, david.holmes@oracle.com, harold.seigel@oracle.com, serguei.spitsyn@oracle.com, alex.buckley@oracle.com, jamsheed.c.m@oracle.com
@@ -25,63 +25,65 @@
package java.lang.invoke;
import jdk.internal.org.objectweb.asm.*;
import sun.invoke.util.BytecodeDescriptor;
-import jdk.internal.misc.Unsafe;
import sun.security.action.GetPropertyAction;
import sun.security.action.GetBooleanAction;
import java.io.FilePermission;
import java.io.Serializable;
+import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Constructor;
+import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.LinkedHashSet;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.PropertyPermission;
import java.util.Set;
+import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE;
+import static java.lang.invoke.MethodHandles.Lookup.ClassOption.STRONG;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
/**
* Lambda metafactory implementation which dynamically creates an
* inner-class-like class per lambda callsite.
*
* @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";
private static final String NAME_CTOR = "<init>";
- private static final String NAME_FACTORY = "get$Lambda";
//Serialization support
private static final String NAME_SERIALIZED_LAMBDA = "java/lang/invoke/SerializedLambda";
private static final String NAME_NOT_SERIALIZABLE_EXCEPTION = "java/io/NotSerializableException";
private static final String DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;";
private static final String DESCR_METHOD_WRITE_OBJECT = "(Ljava/io/ObjectOutputStream;)V";
private static final String DESCR_METHOD_READ_OBJECT = "(Ljava/io/ObjectInputStream;)V";
+ private static final String DESCR_SET_IMPL_METHOD = "(Ljava/lang/invoke/MethodHandle;)V";
+
private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace";
private static final String NAME_METHOD_READ_OBJECT = "readObject";
private static final String NAME_METHOD_WRITE_OBJECT = "writeObject";
+ private static final String NAME_FIELD_IMPL_METHOD = "protectedImplMethod";
private static final String DESCR_CLASS = "Ljava/lang/Class;";
private static final String DESCR_STRING = "Ljava/lang/String;";
private static final String DESCR_OBJECT = "Ljava/lang/Object;";
+ private static final String DESCR_METHOD_HANDLE = "Ljava/lang/invoke/MethodHandle;";
private static final String DESCR_CTOR_SERIALIZED_LAMBDA
= "(" + DESCR_CLASS + DESCR_STRING + DESCR_STRING + DESCR_STRING + "I"
+ DESCR_STRING + DESCR_STRING + DESCR_STRING + DESCR_STRING + "[" + DESCR_OBJECT + ")V";
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
private static final AtomicInteger counter = new AtomicInteger(0);
@@ -106,10 +108,11 @@
private final MethodType constructorType; // Generated class constructor type "(CC)void"
private final ClassWriter cw; // ASM class writer
private final String[] argNames; // Generated names for the constructor arguments
private final String[] argDescs; // Type descriptors for the constructor arguments
private final String lambdaClassName; // Generated name for the generated class "X$$Lambda$1"
+ private final boolean useImplMethodHandle; // use MethodHandle invocation instead of symbolic bytecode invocation
/**
* General meta-factory constructor, supporting both standard cases and
* allowing for uncommon options such as serialization or bridging.
*
@@ -161,11 +164,13 @@
isSerializable, markerInterfaces, additionalBridges);
implMethodClassName = implClass.getName().replace('.', '/');
implMethodName = implInfo.getName();
implMethodDesc = implInfo.getMethodType().toMethodDescriptorString();
constructorType = invokedType.changeReturnType(Void.TYPE);
- lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
+ lambdaClassName = lambdaClassName(targetClass);
+ useImplMethodHandle = !implClass.getPackageName().equals(implInfo.getDeclaringClass().getPackageName())
+ && !Modifier.isPublic(implInfo.getModifiers());
cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
int parameterCount = invokedType.parameterCount();
if (parameterCount > 0) {
argNames = new String[parameterCount];
argDescs = new String[parameterCount];
@@ -176,10 +181,19 @@
} else {
argNames = argDescs = EMPTY_STRING_ARRAY;
}
}
+ 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
* of the class which the CallSite will return, otherwise, generate handles
* which will call the class' constructor.
@@ -215,24 +229,18 @@
}
try {
Object inst = ctrs[0].newInstance();
return new ConstantCallSite(MethodHandles.constant(samBase, inst));
- }
- catch (ReflectiveOperationException e) {
+ } catch (ReflectiveOperationException e) {
throw new LambdaConversionException("Exception instantiating lambda object", e);
}
} else {
try {
- if (!disableEagerInitialization) {
- UNSAFE.ensureClassInitialized(innerClass);
- }
- return new ConstantCallSite(
- MethodHandles.Lookup.IMPL_LOOKUP
- .findStatic(innerClass, NAME_FACTORY, invokedType));
- }
- catch (ReflectiveOperationException e) {
+ MethodHandle mh = caller.findConstructor(innerClass, invokedType.changeReturnType(void.class));
+ return new ConstantCallSite(mh.asType(invokedType));
+ } catch (ReflectiveOperationException e) {
throw new LambdaConversionException("Exception finding constructor", e);
}
}
}
@@ -281,41 +289,51 @@
fv.visitEnd();
}
generateConstructor();
- if (invokedType.parameterCount() != 0 || disableEagerInitialization) {
- generateFactory();
- }
-
// 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
if (additionalBridges != null) {
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);
}
}
+ if (useImplMethodHandle) {
+ FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_STATIC,
+ NAME_FIELD_IMPL_METHOD,
+ DESCR_METHOD_HANDLE,
+ null, null);
+ fv.visitEnd();
+
+ mv = cw.visitMethod(ACC_PRIVATE + ACC_STATIC,
+ "setImplMethod", DESCR_SET_IMPL_METHOD,
+ null, null);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(PUTSTATIC, lambdaClassName, NAME_FIELD_IMPL_METHOD, DESCR_METHOD_HANDLE);
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(-1, -1);
+ mv.visitEnd();
+ }
+
if (isSerializable)
generateSerializationFriendlyMethods();
else if (accidentallySerializable)
generateSerializationHostileMethods();
cw.visitEnd();
// Define the generated class in this VM.
final byte[] classBytes = cw.toByteArray();
-
// If requested, dump out to a file for debugging purposes
if (dumper != null) {
AccessController.doPrivileged(new PrivilegedAction<>() {
@Override
public Void run() {
@@ -325,32 +343,30 @@
}, null,
new FilePermission("<<ALL FILES>>", "read, write"),
// createDirectories may need it
new PropertyPermission("user.dir", "read"));
}
-
- return UNSAFE.defineAnonymousClass(targetClass, classBytes, null);
+ try {
+ // this class is linked at the indy callsite; so define a hidden nestmate
+ Lookup lookup = caller.defineHiddenClass(classBytes, !disableEagerInitialization, NESTMATE, STRONG);
+ if (useImplMethodHandle) {
+ // If the target class invokes a method reference this::m which is
+ // resolved to a protected method inherited from a superclass in a different
+ // package, the target class does not have a bridge and this method reference
+ // has been changed from public to protected after the target class was compiled.
+ // This lambda proxy class has no access to the resolved method.
+ // So this workaround by passing the live implMethod method handle
+ // to the proxy class to invoke directly.
+ MethodHandle mh = lookup.findStaticSetter(lookup.lookupClass(), NAME_FIELD_IMPL_METHOD, MethodHandle.class);
+ mh.invokeExact(implMethod);
+ }
+ return lookup.lookupClass();
+ } catch (IllegalAccessException e) {
+ throw new LambdaConversionException("Exception defining lambda proxy class", e);
+ } catch (Throwable t) {
+ throw new InternalError(t);
}
-
- /**
- * Generate the factory method for the class
- */
- private void generateFactory() {
- MethodVisitor m = cw.visitMethod(ACC_PRIVATE | ACC_STATIC, NAME_FACTORY, invokedType.toMethodDescriptorString(), null, null);
- m.visitCode();
- m.visitTypeInsn(NEW, lambdaClassName);
- m.visitInsn(Opcodes.DUP);
- int parameterCount = invokedType.parameterCount();
- for (int typeIndex = 0, varIndex = 0; typeIndex < parameterCount; typeIndex++) {
- Class<?> argType = invokedType.parameterType(typeIndex);
- m.visitVarInsn(getLoadOpcode(argType), varIndex);
- varIndex += getParameterSize(argType);
- }
- m.visitMethodInsn(INVOKESPECIAL, lambdaClassName, NAME_CTOR, constructorType.toMethodDescriptorString(), false);
- m.visitInsn(ARETURN);
- m.visitMaxs(-1, -1);
- m.visitEnd();
}
/**
* Generate the constructor for the class
*/
@@ -462,22 +478,31 @@
if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
visitTypeInsn(NEW, implMethodClassName);
visitInsn(DUP);
}
+ if (useImplMethodHandle) {
+ visitVarInsn(ALOAD, 0);
+ visitFieldInsn(GETSTATIC, lambdaClassName, NAME_FIELD_IMPL_METHOD, DESCR_METHOD_HANDLE);
+ }
for (int i = 0; i < argNames.length; i++) {
visitVarInsn(ALOAD, 0);
visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
}
convertArgumentTypes(methodType);
+ if (useImplMethodHandle) {
+ MethodType mtype = implInfo.getMethodType().insertParameterTypes(0, implClass);
+ visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle",
+ "invokeExact", mtype.descriptorString(), false);
+ } else {
// Invoke the method we want to forward to
visitMethodInsn(invocationOpcode(), implMethodClassName,
implMethodName, implMethodDesc,
implClass.isInterface());
-
+ }
// Convert the return value (if any) and return it
// Note: if adapting from non-void to void, the 'return'
// instruction will pop the unneeded result
Class<?> implReturnClass = implMethodType.returnType();
Class<?> samReturnClass = methodType.returnType();
< prev index next >