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 4ddf1831 JEXL-369: move const/defined logic to lexical scope; - restore explicit lexical shade through features; - detect non-initialized const variables 4ddf1831 is described below commit 4ddf18313cadee541132d19beac62fcaa02f7321 Author: henrib <hen...@apache.org> AuthorDate: Mon May 16 18:58:33 2022 +0200 JEXL-369: move const/defined logic to lexical scope; - restore explicit lexical shade through features; - detect non-initialized const variables --- .../commons/jexl3/internal/LexicalFrame.java | 2 +- .../commons/jexl3/internal/LexicalScope.java | 143 ++++++++++++++++----- .../org/apache/commons/jexl3/internal/Scope.java | 25 ---- .../apache/commons/jexl3/parser/ASTIdentifier.java | 10 ++ .../commons/jexl3/parser/JexlLexicalNode.java | 40 +++++- .../apache/commons/jexl3/parser/JexlParser.java | 55 +++++--- .../org/apache/commons/jexl3/parser/Parser.jjt | 13 +- .../java/org/apache/commons/jexl3/LambdaTest.java | 25 ++++ .../java/org/apache/commons/jexl3/LexicalTest.java | 20 ++- 9 files changed, 242 insertions(+), 91 deletions(-) diff --git a/src/main/java/org/apache/commons/jexl3/internal/LexicalFrame.java b/src/main/java/org/apache/commons/jexl3/internal/LexicalFrame.java index 6bf8cbdd..59fd6315 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/LexicalFrame.java +++ b/src/main/java/org/apache/commons/jexl3/internal/LexicalFrame.java @@ -54,7 +54,7 @@ public class LexicalFrame extends LexicalScope { * @param src the frame to copy */ public LexicalFrame(final LexicalFrame src) { - super(src.symbols, src.moreSymbols); + super(src); frame = src.frame; previous = src.previous; stack = src.stack != null ? new ArrayDeque<>(src.stack) : null; diff --git a/src/main/java/org/apache/commons/jexl3/internal/LexicalScope.java b/src/main/java/org/apache/commons/jexl3/internal/LexicalScope.java index 4059e55e..5551551a 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/LexicalScope.java +++ b/src/main/java/org/apache/commons/jexl3/internal/LexicalScope.java @@ -20,7 +20,10 @@ import java.util.BitSet; /** * The set of symbols declared in a lexical scope. - * <p>The symbol identifiers are determined by the functional scope. + * <p>The symbol identifiers are determined by the functional scope.</p> + * <p>We use 3 bits per symbol; bit 0 sets the actual symbol as lexical (let), + * bit 1 as a const, bit 2 as a defined (valued) const. + * There are actually only 4 used states: 0, 1, 3, 7</p> */ public class LexicalScope { /** @@ -28,7 +31,16 @@ public class LexicalScope { */ protected static final int LONGBITS = 64; /** - * The mask of symbols in the frame. + * Bits per symbol. + * Declared, const, defined. + */ + protected static final int BITS_PER_SYMBOL = 3; + /** + * Number of symbols. + */ + protected int count = 0; + /** + * The mask of symbols in the scope. */ protected long symbols = 0L; /** @@ -36,6 +48,7 @@ public class LexicalScope { */ protected BitSet moreSymbols = null; + /** * Create a scope. */ @@ -44,21 +57,20 @@ public class LexicalScope { /** * Frame copy ctor base. - * - * @param s the symbols mask - * @param ms the more symbols bitset */ - protected LexicalScope(final long s, final BitSet ms) { - symbols = s; + protected LexicalScope(LexicalScope other) { + BitSet ms; + symbols = other.symbols; + ms = other.moreSymbols; moreSymbols = ms != null ? (BitSet) ms.clone() : null; } /** - * Ensure more symbpls can be stored. + * Ensures more symbols can be stored. * * @return the set of more symbols */ - protected final BitSet moreSymbols() { + private BitSet moreSymbols() { if (moreSymbols == null) { moreSymbols = new BitSet(); } @@ -66,32 +78,30 @@ public class LexicalScope { } /** - * Checks whether a symbol has already been declared. - * - * @param symbol the symbol - * @return true if declared, false otherwise + * Whether a given bit (not symbol) is set. + * @param bit the bit + * @return true if set */ - public boolean hasSymbol(final int symbol) { - if (symbol < LONGBITS) { - return (symbols & (1L << symbol)) != 0L; + private boolean isSet(final int bit) { + if (bit < LONGBITS) { + return (symbols & (1L << bit)) != 0L; } - return moreSymbols != null && moreSymbols.get(symbol - LONGBITS); + return moreSymbols != null && moreSymbols.get(bit - LONGBITS); } /** - * Adds a symbol in this scope. - * - * @param symbol the symbol - * @return true if registered, false if symbol was already registered + * Sets a given bit (not symbol). + * @param bit the bit + * @return true if it was actually set, false if it was set before */ - public boolean addSymbol(final int symbol) { - if (symbol < LONGBITS) { - if ((symbols & (1L << symbol)) != 0L) { + private boolean set(final int bit) { + if (bit < LONGBITS) { + if ((symbols & (1L << bit)) != 0L) { return false; } - symbols |= (1L << symbol); + symbols |= (1L << bit); } else { - final int s = symbol - LONGBITS; + final int s = bit - LONGBITS; final BitSet ms = moreSymbols(); if (ms.get(s)) { return false; @@ -101,6 +111,77 @@ public class LexicalScope { return true; } + /** + * Checks whether a symbol has already been declared. + * + * @param symbol the symbol + * @return true if declared, false otherwise + */ + public boolean hasSymbol(final int symbol) { + final int bit = symbol << BITS_PER_SYMBOL; + return isSet(bit); + } + + /** + * Checks whether a symbol is declared as a constant. + * + * @param symbol the symbol + * @return true if declared as constant, false otherwise + */ + public boolean isConstant(final int symbol) { + final int bit = (symbol << BITS_PER_SYMBOL) | 1; + return isSet(bit); + } + + /** + * Checks whether a const symbol has been defined, ie has a value. + * + * @param symbol the symbol + * @return true if defined, false otherwise + */ + public boolean isDefined(final int symbol) { + final int bit = (symbol << BITS_PER_SYMBOL) | 2; + return isSet(bit); + + } + + /** + * Adds a symbol in this scope. + * + * @param symbol the symbol + * @return true if registered, false if symbol was already registered + */ + public boolean addSymbol(final int symbol) { + final int bit = (symbol << BITS_PER_SYMBOL) ; + if (set(bit)) { + count += 1; + return true; + } + return false; + } + + /** + * Adds a constant in this scope. + * + * @param symbol the symbol + * @return true if registered, false if symbol was already registered + */ + public boolean addConstant(final int symbol) { + final int bit = (symbol << BITS_PER_SYMBOL) | 1; + return set(bit); + } + + /** + * Defines a constant in this scope. + * + * @param symbol the symbol + * @return true if registered, false if symbol was already registered + */ + public boolean defineSymbol(final int symbol) { + final int bit = (symbol << BITS_PER_SYMBOL) | 2; + return set(bit); + } + /** * Clear all symbols. * @@ -112,14 +193,16 @@ public class LexicalScope { long clean = symbols; while (clean != 0L) { final int s = Long.numberOfTrailingZeros(clean); - clean &= ~(1L << s); - cleanSymbol.accept(s); + // call clean for symbol definition (7 as a mask for 3 bits,1+2+4) + clean &= ~(7L << s); + cleanSymbol.accept(s >> BITS_PER_SYMBOL); } } symbols = 0L; if (moreSymbols != null) { if (cleanSymbol != null) { - for (int s = moreSymbols.nextSetBit(0); s != -1; s = moreSymbols.nextSetBit(s + 1)) { + // step over const and definition (3 bits per symbol) + for (int s = moreSymbols.nextSetBit(0); s != -1; s = moreSymbols.nextSetBit(s + BITS_PER_SYMBOL)) { cleanSymbol.accept(s + LONGBITS); } } @@ -131,6 +214,6 @@ public class LexicalScope { * @return the number of symbols defined in this scope. */ public int getSymbolCount() { - return Long.bitCount(symbols) + (moreSymbols == null ? 0 : moreSymbols.cardinality()); + return count; } } 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 77b045fc..ee14bbd8 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/Scope.java +++ b/src/main/java/org/apache/commons/jexl3/internal/Scope.java @@ -66,10 +66,6 @@ public final class Scope { * The map of local captured variables to parent scope variables, ie closure. */ private Map<Integer, Integer> capturedVariables = null; - /** - * Const symbols. - */ - private LexicalScope constVariables = null; /** * Let symbols. */ @@ -178,27 +174,6 @@ public final class Scope { return lexicalVariables != null && s >= 0 && lexicalVariables.hasSymbol(s); } - /** - * Marks a symbol as a const. - * @param s the symbol - * @return true if the symbol was not already present in the constant set - */ - public boolean addConstant(int s) { - if (constVariables == null) { - constVariables = new LexicalScope(); - } - return constVariables.addSymbol(s); - } - - /** - * Checks whether a symbol is declared through a const. - * @param s the symbol - * @return true if symbol was declared through const - */ - public boolean isConstant(int s) { - return constVariables != null && s >= 0 && constVariables.hasSymbol(s); - } - /** * Checks whether a given symbol is captured. * @param symbol the symbol number diff --git a/src/main/java/org/apache/commons/jexl3/parser/ASTIdentifier.java b/src/main/java/org/apache/commons/jexl3/parser/ASTIdentifier.java index 944b5915..88748750 100644 --- a/src/main/java/org/apache/commons/jexl3/parser/ASTIdentifier.java +++ b/src/main/java/org/apache/commons/jexl3/parser/ASTIdentifier.java @@ -34,6 +34,8 @@ public class ASTIdentifier extends JexlNode { private static final int LEXICAL = 3; /** The const variable flag. */ private static final int CONST = 4; + /** The defined variable flag. */ + private static final int DEFINED = 5; ASTIdentifier(final int id) { super(id); @@ -125,6 +127,14 @@ public class ASTIdentifier extends JexlNode { flags = set(CONST, flags, f); } + public boolean isDefined() { + return isSet(DEFINED, flags); + } + + public void setDefined(final boolean f) { + flags = set(DEFINED, flags, f); + } + public String getName() { return name; } diff --git a/src/main/java/org/apache/commons/jexl3/parser/JexlLexicalNode.java b/src/main/java/org/apache/commons/jexl3/parser/JexlLexicalNode.java index 8d1a5f17..475f11cc 100644 --- a/src/main/java/org/apache/commons/jexl3/parser/JexlLexicalNode.java +++ b/src/main/java/org/apache/commons/jexl3/parser/JexlLexicalNode.java @@ -23,7 +23,7 @@ import org.apache.commons.jexl3.internal.LexicalScope; * @since 3.2 */ public class JexlLexicalNode extends JexlNode implements JexlParser.LexicalUnit { - private LexicalScope locals = null; + private LexicalScope lexicalScope = null; public JexlLexicalNode(final int id) { super(id); @@ -35,24 +35,50 @@ public class JexlLexicalNode extends JexlNode implements JexlParser.LexicalUnit @Override public boolean declareSymbol(final int symbol) { - if (locals == null) { - locals = new LexicalScope(); + if (lexicalScope == null) { + lexicalScope = new LexicalScope(); } - return locals.addSymbol(symbol); + return lexicalScope.addSymbol(symbol); + } + + @Override + public boolean isConstant(final int symbol) { + return lexicalScope != null && lexicalScope.isConstant(symbol); + } + + @Override + public boolean isDefined(final int symbol) { + return lexicalScope != null && lexicalScope.isDefined(symbol); + } + + @Override + public void setConstant(int symbol) { + lexicalScope.addConstant(symbol); + } + + @Override + public void setDefined(int symbol) { + lexicalScope.defineSymbol(symbol); } @Override public int getSymbolCount() { - return locals == null? 0 : locals.getSymbolCount(); + return lexicalScope == null? 0 : lexicalScope.getSymbolCount(); } @Override public boolean hasSymbol(final int symbol) { - return locals != null && locals.hasSymbol(symbol); + return lexicalScope != null && lexicalScope.hasSymbol(symbol); } @Override public LexicalScope getLexicalScope() { - return locals; + return lexicalScope; + } + + + @Override + public void jjtClose() { + } } diff --git a/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java b/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java index 615de42b..7f971216 100644 --- a/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java +++ b/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java @@ -26,7 +26,6 @@ import org.apache.commons.jexl3.internal.Scope; import java.io.BufferedReader; import java.io.IOException; import java.io.StringReader; -import java.lang.reflect.Constructor; import java.util.ArrayDeque; import java.util.Arrays; import java.util.Deque; @@ -99,6 +98,8 @@ public abstract class JexlParser extends StringParser { * @return true if declaration was successful, false if symbol was already declared */ boolean declareSymbol(int symbol); + void setConstant(int symbol); + void setDefined(int symbol); /** * Checks whether a symbol is declared in this lexical unit. @@ -106,6 +107,8 @@ public abstract class JexlParser extends StringParser { * @return true if declared, false otherwise */ boolean hasSymbol(int symbol); + boolean isConstant(int symbol); + boolean isDefined(int symbol); /** * @return the number of local variables declared in this unit @@ -313,32 +316,41 @@ public abstract class JexlParser extends StringParser { final Integer symbol = scope.getSymbol(name); if (symbol != null) { identifier.setLexical(scope.isLexical(symbol)); - identifier.setConstant(scope.isConstant(symbol)); boolean declared = true; if (scope.isCapturedSymbol(symbol)) { // captured are declared in all cases identifier.setCaptured(true); } else { - declared = block.hasSymbol(symbol); + LexicalUnit unit = block; + declared = unit.hasSymbol(symbol); // one of the lexical blocks above should declare it if (!declared) { for (final LexicalUnit u : blocks) { if (u.hasSymbol(symbol)) { + unit = u; declared = true; break; } } } - if (!declared && info instanceof JexlNode.Info) { + if (declared) { + if (unit.isConstant(symbol)) { + identifier.setConstant(true); + if (!unit.isDefined(symbol)) { + throw new JexlException.Parsing(info, name + ": variable is not defined").clean(); + } + identifier.setDefined(true); + } + } else if (info instanceof JexlNode.Info) { declared = isSymbolDeclared((JexlNode.Info) info, symbol); } } identifier.setSymbol(symbol, name); if (!declared) { identifier.setShaded(true); - if (identifier.isLexical() || getFeatures().isLexicalShade()) { + if (/*identifier.isLexical() ||*/ getFeatures().isLexicalShade()) { // can not reuse a local as a global - throw new JexlException.Parsing(info, name + ": variable is not defined").clean(); + throw new JexlException.Parsing(info, name + ": variable is not declared").clean(); } } } @@ -386,7 +398,7 @@ public abstract class JexlParser extends StringParser { * @param variable the identifier used to declare * @param token the variable name toekn */ - protected void declareFunction(final ASTVar variable, final Token token, Scope scope) { + protected void declareFunction(final ASTVar variable, final Token token) { final String name = token.image; // function foo() ... <=> const foo = ()->... if (scope == null) { @@ -394,11 +406,16 @@ public abstract class JexlParser extends StringParser { } final int symbol = scope.declareVariable(name, true, true); variable.setSymbol(symbol, name); + variable.setLexical(true); if (scope.isCapturedSymbol(symbol)) { variable.setCaptured(true); } - // lexical feature error - if (!declareSymbol(symbol)) { + // function is const fun... + if (declareSymbol(symbol)) { + scope.addLexical(symbol); + block.setConstant(symbol); + block.setDefined(symbol); + } else { if (getFeatures().isLexical()) { throw new JexlException(variable, name + ": variable is already declared"); } @@ -440,7 +457,7 @@ public abstract class JexlParser extends StringParser { if (lexical) { scope.addLexical(symbol); if (constant) { - scope.addConstant(symbol); + block.setConstant(symbol); } } } @@ -472,7 +489,8 @@ public abstract class JexlParser extends StringParser { } else if (lexical) { scope.addLexical(symbol); if (constant) { - scope.addConstant(symbol); + block.setConstant(symbol); + block.setDefined(symbol); // worst case is no argument, parameter will bind to null } } } @@ -612,10 +630,17 @@ public abstract class JexlParser extends StringParser { } if (lv instanceof ASTIdentifier) { ASTIdentifier var = (ASTIdentifier) lv; - if (!(var instanceof ASTVar)) { // if not a declaration... - int symbol = var.getSymbol(); - if (symbol >= 0 && scope.isConstant(symbol)) { - throw new JexlException.Assignment(var.jexlInfo(), var.getName()).clean(); + int symbol = var.getSymbol(); + boolean isconst = symbol >= 0 && block != null && block.isConstant(symbol); + if (isconst) { + if (!(var instanceof ASTVar)) { // if not a declaration... + if (block.isDefined(symbol)) { + throw new JexlException.Assignment(var.jexlInfo(), var.getName()).clean(); + } else { + block.setDefined(symbol); + } + } else { + block.setDefined(symbol); } } } diff --git a/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt b/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt index bfc476e6..af16272a 100644 --- a/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt +++ b/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt @@ -455,12 +455,12 @@ void DeclareVar(boolean lexical, boolean constant) #Var : { t=<IDENTIFIER> { declareVariable(jjtThis, t, lexical, constant); } } -void DeclareFunction(Scope scope) #Var : +void DeclareFunction() #Var : { Token t; } { - t=<IDENTIFIER> { declareFunction(jjtThis, t, scope); } + t=<IDENTIFIER> { declareFunction(jjtThis, t); } } void Pragma() #void : @@ -898,15 +898,14 @@ void Lambda() #JexlLambda : { Token arrow; Token name; - Scope scope = getScope(); - pushScope(); } { - { pushUnit(jjtThis); } <FUNCTION> (LOOKAHEAD(<IDENTIFIER>) DeclareFunction(scope))? Parameters() ( LOOKAHEAD(3) Block() | Expression()) { popUnit(jjtThis); popScope(); } + <FUNCTION> (LOOKAHEAD(<IDENTIFIER>) DeclareFunction())? { + pushScope(); pushUnit(jjtThis); } Parameters() ( LOOKAHEAD(3) Block() | Expression()) { popUnit(jjtThis); popScope(); } | - { pushUnit(jjtThis); } Parameters() (arrow=<LAMBDA> | arrow=<FATARROW>) ( LOOKAHEAD(3) Block() | Expression()) { checkLambda(arrow); popUnit(jjtThis); popScope(); } + { pushScope(); pushUnit(jjtThis); } Parameters() (arrow=<LAMBDA> | arrow=<FATARROW>) ( LOOKAHEAD(3) Block() | Expression()) { checkLambda(arrow); popUnit(jjtThis); popScope(); } | - { pushUnit(jjtThis); } Parameter() (arrow=<LAMBDA> | arrow=<FATARROW>)( LOOKAHEAD(3) Block() | Expression()) { checkLambda(arrow); popUnit(jjtThis); popScope(); } + { pushScope(); pushUnit(jjtThis); } Parameter() (arrow=<LAMBDA> | arrow=<FATARROW>)( LOOKAHEAD(3) Block() | Expression()) { checkLambda(arrow); popUnit(jjtThis); popScope(); } } diff --git a/src/test/java/org/apache/commons/jexl3/LambdaTest.java b/src/test/java/org/apache/commons/jexl3/LambdaTest.java index ea32a7e9..de7dab86 100644 --- a/src/test/java/org/apache/commons/jexl3/LambdaTest.java +++ b/src/test/java/org/apache/commons/jexl3/LambdaTest.java @@ -437,11 +437,36 @@ public class LambdaTest extends JexlTestCase { Assert.assertEquals(simpleWhitespace(src), parsed); } + @Test + public void testConst0() { + final JexlFeatures f = new JexlFeatures(); + final JexlEngine jexl = new JexlBuilder().strict(true).create(); + final JexlScript script = jexl.createScript( + "{ const x = 10; x + 1 }; { let x = 20; x = 22}"); + final JexlContext jc = new MapContext(); + final Object result = script.execute(jc); + Assert.assertEquals(22, result); + } + + @Test + public void testConst1() { + final JexlFeatures f = new JexlFeatures(); + final JexlEngine jexl = new JexlBuilder().strict(true).create(); + try { + final JexlScript script = jexl.createScript( + "const x; x + 1"); + Assert.fail("should fail, x is not defined"); + } catch(JexlException.Parsing xparse) { + Assert.assertTrue(xparse.getMessage().contains("x")); + } + } + @Test public void testNamedFuncIsConst() { String src = "function foo(x) { x + x }; var foo ='nonononon'"; JexlEngine jexl = createEngine(); try { JexlScript script = jexl.createScript(src); + Assert.fail("should fail, foo is already defined"); } catch(JexlException.Parsing xparse) { Assert.assertTrue(xparse.getMessage().contains("foo")); } diff --git a/src/test/java/org/apache/commons/jexl3/LexicalTest.java b/src/test/java/org/apache/commons/jexl3/LexicalTest.java index 058050fe..9271c554 100644 --- a/src/test/java/org/apache/commons/jexl3/LexicalTest.java +++ b/src/test/java/org/apache/commons/jexl3/LexicalTest.java @@ -29,6 +29,8 @@ import org.apache.commons.jexl3.internal.Script; import org.junit.Assert; import org.junit.Test; +import static org.apache.commons.jexl3.JexlTestCase.createEngine; + /** * Test cases for lexical option and feature. */ @@ -525,7 +527,10 @@ public class LexicalTest { @Test public void testForVariable0a() { - final JexlEngine jexl = new JexlBuilder().strict(true).create(); + final JexlFeatures f = new JexlFeatures(); + f.lexical(true); + f.lexicalShade(true); + final JexlEngine jexl = createEngine(f); try { final JexlScript script = jexl.createScript("for(let x : 1..3) { let c = 0}; return x"); Assert.fail("Should not have been parsed"); @@ -539,7 +544,7 @@ public class LexicalTest { final JexlFeatures f = new JexlFeatures(); f.lexical(true); f.lexicalShade(true); - final JexlEngine jexl = new JexlBuilder().strict(true).features(f).create(); + final JexlEngine jexl = createEngine(f); try { final JexlScript script = jexl.createScript("for(var x : 1..3) { var c = 0}; return x"); Assert.fail("Should not have been parsed"); @@ -553,7 +558,7 @@ public class LexicalTest { final JexlFeatures f = new JexlFeatures(); f.lexical(true); f.lexicalShade(true); - final JexlEngine jexl = new JexlBuilder().strict(true).features(f).create(); + final JexlEngine jexl = createEngine(f); try { final JexlScript script = jexl.createScript("for(var x : 1..3) { var c = 0} for(var x : 1..3) { var c = 0}; return x"); Assert.fail("Should not have been parsed"); @@ -565,7 +570,10 @@ public class LexicalTest { @Test public void testForVariable1b() { - final JexlEngine jexl = new JexlBuilder().strict(true).create(); + final JexlFeatures f = new JexlFeatures(); + f.lexical(true); + f.lexicalShade(true); + final JexlEngine jexl = createEngine(f); try { final JexlScript script = jexl.createScript("for(let x : 1..3) { let c = 0} for(let x : 1..3) { var c = 0}; return x"); Assert.fail("Should not have been parsed"); @@ -580,7 +588,7 @@ public class LexicalTest { final JexlFeatures f = new JexlFeatures(); f.lexical(true); f.lexicalShade(true); - final JexlEngine jexl = new JexlBuilder().strict(true).features(f).create(); + final JexlEngine jexl = createEngine(f); try { final JexlScript script = jexl.createScript("{var x = 0}; return x"); Assert.fail("Should not have been parsed"); @@ -595,7 +603,7 @@ public class LexicalTest { final String str = "i = 0; { var i = 32; }; i"; final JexlFeatures f = new JexlFeatures(); f.lexical(true); - final JexlEngine jexl = new JexlBuilder().strict(true).features(f).create(); + final JexlEngine jexl = createEngine(f); final JexlScript e = jexl.createScript(str); final JexlContext ctxt = new MapContext(); final Object o = e.execute(ctxt);