Repository: jsonata-js/jsonata (js)

Issue:
Array length/memory errors with the range operator
The range operator doesn't do any checks on the size of the arrays that it creates. If you try to create an array larger than JavaScript can handle, e.g. `[1..1e10]`, you get an [Invalid array length](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Invalid_array_length) error. Note that this is a JavaScript error rather than a JSONata error. Maybe JSONata could check for this case and handle it more gracefully.

Along similar lines, the array `[1..1e9]` is legal but my machine runs out of memory trying to evaluate it. I suspect that if memory were not an issue, the expression would still be prohibitively slow to run. Is it worth imposing an upper bound on range operator array size?


Pull request description:
limit size allocated by range operator to 1e7
Impose implementation defined limit of 1e7 to the size of array that can be allocated by the range operator (`..`)

resolves #240 

Patch:
diff --git a/src/jsonata.js b/src/jsonata.js
index 639c2d1..ac64ec8 100644
--- a/src/jsonata.js
+++ b/src/jsonata.js
@@ -849,7 +849,19 @@ var jsonata = (function() {
             return result;
         }
 
-        result = new Array(rhs - lhs + 1);
+        // limit the size of the array to ten million entries (1e7)
+        // this is an implementation defined limit to protect against
+        // memory and performance issues.  This value may increase in the future.
+        var size = rhs - lhs + 1;
+        if(size > 1e7) {
+            throw {
+                code: "D2014",
+                stack: (new Error()).stack,
+                value: size
+            };
+        }
+
+        result = new Array(size);
         for (var item = lhs, index = 0; item <= rhs; item++, index++) {
             result[index] = item;
         }
@@ -1761,6 +1773,7 @@ var jsonata = (function() {
         "T2011": "The insert/update clause of the transform expression must evaluate to an object: {{value}}",
         "T2012": "The delete clause of the transform expression must evaluate to a string or array of strings: {{value}}",
         "T2013": "The transform expression clones the input object using the $clone() function.  This has been overridden in the current scope by a non-function.",
+        "D2014": "The size of the sequence allocated by the range operator (..) must not exceed 1e6.  Attempted to allocate {{value}}.",
         "D3001": "Attempting to invoke string function on Infinity or NaN",
         "D3010": "Second argument of replace function cannot be an empty string",
         "D3011": "Fourth argument of replace function must evaluate to a positive number",


Test patch:
diff --git a/test/test-suite/groups/range-operator/case021.json b/test/test-suite/groups/range-operator/case021.json
new file mode 100644
index 0000000..50e318c
--- /dev/null
+++ b/test/test-suite/groups/range-operator/case021.json
@@ -0,0 +1,8 @@
+{
+    "expr": "[1..10000000] ~> $count()",
+    "dataset": null,
+    "bindings": {},
+    "result": 1e7,
+    "timelimit": 10000,
+    "depth": 10
+}
diff --git a/test/test-suite/groups/range-operator/case022.json b/test/test-suite/groups/range-operator/case022.json
new file mode 100644
index 0000000..92fbbc6
--- /dev/null
+++ b/test/test-suite/groups/range-operator/case022.json
@@ -0,0 +1,6 @@
+{
+    "expr": "[0..10000000] ~> $count()",
+    "dataset": null,
+    "bindings": {},
+    "code": "D2014"
+}
diff --git a/test/test-suite/groups/range-operator/case023.json b/test/test-suite/groups/range-operator/case023.json
new file mode 100644
index 0000000..665b3be
--- /dev/null
+++ b/test/test-suite/groups/range-operator/case023.json
@@ -0,0 +1,6 @@
+{
+    "expr": "[1..10000001] ~> $count()",
+    "dataset": null,
+    "bindings": {},
+    "code": "D2014"
+}
diff --git a/test/test-suite/groups/range-operator/case024.json b/test/test-suite/groups/range-operator/case024.json
new file mode 100644
index 0000000..50fbacd
--- /dev/null
+++ b/test/test-suite/groups/range-operator/case024.json
@@ -0,0 +1,8 @@
+{
+    "expr": "[100..10000099] ~> $count()",
+    "dataset": null,
+    "bindings": {},
+    "result": 1e7,
+    "timelimit": 10000,
+    "depth": 10
+}