You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2021/06/25 05:33:33 UTC
[groovy] branch master updated: GROOVY-10145: Support JDK16 (#1598)
This is an automated email from the ASF dual-hosted git repository.
sunlan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new d4f1636 GROOVY-10145: Support JDK16 (#1598)
d4f1636 is described below
commit d4f1636dec61f5236f1f0532a1d3bdab45446501
Author: Daniel.Sun <su...@apache.org>
AuthorDate: Fri Jun 25 13:33:27 2021 +0800
GROOVY-10145: Support JDK16 (#1598)
* GROOVY-10145: Support JDK16
* Bump JVM compatibility version to 11
* Trivial refactoring: move `isSealed` to `ReflectionUtils`
---
.travis.yml | 6 +-
build.gradle | 2 +-
.../src/main/groovy/org.apache.groovy-core.gradle | 14 ++-
.../main/groovy/org.apache.groovy-tested.gradle | 7 ++
src/main/java/groovy/lang/MetaClassImpl.java | 139 +++++++++++++++++++++
.../org/apache/groovy/lang/GroovyObjectHelper.java | 127 +++++++++++++++++++
.../org/codehaus/groovy/classgen/Verifier.java | 29 +++++
.../groovy/control/CompilerConfiguration.java | 2 +-
.../groovy/reflection/ReflectionUtils.java | 63 +++++++---
.../org/codehaus/groovy/runtime/ArrayUtil.java | 7 +-
.../groovy/runtime/ProxyGeneratorAdapter.java | 4 +-
.../codehaus/groovy/vmplugin/VMPluginFactory.java | 1 +
.../org/codehaus/groovy/vmplugin/v10/Java10.java | 5 +
.../org/codehaus/groovy/vmplugin/v16/Java16.java | 116 +++++++++++++++++
.../vmplugin/v16/PluginDefaultGroovyMethods.java} | 24 ++--
.../vmplugin/v16/ProxyDefaultMethodHandle.java | 38 ++++++
.../org/codehaus/groovy/vmplugin/v8/Java8.java | 23 +++-
.../org/codehaus/groovy/vmplugin/v9/Java9.java | 10 +-
src/test/groovy/IllegalAccessTests.groovy | 4 +-
src/test/groovy/bugs/Groovy8815.groovy | 7 ++
.../groovy/classgen/TransientMetaClassTest.groovy | 4 +-
.../codehaus/groovy/classgen/VerifierTest.groovy | 72 +++++++++++
.../codehaus/groovy/reflection/SecurityTest.java | 3 +
.../runtime/typehandling/NumberMathTest.groovy | 2 +-
.../TransformsAndCustomClassLoadersTest.groovy | 5 +-
25 files changed, 662 insertions(+), 52 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 14edd60..f6a28cd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,6 +25,8 @@ install:
matrix:
include:
+ - env: BC='indy' FEATURE='16' TARGET_JAVA_HOME="/home/travis/openjdk$FEATURE" LICENSE='GPL'
+ jdk: openjdk11
- env: BC='indy'
jdk: openjdk15
- env: BC='indy'
@@ -50,7 +52,9 @@ before_script:
script:
- ./gradlew -version
- - if [ "$BC" == "indy" ]; then travis_wait 60 ./gradlew test; elif [ "$BC" == "sonar" ]; then travis_wait 60 ./gradlew sonarqube -Dsonar.login=$SONAR_LOGIN -Pcoverage=true; fi
+ - if [ "$TARGET_JAVA_HOME" != "" ]; then wget https://github.com/sormuras/bach/raw/master/install-jdk.sh -P /tmp/ && chmod 755 /tmp/install-jdk.sh; fi
+ - if [ "$TARGET_JAVA_HOME" != "" ]; then /tmp/install-jdk.sh --target "$TARGET_JAVA_HOME" --workspace "/home/travis/.cache/install-jdk" --feature "$FEATURE" --license "$LICENSE" --cacerts; fi
+ - if [ "$BC" == "indy" ]; then travis_wait 60 ./gradlew test -Ptarget.java.home=$TARGET_JAVA_HOME; elif [ "$BC" == "sonar" ]; then travis_wait 60 ./gradlew sonarqube -Dsonar.login=$SONAR_LOGIN -Pcoverage=true; fi
# As recommended in:
# https://docs.travis-ci.com/user/languages/java/#Caching
diff --git a/build.gradle b/build.gradle
index 28a08ff..8cc55d9 100644
--- a/build.gradle
+++ b/build.gradle
@@ -229,7 +229,7 @@ tasks.named('test') {
dependsOn testExtensionModuleJar
}
-if (!JavaVersion.current().java10Compatible) {
+if (!JavaVersion.current().java11Compatible) {
logger.lifecycle '''
**************************************** WARNING ********************************************
****** You are running the build with an older JDK. NEVER try to release with 1.8. ******
diff --git a/buildSrc/src/main/groovy/org.apache.groovy-core.gradle b/buildSrc/src/main/groovy/org.apache.groovy-core.gradle
index e937221..4638c77 100644
--- a/buildSrc/src/main/groovy/org.apache.groovy-core.gradle
+++ b/buildSrc/src/main/groovy/org.apache.groovy-core.gradle
@@ -50,6 +50,10 @@ sourceSets {
exclude '**/v10/*'
exclude '**/vm10/*'
}
+ if (!JavaVersion.current().isJava11Compatible()) {
+ exclude '**/v16/*'
+ exclude '**/vm16/*'
+ }
}
groovy {
if (!JavaVersion.current().isJava9Compatible()) {
@@ -60,6 +64,10 @@ sourceSets {
exclude '**/v10/*'
exclude '**/vm10/*'
}
+ if (!JavaVersion.current().isJava11Compatible()) {
+ exclude '**/v16/*'
+ exclude '**/vm16/*'
+ }
}
antlr {
srcDirs = ['src/antlr']
@@ -79,6 +87,10 @@ sourceSets {
exclude '**/v10/*'
exclude '**/vm10/*'
}
+ if (!JavaVersion.current().isJava11Compatible()) {
+ exclude '**/v16/*'
+ exclude '**/vm16/*'
+ }
}
resources {
srcDirs += ['src/test-resources']
@@ -165,7 +177,7 @@ tasks.named('compileTestGroovy') {
tasks.register('checkCompatibility') {
doLast {
- assert JavaVersion.current().java9Compatible
+ assert JavaVersion.current().java11Compatible
}
}
diff --git a/buildSrc/src/main/groovy/org.apache.groovy-tested.gradle b/buildSrc/src/main/groovy/org.apache.groovy-tested.gradle
index e2cb9de..254b9e4 100644
--- a/buildSrc/src/main/groovy/org.apache.groovy-tested.gradle
+++ b/buildSrc/src/main/groovy/org.apache.groovy-tested.gradle
@@ -55,6 +55,13 @@ tasks.withType(Test).configureEach {
'gradle.home': gradle.gradleHomeDir, // this is needed by the security.policy
'user.home': temporaryDir.absolutePath // make sure tests are isolated from real user home or tests using Grape may fail
+ if (rootProject.hasProperty('target.java.home')) {
+ String targetJavaHome = rootProject.property('target.java.home')
+ if (targetJavaHome?.trim()) {
+ executable = "${targetJavaHome}/bin/java"
+ }
+ }
+
forkEvery = 50
maxParallelForks = sharedConfiguration.isRunningOnCI ? 1 : Runtime.runtime.availableProcessors().intdiv(2) ?: 1
scanForTestClasses = true
diff --git a/src/main/java/groovy/lang/MetaClassImpl.java b/src/main/java/groovy/lang/MetaClassImpl.java
index 2046b3e..e8068f2 100644
--- a/src/main/java/groovy/lang/MetaClassImpl.java
+++ b/src/main/java/groovy/lang/MetaClassImpl.java
@@ -19,6 +19,7 @@
package groovy.lang;
import org.apache.groovy.internal.util.UncheckedThrow;
+import org.apache.groovy.lang.GroovyObjectHelper;
import org.apache.groovy.util.BeanUtils;
import org.apache.groovy.util.SystemUtil;
import org.codehaus.groovy.GroovyBugError;
@@ -35,8 +36,10 @@ import org.codehaus.groovy.reflection.ClassInfo;
import org.codehaus.groovy.reflection.GeneratedMetaMethod;
import org.codehaus.groovy.reflection.ParameterTypes;
import org.codehaus.groovy.reflection.ReflectionCache;
+import org.codehaus.groovy.reflection.ReflectionUtils;
import org.codehaus.groovy.reflection.android.AndroidSupport;
import org.codehaus.groovy.runtime.ArrayTypeUtils;
+import org.codehaus.groovy.runtime.ArrayUtil;
import org.codehaus.groovy.runtime.ConvertedClosure;
import org.codehaus.groovy.runtime.CurriedClosure;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
@@ -85,6 +88,8 @@ import java.beans.BeanInfo;
import java.beans.EventSetDescriptor;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
@@ -106,6 +111,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -115,6 +121,7 @@ import static java.lang.Character.isUpperCase;
import static org.apache.groovy.util.Arrays.concat;
import static org.codehaus.groovy.ast.tools.GeneralUtils.inSamePackage;
import static org.codehaus.groovy.reflection.ReflectionCache.isAssignableFrom;
+import static org.codehaus.groovy.reflection.ReflectionUtils.parameterTypeMatches;
/**
* Allows methods to be dynamically added to existing classes at runtime
@@ -1127,6 +1134,138 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
*/
@Override
public Object invokeMethod(Class sender, Object object, String methodName, Object[] originalArguments, boolean isCallToSuper, boolean fromInsideClass) {
+ try {
+ return doInvokeMethod(sender, object, methodName, originalArguments, isCallToSuper, fromInsideClass);
+ } catch (MissingMethodException mme) {
+ return doInvokeMethodFallback(sender, object, methodName, originalArguments, isCallToSuper, mme);
+ }
+ }
+
+ private static class MethodHandleHolder {
+ private static final MethodHandle CLONE_ARRAY_METHOD_HANDLE;
+ static {
+ Optional<Method> methodOptional = Arrays.stream(ArrayUtil.class.getDeclaredMethods()).filter(m -> "cloneArray".equals(m.getName())).findFirst();
+ if (!methodOptional.isPresent()) {
+ throw new GroovyBugError("Failed to find `cloneArray` method in class `" + ArrayUtil.class.getName() + "`");
+ }
+ Method cloneArrayMethod = methodOptional.get();
+
+ try {
+ CLONE_ARRAY_METHOD_HANDLE = MethodHandles.lookup().in(ArrayUtil.class).unreflect(cloneArrayMethod);
+ } catch (IllegalAccessException e) {
+ throw new GroovyBugError("Failed to create method handle for " + cloneArrayMethod);
+ }
+ }
+ private MethodHandleHolder() {}
+ }
+
+ private static final ClassValue<Map<String, Set<Method>>> SPECIAL_METHODS_MAP = new ClassValue<Map<String, Set<Method>>>() {
+ @Override
+ protected Map<String, Set<Method>> computeValue(Class<?> type) {
+ return new ConcurrentHashMap<>(4);
+ }
+ };
+
+ private Object doInvokeMethodFallback(Class sender, Object object, String methodName, Object[] originalArguments, boolean isCallToSuper, MissingMethodException mme) {
+ MethodHandles.Lookup lookup = null;
+ if (object instanceof GroovyObject) {
+ Optional<MethodHandles.Lookup> lookupOptional = GroovyObjectHelper.lookup((GroovyObject) object);
+ if (!lookupOptional.isPresent()) throw mme;
+ lookup = lookupOptional.get();
+ }
+
+ final Class<?> receiverClass = object.getClass();
+ if (isCallToSuper) {
+ if (null == lookup) throw mme;
+ Class[] argTypes = MetaClassHelper.castArgumentsToClassArray(originalArguments);
+ Method superMethod = findMethod(sender, methodName, argTypes);
+ if (null == superMethod) throw mme;
+ MethodHandle superMethodHandle;
+ try {
+ superMethodHandle = lookup.unreflectSpecial(superMethod, receiverClass);
+ } catch (IllegalAccessException e) {
+ throw mme;
+ }
+ cacheMethod(sender, superMethod);
+ try {
+ return superMethodHandle.bindTo(object).invokeWithArguments(originalArguments);
+ } catch (Throwable t) {
+ throw new GroovyRuntimeException(t);
+ }
+ } else {
+ if (receiverClass.isArray()) {
+ if ("clone".equals(methodName) && 0 == originalArguments.length) {
+ try {
+ Object[] array = (Object[]) object;
+ Object[] result = (Object[]) MethodHandleHolder.CLONE_ARRAY_METHOD_HANDLE.invokeExact(array);
+ return result;
+ } catch (Throwable t) {
+ throw new GroovyRuntimeException(t);
+ }
+ }
+ throw mme;
+ }
+
+ if (null == lookup) {
+ try {
+ lookup = MethodHandles.lookup().in(receiverClass);
+ } catch (IllegalArgumentException e) {
+ throw mme;
+ }
+ }
+ Class[] argTypes = MetaClassHelper.castArgumentsToClassArray(originalArguments);
+ Method thisMethod = findMethod(receiverClass, methodName, argTypes);
+ if (null == thisMethod) throw mme;
+ MethodHandle thisMethodHandle;
+ try {
+ thisMethodHandle = lookup.unreflect(thisMethod);
+ } catch (IllegalAccessException e) {
+ throw mme;
+ }
+ cacheMethod(receiverClass, thisMethod);
+ try {
+ return thisMethodHandle.bindTo(object).invokeWithArguments(originalArguments);
+ } catch (Throwable t) {
+ throw new GroovyRuntimeException(t);
+ }
+ }
+ }
+
+ private static void cacheMethod(Class clazz, Method method) {
+ SPECIAL_METHODS_MAP.get(clazz)
+ .computeIfAbsent(method.getName(), k -> Collections.newSetFromMap(new ConcurrentHashMap<>(2)))
+ .add(method);
+ }
+
+ private static Method findMethod(Class clazz, String methodName, Class[] argTypes) {
+ Set<Method> methods = SPECIAL_METHODS_MAP.get(clazz).get(methodName);
+
+ if (null != methods) {
+ for (Method method : methods) {
+ if (parameterTypeMatches(method.getParameterTypes(), argTypes)) {
+ return method;
+ }
+ }
+ }
+
+ return doFindMethod(clazz, methodName, argTypes);
+ }
+
+ private static Method doFindMethod(Class clazz, String messageName, Class[] argTypes) {
+ for (Class<?> c = clazz; null != c; c = c.getSuperclass()) {
+ List<Method> declaredMethodList = ReflectionUtils.getDeclaredMethods(c, messageName, argTypes);
+ if (!declaredMethodList.isEmpty()) {
+ Method superMethod = declaredMethodList.get(0);
+ if (Modifier.isAbstract(superMethod.getModifiers())) {
+ continue;
+ }
+ return superMethod;
+ }
+ }
+ return null;
+ }
+
+ private Object doInvokeMethod(Class sender, Object object, String methodName, Object[] originalArguments, boolean isCallToSuper, boolean fromInsideClass) {
checkInitalised();
if (object == null) {
throw new NullPointerException("Cannot invoke method: " + methodName + " on null object");
diff --git a/src/main/java/org/apache/groovy/lang/GroovyObjectHelper.java b/src/main/java/org/apache/groovy/lang/GroovyObjectHelper.java
new file mode 100644
index 0000000..de260d2
--- /dev/null
+++ b/src/main/java/org/apache/groovy/lang/GroovyObjectHelper.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.groovy.lang;
+
+import groovy.lang.GroovyObject;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Helper for {@link groovy.lang.GroovyObject}
+ *
+ * @since 4.0.0
+ */
+public class GroovyObjectHelper {
+ /**
+ * Get the {@link Lookup} instance of the {@link GroovyObject} instance
+ *
+ * @param groovyObject the {@link GroovyObject} instance
+ * @return the {@link Lookup} instance
+ * @since 4.0.0
+ */
+ public static Optional<Lookup> lookup(GroovyObject groovyObject) {
+ AtomicReference<Lookup> lookupAtomicRef = LOOKUP_MAP.get(groovyObject.getClass());
+ Lookup lookup = lookupAtomicRef.get();
+ if (null != lookup) return Optional.of(lookup);
+
+ final Class<? extends GroovyObject> groovyObjectClass = groovyObject.getClass();
+ if (groovyObjectClass.isMemberClass() && Modifier.isStatic(groovyObjectClass.getModifiers())) {
+ List<Class<?>> classList = new ArrayList<>(3);
+ for (Class<?> clazz = groovyObjectClass; null != clazz; clazz = clazz.getEnclosingClass()) {
+ if (isNonStaticInnerClass(clazz)) {
+ return Optional.empty();
+ }
+ classList.add(clazz);
+ }
+
+ Lookup caller = MethodHandles.lookup();
+ for (int i = classList.size() - 1; i >= 0; i--) {
+ Class<?> c = classList.get(i);
+ caller = doLookup(c, caller);
+ if (null == caller) {
+ return Optional.empty();
+ }
+ }
+ lookup = caller;
+ } else {
+ lookup = doLookup(groovyObject);
+ }
+
+ if (null != lookup) lookupAtomicRef.set(lookup);
+
+ return Optional.ofNullable(lookup);
+ }
+
+ private static boolean isNonStaticInnerClass(Class<?> clazz) {
+ return clazz.isMemberClass() && !Modifier.isStatic(clazz.getModifiers());
+ }
+
+ private static Lookup doLookup(GroovyObject groovyObject) {
+ MethodHandles.Lookup lookup;
+ try {
+ final Class<? extends GroovyObject> groovyObjectClass = groovyObject.getClass();
+ if (groovyObjectClass.isAnonymousClass() ||
+ (isNonStaticInnerClass(groovyObjectClass))) {
+ lookup = (MethodHandles.Lookup) MethodHandles.lookup()
+ .unreflect(findGetLookupMethod(groovyObjectClass))
+ .bindTo(groovyObject)
+ .invokeExact();
+ } else {
+ lookup = doLookup(groovyObjectClass);
+ }
+ } catch (Throwable e) {
+ lookup = null;
+ }
+
+ return lookup;
+ }
+
+ private static Lookup doLookup(Class<?> groovyObjectClass) {
+ return doLookup(groovyObjectClass, MethodHandles.lookup());
+ }
+
+ private static Lookup doLookup(Class<?> groovyObjectClass, Lookup caller) {
+ try {
+ return (Lookup) caller
+ .unreflect(findGetLookupMethod(groovyObjectClass))
+ .invokeExact();
+ } catch (Throwable throwable) {
+ return null;
+ }
+ }
+
+ private static Method findGetLookupMethod(Class<?> groovyObjectClass) throws NoSuchMethodException {
+ return groovyObjectClass.getDeclaredMethod("$getLookup");
+ }
+
+ private GroovyObjectHelper() {}
+ private static final ClassValue<AtomicReference<Lookup>> LOOKUP_MAP = new ClassValue<AtomicReference<Lookup>>() {
+ @Override
+ protected AtomicReference<Lookup> computeValue(Class<?> type) {
+ return new AtomicReference<>();
+ }
+ };
+}
diff --git a/src/main/java/org/codehaus/groovy/classgen/Verifier.java b/src/main/java/org/codehaus/groovy/classgen/Verifier.java
index 4293633..ec068b4 100644
--- a/src/main/java/org/codehaus/groovy/classgen/Verifier.java
+++ b/src/main/java/org/codehaus/groovy/classgen/Verifier.java
@@ -74,7 +74,9 @@ import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import java.beans.Transient;
+import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -139,6 +141,7 @@ import static org.codehaus.groovy.ast.tools.PropertyNodeUtils.adjustPropertyModi
* <li>Property accessor methods</li>
* <li>Covariant methods</li>
* <li>Additional methods/constructors as needed for default parameters</li>
+ * <li>{@link java.lang.invoke.MethodHandles.Lookup} getter</li>
* </ul>
*/
public class Verifier implements GroovyClassVisitor, Opcodes {
@@ -252,6 +255,8 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
addFastPathHelperFieldsAndHelperMethod(node, classInternalName, knownSpecialCase);
if (!knownSpecialCase) addGroovyObjectInterfaceAndMethods(node, classInternalName);
+
+ addGetLookupMethod(node);
}
addDefaultConstructor(node);
@@ -354,6 +359,30 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
node.addConstructor(constructor);
}
+ private void addGetLookupMethod(final ClassNode node) {
+ int modifiers = ACC_PUBLIC;
+ boolean nonStaticInnerClass = null != node.getOuterClass() && !Modifier.isStatic(node.getModifiers());
+ if (!nonStaticInnerClass) {
+ // static method cannot be declared in non-static inner class util Java 16
+ modifiers |= ACC_STATIC;
+ }
+
+ node.addSyntheticMethod(
+ "$getLookup",
+ modifiers,
+ ClassHelper.make(MethodHandles.Lookup.class),
+ Parameter.EMPTY_ARRAY,
+ ClassNode.EMPTY_ARRAY,
+ new BytecodeSequence(new BytecodeInstruction() {
+ @Override
+ public void visit(final MethodVisitor mv) {
+ mv.visitMethodInsn(INVOKESTATIC, "java/lang/invoke/MethodHandles", "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;", false);
+ mv.visitInsn(ARETURN);
+ }
+ })
+ );
+ }
+
private void addStaticMetaClassField(final ClassNode node, final String classInternalName) {
String _staticClassInfoFieldName = "$staticClassInfo";
while (node.getDeclaredField(_staticClassInfoFieldName) != null)
diff --git a/src/main/java/org/codehaus/groovy/control/CompilerConfiguration.java b/src/main/java/org/codehaus/groovy/control/CompilerConfiguration.java
index d706387..40a7432 100644
--- a/src/main/java/org/codehaus/groovy/control/CompilerConfiguration.java
+++ b/src/main/java/org/codehaus/groovy/control/CompilerConfiguration.java
@@ -134,7 +134,7 @@ public class CompilerConfiguration {
/**
* The ASM API version used when loading/parsing classes and generating proxy adapter classes.
*/
- public static final int ASM_API_VERSION = Opcodes.ASM8;
+ public static final int ASM_API_VERSION = Opcodes.ASM9;
/**
* The default source encoding.
diff --git a/src/main/java/org/codehaus/groovy/reflection/ReflectionUtils.java b/src/main/java/org/codehaus/groovy/reflection/ReflectionUtils.java
index e83b3d9..bdb23c2 100644
--- a/src/main/java/org/codehaus/groovy/reflection/ReflectionUtils.java
+++ b/src/main/java/org/codehaus/groovy/reflection/ReflectionUtils.java
@@ -22,6 +22,9 @@ import org.codehaus.groovy.classgen.asm.util.TypeUtil;
import org.codehaus.groovy.vmplugin.VMPlugin;
import org.codehaus.groovy.vmplugin.VMPluginFactory;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
@@ -145,35 +148,44 @@ public class ReflectionUtils {
private static List<Method> doGetMethods(final Class<?> type, final String name, final Class<?>[] parameterTypes, final Function<? super Class<?>, ? extends Method[]> f) {
List<Method> methodList = new LinkedList<>();
- out:
for (Method m : f.apply(type)) {
if (!m.getName().equals(name)) {
continue;
}
-
Class<?>[] methodParameterTypes = m.getParameterTypes();
- if (methodParameterTypes.length != parameterTypes.length) {
+ if (!parameterTypeMatches(methodParameterTypes, parameterTypes)) {
continue;
}
- for (int i = 0, n = methodParameterTypes.length; i < n; i += 1) {
- Class<?> parameterType = TypeUtil.autoboxType(parameterTypes[i]);
- if (null == parameterType) {
- continue out;
- }
-
- Class<?> methodParameterType = TypeUtil.autoboxType(methodParameterTypes[i]);
- if (!methodParameterType.isAssignableFrom(parameterType)) {
- continue out;
- }
- }
-
methodList.add(m);
}
return methodList;
}
+ public static boolean parameterTypeMatches(final Class<?>[] parameterTypes, final Class<?>[] argTypes) {
+ if (parameterTypes.length != argTypes.length) {
+ return false;
+ }
+
+ for (int i = 0, n = parameterTypes.length; i < n; i += 1) {
+ Class<?> parameterType = parameterTypes[i];
+ if (Object.class == parameterType) continue;
+
+ Class<?> argType = argTypes[i];
+ if (null == argType) return false;
+ if (parameterType == argType) continue;
+
+ Class<?> boxedArgType = TypeUtil.autoboxType(argType);
+ Class<?> boxedParameterType = TypeUtil.autoboxType(parameterType);
+ if (!boxedParameterType.isAssignableFrom(boxedArgType)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
public static boolean checkCanSetAccessible(final AccessibleObject accessibleObject, final Class<?> caller) {
return VM_PLUGIN.checkCanSetAccessible(accessibleObject, caller);
}
@@ -226,6 +238,17 @@ public class ReflectionUtils {
}
}
+ public static boolean isSealed(Class<?> clazz) {
+ if (null == IS_SEALED_METHODHANDLE) return false;
+
+ boolean sealed = false;
+ try {
+ sealed = (boolean) IS_SEALED_METHODHANDLE.bindTo(clazz).invokeExact();
+ } catch (Throwable ignored) {
+ }
+ return sealed;
+ }
+
private static boolean classShouldBeIgnored(final Class c, final Collection<String> extraIgnoredPackages) {
return (c != null
&& (c.isSynthetic()
@@ -240,4 +263,14 @@ public class ReflectionUtils {
return super.getClassContext();
}
}
+
+ private static final MethodHandle IS_SEALED_METHODHANDLE;
+ static {
+ MethodHandle mh = null;
+ try {
+ mh = MethodHandles.lookup().findVirtual(Class.class, "isSealed", MethodType.methodType(boolean.class, new Class[0]));
+ } catch (NoSuchMethodException | IllegalAccessException ignored) {
+ }
+ IS_SEALED_METHODHANDLE = mh;
+ }
}
diff --git a/src/main/java/org/codehaus/groovy/runtime/ArrayUtil.java b/src/main/java/org/codehaus/groovy/runtime/ArrayUtil.java
index aa8096f..d0697de 100644
--- a/src/main/java/org/codehaus/groovy/runtime/ArrayUtil.java
+++ b/src/main/java/org/codehaus/groovy/runtime/ArrayUtil.java
@@ -53,8 +53,11 @@ package org.codehaus.groovy.runtime;
* absolutely no sense in normal Java. But it is not used in normal Java, but from the bytecode.
*/
public class ArrayUtil {
- private static final Object[] EMPTY = new Object[0]
- ;
+ private static final Object[] EMPTY = new Object[0];
+
+ public static <T> T[] cloneArray(T[] array) {
+ return array.clone();
+ }
public static Object[] createArray() {
return EMPTY;
diff --git a/src/main/java/org/codehaus/groovy/runtime/ProxyGeneratorAdapter.java b/src/main/java/org/codehaus/groovy/runtime/ProxyGeneratorAdapter.java
index 422ab8a..6ad943e 100644
--- a/src/main/java/org/codehaus/groovy/runtime/ProxyGeneratorAdapter.java
+++ b/src/main/java/org/codehaus/groovy/runtime/ProxyGeneratorAdapter.java
@@ -60,6 +60,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
+import static org.codehaus.groovy.reflection.ReflectionUtils.isSealed;
import static org.objectweb.asm.Opcodes.AASTORE;
import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
import static org.objectweb.asm.Opcodes.ACC_FINAL;
@@ -210,7 +211,7 @@ public class ProxyGeneratorAdapter extends ClassVisitor {
this.classList.add(superClass);
if (generateDelegateField) {
classList.add(delegateClass);
- Collections.addAll(this.classList, delegateClass.getInterfaces());
+ Collections.addAll(this.classList, Arrays.stream(delegateClass.getInterfaces()).filter(c -> !isSealed(c)).toArray(Class[]::new));
}
if (interfaces != null) {
Collections.addAll(this.classList, interfaces);
@@ -940,5 +941,4 @@ public class ProxyGeneratorAdapter extends ClassVisitor {
return value;
}
}
-
}
diff --git a/src/main/java/org/codehaus/groovy/vmplugin/VMPluginFactory.java b/src/main/java/org/codehaus/groovy/vmplugin/VMPluginFactory.java
index 7e2d66b..d3ac172 100644
--- a/src/main/java/org/codehaus/groovy/vmplugin/VMPluginFactory.java
+++ b/src/main/java/org/codehaus/groovy/vmplugin/VMPluginFactory.java
@@ -39,6 +39,7 @@ public class VMPluginFactory {
private static final Logger LOGGER = Logger.getLogger(VMPluginFactory.class.getName());
private static final Map<BigDecimal, String> PLUGIN_MAP = Maps.of(
// Note: list the vm plugin entries in *descending* order:
+ new BigDecimal("16"), "org.codehaus.groovy.vmplugin.v16.Java16",
new BigDecimal("10"), "org.codehaus.groovy.vmplugin.v10.Java10",
new BigDecimal("9"), "org.codehaus.groovy.vmplugin.v9.Java9",
new BigDecimal("1.8"), "org.codehaus.groovy.vmplugin.v8.Java8"
diff --git a/src/main/java/org/codehaus/groovy/vmplugin/v10/Java10.java b/src/main/java/org/codehaus/groovy/vmplugin/v10/Java10.java
index e9f1bd9..94be0f1 100644
--- a/src/main/java/org/codehaus/groovy/vmplugin/v10/Java10.java
+++ b/src/main/java/org/codehaus/groovy/vmplugin/v10/Java10.java
@@ -42,4 +42,9 @@ public class Java10 extends Java9 {
public Class<?>[] getPluginDefaultGroovyMethods() {
return PLUGIN_DGM;
}
+
+ @Override
+ public int getVersion() {
+ return 10;
+ }
}
diff --git a/src/main/java/org/codehaus/groovy/vmplugin/v16/Java16.java b/src/main/java/org/codehaus/groovy/vmplugin/v16/Java16.java
new file mode 100644
index 0000000..5eef73d
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/vmplugin/v16/Java16.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.groovy.vmplugin.v16;
+
+import groovy.lang.GroovyRuntimeException;
+import org.codehaus.groovy.vmplugin.v10.Java10;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Additional Java 16 based functions will be added here as needed.
+ */
+public class Java16 extends Java10 {
+ private final Class<?>[] PLUGIN_DGM;
+
+ public Java16() {
+ super();
+ List<Class<?>> dgmClasses = new ArrayList<>();
+ Collections.addAll(dgmClasses, super.getPluginDefaultGroovyMethods());
+ dgmClasses.add(PluginDefaultGroovyMethods.class);
+ PLUGIN_DGM = dgmClasses.toArray(new Class<?>[0]);
+ }
+
+ @Override
+ protected MethodHandles.Lookup newLookup(final Class<?> declaringClass) {
+ return of(declaringClass);
+ }
+
+ public static MethodHandles.Lookup of(final Class<?> declaringClass) {
+ try {
+ final Method privateLookup = getPrivateLookup();
+ if (privateLookup != null) {
+ MethodHandles.Lookup caller = MethodHandles.lookup();
+ Class<?> callerClass = caller.lookupClass();
+ Module callerModule = callerClass.getModule();
+ Module targetModule = declaringClass.getModule();
+ if (targetModule != callerModule) {
+ if (targetModule.isNamed()) {
+ String pn = declaringClass.getPackageName();
+ if (!targetModule.isOpen(pn, callerModule)) {
+ return MethodHandles.lookup().in(declaringClass);
+ }
+ }
+ }
+
+ return (MethodHandles.Lookup) privateLookup.invoke(null, declaringClass, caller);
+ }
+ return getLookupConstructor().newInstance(declaringClass, MethodHandles.Lookup.PRIVATE).in(declaringClass);
+ } catch (final IllegalAccessException | InstantiationException e) {
+ throw new IllegalArgumentException(e);
+ } catch (final InvocationTargetException e) {
+ throw new GroovyRuntimeException(e);
+ }
+ }
+
+ @Override
+ public Object getInvokeSpecialHandle(Method method, Object receiver) {
+ try {
+ final Class<?> receiverType = receiver.getClass();
+ if (method.isDefault() && Proxy.isProxyClass(receiverType)) {
+ return new ProxyDefaultMethodHandle((Proxy) receiver, method);
+ }
+
+ MethodHandles.Lookup lookup = getLookup(receiver);
+ if (0 != (MethodHandles.Lookup.PRIVATE & lookup.lookupModes())) {
+ return lookup.unreflectSpecial(method, receiverType).bindTo(receiver);
+ }
+ return lookup.unreflect(method).bindTo(receiver);
+ } catch (ReflectiveOperationException e) {
+ return new GroovyRuntimeException(e);
+ }
+ }
+
+ @Override
+ public Object invokeHandle(Object handle, Object[] args) throws Throwable {
+ if (handle instanceof ProxyDefaultMethodHandle) {
+ return ((ProxyDefaultMethodHandle) handle).invokeWithArguments(args);
+ }
+ if (handle instanceof Throwable) throw (Throwable) handle;
+ MethodHandle mh = (MethodHandle) handle;
+ return mh.invokeWithArguments(args);
+ }
+
+ @Override
+ public Class<?>[] getPluginDefaultGroovyMethods() {
+ return PLUGIN_DGM;
+ }
+
+ @Override
+ public int getVersion() {
+ return 16;
+ }
+}
diff --git a/src/test/org/codehaus/groovy/classgen/TransientMetaClassTest.groovy b/src/main/java/org/codehaus/groovy/vmplugin/v16/PluginDefaultGroovyMethods.java
similarity index 54%
copy from src/test/org/codehaus/groovy/classgen/TransientMetaClassTest.groovy
copy to src/main/java/org/codehaus/groovy/vmplugin/v16/PluginDefaultGroovyMethods.java
index 18265cf..232ef8b 100644
--- a/src/test/org/codehaus/groovy/classgen/TransientMetaClassTest.groovy
+++ b/src/main/java/org/codehaus/groovy/vmplugin/v16/PluginDefaultGroovyMethods.java
@@ -16,22 +16,14 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.codehaus.groovy.classgen
+package org.codehaus.groovy.vmplugin.v16;
-import groovy.test.GroovyTestCase
-
-class TransientMetaClassTest extends GroovyTestCase {
- // GROOVY-8284
- void testGetMetaClassMethodIsDeemedTransient() {
- assertScript '''
- def gcl = new GroovyClassLoader()
- def fooClass = gcl.parseClass('class Foo {}')
- def fooInfo = java.beans.Introspector.getBeanInfo(fooClass)
- assert fooInfo.propertyDescriptors.find{ it.name == 'metaClass' }.transient
+/**
+ * Defines new Groovy methods which appear on normal JDK 16
+ * classes inside the Groovy environment.
+ *
+ * @since 4.0.0
+ */
+public class PluginDefaultGroovyMethods {
- Closure c = { -> }
- def cInfo = java.beans.Introspector.getBeanInfo(c.getClass())
- assert cInfo.propertyDescriptors.find{ it.name == 'metaClass' }.transient
- '''
- }
}
diff --git a/src/main/java/org/codehaus/groovy/vmplugin/v16/ProxyDefaultMethodHandle.java b/src/main/java/org/codehaus/groovy/vmplugin/v16/ProxyDefaultMethodHandle.java
new file mode 100644
index 0000000..b1fbd5d
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/vmplugin/v16/ProxyDefaultMethodHandle.java
@@ -0,0 +1,38 @@
+package org.codehaus.groovy.vmplugin.v16;
+
+import org.codehaus.groovy.GroovyBugError;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+class ProxyDefaultMethodHandle {
+ private static final MethodHandle INVOKE_DEFAULT_METHOD_HANDLE;
+ static {
+ try {
+ // `invokeDefault` is JDK 16+ API, but we still build Groovy with JDK11,
+ // so use method handle instead of invoking the method directly
+ INVOKE_DEFAULT_METHOD_HANDLE = MethodHandles.lookup().findStatic(
+ InvocationHandler.class, "invokeDefault",
+ MethodType.methodType(Object.class, Object.class, Method.class, Object[].class));
+ } catch (NoSuchMethodException | IllegalAccessException e) {
+ throw new GroovyBugError(e);
+ }
+ }
+
+ private final Proxy proxy;
+ private final Method method;
+
+ ProxyDefaultMethodHandle(Proxy proxy, Method method) {
+ this.proxy = proxy;
+ this.method = method;
+ }
+
+ Object invokeWithArguments(Object... arguments) throws Throwable {
+ Object proxy = this.proxy;
+ return INVOKE_DEFAULT_METHOD_HANDLE.invokeExact(proxy, method, arguments);
+ }
+}
diff --git a/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java b/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java
index 3f54df0..ff6da29 100644
--- a/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java
+++ b/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java
@@ -18,9 +18,11 @@
*/
package org.codehaus.groovy.vmplugin.v8;
+import groovy.lang.GroovyObject;
import groovy.lang.GroovyRuntimeException;
import groovy.lang.MetaClass;
import groovy.lang.MetaMethod;
+import org.apache.groovy.lang.GroovyObjectHelper;
import org.codehaus.groovy.GroovyBugError;
import org.codehaus.groovy.ast.AnnotatedNode;
import org.codehaus.groovy.ast.AnnotationNode;
@@ -68,6 +70,7 @@ import java.security.AccessController;
import java.security.Permission;
import java.security.PrivilegedAction;
import java.util.List;
+import java.util.Optional;
/**
* Java 8 based functions.
@@ -623,11 +626,27 @@ public class Java8 implements VMPlugin {
IndyInterface.invalidateSwitchPoints();
}
+ protected MethodHandles.Lookup getLookup(Object receiver) {
+ MethodHandles.Lookup lookup = null;
+ if (receiver instanceof GroovyObject) {
+ Optional<MethodHandles.Lookup> lookupOptional = GroovyObjectHelper.lookup((GroovyObject) receiver);
+ if (lookupOptional.isPresent()) {
+ lookup = lookupOptional.get();
+ }
+ }
+ if (null == lookup) {
+ final Class<?> receiverType = receiver.getClass();
+ lookup = newLookup(receiverType);
+ }
+ return lookup;
+ }
+
@Override
public Object getInvokeSpecialHandle(Method method, Object receiver) {
- final Class<?> receiverType = receiver.getClass();
try {
- return newLookup(receiverType).unreflectSpecial(method, receiverType).bindTo(receiver);
+ MethodHandles.Lookup lookup = getLookup(receiver);
+ Class<?> receiverType = receiver.getClass();
+ return lookup.unreflectSpecial(method, receiverType).bindTo(receiver);
} catch (ReflectiveOperationException e) {
return getInvokeSpecialHandleFallback(method, receiver);
}
diff --git a/src/main/java/org/codehaus/groovy/vmplugin/v9/Java9.java b/src/main/java/org/codehaus/groovy/vmplugin/v9/Java9.java
index 1600070..bb050d8 100644
--- a/src/main/java/org/codehaus/groovy/vmplugin/v9/Java9.java
+++ b/src/main/java/org/codehaus/groovy/vmplugin/v9/Java9.java
@@ -169,11 +169,11 @@ public class Java9 extends Java8 {
return of(declaringClass);
}
- private static Constructor<MethodHandles.Lookup> getLookupConstructor() {
+ protected static Constructor<MethodHandles.Lookup> getLookupConstructor() {
return LookupHolder.LOOKUP_Constructor;
}
- private static Method getPrivateLookup() {
+ protected static Method getPrivateLookup() {
return LookupHolder.PRIVATE_LOOKUP;
}
@@ -354,8 +354,8 @@ public class Java9 extends Java8 {
private static List<CachedMethod> getMetaMethods(CachedMethod metaMethod, Class<?>[] params, Class<?> sc, boolean declared) {
String metaMethodName = metaMethod.getName();
List<Method> optionalMethodList = declared
- ? ReflectionUtils.getDeclaredMethods(sc, metaMethodName, params)
- : ReflectionUtils.getMethods(sc, metaMethodName, params);
+ ? ReflectionUtils.getDeclaredMethods(sc, metaMethodName, params)
+ : ReflectionUtils.getMethods(sc, metaMethodName, params);
return optionalMethodList.stream().map(CachedMethod::new).collect(Collectors.toList());
}
@@ -1813,4 +1813,4 @@ public class Java9 extends Java8 {
"sun.util.xml"
};
}
-}
+}
\ No newline at end of file
diff --git a/src/test/groovy/IllegalAccessTests.groovy b/src/test/groovy/IllegalAccessTests.groovy
index f0d1192..ceb5fca 100644
--- a/src/test/groovy/IllegalAccessTests.groovy
+++ b/src/test/groovy/IllegalAccessTests.groovy
@@ -30,14 +30,14 @@ import static org.junit.Assume.assumeTrue
* Java via means such as reflection.
*
* In JDK versions < 9, Groovy supports permissive access and no warnings are given by the JDK.
- * In JDK versions >= 9, Groovy supports permissive access but the JDK gives illegal access warnings.
+ * In 9 <= JDK versions < 16, Groovy supports permissive access but the JDK gives illegal access warnings.
* At some point, the JDK may further restrict permissive access and Groovy's support for this feature may be limited.
*/
final class IllegalAccessTests {
@Before
void setUp() {
- assumeTrue(isAtLeastJdk('9.0') && !Boolean.getBoolean('groovy.force.illegal.access'))
+ assumeTrue(!isAtLeastJdk('16.0') && isAtLeastJdk('9.0') && !Boolean.getBoolean('groovy.force.illegal.access'))
}
@Test
diff --git a/src/test/groovy/bugs/Groovy8815.groovy b/src/test/groovy/bugs/Groovy8815.groovy
index 8a8c9f4..e520b8c 100644
--- a/src/test/groovy/bugs/Groovy8815.groovy
+++ b/src/test/groovy/bugs/Groovy8815.groovy
@@ -22,10 +22,17 @@ import org.codehaus.groovy.control.CompilerConfiguration
import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit
import org.junit.Test
+import static groovy.test.GroovyAssert.isAtLeastJdk
+import static org.junit.Assume.assumeTrue
+
final class Groovy8815 {
@Test
void testGenerics() {
+ // illegal access to `java.lang.reflect.Method.getGenericSignature`, i.e. `method.genericSignature` in the test
+ // but illegal access is not allowed by default since JDK 16
+ assumeTrue(!isAtLeastJdk('16.0'))
+
def config = new CompilerConfiguration(
targetDirectory: File.createTempDir(),
jointCompilationOptions: [memStub: true]
diff --git a/src/test/org/codehaus/groovy/classgen/TransientMetaClassTest.groovy b/src/test/org/codehaus/groovy/classgen/TransientMetaClassTest.groovy
index 18265cf..cc14bfa 100644
--- a/src/test/org/codehaus/groovy/classgen/TransientMetaClassTest.groovy
+++ b/src/test/org/codehaus/groovy/classgen/TransientMetaClassTest.groovy
@@ -27,11 +27,11 @@ class TransientMetaClassTest extends GroovyTestCase {
def gcl = new GroovyClassLoader()
def fooClass = gcl.parseClass('class Foo {}')
def fooInfo = java.beans.Introspector.getBeanInfo(fooClass)
- assert fooInfo.propertyDescriptors.find{ it.name == 'metaClass' }.transient
+ assert fooInfo.propertyDescriptors.find{ it.name == 'metaClass' }.getValue('transient')
Closure c = { -> }
def cInfo = java.beans.Introspector.getBeanInfo(c.getClass())
- assert cInfo.propertyDescriptors.find{ it.name == 'metaClass' }.transient
+ assert cInfo.propertyDescriptors.find{ it.name == 'metaClass' }.getValue('transient')
'''
}
}
diff --git a/src/test/org/codehaus/groovy/classgen/VerifierTest.groovy b/src/test/org/codehaus/groovy/classgen/VerifierTest.groovy
new file mode 100644
index 0000000..942911d
--- /dev/null
+++ b/src/test/org/codehaus/groovy/classgen/VerifierTest.groovy
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.groovy.classgen
+
+
+import org.junit.Test
+
+import static groovy.test.GroovyAssert.assertScript
+
+class VerifierTest {
+ @Test
+ void testGenGetLookupMethod() {
+ assertScript '''
+ import java.lang.reflect.Modifier
+ class Outer {
+ static class StaticInner {
+ static class StaticInnest {}
+ }
+ class Inner {}
+ }
+
+ {
+ def getLookupMethod = Outer.getDeclaredMethods().grep(m -> '$getLookup' == m.name)[0]
+ def modifiers = getLookupMethod.getModifiers()
+ assert Modifier.isPublic(modifiers)
+ assert Modifier.isStatic(modifiers)
+ assert getLookupMethod.synthetic
+ }
+
+ {
+ def getLookupMethod = Outer.StaticInner.getDeclaredMethods().grep(m -> '$getLookup' == m.name)[0]
+ def modifiers = getLookupMethod.getModifiers()
+ assert Modifier.isPublic(modifiers)
+ assert Modifier.isStatic(modifiers)
+ assert getLookupMethod.synthetic
+ }
+
+ {
+ def getLookupMethod = Outer.StaticInner.StaticInnest.getDeclaredMethods().grep(m -> '$getLookup' == m.name)[0]
+ def modifiers = getLookupMethod.getModifiers()
+ assert Modifier.isPublic(modifiers)
+ assert Modifier.isStatic(modifiers)
+ assert getLookupMethod.synthetic
+ }
+
+ {
+ def getLookupMethod = Outer.Inner.getDeclaredMethods().grep(m -> '$getLookup' == m.name)[0]
+ def modifiers = getLookupMethod.getModifiers()
+ assert Modifier.isPublic(modifiers)
+ assert !Modifier.isStatic(modifiers)
+ assert getLookupMethod.synthetic
+ }
+ '''
+ }
+
+}
diff --git a/src/test/org/codehaus/groovy/reflection/SecurityTest.java b/src/test/org/codehaus/groovy/reflection/SecurityTest.java
index d466847..5f24859 100644
--- a/src/test/org/codehaus/groovy/reflection/SecurityTest.java
+++ b/src/test/org/codehaus/groovy/reflection/SecurityTest.java
@@ -241,6 +241,9 @@ public class SecurityTest extends GroovyTestCase {
public void testChecksCreateClassLoaderPermissionForClassLoaderProtectedMethodAccess() throws Exception {
+ // Illegal access to java.lang.ClassLoader.defineClass(java.lang.String,java.nio.ByteBuffer,java.security.ProtectionDomain)
+ if (isAtLeastJdk("16.0")) return;
+
cachedMethodUnderTest = createCachedMethod(ClassLoader.class, "defineClass", new Class[]{String.class, ByteBuffer.class, ProtectionDomain.class});
forbidden = new Permissions();
forbidden.add(new RuntimePermission("createClassLoader"));
diff --git a/src/test/org/codehaus/groovy/runtime/typehandling/NumberMathTest.groovy b/src/test/org/codehaus/groovy/runtime/typehandling/NumberMathTest.groovy
index dbf0d6a..2c4a35f 100644
--- a/src/test/org/codehaus/groovy/runtime/typehandling/NumberMathTest.groovy
+++ b/src/test/org/codehaus/groovy/runtime/typehandling/NumberMathTest.groovy
@@ -109,7 +109,7 @@ class NumberMathTest extends GroovyTestCase {
def BD1 = new BigDecimal("1.0")
def BD2 = new BigDecimal("2.0")
def BD20 = new BigDecimal("2.00")
- def BD100 = new BigDecimal(new BigInteger(1), -2) // 100
+ def BD100 = new BigDecimal(new BigInteger("1"), -2) // 100
assert I1 / I2 instanceof BigDecimal
assert I1 / I2 == new BigDecimal("0.5")
diff --git a/src/test/org/codehaus/groovy/transform/classloading/TransformsAndCustomClassLoadersTest.groovy b/src/test/org/codehaus/groovy/transform/classloading/TransformsAndCustomClassLoadersTest.groovy
index c04c371..4676486 100644
--- a/src/test/org/codehaus/groovy/transform/classloading/TransformsAndCustomClassLoadersTest.groovy
+++ b/src/test/org/codehaus/groovy/transform/classloading/TransformsAndCustomClassLoadersTest.groovy
@@ -137,7 +137,10 @@ class TransformsAndCustomClassLoadersTest extends GroovyTestCase {
}
if (name.startsWith("java.") || name.startsWith("groovy.")
|| name.startsWith("org.codehaus.groovy.") || name.startsWith("org.apache.groovy.")) {
- return getClass().classLoader.loadClass(name, resolve)
+ def loader = getClass().classLoader
+ def result = loader.loadClass(name)
+ if (resolve) loader.resolveClass(result)
+ return result
}
throw new ClassNotFoundException(name)
}