You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2024/03/09 22:36:39 UTC
(groovy) branch master updated: GROOVY-11186: iterable, iterator and stream as spread method argument(s)
This is an automated email from the ASF dual-hosted git repository.
emilles 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 5fbc4fbdb5 GROOVY-11186: iterable, iterator and stream as spread method argument(s)
5fbc4fbdb5 is described below
commit 5fbc4fbdb54450845db49c1a014e924dafd68fc1
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sat Mar 9 16:25:44 2024 -0600
GROOVY-11186: iterable, iterator and stream as spread method argument(s)
---
.../groovy/runtime/ScriptBytecodeAdapter.java | 18 ++--
src/test/gls/invocation/MethodSelectionTest.groovy | 13 ---
src/test/groovy/GroovyMethodsTest.groovy | 6 +-
src/test/groovy/SpreadArgTest.groovy | 117 +++++++++++++++++++++
src/test/groovy/SpreadDotTest.groovy | 99 +++++++++--------
src/test/groovy/bugs/Groovy9515.groovy | 43 --------
.../classgen/asm/sc/BugsStaticCompileTest.groovy | 19 ----
7 files changed, 187 insertions(+), 128 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java b/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
index 483e016524..308e793a85 100644
--- a/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
+++ b/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
@@ -47,6 +47,7 @@ import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.util.stream.BaseStream;
/**
* A static helper class to interface bytecode and runtime
@@ -920,17 +921,22 @@ public class ScriptBytecodeAdapter {
//spread
public static Object[] despreadList(final Object[] args, final Object[] spreads, final int[] positions) {
List<Object> ret = new ArrayList<>();
- int argsPos = 0;
- int spreadPos = 0;
+ int argsPos = 0, spreadsPos = 0;
for (int position : positions) {
- for (; argsPos < position; argsPos++) {
+ for (; argsPos < position; ++argsPos) {
ret.add(args[argsPos]);
}
- Object value = spreads[spreadPos];
+ Object value = spreads[spreadsPos];
if (value == null) {
ret.add(null);
} else if (value instanceof List) {
ret.addAll((List<?>) value);
+ } else if (value instanceof Iterable) {
+ ((Iterable<?>) value).forEach(ret::add);
+ } else if (value instanceof Iterator) {
+ ((Iterator<?>) value).forEachRemaining(ret::add);
+ } else if (value instanceof BaseStream) {
+ ((BaseStream<?,?>) value).iterator().forEachRemaining(ret::add);
} else if (value.getClass().isArray()) {
ret.addAll(DefaultTypeTransformation.primitiveArrayToList(value));
} else {
@@ -940,9 +946,9 @@ public class ScriptBytecodeAdapter {
}
throw new IllegalArgumentException(error);
}
- spreadPos++;
+ ++spreadsPos;
}
- for (; argsPos < args.length; argsPos++) {
+ for (; argsPos < args.length; ++argsPos) {
ret.add(args[argsPos]);
}
return ret.toArray();
diff --git a/src/test/gls/invocation/MethodSelectionTest.groovy b/src/test/gls/invocation/MethodSelectionTest.groovy
index e347bbb2f8..9c0f3698fd 100644
--- a/src/test/gls/invocation/MethodSelectionTest.groovy
+++ b/src/test/gls/invocation/MethodSelectionTest.groovy
@@ -229,19 +229,6 @@ final class MethodSelectionTest extends gls.CompilableTestSupport {
'''
}
- void testSpreadOperatorAndVarargs(){
- assertScript '''
- class SpreadBug {
- def foo(String... args) {
- bar(*args)
- }
- def bar(String... args) {args.length}
- }
- def sb = new SpreadBug()
- assert sb.foo("1","42")==2
- '''
- }
-
// GROOVY-3977
void testBDandBIToFloatAutoConversionInMethodSelection() {
assertScript '''
diff --git a/src/test/groovy/GroovyMethodsTest.groovy b/src/test/groovy/GroovyMethodsTest.groovy
index d42aca5084..7a761e5d2b 100644
--- a/src/test/groovy/GroovyMethodsTest.groovy
+++ b/src/test/groovy/GroovyMethodsTest.groovy
@@ -19,16 +19,16 @@
package groovy
import groovy.test.GroovyTestCase
+import org.codehaus.groovy.util.StringUtil
import java.awt.Dimension
import java.nio.CharBuffer
import java.util.concurrent.LinkedBlockingQueue
-import org.codehaus.groovy.util.StringUtil
/**
* Tests various GDK methods
*/
-class GroovyMethodsTest extends GroovyTestCase {
+final class GroovyMethodsTest extends GroovyTestCase {
void testAbs() {
def absoluteNumberOne = 1
@@ -2357,7 +2357,7 @@ class WackyHashCode {
}
class Things implements Iterable<String> {
- Iterator iterator() {
+ Iterator<String> iterator() {
["a", "B", "c"].iterator()
}
}
diff --git a/src/test/groovy/SpreadArgTest.groovy b/src/test/groovy/SpreadArgTest.groovy
new file mode 100644
index 0000000000..c40e0fc645
--- /dev/null
+++ b/src/test/groovy/SpreadArgTest.groovy
@@ -0,0 +1,117 @@
+/*
+ * 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-11186
+ @Test
+ void testSpreadOther() {
+ assertScript '''
+ int f(int x, int y, int z) {
+ x + y + z
+ }
+
+ Set<Number> nums = [1, 2, 39]
+ assert f(*nums) == 42
+ '''
+ }
+
+ // GROOVY-11186
+ @Test
+ void testSpreadStream() {
+ assertScript '''
+ int f(int x, int y, int z) {
+ x + y + z
+ }
+
+ def nums = java.util.stream.IntStream.of(1, 2, 39)
+ assert f(*nums) == 42
+ '''
+ }
+
+ // 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/SpreadDotTest.groovy b/src/test/groovy/SpreadDotTest.groovy
index 75747523ee..164cb34b24 100644
--- a/src/test/groovy/SpreadDotTest.groovy
+++ b/src/test/groovy/SpreadDotTest.groovy
@@ -18,17 +18,21 @@
*/
package groovy
-import groovy.test.GroovyTestCase
+import org.junit.Test
+
+import static org.junit.Assert.assertEquals
/**
- * Test for the spread dot operator "*.".
+ * Tests for the spread dot operator "*.".
*
* For an example,
* list*.property
* means
* list.collect { it?.property }
*/
-class SpreadDotTest extends GroovyTestCase {
+final class SpreadDotTest {
+
+ @Test
void testSpreadDot() {
def m1 = ["a": 1, "b": 2]
def m2 = ["a": 11, "b": 22]
@@ -59,6 +63,7 @@ class SpreadDotTest extends GroovyTestCase {
assert x == [m1, m2, m3, null, d, y]
}
+ @Test
void testSpreadDot2() {
def a = new SpreadDotDemo()
def b = new SpreadDotDemo2()
@@ -68,6 +73,7 @@ class SpreadDotTest extends GroovyTestCase {
assert [a, b]*.fnB() == [a.fnB(), b.fnB()]
}
+ @Test
void testSpreadDotArrays() {
def a = new SpreadDotDemo()
def b = new SpreadDotDemo2()
@@ -86,16 +92,18 @@ class SpreadDotTest extends GroovyTestCase {
assert pets*.length() == nums
}
- void testSpreadDotOnArrays2() {
+ @Test
+ void testSpreadDotArrays2() {
def books = [Book1, Book2, Book3] as Object[]
- books*.metaClass*.foo = { "Hello, ${delegate.class.name}" }
+ books*.metaClass*.foo = { "Hello, ${delegate.class.simpleName}".toString() }
- assertEquals "Hello, groovy.Book1", new Book1().foo()
- assertEquals "Hello, groovy.Book2", new Book2().foo()
- assertEquals "Hello, groovy.Book3", new Book3().foo()
+ assertEquals("Hello, Book1", new Book1().foo())
+ assertEquals("Hello, Book2", new Book2().foo())
+ assertEquals("Hello, Book3", new Book3().foo())
}
+ @Test
void testSpreadDotAdvanced() {
assertEquals([3, 3], ['cat', 'dog']*.size())
assertEquals([3, 3], (['cat', 'dog'] as Vector)*.size())
@@ -108,6 +116,7 @@ class SpreadDotTest extends GroovyTestCase {
assertEquals(['Large'], new Shirt()*.size())
}
+ @Test
void testSpreadDotMap() {
def map = [A: "one", B: "two", C: "three"]
assert map.collect { child -> child.value.size() } == [3, 3, 5]
@@ -115,6 +124,7 @@ class SpreadDotTest extends GroovyTestCase {
assert map*.getKey() == ['A', 'B', 'C']
}
+ @Test
void testSpreadDotAttribute() {
def s = new Singlet()
assert s.size == 1
@@ -124,7 +134,8 @@ class SpreadDotTest extends GroovyTestCase {
assert wardrobe*.@size == [12, 12]
}
- void testNewLine() {
+ @Test
+ void testSpreadDotMultiLine() {
def x = [a: 1, b: 2]
def y = x
*.value
@@ -140,53 +151,53 @@ class SpreadDotTest extends GroovyTestCase {
*.@size
assert y == [12, 12]
}
-}
-
-class SpreadDotDemo {
- java.util.Date getA() {
- return new Date()
- }
- String fnB() {
- return "bb"
- }
+ //--------------------------------------------------------------------------
- String fnB(String m) {
- return "BB$m"
- }
-}
+ static class SpreadDotDemo {
+ Date getA() {
+ return new Date()
+ }
-class SpreadDotDemo2 {
- String getAttribute(String key) {
- return "Attribute $key"
- }
+ String fnB() {
+ return "bb"
+ }
- String get(String key) {
- return getAttribute("Get $key")
+ String fnB(String m) {
+ return "BB$m"
+ }
}
- String fnB() {
- return "cc"
- }
+ static class SpreadDotDemo2 {
+ String getAttribute(String key) {
+ return "Attribute $key"
+ }
- String fnB(String m) {
- return "CC$m"
- }
-}
+ String get(String key) {
+ return getAttribute("Get $key")
+ }
+ String fnB() {
+ return "cc"
+ }
-class Book1 {}
+ String fnB(String m) {
+ return "CC$m"
+ }
+ }
-class Book2 {}
+ static class Book1 {}
-class Book3 {}
+ static class Book2 {}
-class Shirt {
- def size() { 'Large' }
-}
+ static class Book3 {}
-class Singlet {
- private size = 12
+ static class Shirt {
+ def size() { 'Large' }
+ }
- def getSize() { 1 }
+ static class Singlet {
+ private size = 12
+ def getSize() {1}
+ }
}
diff --git a/src/test/groovy/bugs/Groovy9515.groovy b/src/test/groovy/bugs/Groovy9515.groovy
deleted file mode 100644
index d24da084a6..0000000000
--- a/src/test/groovy/bugs/Groovy9515.groovy
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.bugs
-
-import groovy.transform.CompileStatic
-import org.codehaus.groovy.control.CompilerConfiguration
-import org.junit.Test
-
-@CompileStatic
-final class Groovy9515 {
-
- @Test
- void testSpreadArgsIndy() {
- def config = new CompilerConfiguration()
- config.optimizationOptions.indy = true
- new GroovyShell(config).evaluate '''
-def x(int a) {a}
-def x(int a, int b) {a + b}
-def y(p) {
- x(*p)
-}
-
-assert 1 == y([1])
-assert 3 == y([1, 2])
-'''
- }
-}
diff --git a/src/test/org/codehaus/groovy/classgen/asm/sc/BugsStaticCompileTest.groovy b/src/test/org/codehaus/groovy/classgen/asm/sc/BugsStaticCompileTest.groovy
index e8ca9036f5..4e99b8c489 100644
--- a/src/test/org/codehaus/groovy/classgen/asm/sc/BugsStaticCompileTest.groovy
+++ b/src/test/org/codehaus/groovy/classgen/asm/sc/BugsStaticCompileTest.groovy
@@ -415,25 +415,6 @@ final class BugsStaticCompileTest extends BugsSTCTest implements StaticCompilati
'''
}
- void testShouldSkipSpreadOperator() {
- new GroovyShell().evaluate '''import groovy.transform.CompileStatic
- import static groovy.transform.TypeCheckingMode.SKIP
-
- @CompileStatic // top level must be @CS
- class Foo {
- @CompileStatic(SKIP)
- static void 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 Foo()
- '''
- }
-
// GROOVY-5672
void testTypeCheckedPlusCompileStatic() {
new GroovyShell().evaluate '''import groovy.transform.*