You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2017/10/09 06:08:14 UTC
groovy git commit: GROOVY-8252: AIOOBE in combination with ncurry and
rcurry (closes #612)
Repository: groovy
Updated Branches:
refs/heads/master 20848bd1f -> 9a7c98d24
GROOVY-8252: AIOOBE in combination with ncurry and rcurry (closes #612)
Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/9a7c98d2
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/9a7c98d2
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/9a7c98d2
Branch: refs/heads/master
Commit: 9a7c98d249928da6c81f71cbac3b03574271fcbb
Parents: 20848bd
Author: paulk <pa...@asert.com.au>
Authored: Mon Oct 2 11:29:43 2017 +1000
Committer: paulk <pa...@asert.com.au>
Committed: Mon Oct 9 16:07:36 2017 +1000
----------------------------------------------------------------------
src/main/groovy/lang/Closure.java | 8 ++++++
.../codehaus/groovy/runtime/CurriedClosure.java | 24 +++++++++++++++--
src/test/groovy/ClosureCurryTest.groovy | 28 +++++++++++++++++++-
3 files changed, 57 insertions(+), 3 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/groovy/blob/9a7c98d2/src/main/groovy/lang/Closure.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/lang/Closure.java b/src/main/groovy/lang/Closure.java
index e6bd284..9e3d432 100644
--- a/src/main/groovy/lang/Closure.java
+++ b/src/main/groovy/lang/Closure.java
@@ -556,6 +556,10 @@ public abstract class Closure<V> extends GroovyObjectSupport implements Cloneabl
* assert halver(8) == 4
* </pre>
*
+ * The position of the curried parameters will be calculated lazily, for example,
+ * if two overloaded doCall methods are available, the supplied arguments plus the
+ * curried arguments will be concatenated and the result used for method selection.
+ *
* @param arguments the arguments to bind
* @return the new closure with its arguments bound
* @see #curry(Object...)
@@ -600,6 +604,10 @@ public abstract class Closure<V> extends GroovyObjectSupport implements Cloneabl
* // [BEE, Cat, ant, dog] Not found but would belong in position 3
* </pre>
*
+ * The position of the curried parameters will be calculated eagerly
+ * and implies all arguments prior to the specified n index are supplied.
+ * Default parameter values prior to the n index will not be available.
+ *
* @param n the index from which to bind parameters (may be -ve in which case it will be normalized)
* @param arguments the arguments to bind
* @return the new closure with its arguments bound
http://git-wip-us.apache.org/repos/asf/groovy/blob/9a7c98d2/src/main/org/codehaus/groovy/runtime/CurriedClosure.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/runtime/CurriedClosure.java b/src/main/org/codehaus/groovy/runtime/CurriedClosure.java
index 1d2d876..22a83cb 100644
--- a/src/main/org/codehaus/groovy/runtime/CurriedClosure.java
+++ b/src/main/org/codehaus/groovy/runtime/CurriedClosure.java
@@ -39,15 +39,25 @@ import groovy.lang.Closure;
* assert new CurriedClosure(unitAdder, "six", "ty")("minutes") == "sixty minutes"
* </pre>
*
- * @author Jochen Theodorou
- * @author Paul King
+ * Notes:
+ * <ul>
+ * <li>Caters for Groovy's lazy (rcurry) and eager (ncurry) calculation of argument position</li>
+ * </ul>
*/
public final class CurriedClosure<V> extends Closure<V> {
private final Object[] curriedParams;
+ private final int minParamsExpected;
private int index;
private Class varargType = null;
+ /**
+ * Creates the curried closure.
+ *
+ * @param index the position where the parameters should be injected (-ve for lazy)
+ * @param uncurriedClosure the closure to be called after the curried parameters are injected
+ * @param arguments the supplied parameters
+ */
public CurriedClosure(int index, Closure<V> uncurriedClosure, Object... arguments) {
super(uncurriedClosure.clone());
curriedParams = arguments;
@@ -65,6 +75,9 @@ public final class CurriedClosure<V> extends Closure<V> {
if (index < 0) {
// normalise
this.index += origMaxLen;
+ minParamsExpected = 0;
+ } else {
+ minParamsExpected = index + arguments.length;
}
if (maximumNumberOfParameters < 0) {
throw new IllegalArgumentException("Can't curry " + arguments.length + " arguments for a closure with " + origMaxLen + " parameters.");
@@ -77,6 +90,8 @@ public final class CurriedClosure<V> extends Closure<V> {
throw new IllegalArgumentException("To curry " + arguments.length + " argument(s) expect index range 0.." +
maximumNumberOfParameters + " but found " + index);
}
+ } else {
+ minParamsExpected = 0;
}
}
@@ -98,8 +113,13 @@ public final class CurriedClosure<V> extends Closure<V> {
System.arraycopy(arguments, normalizedIndex, newCurriedParams, curriedParams.length + normalizedIndex, arguments.length - normalizedIndex);
return newCurriedParams;
}
+ if (curriedParams.length + arguments.length < minParamsExpected) {
+ throw new IllegalArgumentException("When currying expected at least " + index + " argument(s) to be supplied before known curried arguments but found " + arguments.length);
+ }
final Object newCurriedParams[] = new Object[curriedParams.length + arguments.length];
int newIndex = Math.min(index, curriedParams.length + arguments.length - 1);
+ // rcurried arguments are done lazily to allow normal method selection between overloaded alternatives
+ newIndex = Math.min(newIndex, arguments.length);
System.arraycopy(arguments, 0, newCurriedParams, 0, newIndex);
System.arraycopy(curriedParams, 0, newCurriedParams, newIndex, curriedParams.length);
if (arguments.length - newIndex > 0)
http://git-wip-us.apache.org/repos/asf/groovy/blob/9a7c98d2/src/test/groovy/ClosureCurryTest.groovy
----------------------------------------------------------------------
diff --git a/src/test/groovy/ClosureCurryTest.groovy b/src/test/groovy/ClosureCurryTest.groovy
index fbd5d1d..5bdfb42 100644
--- a/src/test/groovy/ClosureCurryTest.groovy
+++ b/src/test/groovy/ClosureCurryTest.groovy
@@ -21,7 +21,7 @@ package groovy
import org.codehaus.groovy.runtime.DefaultGroovyMethods
/**
- * @author Hallvard Tr�tteberg
+ * Tests for curried closures
*/
class ClosureCurryTest extends GroovyTestCase {
@@ -252,6 +252,7 @@ class ClosureCurryTest extends GroovyTestCase {
private a(b,c){ "2Obj: $b $c" }
private a(b){ "1Obj: $b" }
+
void testCurryWithMethodClosure() {
def c = (this.&a).curry(0)
assert c(1,2) == '3Int: 0 1 2'
@@ -265,4 +266,29 @@ class ClosureCurryTest extends GroovyTestCase {
assert b(1) == '2Obj: 1 0'
assert b() == '1Obj: 0'
}
+
+ void testInsufficientSuppliedArgsWhenUsingNCurry() {
+ def cl = { a, b, c = 'x' -> a + b + c }
+ assert cl.ncurry(1, 'b')('a') == 'abx'
+ // ncurry is done eagerly and implies a minimum number of params
+ shouldFail(IllegalArgumentException) {
+ cl.ncurry(1, 'b')()
+ }
+ // rcurry is done lazily
+ assert cl.rcurry('b', 'c')('a') == 'abc'
+ assert cl.rcurry('b', 'c')() == 'bcx'
+ }
+
+ void testCurryInComboWithDefaultArgs() {
+ def cl = { a, b, c='c', d='d' -> a + b + c + d }
+ assert 'abcd' == cl.ncurry(0, 'a')('b')
+ assert 'xycd' == cl.ncurry(1, 'y')('x')
+ assert 'abdd' == cl.ncurry(0, 'a').rcurry('d')('b')
+ assert 'abcx' == cl.ncurry(3, 'x')('a', 'b', 'c')
+ shouldFail(IllegalArgumentException) {
+ // ncurry is done eagerly and implies a minimum number of params
+ // default arguments are ignored prior to the ncurried argument
+ cl.ncurry(4, 'd')('a', 'b')
+ }
+ }
}