You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by he...@apache.org on 2018/09/12 06:32:28 UTC
[commons-jexl] branch master updated: JEXL-271: reporting the
unbound parameters (curry()), tls interpreter cleanup
This is an automated email from the ASF dual-hosted git repository.
henrib pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-jexl.git
The following commit(s) were added to refs/heads/master by this push:
new 8bdde09 JEXL-271: reporting the unbound parameters (curry()), tls interpreter cleanup
8bdde09 is described below
commit 8bdde09be9a2e9abe2ee08bbd87f54d4871e602e
Author: Henri Biestro <he...@apache.org>
AuthorDate: Wed Sep 12 08:31:59 2018 +0200
JEXL-271: reporting the unbound parameters (curry()), tls interpreter cleanup
---
.../java/org/apache/commons/jexl3/JexlScript.java | 42 ++++++-----
.../org/apache/commons/jexl3/internal/Closure.java | 7 +-
.../apache/commons/jexl3/internal/Interpreter.java | 8 +--
.../org/apache/commons/jexl3/internal/Scope.java | 29 ++++++--
.../org/apache/commons/jexl3/internal/Script.java | 18 ++---
.../org/apache/commons/jexl3/Issues200Test.java | 83 ++++------------------
.../java/org/apache/commons/jexl3/LambdaTest.java | 67 ++++++++++++++++-
7 files changed, 146 insertions(+), 108 deletions(-)
diff --git a/src/main/java/org/apache/commons/jexl3/JexlScript.java b/src/main/java/org/apache/commons/jexl3/JexlScript.java
index e7c041d..67b3e5d 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlScript.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlScript.java
@@ -24,15 +24,15 @@ import java.util.concurrent.Callable;
/**
* A JEXL Script.
- *
+ *
* <p>A script is some valid JEXL syntax to be executed with a given set of {@link JexlContext} variables.</p>
- *
+ *
* <p>A script is a group of statements, separated by semicolons.</p>
- *
+ *
* <p>The statements can be <code>blocks</code> (curly braces containing code),
* Control statements such as <code>if</code> and <code>while</code>
* as well as expressions and assignment statements.</p>
- *
+ *
* <p>Do <em>not</em> create classes that implement this interface; delegate or compose instead.</p>
*
* @since 1.1
@@ -41,21 +41,21 @@ public interface JexlScript {
/**
* Returns the source text of this expression.
- *
+ *
* @return the source text
*/
String getSourceText();
/**
* Recreates the source text of this expression from the internal syntactic tree.
- *
+ *
* @return the source text
*/
String getParsedText();
/**
* Recreates the source text of this expression from the internal syntactic tree.
- *
+ *
* @param indent the number of spaces for indentation, 0 meaning no indentation
* @return the source text
*/
@@ -86,15 +86,23 @@ public interface JexlScript {
/**
* Gets this script parameters.
- *
+ *
* @return the parameters or null
* @since 2.1
*/
String[] getParameters();
/**
+ * Gets this script unbound parameters.
+ * <p>Parameters that haven't been bound by a previous call to curry().
+ * @return the parameters or null
+ * @since 3.2
+ */
+ String[] getUnboundParameters();
+
+ /**
* Gets this script local variables.
- *
+ *
* @return the local variables or null
* @since 2.1
*/
@@ -104,7 +112,7 @@ public interface JexlScript {
* Gets this script variables.
* <p>Note that since variables can be in an ant-ish form (ie foo.bar.quux), each variable is returned as
* a list of strings where each entry is a fragment of the variable ({"foo", "bar", "quux"} in the example.</p>
- *
+ *
* @return the variables or null
* @since 2.1
*/
@@ -112,17 +120,17 @@ public interface JexlScript {
/**
* Gets this script pragmas.
- *
+ *
* @return the (non null, may be empty) pragmas map
*/
Map<String, Object> getPragmas();
/**
* Creates a Callable from this script.
- *
+ *
* <p>This allows to submit it to an executor pool and provides support for asynchronous calls.</p>
* <p>The interpreter will handle interruption/cancellation gracefully if needed.</p>
- *
+ *
* @param context the context
* @return the callable
* @since 2.1
@@ -131,10 +139,10 @@ public interface JexlScript {
/**
* Creates a Callable from this script.
- *
+ *
* <p>This allows to submit it to an executor pool and provides support for asynchronous calls.</p>
* <p>The interpreter will handle interruption/cancellation gracefully if needed.</p>
- *
+ *
* @param context the context
* @param args the script arguments
* @return the callable
@@ -144,10 +152,10 @@ public interface JexlScript {
/**
* Curries this script, returning a script with bound arguments.
- *
+ *
* <p>If this script does not declare parameters or if all of them are already bound,
* no error is generated and this script is returned.</p>
- *
+ *
* @param args the arguments to bind
* @return the curried script or this script if no binding can occur
*/
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Closure.java b/src/main/java/org/apache/commons/jexl3/internal/Closure.java
index c297db0..009c962 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Closure.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Closure.java
@@ -37,7 +37,7 @@ public class Closure extends Script {
super(theCaller.jexl, null, lambda);
frame = lambda.createFrame(theCaller.frame);
}
-
+
/**
* Creates a curried version of a script.
* @param base the base script
@@ -83,6 +83,11 @@ public class Closure extends Script {
return true;
}
+ @Override
+ public String[] getUnboundParameters() {
+ return frame.getUnboundParameters();
+ }
+
/**
* Sets the hoisted index of a given symbol, ie the target index of a parent hoisted symbol in this closure's frame.
* <p>This is meant to allow a locally defined function to "see" and call itself as a local (hoisted) variable;
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
index 09c229f..51b25ee 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
@@ -180,11 +180,11 @@ public class Interpreter extends InterpreterBase {
JexlContext.ThreadLocal tcontext = null;
JexlEngine tjexl = null;
try {
- cancelCheck(node);
if (context instanceof JexlContext.ThreadLocal) {
tcontext = jexl.putThreadLocal((JexlContext.ThreadLocal) context);
}
tjexl = jexl.putThreadEngine(jexl);
+ cancelCheck(node);
return node.jjtAccept(this, null);
} catch (JexlException.Return xreturn) {
return xreturn.getValue();
@@ -1413,7 +1413,7 @@ public class Interpreter extends InterpreterBase {
} else if (context.has(methodName)) {
functor = context.get(methodName);
isavar = functor != null;
- }
+ }
// name is a variable, cant be cached
cacheable &= !isavar;
}
@@ -1431,7 +1431,7 @@ public class Interpreter extends InterpreterBase {
} else {
return unsolvableMethod(node, "?");
}
-
+
// solving the call site
CallDispatcher call = new CallDispatcher(node, cacheable);
try {
@@ -1439,7 +1439,7 @@ public class Interpreter extends InterpreterBase {
Object eval = call.tryEval(target, methodName, argv);
if (JexlEngine.TRY_FAILED != eval) {
return eval;
- }
+ }
boolean functorp = false;
boolean narrow = false;
// pseudo loop to try acquiring methods without and with argument narrowing
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Scope.java b/src/main/java/org/apache/commons/jexl3/internal/Scope.java
index 0625daf..245f38c 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Scope.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Scope.java
@@ -47,6 +47,10 @@ public final class Scope {
* The map of local hoisted variables to parent scope variables, ie closure.
*/
private Map<Integer, Integer> hoistedVariables = null;
+ /**
+ * The empty string array.
+ */
+ private static final String[] EMPTY_STRS = new String[0];
/**
* Creates a new scope with a list of parameters.
@@ -229,7 +233,7 @@ public final class Scope {
* @return the symbol names
*/
public String[] getSymbols() {
- return namedVariables != null ? namedVariables.keySet().toArray(new String[0]) : new String[0];
+ return namedVariables != null ? namedVariables.keySet().toArray(new String[0]) : EMPTY_STRS;
}
/**
@@ -237,17 +241,22 @@ public final class Scope {
* @return the parameter names
*/
public String[] getParameters() {
- if (namedVariables != null && parms > 0) {
- String[] pa = new String[parms];
+ return getParameters(0);
+ }
+ protected String[] getParameters(int bound) {
+ int unbound = parms - bound;
+ if (namedVariables != null && unbound > 0) {
+ String[] pa = new String[unbound];
int p = 0;
for (Map.Entry<String, Integer> entry : namedVariables.entrySet()) {
- if (entry.getValue().intValue() < parms) {
+ int argn = entry.getValue();
+ if (argn >= bound && argn < parms) {
pa[p++] = entry.getKey();
}
}
return pa;
} else {
- return null;
+ return EMPTY_STRS;
}
}
@@ -267,7 +276,7 @@ public final class Scope {
}
return pa;
} else {
- return null;
+ return EMPTY_STRS;
}
}
@@ -296,6 +305,14 @@ public final class Scope {
}
/**
+ * Gets this script unbound parameters, i.e. parameters not bound through curry().
+ * @return the parameter names
+ */
+ public String[] getUnboundParameters() {
+ return scope.getParameters(curried);
+ }
+
+ /**
* Gets the scope.
* @return this frame scope
*/
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Script.java b/src/main/java/org/apache/commons/jexl3/internal/Script.java
index f7450cf..2cd6642 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Script.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Script.java
@@ -123,7 +123,7 @@ public class Script implements JexlScript, JexlExpression {
public String getParsedText() {
return getParsedText(2);
}
-
+
@Override
public String getParsedText(int indent) {
Debugger debug = new Debugger();
@@ -191,7 +191,7 @@ public class Script implements JexlScript, JexlExpression {
Interpreter interpreter = createInterpreter(context, frame);
return interpreter.interpret(script);
}
-
+
@Override
public JexlScript curry(Object... args) {
String[] parms = script.getParameters();
@@ -201,20 +201,16 @@ public class Script implements JexlScript, JexlExpression {
return new Closure(this, args);
}
- /**
- * Gets this script parameters.
- * @return the parameters or null
- * @since 3.0
- */
@Override
public String[] getParameters() {
return script.getParameters();
}
- /**
- * Gets this script local variables.
- * @return the local variables or null
- */
+ @Override
+ public String[] getUnboundParameters() {
+ return getParameters();
+ }
+
@Override
public String[] getLocalVariables() {
return script.getLocalVariables();
diff --git a/src/test/java/org/apache/commons/jexl3/Issues200Test.java b/src/test/java/org/apache/commons/jexl3/Issues200Test.java
index 1102c07..184b371 100644
--- a/src/test/java/org/apache/commons/jexl3/Issues200Test.java
+++ b/src/test/java/org/apache/commons/jexl3/Issues200Test.java
@@ -469,7 +469,7 @@ public class Issues200Test extends JexlTestCase {
result = script.execute(ctx);
Assert.assertEquals(10, result);
}
-
+
@Test
public void test230() throws Exception {
JexlEngine jexl = new JexlBuilder().cache(4).create();
@@ -488,7 +488,7 @@ public class Issues200Test extends JexlTestCase {
Assert.assertEquals(42, value);
}
}
-
+
@Test
public void test265() throws Exception {
JexlEngine jexl = new JexlBuilder().cache(4).create();
@@ -502,17 +502,17 @@ public class Issues200Test extends JexlTestCase {
// ambiguous, parsing fails
}
script = jexl.createScript("(true) ? (x) : abs(2)");
- result = script.execute(ctxt);
+ result = script.execute(ctxt);
Assert.assertEquals(42, result);
script = jexl.createScript("(true) ? x : (abs(3))");
- result = script.execute(ctxt);
+ result = script.execute(ctxt);
Assert.assertEquals(42, result);
script = jexl.createScript("(!true) ? abs(4) : x");
- result = script.execute(ctxt);
+ result = script.execute(ctxt);
Assert.assertEquals(42, result);
}
-
-
+
+
/**
* An iterator that implements Closeable (at least implements a close method).
*/
@@ -574,25 +574,25 @@ public class Issues200Test extends JexlTestCase {
public Arithmetic266(boolean strict) {
super(strict);
}
-
+
static void closeIterator(Iterator266 i266) {
Deque<Iterator266> queue = TLS_FOREACH.get();
if (queue != null) {
queue.remove(i266);
}
}
-
+
public Iterator<?> forEach(Iterable<?> collection) {
Iterator266 it266 = new Iterator266((Iterator<Object>) collection.iterator());
Deque<Iterator266> queue = TLS_FOREACH.get();
queue.addFirst(it266);
return it266;
}
-
+
public Iterator<?> forEach(Map<?,?> collection) {
return forEach(collection.values());
}
-
+
public void remove() {
Deque<Iterator266> queue = TLS_FOREACH.get();
Iterator266 i266 = queue.getFirst();
@@ -604,21 +604,21 @@ public class Issues200Test extends JexlTestCase {
}
}
}
-
+
@Test
public void test266() throws Exception {
Object result;
JexlScript script;
JexlEngine jexl = new JexlBuilder().arithmetic(new Arithmetic266(true)).create();
JexlContext ctxt = new MapContext();
-
+
List<Integer> li = new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4, 5 ,6));
ctxt.set("list", li);
script = jexl.createScript("for (var item : list) { if (item <= 3) remove(); } return size(list)");
result = script.execute(ctxt);
Assert.assertEquals(3, result);
Assert.assertEquals(3, li.size());
-
+
Map<String, Integer> msi = new HashMap<String, Integer>();
msi.put("a", 1);
msi.put("b", 2);
@@ -632,7 +632,7 @@ public class Issues200Test extends JexlTestCase {
Assert.assertEquals(4, result);
Assert.assertEquals(4, msi.size());
}
-
+
@Test
public void test267() throws Exception {
Object result;
@@ -652,57 +652,4 @@ public class Issues200Test extends JexlTestCase {
result = script.execute(ctxt);
Assert.assertTrue(result instanceof JexlScript);
}
-
- @Test
- public void test270() throws Exception {
- JexlEngine jexl = new JexlBuilder().create();
- JexlScript base = jexl.createScript("(x, y, z)->{ x + y + z }");
- String text = base.toString();
- JexlScript script = base.curry(5, 15);
- Assert.assertEquals(text, script.toString());
-
- JexlEvalContext ctxt = new JexlEvalContext();
- ctxt.set("s", base);
- script = jexl.createScript("return s");
- Object result = script.execute(ctxt);
- Assert.assertEquals(text, result.toString());
-
- script = jexl.createScript("return s.curry(1)");
- result = script.execute(ctxt);
- Assert.assertEquals(text, result.toString());
- }
-
- @Test
- public void test271a() throws Exception {
- JexlEngine jexl = new JexlBuilder().strict(false).create();
- JexlScript base = jexl.createScript("var base = 1; var x = (a)->{ var y = (b) -> {base + b}; return base + y(a)}; x(40)");
- Object result = base.execute(null);
- Assert.assertEquals(42, result);
- }
-
- @Test
- public void test271b() throws Exception {
- JexlEngine jexl = new JexlBuilder().strict(false).create();
- JexlScript base = jexl.createScript("var base = 2; var sum = (x, y, z)->{ base + x + y + z }; var y = sum.curry(1); y(2,3)");
- Object result = base.execute(null);
- Assert.assertEquals(8, result);
- }
-
- @Test
- public void test271c() throws Exception {
- JexlEngine jexl = new JexlBuilder().strict(false).create();
- JexlScript base = jexl.createScript("(x, y, z)->{ 2 + x + y + z };");
- JexlScript y = base.curry(1);
- Object result = y.execute((JexlContext) null, 2, 3);
- Assert.assertEquals(8, result);
- }
-
- @Test
- public void test271d() throws Exception {
- JexlEngine jexl = new JexlBuilder().strict(false).create();
- JexlScript base = jexl.createScript("var base = 2; return (x, y, z)->{ base + x + y + z };");
- JexlScript y = ((JexlScript) base.execute(null)).curry(1);
- Object result = y.execute((JexlContext) null, 2, 3);
- Assert.assertEquals(8, result);
- }
}
diff --git a/src/test/java/org/apache/commons/jexl3/LambdaTest.java b/src/test/java/org/apache/commons/jexl3/LambdaTest.java
index a4f18e9..353cbc2 100644
--- a/src/test/java/org/apache/commons/jexl3/LambdaTest.java
+++ b/src/test/java/org/apache/commons/jexl3/LambdaTest.java
@@ -164,7 +164,7 @@ public class LambdaTest extends JexlTestCase {
Assert.assertTrue(result instanceof JexlScript);
s15 = (JexlScript) result;
localv = s15.getLocalVariables();
- Assert.assertNull(localv);
+ Assert.assertEquals(0, localv.length);
hvars = s15.getVariables();
Assert.assertEquals(1, hvars.size());
@@ -246,11 +246,20 @@ public class LambdaTest extends JexlTestCase {
JexlEngine jexl = new Engine();
JexlScript script;
Object result;
+ String[] parms;
JexlScript base = jexl.createScript("(x, y, z)->{ x + y + z }");
+ parms = base.getUnboundParameters();
+ Assert.assertEquals(3, parms.length);
script = base.curry(5);
+ parms = script.getUnboundParameters();
+ Assert.assertEquals(2, parms.length);
script = script.curry(15);
+ parms = script.getUnboundParameters();
+ Assert.assertEquals(1, parms.length);
script = script.curry(22);
+ parms = script.getUnboundParameters();
+ Assert.assertEquals(0, parms.length);
result = script.execute(null);
Assert.assertEquals(42, result);
}
@@ -260,9 +269,12 @@ public class LambdaTest extends JexlTestCase {
JexlEngine jexl = new Engine();
JexlScript script;
Object result;
+ String[] parms;
JexlScript base = jexl.createScript("(x, y, z)->{ x + y + z }");
script = base.curry(5, 15);
+ parms = script.getUnboundParameters();
+ Assert.assertEquals(1, parms.length);
script = script.curry(22);
result = script.execute(null);
Assert.assertEquals(42, result);
@@ -279,4 +291,57 @@ public class LambdaTest extends JexlTestCase {
result = script.execute(null, 22);
Assert.assertEquals(42, result);
}
+
+ @Test
+ public void test270() throws Exception {
+ JexlEngine jexl = new Engine();
+ JexlScript base = jexl.createScript("(x, y, z)->{ x + y + z }");
+ String text = base.toString();
+ JexlScript script = base.curry(5, 15);
+ Assert.assertEquals(text, script.toString());
+
+ JexlEvalContext ctxt = new JexlEvalContext();
+ ctxt.set("s", base);
+ script = jexl.createScript("return s");
+ Object result = script.execute(ctxt);
+ Assert.assertEquals(text, result.toString());
+
+ script = jexl.createScript("return s.curry(1)");
+ result = script.execute(ctxt);
+ Assert.assertEquals(text, result.toString());
+ }
+
+ @Test
+ public void test271a() throws Exception {
+ JexlEngine jexl = new Engine();
+ JexlScript base = jexl.createScript("var base = 1; var x = (a)->{ var y = (b) -> {base + b}; return base + y(a)}; x(40)");
+ Object result = base.execute(null);
+ Assert.assertEquals(42, result);
+ }
+
+ @Test
+ public void test271b() throws Exception {
+ JexlEngine jexl = new Engine();
+ JexlScript base = jexl.createScript("var base = 2; var sum = (x, y, z)->{ base + x + y + z }; var y = sum.curry(1); y(2,3)");
+ Object result = base.execute(null);
+ Assert.assertEquals(8, result);
+ }
+
+ @Test
+ public void test271c() throws Exception {
+ JexlEngine jexl = new Engine();
+ JexlScript base = jexl.createScript("(x, y, z)->{ 2 + x + y + z };");
+ JexlScript y = base.curry(1);
+ Object result = y.execute(null, 2, 3);
+ Assert.assertEquals(8, result);
+ }
+
+ @Test
+ public void test271d() throws Exception {
+ JexlEngine jexl = new Engine();
+ JexlScript base = jexl.createScript("var base = 2; return (x, y, z)->{ base + x + y + z };");
+ JexlScript y = ((JexlScript) base.execute(null)).curry(1);
+ Object result = y.execute(null, 2, 3);
+ Assert.assertEquals(8, result);
+ }
}