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 2019/08/21 22:04:59 UTC
[commons-jexl] branch master updated: JEXL-311: parse verbatim
expressions with correct scope,
added tests using lambdas Task #JEXL-311 - Jxlt template scripts fail using
verbatim expressions embedded in lambdas
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 9064d42 JEXL-311: parse verbatim expressions with correct scope, added tests using lambdas Task #JEXL-311 - Jxlt template scripts fail using verbatim expressions embedded in lambdas
9064d42 is described below
commit 9064d425b5e1d3e7cea1f5a6deeeb23effb66c2a
Author: henrib <he...@apache.org>
AuthorDate: Thu Aug 22 00:04:11 2019 +0200
JEXL-311: parse verbatim expressions with correct scope, added tests using lambdas
Task #JEXL-311 - Jxlt template scripts fail using verbatim expressions embedded in lambdas
---
RELEASE-NOTES.txt | 1 +
.../org/apache/commons/jexl3/internal/Closure.java | 5 +-
.../commons/jexl3/internal/TemplateEngine.java | 4 +-
.../jexl3/internal/TemplateInterpreter.java | 68 ++++++---------
.../commons/jexl3/internal/TemplateScript.java | 56 ++++++++++--
src/site/xdoc/changes.xml | 3 +
.../java/org/apache/commons/jexl3/JXLTTest.java | 99 +++++++++++++++++++++-
7 files changed, 180 insertions(+), 56 deletions(-)
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index eeca54d..8876a2b 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -73,6 +73,7 @@ New Features in 3.2:
Bugs Fixed in 3.2:
==================
+* JEXL-311: Jxlt template scripts fail using verbatim expressions embedded in lambdas
* JEXL-309: Line numbers are not correct when template report errors
* JEXL-306: Ternary operator ? protects also its branches from resolution errors
* JEXL-305: Script debugger produces incorrect syntax
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 009c962..a346fa7 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Closure.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Closure.java
@@ -124,7 +124,7 @@ public class Closure extends Script {
if (frame != null) {
callFrame = frame.assign(args);
}
- Interpreter interpreter = jexl.createInterpreter(context, callFrame);
+ Interpreter interpreter = createInterpreter(context, callFrame);
JexlNode block = script.jjtGetChild(script.jjtGetNumChildren() - 1);
return interpreter.interpret(block);
}
@@ -135,7 +135,7 @@ public class Closure extends Script {
if (frame != null) {
local = frame.assign(args);
}
- return new Callable(jexl.createInterpreter(context, local)) {
+ return new Callable(createInterpreter(context, local)) {
@Override
public Object interpret() {
JexlNode block = script.jjtGetChild(script.jjtGetNumChildren() - 1);
@@ -143,5 +143,4 @@ public class Closure extends Script {
}
};
}
-
}
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 43a3023..4609a3f 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java
@@ -925,7 +925,7 @@ public final class TemplateEngine extends JxltEngine {
line = theLine;
body = theBlock;
}
-
+
/**
* @return type
*/
@@ -980,8 +980,6 @@ public final class TemplateEngine extends JxltEngine {
}
}
-
-
/**
* Whether a sequence starts with a given set of characters (following spaces).
* <p>Space characters at beginning of line before the pattern are discarded.</p>
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 5e9ec70..ffca1ef 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/TemplateInterpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/TemplateInterpreter.java
@@ -18,16 +18,14 @@ package org.apache.commons.jexl3.internal;
import org.apache.commons.jexl3.JexlContext;
import org.apache.commons.jexl3.JexlInfo;
-import org.apache.commons.jexl3.JxltEngine;
import org.apache.commons.jexl3.internal.TemplateEngine.TemplateExpression;
import org.apache.commons.jexl3.introspection.JexlMethod;
import org.apache.commons.jexl3.introspection.JexlUberspect;
-import org.apache.commons.jexl3.parser.ASTArguments;
-import org.apache.commons.jexl3.parser.ASTFunctionNode;
import org.apache.commons.jexl3.parser.ASTIdentifier;
import org.apache.commons.jexl3.parser.JexlNode;
import java.io.Writer;
-import java.util.Arrays;
+import org.apache.commons.jexl3.parser.ASTJexlLambda;
+import org.apache.commons.jexl3.parser.ASTJexlScript;
/**
* The type of interpreter to use during evaluation of templates.
@@ -137,45 +135,6 @@ public class TemplateInterpreter extends Interpreter {
}
@Override
- protected Object visit(ASTFunctionNode node, Object data) {
- int argc = node.jjtGetNumChildren();
- if (argc > 2) {
- // objectNode 0 is the prefix
- String prefix = ((ASTIdentifier) node.jjtGetChild(0)).getName();
- if ("jexl".equals(prefix)) {
- ASTIdentifier functionNode = (ASTIdentifier) node.jjtGetChild(1);
- ASTArguments argNode = (ASTArguments) node.jjtGetChild(2);
- String fname = functionNode.getName();
- if ("print".equals(fname)) {
- // evaluate the arguments
- Object[] argv = visit(argNode, null);
- print((Integer) argv[0]);
- return null;
- }
- if ("include".equals(fname)) {
- // evaluate the arguments
- Object[] argv = visit(argNode, null);
- if (argv != null && argv.length > 0) {
- if (argv[0] instanceof TemplateScript) {
- TemplateScript script = (TemplateScript) argv[0];
- if (argv.length > 1) {
- argv = Arrays.copyOfRange(argv, 1, argv.length);
- } else {
- argv = null;
- }
- include(script, argv);
- return null;
- }
- }
- }
- // fail safe
- throw new JxltEngine.Exception(node.jexlInfo(), "no callable template function " + fname, null);
- }
- }
- return super.visit(node, data);
- }
-
- @Override
protected Object visit(ASTIdentifier node, Object data) {
String name = node.getName();
if ("$jexl".equals(name)) {
@@ -183,5 +142,26 @@ public class TemplateInterpreter extends Interpreter {
}
return super.visit(node, data);
}
-
+
+ @Override
+ protected Object visit(ASTJexlScript node, Object data) {
+ if (node instanceof ASTJexlLambda && !((ASTJexlLambda) node).isTopLevel()) {
+ return new Closure(this, (ASTJexlLambda) node) {
+ @Override
+ protected Interpreter createInterpreter(JexlContext context, Scope.Frame local) {
+ return new TemplateInterpreter(jexl, context, local, exprs, writer);
+ }
+ };
+ }
+ // otherwise...
+ final int numChildren = node.jjtGetNumChildren();
+ Object result = null;
+ for (int i = 0; i < numChildren; i++) {
+ JexlNode child = node.jjtGetChild(i);
+ result = child.jjtAccept(this, data);
+ cancelCheck(child);
+ }
+ return result;
+ }
+
}
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 9d6c7bc..63829f2 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/TemplateScript.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/TemplateScript.java
@@ -29,6 +29,12 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.TreeMap;
+import org.apache.commons.jexl3.parser.ASTArguments;
+import org.apache.commons.jexl3.parser.ASTFunctionNode;
+import org.apache.commons.jexl3.parser.ASTIdentifier;
+import org.apache.commons.jexl3.parser.ASTNumberLiteral;
+import org.apache.commons.jexl3.parser.JexlNode;
/**
* A Template instance.
@@ -70,8 +76,7 @@ public final class TemplateScript implements JxltEngine.Template {
throw new NullPointerException("null input");
}
this.jxlt = engine;
- Scope scope = parms == null ? null : new Scope(null, parms);
- prefix = directive;
+ this.prefix = directive;
List<Block> blocks = jxlt.readTemplate(prefix, reader);
List<TemplateExpression> uexprs = new ArrayList<TemplateExpression>();
StringBuilder strb = new StringBuilder();
@@ -109,9 +114,15 @@ public final class TemplateScript implements JxltEngine.Template {
info = jxlt.getEngine().createInfo();
}
// allow lambda defining params
+ Scope scope = parms == null ? null : new Scope(null, parms);
script = jxlt.getEngine().parse(info.at(1, 1), false, strb.toString(), scope).script();
- scope = script.getScope();
- // create the exprs using the code frame for those appearing after the first block of code
+ // seek the map of expression number to scope so we can parse Unified
+ // expression blocks with the appropriate symbols
+ Map<Integer, Scope> mscope = new TreeMap<Integer, Scope>();
+ collectPrintScope(script.script(), null, mscope);
+ // jexl:print(...) expression counter
+ int jpe = 0;
+ // create the exprs using the intended scopes
for (int b = 0; b < blocks.size(); ++b) {
Block block = blocks.get(b);
if (block.getType() == BlockType.VERBATIM) {
@@ -119,7 +130,7 @@ public final class TemplateScript implements JxltEngine.Template {
jxlt.parseExpression(
info.at(block.getLine(), 1),
block.getBody(),
- b > codeStart ? scope : null)
+ mscope.get(jpe++))
);
}
}
@@ -146,6 +157,41 @@ public final class TemplateScript implements JxltEngine.Template {
script = theScript;
exprs = theExprs;
}
+
+ /**
+ * Collects the scope surrounding a call to jexl:print(i).
+ * <p>This allows to later parse the blocks with the known symbols
+ * in the frame visible to the parser.
+ * @param node the visited node
+ * @param scope the current scope
+ * @param mscope the map of printed expression number to scope
+ */
+ static void collectPrintScope(JexlNode node, Scope scope, Map<Integer, Scope> mscope) {
+ int nc = node.jjtGetNumChildren();
+ if (node instanceof ASTFunctionNode) {
+ if (nc == 2) {
+ // 0 must be the prefix jexl:
+ ASTIdentifier nameNode = (ASTIdentifier) node.jjtGetChild(0);
+ if ("print".equals(nameNode.getName()) && "jexl".equals(nameNode.getNamespace())) {
+ ASTArguments argNode = (ASTArguments) node.jjtGetChild(1);
+ if (argNode.jjtGetNumChildren() == 1) {
+ // seek the epression number
+ JexlNode arg0 = argNode.jjtGetChild(0);
+ if (arg0 instanceof ASTNumberLiteral) {
+ int exprNumber = ((ASTNumberLiteral) arg0).getLiteral().intValue();
+ mscope.put(exprNumber, scope);
+ return;
+ }
+ }
+ }
+ }
+ } else if (node instanceof ASTJexlScript) {
+ scope = ((ASTJexlScript) node).getScope();
+ }
+ for (int c = 0; c < nc; ++c) {
+ collectPrintScope(node.jjtGetChild(c), scope, mscope);
+ }
+ }
/**
* @return script
diff --git a/src/site/xdoc/changes.xml b/src/site/xdoc/changes.xml
index 7d7ffa6..fdfe443 100644
--- a/src/site/xdoc/changes.xml
+++ b/src/site/xdoc/changes.xml
@@ -26,6 +26,9 @@
</properties>
<body>
<release version="3.2" date="unreleased">
+ <action dev="henrib" type="fix" issue="JEXL-311">
+ Jxlt template scripts fail using verbatim expressions embedded in lambdas
+ </action>
<action dev="henrib" type="fix" issue="JEXL-309">
Line numbers are not correct when template report errors
</action>
diff --git a/src/test/java/org/apache/commons/jexl3/JXLTTest.java b/src/test/java/org/apache/commons/jexl3/JXLTTest.java
index 664a088..cc2b8d5 100644
--- a/src/test/java/org/apache/commons/jexl3/JXLTTest.java
+++ b/src/test/java/org/apache/commons/jexl3/JXLTTest.java
@@ -728,7 +728,8 @@ public class JXLTTest extends JexlTestCase {
Object value = JEXL.createScript(expr).execute(context);
Assert.assertEquals(expr, "H\"ello \nHenrib", value);
}
- @Test
+
+ @Test
public void testInterpolationLvsG2() throws Exception {
String expr = "user='Dimitri'; var user='Henrib'; `H\\`ello \n${user}`";
Object value = JEXL.createScript(expr).execute(context);
@@ -764,4 +765,100 @@ public class JXLTTest extends JexlTestCase {
// Assert.assertEquals("fourty-two", output);
//
// }
+
+ public static class Executor311 {
+ private final String name;
+
+ public Executor311(String name) {
+ this.name = name;
+ }
+ // Injects name as first arg of any called script
+ public Object execute(JexlScript script, Object ...args) {
+ Object[] actuals;
+ if (args != null && args.length > 0) {
+ actuals = new Object[args.length + 1] ;
+ System.arraycopy(args, 0, actuals, 1, args.length);
+ actuals[0] = name;
+ } else {
+ actuals = new Object[]{name};
+ }
+ return script.execute(JexlEngine.getThreadContext(), actuals);
+ }
+ }
+
+ public static class Context311 extends MapContext {
+ public Executor311 exec(String name) {
+ return new Executor311(name);
+ }
+ }
+
+ @Test
+ public void test311a() throws Exception {
+ JexlContext ctx = null;
+ String rpt
+ = "$$((a)->{\n"
+ + "<p>Universe ${a}</p>\n"
+ + "$$})(42)";
+ JxltEngine.Template t = JXLT.createTemplate("$$", new StringReader(rpt));
+ StringWriter strw = new StringWriter();
+ t.evaluate(ctx, strw);
+ String output = strw.toString();
+ Assert.assertEquals("<p>Universe 42</p>\n", output);
+ }
+
+ @Test
+ public void test311b() throws Exception {
+ JexlContext ctx311 = new Context311();
+ String rpt
+ = "$$ exec('42').execute(()->{\n"
+ + "<p>Universe 42</p>\n"
+ + "$$})";
+ JxltEngine.Template t = JXLT.createTemplate("$$", new StringReader(rpt));
+ StringWriter strw = new StringWriter();
+ t.evaluate(ctx311, strw, 42);
+ String output = strw.toString();
+ Assert.assertEquals("<p>Universe 42</p>\n", output);
+ }
+
+ @Test
+ public void test311c() throws Exception {
+ JexlContext ctx311 = new Context311();
+ String rpt
+ = "$$ exec('42').execute((a)->{"
+ + "\n<p>Universe ${a}</p>"
+ + "\n$$})";
+ JxltEngine.Template t = JXLT.createTemplate("$$", new StringReader(rpt));
+ StringWriter strw = new StringWriter();
+ t.evaluate(ctx311, strw, 42);
+ String output = strw.toString();
+ Assert.assertEquals("<p>Universe 42</p>\n", output);
+ }
+
+ @Test
+ public void test311d() throws Exception {
+ JexlContext ctx311 = new Context311();
+ String rpt
+ = "$$ exec('4').execute((a, b)->{"
+ + "\n<p>Universe ${a}${b}</p>"
+ + "\n$$}, '2')";
+ JxltEngine.Template t = JXLT.createTemplate("$$", new StringReader(rpt));
+ StringWriter strw = new StringWriter();
+ t.evaluate(ctx311, strw, 42);
+ String output = strw.toString();
+ Assert.assertEquals("<p>Universe 42</p>\n", output);
+ }
+
+ @Test
+ public void test311e() throws Exception {
+ JexlContext ctx311 = new Context311();
+ String rpt
+ = "$$var u = 'Universe'; exec('4').execute((a, b)->{"
+ + "\n<p>${u} ${a}${b}</p>"
+ + "\n$$}, '2')";
+ JxltEngine.Template t = JXLT.createTemplate("$$", new StringReader(rpt));
+ StringWriter strw = new StringWriter();
+ t.evaluate(ctx311, strw, 42);
+ String output = strw.toString();
+ Assert.assertEquals("<p>Universe 42</p>\n", output);
+ }
}