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.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.assertFalse;
21  import static org.junit.jupiter.api.Assertions.assertNotNull;
22  import static org.junit.jupiter.api.Assertions.assertNull;
23  import static org.junit.jupiter.api.Assertions.assertThrows;
24  import static org.junit.jupiter.api.Assertions.assertTrue;
25  import static org.junit.jupiter.api.Assertions.fail;
26  
27  import java.io.ByteArrayInputStream;
28  import java.io.InputStream;
29  import java.math.BigDecimal;
30  import java.math.BigInteger;
31  import java.math.MathContext;
32  import java.nio.charset.StandardCharsets;
33  import java.util.Arrays;
34  import java.util.Collections;
35  import java.util.Date;
36  import java.util.HashMap;
37  import java.util.List;
38  import java.util.Map;
39  import java.util.Objects;
40  import java.util.concurrent.atomic.AtomicBoolean;
41  
42  import javax.xml.parsers.DocumentBuilder;
43  import javax.xml.parsers.DocumentBuilderFactory;
44  
45  import org.apache.commons.jexl3.junit.Asserter;
46  import org.apache.commons.lang3.SystemProperties;
47  import org.junit.jupiter.api.BeforeEach;
48  import org.junit.jupiter.api.Test;
49  import org.w3c.dom.Attr;
50  import org.w3c.dom.Document;
51  import org.w3c.dom.NamedNodeMap;
52  import org.w3c.dom.Node;
53  
54  @SuppressWarnings({"UnnecessaryBoxing", "AssertEqualsBetweenInconvertibleTypes"})
55  class ArithmeticTest extends JexlTestCase {
56      public static class Arithmetic132 extends JexlArithmetic {
57          public Arithmetic132() {
58              super(false);
59          }
60  
61          @Override
62          public Object divide(final Object left, final Object right) {
63              if (left == null && right == null) {
64                  return controlNullNullOperands(JexlOperator.DIVIDE);
65              }
66              // if either are bigdecimal use that type
67              if (left instanceof BigDecimal || right instanceof BigDecimal) {
68                  final BigDecimal l = toBigDecimal(left);
69                  final BigDecimal r = toBigDecimal(right);
70                  if (BigDecimal.ZERO.equals(r)) {
71                      return divideZero(l);
72                  }
73                  final BigDecimal result = l.divide(r, getMathContext());
74                  return narrowBigDecimal(left, right, result);
75              }
76              // if either are floating point (double or float) use double
77              if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
78                  final double l = toDouble(left);
79                  final double r = toDouble(right);
80                  return Double.valueOf(l / r);
81              }
82              // otherwise treat as integers
83              final BigInteger l = toBigInteger(left);
84              final BigInteger r = toBigInteger(right);
85              if (BigInteger.ZERO.equals(r)) {
86                  return divideZero(l);
87              }
88              final BigInteger result = l.divide(r);
89              return narrowBigInteger(left, right, result);
90          }
91  
92          protected double divideZero(final BigDecimal x) {
93              final int ls = x.signum();
94              if (ls < 0) {
95                  return Double.NEGATIVE_INFINITY;
96              }
97              if (ls > 0) {
98                  return Double.POSITIVE_INFINITY;
99              }
100             return Double.NaN;
101         }
102 
103         protected double divideZero(final BigInteger x) {
104             final int ls = x.signum();
105             if (ls < 0) {
106                 return Double.NEGATIVE_INFINITY;
107             }
108             if (ls > 0) {
109                 return Double.POSITIVE_INFINITY;
110             }
111             return Double.NaN;
112         }
113 
114         @Override
115         public Object mod(final Object left, final Object right) {
116             if (left == null && right == null) {
117                 return controlNullNullOperands(JexlOperator.MOD);
118             }
119             // if either are bigdecimal use that type
120             if (left instanceof BigDecimal || right instanceof BigDecimal) {
121                 final BigDecimal l = toBigDecimal(left);
122                 final BigDecimal r = toBigDecimal(right);
123                 if (BigDecimal.ZERO.equals(r)) {
124                     return divideZero(l);
125                 }
126                 final BigDecimal remainder = l.remainder(r, getMathContext());
127                 return narrowBigDecimal(left, right, remainder);
128             }
129             // if either are floating point (double or float) use double
130             if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
131                 final double l = toDouble(left);
132                 final double r = toDouble(right);
133                 return Double.valueOf(l % r);
134             }
135             // otherwise treat as integers
136             final BigInteger l = toBigInteger(left);
137             final BigInteger r = toBigInteger(right);
138             final BigInteger result = l.mod(r);
139             if (BigInteger.ZERO.equals(r)) {
140                 return divideZero(l);
141             }
142             return narrowBigInteger(left, right, result);
143         }
144     }
145     // an arithmetic that fail systematically with vars
146     public static class ArithmeticFail extends JexlArithmetic {
147         public ArithmeticFail(final boolean strict) {
148             super(strict);
149         }
150 
151         @Override
152         public Object add(final Object lhs, final Object rhs) {
153             throw new ArithmeticException(lhs + " o " + rhs);
154         }
155 
156         @Override
157         public Object and(final Object lhs, final Object rhs) {
158             throw new ArithmeticException(lhs + " o " + rhs);
159         }
160 
161         @Override
162         public Object complement(final Object arg) {
163             throw new ArithmeticException(Objects.toString(arg));
164         }
165 
166         @Override
167         public Boolean contains(final Object lhs, final Object rhs) {
168             throw new ArithmeticException(lhs + " o " + rhs);
169         }
170 
171         @Override
172         public Object decrement(final Object arg) {
173             throw new ArithmeticException(Objects.toString(arg));
174         }
175 
176         @Override
177         public Object divide(final Object lhs, final Object rhs) {
178             throw new ArithmeticException(lhs + " o " + rhs);
179         }
180 
181         @Override
182         public Boolean endsWith(final Object lhs, final Object rhs) {
183             throw new ArithmeticException(lhs + " o " + rhs);
184         }
185 
186         @Override
187         public boolean equals(final Object lhs, final Object rhs) {
188             throw new ArithmeticException(lhs + " o " + rhs);
189         }
190 
191         @Override
192         public boolean greaterThan(final Object lhs, final Object rhs) {
193             throw new ArithmeticException(lhs + " o " + rhs);
194         }
195 
196         @Override
197         public boolean greaterThanOrEqual(final Object lhs, final Object rhs) {
198             throw new ArithmeticException(lhs + " o " + rhs);
199         }
200 
201         @Override
202         public Object increment(final Object arg) {
203             throw new ArithmeticException(Objects.toString(arg));
204         }
205 
206         @Override
207         public boolean lessThan(final Object lhs, final Object rhs) {
208             throw new ArithmeticException(lhs + " o " + rhs);
209         }
210 
211         @Override
212         public boolean lessThanOrEqual(final Object lhs, final Object rhs) {
213             throw new ArithmeticException(lhs + " o " + rhs);
214         }
215 
216         @Override
217         public Object mod(final Object lhs, final Object rhs) {
218             throw new ArithmeticException(lhs + " o " + rhs);
219         }
220 
221         @Override
222         public Object multiply(final Object lhs, final Object rhs) {
223             throw new ArithmeticException(lhs + " o " + rhs);
224         }
225 
226         @Override
227         public Object negate(final Object arg) {
228             throw new ArithmeticException(Objects.toString(arg));
229         }
230 
231         @Override
232         public Object not(final Object arg) {
233             throw new ArithmeticException(Objects.toString(arg));
234         }
235 
236         @Override
237         public Object or(final Object lhs, final Object rhs) {
238             throw new ArithmeticException(lhs + " o " + rhs);
239         }
240 
241         @Override
242         public Object shiftLeft(final Object lhs, final Object rhs) {
243             throw new ArithmeticException(lhs + " o " + rhs);
244         }
245 
246         @Override
247         public Object shiftRight(final Object lhs, final Object rhs) {
248             throw new ArithmeticException(lhs + " o " + rhs);
249         }
250 
251         @Override
252         public Object shiftRightUnsigned(final Object lhs, final Object rhs) {
253             throw new ArithmeticException(lhs + " o " + rhs);
254         }
255 
256         @Override
257         public Boolean startsWith(final Object lhs, final Object rhs) {
258             throw new ArithmeticException(lhs + " o " + rhs);
259         }
260 
261         @Override
262         public boolean strictEquals(final Object lhs, final Object rhs) {
263             throw new ArithmeticException(lhs + " o " + rhs);
264         }
265 
266         @Override
267         public Object subtract(final Object lhs, final Object rhs) {
268             throw new ArithmeticException(lhs + " o " + rhs);
269         }
270 
271         @Override
272         public boolean toBoolean(final Object x) {
273             throw new ArithmeticException(Objects.toString(x));
274         }
275 
276         @Override
277         public Object xor(final Object lhs, final Object rhs) {
278             throw new ArithmeticException(lhs + " o " + rhs);
279         }
280     }
281 
282     // an arithmetic that knows how to deal with vars
283     public static class ArithmeticPlus extends JexlArithmetic {
284         public ArithmeticPlus(final boolean strict) {
285             super(strict);
286         }
287 
288         public Var add(final Var lhs, final Var rhs) {
289             return new Var(lhs.value + rhs.value);
290         }
291 
292         public Var and(final Var lhs, final Var rhs) {
293             return new Var(lhs.value & rhs.value);
294         }
295 
296         public Var complement(final Var arg) {
297             return new Var(~arg.value);
298         }
299 
300         public Boolean contains(final Var lhs, final Var rhs) {
301             return lhs.toString().contains(rhs.toString());
302         }
303 
304         public Var divide(final Var lhs, final Var rhs) {
305             return new Var(lhs.value / rhs.value);
306         }
307 
308         public Boolean endsWith(final Var lhs, final Var rhs) {
309             return lhs.toString().endsWith(rhs.toString());
310         }
311 
312         public boolean equals(final Var lhs, final Var rhs) {
313             return lhs.value == rhs.value;
314         }
315 
316         public boolean greaterThan(final Var lhs, final Var rhs) {
317             return lhs.value > rhs.value;
318         }
319 
320         public boolean greaterThanOrEqual(final Var lhs, final Var rhs) {
321             return lhs.value >= rhs.value;
322         }
323 
324         public boolean lessThan(final Var lhs, final Var rhs) {
325             return lhs.value < rhs.value;
326         }
327 
328         public boolean lessThanOrEqual(final Var lhs, final Var rhs) {
329             return lhs.value <= rhs.value;
330         }
331 
332         public Var mod(final Var lhs, final Var rhs) {
333             return new Var(lhs.value / rhs.value);
334         }
335 
336         public Var multiply(final Var lhs, final Var rhs) {
337             return new Var(lhs.value * rhs.value);
338         }
339 
340         public Object negate(final String str) {
341             final int length = str.length();
342             final StringBuilder strb = new StringBuilder(str.length());
343             for (int c = length - 1; c >= 0; --c) {
344                 strb.append(str.charAt(c));
345             }
346             return strb.toString();
347         }
348 
349         public Var negate(final Var arg) {
350             return new Var(-arg.value);
351         }
352 
353         public Object not(final Var x) {
354             throw new NullPointerException("make it fail");
355         }
356 
357         public Var or(final Var lhs, final Var rhs) {
358             return new Var(lhs.value | rhs.value);
359         }
360 
361         public Var shiftLeft(final Var lhs, final Var rhs) {
362             return new Var(lhs.value << rhs.value);
363         }
364 
365         public Var shiftRight(final Var lhs, final Var rhs) {
366             return new Var(lhs.value >> rhs.value);
367         }
368 
369         public Var shiftRightUnsigned(final Var lhs, final Var rhs) {
370             return new Var(lhs.value >>> rhs.value);
371         }
372 
373         public Boolean startsWith(final Var lhs, final Var rhs) {
374             return lhs.toString().startsWith(rhs.toString());
375         }
376 
377         public Object subtract(final String x, final String y) {
378             final int ix = x.indexOf(y);
379             if (ix < 0) {
380                 return x;
381             }
382             final StringBuilder strb = new StringBuilder(x.substring(0, ix));
383             strb.append(x.substring(ix + y.length()));
384             return strb.toString();
385         }
386 
387         public Var subtract(final Var lhs, final Var rhs) {
388             return new Var(lhs.value - rhs.value);
389         }
390 
391         public Var xor(final Var lhs, final Var rhs) {
392             return new Var(lhs.value ^ rhs.value);
393         }
394     }
395 
396     public static class Callable173 {
397         public Object call(final Integer... arg) {
398             return arg[0] * arg[1];
399         }
400         public Object call(final String... arg) {
401             return 42;
402         }
403     }
404 
405     public static class EmptyTestContext extends MapContext implements JexlContext.NamespaceResolver {
406         public static int log(final Object fmt, final int... arr) {
407             //System.out.println(String.format(fmt.toString(), arr));
408             return arr == null ? 0 : arr.length;
409         }
410 
411         public static int log(final Object fmt, final Object... arr) {
412             //System.out.println(String.format(fmt.toString(), arr));
413             return arr == null ? 0 : arr.length;
414         }
415 
416         @Override
417         public Object resolveNamespace(final String name) {
418             return this;
419         }
420     }
421     public static class InstanceofContext extends MapContext implements JexlContext.ClassNameResolver {
422         @Override
423         public String resolveClassName(final String name) {
424             if ("Double".equals(name)) {
425                 return Double.class.getName();
426             }
427             return null;
428         }
429     }
430     public static class Var {
431         int value;
432 
433         Var(final int v) {
434             value = v;
435         }
436 
437         @Override
438         public String toString() {
439             return Integer.toString(value);
440         }
441     }
442 
443     public static class XmlArithmetic extends JexlArithmetic {
444         public XmlArithmetic(final boolean astrict) {
445             super(astrict);
446         }
447 
448         public XmlArithmetic(final boolean astrict, final MathContext bigdContext, final int bigdScale) {
449             super(astrict, bigdContext, bigdScale);
450         }
451 
452         public boolean empty(final org.w3c.dom.Element elt) {
453             return !elt.hasAttributes() && !elt.hasChildNodes();
454         }
455 
456         public int size(final org.w3c.dom.Element elt) {
457             return elt.getChildNodes().getLength();
458         }
459     }
460 
461     /** A small delta to compare doubles. */
462     private static final double EPSILON = 1.e-6;
463 
464     private static void assertArithmeticException(final java.util.function.Supplier<Object> fun) {
465         assertThrows(ArithmeticException.class, fun::get);
466     }
467 
468     private static void assertNullOperand(final java.util.function.Supplier<Object> fun) {
469         try {
470             assertNull(fun.get());
471         } catch (final JexlArithmetic.NullOperand xany) {
472             assertNotNull(xany);
473         }
474     }
475 
476     private static Document getDocument(final String xml) throws Exception {
477         final DocumentBuilder xmlBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
478         final InputStream stringInputStream = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8));
479         return xmlBuilder.parse(stringInputStream);
480     }
481 
482     /**
483      * Returns the Java version as an int value.
484      * @return the Java version as an int value (8, 9, etc.)
485      */
486     private static int getJavaVersion() {
487         String version = SystemProperties.getJavaVersion();
488         if (version.startsWith("1.")) {
489             version = version.substring(2);
490         }
491         int sep = version.indexOf(".");
492         if (sep < 0) {
493             sep = version.indexOf("-");
494         }
495         if (sep > 0) {
496             version = version.substring(0, sep);
497         }
498         return Integer.parseInt(version);
499     }
500 
501     private final Asserter asserter;
502 
503     private final JexlArithmetic jexla;
504 
505     private final JexlArithmetic jexlb;
506 
507     private final Date date = new Date();
508 
509     public ArithmeticTest() {
510         super("ArithmeticTest");
511         asserter = new Asserter(JEXL);
512         jexla = JEXL.getArithmetic();
513         final JexlOptions options = new JexlOptions();
514         options.setStrictArithmetic(false);
515         jexlb = jexla.options(options);
516     }
517 
518     void checkEmpty(final Object x, final boolean expect) {
519         final JexlScript s0 = JEXL.createScript("empty(x)", "x");
520         boolean empty = (Boolean) s0.execute(null, x);
521         assertEquals(expect, empty);
522         final JexlScript s1 = JEXL.createScript("empty x", "x");
523         empty = (Boolean) s1.execute(null, x);
524         assertEquals(expect, empty);
525         final JexlScript s2 = JEXL.createScript("x.empty()", "x");
526         empty = (Boolean) s2.execute(null, x);
527         assertEquals(expect, empty);
528     }
529 
530     private void runInstanceof(final JexlEngine jexl, final JexlContext ctxt) {
531         Object r = jexl.createExpression("3.0 instanceof 'Double'").evaluate(ctxt);
532         assertTrue((Boolean) r);
533         r = jexl.createExpression("'3.0' !instanceof 'Double'").evaluate(ctxt);
534         assertTrue((Boolean) r);
535         JexlScript script = jexl.createScript("x instanceof y", "x", "y");
536         r = script.execute(ctxt, "foo", String.class);
537         assertTrue((Boolean) r);
538         r = script.execute(ctxt, 42.0, Double.class);
539         assertTrue((Boolean) r);
540         script = jexl.createScript("x !instanceof y", "x", "y");
541         r = script.execute(ctxt, "foo", Double.class);
542         assertTrue((Boolean) r);
543         r = script.execute(ctxt, 42.0, String.class);
544         assertTrue((Boolean) r);
545     }
546 
547     protected void runOverload(final JexlEngine jexl, final JexlContext jc) {
548         JexlScript script;
549         Object result;
550 
551         script = jexl.createScript("(x, y)->{ x < y }");
552         result = script.execute(jc, 42, 43);
553         assertEquals(true, result);
554         result = script.execute(jc, new Var(42), new Var(43));
555         assertEquals(true, result);
556         result = script.execute(jc, new Var(42), new Var(43));
557         assertEquals(true, result);
558         result = script.execute(jc, 43, 42);
559         assertEquals(false, result);
560         result = script.execute(jc, new Var(43), new Var(42));
561         assertEquals(false, result);
562 
563         script = jexl.createScript("(x, y)->{ x <= y }");
564         result = script.execute(jc, 42, 43);
565         assertEquals(true, result);
566         result = script.execute(jc, new Var(42), new Var(43));
567         assertEquals(true, result);
568         result = script.execute(jc, new Var(41), new Var(44));
569         assertEquals(true, result);
570         result = script.execute(jc, 43, 42);
571         assertEquals(false, result);
572         result = script.execute(jc, new Var(45), new Var(40));
573         assertEquals(false, result);
574 
575         script = jexl.createScript("(x, y)->{ x > y }");
576         result = script.execute(jc, 42, 43);
577         assertEquals(false, result);
578         result = script.execute(jc, new Var(42), new Var(43));
579         assertEquals(false, result);
580         result = script.execute(jc, new Var(42), new Var(43));
581         assertEquals(false, result);
582         result = script.execute(jc, 43, 42);
583         assertEquals(true, result);
584         result = script.execute(jc, new Var(43), new Var(42));
585         assertEquals(true, result);
586 
587         script = jexl.createScript("(x, y)->{ x >= y }");
588         result = script.execute(jc, 42, 43);
589         assertEquals(false, result);
590         result = script.execute(jc, new Var(42), new Var(43));
591         assertEquals(false, result);
592         result = script.execute(jc, new Var(41), new Var(44));
593         assertEquals(false, result);
594         result = script.execute(jc, 43, 42);
595         assertEquals(true, result);
596         result = script.execute(jc, new Var(45), new Var(40));
597         assertEquals(true, result);
598 
599         script = jexl.createScript("(x, y)->{ x == y }");
600         result = script.execute(jc, 42, 43);
601         assertEquals(false, result);
602         result = script.execute(jc, new Var(42), new Var(43));
603         assertEquals(false, result);
604         result = script.execute(jc, new Var(41), new Var(44));
605         assertEquals(false, result);
606         result = script.execute(jc, 43, 42);
607         assertEquals(false, result);
608         result = script.execute(jc, new Var(45), new Var(40));
609         assertEquals(false, result);
610 
611         script = jexl.createScript("(x, y)->{ x != y }");
612         result = script.execute(jc, 42, 43);
613         assertEquals(true, result);
614         result = script.execute(jc, new Var(42), new Var(43));
615         assertEquals(true, result);
616         result = script.execute(jc, new Var(44), new Var(44));
617         assertEquals(false, result);
618         result = script.execute(jc, 44, 44);
619         assertEquals(false, result);
620         result = script.execute(jc, new Var(45), new Var(40));
621         assertEquals(true, result);
622 
623         script = jexl.createScript("(x, y)->{ x % y }");
624         result = script.execute(jc, 4242, 100);
625         assertEquals(42, result);
626         result = script.execute(jc, new Var(4242), new Var(100));
627         assertEquals(42, ((Var) result).value);
628 
629         script = jexl.createScript("(x, y)->{ x * y }");
630         result = script.execute(jc, 6, 7);
631         assertEquals(42, result);
632         result = script.execute(jc, new Var(6), new Var(7));
633         assertEquals(42, ((Var) result).value);
634 
635         script = jexl.createScript("(x, y)->{ x + y }");
636         result = script.execute(jc, 35, 7);
637         assertEquals(42, result);
638         result = script.execute(jc, new Var(35), new Var(7));
639         assertEquals(42, ((Var) result).value);
640 
641         script = jexl.createScript("(x, y)->{ x - y }");
642         result = script.execute(jc, 49, 7);
643         assertEquals(42, result);
644         result = script.execute(jc, "foobarquux", "bar");
645         assertEquals("fooquux", result);
646         result = script.execute(jc, 50, 8);
647         assertEquals(42, result);
648         result = script.execute(jc, new Var(50), new Var(8));
649         assertEquals(42, ((Var) result).value);
650 
651         script = jexl.createScript("(x)->{ -x }");
652         result = script.execute(jc, -42);
653         assertEquals(42, result);
654         result = script.execute(jc, new Var(-42));
655         assertEquals(42, ((Var) result).value);
656         result = script.execute(jc, "pizza");
657         assertEquals("azzip", result);
658         result = script.execute(jc, -142);
659         assertEquals(142, result);
660 
661         script = jexl.createScript("(x)->{ ~x }");
662         result = script.execute(jc, -1);
663         assertEquals(0L, result);
664         result = script.execute(jc, new Var(-1));
665         assertEquals(0L, ((Var) result).value);
666         result = script.execute(jc, new Var(-42));
667         assertEquals(41, ((Var) result).value);
668 
669         script = jexl.createScript("(x, y)->{ x ^ y }");
670         result = script.execute(jc, 35, 7);
671         assertEquals(36L, result);
672         result = script.execute(jc, new Var(35), new Var(7));
673         assertEquals(36L, ((Var) result).value);
674         // legacy
675         script = jexl.createScript("(x, y)->bitwiseXor(x,y)");
676         result = script.execute(jc, 35, 7);
677         assertEquals(36L, result);
678 
679         script = jexl.createScript("(x, y)->{ x | y }");
680         result = script.execute(jc, 35, 7);
681         assertEquals(39L, result);
682         result = script.execute(jc, new Var(35), new Var(7));
683         assertEquals(39L, ((Var) result).value);
684         // legacy
685         script = jexl.createScript("(x, y)->bitwiseOr(x,y)");
686         result = script.execute(jc, 35, 7);
687         assertEquals(39L, result);
688 
689         script = jexl.createScript("(x, y)->{ x << y }");
690         result = script.execute(jc, 35, 1);
691         assertEquals(70L, result);
692         result = script.execute(jc, new Var(35), new Var(1));
693         assertEquals(70L, ((Var) result).value);
694 
695         script = jexl.createScript("(x, y)->{ x >> y }");
696         result = script.execute(jc, 42, 1);
697         assertEquals(21L, result);
698         result = script.execute(jc, new Var(42), new Var(1));
699         assertEquals(21, ((Var) result).value);
700 
701         script = jexl.createScript("(x, y)->{ x >>> y }");
702         result = script.execute(jc, 84, 2);
703         assertEquals(21L, result);
704         result = script.execute(jc, new Var(84), new Var(2));
705         assertEquals(21, ((Var) result).value);
706 
707         script = jexl.createScript("(x, y)->{ x & y }");
708         result = script.execute(jc, 35, 7);
709         assertEquals(3L, result);
710         result = script.execute(jc, new Var(35), new Var(7));
711         assertEquals(3L, ((Var) result).value);
712         // legacy
713         script = jexl.createScript("(x, y)->bitwiseAnd(x,y)");
714         result = script.execute(jc, 35, 7);
715         assertEquals(3L, result);
716 
717         script = jexl.createScript("(x, y)->{ x =^ y }");
718         result = script.execute(jc, 3115, 31);
719         assertFalse((Boolean) result);
720         result = script.execute(jc, new Var(3115), new Var(31));
721         assertTrue((Boolean) result);
722 
723         script = jexl.createScript("(x, y)->{ x !^ y }");
724         result = script.execute(jc, 3115, 31);
725         assertTrue((Boolean) result);
726         result = script.execute(jc, new Var(3115), new Var(31));
727         assertFalse((Boolean) result);
728 
729         script = jexl.createScript("(x, y)->{ x =$ y }");
730         result = script.execute(jc, 3115, 15);
731         assertFalse((Boolean) result);
732         result = script.execute(jc, new Var(3115), new Var(15));
733         assertTrue((Boolean) result);
734 
735         script = jexl.createScript("(x, y)->{ x !$ y }");
736         result = script.execute(jc, 3115, 15);
737         assertTrue((Boolean) result);
738         result = script.execute(jc, new Var(3115), new Var(15));
739         assertFalse((Boolean) result);
740 
741         script = jexl.createScript("(x, y)->{ x =~ y }");
742         result = script.execute(jc, 3155, 15);
743         assertFalse((Boolean) result);
744         result = script.execute(jc, new Var(3155), new Var(15));
745         assertFalse((Boolean) result);
746         result = script.execute(jc, new Var(15), new Var(3155));
747         assertTrue((Boolean) result);
748 
749         script = jexl.createScript("(x, y)->{ x !~ y }");
750         result = script.execute(jc, 3115, 15);
751         assertTrue((Boolean) result);
752         result = script.execute(jc, new Var(3155), new Var(15));
753         assertTrue((Boolean) result);
754         result = script.execute(jc, new Var(15), new Var(3155));
755         assertFalse((Boolean) result);
756 
757         final JexlScript script1 = jexl.createScript("(x)->{ !x }");
758         assertThrows(JexlException.Operator.class, () -> script1.execute(jc, new Var(-42)));
759     }
760 
761     @BeforeEach
762     @Override
763     public void setUp() {
764     }
765 
766     // JEXL-24: doubles with exponent
767     @Test
768     void test2DoubleLiterals() {
769         final JexlEvalContext ctxt = new JexlEvalContext();
770         final JexlOptions options = ctxt.getEngineOptions();
771         options.setStrictArithmetic(true);
772         final String stmt = "{a = 42.0e1D; b = 42.0E+2D; c = 42.0e-1d; d = 42.0E-2d; e=10e10; f= +1.e1; g=1e1; }";
773         final JexlScript expr = JEXL.createScript(stmt);
774         /* Object value = */ expr.execute(ctxt);
775         assertEquals(Double.valueOf("42.0e+1"), ctxt.get("a"));
776         assertEquals(Double.valueOf("42.0e+2"), ctxt.get("b"));
777         assertEquals(Double.valueOf("42.0e-1"), ctxt.get("c"));
778         assertEquals(Double.valueOf("42.0e-2"), ctxt.get("d"));
779         assertEquals(Double.valueOf("10e10"), ctxt.get("e"));
780         assertEquals(Double.valueOf("10"), ctxt.get("f"));
781         assertEquals(Double.valueOf("10"), ctxt.get("g"));
782     }
783 
784     @Test
785     void testAddWithStringsLenient() {
786         final JexlEngine jexl = new JexlBuilder().arithmetic(new JexlArithmetic(false)).create();
787         JexlScript script;
788         Object result;
789         script = jexl.createScript("'a' + 0");
790         result = script.execute(null);
791         assertEquals("a0", result);
792 
793         script = jexl.createScript("0 + 'a' ");
794         result = script.execute(null);
795         assertEquals("0a", result);
796 
797         script = jexl.createScript("0 + '1.2' ");
798         result = script.execute(null);
799         assertEquals(1.2d, (Double) result, EPSILON);
800 
801         script = jexl.createScript("'1.2' + 1.2 ");
802         result = script.execute(null);
803         assertEquals(2.4d, (Double) result, EPSILON);
804 
805         script = jexl.createScript("1.2 + 1.2 ");
806         result = script.execute(null);
807         assertEquals(2.4d, (Double) result, EPSILON);
808 
809         script = jexl.createScript("1.2 + '1.2' ");
810         result = script.execute(null);
811         assertEquals(2.4d, (Double) result, EPSILON);
812 
813         script = jexl.createScript("'1.2' + 0 ");
814         result = script.execute(null);
815         assertEquals(1.2d, (Double) result, EPSILON);
816 
817         script = jexl.createScript("'1.2' + '1.2' ");
818         result = script.execute(null);
819         assertEquals("1.21.2", result);
820     }
821 
822     @Test
823     void testAddWithStringsStrict() {
824         final JexlEngine jexl = new JexlBuilder().arithmetic(new JexlArithmetic(true)).create();
825         JexlScript script;
826         Object result;
827         script = jexl.createScript("'a' + 0");
828         result = script.execute(null);
829         assertEquals("a0", result);
830 
831         script = jexl.createScript("0 + 'a' ");
832         result = script.execute(null);
833         assertEquals("0a", result);
834 
835         script = jexl.createScript("0 + '1.2' ");
836         result = script.execute(null);
837         assertEquals("01.2", result);
838 
839         script = jexl.createScript("'1.2' + 1.2 ");
840         result = script.execute(null);
841         assertEquals("1.21.2", result);
842 
843         script = jexl.createScript("1.2 + 1.2 ");
844         result = script.execute(null);
845         assertEquals(2.4d, (Double) result, EPSILON);
846 
847         script = jexl.createScript("1.2 + '1.2' ");
848         result = script.execute(null);
849         assertEquals("1.21.2", result);
850 
851         script = jexl.createScript("'1.2' + 0 ");
852         result = script.execute(null);
853         assertEquals("1.20", result);
854 
855         script = jexl.createScript("'1.2' + '1.2' ");
856         result = script.execute(null);
857         assertEquals("1.21.2", result);
858 
859     }
860 
861     @Test
862     void testArithmeticPlus() {
863         final JexlEngine jexl = new JexlBuilder().cache(64).arithmetic(new ArithmeticPlus(false)).create();
864         final JexlContext jc = new EmptyTestContext();
865         runOverload(jexl, jc);
866         runOverload(jexl, jc);
867     }
868 
869     @Test
870     void testArithmeticPlusNoCache() {
871         final JexlEngine jexl = new JexlBuilder().cache(0).arithmetic(new ArithmeticPlus(false)).create();
872         final JexlContext jc = new EmptyTestContext();
873         runOverload(jexl, jc);
874     }
875 
876     @Test
877     void testAtomicBoolean() {
878         // in a condition
879         JexlScript e = JEXL.createScript("if (x) 1 else 2;", "x");
880         final JexlArithmetic jexla = JEXL.getArithmetic();
881         final JexlContext jc = new MapContext();
882         final AtomicBoolean ab = new AtomicBoolean();
883         Object o;
884         o = e.execute(jc, ab);
885         assertEquals(Integer.valueOf(2), o);
886         ab.set(true);
887         o = e.execute(jc, ab);
888         assertEquals(Integer.valueOf(1), o);
889         // in a binary logical op
890         e = JEXL.createScript("x && y", "x", "y");
891         ab.set(true);
892         o = e.execute(jc, ab, Boolean.FALSE);
893         assertFalse(jexla.toBoolean(o));
894         ab.set(true);
895         o = e.execute(jc, ab, Boolean.TRUE);
896         assertTrue(jexla.toBoolean(o));
897         ab.set(false);
898         o = e.execute(jc, ab, Boolean.FALSE);
899         assertFalse(jexla.toBoolean(o));
900         ab.set(false);
901         o = e.execute(jc, ab, Boolean.FALSE);
902         assertFalse(jexla.toBoolean(o));
903         // in arithmetic op
904         e = JEXL.createScript("x + y", "x", "y");
905         ab.set(true);
906         o = e.execute(jc, ab, 10);
907         assertEquals(11, o);
908         o = e.execute(jc, 10, ab);
909         assertEquals(11, o);
910         o = e.execute(jc, ab, 10.d);
911         assertEquals(11.d, (Double) o, EPSILON);
912         o = e.execute(jc, 10.d, ab);
913         assertEquals(11.d, (Double) o, EPSILON);
914 
915         final BigInteger bi10 = BigInteger.TEN;
916         ab.set(false);
917         o = e.execute(jc, ab, bi10);
918         assertEquals(bi10, o);
919         o = e.execute(jc, bi10, ab);
920         assertEquals(bi10, o);
921 
922         final BigDecimal bd10 = BigDecimal.TEN;
923         ab.set(false);
924         o = e.execute(jc, ab, bd10);
925         assertEquals(bd10, o);
926         o = e.execute(jc, bd10, ab);
927         assertEquals(bd10, o);
928 
929         // in a (the) monadic op
930         e = JEXL.createScript("!x", "x");
931         ab.set(true);
932         o = e.execute(jc, ab);
933         assertFalse((Boolean) o);
934         ab.set(false);
935         o = e.execute(jc, ab);
936         assertTrue((Boolean) o);
937 
938         // in a (the) monadic op
939         e = JEXL.createScript("-x", "x");
940         ab.set(true);
941         o = e.execute(jc, ab);
942         assertFalse((Boolean) o);
943         ab.set(false);
944         o = e.execute(jc, ab);
945         assertTrue((Boolean) o);
946     }
947 
948     @Test
949     void testBigDecimal() throws Exception {
950         asserter.setVariable("left", new BigDecimal(2));
951         asserter.setVariable("right", new BigDecimal(6));
952         asserter.assertExpression("left + right", new BigDecimal(8));
953         asserter.assertExpression("right - left", new BigDecimal(4));
954         asserter.assertExpression("right * left", new BigDecimal(12));
955         asserter.assertExpression("right / left", new BigDecimal(3));
956         asserter.assertExpression("right % left", new BigDecimal(0));
957     }
958 
959     @Test
960     void testBigdOp() {
961         final BigDecimal sevendot475 = new BigDecimal("7.475");
962         final BigDecimal SO = new BigDecimal("325");
963         final JexlContext jc = new MapContext();
964         jc.set("SO", SO);
965 
966         final String expr = "2.3*SO/100";
967 
968         final Object evaluate = JEXL.createExpression(expr).evaluate(jc);
969         assertEquals(sevendot475, evaluate);
970     }
971 
972     // JEXL-24: big decimals with exponent
973     @Test
974     void testBigExponentLiterals() {
975         final JexlEvalContext ctxt = new JexlEvalContext();
976         final JexlOptions options = ctxt.getEngineOptions();
977         options.setStrictArithmetic(true);
978         final String stmt = "{a = 42.0e1B; b = 42.0E+2B; c = 42.0e-1B; d = 42.0E-2b; e=4242.4242e1b}";
979         final JexlScript expr = JEXL.createScript(stmt);
980         /* Object value = */ expr.execute(ctxt);
981         assertEquals(new BigDecimal("42.0e+1"), ctxt.get("a"));
982         assertEquals(new BigDecimal("42.0e+2"), ctxt.get("b"));
983         assertEquals(new BigDecimal("42.0e-1"), ctxt.get("c"));
984         assertEquals(new BigDecimal("42.0e-2"), ctxt.get("d"));
985         assertEquals(new BigDecimal("4242.4242e1"), ctxt.get("e"));
986     }
987 
988     @Test
989     void testBigInteger() throws Exception {
990         asserter.setVariable("left", new BigInteger("2"));
991         asserter.setVariable("right", new BigInteger("6"));
992         asserter.assertExpression("left + right", new BigInteger("8"));
993         asserter.assertExpression("right - left", new BigInteger("4"));
994         asserter.assertExpression("right * left", new BigInteger("12"));
995         asserter.assertExpression("right / left", new BigInteger("3"));
996         asserter.assertExpression("right % left", new BigInteger("0"));
997     }
998 
999     // JEXL-24: big integers and big decimals
1000     @Test
1001     void testBigLiterals() {
1002         final JexlEvalContext ctxt = new JexlEvalContext();
1003         final JexlOptions options = ctxt.getEngineOptions();
1004         options.setStrictArithmetic(true);
1005         final String stmt = "{a = 10H; b = 10h; c = 42.0B; d = 42.0b;}";
1006         final JexlScript expr = JEXL.createScript(stmt);
1007         /* Object value = */ expr.execute(ctxt);
1008         assertEquals(new BigInteger("10"), ctxt.get("a"));
1009         assertEquals(new BigInteger("10"), ctxt.get("b"));
1010         assertEquals(new BigDecimal("42.0"), ctxt.get("c"));
1011         assertEquals(new BigDecimal("42.0"), ctxt.get("d"));
1012     }
1013 
1014     @Test
1015     void testBigLiteralValue() {
1016         final JexlEvalContext ctxt = new JexlEvalContext();
1017         final JexlOptions options = ctxt.getEngineOptions();
1018         options.setStrictArithmetic(true);
1019         final JexlExpression e = JEXL.createExpression("9223372036854775806.5B");
1020         final String res = String.valueOf(e.evaluate(ctxt));
1021         assertEquals("9223372036854775806.5", res);
1022     }
1023 
1024     /**
1025      * test some simple mathematical calculations
1026      */
1027     @Test
1028     void testCalculations() throws Exception {
1029         asserter.setStrict(true, false);
1030         /*
1031          * test new null coersion
1032          */
1033         asserter.setVariable("imanull", null);
1034         asserter.assertExpression("imanull + 2", Integer.valueOf(2));
1035         asserter.assertExpression("imanull + imanull", Integer.valueOf(0));
1036         asserter.setVariable("foo", Integer.valueOf(2));
1037 
1038         asserter.assertExpression("foo + 2", Integer.valueOf(4));
1039         asserter.assertExpression("3 + 3", Integer.valueOf(6));
1040         asserter.assertExpression("3 + 3 + foo", Integer.valueOf(8));
1041         asserter.assertExpression("3 * 3", Integer.valueOf(9));
1042         asserter.assertExpression("3 * 3 + foo", Integer.valueOf(11));
1043         asserter.assertExpression("3 * 3 - foo", Integer.valueOf(7));
1044 
1045         /*
1046          * test parenthesized exprs
1047          */
1048         asserter.assertExpression("(4 + 3) * 6", Integer.valueOf(42));
1049         asserter.assertExpression("(8 - 2) * 7", Integer.valueOf(42));
1050 
1051         /*
1052          * test some floaty stuff
1053          */
1054         asserter.assertExpression("3 * \"3.0\"", Double.valueOf(9));
1055         asserter.assertExpression("3 * 3.0", Double.valueOf(9));
1056 
1057         /*
1058          * test / and %
1059          */
1060         asserter.setStrict(false, false);
1061         asserter.assertExpression("6 / 3", Integer.valueOf(6 / 3));
1062         asserter.assertExpression("6.4 / 3", Double.valueOf(6.4 / 3));
1063         asserter.assertExpression("0 / 3", Integer.valueOf(0 / 3));
1064         asserter.assertExpression("3 / 0", Double.valueOf(0));
1065         asserter.assertExpression("4 % 3", Integer.valueOf(1));
1066         asserter.assertExpression("4.8 % 3", Double.valueOf(4.8 % 3));
1067 
1068     }
1069 
1070     @Test
1071     void testCoerceBigDecimal() {
1072         final JexlArithmetic ja = JEXL.getArithmetic();
1073         final JexlEvalContext ctxt = new JexlEvalContext();
1074         final JexlOptions options = ctxt.getEngineOptions();
1075         options.setStrictArithmetic(true);
1076         final String stmt = "{a = 34L; b = 45.0D; c=56.0F; d=67B; e=78H; }";
1077         final JexlScript expr = JEXL.createScript(stmt);
1078         /* Object value = */ expr.execute(ctxt);
1079         assertEquals(BigDecimal.valueOf(34), ja.toBigDecimal(ctxt.get("a")));
1080         assertEquals(BigDecimal.valueOf(45.), ja.toBigDecimal(ctxt.get("b")));
1081         assertEquals(BigDecimal.valueOf(56.), ja.toBigDecimal(ctxt.get("c")));
1082         assertEquals(BigDecimal.valueOf(67), ja.toBigDecimal(ctxt.get("d")));
1083         assertEquals(BigDecimal.valueOf(78), ja.toBigDecimal(ctxt.get("e")));
1084         assertEquals(BigDecimal.valueOf(10), ja.toBigDecimal("10"));
1085         assertEquals(BigDecimal.valueOf(1.), ja.toBigDecimal(true));
1086         assertEquals(BigDecimal.valueOf(0.), ja.toBigDecimal(false));
1087         // BigDecimal precision is kept when used as argument
1088         final BigDecimal a42 = BigDecimal.valueOf(42);
1089         final BigDecimal a49 = BigDecimal.valueOf(49);
1090         JexlScript bde = JEXL.createScript("a * 6 / 7", "a");
1091         assertEquals(a42, bde.execute(null, a49));
1092         bde = JEXL.createScript("(a - 12) / 12", "a");
1093         final MathContext mc = ja.getMathContext();
1094         final BigDecimal b56 = BigDecimal.valueOf(56);
1095         final BigDecimal b12 = BigDecimal.valueOf(12);
1096         final BigDecimal b3dot666 = b56.subtract(b12, mc).divide(b12, mc);
1097         assertEquals(b3dot666, bde.execute(null, b56));
1098     }
1099 
1100     @Test
1101     void testCoerceBigInteger() {
1102         final JexlArithmetic ja = JEXL.getArithmetic();
1103         final JexlEvalContext ctxt = new JexlEvalContext();
1104         final JexlOptions options = ctxt.getEngineOptions();
1105         options.setStrictArithmetic(true);
1106         final String stmt = "{a = 34L; b = 45.0D; c=56.0F; d=67B; e=78H; }";
1107         final JexlScript expr = JEXL.createScript(stmt);
1108         /* Object value = */ expr.execute(ctxt);
1109         assertEquals(BigInteger.valueOf(34), ja.toBigInteger(ctxt.get("a")));
1110         assertEquals(BigInteger.valueOf(45), ja.toBigInteger(ctxt.get("b")));
1111         assertEquals(BigInteger.valueOf(56), ja.toBigInteger(ctxt.get("c")));
1112         assertEquals(BigInteger.valueOf(67), ja.toBigInteger(ctxt.get("d")));
1113         assertEquals(BigInteger.valueOf(78), ja.toBigInteger(ctxt.get("e")));
1114         assertEquals(BigInteger.valueOf(10), ja.toBigInteger("10"));
1115         assertEquals(BigInteger.valueOf(1), ja.toBigInteger(true));
1116         assertEquals(BigInteger.valueOf(0), ja.toBigInteger(false));
1117     }
1118 
1119     @Test
1120     void testCoerceDouble() {
1121         final JexlArithmetic ja = JEXL.getArithmetic();
1122         final JexlEvalContext ctxt = new JexlEvalContext();
1123         final JexlOptions options = ctxt.getEngineOptions();
1124         options.setStrictArithmetic(true);
1125         final String stmt = "{a = 34L; b = 45.0D; c=56.0F; d=67B; e=78H; }";
1126         final JexlScript expr = JEXL.createScript(stmt);
1127         /* Object value = */ expr.execute(ctxt);
1128         assertEquals(34, ja.toDouble(ctxt.get("a")), EPSILON);
1129         assertEquals(45, ja.toDouble(ctxt.get("b")), EPSILON);
1130         assertEquals(56, ja.toDouble(ctxt.get("c")), EPSILON);
1131         assertEquals(67, ja.toDouble(ctxt.get("d")), EPSILON);
1132         assertEquals(78, ja.toDouble(ctxt.get("e")), EPSILON);
1133         assertEquals(10d, ja.toDouble("10"), EPSILON);
1134         assertEquals(1.D, ja.toDouble(true), EPSILON);
1135         assertEquals(0.D, ja.toDouble(false), EPSILON);
1136     }
1137 
1138     @Test
1139     void testCoerceInteger() {
1140         final JexlArithmetic ja = JEXL.getArithmetic();
1141         final JexlEvalContext ctxt = new JexlEvalContext();
1142         final JexlOptions options = ctxt.getEngineOptions();
1143         options.setStrictArithmetic(true);
1144         final String stmt = "a = 34L; b = 45.0D; c=56.0F; d=67B; e=78H;";
1145         final JexlScript expr = JEXL.createScript(stmt);
1146         /* Object value = */ expr.execute(ctxt);
1147         assertEquals(34, ja.toInteger(ctxt.get("a")));
1148         assertEquals(45, ja.toInteger(ctxt.get("b")));
1149         assertEquals(56, ja.toInteger(ctxt.get("c")));
1150         assertEquals(67, ja.toInteger(ctxt.get("d")));
1151         assertEquals(78, ja.toInteger(ctxt.get("e")));
1152         assertEquals(10, ja.toInteger("10"));
1153         assertEquals(1, ja.toInteger(true));
1154         assertEquals(0, ja.toInteger(false));
1155     }
1156 
1157     @Test
1158     void testCoerceLong() {
1159         final JexlArithmetic ja = JEXL.getArithmetic();
1160         final JexlEvalContext ctxt = new JexlEvalContext();
1161         final JexlOptions options = ctxt.getEngineOptions();
1162         options.setStrictArithmetic(true);
1163         final String stmt = "a = 34L; b = 45.0D; c=56.0F; d=67B; e=78H;";
1164         final JexlScript expr = JEXL.createScript(stmt);
1165         /* Object value = */ expr.execute(ctxt);
1166         assertEquals(34L, ja.toLong(ctxt.get("a")));
1167         assertEquals(45L, ja.toLong(ctxt.get("b")));
1168         assertEquals(56L, ja.toLong(ctxt.get("c")));
1169         assertEquals(67L, ja.toLong(ctxt.get("d")));
1170         assertEquals(78L, ja.toLong(ctxt.get("e")));
1171         assertEquals(10L, ja.toLong("10"));
1172         assertEquals(1L, ja.toLong(true));
1173         assertEquals(0L, ja.toLong(false));
1174     }
1175 
1176     @Test
1177     void testCoercions() throws Exception {
1178         asserter.assertExpression("1", Integer.valueOf(1)); // numerics default to Integer
1179         asserter.assertExpression("5L", Long.valueOf(5));
1180 
1181         asserter.setVariable("I2", Integer.valueOf(2));
1182         asserter.setVariable("L2", Long.valueOf(2));
1183         asserter.setVariable("L3", Long.valueOf(3));
1184         asserter.setVariable("B10", BigInteger.TEN);
1185 
1186         // Integer & Integer => Integer
1187         asserter.assertExpression("I2 + 2", Integer.valueOf(4));
1188         asserter.assertExpression("I2 * 2", Integer.valueOf(4));
1189         asserter.assertExpression("I2 - 2", Integer.valueOf(0));
1190         asserter.assertExpression("I2 / 2", Integer.valueOf(1));
1191 
1192         // Integer & Long => Long
1193         asserter.assertExpression("I2 * L2", Long.valueOf(4));
1194         asserter.assertExpression("I2 / L2", Long.valueOf(1));
1195 
1196         // Long & Long => Long
1197         asserter.assertExpression("L2 + 3", Long.valueOf(5));
1198         asserter.assertExpression("L2 + L3", Long.valueOf(5));
1199         asserter.assertExpression("L2 / L2", Long.valueOf(1));
1200         asserter.assertExpression("L2 / 2", Long.valueOf(1));
1201 
1202         // BigInteger
1203         asserter.assertExpression("B10 / 10", BigInteger.ONE);
1204         asserter.assertExpression("B10 / I2", new BigInteger("5"));
1205         asserter.assertExpression("B10 / L2", new BigInteger("5"));
1206     }
1207 
1208     @Test
1209     void testCompare() {
1210         // JEXL doesn't support more than one operator in the same expression, for example: 1 == 1 == 1
1211         final Object[] EXPRESSIONS = {
1212                 // Basic compare
1213                 "1 == 1", true,
1214                 "1 === 1", true,
1215                 "1.d === 1", false,
1216                 "1 != 1", false,
1217                 "1 != 2", true,
1218                 "1 > 2", false,
1219                 "1 >= 2", false,
1220                 "1 < 2", true,
1221                 "1 <= 2", true,
1222                 // Int <-> Float Coercion
1223                 "1.0 == 1", true,
1224                 "1 == 1.0", true,
1225                 "1.1 != 1", true,
1226                 "1.1 < 2", true,
1227                 // Big Decimal <-> Big Integer Coercion
1228                 "1.0b == 1h", true,
1229                 "1.0b !== 1h", true,
1230                 "1h == 1.0b", true,
1231                 "1.1b != 1h", true,
1232                 "1.1b < 2h", true,
1233                 // Mix all type of numbers
1234                 "1l == 1.0", true, // long and int
1235                 "1l !== 1.0", true, // long and int
1236                 "1.0d == 1.0f", true, // double and float
1237                 "1l == 1.0b", true,
1238                 "1l == 1h", true,
1239                 "1.0d == 1.0b", true,
1240                 "1.0f == 1.0b", true,
1241                 "1.0d == 1h", true,
1242                 "1.0f == 1h", true,
1243                 // numbers and strings
1244                 "'1' == 1", true,
1245                 "'1' == 1l", true,
1246                 "'1' == 1h", true,
1247                 "'' == 0", true, // empty string is coerced to zero (ECMA compliance)
1248                 "'1.0' == 1", true,
1249                 "'1.0' == 1.0f", true,
1250                 "'1.0' == 1.0d", true,
1251                 "'1.0' == 1.0b", true,
1252                 "'1.01' == 1.01", true,
1253                 "'1.01' == 1", false,
1254                 "'1.01' == 1b", false,
1255                 "'1.01' == 1h", false,
1256                 "'1.00001' == 1b", false,
1257                 "'1.00001' == 1h", false,
1258                 "'1.00000001' == 1", false,
1259                 "'1.00000001' == 1b", false,
1260                 "'1.00000001' == 1h", false,
1261                 "1.0 >= '1'", true,
1262                 "1.0 > '1'", false,
1263                 "1.0 == 'a'", false,
1264                 "10 == 'a'", false,
1265                 "10 > 'a'", ArithmeticException.class,
1266                 "10.0 > 'a'", ArithmeticException.class,
1267                 "'a' <= 10b", ArithmeticException.class,
1268                 "'a' >= 1h", ArithmeticException.class
1269         };
1270         final JexlEngine jexl = new JexlBuilder().create();
1271         final JexlContext jc = new EmptyTestContext();
1272 
1273         for (int e = 0; e < EXPRESSIONS.length; e += 2) {
1274             final String stext = (String) EXPRESSIONS[e];
1275             final Object expected = EXPRESSIONS[e + 1];
1276             final JexlExpression expression = jexl.createExpression(stext);
1277             try {
1278                 final Object result = expression.evaluate(jc);
1279                 assertEquals(expected, result, () -> "failed on " + stext);
1280             } catch (final JexlException jexlException) {
1281                 final Throwable cause = jexlException.getCause();
1282                 if (cause == null || !cause.getClass().equals(expected)) {
1283                     fail(stext);
1284                 }
1285             }
1286         }
1287     }
1288 
1289     @Test
1290     void testDivClass() {
1291         final JexlEngine jexl = new JexlBuilder().create();
1292         final JexlContext jc = new MapContext();
1293         final Object ra = jexl.createExpression("463.0d / 0.1").evaluate(jc);
1294         assertEquals(Double.class, ra.getClass());
1295         final Object r0 = jexl.createExpression("463.0B / 0.1").evaluate(jc);
1296         assertEquals(java.math.BigDecimal.class, r0.getClass());
1297         final Object r1 = jexl.createExpression("463.0B / 0.1B").evaluate(jc);
1298         assertEquals(java.math.BigDecimal.class, r1.getClass());
1299     }
1300 
1301     /**
1302      *
1303      * if silent, all arith exception return 0.0
1304      * if not silent, all arith exception throw
1305      */
1306     @Test
1307     void testDivideByZero() throws Exception {
1308         final Map<String, Object> vars = new HashMap<>();
1309         final JexlEvalContext context = new JexlEvalContext(vars);
1310         final JexlOptions options = context.getEngineOptions();
1311         options.setStrictArithmetic(true);
1312         vars.put("aByte", Byte.valueOf((byte) 1));
1313         vars.put("aShort", Short.valueOf((short) 2));
1314         vars.put("aInteger", Integer.valueOf(3));
1315         vars.put("aLong", Long.valueOf(4));
1316         vars.put("aFloat", Float.valueOf((float) 5.5));
1317         vars.put("aDouble", Double.valueOf(6.6));
1318         vars.put("aBigInteger", new BigInteger("7"));
1319         vars.put("aBigDecimal", new BigDecimal("8.8"));
1320 
1321         vars.put("zByte", Byte.valueOf((byte) 0));
1322         vars.put("zShort", Short.valueOf((short) 0));
1323         vars.put("zInteger", Integer.valueOf(0));
1324         vars.put("zLong", Long.valueOf(0));
1325         vars.put("zFloat", Float.valueOf(0));
1326         vars.put("zDouble", Double.valueOf(0));
1327         vars.put("zBigInteger", new BigInteger("0"));
1328         vars.put("zBigDecimal", new BigDecimal("0"));
1329 
1330         final String[] tnames = {
1331             "Byte", "Short", "Integer", "Long",
1332             "Float", "Double",
1333             "BigInteger", "BigDecimal"
1334         };
1335         // number of permutations this will generate
1336         final int PERMS = tnames.length * tnames.length;
1337 
1338         final JexlEngine jexl = JEXL;
1339         // for non-silent, silent...
1340         for (int s = 0; s < 2; ++s) {
1341             final boolean strict = s != 0;
1342             options.setStrict(true);
1343             options.setStrictArithmetic(strict);
1344             int zthrow = 0;
1345             int zeval = 0;
1346             // for vars of all types...
1347             for (final String vname : tnames) {
1348                 // for zeros of all types...
1349                 for (final String zname : tnames) {
1350                     // divide var by zero
1351                     final String expr = "a" + vname + " / " + "z" + zname;
1352                     try {
1353                         final JexlExpression zexpr = jexl.createExpression(expr);
1354                         final Object nan = zexpr.evaluate(context);
1355                         // check we have a zero & increment zero count
1356                         if (nan instanceof Number) {
1357                             final double zero = ((Number) nan).doubleValue();
1358                             if (zero == 0.0) {
1359                                 zeval += 1;
1360                             }
1361                         }
1362                     } catch (final Exception any) {
1363                         // increment the exception count
1364                         zthrow += 1;
1365                     }
1366                 }
1367             }
1368             if (strict) {
1369                 assertEquals(zthrow, PERMS, "All expressions should have thrown " + zthrow + "/" + PERMS);
1370             } else {
1371                 assertEquals(zeval, PERMS, "All expressions should have zeroed " + zeval + "/" + PERMS);
1372             }
1373         }
1374         debuggerCheck(jexl);
1375     }
1376 
1377     @Test
1378     void testDivideEdges() {
1379         assertNullOperand(() -> jexla.divide(null, null));
1380         assertEquals(0, jexlb.divide(null, null));
1381         assertNullOperand(() -> jexla.divide(null, null));
1382         assertEquals(0, jexlb.mod(null, null));
1383         assertArithmeticException(() -> jexla.divide(1, 0));
1384         assertArithmeticException(() -> jexla.divide(1L, 0L));
1385         assertArithmeticException(() -> jexla.divide(1f, 0f));
1386         assertArithmeticException(() -> jexla.divide(1d, 0d));
1387         assertArithmeticException(() -> jexla.divide(BigInteger.ONE, BigInteger.ZERO));
1388         assertArithmeticException(() -> jexla.divide(BigInteger.ONE, BigDecimal.ZERO));
1389     }
1390 
1391     @Test
1392     void testEmpty() throws Exception {
1393         final Object[] SCRIPTS = {
1394             "var x = null; log('x = %s', x);", 0,
1395             "var x = 'abc'; log('x = %s', x);", 1,
1396             "var x = 333; log('x = %s', x);", 1,
1397             "var x = [1, 2]; log('x = %s', x);", 2,
1398             "var x = ['a', 'b']; log('x = %s', x);", 2,
1399             "var x = {1:'A', 2:'B'}; log('x = %s', x);", 1,
1400             "var x = null; return empty(x);", true,
1401             "var x = ''; return empty(x);", true,
1402             "var x = 'abc'; return empty(x);", false,
1403             "var x = 0; return empty(x);", true,
1404             "var x = 333; return empty(x);", false,
1405             "var x = []; return empty(x);", true,
1406             "var x = [1, 2]; return empty(x);", false,
1407             "var x = ['a', 'b']; return empty(x);", false,
1408             "var x = [...]; return empty(x);", true,
1409             "var x = [1, 2,...]; return empty(x);", false,
1410             "var x = {:}; return empty(x);", true,
1411             "var x = {1:'A', 2:'B'}; return empty(x);", false,
1412             "var x = {}; return empty(x);", true,
1413             "var x = {'A','B'}; return empty(x);", false
1414         };
1415         final JexlEngine jexl = new JexlBuilder().create();
1416         final JexlContext jc = new EmptyTestContext();
1417         JexlScript script;
1418 
1419         for (int e = 0; e < SCRIPTS.length; e += 2) {
1420             final String stext = (String) SCRIPTS[e];
1421             final Object expected = SCRIPTS[e + 1];
1422             script = jexl.createScript(stext);
1423             final Object result = script.execute(jc);
1424             assertEquals(expected, result, () -> "failed on " + stext);
1425         }
1426     }
1427 
1428     @Test
1429     void testEmptyDouble()  {
1430         Object x;
1431         x = JEXL.createScript("4294967296.d").execute(null);
1432         assertEquals(4294967296.0d, (Double) x, EPSILON);
1433         checkEmpty(x, false);
1434         x = JEXL.createScript("4294967296.0d").execute(null);
1435         assertEquals(4294967296.0d, (Double) x, EPSILON);
1436         checkEmpty(x, false);
1437         x = JEXL.createScript("0.0d").execute(null);
1438         assertEquals(0.0d, (Double) x, EPSILON);
1439         checkEmpty(x, true);
1440         x = Double.NaN;
1441         checkEmpty(x, true);
1442 
1443     }
1444 
1445     @Test
1446     void testEmptyFloat()  {
1447         Object x;
1448         x = JEXL.createScript("4294967296.f").execute(null);
1449         assertEquals(4294967296.0f, (Float) x, EPSILON);
1450         checkEmpty(x, false);
1451         x = JEXL.createScript("4294967296.0f").execute(null);
1452         assertEquals(4294967296.0f, (Float) x, EPSILON);
1453         checkEmpty(x, false);
1454         x = JEXL.createScript("0.0f").execute(null);
1455         assertEquals(0.0f, (Float) x, EPSILON);
1456         checkEmpty(x, true);
1457         x = Float.NaN;
1458         checkEmpty(x, true);
1459     }
1460 
1461     @Test
1462     void testEmptyLong()  {
1463         Object x;
1464         x = JEXL.createScript("new('java.lang.Long', 4294967296)").execute(null);
1465         assertEquals(4294967296L, ((Long) x).longValue());
1466         x = JEXL.createScript("new Long(4294967296)").execute(null);
1467         assertEquals(4294967296L, ((Long) x).longValue());
1468         x = JEXL.createScript("new('java.lang.Long', '4294967296')").execute(null);
1469         assertEquals(4294967296L, ((Long) x).longValue());
1470         x = JEXL.createScript("4294967296l").execute(null);
1471         assertEquals(4294967296L, ((Long) x).longValue());
1472         x = JEXL.createScript("4294967296L").execute(null);
1473         assertEquals(4294967296L, ((Long) x).longValue());
1474         checkEmpty(x, false);
1475         x = JEXL.createScript("0L").execute(null);
1476         assertEquals(0, ((Long) x).longValue());
1477         checkEmpty(x, true);
1478     }
1479 
1480     @Test
1481     void testFailAllOperators() {
1482         // @formatter:off
1483         final String[] scripts = {
1484             "(x, y)->{ x < y }",
1485             "(x, y)->{ x <= y }",
1486             "(x, y)->{ x > y }",
1487             "(x, y)->{ x >= y }",
1488             "(x, y)->{ x == y }",
1489             "(x, y)->{ x != y }",
1490             "(x, y)->{ x === y }",
1491             "(x, y)->{ x !== y }",
1492             "(x, y)->{ x % y }",
1493             "(x, y)->{ x * y }",
1494             "(x, y)->{ x + y }",
1495             "(x, y)->{ x - y }",
1496             "(x, y)->{ x ^ y }",
1497             "(x, y)->bitwiseXor(x,y)",
1498             "(x, y)->{ x || y }",
1499             "(x, y)->{ x | y }",
1500             "(x, y)->bitwiseOr(x,y)",
1501             "(x, y)->{ x << y }",
1502             "(x, y)->{ x >> y }",
1503             "(x, y)->{ x >>> y }",
1504             "(x, y)->{ x & y }",
1505             "(x, y)->{ x && y }",
1506             "(x, y)->bitwiseAnd(x,y)",
1507             "(x, y)->{ x =^ y }",
1508             "(x, y)->{ x !^ y }",
1509             "(x, y)->{ x =$ y }",
1510             "(x, y)->{ x !$ y }",
1511             "(x, y)->{ x =~ y }",
1512             "(x, y)->{ x !~ y }",
1513             "(x, ignore)->{ -x }",
1514             "(x, ignore)->{ +x }",
1515             "(x, ignore)->{ --x }",
1516             "(x, ignore)->{ ++x }",
1517             "(x, ignore)->{ x-- }",
1518             "(x, ignore)->{ x++ }"
1519         };
1520         // @formatter:on
1521         final JexlEngine jexl = new JexlBuilder().cache(64).arithmetic(new ArithmeticFail(true)).create();
1522         final JexlContext jc = new EmptyTestContext();
1523         for (final String src : scripts) {
1524             final JexlScript script = jexl.createScript(src);
1525             assertThrows(JexlException.class, () -> script.execute(jc, new Var(42), new Var(43)));
1526         }
1527     }
1528 
1529     @Test
1530     void testInfiniteArithmetic() {
1531         final Map<String, Object> ns = new HashMap<>();
1532         ns.put("math", Math.class);
1533         final JexlEngine jexl = new JexlBuilder().arithmetic(new Arithmetic132()).namespaces(ns).create();
1534 
1535         Object evaluate = jexl.createExpression("1/0").evaluate(null);
1536         assertTrue(Double.isInfinite((Double) evaluate));
1537 
1538         evaluate = jexl.createExpression("-1/0").evaluate(null);
1539         assertTrue(Double.isInfinite((Double) evaluate));
1540 
1541         evaluate = jexl.createExpression("1.0/0.0").evaluate(null);
1542         assertTrue(Double.isInfinite((Double) evaluate));
1543 
1544         evaluate = jexl.createExpression("-1.0/0.0").evaluate(null);
1545         assertTrue(Double.isInfinite((Double) evaluate));
1546 
1547         evaluate = jexl.createExpression("math:abs(-42)").evaluate(null);
1548         assertEquals(42, evaluate);
1549 
1550         evaluate =  jexl.createExpression("42B / 7").evaluate(null);
1551         assertEquals(6, evaluate);
1552 
1553         evaluate =  jexl.createExpression("42.7B / 7").evaluate(null);
1554         assertEquals(BigDecimal.valueOf(6.1d), evaluate);
1555     }
1556 
1557     @Test
1558     void testInstanceOf0() {
1559         final JexlEngine jexl = new JexlBuilder().strict(true).safe(false).create();
1560         final JexlContext ctxt = new InstanceofContext();
1561         runInstanceof(jexl, ctxt);
1562     }
1563 
1564     @Test
1565     void testInstanceOf1() {
1566         final JexlEngine jexl = new JexlBuilder().strict(true).safe(false).imports("java.lang").create();
1567         runInstanceof(jexl, null);
1568     }
1569 
1570     @Test
1571     void testIntegerCoercionEdges() {
1572         assertNullOperand(() -> jexla.toBoolean(null));
1573         assertTrue(jexla.toBoolean(date));
1574         // int coercions
1575         assertNullOperand(() -> jexla.toInteger(null));
1576         assertEquals(0, jexlb.toInteger(null));
1577         assertArithmeticException(() -> jexla.toInteger(date));
1578         assertEquals(0, jexla.toInteger(Double.NaN));
1579         assertEquals(0, jexla.toInteger(""));
1580         assertEquals('b', jexla.toInteger('b'));
1581         assertEquals(1, jexla.toInteger(new AtomicBoolean(true)));
1582         assertEquals(0, jexla.toInteger(new AtomicBoolean()));
1583 
1584         // long coercions
1585         assertNullOperand(() -> jexla.toLong(null));
1586         assertEquals(0L, jexlb.toLong(null));
1587         assertArithmeticException(() -> jexla.toLong(date));
1588         assertEquals(0L, jexla.toLong(Double.NaN));
1589         assertEquals(0L, jexla.toLong(""));
1590         assertEquals('b', jexla.toLong('b'));
1591         assertEquals(1L, jexla.toLong(new AtomicBoolean(true)));
1592         assertEquals(0L, jexla.toLong(new AtomicBoolean()));
1593     }
1594 
1595     @Test
1596     void testIsFloatingPointPattern() {
1597         final JexlArithmetic ja = new JexlArithmetic(true);
1598 
1599         assertFalse(ja.isFloatingPointNumber("floating point"));
1600         assertFalse(ja.isFloatingPointNumber("a1."));
1601         assertFalse(ja.isFloatingPointNumber("b1.2"));
1602         assertFalse(ja.isFloatingPointNumber("-10.2a-34"));
1603         assertFalse(ja.isFloatingPointNumber("+10.2a+34"));
1604         assertFalse(ja.isFloatingPointNumber("0"));
1605         assertFalse(ja.isFloatingPointNumber("1"));
1606         assertFalse(ja.isFloatingPointNumber("12A"));
1607         assertFalse(ja.isFloatingPointNumber("2F3"));
1608         assertFalse(ja.isFloatingPointNumber("23"));
1609         assertFalse(ja.isFloatingPointNumber("+3"));
1610         assertFalse(ja.isFloatingPointNumber("+34"));
1611         assertFalse(ja.isFloatingPointNumber("+3-4"));
1612         assertFalse(ja.isFloatingPointNumber("+3.-4"));
1613         assertFalse(ja.isFloatingPointNumber("3ee4"));
1614 
1615         assertTrue(ja.isFloatingPointNumber("0."));
1616         assertTrue(ja.isFloatingPointNumber("1."));
1617         assertTrue(ja.isFloatingPointNumber("1.2"));
1618         assertTrue(ja.isFloatingPointNumber("1.2e3"));
1619         assertTrue(ja.isFloatingPointNumber("2e3"));
1620         assertTrue(ja.isFloatingPointNumber("+2e-3"));
1621         assertTrue(ja.isFloatingPointNumber("+23E-34"));
1622         assertTrue(ja.isFloatingPointNumber("+23.E-34"));
1623         assertTrue(ja.isFloatingPointNumber("-23.4E+45"));
1624         assertTrue(ja.isFloatingPointNumber("1.2e34"));
1625         assertTrue(ja.isFloatingPointNumber("10.2e34"));
1626         assertTrue(ja.isFloatingPointNumber("+10.2e34"));
1627         assertTrue(ja.isFloatingPointNumber("-10.2e34"));
1628         assertTrue(ja.isFloatingPointNumber("10.2e-34"));
1629         assertTrue(ja.isFloatingPointNumber("10.2e+34"));
1630         assertTrue(ja.isFloatingPointNumber("-10.2e-34"));
1631         assertTrue(ja.isFloatingPointNumber("+10.2e+34"));
1632         assertTrue(ja.isFloatingPointNumber("-10.2E-34"));
1633         assertTrue(ja.isFloatingPointNumber("+10.2E+34"));
1634     }
1635 
1636     @Test
1637     void testJexl173() {
1638         final JexlEngine jexl = new JexlBuilder().create();
1639         final JexlContext jc = new MapContext();
1640         final Callable173 c173 = new Callable173();
1641         JexlScript e = jexl.createScript("c173(9, 6)", "c173");
1642         Object result = e.execute(jc, c173);
1643         assertEquals(54, result);
1644         e = jexl.createScript("c173('fourty', 'two')", "c173");
1645         result = e.execute(jc, c173);
1646         assertEquals(42, result);
1647 
1648     }
1649 
1650     @Test
1651     void testLeftNullOperand() throws Exception {
1652         asserter.setVariable("left", null);
1653         asserter.setVariable("right", Integer.valueOf(8));
1654         asserter.setStrict(true);
1655         asserter.failExpression("left + right", ".*null.*");
1656         asserter.failExpression("left - right", ".*null.*");
1657         asserter.failExpression("left * right", ".*null.*");
1658         asserter.failExpression("left / right", ".*null.*");
1659         asserter.failExpression("left % right", ".*null.*");
1660         asserter.failExpression("left & right", ".*null.*");
1661         asserter.failExpression("left | right", ".*null.*");
1662         asserter.failExpression("left ^ right", ".*null.*");
1663         asserter.failExpression("left < right", ".*null.*");
1664         asserter.failExpression("left <= right", ".*null.*");
1665         asserter.failExpression("left > right", ".*null.*");
1666         asserter.failExpression("left >= right", ".*null.*");
1667     }
1668 
1669     @Test
1670     void testLeftNullOperand2() throws Exception {
1671         asserter.setVariable("x.left", null);
1672         asserter.setVariable("right", Integer.valueOf(8));
1673         asserter.setStrict(true);
1674         asserter.failExpression("x.left + right", ".*null.*");
1675         asserter.failExpression("x.left - right", ".*null.*");
1676         asserter.failExpression("x.left * right", ".*null.*");
1677         asserter.failExpression("x.left / right", ".*null.*");
1678         asserter.failExpression("x.left % right", ".*null.*");
1679         asserter.failExpression("x.left & right", ".*null.*");
1680         asserter.failExpression("x.left | right", ".*null.*");
1681         asserter.failExpression("x.left ^ right", ".*null.*");
1682         asserter.failExpression("x.left < right", ".*null.*");
1683         asserter.failExpression("x.left <= right", ".*null.*");
1684         asserter.failExpression("x.left > right", ".*null.*");
1685         asserter.failExpression("x.left >= right", ".*null.*");
1686     }
1687 
1688     // JEXL-24: long integers (and doubles)
1689     @Test
1690     void testLongLiterals() {
1691         final JexlEvalContext ctxt = new JexlEvalContext();
1692         final JexlOptions options = ctxt.getEngineOptions();
1693         options.setStrictArithmetic(true);
1694         final String stmt = "{a = 10L; b = 10l; c = 42.0D; d = 42.0d; e=56.3F; f=56.3f; g=63.5; h=0x10; i=010; j=0x10L; k=010l}";
1695         final JexlScript expr = JEXL.createScript(stmt);
1696         /* Object value = */ expr.execute(ctxt);
1697         assertEquals(10L, ctxt.get("a"));
1698         assertEquals(10L, ctxt.get("b"));
1699         assertEquals(42.0D, ctxt.get("c"));
1700         assertEquals(42.0d, ctxt.get("d"));
1701         assertEquals(56.3f, ctxt.get("e"));
1702         assertEquals(56.3f, ctxt.get("f"));
1703         assertEquals(63.5d, ctxt.get("g"));
1704         assertEquals(0x10, ctxt.get("h"));
1705         assertEquals(010, ctxt.get("i")); // octal 010
1706         assertEquals(0x10L, ctxt.get("j")); // hexa 010L
1707         assertEquals(010L, ctxt.get("k")); // octal 010L
1708     }
1709 
1710     @Test
1711     void testMinusClass() {
1712         final JexlEngine jexl = new JexlBuilder().create();
1713         final JexlContext jc = new MapContext();
1714         final Object ra = jexl.createExpression("463.0d - 0.1").evaluate(jc);
1715         assertEquals(Double.class, ra.getClass());
1716         final Object r0 = jexl.createExpression("463.0B - 0.1").evaluate(jc);
1717         assertEquals(java.math.BigDecimal.class, r0.getClass());
1718         final Object r1 = jexl.createExpression("463.0B - 0.1B").evaluate(jc);
1719         assertEquals(java.math.BigDecimal.class, r1.getClass());
1720     }
1721 
1722     @Test
1723     void testMinusMinusPostfix() throws Exception {
1724         asserter.setVariable("aByte", Byte.valueOf((byte) 2));
1725         asserter.setVariable("aShort", Short.valueOf((short) 3));
1726         asserter.setVariable("anInteger", Integer.valueOf(4));
1727         asserter.setVariable("aLong", Long.valueOf(5));
1728         asserter.setVariable("aFloat", Float.valueOf((float) 6.6));
1729         asserter.setVariable("aDouble", Double.valueOf(7.7));
1730         asserter.setVariable("aBigInteger", new BigInteger("8"));
1731         asserter.setVariable("aBigDecimal", new BigDecimal("9.9"));
1732         asserter.setVariable("aString", "forty-two");
1733 
1734         asserter.assertExpression("aByte--",Byte.valueOf((byte) 2));
1735         asserter.assertExpression("aShort--", Short.valueOf((short) 3));
1736         asserter.assertExpression("anInteger--", Integer.valueOf(4));
1737         asserter.assertExpression("aLong--", Long.valueOf(5));
1738         asserter.assertExpression("aFloat--", Float.valueOf((float) 6.6));
1739         asserter.assertExpression("aDouble--", Double.valueOf(7.7));
1740         asserter.assertExpression("aBigInteger--", new BigInteger("8"));
1741         asserter.assertExpression("aBigDecimal--", new BigDecimal("9.9"));
1742 
1743         asserter.assertExpression("aByte", Byte.valueOf((byte) 1));
1744         asserter.assertExpression("aShort", Short.valueOf((short) 2));
1745         asserter.assertExpression("anInteger", Integer.valueOf(3));
1746         asserter.assertExpression("aLong", Long.valueOf(4));
1747         asserter.assertExpression("aFloat", Float.valueOf((float) 5.6));
1748         asserter.assertExpression("aDouble", Double.valueOf(6.7));
1749         asserter.assertExpression("aBigInteger", new BigInteger("7"));
1750         asserter.assertExpression("aBigDecimal", new BigDecimal("8.9"));
1751 
1752         asserter.failExpression("aString--", "--", String::contains);
1753     }
1754 
1755     @Test
1756     void testMinusMinusPrefix() throws Exception {
1757         asserter.setVariable("aByte", Byte.valueOf((byte) 2));
1758         asserter.setVariable("aShort", Short.valueOf((short) 3));
1759         asserter.setVariable("anInteger", Integer.valueOf(4));
1760         asserter.setVariable("aLong", Long.valueOf(5));
1761         asserter.setVariable("aFloat", Float.valueOf((float) 6.6));
1762         asserter.setVariable("aDouble", Double.valueOf(7.7));
1763         asserter.setVariable("aBigInteger", new BigInteger("8"));
1764         asserter.setVariable("aBigDecimal", new BigDecimal("9.9"));
1765         asserter.setVariable("aString", "forty-two");
1766 
1767         asserter.assertExpression("--aByte", Byte.valueOf((byte) 1));
1768         asserter.assertExpression("--aShort", Short.valueOf((short) 2));
1769         asserter.assertExpression("--anInteger", Integer.valueOf(3));
1770         asserter.assertExpression("--aLong", Long.valueOf(4));
1771         asserter.assertExpression("--aFloat", Float.valueOf((float) 5.6));
1772         asserter.assertExpression("--aDouble", Double.valueOf(6.7));
1773         asserter.assertExpression("--aBigInteger", new BigInteger("7"));
1774         asserter.assertExpression("--aBigDecimal", new BigDecimal("8.9"));
1775 
1776         asserter.failExpression("aString--", "--", String::contains);
1777     }
1778 
1779     @Test
1780     void testModEdge() {
1781         assertNullOperand(() -> jexla.mod(null, null));
1782         assertEquals(0, jexlb.mod(null, null));
1783         assertArithmeticException(() -> jexla.mod(1, 0));
1784         assertArithmeticException(() -> jexla.mod(1L, 0L));
1785         assertArithmeticException(() -> jexla.mod(1f, 0f));
1786         assertArithmeticException(() -> jexla.mod(1d, 0d));
1787         assertArithmeticException(() -> jexla.mod(BigInteger.ONE, BigInteger.ZERO));
1788         assertArithmeticException(() -> jexla.mod(BigInteger.ONE, BigDecimal.ZERO));
1789         assertNullOperand(() -> jexla.divide(null, null));
1790     }
1791 
1792     /**
1793      * JEXL-156.
1794      */
1795     @Test
1796     void testMultClass(){
1797         final JexlEngine jexl = new JexlBuilder().create();
1798         final JexlContext jc = new MapContext();
1799         final Object ra = jexl.createExpression("463.0d * 0.1").evaluate(jc);
1800         assertEquals(Double.class, ra.getClass());
1801         final Object r0 = jexl.createExpression("463.0B * 0.1").evaluate(jc);
1802         assertEquals(java.math.BigDecimal.class, r0.getClass());
1803         final Object r1 = jexl.createExpression("463.0B * 0.1B").evaluate(jc);
1804         assertEquals(java.math.BigDecimal.class, r1.getClass());
1805     }
1806 
1807     @Test
1808     void testNaN() {
1809         final Map<String, Object> ns = new HashMap<>();
1810         ns.put("double", Double.class);
1811         final JexlEngine jexl = new JexlBuilder().namespaces(ns).create();
1812         JexlScript script;
1813         Object result;
1814         script = jexl.createScript("#NaN");
1815         result = script.execute(null);
1816         assertTrue(Double.isNaN((Double) result));
1817         script = jexl.createScript("NaN");
1818         result = script.execute(null);
1819         assertTrue(Double.isNaN((Double) result));
1820         script = jexl.createScript("double:isNaN(#NaN)");
1821         result = script.execute(null);
1822         assertTrue((Boolean) result);
1823         script = jexl.createScript("double:isNaN(NaN)");
1824         result = script.execute(null);
1825         assertTrue((Boolean) result);
1826     }
1827 
1828     @Test
1829     void testNarrowBigDecimal() throws Exception {
1830         final List<String> ls = Arrays.asList("zero", "one", "two");
1831         asserter.setVariable("list", ls);
1832         asserter.assertExpression("a -> list.get(a)", "zero", BigDecimal.ZERO);
1833         asserter.assertExpression("a -> list.get(a)", "one", BigDecimal.ONE);
1834         asserter.assertExpression("a -> list.get(2B)", "two");
1835         final BigDecimal bd42 = BigDecimal.valueOf(42);
1836         asserter.setVariable("bd10", BigDecimal.valueOf(10d));
1837         asserter.setVariable("bd420",BigDecimal.valueOf(420d));
1838         asserter.assertExpression("420 / bd10", bd42);
1839         asserter.assertExpression("420l / bd10", bd42);
1840         asserter.assertExpression("420H / bd10", bd42);
1841         asserter.assertExpression("bd420 / 10", bd42);
1842         asserter.assertExpression("bd420 / 10H", bd42);
1843         asserter.assertExpression("bd420 / 10B", bd42);
1844     }
1845 
1846     @Test
1847     void testNarrowBigInteger() throws Exception {
1848         final List<String> ls = Arrays.asList("zero", "one", "two");
1849         asserter.setVariable("list",ls);
1850         asserter.assertExpression("a -> list.get(a)", "zero", BigInteger.ZERO);
1851         asserter.assertExpression("a -> list.get(a)", "one", BigInteger.ONE);
1852         asserter.assertExpression("a -> list.get(2H)", "two");
1853         final BigInteger b42 = BigInteger.valueOf(42);
1854         asserter.setVariable("bi10", BigInteger.valueOf(10));
1855         asserter.setVariable("bi420", BigInteger.valueOf(420));
1856         asserter.assertExpression("420 / bi10", b42);
1857         asserter.assertExpression("420l / bi10", b42);
1858         asserter.assertExpression("bi420 / 420", BigInteger.ONE);
1859         asserter.assertExpression("bi420 / 420l", BigInteger.ONE);
1860         asserter.assertExpression("bi420 / 420H", BigInteger.ONE);
1861     }
1862 
1863     @Test
1864     void testNullArgs() {
1865         final JexlEngine jexl =  new JexlBuilder().arithmetic(new JexlArithmetic(true) {
1866             @Override public boolean isStrict(final JexlOperator op) {
1867                 return JexlOperator.ADD != op && super.isStrict(op);
1868             }
1869         }).create();
1870         final JexlScript script = jexl.createScript("'1.2' + x ", "x");
1871         final Object result = script.execute(null);
1872         assertEquals("1.2", result);
1873     }
1874 
1875     @Test
1876     void testNullOperand() throws Exception {
1877         asserter.setVariable("right", null);
1878         asserter.failExpression("~right", ".*null.*");
1879     }
1880 
1881     @Test
1882     void testNullOperands() throws Exception {
1883         asserter.setVariable("left", null);
1884         asserter.setVariable("right", null);
1885         asserter.failExpression("left + right", ".*null.*");
1886         asserter.failExpression("left - right", ".*null.*");
1887         asserter.failExpression("left * right", ".*null.*");
1888         asserter.failExpression("left / right", ".*null.*");
1889         asserter.failExpression("left % right", ".*null.*");
1890         asserter.failExpression("left & right", ".*null.*");
1891         asserter.failExpression("left | right", ".*null.*");
1892         asserter.failExpression("left ^ right", ".*null.*");
1893     }
1894 
1895     @Test
1896     void testOperatorsEdges() {
1897         assertNullOperand(() -> jexla.multiply(null, null));
1898         assertEquals(0, jexlb.multiply(null, null));
1899         assertNullOperand(() -> jexla.add(null, null));
1900         assertEquals(0, jexlb.add(null, null));
1901         assertNullOperand(() -> jexla.subtract(null, null));
1902         assertEquals(0, jexlb.subtract(null, null));
1903 
1904         assertTrue(jexla.contains(null, null));
1905         assertFalse(jexla.contains(true, null));
1906         assertFalse(jexla.contains(null, true));
1907         assertTrue(jexla.endsWith(null, null));
1908         assertFalse(jexla.endsWith(true, null));
1909         assertFalse(jexla.endsWith(null, true));
1910         assertTrue(jexla.startsWith(null, null));
1911         assertFalse(jexla.startsWith(true, null));
1912         assertFalse(jexla.startsWith(null, true));
1913         assertTrue(jexla.isEmpty(null));
1914     }
1915 
1916     @Test
1917     void testOption() {
1918         final Map<String, Object> vars = new HashMap<>();
1919         final JexlEvalContext context = new JexlEvalContext(vars);
1920         final JexlOptions options = context.getEngineOptions();
1921         options.setStrictArithmetic(true);
1922         final JexlScript script = JEXL.createScript("0 + '1.2' ");
1923         Object result;
1924 
1925         options.setStrictArithmetic(true);
1926         result = script.execute(context);
1927         assertEquals("01.2", result);
1928 
1929         options.setStrictArithmetic(false);
1930         result = script.execute(context);
1931         assertEquals(1.2d, (Double) result, EPSILON);
1932     }
1933 
1934     @Test
1935     void testOverflows() throws Exception {
1936         asserter.assertExpression("1 + 2147483647", Long.valueOf("2147483648"));
1937         asserter.assertExpression("3 + " + (Long.MAX_VALUE - 2),  BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE));
1938         asserter.assertExpression("-2147483648 - 1", Long.valueOf("-2147483649"));
1939         asserter.assertExpression("-3 + " + (Long.MIN_VALUE + 2),  BigInteger.valueOf(Long.MIN_VALUE).subtract(BigInteger.ONE));
1940         asserter.assertExpression("1 + 9223372036854775807", new BigInteger("9223372036854775808"));
1941         asserter.assertExpression("-1 + (-9223372036854775808)", new BigInteger("-9223372036854775809"));
1942         asserter.assertExpression("-9223372036854775808 - 1", new BigInteger("-9223372036854775809"));
1943         final BigInteger maxl = BigInteger.valueOf(Long.MAX_VALUE);
1944         asserter.assertExpression(maxl + " * " + maxl , maxl.multiply(maxl));
1945     }
1946 
1947     @Test
1948     void testPlusClass() {
1949         final JexlEngine jexl = new JexlBuilder().create();
1950         final JexlContext jc = new MapContext();
1951         final Object ra = jexl.createExpression("463.0d + 0.1").evaluate(jc);
1952         assertEquals(Double.class, ra.getClass());
1953         final Object r0 = jexl.createExpression("463.0B + 0.1").evaluate(jc);
1954         assertEquals(java.math.BigDecimal.class, r0.getClass());
1955         final Object r1 = jexl.createExpression("463.0B + 0.1B").evaluate(jc);
1956         assertEquals(java.math.BigDecimal.class, r1.getClass());
1957     }
1958 
1959     @Test
1960     void testPlusPlusPostfix() throws Exception {
1961         asserter.setVariable("aByte", Byte.valueOf((byte) 0));
1962         asserter.setVariable("aShort", Short.valueOf((short) 1));
1963         asserter.setVariable("anInteger", Integer.valueOf(2));
1964         asserter.setVariable("aLong", Long.valueOf(3));
1965         asserter.setVariable("aFloat", Float.valueOf((float) 4.4));
1966         asserter.setVariable("aDouble", Double.valueOf(5.5));
1967         asserter.setVariable("aBigInteger", new BigInteger("6"));
1968         asserter.setVariable("aBigDecimal", new BigDecimal("7.7"));
1969         asserter.setVariable("aString", "forty-two");
1970 
1971         asserter.assertExpression("aByte++", Byte.valueOf((byte) 0));
1972         asserter.assertExpression("aShort++", Short.valueOf((short) 1));
1973         asserter.assertExpression("anInteger++", Integer.valueOf(2));
1974         asserter.assertExpression("aLong++", Long.valueOf(3));
1975         asserter.assertExpression("aFloat++", Float.valueOf((float) 4.4));
1976         asserter.assertExpression("aDouble++", Double.valueOf(5.5));
1977         asserter.assertExpression("aBigInteger++", new BigInteger("6"));
1978         asserter.assertExpression("aBigDecimal++", new BigDecimal("7.7"));
1979 
1980         asserter.assertExpression("aByte", Byte.valueOf((byte) 1));
1981         asserter.assertExpression("aShort", Short.valueOf((short) 2));
1982         asserter.assertExpression("anInteger", Integer.valueOf(3));
1983         asserter.assertExpression("aLong", Long.valueOf(4));
1984         asserter.assertExpression("aFloat", Float.valueOf((float) 5.4));
1985         asserter.assertExpression("aDouble", Double.valueOf(6.5));
1986         asserter.assertExpression("aBigInteger", new BigInteger("7"));
1987         asserter.assertExpression("aBigDecimal", new BigDecimal("8.7"));
1988 
1989         asserter.failExpression("aString++", "++", String::contains);
1990     }
1991 
1992     @Test
1993     void testPlusPlusPrefix() throws Exception {
1994         asserter.setVariable("aByte", Byte.valueOf((byte) 0));
1995         asserter.setVariable("aShort", Short.valueOf((short) 1));
1996         asserter.setVariable("anInteger", Integer.valueOf(2));
1997         asserter.setVariable("aLong", Long.valueOf(3));
1998         asserter.setVariable("aFloat", Float.valueOf((float) 4.4));
1999         asserter.setVariable("aDouble", Double.valueOf(5.5));
2000         asserter.setVariable("aBigInteger", new BigInteger("6"));
2001         asserter.setVariable("aBigDecimal", new BigDecimal("7.7"));
2002         asserter.setVariable("aString", "forty-two");
2003 
2004         asserter.assertExpression("++aByte", Byte.valueOf((byte) 1));
2005         asserter.assertExpression("++aShort", Short.valueOf((short) 2));
2006         asserter.assertExpression("++anInteger", Integer.valueOf(3));
2007         asserter.assertExpression("++aLong", Long.valueOf(4));
2008         asserter.assertExpression("++aFloat", Float.valueOf((float) 5.4));
2009         asserter.assertExpression("++aDouble", Double.valueOf(6.5));
2010         asserter.assertExpression("++aBigInteger", new BigInteger("7"));
2011         asserter.assertExpression("++aBigDecimal", new BigDecimal("8.7"));
2012 
2013         asserter.failExpression("++aString", "++", String::contains);
2014     }
2015 
2016     @Test
2017     void testRealCoercionEdges() throws Exception {
2018         assertNullOperand(() -> jexla.toDouble(null));
2019         assertEquals(0.0d, jexlb.toDouble(null), EPSILON);
2020         assertEquals(32.0d, jexlb.toDouble((char) 32), EPSILON);
2021         assertArithmeticException(() -> jexla.toDouble(date));
2022         assertTrue(Double.isNaN(jexla.toDouble("")));
2023         assertEquals("", jexla.toString(Double.NaN));
2024 
2025         assertNullOperand(() -> jexla.toBigInteger(null));
2026         assertArithmeticException(() -> jexla.toBigInteger(date));
2027         assertEquals(BigInteger.ZERO, jexla.toBigInteger(Double.NaN));
2028         assertEquals(BigInteger.ZERO, jexla.toBigInteger(""));
2029         assertEquals(BigInteger.ZERO, jexla.toBigInteger((char) 0));
2030 
2031         assertNullOperand(() -> jexla.toBigDecimal(null));
2032         assertArithmeticException(() -> jexla.toBigDecimal(date));
2033 
2034         assertEquals(BigDecimal.ZERO, jexla.toBigDecimal(Double.NaN));
2035         assertEquals(BigDecimal.ZERO, jexla.toBigDecimal(""));
2036         assertEquals(BigDecimal.ZERO, jexla.toBigDecimal((char) 0));
2037 
2038         final Double d64d3 = Double.valueOf(6.4 / 3);
2039         assertEquals(d64d3, ((Number) JEXL.createExpression("6.4 / 3").evaluate(null)).doubleValue(), EPSILON);
2040         asserter.assertExpression("6.4 / 3", d64d3);
2041         assertEquals(d64d3, ((Number) JEXL.createExpression("6.4 / 3d").evaluate(null)).doubleValue(), EPSILON);
2042         asserter.assertExpression("6.4 / 3d", d64d3);
2043         assertEquals(64d / 3, ((Number) JEXL.createExpression("64d / 3").evaluate(null)).doubleValue(), EPSILON);
2044         asserter.assertExpression("64 / 3d", 64 / 3d);
2045     }
2046 
2047     @Test
2048     void testRightNullOperand() throws Exception {
2049         asserter.setVariable("left", Integer.valueOf(9));
2050         asserter.setVariable("right", null);
2051         asserter.failExpression("left + right", ".*null.*");
2052         asserter.failExpression("left - right", ".*null.*");
2053         asserter.failExpression("left * right", ".*null.*");
2054         asserter.failExpression("left / right", ".*null.*");
2055         asserter.failExpression("left % right", ".*null.*");
2056         asserter.failExpression("left & right", ".*null.*");
2057         asserter.failExpression("left | right", ".*null.*");
2058         asserter.failExpression("left ^ right", ".*null.*");
2059         asserter.failExpression("left < right", ".*null.*");
2060         asserter.failExpression("left <= right", ".*null.*");
2061         asserter.failExpression("left > right", ".*null.*");
2062         asserter.failExpression("left >= right", ".*null.*");
2063     }
2064 
2065     @Test
2066     void testRightNullOperand2() throws Exception {
2067         asserter.setVariable("left", Integer.valueOf(9));
2068         asserter.setVariable("y.right", null);
2069         asserter.failExpression("left + y.right", ".*null.*");
2070         asserter.failExpression("left - y.right", ".*null.*");
2071         asserter.failExpression("left * y.right", ".*null.*");
2072         asserter.failExpression("left / y.right", ".*null.*");
2073         asserter.failExpression("left % y.right", ".*null.*");
2074         asserter.failExpression("left & y.right", ".*null.*");
2075         asserter.failExpression("left | y.right", ".*null.*");
2076         asserter.failExpression("left ^ y.right", ".*null.*");
2077         asserter.failExpression("left < y.right", ".*null.*");
2078         asserter.failExpression("left <= y.right", ".*null.*");
2079         asserter.failExpression("left > y.right", ".*null.*");
2080         asserter.failExpression("left >= y.right", ".*null.*");
2081     }
2082 
2083     /**
2084      * test some simple mathematical calculations
2085      */
2086     @Test
2087     void testUnaryMinus() throws Exception {
2088         asserter.setVariable("aByte", Byte.valueOf((byte) 1));
2089         asserter.setVariable("aShort", Short.valueOf((short) 2));
2090         asserter.setVariable("anInteger", Integer.valueOf(3));
2091         asserter.setVariable("aLong", Long.valueOf(4));
2092         asserter.setVariable("aFloat", Float.valueOf((float) 5.5));
2093         asserter.setVariable("aDouble", Double.valueOf(6.6));
2094         asserter.setVariable("aBigInteger", new BigInteger("7"));
2095         asserter.setVariable("aBigDecimal", new BigDecimal("8.8"));
2096 
2097         // loop to allow checking caching of constant numerals (debug)
2098         for(int i = 0 ; i < 2; ++i) {
2099             asserter.assertExpression("-3", Integer.valueOf("-3"));
2100             asserter.assertExpression("-3.0", Double.valueOf("-3.0"));
2101             asserter.assertExpression("-aByte", Byte.valueOf((byte) -1));
2102             asserter.assertExpression("-aShort", Short.valueOf((short) -2));
2103             asserter.assertExpression("-anInteger", Integer.valueOf(-3));
2104             asserter.assertExpression("-aLong", Long.valueOf(-4));
2105             asserter.assertExpression("-aFloat", Float.valueOf((float) -5.5));
2106             asserter.assertExpression("-aDouble", Double.valueOf(-6.6));
2107             asserter.assertExpression("-aBigInteger", new BigInteger("-7"));
2108             asserter.assertExpression("-aBigDecimal", new BigDecimal("-8.8"));
2109         }
2110     }
2111 
2112     @Test
2113     void testUnaryopsEdges() {
2114         assertArithmeticException(() -> jexla.positivize(date));
2115         assertNullOperand(() -> jexla.positivize(null));
2116         assertNull(jexlb.positivize(null));
2117         assertEquals(42, jexla.positivize((char) 42));
2118         assertEquals(Boolean.TRUE, jexla.positivize(Boolean.TRUE));
2119         assertEquals(Boolean.FALSE, jexla.positivize(Boolean.FALSE));
2120         assertEquals(Boolean.TRUE, jexla.positivize(new AtomicBoolean(true)));
2121         assertEquals(Boolean.FALSE, jexla.positivize(new AtomicBoolean()));
2122 
2123         assertNullOperand(() -> jexla.negate(null));
2124         assertNull(jexlb.negate(null));
2125         assertArithmeticException(() -> jexla.negate(date));
2126         assertEquals(Boolean.FALSE, jexla.negate(Boolean.TRUE));
2127         assertEquals(Boolean.TRUE, jexla.negate(Boolean.FALSE));
2128     }
2129 
2130     /**
2131      * test some simple mathematical calculations
2132      */
2133     @Test
2134     void testUnaryPlus() throws Exception {
2135         asserter.setVariable("aByte", Byte.valueOf((byte) 1));
2136         asserter.setVariable("aShort", Short.valueOf((short) 2));
2137         asserter.setVariable("anInteger", Integer.valueOf(3));
2138         asserter.setVariable("aLong", Long.valueOf(4));
2139         asserter.setVariable("aFloat", Float.valueOf((float) 5.5));
2140         asserter.setVariable("aDouble", Double.valueOf(6.6));
2141         asserter.setVariable("aBigInteger", new BigInteger("7"));
2142         asserter.setVariable("aBigDecimal", new BigDecimal("8.8"));
2143 
2144         // loop to allow checking caching of constant numerals (debug)
2145         for(int i = 0 ; i < 2; ++i) {
2146             asserter.assertExpression("+3", Integer.valueOf("3"));
2147             asserter.assertExpression("+3.0", Double.valueOf("3.0"));
2148             asserter.assertExpression("+aByte", Integer.valueOf(1));
2149             asserter.assertExpression("+aShort", Integer.valueOf(2));
2150             asserter.assertExpression("+anInteger", Integer.valueOf(3));
2151             asserter.assertExpression("+aLong", Long.valueOf(4));
2152             asserter.assertExpression("+aFloat", Float.valueOf((float) 5.5));
2153             asserter.assertExpression("+aDouble", Double.valueOf(6.6));
2154             asserter.assertExpression("+aBigInteger", new BigInteger("7"));
2155             asserter.assertExpression("+aBigDecimal", new BigDecimal("8.8"));
2156         }
2157     }
2158 
2159     @Test
2160     void testUndefinedVar() throws Exception {
2161         asserter.failExpression("objects[1].status", ".*variable 'objects' is undefined.*");
2162     }
2163 
2164     /**
2165      * Inspired by JEXL-16{1,2}.
2166      */
2167     @Test
2168     void testXmlArithmetic() throws Exception {
2169         Document xml;
2170         Node x;
2171         Boolean empty;
2172         int size;
2173         final JexlEvalContext ctxt = new JexlEvalContext();
2174         final JexlEngine jexl = new JexlBuilder().strict(true).safe(false).arithmetic(new XmlArithmetic(false)).create();
2175         final JexlScript e0 = jexl.createScript("x.empty()", "x");
2176         final JexlScript e1 = jexl.createScript("empty(x)", "x");
2177         final JexlScript s0 = jexl.createScript("x.size()", "x");
2178         final JexlScript s1 = jexl.createScript("size(x)", "x");
2179 
2180         empty = (Boolean) e1.execute(null, (Object) null);
2181         assertTrue(empty);
2182         size = (Integer) s1.execute(null, (Object) null);
2183         assertEquals(0, size);
2184 
2185         try {
2186             final Object xx = e0.execute(null, (Object) null);
2187             assertNull(xx);
2188         } catch (final JexlException.Variable xvar) {
2189             assertNotNull(xvar);
2190         }
2191         try {
2192             final Object xx = s0.execute(null, (Object) null);
2193             assertNull(xx);
2194         } catch (final JexlException.Variable xvar) {
2195             assertNotNull(xvar);
2196         }
2197         final JexlOptions options = ctxt.getEngineOptions();
2198         options.setSafe(true);
2199         final Object x0 = e0.execute(ctxt, (Object) null);
2200         assertNull(x0);
2201         final Object x1 = s0.execute(ctxt, (Object) null);
2202         assertNull(x1);
2203 
2204         xml = getDocument("<node info='123'/>");
2205         x = xml.getLastChild();
2206         empty = (Boolean) e0.execute(null, x);
2207         assertFalse(empty);
2208         empty = (Boolean) e1.execute(null, x);
2209         assertFalse(empty);
2210         size = (Integer) s0.execute(null, x);
2211         assertEquals(0, size);
2212         size = (Integer) s1.execute(null, x);
2213         assertEquals(0, size);
2214         xml = getDocument("<node><a/><b/></node>");
2215         x = xml.getLastChild();
2216         empty = (Boolean) e0.execute(null, x);
2217         assertFalse(empty);
2218         empty = (Boolean) e1.execute(null, x);
2219         assertFalse(empty);
2220         size = (Integer) s0.execute(null, x);
2221         assertEquals(2, size);
2222         size = (Integer) s1.execute(null, x);
2223         assertEquals(2, size);
2224         xml = getDocument("<node/>");
2225         x = xml.getLastChild();
2226         empty = (Boolean) e0.execute(null, x);
2227         assertTrue(empty);
2228         empty = (Boolean) e1.execute(null, x);
2229         assertTrue(empty);
2230         size = (Integer) s0.execute(null, x);
2231         assertEquals(0, size);
2232         size = (Integer) s1.execute(null, x);
2233         assertEquals(0, size);
2234         xml = getDocument("<node info='123'/>");
2235         NamedNodeMap nnm = xml.getLastChild().getAttributes();
2236         Attr info = (Attr) nnm.getNamedItem("info");
2237         assertEquals("123", info.getValue());
2238 
2239         // JEXL-161
2240         final JexlContext jc = new MapContext();
2241         jc.set("x", xml.getLastChild());
2242         final String y = "456";
2243         jc.set("y", y);
2244         final JexlScript s = jexl.createScript("x.attribute.info = y");
2245         Object r;
2246         try {
2247             r = s.execute(jc);
2248             nnm = xml.getLastChild().getAttributes();
2249             info = (Attr) nnm.getNamedItem("info");
2250             assertEquals(y, r);
2251             assertEquals(y, info.getValue());
2252         } catch (final JexlException.Property xprop) {
2253             // test fails in java > 11 because modules, etc; need investigation
2254             assertTrue(xprop.getMessage().contains("info"));
2255             assertTrue(getJavaVersion() > 11);
2256         }
2257     }
2258 
2259     @Test void testShortCircuitAnd() {
2260         final String src = "(x, y, z) -> x && y && z";
2261         final JexlBuilder builder = new JexlBuilder();
2262         final JexlEngine jexl = builder.create();
2263         JexlScript script;
2264         Object result;
2265         script = jexl.createScript(src);
2266         result = script.execute(null, true, "foo", 42);
2267         assertEquals(42, result);
2268         result = script.execute(null, true, "", 42);
2269         assertEquals("", result);
2270     }
2271 
2272     @Test void testShortCircuitOr() {
2273         final OptContext optc = new OptContext();
2274         final String src = "(x, y, z) -> x || y || z";
2275         final JexlBuilder builder = new JexlBuilder();
2276         final JexlEngine jexl = builder.create();
2277         final JexlOptions options = builder.options();
2278         optc.setOptions(options);
2279         JexlScript script;
2280         Object result;
2281         script = jexl.createScript(src);
2282         result = script.execute(optc, 0, "", 42);
2283         assertEquals(42, result);
2284         result = script.execute(optc, true, 42, null);
2285         assertEquals(true, result);
2286 
2287         options.setBooleanLogical(true);
2288         result = script.execute(optc, 0, "", Double.NaN);
2289         assertEquals(false, result);
2290         result = script.execute(optc, 0, "", Collections.emptySet());
2291         assertEquals(true, result);
2292 
2293     }
2294 
2295     @Test void testLogicalValue() {
2296         final String src = "function sanitize(const n) { n == 0 ? NaN : n }; sanitize(x) && 420 / x";
2297         final JexlEngine jexl = new JexlBuilder().create();
2298         JexlScript script;
2299         Object result;
2300         script = jexl.createScript(src, "x");
2301         result = script.execute(null, 10);
2302         assertEquals(42, result);
2303         result = script.execute(null, 0);
2304         assertTrue(Double.isNaN(((Number) result).doubleValue()));
2305     }
2306 
2307     public static class OptContext extends MapContext implements JexlContext.OptionsHandle {
2308         private JexlOptions options;
2309 
2310         @Override
2311         public JexlOptions getEngineOptions() {
2312             return options;
2313         }
2314 
2315         void setOptions(final JexlOptions options) {
2316             this.options = options;
2317         }
2318     }
2319 }