This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY_4_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/GROOVY_4_0_X by this push:
new 4668a7681a GROOVY-4843, GROOVY-8560: box primitive array for spread
invocation
4668a7681a is described below
commit 4668a7681a9b7ba26591bdfcec145d20b5f02eb4
Author: Eric Milles <[email protected]>
AuthorDate: Sat Mar 15 12:50:33 2025 -0500
GROOVY-4843, GROOVY-8560: box primitive array for spread invocation
4_0_X backport
---
.../groovy/runtime/ScriptBytecodeAdapter.java | 3 +-
.../org/codehaus/groovy/vmplugin/v8/Selector.java | 35 ++++---
src/test/groovy/SpreadArgTest.groovy | 106 +++++++++++++++++++++
.../groovy/mock/interceptor/MockForJavaTest.groovy | 38 ++++++--
4 files changed, 161 insertions(+), 21 deletions(-)
diff --git
a/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
b/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
index 9ff0f761e1..c044f08e97 100644
--- a/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
+++ b/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
@@ -911,7 +911,8 @@ public class ScriptBytecodeAdapter {
} else if (value instanceof List) {
ret.addAll((List) value);
} else if (value.getClass().isArray()) {
- Collections.addAll(ret,
DefaultTypeTransformation.primitiveArrayBox(value));
+ Collections.addAll(ret,
value.getClass().getComponentType().isPrimitive()
+ ? DefaultTypeTransformation.primitiveArrayBox(value) :
(Object[]) value);
} else {
String error = "Cannot spread the type " +
value.getClass().getName() + " with value " + value;
if (value instanceof Map) {
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 c0fa515a74..30c6434346 100644
--- a/src/main/java/org/codehaus/groovy/vmplugin/v8/Selector.java
+++ b/src/main/java/org/codehaus/groovy/vmplugin/v8/Selector.java
@@ -50,6 +50,7 @@ import
org.codehaus.groovy.runtime.metaclass.MethodMetaProperty;
import org.codehaus.groovy.runtime.metaclass.NewInstanceMetaMethod;
import org.codehaus.groovy.runtime.metaclass.NewStaticMetaMethod;
import org.codehaus.groovy.runtime.metaclass.ReflectionMetaMethod;
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
import org.codehaus.groovy.runtime.wrappers.Wrapper;
import org.codehaus.groovy.vmplugin.VMPluginFactory;
@@ -154,12 +155,24 @@ public abstract class Selector {
* number arguments.
*/
private static Object[] spread(Object[] args, boolean spreadCall) {
- if (!spreadCall) return args;
- Object[] normalArguments = (Object[]) args[1];
- Object[] ret = new Object[normalArguments.length + 1];
- ret[0] = args[0];
- System.arraycopy(normalArguments, 0, ret, 1, ret.length - 1);
- return ret;
+ Object[] result = args;
+ if (spreadCall) {
+ Object[] arguments = (Object[]) args[1];
+ final int nArguments = arguments.length;
+
+ result = new Object[nArguments + 1];
+ result[0] = args[0]; // the receiver
+ System.arraycopy(arguments, 0, result, 1, nArguments);
+
+ // accommodate Object[] spreader m. handle
+ for (int i = 1; i <= nArguments; i += 1) {
+ Class argumentType = result[i] != null ? result[i].getClass()
: Object.class;
+ if (argumentType.isArray() &&
argumentType.getComponentType().isPrimitive()) {
+ result[i] =
DefaultTypeTransformation.primitiveArrayBox(result[i]); // GROOVY-4843,
GROOVY-8560
+ }
+ }
+ }
+ return result;
}
private static class CastSelector extends MethodSelector {
@@ -523,7 +536,7 @@ public abstract class Selector {
this.safeNavigation = safeNavigation && arguments[0] == null;
this.thisCall = thisCall;
this.spread = spreadCall;
- this.cache = !spread;
+ this.cache = !spreadCall;
if (LOG_ENABLED) {
StringBuilder msg =
@@ -762,8 +775,7 @@ public abstract class Selector {
Class<?>[] params = handle.type().parameterArray();
if (currentType != null) params = currentType.parameterArray();
if (!isVargs) {
- if (spread && useMetaClass) return;
- if (params.length == 2 && args.length == 1) {
+ if (!(spread && useMetaClass) && params.length == 2 &&
args.length == 1) {
handle = MethodHandles.insertArguments(handle, 1,
SINGLE_NULL_ARRAY);
}
return;
@@ -860,8 +872,9 @@ public abstract class Selector {
}
public void correctSpreading() {
- if (!spread || useMetaClass || skipSpreadCollector) return;
- handle = handle.asSpreader(Object[].class, args.length - 1);
+ if (spread && !useMetaClass && !skipSpreadCollector) {
+ handle = handle.asSpreader(Object[].class, args.length - 1);
+ }
}
/**
diff --git a/src/test/groovy/SpreadArgTest.groovy
b/src/test/groovy/SpreadArgTest.groovy
new file mode 100644
index 0000000000..d08cdd3814
--- /dev/null
+++ b/src/test/groovy/SpreadArgTest.groovy
@@ -0,0 +1,106 @@
+/*
+ * 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 groovy
+
+import org.junit.Test
+
+import static groovy.test.GroovyAssert.assertScript
+
+/**
+ * Tests for the spread arg(s) operator "m(*x)".
+ */
+final class SpreadArgTest {
+
+ // GROOVY-9515
+ @Test
+ void testSpreadList() {
+ assertScript '''
+ int f(int x, int y) { x + y }
+ int f(int x) { x }
+ int g(x) { f(*x) }
+
+ assert g([1]) == 1
+ assert g([1, 2]) == 3
+ '''
+ }
+
+ @Test
+ void testSpreadArray() {
+ assertScript '''
+ int f(int x, int y, int z) {
+ x + y + z
+ }
+
+ Number[] nums = [1, 2, 39]
+ assert f(*nums) == 42
+ '''
+ }
+
+ // GROOVY-8560
+ @Test
+ void testSpreadArray2() {
+ assertScript '''
+ def f(byte[] bytes) {
+ assert bytes.length == 2
+ }
+
+ def ba = new byte[]{0, 0}
+ def oa = new Object[]{ba}
+ f(oa[0])
+ f( *oa )
+ '''
+ }
+
+ // GROOVY-5647
+ @Test
+ void testSpreadSkipSTC() {
+ assertScript '''
+ import groovy.transform.CompileStatic
+ import static groovy.transform.TypeCheckingMode.SKIP
+
+ @CompileStatic
+ class C {
+ @CompileStatic(SKIP)
+ def foo(fun, args) {
+ new Runnable() { // create an anonymous class which should
*not* be visited
+ void run() {
+ fun(*args) // spread operator is disallowed with
STC/SC, but SKIP should prevent from an error
+ }
+ }
+ }
+ }
+
+ new C()
+ '''
+ }
+
+ @Test
+ void testSpreadVarargs() {
+ assertScript '''
+ int f(String... strings) {
+ g(*strings)
+ }
+ int g(String... strings) {
+ strings.length
+ }
+
+ assert f("1","2") == 2
+ '''
+ }
+}
diff --git a/src/test/groovy/mock/interceptor/MockForJavaTest.groovy
b/src/test/groovy/mock/interceptor/MockForJavaTest.groovy
index 40d2e3c978..be617e88ff 100644
--- a/src/test/groovy/mock/interceptor/MockForJavaTest.groovy
+++ b/src/test/groovy/mock/interceptor/MockForJavaTest.groovy
@@ -18,20 +18,22 @@
*/
package groovy.mock.interceptor
-import groovy.test.GroovyTestCase
+import org.junit.Test
-class MockForJavaTest extends GroovyTestCase {
+final class MockForJavaTest {
+
+ @Test
void testIterator() {
def iteratorContext = new MockFor(Iterator)
iteratorContext.demand.hasNext() { true }
iteratorContext.demand.hasNext() { true }
iteratorContext.demand.hasNext() { false }
def iterator = iteratorContext.proxyDelegateInstance()
- iteratorContext.demand.next() { "foo" }
+ iteratorContext.demand.next() { 'foo' }
def iterator2 = iteratorContext.proxyDelegateInstance()
assert new IteratorCounter().count(iterator2) == 2
- assert iterator2.next() == "foo"
+ assert iterator2.next() == 'foo'
iteratorContext.verify(iterator2)
assert new IteratorCounter().count(iterator) == 2
@@ -45,13 +47,31 @@ class MockForJavaTest extends GroovyTestCase {
iteratorContext.verify(iterator3)
}
+ // GROOVY-4843
+ @Test
+ void testStream() {
+ def streamContext = new MockFor(FileInputStream)
+ streamContext.demand.read(1..1) { byte[] b ->
+ b[0] = 1
+ b[1] = 2
+ return 2
+ }
+ def p = System.getProperty('os.name').contains('Windows') ? 'NUL' :
'/dev/null'
+ def s = streamContext.proxyDelegateInstance(p)
+ byte[] buffer = new byte[2]
+ assert s.read(buffer) == 2
+ assert buffer[0] == 1
+ assert buffer[1] == 2
+ streamContext.verify(s)
+ }
+
+ @Test
void testString() {
def stringContext = new MockFor(String)
- stringContext.demand.endsWith(2..2) { String arg -> arg == "foo" }
+ stringContext.demand.endsWith(2..2) { String arg -> arg == 'foo' }
def s = stringContext.proxyDelegateInstance()
- assert !s.endsWith("bar")
- assert s.endsWith("foo")
+ assert !s.endsWith('bar')
+ assert s.endsWith('foo')
stringContext.verify(s)
}
-
-}
\ No newline at end of file
+}