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