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 2022/03/14 15:31:37 UTC
[commons-jexl] branch master updated: JEXL-364: let evaluation options flow through closures
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 4b3d1ca JEXL-364: let evaluation options flow through closures
4b3d1ca is described below
commit 4b3d1ca57d64655fa6b669d7a9cbe87f494352ee
Author: henrib <he...@apache.org>
AuthorDate: Mon Mar 14 16:31:30 2022 +0100
JEXL-364: let evaluation options flow through closures
---
.../java/org/apache/commons/jexl3/JexlContext.java | 20 +++
.../java/org/apache/commons/jexl3/JexlOptions.java | 14 +-
.../org/apache/commons/jexl3/internal/Closure.java | 14 +-
.../org/apache/commons/jexl3/internal/Engine.java | 9 +-
.../apache/commons/jexl3/internal/Interpreter.java | 12 +-
.../org/apache/commons/jexl3/internal/Script.java | 22 +++-
.../commons/jexl3/internal/TemplateEngine.java | 16 +--
.../jexl3/internal/TemplateInterpreter.java | 9 +-
.../commons/jexl3/internal/TemplateScript.java | 4 +-
.../org/apache/commons/jexl3/Issues300Test.java | 141 +++++++++++++++++++++
.../java/org/apache/commons/jexl3/JXLTTest.java | 50 ++------
.../org/apache/commons/jexl3/JexlTestCase.java | 39 ++++++
.../java/org/apache/commons/jexl3/LambdaTest.java | 138 ++++++++++----------
.../commons/jexl3/internal/OptionsContext.java | 38 ++++++
14 files changed, 389 insertions(+), 137 deletions(-)
diff --git a/src/main/java/org/apache/commons/jexl3/JexlContext.java b/src/main/java/org/apache/commons/jexl3/JexlContext.java
index 658a8f8..67dc118 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlContext.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlContext.java
@@ -172,10 +172,30 @@ public interface JexlContext {
interface PragmaProcessor {
/**
* Process one pragma.
+ * <p>Never called in 3.3, must be implemented for 3.2 binary compatibility reasons.</p>
+ * <p>Typical implementation in 3.3:</p>
+ * <code>
+ * @Override
+ * public void processPragma(String key, Object value) {
+ * processPragma(null, key, value);
+ * }
+ * </code>
* @param key the key
* @param value the value
+ * @deprecated 3.3
*/
void processPragma(String key, Object value);
+
+ /**
+ * Process one pragma.
+ * @param opts the current evaluator options
+ * @param key the key
+ * @param value the value
+ * @since 3.3
+ */
+ default void processPragma(JexlOptions opts, String key, Object value) {
+ processPragma(key, value);
+ }
}
/**
diff --git a/src/main/java/org/apache/commons/jexl3/JexlOptions.java b/src/main/java/org/apache/commons/jexl3/JexlOptions.java
index ab9e573..1a37824 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlOptions.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlOptions.java
@@ -157,7 +157,7 @@ public final class JexlOptions {
* Sets this option flags using the +/- syntax.
* @param opts the option flags
*/
- public void setFlags(final String[] opts) {
+ public void setFlags(final String... opts) {
flags = parseFlags(flags, opts);
}
@@ -417,4 +417,16 @@ public final class JexlOptions {
return new JexlOptions().set(this);
}
+ @Override public String toString() {
+ StringBuilder strb = new StringBuilder();
+ for(int i = 0; i < NAMES.length; ++i) {
+ if (i > 0) {
+ strb.append(' ');
+ }
+ strb.append((flags & (1 << i)) != 0? '+':'-');
+ strb.append(NAMES[i]);
+ }
+ return strb.toString();
+ }
+
}
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 18ae8e1..ced91c1 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Closure.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Closure.java
@@ -17,6 +17,7 @@
package org.apache.commons.jexl3.internal;
import org.apache.commons.jexl3.JexlContext;
+import org.apache.commons.jexl3.JexlOptions;
import org.apache.commons.jexl3.parser.ASTJexlLambda;
import java.util.Objects;
@@ -27,6 +28,8 @@ import java.util.Objects;
public class Closure extends Script {
/** The frame. */
protected final Frame frame;
+ /** The options. */
+ protected final JexlOptions options;
/**
* Creates a closure.
@@ -36,6 +39,8 @@ public class Closure extends Script {
protected Closure(final Interpreter theCaller, final ASTJexlLambda lambda) {
super(theCaller.jexl, null, lambda);
frame = lambda.createFrame(theCaller.frame);
+ JexlOptions callerOptions = theCaller.options;
+ options = callerOptions != null ? callerOptions.copy() : null;
}
/**
@@ -49,6 +54,11 @@ public class Closure extends Script {
frame = sf == null
? script.createFrame(args)
: sf.assign(args);
+ JexlOptions closureOptions = null;
+ if (base instanceof Closure) {
+ closureOptions = ((Closure) base).options;
+ }
+ options = closureOptions != null ? closureOptions.copy() : null;
}
@Override
@@ -123,14 +133,14 @@ public class Closure extends Script {
@Override
public Object execute(final JexlContext context, final Object... args) {
final Frame local = frame != null? frame.assign(args) : null;
- final Interpreter interpreter = createInterpreter(context, local);
+ final Interpreter interpreter = createInterpreter(context, local, options);
return interpreter.runClosure(this, null);
}
@Override
public Callable callable(final JexlContext context, final Object... args) {
final Frame local = frame != null? frame.assign(args) : null;
- return new Callable(createInterpreter(context, local)) {
+ return new Callable(createInterpreter(context, local, options)) {
@Override
public Object interpret() {
return interpreter.runClosure(Closure.this, null);
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Engine.java b/src/main/java/org/apache/commons/jexl3/internal/Engine.java
index 11d6338..c701b6f 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Engine.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Engine.java
@@ -449,7 +449,7 @@ public class Engine extends JexlEngine {
}
}
if (processor != null) {
- processor.processPragma(key, value);
+ processor.processPragma(opts, key, value);
}
}
if (ns != null) {
@@ -493,6 +493,13 @@ public class Engine extends JexlEngine {
return new Interpreter(this, opts, context, frame);
}
+ /**
+ * Creates a template interpreter.
+ * @param args the template interpreter arguments
+ */
+ protected Interpreter createTemplateInterpreter(TemplateInterpreter.Arguments args) {
+ return new TemplateInterpreter(args);
+ }
@Override
public Script createExpression(final JexlInfo info, final String expression) {
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 9ff211a..226040e 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
@@ -338,9 +338,6 @@ public class Interpreter extends InterpreterBase {
protected Object visit(final ASTBitwiseOrNode node, final Object data) {
final Object left = node.jjtGetChild(0).jjtAccept(this, data);
final Object right = node.jjtGetChild(1).jjtAccept(this, data);
- if (arithmetic.isStrict(JexlOperator.OR) && left == null || right == null) {
- // boum
- }
try {
final Object result = operators.tryOverload(node, JexlOperator.OR, left, right);
return result != JexlEngine.TRY_FAILED ? result : arithmetic.or(left, right);
@@ -1091,7 +1088,7 @@ public class Interpreter extends InterpreterBase {
accessJxlt.setExpression(expr);
}
if (expr != null) {
- final Object name = expr.evaluate(frame, context);
+ final Object name = expr.evaluate(context, frame, options);
if (name != null) {
final Integer id = ASTIdentifierAccess.parseIdentifier(name.toString());
return id != null ? id : name;
@@ -1178,12 +1175,9 @@ public class Interpreter extends InterpreterBase {
// *... and continue
if (!options.isAntish()) {
antish = false;
- continue;
}
+ continue;
// skip the first node case since it was trialed in jjtAccept above and returned null
- if (c == 0) {
- continue;
- }
}
// catch up to current node
for (; v <= c; ++v) {
@@ -1790,7 +1784,7 @@ public class Interpreter extends InterpreterBase {
node.jjtSetValue(tp);
}
if (tp != null) {
- return tp.evaluate(frame, context);
+ return tp.evaluate(context, frame, options);
}
return null;
}
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 8e0d1c6..f121858 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Script.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Script.java
@@ -107,8 +107,18 @@ public class Script implements JexlScript, JexlExpression {
* @return the interpreter
*/
protected Interpreter createInterpreter(final JexlContext context, final Frame frame) {
- final JexlOptions opts = jexl.evalOptions(script, context);
- return jexl.createInterpreter(context, frame, opts);
+ return createInterpreter(context, frame, null);
+ }
+
+ /**
+ * Creates this script interpreter.
+ * @param context the context
+ * @param frame the calling frame
+ * @param options the interpreter options
+ * @return the interpreter
+ */
+ protected Interpreter createInterpreter(final JexlContext context, final Frame frame, final JexlOptions options) {
+ return jexl.createInterpreter(context, frame, options != null? options : jexl.evalOptions(script, context));
}
/**
@@ -221,6 +231,14 @@ public class Script implements JexlScript, JexlExpression {
}
/**
+ * Gets this script captured variable, i.e. symbols captured from outer scopes.
+ * @return the captured variable names
+ */
+ public String[] getCapturedVariables() {
+ return script.getCapturedVariables();
+ }
+
+ /**
* @return the info
*/
public JexlInfo getInfo() {
diff --git a/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java b/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java
index 55c032e..3c9bf99 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java
@@ -296,7 +296,7 @@ public final class TemplateEngine extends JxltEngine {
@Override
public final TemplateExpression prepare(final JexlContext context) {
- return prepare(null, context);
+ return prepare(context, null, null);
}
/**
@@ -306,9 +306,10 @@ public final class TemplateEngine extends JxltEngine {
* @return the expression value
* @throws JexlException
*/
- protected final TemplateExpression prepare(final Frame frame, final JexlContext context) {
+ protected final TemplateExpression prepare(final JexlContext context, final Frame frame, final JexlOptions opts) {
try {
- final Interpreter interpreter = jexl.createInterpreter(context, frame, jexl.evalOptions(context));
+ final JexlOptions interOptions = opts != null? opts : jexl.evalOptions(context);
+ final Interpreter interpreter = jexl.createInterpreter(context, frame, interOptions);
return prepare(interpreter);
} catch (final JexlException xjexl) {
final JexlException xuel = createException(xjexl.getInfo(), "prepare", this, xjexl);
@@ -334,7 +335,7 @@ public final class TemplateEngine extends JxltEngine {
@Override
public final Object evaluate(final JexlContext context) {
- return evaluate(null, context);
+ return evaluate(context, null, null);
}
/**
@@ -353,15 +354,14 @@ public final class TemplateEngine extends JxltEngine {
* @return the expression value
* @throws JexlException
*/
- protected final Object evaluate(final Frame frame, final JexlContext context) {
+ protected final Object evaluate( final JexlContext context, final Frame frame, final JexlOptions options) {
try {
- final JexlOptions options = options(context);
final TemplateInterpreter.Arguments args = new TemplateInterpreter
.Arguments(jexl)
.context(context)
- .options(options)
+ .options(options != null? options : options(context))
.frame(frame);
- final Interpreter interpreter = new TemplateInterpreter(args);
+ final Interpreter interpreter = jexl.createTemplateInterpreter(args);
return evaluate(interpreter);
} catch (final JexlException xjexl) {
final JexlException xuel = createException(xjexl.getInfo(), "evaluate", this, xjexl);
diff --git a/src/main/java/org/apache/commons/jexl3/internal/TemplateInterpreter.java b/src/main/java/org/apache/commons/jexl3/internal/TemplateInterpreter.java
index f3dffd8..4c15a51 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/TemplateInterpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/TemplateInterpreter.java
@@ -148,7 +148,7 @@ public class TemplateInterpreter extends Interpreter {
}
TemplateEngine.TemplateExpression expr = exprs[e];
if (expr.isDeferred()) {
- expr = expr.prepare(frame, context);
+ expr = expr.prepare(context, frame, options);
}
if (expr instanceof TemplateEngine.CompositeExpression) {
printComposite((TemplateEngine.CompositeExpression) expr);
@@ -268,15 +268,14 @@ public class TemplateInterpreter extends Interpreter {
if (script instanceof ASTJexlLambda && !((ASTJexlLambda) script).isTopLevel()) {
return new Closure(this, (ASTJexlLambda) script) {
@Override
- protected Interpreter createInterpreter(final JexlContext context, final Frame local) {
- final JexlOptions opts = jexl.evalOptions(script, context);
+ protected Interpreter createInterpreter(final JexlContext context, final Frame local, final JexlOptions options) {
final TemplateInterpreter.Arguments targs = new TemplateInterpreter.Arguments(jexl)
.context(context)
- .options(opts)
+ .options(options)
.frame(local)
.expressions(exprs)
.writer(writer);
- return new TemplateInterpreter(targs);
+ return jexl.createTemplateInterpreter(targs);
}
};
}
diff --git a/src/main/java/org/apache/commons/jexl3/internal/TemplateScript.java b/src/main/java/org/apache/commons/jexl3/internal/TemplateScript.java
index 08d5d43..f71ca51 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/TemplateScript.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/TemplateScript.java
@@ -264,7 +264,7 @@ public final class TemplateScript implements JxltEngine.Template {
.context(context)
.options(options)
.frame(frame);
- final Interpreter interpreter = new TemplateInterpreter(targs);
+ final Interpreter interpreter = jexl.createTemplateInterpreter(targs);
final TemplateExpression[] immediates = new TemplateExpression[exprs.length];
for (int e = 0; e < exprs.length; ++e) {
try {
@@ -300,7 +300,7 @@ public final class TemplateScript implements JxltEngine.Template {
.frame(frame)
.expressions(exprs)
.writer(writer);
- final Interpreter interpreter = new TemplateInterpreter(targs);
+ final Interpreter interpreter = jexl.createTemplateInterpreter(targs);
interpreter.interpret(script);
}
diff --git a/src/test/java/org/apache/commons/jexl3/Issues300Test.java b/src/test/java/org/apache/commons/jexl3/Issues300Test.java
index b682f37..b6ce0b8 100644
--- a/src/test/java/org/apache/commons/jexl3/Issues300Test.java
+++ b/src/test/java/org/apache/commons/jexl3/Issues300Test.java
@@ -20,10 +20,14 @@ import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+
+import org.apache.commons.jexl3.internal.Engine32;
+import org.apache.commons.jexl3.internal.OptionsContext;
import org.junit.Assert;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
@@ -601,4 +605,141 @@ public class Issues300Test {
JexlExpression expr = jexl.createExpression(text);
JexlScript script = jexl.createScript(text);
}
+
+ static JexlContext pragmaticContext() {
+ final JexlOptions opts = new JexlOptions();
+ opts.setFlags( "-strict", "-cancellable", "-lexical", "-lexicalShade", "+safe", "+sharedInstance");
+ return new JexlTestCase.PragmaticContext(opts);
+ }
+
+ @Test public void testPropagateOptions() throws Exception {
+ final String src0 = "`${$options.strict?'+':'-'}strict"
+ + " ${$options.cancellable?'+':'-'}cancellable"
+ + " ${$options.lexical?'+':'-'}lexical"
+ + " ${$options.lexicalShade?'+':'-'}lexicalShade"
+ + " ${$options.sharedInstance?'+':'-'}sharedInstance"
+ + " ${$options.safe?'+':'-'}safe`";
+ String text = "#pragma script.mode pro50\n" +
+ "()->{ ()->{ "+src0+"; } }";
+ JexlEngine jexl = new JexlBuilder().safe(true).create();
+ JexlScript script = jexl.createScript(text);
+ JexlContext context = pragmaticContext();
+ JexlScript closure = (JexlScript) script.execute(context);
+ JexlContext opts = new OptionsContext();
+ Object result = closure.execute(opts);
+ Assert.assertEquals("+strict +cancellable +lexical +lexicalShade -sharedInstance -safe", result);
+
+ String text0 = "#pragma script.mode pro50\n" +
+ "()->{ "+src0+"; }";
+ JexlScript script0 = jexl.createScript(text0);
+ context = pragmaticContext();
+ Object result0 = script0.execute(context);
+ Assert.assertEquals("+strict +cancellable +lexical +lexicalShade -sharedInstance -safe", result0);
+
+ String text1 = "#pragma script.mode pro50\n"+src0;
+ JexlScript script1 = jexl.createScript(text1);
+ context = pragmaticContext();
+ Object result1 = script1.execute(context);
+ Assert.assertEquals("+strict +cancellable +lexical +lexicalShade -sharedInstance -safe", result1);
+
+ String text2 = src0;
+ JexlScript script2 = jexl.createScript(text2);
+ context = pragmaticContext();
+ Object result2 = script2.execute(context);
+ Assert.assertEquals("-strict -cancellable -lexical -lexicalShade +sharedInstance +safe", result2);
+ }
+
+ @Test
+ public void tes361a_32() throws Exception {
+ JexlEngine jexl = new Engine32(new JexlBuilder().safe(false));
+ Object result = run361a(jexl);
+ Assert.assertNotNull(result);
+ }
+
+ @Test
+ public void test361a_33() throws Exception {
+ JexlEngine jexl = new JexlBuilder().safe(false).strict(true).create();
+ try {
+ Object result = run361a(jexl);
+ Assert.fail("null arg should fail");
+ } catch(JexlException xany) {
+ Assert.assertNotNull(xany);
+ }
+ }
+
+ private Object run361a(JexlEngine jexl) throws Exception {
+ String src = "()-> { ()-> { if (versionFile != null) { return 'foo'; } else { return 'bar'; }} }";
+ JexlScript script = jexl.createScript(src);
+ Object result = script.execute(null);
+ JexlScript rs = (JexlScript) result;
+ return rs.execute(null);
+ }
+
+ @Test
+ public void test361b_33() throws Exception {
+ JexlEngine jexl = new JexlBuilder().safe(false).strict(true).create();
+ try {
+ Object result = run361b(jexl);
+ Assert.fail("null arg should fail");
+ } catch(JexlException xany) {
+ Assert.assertNotNull(xany);
+ }
+ }
+
+ @Test
+ public void test361b_32() {
+ JexlEngine jexl = new Engine32(new JexlBuilder().safe(false));
+ Object result = run361b(jexl);
+ Assert.assertNotNull(result);
+ }
+
+ private Object run361b(JexlEngine jexl) {
+ String src = "()-> { ()-> {" +
+ "var voa = vaf.value;\n" +
+ "if (voa != NaN && voa <= 0)" +
+ "{ return 'foo'; } else { return 'bar'; }" +
+ "} }";
+ MapContext context = new MapContext();
+ Map<String,Object> vaf = Collections.singletonMap("value", null);
+ context.set("vaf", vaf);
+ JexlScript script = jexl.createScript(src);
+ Object result = script.execute(null);
+ JexlScript rs = (JexlScript) result;
+ return rs.execute(context);
+ }
+
+ @Test
+ public void test361_33() {
+ JexlEngine jexl = new JexlBuilder().safe(false).strict(true).create();
+ try {
+ run361c(jexl);
+ Assert.fail("null arg should fail");
+ } catch(JexlException xany) {
+ Assert.assertNotNull(xany);
+ }
+ }
+
+ @Test
+ public void test361c_32() {
+ JexlEngine jexl = new Engine32(new JexlBuilder().safe(false));
+ String result = run361c(jexl);
+ Assert.assertNotNull(result);
+ }
+
+ private String run361c(JexlEngine jexl) {
+ String src = "$$var t = null;\n" +
+ "$$if (t < 0) {\n" +
+ "'foo'\n" +
+ "$$} else {\n" +
+ "'bar'\n" +
+ "$$}";
+ JxltEngine jxlt = jexl.createJxltEngine();
+ MapContext context = new MapContext();
+ Map<String,Object> vaf = Collections.singletonMap("value", null);
+ context.set("vaf", vaf);
+ JxltEngine.Template template = jxlt.createTemplate(src);
+ StringWriter strw = new StringWriter();
+ template.evaluate(context, strw);
+ return strw.toString();
+ }
}
diff --git a/src/test/java/org/apache/commons/jexl3/JXLTTest.java b/src/test/java/org/apache/commons/jexl3/JXLTTest.java
index 7ecca69..6a65ba6 100644
--- a/src/test/java/org/apache/commons/jexl3/JXLTTest.java
+++ b/src/test/java/org/apache/commons/jexl3/JXLTTest.java
@@ -69,11 +69,10 @@ public class JXLTTest extends JexlTestCase {
public static List<JexlBuilder> engines() {
final JexlFeatures f = new JexlFeatures();
f.lexical(true).lexicalShade(true);
- return Arrays.<JexlBuilder>asList(new JexlBuilder().silent(false)
- .lexical(true).lexicalShade(true)
- .cache(128).strict(true), new JexlBuilder().features(f).silent(false)
- .cache(128).strict(true), new JexlBuilder().silent(false)
- .cache(128).strict(true));
+ return Arrays.<JexlBuilder>asList(
+ new JexlBuilder().silent(false).lexical(true).lexicalShade(true).cache(128).strict(true),
+ new JexlBuilder().features(f).silent(false).cache(128).strict(true),
+ new JexlBuilder().silent(false).cache(128).strict(true));
}
@Before
@@ -1021,40 +1020,6 @@ public class JXLTTest extends JexlTestCase {
Assert.assertEquals(s315, output);
}
- // define mode pro50
- static final JexlOptions MODE_PRO50 = new JexlOptions();
- static {
- MODE_PRO50.setFlags( "+strict +cancellable +lexical +lexicalShade -safe".split(" "));
- }
-
- public static class PragmaticContext extends MapContext implements JexlContext.PragmaProcessor, JexlContext.OptionsHandle {
- private final JexlOptions options;
-
- public PragmaticContext(final JexlOptions o) {
- this.options = o;
- }
-
- @Override
- public void processPragma(final String key, final Object value) {
- if ("script.mode".equals(key) && "pro50".equals(value)) {
- options.set(MODE_PRO50);
- }
- }
-
- @Override
- public Object get(final String name) {
- if ("$options".equals(name)) {
- return options;
- }
- return super.get(name);
- }
-
- @Override
- public JexlOptions getEngineOptions() {
- return options;
- }
- }
-
@Test
public void testLexicalTemplate() throws Exception {
final JexlOptions opts = new JexlOptions();
@@ -1074,7 +1039,12 @@ public class JXLTTest extends JexlTestCase {
final Writer strw0 = new StringWriter();
tmplt0.evaluate(ctxt, strw0);
final String output0 = strw0.toString();
- Assert.assertEquals( "-strict -cancellable -lexical -lexicalShade +safe", output0);
+ JexlFeatures features = BUILDER.features();
+ if (features != null && features.isLexical() && features.isLexicalShade()) {
+ Assert.assertEquals("-strict -cancellable +lexical +lexicalShade +safe", output0);
+ } else {
+ Assert.assertEquals("-strict -cancellable -lexical -lexicalShade +safe", output0);
+ }
final String src = "$$ #pragma script.mode pro50\n" + src0;
diff --git a/src/test/java/org/apache/commons/jexl3/JexlTestCase.java b/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
index 56d031c..8a04c97 100644
--- a/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
+++ b/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
@@ -21,6 +21,8 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
+import org.apache.commons.jexl3.internal.Interpreter;
+import org.apache.commons.jexl3.internal.OptionsContext;
import org.apache.commons.jexl3.internal.Util;
import org.apache.commons.jexl3.internal.introspection.Permissions;
import org.apache.commons.jexl3.internal.introspection.Uberspect;
@@ -69,6 +71,43 @@ public class JexlTestCase {
return new JexlBuilder().create();
}
+ // define mode pro50
+ static final JexlOptions MODE_PRO50 = new JexlOptions();
+ static {
+ MODE_PRO50.setFlags( "+strict +cancellable +lexical +lexicalShade -safe".split(" "));
+ }
+
+ public static class PragmaticContext extends OptionsContext implements JexlContext.PragmaProcessor, JexlContext.OptionsHandle {
+ private final JexlOptions options;
+
+ public PragmaticContext() {
+ this(new JexlOptions());
+ }
+
+ public PragmaticContext(final JexlOptions o) {
+ super();
+ this.options = o;
+ }
+
+ @Override
+ public void processPragma(String key, Object value) {
+ processPragma(null, key, value);
+ }
+
+ @Override
+ public void processPragma(JexlOptions opts, final String key, final Object value) {
+ if ("script.mode".equals(key) && "pro50".equals(value)) {
+ opts.set(MODE_PRO50);
+ }
+ }
+
+ @Override
+ public JexlOptions getEngineOptions() {
+ return options;
+ }
+ }
+
+
/**
* A very secure singleton.
*/
diff --git a/src/test/java/org/apache/commons/jexl3/LambdaTest.java b/src/test/java/org/apache/commons/jexl3/LambdaTest.java
index 328e89b..34bed32 100644
--- a/src/test/java/org/apache/commons/jexl3/LambdaTest.java
+++ b/src/test/java/org/apache/commons/jexl3/LambdaTest.java
@@ -16,16 +16,19 @@
*/
package org.apache.commons.jexl3;
+import org.apache.commons.jexl3.internal.Script;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
-import org.junit.Assert;
-import org.junit.Test;
/**
* Tests function/lambda/closure features.
*/
-@SuppressWarnings({"UnnecessaryBoxing", "AssertEqualsBetweenInconvertibleTypes"})
+@SuppressWarnings({"AssertEqualsBetweenInconvertibleTypes"})
public class LambdaTest extends JexlTestCase {
public LambdaTest() {
@@ -33,7 +36,7 @@ public class LambdaTest extends JexlTestCase {
}
@Test
- public void testScriptArguments() throws Exception {
+ public void testScriptArguments() {
final JexlEngine jexl = createEngine();
final JexlScript s = jexl.createScript(" x + x ", "x");
final JexlScript s42 = jexl.createScript("s(21)", "s");
@@ -42,14 +45,14 @@ public class LambdaTest extends JexlTestCase {
}
@Test
- public void testScriptContext() throws Exception {
+ public void testScriptContext() {
final JexlEngine jexl = createEngine();
final JexlScript s = jexl.createScript("function(x) { x + x }");
final String fsstr = s.getParsedText(0);
Assert.assertEquals("(x)->{ x + x; }", fsstr);
Assert.assertEquals(42, s.execute(null, 21));
JexlScript s42 = jexl.createScript("s(21)");
- final JexlEvalContext ctxt = new JexlEvalContext();
+ final JexlContext ctxt = new JexlEvalContext();
ctxt.set("s", s);
Object result = s42.execute(ctxt);
Assert.assertEquals(42, result);
@@ -61,7 +64,7 @@ public class LambdaTest extends JexlTestCase {
}
@Test
- public void testLambda() throws Exception {
+ public void testLambda() {
final JexlEngine jexl = createEngine();
String strs = "var s = function(x) { x + x }; s(21)";
JexlScript s42 = jexl.createScript(strs);
@@ -74,7 +77,7 @@ public class LambdaTest extends JexlTestCase {
}
@Test
- public void testLambdaClosure() throws Exception {
+ public void testLambdaClosure() {
final JexlEngine jexl = createEngine();
String strs = "var t = 20; var s = function(x, y) { x + y + t}; s(15, 7)";
JexlScript s42 = jexl.createScript(strs);
@@ -95,7 +98,7 @@ public class LambdaTest extends JexlTestCase {
}
@Test
- public void testLambdaLambda() throws Exception {
+ public void testLambdaLambda() {
final JexlEngine jexl = createEngine();
String strs = "var t = 19; ( (x, y)->{ var t = 20; x + y + t} )(15, 7);";
JexlScript s42 = jexl.createScript(strs);
@@ -114,7 +117,7 @@ public class LambdaTest extends JexlTestCase {
}
@Test
- public void testNestLambda() throws Exception {
+ public void testNestLambda() {
final JexlEngine jexl = createEngine();
final String strs = "( (x)->{ (y)->{ x + y } })(15)(27)";
final JexlScript s42 = jexl.createScript(strs);
@@ -125,28 +128,27 @@ public class LambdaTest extends JexlTestCase {
@Test
public void testNestLambada() throws Exception {
final JexlEngine jexl = createEngine();
- final JexlContext ctx = null;
final String strs = "(x)->{ (y)->{ x + y } }";
final JexlScript s42 = jexl.createScript(strs);
final JexlScript s42b = jexl.createScript(s42.toString());
Assert.assertEquals(s42.hashCode(), s42b.hashCode());
Assert.assertEquals(s42, s42b);
- Object result = s42.execute(ctx, 15);
+ Object result = s42.execute(null, 15);
Assert.assertTrue(result instanceof JexlScript);
- final Object resultb = s42.execute(ctx, 15);
+ final Object resultb = s42.execute(null, 15);
Assert.assertEquals(result.hashCode(), resultb.hashCode());
Assert.assertEquals(result, resultb);
- Assert.assertEquals(result, jexl.createScript(resultb.toString(), "x").execute(ctx, 15));
+ Assert.assertEquals(result, jexl.createScript(resultb.toString(), "x").execute(null, 15));
final JexlScript s15 = (JexlScript) result;
- final Callable<Object> s15b = s15.callable(ctx, 27);
- result = s15.execute(ctx, 27);
+ final Callable<Object> s15b = s15.callable(null, 27);
+ result = s15.execute(null, 27);
Assert.assertEquals(42, result);
result = s15b.call();
Assert.assertEquals(42, result);
}
@Test
- public void testHoistLambda() throws Exception {
+ public void testHoistLambda() {
final JexlEngine jexl = createEngine();
final JexlEvalContext ctx = new JexlEvalContext();
ctx.getEngineOptions().setLexical(false);
@@ -187,55 +189,57 @@ public class LambdaTest extends JexlTestCase {
}
@Test
- public void testRecurse() throws Exception {
+ public void testRecurse() {
+ final JexlEngine jexl = createEngine();
+ final JexlContext jc = new MapContext();
+ final JexlScript script = jexl.createScript("var fact = (x)->{ if (x <= 1) 1; else x * fact(x - 1) }; fact(5)");
+ final int result = (Integer) script.execute(jc);
+ Assert.assertEquals(120, result);
+ }
+
+ @Test
+ public void testRecurse2() {
final JexlEngine jexl = createEngine();
final JexlContext jc = new MapContext();
- try {
- final JexlScript script = jexl.createScript("var fact = (x)->{ if (x <= 1) 1; else x * fact(x - 1) }; fact(5)");
- final int result = (Integer) script.execute(jc);
- Assert.assertEquals(120, result);
- } catch (final JexlException xany) {
- final String msg = xany.toString();
- throw xany;
- }
+ // adding some captured vars to get it confused
+ final JexlScript script = jexl.createScript(
+ "var y = 1; var z = 1; "
+ +"var fact = (x)->{ if (x <= y) z; else x * fact(x - 1) }; fact(6)");
+ final int result = (Integer) script.execute(jc);
+ Assert.assertEquals(720, result);
}
@Test
- public void testRecurse2() throws Exception {
+ public void testRecurse2b() {
final JexlEngine jexl = createEngine();
final JexlContext jc = new MapContext();
// adding some captured vars to get it confused
- try {
- final JexlScript script = jexl.createScript(
- "var y = 1; var z = 1; "
- +"var fact = (x)->{ if (x <= y) z; else x * fact(x - 1) }; fact(6)");
- final int result = (Integer) script.execute(jc);
- Assert.assertEquals(720, result);
- } catch (final JexlException xany) {
- final String msg = xany.toString();
- throw xany;
- }
+ final JexlScript fact = jexl.createScript(
+ "var y = 1; var z = 1; "
+ +"var fact = (x)->{ if (x <= y) z; else x * fact(x - 1) };" +
+ "fact");
+ Script func = (Script) fact.execute(jc);
+ String[] captured = func.getCapturedVariables();
+ Assert.assertEquals(3, captured.length);
+ Assert.assertTrue(Arrays.asList(captured).containsAll(Arrays.asList("z", "y", "fact")));
+ final int result = (Integer) func.execute(jc, 6);
+ Assert.assertEquals(720, result);
}
@Test
- public void testRecurse3() throws Exception {
+ public void testRecurse3() {
final JexlEngine jexl = createEngine();
final JexlContext jc = new MapContext();
// adding some captured vars to get it confused
- try {
- final JexlScript script = jexl.createScript(
- "var y = 1; var z = 1;var foo = (x)->{y + z}; "
- +"var fact = (x)->{ if (x <= y) z; else x * fact(x - 1) }; fact(6)");
- final int result = (Integer) script.execute(jc);
- Assert.assertEquals(720, result);
- } catch (final JexlException xany) {
- final String msg = xany.toString();
- throw xany;
- }
+ final JexlScript script = jexl.createScript(
+ "var y = 1; var z = 1;var foo = (x)->{y + z}; "
+ +"var fact = (x)->{ if (x <= y) z; else x * fact(x - 1) }; fact(6)");
+ final int result = (Integer) script.execute(jc);
+ Assert.assertEquals(720, result);
}
@Test
- public void testIdentity() throws Exception {
+ public void testIdentity() {
final JexlEngine jexl = createEngine();
JexlScript script;
Object result;
@@ -247,7 +251,7 @@ public class LambdaTest extends JexlTestCase {
}
@Test
- public void testCurry1() throws Exception {
+ public void testCurry1() {
final JexlEngine jexl = createEngine();
JexlScript script;
Object result;
@@ -270,7 +274,7 @@ public class LambdaTest extends JexlTestCase {
}
@Test
- public void testCurry2() throws Exception {
+ public void testCurry2() {
final JexlEngine jexl = createEngine();
JexlScript script;
Object result;
@@ -286,7 +290,7 @@ public class LambdaTest extends JexlTestCase {
}
@Test
- public void testCurry3() throws Exception {
+ public void testCurry3() {
final JexlEngine jexl = createEngine();
JexlScript script;
Object result;
@@ -298,7 +302,7 @@ public class LambdaTest extends JexlTestCase {
}
@Test
- public void testCurry4() throws Exception {
+ public void testCurry4() {
final JexlEngine jexl = createEngine();
JexlScript script;
Object result;
@@ -310,7 +314,7 @@ public class LambdaTest extends JexlTestCase {
}
@Test
- public void testCurry5() throws Exception {
+ public void testCurry5() {
final JexlEngine jexl = createEngine();
JexlScript script;
Object result;
@@ -322,14 +326,14 @@ public class LambdaTest extends JexlTestCase {
}
@Test
- public void test270() throws Exception {
+ public void test270() {
final JexlEngine jexl = createEngine();
final JexlScript base = jexl.createScript("(x, y, z)->{ x + y + z }");
final String text = base.toString();
JexlScript script = base.curry(5, 15);
Assert.assertEquals(text, script.toString());
- final JexlEvalContext ctxt = new JexlEvalContext();
+ final JexlContext ctxt = new JexlEvalContext();
ctxt.set("s", base);
script = jexl.createScript("return s");
Object result = script.execute(ctxt);
@@ -341,7 +345,7 @@ public class LambdaTest extends JexlTestCase {
}
@Test
- public void test271a() throws Exception {
+ public void test271a() {
final JexlEngine jexl = createEngine();
final JexlScript base = jexl.createScript("var base = 1; var x = (a)->{ var y = (b) -> {base + b}; return base + y(a)}; x(40)");
final Object result = base.execute(null);
@@ -349,7 +353,7 @@ public class LambdaTest extends JexlTestCase {
}
@Test
- public void test271b() throws Exception {
+ public void test271b() {
final JexlEngine jexl = createEngine();
final JexlScript base = jexl.createScript("var base = 2; var sum = (x, y, z)->{ base + x + y + z }; var y = sum.curry(1); y(2,3)");
final Object result = base.execute(null);
@@ -357,7 +361,7 @@ public class LambdaTest extends JexlTestCase {
}
@Test
- public void test271c() throws Exception {
+ public void test271c() {
final JexlEngine jexl = createEngine();
final JexlScript base = jexl.createScript("(x, y, z)->{ 2 + x + y + z };");
final JexlScript y = base.curry(1);
@@ -366,7 +370,7 @@ public class LambdaTest extends JexlTestCase {
}
@Test
- public void test271d() throws Exception {
+ public void test271d() {
final JexlEngine jexl = createEngine();
final JexlScript base = jexl.createScript("var base = 2; return (x, y, z)->{ base + x + y + z };");
final JexlScript y = ((JexlScript) base.execute(null)).curry(1);
@@ -376,11 +380,11 @@ public class LambdaTest extends JexlTestCase {
// redefining an captured var is not resolved correctly in left hand side;
// declare the var in local frame, resolved in local frame instead of parent
-// @Test
-// public void test271e() throws Exception {
-// JexlEngine jexl = createEngine();
-// JexlScript base = jexl.createScript("var base = 1000; var f = (x, y)->{ var base = x + y + (base?:-1000); base; }; f(100, 20)");
-// Object result = base.execute(null);
-// Assert.assertEquals(-880, result);
-// }
+ @Test
+ public void test271e() {
+ JexlEngine jexl = createEngine();
+ JexlScript base = jexl.createScript("var base = 1000; var f = (x, y)->{ var base = x + y + (base?:-1000); base; }; f(100, 20)");
+ Object result = base.execute(null);
+ Assert.assertEquals(1120, result);
+ }
}
diff --git a/src/test/java/org/apache/commons/jexl3/internal/OptionsContext.java b/src/test/java/org/apache/commons/jexl3/internal/OptionsContext.java
new file mode 100644
index 0000000..ee24979
--- /dev/null
+++ b/src/test/java/org/apache/commons/jexl3/internal/OptionsContext.java
@@ -0,0 +1,38 @@
+/*
+ * 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 org.apache.commons.jexl3.internal;
+
+import org.apache.commons.jexl3.MapContext;
+
+/**
+ * A map context that has access to the interpreter evaluation options.
+ */
+public class OptionsContext extends MapContext {
+ @Override
+ public Object get(final String name) {
+ if ("$options".equals(name)) {
+ Interpreter pinter = Interpreter.INTER.get();
+ return pinter.options;
+ }
+ return super.get(name);
+ }
+
+ @Override
+ public boolean has(final String name) {
+ return "$options".equals(name) || super.has(name);
+ }
+}