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/05/20 19:11:25 UTC
[commons-jexl] branch master updated: JEXL-260: added logic in
interpreter, added test, updated release notes & changes
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 cd40852 JEXL-260: added logic in interpreter, added test, updated release notes & changes
cd40852 is described below
commit cd40852bf7063bb5aee438129015e085b51d68fe
Author: henrib <he...@apache.org>
AuthorDate: Sun May 20 21:10:58 2018 +0200
JEXL-260: added logic in interpreter, added test, updated release notes & changes
---
RELEASE-NOTES.txt | 1 +
.../apache/commons/jexl3/internal/Interpreter.java | 116 ++++++++++++++++-----
src/site/xdoc/changes.xml | 3 +
.../org/apache/commons/jexl3/ClassCreatorTest.java | 28 +++++
4 files changed, 124 insertions(+), 24 deletions(-)
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 48a5f1a..f7b5af5 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -39,6 +39,7 @@ What's new in 3.2:
New Features in 3.2:
====================
+* JEXL-260: Automatically inject JexlContext in constructor call when possible
* JEXL-252: Allow for interpolated strings to be used in property access operators
* JEXL-250: Safe navigation operator
* JEXL-248: Allow range subexpression as an array property assignment identifier
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 bc006f6..6eb0fa0 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
@@ -1397,6 +1397,28 @@ public class Interpreter extends InterpreterBase {
}
/**
+ * Concatenate arguments in call(...).
+ * @param target the pseudo-method owner, first to-be argument
+ * @param narrow whether we should attempt to narrow number arguments
+ * @param args the other (non null) arguments
+ * @return the arguments array
+ */
+ private Object[] callArguments(Object target, boolean narrow, Object[] args) {
+ // makes target 1st args, copy others - optionally narrow numbers
+ Object[] nargv = new Object[args.length + 1];
+ if (narrow) {
+ nargv[0] = functionArgument(true, target);
+ for (int a = 1; a <= args.length; ++a) {
+ nargv[a] = functionArgument(true, args[a - 1]);
+ }
+ } else {
+ nargv[0] = target;
+ System.arraycopy(args, 0, nargv, 1, args.length);
+ }
+ return nargv;
+ }
+
+ /**
* Optionally narrows an argument for a function call.
* @param narrow whether narrowing should occur
* @param arg the argument
@@ -1474,7 +1496,26 @@ public class Interpreter extends InterpreterBase {
return me.tryInvoke(name, ii.context, ii.functionArguments(target, narrow, args));
}
}
+
+ /**
+ * A ctor that needs a context as 1st argument.
+ */
+ private static class ContextualCtor extends Funcall {
+ /**
+ * Constructor.
+ * @param jme the method
+ * @param flag the narrow flag
+ */
+ protected ContextualCtor(JexlMethod jme, boolean flag) {
+ super(jme, flag);
+ }
+ @Override
+ protected Object tryInvoke(Interpreter ii, String name, Object target, Object[] args) {
+ return me.tryInvoke(name, target, ii.callArguments(ii.context, narrow, args));
+ }
+ }
+
/**
* Calls a method (or function).
* <p>
@@ -1656,9 +1697,11 @@ public class Interpreter extends InterpreterBase {
@Override
protected Object visit(ASTConstructorNode node, Object data) {
- cancelCheck(node);
+ if (isCancelled()) {
+ throw new JexlException.Cancel(node);
+ }
// first child is class or class name
- Object cobject = node.jjtGetChild(0).jjtAccept(this, data);
+ final Object target = node.jjtGetChild(0).jjtAccept(this, data);
// get the ctor args
int argc = node.jjtGetNumChildren() - 1;
Object[] argv = new Object[argc];
@@ -1667,39 +1710,64 @@ public class Interpreter extends InterpreterBase {
}
try {
- // attempt to reuse last constructor cached in volatile JexlNode.value
+ boolean cacheable = cache;
+ // attempt to reuse last funcall cached in volatile JexlNode.value
if (cache) {
Object cached = node.jjtGetValue();
- if (cached instanceof JexlMethod) {
- JexlMethod mctor = (JexlMethod) cached;
- Object eval = mctor.tryInvoke(null, cobject, argv);
- if (!mctor.tryFailed(eval)) {
+ if (cached instanceof Funcall) {
+ Object eval = ((Funcall) cached).tryInvoke(this, null, target, argv);
+ if (JexlEngine.TRY_FAILED != eval) {
return eval;
}
}
}
- JexlMethod ctor = uberspect.getConstructor(cobject, argv);
- // DG: If we can't find an exact match, narrow the parameters and try again
- if (ctor == null) {
- if (arithmetic.narrowArguments(argv)) {
- ctor = uberspect.getConstructor(cobject, argv);
+ boolean narrow = false;
+ JexlMethod ctor = null;
+ Funcall funcall = null;
+ while (true) {
+ // try as stated
+ ctor = uberspect.getConstructor(target, argv);
+ if (ctor != null) {
+ if (cacheable && ctor.isCacheable()) {
+ funcall = new Funcall(ctor, narrow);
+ }
+ break;
+ }
+ // try with prepending context as first argument
+ Object[] nargv = callArguments(context, narrow, argv);
+ ctor = uberspect.getConstructor(target, nargv);
+ if (ctor != null) {
+ if (cacheable && ctor.isCacheable()) {
+ funcall = new ContextualCtor(ctor, narrow);
+ }
+ argv = nargv;
+ break;
}
- if (ctor == null) {
- String dbgStr = cobject != null ? cobject.toString() : null;
- return unsolvableMethod(node, dbgStr);
+ // if we did not find an exact method by name and we haven't tried yet,
+ // attempt to narrow the parameters and if this succeeds, try again in next loop
+ if (arithmetic.narrowArguments(argv)) {
+ narrow = true;
+ continue;
}
+ // we are done trying
+ break;
}
- Object instance = ctor.invoke(cobject, argv);
- // cache executor in volatile JexlNode.value
- if (cache && ctor.isCacheable()) {
- node.jjtSetValue(ctor);
+ // we have either evaluated and returned or might have found a ctor
+ if (ctor != null) {
+ Object eval = ctor.invoke(target, argv);
+ // cache executor in volatile JexlNode.value
+ if (funcall != null) {
+ node.jjtSetValue(funcall);
+ }
+ return eval;
}
- return instance;
- } catch (JexlException xthru) {
- throw xthru;
+ String tstr = target != null ? target.toString() : "?";
+ return unsolvableMethod(node, tstr);
+ } catch (JexlException.Method xmethod) {
+ throw xmethod;
} catch (Exception xany) {
- String dbgStr = cobject != null ? cobject.toString() : null;
- throw invocationException(node, dbgStr, xany);
+ String tstr = target != null ? target.toString() : "?";
+ throw invocationException(node, tstr, xany);
}
}
diff --git a/src/site/xdoc/changes.xml b/src/site/xdoc/changes.xml
index b038449..225cf4e 100644
--- a/src/site/xdoc/changes.xml
+++ b/src/site/xdoc/changes.xml
@@ -26,6 +26,9 @@
</properties>
<body>
<release version="3.2" date="unreleased">
+ <action dev="henrib" type="add" issue="JEXL-260">
+ Automatically inject JexlContext in constructor call when possible
+ </action>
<action dev="henrib" type="add" issue="JEXL-252" due-to="Dmitri Blinov">
Allow for interpolated strings to be used in property access operators
</action>
diff --git a/src/test/java/org/apache/commons/jexl3/ClassCreatorTest.java b/src/test/java/org/apache/commons/jexl3/ClassCreatorTest.java
index aec7c44..abde7a6 100644
--- a/src/test/java/org/apache/commons/jexl3/ClassCreatorTest.java
+++ b/src/test/java/org/apache/commons/jexl3/ClassCreatorTest.java
@@ -233,4 +233,32 @@ public class ClassCreatorTest extends JexlTestCase {
r = s.execute(null, TwoCtors.class, 100f);
Assert.assertEquals(-100, r);
}
+
+ public static class ContextualCtor {
+ int value = -1;
+
+ public ContextualCtor(JexlContext ctxt) {
+ value = (Integer) ctxt.get("value");
+ }
+
+ public ContextualCtor(JexlContext ctxt, int v) {
+ value = (Integer) ctxt.get("value") + v;
+ }
+
+ public int getValue() {
+ return value;
+ }
+ }
+
+ @Test
+ public void testContextualCtor() throws Exception {
+ MapContext ctxt = new MapContext();
+ ctxt.set("value", 42);
+ JexlScript s = jexl.createScript("(c)->{ new(c).value }");
+ Object r = s.execute(ctxt, ContextualCtor.class);
+ Assert.assertEquals(42, r);
+ s = jexl.createScript("(c, v)->{ new(c, v).value }");
+ r = s.execute(ctxt, ContextualCtor.class, 100);
+ Assert.assertEquals(142, r);
+ }
}
--
To stop receiving notification emails like this one, please contact
henrib@apache.org.