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 8e4f41d JEXL-328: ensure creation of options before evaluation do call pragma processing, added tests Task #JEXL-328 - JXLT template scripts evaluation do not process pragmas 8e4f41d is described below commit 8e4f41de2f8407bc3b9e42bbbac7749944c1d538 Author: henrib <hen...@apache.org> AuthorDate: Wed Mar 4 17:44:54 2020 +0100 JEXL-328: ensure creation of options before evaluation do call pragma processing, added tests Task #JEXL-328 - JXLT template scripts evaluation do not process pragmas --- RELEASE-NOTES.txt | 1 + .../org/apache/commons/jexl3/internal/Engine.java | 59 +++++++++- .../apache/commons/jexl3/internal/Interpreter.java | 6 +- .../commons/jexl3/internal/InterpreterBase.java | 21 ++++ .../org/apache/commons/jexl3/internal/Script.java | 52 +-------- .../commons/jexl3/internal/TemplateEngine.java | 25 ++++- .../jexl3/internal/TemplateInterpreter.java | 124 +++++++++++++++++---- .../commons/jexl3/internal/TemplateScript.java | 35 ++++-- src/site/xdoc/changes.xml | 3 + .../org/apache/commons/jexl3/FeaturesTest.java | 2 +- .../java/org/apache/commons/jexl3/JXLTTest.java | 98 ++++++++++++++-- 11 files changed, 330 insertions(+), 96 deletions(-) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 27f7655..4249085 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -74,6 +74,7 @@ New Features in 3.2: Bugs Fixed in 3.2: ================== +* JEXL-328: JXLT template scripts evaluation do not process pragmas * JEXL-327: map[null] does not work in assignment context * JEXL-326: Link to "JavaCC" on syntax reference page is broken * JEXL-325: Potential race-condition in NumberParser.toString() diff --git a/src/main/java/org/apache/commons/jexl3/internal/Engine.java b/src/main/java/org/apache/commons/jexl3/internal/Engine.java index 20da2a0..f470fd9 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/Engine.java +++ b/src/main/java/org/apache/commons/jexl3/internal/Engine.java @@ -341,6 +341,62 @@ public class Engine extends JexlEngine { } /** + * Compute a script options for evaluation. + * <p>This calls processPragma(...). + * @param script the script + * @param context the context + * @return the options + */ + protected JexlOptions options(ASTJexlScript script, JexlContext context) { + final JexlOptions opts = options(context); + if (script != null) { + // when parsing lexical, try hard to run lexical + JexlFeatures features = script.getFeatures(); + if (features != null) { + if (features.isLexical()) { + opts.setLexical(true); + } + if (features.isLexicalShade()) { + opts.setLexicalShade(true); + } + } + // process script pragmas if any + processPragmas(script, context, opts); + } + return opts; + } + + /** + * Processes a script pragmas. + * <p>Only called from options(...) + * @param script the script + * @param context the context + * @param opts the options + */ + protected void processPragmas(ASTJexlScript script, JexlContext context, JexlOptions opts) { + Map<String, Object> pragmas = script.getPragmas(); + if (pragmas != null && !pragmas.isEmpty()) { + JexlContext.PragmaProcessor processor = + context instanceof JexlContext.PragmaProcessor + ? (JexlContext.PragmaProcessor) context + : null; + for(Map.Entry<String, Object> pragma : pragmas.entrySet()) { + String key = pragma.getKey(); + Object value = pragma.getValue(); + if (PRAGMA_OPTIONS.equals(key)) { + if (value instanceof String) { + String[] vs = ((String) value).split(" "); + opts.setFlags(vs); + } + } + if (processor != null) { + processor.processPragma(key, value); + } + } + } + } + + /** * Sets options from this engine options. * @param opts the options to set * @return the options @@ -348,7 +404,7 @@ public class Engine extends JexlEngine { public JexlOptions optionsSet(JexlOptions opts) { if (opts != null) { opts.set(options); - } + } return opts; } @@ -375,6 +431,7 @@ public class Engine extends JexlEngine { return new Interpreter(this, opts, context, frame); } + @Override public Script createExpression(JexlInfo info, String expression) { return createScript(expressionFeatures, info, expression, null); 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 99a81c9..8d955eb 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java +++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java @@ -1223,8 +1223,8 @@ public class Interpreter extends InterpreterBase { } if (ant != null) { String aname = ant.toString(); - boolean undefined = !(context.has(aname)); - return unsolvableVariable(node, aname, undefined); + boolean defined = isVariableDefined(frame, block, aname); + return unsolvableVariable(node, aname, !defined); } return unsolvableProperty(node, stringifyProperty(ptyNode), ptyNode == objectNode, null); @@ -1234,7 +1234,7 @@ public class Interpreter extends InterpreterBase { return null; } String aname = ant != null ? ant.toString() : "?"; - boolean defined = context.has(aname); + boolean defined = isVariableDefined(frame, block, aname); if (defined && !arithmetic.isStrict()) { return null; } diff --git a/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java b/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java index 1a69717..8655760 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java +++ b/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java @@ -246,6 +246,27 @@ public abstract class InterpreterBase extends ParserVisitor { } /** + * Checks whether a variable is defined. + * <p>The var may be either a local variable declared in the frame and + * visible from the block or defined in the context. + * @param frame the frame + * @param block the block + * @param name the variable name + * @return true if variable is defined, false otherwise + */ + protected boolean isVariableDefined(Frame frame, LexicalScope block, String name) { + if (frame != null && block != null) { + Integer ref = frame.getScope().getSymbol(name); + int symbol = ref != null? ref : -1; + if (symbol >= 0 && block.hasSymbol(symbol)) { + Object value = frame.get(symbol); + return value != Scope.UNDEFINED && value != Scope.UNDECLARED; + } + } + return context.has(name); + } + + /** * Gets a value of a defined local variable or from the context. * @param frame the local frame * @param block the lexical block if any diff --git a/src/main/java/org/apache/commons/jexl3/internal/Script.java b/src/main/java/org/apache/commons/jexl3/internal/Script.java index c458242..ed01345 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/Script.java +++ b/src/main/java/org/apache/commons/jexl3/internal/Script.java @@ -17,6 +17,7 @@ package org.apache.commons.jexl3.internal; import org.apache.commons.jexl3.JexlContext; +import org.apache.commons.jexl3.JexlFeatures; import org.apache.commons.jexl3.JexlInfo; import org.apache.commons.jexl3.JexlOptions; import org.apache.commons.jexl3.JexlEngine; @@ -27,8 +28,6 @@ import org.apache.commons.jexl3.parser.ASTJexlScript; import java.util.List; import java.util.Map; import java.util.Set; -import org.apache.commons.jexl3.JexlFeatures; - /** * <p>A JexlScript implementation.</p> * @since 1.1 @@ -50,10 +49,6 @@ public class Script implements JexlScript, JexlExpression { * The engine version (as class loader change count) that last evaluated this script. */ protected int version; - /** - * The name of the options pragma. - */ - protected static final String PRAGMA_OPTIONS = "jexl.options"; /** * @return the script AST @@ -105,55 +100,14 @@ public class Script implements JexlScript, JexlExpression { } /** - * Compute this script options for evaluation. - * <p>This also calls the pragma processor - * @param context the context - * @return the options - */ - protected JexlOptions options(JexlContext context) { - JexlOptions opts = jexl.options(context); - // when parsing lexical, try hard to run lexical - JexlFeatures features = script.getFeatures(); - if (features != null) { - if (features.isLexical()) { - opts.setLexical(true); - } - if (features.isLexicalShade()) { - opts.setLexicalShade(true); - } - } - // process script pragmas if any - Map<String, Object> pragmas = script.getPragmas(); - if (pragmas != null) { - JexlContext.PragmaProcessor processor = - context instanceof JexlContext.PragmaProcessor - ? (JexlContext.PragmaProcessor) context - : null; - for(Map.Entry<String, Object> pragma : pragmas.entrySet()) { - String key = pragma.getKey(); - Object value = pragma.getValue(); - if (PRAGMA_OPTIONS.equals(key)) { - if (value instanceof String) { - String[] vs = ((String) value).split(" "); - opts.setFlags(vs); - } - } - if (processor != null) { - processor.processPragma(key, value); - } - } - } - return opts; - } - - /** * Creates this script interpreter. * @param context the context * @param frame the calling frame * @return the interpreter */ protected Interpreter createInterpreter(JexlContext context, Frame frame) { - return jexl.createInterpreter(context, frame, options(context)); + JexlOptions opts = jexl.options(script, context); + return jexl.createInterpreter(context, frame, opts); } /** 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 8f1a7ea..fa2831b 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java +++ b/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java @@ -18,6 +18,7 @@ package org.apache.commons.jexl3.internal; import org.apache.commons.jexl3.JexlContext; import org.apache.commons.jexl3.JexlException; +import org.apache.commons.jexl3.JexlOptions; import org.apache.commons.jexl3.JexlInfo; import org.apache.commons.jexl3.JxltEngine; import org.apache.commons.jexl3.parser.ASTJexlScript; @@ -291,7 +292,7 @@ public final class TemplateEngine extends JxltEngine { */ protected final TemplateExpression prepare(Frame frame, JexlContext context) { try { - Interpreter interpreter = new TemplateInterpreter(jexl, context, frame, null, null); + Interpreter interpreter = jexl.createInterpreter(context, frame, jexl.options(context)); return prepare(interpreter); } catch (JexlException xjexl) { JexlException xuel = createException(xjexl.getInfo(), "prepare", this, xjexl); @@ -319,6 +320,15 @@ public final class TemplateEngine extends JxltEngine { } /** + * The options to use during evaluation. + * @param context the context + * @return the options + */ + protected JexlOptions options(JexlContext context) { + return jexl.options(null, context); + } + + /** * Evaluates this expression. * @param frame the frame storing parameters and local variables * @param context the context storing global variables @@ -327,7 +337,13 @@ public final class TemplateEngine extends JxltEngine { */ protected final Object evaluate(Frame frame, JexlContext context) { try { - Interpreter interpreter = new TemplateInterpreter(jexl, context, frame, null, null); + JexlOptions options = options(context); + TemplateInterpreter.Arguments args = new TemplateInterpreter + .Arguments(jexl) + .context(context) + .options(options) + .frame(frame); + Interpreter interpreter = new TemplateInterpreter(args); return evaluate(interpreter); } catch (JexlException xjexl) { JexlException xuel = createException(xjexl.getInfo(), "evaluate", this, xjexl); @@ -420,6 +436,11 @@ public final class TemplateEngine extends JxltEngine { strb.append("}"); return strb; } + + @Override + protected JexlOptions options(JexlContext context) { + return jexl.options(node instanceof ASTJexlScript? (ASTJexlScript) node : null, context); + } @Override protected Object evaluate(Interpreter interpreter) { 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 7babe6f..d095625 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/TemplateInterpreter.java +++ b/src/main/java/org/apache/commons/jexl3/internal/TemplateInterpreter.java @@ -18,14 +18,16 @@ package org.apache.commons.jexl3.internal; import org.apache.commons.jexl3.JexlContext; import org.apache.commons.jexl3.JexlInfo; +import org.apache.commons.jexl3.JexlOptions; 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.ASTIdentifier; -import org.apache.commons.jexl3.parser.JexlNode; -import java.io.Writer; import org.apache.commons.jexl3.parser.ASTJexlLambda; import org.apache.commons.jexl3.parser.ASTJexlScript; +import org.apache.commons.jexl3.parser.JexlNode; + +import java.io.Writer; /** * The type of interpreter to use during evaluation of templates. @@ -39,18 +41,85 @@ public class TemplateInterpreter extends Interpreter { private final Writer writer; /** + * Helper ctor. + * <p>Stores the different properties required to create a Template interpreter. + */ + static class Arguments { + /** The engine. */ + Engine jexl; + /** The options. */ + JexlOptions options; + /** The context. */ + JexlContext jcontext; + /** The frame. */ + Frame jframe; + /** The expressions. */ + TemplateExpression[] expressions; + /** The writer. */ + Writer out; + + /** + * Sole ctor. + * @param e the JEXL engine + */ + Arguments(Engine e) { + this.jexl = e; + } + /** + * Sets the options. + * @param o the options + * @return this instance + */ + Arguments options(JexlOptions o) { + this.options = o; + return this; + } + /** + * Sets the context. + * @param j the context + * @return this instance + */ + Arguments context(JexlContext j) { + this.jcontext = j; + return this; + } + /** + * Sets the frame. + * @param f the frame + * @return this instance + */ + Arguments frame(Frame f) { + this.jframe = f; + return this; + } + /** + * Sets the expressions. + * @param e the expressions + * @return this instance + */ + Arguments expressions(TemplateExpression[] e) { + this.expressions = e; + return this; + } + /** + * Sets the writer. + * @param o the writer + * @return this instance + */ + Arguments writer(Writer o) { + this.out = o; + return this; + } + } + + /** * Creates a template interpreter instance. - * @param jexl the engine instance - * @param jcontext the base context - * @param jframe the calling frame - * @param expressions the list of TemplateExpression from the TemplateScript to evaluate - * @param out the output writer + * @param args the template interpreter arguments */ - TemplateInterpreter(Engine jexl, - JexlContext jcontext, Frame jframe, TemplateExpression[] expressions, Writer out) { - super(jexl, null, jcontext, jframe); - exprs = expressions; - writer = out; + TemplateInterpreter(Arguments args) { + super(args.jexl, args.options, args.jcontext, args.jframe); + exprs = args.expressions; + writer = args.out; block = new LexicalFrame(frame, null); } @@ -145,24 +214,31 @@ public class TemplateInterpreter extends Interpreter { } @Override - protected Object visit(ASTJexlScript node, Object data) { - if (node instanceof ASTJexlLambda && !((ASTJexlLambda) node).isTopLevel()) { - return new Closure(this, (ASTJexlLambda) node) { + protected Object visit(ASTJexlScript script, Object data) { + if (script instanceof ASTJexlLambda && !((ASTJexlLambda) script).isTopLevel()) { + return new Closure(this, (ASTJexlLambda) script) { @Override protected Interpreter createInterpreter(JexlContext context, Frame local) { - return new TemplateInterpreter(jexl, context, local, exprs, writer); + JexlOptions opts = jexl.options(script, context); + TemplateInterpreter.Arguments targs = new TemplateInterpreter.Arguments(jexl) + .context(context) + .options(opts) + .frame(local) + .expressions(exprs) + .writer(writer); + return new TemplateInterpreter(targs); } }; } // 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); + final int numChildren = script.jjtGetNumChildren(); + Object result = null; + for (int i = 0; i < numChildren; i++) { + JexlNode child = script.jjtGetChild(i); + result = child.jjtAccept(this, data); + cancelCheck(child); + } + return result; } - 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 eed231c..88f4ccc 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/TemplateScript.java +++ b/src/main/java/org/apache/commons/jexl3/internal/TemplateScript.java @@ -23,6 +23,14 @@ import org.apache.commons.jexl3.internal.TemplateEngine.Block; import org.apache.commons.jexl3.internal.TemplateEngine.BlockType; import org.apache.commons.jexl3.internal.TemplateEngine.TemplateExpression; import org.apache.commons.jexl3.parser.ASTJexlScript; +import org.apache.commons.jexl3.JexlException; +import org.apache.commons.jexl3.JexlOptions; +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; + import java.io.Reader; import java.io.Writer; import java.util.ArrayList; @@ -30,12 +38,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; -import org.apache.commons.jexl3.JexlException; -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. @@ -246,9 +248,15 @@ public final class TemplateScript implements JxltEngine.Template { @Override public TemplateScript prepare(JexlContext context) { - Engine jexl = jxlt.getEngine(); + final Engine jexl = jxlt.getEngine(); + JexlOptions options = jexl.options(script, context); Frame frame = script.createFrame((Object[]) null); - Interpreter interpreter = new TemplateInterpreter(jexl, context, frame, null, null); + TemplateInterpreter.Arguments targs = new TemplateInterpreter + .Arguments(jxlt.getEngine()) + .context(context) + .options(options) + .frame(frame); + Interpreter interpreter = new TemplateInterpreter(targs); TemplateExpression[] immediates = new TemplateExpression[exprs.length]; for (int e = 0; e < exprs.length; ++e) { try { @@ -272,8 +280,17 @@ public final class TemplateScript implements JxltEngine.Template { @Override public void evaluate(JexlContext context, Writer writer, Object... args) { + final Engine jexl = jxlt.getEngine(); + JexlOptions options = jexl.options(script, context); Frame frame = script.createFrame(args); - Interpreter interpreter = new TemplateInterpreter(jxlt.getEngine(), context, frame, exprs, writer); + TemplateInterpreter.Arguments targs = new TemplateInterpreter + .Arguments(jexl) + .context(context) + .options(options) + .frame(frame) + .expressions(exprs) + .writer(writer); + Interpreter interpreter = new TemplateInterpreter(targs); interpreter.interpret(script); } diff --git a/src/site/xdoc/changes.xml b/src/site/xdoc/changes.xml index 8d80239..83c6b23 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-328"> + JXLT template scripts evaluation do not process pragmas + </action> <action dev="henrib" type="fix" issue="JEXL-327" due-to="David Costanzo"> map[null] does not work in assignment context </action> diff --git a/src/test/java/org/apache/commons/jexl3/FeaturesTest.java b/src/test/java/org/apache/commons/jexl3/FeaturesTest.java index 02256ad..01fbf5e 100644 --- a/src/test/java/org/apache/commons/jexl3/FeaturesTest.java +++ b/src/test/java/org/apache/commons/jexl3/FeaturesTest.java @@ -46,7 +46,7 @@ public class FeaturesTest extends JexlTestCase { JexlScript ctl = JEXL.createScript(script); Assert.assertNotNull(ctl); try { - JexlScript e = jexl.createScript(features, null, script, null); + JexlScript e = jexl.createScript(features, null, script); Assert.fail("should fail parse: " + script); } catch (JexlException.Feature xfeature) { String msg = xfeature.getMessage(); diff --git a/src/test/java/org/apache/commons/jexl3/JXLTTest.java b/src/test/java/org/apache/commons/jexl3/JXLTTest.java index 95d1dd3..9f91d3c 100644 --- a/src/test/java/org/apache/commons/jexl3/JXLTTest.java +++ b/src/test/java/org/apache/commons/jexl3/JXLTTest.java @@ -17,7 +17,6 @@ package org.apache.commons.jexl3; import org.apache.commons.jexl3.internal.Debugger; -import org.apache.commons.jexl3.internal.Engine; import org.apache.commons.jexl3.internal.TemplateDebugger; import org.apache.commons.jexl3.internal.TemplateScript; import org.apache.commons.logging.Log; @@ -28,7 +27,6 @@ import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; import java.util.Arrays; -import java.util.Collection; import java.util.List; import java.util.Set; @@ -95,8 +93,7 @@ public class JXLTTest extends JexlTestCase { if (options.isLexicalShade()) { return true; } - options = new JexlOptions(); - ((Engine) ENGINE).optionsSet(options); + options = new JexlOptions().set(ENGINE); return options.isLexicalShade(); } @@ -161,12 +158,14 @@ public class JXLTTest extends JexlTestCase { @Test public void testAssign() throws Exception { - JxltEngine.Expression assign = JXLT.createExpression("${froboz.value = 10}"); + Froboz froboz = new Froboz(32); + context.set("froboz", froboz); + JxltEngine.Expression assign = JXLT.createExpression("${froboz.value = 42}"); JxltEngine.Expression check = JXLT.createExpression("${froboz.value}"); Object o = assign.evaluate(context); - Assert.assertEquals("Result is not 10", new Integer(10), o); + Assert.assertEquals("Result is not 10", new Integer(42), o); o = check.evaluate(context); - Assert.assertEquals("Result is not 10", new Integer(10), o); + Assert.assertEquals("Result is not 10", new Integer(42), o); } @Test @@ -1001,5 +1000,90 @@ public class JXLTTest extends JexlTestCase { output = strw.toString(); Assert.assertEquals(s315, output); } + + // define mode pro50 + static final JexlOptions MODE_PRO50 = new JexlOptions(); + static { + MODE_PRO50.setFlags( "+strict +cancellable +lexical +lexicalShade -safe".split(" ")); + } + + public static class PragmaticContext extends MapContext implements JexlContext.PragmaProcessor, JexlContext.OptionsHandle { + private final JexlOptions options; + + public PragmaticContext(JexlOptions o) { + this.options = o; + } + + @Override + public void processPragma(String key, Object value) { + if ("script.mode".equals(key) && "pro50".equals(value)) { + options.set(MODE_PRO50); + } + } + + @Override + public Object get(String name) { + if ("$options".equals(name)) { + return options; + } + return super.get(name); + } + + @Override + public JexlOptions getEngineOptions() { + return options; + } + } + @Test + public void testLexicalTemplate() throws Exception { + JexlOptions opts = new JexlOptions(); + JexlContext ctxt = new PragmaticContext(opts); + opts.setCancellable(false); + opts.setStrict(false); + opts.setSafe(true); + opts.setLexical(false); + opts.setLexicalShade(false); + String src0 = "${$options.strict?'+':'-'}strict" + + " ${$options.cancellable?'+':'-'}cancellable" + + " ${$options.lexical?'+':'-'}lexical" + + " ${$options.lexicalShade?'+':'-'}lexicalShade" + + " ${$options.safe?'+':'-'}safe"; + + JxltEngine.Template tmplt0 = JXLT.createTemplate("$$", new StringReader(src0)); + Writer strw0 = new StringWriter(); + tmplt0.evaluate(ctxt, strw0); + String output0 = strw0.toString(); + Assert.assertEquals( "-strict -cancellable -lexical -lexicalShade +safe", output0); + + String src = "$$ #pragma script.mode pro50\n" + + "${$options.strict?'+':'-'}strict" + + " ${$options.cancellable?'+':'-'}cancellable" + + " ${$options.lexical?'+':'-'}lexical" + + " ${$options.lexicalShade?'+':'-'}lexicalShade" + + " ${$options.safe?'+':'-'}safe"; + + JxltEngine.Template tmplt = JXLT.createTemplate("$$", new StringReader(src)); + Writer strw = new StringWriter(); + tmplt.evaluate(ctxt, strw); + String output = strw.toString(); + Assert.assertEquals("+strict +cancellable +lexical +lexicalShade -safe", output); + } + @Test + public void testTemplatePragmaPro50() throws Exception { + JexlOptions opts = new JexlOptions(); + JexlContext ctxt = new PragmaticContext(opts); + String src = "$$ #pragma script.mode pro50\n" + + "$$ var tab = null;\n" + + "$$ tab.dummy();"; + JxltEngine.Template tmplt = JXLT.createTemplate("$$", new StringReader(src)); + Writer strw = new StringWriter(); + try { + tmplt.evaluate(ctxt, strw); + Assert.fail("tab var is null"); + } catch (JexlException.Variable xvar) { + Assert.assertTrue("tab".equals(xvar.getVariable())); + Assert.assertFalse(xvar.isUndefined()); + } + } }