Skip to content
Permalink
Browse files
JEXL-364: let evaluation options flow through closures
  • Loading branch information
henrib committed Mar 14, 2022
1 parent d1c0a05 commit 4b3d1ca57d64655fa6b669d7a9cbe87f494352ee
Show file tree
Hide file tree
Showing 14 changed files with 389 additions and 137 deletions.
@@ -172,10 +172,30 @@ interface OptionsHandle {
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>
* &#64;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);
}
}

/**
@@ -157,7 +157,7 @@ public static int parseFlags(int initial, final String...flags) {
* 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 JexlOptions copy() {
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();
}

}
@@ -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 @@
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 @@ protected Closure(final Script base, final Object[] args) {
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 Object execute(final JexlContext context) {
@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);
@@ -449,7 +449,7 @@ protected void processPragmas(final ASTJexlScript script, final JexlContext cont
}
}
if (processor != null) {
processor.processPragma(key, value);
processor.processPragma(opts, key, value);
}
}
if (ns != null) {
@@ -493,6 +493,13 @@ protected Interpreter createInterpreter(final JexlContext context, final Frame f
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) {
@@ -338,9 +338,6 @@ protected Object visit(final ASTBitwiseAndNode node, final Object data) {
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 @@ private Object evalIdentifier(final ASTIdentifierAccess node) {
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 @@ protected Object visit(final ASTReference node, final Object data) {
// *... 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 @@ protected Object visit(final ASTJxltLiteral node, final Object data) {
node.jjtSetValue(tp);
}
if (tp != null) {
return tp.evaluate(frame, context);
return tp.evaluate(context, frame, options);
}
return null;
}
@@ -107,8 +107,18 @@ protected Frame createFrame(final Object[] args) {
* @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));
}

/**
@@ -220,6 +230,14 @@ public String[] getLocalVariables() {
return script.getLocalVariables();
}

/**
* 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
*/
@@ -296,7 +296,7 @@ protected void getVariables(final Engine.VarCollector collector) {

@Override
public final TemplateExpression prepare(final JexlContext context) {
return prepare(null, context);
return prepare(context, null, null);
}

/**
@@ -306,9 +306,10 @@ public final TemplateExpression prepare(final JexlContext context) {
* @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 @@ protected TemplateExpression prepare(final Interpreter interpreter) {

@Override
public final Object evaluate(final JexlContext context) {
return evaluate(null, context);
return evaluate(context, null, null);
}

/**
@@ -353,15 +354,14 @@ protected JexlOptions options(final JexlContext context) {
* @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);
@@ -148,7 +148,7 @@ public void print(final int e) {
}
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 @@ protected Object visit(final ASTJexlScript script, final Object data) {
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);
}
};
}
@@ -264,7 +264,7 @@ public TemplateScript prepare(final JexlContext context) {
.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 void evaluate(final JexlContext context, final Writer writer, final Objec
.frame(frame)
.expressions(exprs)
.writer(writer);
final Interpreter interpreter = new TemplateInterpreter(targs);
final Interpreter interpreter = jexl.createTemplateInterpreter(targs);
interpreter.interpret(script);
}

0 comments on commit 4b3d1ca

Please sign in to comment.