diff --git a/src/main/java/com/dashjoin/jsonata/Jsonata.java b/src/main/java/com/dashjoin/jsonata/Jsonata.java index 575279b..03c80ce 100644 --- a/src/main/java/com/dashjoin/jsonata/Jsonata.java +++ b/src/main/java/com/dashjoin/jsonata/Jsonata.java @@ -662,6 +662,7 @@ public Object call() throws Exception { // array constructor - evaluate each item result = new JList<>(); // []; int idx = 0; + boolean wasParallelCall = environment.isParallelCall; for (var item : expr.expressions) { environment.isParallelCall = idx > 0; Object value = evaluate(item, input, environment); @@ -673,6 +674,7 @@ public Object call() throws Exception { } idx++; } + environment.isParallelCall = wasParallelCall; if(expr.consarray) { if (!(result instanceof JList)) result = new JList((List)result); @@ -1117,6 +1119,7 @@ static class GroupEntry { // iterate over the groups to evaluate the "value" expression //let generators = /* await */ Promise.all(Object.keys(groups).map(/* async */ (key, idx) => { int idx = 0; + boolean wasParallelCall = environment.isParallelCall; for (Entry e : groups.entrySet()) { var entry = e.getValue(); var context = entry.data; @@ -1135,6 +1138,7 @@ static class GroupEntry { idx++; } + environment.isParallelCall = wasParallelCall; // for (let generator of generators) { // var [key, value] = /* await */ generator; diff --git a/src/test/java/com/dashjoin/jsonata/RuntimeTest.java b/src/test/java/com/dashjoin/jsonata/RuntimeTest.java index dcc1bfe..58c1c4c 100644 --- a/src/test/java/com/dashjoin/jsonata/RuntimeTest.java +++ b/src/test/java/com/dashjoin/jsonata/RuntimeTest.java @@ -24,6 +24,54 @@ public void testRuntimeBounds() { expr.evaluate(null, frame); } + /** + * https://github.com/dashjoin/jsonata-java/issues/120 + */ + @Test + public void testRuntimeBoundsArrayConstructorInLoop() { + var expr = jsonata("$map([1..30], function($i) { [$i, $i] })"); + var frame = expr.createFrame(); + frame.setRuntimeBounds(1000, 10); + expr.evaluate(null, frame); + } + + @Test + public void testRuntimeBoundsNestedArrayConstructorInLoop() { + var expr = jsonata("$map([1..30], function($i) { [$i, [$i, $i], $i] })"); + var frame = expr.createFrame(); + frame.setRuntimeBounds(1000, 10); + expr.evaluate(null, frame); + } + + @Test + public void testRuntimeBoundsObjectConstructorInLoop() { + var expr = jsonata("$map([1..30], function($i) { {'a': $i, 'b': $i} })"); + var frame = expr.createFrame(); + frame.setRuntimeBounds(1000, 10); + expr.evaluate(null, frame); + } + + /** + * restoring isParallelCall to false instead of its previous value would let depth go negative, + * silently loosening the bound without ever overflowing + */ + @Test + public void testRuntimeBoundsDepthBalancedAfterConstructors() { + var expr = jsonata("$map([1..30], function($i) { [$i, [$i, $i], {'a': $i, 'b': [$i, $i]}] })"); + var frame = expr.createFrame(); + var timebox = new Timebox(frame, 1000, 10); + expr.evaluate(null, frame); + Assertions.assertEquals(0, timebox.depth); + } + + @Test + public void testRuntimeBoundsRecursionWithArrayConstructor() { + var expr = jsonata("($f := function($n) { $n > 0 ? [$n, $f($n - 1)] : [] }; $f(30))"); + var frame = expr.createFrame(); + frame.setRuntimeBounds(1000, 10); + Assertions.assertThrows(JException.class, () -> expr.evaluate(null, frame)); + } + boolean entered = false; boolean exited = false;