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 <eric.mil...@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.*