From 570b507b6ccec44e62d20400fc486cc13177ec0d Mon Sep 17 00:00:00 2001 From: Babneet Singh Date: Tue, 10 Oct 2023 22:38:30 -0400 Subject: [PATCH] [JDK11] Fix AccessControlException in resolveInvokeDynamic MethodHandleResolver.resolveInvokeDynamic (linkage) relies upon MethodType.fromMethodDescriptorString to derive the MethodType from the method descriptor string. Enabling OJDK's MethodType.fromMethodDescriptorString in OpenJ9 JDK11 causes an AccessControlException, which makes it unsuitable for usage during linkage. resolveInvokeDynamic (linkage) can employ its own approach to derive the MethodType from the method descriptor string. To resolve the AccessControlException, a helper method is derived from OJ9's MethodType.fromMethodDescriptorString, and it is utilized in MethodHandleResolver. The helper also enables a Map based cache per ClassLoader in both implementations. Currently, the cache is only available in OJ9 MHs. Related: #14555 Signed-off-by: Babneet Singh --- .../classes/java/lang/invoke/MethodType.java | 84 +------------ .../java/lang/invoke/MethodTypeHelper.java | 115 +++++++++++++++++- 2 files changed, 116 insertions(+), 83 deletions(-) diff --git a/jcl/src/java.base/share/classes/java/lang/invoke/MethodType.java b/jcl/src/java.base/share/classes/java/lang/invoke/MethodType.java index 7799198aea7..1791fe63636 100644 --- a/jcl/src/java.base/share/classes/java/lang/invoke/MethodType.java +++ b/jcl/src/java.base/share/classes/java/lang/invoke/MethodType.java @@ -51,7 +51,6 @@ import java.util.WeakHashMap; import com.ibm.oti.util.Msg; -import com.ibm.oti.vm.VM; /*[IF CRIU_SUPPORT]*/ import openj9.internal.criu.NotCheckpointSafe; @@ -337,43 +336,7 @@ public MethodType erase() { */ @VMCONSTANTPOOL_METHOD public static MethodType fromMethodDescriptorString(String methodDescriptor, ClassLoader loader) { - ClassLoader classLoader = loader; - if (classLoader == null) { - /*[IF JAVA_SPEC_VERSION >= 14]*/ - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkPermission(sun.security.util.SecurityConstants.GET_CLASSLOADER_PERMISSION); - } - /*[ENDIF] JAVA_SPEC_VERSION >= 14 */ - classLoader = ClassLoader.getSystemClassLoader(); - } - - // Check cache - Map classLoaderMethodTypeCache = VM.getVMLangAccess().getMethodTypeCache(classLoader); - MethodType mt = classLoaderMethodTypeCache != null ? classLoaderMethodTypeCache.get(methodDescriptor) : null; - - // MethodDescriptorString is not in cache - if (null == mt) { - // ensure '.' is not included in the descriptor - if (methodDescriptor.indexOf((int)'.') != -1) { - throw new IllegalArgumentException(methodDescriptor); - } - - // split descriptor into classes - last one is the return type - ArrayList> classes = parseIntoClasses(methodDescriptor, classLoader); - if (classes.size() == 0) { - throw new IllegalArgumentException(methodDescriptor); - } - - Class returnType = classes.remove(classes.size() - 1); - mt = methodType(returnType, classes); - if (classLoaderMethodTypeCache != null) { - classLoaderMethodTypeCache.put(mt.methodDescriptor, mt); - } - } - - return mt; + return MethodTypeHelper.fromMethodDescriptorStringInternal(methodDescriptor, loader); } /** @@ -385,7 +348,7 @@ public static MethodType fromMethodDescriptorString(String methodDescriptor, Cla */ @SuppressWarnings("unused") /* Used by native code */ private static final MethodType fromMethodDescriptorStringAppendArg(String methodDescriptor, ClassLoader loader, Class appendArgumentType) { - List> types = parseIntoClasses(methodDescriptor, loader); + List> types = MethodTypeHelper.parseIntoClasses(methodDescriptor, loader); Class returnType = types.remove(types.size() - 1); types.add(appendArgumentType); @@ -412,49 +375,6 @@ private static final Throwable throwNoClassDefFoundError(TypeNotPresentException } throw e; } - - /* - * Parse the MethodDescriptor string into a list of Class objects. The last class in the list - * is the return type. - */ - private static final ArrayList> parseIntoClasses(String methodDescriptor, ClassLoader classLoader) { - int length = methodDescriptor.length(); - if (length == 0) { - /*[MSG "K05d3", "invalid descriptor: {0}"]*/ - throw new IllegalArgumentException(Msg.getString("K05d3", methodDescriptor)); //$NON-NLS-1$ - } - - char[] signature = new char[length]; - methodDescriptor.getChars(0, length, signature, 0); - int index = 0; - boolean closeBracket = false; - - if (signature[index] != '(') { - /*[MSG "K05d4", "missing opening '(': {0}"]*/ - throw new IllegalArgumentException(Msg.getString("K05d4", methodDescriptor)); //$NON-NLS-1$ - } - index++; - - ArrayList> args = new ArrayList>(); - - while(index < length) { - /* Ensure we only see one ')' closing bracket */ - if ((signature[index] == ')')) { - if (closeBracket) { - /*[MSG "K05d5", "too many ')': {0}"]*/ - throw new IllegalArgumentException(Msg.getString("K05d5", methodDescriptor)); //$NON-NLS-1$ - } - closeBracket = true; - index++; - continue; - } - - index = MethodTypeHelper.parseIntoClass(signature, index, args, classLoader, methodDescriptor); - index++; - } - return args; - } - /** * Convenience method to convert all types to Object. diff --git a/jcl/src/java.base/share/classes/java/lang/invoke/MethodTypeHelper.java b/jcl/src/java.base/share/classes/java/lang/invoke/MethodTypeHelper.java index fe1b48cf752..8d8fd7cc4a3 100644 --- a/jcl/src/java.base/share/classes/java/lang/invoke/MethodTypeHelper.java +++ b/jcl/src/java.base/share/classes/java/lang/invoke/MethodTypeHelper.java @@ -32,7 +32,10 @@ import jdk.internal.value.PrimitiveClass; /*[ENDIF] INLINE-TYPES */ +import java.util.Map; + import com.ibm.oti.util.Msg; +import com.ibm.oti.vm.VM; /** * MethodTypeHelper - static methods @@ -270,6 +273,116 @@ static final int parseIntoClass(char[] signature, int index, ArrayList> return index; } + /** + * Parse the MethodDescriptor string into a list of Class objects. The last class in the list + * is the return type. + * + * @param methodDescriptor the method descriptor string + * @param classLoader the ClassLoader to be used or null for System ClassLoader + * @return list of classes representing the parameters and return type + * @throws IllegalArgumentException if the string is not well-formed + */ + static final ArrayList> parseIntoClasses(String methodDescriptor, ClassLoader classLoader) { + int length = methodDescriptor.length(); + if (length == 0) { + /*[MSG "K05d3", "invalid descriptor: {0}"]*/ + throw new IllegalArgumentException(Msg.getString("K05d3", methodDescriptor)); //$NON-NLS-1$ + } + + char[] signature = new char[length]; + methodDescriptor.getChars(0, length, signature, 0); + int index = 0; + boolean closeBracket = false; + + if (signature[index] != '(') { + /*[MSG "K05d4", "missing opening '(': {0}"]*/ + throw new IllegalArgumentException(Msg.getString("K05d4", methodDescriptor)); //$NON-NLS-1$ + } + index++; + + ArrayList> args = new ArrayList>(); + + while(index < length) { + /* Ensure we only see one ')' closing bracket */ + if ((signature[index] == ')')) { + if (closeBracket) { + /*[MSG "K05d5", "too many ')': {0}"]*/ + throw new IllegalArgumentException(Msg.getString("K05d5", methodDescriptor)); //$NON-NLS-1$ + } + closeBracket = true; + index++; + continue; + } + + index = parseIntoClass(signature, index, args, classLoader, methodDescriptor); + index++; + } + return args; + } + + /** + * Convenience Method to create a MethodType from bytecode-level method descriptor. + * (See JVM Spec 2nd Ed. section 4.4.3). + * + * All of the classes used in the method descriptor string must be reachable from a + * common ClassLoader or an exception will result. + * + * The ClassLoader parameter may be null, in which case the System ClassLoader will be used. + * + * Note, the Class names must use JVM syntax in the method descriptor String and therefore + * java.lang.Class will be represented as Ljava/lang/Class; + * + * Example method descriptors: + * (II)V - method taking two ints and return void + * (I)Ljava/lang/Integer; - method taking an int and returning an Integer + * ([I)I - method taking an array of ints and returning an int + * + * @param methodDescriptor the method descriptor string + * @param loader the ClassLoader to be used or null for System ClassLoader + * @return a MethodType object representing the method descriptor string + * @throws IllegalArgumentException if the string is not well-formed + * @throws TypeNotPresentException if a named type cannot be found + */ + static MethodType fromMethodDescriptorStringInternal(String methodDescriptor, ClassLoader loader) { + ClassLoader classLoader = loader; + if (classLoader == null) { + /*[IF JAVA_SPEC_VERSION >= 14]*/ + @SuppressWarnings("removal") + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkPermission(sun.security.util.SecurityConstants.GET_CLASSLOADER_PERMISSION); + } + /*[ENDIF] JAVA_SPEC_VERSION >= 14 */ + classLoader = ClassLoader.getSystemClassLoader(); + } + + // Check cache + Map classLoaderMethodTypeCache = VM.getVMLangAccess().getMethodTypeCache(classLoader); + MethodType mt = classLoaderMethodTypeCache != null ? classLoaderMethodTypeCache.get(methodDescriptor) : null; + + // MethodDescriptorString is not in cache + if (null == mt) { + // ensure '.' is not included in the descriptor + if (methodDescriptor.indexOf((int)'.') != -1) { + throw new IllegalArgumentException(methodDescriptor); + } + + // split descriptor into classes - last one is the return type + ArrayList> classes = parseIntoClasses(methodDescriptor, classLoader); + if (classes.size() == 0) { + throw new IllegalArgumentException(methodDescriptor); + } + + Class returnType = classes.remove(classes.size() - 1); + mt = MethodType.methodType(returnType, classes); + if (classLoaderMethodTypeCache != null) { + classLoaderMethodTypeCache.put(mt.toMethodDescriptorString(), mt); + } + } + + return mt; + } + /** * This helper calls MethodType.fromMethodDescriptorString(...) or * MethodType.fromMethodDescriptorStringAppendArg(...) but throws @@ -287,7 +400,7 @@ static final int parseIntoClass(char[] signature, int index, ArrayList> */ static final MethodType vmResolveFromMethodDescriptorString(String methodDescriptor, ClassLoader loader, Class appendArgumentType) throws Throwable { try { - MethodType result = MethodType.fromMethodDescriptorString(methodDescriptor, loader); + MethodType result = fromMethodDescriptorStringInternal(methodDescriptor, loader); if (null != appendArgumentType) { result = result.appendParameterTypes(appendArgumentType); }