1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.jexl3;
18
19 import static org.junit.jupiter.api.Assertions.assertEquals;
20 import static org.junit.jupiter.api.Assertions.assertNotNull;
21 import static org.junit.jupiter.api.Assertions.assertNull;
22 import static org.junit.jupiter.api.Assertions.assertThrows;
23 import static org.junit.jupiter.api.Assertions.assertTrue;
24
25 import java.math.BigDecimal;
26 import java.math.BigInteger;
27 import java.time.Duration;
28 import java.util.Collections;
29 import java.util.Map;
30 import java.util.Set;
31 import java.util.TreeMap;
32 import java.util.concurrent.ConcurrentHashMap;
33 import java.util.concurrent.ConcurrentMap;
34 import java.util.concurrent.atomic.AtomicInteger;
35
36 import org.apache.commons.lang3.ThreadUtils;
37 import org.junit.jupiter.api.Test;
38
39
40
41
42 public class PragmaTest extends JexlTestCase {
43 public static class CachingModuleContext extends ModuleContext implements JexlContext.ModuleProcessor {
44 private final ConcurrentMap<String, Object> modules = new ConcurrentHashMap<>();
45 private final AtomicInteger count = new AtomicInteger();
46
47 CachingModuleContext() {
48 }
49
50 public int getCountCompute() {
51 return count.get();
52 }
53
54 @Override
55 public Object processModule(final JexlEngine engine, final JexlInfo info, final String name, final String body) {
56 if (body.isEmpty()) {
57 modules.remove(name);
58 return null;
59 }
60 return modules.computeIfAbsent(name, n -> {
61 Object module = engine.createExpression(info, body).evaluate(this);
62 if (module instanceof JexlScript) {
63 module = ((JexlScript) module).execute(this);
64 }
65 count.incrementAndGet();
66 return module;
67 });
68 }
69 }
70
71 public static class ModuleContext extends MapContext {
72 protected final Map<String, JexlScript> sources = new TreeMap<>();
73
74 ModuleContext() { }
75 public Object script(final String name) {
76 return sources.get(name);
77 }
78
79 void script(final String name, final JexlScript script) { sources.put(name, script); }
80 }
81
82 public static class SafeContext extends JexlEvalContext {
83
84 public void processPragmas(final Map<String, Object> pragmas) {
85 if (pragmas != null && !pragmas.isEmpty()) {
86 final JexlOptions options = getEngineOptions();
87 for (final Map.Entry<String, Object> pragma : pragmas.entrySet()) {
88 final String key = pragma.getKey();
89 final Object value = pragma.getValue();
90 if ("jexl.safe".equals(key) && value instanceof Boolean) {
91 options.setSafe((Boolean) value);
92 } else if ("jexl.strict".equals(key) && value instanceof Boolean) {
93 options.setStrict((Boolean) value);
94 } else if ("jexl.silent".equals(key) && value instanceof Boolean) {
95 options.setSilent((Boolean) value);
96 }
97 }
98 }
99 }
100
101
102
103
104
105 public void sleep(final long ms) {
106 ThreadUtils.sleepQuietly(Duration.ofMillis(ms));
107 }
108 }
109
110 public static class Sleeper {
111 public void sleep(final long ms) {
112 ThreadUtils.sleepQuietly(Duration.ofMillis(ms));
113 }
114 }
115
116 public static class StaticSleeper {
117 public static void sleep(final long ms) {
118 ThreadUtils.sleepQuietly(Duration.ofMillis(ms));
119 }
120
121
122 private StaticSleeper() {}
123 }
124
125
126
127
128 public PragmaTest() {
129 super("PragmaTest");
130 }
131
132 void runPragmaModule(final ModuleContext ctxt, final CachingModuleContext cmCtxt) {
133 ctxt.script("module0", JEXL.createScript("function f42(x) { 42 + x; } function f43(x) { 43 + x; }; { 'f42' : f42, 'f43' : f43 }"));
134 final ConcurrentMap<String, Object> modules = new ConcurrentHashMap<>();
135 JexlScript script;
136 Object result;
137 script = JEXL.createScript("#pragma jexl.module.m0 \"script('module0')\"\n m0:f42(10);");
138 result = script.execute(ctxt);
139 assertEquals(52, result);
140 if (cmCtxt != null) {
141 assertEquals(1, cmCtxt.getCountCompute());
142 }
143 result = script.execute(ctxt);
144 assertEquals(52, result);
145 if (cmCtxt != null) {
146 assertEquals(1, cmCtxt.getCountCompute());
147 }
148 script = JEXL.createScript("#pragma jexl.module.m0 \"script('module0')\"\n m0:f43(10);");
149 result = script.execute(ctxt);
150 assertEquals(53, result);
151 if (cmCtxt != null) {
152 assertEquals(1, cmCtxt.getCountCompute());
153 }
154 final JexlScript script1 = JEXL.createScript("#pragma jexl.module.m0 ''\n#pragma jexl.module.m0 \"fubar('module0')\"\n m0:f43(10);");
155 final JexlException.Method xmethod = assertThrows(JexlException.Method.class, () -> script1.execute(ctxt));
156 assertEquals("fubar", xmethod.getMethod());
157 }
158
159 @Test public void test354() {
160 final Map<String, Number> values = new TreeMap<>();
161 values.put("1", 1);
162 values.put("+1", 1);
163 values.put("-1", -1);
164 values.put("1l", 1L);
165 values.put("+1l", 1L);
166 values.put("-1l", -1L);
167 values.put("10h", BigInteger.valueOf(10));
168 values.put("-11h", BigInteger.valueOf(-11));
169 values.put("+12h", BigInteger.valueOf(12));
170 values.put("0xa", 0xa);
171 values.put("+0xa", 0xa);
172 values.put("-0xa", -0xa);
173 values.put("0xacl", 0xacL);
174 values.put("+0xadl", 0xadL);
175 values.put("-0xafl", -0xafL);
176 values.put("1d", 1d);
177 values.put("-1d", -1d);
178 values.put("+1d", 1d);
179 values.put("1f", 1f);
180 values.put("-1f", -1f);
181 values.put("+1f", 1f);
182 values.put("1B", new BigDecimal(1));
183 values.put("-1B", new BigDecimal(-1));
184 values.put("+1B", new BigDecimal(1));
185 values.put("-42424242424242424242424242424242", new BigInteger("-42424242424242424242424242424242"));
186 values.put("+42424242424242424242424242424242", new BigInteger("+42424242424242424242424242424242"));
187 values.put("42424242424242424242424242424242", new BigInteger("42424242424242424242424242424242"));
188 final JexlEngine jexl = new JexlBuilder().safe(true).create();
189 for(final Map.Entry<String, Number> e : values.entrySet()) {
190 final String text = "#pragma number " + e.getKey();
191 final JexlScript script = jexl.createScript(text);
192 assertNotNull(script);
193 final Map<String, Object> pragmas = script.getPragmas();
194 assertNotNull(pragmas);
195 assertEquals(e.getValue(), pragmas.get("number"), e::getKey);
196 }
197 }
198 @Test
199 public void testImportPragmaDisabled() {
200 final String src =
201 "#pragma jexl.import java.util\n"+
202 "#pragma jexl.import java.io\n"+
203 "#pragma jexl.import java.net\n"+
204 "42";
205 final JexlFeatures features = new JexlFeatures();
206 features.importPragma(false);
207 final JexlEngine jexl = new JexlBuilder().features(features).create();
208 final JexlException.Parsing xparse = assertThrows(JexlException.Parsing.class, () -> jexl.createScript(src));
209 assertTrue(xparse.getMessage().contains("import pragma"));
210 }
211
212 @Test
213 public void testImportPragmaValueSet() {
214 final String src =
215 "#pragma jexl.import java.util\n"+
216 "#pragma jexl.import java.io\n"+
217 "#pragma jexl.import java.net\n"+
218 "42";
219 final JexlScript script = JEXL.createScript(src);
220 final Map<String, Object> pragmas = script.getPragmas();
221 final Object importz = pragmas.get("jexl.import");
222 assertTrue(importz instanceof Set<?>);
223 final Set<String> importzz = (Set<String>) importz;
224 assertTrue(importzz.contains("java.util"));
225 assertTrue(importzz.contains("java.io"));
226 assertTrue(importzz.contains("java.net"));
227 assertEquals(3, importzz.size());
228 final String parsed = script.getParsedText();
229 assertEquals(src, parsed);
230 }
231
232 @Test
233 public void testIssue416() {
234 final JexlEngine jexl = new JexlBuilder().create();
235 final JexlScript script = jexl.createScript("#pragma myNull null\n");
236 final Map<String, Object> pragmas = script.getPragmas();
237 assertTrue(pragmas.containsKey("myNull"), "pragma key present?");
238 assertNull(pragmas.get("myNull"), "expected null value");
239 }
240
241 @Test
242 @SuppressWarnings("AssertEqualsBetweenInconvertibleTypes")
243 public void testJxltPragmas() {
244 final JxltEngine engine = new JexlBuilder().create().createJxltEngine();
245 final JxltEngine.Template tscript = engine.createTemplate("$$ #pragma one 1\n$$ #pragma the.very.hard 'truth'\n2;");
246 assertNotNull(tscript);
247 final Map<String, Object> pragmas = tscript.getPragmas();
248 assertEquals(2, pragmas.size());
249 assertEquals(1, pragmas.get("one"));
250 assertEquals("truth", pragmas.get("the.very.hard"));
251 }
252
253 @Test
254 @SuppressWarnings("AssertEqualsBetweenInconvertibleTypes")
255 public void testNamespacePragma() {
256 final JexlContext jc = new SafeContext();
257 final String src =
258 "#pragma jexl.namespace.sleeper " + Sleeper.class.getName() + "\n"
259 + "sleeper:sleep(100);\n"
260 + "42;\n";
261 final JexlScript script = JEXL.createScript(src);
262 final Object result = script.execute(jc);
263 assertEquals(42, result);
264 final String parsed = script.getParsedText();
265 assertEquals(src, parsed);
266 }
267 @Test
268 @SuppressWarnings("AssertEqualsBetweenInconvertibleTypes")
269 public void testNamespacePragmaCtl() {
270 final Map<String, Object> ns = Collections.singletonMap("sleeper", Sleeper.class.getName());
271 final JexlEngine jexl = new JexlBuilder().namespaces(ns).create();
272 final JexlContext jc = new SafeContext();
273 final JexlScript script = jexl.createScript(
274 "sleeper:sleep(100);"
275 + "42");
276 final Object result = script.execute(jc);
277 assertEquals(42, result);
278 }
279 @Test
280 public void testNamespacePragmaDisabled() {
281 final JexlFeatures features = new JexlFeatures();
282 features.namespacePragma(false);
283 final JexlEngine jexl = new JexlBuilder().features(features).create();
284
285 final JexlException.Parsing xparse = assertThrows(JexlException.Parsing.class, () -> jexl.createScript(
286 "#pragma jexl.namespace.sleeper " + StaticSleeper.class.getName() + "\n"
287 + "sleeper:sleep(100);"
288 + "42"));
289
290 assertTrue(xparse.getMessage().contains("namespace pragma"));
291 }
292
293 @Test public void testPragmaModuleCache() {
294 final CachingModuleContext ctxt = new CachingModuleContext();
295 runPragmaModule(ctxt, ctxt);
296 }
297
298 @Test public void testPragmaModuleNoCache() {
299 final ModuleContext ctxt = new ModuleContext();
300 runPragmaModule(ctxt, null);
301 }
302
303 @Test
304 public void testPragmaOptions1() {
305 final String str = "i; #pragma jexl.options '-strict'\n";
306
307 final JexlEngine jexl = new JexlBuilder()
308 .features(new JexlFeatures().pragmaAnywhere(false))
309 .strict(true).create();
310
311 assertThrows(JexlException.class, () -> jexl.createScript(str), "i should not be resolved");
312 }
313
314
315
316
317 @Test
318 @SuppressWarnings("AssertEqualsBetweenInconvertibleTypes")
319 public void testPragmas() {
320 final JexlScript script = JEXL.createScript("#pragma one 1\n#pragma the.very.hard 'truth'\n2;");
321 assertNotNull(script);
322 final Map<String, Object> pragmas = script.getPragmas();
323 assertEquals(2, pragmas.size());
324 assertEquals(1, pragmas.get("one"));
325 assertEquals("truth", pragmas.get("the.very.hard"));
326 }
327
328 @Test
329 @SuppressWarnings("AssertEqualsBetweenInconvertibleTypes")
330 public void testSafePragma() {
331 final SafeContext jc = new SafeContext();
332 jc.set("foo", null);
333 final JexlScript script = JEXL.createScript("#pragma jexl.safe true\nfoo.bar;");
334 assertNotNull(script);
335 jc.processPragmas(script.getPragmas());
336 final Object result = script.execute(jc);
337 assertNull(result);
338 final SafeContext jc1 = new SafeContext();
339 jc1.set("foo", null);
340 assertThrows(JexlException.class, () -> script.execute(jc1));
341 }
342
343 @Test
344 @SuppressWarnings("AssertEqualsBetweenInconvertibleTypes")
345 public void testStaticNamespacePragma() {
346 final JexlContext jc = new SafeContext();
347 final JexlScript script = JEXL.createScript(
348 "#pragma jexl.namespace.sleeper " + StaticSleeper.class.getName() + "\n"
349 + "sleeper:sleep(100);"
350 + "42");
351 final Object result = script.execute(jc);
352 assertEquals(42, result);
353 }
354
355 @Test
356 @SuppressWarnings("AssertEqualsBetweenInconvertibleTypes")
357 public void testStatictNamespacePragmaCtl() {
358 final Map<String, Object> ns = Collections.singletonMap("sleeper", StaticSleeper.class.getName());
359 final JexlEngine jexl = new JexlBuilder().namespaces(ns).create();
360 final JexlContext jc = new SafeContext();
361 final JexlScript script = jexl.createScript(
362 "sleeper:sleep(100);"
363 + "42");
364 final Object result = script.execute(jc);
365 assertEquals(42, result);
366 }
367 }