This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY_5_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/GROOVY_5_0_X by this push:
new bbbf036b9c GROOVY-11782: indy guard for null alternation of primitive
parameters
bbbf036b9c is described below
commit bbbf036b9c4878b50bf49c2d6488ec2e1ab72d5e
Author: Eric Milles <[email protected]>
AuthorDate: Sun Oct 12 11:35:00 2025 -0500
GROOVY-11782: indy guard for null alternation of primitive parameters
---
.../org/codehaus/groovy/vmplugin/v8/Selector.java | 27 +++++------
.../codehaus/groovy/vmplugin/v8/TypeHelper.java | 53 ++++++++++++----------
src/test/groovy/bugs/Groovy11126.groovy | 50 ++++++++++----------
.../{Groovy11126.groovy => Groovy11782.groovy} | 48 ++++++++------------
4 files changed, 85 insertions(+), 93 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/vmplugin/v8/Selector.java
b/src/main/java/org/codehaus/groovy/vmplugin/v8/Selector.java
index f173e9df8d..c6d9b565d5 100644
--- a/src/main/java/org/codehaus/groovy/vmplugin/v8/Selector.java
+++ b/src/main/java/org/codehaus/groovy/vmplugin/v8/Selector.java
@@ -929,8 +929,7 @@ public abstract class Selector {
* Sets all argument and receiver guards.
*/
public void setGuards(Object receiver) {
- if (handle == null) return;
- if (!cache) return;
+ if (!cache || handle == null) return;
MethodHandle fallback = callSite.getFallbackTarget();
@@ -950,7 +949,7 @@ public abstract class Selector {
if (LOG_ENABLED) LOG.info("added class equality check");
}
- if (!useMetaClass && isCategoryMethod) {
+ if (isCategoryMethod && !useMetaClass) {
// category method needs Thread check
// cases:
// (1) method is a category method
@@ -971,35 +970,37 @@ public abstract class Selector {
handle = switchPoint.guardWithTest(handle, fallback);
if (LOG_ENABLED) LOG.info("added switch point guard");
+ java.util.function.Predicate<Class<?>> nonFinalOrNullUnsafe = (t)
-> {
+ return !Modifier.isFinal(t.getModifiers())
+ || TypeHelper.getUnboxedType(t).isPrimitive(); //
GROOVY-11782
+ };
+
// guards for receiver and parameter
Class<?>[] pt = handle.type().parameterArray();
if (Arrays.stream(args).anyMatch(Objects::isNull)) {
for (int i = 0; i < args.length; i++) {
- Object arg = args[i];
- Class<?> paramType = pt[i];
MethodHandle test;
+ var arg = args[i];
if (arg == null) {
- test =
IS_NULL.asType(MethodType.methodType(boolean.class, paramType));
+ test =
IS_NULL.asType(MethodType.methodType(boolean.class, pt[i]));
if (LOG_ENABLED) LOG.info("added null argument check
at pos " + i);
} else {
- if (Modifier.isFinal(paramType.getModifiers())) {
- // primitive types are also `final`
- continue;
- }
- test =
SAME_CLASS.bindTo(arg.getClass()).asType(MethodType.methodType(boolean.class,
paramType));
- if (LOG_ENABLED) LOG.info("added same class check at
pos " + i);
+ if (nonFinalOrNullUnsafe.negate().test(pt[i]))
continue; // null-safe type that cannot change
+ test =
SAME_CLASS.bindTo(arg.getClass()).asType(MethodType.methodType(boolean.class,
pt[i]));
+ if (LOG_ENABLED) LOG.info("added same-class argument
check at pos " + i);
}
Class<?>[] drops = new Class[i];
System.arraycopy(pt, 0, drops, 0, drops.length);
test = MethodHandles.dropArguments(test, 0, drops);
handle = MethodHandles.guardWithTest(test, handle,
fallback);
}
- } else if (Arrays.stream(pt).anyMatch(paramType ->
!Modifier.isFinal(paramType.getModifiers()))) {
+ } else if (Arrays.stream(pt).anyMatch(nonFinalOrNullUnsafe)) {
MethodHandle test = SAME_CLASSES
.bindTo(Arrays.stream(args).map(Object::getClass).toArray(Class[]::new))
.asCollector(Object[].class, pt.length)
.asType(MethodType.methodType(boolean.class, pt));
handle = MethodHandles.guardWithTest(test, handle, fallback);
+ if (LOG_ENABLED) LOG.info("added same-class argument check");
} else if (safeNavigationOrig) { // GROOVY-11126
MethodHandle test =
NON_NULL.asType(MethodType.methodType(boolean.class, pt[0]));
handle = MethodHandles.guardWithTest(test, handle, fallback);
diff --git a/src/main/java/org/codehaus/groovy/vmplugin/v8/TypeHelper.java
b/src/main/java/org/codehaus/groovy/vmplugin/v8/TypeHelper.java
index 1ebd9f664a..cda886e07d 100644
--- a/src/main/java/org/codehaus/groovy/vmplugin/v8/TypeHelper.java
+++ b/src/main/java/org/codehaus/groovy/vmplugin/v8/TypeHelper.java
@@ -25,34 +25,41 @@ import java.math.BigInteger;
/**
* This class contains helper methods for converting and comparing types.
- * WARNING: This class is for internal use only. do not use it outside of its
- * package and not outside groovy-core.
+ * WARNING: This class is for internal use only. Do not use it outside of its
+ * package and not outside of groovy-core.
*/
public class TypeHelper {
+
+ protected static Class<?> getUnboxedType(Class<?> c) {
+ if (c == null || c.isPrimitive()) ;
+ else if (c == Boolean.class) c = Boolean.TYPE;
+ else if (c == Byte.class) c = Byte.TYPE;
+ else if (c == Character.class) c = Character.TYPE;
+ else if (c == Float.class) c = Float.TYPE;
+ else if (c == Double.class) c = Double.TYPE;
+ else if (c == Integer.class) c = Integer.TYPE;
+ else if (c == Long.class) c = Long.TYPE;
+ else if (c == Short.class) c = Short.TYPE;
+ //else if (c == Void.class) c = Void.TYPE;
+ return c;
+ }
+
/**
- * Get wrapper class for a given class.
- * If the class is for a primitive number type, then the wrapper class
- * will be returned. If it is no primitive number type, we return the
- * class itself.
+ * Gets wrapper class for a given class. If the class is for a primitive
+ * number type, then the wrapper class will be returned. If it is not a
+ * primitive number type, we return the class itself.
*/
protected static Class<?> getWrapperClass(Class<?> c) {
- if (c == Integer.TYPE) {
- c = Integer.class;
- } else if (c == Byte.TYPE) {
- c = Byte.class;
- } else if (c == Long.TYPE) {
- c = Long.class;
- } else if (c == Double.TYPE) {
- c = Double.class;
- } else if (c == Float.TYPE) {
- c = Float.class;
- } else if (c == Boolean.TYPE) {
- c = Boolean.class;
- } else if (c == Character.TYPE) {
- c = Character.class;
- } else if (c == Short.TYPE) {
- c = Short.class;
- }
+ if (c == null || !c.isPrimitive()) ;
+ else if (c == Integer.TYPE) c = Integer.class;
+ else if (c == Byte.TYPE) c = Byte.class;
+ else if (c == Long.TYPE) c = Long.class;
+ else if (c == Double.TYPE) c = Double.class;
+ else if (c == Float.TYPE) c = Float.class;
+ else if (c == Boolean.TYPE) c = Boolean.class;
+ else if (c == Character.TYPE) c = Character.class;
+ else if (c == Short.TYPE) c = Short.class;
+ //else if (c == Void.TYPE) c = Void.class;
return c;
}
diff --git a/src/test/groovy/bugs/Groovy11126.groovy
b/src/test/groovy/bugs/Groovy11126.groovy
index cb3cebdff1..f2b01cd2db 100644
--- a/src/test/groovy/bugs/Groovy11126.groovy
+++ b/src/test/groovy/bugs/Groovy11126.groovy
@@ -18,41 +18,37 @@
*/
package bugs
-import org.junit.Test
-
-import static groovy.test.GroovyAssert.assertScript
+import org.junit.jupiter.api.Test
final class Groovy11126 {
+ private safeCall(String string) {
+ string?.size()
+ }
+
+ private safeName(String string) {
+ string?.chars
+ }
+
@Test
void testSafeCall() {
- assertScript '''
- def test(String string) {
- string?.size()
- }
- 10000.times {
- test('1')
- test('')
- }
- test(null)
- test('22')
- test(null)
- '''
+ 10000.times {
+ safeCall('1')
+ safeCall('')
+ }
+ safeCall(null)
+ safeCall('22')
+ safeCall(null)
}
@Test
void testSafeName() {
- assertScript '''
- def test(String string) {
- string?.chars
- }
- 10000.times {
- test('1')
- test('')
- }
- test(null)
- test('22')
- test(null)
- '''
+ 10000.times {
+ safeName('1')
+ safeName('')
+ }
+ safeName(null)
+ safeName('22')
+ safeName(null)
}
}
diff --git a/src/test/groovy/bugs/Groovy11126.groovy
b/src/test/groovy/bugs/Groovy11782.groovy
similarity index 54%
copy from src/test/groovy/bugs/Groovy11126.groovy
copy to src/test/groovy/bugs/Groovy11782.groovy
index cb3cebdff1..1790adc1ff 100644
--- a/src/test/groovy/bugs/Groovy11126.groovy
+++ b/src/test/groovy/bugs/Groovy11782.groovy
@@ -18,41 +18,29 @@
*/
package bugs
-import org.junit.Test
+import org.junit.jupiter.api.Test
-import static groovy.test.GroovyAssert.assertScript
+import static groovy.test.GroovyAssert.shouldFail
-final class Groovy11126 {
+final class Groovy11782 {
- @Test
- void testSafeCall() {
- assertScript '''
- def test(String string) {
- string?.size()
- }
- 10000.times {
- test('1')
- test('')
- }
- test(null)
- test('22')
- test(null)
- '''
+ private static int inc(int i) {
+ ++i
+ }
+
+ final Closure functor = { Integer i ->
+ Groovy11782.inc(i)
}
@Test
- void testSafeName() {
- assertScript '''
- def test(String string) {
- string?.chars
- }
- 10000.times {
- test('1')
- test('')
- }
- test(null)
- test('22')
- test(null)
- '''
+ void testNullForPrimitiveParameter() {
+ assert functor(0) == 1
+ shouldFail(MissingMethodException) {
+ functor(null)
+ }
+ assert functor(1) == 2
+ shouldFail(MissingMethodException) {
+ functor(null)
+ }
}
}