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 b640ba68 JEXL: last updates before 3.4; - !==, === operators; - instanceof, !instanceof operator; - Exception cause unwrap in catch; - JEXL-235 check; b640ba68 is described below commit b640ba6820eb07ffc23043f118a3497f64339df5 Author: Henri Biestro <hbies...@cloudera.com> AuthorDate: Fri May 24 11:55:43 2024 +0200 JEXL: last updates before 3.4; - !==, === operators; - instanceof, !instanceof operator; - Exception cause unwrap in catch; - JEXL-235 check; --- RELEASE-NOTES.txt | 22 ++++--- pom.xml | 2 +- src/changes/changes.xml | 9 ++- .../org/apache/commons/jexl3/JexlArithmetic.java | 22 +++++++ .../org/apache/commons/jexl3/JexlFeatures.java | 5 +- .../org/apache/commons/jexl3/JexlOperator.java | 8 +++ .../apache/commons/jexl3/internal/Debugger.java | 22 ++++++- .../apache/commons/jexl3/internal/Interpreter.java | 71 +++++++++++++++++++++- .../commons/jexl3/internal/ScriptVisitor.java | 18 +++++- .../org/apache/commons/jexl3/internal/Source.java | 7 ++- .../commons/jexl3/parser/OperatorController.java | 10 +++ .../org/apache/commons/jexl3/parser/Parser.jjt | 35 ++++++++--- .../apache/commons/jexl3/parser/ParserVisitor.java | 8 +++ .../jexl3/scripting/JexlScriptEngineFactory.java | 4 +- src/site/site.xml | 2 +- .../org/apache/commons/jexl3/ArithmeticTest.java | 25 +++++++- .../apache/commons/jexl3/TryCatchFinallyTest.java | 7 +++ .../jexl3/scripting/JexlScriptEngineTest.java | 4 +- src/test/scripts/httpPost.jexl | 2 +- 19 files changed, 250 insertions(+), 33 deletions(-) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index dfae9e13..2492472d 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -1,6 +1,6 @@ Apache Commons JEXL - Version 3.3 + Version 3.4 Release Notes @@ -19,16 +19,24 @@ Its goal is to expose scripting features usable by technical operatives or consu https://commons.apache.org/jexl/ ======================================================================================================================== -Release 3.3.1 +Release 3.4 ======================================================================================================================== -Version 3.3.1 is a maintenance release. +Version 3.4 is a minor release. Compatibility with previous releases ==================================== -Version 3.3.1 is source and binary compatible with 3.3. +Version 3.4 is source and binary compatible with 3.3. -New Features in 3.3.1: + +What's new in 3.4: +================== +Features and permissions are easier to define through new methods. +Some new syntaxes are introduced (thus the new minor); try/catch/finally (including with resources), +an array-access safe navigation ((x?[y]), strict equality/inequality operators (===, !==) and +more permissive structured literals. See the JIRA tickets for more details. + +New Features in 3.4: ==================== * JEXL-421: ArrayBuilder: array type should reflect common class of its entries * JEXL-419: Add permission syntax to allow class/method/field @@ -38,8 +46,8 @@ New Features in 3.3.1: * JEXL-401: Captured variables should be read-only * JEXL-398: Allow 'trailing commas' or ellipsis while defining array, map and set literals -Bugs Fixed in 3.3.1: -=================== +Bugs Fixed in 3.4: +================== * JEXL-420: Error while comparing float and string value * JEXL-417: JexlArithmetic looses precision during arithmetic operator execution * JEXL-416: Null-valued pragma throws NPE in 3.3 diff --git a/pom.xml b/pom.xml index e1fca938..f86e5ad3 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ <commons.jacoco.haltOnFailure>true</commons.jacoco.haltOnFailure> <commons.jacoco.classRatio>0.96</commons.jacoco.classRatio> <commons.jacoco.instructionRatio>0.89</commons.jacoco.instructionRatio> - <commons.jacoco.methodRatio>0.90</commons.jacoco.methodRatio> + <commons.jacoco.methodRatio>0.89</commons.jacoco.methodRatio> <commons.jacoco.branchRatio>0.80</commons.jacoco.branchRatio> <commons.jacoco.lineRatio>0.89</commons.jacoco.lineRatio> <commons.jacoco.complexityRatio>0.75</commons.jacoco.complexityRatio> diff --git a/src/changes/changes.xml b/src/changes/changes.xml index e0de8faa..b6d65c71 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -27,7 +27,7 @@ <author email="d...@commons.apache.org">Commons Developers</author> </properties> <body> - <release version="3.4.0" date="YYYY-MM-DD"> + <release version="3.4" date="YYYY-MM-DD"> <!-- ADD --> <action dev="henrib" type="add" issue="JEXL-421"> ArrayBuilder: array type should reflect common class of its entries @@ -50,7 +50,9 @@ <action dev="henrib" type="add" issue="JEXL-398" due-to="Xu Pengcheng"> Allow 'trailing commas' or ellipsis while defining array, map and set literals </action> - <action type="add" dev="ggregory" due-to="Gary Gregory">Add Maven property project.build.outputTimestamp for build reproducibility.</action> + <action type="add" dev="ggregory" due-to="Gary Gregory"> + Add Maven property project.build.outputTimestamp for build reproducibility. + </action> <!-- FIX --> <action dev="henrib" type="fix" issue="JEXL-420" due-to="Xu Pengcheng"> Error while comparing float and string value @@ -98,6 +100,9 @@ Deprecate JexlNode.JexlNode(Parser, int) in favor of JexlNode.JexlNode(int). </action> <!-- UPDATE --> + <action dev="henrib" type="update" due-to="dependabot"> + Bump commons-parent from 67 to 69. + </action> <action dev="henrib" type="update" due-to="dependabot"> Bump github actions. </action> diff --git a/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java b/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java index cd74350c..cd499a19 100644 --- a/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java +++ b/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java @@ -828,6 +828,7 @@ public class JexlArithmetic { if (operator != null) { switch (operator) { case EQ: + case EQSTRICT: case ARRAY_GET: case ARRAY_SET: case PROPERTY_GET: @@ -2070,6 +2071,27 @@ public class JexlArithmetic { return compare(left, right, EQ) == 0; } + /** + * Test if left and right are strictly equal. + * <p>They must have the same class, comparable and the comparison returns 0.</p> + * + * @param left left argument + * @param right right argument + * @return the test result + */ + public boolean strictEquals(final Object left, final Object right) { + if (left == right) { + return true; + } + if (left == null || right == null) { + return false; + } + if (left.getClass().equals(right.getClass()) && left instanceof Comparable<?>) { + return ((Comparable) left).compareTo((Comparable) right) == 0; + } + return false; + } + /** * Test if left < right. * diff --git a/src/main/java/org/apache/commons/jexl3/JexlFeatures.java b/src/main/java/org/apache/commons/jexl3/JexlFeatures.java index be45f9b5..309ea329 100644 --- a/src/main/java/org/apache/commons/jexl3/JexlFeatures.java +++ b/src/main/java/org/apache/commons/jexl3/JexlFeatures.java @@ -233,7 +233,7 @@ public final class JexlFeatures { private static final Set<String> RESERVED_WORDS = Collections.unmodifiableSet( new HashSet<>((Arrays.asList( - "switch", "case", "default", "class", "instanceof", "jexl", "$jexl")))); + "switch", "case", "default", "class", "jexl", "$jexl")))); /** * The modern scripting features set. @@ -309,6 +309,9 @@ public final class JexlFeatures { if (this.flags != other.flags) { return false; } + if (this.nameSpaces != other.nameSpaces) { + return false; + } if (!Objects.equals(this.reservedNames, other.reservedNames)) { return false; } diff --git a/src/main/java/org/apache/commons/jexl3/JexlOperator.java b/src/main/java/org/apache/commons/jexl3/JexlOperator.java index 905f92a9..5861200e 100644 --- a/src/main/java/org/apache/commons/jexl3/JexlOperator.java +++ b/src/main/java/org/apache/commons/jexl3/JexlOperator.java @@ -144,6 +144,14 @@ public enum JexlOperator { */ EQ("==", "equals", 2), + /** + * Equal-strict operator. + * <br><strong>Syntax:</strong> <code>x === y</code> + * <br><strong>Method:</strong> <code>boolean strictEquals(L x, R y);</code>. + * @see JexlArithmetic#strictEquals(Object, Object) + */ + EQSTRICT("===", "strictEquals", 2), + /** * Less-than operator. * <br><strong>Syntax:</strong> <code>x < y</code> diff --git a/src/main/java/org/apache/commons/jexl3/internal/Debugger.java b/src/main/java/org/apache/commons/jexl3/internal/Debugger.java index 2ebf8899..f5a530c7 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/Debugger.java +++ b/src/main/java/org/apache/commons/jexl3/internal/Debugger.java @@ -615,6 +615,26 @@ public class Debugger extends ParserVisitor implements JexlInfo.Detail { return infixChildren(node, " == ", false, data); } + @Override + protected Object visit(final ASTEQSNode node, final Object data) { + return infixChildren(node, " === ", false, data); + } + + @Override + protected Object visit(final ASTNESNode node, final Object data) { + return infixChildren(node, " !== ", false, data); + } + + @Override + protected Object visit(final ASTInstanceOf node, final Object data) { + return infixChildren(node, " instanceof ", false, data); + } + + @Override + protected Object visit(final ASTNotInstanceOf node, final Object data) { + return infixChildren(node, " !instanceof ", false, data); + } + @Override protected Object visit(final ASTERNode node, final Object data) { return infixChildren(node, " =~ ", false, data); @@ -731,7 +751,7 @@ public class Debugger extends ParserVisitor implements JexlInfo.Detail { if (node.hasFinallyClause()) { builder.append(indent > 0? lf : ' '); builder.append("finally "); - accept(node.jjtGetChild(nc++), data); + accept(node.jjtGetChild(nc), data); } return data; } 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 55bc6e64..4cbfa331 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java +++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java @@ -315,6 +315,18 @@ public class Interpreter extends InterpreterBase { } } + @Override + protected Object visit(final ASTEQSNode node, final Object data) { + final Object left = node.jjtGetChild(0).jjtAccept(this, data); + final Object right = node.jjtGetChild(1).jjtAccept(this, data); + try { + final Object result = operators.tryOverload(node, JexlOperator.EQSTRICT, left, right); + return result != JexlEngine.TRY_FAILED ? result : arithmetic.strictEquals(left, right); + } catch (final ArithmeticException xrt) { + throw new JexlException(findNullOperand(node, left, right), "=== error", xrt); + } + } + @Override protected Object visit(final ASTNENode node, final Object data) { final Object left = node.jjtGetChild(0).jjtAccept(this, data); @@ -329,6 +341,20 @@ public class Interpreter extends InterpreterBase { } } + @Override + protected Object visit(final ASTNESNode node, final Object data) { + final Object left = node.jjtGetChild(0).jjtAccept(this, data); + final Object right = node.jjtGetChild(1).jjtAccept(this, data); + try { + final Object result = operators.tryOverload(node, JexlOperator.EQSTRICT, left, right); + return result != JexlEngine.TRY_FAILED + ? !arithmetic.toBoolean(result) + : !arithmetic.strictEquals(left, right); + } catch (final ArithmeticException xrt) { + throw new JexlException(findNullOperand(node, left, right), "!== error", xrt); + } + } + @Override protected Object visit(final ASTGENode node, final Object data) { final Object left = node.jjtGetChild(0).jjtAccept(this, data); @@ -524,6 +550,36 @@ public class Interpreter extends InterpreterBase { return arithmetic.testPredicate(predicate != JexlEngine.TRY_FAILED? predicate : condition); } + @Override + protected Object visit(final ASTInstanceOf node, final Object data) { + final Object left = node.jjtGetChild(0).jjtAccept(this, data); + final Object right = node.jjtGetChild(1).jjtAccept(this, data); + return isInstance(left, right); + } + @Override + + protected Object visit(final ASTNotInstanceOf node, final Object data) { + final Object left = node.jjtGetChild(0).jjtAccept(this, data); + final Object right = node.jjtGetChild(1).jjtAccept(this, data); + return !isInstance(left, right); + } + + /** + * Determines if the specified Object is assignment-compatible with the object represented by the Class. + * @param object the Object + * @param clazz the Class + * @return the result of isInstance call + */ + private boolean isInstance(final Object object, final Object clazz) { + if (object == null || clazz == null) { + return false; + } + Class<?> c = clazz instanceof Class<?> + ? (Class<?>) clazz + : uberspect.getClassByName(resolveClassName(clazz.toString())); + return c != null && c.isInstance(object); + } + @Override protected Object visit(final ASTIfStatement node, final Object data) { final int n = 0; @@ -541,7 +597,7 @@ public class Interpreter extends InterpreterBase { } // if odd... if ((numChildren & 1) == 1) { - // if there is an else, there are an odd number of children in the statement and it is the last child, + // If there is an else, it is the last child of an odd number of children in the statement, // execute it. result = node.jjtGetChild(numChildren - 1).jjtAccept(this, null); } @@ -751,7 +807,8 @@ public class Interpreter extends InterpreterBase { if (symbol < 0) { setContextVariable(catchVar.jjtGetParent(), catchVariable.getName(), caught); } else { - frame.set(symbol, caught); + Throwable cause = caught.getCause(); + frame.set(symbol, cause == null? caught : cause); } try { // evaluate body @@ -1275,7 +1332,15 @@ public class Interpreter extends InterpreterBase { @Override protected Object visit(final ASTQualifiedIdentifier node, final Object data) { - final String name = node.getName(); + return resolveClassName(node.getName()); + } + + /** + * Resolves a class name. + * @param name the simple class name + * @return the fully qualified class name or the name + */ + private String resolveClassName(final String name) { // try with local solver String fqcn = fqcnSolver.resolveClassName(name); if (fqcn != null) { diff --git a/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java b/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java index 8ccd9261..7869ad50 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java +++ b/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java @@ -115,12 +115,18 @@ public class ScriptVisitor extends ParserVisitor { protected Object visit(final ASTThrowStatement node, final Object data) { return visitNode(node, data); } - @Override + @Override protected Object visit(final ASTReturnStatement node, final Object data) { return visitNode(node, data); } + @Override + protected Object visit(final ASTInstanceOf node, final Object data) { return visitNode(node, data); } + + @Override + protected Object visit(final ASTNotInstanceOf node, final Object data) { return visitNode(node, data); } + @Override protected Object visit(final ASTAssignment node, final Object data) { return visitNode(node, data); @@ -195,12 +201,20 @@ public class ScriptVisitor extends ParserVisitor { protected Object visit(final ASTEQNode node, final Object data) { return visitNode(node, data); } - + @Override + protected Object visit(final ASTEQSNode node, final Object data) { + return visitNode(node, data); + } @Override protected Object visit(final ASTNENode node, final Object data) { return visitNode(node, data); } + @Override + protected Object visit(final ASTNESNode node, final Object data) { + return visitNode(node, data); + } + @Override protected Object visit(final ASTLTNode node, final Object data) { return visitNode(node, data); diff --git a/src/main/java/org/apache/commons/jexl3/internal/Source.java b/src/main/java/org/apache/commons/jexl3/internal/Source.java index afe17571..255435b5 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/Source.java +++ b/src/main/java/org/apache/commons/jexl3/internal/Source.java @@ -25,7 +25,7 @@ import org.apache.commons.jexl3.JexlFeatures; * <p>This is meant for caching scripts using their 'source' as key but still distinguishing * scripts with different features and prevent false sharing. */ -public final class Source { +public final class Source implements Comparable<Source> { /** The hash code, pre-computed for fast op. */ private final int hashCode; /** The set of features. */ @@ -59,6 +59,11 @@ public final class Source { return hashCode; } + @Override + public int compareTo(Source s) { + return str.compareTo(s.str); + } + @Override public boolean equals(final Object obj) { if (this == obj) { diff --git a/src/main/java/org/apache/commons/jexl3/parser/OperatorController.java b/src/main/java/org/apache/commons/jexl3/parser/OperatorController.java index b9781b13..cd85d9f7 100644 --- a/src/main/java/org/apache/commons/jexl3/parser/OperatorController.java +++ b/src/main/java/org/apache/commons/jexl3/parser/OperatorController.java @@ -142,11 +142,21 @@ final class OperatorController extends ScriptVisitor { return JexlOperator.EQ; } + @Override + protected JexlOperator visit(final ASTEQSNode node, final Object data) { + return JexlOperator.EQSTRICT; + } + @Override protected JexlOperator visit(final ASTNENode node, final Object data) { return JexlOperator.EQ; } + @Override + protected JexlOperator visit(final ASTNESNode node, final Object data) { + return JexlOperator.EQSTRICT; + } + @Override protected JexlOperator visit(final ASTGTNode node, final Object data) { return JexlOperator.GT; 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 93e22d9e..2aa71f9c 100644 --- a/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt +++ b/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt @@ -102,6 +102,19 @@ TOKEN_MGR_DECLS : { | <"//" (~["\n","\r"])* ("\n" | "\r" | "\r\n")? > } +<NEVER> TOKEN : /* Exception handling. */ +{ + < THROW : "throw" > + | < TRY : "try" > + | < CATCH : "catch" > + | < FINALLY : "finally" > +} + +<NEVER> TOKEN : /* Type check. */ +{ + < ISA : "instanceof" > +} + <*> TOKEN : { /* SEPARATORS */ < LPAREN : "("> | < RPAREN : ")"> @@ -124,6 +137,7 @@ TOKEN_MGR_DECLS : { | < NULLP : "??" > | < AND : "&&" > | < OR : "||" > + | < NISA : "!instanceof"> } <DEFAULT> TOKEN : { /* CONDITIONALS */ @@ -144,6 +158,8 @@ TOKEN_MGR_DECLS : { | < eeq : "=$" > // ends equal | < sne : "!^" > // start not equal | < ene : "!$" > // ends not equal + | < eqstrict : "===" > // strict equal + | < neqstrict : "!==" > // strict not equal } <NEVER> TOKEN : { /* COMPARISONS */ @@ -223,13 +239,6 @@ TOKEN_MGR_DECLS : { | < FALSE : "false" > } -<NEVER> TOKEN : /* Exception handling. */ -{ - < THROW : "throw" > - | < TRY : "try" > - | < CATCH : "catch" > - | < FINALLY : "finally" > -} /*************************************** * Identifier & String tokens @@ -264,12 +273,14 @@ TOKEN_MGR_DECLS : { case "gt" : matchedToken.kind = GT; break; case "ge" : matchedToken.kind = GE; break; } - } else if (jexl331 && length >= 3 && length <= 7) { + } else if (jexl331 && length >= 3 && length <= 10) { switch (matchedToken.image) { case "try" : matchedToken.kind = TRY; break; case "catch" : matchedToken.kind = CATCH; break; case "finally" : matchedToken.kind = FINALLY; break; case "throw" : matchedToken.kind = THROW; break; + case "instanceof": matchedToken.kind = ISA; break; + case "!instanceof": matchedToken.kind = NISA; break; } } } @@ -700,6 +711,10 @@ void EqualityExpression() #void : {} (<eq> | <EQ>) RelationalExpression() #EQNode(2) | (<ne> | <NE>) RelationalExpression() #NENode(2) + | + (<eqstrict>) RelationalExpression() #EQSNode(2) + | + (<neqstrict>) RelationalExpression() #NESNode(2) | <range> RelationalExpression() #RangeNode(2) // range ) )? @@ -728,6 +743,10 @@ void RelationalExpression() #void : {} <eeq> ShiftExpression() #EWNode(2) // ends with | <ene> ShiftExpression() #NEWNode(2) // not ends with + | + <ISA> ShiftExpression() #InstanceOf(2) // instanceof + | + <NISA> ShiftExpression() #NotInstanceOf(2) // not instanceof ) )? } diff --git a/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java b/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java index 0beea51c..675e03fc 100644 --- a/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java +++ b/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java @@ -64,6 +64,10 @@ public abstract class ParserVisitor { protected abstract Object visit(ASTReturnStatement node, Object data); + protected abstract Object visit(final ASTInstanceOf node, final Object data); + + protected abstract Object visit(final ASTNotInstanceOf node, final Object data); + protected abstract Object visit(ASTAssignment node, Object data); protected abstract Object visit(ASTVar node, Object data); @@ -94,8 +98,12 @@ public abstract class ParserVisitor { protected abstract Object visit(ASTEQNode node, Object data); + protected abstract Object visit(ASTEQSNode node, Object data); + protected abstract Object visit(ASTNENode node, Object data); + protected abstract Object visit(ASTNESNode node, Object data); + protected abstract Object visit(ASTLTNode node, Object data); protected abstract Object visit(ASTGTNode node, Object data); diff --git a/src/main/java/org/apache/commons/jexl3/scripting/JexlScriptEngineFactory.java b/src/main/java/org/apache/commons/jexl3/scripting/JexlScriptEngineFactory.java index debce368..72e08938 100644 --- a/src/main/java/org/apache/commons/jexl3/scripting/JexlScriptEngineFactory.java +++ b/src/main/java/org/apache/commons/jexl3/scripting/JexlScriptEngineFactory.java @@ -50,7 +50,7 @@ public class JexlScriptEngineFactory implements ScriptEngineFactory { @Override public String getEngineVersion() { - return "3.3"; // ensure this is updated if function changes are made to this class + return "3.4"; // ensure this is updated if function changes are made to this class } @Override @@ -60,7 +60,7 @@ public class JexlScriptEngineFactory implements ScriptEngineFactory { @Override public String getLanguageVersion() { - return "3.3"; // this should be derived from the actual version + return "3.4"; // this should be derived from the actual version } @Override diff --git a/src/site/site.xml b/src/site/site.xml index d17a2f96..fe28e10c 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -26,7 +26,7 @@ <menu name="JEXL"> <item name="Overview" href="index.html" /> <item name="Release Notes" href="relnotes33.html"/> - <item name="Javadoc 3.3" href="apidocs/index.html"/> + <item name="Javadoc 3.4" href="apidocs/index.html"/> <item name="Javadoc 2.1.1" href="javadocs/apidocs-2.1.1/index.html"/> <item name="Javadoc 1.1" href="javadocs/apidocs-1.1/index.html"/> <item name="Download" href="download_jexl.cgi"/> diff --git a/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java b/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java index 029d25ed..473cd2a8 100644 --- a/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java +++ b/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java @@ -1045,6 +1045,8 @@ public class ArithmeticTest extends JexlTestCase { final Object[] EXPRESSIONS = { // Basic compare "1 == 1", true, + "1 === 1", true, + "1.d === 1", false, "1 != 1", false, "1 != 2", true, "1 > 2", false, @@ -1058,11 +1060,13 @@ public class ArithmeticTest extends JexlTestCase { "1.1 < 2", true, // Big Decimal <-> Big Integer Coercion "1.0b == 1h", true, + "1.0b !== 1h", true, "1h == 1.0b", true, "1.1b != 1h", true, "1.1b < 2h", true, // Mix all type of numbers "1l == 1.0", true, // long and int + "1l !== 1.0", true, // long and int "1.0d == 1.0f", true, // double and float "1l == 1.0b", true, "1l == 1h", true, @@ -1183,7 +1187,7 @@ public class ArithmeticTest extends JexlTestCase { try { final JexlExpression zexpr = jexl.createExpression(expr); final Object nan = zexpr.evaluate(context); - // check we have a zero & incremement zero count + // check we have a zero & increment zero count if (nan instanceof Number) { final double zero = ((Number) nan).doubleValue(); if (zero == 0.0) { @@ -1930,6 +1934,25 @@ public class ArithmeticTest extends JexlTestCase { asserter.failExpression("objects[1].status", ".*variable 'objects' is undefined.*"); } + @Test + public void testInstanceOf() throws Exception { + final JexlEngine jexl = new JexlBuilder().strict(true).safe(false).imports("java.lang").create(); + Object r = jexl.createExpression("3.0 instanceof 'Double'").evaluate(null); + Assert.assertTrue((Boolean) r); + r = jexl.createExpression("'3.0' !instanceof 'Double'").evaluate(null); + Assert.assertTrue((Boolean) r); + JexlScript script = jexl.createScript("x instanceof y", "x", "y"); + r = script.execute(null, "foo", String.class); + Assert.assertTrue((Boolean) r); + r = script.execute(null, 42.0, Double.class); + Assert.assertTrue((Boolean) r); + script = jexl.createScript("x !instanceof y", "x", "y"); + r = script.execute(null, "foo", Double.class); + Assert.assertTrue((Boolean) r); + r = script.execute(null, 42.0, String.class); + Assert.assertTrue((Boolean) r); + } + /** * Inspired by JEXL-16{1,2}. */ diff --git a/src/test/java/org/apache/commons/jexl3/TryCatchFinallyTest.java b/src/test/java/org/apache/commons/jexl3/TryCatchFinallyTest.java index 89df8dcd..ce9f5323 100644 --- a/src/test/java/org/apache/commons/jexl3/TryCatchFinallyTest.java +++ b/src/test/java/org/apache/commons/jexl3/TryCatchFinallyTest.java @@ -248,4 +248,11 @@ public class TryCatchFinallyTest extends JexlTestCase { } } + @Test + public void testExceptionType() throws Exception { + JexlScript e = JEXL.createScript("try { 'asb'.getBytes('NoSuchCharacterSet'); } catch (let ex) { ex }"); + JexlContext jc = new MapContext(); + Object o = e.execute(jc); + Assert.assertTrue(o instanceof java.io.UnsupportedEncodingException); + } } diff --git a/src/test/java/org/apache/commons/jexl3/scripting/JexlScriptEngineTest.java b/src/test/java/org/apache/commons/jexl3/scripting/JexlScriptEngineTest.java index 26975c2d..65357afa 100644 --- a/src/test/java/org/apache/commons/jexl3/scripting/JexlScriptEngineTest.java +++ b/src/test/java/org/apache/commons/jexl3/scripting/JexlScriptEngineTest.java @@ -221,9 +221,9 @@ public class JexlScriptEngineTest { public void testScriptEngineFactory() throws Exception { final JexlScriptEngineFactory factory = new JexlScriptEngineFactory(); Assert.assertEquals("JEXL Engine", factory.getParameter(ScriptEngine.ENGINE)); - Assert.assertEquals("3.3", factory.getParameter(ScriptEngine.ENGINE_VERSION)); + Assert.assertEquals("3.4", factory.getParameter(ScriptEngine.ENGINE_VERSION)); Assert.assertEquals("JEXL", factory.getParameter(ScriptEngine.LANGUAGE)); - Assert.assertEquals("3.3", factory.getParameter(ScriptEngine.LANGUAGE_VERSION)); + Assert.assertEquals("3.4", factory.getParameter(ScriptEngine.LANGUAGE_VERSION)); Assert.assertNull(factory.getParameter("THREADING")); Assert.assertEquals(NAMES, factory.getParameter(ScriptEngine.NAME)); Assert.assertEquals(EXTENSIONS, factory.getExtensions()); diff --git a/src/test/scripts/httpPost.jexl b/src/test/scripts/httpPost.jexl index 083e4ada..322ef2e4 100644 --- a/src/test/scripts/httpPost.jexl +++ b/src/test/scripts/httpPost.jexl @@ -38,7 +38,7 @@ // read response let responseCode = con.getResponseCode(); let inputStream = con.getInputStream(); - let response = new StringBuffer(); + let response = new StringBuilder(); if (inputStream != null) { let in = new BufferedReader(new InputStreamReader(inputStream)); var inputLine = "";