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 c27b0f5e JEXL: code cleanup; - remove final parameters from method
declarations (no body); - set/get attributes caching was broken; - try and
please PMD and spotbugs;
c27b0f5e is described below
commit c27b0f5e0760ed0a5c4a7715ff090f006f340eb2
Author: Henrib <[email protected]>
AuthorDate: Mon Feb 16 18:28:56 2026 +0100
JEXL: code cleanup;
- remove final parameters from method declarations (no body);
- set/get attributes caching was broken;
- try and please PMD and spotbugs;
---
RELEASE-NOTES.txt | 43 +++-
src/changes/changes.xml | 1 +
src/main/config/findbugs-exclude-filter.xml | 30 ++-
src/main/config/pmd.xml | 3 +
.../org/apache/commons/jexl3/JexlArithmetic.java | 9 +-
.../org/apache/commons/jexl3/JexlOperator.java | 36 +--
.../org/apache/commons/jexl3/internal/Engine.java | 14 +-
.../org/apache/commons/jexl3/internal/Frame.java | 10 +-
.../commons/jexl3/internal/InterpreterBase.java | 67 +++---
.../commons/jexl3/internal/TemplateDebugger.java | 2 +-
.../commons/jexl3/internal/TemplateEngine.java | 254 ++++++++++-----------
.../jexl3/internal/introspection/Uberspect.java | 3 +-
.../jexl3/introspection/JexlPermissions.java | 12 +-
.../apache/commons/jexl3/parser/ParserVisitor.java | 34 +--
.../org/apache/commons/jexl3/Issues400Test.java | 1 -
.../internal/introspection/PermissionsTest.java | 40 ++++
src/test/java/org/example/Pair.java | 38 +++
17 files changed, 365 insertions(+), 232 deletions(-)
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 90ea4f06..8861c1a3 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -1,27 +1,24 @@
-Apache Commons JEXL 3.6.2 Release Notes
----------------------------------------
+Apache Commons JEXL 3.6.3 Release Notes
+=======================================
-The Apache Commons JEXL team is pleased to announce the release of Apache
Commons JEXL 3.6.2.
+The Apache Commons JEXL team is pleased to announce the release of Apache
Commons JEXL 3.6.3.
Introduction
-------------
+============
Apache Commons JEXL is a library that enables scripting features in Java
applications and frameworks..
This is a feature and maintenance release. Java 8 or later is required.
-Fixed Bugs
-----------
+Bugs Fixed in 3.6.3:
+====================
-o JEXL-455: Tokenization error with multiline expressions. Thanks to Vincent
Bussol.
-o JEXL-454: Switch NaN case not found. Thanks to Vincent Bussol.
-o JEXL-453: Finally clause is not evaluated. Thanks to Vincent Bussol.
+o JEXL-456: Change in template parser behavior.
-Changes
--------
-o Bump org.apache.commons:commons-parent from 93 to 96. Thanks to
Gary Gregory.
+Changes in 3.6.3:
+=================
Historical list of changes:
https://commons.apache.org/proper/commons-jexl/changes.html
@@ -34,6 +31,28 @@ https://commons.apache.org/proper/commons-jexl/
Download page: https://commons.apache.org/proper/commons-jexl/download_jexl.cgi
+========================================================================================================================
+Release 3.6.2
+========================================================================================================================
+
+Version 3.6.2 is a minor release.
+
+Compatibility with previous releases
+====================================
+Version 3.6.2 is source and binary compatible with 3.6.
+
+
+Bugs Fixed in 3.6.2:
+====================
+o JEXL-455: Tokenization error with multiline expressions.
+o JEXL-454: Switch NaN case not found.
+o JEXL-453: Finally clause is not evaluated.
+
+Changes in 3.6.2:
+=================
+o Bump org.apache.commons:commons-parent from 93 to 96. Thanks to
Gary Gregory.
+
+
========================================================================================================================
Release 3.6.1
========================================================================================================================
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 86823fe3..9b65913d 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -30,6 +30,7 @@
<release version="3.6.3" date="YYYY-MM-DD"
description="This is a feature and maintenance release. Java
8 or later is required.">
<!-- FIX -->
+ <action dev="henrib" type="fix" issue="JEXL-456" due-to="Vincent
Bussol">Change in template parser behavior.</action>
<!-- ADD -->
<!-- UPDATE -->
</release>
diff --git a/src/main/config/findbugs-exclude-filter.xml
b/src/main/config/findbugs-exclude-filter.xml
index d781bd17..d0005c7c 100644
--- a/src/main/config/findbugs-exclude-filter.xml
+++ b/src/main/config/findbugs-exclude-filter.xml
@@ -55,26 +55,44 @@
<Match>
<Class name="org.apache.commons.jexl3.parser.SimpleNode"/>
</Match>
+ <Match>
+ <Class name="org.apache.commons.jexl3.parser.ASTCaseStatement"/>
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.jexl3.parser.ASTSwitchStatement"/>
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.jexl3.parser.ASTIdentifierAccessJxlt"/>
+ </Match>
<Match>
<Class name="org.apache.commons.jexl3.JexlBuilder"/>
- <Bug code="EI2,EI"></Bug>
+ <Bug code="EI2,EI"/>
</Match>
<Match>
<Package name="org.apache.commons.jexl3.internal"/>
- <Bug code="EI2,EI"></Bug>
+ <Bug code="EI2,EI"/>
</Match>
<Match>
<Package name="org.apache.commons.jexl3.introspection.internal"/>
- <Bug code="EI2,EI"></Bug>
+ <Bug code="EI2,EI"/>
</Match>
<Match>
<Package name="org.apache.commons.jexl3.parser"/>
- <Bug code="EI2,EI"></Bug>
+ <Bug code="EI2,EI"/>
</Match>
<Match>
- <Class name="org.apache.commons.jexl3.parser.ASTCaseStatement"/>
+ <Bug pattern="EI_EXPOSE_REP"/>
</Match>
<Match>
- <Class name="org.apache.commons.jexl3.parser.ASTSwitchStatement"/>
+ <Bug pattern="EI_EXPOSE_REP2"/>
+ </Match>
+ <Match>
+ <Bug pattern="NP_BOOLEAN_RETURN_NULL"/>
+ </Match>
+ <Match>
+ <Bug pattern="CT_CONSTRUCTOR_THROW"/>
+ </Match>
+ <Match>
+ <Bug pattern="ES_COMPARING_STRING_WITH_EQ"/>
</Match>
</FindBugsFilter>
diff --git a/src/main/config/pmd.xml b/src/main/config/pmd.xml
index e2ecd9c3..c21230a2 100644
--- a/src/main/config/pmd.xml
+++ b/src/main/config/pmd.xml
@@ -23,6 +23,9 @@ limitations under the License.
<description>
JEXL custom rules
</description>
+ <exclude-pattern>.*/JexlContext.java</exclude-pattern>
+ <exclude-pattern>.*/JexlUberspect.java</exclude-pattern>
+ <exclude-pattern>.*/parser/.*</exclude-pattern>
<rule ref="category/java/bestpractices.xml">
<!-- Often arguments and parameters array, not semantically varargs -->
diff --git a/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
b/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
index f2d36eac..cfa3b986 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
@@ -240,6 +240,9 @@ public class JexlArithmetic {
}
} catch (final NoSuchMethodException xany) {
arithmeticClass = arithmeticClass.getSuperclass();
+ } catch (final SecurityException xany) {
+ // ignore
+ break;
}
}
return false;
@@ -1624,7 +1627,7 @@ public class JexlArithmetic {
@Deprecated
public JexlArithmetic options(final JexlEngine.Options options) {
if (options != null) {
- final boolean isstrict = Boolean.TRUE ==
options.isStrictArithmetic() || isStrict();
+ final boolean strict =
Boolean.TRUE.equals(options.isStrictArithmetic()) || isStrict();
MathContext bigdContext = options.getArithmeticMathContext();
if (bigdContext == null) {
bigdContext = getMathContext();
@@ -1633,10 +1636,10 @@ public class JexlArithmetic {
if (bigdScale == Integer.MIN_VALUE) {
bigdScale = getMathScale();
}
- if (isstrict != isStrict()
+ if (strict != isStrict()
|| bigdScale != getMathScale()
|| bigdContext != getMathContext()) {
- return createWithOptions(isstrict, bigdContext, bigdScale);
+ return createWithOptions(strict, bigdContext, bigdScale);
}
}
return this;
diff --git a/src/main/java/org/apache/commons/jexl3/JexlOperator.java
b/src/main/java/org/apache/commons/jexl3/JexlOperator.java
index bae950b5..29e66940 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlOperator.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlOperator.java
@@ -622,10 +622,10 @@ public enum JexlOperator {
* @return JexlEngine.TRY_FAILED if no operation was performed,
* the value to use as the side effect argument otherwise
*/
- Object tryAssignOverload(final JexlCache.Reference node,
- final JexlOperator operator,
- final Consumer<Object> assign,
- final Object... args);
+ Object tryAssignOverload(JexlCache.Reference node,
+ JexlOperator operator,
+ Consumer<Object> assign,
+ Object... args);
/**
* Calculate the {@code size} of various types:
@@ -637,7 +637,7 @@ public enum JexlOperator {
* @param object the object to get the size of
* @return the evaluation result
*/
- Object size(final JexlCache.Reference node, final Object object);
+ Object size(JexlCache.Reference node, Object object);
/**
* Check for emptiness of various types: Collection, Array, Map,
String, and anything that has a boolean isEmpty()
@@ -649,7 +649,7 @@ public enum JexlOperator {
* @param object the object to check the emptiness of
* @return the evaluation result
*/
- Object empty(final JexlCache.Reference node, final Object object);
+ Object empty(JexlCache.Reference node, Object object);
/**
* The 'match'/'in' operator implementation.
@@ -665,10 +665,10 @@ public enum JexlOperator {
* @param left the right operand
* @return true if left matches right, false otherwise
*/
- boolean contains(final JexlCache.Reference node,
- final JexlOperator operator,
- final Object left,
- final Object right);
+ boolean contains(JexlCache.Reference node,
+ JexlOperator operator,
+ Object left,
+ Object right);
/**
* The 'startsWith' operator implementation.
@@ -680,10 +680,10 @@ public enum JexlOperator {
* @param right the right operand
* @return true if left starts with right, false otherwise
*/
- boolean startsWith(final JexlCache.Reference node,
- final JexlOperator operator,
- final Object left,
- final Object right);
+ boolean startsWith(JexlCache.Reference node,
+ JexlOperator operator,
+ Object left,
+ Object right);
/**
* The 'endsWith' operator implementation.
@@ -695,9 +695,9 @@ public enum JexlOperator {
* @param right the right operand
* @return true if left ends with right, false otherwise
*/
- boolean endsWith(final JexlCache.Reference node,
- final JexlOperator operator,
- final Object left,
- final Object right);
+ boolean endsWith(JexlCache.Reference node,
+ JexlOperator operator,
+ Object left,
+ Object right);
}
}
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 f5bb1477..952094fa 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Engine.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Engine.java
@@ -360,6 +360,13 @@ public class Engine extends JexlEngine implements
JexlUberspect.ConstantResolver
* @param conf the builder
*/
public Engine(final JexlBuilder conf) {
+ // core properties:
+ final JexlUberspect uber = conf.uberspect() == null
+ ? getUberspect(conf.logger(), conf.strategy(), conf.permissions())
+ : conf.uberspect();
+ if (uber == null) {
+ throw new IllegalArgumentException("uberspect cannot be null");
+ }
// options:
this.options = conf.options().copy();
this.strict = options.isStrict();
@@ -370,13 +377,6 @@ public class Engine extends JexlEngine implements
JexlUberspect.ConstantResolver
this.debug = option(conf.debug(), true);
this.collectMode = conf.collectMode();
this.stackOverflow = conf.stackOverflow() > 0? conf.stackOverflow() :
Integer.MAX_VALUE;
- // core properties:
- final JexlUberspect uber = conf.uberspect() == null
- ? getUberspect(conf.logger(), conf.strategy(),
conf.permissions())
- : conf.uberspect();
- if (uber == null) {
- throw new IllegalArgumentException("uberspect cannot be null");
- }
final ClassLoader loader = conf.loader();
if (loader != null) {
uber.setClassLoader(loader);
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Frame.java
b/src/main/java/org/apache/commons/jexl3/internal/Frame.java
index b55090ad..dbfaf212 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Frame.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Frame.java
@@ -43,14 +43,14 @@ public class Frame {
* @param c the number of curried parameters
*/
protected Frame(final Scope s, final Object[] r, final int c) {
- scope = s;
- stack = r;
- curried = c;
- final String[] symbols = scope.getSymbols();
+ final String[] symbols = s.getSymbols();
if (symbols.length != r.length) {
throw new IllegalArgumentException("Scope and stack frame size
mismatch: "
- + symbols.length + " != " + r.length);
+ + symbols.length + " != " + r.length);
}
+ scope = s;
+ stack = r;
+ curried = c;
}
/**
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 e67dfe60..20a824bb 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java
@@ -617,24 +617,29 @@ public abstract class InterpreterBase extends
ParserVisitor {
throw new JexlException(node, "object is null");
}
cancelCheck(node);
- final JexlOperator operator = node != null && node.jjtGetParent()
instanceof ASTArrayAccess
- ? JexlOperator.ARRAY_GET : JexlOperator.PROPERTY_GET;
- final Object result = operators.tryOverload(node, operator, object,
attribute);
- if (result != JexlEngine.TRY_FAILED) {
- return result;
- }
Exception xcause = null;
try {
- // attempt to reuse last executor cached in volatile JexlNode.value
- if (node != null && cache) {
- final Object cached = node.jjtGetValue();
- if (cached instanceof JexlPropertyGet) {
- final JexlPropertyGet vg = (JexlPropertyGet) cached;
- final Object value = vg.tryInvoke(object, attribute);
- if (!vg.tryFailed(value)) {
- return value;
+ final JexlOperator operator;
+ if (node != null) {
+ // attempt to reuse last executor cached in volatile
JexlNode.value
+ if (cache) {
+ final Object cached = node.jjtGetValue();
+ if (cached instanceof JexlPropertyGet) {
+ final JexlPropertyGet vg = (JexlPropertyGet) cached;
+ final Object value = vg.tryInvoke(object, attribute);
+ if (!vg.tryFailed(value)) {
+ return value;
+ }
}
}
+ // try operator overload
+ operator = node.jjtGetParent() instanceof ASTArrayAccess ?
JexlOperator.ARRAY_GET : JexlOperator.PROPERTY_GET;
+ final Object result = operators.tryOverload(node, operator,
object, attribute);
+ if (result != JexlEngine.TRY_FAILED) {
+ return result;
+ }
+ } else {
+ operator = JexlOperator.PROPERTY_GET;
}
// resolve that property
final List<JexlUberspect.PropertyResolver> resolvers =
uberspect.getResolvers(operator, object);
@@ -985,25 +990,31 @@ public abstract class InterpreterBase extends
ParserVisitor {
*/
protected void setAttribute(final Object object, final Object attribute,
final Object value, final JexlNode node) {
cancelCheck(node);
- final JexlOperator operator = node != null && node.jjtGetParent()
instanceof ASTArrayAccess
- ? JexlOperator.ARRAY_SET :
JexlOperator.PROPERTY_SET;
- final Object result = operators.tryOverload(node, operator, object,
attribute, value);
- if (result != JexlEngine.TRY_FAILED) {
- return;
- }
Exception xcause = null;
try {
- // attempt to reuse last executor cached in volatile JexlNode.value
- if (node != null && cache) {
- final Object cached = node.jjtGetValue();
- if (cached instanceof JexlPropertySet) {
- final JexlPropertySet setter = (JexlPropertySet) cached;
- final Object eval = setter.tryInvoke(object, attribute,
value);
- if (!setter.tryFailed(eval)) {
- return;
+ final JexlOperator operator;
+ if (node != null) {
+ // attempt to reuse last executor cached in volatile
JexlNode.value
+ if (cache) {
+ final Object cached = node.jjtGetValue();
+ if (cached instanceof JexlPropertySet) {
+ final JexlPropertySet setter = (JexlPropertySet)
cached;
+ final Object eval = setter.tryInvoke(object,
attribute, value);
+ if (!setter.tryFailed(eval)) {
+ return;
+ }
}
}
+ // try operator overload
+ operator = node.jjtGetParent() instanceof ASTArrayAccess ?
JexlOperator.ARRAY_SET : JexlOperator.PROPERTY_SET;
+ final Object result = operators.tryOverload(node, operator,
object, attribute, value);
+ if (result != JexlEngine.TRY_FAILED) {
+ return;
+ }
+ } else {
+ operator = JexlOperator.PROPERTY_SET;
}
+ // resolve that property
final List<JexlUberspect.PropertyResolver> resolvers =
uberspect.getResolvers(operator, object);
JexlPropertySet vs = uberspect.getPropertySet(resolvers, object,
attribute, value);
// if we can't find an exact match, narrow the value argument and
try again
diff --git
a/src/main/java/org/apache/commons/jexl3/internal/TemplateDebugger.java
b/src/main/java/org/apache/commons/jexl3/internal/TemplateDebugger.java
index 383ae2c8..fdb660a7 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/TemplateDebugger.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/TemplateDebugger.java
@@ -317,7 +317,7 @@ public class TemplateDebugger extends Debugger {
case COMPOSITE:
r = visit((CompositeExpression) expr, data);
break;
- default:
+ default: // in case of new state in the future
r = null;
}
return r;
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 28b11a08..d3dc7e88 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java
@@ -99,7 +99,7 @@ public final class TemplateEngine extends JxltEngine {
* @param prefix the line prefix (immediate or deferred)
*/
void toString(final StringBuilder strb, final String prefix) {
- if (BlockType.VERBATIM.equals(type)) {
+ if (BlockType.VERBATIM == type) {
strb.append(body);
} else {
final Iterator<CharSequence> lines = readLines(new
StringReader(body));
@@ -997,145 +997,145 @@ public final class TemplateEngine extends JxltEngine {
for (int column = 0; column < size; ++column) {
final char c = expr.charAt(column);
switch (state) {
- case CONST:
- if (c == immediateChar) {
- state = ParseState.IMMEDIATE0;
- } else if (c == deferredChar) {
- inested = column;
- state = ParseState.DEFERRED0;
- } else if (c == '\\') {
- state = ParseState.ESCAPE;
- } else {
- // do buildup expr
- strb.append(c);
- }
- break;
- case IMMEDIATE0: // $
- if (c != '{') {
- // revert to CONST
- strb.append(immediateChar);
- state = ParseState.CONST;
- // 'unread' the current character
- column -= 1;
- continue;
- }
- state = ParseState.IMMEDIATE1;
- // if chars in buffer, create constant
- if (strb.length() > 0) {
- final TemplateExpression cexpr = new
ConstantExpression(strb.toString(), null);
- builder.add(cexpr);
- strb.delete(0, Integer.MAX_VALUE);
- }
- break;
- case DEFERRED0: // #
- if (c != '{') {
- // revert to CONST
- strb.append(deferredChar);
- state = ParseState.CONST;
- // 'unread' the current character
- column -= 1;
- continue;
- }
- state = ParseState.DEFERRED1;
- // if chars in buffer, create constant
- if (strb.length() > 0) {
- final TemplateExpression cexpr = new
ConstantExpression(strb.toString(), null);
- builder.add(cexpr);
- strb.delete(0, Integer.MAX_VALUE);
- }
- break;
- case IMMEDIATE1: // ${...
- if (c == '}') {
- if (immediate1 > 0) {
- immediate1 -= 1;
- strb.append(c);
+ case CONST:
+ if (c == immediateChar) {
+ state = ParseState.IMMEDIATE0;
+ } else if (c == deferredChar) {
+ inested = column;
+ state = ParseState.DEFERRED0;
+ } else if (c == '\\') {
+ state = ParseState.ESCAPE;
} else {
- // materialize the immediate expr
- final String src = escapeString(strb);
- final JexlInfo srcInfo = info.at(lineno, column);
- final TemplateExpression iexpr = new
ImmediateExpression(src,
- jexl.jxltParse(srcInfo, noscript, src, scope),
null);
- builder.add(iexpr);
- strb.delete(0, Integer.MAX_VALUE);
+ // do buildup expr
+ strb.append(c);
+ }
+ break;
+ case IMMEDIATE0: // $
+ if (c != '{') {
+ // revert to CONST
+ strb.append(immediateChar);
state = ParseState.CONST;
+ // 'unread' the current character
+ column -= 1;
+ continue;
}
- } else if (!isIgnorable(c)) {
- if (c == '{') {
- immediate1 += 1;
+ state = ParseState.IMMEDIATE1;
+ // if chars in buffer, create constant
+ if (strb.length() > 0) {
+ final TemplateExpression cexpr = new
ConstantExpression(strb.toString(), null);
+ builder.add(cexpr);
+ strb.delete(0, Integer.MAX_VALUE);
}
- // do buildup expr
- column = append(strb, expr, column, c);
- }
- break;
- case DEFERRED1: // #{...
- // skip inner strings - for '}' -
- // nested immediate in deferred; need to balance count of '{'
& '}'
- // closing '}'
- switch (c) {
- case '"':
- case '\'':
- strb.append(c);
- column = StringParser.readString(strb, expr, column +
1, c);
- continue;
- case '{':
- if (expr.charAt(column - 1) == immediateChar) {
- inner1 += 1;
- strb.deleteCharAt(strb.length() - 1);
- nested = true;
- } else {
- deferred1 += 1;
- strb.append(c);
- }
+ break;
+ case DEFERRED0: // #
+ if (c != '{') {
+ // revert to CONST
+ strb.append(deferredChar);
+ state = ParseState.CONST;
+ // 'unread' the current character
+ column -= 1;
continue;
- case '}':
- // balance nested immediate
- if (deferred1 > 0) {
- deferred1 -= 1;
+ }
+ state = ParseState.DEFERRED1;
+ // if chars in buffer, create constant
+ if (strb.length() > 0) {
+ final TemplateExpression cexpr = new
ConstantExpression(strb.toString(), null);
+ builder.add(cexpr);
+ strb.delete(0, Integer.MAX_VALUE);
+ }
+ break;
+ case IMMEDIATE1: // ${...
+ if (c == '}') {
+ if (immediate1 > 0) {
+ immediate1 -= 1;
strb.append(c);
- } else if (inner1 > 0) {
- inner1 -= 1;
} else {
- // materialize the nested/deferred expr
+ // materialize the immediate expr
final String src = escapeString(strb);
final JexlInfo srcInfo = info.at(lineno, column);
- TemplateExpression dexpr;
- if (nested) {
- dexpr = new NestedExpression(
- escapeString(expr.substring(inested,
column + 1)),
- jexl.jxltParse(srcInfo, noscript, src,
scope),
- scope);
- } else {
- dexpr = new DeferredExpression(
- src,
- jexl.jxltParse(srcInfo, noscript, src,
scope));
- }
- builder.add(dexpr);
+ final TemplateExpression iexpr = new
ImmediateExpression(src,
+ jexl.jxltParse(srcInfo, noscript, src, scope),
null);
+ builder.add(iexpr);
strb.delete(0, Integer.MAX_VALUE);
- nested = false;
state = ParseState.CONST;
}
- break;
- default:
- if (!isIgnorable(c)) {
- // do buildup expr
- column = append(strb, expr, column, c);
+ } else if (!isIgnorable(c)) {
+ if (c == '{') {
+ immediate1 += 1;
}
- break;
- }
- break;
- case ESCAPE:
- if (c == deferredChar) {
- strb.append(deferredChar);
- } else if (c == immediateChar) {
- strb.append(immediateChar);
- } else {
- strb.append('\\');
- strb.append(c);
- }
- state = ParseState.CONST;
- break;
- default: // in case we ever add new unified expression type
- throw new UnsupportedOperationException("unexpected unified
expression type");
+ // do buildup expr
+ column = append(strb, expr, column, c);
+ }
+ break;
+ case DEFERRED1: // #{...
+ // skip inner strings - for '}' -
+ // nested immediate in deferred; need to balance count of
'{' & '}'
+ // closing '}'
+ switch (c) {
+ case '"':
+ case '\'':
+ strb.append(c);
+ column = StringParser.readString(strb, expr,
column + 1, c);
+ continue;
+ case '{':
+ if (expr.charAt(column - 1) == immediateChar) {
+ inner1 += 1;
+ strb.deleteCharAt(strb.length() - 1);
+ nested = true;
+ } else {
+ deferred1 += 1;
+ strb.append(c);
+ }
+ continue;
+ case '}':
+ // balance nested immediate
+ if (deferred1 > 0) {
+ deferred1 -= 1;
+ strb.append(c);
+ } else if (inner1 > 0) {
+ inner1 -= 1;
+ } else {
+ // materialize the nested/deferred expr
+ final String src = escapeString(strb);
+ final JexlInfo srcInfo = info.at(lineno,
column);
+ TemplateExpression dexpr;
+ if (nested) {
+ dexpr = new NestedExpression(
+
escapeString(expr.substring(inested, column + 1)),
+ jexl.jxltParse(srcInfo, noscript,
src, scope),
+ scope);
+ } else {
+ dexpr = new DeferredExpression(
+ src,
+ jexl.jxltParse(srcInfo, noscript,
src, scope));
+ }
+ builder.add(dexpr);
+ strb.delete(0, Integer.MAX_VALUE);
+ nested = false;
+ state = ParseState.CONST;
+ }
+ break;
+ default:
+ if (!isIgnorable(c)) {
+ // do buildup expr
+ column = append(strb, expr, column, c);
+ }
+ break;
+ }
+ break;
+ case ESCAPE:
+ if (c == deferredChar) {
+ strb.append(deferredChar);
+ } else if (c == immediateChar) {
+ strb.append(immediateChar);
+ } else {
+ strb.append('\\');
+ strb.append(c);
+ }
+ state = ParseState.CONST;
+ break;
+ default: // in case we ever add new unified expression type //
NOPMD
+ throw new UnsupportedOperationException("unexpected
unified expression type");
}
if (c == '\n') {
lineno += 1;
diff --git
a/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java
b/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java
index 610120e4..d8d2dbf0 100644
---
a/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java
+++
b/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java
@@ -99,11 +99,12 @@ public class Uberspect implements JexlUberspect {
* @param perms the introspector permissions
*/
public Uberspect(final Log runtimeLogger, final
JexlUberspect.ResolverStrategy sty, final JexlPermissions perms) {
+ final ClassLoader cl = getClass().getClassLoader();
logger = runtimeLogger == null ? LogFactory.getLog(JexlEngine.class) :
runtimeLogger;
strategy = sty == null ? JexlUberspect.JEXL_STRATEGY : sty;
permissions = perms == null ? JexlPermissions.RESTRICTED : perms;
ref = new SoftReference<>(null);
- loader = new SoftReference<>(getClass().getClassLoader());
+ loader = new SoftReference<>(cl);
operatorMap = new ConcurrentHashMap<>();
version = new AtomicInteger();
}
diff --git
a/src/main/java/org/apache/commons/jexl3/introspection/JexlPermissions.java
b/src/main/java/org/apache/commons/jexl3/introspection/JexlPermissions.java
index a70424ef..d84817b5 100644
--- a/src/main/java/org/apache/commons/jexl3/introspection/JexlPermissions.java
+++ b/src/main/java/org/apache/commons/jexl3/introspection/JexlPermissions.java
@@ -339,7 +339,7 @@ public interface JexlPermissions {
* @return true if JEXL is allowed to introspect, false otherwise
* @since 3.3
*/
- boolean allow(final Class<?> clazz);
+ boolean allow(Class<?> clazz);
/**
* Checks whether a constructor allows JEXL introspection.
@@ -350,7 +350,7 @@ public interface JexlPermissions {
* @return true if JEXL is allowed to introspect, false otherwise
* @since 3.3
*/
- boolean allow(final Constructor<?> ctor);
+ boolean allow(Constructor<?> ctor);
/**
* Checks whether a field explicitly disallows JEXL introspection.
@@ -360,7 +360,7 @@ public interface JexlPermissions {
* @return true if JEXL is allowed to introspect, false otherwise
* @since 3.3
*/
- boolean allow(final Field field);
+ boolean allow(Field field);
/**
* Checks whether a method allows JEXL introspection.
@@ -372,7 +372,7 @@ public interface JexlPermissions {
* @return true if JEXL is allowed to introspect, false otherwise
* @since 3.3
*/
- boolean allow(final Method method);
+ boolean allow(Method method);
/**
* Checks whether a package allows JEXL introspection.
@@ -383,7 +383,7 @@ public interface JexlPermissions {
* @return true if JEXL is allowed to introspect, false otherwise
* @since 3.3
*/
- boolean allow(final Package pack);
+ boolean allow(Package pack);
/**
* Compose these permissions with a new set.
@@ -394,7 +394,7 @@ public interface JexlPermissions {
* @param src the new constraints
* @return the new permissions
*/
- JexlPermissions compose(final String... src);
+ JexlPermissions compose(String... src);
/**
* Checks that a class is valid for permission check.
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 44e7c497..bf089df3 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java
@@ -61,17 +61,17 @@ public abstract class ParserVisitor {
protected abstract Object visit(ASTConstructorNode node, Object data);
- protected abstract Object visit(ASTSwitchStatement node, final Object
data);
+ protected abstract Object visit(ASTSwitchStatement node, Object data);
- protected abstract Object visit(ASTCaseStatement node, final Object data);
+ protected abstract Object visit(ASTCaseStatement node, Object data);
- protected abstract Object visit(ASTSwitchExpression node, final Object
data);
+ protected abstract Object visit(ASTSwitchExpression node, Object data);
- protected abstract Object visit(ASTCaseExpression node, final Object data);
+ protected abstract Object visit(ASTCaseExpression node, Object data);
protected abstract Object visit(ASTContinue node, Object data);
- protected abstract Object visit(ASTDecrementGetNode node, final Object
data);
+ protected abstract Object visit(ASTDecrementGetNode node, Object data);
protected abstract Object visit(ASTDefineVars node, Object data);
@@ -99,9 +99,9 @@ public abstract class ParserVisitor {
protected abstract Object visit(ASTGENode node, Object data);
- protected abstract Object visit(ASTGetDecrementNode node, final Object
data);
+ protected abstract Object visit(ASTGetDecrementNode node, Object data);
- protected abstract Object visit(ASTGetIncrementNode node, final Object
data);
+ protected abstract Object visit(ASTGetIncrementNode node, Object data);
protected abstract Object visit(ASTGTNode node, Object data);
@@ -111,9 +111,9 @@ public abstract class ParserVisitor {
protected abstract Object visit(ASTIfStatement node, Object data);
- protected abstract Object visit(ASTIncrementGetNode node, final Object
data);
+ protected abstract Object visit(ASTIncrementGetNode node, Object data);
- protected abstract Object visit(final ASTInstanceOf node, final Object
data);
+ protected abstract Object visit(ASTInstanceOf node, Object data);
protected abstract Object visit(ASTJexlScript node, Object data);
@@ -139,7 +139,7 @@ public abstract class ParserVisitor {
protected abstract Object visit(ASTNEWNode node, Object data);
- protected abstract Object visit(final ASTNotInstanceOf node, final Object
data);
+ protected abstract Object visit(ASTNotInstanceOf node, Object data);
protected abstract Object visit(ASTNotNode node, Object data);
@@ -155,7 +155,7 @@ public abstract class ParserVisitor {
protected abstract Object visit(ASTOrNode node, Object data);
- protected abstract Object visit(final ASTQualifiedIdentifier node, final
Object data);
+ protected abstract Object visit(ASTQualifiedIdentifier node, Object data);
protected abstract Object visit(ASTRangeNode node, Object data);
@@ -181,21 +181,21 @@ public abstract class ParserVisitor {
protected abstract Object visit(ASTSetOrNode node, Object data);
- protected abstract Object visit(ASTSetShiftLeftNode node, final Object
data);
+ protected abstract Object visit(ASTSetShiftLeftNode node, Object data);
- protected abstract Object visit(ASTSetShiftRightNode node, final Object
data);
+ protected abstract Object visit(ASTSetShiftRightNode node, Object data);
- protected abstract Object visit(ASTSetShiftRightUnsignedNode node, final
Object data);
+ protected abstract Object visit(ASTSetShiftRightUnsignedNode node, Object
data);
protected abstract Object visit(ASTSetSubNode node, Object data);
protected abstract Object visit(ASTSetXorNode node, Object data);
- protected abstract Object visit(ASTShiftLeftNode node, final Object data);
+ protected abstract Object visit(ASTShiftLeftNode node, Object data);
- protected abstract Object visit(ASTShiftRightNode node, final Object data);
+ protected abstract Object visit(ASTShiftRightNode node, Object data);
- protected abstract Object visit(ASTShiftRightUnsignedNode node, final
Object data);
+ protected abstract Object visit(ASTShiftRightUnsignedNode node, Object
data);
protected abstract Object visit(ASTSizeFunction node, Object data);
diff --git a/src/test/java/org/apache/commons/jexl3/Issues400Test.java
b/src/test/java/org/apache/commons/jexl3/Issues400Test.java
index 280fd6d6..8b2a44ba 100644
--- a/src/test/java/org/apache/commons/jexl3/Issues400Test.java
+++ b/src/test/java/org/apache/commons/jexl3/Issues400Test.java
@@ -1157,6 +1157,5 @@ public class Issues400Test {
template.evaluate(null, writer);
Assertions.assertEquals("42", writer.toString());
}
-
}
diff --git
a/src/test/java/org/apache/commons/jexl3/internal/introspection/PermissionsTest.java
b/src/test/java/org/apache/commons/jexl3/internal/introspection/PermissionsTest.java
index 73f30511..37a8145a 100644
---
a/src/test/java/org/apache/commons/jexl3/internal/introspection/PermissionsTest.java
+++
b/src/test/java/org/apache/commons/jexl3/internal/introspection/PermissionsTest.java
@@ -29,6 +29,7 @@ import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
@@ -46,6 +47,8 @@ import org.apache.commons.jexl3.MapContext;
import org.apache.commons.jexl3.internal.introspection.nojexlpackage.Invisible;
import org.apache.commons.jexl3.introspection.JexlPermissions;
import org.apache.commons.jexl3.introspection.JexlUberspect;
+import org.example.Pair;
+import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
/**
@@ -494,4 +497,41 @@ class PermissionsTest {
found = Permissions.wildcardAllow(wildcards, "com.google.spexl");
assertFalse(found);
}
+
+ public static class Scheme {
+ public Pair cons(Object first, Object second) {
+ return new Pair(first, second);
+ }
+ }
+
+ @Test
+ void testPair0() {
+ final Map<String, Object> funcs = new HashMap<>();
+ funcs.put("lisp", new Scheme());
+ final JexlPermissions permissions =
JexlPermissions.RESTRICTED.compose("org.example.*");
+ final JexlEngine jexl = new
JexlBuilder().cache(8).permissions(permissions).namespaces(funcs).create();
+ String src = "let p = lisp:cons(17, 25); p.car + p.cdr;";
+ JexlScript script = jexl.createScript(src);
+ Assertions.assertEquals(42, script.execute(null));
+ Assertions.assertEquals(42, script.execute(null));
+ src = "(p, x, y) -> { p.car = x; p.cdr = y; }";
+ Pair p = new Pair(-1, -41);
+ script = jexl.createScript(src);
+ Assertions.assertNotNull(script.execute(null, p, 22, 20));
+ Assertions.assertEquals(22, p.car);
+ Assertions.assertEquals(20, p.cdr);
+ Assertions.assertNotNull(script.execute(null, p, 18, 24));
+ Assertions.assertEquals(18, p.car);
+ Assertions.assertEquals(24, p.cdr);
+ }
+
+ @Test
+ void testPair1() {
+ final Map<String, Object> funcs = new HashMap<>();
+ final JexlPermissions permissions =
JexlPermissions.RESTRICTED.compose("org.example.*");
+ final JexlEngine jexl = new
JexlBuilder().cache(8).permissions(permissions).namespaces(funcs).create();
+ final String src = "import org.example.Pair; new Pair(17, 25);";
+ final JexlScript script = jexl.createScript(src);
+ Assertions.assertThrows(JexlException.class, ()->
script.execute(null));
+ }
}
diff --git a/src/test/java/org/example/Pair.java
b/src/test/java/org/example/Pair.java
new file mode 100644
index 00000000..ae690b48
--- /dev/null
+++ b/src/test/java/org/example/Pair.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.example;
+
+import org.apache.commons.jexl3.annotations.NoJexl;
+
+public class Pair {
+ public Object car;
+ public Object cdr;
+
+ @NoJexl
+ public Pair(Object car, Object cdr) {
+ this.car = car;
+ this.cdr = cdr;
+ }
+
+ public static Pair cons(Number car, Number cdr) {
+ return new Pair(car, cdr);
+ }
+
+ public static Pair cons(Object car, Object cdr) {
+ return new Pair(car, cdr);
+ }
+}