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.assertArrayEquals;
20 import static org.junit.jupiter.api.Assertions.assertEquals;
21 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
22 import static org.junit.jupiter.api.Assertions.assertNotNull;
23 import static org.junit.jupiter.api.Assertions.assertThrows;
24 import static org.junit.jupiter.api.Assertions.assertTrue;
25
26 import java.util.Arrays;
27 import java.util.List;
28 import java.util.Set;
29 import java.util.concurrent.Callable;
30
31 import org.apache.commons.jexl3.internal.Closure;
32 import org.apache.commons.jexl3.internal.Script;
33 import org.junit.jupiter.api.Test;
34 import org.junit.jupiter.params.ParameterizedTest;
35 import org.junit.jupiter.params.provider.CsvSource;
36
37
38
39
40 @SuppressWarnings({"AssertEqualsBetweenInconvertibleTypes"})
41 class LambdaTest extends JexlTestCase {
42
43 public LambdaTest() {
44 super("LambdaTest");
45 }
46
47 @Test void test270() {
48 final JexlEngine jexl = createEngine();
49 final JexlScript base = jexl.createScript("(x, y, z)->{ x + y + z }");
50 final String text = base.toString();
51 JexlScript script = base.curry(5, 15);
52 assertEquals(text, script.toString());
53
54 final JexlContext ctxt = new JexlEvalContext();
55 ctxt.set("s", base);
56 script = jexl.createScript("return s");
57 Object result = script.execute(ctxt);
58 assertEquals(text, result.toString());
59
60 script = jexl.createScript("return s.curry(1)");
61 result = script.execute(ctxt);
62 assertEquals(text, result.toString());
63 }
64
65 @Test void test271a() {
66 final JexlEngine jexl = createEngine();
67 final JexlScript base = jexl.createScript("var base = 1; var x = (a)->{ var y = (b) -> {base + b}; return base + y(a)}; x(40)");
68 final Object result = base.execute(null);
69 assertEquals(42, result);
70 }
71
72 @Test void test271b() {
73 final JexlEngine jexl = createEngine();
74 final JexlScript base = jexl.createScript("var base = 2; var sum = (x, y, z)->{ base + x + y + z }; var y = sum.curry(1); y(2,3)");
75 final Object result = base.execute(null);
76 assertEquals(8, result);
77 }
78
79 @Test void test271c() {
80 final JexlEngine jexl = createEngine();
81 final JexlScript base = jexl.createScript("(x, y, z)->{ 2 + x + y + z };");
82 final JexlScript y = base.curry(1);
83 final Object result = y.execute(null, 2, 3);
84 assertEquals(8, result);
85 }
86
87 @Test void test271d() {
88 final JexlEngine jexl = createEngine();
89 final JexlScript base = jexl.createScript("var base = 2; (x, y, z)->base + x + y + z;");
90 final JexlScript y = ((JexlScript) base.execute(null)).curry(1);
91 final Object result = y.execute(null, 2, 3);
92 assertEquals(8, result);
93 }
94
95
96
97 @Test void test271e() {
98 final JexlEngine jexl = createEngine();
99 final JexlScript base = jexl.createScript("var base = 1000; var f = (x, y)->{ var base = x + y + (base?:-1000); base; }; f(100, 20)");
100 final Object result = base.execute(null);
101 assertEquals(1120, result);
102 }
103
104 @Test void test405a() {
105 final JexlEngine jexl = new JexlBuilder()
106 .cache(4).strict(true).safe(false)
107 .create();
108 final String libSrc = "var theFunction = argFn -> { var fn = argFn; fn() }; { 'theFunction' : theFunction }";
109 final String src1 = "var v0 = 42; var v1 = -42; lib.theFunction(()->{ v1 + v0 }) ";
110 final JexlScript libMap = jexl.createScript(libSrc);
111 final Object theLib = libMap.execute(null);
112 final JexlScript f1 = jexl.createScript(src1, "lib");
113 final Object result = f1.execute(null, theLib);
114 assertEquals(0, result);
115 }
116
117 @Test void test405b() {
118 final JexlEngine jexl = new JexlBuilder()
119 .cache(4).strict(true).safe(false)
120 .create();
121 final String libSrc = "function theFunction(argFn) { var fn = argFn; fn() }; { 'theFunction' : theFunction }";
122 final String src1 = "var v0 = 42; var v1 = -42; lib.theFunction(()->{ v1 + v0 }) ";
123 final JexlScript libMap = jexl.createScript(libSrc);
124 final Object theLib = libMap.execute(null);
125 final JexlScript f1 = jexl.createScript(src1, "lib");
126 final Object result = f1.execute(null, theLib);
127 assertEquals(0, result);
128 }
129
130 @Test void testCompareLambdaRecurse() throws Exception {
131 final JexlEngine jexl = createEngine();
132 final String factSrc = "function fact(x) { x < 2? 1 : x * fact(x - 1) }";
133 final JexlScript fact0 = jexl.createScript(factSrc);
134 final JexlScript fact1 = jexl.createScript(fact0.toString());
135 assertEquals(fact0, fact1);
136 final Closure r0 = (Closure) fact0.execute(null);
137 final Closure r1 = (Closure) fact1.execute(null);
138 assertEquals(720, r0.execute(null, 6));
139 assertEquals(720, r1.execute(null, 6));
140 assertEquals(r0, r1);
141 assertEquals(r1, r0);
142
143 assertEquals(720, r0.execute(null, 6));
144 assertEquals(720, r1.execute(null, 6));
145 }
146
147 @Test void testCurry1() {
148 final JexlEngine jexl = createEngine();
149 JexlScript script;
150 Object result;
151 String[] parms;
152
153 final JexlScript base = jexl.createScript("(x, y, z)->{ x + y + z }");
154 parms = base.getUnboundParameters();
155 assertEquals(3, parms.length);
156 script = base.curry(5);
157 parms = script.getUnboundParameters();
158 assertEquals(2, parms.length);
159 script = script.curry(15);
160 parms = script.getUnboundParameters();
161 assertEquals(1, parms.length);
162 script = script.curry(22);
163 parms = script.getUnboundParameters();
164 assertEquals(0, parms.length);
165 result = script.execute(null);
166 assertEquals(42, result);
167 }
168
169 @Test void testCurry2() {
170 final JexlEngine jexl = createEngine();
171 JexlScript script;
172 Object result;
173 String[] parms;
174
175 final JexlScript base = jexl.createScript("(x, y, z)->{ x + y + z }");
176 script = base.curry(5, 15);
177 parms = script.getUnboundParameters();
178 assertEquals(1, parms.length);
179 script = script.curry(22);
180 result = script.execute(null);
181 assertEquals(42, result);
182 }
183
184 @Test void testCurry3() {
185 final JexlEngine jexl = createEngine();
186 JexlScript script;
187 Object result;
188
189 final JexlScript base = jexl.createScript("(x, y, z)->{ x + y + z }");
190 script = base.curry(5, 15);
191 result = script.execute(null, 22);
192 assertEquals(42, result);
193 }
194
195 @Test void testCurry4() {
196 final JexlEngine jexl = createEngine();
197 JexlScript script;
198 Object result;
199
200 final JexlScript base = jexl.createScript("(x, y, z)->{ x + y + z }");
201 script = base.curry(5);
202 result = script.execute(null, 15, 22);
203 assertEquals(42, result);
204 }
205
206 @Test void testCurry5() {
207 final JexlEngine jexl = createEngine();
208 JexlScript script;
209 Object result;
210
211 final JexlScript base = jexl.createScript("var t = x + y + z; return t", "x", "y", "z");
212 script = base.curry(5);
213 result = script.execute(null, 15, 22);
214 assertEquals(42, result);
215 }
216
217 @ParameterizedTest
218 @CsvSource({
219 "'if (false) function foo(x) { x + x }; var foo = 1', 'function'",
220 "'if (false) let foo = (x) -> { x + x }; var foo = 1', 'let'",
221 "'function foo(x) { x + x }; var foo = 42', 'foo'"
222 })
223 void testFailParseFunc(final String src, final String expectedKeyword) {
224 final JexlEngine jexl = createEngine();
225 final JexlException.Parsing xparse = assertThrows(JexlException.Parsing.class, () -> jexl.createScript(src));
226 assertTrue(xparse.getMessage().contains(expectedKeyword));
227 }
228
229 @Test void testFatFact0() {
230 final JexlFeatures features = new JexlFeatures();
231 features.fatArrow(true);
232 final String src = "function (a) { const fact = x =>{ x <= 1? 1 : x * fact(x - 1) }; fact(a) }";
233 final JexlEngine jexl = createEngine(features);
234 final JexlScript script = jexl.createScript(src);
235 final Object result = script.execute(null, 6);
236 assertEquals(720, result);
237 }
238
239 @Test void testFatFact1() {
240 final String src = "function (a) { const fact = (x)=> x <= 1? 1 : x * fact(x - 1) ; fact(a) }";
241 final JexlFeatures features = new JexlFeatures();
242 features.fatArrow(true);
243 final JexlEngine jexl = createEngine(features);
244 final JexlScript script = jexl.createScript(src);
245 final Object result = script.execute(null, 6);
246 assertEquals(720, result);
247 features.fatArrow(false);
248 final JexlEngine jexl1 = createEngine(features);
249 final JexlException.Feature xfeature = assertThrows(JexlException.Feature.class, () -> jexl1.createScript(src));
250 assertTrue(xfeature.getMessage().contains("fat-arrow"));
251 }
252
253 @Test void testHoistLambda() {
254 final JexlEngine jexl = createEngine();
255 final JexlEvalContext ctx = new JexlEvalContext();
256 ctx.getEngineOptions().setLexical(false);
257 JexlScript s42;
258 Object result;
259 JexlScript s15;
260 String[] localv;
261 Set<List<String>> hvars;
262 String strs;
263
264
265 strs = "(x)->{ (y)->{ x + y } }";
266 s42 = jexl.createScript(strs);
267 result = s42.execute(ctx, 15);
268 assertInstanceOf(JexlScript.class, result);
269 s15 = (JexlScript) result;
270 localv = s15.getLocalVariables();
271 assertEquals(0, localv.length);
272 hvars = s15.getVariables();
273 assertEquals(1, hvars.size());
274
275
276
277
278 strs = "(x)->{ (y)->{ var z = 169; var x; x + y } }";
279 s42 = jexl.createScript(strs);
280 result = s42.execute(ctx, 15);
281 assertInstanceOf(JexlScript.class, result);
282 s15 = (JexlScript) result;
283 localv = s15.getLocalVariables();
284 assertNotNull(localv);
285 assertEquals(1, localv.length);
286 hvars = s15.getVariables();
287 assertEquals(1, hvars.size());
288
289 result = s15.execute(ctx, 27);
290 assertEquals(42, result);
291 }
292
293 @Test void testIdentity() {
294 final JexlEngine jexl = createEngine();
295 JexlScript script;
296 Object result;
297
298 script = jexl.createScript("(x)->{ x }");
299 assertArrayEquals(new String[]{"x"}, script.getParameters());
300 result = script.execute(null, 42);
301 assertEquals(42, result);
302 }
303
304 @Test void testLambda() {
305 final JexlEngine jexl = createEngine();
306 String strs = "var s = function(x) { x + x }; s(21)";
307 JexlScript s42 = jexl.createScript(strs);
308 Object result = s42.execute(null);
309 assertEquals(42, result);
310 strs = "var s = function(x, y) { x + y }; s(15, 27)";
311 s42 = jexl.createScript(strs);
312 result = s42.execute(null);
313 assertEquals(42, result);
314 }
315
316 @Test void testLambdaClosure() {
317 final JexlEngine jexl = createEngine();
318 String strs = "var t = 20; var s = function(x, y) { x + y + t}; s(15, 7)";
319 JexlScript s42 = jexl.createScript(strs);
320 Object result = s42.execute(null);
321 assertEquals(42, result);
322 strs = "var t = 19; var s = function(x, y) { var t = 20; x + y + t}; s(15, 7)";
323 s42 = jexl.createScript(strs);
324 result = s42.execute(null);
325 assertEquals(42, result);
326 strs = "var t = 20; var s = function(x, y) {x + y + t}; t = 54; s(15, 7)";
327 s42 = jexl.createScript(strs);
328 result = s42.execute(null);
329 assertEquals(42, result);
330 strs = "var t = 19; var s = function(x, y) { var t = 20; x + y + t}; t = 54; s(15, 7)";
331 s42 = jexl.createScript(strs);
332 result = s42.execute(null);
333 assertEquals(42, result);
334 }
335
336 @Test void testLambdaExpr0() {
337 final String src = "(x, y) -> x + y";
338 final JexlEngine jexl = createEngine();
339 final JexlScript script = jexl.createScript(src);
340 final Object result = script.execute(null, 11, 31);
341 assertEquals(42, result);
342 }
343
344 @Test void testLambdaExpr1() {
345 final String src = "x -> x + x";
346 final JexlEngine jexl = createEngine();
347 final JexlScript script = jexl.createScript(src);
348 final Object result = script.execute(null, 21);
349 assertEquals(42, result);
350 }
351
352 @Test void testLambdaExpr10() {
353 final String src = "(a)->{ var x = x -> x + x; x(a) }";
354 final JexlEngine jexl = createEngine();
355 final JexlScript script = jexl.createScript(src);
356 final Object result = script.execute(null, 21);
357 assertEquals(42, result);
358 }
359
360 @Test void testLambdaExpr2() {
361 final String src = "x -> { { x + x } }";
362 final JexlEngine jexl = createEngine();
363 final JexlScript script = jexl.createScript(src);
364 final Object result = script.execute(null, 21);
365 assertInstanceOf(Set.class, result);
366 final Set<?> set = (Set<?>) result;
367 assertEquals(1, set.size());
368 assertTrue(set.contains(42));
369 }
370
371 @Test void testLambdaExpr3() {
372 final String src = "x -> ( { x + x } )";
373 final JexlEngine jexl = createEngine();
374 final JexlScript script = jexl.createScript(src);
375 final Object result = script.execute(null, 21);
376 assertInstanceOf(Set.class, result);
377 final Set<?> set = (Set<?>) result;
378 assertEquals(1, set.size());
379 assertTrue(set.contains(42));
380 }
381
382 @Test void testLambdaLambda() {
383 final JexlEngine jexl = createEngine();
384 String strs = "var t = 19; ( (x, y)->{ var t = 20; x + y + t} )(15, 7);";
385 JexlScript s42 = jexl.createScript(strs);
386 Object result = s42.execute(null);
387 assertEquals(42, result);
388
389 strs = "( (x, y)->{ ( (xx, yy)->{xx + yy } )(x, y) } )(15, 27)";
390 s42 = jexl.createScript(strs);
391 result = s42.execute(null);
392 assertEquals(42, result);
393
394 strs = "var t = 19; var s = (x, y)->{ var t = 20; x + y + t}; t = 54; s(15, 7)";
395 s42 = jexl.createScript(strs);
396 result = s42.execute(null);
397 assertEquals(42, result);
398 }
399
400 @Test void testNamedFunc() {
401 final String src = "(let a)->{ function fact(const x) { x <= 1? 1 : x * fact(x - 1); } fact(a); }";
402 final JexlEngine jexl = createEngine();
403 final JexlScript script = jexl.createScript(src);
404 final Object result = script.execute(null, 6);
405 assertEquals(720, result);
406 final String parsed = simpleWhitespace(script.getParsedText());
407 assertEquals(simpleWhitespace(src), parsed);
408 }
409
410
411 @Test void testNestLambada() throws Exception {
412 final JexlEngine jexl = createEngine();
413 final String strs = "(x)->{ (y)->{ x + y } }";
414 final JexlScript s42 = jexl.createScript(strs);
415 final JexlScript s42b = jexl.createScript(s42.toString());
416 assertEquals(s42.hashCode(), s42b.hashCode());
417 assertEquals(s42, s42b);
418 Object result = s42.execute(null, 15);
419 assertInstanceOf(JexlScript.class, result);
420 final Object resultb = s42.execute(null, 15);
421 assertEquals(result.hashCode(), resultb.hashCode());
422 assertEquals(result, resultb);
423 assertEquals(result, jexl.createScript(resultb.toString(), "x").execute(null, 15));
424 final JexlScript s15 = (JexlScript) result;
425 final Callable<Object> s15b = s15.callable(null, 27);
426 result = s15.execute(null, 27);
427 assertEquals(42, result);
428 result = s15b.call();
429 assertEquals(42, result);
430 }
431
432 @Test void testNestLambda() {
433 final JexlEngine jexl = createEngine();
434 final String strs = "( (x)->{ (y)->{ x + y } })(15)(27)";
435 final JexlScript s42 = jexl.createScript(strs);
436 final Object result = s42.execute(null);
437 assertEquals(42, result);
438 }
439
440 @Test void testRecurse() {
441 final JexlEngine jexl = createEngine();
442 final JexlContext jc = new MapContext();
443 final JexlScript script = jexl.createScript("var fact = (x)->{ if (x <= 1) 1; else x * fact(x - 1) }; fact(5)");
444 final int result = (Integer) script.execute(jc);
445 assertEquals(120, result);
446 }
447
448 @Test void testRecurse1() {
449 final JexlEngine jexl = createEngine();
450 final JexlContext jc = new MapContext();
451 final String src = "var fact = (x)-> x <= 1? 1 : x * fact(x - 1);\nfact(5);\n";
452 final JexlScript script = jexl.createScript(src);
453 final int result = (Integer) script.execute(jc);
454 assertEquals(120, result);
455 final String parsed = script.getParsedText();
456 assertEquals(src, parsed);
457 }
458
459 @Test void testRecurse2() {
460 final JexlEngine jexl = createEngine();
461 final JexlContext jc = new MapContext();
462
463 final JexlScript script = jexl.createScript(
464 "var y = 1; var z = 1; "
465 +"var fact = (x)->{ if (x <= y) z; else x * fact(x - 1) }; fact(6)");
466 final int result = (Integer) script.execute(jc);
467 assertEquals(720, result);
468 }
469
470 @Test void testRecurse2b() {
471 final JexlEngine jexl = createEngine();
472 final JexlContext jc = new MapContext();
473
474 final JexlScript fact = jexl.createScript(
475 "var y = 1; var z = 1; "
476 +"var fact = (x)->{ if (x <= y) z; else x * fact(x - 1) };" +
477 "fact");
478 final Script func = (Script) fact.execute(jc);
479 final String[] captured = func.getCapturedVariables();
480 assertEquals(3, captured.length);
481 assertTrue(Arrays.asList(captured).containsAll(Arrays.asList("z", "y", "fact")));
482 final int result = (Integer) func.execute(jc, 6);
483 assertEquals(720, result);
484 }
485
486 @Test void testRecurse3() {
487 final JexlEngine jexl = createEngine();
488 final JexlContext jc = new MapContext();
489
490 final JexlScript script = jexl.createScript(
491 "var y = 1; var z = 1;var foo = (x)->{y + z}; "
492 +"var fact = (x)->{ if (x <= y) z; else x * fact(x - 1) }; fact(6)");
493 final int result = (Integer) script.execute(jc);
494 assertEquals(720, result);
495 }
496
497 @Test void testScriptArguments() {
498 final JexlEngine jexl = createEngine();
499 final JexlScript s = jexl.createScript(" x + x ", "x");
500 final JexlScript s42 = jexl.createScript("s(21)", "s");
501 final Object result = s42.execute(null, s);
502 assertEquals(42, result);
503 }
504
505 @Test void testScriptContext() {
506 final JexlEngine jexl = createEngine();
507 final JexlScript s = jexl.createScript("function(x) { x + x }");
508 final String fsstr = s.getParsedText(0);
509 assertEquals("(x)->{ x + x; }", fsstr);
510 assertEquals(42, s.execute(null, 21));
511 JexlScript s42 = jexl.createScript("s(21)");
512 final JexlContext ctxt = new JexlEvalContext();
513 ctxt.set("s", s);
514 Object result = s42.execute(ctxt);
515 assertEquals(42, result);
516 result = s42.execute(ctxt);
517 assertEquals(42, result);
518 s42 = jexl.createScript("x-> { x + x }");
519 result = s42.execute(ctxt, 21);
520 assertEquals(42, result);
521 }
522
523
524
525
526 @Test void testRefCapture1() {
527 final String src = "let x = 10;\n" +
528 "let foo = () -> {\n" +
529 "x += 2;\n" +
530 "}\n" +
531 "x = 40;\n" +
532 "foo();\n" +
533 "x";
534 final JexlEngine jexl = new JexlBuilder()
535 .features(new JexlFeatures().referenceCapture(true))
536 .create();
537 JexlScript script;
538 Object result;
539 script = jexl.createScript(src);
540 result = script.execute(null);
541 assertEquals(42, result);
542 }
543
544 @Test void testRefCapture2() {
545 final String src = "let x = 10; let f = () -> { x + 2 }; x = 40; f()";
546 final JexlEngine jexl = new JexlBuilder()
547 .features(new JexlFeatures().constCapture(true).referenceCapture(true))
548 .create();
549 JexlScript script;
550 Object result;
551 script = jexl.createScript(src);
552 result = script.execute(null);
553 assertEquals(42, result);
554 }
555
556 @Test void testRefCapture3() {
557 final String src = "let x = 10; let f = () -> { x + 2 }; x = 40; f";
558 final JexlEngine jexl = new JexlBuilder()
559 .features(new JexlFeatures().constCapture(true).referenceCapture(true))
560 .create();
561 JexlScript script;
562 Object result;
563 script = jexl.createScript(src);
564 result = script.execute(null);
565 assertInstanceOf(JexlScript.class, result);
566 script = jexl.createScript("f()", "f");
567 result = script.execute(null, result);
568 assertEquals(42, result);
569 }
570
571 @Test void testRefCapture4() {
572 final String src = "let x = 10; let f = () -> { let x = 142; x }; x = 40; f";
573 final JexlEngine jexl = new JexlBuilder()
574 .features(new JexlFeatures().referenceCapture(true))
575 .create();
576 JexlScript script;
577 Object result;
578 script = jexl.createScript(src);
579 result = script.execute(null);
580 assertInstanceOf(JexlScript.class, result);
581 script = jexl.createScript("f()", "f");
582 result = script.execute(null, result);
583 assertEquals(142, result);
584 }
585
586 @Test void testRefCapture5() {
587 final JexlFeatures f426 = new JexlFeatures().referenceCapture(true);
588 final JexlEngine jexl = new JexlBuilder().features(f426).create();
589 final String src = "let z = 32; let x = 40; function foo() { x += 2; }; function bar() { let x = -169; foo(); x;}; bar();";
590 final JexlScript script = jexl.createScript(src);
591 assertNotNull(script);
592 final Object result = script.execute(null);
593 assertEquals(-169, result);
594 }
595
596 @Test void testRefCapture6() {
597 final JexlFeatures f426 = new JexlFeatures().referenceCapture(true);
598 final JexlEngine jexl = new JexlBuilder().features(f426).create();
599 final String src = "let x = 40; function foo() { x += 2; }; function bar() { x = -169; () -> { foo(); }}; bar();";
600 JexlScript script = jexl.createScript(src);
601 assertNotNull(script);
602 Object result = script.execute(null);
603 assertInstanceOf(JexlScript.class, result);
604 script = jexl.createScript("f()", "f");
605 result = script.execute(null, result);
606 assertEquals(-167, result);
607 }
608 }