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 5451c305 JEXL-410: added createAll() method to create a fully enabled 
set of JexlFeatures; - updated tests, Javadoc, release notes & changes;
5451c305 is described below

commit 5451c30530af8dc1fc145bbee9ce23df73041b9c
Author: Henri Biestro <hbies...@cloudera.com>
AuthorDate: Fri Oct 20 17:45:22 2023 +0200

    JEXL-410: added createAll() method to create a fully enabled set of 
JexlFeatures;
    - updated tests, Javadoc, release notes & changes;
---
 RELEASE-NOTES.txt                                  |   7 +-
 src/changes/changes.xml                            |   9 ++
 .../org/apache/commons/jexl3/JexlFeatures.java     | 103 ++++++++++++++-------
 .../org/apache/commons/jexl3/FeaturesTest.java     |  51 +++++++++-
 4 files changed, 132 insertions(+), 38 deletions(-)

diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 109ee9e0..b00c1a95 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -19,7 +19,7 @@ Its goal is to expose scripting features usable by technical 
operatives or consu
   https://commons.apache.org/jexl/
 
 
========================================================================================================================
-Release 3.2.1
+Release 3.3.1
 
========================================================================================================================
 
 Version 3.3.1 is a maintenance release.
@@ -30,12 +30,15 @@ Version 3.3.1 is source and binary compatible with 3.3.
 
 New Features in 3.3.1:
 ====================
+* JEXL-408:     Using JexlFeatures is tedious
 * JEXL-404:     Support array-access safe navigation (x?[y])
 * 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:
 ===================
+* JEXL-410:     JexlFeatures: ctor does not enable all features
+* JEXL-409:     Disable LEXICAL should disable LEXICAL_SHADE
 * JEXL-405:     Recursive functions corrupt evaluation frame if reassigned
 * JEXL-403:     Exception while evaluating template literal used in array 
assignment in loop.
 * JEXL-402:     parse failed with empty return value.
@@ -57,7 +60,7 @@ accessible through scripts has a real impact on your 
application security and st
 an informed review and conscious choice on your end.
 To mitigate the change, you can revert to the previous behavior with one line 
of code  (see JexlPermissions, JexlBuilder
 and JexlScriptEngine) or use this opportunity to reduce exposure. Whether 
Files, URLs, networking, processes,
-class-loaders or reflection classes or whether loops or side-effects are 
accessible are part of your choice to make.
+class-loaders or reflection classes or whether loops or side effects are 
accessible are part of your choice to make.
 
 What's new in 3.3:
 ==================
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 0916150f..97e5fa12 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -29,6 +29,9 @@
     <body>
         <release version="3.3.1" date="20YY-MM-DD">
             <!-- ADD -->
+            <action dev="henrib" type="add" issue="JEXL-408" due-to="sebb">
+                Using JexlFeatures is tedious
+            </action>
             <action dev="henrib" type="add" issue="JEXL-404" due-to="Xu 
Pengcheng">
                 Support array-access safe navigation (x?[y])
             </action>
@@ -39,6 +42,12 @@
                 Allow 'trailing commas' or ellipsis while defining array, map 
and set literals
             </action>
             <!-- FIX -->
+            <action dev="henrib" type="fix" issue="JEXL-410" due-to="sebb">
+                JexlFeatures: ctor does not enable all features
+            </action>
+            <action dev="henrib" type="fix" issue="JEXL-409" due-to="sebb">
+                Disable LEXICAL should disable LEXICAL_SHADE
+            </action>
             <action dev="henrib" type="fix" issue="JEXL-405">
                 Recursive functions corrupt evaluation frame if reassigned
             </action>
diff --git a/src/main/java/org/apache/commons/jexl3/JexlFeatures.java 
b/src/main/java/org/apache/commons/jexl3/JexlFeatures.java
index 02d3e26a..1e91af8e 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlFeatures.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlFeatures.java
@@ -16,8 +16,10 @@
  */
 package org.apache.commons.jexl3;
 
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.Objects;
 import java.util.Set;
 import java.util.TreeSet;
@@ -25,8 +27,13 @@ import java.util.function.Predicate;
 
 /**
  * A set of language feature options.
+ * <p>
  * These control <em>syntactical</em> constructs that will throw 
JexlException.Feature exceptions (a
  * subclass of JexlException.Parsing) when disabled.
+ * </p>
+ * <p>It is recommended to be explicit in choosing the features you need 
rather than rely on the default
+ * constructor: the 2 convenience methods {@link JexlFeatures#createNone()} 
and {@link JexlFeatures#createAll()}
+ * are the recommended starting points to selectively enable or disable chosen 
features.</p>
  * <ul>
  * <li>Registers: register syntax (#number), used internally for 
{g,s}etProperty
  * <li>Reserved Names: a set of reserved variable names that can not be used 
as local variable (or parameter) names
@@ -63,7 +70,7 @@ public final class JexlFeatures {
     /** The namespace names. */
     private Predicate<String> nameSpaces;
     /** The false predicate. */
