1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.jexl3.internal;
18
19 import java.util.Collection;
20 import java.util.HashMap;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Objects;
24 import java.util.Queue;
25 import java.util.concurrent.atomic.AtomicBoolean;
26
27 import org.apache.commons.jexl3.JexlArithmetic;
28 import org.apache.commons.jexl3.JexlContext;
29 import org.apache.commons.jexl3.JexlContext.NamespaceFunctor;
30 import org.apache.commons.jexl3.JexlEngine;
31 import org.apache.commons.jexl3.JexlException;
32 import org.apache.commons.jexl3.JexlException.VariableIssue;
33 import org.apache.commons.jexl3.JexlOperator;
34 import org.apache.commons.jexl3.JexlOptions;
35 import org.apache.commons.jexl3.introspection.JexlMethod;
36 import org.apache.commons.jexl3.introspection.JexlPropertyGet;
37 import org.apache.commons.jexl3.introspection.JexlPropertySet;
38 import org.apache.commons.jexl3.introspection.JexlUberspect;
39 import org.apache.commons.jexl3.parser.ASTArrayAccess;
40 import org.apache.commons.jexl3.parser.ASTAssignment;
41 import org.apache.commons.jexl3.parser.ASTFunctionNode;
42 import org.apache.commons.jexl3.parser.ASTIdentifier;
43 import org.apache.commons.jexl3.parser.ASTIdentifierAccess;
44 import org.apache.commons.jexl3.parser.ASTMethodNode;
45 import org.apache.commons.jexl3.parser.ASTNullpNode;
46 import org.apache.commons.jexl3.parser.ASTReference;
47 import org.apache.commons.jexl3.parser.ASTTernaryNode;
48 import org.apache.commons.jexl3.parser.ASTVar;
49 import org.apache.commons.jexl3.parser.JexlNode;
50 import org.apache.commons.jexl3.parser.ParserVisitor;
51 import org.apache.commons.logging.Log;
52
53
54
55
56
57 public abstract class InterpreterBase extends ParserVisitor {
58
59
60
61 protected class CallDispatcher {
62
63 final JexlNode node;
64
65 final boolean cacheable;
66
67 boolean narrow;
68
69 JexlMethod vm;
70
71 Object target;
72
73 Object[] argv;
74
75 Funcall funcall;
76
77
78
79
80
81
82
83 CallDispatcher(final JexlNode anode, final boolean acacheable) {
84 this.node = anode;
85 this.cacheable = acacheable;
86 }
87
88
89
90
91
92
93
94
95 protected Object eval(final String methodName) throws Exception {
96
97 if (vm != null) {
98
99 final Object eval = vm.invoke(target, argv);
100
101 if (funcall != null) {
102 node.jjtSetValue(funcall);
103 }
104 return eval;
105 }
106 return unsolvableMethod(node, methodName, argv);
107 }
108
109
110
111
112
113
114
115
116 protected boolean isArithmeticMethod(final String methodName, final Object[] arguments) {
117 vm = uberspect.getMethod(arithmetic, methodName, arguments);
118 if (vm != null) {
119 argv = arguments;
120 target = arithmetic;
121 if (cacheable && vm.isCacheable()) {
122 funcall = new ArithmeticFuncall(vm, narrow);
123 }
124 return true;
125 }
126 return false;
127 }
128
129
130
131
132
133
134
135
136 protected boolean isContextMethod(final String methodName, final Object[] arguments) {
137 vm = uberspect.getMethod(context, methodName, arguments);
138 if (vm != null) {
139 argv = arguments;
140 target = context;
141 if (cacheable && vm.isCacheable()) {
142 funcall = new ContextFuncall(vm, narrow);
143 }
144 return true;
145 }
146 return false;
147 }
148
149
150
151
152
153
154
155
156
157 protected boolean isTargetMethod(final Object ntarget, final String methodName, final Object[] arguments) {
158
159 vm = uberspect.getMethod(ntarget, methodName, arguments);
160 if (vm != null) {
161 argv = arguments;
162 target = ntarget;
163 if (cacheable && vm.isCacheable()) {
164 funcall = new Funcall(vm, narrow);
165 }
166 return true;
167 }
168 return false;
169 }
170
171
172
173
174
175
176
177
178
179
180
181 protected Object tryEval(final Object ntarget, final String methodName, final Object[] arguments) {
182
183
184 if (methodName != null && cacheable && ntarget != null) {
185 final Object cached = node.jjtGetValue();
186 if (cached instanceof Funcall) {
187 return ((Funcall) cached).tryInvoke(InterpreterBase.this, methodName, ntarget, arguments);
188 }
189 }
190 return JexlEngine.TRY_FAILED;
191 }
192 }
193
194
195
196
197 protected static class ArithmeticFuncall extends Funcall {
198
199
200
201
202
203 protected ArithmeticFuncall(final JexlMethod jme, final boolean flag) {
204 super(jme, flag);
205 }
206
207 @Override
208 protected Object tryInvoke(final InterpreterBase ii, final String name, final Object target, final Object[] args) {
209 return me.tryInvoke(name, ii.arithmetic, ii.functionArguments(target, narrow, args));
210 }
211 }
212
213
214
215
216 protected static class ContextFuncall extends Funcall {
217
218
219
220
221
222 protected ContextFuncall(final JexlMethod jme, final boolean flag) {
223 super(jme, flag);
224 }
225
226 @Override
227 protected Object tryInvoke(final InterpreterBase ii, final String name, final Object target, final Object[] args) {
228 return me.tryInvoke(name, ii.context, ii.functionArguments(target, narrow, args));
229 }
230 }
231
232
233
234 protected static class ContextualCtor extends Funcall {
235
236
237
238
239
240 protected ContextualCtor(final JexlMethod jme, final boolean flag) {
241 super(jme, flag);
242 }
243
244 @Override
245 protected Object tryInvoke(final InterpreterBase ii, final String name, final Object target, final Object[] args) {
246 return me.tryInvoke(name, target, ii.callArguments(ii.context, narrow, args));
247 }
248 }
249
250
251
252 protected static class Funcall implements JexlNode.Funcall {
253
254 protected final boolean narrow;
255
256 protected final JexlMethod me;
257
258
259
260
261
262 protected Funcall(final JexlMethod jme, final boolean flag) {
263 this.me = jme;
264 this.narrow = flag;
265 }
266
267
268
269
270
271
272
273
274
275 protected Object tryInvoke(final InterpreterBase ii, final String name, final Object target, final Object[] args) {
276 return me.tryInvoke(name, target, ii.functionArguments(null, narrow, args));
277 }
278 }
279
280
281 protected static final Object[] EMPTY_PARAMS = {};
282
283
284
285
286
287
288
289 protected static String stringifyPropertyValue(final JexlNode node) {
290 return node != null ? new Debugger().depth(1).data(node) : "???";
291 }
292
293
294 protected final Engine jexl;
295
296 protected final Log logger;
297
298 protected final JexlUberspect uberspect;
299
300 protected final JexlArithmetic arithmetic;
301
302 protected final JexlContext context;
303
304 protected final JexlOptions options;
305
306 protected final boolean cache;
307
308 protected final AtomicBoolean cancelled;
309
310 protected final JexlContext.NamespaceResolver ns;
311
312 protected final JexlContext.ClassNameResolver fqcnSolver;
313
314 protected final JexlOperator.Uberspect operators;
315
316 protected final Map<String, Object> functions;
317
318 protected Map<String, Object> functors;
319
320
321
322
323
324
325
326 protected InterpreterBase(final Engine engine, final JexlOptions opts, final JexlContext aContext) {
327 this.jexl = engine;
328 this.logger = jexl.logger;
329 this.uberspect = jexl.uberspect;
330 this.context = aContext != null ? aContext : JexlEngine.EMPTY_CONTEXT;
331 this.cache = engine.cache != null;
332 final JexlArithmetic jexla = jexl.arithmetic;
333 this.options = opts == null ? engine.evalOptions(aContext) : opts;
334 this.arithmetic = jexla.options(options);
335 if (arithmetic != jexla && !arithmetic.getClass().equals(jexla.getClass()) && logger.isWarnEnabled()) {
336 logger.warn("expected arithmetic to be " + jexla.getClass().getSimpleName()
337 + ", got " + arithmetic.getClass().getSimpleName()
338 );
339 }
340 if (this.context instanceof JexlContext.NamespaceResolver) {
341 ns = (JexlContext.NamespaceResolver) context;
342 } else {
343 ns = JexlEngine.EMPTY_NS;
344 }
345 AtomicBoolean acancel = null;
346 if (this.context instanceof JexlContext.CancellationHandle) {
347 acancel = ((JexlContext.CancellationHandle) context).getCancellation();
348 }
349 this.cancelled = acancel != null ? acancel : new AtomicBoolean();
350 this.functions = options.getNamespaces();
351 this.functors = null;
352 JexlOperator.Uberspect ops = uberspect.getOperator(arithmetic);
353 if (ops == null) {
354 ops = new Operator(uberspect, arithmetic);
355 }
356 this.operators = ops;
357
358 final Collection<String> imports = options.getImports();
359 this.fqcnSolver = imports.isEmpty()
360 ? engine.classNameSolver
361 : new FqcnResolver(engine.classNameSolver).importPackages(imports);
362 }
363
364
365
366
367
368
369 protected InterpreterBase(final InterpreterBase ii, final JexlArithmetic jexla) {
370 jexl = ii.jexl;
371 logger = ii.logger;
372 uberspect = ii.uberspect;
373 arithmetic = jexla;
374 context = ii.context;
375 options = ii.options.copy();
376 cache = ii.cache;
377 ns = ii.ns;
378 operators = ii.operators;
379 cancelled = ii.cancelled;
380 functions = ii.functions;
381 functors = ii.functors;
382 fqcnSolver = ii.fqcnSolver;
383 }
384
385
386
387
388
389
390
391
392 protected Object annotationError(final JexlNode node, final String annotation, final Throwable cause) {
393 if (isStrictEngine()) {
394 throw new JexlException.Annotation(node, annotation, cause);
395 }
396 if (logger.isDebugEnabled()) {
397 logger.debug(JexlException.annotationError(node, annotation), cause);
398 }
399 return null;
400 }
401
402
403
404
405
406
407
408
409 protected Object[] callArguments(final Object target, final boolean narrow, final Object[] args) {
410
411 final Object[] nargv = new Object[args.length + 1];
412 if (narrow) {
413 nargv[0] = functionArgument(true, target);
414 for (int a = 1; a <= args.length; ++a) {
415 nargv[a] = functionArgument(true, args[a - 1]);
416 }
417 } else {
418 nargv[0] = target;
419 System.arraycopy(args, 0, nargv, 1, args.length);
420 }
421 return nargv;
422 }
423
424
425
426
427
428 protected boolean cancel() {
429 return cancelled.compareAndSet(false, true);
430 }
431
432
433
434
435
436 protected void cancelCheck(final JexlNode node) {
437 if (isCancelled()) {
438 throw new JexlException.Cancel(node);
439 }
440 }
441
442
443
444
445
446
447 protected void closeIfSupported(final Object closeable) {
448 if (closeable != null) {
449 final JexlMethod mclose = uberspect.getMethod(closeable, "close", EMPTY_PARAMS);
450 if (mclose != null) {
451 try {
452 mclose.invoke(closeable, EMPTY_PARAMS);
453 } catch (final Exception xignore) {
454 logger.warn(xignore);
455 }
456 }
457 }
458 }
459
460
461
462
463
464
465 protected void closeIfSupported(final Queue<Object> closeables) {
466 for(final Object closeable : closeables) {
467 closeIfSupported(closeable);
468 }
469 }
470
471
472
473
474
475
476
477 protected Object constVariable(final JexlNode node, final String variable) {
478 return variableError(node, variable, VariableIssue.CONST);
479 }
480
481
482
483
484
485
486
487 protected boolean defineVariable(final ASTVar variable, final LexicalFrame frame) {
488 final int symbol = variable.getSymbol();
489 if (symbol < 0) {
490 return false;
491 }
492 if (variable.isRedefined()) {
493 return false;
494 }
495 return frame.defineSymbol(symbol, variable.isCaptured());
496 }
497
498
499
500
501
502
503
504
505 protected JexlNode findNullOperand(final JexlNode node, final Object left, final Object right) {
506 if (left == null) {
507 return node.jjtGetChild(0);
508 }
509 if (right == null) {
510 return node.jjtGetChild(1);
511 }
512 return node;
513 }
514
515
516
517
518 @Deprecated
519 protected JexlNode findNullOperand(final RuntimeException xrt, final JexlNode node, final Object left, final Object right) {
520 return findNullOperand(node, left, right);
521 }
522
523
524
525
526
527
528
529 protected Object functionArgument(final boolean narrow, final Object arg) {
530 return narrow && arg instanceof Number ? arithmetic.narrow((Number) arg) : arg;
531 }
532
533
534
535
536
537
538
539
540
541 protected Object[] functionArguments(final Object target, final boolean narrow, final Object[] args) {
542
543 if (target == null || target == context) {
544 if (narrow) {
545 arithmetic.narrowArguments(args);
546 }
547 return args;
548 }
549
550 final Object[] nargv = new Object[args.length + 1];
551 if (narrow) {
552 nargv[0] = functionArgument(true, target);
553 for (int a = 1; a <= args.length; ++a) {
554 nargv[a] = functionArgument(true, args[a - 1]);
555 }
556 } else {
557 nargv[0] = target;
558 System.arraycopy(args, 0, nargv, 1, args.length);
559 }
560 return nargv;
561 }
562
563
564
565
566
567
568
569
570
571 protected Object getAttribute(final Object object, final Object attribute, final JexlNode node) {
572 if (object == null) {
573 throw new JexlException(node, "object is null");
574 }
575 cancelCheck(node);
576 final JexlOperator operator = node != null && node.jjtGetParent() instanceof ASTArrayAccess
577 ? JexlOperator.ARRAY_GET : JexlOperator.PROPERTY_GET;
578 final Object result = operators.tryOverload(node, operator, object, attribute);
579 if (result != JexlEngine.TRY_FAILED) {
580 return result;
581 }
582 Exception xcause = null;
583 try {
584
585 if (node != null && cache) {
586 final Object cached = node.jjtGetValue();
587 if (cached instanceof JexlPropertyGet) {
588 final JexlPropertyGet vg = (JexlPropertyGet) cached;
589 final Object value = vg.tryInvoke(object, attribute);
590 if (!vg.tryFailed(value)) {
591 return value;
592 }
593 }
594 }
595
596 final List<JexlUberspect.PropertyResolver> resolvers = uberspect.getResolvers(operator, object);
597 final JexlPropertyGet vg = uberspect.getPropertyGet(resolvers, object, attribute);
598 if (vg != null) {
599 final Object value = vg.invoke(object);
600
601 if (node != null && cache && vg.isCacheable()) {
602 node.jjtSetValue(vg);
603 }
604 return value;
605 }
606 } catch (final Exception xany) {
607 xcause = xany;
608 }
609
610 if (node == null) {
611
612 final String error = "unable to get object property"
613 + ", class: " + object.getClass().getName()
614 + ", property: " + attribute;
615 throw new UnsupportedOperationException(error, xcause);
616 }
617 final boolean safe = node instanceof ASTIdentifierAccess && ((ASTIdentifierAccess) node).isSafe();
618 if (safe) {
619 return null;
620 }
621 final String attrStr = Objects.toString(attribute, null);
622 return unsolvableProperty(node, attrStr, true, xcause);
623 }
624
625
626
627
628
629
630
631
632 protected Object getVariable(final Frame frame, final LexicalScope block, final ASTIdentifier identifier) {
633 final int symbol = identifier.getSymbol();
634 final String name = identifier.getName();
635
636 if ((options.isLexicalShade() || identifier.isLexical()) && identifier.isShaded()) {
637 return undefinedVariable(identifier, name);
638 }
639
640 if (symbol >= 0 && frame.has(symbol)) {
641 final Object value = frame.get(symbol);
642
643 if (value != Scope.UNDEFINED) {
644
645 if (value == null && isStrictOperand(identifier)) {
646 return unsolvableVariable(identifier, name, false);
647 }
648 return value;
649 }
650 }
651
652 final Object value = context.get(name);
653
654 if (value == null) {
655
656 if (!context.has(name)) {
657
658 final boolean ignore = identifier.jjtGetParent() instanceof ASTReference
659 || isSafe() && (symbol >= 0 || identifier.jjtGetParent() instanceof ASTAssignment);
660 if (!ignore) {
661 return undefinedVariable(identifier, name);
662 }
663 } else if (isStrictOperand(identifier)) {
664 return unsolvableVariable(identifier, name, false);
665 }
666 }
667 return value;
668 }
669
670
671
672
673
674
675
676 protected JexlException invocationException(final JexlNode node, final String methodName, final Throwable xany) {
677 final Throwable cause = xany.getCause();
678 if (cause instanceof JexlException) {
679 return (JexlException) cause;
680 }
681 if (cause instanceof InterruptedException) {
682 return new JexlException.Cancel(node);
683 }
684 return new JexlException(node, methodName, xany);
685 }
686
687
688
689
690 protected boolean isCancellable() {
691 return options.isCancellable();
692 }
693
694
695
696
697
698 protected boolean isCancelled() {
699 return cancelled.get() || Thread.currentThread().isInterrupted();
700 }
701
702
703
704
705
706 protected boolean isSafe() {
707 return options.isSafe();
708 }
709
710
711
712
713
714 protected boolean isSilent() {
715 return options.isSilent();
716 }
717
718
719
720
721
722 protected boolean isStrictEngine() {
723 return options.isStrict();
724 }
725
726
727
728
729
730 protected boolean isStrictOperand(final JexlNode node) {
731 return node.jjtGetParent().isStrictOperator(arithmetic);
732 }
733
734
735
736
737
738
739
740
741
742
743 protected boolean isTernaryProtected(final JexlNode startNode) {
744 JexlNode node = startNode;
745 for (JexlNode walk = node.jjtGetParent(); walk != null; walk = walk.jjtGetParent()) {
746
747 if (walk instanceof ASTTernaryNode
748 || walk instanceof ASTNullpNode) {
749 return node == walk.jjtGetChild(0);
750 }
751 if (!(walk instanceof ASTReference || walk instanceof ASTArrayAccess)) {
752 break;
753 }
754 node = walk;
755 }
756 return false;
757 }
758
759
760
761
762
763
764
765
766
767
768 protected boolean isVariableDefined(final Frame frame, final LexicalScope block, final String name) {
769 if (frame != null && block != null) {
770 final Integer ref = frame.getScope().getSymbol(name);
771 final int symbol = ref != null ? ref : -1;
772 if (symbol >= 0 && block.hasSymbol(symbol)) {
773 final Object value = frame.get(symbol);
774 return value != Scope.UNDEFINED && value != Scope.UNDECLARED;
775 }
776 }
777 return context.has(name);
778 }
779
780
781
782
783
784
785
786
787 protected Object operatorError(final JexlNode node, final JexlOperator operator, final Throwable cause) {
788 if (isStrictEngine()) {
789 throw new JexlException.Operator(node, operator.getOperatorSymbol(), cause);
790 }
791 if (logger.isDebugEnabled()) {
792 logger.debug(JexlException.operatorError(node, operator.getOperatorSymbol()), cause);
793 }
794 return null;
795 }
796
797
798
799
800
801
802
803 protected Object redefinedVariable(final JexlNode node, final String variable) {
804 return variableError(node, variable, VariableIssue.REDEFINED);
805 }
806
807
808
809
810
811
812
813
814
815 protected Object resolveNamespace(final String prefix, final JexlNode node) {
816 Object namespace;
817
818 synchronized (this) {
819 if (functors != null) {
820 namespace = functors.get(prefix);
821 if (namespace != null) {
822 return namespace;
823 }
824 }
825 }
826
827 namespace = ns.resolveNamespace(prefix);
828 if (namespace == null) {
829 namespace = functions.get(prefix);
830 if (namespace == null) {
831 namespace = jexl.getNamespace(prefix);
832 }
833 if (prefix != null && namespace == null) {
834 throw new JexlException(node, "no such function namespace " + prefix, null);
835 }
836 }
837 Object functor = null;
838
839 if (namespace instanceof Class<?> || namespace instanceof String) {
840
841 final ASTIdentifier nsNode = (ASTIdentifier) node.jjtGetChild(0);
842 final boolean cacheable = cache && prefix != null;
843 final Object cached = cacheable ? nsNode.jjtGetValue() : null;
844
845 if (cached instanceof Class<?>) {
846 return cached;
847 }
848
849 if (cached instanceof JexlContext.NamespaceFunctor) {
850 final Object eval = ((JexlContext.NamespaceFunctor) cached).createFunctor(context);
851 if (JexlEngine.TRY_FAILED != eval) {
852 functor = eval;
853 namespace = cached;
854 }
855 }
856 if (functor == null) {
857
858 for (int tried = 0; tried < 2; ++tried) {
859 final boolean withContext = tried == 0;
860 final JexlMethod ctor = withContext
861 ? uberspect.getConstructor(namespace, context)
862 : uberspect.getConstructor(namespace);
863 if (ctor != null) {
864 try {
865 functor = withContext
866 ? ctor.invoke(namespace, context)
867 : ctor.invoke(namespace);
868
869 if (functor != null) {
870
871
872 final Object nsFinal = namespace;
873
874 namespace = (NamespaceFunctor) context -> withContext
875 ? ctor.tryInvoke(null, nsFinal, context)
876 : ctor.tryInvoke(null, nsFinal);
877 if (cacheable && ctor.isCacheable()) {
878 nsNode.jjtSetValue(namespace);
879 }
880 break;
881 }
882 } catch (final Exception xinst) {
883 throw new JexlException(node, "unable to instantiate namespace " + prefix, xinst);
884 }
885 }
886 }
887
888 if (functor == null) {
889 try {
890
891 if (namespace instanceof String) {
892 namespace = uberspect.getClassLoader().loadClass((String) namespace);
893 }
894
895 if (cacheable) {
896 nsNode.jjtSetValue(namespace);
897 }
898 } catch (final ClassNotFoundException e) {
899
900 throw new JexlException(node, "no such class namespace " + prefix, e);
901 }
902 }
903 }
904 }
905
906 if (functor == null && namespace instanceof JexlContext.NamespaceFunctor) {
907 functor = ((JexlContext.NamespaceFunctor) namespace).createFunctor(context);
908 }
909
910 if (functor != null) {
911 synchronized (this) {
912 if (functors == null) {
913 functors = new HashMap<>();
914 }
915 functors.put(prefix, functor);
916 }
917 return functor;
918 }
919 return namespace;
920 }
921
922
923
924
925
926
927
928
929
930 protected void setAttribute(final Object object, final Object attribute, final Object value, final JexlNode node) {
931 cancelCheck(node);
932 final JexlOperator operator = node != null && node.jjtGetParent() instanceof ASTArrayAccess
933 ? JexlOperator.ARRAY_SET : JexlOperator.PROPERTY_SET;
934 final Object result = operators.tryOverload(node, operator, object, attribute, value);
935 if (result != JexlEngine.TRY_FAILED) {
936 return;
937 }
938 Exception xcause = null;
939 try {
940
941 if (node != null && cache) {
942 final Object cached = node.jjtGetValue();
943 if (cached instanceof JexlPropertySet) {
944 final JexlPropertySet setter = (JexlPropertySet) cached;
945 final Object eval = setter.tryInvoke(object, attribute, value);
946 if (!setter.tryFailed(eval)) {
947 return;
948 }
949 }
950 }
951 final List<JexlUberspect.PropertyResolver> resolvers = uberspect.getResolvers(operator, object);
952 JexlPropertySet vs = uberspect.getPropertySet(resolvers, object, attribute, value);
953
954 if (vs == null) {
955
956 final Object[] narrow = {value};
957 if (arithmetic.narrowArguments(narrow)) {
958 vs = uberspect.getPropertySet(resolvers, object, attribute, narrow[0]);
959 }
960 }
961 if (vs != null) {
962
963 vs.invoke(object, value);
964 if (node != null && cache && vs.isCacheable()) {
965 node.jjtSetValue(vs);
966 }
967 return;
968 }
969 } catch (final Exception xany) {
970 xcause = xany;
971 }
972
973 if (node == null) {
974
975 final String error = "unable to set object property"
976 + ", class: " + object.getClass().getName()
977 + ", property: " + attribute
978 + ", argument: " + value.getClass().getSimpleName();
979 throw new UnsupportedOperationException(error, xcause);
980 }
981 final String attrStr = Objects.toString(attribute, null);
982 unsolvableProperty(node, attrStr, true, xcause);
983 }
984
985
986
987
988
989
990
991
992
993 protected void setContextVariable(final JexlNode node, final String name, final Object value) {
994 boolean lexical = options.isLexicalShade();
995 if (!lexical && node instanceof ASTIdentifier) {
996 lexical = ((ASTIdentifier) node).isLexical();
997 }
998 if (lexical && !context.has(name)) {
999 throw new JexlException.Variable(node, name, true);
1000 }
1001 try {
1002 context.set(name, value);
1003 } catch (final UnsupportedOperationException xsupport) {
1004 throw new JexlException(node, "context is readonly", xsupport);
1005 }
1006 }
1007
1008
1009
1010
1011
1012
1013
1014 protected String stringifyProperty(final JexlNode node) {
1015 if (node instanceof ASTArrayAccess) {
1016 return "[" + stringifyPropertyValue(node.jjtGetChild(0)) + "]";
1017 }
1018 if (node instanceof ASTMethodNode) {
1019 return stringifyPropertyValue(node.jjtGetChild(0));
1020 }
1021 if (node instanceof ASTFunctionNode) {
1022 return stringifyPropertyValue(node.jjtGetChild(0));
1023 }
1024 if (node instanceof ASTIdentifier) {
1025 return ((ASTIdentifier) node).getName();
1026 }
1027 if (node instanceof ASTReference) {
1028 return stringifyProperty(node.jjtGetChild(0));
1029 }
1030 return stringifyPropertyValue(node);
1031 }
1032
1033
1034
1035
1036
1037
1038
1039 protected Object undefinedVariable(final JexlNode node, final String variable) {
1040 return variableError(node, variable, VariableIssue.UNDEFINED);
1041 }
1042
1043
1044
1045
1046
1047
1048
1049 protected Object unsolvableMethod(final JexlNode node, final String method) {
1050 return unsolvableMethod(node, method, null);
1051 }
1052
1053
1054
1055
1056
1057
1058
1059
1060 protected Object unsolvableMethod(final JexlNode node, final String method, final Object[] args) {
1061 if (isStrictEngine()) {
1062 throw new JexlException.Method(node, method, args);
1063 }
1064 if (logger.isDebugEnabled()) {
1065 logger.debug(JexlException.methodError(node, method, args));
1066 }
1067 return null;
1068 }
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078 protected Object unsolvableProperty(final JexlNode node, final String property, final boolean undef, final Throwable cause) {
1079 if (isStrictEngine() && !isTernaryProtected(node)) {
1080 throw new JexlException.Property(node, property, undef, cause);
1081 }
1082 if (logger.isDebugEnabled()) {
1083 logger.debug(JexlException.propertyError(node, property, undef));
1084 }
1085 return null;
1086 }
1087
1088
1089
1090
1091
1092
1093
1094
1095 protected Object unsolvableVariable(final JexlNode node, final String variable, final boolean undef) {
1096 return variableError(node, variable, undef? VariableIssue.UNDEFINED : VariableIssue.NULLVALUE);
1097 }
1098
1099
1100
1101
1102
1103
1104
1105
1106 protected Object variableError(final JexlNode node, final String variable, final VariableIssue issue) {
1107 if (isStrictEngine() && !isTernaryProtected(node)) {
1108 throw new JexlException.Variable(node, variable, issue);
1109 }
1110 if (logger.isDebugEnabled()) {
1111 logger.debug(JexlException.variableError(node, variable, issue));
1112 }
1113 return null;
1114 }
1115 }