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/05/09 15:20:26 UTC
[commons-jexl] 02/03: JEXL-367: function f() {} creates a const f variable; - checked the thin vs fat arrow feature;
This is an automated email from the ASF dual-hosted git repository.
henrib pushed a commit to branch JEXL-367
in repository https://gitbox.apache.org/repos/asf/commons-jexl.git
commit 840b9d6cc41d00046625c3bb2227f8ddc628d901
Author: henrib <he...@apache.org>
AuthorDate: Mon May 9 17:06:13 2022 +0200
JEXL-367: function f() {} creates a const f variable;
- checked the thin vs fat arrow feature;
---
.../org/apache/commons/jexl3/JexlFeatures.java | 4 +-
.../apache/commons/jexl3/internal/Interpreter.java | 5 ++-
.../apache/commons/jexl3/parser/JexlParser.java | 11 +++--
.../org/apache/commons/jexl3/parser/Parser.jjt | 27 +++++++------
.../org/apache/commons/jexl3/JexlTestCase.java | 10 +++--
.../java/org/apache/commons/jexl3/LambdaTest.java | 47 ++++++++++++++++++++++
6 files changed, 81 insertions(+), 23 deletions(-)
diff --git a/src/main/java/org/apache/commons/jexl3/JexlFeatures.java b/src/main/java/org/apache/commons/jexl3/JexlFeatures.java
index 06ed52a5..124468fb 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlFeatures.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlFeatures.java
@@ -442,7 +442,7 @@ public final class JexlFeatures {
/**
* 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 (-<)
* will throw a parsing exception.
* @param flag true to enable, false to disable
* @return this features instance
@@ -462,7 +462,7 @@ public final class JexlFeatures {
/**
* 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 (=<)
* will throw a parsing exception.
* @param flag true to enable, false to disable
* @return this features instance
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 8c3e7351..98b01ea4 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
@@ -989,7 +989,10 @@ public class Interpreter extends InterpreterBase {
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;
}
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 990c4c57..615de42b 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
@@ -205,7 +205,7 @@ public abstract class JexlParser extends StringParser {
* 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 @@ public abstract class JexlParser extends StringParser {
*/
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 @@ public abstract class JexlParser extends StringParser {
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 @@ public abstract class JexlParser extends StringParser {
if (script.getScope() != scope) {
script.setScope(scope);
}
- popScope();
} else if (ASSIGN_NODES.contains(node.getClass())) {
final JexlNode lv = node.jjtGetChild(0);
if (!lv.isLeftValue()) {
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 b25f529d..bfc476e6 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
+++ b/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
@@ -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(); }
}
diff --git a/src/test/java/org/apache/commons/jexl3/JexlTestCase.java b/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
index 5c63c58d..a8249556 100644
--- a/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
+++ b/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
@@ -20,11 +20,8 @@ package org.apache.commons.jexl3;
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 class JexlTestCase {
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 class JexlTestCase {
return lhsw.equals(rhsw);
}
+ public String simpleWhitespace(String arg) {
+ return arg.trim().replaceAll("\\s+", " ");
+ }
+
/**
* 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 31a836ae..ea32a7e9 100644
--- a/src/test/java/org/apache/commons/jexl3/LambdaTest.java
+++ b/src/test/java/org/apache/commons/jexl3/LambdaTest.java
@@ -400,6 +400,53 @@ public class LambdaTest extends JexlTestCase {
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();