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.internal.Util.debuggerCheck;
20  import static org.apache.commons.jexl3.JexlTestCase.TEST_PERMS;
21  import static org.junit.jupiter.api.Assertions.assertEquals;
22  import static org.junit.jupiter.api.Assertions.assertFalse;
23  import static org.junit.jupiter.api.Assertions.assertInstanceOf;
24  import static org.junit.jupiter.api.Assertions.assertNotEquals;
25  import static org.junit.jupiter.api.Assertions.assertNotNull;
26  import static org.junit.jupiter.api.Assertions.assertNull;
27  import static org.junit.jupiter.api.Assertions.assertThrows;
28  import static org.junit.jupiter.api.Assertions.assertTrue;
29  import static org.junit.jupiter.api.Assertions.fail;
30  
31  import java.io.StringReader;
32  import java.io.StringWriter;
33  import java.lang.reflect.Proxy;
34  import java.math.MathContext;
35  import java.util.ArrayList;
36  import java.util.Arrays;
37  import java.util.Collections;
38  import java.util.HashMap;
39  import java.util.HashSet;
40  import java.util.LinkedHashMap;
41  import java.util.LinkedHashSet;
42  import java.util.List;
43  import java.util.Map;
44  import java.util.concurrent.atomic.AtomicInteger;
45  import java.util.stream.Collectors;
46  import java.util.stream.Stream;
47  
48  import org.apache.commons.jexl3.internal.Engine32;
49  import org.apache.commons.jexl3.internal.OptionsContext;
50  import org.apache.commons.jexl3.introspection.JexlSandbox;
51  import org.junit.jupiter.api.Assertions;
52  import org.junit.jupiter.api.Test;
53  
54  /**
55   * Test cases for reported issue between JEXL-300 and JEXL-399.
56   */
57  class Issues300Test {
58      public static class Arithmetic383 extends JexlArithmetic {
59          public Arithmetic383(final boolean astrict) {
60              super(astrict);
61          }
62  
63          @Override
64          public boolean isStrict(final JexlOperator op) {
65              switch (op) {
66                  case NOT:
67                  case CONDITION:
68                      return false;
69                  default: return super.isStrict(op);
70              }
71          }
72      }
73  
74      public static class Arithmetic384 extends JexlArithmetic {
75          public Arithmetic384(final boolean astrict) {
76              super(astrict);
77          }
78  
79          @Override
80          public boolean isStrict(final JexlOperator op) {
81              if (JexlOperator.ADD == op) {
82                  return false;
83              }
84              return super.isStrict(op);
85          }
86      }
87  
88      public static class Arithmetic384c extends JexlArithmetic {
89          AtomicInteger cmp = new AtomicInteger();
90  
91          public Arithmetic384c(final boolean astrict) {
92              super(astrict);
93          }
94  
95          public Arithmetic384c(final boolean astrict, final MathContext bigdContext, final int bigdScale) {
96              super(astrict, bigdContext, bigdScale);
97          }
98  
99          @Override
100         protected int compare(final Object l, final Object r, final String op) {
101             cmp.incrementAndGet();
102             return super.compare(l, r, op);
103         }
104 
105         int getCmpCalls() {
106             return cmp.get();
107         }
108     }
109 
110     public static class Arithmetic384d extends Arithmetic384c {
111         public Arithmetic384d(final boolean astrict) {
112             super(astrict);
113         }
114 
115         public Arithmetic384d(final boolean astrict, final MathContext bigdContext, final int bigdScale) {
116             super(astrict, bigdContext, bigdScale);
117         }
118     }
119 
120     private static class Class397 implements Interface397i {
121         @Override
122         public String summary() {
123             return getClass().getName();
124         }
125     }
126 
127     public static class Context0930 extends MapContext {
128 
129         /**
130          * This allows using a JEXL lambda as a filter.
131          *
132          * @param stream The stream
133          * @param filter The lambda to use as filter
134          * @return The filtered stream
135          */
136         public Stream<?> filter(final Stream<?> stream, final JexlScript filter) {
137             return stream.filter(x -> Boolean.TRUE.equals(filter.execute(this, x)));
138         }
139     }
140 
141     /**
142      * Mock driver.
143      */
144     public static class Driver0930 {
145         private final String name;
146 
147         Driver0930(final String n) {
148             name = n;
149         }
150 
151         public String getAttributeName() {
152             return name;
153         }
154     }
155 
156     public interface Interface397i {
157         String summary();
158     }
159 
160     public static class Session322 {
161         public User322 getUser() {
162             return new User322();
163         }
164     }
165 
166     public static class TestObject374 {
167         private String name;
168         private TestObject374 nested;
169 
170         public String getName() {
171             return name;
172         }
173 
174         public TestObject374 getNested() {
175             return nested;
176         }
177 
178         public void setName(final String name) {
179             this.name = name;
180         }
181 
182         public void setNested(final TestObject374 nested) {
183             this.nested = nested;
184         }
185     }
186 
187     public enum Type375 {
188         DELIVERY_ADDRESS, DOMICILE
189     }
190 
191     public static class User322 {
192         public String getName() {
193             return "user322";
194         }
195     }
196 
197     public static class VaContext extends MapContext {
198         VaContext(final Map<String, Object> vars) {
199             super(vars);
200         }
201 
202         public int cell(final List<?> l, final String... ms) {
203             return 42 + cell(ms);
204         }
205 
206         public int cell(final String... ms) {
207             return ms.length;
208         }
209     }
210 
211     public static class Var370 {
212         private String name;
213 
214         public String getName() {
215             return name;
216         }
217 
218         public void setName(final String s) {
219             name = s;
220         }
221     }
222 
223     static JexlContext pragmaticContext() {
224         final JexlOptions opts = new JexlOptions();
225         opts.setFlags("-strict", "-cancellable", "-lexical", "-lexicalShade", "+safe", "+sharedInstance");
226         return new JexlTestCase.PragmaticContext(opts);
227     }
228 
229     <T> T createProxy(final JexlEngine jexl, final Object o, final Class[] clazzz) {
230         // a JEX-based delegating proxy
231         return (T) Proxy.newProxyInstance(getClass().getClassLoader(), clazzz, (proxy, method, args) -> jexl.invokeMethod(o, method.getName(), args));
232     }
233 
234     private Object run361a(final JexlEngine jexl) {
235         final String src = "()-> { ()-> { if (versionFile != null) { return 'foo'; } else { return 'bar'; }} }";
236         final JexlScript script = jexl.createScript(src);
237         final Object result = script.execute(null);
238         final JexlScript rs = (JexlScript) result;
239         return rs.execute(null);
240     }
241 
242     private Object run361b(final JexlEngine jexl) {
243         // @formatter:off
244         final String src = "()-> { ()-> {" +
245                 "var voa = vaf.value;\n" +
246                 "if (voa != NaN && voa <= 0)" +
247                 "{ return 'foo'; } else { return 'bar'; }" +
248                 "} }";
249         // @formatter:on
250         final JexlContext context = new MapContext();
251         final Map<String, Object> vaf = Collections.singletonMap("value", null);
252         context.set("vaf", vaf);
253         final JexlScript script = jexl.createScript(src);
254         final Object result = script.execute(null);
255         final JexlScript rs = (JexlScript) result;
256         return rs.execute(context);
257     }
258 
259     private String run361c(final JexlEngine jexl) {
260         // @formatter:off
261         final String src = "$$var t = null;\n" +
262                 "$$if (t < 0) {\n" +
263                 "'foo'\n" +
264                 "$$} else {\n" +
265                 "'bar'\n" +
266                 "$$}";
267         // @formatter:on
268         final JxltEngine jxlt = jexl.createJxltEngine();
269         final JexlContext context = new MapContext();
270         final Map<String, Object> vaf = Collections.singletonMap("value", null);
271         context.set("vaf", vaf);
272         final JxltEngine.Template template = jxlt.createTemplate(src);
273         final StringWriter strw = new StringWriter();
274         template.evaluate(context, strw);
275         return strw.toString();
276     }
277 
278     private Object run361d(final JexlEngine jexl) {
279         final String src = "var foo = 42; var foo = 43;";
280         final JexlScript script = jexl.createScript(src);
281         return script.execute(null);
282     }
283 
284     @Test
285     void test301a() {
286         final JexlEngine jexl = new JexlBuilder().safe(false).arithmetic(new JexlArithmetic(false)).create();
287         final String[] srcs = { "var x = null; x.0", "var x = null; x[0]", "var x = [null,1]; x[0][0]" };
288         for (int i = 0; i < srcs.length; ++i) {
289             final String src = srcs[i];
290             final JexlScript s = jexl.createScript(src);
291             try {
292                 final Object o = s.execute(null);
293                 if (i > 0) {
294                     fail(src + ": Should have failed");
295                 }
296             } catch (final Exception ex) {
297                 assertTrue(ex.getMessage().contains("x"));
298             }
299         }
300     }
301 
302     @Test
303     void test302() {
304         final JexlContext jc = new MapContext();
305         // @formatter:off
306         final String[] strs = {
307                 "{if (0) 1 else 2; var x = 4;}",
308                 "if (0) 1; else 2; ",
309                 "{ if (0) 1; else 2; }",
310                 "{ if (0) { if (false) 1 else -3 } else 2; }"
311         };
312         // @formatter:on
313         final JexlEngine jexl = new JexlBuilder().create();
314         for (final String str : strs) {
315             final JexlScript e = jexl.createScript(str);
316             final Object o = e.execute(jc);
317             final int oo = ((Number) o).intValue() % 2;
318             assertEquals(0, oo, () -> "Block result is wrong " + str);
319         }
320     }
321 
322     @Test
323     void test304() {
324         final JexlEngine jexlEngine = new JexlBuilder().strict(false).create();
325         JexlExpression e304 = jexlEngine.createExpression("overview.limit.var");
326 
327         final Map<String, Object> map3 = new HashMap<>();
328         map3.put("var", "4711");
329         final Map<String, Object> map2 = new HashMap<>();
330         map2.put("limit", map3);
331         final Map<String, Object> map = new HashMap<>();
332         map.put("overview", map2);
333 
334         final JexlContext context = new MapContext(map);
335         Object value = e304.evaluate(context);
336         assertEquals("4711", value); // fails
337 
338         map.clear();
339         map.put("overview.limit.var", 42);
340         value = e304.evaluate(context);
341         assertEquals(42, value);
342 
343         // @formatter:off
344         final String[]  keywords = {
345                 "if", "else", "do", "while", "for", "break", "continue", "function", "return", "new", "size", "empty",
346                 "var", "let", "const",
347                 "null", "true", "false",
348                 "not", "div", "mod", "and", "or",
349                 "eq", "ne", "lt", "gt", "ge", "le",
350         };
351         // @formatter:on
352         for (final String keyword : keywords) {
353             final String pkw = "e304." + keyword;
354             map.put(pkw, 42);
355             e304 = jexlEngine.createExpression(pkw);
356             value = e304.evaluate(context);
357             assertEquals(42, value);
358         }
359         for (int i = 0; i < keywords.length; ++i) {
360             final String pkw = "e304." + keywords[i] + "." + keywords[keywords.length - 1 - i];
361             map.put(pkw, 42);
362             e304 = jexlEngine.createExpression(pkw);
363             value = e304.evaluate(context);
364             assertEquals(42, value);
365         }
366         final String allkw = "e304." + String.join(".", keywords);
367         map.put(allkw, 42);
368         e304 = jexlEngine.createExpression(allkw);
369         value = e304.evaluate(context);
370         assertEquals(42, value);
371     }
372 
373     @Test
374     void test305() {
375         final JexlEngine jexl = new JexlBuilder().create();
376         JexlScript e;
377         e = jexl.createScript("{while(false) {}; var x = 1;}");
378         final String str0 = e.getParsedText();
379         e = jexl.createScript(str0);
380         assertNotNull(e);
381         final String str1 = e.getParsedText();
382         assertEquals(str0, str1);
383     }
384 
385     @Test
386     void test306() {
387         final JexlContext ctxt = new MapContext();
388         final JexlEngine jexl = new JexlBuilder().create();
389         final JexlScript e = jexl.createScript("x.y ?: 2");
390         final Object o1 = e.execute(null);
391         assertEquals(2, o1);
392         ctxt.set("x.y", null);
393         final Object o2 = e.execute(ctxt);
394         assertEquals(2, o2);
395     }
396 
397     @Test
398     void test306a() {
399         final JexlEngine jexl = new JexlBuilder().create();
400         final JexlScript e = jexl.createScript("x.y ?: 2", "x");
401         Object o = e.execute(null, new Object());
402         assertEquals(2, o);
403         o = e.execute(null);
404         assertEquals(2, o);
405     }
406 
407     @Test
408     void test306b() {
409         final JexlEngine jexl = new JexlBuilder().create();
410         final JexlScript e = jexl.createScript("x?.y ?: 2", "x");
411         final Object o1 = e.execute(null, new Object());
412         assertEquals(2, o1);
413         final Object o2 = e.execute(null);
414         assertEquals(2, o2);
415     }
416 
417     @Test
418     void test306c() {
419         final JexlEngine jexl = new JexlBuilder().safe(true).create();
420         final JexlScript e = jexl.createScript("x.y ?: 2", "x");
421         Object o = e.execute(null, new Object());
422         assertEquals(2, o);
423         o = e.execute(null);
424         assertEquals(2, o);
425     }
426 
427     @Test
428     void test306d() {
429         final JexlEngine jexl = new JexlBuilder().safe(true).create();
430         final JexlScript e = jexl.createScript("x.y[z.t] ?: 2", "x");
431         Object o = e.execute(null, new Object());
432         assertEquals(2, o);
433         o = e.execute(null);
434         assertEquals(2, o);
435     }
436 
437     @Test
438     void test309a() {
439         // @formatter:off
440         final String src = "<html lang=\"en\">\n"
441                 + "  <body>\n"
442                 + "    <h1>Hello World!</h1>\n"
443                 + "$$ var i = 12++;\n"
444                 + "  </body>\n"
445                 + "</html>";
446         // @formatter:on
447         final JexlEngine jexl = new JexlBuilder().safe(true).create();
448         final JxltEngine jxlt = jexl.createJxltEngine();
449         final JexlInfo info = new JexlInfo("template", 1, 1);
450         final JexlException.Parsing xerror = assertThrows(JexlException.Parsing.class, () -> jxlt.createTemplate(info, src));
451         assertEquals(4, xerror.getInfo().getLine());
452     }
453 
454     @Test
455     void test309b() {
456         // @formatter:off
457         final String src = "<html lang=\"en\">\n"
458                 + "  <body>\n"
459                 + "    <h1>Hello World!</h1>\n"
460                 + "$$ var i = a b c;\n"
461                 + "  </body>\n"
462                 + "</html>";
463         // @formatter:on
464         final JexlEngine jexl = new JexlBuilder().safe(true).create();
465         final JxltEngine jxlt = jexl.createJxltEngine();
466         final JexlInfo info = new JexlInfo("template", 1, 1);
467         final JexlException.Parsing xerror = assertThrows(JexlException.Parsing.class, () -> jxlt.createTemplate(info, src));
468         assertEquals(4, xerror.getInfo().getLine());
469     }
470 
471     @Test
472     void test309c() {
473         // @formatter:off
474         final String src = "<html lang=\"en\">\n"
475                 + "  <body>\n"
476                 + "    <h1>Hello World!</h1>\n"
477                 + "$$ var i =12;\n"
478                 + "  </body>\n"
479                 + "</html>";
480         // @formatter:on
481         final JexlEngine jexl = new JexlBuilder().safe(true).create();
482         final JxltEngine jxlt = jexl.createJxltEngine();
483         final JexlInfo info = new JexlInfo("template", 1, 1);
484         final JxltEngine.Template tmplt = jxlt.createTemplate(info, src);
485         final String src1 = tmplt.asString();
486         final String src2 = tmplt.toString();
487         assertEquals(src1, src2);
488     }
489 
490     @Test
491     void test314() {
492         final JexlEngine jexl = new JexlBuilder().strict(true).safe(false).create();
493         final Map<String, Object> vars = new HashMap<>();
494         final JexlContext ctxt = new VaContext(vars);
495         JexlScript script;
496         Object result;
497         script = jexl.createScript("cell()");
498         result = script.execute(ctxt);
499         assertEquals(0, result);
500         script = jexl.createScript("x.cell()", "x");
501         result = script.execute(ctxt, Arrays.asList(10, 20));
502         assertEquals(42, result);
503         script = jexl.createScript("cell('1', '2')");
504         result = script.execute(ctxt);
505         assertEquals(2, result);
506         script = jexl.createScript("x.cell('1', '2')", "x");
507         result = script.execute(ctxt, Arrays.asList(10, 20));
508         assertEquals(44, result);
509 
510         vars.put("TVALOGAR", null);
511         String jexlExp = "TVALOGAR==null ?'SIMON':'SIMONAZO'";
512         script = jexl.createScript(jexlExp);
513         result = script.execute(ctxt);
514         assertEquals("SIMON", result);
515 
516         jexlExp = "TVALOGAR.PEPITO==null ?'SIMON':'SIMONAZO'";
517         script = jexl.createScript(jexlExp);
518 
519         final Map<String, Object> tva = new LinkedHashMap<>();
520         tva.put("PEPITO", null);
521         vars.put("TVALOGAR", tva);
522         result = script.execute(ctxt);
523         assertEquals("SIMON", result);
524 
525         vars.remove("TVALOGAR");
526         ctxt.set("TVALOGAR.PEPITO", null);
527         result = script.execute(ctxt);
528         assertEquals("SIMON", result);
529     }
530 
531     @Test
532     void test315() {
533         final JexlEngine jexl = new JexlBuilder().strict(true).create();
534         final Map<String, Object> vars = new HashMap<>();
535         final JexlContext ctxt = new VaContext(vars);
536         JexlScript script;
537         Object result;
538         script = jexl.createScript("a?? 42 + 10", "a");
539         result = script.execute(ctxt, 32);
540         assertEquals(32, result);
541         result = script.execute(ctxt, (Object) null);
542         assertEquals(52, result);
543         script = jexl.createScript("- a??42 + +10", "a");
544         result = script.execute(ctxt, 32);
545         assertEquals(-32, result);
546         result = script.execute(ctxt, (Object) null);
547         assertEquals(52, result);
548         // long version of ternary
549         script = jexl.createScript("a? a : +42 + 10", "a");
550         result = script.execute(ctxt, 32);
551         assertEquals(32, result);
552         result = script.execute(ctxt, (Object) null);
553         assertEquals(52, result);
554         // short one, elvis, equivalent
555         script = jexl.createScript("a ?: +42 + 10", "a");
556         result = script.execute(ctxt, 32);
557         assertEquals(32, result);
558         result = script.execute(ctxt, (Object) null);
559         assertEquals(52, result);
560     }
561 
562     @Test
563     void test317() {
564         final JexlEngine jexl = new JexlBuilder().strict(true).create();
565         final JexlContext ctxt = new MapContext();
566         JexlScript script;
567         Object result;
568         JexlInfo info = new JexlInfo("test317", 1, 1);
569         // @formatter:off
570         script = jexl.createScript(info, "var f = "
571                         + "()-> {x + x }; f",
572                 "x");
573         // @formatter:on
574         result = script.execute(ctxt, 21);
575         assertInstanceOf(JexlScript.class, result);
576         script = (JexlScript) result;
577         info = JexlInfo.from(script);
578         assertNotNull(info);
579         assertEquals("test317", info.getName());
580         result = script.execute(ctxt, 21);
581         assertEquals(42, result);
582     }
583 
584     @Test
585     void test322a() {
586         final JexlEngine jexl = new JexlBuilder().strict(true).create();
587         final JxltEngine jxlt = jexl.createJxltEngine();
588         final JexlContext context = new MapContext();
589 
590         final String[] ins = { "${'{'}", "${\"{\"}", "${\"{}\"}", "${'{42}'}", "${\"{\\\"\\\"}\"}" };
591         final String[] ctls = { "{", "{", "{}", "{42}", "{\"\"}" };
592         StringWriter strw;
593         JxltEngine.Template template;
594         String output;
595 
596         for (int i = 0; i < ins.length; ++i) {
597             final String src = ins[i];
598             template = jxlt.createTemplate("$$", new StringReader(src));
599             strw = new StringWriter();
600             template.evaluate(context, strw);
601             output = strw.toString();
602             assertEquals(ctls[i], output);
603         }
604     }
605 
606     @Test
607     void test322b() {
608         final JexlContext ctxt = new MapContext();
609         final String src = "L'utilisateur ${session.user.name} s'est connecte";
610         final JexlEngine jexl = new JexlBuilder().strict(true).create();
611         final JxltEngine jxlt = jexl.createJxltEngine();
612         StringWriter strw;
613         JxltEngine.Template template;
614         String output;
615         template = jxlt.createTemplate("$$", new StringReader(src));
616 
617         ctxt.set("session", new Session322());
618         strw = new StringWriter();
619         template.evaluate(ctxt, strw);
620         output = strw.toString();
621         assertEquals("L'utilisateur user322 s'est connecte", output);
622 
623         ctxt.set("session.user", new User322());
624         strw = new StringWriter();
625         template.evaluate(ctxt, strw);
626         output = strw.toString();
627         assertEquals("L'utilisateur user322 s'est connecte", output);
628 
629         ctxt.set("session.user.name", "user322");
630         strw = new StringWriter();
631         template.evaluate(ctxt, strw);
632         output = strw.toString();
633         assertEquals("L'utilisateur user322 s'est connecte", output);
634     }
635 
636     @Test
637     void test323() {
638         final JexlEngine jexl = new JexlBuilder().safe(false).create();
639         final Map<String, Object> vars = new HashMap<>();
640         final JexlContext jc = new MapContext(vars);
641         Object result;
642 
643         // nothing in context, ex
644         final JexlScript script0 = jexl.createScript("a.n.t.variable");
645         JexlException.Variable xvar = assertThrows(JexlException.Variable.class, () -> script0.execute(jc), "a.n.t.variable is undefined.");
646         assertTrue(xvar.toString().contains("a.n.t"));
647 
648         // defined and null
649         jc.set("a.n.t.variable", null);
650         final JexlScript script = jexl.createScript("a.n.t.variable");
651         result = script.execute(jc);
652         assertNull(result);
653 
654         // defined and null, dereference
655         jc.set("a.n.t", null);
656         final JexlScript script1 = jexl.createScript("a.n.t[0].variable");
657         xvar = assertThrows(JexlException.Variable.class, () -> script1.execute(jc), "a.n.t is null.");
658         assertTrue(xvar.toString().contains("a.n.t"));
659 
660         // undefined, dereference
661         vars.remove("a.n.t");
662         final JexlScript script2 = jexl.createScript("a.n.t[0].variable");
663         xvar = assertThrows(JexlException.Variable.class, () -> script2.execute(jc), "a.n.t is undefined.");
664         assertTrue(xvar.toString().contains("a.n.t"));
665 
666         // defined, derefence undefined property
667         final List<Object> inner = new ArrayList<>();
668         vars.put("a.n.t", inner);
669         final JexlScript script3 = jexl.createScript("a.n.t[0].variable");
670         JexlException.Property xprop = assertThrows(JexlException.Property.class, () -> script3.execute(jc), "a.n.t is null.");
671         assertTrue(xprop.toString().contains("0"));
672 
673         // defined, derefence undefined property
674         inner.add(42);
675         final JexlScript script4 = jexl.createScript("a.n.t[0].variable");
676         xprop = assertThrows(JexlException.Property.class, () -> script4.execute(jc), "a.n.t is null.");
677         assertTrue(xprop.toString().contains("variable"));
678     }
679 
680     @Test
681     void test324() {
682         final JexlEngine jexl = new JexlBuilder().create();
683         final String src42 = "new('java.lang.Integer', 42)";
684         final JexlExpression expr0 = jexl.createExpression(src42);
685         assertEquals(42, expr0.evaluate(null));
686         final String parsed = expr0.getParsedText();
687         assertEquals(src42, parsed);
688         final JexlException.Parsing xparse = assertThrows(JexlException.Parsing.class, () -> jexl.createExpression("new()"), "should not parse");
689         assertTrue(xparse.toString().contains(")"));
690     }
691 
692     @Test
693     void test325() {
694         final JexlEngine jexl = new JexlBuilder().safe(false).create();
695         final Map<String, Object> map = new HashMap<String, Object>() {
696             private static final long serialVersionUID = 1L;
697 
698             @Override
699             public Object get(final Object key) {
700                 return super.get(key == null ? "" : key);
701             }
702 
703             @Override
704             public Object put(final String key, final Object value) {
705                 return super.put(key == null ? "" : key, value);
706             }
707         };
708         map.put("42", 42);
709         final JexlContext jc = new MapContext();
710         JexlScript script;
711         Object result;
712 
713         script = jexl.createScript("map[null] = 42", "map");
714         result = script.execute(jc, map);
715         assertEquals(42, result);
716         script = jexl.createScript("map[key]", "map", "key");
717         result = script.execute(jc, map, null);
718         assertEquals(42, result);
719         result = script.execute(jc, map, "42");
720         assertEquals(42, result);
721     }
722 
723     @Test
724     void test330() {
725         final JexlEngine jexl = new JexlBuilder().create();
726         // Extended form of: 'literal' + VARIABLE 'literal'
727         // missing + operator here ---------------^
728         // @formatter:off
729         final String longExpression = ""
730                 + //
731                 "'THIS IS A VERY VERY VERY VERY VERY VERY VERY "
732                 + //
733                 "VERY VERY LONG STRING CONCATENATION ' + VARIABLE ' <--- "
734                 + //
735                 "error: missing + between VARIABLE and literal'";
736         // @formatter:on
737         final JexlException.Parsing xparse = assertThrows(JexlException.Parsing.class, () -> jexl.createExpression(longExpression),
738                 "parsing malformed expression did not throw exception");
739         assertTrue(xparse.getMessage().contains("VARIABLE"));
740     }
741 
742     @Test
743     void test331() {
744         final JexlEngine jexl = new JexlBuilder().create();
745         final JexlContext ctxt = new MapContext();
746         JexlScript script;
747         Object result;
748         script = jexl.createScript("a + '\\n' + b", "a", "b");
749         result = script.execute(ctxt, "hello", "world");
750         assertTrue(result.toString().contains("\n"));
751     }
752 
753     @Test
754     void test347() {
755         final String src = "A.B == 5";
756         JexlEngine jexl = new JexlBuilder().safe(true).create();
757         final JexlScript script = jexl.createScript(src);
758         Object result = script.execute(null);
759         // Safe navigation is lenient regarding null.
760         assertFalse((Boolean) result);
761 
762         jexl = new JexlBuilder().strict(true).safe(false).create();
763         final JexlContext ctxt = new MapContext();
764         final JexlScript script1 = jexl.createScript(src);
765         // A and A.B undefined
766         assertThrows(JexlException.class, () -> script1.execute(ctxt));
767         // A is null, A.B is undefined
768         ctxt.set("A", null);
769         assertThrows(JexlException.class, () -> script1.execute(ctxt), "should only succeed with safe navigation");
770         // A.B is null
771         ctxt.set("A.B", null);
772         result = script1.execute(ctxt);
773         assertFalse((Boolean) result);
774     }
775 
776     @Test
777     void test349() {
778         final String text = "(A ? C.D : E)";
779         final JexlEngine jexl = new JexlBuilder().safe(true).create();
780         final JexlExpression expr = jexl.createExpression(text);
781         Assertions.assertNotNull(expr);
782         final JexlScript script = jexl.createScript(text);
783         Assertions.assertNotNull(script);
784     }
785 
786     @Test
787     void test361_33() {
788         final JexlEngine jexl = new JexlBuilder().safe(false).strict(true).create();
789         assertThrows(JexlException.class, () -> run361c(jexl), "null arg should fail");
790     }
791 
792     @Test
793     void test361a_32() {
794         final JexlEngine jexl = new Engine32(new JexlBuilder().safe(false));
795         final Object result = run361a(jexl);
796         assertNotNull(result);
797     }
798 
799     @Test
800     void test361a_33() {
801         final JexlEngine jexl = new JexlBuilder().safe(false).strict(true).create();
802         assertThrows(JexlException.class, () -> run361a(jexl), "null arg should fail");
803     }
804 
805     @Test
806     void test361b_32() {
807         final JexlEngine jexl = new Engine32(new JexlBuilder().safe(false).strict(false));
808         final Object result = run361b(jexl);
809         assertNotNull(result);
810     }
811 
812     @Test
813     void test361b_33() {
814         final JexlEngine jexl = new JexlBuilder().safe(false).strict(true).create();
815         assertThrows(JexlException.class, () -> run361b(jexl), "null arg should fail");
816     }
817 
818     @Test
819     void test361c_32() {
820         final JexlEngine jexl = new Engine32(new JexlBuilder().safe(false).strict(false));
821         final String result = run361c(jexl);
822         assertNotNull(result);
823     }
824 
825     @Test
826     void test361d_32() {
827         final JexlEngine jexl = new Engine32(new JexlBuilder().lexical(false).lexicalShade(false).safe(false));
828         final Object result = run361d(jexl);
829         assertNotNull(result);
830     }
831 
832     @Test
833     void test361d_33() {
834         final JexlEngine jexl = new JexlBuilder().lexical(true).lexicalShade(true).safe(false).strict(true).create();
835         assertThrows(JexlException.class, () -> run361d(jexl), "null arg should fail");
836     }
837 
838     @Test
839     void test367() {
840         final String text = "var toto; function foo(x) { x }; var tata = 3; foo(3)";
841         final JexlEngine jexl = new JexlBuilder().safe(true).create();
842         final JexlScript script = jexl.createScript(text);
843         final Object result = script.execute(null);
844         assertEquals(3, result);
845         final String s0 = script.getParsedText();
846         final String s1 = script.getSourceText();
847         assertNotEquals(s0, s1);
848     }
849 
850     @Test
851     void test370() {
852         final Var370 var370 = new Var370();
853         final JexlEngine jexl = new JexlBuilder().safe(true).create();
854         final ObjectContext<Var370> ctxt = new ObjectContext<>(jexl, var370);
855         final JexlExpression get = jexl.createExpression("name");
856         // not null
857         var370.setName("John");
858         assertEquals("John", get.evaluate(ctxt));
859         assertTrue(ctxt.has("name"));
860         // null
861         var370.setName(null);
862         assertNull(get.evaluate(ctxt));
863         assertTrue(ctxt.has("name"));
864         // undefined
865         final JexlExpression get1 = jexl.createExpression("phone");
866         assertFalse(ctxt.has("phone"));
867         final JexlException.Variable xvar = assertThrows(JexlException.Variable.class, () -> get1.evaluate(ctxt), "phone should be undefined.");
868         assertEquals("phone", xvar.getVariable());
869     }
870 
871     @Test
872     void test373b() {
873         final String src = "var i = ++1";
874         final JexlEngine jexl = new JexlBuilder().safe(true).create();
875         final JexlInfo info = new JexlInfo("badscript", 0, 0);
876         final JexlException.Parsing xparse = assertThrows(JexlException.Parsing.class, () -> jexl.createScript(info, src), "should not parse");
877         assertTrue(xparse.getMessage().contains("badscript"));
878     }
879 
880     @Test
881     void test374() {
882         final JexlEngine engine = new JexlBuilder().cache(512).strict(true).silent(false).antish(false).safe(false).create();
883         // Create expression to evaluate 'name'
884         final JexlExpression expr = engine.createExpression("nested.name");
885         // Create an object with getter for name
886         final TestObject374 myObject = new TestObject374();
887         myObject.setName("John");
888         final JexlContext context = new ObjectContext<>(engine, myObject);
889         // Expect an exception because nested is null, so we are doing null.name
890         assertThrows(JexlException.class, () -> expr.evaluate(context));
891     }
892 
893     @Test
894     void test375() {
895         final JexlSandbox jexlSandbox = new JexlSandbox(false);
896         jexlSandbox.allow(Type375.class.getName());
897         final JexlEngine engine = new JexlBuilder().sandbox(jexlSandbox).create();
898 
899         final JexlContext context = new MapContext();
900         context.set("Type", Type375.class);
901 
902         Object result = engine.createScript("Type.valueOf('DOMICILE')").execute(context);
903         assertEquals(Type375.DOMICILE, result);
904 
905         result = engine.createScript("Type.DOMICILE").execute(context);
906         assertEquals(Type375.DOMICILE, result);
907     }
908 
909     @Test
910     void test377() {
911         final String text = "function add(x, y) { x + y } add(a, b)";
912         final JexlEngine jexl = new JexlBuilder().safe(true).create();
913         final JexlScript script = jexl.createScript(text, "a", "b");
914         final Object result = script.execute(null, 20, 22);
915         assertEquals(42, result);
916     }
917 
918     @Test
919     void test379a() {
920         // @formatter:off
921         final String src =
922                 "#pragma jexl.import java.util\n"+
923                         "const map = new LinkedHashMap({0 : 'zero'});";
924         // @formatter:on
925         final JexlEngine jexl = new JexlBuilder().safe(true).create();
926         final JexlScript script = jexl.createScript(src);
927         assertNotNull(script);
928         final Object result = script.execute(null);
929         assertNotNull(result);
930         assertInstanceOf(LinkedHashMap.class, result);
931         assertEquals(1, ((Map) result).size());
932     }
933 
934     @Test
935     void test383() {
936         final JexlEngine jexl = new JexlBuilder().safe(false).arithmetic(new Arithmetic383(true)).create();
937         final String src0 = "if (a) 1; else 2;";
938         final String src1 = "if (!a) 1; else 2;";
939         // local var
940         final JexlScript s0 = jexl.createScript(src0, "a");
941         final JexlScript s1 = jexl.createScript(src1, "a");
942         assertEquals(2, s0.execute(null, (Object) null));
943         assertEquals(1, s1.execute(null, (Object) null));
944         // global var undefined
945         final JexlScript s2 = jexl.createScript(src0);
946         final JexlScript s3 = jexl.createScript(src1);
947         JexlException.Variable xvar = assertThrows(JexlException.Variable.class, () -> s2.execute(null, (Object) null));
948         assertEquals("a", xvar.getVariable());
949         xvar = assertThrows(JexlException.Variable.class, () -> s3.execute(null, (Object) null));
950         assertEquals("a", xvar.getVariable());
951 
952         // global var null
953         final MapContext ctxt = new MapContext();
954         ctxt.set("a", null);
955         assertEquals(2, s2.execute(ctxt, (Object) null));
956         assertEquals(1, s3.execute(ctxt, (Object) null));
957     }
958 
959     @Test
960     void test384a() {
961         final JexlEngine jexl = new JexlBuilder().safe(false).strict(true).create();
962         // constant
963         for (final String src0 : Arrays.asList("'ABC' + null", "null + 'ABC'")) {
964             final JexlContext ctxt = new MapContext();
965             final JexlScript s0 = jexl.createScript(src0);
966             final JexlException xvar = assertThrows(JexlException.class, () -> s0.execute(ctxt, (Object) null), "null argument should throw");
967             assertTrue(xvar.toString().contains("+"));
968         }
969         // null local a
970         for (final String src1 : Arrays.asList("'ABC' + a", "a + 'ABC'")) {
971             final JexlContext ctxt = new MapContext();
972             final JexlScript s1 = jexl.createScript(src1, "a");
973             JexlException.Variable xvar = assertThrows(JexlException.Variable.class, () -> s1.execute(ctxt, (Object) null), "null argument should throw");
974             assertEquals("a", xvar.getVariable());
975             // undefined a
976             final JexlScript s2 = jexl.createScript(src1);
977             xvar = assertThrows(JexlException.Variable.class, () -> s2.execute(ctxt, (Object) null), "null argument should throw");
978             assertEquals("a", xvar.getVariable());
979             assertTrue(xvar.isUndefined());
980             // null a
981             ctxt.set("a", null);
982             xvar = assertThrows(JexlException.Variable.class, () -> s2.execute(ctxt, (Object) null), "null argument should throw");
983             assertEquals("a", xvar.getVariable());
984             assertFalse(xvar.isUndefined());
985         }
986     }
987 
988     @Test
989     void test384b() {
990         // be explicit about + handling null
991         // @formatter:off
992         final JexlEngine jexl = new JexlBuilder()
993                 .arithmetic(new Arithmetic384(true))
994                 .safe(false)
995                 .strict(true)
996                 .create();
997         // @formatter:on
998         // constant
999         for (final String src0 : Arrays.asList("'ABC' + null", "null + 'ABC'")) {
1000             final JexlContext ctxt = new MapContext();
1001             final JexlScript s0 = jexl.createScript(src0);
1002             assertEquals("ABC", s0.execute(ctxt));
1003         }
1004         // null local a
1005         for (final String src1 : Arrays.asList("'ABC' + a", "a + 'ABC'")) {
1006             final JexlContext ctxt = new MapContext();
1007             final JexlScript s1 = jexl.createScript(src1, "a");
1008             assertEquals("ABC", s1.execute(ctxt, (Object) null));
1009             // undefined a
1010             final JexlScript s2 = jexl.createScript(src1);
1011             final JexlException.Variable xvar = assertThrows(JexlException.Variable.class, () -> s2.execute(ctxt, (Object) null), "null argument should throw");
1012             assertEquals("a", xvar.getVariable());
1013             assertTrue(xvar.isUndefined());
1014 
1015             // null a
1016             ctxt.set("a", null);
1017             assertEquals("ABC", s1.execute(ctxt, (Object) null));
1018         }
1019     }
1020 
1021     @Test
1022     void test384c() {
1023         final Arithmetic384c ja = new Arithmetic384c(true);
1024         // @formatter:off
1025         final JexlEngine jexl = new JexlBuilder()
1026                 .safe(false)
1027                 .strict(true)
1028                 .arithmetic(ja)
1029                 .create();
1030         // @formatter:on
1031         assertTrue(ja.toBoolean(jexl.createExpression("3 < 4").evaluate(null)));
1032         assertTrue(ja.toBoolean(jexl.createExpression("6 <= 8").evaluate(null)));
1033         assertFalse(ja.toBoolean(jexl.createExpression("6 == 7").evaluate(null)));
1034         assertTrue(ja.toBoolean(jexl.createExpression("4 > 2").evaluate(null)));
1035         assertTrue(ja.toBoolean(jexl.createExpression("8 > 6").evaluate(null)));
1036         assertTrue(ja.toBoolean(jexl.createExpression("7 != 6").evaluate(null)));
1037         assertEquals(6, ja.getCmpCalls());
1038     }
1039 
1040     @Test
1041     void test384d() {
1042         final Arithmetic384c ja = new Arithmetic384d(true);
1043         // @formatter:off
1044         final JexlEngine jexl = new JexlBuilder()
1045                 .safe(false)
1046                 .strict(true)
1047                 .arithmetic(ja)
1048                 .create();
1049         // @formatter:on
1050         assertTrue(ja.toBoolean(jexl.createExpression("3 < 4").evaluate(null)));
1051         assertTrue(ja.toBoolean(jexl.createExpression("6 <= 8").evaluate(null)));
1052         assertFalse(ja.toBoolean(jexl.createExpression("6 == 7").evaluate(null)));
1053         assertTrue(ja.toBoolean(jexl.createExpression("4 > 2").evaluate(null)));
1054         assertTrue(ja.toBoolean(jexl.createExpression("8 > 6").evaluate(null)));
1055         assertTrue(ja.toBoolean(jexl.createExpression("7 != 6").evaluate(null)));
1056         assertEquals(6, ja.getCmpCalls());
1057     }
1058 
1059     @Test
1060     void test390() throws Exception {
1061         // @formatter:off
1062         final JexlEngine jexl = new JexlBuilder()
1063                 .safe(false)
1064                 .strict(true)
1065                 .create();
1066         // @formatter:on
1067         JexlScript script = null;
1068         final String src = "if (true) #pragma one 42";
1069         final JexlException.Parsing xparse = assertThrows(JexlException.Parsing.class, () -> jexl.createScript(src), "should have failed parsing");
1070         assertTrue(xparse.getDetail().contains("pragma"));
1071 
1072         final String src1 = "if (true) { #pragma one 42 }";
1073         script = jexl.createScript(src1);
1074         final Object result = script.execute(null);
1075         debuggerCheck(jexl);
1076     }
1077 
1078     @Test
1079     void test393() {
1080         // @formatter:off
1081         final String src = "const total = 0;\n" +
1082                 "if (true) {\n" +
1083                 "  total = 1;\n" +
1084                 "}\n" +
1085                 "total; ";
1086         final JexlEngine jexl = new JexlBuilder()
1087                 .safe(false)
1088                 .strict(true)
1089                 .create();
1090         // @formatter:on
1091         final JexlException.Parsing xparse = assertThrows(JexlException.Parsing.class, () -> jexl.createScript(src), "should fail on const total assignment");
1092         assertTrue(xparse.getMessage().contains("total"));
1093     }
1094 
1095     @Test
1096     void testBackslashes() {
1097         final JexlEngine jexl = new JexlBuilder().safe(false).create();
1098         final String src = "\"\b\t\f\"";
1099         JexlScript s = jexl.createScript(src);
1100         assertNotNull(s);
1101         final String ctl = "\b\t\f";
1102         assertEquals(ctl, s.execute(null));
1103         String parsed = s.getParsedText();
1104         assertEquals("'\\b\\t\\f'", parsed);
1105         s = jexl.createScript(src);
1106         assertNotNull(s);
1107         assertEquals(ctl, s.execute(null));
1108         parsed = s.getParsedText();
1109         assertEquals("'\\b\\t\\f'", parsed);
1110     }
1111 
1112     @Test
1113     void testDow() {
1114         // @formatter:off
1115         final String src = "(y, m, d)->{\n" +
1116                 "// will return 0 for Sunday, 6 for Saturday\n" +
1117                 "const t = [0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4];\n"+
1118                 "if (m < 3) { --y }\n" +
1119                 "(y + y/4 - y/100 + y/400 + t[m-1] + d) % 7;\n" +
1120             "}";
1121         // @formatter:on
1122         final JexlEngine jexl = new JexlBuilder().create();
1123         final JexlScript script = jexl.createScript(src);
1124         Object r = script.execute(null, 2023, 3, 1);
1125         assertInstanceOf(Number.class, r);
1126         Number dow = (Number) r;
1127         assertEquals(3, dow.intValue());
1128         r = script.execute(null, 1969, 7, 20);
1129         assertInstanceOf(Number.class, r);
1130         dow = (Number) r;
1131         assertEquals(0, dow.intValue());
1132     }
1133 
1134     @Test
1135     void testIssue394() {
1136         final StringBuilder x = new StringBuilder("foobar");
1137         assertEquals("foobar", x.toString());
1138         final String src = "x -> x.setLength(3)";
1139         final JexlEngine jexl = new JexlBuilder().create();
1140         final JexlScript script = jexl.createScript(src);
1141         final Object result = script.execute(null, x);
1142         assertEquals("foo", x.toString());
1143     }
1144 
1145     @Test
1146     void testIssue397() {
1147         String result;
1148         final String control = Class397.class.getName();
1149         final JexlEngine jexl = new JexlBuilder().permissions(TEST_PERMS).create();
1150 
1151         final Interface397i instance = new Class397();
1152         result = (String) jexl.invokeMethod(instance, "summary");
1153         assertEquals(control, result);
1154 
1155         final Interface397i proxy = createProxy(jexl, instance, new Class[] { Interface397i.class });
1156         result = (String) jexl.invokeMethod(proxy, "summary");
1157         assertEquals(control, result);
1158 
1159         final JexlScript script = jexl.createScript("dan.summary()", "dan");
1160         result = (String) script.execute(null, instance);
1161         assertEquals(control, result);
1162         result = (String) script.execute(null, proxy);
1163         assertEquals(control, result);
1164     }
1165 
1166     @Test
1167     void testIssue398a() {
1168         // @formatter:off
1169         final String src = "let m = {\n" +
1170             "  \"foo\": 1,\n" +
1171             "  \"bar\": 2,\n" +
1172             "}";
1173         // @formatter:on
1174         final JexlEngine jexl = new JexlBuilder().create();
1175         final JexlScript script = jexl.createScript(src);
1176         final Object result = script.execute(null);
1177         assertInstanceOf(Map.class, result);
1178         final Map<?, ?> map = (Map<?, ?>) result;
1179         assertEquals(2, map.size());
1180     }
1181 
1182     @Test
1183     void testIssue398b() {
1184         final Map<String, Object> foo = Collections.singletonMap("X", "x");
1185         final Map<String, Object> bar = Collections.singletonMap("Y", "y");
1186         final JexlContext ctxt = new MapContext();
1187         ctxt.set("foo", foo);
1188         ctxt.set("bar", bar);
1189         // @formatter:off
1190         final String src = "let m = {\n" +
1191                 "  foo.X: 1,\n" +
1192                 "  bar.Y: 2,\n" +
1193                 "}";
1194         // @formatter:on
1195         final JexlEngine jexl = new JexlBuilder().create();
1196         JexlScript script = jexl.createScript(src);
1197         Object result = script.execute(ctxt);
1198         assertInstanceOf(Map.class, result);
1199         Map<?, ?> map = (Map<?, ?>) result;
1200         assertEquals(2, map.size());
1201         assertEquals(1, map.get("x"));
1202         assertEquals(2, map.get("y"));
1203 
1204         script = jexl.createScript(src, "foo", "bar");
1205         result = script.execute(null, foo, bar);
1206         assertInstanceOf(Map.class, result);
1207         map = (Map<?, ?>) result;
1208         assertEquals(2, map.size());
1209         assertEquals(1, map.get("x"));
1210         assertEquals(2, map.get("y"));
1211     }
1212 
1213     @Test
1214     void testIssue398c() {
1215         final JexlEngine jexl = new JexlBuilder().create();
1216         final Object empty = jexl.createScript("[,...]").execute(null);
1217         assertNotNull(empty);
1218         assertTrue(jexl.createScript("[1]").execute(null) instanceof int[]);
1219         assertTrue(jexl.createScript("[1,...]").execute(null) instanceof ArrayList<?>);
1220         assertTrue(jexl.createScript("{1}").execute(null) instanceof HashSet<?>);
1221         assertTrue(jexl.createScript("{1,...}").execute(null) instanceof LinkedHashSet<?>);
1222         assertTrue(jexl.createScript("{'one': 1}").execute(null) instanceof HashMap<?, ?>);
1223         assertTrue(jexl.createScript("{'one': 1,...}").execute(null) instanceof LinkedHashMap<?, ?>);
1224     }
1225 
1226     @Test
1227     void testPropagateOptions() {
1228         // @formatter:off
1229         final String src0 = "`${$options.strict?'+':'-'}strict"
1230                 + " ${$options.cancellable?'+':'-'}cancellable"
1231                 + " ${$options.lexical?'+':'-'}lexical"
1232                 + " ${$options.lexicalShade?'+':'-'}lexicalShade"
1233                 + " ${$options.sharedInstance?'+':'-'}sharedInstance"
1234                 + " ${$options.safe?'+':'-'}safe`";
1235         // @formatter:on
1236         final String text = "#pragma script.mode pro50\n" + "()->{ ()->{ " + src0 + "; } }";
1237         final JexlEngine jexl = new JexlBuilder().safe(true).create();
1238         final JexlScript script = jexl.createScript(text);
1239         JexlContext context = pragmaticContext();
1240         final JexlScript closure = (JexlScript) script.execute(context);
1241         final JexlContext opts = new OptionsContext();
1242         final Object result = closure.execute(opts);
1243         assertEquals("+strict +cancellable +lexical +lexicalShade -sharedInstance -safe", result);
1244 
1245         final String text0 = "#pragma script.mode pro50\n" + "()->{ " + src0 + "; }";
1246         final JexlScript script0 = jexl.createScript(text0);
1247         context = pragmaticContext();
1248         final Object result0 = script0.execute(context);
1249         assertEquals("+strict +cancellable +lexical +lexicalShade -sharedInstance -safe", result0);
1250 
1251         final String text1 = "#pragma script.mode pro50\n" + src0;
1252         final JexlScript script1 = jexl.createScript(text1);
1253         context = pragmaticContext();
1254         final Object result1 = script1.execute(context);
1255         assertEquals("+strict +cancellable +lexical +lexicalShade -sharedInstance -safe", result1);
1256 
1257         final String text2 = src0;
1258         final JexlScript script2 = jexl.createScript(text2);
1259         context = pragmaticContext();
1260         final Object result2 = script2.execute(context);
1261         assertEquals("-strict -cancellable -lexical -lexicalShade +sharedInstance +safe", result2);
1262     }
1263 
1264     @Test
1265     void tests301b() {
1266         final JexlEngine jexl = new JexlBuilder().safe(false).arithmetic(new JexlArithmetic(false)).create();
1267         final Object[] xs = { null, null, new Object[] { null, 1 } };
1268         final String[] srcs = { "x.0", "x[0]", "x[0][0]" };
1269         final JexlContext ctxt = new MapContext();
1270         for (int i = 0; i < xs.length; ++i) {
1271             ctxt.set("x", xs[i]);
1272             final String src = srcs[i];
1273             final JexlScript s = jexl.createScript(src);
1274             assertThrows(JexlException.class, () -> s.execute(null));
1275         }
1276     }
1277 
1278     @Test
1279     void testSO20220930() {
1280         // fill some drivers in a list
1281         final List<Driver0930> values = new ArrayList<>();
1282         for (int i = 0; i < 8; ++i) {
1283             values.add(new Driver0930("drvr" + Integer.toOctalString(i)));
1284         }
1285         for (int i = 0; i < 4; ++i) {
1286             values.add(new Driver0930("favorite" + Integer.toOctalString(i)));
1287         }
1288         // Use a context that can filter and that exposes Collectors
1289         final JexlEngine jexl = new JexlBuilder().safe(false).create();
1290         final JexlContext context = new Context0930();
1291         context.set("values", values);
1292         context.set("Collectors", Collectors.class);
1293         // The script with a JEXL 3.2 (lambda function) and 3.3 syntax (lambda expression)
1294         final String src32 = "values.stream().filter((driver) ->{ driver.attributeName =^ 'favorite' }).collect(Collectors.toList())";
1295         final String src33 = "values.stream().filter(driver -> driver.attributeName =^ 'favorite').collect(Collectors.toList())";
1296         for (final String src : Arrays.asList(src32, src33)) {
1297             final JexlExpression s = jexl.createExpression(src);
1298             assertNotNull(s);
1299 
1300             final Object r = s.evaluate(context);
1301             assertNotNull(r);
1302             // got a filtered list of 4 drivers whose attribute name starts with 'favorite'
1303             final List<Driver0930> l = (List<Driver0930>) r;
1304             assertEquals(4, l.size());
1305             for (final Driver0930 d : l) {
1306                 assertTrue(d.getAttributeName().startsWith("favorite"));
1307             }
1308         }
1309     }
1310 
1311     @Test
1312     void testUnsolvableMethod() {
1313         final JexlEngine jexl = new JexlBuilder().create();
1314         // @formatter:off
1315         final JexlScript script = jexl.createScript(
1316             "var myFunction1 = function(object) {"
1317                 + " myNonExistentFunction();"
1318                 + "}"
1319                 + "var myFunction2 = function(object) {"
1320                 + " myFunction1();"
1321                 + "}"
1322                 + "myFunction2();");
1323         // @formatter:on
1324         final JexlException.Method unsolvable = assertThrows(JexlException.Method.class, () -> script.execute(new MapContext()));
1325         assertEquals("myNonExistentFunction", unsolvable.getMethod());
1326     }
1327 }