8332457: Examine startup overheads from JDK-8294961
8229959: Convert proxy class to use constant dynamic Reviewed-by: liach, redestad
This commit is contained in:
parent
326dbb1b13
commit
d85b0ca5cd
@ -24,6 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package java.lang.constant;
|
package java.lang.constant;
|
||||||
|
|
||||||
|
import jdk.internal.constant.MethodTypeDescImpl;
|
||||||
import jdk.internal.constant.PrimitiveClassDescImpl;
|
import jdk.internal.constant.PrimitiveClassDescImpl;
|
||||||
import jdk.internal.constant.ReferenceClassDescImpl;
|
import jdk.internal.constant.ReferenceClassDescImpl;
|
||||||
|
|
||||||
@ -349,8 +350,11 @@ public final class ConstantDescs {
|
|||||||
String name,
|
String name,
|
||||||
ClassDesc returnType,
|
ClassDesc returnType,
|
||||||
ClassDesc... paramTypes) {
|
ClassDesc... paramTypes) {
|
||||||
return MethodHandleDesc.ofMethod(STATIC, owner, name, MethodTypeDesc.of(returnType, paramTypes)
|
int prefixLen = INDY_BOOTSTRAP_ARGS.length;
|
||||||
.insertParameterTypes(0, INDY_BOOTSTRAP_ARGS));
|
ClassDesc[] fullParamTypes = new ClassDesc[paramTypes.length + prefixLen];
|
||||||
|
System.arraycopy(INDY_BOOTSTRAP_ARGS, 0, fullParamTypes, 0, prefixLen);
|
||||||
|
System.arraycopy(paramTypes, 0, fullParamTypes, prefixLen, paramTypes.length);
|
||||||
|
return MethodHandleDesc.ofMethod(STATIC, owner, name, MethodTypeDescImpl.ofTrusted(returnType, fullParamTypes));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -370,7 +374,10 @@ public final class ConstantDescs {
|
|||||||
String name,
|
String name,
|
||||||
ClassDesc returnType,
|
ClassDesc returnType,
|
||||||
ClassDesc... paramTypes) {
|
ClassDesc... paramTypes) {
|
||||||
return MethodHandleDesc.ofMethod(STATIC, owner, name, MethodTypeDesc.of(returnType, paramTypes)
|
int prefixLen = CONDY_BOOTSTRAP_ARGS.length;
|
||||||
.insertParameterTypes(0, CONDY_BOOTSTRAP_ARGS));
|
ClassDesc[] fullParamTypes = new ClassDesc[paramTypes.length + prefixLen];
|
||||||
|
System.arraycopy(CONDY_BOOTSTRAP_ARGS, 0, fullParamTypes, 0, prefixLen);
|
||||||
|
System.arraycopy(paramTypes, 0, fullParamTypes, prefixLen, paramTypes.length);
|
||||||
|
return MethodHandleDesc.ofMethod(STATIC, owner, name, MethodTypeDescImpl.ofTrusted(returnType, fullParamTypes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -856,6 +856,8 @@ public class Proxy implements java.io.Serializable {
|
|||||||
*/
|
*/
|
||||||
private static void ensureAccess(Module target, Class<?> c) {
|
private static void ensureAccess(Module target, Class<?> c) {
|
||||||
Module m = c.getModule();
|
Module m = c.getModule();
|
||||||
|
if (target == m) return;
|
||||||
|
|
||||||
// add read edge and qualified export for the target module to access
|
// add read edge and qualified export for the target module to access
|
||||||
if (!target.canRead(m)) {
|
if (!target.canRead(m)) {
|
||||||
Modules.addReads(target, m);
|
Modules.addReads(target, m);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -25,28 +25,32 @@
|
|||||||
|
|
||||||
package java.lang.reflect;
|
package java.lang.reflect;
|
||||||
|
|
||||||
import java.lang.classfile.*;
|
|
||||||
import java.lang.classfile.constantpool.*;
|
|
||||||
import java.lang.classfile.attribute.ExceptionsAttribute;
|
|
||||||
import sun.security.action.GetBooleanAction;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import static java.lang.classfile.ClassFile.*;
|
import java.lang.classfile.*;
|
||||||
import java.lang.classfile.attribute.StackMapFrameInfo;
|
import java.lang.classfile.attribute.ExceptionsAttribute;
|
||||||
import java.lang.classfile.attribute.StackMapTableAttribute;
|
import java.lang.classfile.constantpool.*;
|
||||||
import java.lang.constant.ClassDesc;
|
import java.lang.constant.ClassDesc;
|
||||||
import static java.lang.constant.ConstantDescs.*;
|
|
||||||
import java.lang.constant.MethodTypeDesc;
|
import java.lang.constant.MethodTypeDesc;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ListIterator;
|
import java.util.ListIterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.IntFunction;
|
import jdk.internal.constant.ConstantUtils;
|
||||||
|
import jdk.internal.constant.MethodTypeDescImpl;
|
||||||
|
import jdk.internal.constant.ReferenceClassDescImpl;
|
||||||
|
import sun.security.action.GetBooleanAction;
|
||||||
|
|
||||||
|
import static java.lang.classfile.ClassFile.*;
|
||||||
|
import java.lang.classfile.attribute.StackMapFrameInfo;
|
||||||
|
import java.lang.classfile.attribute.StackMapTableAttribute;
|
||||||
|
import java.lang.constant.ConstantDescs;
|
||||||
|
import static java.lang.constant.ConstantDescs.*;
|
||||||
|
import java.lang.constant.DirectMethodHandleDesc;
|
||||||
|
import java.lang.constant.DynamicConstantDesc;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ProxyGenerator contains the code to generate a dynamic proxy class
|
* ProxyGenerator contains the code to generate a dynamic proxy class
|
||||||
@ -57,31 +61,32 @@ import java.util.function.IntFunction;
|
|||||||
*/
|
*/
|
||||||
final class ProxyGenerator {
|
final class ProxyGenerator {
|
||||||
|
|
||||||
|
private static final ClassFile CF_CONTEXT =
|
||||||
|
ClassFile.of(ClassFile.StackMapsOption.DROP_STACK_MAPS);
|
||||||
|
|
||||||
private static final ClassDesc
|
private static final ClassDesc
|
||||||
CD_ClassLoader = ClassDesc.ofInternalName("java/lang/ClassLoader"),
|
CD_Class_array = ReferenceClassDescImpl.ofValidated("[Ljava/lang/Class;"),
|
||||||
CD_ClassNotFoundException = ClassDesc.ofInternalName("java/lang/ClassNotFoundException"),
|
CD_IllegalAccessException = ReferenceClassDescImpl.ofValidated("Ljava/lang/IllegalAccessException;"),
|
||||||
CD_IllegalAccessException = ClassDesc.ofInternalName("java/lang/IllegalAccessException"),
|
CD_InvocationHandler = ReferenceClassDescImpl.ofValidated("Ljava/lang/reflect/InvocationHandler;"),
|
||||||
CD_InvocationHandler = ClassDesc.ofInternalName("java/lang/reflect/InvocationHandler"),
|
CD_Method = ReferenceClassDescImpl.ofValidated("Ljava/lang/reflect/Method;"),
|
||||||
CD_Method = ClassDesc.ofInternalName("java/lang/reflect/Method"),
|
CD_NoSuchMethodError = ReferenceClassDescImpl.ofValidated("Ljava/lang/NoSuchMethodError;"),
|
||||||
CD_NoClassDefFoundError = ClassDesc.ofInternalName("java/lang/NoClassDefFoundError"),
|
CD_NoSuchMethodException = ReferenceClassDescImpl.ofValidated("Ljava/lang/NoSuchMethodException;"),
|
||||||
CD_NoSuchMethodError = ClassDesc.ofInternalName("java/lang/NoSuchMethodError"),
|
CD_Object_array = ReferenceClassDescImpl.ofValidated("[Ljava/lang/Object;"),
|
||||||
CD_NoSuchMethodException = ClassDesc.ofInternalName("java/lang/NoSuchMethodException"),
|
CD_Proxy = ReferenceClassDescImpl.ofValidated("Ljava/lang/reflect/Proxy;"),
|
||||||
CD_Proxy = ClassDesc.ofInternalName("java/lang/reflect/Proxy"),
|
CD_UndeclaredThrowableException = ReferenceClassDescImpl.ofValidated("Ljava/lang/reflect/UndeclaredThrowableException;");
|
||||||
CD_UndeclaredThrowableException = ClassDesc.ofInternalName("java/lang/reflect/UndeclaredThrowableException");
|
|
||||||
|
|
||||||
private static final MethodTypeDesc
|
private static final MethodTypeDesc
|
||||||
MTD_boolean = MethodTypeDesc.of(CD_boolean),
|
MTD_boolean = MethodTypeDescImpl.ofValidated(CD_boolean),
|
||||||
MTD_void_InvocationHandler = MethodTypeDesc.of(CD_void, CD_InvocationHandler),
|
MTD_void_InvocationHandler = MethodTypeDescImpl.ofValidated(CD_void, CD_InvocationHandler),
|
||||||
MTD_void_String = MethodTypeDesc.of(CD_void, CD_String),
|
MTD_void_String = MethodTypeDescImpl.ofValidated(CD_void, CD_String),
|
||||||
MTD_void_Throwable = MethodTypeDesc.of(CD_void, CD_Throwable),
|
MTD_void_Throwable = MethodTypeDescImpl.ofValidated(CD_void, CD_Throwable),
|
||||||
MTD_Class = MethodTypeDesc.of(CD_Class),
|
MTD_Class = MethodTypeDescImpl.ofValidated(CD_Class),
|
||||||
MTD_Class_String_boolean_ClassLoader = MethodTypeDesc.of(CD_Class, CD_String, CD_boolean, CD_ClassLoader),
|
MTD_Class_array = MethodTypeDescImpl.ofValidated(CD_Class_array),
|
||||||
MTD_ClassLoader = MethodTypeDesc.of(CD_ClassLoader),
|
MTD_Method_String_Class_array = MethodTypeDescImpl.ofValidated(CD_Method, ConstantDescs.CD_String, CD_Class_array),
|
||||||
MTD_MethodHandles$Lookup = MethodTypeDesc.of(CD_MethodHandles_Lookup),
|
MTD_MethodHandles$Lookup = MethodTypeDescImpl.ofValidated(CD_MethodHandles_Lookup),
|
||||||
MTD_MethodHandles$Lookup_MethodHandles$Lookup = MethodTypeDesc.of(CD_MethodHandles_Lookup, CD_MethodHandles_Lookup),
|
MTD_MethodHandles$Lookup_MethodHandles$Lookup = MethodTypeDescImpl.ofValidated(CD_MethodHandles_Lookup, CD_MethodHandles_Lookup),
|
||||||
MTD_Method_String_ClassArray = MethodTypeDesc.of(CD_Method, CD_String, CD_Class.arrayType()),
|
MTD_Object_Object_Method_ObjectArray = MethodTypeDescImpl.ofValidated(CD_Object, CD_Object, CD_Method, CD_Object_array),
|
||||||
MTD_Object_Object_Method_ObjectArray = MethodTypeDesc.of(CD_Object, CD_Object, CD_Method, CD_Object.arrayType()),
|
MTD_String = MethodTypeDescImpl.ofValidated(CD_String);
|
||||||
MTD_String = MethodTypeDesc.of(CD_String);
|
|
||||||
|
|
||||||
private static final String NAME_LOOKUP_ACCESSOR = "proxyClassLookup";
|
private static final String NAME_LOOKUP_ACCESSOR = "proxyClassLookup";
|
||||||
|
|
||||||
@ -90,133 +95,41 @@ final class ProxyGenerator {
|
|||||||
/**
|
/**
|
||||||
* name of field for storing a proxy instance's invocation handler
|
* name of field for storing a proxy instance's invocation handler
|
||||||
*/
|
*/
|
||||||
private static final String handlerFieldName = "h";
|
private static final String NAME_HANDLER_FIELD = "h";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* debugging flag for saving generated class files
|
* debugging flag for saving generated class files
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("removal")
|
@SuppressWarnings("removal")
|
||||||
private static final boolean saveGeneratedFiles =
|
private static final boolean SAVE_GENERATED_FILES =
|
||||||
java.security.AccessController.doPrivileged(
|
java.security.AccessController.doPrivileged(
|
||||||
new GetBooleanAction(
|
new GetBooleanAction(
|
||||||
"jdk.proxy.ProxyGenerator.saveGeneratedFiles"));
|
"jdk.proxy.ProxyGenerator.saveGeneratedFiles"));
|
||||||
|
|
||||||
/* Preloaded ProxyMethod objects for methods in java.lang.Object */
|
/* Preloaded ProxyMethod objects for methods in java.lang.Object */
|
||||||
private static final ProxyMethod hashCodeMethod;
|
private static final ProxyMethod HASH_CODE_METHOD;
|
||||||
private static final ProxyMethod equalsMethod;
|
private static final ProxyMethod EQUALS_METHOD;
|
||||||
private static final ProxyMethod toStringMethod;
|
private static final ProxyMethod TO_STRING_METHOD;
|
||||||
|
|
||||||
private static final ClassModel TEMPLATE;
|
|
||||||
|
|
||||||
private static final ClassEntry CE_Class;
|
|
||||||
private static final ClassEntry CE_ClassNotFoundException;
|
|
||||||
private static final ClassEntry CE_NoClassDefFoundError;
|
|
||||||
private static final ClassEntry CE_NoSuchMethodError;
|
|
||||||
private static final ClassEntry CE_NoSuchMethodException;
|
|
||||||
private static final ClassEntry CE_Object;
|
|
||||||
private static final ClassEntry CE_Throwable;
|
|
||||||
private static final ClassEntry CE_UndeclaredThrowableException;
|
|
||||||
|
|
||||||
private static final FieldRefEntry FRE_Proxy_h;
|
|
||||||
|
|
||||||
private static final InterfaceMethodRefEntry IMRE_InvocationHandler_invoke;
|
|
||||||
|
|
||||||
private static final MethodRefEntry MRE_Class_forName;
|
|
||||||
private static final MethodRefEntry MRE_Class_getClassLoader;
|
|
||||||
private static final MethodRefEntry MRE_Class_getMethod;
|
|
||||||
private static final MethodRefEntry MRE_NoClassDefFoundError_init;
|
|
||||||
private static final MethodRefEntry MRE_NoSuchMethodError_init;
|
|
||||||
private static final MethodRefEntry MRE_Throwable_getMessage;
|
|
||||||
private static final MethodRefEntry MRE_UndeclaredThrowableException_init;
|
|
||||||
|
|
||||||
private static final Utf8Entry UE_Method;
|
|
||||||
|
|
||||||
private static final List<StackMapFrameInfo.VerificationTypeInfo> THROWABLE_STACK;
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private static <T extends PoolEntry> T entryByIndex(int index) {
|
|
||||||
return (T) TEMPLATE.constantPool().entryByIndex(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// static template ClassModel holds pre-defined constant pool entries
|
|
||||||
// proxy transformed from the template shares the template constant pool
|
|
||||||
// each direct use of the template pool entry is significantly faster
|
|
||||||
var cc = ClassFile.of();
|
|
||||||
var ei = new int[21];
|
|
||||||
TEMPLATE = cc.parse(cc.build(CD_Proxy, clb -> {
|
|
||||||
clb.withSuperclass(CD_Proxy);
|
|
||||||
generateConstructor(clb);
|
|
||||||
generateLookupAccessor(clb);
|
|
||||||
var cp = clb.constantPool();
|
|
||||||
|
|
||||||
ei[0] = cp.classEntry(CD_Class).index();
|
|
||||||
ei[1] = cp.classEntry(CD_ClassNotFoundException).index();
|
|
||||||
ei[2] = cp.classEntry(CD_NoClassDefFoundError).index();
|
|
||||||
ei[3] = cp.classEntry(CD_NoSuchMethodError).index();
|
|
||||||
ei[4] = cp.classEntry(CD_NoSuchMethodException).index();
|
|
||||||
ei[5] = cp.classEntry(CD_Object).index();
|
|
||||||
ei[6] = cp.classEntry(CD_Throwable).index();
|
|
||||||
ei[7] = cp.classEntry(CD_UndeclaredThrowableException).index();
|
|
||||||
|
|
||||||
ei[8] = cp.fieldRefEntry(CD_Proxy, handlerFieldName, CD_InvocationHandler).index();
|
|
||||||
|
|
||||||
ei[9] = cp.interfaceMethodRefEntry(CD_InvocationHandler, "invoke", MTD_Object_Object_Method_ObjectArray).index();
|
|
||||||
|
|
||||||
ei[10] = cp.methodRefEntry(CD_Class, "forName", MTD_Class_String_boolean_ClassLoader).index();
|
|
||||||
ei[11] = cp.methodRefEntry(CD_Class, "getClassLoader", MTD_ClassLoader).index();
|
|
||||||
ei[12] = cp.methodRefEntry(CD_Class, "getMethod", MTD_Method_String_ClassArray).index();
|
|
||||||
ei[13] = cp.methodRefEntry(CD_NoClassDefFoundError, INIT_NAME, MTD_void_String).index();
|
|
||||||
ei[14] = cp.methodRefEntry(CD_NoSuchMethodError, INIT_NAME, MTD_void_String).index();
|
|
||||||
ei[15] = cp.methodRefEntry(CD_Throwable, "getMessage", MTD_String).index();
|
|
||||||
ei[16] = cp.methodRefEntry(CD_UndeclaredThrowableException, INIT_NAME, MTD_void_Throwable).index();
|
|
||||||
|
|
||||||
ei[17] = cp.utf8Entry(CD_Method).index();
|
|
||||||
|
|
||||||
ei[18] = cp.utf8Entry("m0").index();
|
|
||||||
ei[19] = cp.utf8Entry("m1").index();
|
|
||||||
ei[20] = cp.utf8Entry("m2").index();
|
|
||||||
}));
|
|
||||||
|
|
||||||
CE_Class = entryByIndex(ei[0]);
|
|
||||||
CE_ClassNotFoundException = entryByIndex(ei[1]);
|
|
||||||
CE_NoClassDefFoundError = entryByIndex(ei[2]);
|
|
||||||
CE_NoSuchMethodError = entryByIndex(ei[3]);
|
|
||||||
CE_NoSuchMethodException = entryByIndex(ei[4]);
|
|
||||||
CE_Object = entryByIndex(ei[5]);
|
|
||||||
CE_Throwable = entryByIndex(ei[6]);
|
|
||||||
CE_UndeclaredThrowableException = entryByIndex(ei[7]);
|
|
||||||
|
|
||||||
FRE_Proxy_h = entryByIndex(ei[8]);
|
|
||||||
|
|
||||||
IMRE_InvocationHandler_invoke = entryByIndex(ei[9]);
|
|
||||||
|
|
||||||
MRE_Class_forName = entryByIndex(ei[10]);
|
|
||||||
MRE_Class_getClassLoader = entryByIndex(ei[11]);
|
|
||||||
MRE_Class_getMethod = entryByIndex(ei[12]);
|
|
||||||
MRE_NoClassDefFoundError_init = entryByIndex(ei[13]);
|
|
||||||
MRE_NoSuchMethodError_init = entryByIndex(ei[14]);
|
|
||||||
MRE_Throwable_getMessage = entryByIndex(ei[15]);
|
|
||||||
MRE_UndeclaredThrowableException_init = entryByIndex(ei[16]);
|
|
||||||
|
|
||||||
UE_Method = entryByIndex(ei[17]);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
hashCodeMethod = new ProxyMethod(Object.class.getMethod("hashCode"), entryByIndex(ei[18]));
|
HASH_CODE_METHOD = new ProxyMethod(Object.class.getMethod("hashCode"));
|
||||||
equalsMethod = new ProxyMethod(Object.class.getMethod("equals", Object.class), entryByIndex(ei[19]));
|
EQUALS_METHOD = new ProxyMethod(Object.class.getMethod("equals", Object.class));
|
||||||
toStringMethod = new ProxyMethod(Object.class.getMethod("toString"), entryByIndex(ei[20]));
|
TO_STRING_METHOD = new ProxyMethod(Object.class.getMethod("toString"));
|
||||||
} catch (NoSuchMethodException e) {
|
} catch (NoSuchMethodException e) {
|
||||||
throw new NoSuchMethodError(e.getMessage());
|
throw new NoSuchMethodError(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
THROWABLE_STACK = List.of(StackMapFrameInfo.ObjectVerificationTypeInfo.of(CE_Throwable));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Classfile context
|
|
||||||
*/
|
|
||||||
private final ClassFile classfileContext;
|
|
||||||
private final ConstantPoolBuilder cp;
|
private final ConstantPoolBuilder cp;
|
||||||
|
private final List<StackMapFrameInfo.VerificationTypeInfo> throwableStack;
|
||||||
|
private final NameAndTypeEntry exInit;
|
||||||
|
private final ClassEntry object, proxy, ute;
|
||||||
|
private final FieldRefEntry handlerField;
|
||||||
|
private final InterfaceMethodRefEntry invoke;
|
||||||
|
private final MethodRefEntry uteInit;
|
||||||
|
private final DirectMethodHandleDesc bsm;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name of proxy class
|
* Name of proxy class
|
||||||
@ -240,12 +153,6 @@ final class ProxyGenerator {
|
|||||||
*/
|
*/
|
||||||
private final Map<String, List<ProxyMethod>> proxyMethods = new LinkedHashMap<>();
|
private final Map<String, List<ProxyMethod>> proxyMethods = new LinkedHashMap<>();
|
||||||
|
|
||||||
/**
|
|
||||||
* Ordinal of next ProxyMethod object added to proxyMethods.
|
|
||||||
* Indexes are reserved for hashcode(0), equals(1), toString(2).
|
|
||||||
*/
|
|
||||||
private int proxyMethodCount = 3;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a ProxyGenerator to generate a proxy class with the
|
* Construct a ProxyGenerator to generate a proxy class with the
|
||||||
* specified name and for the given interfaces.
|
* specified name and for the given interfaces.
|
||||||
@ -255,14 +162,19 @@ final class ProxyGenerator {
|
|||||||
*/
|
*/
|
||||||
private ProxyGenerator(ClassLoader loader, String className, List<Class<?>> interfaces,
|
private ProxyGenerator(ClassLoader loader, String className, List<Class<?>> interfaces,
|
||||||
int accessFlags) {
|
int accessFlags) {
|
||||||
this.classfileContext = ClassFile.of(
|
this.cp = ConstantPoolBuilder.of();
|
||||||
ClassFile.StackMapsOption.DROP_STACK_MAPS,
|
this.classEntry = cp.classEntry(ReferenceClassDescImpl.ofValidatedBinaryName(className));
|
||||||
ClassFile.ClassHierarchyResolverOption.of(
|
|
||||||
ClassHierarchyResolver.ofClassLoading(loader).cached()));
|
|
||||||
this.cp = ConstantPoolBuilder.of(TEMPLATE);
|
|
||||||
this.classEntry = cp.classEntry(ClassDesc.of(className));
|
|
||||||
this.interfaces = interfaces;
|
this.interfaces = interfaces;
|
||||||
this.accessFlags = accessFlags;
|
this.accessFlags = accessFlags;
|
||||||
|
this.throwableStack = List.of(StackMapFrameInfo.ObjectVerificationTypeInfo.of(cp.classEntry(CD_Throwable)));
|
||||||
|
this.exInit = cp.nameAndTypeEntry(INIT_NAME, MTD_void_String);
|
||||||
|
this.object = cp.classEntry(CD_Object);
|
||||||
|
this.proxy = cp.classEntry(CD_Proxy);
|
||||||
|
this.handlerField = cp.fieldRefEntry(proxy, cp.nameAndTypeEntry(NAME_HANDLER_FIELD, CD_InvocationHandler));
|
||||||
|
this.invoke = cp.interfaceMethodRefEntry(CD_InvocationHandler, "invoke", MTD_Object_Object_Method_ObjectArray);
|
||||||
|
this.ute = cp.classEntry(CD_UndeclaredThrowableException);
|
||||||
|
this.uteInit = cp.methodRefEntry(ute, cp.nameAndTypeEntry(INIT_NAME, MTD_void_Throwable));
|
||||||
|
this.bsm = ConstantDescs.ofConstantBootstrap(classEntry.asSymbol(), "$getMethod", CD_Method, CD_Class, CD_String, CD_MethodType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -281,7 +193,7 @@ final class ProxyGenerator {
|
|||||||
ProxyGenerator gen = new ProxyGenerator(loader, name, interfaces, accessFlags);
|
ProxyGenerator gen = new ProxyGenerator(loader, name, interfaces, accessFlags);
|
||||||
final byte[] classFile = gen.generateClassFile();
|
final byte[] classFile = gen.generateClassFile();
|
||||||
|
|
||||||
if (saveGeneratedFiles) {
|
if (SAVE_GENERATED_FILES) {
|
||||||
java.security.AccessController.doPrivileged(
|
java.security.AccessController.doPrivileged(
|
||||||
new java.security.PrivilegedAction<Void>() {
|
new java.security.PrivilegedAction<Void>() {
|
||||||
public Void run() {
|
public Void run() {
|
||||||
@ -312,10 +224,10 @@ final class ProxyGenerator {
|
|||||||
* {@return the entries of the given type}
|
* {@return the entries of the given type}
|
||||||
* @param types the {@code Class} objects, not primitive types nor array types
|
* @param types the {@code Class} objects, not primitive types nor array types
|
||||||
*/
|
*/
|
||||||
private static ClassEntry[] toClassEntries(ConstantPoolBuilder cp, List<Class<?>> types) {
|
private static List<ClassEntry> toClassEntries(ConstantPoolBuilder cp, List<Class<?>> types) {
|
||||||
var ces = new ClassEntry[types.size()];
|
var ces = new ArrayList<ClassEntry>(types.size());
|
||||||
for (int i = 0; i < ces.length; i++)
|
for (var t : types)
|
||||||
ces[i] = cp.classEntry(cp.utf8Entry(types.get(i).getName().replace('.', '/')));
|
ces.add(cp.classEntry(ReferenceClassDescImpl.ofValidatedBinaryName(t.getName())));
|
||||||
return ces;
|
return ces;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -529,9 +441,9 @@ final class ProxyGenerator {
|
|||||||
* java.lang.Object take precedence over duplicate methods in the
|
* java.lang.Object take precedence over duplicate methods in the
|
||||||
* proxy interfaces.
|
* proxy interfaces.
|
||||||
*/
|
*/
|
||||||
addProxyMethod(hashCodeMethod);
|
addProxyMethod(HASH_CODE_METHOD);
|
||||||
addProxyMethod(equalsMethod);
|
addProxyMethod(EQUALS_METHOD);
|
||||||
addProxyMethod(toStringMethod);
|
addProxyMethod(TO_STRING_METHOD);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Accumulate all of the methods from the proxy interfaces.
|
* Accumulate all of the methods from the proxy interfaces.
|
||||||
@ -539,7 +451,7 @@ final class ProxyGenerator {
|
|||||||
for (Class<?> intf : interfaces) {
|
for (Class<?> intf : interfaces) {
|
||||||
for (Method m : intf.getMethods()) {
|
for (Method m : intf.getMethods()) {
|
||||||
if (!Modifier.isStatic(m.getModifiers())) {
|
if (!Modifier.isStatic(m.getModifiers())) {
|
||||||
addProxyMethod(m, intf, cp);
|
addProxyMethod(m, intf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -552,22 +464,21 @@ final class ProxyGenerator {
|
|||||||
checkReturnTypes(sigmethods);
|
checkReturnTypes(sigmethods);
|
||||||
}
|
}
|
||||||
|
|
||||||
return classfileContext.build(classEntry, cp, clb -> {
|
return CF_CONTEXT.build(classEntry, cp, clb -> {
|
||||||
TEMPLATE.forEach(clb);
|
clb.withSuperclass(proxy);
|
||||||
clb.withFlags(accessFlags);
|
clb.withFlags(accessFlags);
|
||||||
clb.withInterfaces(toClassEntries(cp, interfaces));
|
clb.withInterfaces(toClassEntries(cp, interfaces));
|
||||||
|
generateConstructor(clb);
|
||||||
|
|
||||||
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
|
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
|
||||||
for (ProxyMethod pm : sigmethods) {
|
for (ProxyMethod pm : sigmethods) {
|
||||||
// add static field for the Method object
|
|
||||||
clb.withField(pm.methodFieldName, UE_Method, ACC_PRIVATE | ACC_STATIC | ACC_FINAL);
|
|
||||||
|
|
||||||
// Generate code for proxy method
|
// Generate code for proxy method
|
||||||
pm.generateMethod(clb, classEntry);
|
pm.generateMethod(this, clb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
generateStaticInitializer(clb);
|
generateBootstrapMethod(clb);
|
||||||
|
generateLookupAccessor(clb);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -584,7 +495,7 @@ final class ProxyGenerator {
|
|||||||
* passed to the invocation handler's "invoke" method for a given
|
* passed to the invocation handler's "invoke" method for a given
|
||||||
* set of duplicate methods.
|
* set of duplicate methods.
|
||||||
*/
|
*/
|
||||||
private void addProxyMethod(Method m, Class<?> fromClass, ConstantPoolBuilder cp) {
|
private void addProxyMethod(Method m, Class<?> fromClass) {
|
||||||
Class<?> returnType = m.getReturnType();
|
Class<?> returnType = m.getReturnType();
|
||||||
Class<?>[] exceptionTypes = m.getSharedExceptionTypes();
|
Class<?>[] exceptionTypes = m.getSharedExceptionTypes();
|
||||||
|
|
||||||
@ -609,8 +520,7 @@ final class ProxyGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
sigmethods.add(new ProxyMethod(m, sig, m.getSharedParameterTypes(), returnType,
|
sigmethods.add(new ProxyMethod(m, sig, m.getSharedParameterTypes(), returnType,
|
||||||
exceptionTypes, fromClass,
|
exceptionTypes, fromClass));
|
||||||
cp.utf8Entry("m" + proxyMethodCount++)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -628,50 +538,36 @@ final class ProxyGenerator {
|
|||||||
/**
|
/**
|
||||||
* Generate the constructor method for the proxy class.
|
* Generate the constructor method for the proxy class.
|
||||||
*/
|
*/
|
||||||
private static void generateConstructor(ClassBuilder clb) {
|
private void generateConstructor(ClassBuilder clb) {
|
||||||
clb.withMethodBody(INIT_NAME, MTD_void_InvocationHandler, ACC_PUBLIC, cob -> cob
|
clb.withMethodBody(INIT_NAME, MTD_void_InvocationHandler, ACC_PUBLIC, cob -> cob
|
||||||
.aload(cob.receiverSlot())
|
.aload(0)
|
||||||
.aload(cob.parameterSlot(0))
|
.aload(1)
|
||||||
.invokespecial(CD_Proxy, INIT_NAME, MTD_void_InvocationHandler)
|
.invokespecial(cp.methodRefEntry(proxy, cp.nameAndTypeEntry(INIT_NAME, MTD_void_InvocationHandler)))
|
||||||
.return_());
|
.return_());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate the static initializer method for the proxy class.
|
* Generate CONDY bootstrap method for the proxy class to retrieve {@link Method} instances.
|
||||||
*/
|
*/
|
||||||
private void generateStaticInitializer(ClassBuilder clb) {
|
private void generateBootstrapMethod(ClassBuilder clb) {
|
||||||
clb.withMethodBody(CLASS_INIT_NAME, MTD_void, ACC_STATIC, cob -> {
|
clb.withMethodBody(bsm.methodName(), bsm.invocationType(), ClassFile.ACC_PRIVATE | ClassFile.ACC_STATIC, cob -> {
|
||||||
// Put ClassLoader at local variable index 0, used by
|
cob.aload(3) //interface Class
|
||||||
// Class.forName(String, boolean, ClassLoader) calls
|
.aload(4) //interface method name String
|
||||||
cob.ldc(classEntry)
|
.aload(5) //interface MethodType
|
||||||
.invokevirtual(MRE_Class_getClassLoader)
|
.invokevirtual(CD_MethodType, "parameterArray", MTD_Class_array)
|
||||||
.astore(0);
|
.invokevirtual(ConstantDescs.CD_Class, "getMethod", MTD_Method_String_Class_array)
|
||||||
var ts = cob.newBoundLabel();
|
.areturn();
|
||||||
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
|
Label failLabel = cob.newBoundLabel();
|
||||||
for (ProxyMethod pm : sigmethods) {
|
ClassEntry nsme = cp.classEntry(CD_NoSuchMethodError);
|
||||||
pm.codeFieldInitialization(cob, classEntry);
|
cob.exceptionCatch(cob.startLabel(), failLabel, failLabel, CD_NoSuchMethodException)
|
||||||
}
|
.new_(nsme)
|
||||||
}
|
|
||||||
cob.return_();
|
|
||||||
var c1 = cob.newBoundLabel();
|
|
||||||
cob.exceptionCatch(ts, c1, c1, CE_NoSuchMethodException)
|
|
||||||
.new_(CE_NoSuchMethodError)
|
|
||||||
.dup_x1()
|
.dup_x1()
|
||||||
.swap()
|
.swap()
|
||||||
.invokevirtual(MRE_Throwable_getMessage)
|
.invokevirtual(cp.methodRefEntry(CD_Throwable, "getMessage", MTD_String))
|
||||||
.invokespecial(MRE_NoSuchMethodError_init)
|
.invokespecial(cp.methodRefEntry(nsme, exInit))
|
||||||
.athrow();
|
|
||||||
var c2 = cob.newBoundLabel();
|
|
||||||
cob.exceptionCatch(ts, c1, c2, CE_ClassNotFoundException)
|
|
||||||
.new_(CE_NoClassDefFoundError)
|
|
||||||
.dup_x1()
|
|
||||||
.swap()
|
|
||||||
.invokevirtual(MRE_Throwable_getMessage)
|
|
||||||
.invokespecial(MRE_NoClassDefFoundError_init)
|
|
||||||
.athrow()
|
.athrow()
|
||||||
.with(StackMapTableAttribute.of(List.of(
|
.with(StackMapTableAttribute.of(List.of(
|
||||||
StackMapFrameInfo.of(c1, List.of(), THROWABLE_STACK),
|
StackMapFrameInfo.of(failLabel, List.of(), throwableStack))));
|
||||||
StackMapFrameInfo.of(c2, List.of(), THROWABLE_STACK))));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -680,28 +576,36 @@ final class ProxyGenerator {
|
|||||||
* on this proxy class if the caller's lookup class is java.lang.reflect.Proxy;
|
* on this proxy class if the caller's lookup class is java.lang.reflect.Proxy;
|
||||||
* otherwise, IllegalAccessException is thrown
|
* otherwise, IllegalAccessException is thrown
|
||||||
*/
|
*/
|
||||||
private static void generateLookupAccessor(ClassBuilder clb) {
|
private void generateLookupAccessor(ClassBuilder clb) {
|
||||||
clb.withMethod(NAME_LOOKUP_ACCESSOR,
|
clb.withMethod(NAME_LOOKUP_ACCESSOR,
|
||||||
MTD_MethodHandles$Lookup_MethodHandles$Lookup,
|
MTD_MethodHandles$Lookup_MethodHandles$Lookup,
|
||||||
ACC_PRIVATE | ACC_STATIC,
|
ACC_PRIVATE | ACC_STATIC,
|
||||||
mb -> mb.with(ExceptionsAttribute.of(List.of(mb.constantPool().classEntry(CD_IllegalAccessException))))
|
mb -> mb.with(ExceptionsAttribute.of(List.of(mb.constantPool().classEntry(CD_IllegalAccessException))))
|
||||||
.withCode(cob -> cob
|
.withCode(cob -> {
|
||||||
.block(blockBuilder -> blockBuilder
|
Label failLabel = cob.newLabel();
|
||||||
|
ClassEntry mhl = cp.classEntry(CD_MethodHandles_Lookup);
|
||||||
|
ClassEntry iae = cp.classEntry(CD_IllegalAccessException);
|
||||||
|
cob.aload(cob.parameterSlot(0))
|
||||||
|
.invokevirtual(cp.methodRefEntry(mhl, cp.nameAndTypeEntry("lookupClass", MTD_Class)))
|
||||||
|
.ldc(proxy)
|
||||||
|
.if_acmpne(failLabel)
|
||||||
.aload(cob.parameterSlot(0))
|
.aload(cob.parameterSlot(0))
|
||||||
.invokevirtual(CD_MethodHandles_Lookup, "lookupClass", MTD_Class)
|
.invokevirtual(cp.methodRefEntry(mhl, cp.nameAndTypeEntry("hasFullPrivilegeAccess", MTD_boolean)))
|
||||||
.ldc(CD_Proxy)
|
.ifeq(failLabel)
|
||||||
.if_acmpne(blockBuilder.breakLabel())
|
|
||||||
.aload(cob.parameterSlot(0))
|
|
||||||
.invokevirtual(CD_MethodHandles_Lookup, "hasFullPrivilegeAccess", MTD_boolean)
|
|
||||||
.ifeq(blockBuilder.breakLabel())
|
|
||||||
.invokestatic(CD_MethodHandles, "lookup", MTD_MethodHandles$Lookup)
|
.invokestatic(CD_MethodHandles, "lookup", MTD_MethodHandles$Lookup)
|
||||||
.areturn())
|
.areturn()
|
||||||
.new_(CD_IllegalAccessException)
|
.labelBinding(failLabel)
|
||||||
|
.new_(iae)
|
||||||
.dup()
|
.dup()
|
||||||
.aload(cob.parameterSlot(0))
|
.aload(cob.parameterSlot(0))
|
||||||
.invokevirtual(CD_MethodHandles_Lookup, "toString", MTD_String)
|
.invokevirtual(cp.methodRefEntry(mhl, cp.nameAndTypeEntry("toString", MTD_String)))
|
||||||
.invokespecial(CD_IllegalAccessException, INIT_NAME, MTD_void_String)
|
.invokespecial(cp.methodRefEntry(iae, exInit))
|
||||||
.athrow()));
|
.athrow()
|
||||||
|
.with(StackMapTableAttribute.of(List.of(
|
||||||
|
StackMapFrameInfo.of(failLabel,
|
||||||
|
List.of(StackMapFrameInfo.ObjectVerificationTypeInfo.of(mhl)),
|
||||||
|
List.of()))));
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -716,19 +620,17 @@ final class ProxyGenerator {
|
|||||||
private final Class<?> fromClass;
|
private final Class<?> fromClass;
|
||||||
private final Class<?>[] parameterTypes;
|
private final Class<?>[] parameterTypes;
|
||||||
private final Class<?> returnType;
|
private final Class<?> returnType;
|
||||||
private final Utf8Entry methodFieldName;
|
|
||||||
private Class<?>[] exceptionTypes;
|
private Class<?>[] exceptionTypes;
|
||||||
|
|
||||||
private ProxyMethod(Method method, String sig, Class<?>[] parameterTypes,
|
private ProxyMethod(Method method, String sig, Class<?>[] parameterTypes,
|
||||||
Class<?> returnType, Class<?>[] exceptionTypes,
|
Class<?> returnType, Class<?>[] exceptionTypes,
|
||||||
Class<?> fromClass, Utf8Entry methodFieldName) {
|
Class<?> fromClass) {
|
||||||
this.method = method;
|
this.method = method;
|
||||||
this.shortSignature = sig;
|
this.shortSignature = sig;
|
||||||
this.parameterTypes = parameterTypes;
|
this.parameterTypes = parameterTypes;
|
||||||
this.returnType = returnType;
|
this.returnType = returnType;
|
||||||
this.exceptionTypes = exceptionTypes;
|
this.exceptionTypes = exceptionTypes;
|
||||||
this.fromClass = fromClass;
|
this.fromClass = fromClass;
|
||||||
this.methodFieldName = methodFieldName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -737,34 +639,39 @@ final class ProxyGenerator {
|
|||||||
* @param method The method for which to create a proxy
|
* @param method The method for which to create a proxy
|
||||||
* @param methodFieldName the fieldName to generate
|
* @param methodFieldName the fieldName to generate
|
||||||
*/
|
*/
|
||||||
private ProxyMethod(Method method, Utf8Entry methodFieldName) {
|
private ProxyMethod(Method method) {
|
||||||
this(method, method.toShortSignature(),
|
this(method, method.toShortSignature(),
|
||||||
method.getSharedParameterTypes(), method.getReturnType(),
|
method.getSharedParameterTypes(), method.getReturnType(),
|
||||||
method.getSharedExceptionTypes(), method.getDeclaringClass(), methodFieldName);
|
method.getSharedExceptionTypes(), method.getDeclaringClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate this method, including the code and exception table entry.
|
* Generate this method, including the code and exception table entry.
|
||||||
*/
|
*/
|
||||||
private void generateMethod(ClassBuilder clb, ClassEntry className) {
|
private void generateMethod(ProxyGenerator pg, ClassBuilder clb) {
|
||||||
var cp = clb.constantPool();
|
var cp = pg.cp;
|
||||||
MethodTypeDesc desc = MethodTypeDesc.of(toClassDesc(returnType),
|
var pTypes = new ClassDesc[parameterTypes.length];
|
||||||
Arrays.stream(parameterTypes).map(ProxyGenerator::toClassDesc).toArray(ClassDesc[]::new));
|
for (int i = 0; i < pTypes.length; i++) {
|
||||||
|
pTypes[i] = toClassDesc(parameterTypes[i]);
|
||||||
|
}
|
||||||
|
MethodTypeDesc desc = MethodTypeDescImpl.ofTrusted(toClassDesc(returnType), pTypes);
|
||||||
int accessFlags = (method.isVarArgs()) ? ACC_VARARGS | ACC_PUBLIC | ACC_FINAL
|
int accessFlags = (method.isVarArgs()) ? ACC_VARARGS | ACC_PUBLIC | ACC_FINAL
|
||||||
: ACC_PUBLIC | ACC_FINAL;
|
: ACC_PUBLIC | ACC_FINAL;
|
||||||
var catchList = computeUniqueCatchList(exceptionTypes);
|
var catchList = computeUniqueCatchList(exceptionTypes);
|
||||||
clb.withMethod(method.getName(), desc, accessFlags, mb ->
|
clb.withMethod(method.getName(), desc, accessFlags, mb ->
|
||||||
mb.with(ExceptionsAttribute.of(toClassEntries(cp, List.of(exceptionTypes))))
|
mb.with(ExceptionsAttribute.of(toClassEntries(cp, List.of(exceptionTypes))))
|
||||||
.withCode(cob -> {
|
.withCode(cob -> {
|
||||||
cob.aload(cob.receiverSlot())
|
cob.aload(0)
|
||||||
.getfield(FRE_Proxy_h)
|
.getfield(pg.handlerField)
|
||||||
.aload(cob.receiverSlot())
|
.aload(0)
|
||||||
.getstatic(cp.fieldRefEntry(className, cp.nameAndTypeEntry(methodFieldName, UE_Method)));
|
.ldc(DynamicConstantDesc.of(pg.bsm,
|
||||||
|
toClassDesc(fromClass),
|
||||||
|
method.getName(),
|
||||||
|
desc));
|
||||||
if (parameterTypes.length > 0) {
|
if (parameterTypes.length > 0) {
|
||||||
// Create an array and fill with the parameters converting primitives to wrappers
|
// Create an array and fill with the parameters converting primitives to wrappers
|
||||||
cob.loadConstant(parameterTypes.length)
|
cob.loadConstant(parameterTypes.length)
|
||||||
.anewarray(CE_Object);
|
.anewarray(pg.object);
|
||||||
for (int i = 0; i < parameterTypes.length; i++) {
|
for (int i = 0; i < parameterTypes.length; i++) {
|
||||||
cob.dup()
|
cob.dup()
|
||||||
.loadConstant(i);
|
.loadConstant(i);
|
||||||
@ -775,7 +682,7 @@ final class ProxyGenerator {
|
|||||||
cob.aconst_null();
|
cob.aconst_null();
|
||||||
}
|
}
|
||||||
|
|
||||||
cob.invokeinterface(IMRE_InvocationHandler_invoke);
|
cob.invokeinterface(pg.invoke);
|
||||||
|
|
||||||
if (returnType == void.class) {
|
if (returnType == void.class) {
|
||||||
cob.pop()
|
cob.pop()
|
||||||
@ -791,14 +698,14 @@ final class ProxyGenerator {
|
|||||||
cob.athrow(); // just rethrow the exception
|
cob.athrow(); // just rethrow the exception
|
||||||
var c2 = cob.newBoundLabel();
|
var c2 = cob.newBoundLabel();
|
||||||
cob.exceptionCatchAll(cob.startLabel(), c1, c2)
|
cob.exceptionCatchAll(cob.startLabel(), c1, c2)
|
||||||
.new_(CE_UndeclaredThrowableException)
|
.new_(pg.ute)
|
||||||
.dup_x1()
|
.dup_x1()
|
||||||
.swap()
|
.swap()
|
||||||
.invokespecial(MRE_UndeclaredThrowableException_init)
|
.invokespecial(pg.uteInit)
|
||||||
.athrow()
|
.athrow()
|
||||||
.with(StackMapTableAttribute.of(List.of(
|
.with(StackMapTableAttribute.of(List.of(
|
||||||
StackMapFrameInfo.of(c1, List.of(), THROWABLE_STACK),
|
StackMapFrameInfo.of(c1, List.of(), pg.throwableStack),
|
||||||
StackMapFrameInfo.of(c2, List.of(), THROWABLE_STACK))));
|
StackMapFrameInfo.of(c2, List.of(), pg.throwableStack))));
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@ -813,7 +720,7 @@ final class ProxyGenerator {
|
|||||||
if (type.isPrimitive()) {
|
if (type.isPrimitive()) {
|
||||||
cob.loadLocal(TypeKind.from(type).asLoadable(), slot);
|
cob.loadLocal(TypeKind.from(type).asLoadable(), slot);
|
||||||
PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type);
|
PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type);
|
||||||
cob.invokestatic(prim.wrapperMethodRef);
|
cob.invokestatic(prim.wrapperMethodRef(cob.constantPool()));
|
||||||
} else {
|
} else {
|
||||||
cob.aload(slot);
|
cob.aload(slot);
|
||||||
}
|
}
|
||||||
@ -829,7 +736,7 @@ final class ProxyGenerator {
|
|||||||
PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type);
|
PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type);
|
||||||
|
|
||||||
cob.checkcast(prim.wrapperClass)
|
cob.checkcast(prim.wrapperClass)
|
||||||
.invokevirtual(prim.unwrapMethodRef)
|
.invokevirtual(prim.unwrapMethodRef(cob.constantPool()))
|
||||||
.return_(TypeKind.from(type).asLoadable());
|
.return_(TypeKind.from(type).asLoadable());
|
||||||
} else {
|
} else {
|
||||||
cob.checkcast(toClassDesc(type))
|
cob.checkcast(toClassDesc(type))
|
||||||
@ -837,61 +744,12 @@ final class ProxyGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate code for initializing the static field that stores
|
|
||||||
* the Method object for this proxy method. A class loader is
|
|
||||||
* anticipated at local variable index 0.
|
|
||||||
*/
|
|
||||||
private void codeFieldInitialization(CodeBuilder cob, ClassEntry className) {
|
|
||||||
var cp = cob.constantPool();
|
|
||||||
codeClassForName(cob, fromClass);
|
|
||||||
|
|
||||||
cob.ldc(method.getName())
|
|
||||||
.loadConstant(parameterTypes.length)
|
|
||||||
.anewarray(CE_Class);
|
|
||||||
|
|
||||||
// Construct an array with the parameter types mapping primitives to Wrapper types
|
|
||||||
for (int i = 0; i < parameterTypes.length; i++) {
|
|
||||||
cob.dup()
|
|
||||||
.loadConstant(i);
|
|
||||||
if (parameterTypes[i].isPrimitive()) {
|
|
||||||
PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(parameterTypes[i]);
|
|
||||||
cob.getstatic(prim.typeFieldRef);
|
|
||||||
} else {
|
|
||||||
codeClassForName(cob, parameterTypes[i]);
|
|
||||||
}
|
|
||||||
cob.aastore();
|
|
||||||
}
|
|
||||||
// lookup the method
|
|
||||||
cob.invokevirtual(MRE_Class_getMethod)
|
|
||||||
.putstatic(cp.fieldRefEntry(className, cp.nameAndTypeEntry(methodFieldName, UE_Method)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* =============== Code Generation Utility Methods ===============
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate code to invoke the Class.forName with the name of the given
|
|
||||||
* class to get its Class object at runtime. The code is written to
|
|
||||||
* the supplied stream. Note that the code generated by this method
|
|
||||||
* may cause the checked ClassNotFoundException to be thrown. A class
|
|
||||||
* loader is anticipated at local variable index 0.
|
|
||||||
*/
|
|
||||||
private void codeClassForName(CodeBuilder cob, Class<?> cl) {
|
|
||||||
cob.ldc(cl.getName())
|
|
||||||
.iconst_0() // false
|
|
||||||
.aload(0)// classLoader
|
|
||||||
.invokestatic(MRE_Class_forName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return method.toShortString();
|
return method.toShortString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final ConstantPoolBuilder CP = ConstantPoolBuilder.of();
|
|
||||||
/**
|
/**
|
||||||
* A PrimitiveTypeInfo object contains bytecode-related information about
|
* A PrimitiveTypeInfo object contains bytecode-related information about
|
||||||
* a primitive type in its instance fields. The struct for a particular
|
* a primitive type in its instance fields. The struct for a particular
|
||||||
@ -908,28 +766,28 @@ final class ProxyGenerator {
|
|||||||
BOOLEAN(boolean.class, CD_boolean, CD_Boolean);
|
BOOLEAN(boolean.class, CD_boolean, CD_Boolean);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CP entry of corresponding wrapper class
|
* wrapper class
|
||||||
*/
|
*/
|
||||||
private final ClassEntry wrapperClass;
|
private final ClassDesc wrapperClass;
|
||||||
/**
|
/**
|
||||||
* CP entry for wrapper class "valueOf" factory method
|
* wrapper factory method type
|
||||||
*/
|
*/
|
||||||
private final MethodRefEntry wrapperMethodRef;
|
private final MethodTypeDesc wrapperMethodType;
|
||||||
/**
|
/**
|
||||||
* CP entry of wrapper class method for retrieving primitive value
|
* wrapper class method name for retrieving primitive value
|
||||||
*/
|
*/
|
||||||
private final MethodRefEntry unwrapMethodRef;
|
private final String unwrapMethodName;
|
||||||
/**
|
/**
|
||||||
* CP entry of wrapper class TYPE field
|
* wrapper class method type for retrieving primitive value
|
||||||
*/
|
*/
|
||||||
private final FieldRefEntry typeFieldRef;
|
private final MethodTypeDesc unwrapMethodType;
|
||||||
|
|
||||||
PrimitiveTypeInfo(Class<?> primitiveClass, ClassDesc baseType, ClassDesc wrapperClass) {
|
PrimitiveTypeInfo(Class<?> primitiveClass, ClassDesc baseType, ClassDesc wrapperClass) {
|
||||||
assert baseType.isPrimitive();
|
assert baseType.isPrimitive();
|
||||||
this.wrapperClass = CP.classEntry(wrapperClass);
|
this.wrapperClass = wrapperClass;
|
||||||
this.wrapperMethodRef = CP.methodRefEntry(wrapperClass, "valueOf", MethodTypeDesc.of(wrapperClass, baseType));
|
this.wrapperMethodType = MethodTypeDescImpl.ofValidated(wrapperClass, baseType);
|
||||||
this.unwrapMethodRef = CP.methodRefEntry(wrapperClass, primitiveClass.getName() + "Value", MethodTypeDesc.of(baseType));
|
this.unwrapMethodName = primitiveClass.getName() + "Value";
|
||||||
this.typeFieldRef = CP.fieldRefEntry(wrapperClass, "TYPE", CD_Class);
|
this.unwrapMethodType = MethodTypeDescImpl.ofValidated(baseType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PrimitiveTypeInfo get(Class<?> cl) {
|
public static PrimitiveTypeInfo get(Class<?> cl) {
|
||||||
@ -944,5 +802,13 @@ final class ProxyGenerator {
|
|||||||
if (cl == double.class) return DOUBLE;
|
if (cl == double.class) return DOUBLE;
|
||||||
throw new AssertionError(cl);
|
throw new AssertionError(cl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MethodRefEntry wrapperMethodRef(ConstantPoolBuilder cp) {
|
||||||
|
return cp.methodRefEntry(wrapperClass, "valueOf", wrapperMethodType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodRefEntry unwrapMethodRef(ConstantPoolBuilder cp) {
|
||||||
|
return cp.methodRefEntry(wrapperClass, unwrapMethodName, unwrapMethodType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,21 +25,21 @@
|
|||||||
|
|
||||||
package jdk.internal.classfile.impl;
|
package jdk.internal.classfile.impl;
|
||||||
|
|
||||||
|
import java.lang.classfile.BufWriter;
|
||||||
|
import java.lang.classfile.ClassReader;
|
||||||
|
import java.lang.classfile.Label;
|
||||||
|
import java.lang.classfile.MethodModel;
|
||||||
|
import java.lang.classfile.attribute.StackMapFrameInfo;
|
||||||
|
import java.lang.classfile.attribute.StackMapFrameInfo.*;
|
||||||
|
import java.lang.classfile.constantpool.ClassEntry;
|
||||||
import java.lang.constant.ConstantDescs;
|
import java.lang.constant.ConstantDescs;
|
||||||
import java.lang.constant.MethodTypeDesc;
|
import java.lang.constant.MethodTypeDesc;
|
||||||
import java.lang.reflect.AccessFlag;
|
import java.lang.reflect.AccessFlag;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import java.lang.classfile.BufWriter;
|
|
||||||
|
|
||||||
import java.lang.classfile.constantpool.ClassEntry;
|
|
||||||
import java.lang.classfile.attribute.StackMapFrameInfo;
|
|
||||||
import java.lang.classfile.attribute.StackMapFrameInfo.*;
|
|
||||||
import java.lang.classfile.ClassReader;
|
|
||||||
|
|
||||||
import static java.lang.classfile.ClassFile.*;
|
import static java.lang.classfile.ClassFile.*;
|
||||||
import java.lang.classfile.Label;
|
|
||||||
import java.lang.classfile.MethodModel;
|
|
||||||
|
|
||||||
public class StackMapDecoder {
|
public class StackMapDecoder {
|
||||||
|
|
||||||
@ -243,6 +243,20 @@ public class StackMapDecoder {
|
|||||||
@Override
|
@Override
|
||||||
public int tag() { return VT_OBJECT; }
|
public int tag() { return VT_OBJECT; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o instanceof ObjectVerificationTypeInfoImpl that) {
|
||||||
|
return Objects.equals(className, that.className);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(className);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return className.asInternalName();
|
return className.asInternalName();
|
||||||
|
@ -1045,7 +1045,9 @@ public final class StackMapGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setLocalsFromArg(String name, MethodTypeDesc methodDesc, boolean isStatic, Type thisKlass) {
|
void setLocalsFromArg(String name, MethodTypeDesc methodDesc, boolean isStatic, Type thisKlass) {
|
||||||
localsSize = 0;
|
int localsSize = 0;
|
||||||
|
// Pre-emptively create a locals array that encompass all parameter slots
|
||||||
|
checkLocal(methodDesc.parameterCount() + (isStatic ? 0 : -1));
|
||||||
if (!isStatic) {
|
if (!isStatic) {
|
||||||
localsSize++;
|
localsSize++;
|
||||||
if (OBJECT_INITIALIZER_NAME.equals(name) && !CD_Object.equals(thisKlass.sym)) {
|
if (OBJECT_INITIALIZER_NAME.equals(name) && !CD_Object.equals(thisKlass.sym)) {
|
||||||
@ -1057,7 +1059,7 @@ public final class StackMapGenerator {
|
|||||||
}
|
}
|
||||||
for (int i = 0; i < methodDesc.parameterCount(); i++) {
|
for (int i = 0; i < methodDesc.parameterCount(); i++) {
|
||||||
var desc = methodDesc.parameterType(i);
|
var desc = methodDesc.parameterType(i);
|
||||||
if (desc.isClassOrInterface() || desc.isArray()) {
|
if (!desc.isPrimitive()) {
|
||||||
setLocalRawInternal(localsSize++, Type.referenceType(desc));
|
setLocalRawInternal(localsSize++, Type.referenceType(desc));
|
||||||
} else switch (desc.descriptorString().charAt(0)) {
|
} else switch (desc.descriptorString().charAt(0)) {
|
||||||
case 'J' -> {
|
case 'J' -> {
|
||||||
@ -1075,6 +1077,7 @@ public final class StackMapGenerator {
|
|||||||
default -> throw new AssertionError("Should not reach here");
|
default -> throw new AssertionError("Should not reach here");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.localsSize = localsSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
void copyFrom(Frame src) {
|
void copyFrom(Frame src) {
|
||||||
|
@ -157,6 +157,9 @@ public final class ConstantUtils {
|
|||||||
throw new IllegalArgumentException("zero-length member name");
|
throw new IllegalArgumentException("zero-length member name");
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
char ch = name.charAt(i);
|
char ch = name.charAt(i);
|
||||||
|
// common case fast-path
|
||||||
|
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
|
||||||
|
continue;
|
||||||
if (ch == '.' || ch == ';' || ch == '[' || ch == '/')
|
if (ch == '.' || ch == ';' || ch == '[' || ch == '/')
|
||||||
throw new IllegalArgumentException("Invalid member name: " + name);
|
throw new IllegalArgumentException("Invalid member name: " + name);
|
||||||
if (method && (ch == '<' || ch == '>')) {
|
if (method && (ch == '<' || ch == '>')) {
|
||||||
|
@ -89,7 +89,7 @@ public final class MethodTypeDescImpl implements MethodTypeDesc {
|
|||||||
* @param returnType a {@link ClassDesc} describing the return type
|
* @param returnType a {@link ClassDesc} describing the return type
|
||||||
* @param trustedArgTypes {@link ClassDesc}s describing the trusted parameter types
|
* @param trustedArgTypes {@link ClassDesc}s describing the trusted parameter types
|
||||||
*/
|
*/
|
||||||
public static MethodTypeDescImpl ofValidated(ClassDesc returnType, ClassDesc[] trustedArgTypes) {
|
public static MethodTypeDescImpl ofValidated(ClassDesc returnType, ClassDesc... trustedArgTypes) {
|
||||||
if (trustedArgTypes.length == 0)
|
if (trustedArgTypes.length == 0)
|
||||||
return new MethodTypeDescImpl(returnType, ConstantUtils.EMPTY_CLASSDESC);
|
return new MethodTypeDescImpl(returnType, ConstantUtils.EMPTY_CLASSDESC);
|
||||||
return new MethodTypeDescImpl(returnType, trustedArgTypes);
|
return new MethodTypeDescImpl(returnType, trustedArgTypes);
|
||||||
|
@ -126,10 +126,10 @@ public final class ReferenceClassDescImpl implements ClassDesc {
|
|||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
if (o instanceof ReferenceClassDescImpl constant) {
|
||||||
|
return descriptor.equals(constant.descriptor);
|
||||||
ClassDesc constant = (ClassDesc) o;
|
}
|
||||||
return descriptor.equals(constant.descriptorString());
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, 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.reflect;
|
||||||
|
|
||||||
|
|
||||||
|
import org.openjdk.jmh.annotations.Benchmark;
|
||||||
|
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||||
|
import org.openjdk.jmh.annotations.Fork;
|
||||||
|
import org.openjdk.jmh.annotations.Measurement;
|
||||||
|
import org.openjdk.jmh.annotations.Mode;
|
||||||
|
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||||
|
import org.openjdk.jmh.annotations.Scope;
|
||||||
|
import org.openjdk.jmh.annotations.State;
|
||||||
|
import org.openjdk.jmh.annotations.Warmup;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@BenchmarkMode(Mode.SingleShotTime)
|
||||||
|
@Fork(1)
|
||||||
|
@OutputTimeUnit(TimeUnit.MILLISECONDS)
|
||||||
|
@Warmup(iterations = 5, time = 1)
|
||||||
|
@Measurement(iterations = 10, time = 2)
|
||||||
|
@State(Scope.Benchmark)
|
||||||
|
public class ProxyGenBench {
|
||||||
|
|
||||||
|
static final Class<?>[] INTF = new Class<?>[]{Interfaze.class};
|
||||||
|
static final IHandler HANDLER = new IHandler();
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
public void generate100Proxies() {
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
Proxy.newProxyInstance(new ClsLoader(), INTF, HANDLER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Interfaze {
|
||||||
|
default int sum(int a, int b, int c) {
|
||||||
|
return a + b + c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class IHandler implements InvocationHandler {
|
||||||
|
@Override
|
||||||
|
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ClsLoader extends ClassLoader {
|
||||||
|
|
||||||
|
public ClsLoader() {
|
||||||
|
super(Interfaze.class.getClassLoader());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
new ProxyGenBench().generate100Proxies();
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user