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    *      http://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  //CSOFF: FileLength
18  package org.apache.commons.jexl3.internal;
19  
20  import java.util.Iterator;
21  import java.util.concurrent.Callable;
22  import java.util.function.Consumer;
23  
24  import org.apache.commons.jexl3.JexlArithmetic;
25  import org.apache.commons.jexl3.JexlContext;
26  import org.apache.commons.jexl3.JexlEngine;
27  import org.apache.commons.jexl3.JexlException;
28  import org.apache.commons.jexl3.JexlInfo;
29  import org.apache.commons.jexl3.JexlOperator;
30  import org.apache.commons.jexl3.JexlOptions;
31  import org.apache.commons.jexl3.JexlScript;
32  import org.apache.commons.jexl3.JxltEngine;
33  
34  import org.apache.commons.jexl3.introspection.JexlMethod;
35  import org.apache.commons.jexl3.introspection.JexlPropertyGet;
36  
37  import org.apache.commons.jexl3.parser.*;
38  
39  /**
40   * An interpreter of JEXL syntax.
41   *
42   * @since 2.0
43   */
44  public class Interpreter extends InterpreterBase {
45      /** Frame height. */
46      protected int fp = 0;
47      /** Symbol values. */
48      protected final Frame frame;
49      /** Block micro-frames. */
50      protected LexicalFrame block = null;
51  
52      /**
53       * The thread local interpreter.
54       */
55      protected static final java.lang.ThreadLocal<Interpreter> INTER =
56                         new java.lang.ThreadLocal<>();
57  
58      /**
59       * Creates an interpreter.
60       * @param engine   the engine creating this interpreter
61       * @param aContext the evaluation context, global variables, methods and functions
62       * @param opts     the evaluation options, flags modifying evaluation behavior
63       * @param eFrame   the evaluation frame, arguments and local variables
64       */
65      protected Interpreter(final Engine engine, final JexlOptions opts, final JexlContext aContext, final Frame eFrame) {
66          super(engine, opts, aContext);
67          this.frame = eFrame;
68      }
69  
70      /**
71       * Copy constructor.
72       * @param ii  the interpreter to copy
73       * @param jexla the arithmetic instance to use (or null)
74       */
75      protected Interpreter(final Interpreter ii, final JexlArithmetic jexla) {
76          super(ii, jexla);
77          frame = ii.frame;
78          block = ii.block != null? new LexicalFrame(ii.block) : null;
79      }
80  
81      /**
82       * Swaps the current thread local interpreter.
83       * @param inter the interpreter or null
84       * @return the previous thread local interpreter
85       */
86      protected Interpreter putThreadInterpreter(final Interpreter inter) {
87          final Interpreter pinter = INTER.get();
88          INTER.set(inter);
89          return pinter;
90      }
91  
92      /**
93       * Interpret the given script/expression.
94       * <p>
95       * If the underlying JEXL engine is silent, errors will be logged through
96       * its logger as warning.
97       * @param node the script or expression to interpret.
98       * @return the result of the interpretation.
99       * @throws JexlException if any error occurs during interpretation.
100      */
101     public Object interpret(final JexlNode node) {
102         JexlContext.ThreadLocal tcontext = null;
103         JexlEngine tjexl = null;
104         Interpreter tinter = null;
105         try {
106             tinter = putThreadInterpreter(this);
107             if (tinter != null) {
108                 fp = tinter.fp + 1;
109             }
110             if (context instanceof JexlContext.ThreadLocal) {
111                 tcontext = jexl.putThreadLocal((JexlContext.ThreadLocal) context);
112             }
113             tjexl = jexl.putThreadEngine(jexl);
114             if (fp > jexl.stackOverflow) {
115                 throw new JexlException.StackOverflow(node.jexlInfo(), "jexl (" + jexl.stackOverflow + ")", null);
116             }
117             cancelCheck(node);
118             return arithmetic.controlReturn(node.jjtAccept(this, null));
119         } catch(final StackOverflowError xstack) {
120             final JexlException xjexl = new JexlException.StackOverflow(node.jexlInfo(), "jvm", xstack);
121             if (!isSilent()) {
122                 throw xjexl.clean();
123             }
124             if (logger.isWarnEnabled()) {
125                 logger.warn(xjexl.getMessage(), xjexl.getCause());
126             }
127         } catch (final JexlException.Return xreturn) {
128             return xreturn.getValue();
129         } catch (final JexlException.Cancel xcancel) {
130             // cancelled |= Thread.interrupted();
131             cancelled.weakCompareAndSet(false, Thread.interrupted());
132             if (isCancellable()) {
133                 throw xcancel.clean();
134             }
135         } catch (final JexlException xjexl) {
136             if (!isSilent()) {
137                 throw xjexl.clean();
138             }
139             if (logger.isWarnEnabled()) {
140                 logger.warn(xjexl.getMessage(), xjexl.getCause());
141             }
142         } finally {
143             // clean functors at top level
144             if (fp == 0) {
145                 synchronized (this) {
146                     if (functors != null) {
147                         for (final Object functor : functors.values()) {
148                             closeIfSupported(functor);
149                         }
150                         functors.clear();
151                         functors = null;
152                     }
153                 }
154             }
155             jexl.putThreadEngine(tjexl);
156             if (context instanceof JexlContext.ThreadLocal) {
157                 jexl.putThreadLocal(tcontext);
158             }
159             if (tinter != null) {
160                 fp = tinter.fp - 1;
161             }
162             putThreadInterpreter(tinter);
163         }
164         return null;
165     }
166 
167     /**
168      * Gets an attribute of an object.
169      *
170      * @param object    to retrieve value from
171      * @param attribute the attribute of the object, e.g. an index (1, 0, 2) or key for a map
172      * @return the attribute value
173      */
174     public Object getAttribute(final Object object, final Object attribute) {
175         return getAttribute(object, attribute, null);
176     }
177 
178     /**
179      * Sets an attribute of an object.
180      *
181      * @param object    to set the value to
182      * @param attribute the attribute of the object, e.g. an index (1, 0, 2) or key for a map
183      * @param value     the value to assign to the object's attribute
184      */
185     public void setAttribute(final Object object, final Object attribute, final Object value) {
186         setAttribute(object, attribute, value, null);
187     }
188 
189     @Override
190     protected Object visit(final ASTAddNode node, final Object data) {
191         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
192         final Object right = node.jjtGetChild(1).jjtAccept(this, data);
193         try {
194             final Object result = operators.tryOverload(node, JexlOperator.ADD, left, right);
195             return result != JexlEngine.TRY_FAILED ? result : arithmetic.add(left, right);
196         } catch (final ArithmeticException xrt) {
197             throw new JexlException(findNullOperand(node, left, right), "+ error", xrt);
198         }
199     }
200 
201     @Override
202     protected Object visit(final ASTSubNode node, final Object data) {
203         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
204         final Object right = node.jjtGetChild(1).jjtAccept(this, data);
205         try {
206             final Object result = operators.tryOverload(node, JexlOperator.SUBTRACT, left, right);
207             return result != JexlEngine.TRY_FAILED ? result : arithmetic.subtract(left, right);
208         } catch (final ArithmeticException xrt) {
209             throw new JexlException(findNullOperand(node, left, right), "- error", xrt);
210         }
211     }
212 
213     @Override
214     protected Object visit(final ASTMulNode node, final Object data) {
215         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
216         final Object right = node.jjtGetChild(1).jjtAccept(this, data);
217         try {
218             final Object result = operators.tryOverload(node, JexlOperator.MULTIPLY, left, right);
219             return result != JexlEngine.TRY_FAILED ? result : arithmetic.multiply(left, right);
220         } catch (final ArithmeticException xrt) {
221             throw new JexlException(findNullOperand(node, left, right), "* error", xrt);
222         }
223     }
224 
225     @Override
226     protected Object visit(final ASTDivNode node, final Object data) {
227         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
228         final Object right = node.jjtGetChild(1).jjtAccept(this, data);
229         try {
230             final Object result = operators.tryOverload(node, JexlOperator.DIVIDE, left, right);
231             return result != JexlEngine.TRY_FAILED ? result : arithmetic.divide(left, right);
232         } catch (final ArithmeticException xrt) {
233             if (!arithmetic.isStrict()) {
234                 return 0.0d;
235             }
236             throw new JexlException(findNullOperand(node, left, right), "/ error", xrt);
237         }
238     }
239 
240     @Override
241     protected Object visit(final ASTModNode node, final Object data) {
242         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
243         final Object right = node.jjtGetChild(1).jjtAccept(this, data);
244         try {
245             final Object result = operators.tryOverload(node, JexlOperator.MOD, left, right);
246             return result != JexlEngine.TRY_FAILED ? result : arithmetic.mod(left, right);
247         } catch (final ArithmeticException xrt) {
248             if (!arithmetic.isStrict()) {
249                 return 0.0d;
250             }
251             throw new JexlException(findNullOperand(node, left, right), "% error", xrt);
252         }
253     }
254 
255     @Override
256     protected Object visit(final ASTBitwiseAndNode node, final Object data) {
257         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
258         final Object right = node.jjtGetChild(1).jjtAccept(this, data);
259         try {
260             final Object result = operators.tryOverload(node, JexlOperator.AND, left, right);
261             return result != JexlEngine.TRY_FAILED ? result : arithmetic.and(left, right);
262         } catch (final ArithmeticException xrt) {
263             throw new JexlException(findNullOperand(node, left, right), "& error", xrt);
264         }
265     }
266 
267     @Override
268     protected Object visit(final ASTBitwiseOrNode node, final Object data) {
269         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
270         final Object right = node.jjtGetChild(1).jjtAccept(this, data);
271         try {
272             final Object result = operators.tryOverload(node, JexlOperator.OR, left, right);
273             return result != JexlEngine.TRY_FAILED ? result : arithmetic.or(left, right);
274         } catch (final ArithmeticException xrt) {
275             throw new JexlException(findNullOperand(node, left, right), "| error", xrt);
276         }
277     }
278 
279     @Override
280     protected Object visit(final ASTBitwiseXorNode node, final Object data) {
281         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
282         final Object right = node.jjtGetChild(1).jjtAccept(this, data);
283         try {
284             final Object result = operators.tryOverload(node, JexlOperator.XOR, left, right);
285             return result != JexlEngine.TRY_FAILED ? result : arithmetic.xor(left, right);
286         } catch (final ArithmeticException xrt) {
287             throw new JexlException(findNullOperand(node, left, right), "^ error", xrt);
288         }
289     }
290 
291     @Override
292     protected Object visit(final ASTShiftLeftNode node, final Object data) {
293         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
294         final Object right = node.jjtGetChild(1).jjtAccept(this, data);
295         try {
296             final Object result = operators.tryOverload(node, JexlOperator.SHIFTLEFT, left, right);
297             return result != JexlEngine.TRY_FAILED ? result : arithmetic.shiftLeft(left, right);
298         } catch (final ArithmeticException xrt) {
299             throw new JexlException(findNullOperand(node, left, right), "<< error", xrt);
300         }
301     }
302 
303     @Override
304     protected Object visit(final ASTShiftRightNode node, final Object data) {
305         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
306         final Object right = node.jjtGetChild(1).jjtAccept(this, data);
307         try {
308             final Object result = operators.tryOverload(node, JexlOperator.SHIFTRIGHT, left, right);
309             return result != JexlEngine.TRY_FAILED ? result : arithmetic.shiftRight(left, right);
310         } catch (final ArithmeticException xrt) {
311             throw new JexlException(findNullOperand(node, left, right), ">> error", xrt);
312         }
313     }
314 
315     @Override
316     protected Object visit(final ASTShiftRightUnsignedNode node, final Object data) {
317         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
318         final Object right = node.jjtGetChild(1).jjtAccept(this, data);
319         try {
320             final Object result = operators.tryOverload(node, JexlOperator.SHIFTRIGHTU, left, right);
321             return result != JexlEngine.TRY_FAILED ? result : arithmetic.shiftRightUnsigned(left, right);
322         } catch (final ArithmeticException xrt) {
323             throw new JexlException(findNullOperand(node, left, right), ">> error", xrt);
324         }
325     }
326 
327     @Override
328     protected Object visit(final ASTEQNode node, final Object data) {
329         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
330         final Object right = node.jjtGetChild(1).jjtAccept(this, data);
331         try {
332             final Object result = operators.tryOverload(node, JexlOperator.EQ, left, right);
333             return result != JexlEngine.TRY_FAILED ? result : arithmetic.equals(left, right);
334         } catch (final ArithmeticException xrt) {
335             throw new JexlException(findNullOperand(node, left, right), "== error", xrt);
336         }
337     }
338 
339     @Override
340     protected Object visit(final ASTNENode node, final Object data) {
341         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
342         final Object right = node.jjtGetChild(1).jjtAccept(this, data);
343         try {
344             final Object result = operators.tryOverload(node, JexlOperator.EQ, left, right);
345             return result != JexlEngine.TRY_FAILED
346                    ? !arithmetic.toBoolean(result)
347                    : !arithmetic.equals(left, right);
348         } catch (final ArithmeticException xrt) {
349             throw new JexlException(findNullOperand(node, left, right), "!= error", xrt);
350         }
351     }
352 
353     @Override
354     protected Object visit(final ASTGENode node, final Object data) {
355         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
356         final Object right = node.jjtGetChild(1).jjtAccept(this, data);
357         try {
358             final Object result = operators.tryOverload(node, JexlOperator.GTE, left, right);
359             return result != JexlEngine.TRY_FAILED
360                    ? result
361                    : arithmetic.greaterThanOrEqual(left, right);
362         } catch (final ArithmeticException xrt) {
363             throw new JexlException(findNullOperand(node, left, right), ">= error", xrt);
364         }
365     }
366 
367     @Override
368     protected Object visit(final ASTGTNode node, final Object data) {
369         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
370         final Object right = node.jjtGetChild(1).jjtAccept(this, data);
371         try {
372             final Object result = operators.tryOverload(node, JexlOperator.GT, left, right);
373             return result != JexlEngine.TRY_FAILED
374                    ? result
375                    : arithmetic.greaterThan(left, right);
376         } catch (final ArithmeticException xrt) {
377             throw new JexlException(findNullOperand(node, left, right), "> error", xrt);
378         }
379     }
380 
381     @Override
382     protected Object visit(final ASTLENode node, final Object data) {
383         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
384         final Object right = node.jjtGetChild(1).jjtAccept(this, data);
385         try {
386             final Object result = operators.tryOverload(node, JexlOperator.LTE, left, right);
387             return result != JexlEngine.TRY_FAILED
388                    ? result
389                    : arithmetic.lessThanOrEqual(left, right);
390         } catch (final ArithmeticException xrt) {
391             throw new JexlException(findNullOperand(node, left, right), "<= error", xrt);
392         }
393     }
394 
395     @Override
396     protected Object visit(final ASTLTNode node, final Object data) {
397         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
398         final Object right = node.jjtGetChild(1).jjtAccept(this, data);
399         try {
400             final Object result = operators.tryOverload(node, JexlOperator.LT, left, right);
401             return result != JexlEngine.TRY_FAILED
402                    ? result
403                    : arithmetic.lessThan(left, right);
404         } catch (final ArithmeticException xrt) {
405             throw new JexlException(findNullOperand(node, left, right), "< error", xrt);
406         }
407     }
408 
409     @Override
410     protected Object visit(final ASTSWNode node, final Object data) {
411         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
412         final Object right = node.jjtGetChild(1).jjtAccept(this, data);
413         return operators.startsWith(node, "^=", left, right);
414     }
415 
416     @Override
417     protected Object visit(final ASTNSWNode node, final Object data) {
418         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
419         final Object right = node.jjtGetChild(1).jjtAccept(this, data);
420         return !operators.startsWith(node, "^!", left, right);
421     }
422 
423     @Override
424     protected Object visit(final ASTEWNode node, final Object data) {
425         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
426         final Object right = node.jjtGetChild(1).jjtAccept(this, data);
427         return operators.endsWith(node, "$=", left, right);
428     }
429 
430     @Override
431     protected Object visit(final ASTNEWNode node, final Object data) {
432         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
433         final Object right = node.jjtGetChild(1).jjtAccept(this, data);
434         return !operators.endsWith(node, "$!", left, right);
435     }
436 
437     @Override
438     protected Object visit(final ASTERNode node, final Object data) {
439         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
440         final Object right = node.jjtGetChild(1).jjtAccept(this, data);
441         // note the arguments inversion between 'in'/'matches' and 'contains'
442         // if x in y then y contains x
443         return operators.contains(node, "=~", right, left);
444     }
445 
446     @Override
447     protected Object visit(final ASTNRNode node, final Object data) {
448         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
449         final Object right = node.jjtGetChild(1).jjtAccept(this, data);
450         // note the arguments inversion between (not) 'in'/'matches' and  (not) 'contains'
451         // if x not-in y then y not-contains x
452         return !operators.contains(node, "!~", right, left);
453     }
454 
455     @Override
456     protected Object visit(final ASTRangeNode node, final Object data) {
457         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
458         final Object right = node.jjtGetChild(1).jjtAccept(this, data);
459         try {
460             return arithmetic.createRange(left, right);
461         } catch (final ArithmeticException xrt) {
462             throw new JexlException(findNullOperand(node, left, right), ".. error", xrt);
463         }
464     }
465 
466     @Override
467     protected Object visit(final ASTUnaryMinusNode node, final Object data) {
468         // use cached value if literal
469         final Object value = node.jjtGetValue();
470         if (value != null && !(value instanceof JexlMethod)) {
471             return value;
472         }
473         final JexlNode valNode = node.jjtGetChild(0);
474         final Object val = valNode.jjtAccept(this, data);
475         try {
476             final Object result = operators.tryOverload(node, JexlOperator.NEGATE, val);
477             if (result != JexlEngine.TRY_FAILED) {
478                 return result;
479             }
480             Object number = arithmetic.negate(val);
481             // attempt to recoerce to literal class
482             // cache if number literal and negate is idempotent
483             if (number instanceof Number && valNode instanceof ASTNumberLiteral) {
484                 number = arithmetic.narrowNumber((Number) number, ((ASTNumberLiteral) valNode).getLiteralClass());
485                 if (arithmetic.isNegateStable()) {
486                     node.jjtSetValue(number);
487                 }
488             }
489             return number;
490         } catch (final ArithmeticException xrt) {
491             throw new JexlException(valNode, "- error", xrt);
492         }
493     }
494 
495     @Override
496     protected Object visit(final ASTUnaryPlusNode node, final Object data) {
497         // use cached value if literal
498         final Object value = node.jjtGetValue();
499         if (value != null && !(value instanceof JexlMethod)) {
500             return value;
501         }
502         final JexlNode valNode = node.jjtGetChild(0);
503         final Object val = valNode.jjtAccept(this, data);
504         try {
505             final Object result = operators.tryOverload(node, JexlOperator.POSITIVIZE, val);
506             if (result != JexlEngine.TRY_FAILED) {
507                 return result;
508             }
509             final Object number = arithmetic.positivize(val);
510             if (valNode instanceof ASTNumberLiteral
511                 && number instanceof Number
512                 && arithmetic.isPositivizeStable()) {
513                 node.jjtSetValue(number);
514             }
515             return number;
516         } catch (final ArithmeticException xrt) {
517             throw new JexlException(valNode, "- error", xrt);
518         }
519     }
520 
521     @Override
522     protected Object visit(final ASTBitwiseComplNode node, final Object data) {
523         final Object arg = node.jjtGetChild(0).jjtAccept(this, data);
524         try {
525             final Object result = operators.tryOverload(node, JexlOperator.COMPLEMENT, arg);
526             return result != JexlEngine.TRY_FAILED ? result : arithmetic.complement(arg);
527         } catch (final ArithmeticException xrt) {
528             throw new JexlException(node, "~ error", xrt);
529         }
530     }
531 
532     @Override
533     protected Object visit(final ASTNotNode node, final Object data) {
534         final Object val = node.jjtGetChild(0).jjtAccept(this, data);
535         try {
536             final Object result = operators.tryOverload(node, JexlOperator.NOT, val);
537             return result != JexlEngine.TRY_FAILED ? result : arithmetic.not(val);
538         } catch (final ArithmeticException xrt) {
539             throw new JexlException(node, "! error", xrt);
540         }
541     }
542 
543     private boolean testPredicate(final JexlNode node, final Object condition) {
544         final Object predicate = operators.tryOverload(node, JexlOperator.CONDITION, condition);
545         return  arithmetic.testPredicate(predicate != JexlEngine.TRY_FAILED? predicate : condition);
546     }
547 
548     @Override
549     protected Object visit(final ASTIfStatement node, final Object data) {
550         final int n = 0;
551         final int numChildren = node.jjtGetNumChildren();
552         try {
553             Object result = null;
554             // pairs of { conditions , 'then' statement }
555             for(int ifElse = 0; ifElse < (numChildren - 1); ifElse += 2) {
556                 final JexlNode testNode = node.jjtGetChild(ifElse);
557                 final Object condition = testNode.jjtAccept(this, null);
558                 if (testPredicate(testNode, condition)) {
559                     // first objectNode is true statement
560                     return node.jjtGetChild(ifElse + 1).jjtAccept(this, null);
561                 }
562             }
563             // if odd...
564             if ((numChildren & 1) == 1) {
565                 // if there is an else, there are an odd number of children in the statement and it is the last child,
566                 // execute it.
567                 result = node.jjtGetChild(numChildren - 1).jjtAccept(this, null);
568             }
569             return result;
570         } catch (final ArithmeticException xrt) {
571             throw new JexlException(node.jjtGetChild(n), "if error", xrt);
572         }
573     }
574 
575     @Override
576     protected Object visit(final ASTVar node, final Object data) {
577         final int symbol = node.getSymbol();
578         // if we have a var, we have a scope thus a frame
579         if (!options.isLexical() && !node.isLexical()) {
580             if (frame.has(symbol)) {
581                 return frame.get(symbol);
582             }
583         } else if (!defineVariable(node, block)) {
584             return redefinedVariable(node, node.getName());
585         }
586         frame.set(symbol, null);
587         return null;
588     }
589 
590     @Override
591     protected Object visit(final ASTBlock node, final Object data) {
592         final int cnt = node.getSymbolCount();
593         if (cnt <= 0) {
594             return visitBlock(node, data);
595         }
596         try {
597             block = new LexicalFrame(frame, block);
598             return visitBlock(node, data);
599         } finally {
600             block = block.pop();
601         }
602     }
603 
604     /**
605      * Base visitation for blocks.
606      * @param node the block
607      * @param data the usual data
608      * @return the result of the last expression evaluation
609      */
610     private Object visitBlock(final ASTBlock node, final Object data) {
611         final int numChildren = node.jjtGetNumChildren();
612         Object result = null;
613         for (int i = 0; i < numChildren; i++) {
614             cancelCheck(node);
615             result = node.jjtGetChild(i).jjtAccept(this, data);
616         }
617         return result;
618     }
619 
620     @Override
621     protected Object visit(final ASTReturnStatement node, final Object data) {
622         final Object val = node.jjtGetChild(0).jjtAccept(this, data);
623         cancelCheck(node);
624         throw new JexlException.Return(node, null, val);
625     }
626 
627     @Override
628     protected Object visit(final ASTContinue node, final Object data) {
629         throw new JexlException.Continue(node);
630     }
631 
632     @Override
633     protected Object visit(final ASTBreak node, final Object data) {
634         throw new JexlException.Break(node);
635     }
636 
637     @Override
638     protected Object visit(final ASTForeachStatement node, final Object data) {
639         return node.getLoopForm() == 0 ? forIterator(node, data) : forLoop(node, data);
640     }
641 
642     private Object forIterator(final ASTForeachStatement node, final Object data) {
643         Object result = null;
644         /* first objectNode is the loop variable */
645         final ASTReference loopReference = (ASTReference) node.jjtGetChild(0);
646         final ASTIdentifier loopVariable = (ASTIdentifier) loopReference.jjtGetChild(0);
647         final int symbol = loopVariable.getSymbol();
648         final boolean lexical = loopVariable.isLexical() || options.isLexical() ;// && node.getSymbolCount() > 0;
649         final LexicalFrame locals = lexical? new LexicalFrame(frame, block) : null;
650         final boolean loopSymbol = symbol >= 0 && loopVariable instanceof ASTVar;
651         if (lexical) {
652             // create lexical frame
653             // it may be a local previously declared
654             if (loopSymbol && !defineVariable((ASTVar) loopVariable, locals)) {
655                 return redefinedVariable(node, loopVariable.getName());
656             }
657             block = locals;
658         }
659         Object forEach = null;
660         try {
661             /* second objectNode is the variable to iterate */
662             final Object iterableValue = node.jjtGetChild(1).jjtAccept(this, data);
663             // make sure there is a value to iterate upon
664             if (iterableValue == null) {
665                 return null;
666             }
667             /* last child node is the statement to execute */
668             final int numChildren = node.jjtGetNumChildren();
669             final JexlNode statement = numChildren >= 3 ? node.jjtGetChild(numChildren - 1) : null;
670             // get an iterator for the collection/array/etc. via the introspector.
671             forEach = operators.tryOverload(node, JexlOperator.FOR_EACH, iterableValue);
672             final Iterator<?> itemsIterator = forEach instanceof Iterator
673                     ? (Iterator<?>) forEach
674                     : uberspect.getIterator(iterableValue);
675             if (itemsIterator == null) {
676                 return null;
677             }
678             int cnt = 0;
679             while (itemsIterator.hasNext()) {
680                 cancelCheck(node);
681                 // reset loop variable
682                 if (lexical && cnt++ > 0) {
683                     // clean up but remain current
684                     block.pop();
685                     // unlikely to fail
686                     if (loopSymbol && !defineVariable((ASTVar) loopVariable, locals)) {
687                         return redefinedVariable(node, loopVariable.getName());
688                     }
689                 }
690                 // set loopVariable to value of iterator
691                 final Object value = itemsIterator.next();
692                 if (symbol < 0) {
693                     setContextVariable(node, loopVariable.getName(), value);
694                 } else {
695                     frame.set(symbol, value);
696                 }
697                 if (statement != null) {
698                     try {
699                         // execute statement
700                         result = statement.jjtAccept(this, data);
701                     } catch (final JexlException.Break stmtBreak) {
702                         break;
703                     } catch (final JexlException.Continue stmtContinue) {
704                         //continue;
705                     }
706                 }
707             }
708         } finally {
709             //  closeable iterator handling
710             closeIfSupported(forEach);
711             // restore lexical frame
712             if (lexical) {
713                 block = block.pop();
714             }
715         }
716         return result;
717     }
718 
719     private Object forLoop(final ASTForeachStatement node, final Object data) {
720         Object result = null;
721         int nc;
722         final int form = node.getLoopForm();
723         final LexicalFrame locals;
724         /* first child node might be the loop variable */
725         if ((form & 1) != 0) {
726             nc = 1;
727             final JexlNode init = node.jjtGetChild(0);
728             ASTVar loopVariable = null;
729             if (init instanceof ASTAssignment) {
730                 final JexlNode child = init.jjtGetChild(0);
731                 if (child instanceof ASTVar) {
732                     loopVariable = (ASTVar) child;
733                 }
734             } else if (init instanceof  ASTVar){
735                 loopVariable = (ASTVar) init;
736             }
737             if (loopVariable != null) {
738                 final boolean lexical = loopVariable.isLexical() || options.isLexical();
739                 locals = lexical ? new LexicalFrame(frame, block) : null;
740                 if (locals != null) {
741                     block = locals;
742                 }
743             } else {
744                 locals = null;
745             }
746             // initialize after eventual creation of local lexical frame
747             init.jjtAccept(this, data);
748             // other inits
749             for (JexlNode moreAssignment = node.jjtGetChild(nc);
750                  moreAssignment instanceof ASTAssignment;
751                  moreAssignment = node.jjtGetChild(++nc)) {
752                 moreAssignment.jjtAccept(this, data);
753             }
754         } else {
755             locals = null;
756             nc = 0;
757         }
758         try {
759             // the loop condition
760             final JexlNode predicate = (form & 2) != 0? node.jjtGetChild(nc++) : null;
761             // the loop step
762             final JexlNode step = (form & 4) != 0? node.jjtGetChild(nc++) : null;
763             // last child is body
764             final JexlNode statement = (form & 8) != 0 ? node.jjtGetChild(nc) : null;
765             // while(predicate())...
766             while (predicate == null || testPredicate(predicate, predicate.jjtAccept(this, data))) {
767                 cancelCheck(node);
768                 // the body
769                 if (statement != null) {
770                     try {
771                         // execute statement
772                         result = statement.jjtAccept(this, data);
773                     } catch (final JexlException.Break stmtBreak) {
774                         break;
775                     } catch (final JexlException.Continue stmtContinue) {
776                         //continue;
777                     }
778                 }
779                 // the step
780                 if (step != null) {
781                     step.jjtAccept(this, data);
782                 }
783             }
784         } finally {
785             // restore lexical frame
786             if (locals != null) {
787                 block = block.pop();
788             }
789         }
790         return result;
791     }
792 
793     @Override
794     protected Object visit(final ASTWhileStatement node, final Object data) {
795         Object result = null;
796         /* first objectNode is the condition */
797         final JexlNode condition = node.jjtGetChild(0);
798         while (testPredicate(condition, condition.jjtAccept(this, data))) {
799             cancelCheck(node);
800             if (node.jjtGetNumChildren() > 1) {
801                 try {
802                     // execute statement
803                     result = node.jjtGetChild(1).jjtAccept(this, data);
804                 } catch (final JexlException.Break stmtBreak) {
805                     break;
806                 } catch (final JexlException.Continue stmtContinue) {
807                     //continue;
808                 }
809             }
810         }
811         return result;
812     }
813 
814     @Override
815     protected Object visit(final ASTDoWhileStatement node, final Object data) {
816         Object result = null;
817         final int nc = node.jjtGetNumChildren();
818         /* last objectNode is the condition */
819         final JexlNode condition = node.jjtGetChild(nc - 1);
820         do {
821             cancelCheck(node);
822             if (nc > 1) {
823                 try {
824                     // execute statement
825                     result = node.jjtGetChild(0).jjtAccept(this, data);
826                 } catch (final JexlException.Break stmtBreak) {
827                     break;
828                 } catch (final JexlException.Continue stmtContinue) {
829                     //continue;
830                 }
831             }
832         } while (testPredicate(condition, condition.jjtAccept(this, data)));
833         return result;
834     }
835 
836     @Override
837     protected Object visit(final ASTAndNode node, final Object data) {
838         /*
839          * The pattern for exception mgmt is to let the child*.jjtAccept out of the try/catch loop so that if one fails,
840          * the ex will traverse up to the interpreter. In cases where this is not convenient/possible, JexlException
841          * must be caught explicitly and rethrown.
842          */
843         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
844         try {
845             final boolean leftValue = arithmetic.toBoolean(left);
846             if (!leftValue) {
847                 return Boolean.FALSE;
848             }
849         } catch (final ArithmeticException xrt) {
850             throw new JexlException(node.jjtGetChild(0), "boolean coercion error", xrt);
851         }
852         final Object right = node.jjtGetChild(1).jjtAccept(this, data);
853         try {
854             final boolean rightValue = arithmetic.toBoolean(right);
855             if (!rightValue) {
856                 return Boolean.FALSE;
857             }
858         } catch (final ArithmeticException xrt) {
859             throw new JexlException(node.jjtGetChild(1), "boolean coercion error", xrt);
860         }
861         return Boolean.TRUE;
862     }
863 
864     @Override
865     protected Object visit(final ASTOrNode node, final Object data) {
866         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
867         try {
868             final boolean leftValue = arithmetic.toBoolean(left);
869             if (leftValue) {
870                 return Boolean.TRUE;
871             }
872         } catch (final ArithmeticException xrt) {
873             throw new JexlException(node.jjtGetChild(0), "boolean coercion error", xrt);
874         }
875         final Object right = node.jjtGetChild(1).jjtAccept(this, data);
876         try {
877             final boolean rightValue = arithmetic.toBoolean(right);
878             if (rightValue) {
879                 return Boolean.TRUE;
880             }
881         } catch (final ArithmeticException xrt) {
882             throw new JexlException(node.jjtGetChild(1), "boolean coercion error", xrt);
883         }
884         return Boolean.FALSE;
885     }
886 
887     @Override
888     protected Object visit(final ASTNullLiteral node, final Object data) {
889         return null;
890     }
891 
892     @Override
893     protected Object visit(final ASTTrueNode node, final Object data) {
894         return Boolean.TRUE;
895     }
896 
897     @Override
898     protected Object visit(final ASTFalseNode node, final Object data) {
899         return Boolean.FALSE;
900     }
901 
902     @Override
903     protected Object visit(final ASTNumberLiteral node, final Object data) {
904         if (data != null && node.isInteger()) {
905             return getAttribute(data, node.getLiteral(), node);
906         }
907         return node.getLiteral();
908     }
909 
910     @Override
911     protected Object visit(final ASTStringLiteral node, final Object data) {
912         if (data != null) {
913             return getAttribute(data, node.getLiteral(), node);
914         }
915         return node.getLiteral();
916     }
917 
918     @Override
919     protected Object visit(final ASTRegexLiteral node, final Object data) {
920         return node.getLiteral();
921     }
922 
923     @Override
924     protected Object visit(final ASTArrayLiteral node, final Object data) {
925         final int childCount = node.jjtGetNumChildren();
926         final JexlArithmetic.ArrayBuilder ab = arithmetic.arrayBuilder(childCount);
927         boolean extended = false;
928         for (int i = 0; i < childCount; i++) {
929             cancelCheck(node);
930             final JexlNode child = node.jjtGetChild(i);
931             if (child instanceof ASTExtendedLiteral) {
932                 extended = true;
933             } else {
934                 final Object entry = node.jjtGetChild(i).jjtAccept(this, data);
935                 ab.add(entry);
936             }
937         }
938         return ab.create(extended);
939     }
940 
941     @Override
942     protected Object visit(final ASTExtendedLiteral node, final Object data) {
943         return node;
944     }
945 
946     @Override
947     protected Object visit(final ASTSetLiteral node, final Object data) {
948         final int childCount = node.jjtGetNumChildren();
949         final JexlArithmetic.SetBuilder mb = arithmetic.setBuilder(childCount);
950         for (int i = 0; i < childCount; i++) {
951             cancelCheck(node);
952             final Object entry = node.jjtGetChild(i).jjtAccept(this, data);
953             mb.add(entry);
954         }
955         return mb.create();
956     }
957 
958     @Override
959     protected Object visit(final ASTMapLiteral node, final Object data) {
960         final int childCount = node.jjtGetNumChildren();
961         final JexlArithmetic.MapBuilder mb = arithmetic.mapBuilder(childCount);
962         for (int i = 0; i < childCount; i++) {
963             cancelCheck(node);
964             final Object[] entry = (Object[]) (node.jjtGetChild(i)).jjtAccept(this, data);
965             mb.put(entry[0], entry[1]);
966         }
967         return mb.create();
968     }
969 
970     @Override
971     protected Object visit(final ASTMapEntry node, final Object data) {
972         final Object key = node.jjtGetChild(0).jjtAccept(this, data);
973         final Object value = node.jjtGetChild(1).jjtAccept(this, data);
974         return new Object[]{key, value};
975     }
976 
977     @Override
978     protected Object visit(final ASTTernaryNode node, final Object data) {
979         Object condition;
980         try {
981             condition = node.jjtGetChild(0).jjtAccept(this, data);
982         } catch(final JexlException xany) {
983             if (!(xany.getCause() instanceof JexlArithmetic.NullOperand)) {
984                 throw xany;
985             }
986             condition = null;
987         }
988         // ternary as in "x ? y : z"
989         if (node.jjtGetNumChildren() == 3) {
990             if (condition != null && arithmetic.toBoolean(condition)) {
991                 return node.jjtGetChild(1).jjtAccept(this, data);
992             }
993             return node.jjtGetChild(2).jjtAccept(this, data);
994         }
995         // elvis as in "x ?: z"
996         if (condition != null && arithmetic.toBoolean(condition)) {
997             return condition;
998         }
999         return node.jjtGetChild(1).jjtAccept(this, data);
1000     }
1001 
1002     @Override
1003     protected Object visit(final ASTNullpNode node, final Object data) {
1004         Object lhs;
1005         try {
1006             lhs = node.jjtGetChild(0).jjtAccept(this, data);
1007         } catch(final JexlException xany) {
1008             if (!(xany.getCause() instanceof JexlArithmetic.NullOperand)) {
1009                 throw xany;
1010             }
1011             lhs = null;
1012         }
1013         // null elision as in "x ?? z"
1014         return lhs != null? lhs : node.jjtGetChild(1).jjtAccept(this, data);
1015     }
1016 
1017     @Override
1018     protected Object visit(final ASTSizeFunction node, final Object data) {
1019         try {
1020             final Object val = node.jjtGetChild(0).jjtAccept(this, data);
1021             return operators.size(node, val);
1022         } catch(final JexlException xany) {
1023             return 0;
1024         }
1025     }
1026 
1027     @Override
1028     protected Object visit(final ASTEmptyFunction node, final Object data) {
1029         try {
1030             final Object value = node.jjtGetChild(0).jjtAccept(this, data);
1031             return operators.empty(node, value);
1032         } catch(final JexlException xany) {
1033             return true;
1034         }
1035     }
1036 
1037     /**
1038      * Runs a node.
1039      * @param node the node
1040      * @param data the usual data
1041      * @return the return value
1042      */
1043     protected Object visitLexicalNode(final JexlNode node, final Object data) {
1044         block = new LexicalFrame(frame, null);
1045         try {
1046             return node.jjtAccept(this, data);
1047         } finally {
1048             block = block.pop();
1049         }
1050     }
1051 
1052     /**
1053      * Runs a closure.
1054      * @param closure the closure
1055      * @param data the usual data
1056      * @return the closure return value
1057      */
1058     protected Object runClosure(final Closure closure, final Object data) {
1059         final ASTJexlScript script = closure.getScript();
1060         // if empty script, nothing to evaluate
1061         final int numChildren = script.jjtGetNumChildren();
1062         if (numChildren == 0) {
1063             return null;
1064         }
1065         block = new LexicalFrame(frame, block).defineArgs();
1066         try {
1067             final JexlNode body = script instanceof ASTJexlLambda
1068                     ? script.jjtGetChild(numChildren - 1)
1069                     : script;
1070             return interpret(body);
1071         } finally {
1072             block = block.pop();
1073         }
1074     }
1075 
1076     @Override
1077     protected Object visit(final ASTJexlScript script, final Object data) {
1078         if (script instanceof ASTJexlLambda && !((ASTJexlLambda) script).isTopLevel()) {
1079             final Closure closure = new Closure(this, (ASTJexlLambda) script);
1080             // if the function is named, assign in the local frame
1081             final JexlNode child0 = script.jjtGetChild(0);
1082             if (child0 instanceof ASTVar) {
1083                 final ASTVar var = (ASTVar) child0;
1084                 this.visit(var, data);
1085                 final int symbol = var.getSymbol();
1086                 frame.set(symbol, closure);
1087                 // make the closure accessible to itself, ie capture the currently set variable after frame creation
1088                 closure.setCaptured(symbol, closure);
1089             }
1090             return closure;
1091         }
1092         block = new LexicalFrame(frame, block).defineArgs();
1093         try {
1094             final int numChildren = script.jjtGetNumChildren();
1095             Object result = null;
1096             for (int i = 0; i < numChildren; i++) {
1097                 final JexlNode child = script.jjtGetChild(i);
1098                 result = child.jjtAccept(this, data);
1099                 cancelCheck(child);
1100             }
1101             return result;
1102         } finally {
1103             block = block.pop();
1104         }
1105     }
1106 
1107     @Override
1108     protected Object visit(final ASTReferenceExpression node, final Object data) {
1109         return node.jjtGetChild(0).jjtAccept(this, data);
1110     }
1111 
1112     @Override
1113     protected Object visit(final ASTIdentifier identifier, final Object data) {
1114         cancelCheck(identifier);
1115         return data != null
1116                 ? getAttribute(data, identifier.getName(), identifier)
1117                 : getVariable(frame, block, identifier);
1118     }
1119 
1120     @Override
1121     protected Object visit(final ASTArrayAccess node, final Object data) {
1122         // first objectNode is the identifier
1123         Object object = data;
1124         // can have multiple nodes - either an expression, integer literal or reference
1125         final int numChildren = node.jjtGetNumChildren();
1126         for (int i = 0; i < numChildren; i++) {
1127             final JexlNode nindex = node.jjtGetChild(i);
1128             if (object == null) {
1129                 return unsolvableProperty(nindex, stringifyProperty(nindex), false, null);
1130             }
1131             final Object index = nindex.jjtAccept(this, null);
1132             cancelCheck(node);
1133             object = getAttribute(object, index, nindex);
1134         }
1135         return object;
1136     }
1137 
1138     @Override
1139     protected Object visit(final ASTQualifiedIdentifier node, final Object data) {
1140         final String name = node.getName();
1141         // try with local solver
1142         String fqcn = fqcnSolver.resolveClassName(name);
1143         if (fqcn != null) {
1144             return fqcn;
1145         }
1146         // context may be solving class name ?
1147         if (context instanceof JexlContext.ClassNameResolver) {
1148             final JexlContext.ClassNameResolver resolver = (JexlContext.ClassNameResolver) context;
1149             fqcn = resolver.resolveClassName(name);
1150             if (fqcn != null) {
1151                 return fqcn;
1152             }
1153         }
1154         return name;
1155     }
1156 
1157     /**
1158      * Evaluates an access identifier based on the 2 main implementations;
1159      * static (name or numbered identifier) or dynamic (jxlt).
1160      * @param node the identifier access node
1161      * @return the evaluated identifier
1162      */
1163     private Object evalIdentifier(final ASTIdentifierAccess node) {
1164         if (!(node instanceof ASTIdentifierAccessJxlt)) {
1165             return node.getIdentifier();
1166         }
1167         final ASTIdentifierAccessJxlt accessJxlt = (ASTIdentifierAccessJxlt) node;
1168         final String src = node.getName();
1169         Throwable cause = null;
1170         TemplateEngine.TemplateExpression expr = (TemplateEngine.TemplateExpression) accessJxlt.getExpression();
1171         try {
1172             if (expr == null) {
1173                 final TemplateEngine jxlt = jexl.jxlt();
1174                 expr = jxlt.parseExpression(node.jexlInfo(), src, frame != null ? frame.getScope() : null);
1175                 accessJxlt.setExpression(expr);
1176             }
1177             if (expr != null) {
1178                 final Object name = expr.evaluate(context, frame, options);
1179                 if (name != null) {
1180                     final Integer id = ASTIdentifierAccess.parseIdentifier(name.toString());
1181                     return id != null ? id : name;
1182                 }
1183             }
1184         } catch (final JxltEngine.Exception xjxlt) {
1185             cause = xjxlt;
1186         }
1187         return node.isSafe() ? null : unsolvableProperty(node, src, true, cause);
1188     }
1189 
1190     @Override
1191     protected Object visit(final ASTIdentifierAccess node, final Object data) {
1192         if (data == null) {
1193             return null;
1194         }
1195         final Object id = evalIdentifier(node);
1196         return getAttribute(data, id, node);
1197     }
1198 
1199     @Override
1200     protected Object visit(final ASTReference node, final Object data) {
1201         cancelCheck(node);
1202         final int numChildren = node.jjtGetNumChildren();
1203         final JexlNode parent = node.jjtGetParent();
1204         // pass first piece of data in and loop through children
1205         Object object = null;
1206         JexlNode objectNode = null;
1207         JexlNode ptyNode = null;
1208         StringBuilder ant = null;
1209         boolean antish = !(parent instanceof ASTReference) && options.isAntish();
1210         int v = 1;
1211         main:
1212         for (int c = 0; c < numChildren; c++) {
1213             objectNode = node.jjtGetChild(c);
1214             if (objectNode instanceof ASTMethodNode) {
1215                 antish = false;
1216                 if (object == null) {
1217                     // we may be performing a method call on an antish var
1218                     if (ant != null) {
1219                         final JexlNode child = objectNode.jjtGetChild(0);
1220                         if (child instanceof ASTIdentifierAccess) {
1221                             final int alen = ant.length();
1222                             ant.append('.');
1223                             ant.append(((ASTIdentifierAccess) child).getName());
1224                             object = context.get(ant.toString());
1225                             if (object != null) {
1226                                 object = visit((ASTMethodNode) objectNode, object, context);
1227                                 continue;
1228                             }
1229                             // remove method name from antish
1230                             ant.delete(alen, ant.length());
1231                             ptyNode = objectNode;
1232                         }
1233                     }
1234                     break;
1235                 }
1236             } else if (objectNode instanceof ASTArrayAccess) {
1237                 antish = false;
1238                 if (object == null) {
1239                     ptyNode = objectNode;
1240                     break;
1241                 }
1242             }
1243             // attempt to evaluate the property within the object (visit(ASTIdentifierAccess node))
1244             object = objectNode.jjtAccept(this, object);
1245             cancelCheck(node);
1246             if (object != null) {
1247                 // disallow mixing antish variable & bean with same root; avoid ambiguity
1248                 antish = false;
1249             } else if (antish) {
1250                 // create first from first node
1251                 if (ant == null) {
1252                     // if we still have a null object, check for an antish variable
1253                     final JexlNode first = node.jjtGetChild(0);
1254                     if (!(first instanceof ASTIdentifier)) {
1255                         // not an identifier, not antish
1256                         ptyNode = objectNode;
1257                         break main;
1258                     }
1259                     final ASTIdentifier afirst = (ASTIdentifier) first;
1260                     ant = new StringBuilder(afirst.getName());
1261                     continue;
1262                     // skip the first node case since it was trialed in jjtAccept above and returned null
1263                 }
1264                 // catch up to current node
1265                 for (; v <= c; ++v) {
1266                     final JexlNode child = node.jjtGetChild(v);
1267                     if (!(child instanceof ASTIdentifierAccess)) {
1268                         // not an identifier, not antish
1269                         ptyNode = objectNode;
1270                         break main;
1271                     }
1272                     final ASTIdentifierAccess achild = (ASTIdentifierAccess) child;
1273                     if (achild.isSafe() || achild.isExpression()) {
1274                         break main;
1275                     }
1276                     ant.append('.');
1277                     ant.append(achild.getName());
1278                 }
1279                 // solve antish
1280                 object = context.get(ant.toString());
1281             } else if (c != numChildren - 1) {
1282                 // only the last one may be null
1283                 ptyNode = c == 0 && numChildren > 1 ? node.jjtGetChild(1) : objectNode;
1284                 break; //
1285             }
1286         }
1287         // dealing with null
1288         if (object == null) {
1289             if (ptyNode != null) {
1290                 if (ptyNode.isSafeLhs(isSafe())) {
1291                     return null;
1292                 }
1293                 if (ant != null) {
1294                     final String aname = ant.toString();
1295                     final boolean defined = isVariableDefined(frame, block, aname);
1296                     return unsolvableVariable(node, aname, !defined);
1297                 }
1298                 return unsolvableProperty(node,
1299                         stringifyProperty(ptyNode), ptyNode == objectNode, null);
1300             }
1301             if (antish) {
1302                 if (node.isSafeLhs(isSafe())) {
1303                     return null;
1304                 }
1305                 final String aname = ant != null ? ant.toString() : "?";
1306                 final boolean defined = isVariableDefined(frame, block, aname);
1307                 // defined but null; arg of a strict operator?
1308                 if (defined && !isStrictOperand(node)) {
1309                     return null;
1310                 }
1311                 return unsolvableVariable(node, aname, !defined);
1312             }
1313         }
1314         return object;
1315     }
1316 
1317     @Override
1318     protected Object visit(final ASTAssignment node, final Object data) {
1319         return executeAssign(node, null, data);
1320     }
1321 
1322     @Override
1323     protected Object visit(final ASTSetAddNode node, final Object data) {
1324         return executeAssign(node, JexlOperator.SELF_ADD, data);
1325     }
1326 
1327     @Override
1328     protected Object visit(final ASTSetSubNode node, final Object data) {
1329         return executeAssign(node, JexlOperator.SELF_SUBTRACT, data);
1330     }
1331 
1332     @Override
1333     protected Object visit(final ASTSetMultNode node, final Object data) {
1334         return executeAssign(node, JexlOperator.SELF_MULTIPLY, data);
1335     }
1336 
1337     @Override
1338     protected Object visit(final ASTSetDivNode node, final Object data) {
1339         return executeAssign(node, JexlOperator.SELF_DIVIDE, data);
1340     }
1341 
1342     @Override
1343     protected Object visit(final ASTSetModNode node, final Object data) {
1344         return executeAssign(node, JexlOperator.SELF_MOD, data);
1345     }
1346 
1347     @Override
1348     protected Object visit(final ASTSetAndNode node, final Object data) {
1349         return executeAssign(node, JexlOperator.SELF_AND, data);
1350     }
1351 
1352     @Override
1353     protected Object visit(final ASTSetOrNode node, final Object data) {
1354         return executeAssign(node, JexlOperator.SELF_OR, data);
1355     }
1356 
1357     @Override
1358     protected Object visit(final ASTSetXorNode node, final Object data) {
1359         return executeAssign(node, JexlOperator.SELF_XOR, data);
1360     }
1361 
1362     @Override
1363     protected Object visit(final ASTSetShiftLeftNode node, final Object data) {
1364         return executeAssign(node, JexlOperator.SELF_SHIFTLEFT, data);
1365     }
1366 
1367     @Override
1368     protected Object visit(final ASTSetShiftRightNode node, final Object data) {
1369         return executeAssign(node, JexlOperator.SELF_SHIFTRIGHT, data);
1370     }
1371 
1372     @Override
1373     protected Object visit(final ASTSetShiftRightUnsignedNode node, final Object data) {
1374         return executeAssign(node, JexlOperator.SELF_SHIFTRIGHTU, data);
1375     }
1376 
1377     @Override
1378     protected Object visit(final ASTGetDecrementNode node, final Object data) {
1379         return executeAssign(node, JexlOperator.GET_AND_DECREMENT, data);
1380     }
1381 
1382     @Override
1383     protected Object visit(final ASTGetIncrementNode node, final Object data) {
1384         return executeAssign(node, JexlOperator.GET_AND_INCREMENT, data);
1385     }
1386 
1387     @Override
1388     protected Object visit(final ASTDecrementGetNode node, final Object data) {
1389         return executeAssign(node, JexlOperator.DECREMENT_AND_GET, data);
1390     }
1391 
1392     @Override
1393     protected Object visit(final ASTIncrementGetNode node, final Object data) {
1394         return executeAssign(node, JexlOperator.INCREMENT_AND_GET, data);
1395     }
1396 
1397     /**
1398      * Executes an assignment with an optional side effect operator.
1399      * @param node     the node
1400      * @param assignop the assignment operator or null if simply assignment
1401      * @param data     the data
1402      * @return the left hand side
1403      */
1404     protected Object executeAssign(final JexlNode node, final JexlOperator assignop, final Object data) { // CSOFF: MethodLength
1405         cancelCheck(node);
1406         // left contains the reference to assign to
1407         final JexlNode left = node.jjtGetChild(0);
1408         final ASTIdentifier var;
1409         Object object = null;
1410         final int symbol;
1411         // check var decl with assign is ok
1412         if (left instanceof ASTIdentifier) {
1413             var = (ASTIdentifier) left;
1414             symbol = var.getSymbol();
1415             if (symbol >= 0 && (var.isLexical() || options.isLexical())) {
1416                 if (var instanceof ASTVar) {
1417                     if (!defineVariable((ASTVar) var, block)) {
1418                         return redefinedVariable(var, var.getName());
1419                     }
1420                 } else if (var.isShaded() && (var.isLexical() || options.isLexicalShade())) {
1421                     return undefinedVariable(var, var.getName());
1422                 }
1423             }
1424         } else {
1425             var = null;
1426             symbol = -1;
1427         }
1428         boolean antish = options.isAntish();
1429         // 0: determine initial object & property:
1430         final int last = left.jjtGetNumChildren() - 1;
1431         // right is the value expression to assign
1432        final  Object right = node.jjtGetNumChildren() < 2? null: node.jjtGetChild(1).jjtAccept(this, data);
1433         // actual value to return, right in most cases
1434         Object actual = right;
1435         // a (var?) v = ... expression
1436         if (var != null) {
1437             if (symbol >= 0) {
1438                 // check we are not assigning a symbol itself
1439                 if (last < 0) {
1440                     if (assignop == null) {
1441                         // make the closure accessible to itself, ie capture the currently set variable after frame creation
1442                         if (right instanceof Closure) {
1443                             ((Closure) right).setCaptured(symbol, right);
1444                         }
1445                         frame.set(symbol, right);
1446                     } else {
1447                         // go through potential overload
1448                         final Object self = getVariable(frame, block, var);
1449                         final Consumer<Object> f = r -> frame.set(symbol, r);
1450                         actual = operators.tryAssignOverload(node, assignop, f, self, right);
1451                     }
1452                     return actual; // 1
1453                 }
1454                 object = getVariable(frame, block, var);
1455                 // top level is a symbol, can not be an antish var
1456                 antish = false;
1457             } else {
1458                 // check we are not assigning direct global
1459                 final String name = var.getName();
1460                 if (last < 0) {
1461                     if (assignop == null) {
1462                         setContextVariable(node, name, right);
1463                     } else {
1464                         // go through potential overload
1465                         final Object self = context.get(name);
1466                         final Consumer<Object> f = r ->  setContextVariable(node, name, r);
1467                         actual = operators.tryAssignOverload(node, assignop, f, self, right);
1468                     }
1469                     return actual; // 2
1470                 }
1471                 object = context.get(name);
1472                 // top level accesses object, can not be an antish var
1473                 if (object != null) {
1474                     antish = false;
1475                 }
1476             }
1477         } else if (!(left instanceof ASTReference)) {
1478             throw new JexlException(left, "illegal assignment form 0");
1479         }
1480         // 1: follow children till penultimate, resolve dot/array
1481         JexlNode objectNode = null;
1482         StringBuilder ant = null;
1483         int v = 1;
1484         // start at 1 if symbol
1485         main: for (int c = symbol >= 0 ? 1 : 0; c < last; ++c) {
1486             objectNode = left.jjtGetChild(c);
1487             object = objectNode.jjtAccept(this, object);
1488             if (object != null) {
1489                 // disallow mixing antish variable & bean with same root; avoid ambiguity
1490                 antish = false;
1491             } else if (antish) {
1492                 // initialize if first time
1493                 if (ant == null) {
1494                     final JexlNode first = left.jjtGetChild(0);
1495                     final ASTIdentifier firstId = first instanceof ASTIdentifier
1496                             ? (ASTIdentifier) first
1497                             : null;
1498                     if ((firstId == null) || (firstId.getSymbol() >= 0)) {
1499                         // ant remains null, object is null, stop solving
1500                         antish = false;
1501                         break main;
1502                     }
1503                     ant = new StringBuilder(firstId.getName());
1504                 }
1505                 // catch up to current child
1506                 for (; v <= c; ++v) {
1507                     final JexlNode child = left.jjtGetChild(v);
1508                     final ASTIdentifierAccess aid = child instanceof ASTIdentifierAccess
1509                             ? (ASTIdentifierAccess) child
1510                             : null;
1511                     // remain antish only if unsafe navigation
1512                     if ((aid == null) || aid.isSafe() || aid.isExpression()) {
1513                         antish = false;
1514                         break main;
1515                     }
1516                     ant.append('.');
1517                     ant.append(aid.getName());
1518                 }
1519                 // solve antish
1520                 object = context.get(ant.toString());
1521             } else {
1522                 throw new JexlException(objectNode, "illegal assignment form");
1523             }
1524         }
1525         // 2: last objectNode will perform assignment in all cases
1526         JexlNode propertyNode = left.jjtGetChild(last);
1527         final ASTIdentifierAccess propertyId = propertyNode instanceof ASTIdentifierAccess
1528                 ? (ASTIdentifierAccess) propertyNode
1529                 : null;
1530         final Object property;
1531         if (propertyId != null) {
1532             // deal with creating/assigning antish variable
1533             if (antish && ant != null && object == null && !propertyId.isSafe() && !propertyId.isExpression()) {
1534                 ant.append('.');
1535                 ant.append(propertyId.getName());
1536                 final String name = ant.toString();
1537                 if (assignop == null) {
1538                     setContextVariable(propertyNode, name, right);
1539                 } else {
1540                     final Object self = context.get(ant.toString());
1541                     final JexlNode pnode = propertyNode;
1542                     final Consumer<Object> assign = r -> setContextVariable(pnode, name, r);
1543                     actual = operators.tryAssignOverload(node, assignop, assign, self, right);
1544                 }
1545                 return actual; // 3
1546             }
1547             // property of an object ?
1548             property = evalIdentifier(propertyId);
1549         } else if (propertyNode instanceof ASTArrayAccess) {
1550             // can have multiple nodes - either an expression, integer literal or reference
1551             final int numChildren = propertyNode.jjtGetNumChildren() - 1;
1552             for (int i = 0; i < numChildren; i++) {
1553                 final JexlNode nindex = propertyNode.jjtGetChild(i);
1554                 final Object index = nindex.jjtAccept(this, null);
1555                 object = getAttribute(object, index, nindex);
1556             }
1557             propertyNode = propertyNode.jjtGetChild(numChildren);
1558             property = propertyNode.jjtAccept(this, null);
1559         } else {
1560             throw new JexlException(objectNode, "illegal assignment form");
1561         }
1562         // we may have a null property as in map[null], no check needed.
1563         // we can not *have* a null object though.
1564         if (object == null) {
1565             // no object, we fail
1566             return unsolvableProperty(objectNode, "<null>.<?>", true, null);
1567         }
1568         // 3: one before last, assign
1569         if (assignop == null) {
1570             setAttribute(object, property, right, propertyNode);
1571         } else {
1572             final Object self = getAttribute(object, property, propertyNode);
1573             final Object o = object;
1574             final JexlNode n = propertyNode;
1575             final Consumer<Object> assign = r ->  setAttribute(o, property, r, n);
1576             actual = operators.tryAssignOverload(node, assignop, assign, self, right);
1577         }
1578         return actual;
1579     }
1580 
1581     @Override
1582     protected Object visit(final ASTDefineVars node, final Object data) {
1583         final int argc = node.jjtGetNumChildren();
1584         Object result = null;
1585         for (int i = 0; i < argc; i++) {
1586             result = node.jjtGetChild(i).jjtAccept(this, data);
1587         }
1588         return result;
1589     }
1590 
1591     @Override
1592     protected Object[] visit(final ASTArguments node, final Object data) {
1593         final int argc = node.jjtGetNumChildren();
1594         final Object[] argv = new Object[argc];
1595         for (int i = 0; i < argc; i++) {
1596             argv[i] = node.jjtGetChild(i).jjtAccept(this, data);
1597         }
1598         return argv;
1599     }
1600 
1601     @Override
1602     protected Object visit(final ASTMethodNode node, final Object data) {
1603         return visit(node, null, data);
1604     }
1605 
1606     /**
1607      * Execute a method call, ie syntactically written as name.call(...).
1608      * @param node the actual method call node
1609      * @param antish non-null when name.call is an antish variable
1610      * @param data the context
1611      * @return the method call result
1612      */
1613     private Object visit(final ASTMethodNode node, final Object antish, final Object data) {
1614         Object object = antish;
1615         // left contains the reference to the method
1616         final JexlNode methodNode = node.jjtGetChild(0);
1617         Object method;
1618         // 1: determine object and method or functor
1619         if (methodNode instanceof ASTIdentifierAccess) {
1620             method = methodNode;
1621             if (object == null) {
1622                 object = data;
1623                 if (object == null) {
1624                     // no object, we fail
1625                     return node.isSafeLhs(isSafe())
1626                         ? null
1627                         : unsolvableMethod(methodNode, "<null>.<?>(...)");
1628                 }
1629             } else {
1630                 // edge case of antish var used as functor
1631                 method = object;
1632             }
1633         } else {
1634             method = methodNode.jjtAccept(this, data);
1635         }
1636         Object result = method;
1637         for (int a = 1; a < node.jjtGetNumChildren(); ++a) {
1638             if (result == null) {
1639                 // no method, we fail// variable unknown in context and not a local
1640                 return node.isSafeLhs(isSafe())
1641                         ? null
1642                         : unsolvableMethod(methodNode, "<?>.<null>(...)");
1643             }
1644             final ASTArguments argNode = (ASTArguments) node.jjtGetChild(a);
1645             result = call(node, object, result, argNode);
1646             object = result;
1647         }
1648         return result;
1649     }
1650 
1651     @Override
1652     protected Object visit(final ASTFunctionNode node, final Object data) {
1653         final ASTIdentifier functionNode = (ASTIdentifier) node.jjtGetChild(0);
1654         final String nsid = functionNode.getNamespace();
1655         final Object namespace = (nsid != null)? resolveNamespace(nsid, node) : context;
1656         final ASTArguments argNode = (ASTArguments) node.jjtGetChild(1);
1657         return call(node, namespace, functionNode, argNode);
1658     }
1659 
1660     /**
1661      * Calls a method (or function).
1662      * <p>
1663      * Method resolution is a follows:
1664      * 1 - attempt to find a method in the target passed as parameter;
1665      * 2 - if this fails, seeks a JexlScript or JexlMethod or a duck-callable* as a property of that target;
1666      * 3 - if this fails, narrow the arguments and try again 1
1667      * 4 - if this fails, seeks a context or arithmetic method with the proper name taking the target as first argument;
1668      * </p>
1669      * *duck-callable: an object where a "call" function exists
1670      *
1671      * @param node    the method node
1672      * @param target  the target of the method, what it should be invoked upon
1673      * @param funcNode the object carrying the method or function or the method identifier
1674      * @param argNode the node carrying the arguments
1675      * @return the result of the method invocation
1676      */
1677     protected Object call(final JexlNode node, final Object target, final Object funcNode, final ASTArguments argNode) {
1678         cancelCheck(node);
1679         // evaluate the arguments
1680         final Object[] argv = visit(argNode, null);
1681         final String methodName;
1682         boolean cacheable = cache;
1683         boolean isavar = false;
1684         Object functor = funcNode;
1685         // get the method name if identifier
1686         if (functor instanceof ASTIdentifier) {
1687             // function call, target is context or namespace (if there was one)
1688             final ASTIdentifier methodIdentifier = (ASTIdentifier) functor;
1689             final int symbol = methodIdentifier.getSymbol();
1690             methodName = methodIdentifier.getName();
1691             functor = null;
1692             // is it a global or local variable ?
1693             if (target == context) {
1694                 if (frame != null && frame.has(symbol)) {
1695                     functor = frame.get(symbol);
1696                     isavar = functor != null;
1697                 } else if (context.has(methodName)) {
1698                     functor = context.get(methodName);
1699                     isavar = functor != null;
1700                 }
1701                 // name is a variable, can't be cached
1702                 cacheable &= !isavar;
1703             }
1704         } else if (functor instanceof ASTIdentifierAccess) {
1705             // a method call on target
1706             methodName = ((ASTIdentifierAccess) functor).getName();
1707             functor = null;
1708             cacheable = true;
1709         } else if (functor != null) {
1710             // ...(x)(y)
1711             methodName = null;
1712             cacheable = false;
1713         } else if (!node.isSafeLhs(isSafe())) {
1714             return unsolvableMethod(node, "?(...)");
1715         } else {
1716             // safe lhs
1717             return null;
1718         }
1719 
1720         // solving the call site
1721         final CallDispatcher call = new CallDispatcher(node, cacheable);
1722         try {
1723             // do we have a  cached version method/function name ?
1724             final Object eval = call.tryEval(target, methodName, argv);
1725             if (JexlEngine.TRY_FAILED != eval) {
1726                 return eval;
1727             }
1728             boolean functorp = false;
1729             boolean narrow = false;
1730             // pseudo loop to try acquiring methods without and with argument narrowing
1731             while (true) {
1732                 call.narrow = narrow;
1733                 // direct function or method call
1734                 if (functor == null || functorp) {
1735                     // try a method or function from context
1736                     if (call.isTargetMethod(target, methodName, argv)) {
1737                         return call.eval(methodName);
1738                     }
1739                     if (target == context) {
1740                         // solve 'null' namespace
1741                         final Object namespace = resolveNamespace(null, node);
1742                         if (namespace != null
1743                             && namespace != context
1744                             && call.isTargetMethod(namespace, methodName, argv)) {
1745                             return call.eval(methodName);
1746                         }
1747                         // do not try context function since this was attempted
1748                         // 10 lines above...; solve as an arithmetic function
1749                         if (call.isArithmeticMethod(methodName, argv)) {
1750                             return call.eval(methodName);
1751                         }
1752                         // could not find a method, try as a property of a non-context target (performed once)
1753                     } else {
1754                         // try prepending target to arguments and look for
1755                         // applicable method in context...
1756                         final Object[] pargv = functionArguments(target, narrow, argv);
1757                         if (call.isContextMethod(methodName, pargv)) {
1758                             return call.eval(methodName);
1759                         }
1760                         // ...or arithmetic
1761                         if (call.isArithmeticMethod(methodName, pargv)) {
1762                             return call.eval(methodName);
1763                         }
1764                         // the method may also be a functor stored in a property of the target
1765                         if (!narrow) {
1766                             final JexlPropertyGet get = uberspect.getPropertyGet(target, methodName);
1767                             if (get != null) {
1768                                 functor = get.tryInvoke(target, methodName);
1769                                 functorp = functor != null;
1770                             }
1771                         }
1772                     }
1773                 }
1774                 // this may happen without the above when we are chaining call like x(a)(b)
1775                 // or when a var/symbol or antish var is used as a "function" name
1776                 if (functor != null) {
1777                     // lambda, script or jexl method will do
1778                     if (functor instanceof JexlScript) {
1779                         return ((JexlScript) functor).execute(context, argv);
1780                     }
1781                     if (functor instanceof JexlMethod) {
1782                         return ((JexlMethod) functor).invoke(target, argv);
1783                     }
1784                     final String mCALL = "call";
1785                     // may be a generic callable, try a 'call' method
1786                     if (call.isTargetMethod(functor, mCALL, argv)) {
1787                         return call.eval(mCALL);
1788                     }
1789                     // functor is a var, may be method is a global one ?
1790                     if (isavar) {
1791                         if (call.isContextMethod(methodName, argv)) {
1792                             return call.eval(methodName);
1793                         }
1794                         if (call.isArithmeticMethod(methodName, argv)) {
1795                             return call.eval(methodName);
1796                         }
1797                     }
1798                     // try prepending functor to arguments and look for
1799                     // context or arithmetic function called 'call'
1800                     final Object[] pargv = functionArguments(functor, narrow, argv);
1801                     if (call.isContextMethod(mCALL, pargv)) {
1802                         return call.eval(mCALL);
1803                     }
1804                     if (call.isArithmeticMethod(mCALL, pargv)) {
1805                         return call.eval(mCALL);
1806                     }
1807                 }
1808                 // if we did not find an exact method by name and we haven't tried yet,
1809                 // attempt to narrow the parameters and if this succeeds, try again in next loop
1810                 if (narrow || !arithmetic.narrowArguments(argv)) {
1811                     break;
1812                 }
1813                 narrow = true;
1814                 // continue;
1815             }
1816         } catch (final JexlException.Method xmethod) {
1817             // ignore and handle at end; treat as an inner discover that fails
1818         } catch (final JexlException.TryFailed xany) {
1819             throw invocationException(node, methodName, xany);
1820         } catch (final JexlException xthru) {
1821             throw xthru;
1822         } catch (final Exception xany) {
1823             throw invocationException(node, methodName, xany);
1824         }
1825         // we have either evaluated and returned or no method was found
1826         return node.isSafeLhs(isSafe())
1827                 ? null
1828                 : unsolvableMethod(node, methodName, argv);
1829     }
1830 
1831     @Override
1832     protected Object visit(final ASTConstructorNode node, final Object data) {
1833         if (isCancelled()) {
1834             throw new JexlException.Cancel(node);
1835         }
1836         // first child is class or class name
1837         final Object target = node.jjtGetChild(0).jjtAccept(this, data);
1838         // get the ctor args
1839         final int argc = node.jjtGetNumChildren() - 1;
1840         Object[] argv = new Object[argc];
1841         for (int i = 0; i < argc; i++) {
1842             argv[i] = node.jjtGetChild(i + 1).jjtAccept(this, data);
1843         }
1844 
1845         try {
1846             final boolean cacheable = cache;
1847             // attempt to reuse last funcall cached in volatile JexlNode.value
1848             if (cacheable) {
1849                 final Object cached = node.jjtGetValue();
1850                 if (cached instanceof Funcall) {
1851                     final Object eval = ((Funcall) cached).tryInvoke(this, null, target, argv);
1852                     if (JexlEngine.TRY_FAILED != eval) {
1853                         return eval;
1854                     }
1855                 }
1856             }
1857             boolean narrow = false;
1858             Funcall funcall = null;
1859             JexlMethod ctor;
1860             while (true) {
1861                 // try as stated
1862                 ctor = uberspect.getConstructor(target, argv);
1863                 if (ctor != null) {
1864                     if (cacheable && ctor.isCacheable()) {
1865                         funcall = new Funcall(ctor, narrow);
1866                     }
1867                     break;
1868                 }
1869                 // try with prepending context as first argument
1870                 final Object[] nargv = callArguments(context, narrow, argv);
1871                 ctor = uberspect.getConstructor(target, nargv);
1872                 if (ctor != null) {
1873                     if (cacheable && ctor.isCacheable()) {
1874                         funcall = new ContextualCtor(ctor, narrow);
1875                     }
1876                     argv = nargv;
1877                     break;
1878                 }
1879                 // if we did not find an exact method by name and we haven't tried yet,
1880                 // attempt to narrow the parameters and if this succeeds, try again in next loop
1881                 if (!narrow && arithmetic.narrowArguments(argv)) {
1882                     narrow = true;
1883                     continue;
1884                 }
1885                 // we are done trying
1886                 break;
1887             }
1888             // we have either evaluated and returned or might have found a ctor
1889             if (ctor != null) {
1890                 final Object eval = ctor.invoke(target, argv);
1891                 // cache executor in volatile JexlNode.value
1892                 if (funcall != null) {
1893                     node.jjtSetValue(funcall);
1894                 }
1895                 return eval;
1896             }
1897             final String tstr = target != null ? target.toString() : "?";
1898             return unsolvableMethod(node, tstr, argv);
1899         } catch (final JexlException.Method xmethod) {
1900             throw xmethod;
1901         } catch (final Exception xany) {
1902             final String tstr = target != null ? target.toString() : "?";
1903             throw invocationException(node, tstr, xany);
1904         }
1905     }
1906 
1907     @Override
1908     protected Object visit(final ASTJxltLiteral node, final Object data) {
1909         TemplateEngine.TemplateExpression tp = (TemplateEngine.TemplateExpression) node.jjtGetValue();
1910         if (tp == null) {
1911             final TemplateEngine jxlt = jexl.jxlt();
1912             JexlInfo info = node.jexlInfo();
1913             if (this.block != null) {
1914                 info = new JexlNode.Info(node, info);
1915             }
1916             tp = jxlt.parseExpression(info, node.getLiteral(), frame != null ? frame.getScope() : null);
1917             node.jjtSetValue(tp);
1918         }
1919         if (tp != null) {
1920             return tp.evaluate(context, frame, options);
1921         }
1922         return null;
1923     }
1924 
1925     @Override
1926     protected Object visit(final ASTAnnotation node, final Object data) {
1927         throw new UnsupportedOperationException(ASTAnnotation.class.getName() + ": Not supported.");
1928     }
1929 
1930     @Override
1931     protected Object visit(final ASTAnnotatedStatement node, final Object data) {
1932         return processAnnotation(node, 0, data);
1933     }
1934 
1935     /**
1936      * An annotated call.
1937      */
1938     public class AnnotatedCall implements Callable<Object> {
1939         /** The statement. */
1940         private final ASTAnnotatedStatement stmt;
1941         /** The child index. */
1942         private final int index;
1943         /** The data. */
1944         private final Object data;
1945         /** Tracking whether we processed the annotation. */
1946         private boolean processed = false;
1947 
1948         /**
1949          * Simple ctor.
1950          * @param astmt the statement
1951          * @param aindex the index
1952          * @param adata the data
1953          */
1954         AnnotatedCall(final ASTAnnotatedStatement astmt, final int aindex, final Object adata) {
1955             stmt = astmt;
1956             index = aindex;
1957             data = adata;
1958         }
1959 
1960 
1961         @Override
1962         public Object call() throws Exception {
1963             processed = true;
1964             try {
1965                 return processAnnotation(stmt, index, data);
1966             } catch (JexlException.Return | JexlException.Break | JexlException.Continue xreturn) {
1967                 return xreturn;
1968             }
1969         }
1970 
1971         /**
1972          * @return whether the statement has been processed
1973          */
1974         public boolean isProcessed() {
1975             return processed;
1976         }
1977 
1978         /**
1979          * @return the actual statement.
1980          */
1981         public Object getStatement() {
1982             return stmt;
1983         }
1984     }
1985 
1986     /**
1987      * Processes an annotated statement.
1988      * @param stmt the statement
1989      * @param index the index of the current annotation being processed
1990      * @param data the contextual data
1991      * @return  the result of the statement block evaluation
1992      */
1993     protected Object processAnnotation(final ASTAnnotatedStatement stmt, final int index, final Object data) {
1994         // are we evaluating the block ?
1995         final int last = stmt.jjtGetNumChildren() - 1;
1996         if (index == last) {
1997             final JexlNode cblock = stmt.jjtGetChild(last);
1998             // if the context has changed, might need a new interpreter
1999             final JexlArithmetic jexla = arithmetic.options(context);
2000             if (jexla == arithmetic) {
2001                 return cblock.jjtAccept(Interpreter.this, data);
2002             }
2003             if (!arithmetic.getClass().equals(jexla.getClass()) && logger.isWarnEnabled()) {
2004                 logger.warn("expected arithmetic to be " + arithmetic.getClass().getSimpleName()
2005                         + ", got " + jexla.getClass().getSimpleName()
2006                 );
2007             }
2008             final Interpreter ii = new Interpreter(Interpreter.this, jexla);
2009             final Object r = cblock.jjtAccept(ii, data);
2010             if (ii.isCancelled()) {
2011                 Interpreter.this.cancel();
2012             }
2013             return r;
2014         }
2015         // tracking whether we processed the annotation
2016         final AnnotatedCall jstmt = new AnnotatedCall(stmt, index + 1, data);
2017         // the annotation node and name
2018         final ASTAnnotation anode = (ASTAnnotation) stmt.jjtGetChild(index);
2019         final String aname = anode.getName();
2020         // evaluate the arguments
2021         final Object[] argv = anode.jjtGetNumChildren() > 0
2022                         ? visit((ASTArguments) anode.jjtGetChild(0), null) : null;
2023         // wrap the future, will recurse through annotation processor
2024         Object result;
2025         try {
2026             result = processAnnotation(aname, argv, jstmt);
2027             // not processing an annotation is an error
2028             if (!jstmt.isProcessed()) {
2029                 return annotationError(anode, aname, null);
2030             }
2031         } catch (final JexlException xany) {
2032             throw xany;
2033         } catch (final Exception xany) {
2034             return annotationError(anode, aname, xany);
2035         }
2036         // the caller may return a return, break or continue
2037         if (result instanceof JexlException) {
2038             throw (JexlException) result;
2039         }
2040         return result;
2041     }
2042 
2043     /**
2044      * Delegates the annotation processing to the JexlContext if it is an AnnotationProcessor.
2045      * @param annotation    the annotation name
2046      * @param args          the annotation arguments
2047      * @param stmt          the statement / block that was annotated
2048      * @return the result of statement.call()
2049      * @throws Exception if anything goes wrong
2050      */
2051     protected Object processAnnotation(final String annotation, final Object[] args, final Callable<Object> stmt) throws Exception {
2052                 return context instanceof JexlContext.AnnotationProcessor
2053                 ? ((JexlContext.AnnotationProcessor) context).processAnnotation(annotation, args, stmt)
2054                 : stmt.call();
2055     }
2056 }