8333854: IllegalAccessError with proxies after JDK-8332457

Reviewed-by: redestad, asotona
This commit is contained in:
Chen Liang 2024-06-18 13:51:50 +00:00
parent e681b4e9b3
commit 91bd85d65d
2 changed files with 230 additions and 77 deletions

View File

@ -47,13 +47,10 @@ import sun.security.action.GetBooleanAction;
import static java.lang.classfile.ClassFile.*; import static java.lang.classfile.ClassFile.*;
import java.lang.classfile.attribute.StackMapFrameInfo; import java.lang.classfile.attribute.StackMapFrameInfo;
import java.lang.classfile.attribute.StackMapTableAttribute; import java.lang.classfile.attribute.StackMapTableAttribute;
import java.lang.constant.ConstantDescs;
import static java.lang.constant.ConstantDescs.*; import static java.lang.constant.ConstantDescs.*;
import static jdk.internal.constant.ConstantUtils.*; import static jdk.internal.constant.ConstantUtils.*;
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
* for the java.lang.reflect.Proxy API. * for the java.lang.reflect.Proxy API.
@ -67,7 +64,10 @@ final class ProxyGenerator {
ClassFile.of(ClassFile.StackMapsOption.DROP_STACK_MAPS); ClassFile.of(ClassFile.StackMapsOption.DROP_STACK_MAPS);
private static final ClassDesc private static final ClassDesc
CD_ClassLoader = ReferenceClassDescImpl.ofValidated("Ljava/lang/ClassLoader;"),
CD_Class_array = ReferenceClassDescImpl.ofValidated("[Ljava/lang/Class;"), CD_Class_array = ReferenceClassDescImpl.ofValidated("[Ljava/lang/Class;"),
CD_ClassNotFoundException = ReferenceClassDescImpl.ofValidated("Ljava/lang/ClassNotFoundException;"),
CD_NoClassDefFoundError = ReferenceClassDescImpl.ofValidated("Ljava/lang/NoClassDefFoundError;"),
CD_IllegalAccessException = ReferenceClassDescImpl.ofValidated("Ljava/lang/IllegalAccessException;"), CD_IllegalAccessException = ReferenceClassDescImpl.ofValidated("Ljava/lang/IllegalAccessException;"),
CD_InvocationHandler = ReferenceClassDescImpl.ofValidated("Ljava/lang/reflect/InvocationHandler;"), CD_InvocationHandler = ReferenceClassDescImpl.ofValidated("Ljava/lang/reflect/InvocationHandler;"),
CD_Method = ReferenceClassDescImpl.ofValidated("Ljava/lang/reflect/Method;"), CD_Method = ReferenceClassDescImpl.ofValidated("Ljava/lang/reflect/Method;"),
@ -83,8 +83,9 @@ final class ProxyGenerator {
MTD_void_String = MethodTypeDescImpl.ofValidated(CD_void, CD_String), MTD_void_String = MethodTypeDescImpl.ofValidated(CD_void, CD_String),
MTD_void_Throwable = MethodTypeDescImpl.ofValidated(CD_void, CD_Throwable), MTD_void_Throwable = MethodTypeDescImpl.ofValidated(CD_void, CD_Throwable),
MTD_Class = MethodTypeDescImpl.ofValidated(CD_Class), MTD_Class = MethodTypeDescImpl.ofValidated(CD_Class),
MTD_Class_array = MethodTypeDescImpl.ofValidated(CD_Class_array), MTD_Class_String_boolean_ClassLoader = MethodTypeDescImpl.ofValidated(CD_Class, CD_String, CD_boolean, CD_ClassLoader),
MTD_Method_String_Class_array = MethodTypeDescImpl.ofValidated(CD_Method, ConstantDescs.CD_String, CD_Class_array), MTD_ClassLoader = MethodTypeDescImpl.ofValidated(CD_ClassLoader),
MTD_Method_String_Class_array = MethodTypeDescImpl.ofValidated(CD_Method, CD_String, CD_Class_array),
MTD_MethodHandles$Lookup = MethodTypeDescImpl.ofValidated(CD_MethodHandles_Lookup), MTD_MethodHandles$Lookup = MethodTypeDescImpl.ofValidated(CD_MethodHandles_Lookup),
MTD_MethodHandles$Lookup_MethodHandles$Lookup = MethodTypeDescImpl.ofValidated(CD_MethodHandles_Lookup, CD_MethodHandles_Lookup), MTD_MethodHandles$Lookup_MethodHandles$Lookup = MethodTypeDescImpl.ofValidated(CD_MethodHandles_Lookup, CD_MethodHandles_Lookup),
MTD_Object_Object_Method_ObjectArray = MethodTypeDescImpl.ofValidated(CD_Object, CD_Object, CD_Method, CD_Object_array), MTD_Object_Object_Method_ObjectArray = MethodTypeDescImpl.ofValidated(CD_Object, CD_Object, CD_Method, CD_Object_array),
@ -109,34 +110,33 @@ final class ProxyGenerator {
"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 HASH_CODE_METHOD; private static final Method OBJECT_HASH_CODE_METHOD;
private static final ProxyMethod EQUALS_METHOD; private static final Method OBJECT_EQUALS_METHOD;
private static final ProxyMethod TO_STRING_METHOD; private static final Method OBJECT_TO_STRING_METHOD;
static { static {
try { try {
HASH_CODE_METHOD = new ProxyMethod(Object.class.getMethod("hashCode")); OBJECT_HASH_CODE_METHOD = Object.class.getMethod("hashCode");
EQUALS_METHOD = new ProxyMethod(Object.class.getMethod("equals", Object.class)); OBJECT_EQUALS_METHOD = Object.class.getMethod("equals", Object.class);
TO_STRING_METHOD = new ProxyMethod(Object.class.getMethod("toString")); OBJECT_TO_STRING_METHOD = Object.class.getMethod("toString");
} catch (NoSuchMethodException e) { } catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage()); throw new NoSuchMethodError(e.getMessage());
} }
} }
private final ConstantPoolBuilder cp; private final ConstantPoolBuilder cp;
private final List<StackMapFrameInfo.VerificationTypeInfo> throwableStack; private final List<StackMapFrameInfo.VerificationTypeInfo> classLoaderLocal, throwableStack;
private final NameAndTypeEntry exInit; private final NameAndTypeEntry exInit;
private final ClassEntry object, proxy, ute; private final ClassEntry objectCE, proxyCE, uteCE, classCE;
private final FieldRefEntry handlerField; private final FieldRefEntry handlerField;
private final InterfaceMethodRefEntry invoke; private final InterfaceMethodRefEntry invocationHandlerInvoke;
private final MethodRefEntry uteInit; private final MethodRefEntry uteInit, classGetMethod, classForName, throwableGetMessage;
private final DirectMethodHandleDesc bsm;
/** /**
* Name of proxy class * ClassEntry for this proxy class
*/ */
private final ClassEntry classEntry; private final ClassEntry thisClassCE;
/** /**
* Proxy interfaces * Proxy interfaces
@ -155,6 +155,12 @@ 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.
@ -165,18 +171,23 @@ final class ProxyGenerator {
private ProxyGenerator(String className, List<Class<?>> interfaces, private ProxyGenerator(String className, List<Class<?>> interfaces,
int accessFlags) { int accessFlags) {
this.cp = ConstantPoolBuilder.of(); this.cp = ConstantPoolBuilder.of();
this.classEntry = cp.classEntry(ConstantUtils.binaryNameToDesc(className)); this.thisClassCE = cp.classEntry(ConstantUtils.binaryNameToDesc(className));
this.interfaces = interfaces; this.interfaces = interfaces;
this.accessFlags = accessFlags; this.accessFlags = accessFlags;
this.throwableStack = List.of(StackMapFrameInfo.ObjectVerificationTypeInfo.of(cp.classEntry(CD_Throwable))); var throwable = cp.classEntry(CD_Throwable);
this.classLoaderLocal = List.of(StackMapFrameInfo.ObjectVerificationTypeInfo.of(cp.classEntry(CD_ClassLoader)));
this.throwableStack = List.of(StackMapFrameInfo.ObjectVerificationTypeInfo.of(throwable));
this.exInit = cp.nameAndTypeEntry(INIT_NAME, MTD_void_String); this.exInit = cp.nameAndTypeEntry(INIT_NAME, MTD_void_String);
this.object = cp.classEntry(CD_Object); this.objectCE = cp.classEntry(CD_Object);
this.proxy = cp.classEntry(CD_Proxy); this.proxyCE = cp.classEntry(CD_Proxy);
this.handlerField = cp.fieldRefEntry(proxy, cp.nameAndTypeEntry(NAME_HANDLER_FIELD, CD_InvocationHandler)); this.classCE = cp.classEntry(CD_Class);
this.invoke = cp.interfaceMethodRefEntry(CD_InvocationHandler, "invoke", MTD_Object_Object_Method_ObjectArray); this.handlerField = cp.fieldRefEntry(proxyCE, cp.nameAndTypeEntry(NAME_HANDLER_FIELD, CD_InvocationHandler));
this.ute = cp.classEntry(CD_UndeclaredThrowableException); this.invocationHandlerInvoke = cp.interfaceMethodRefEntry(CD_InvocationHandler, "invoke", MTD_Object_Object_Method_ObjectArray);
this.uteInit = cp.methodRefEntry(ute, cp.nameAndTypeEntry(INIT_NAME, MTD_void_Throwable)); this.uteCE = cp.classEntry(CD_UndeclaredThrowableException);
this.bsm = ConstantDescs.ofConstantBootstrap(classEntry.asSymbol(), "$getMethod", CD_Method, CD_Class, CD_String, CD_MethodType); this.uteInit = cp.methodRefEntry(uteCE, cp.nameAndTypeEntry(INIT_NAME, MTD_void_Throwable));
this.classGetMethod = cp.methodRefEntry(classCE, cp.nameAndTypeEntry("getMethod", MTD_Method_String_Class_array));
this.classForName = cp.methodRefEntry(classCE, cp.nameAndTypeEntry("forName", MTD_Class_String_boolean_ClassLoader));
this.throwableGetMessage = cp.methodRefEntry(throwable, cp.nameAndTypeEntry("getMessage", MTD_String));
} }
/** /**
@ -435,9 +446,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(HASH_CODE_METHOD); addProxyMethod(new ProxyMethod(OBJECT_HASH_CODE_METHOD, "m0"));
addProxyMethod(EQUALS_METHOD); addProxyMethod(new ProxyMethod(OBJECT_EQUALS_METHOD, "m1"));
addProxyMethod(TO_STRING_METHOD); addProxyMethod(new ProxyMethod(OBJECT_TO_STRING_METHOD, "m2"));
/* /*
* Accumulate all of the methods from the proxy interfaces. * Accumulate all of the methods from the proxy interfaces.
@ -458,20 +469,23 @@ final class ProxyGenerator {
checkReturnTypes(sigmethods); checkReturnTypes(sigmethods);
} }
return CF_CONTEXT.build(classEntry, cp, clb -> { return CF_CONTEXT.build(thisClassCE, cp, clb -> {
clb.withSuperclass(proxy); clb.withSuperclass(proxyCE);
clb.withFlags(accessFlags); clb.withFlags(accessFlags);
clb.withInterfaces(toClassEntries(cp, interfaces)); clb.withInterfaces(toClassEntries(cp, interfaces));
generateConstructor(clb); 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, CD_Method, ACC_PRIVATE | ACC_STATIC | ACC_FINAL);
// Generate code for proxy method // Generate code for proxy method
pm.generateMethod(this, clb); pm.generateMethod(clb);
} }
} }
generateBootstrapMethod(clb); generateStaticInitializer(clb);
generateLookupAccessor(clb); generateLookupAccessor(clb);
}); });
} }
@ -514,7 +528,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, "m" + proxyMethodCount++));
} }
/** /**
@ -536,32 +550,56 @@ final class ProxyGenerator {
clb.withMethodBody(INIT_NAME, MTD_void_InvocationHandler, ACC_PUBLIC, cob -> cob clb.withMethodBody(INIT_NAME, MTD_void_InvocationHandler, ACC_PUBLIC, cob -> cob
.aload(0) .aload(0)
.aload(1) .aload(1)
.invokespecial(cp.methodRefEntry(proxy, cp.nameAndTypeEntry(INIT_NAME, MTD_void_InvocationHandler))) .invokespecial(cp.methodRefEntry(proxyCE,
cp.nameAndTypeEntry(INIT_NAME, MTD_void_InvocationHandler)))
.return_()); .return_());
} }
/** /**
* Generate CONDY bootstrap method for the proxy class to retrieve {@link Method} instances. * Generate the class initializer.
* Discussion: Currently, for Proxy to work with SecurityManager,
* we rely on the parameter classes of the methods to be computed
* from Proxy instead of via user code paths like bootstrap method
* lazy evaluation. That might change if we can pass in the live
* Method objects directly..
*/ */
private void generateBootstrapMethod(ClassBuilder clb) { private void generateStaticInitializer(ClassBuilder clb) {
clb.withMethodBody(bsm.methodName(), bsm.invocationType(), ClassFile.ACC_PRIVATE | ClassFile.ACC_STATIC, cob -> { clb.withMethodBody(CLASS_INIT_NAME, MTD_void, ACC_STATIC, cob -> {
cob.aload(3) //interface Class // Put ClassLoader at local variable index 0, used by
.aload(4) //interface method name String // Class.forName(String, boolean, ClassLoader) calls
.aload(5) //interface MethodType cob.ldc(thisClassCE)
.invokevirtual(CD_MethodType, "parameterArray", MTD_Class_array) .invokevirtual(cp.methodRefEntry(classCE,
.invokevirtual(ConstantDescs.CD_Class, "getMethod", MTD_Method_String_Class_array) cp.nameAndTypeEntry("getClassLoader", MTD_ClassLoader)))
.areturn(); .astore(0);
Label failLabel = cob.newBoundLabel(); var ts = cob.newBoundLabel();
ClassEntry nsme = cp.classEntry(CD_NoSuchMethodError); for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
cob.exceptionCatch(cob.startLabel(), failLabel, failLabel, CD_NoSuchMethodException) for (ProxyMethod pm : sigmethods) {
.new_(nsme) pm.codeFieldInitialization(cob);
}
}
cob.return_();
var c1 = cob.newBoundLabel();
var nsmError = cp.classEntry(CD_NoSuchMethodError);
cob.exceptionCatch(ts, c1, c1, CD_NoSuchMethodException)
.new_(nsmError)
.dup_x1() .dup_x1()
.swap() .swap()
.invokevirtual(cp.methodRefEntry(CD_Throwable, "getMessage", MTD_String)) .invokevirtual(throwableGetMessage)
.invokespecial(cp.methodRefEntry(nsme, exInit)) .invokespecial(cp.methodRefEntry(nsmError, exInit))
.athrow() .athrow();
.with(StackMapTableAttribute.of(List.of( var c2 = cob.newBoundLabel();
StackMapFrameInfo.of(failLabel, List.of(), throwableStack)))); var ncdfError = cp.classEntry(CD_NoClassDefFoundError);
cob.exceptionCatch(ts, c1, c2, CD_ClassNotFoundException)
.new_(ncdfError)
.dup_x1()
.swap()
.invokevirtual(throwableGetMessage)
.invokespecial(cp.methodRefEntry(ncdfError, exInit))
.athrow();
cob.with(StackMapTableAttribute.of(List.of(
StackMapFrameInfo.of(c1, classLoaderLocal, throwableStack),
StackMapFrameInfo.of(c2, classLoaderLocal, throwableStack))));
}); });
} }
@ -581,7 +619,7 @@ final class ProxyGenerator {
ClassEntry iae = cp.classEntry(CD_IllegalAccessException); ClassEntry iae = cp.classEntry(CD_IllegalAccessException);
cob.aload(cob.parameterSlot(0)) cob.aload(cob.parameterSlot(0))
.invokevirtual(cp.methodRefEntry(mhl, cp.nameAndTypeEntry("lookupClass", MTD_Class))) .invokevirtual(cp.methodRefEntry(mhl, cp.nameAndTypeEntry("lookupClass", MTD_Class)))
.ldc(proxy) .ldc(proxyCE)
.if_acmpne(failLabel) .if_acmpne(failLabel)
.aload(cob.parameterSlot(0)) .aload(cob.parameterSlot(0))
.invokevirtual(cp.methodRefEntry(mhl, cp.nameAndTypeEntry("hasFullPrivilegeAccess", MTD_boolean))) .invokevirtual(cp.methodRefEntry(mhl, cp.nameAndTypeEntry("hasFullPrivilegeAccess", MTD_boolean)))
@ -607,24 +645,29 @@ final class ProxyGenerator {
* being generated: a method whose implementation will encode and * being generated: a method whose implementation will encode and
* dispatch invocations to the proxy instance's invocation handler. * dispatch invocations to the proxy instance's invocation handler.
*/ */
private static class ProxyMethod { private class ProxyMethod {
private final Method method; private final Method method;
private final String shortSignature; private final String shortSignature;
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 String methodFieldName;
private Class<?>[] exceptionTypes; private Class<?>[] exceptionTypes;
private final FieldRefEntry methodField;
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) { Class<?> fromClass, String methodFieldName) {
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;
this.methodField = cp.fieldRefEntry(thisClassCE,
cp.nameAndTypeEntry(methodFieldName, CD_Method));
} }
/** /**
@ -632,17 +675,16 @@ final class ProxyGenerator {
* *
* @param method The method for which to create a proxy * @param method The method for which to create a proxy
*/ */
private ProxyMethod(Method method) { private ProxyMethod(Method method, String methodFieldName) {
this(method, method.toShortSignature(), this(method, method.toShortSignature(),
method.getSharedParameterTypes(), method.getReturnType(), method.getSharedParameterTypes(), method.getReturnType(),
method.getSharedExceptionTypes(), method.getDeclaringClass()); method.getSharedExceptionTypes(), method.getDeclaringClass(), methodFieldName);
} }
/** /**
* Generate this method, including the code and exception table entry. * Generate this method, including the code and exception table entry.
*/ */
private void generateMethod(ProxyGenerator pg, ClassBuilder clb) { private void generateMethod(ClassBuilder clb) {
var cp = pg.cp;
var desc = methodTypeDesc(returnType, parameterTypes); var desc = methodTypeDesc(returnType, parameterTypes);
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;
@ -650,17 +692,14 @@ final class ProxyGenerator {
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(0) cob.aload(cob.receiverSlot())
.getfield(pg.handlerField) .getfield(handlerField)
.aload(0) .aload(cob.receiverSlot())
.ldc(DynamicConstantDesc.of(pg.bsm, .getstatic(methodField);
referenceClassDesc(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(pg.object); .anewarray(objectCE);
for (int i = 0; i < parameterTypes.length; i++) { for (int i = 0; i < parameterTypes.length; i++) {
cob.dup() cob.dup()
.loadConstant(i); .loadConstant(i);
@ -671,7 +710,7 @@ final class ProxyGenerator {
cob.aconst_null(); cob.aconst_null();
} }
cob.invokeinterface(pg.invoke); cob.invokeinterface(invocationHandlerInvoke);
if (returnType == void.class) { if (returnType == void.class) {
cob.pop() cob.pop()
@ -687,14 +726,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_(pg.ute) .new_(uteCE)
.dup_x1() .dup_x1()
.swap() .swap()
.invokespecial(pg.uteInit) .invokespecial(uteInit)
.athrow() .athrow()
.with(StackMapTableAttribute.of(List.of( .with(StackMapTableAttribute.of(List.of(
StackMapFrameInfo.of(c1, List.of(), pg.throwableStack), StackMapFrameInfo.of(c1, List.of(), throwableStack),
StackMapFrameInfo.of(c2, List.of(), pg.throwableStack)))); StackMapFrameInfo.of(c2, List.of(), throwableStack))));
} }
})); }));
} }
@ -709,7 +748,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.constantPool())); cob.invokestatic(prim.wrapperMethodRef(cp));
} else { } else {
cob.aload(slot); cob.aload(slot);
} }
@ -725,7 +764,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(cob.constantPool())) .invokevirtual(prim.unwrapMethodRef(cp))
.return_(TypeKind.from(type).asLoadable()); .return_(TypeKind.from(type).asLoadable());
} else { } else {
cob.checkcast(referenceClassDesc(type)) cob.checkcast(referenceClassDesc(type))
@ -733,6 +772,57 @@ 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.
* The generated code must be run in an AccessController.doPrivileged
* block if a SecurityManager is present, as otherwise the code
* cannot pass {@code null} ClassLoader to forName.
*/
private void codeFieldInitialization(CodeBuilder cob) {
var cp = cob.constantPool();
codeClassForName(cob, fromClass);
cob.ldc(method.getName())
.loadConstant(parameterTypes.length)
.anewarray(classCE);
// 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(cp));
} else {
codeClassForName(cob, parameterTypes[i]);
}
cob.aastore();
}
// lookup the method
cob.invokevirtual(classGetMethod)
.putstatic(methodField);
}
/*
* =============== 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(classForName);
}
@Override @Override
public String toString() { public String toString() {
return method.toShortString(); return method.toShortString();
@ -799,5 +889,9 @@ final class ProxyGenerator {
public MethodRefEntry unwrapMethodRef(ConstantPoolBuilder cp) { public MethodRefEntry unwrapMethodRef(ConstantPoolBuilder cp) {
return cp.methodRefEntry(wrapperClass, unwrapMethodName, unwrapMethodType); return cp.methodRefEntry(wrapperClass, unwrapMethodName, unwrapMethodType);
} }
public FieldRefEntry typeFieldRef(ConstantPoolBuilder cp) {
return cp.fieldRefEntry(wrapperClass, "TYPE", CD_Class);
}
} }
} }

View File

@ -0,0 +1,59 @@
/*
* 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.
*/
/*
* @test
* @bug 8333854
* @summary Test invoking a method in a proxy interface with package-private
* classes or interfaces in its method type
* @run junit NonPublicMethodTypeTest
*/
import org.junit.jupiter.api.Test;
import java.lang.reflect.Proxy;
import static org.junit.jupiter.api.Assertions.assertNotSame;
public final class NonPublicMethodTypeTest {
interface NonPublicWorker {
void work();
}
public interface PublicWorkable {
void accept(NonPublicWorker worker);
}
@Test
public void test() {
PublicWorkable proxy = (PublicWorkable) Proxy.newProxyInstance(
NonPublicMethodTypeTest.class.getClassLoader(),
new Class[] {PublicWorkable.class},
(_, _, _) -> null);
assertNotSame(NonPublicWorker.class.getPackage(),
proxy.getClass().getPackage(),
"Proxy class should not be able to access method parameter " +
"NonPublic type's package");
proxy.accept(() -> {}); // Call should not fail
}
}