-    public static final Predicate<String> TEST_STR_FALSE = s->false;
+    public static final Predicate<String> TEST_STR_FALSE = s -> false;
     /** Te feature names (for toString()). */
     private static final String[] F_NAMES = {
         "register", "reserved variable", "local variable", "assign/modify",
@@ -74,15 +81,15 @@ public final class JexlFeatures {
     };
     /** Registers feature ordinal. */
     private static final int REGISTER = 0;
-    /** Reserved name feature ordinal. */
-    public static final int RESERVED = 1; // set to indicate that the 
resevedNames collection is non-empty; not read
+    /** Reserved future feature ordinal (unused as of 3.3.1). */
+    public static final int RESERVED = 1;
     /** Locals feature ordinal. */
     public static final int LOCAL_VAR = 2;
     /** Side effects feature ordinal. */
     public static final int SIDE_EFFECT = 3;
     /** Global side effects feature ordinal. */
     public static final int SIDE_EFFECT_GLOBAL = 4;
-    /** Array get is allowed on expr. */
+    /** Expressions allowed in array reference ordinal. */
     public static final int ARRAY_REF_EXPR = 5;
     /** New-instance feature ordinal. */
     public static final int NEW_INSTANCE = 6;
@@ -123,7 +130,7 @@ public final class JexlFeatures {
      * The default features flag mask.
      * <p>Meant for compatibility with scripts written before 3.3.1</p>
      */
-    private static final long DEFAULT_FEATURES =
+    public static final long DEFAULT_FEATURES =
         1L << LOCAL_VAR
         | 1L << SIDE_EFFECT
         | 1L << SIDE_EFFECT_GLOBAL
@@ -146,14 +153,28 @@ public final class JexlFeatures {
      * The canonical scripting (since 3.3.1) features flag mask based on the 
original default.
      * <p>Adds lexical, lexical-shade and const-capture but removes 
comparator-names and pragma-anywhere</p>
      */
-    private static final long SCRIPT_FEATURES =
-        DEFAULT_FEATURES
+    public static final long SCRIPT_FEATURES =
+        ( DEFAULT_FEATURES
         | 1L << LEXICAL
         | 1L << LEXICAL_SHADE
-        | 1L << CONST_CAPTURE
+        | 1L << CONST_CAPTURE ) // these parentheses are necessary :-)
         & ~(1L << COMPARATOR_NAMES)
         & ~(1L << PRAGMA_ANYWHERE);
 
+    /**
+     * All features.
+     */
+    public static final long ALL_FEATURES = (1L << (CONST_CAPTURE + 1)) - 1L;
+
+    /**
+     * Creates an all features enabled set.
+     * @return a new instance of all features set
+     * @since 3.3.1
+     */
+    public static JexlFeatures createAll() {
+        return new JexlFeatures(ALL_FEATURES, null, null);
+    }
+
     /**
      * Creates an empty feature set.
      * <p>This is the strictest base-set since no feature is allowed, suitable 
as-is only
@@ -161,31 +182,32 @@ public final class JexlFeatures {
      * @return a new instance of an empty features set
      * @since 3.3.1
      */
-    public static JexlFeatures create() {
+    public static JexlFeatures createNone() {
         return new JexlFeatures(0L, null, null);
     }
 
     /**
-     * Creates a default features set suitable for basic scripting needs.
-     * <p>Meant for legacy (before 3.3) scripting checks.</p>
+     * Creates a default features set suitable for basic but complete 
scripting needs.
+     * <p>Maximizes compatibility with older version scripts (before 3.3), new 
projects should
+     * use {@link JexlFeatures#createScript()} or equivalent features as a 
base.</p>
      * <p>The following scripting features are enabled:</p>
      * <ul>
-     *   <li>local variable {@link JexlFeatures#supportsLocalVar()}</li>
-     *   <li>side effect {@link JexlFeatures#supportsSideEffect()}</li>
-     *   <li>global side effect {@link 
JexlFeatures#supportsSideEffectGlobal()}</li>
-     *   <li>array reference expression {@link 
JexlFeatures#supportsStructuredLiteral()}</li>
-     *   <li>new instance  {@link JexlFeatures#supportsNewInstance()} </li>
-     *   <li>loop {@link JexlFeatures#supportsLoops()} </li>
-     *   <li>lambda {@link JexlFeatures#supportsLambda()}</li>
-     *   <li>method call {@link JexlFeatures#supportsMethodCall()}</li>
-     *   <li>structured literal {@link 
JexlFeatures#supportsStructuredLiteral()}</li>
-     *   <li>pragma {@link JexlFeatures#supportsPragma()}</li>
-     *   <li>annotation {@link JexlFeatures#supportsAnnotation()}</li>
-     *   <li>script {@link JexlFeatures#supportsScript()}</li>
-     *   <li>comparator names  {@link 
JexlFeatures#supportsComparatorNames()}</li>
-     *   <li>namespace pragma  {@link 
JexlFeatures#supportsNamespacePragma()}</li>
-     *   <li>import pragma {@link JexlFeatures#supportsImportPragma()}</li>
-     *   <li>pragma anywhere {@link JexlFeatures#supportsPragmaAnywhere()} 
</li>
+     *   <li>local variable, {@link JexlFeatures#supportsLocalVar()}</li>
+     *   <li>side effect, {@link JexlFeatures#supportsSideEffect()}</li>
+     *   <li>global side effect, {@link 
JexlFeatures#supportsSideEffectGlobal()}</li>
+     *   <li>array reference expression, {@link 
JexlFeatures#supportsStructuredLiteral()}</li>
+     *   <li>new instance, {@link JexlFeatures#supportsNewInstance()} </li>
+     *   <li>loop, {@link JexlFeatures#supportsLoops()}</li>
+     *   <li>lambda, {@link JexlFeatures#supportsLambda()}</li>
+     *   <li>method call, {@link JexlFeatures#supportsMethodCall()}</li>
+     *   <li>structured literal, {@link 
JexlFeatures#supportsStructuredLiteral()}</li>
+     *   <li>pragma, {@link JexlFeatures#supportsPragma()}</li>
+     *   <li>annotation, {@link JexlFeatures#supportsAnnotation()}</li>
+     *   <li>script, {@link JexlFeatures#supportsScript()}</li>
+     *   <li>comparator names,  {@link 
JexlFeatures#supportsComparatorNames()}</li>
+     *   <li>namespace pragma,  {@link 
JexlFeatures#supportsNamespacePragma()}</li>
+     *   <li>import pragma, {@link JexlFeatures#supportsImportPragma()}</li>
+     *   <li>pragma anywhere, {@link 
JexlFeatures#supportsPragmaAnywhere()}</li>
      * </ul>
      * @return a new instance of a default scripting features set
      * @since 3.3.1
@@ -194,14 +216,35 @@ public final class JexlFeatures {
         return new JexlFeatures(DEFAULT_FEATURES, null, null);
     }
 
+    /**
+     * Protected future syntactic elements.
+     * <p><em>try, catch, throw, finally, switch, case, default, class, 
instanceof</em></p>
+     * @since 3.3.1
+     */
+    public static final Set<String> RESERVED_WORDS =
+        Collections.unmodifiableSet(
+            new HashSet<>((Arrays.asList(
+                "try", "catch", "throw", "finally", "switch", "case", 
"default", "class", "instanceof"))));
+
     /**
      * The modern scripting features set.
-     * <p>All scripting features are set including lexical, lexical-shade and 
const-capture.</p>
+     * <p>This is the recommended set for new projects.</p>
+     * <p>All default features with the following differences:</p>
+     * <ul>
+     * <li><em>disable</em> pragma-anywhere, {@link 
JexlFeatures#supportsPragmaAnywhere()}</li>
+     * <li><em>disable</em> comparator-names, {@link 
JexlFeatures#supportsComparatorNames()}</li>
+     * <li><em>enable</em> lexical, {@link JexlFeatures#isLexical()}</li>
+     * <li><em>enable</em> lexical-shade, {@link 
JexlFeatures#isLexicalShade()} </li>
+     * <li><em>enable</em> const-capture, {@link 
JexlFeatures#supportsConstCapture()}</li>
+     * </ul>
+     * <p>It also adds a set of reserved words to enable future unencumbered 
syntax evolution:
+     * <em>try, catch, throw, finally, switch, case, default, class, 
instanceof</em>
+     * </p>
      * @return a new instance of a modern scripting features set
      * @since 3.3.1
      */
     public static JexlFeatures createScript() {
-        return new JexlFeatures(SCRIPT_FEATURES, null, null);
+        return new JexlFeatures(SCRIPT_FEATURES, RESERVED_WORDS, null);
     }
 
     /**
@@ -232,7 +275,6 @@ public final class JexlFeatures {
         // This can only be guaranteed if this ctor is private
         this.reservedNames = r == null? Collections.emptySet() : r;
         this.nameSpaces = n == null? TEST_STR_FALSE : n;
-        setFeature(RESERVED, !this.reservedNames.isEmpty());
     }
 
     @Override
@@ -284,7 +326,6 @@ public final class JexlFeatures {
         } else {
             reservedNames = Collections.unmodifiableSet(new TreeSet<>(names));
         }
-        setFeature(RESERVED, !reservedNames.isEmpty());
         return this;
     }
 
diff --git a/src/test/java/org/apache/commons/jexl3/FeaturesTest.java 
b/src/test/java/org/apache/commons/jexl3/FeaturesTest.java
index bb2ae330..1ab59642 100644
--- a/src/test/java/org/apache/commons/jexl3/FeaturesTest.java
+++ b/src/test/java/org/apache/commons/jexl3/FeaturesTest.java
@@ -21,6 +21,8 @@ import java.util.Arrays;
 import org.junit.Assert;
 import org.junit.Test;
 
+import static org.apache.commons.jexl3.JexlFeatures.CONST_CAPTURE;
+
 /**
  * Tests for blocks
  * @since 1.1
@@ -326,7 +328,7 @@ public class FeaturesTest extends JexlTestCase {
 
     @Test
     public void testIssue409() {
-        final JexlFeatures baseFeatures = JexlFeatures.createDefault();
+        final JexlFeatures baseFeatures =  JexlFeatures.createDefault();
         Assert.assertFalse(baseFeatures.isLexical());
         Assert.assertFalse(baseFeatures.isLexicalShade());
         Assert.assertFalse(baseFeatures.supportsConstCapture());
@@ -337,14 +339,11 @@ public class FeaturesTest extends JexlTestCase {
         scriptFeatures.lexical(false);
         Assert.assertFalse(scriptFeatures.isLexical());
         Assert.assertFalse(scriptFeatures.isLexicalShade());
-
-        scriptFeatures.constCapture(false);
-        Assert.assertEquals(baseFeatures, scriptFeatures);
     }
 
     @Test
     public void testCreate() {
-        final JexlFeatures f = JexlFeatures.create();
+        final JexlFeatures f = JexlFeatures.createNone();
         Assert.assertTrue(f.supportsExpression());
 
         Assert.assertFalse(f.supportsAnnotation());
@@ -372,4 +371,46 @@ public class FeaturesTest extends JexlTestCase {
         Assert.assertNotNull(jnof.createExpression("3 + 4"));
     }
 
+    @Test
+    public void test410a() {
+        long x = JexlFeatures.ALL_FEATURES;
+        Assert.assertEquals(23, Long.bitCount(x));
+        Assert.assertTrue((x & (1L << CONST_CAPTURE)) != 0);
+
+        JexlFeatures all = JexlFeatures.createAll();
+        final JexlEngine jexl = new JexlBuilder().features(all).create();
+        JexlScript script = jexl.createScript("#0 * #1", "#0", "#1");
+        Object r = script.execute(null, 6, 7);
+        Assert.assertEquals(42, r);
+    }
+
+    @Test
+    public void test410b() {
+        JexlFeatures features = JexlFeatures.createScript();
+        Assert.assertTrue(features.isLexical());
+        Assert.assertTrue(features.isLexicalShade());
+        Assert.assertTrue(features.supportsConstCapture());
+        //features.pragmaAnywhere(false);
+        Assert.assertFalse(features.supportsPragmaAnywhere());
+        //features.comparatorNames(false);
+        Assert.assertFalse(features.supportsComparatorNames());
+
+        final JexlEngine jexl = new JexlBuilder().features(features).create();
+        for(String varName : JexlFeatures.RESERVED_WORDS) {
+            String src = "var " + varName;
+            //JexlScript script = jexl.createScript(src);
+            Assert.assertThrows(JexlException.Feature.class, () -> 
jexl.createScript(src));
+        }
+        final String[] cmpNameScripts = {
+            "1 eq 1",
+            "2 ne 3",
+            "1 lt 2",
+            "3 le 3",
+            "4 gt 2",
+            "3 ge 2"
+        };
+        for(String src : cmpNameScripts) {
+            Assert.assertThrows(JexlException.Ambiguous.class, () -> 
jexl.createScript(src));
+        }
+    }
 }

Reply via email to