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