GROOVY-11075: method closure swallows `Exception`
diff --git a/src/main/java/groovy/lang/Closure.java b/src/main/java/groovy/lang/Closure.java
index d26cdb1..da59968 100644
--- a/src/main/java/groovy/lang/Closure.java
+++ b/src/main/java/groovy/lang/Closure.java
@@ -403,19 +403,34 @@
}
/**
- * Invokes the closure without any parameters, returning any value if applicable.
+ * Invokes the closure with no arguments, returning any value if applicable.
*
- * @return the value if applicable or null if there is no return statement in the closure
+ * @return The value if applicable or null if there is no return statement in the closure.
*/
@Override
public V call() {
return call(EMPTY_OBJECT_ARRAY);
}
+ /**
+ * Invokes the closure with given argument(s), returning any value if applicable.
+ *
+ * @param arguments could be a single value or a List of values
+ * @return The value if applicable or null if there is no return statement in the closure.
+ */
+ public V call(final Object arguments) {
+ return call(new Object[]{arguments});
+ }
+
+ /**
+ * Invokes the closure with given argument(s), returning any value if applicable.
+ *
+ * @return The value if applicable or null if there is no return statement in the closure.
+ */
@SuppressWarnings("unchecked")
- public V call(Object... args) {
+ public V call(final Object... arguments) {
try {
- return (V) getMetaClass().invokeMethod(this, "doCall", args);
+ return (V) getMetaClass().invokeMethod(this, "doCall", arguments);
} catch (InvokerInvocationException e) {
UncheckedThrow.rethrow(e.getCause());
return null; // unreachable statement
@@ -424,16 +439,6 @@
}
}
- /**
- * Invokes the closure, returning any value if applicable.
- *
- * @param arguments could be a single value or a List of values
- * @return the value if applicable or null if there is no return statement in the closure
- */
- public V call(final Object arguments) {
- return call(new Object[]{arguments});
- }
-
protected static Object throwRuntimeException(Throwable throwable) {
if (throwable instanceof RuntimeException) {
throw (RuntimeException) throwable;
diff --git a/src/main/java/groovy/lang/MetaClassImpl.java b/src/main/java/groovy/lang/MetaClassImpl.java
index ebaba99..f069236 100644
--- a/src/main/java/groovy/lang/MetaClassImpl.java
+++ b/src/main/java/groovy/lang/MetaClassImpl.java
@@ -1008,8 +1008,9 @@
final MetaClass ownerMetaClass = registry.getMetaClass(ownerClass);
try {
return ownerMetaClass.invokeMethod(ownerClass, owner, method, arguments, false, false);
- } catch (GroovyRuntimeException e) { // GROOVY-10929: GroovyRuntimeException(cause:IllegalArgumentException) thrown for final fields
- if (!(ownerIsClass && (e instanceof MissingMethodException || e instanceof InvokerInvocationException || e.getCause() instanceof IllegalArgumentException))) {
+ } catch (GroovyRuntimeException e) { // GroovyRuntimeException(cause:IllegalArgumentException) thrown for final fields
+ // InvokerInvocationException(cause:IllegalArgumentException) thrown for not this
+ if (!ownerIsClass || !(e instanceof MissingMethodException || e.getCause() instanceof IllegalArgumentException)) {
throw e;
}
if (MethodClosure.NEW.equals(method)) {
diff --git a/src/test/groovy/ClosureMethodCallTest.groovy b/src/test/groovy/ClosureMethodCallTest.groovy
index a68f476..18e40dc 100644
--- a/src/test/groovy/ClosureMethodCallTest.groovy
+++ b/src/test/groovy/ClosureMethodCallTest.groovy
@@ -20,11 +20,9 @@
import org.junit.Test
-import java.util.concurrent.Executor
import java.util.concurrent.Executors
import static groovy.test.GroovyAssert.assertScript
-import static groovy.test.GroovyAssert.shouldFail
final class ClosureMethodCallTest {
@@ -79,12 +77,6 @@
assert attribute(x: 2, y: 3) == 6
}
- @Test
- void testSystemOutPrintlnAsAClosure() {
- def closure = System.out.&println
- closure('Hello world')
- }
-
@Test // GROOVY-6819
void testFixForIncompatibleClassChangeError() {
assertScript '''
@@ -103,49 +95,19 @@
'''
}
- @Test // GROOVY-9140
- void testCorrectErrorForClassInstanceMethodReference() {
- assertScript '''
- class Y {
- def m() {1}
- }
-
- ref = Y.&m
- assert ref(new Y()) == 1
- '''
-
- shouldFail MissingMethodException, '''
- class Y {
- def m() {1}
- }
-
- ref = Y.&m
- assert ref() == 1
- '''
-
- shouldFail MissingMethodException, '''
- class Y {
- def m() {1}
- }
-
- ref = Y.&m
- assert ref(1) == 1
- '''
- }
-
@Test // GROOVY-9397
void testRespondsToIsThreadSafe() {
- final Executor executor = Executors.newCachedThreadPool()
+ def executor = Executors.newCachedThreadPool()
try {
final Closure action = { -> }
// ensure that executing the closure and calling respondsTo
// concurrently doesn't throw an exception.
- for (int i = 0; i < 500; ++i) {
+ for (i in 1..500) {
executor.execute(action)
executor.execute(() -> action.respondsTo('test'))
}
} finally {
- executor.shutdownNow();
+ executor.shutdownNow()
}
}
}
diff --git a/src/test/groovy/bugs/MethodClosureTest.groovy b/src/test/groovy/bugs/MethodClosureTest.groovy
index 76b0047..7041107 100644
--- a/src/test/groovy/bugs/MethodClosureTest.groovy
+++ b/src/test/groovy/bugs/MethodClosureTest.groovy
@@ -74,6 +74,38 @@
}
@Test
+ void testMethodClosureForPrintln() {
+ assertScript '''
+ def closure = System.out.&println
+ closure('hello world')
+ '''
+ }
+
+ // GROOVY-9140
+ @Test
+ void testMethodClosureWithoutThis() {
+ String base = '''
+ class C {
+ def m() { 11 }
+ }
+ def closure = C.&m
+ '''
+
+ assertScript base + '''
+ Object result = closure(new C())
+ assert result == 11
+ '''
+
+ shouldFail MissingMethodException, base + '''
+ closure()
+ '''
+
+ shouldFail MissingMethodException, base + '''
+ closure("")
+ '''
+ }
+
+ @Test
void testMethodClosureWithCategory() {
assertScript '''
class Bar {
@@ -82,13 +114,11 @@
methodClosure = this.&method
}
}
-
class Foo extends Bar {
def storeMethodClosure() {
methodClosure = super.&method
}
}
-
class BarCategory {
static method(Bar self) { 'result' }
}
@@ -100,11 +130,13 @@
try {
bar.methodClosure()
assert false
- } catch(MissingMethodException ignore) {}
+ } catch(MissingMethodException ignore) {
+ }
try {
foo.methodClosure()
assert false
- } catch(MissingMethodException ignore) {}
+ } catch(MissingMethodException ignore) {
+ }
use(BarCategory) {
assert bar.methodClosure() == 'result'
assert foo.methodClosure() == 'result'
@@ -112,6 +144,20 @@
'''
}
+ // GROOVY-11075
+ @Test
+ void testMethodClosureCheckedException() {
+ shouldFail IOException, '''
+ class Foo {
+ static void bar(String str) {
+ throw new IOException()
+ }
+ }
+ def baz = Foo.&bar
+ baz("")
+ '''
+ }
+
// GROOVY-10929
@Test
void testMethodClosureIllegalArgumentException() {