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