View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.jexl3;
18  
19  import static org.apache.commons.jexl3.JexlFeatures.NAMESPACE_INSTANTIATE;
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertFalse;
22  import static org.junit.jupiter.api.Assertions.assertNotNull;
23  import static org.junit.jupiter.api.Assertions.assertThrows;
24  import static org.junit.jupiter.api.Assertions.assertTrue;
25  import static org.junit.jupiter.api.Assertions.fail;
26  
27  import java.util.Arrays;
28  import java.util.Collection;
29  
30  import org.junit.jupiter.api.Test;
31  
32  /**
33   * Tests for blocks
34   */
35  @SuppressWarnings({"AssertEqualsBetweenInconvertibleTypes"})
36  class FeaturesTest extends JexlTestCase {
37      private final JexlEngine jexl = new JexlBuilder().create();
38  
39      /**
40       * Create the test
41       */
42      public FeaturesTest() {
43          super("FeaturesTest");
44      }
45  
46      private void assertOk(final String[] scripts) {
47          for(final String str : scripts) {
48              try {
49                  final JexlScript e = jexl.createScript(str);
50              } catch (final JexlException.Feature xfeature) {
51                  fail(str + " :: should not fail parse: " + xfeature.getMessage());
52              }
53          }
54      }
55  
56      /**
57       * Checks that the script is valid with all features on then verifies it
58       * throws a feature exception with the given set (in features param).
59       * @param features The features
60       * @param scripts The scripts
61       * @throws Exception
62       */
63      private void checkFeature(final JexlFeatures features, final String[] scripts) {
64          for (final String script : scripts) {
65              final JexlScript ctl = JEXL.createScript(script);
66              assertNotNull(ctl);
67              final JexlException.Parsing xfeature = assertThrows(JexlException.Parsing.class, () -> jexl.createScript(features, null, script));
68              assertNotNull(xfeature.getMessage());
69          }
70      }
71  
72      @Test
73      void test410a() {
74          final long x = JexlFeatures.createAll().getFlags();
75          assertEquals(NAMESPACE_INSTANTIATE + 1, Long.bitCount(x));
76          assertTrue((x & 1L << NAMESPACE_INSTANTIATE) != 0);
77  
78          final JexlFeatures all = JexlFeatures.createAll();
79          final JexlEngine jexl = new JexlBuilder().features(all).create();
80          final JexlScript script = jexl.createScript("#0 * #1", "#0", "#1");
81          final Object r = script.execute(null, 6, 7);
82          assertEquals(42, r);
83      }
84  
85      @Test
86      void test410b() {
87          final JexlFeatures features = JexlFeatures.createScript();
88          assertTrue(features.isLexical());
89          assertTrue(features.isLexicalShade());
90          assertTrue(features.supportsConstCapture());
91          //features.pragmaAnywhere(false);
92          assertFalse(features.supportsPragmaAnywhere());
93          //features.comparatorNames(false);
94          assertFalse(features.supportsComparatorNames());
95  
96          final JexlEngine jexl = new JexlBuilder().features(features).create();
97          final Collection<String> reserved = features.getReservedNames();
98          for(final String varName : reserved) {
99              final String src = "var " + varName;
100             //JexlScript script = jexl.createScript(src);
101             assertThrows(JexlException.Feature.class, () -> jexl.createScript(src), src);
102         }
103         final String[] cmpNameScripts = {
104             "1 eq 1",
105             "2 ne 3",
106             "1 lt 2",
107             "3 le 3",
108             "4 gt 2",
109             "3 ge 2"
110         };
111         for(final String src : cmpNameScripts) {
112             assertThrows(JexlException.Ambiguous.class, () -> jexl.createScript(src), src);
113         }
114     }
115 
116     @Test
117     void testAnnotations() {
118         final JexlFeatures f = new JexlFeatures().annotation(false);
119         final String[] scripts = {
120             "@synchronized(2) { return 42; }",
121             "@two var x = 3;"
122         };
123         checkFeature(f, scripts);
124     }
125 
126     @Test
127     void testArrayRefs() {
128         final JexlFeatures f = new JexlFeatures().arrayReferenceExpr(false);
129 
130         final String[] scripts = {
131             "x[y]",
132             "x['a'][b]",
133             "x()['a'][b]",
134             "x.y['a'][b]"
135         };
136         checkFeature(f, scripts);
137         assertOk(scripts);
138         // same ones with constant array refs should work
139         final String[] scriptsOk = {
140             "x['y']",
141             "x['a'][1]",
142             "x()['a']['b']",
143             "x.y['a']['b']"
144         };
145         assertOk(scriptsOk);
146     }
147 
148     @Test
149     void testConstCapture() {
150         final JexlFeatures f = new JexlFeatures().constCapture(true);
151         final String[] scripts = {
152             "let x = 0; const f = y -> x += y; f(42)",
153             "let x = 0; function f(y) { z -> x *= y }; f(42)"
154         };
155         checkFeature(f, scripts);
156         final JexlFeatures nof = new JexlFeatures().constCapture(true);
157         assertOk(scripts);
158     }
159 
160     @Test
161     void testCreate() {
162         final JexlFeatures f = JexlFeatures.createNone();
163         assertTrue(f.supportsExpression());
164 
165         assertFalse(f.supportsAnnotation());
166         assertFalse(f.supportsArrayReferenceExpr());
167         assertFalse(f.supportsComparatorNames());
168         assertFalse(f.supportsFatArrow());
169         assertFalse(f.supportsImportPragma());
170         assertFalse(f.supportsLambda());
171         assertFalse(f.supportsLocalVar());
172         assertFalse(f.supportsLoops());
173         assertFalse(f.supportsMethodCall());
174         assertFalse(f.supportsNamespacePragma());
175         assertFalse(f.supportsNewInstance());
176         assertFalse(f.supportsPragma());
177         assertFalse(f.supportsPragmaAnywhere());
178         assertFalse(f.supportsScript());
179         assertFalse(f.supportsStructuredLiteral());
180 
181         assertFalse(f.isLexical());
182         assertFalse(f.isLexicalShade());
183         assertFalse(f.supportsConstCapture());
184 
185         final JexlEngine jnof = new JexlBuilder().features(f).create();
186         assertThrows(JexlException.Feature.class, ()->jnof.createScript("{ 3 + 4 }"));
187         assertNotNull(jnof.createExpression("3 + 4"));
188     }
189 
190     @Test
191     void testIssue409() {
192         final JexlFeatures baseFeatures =  JexlFeatures.createDefault();
193         assertFalse(baseFeatures.isLexical());
194         assertFalse(baseFeatures.isLexicalShade());
195         assertFalse(baseFeatures.supportsConstCapture());
196 
197         final JexlFeatures scriptFeatures = JexlFeatures.createScript();
198         assertTrue(scriptFeatures.isLexical());
199         assertTrue(scriptFeatures.isLexicalShade());
200         scriptFeatures.lexical(false);
201         assertFalse(scriptFeatures.isLexical());
202         assertFalse(scriptFeatures.isLexicalShade());
203     }
204 
205     @Test
206     void testMethodCalls() {
207         final JexlFeatures f = new JexlFeatures().methodCall(false);
208         final String[] scripts = {
209             "x.y(z)",
210             "x['a'].m(b)",
211             "x()['a'](b)",
212             "x.y['a'](b)"
213         };
214         checkFeature(f, scripts);
215         // same ones with constant array refs should work
216         final String[] scriptsOk = {
217             "x('y')",
218             "x('a')[1]",
219             "x()['a']['b']",
220         };
221         assertOk(scriptsOk);
222     }
223 
224     @Test
225     void testMixedFeatures() {
226         // no new, no local, no lambda, no loops, no-side effects
227         final JexlFeatures f = new JexlFeatures()
228                 .newInstance(false)
229                 .localVar(false)
230                 .lambda(false)
231                 .loops(false)
232                 .sideEffectGlobal(false);
233         final String[] scripts = {
234             "return new(clazz);",
235             "()->{ return 0 };",
236             "var x = 0;",
237             "(x, y)->{ return 0 };",
238             "for(var i : {0 .. 10}) { bar(i); }",
239             "x += 1",
240             "x.y += 1"
241         };
242         checkFeature(f, scripts);
243     }
244     @Test
245     void testNoComparatorNames() {
246         final JexlFeatures f = new JexlFeatures().comparatorNames(false);
247         final String[] scripts = {
248             "1 eq 1",
249             "2 ne 3",
250             "1 lt 2",
251             "3 le 3",
252             "4 gt 2",
253             "3 ge 2"
254         };
255         checkFeature(f, scripts);
256     }
257 
258     @Test
259     void testNoLambda() {
260         final JexlFeatures f = new JexlFeatures().lambda(false);
261         final String[] scripts = {
262             "var x  = ()->{ return 0 };",
263             "()->{ return 0 };",
264             "(x, y)->{ return 0 };",
265             "function() { return 0 };",
266             "function(x, y) { return 0 };",
267             "if (false) { (function(x, y) { return x + y })(3, 4) }"
268         };
269         checkFeature(f, scripts);
270     }
271 
272     @Test
273     void testNoLocals() {
274         final JexlFeatures f = new JexlFeatures().localVar(false);
275         final String[] scripts = {
276             "var x = 0;",
277             "(x)->{ x }"
278         };
279         checkFeature(f, scripts);
280     }
281 
282     @Test
283     void testNoLoop() {
284         final JexlFeatures f = new JexlFeatures().loops(false);
285         final String[] scripts = {
286             "while(true);",
287             "for(var i : {0 .. 10}) { bar(i); }"
288         };
289         checkFeature(f, scripts);
290     }
291     @Test
292     void testNoNew() {
293         final JexlFeatures f = new JexlFeatures().newInstance(false);
294         final String[] scripts = {
295             "return new(clazz);",
296             "new('java.math.BigDecimal', 12) + 1"
297         };
298         checkFeature(f, scripts);
299     }
300 
301     @Test
302     void testNoScript() {
303         final JexlFeatures f = new JexlFeatures().script(false);
304         assertTrue(f.supportsExpression());
305         final String[] scripts = {
306             "if (false) { block(); }",
307             "{ noway(); }",
308             "while(true);",
309             "for(var i : {0 .. 10}) { bar(i); }"
310         };
311         checkFeature(f, scripts);
312     }
313 
314     @Test
315     void testNoSideEffects() {
316         final JexlFeatures f = new JexlFeatures().sideEffect(false);
317         final String[] scripts = {
318             "x = 1",
319             "x.y = 1",
320             "x().y = 1",
321             "x += 1",
322             "x.y += 1",
323             "x().y += 1",
324             "x -= 1",
325             "x *= 1",
326             "x /= 1",
327             "x %= 1",
328             "x ^= 1",
329             "x &= 1",
330             "x |= 1",
331             "x >>= 1",
332             "x <<= 1",
333             "x >>>= 1",
334         };
335         checkFeature(f, scripts);
336     }
337 
338     @Test
339     void testNoSideEffectsGlobal() {
340         final JexlFeatures f = new JexlFeatures().sideEffectGlobal(false);
341         final String[] scripts = {
342             "x = 1",
343             "x.y = 1",
344             "x().y = 1",
345             "x += 1",
346             "x.y += 1",
347             "x().y += 1",
348             "x -= 1",
349             "x *= 1",
350             "x /= 1",
351             "x ^= 1",
352             "x &= 1",
353             "x |= 1",
354             "4 + (x.y = 1)",
355             "if (true) x.y.z = 4"
356         };
357         // these should all fail with x undeclared as local, thus x as global
358         checkFeature(f, scripts);
359         // same ones with x as local should work
360         for(final String str : scripts) {
361             try {
362                 final JexlScript e = jexl.createScript("var x = foo(); " + str);
363             } catch (final JexlException.Feature xfeature) {
364                 fail(str + " :: should not fail parse: " + xfeature.getMessage());
365             }
366         }
367     }
368 
369     @Test
370     void testPragma() {
371         final JexlFeatures f = new JexlFeatures().pragma(false);
372         final String[] scripts = {
373             "#pragma foo 42",
374             "#pragma foo 'bar'\n@two var x = 3;"
375         };
376         checkFeature(f, scripts);
377     }
378 
379     @Test
380     void testPragmaAnywhere() {
381         final JexlFeatures f = new JexlFeatures().pragmaAnywhere(false);
382         final String[] scripts = {
383                 "var x = 3;\n#pragma foo 42",
384         };
385         checkFeature(f, scripts);
386     }
387 
388     @Test
389     void testReservedVars() {
390         final JexlFeatures f = new JexlFeatures().reservedNames(Arrays.asList("foo", "bar"));
391         final String[] scripts = {
392             "var foo = 0;",
393             "(bar)->{ bar }",
394             "var f = function(bar) { bar; }"
395         };
396         checkFeature(f, scripts);
397         final String[] scriptsOk = {
398             "var foo0 = 0;",
399             "(bar1)->{ bar }",
400             "var f = function(bar2) { bar2; }"
401         };
402         assertOk(scriptsOk);
403     }
404 
405     @Test
406     void testStructuredLiterals() {
407         final JexlFeatures f = new JexlFeatures().structuredLiteral(false);
408         final String[] scripts = {
409             "{1, 2, 3}",
410             "[1, 2, 3]",
411             "{ 1 :'one', 2 : 'two', 3 : 'three' }",
412             "(1 .. 5)"
413         };
414         checkFeature(f, scripts);
415         assertOk(scripts);
416     }
417 }