Skip to content
Permalink
Browse files
JEXL-367: function f() {} creates a const f variable;
- checked the thin vs fat arrow feature;
  • Loading branch information
henrib committed May 9, 2022
1 parent 4627373 commit 840b9d6cc41d00046625c3bb2227f8ddc628d901
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 23 deletions.
@@ -442,7 +442,7 @@ public boolean supportsLambda() {
/**
* Sets whether thin-arrow lambda syntax is enabled.
* <p>
* When disabled, parsing a script/expression using syntactic thin-arrow (->)
* When disabled, parsing a script/expression using syntactic thin-arrow (-&lt;)
* will throw a parsing exception.
* @param flag true to enable, false to disable
* @return this features instance
@@ -462,7 +462,7 @@ public boolean supportsThinArrow() {
/**
* Sets whether fat-arrow lambda syntax is enabled.
* <p>
* When disabled, parsing a script/expression using syntactic fat-arrow (=>)
* When disabled, parsing a script/expression using syntactic fat-arrow (=&lt;)
* will throw a parsing exception.
* @param flag true to enable, false to disable
* @return this features instance
@@ -989,7 +989,10 @@ protected Object visit(final ASTJexlScript script, final Object data) {
if (child0 instanceof ASTVar) {
ASTVar var = (ASTVar) child0;
this.visit(var, data);
frame.set(var.getSymbol(), closure);
int symbol = var.getSymbol();
frame.set(symbol, closure);
// make the closure accessible to itself, ie capture the currently set variable after frame creation
closure.setCaptured(symbol, closure);
}
return closure;
}
@@ -205,7 +205,7 @@ protected JexlFeatures getFeatures() {
* regain access after parsing to known which / how-many registers are needed. </p>
* @return the named register map
*/
protected Scope getFrame() {
protected Scope getScope() {
return scope;
}

@@ -388,7 +388,11 @@ private boolean declareSymbol(final int symbol) {
*/
protected void declareFunction(final ASTVar variable, final Token token, Scope scope) {
final String name = token.image;
final int symbol = scope.declareVariable(name);
// function foo() ... <=> const foo = ()->...
if (scope == null) {
scope = new Scope(null);
}
final int symbol = scope.declareVariable(name, true, true);
variable.setSymbol(symbol, name);
if (scope.isCapturedSymbol(symbol)) {
variable.setCaptured(true);
@@ -416,7 +420,7 @@ protected void declareVariable(final ASTVar variable, final Token token, boolean
throwFeatureException(JexlFeatures.LOCAL_VAR, token);
}
if (scope == null) {
scope = new Scope(null, (String[]) null);
scope = new Scope(null);
}
final int symbol = scope.declareVariable(name, lexical, constant);
variable.setSymbol(symbol, name);
@@ -601,7 +605,6 @@ protected void jjtreeCloseNodeScope(final JexlNode node) {
if (script.getScope() != scope) {
script.setScope(scope);
}
popScope();
} else if (ASSIGN_NODES.contains(node.getClass())) {
final JexlNode lv = node.jjtGetChild(0);
if (!lv.isLeftValue()) {
@@ -358,18 +358,19 @@ void Statement() #void : {}
void StatementNoVar() #void : {}
{
<SEMICOL>
| LOOKAHEAD(<PRAGMA>) Pragma()
| LOOKAHEAD(<ANNOTATION>) AnnotatedStatement()
| LOOKAHEAD(<IF>) IfStatement()
| LOOKAHEAD(<FOR>) ForeachStatement()
| LOOKAHEAD(<WHILE>) WhileStatement()
| LOOKAHEAD(<DO>) DoWhileStatement()
| LOOKAHEAD(<RETURN>) ReturnStatement()
| LOOKAHEAD(<CONTINUE>) Continue()
| LOOKAHEAD(<BREAK>) Break()
| LOOKAHEAD(<FUNCTION> <IDENTIFIER> <LPAREN>) Lambda()
| LOOKAHEAD(Expression()) ExpressionStatement()
| Block()
| IfStatement()
| ForeachStatement()
| WhileStatement()
| DoWhileStatement()
| ReturnStatement()
| Continue()
| Break()
| LOOKAHEAD(<VAR>, {!getFeatures().isLexical()} ) Var()
| Pragma()
}

void Block() #Block : {}
@@ -897,15 +898,15 @@ void Lambda() #JexlLambda :
{
Token arrow;
Token name;
Scope scope = getFrame();
pushFrame();
Scope scope = getScope();
pushScope();
}
{
{ pushUnit(jjtThis); } <FUNCTION> (LOOKAHEAD(<IDENTIFIER>) DeclareFunction(scope))? Parameters() ( LOOKAHEAD(3) Block() | Expression()) { popUnit(jjtThis); }
{ pushUnit(jjtThis); } <FUNCTION> (LOOKAHEAD(<IDENTIFIER>) DeclareFunction(scope))? Parameters() ( LOOKAHEAD(3) Block() | Expression()) { popUnit(jjtThis); popScope(); }
|
{ pushUnit(jjtThis); } Parameters() (arrow=<LAMBDA> | arrow=<FATARROW>) ( LOOKAHEAD(3) Block() | Expression()) { checkLambda(arrow); popUnit(jjtThis); }
{ 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); }
{ pushUnit(jjtThis); } Parameter() (arrow=<LAMBDA> | arrow=<FATARROW>)( LOOKAHEAD(3) Block() | Expression()) { checkLambda(arrow); popUnit(jjtThis); popScope(); }
}


@@ -20,11 +20,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;
import org.apache.commons.jexl3.introspection.JexlPermissions;
import org.junit.After;
@@ -70,6 +67,9 @@ public void tearDown() throws Exception {
static JexlEngine createEngine() {
return new JexlBuilder().create();
}
static JexlEngine createEngine(JexlFeatures features) {
return new JexlBuilder().features(features).create();
}

// define mode pro50
static final JexlOptions MODE_PRO50 = new JexlOptions();
@@ -121,6 +121,10 @@ public static boolean equalsIgnoreWhiteSpace(String lhs, String rhs) {
return lhsw.equals(rhsw);
}

public String simpleWhitespace(String arg) {
return arg.trim().replaceAll("\\s+", " ");
}

/**
* A very secure singleton.
*/
@@ -400,6 +400,53 @@ public void test271e() {
Assert.assertEquals(1120, result);
}

@Test public void testFatFact0() {
JexlFeatures features = new JexlFeatures();
features.fatArrow(true);
String src = "function (a) { const fact = (x)=>{ x <= 1? 1 : x * fact(x - 1) }; fact(a) }";
JexlEngine jexl = createEngine(features);
JexlScript script = jexl.createScript(src);
Object result = script.execute(null, 6);
Assert.assertEquals(720, result);
}

@Test public void testFatFact1() {
String src = "function (a) { const fact = (x)=> x <= 1? 1 : x * fact(x - 1) ; fact(a) }";
JexlFeatures features = new JexlFeatures();
features.fatArrow(true);
JexlEngine jexl = createEngine(features);
JexlScript script = jexl.createScript(src);
Object result = script.execute(null, 6);
Assert.assertEquals(720, result);
features.fatArrow(false);
jexl = createEngine(features);
try {
script = jexl.createScript(src);
} catch(JexlException.Feature xfeature) {
Assert.assertTrue(xfeature.getMessage().contains("fat-arrow"));
}
}

@Test public void testNamedFunc() {
String src = "(a)->{ function fact(x) { x <= 1? 1 : x * fact(x - 1); } fact(a); }";
JexlEngine jexl = createEngine();
JexlScript script = jexl.createScript(src);
Object result = script.execute(null, 6);
Assert.assertEquals(720, result);
String parsed = simpleWhitespace(script.getParsedText());
Assert.assertEquals(simpleWhitespace(src), parsed);
}

@Test public void testNamedFuncIsConst() {
String src = "function foo(x) { x + x }; var foo ='nonononon'";
JexlEngine jexl = createEngine();
try {
JexlScript script = jexl.createScript(src);
} catch(JexlException.Parsing xparse) {
Assert.assertTrue(xparse.getMessage().contains("foo"));
}
}

@Test public void testLambdaExpr0() {
String src = "(x, y) -> x + y";
JexlEngine jexl = createEngine();

0 comments on commit 840b9d6

Please sign in to comment.