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