Repository: source-academy/js-slang
Pull request #285 (2019-07-09 03:27:52)

Make builtin function error on native report line number
Fixes source-academy/cadet-frontend#690

coveralls: ## Pull Request Test Coverage Report for [Build 1343](https://coveralls.io/builds/24441888)

* **7** of **7**   **(100.0%)**  changed or added relevant lines in **3** files are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage increased (+**0.01%**) to **91.599%**

---



|  Totals | [![Coverage Status](https://coveralls.io/builds/24441888/badge)](https://coveralls.io/builds/24441888) |
| :-- | --: |
| Change from base [Build 1333](https://coveralls.io/builds/24411604): |  0.01% |
| Covered Lines: | 1711 |
| Relevant Lines: | 1846 |

---
##### 💛  - [Coveralls](https://coveralls.io)


Patch:
diff --git a/src/index.ts b/src/index.ts
index 50673ab6..f6d0f1f8 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -179,7 +179,7 @@ export function runInContext(
         value: sandboxedEval(transpiled)
       } as Result)
     } catch (error) {
-      if (error instanceof RuntimeSourceError) {
+      if (error instanceof RuntimeSourceError || error instanceof ExceptionError) {
         context.errors.push(error)
         return resolvedErrorPromise
       }
diff --git a/src/interpreter.ts b/src/interpreter.ts
index d9fbff67..c3217984 100644
--- a/src/interpreter.ts
+++ b/src/interpreter.ts
@@ -614,9 +614,10 @@ export function* apply(
         break
       } catch (e) {
         // Recover from exception
-        const globalEnvironment =
-          context.runtime.environments[context.runtime.environments.length - 1]
-        context.runtime.environments = [globalEnvironment]
+        context.runtime.environments = context.runtime.environments.slice(
+          -context.numberOfOuterEnvironments
+        )
+
         const loc = node ? node.loc! : constants.UNKNOWN_LOCATION
         if (!(e instanceof errors.RuntimeSourceError)) {
           // The error could've arisen when the builtin called a source function which errored.
diff --git a/src/utils/operators.ts b/src/utils/operators.ts
index 87f31e0d..06154611 100644
--- a/src/utils/operators.ts
+++ b/src/utils/operators.ts
@@ -2,6 +2,7 @@ import { BinaryOperator, UnaryOperator } from 'estree'
 import { JSSLANG_PROPERTIES } from '../constants'
 import {
   CallingNonFunctionValue,
+  ExceptionError,
   GetInheritedPropertyError,
   InvalidNumberOfArguments
 } from '../interpreter-errors'
@@ -27,14 +28,20 @@ export function callIfFuncAndRightArgs(
     end: { line, column }
   })
   if (typeof candidate === 'function') {
-    if (candidate.transformedFunction !== undefined) {
+    if (candidate.transformedFunction === undefined) {
+      try {
+        return candidate(...args)
+      } catch (error) {
+        throw new ExceptionError(error, dummy.loc!)
+      }
+    } else {
       const expectedLength = candidate.transformedFunction.length
       const receivedLength = args.length
       if (expectedLength !== receivedLength) {
         throw new InvalidNumberOfArguments(dummy, expectedLength, receivedLength)
       }
+      return candidate(...args)
     }
-    return candidate(...args)
   } else {
     throw new CallingNonFunctionValue(candidate, dummy)
   }


Test patch:
diff --git a/src/stdlib/__tests__/__snapshots__/list.ts.snap b/src/stdlib/__tests__/__snapshots__/list.ts.snap
index 495fc544..973dea97 100644
--- a/src/stdlib/__tests__/__snapshots__/list.ts.snap
+++ b/src/stdlib/__tests__/__snapshots__/list.ts.snap
@@ -161,6 +161,27 @@ Object {
   "parsedErrors": "Line 1: Error: head(xs) expects a pair as argument xs, but encountered null",
   "result": undefined,
   "resultStatus": "error",
+  "transpiled": "const native = $$NATIVE_STORAGE;
+const callIfFuncAndRightArgs = native.operators.get(\\"callIfFuncAndRightArgs\\");
+const boolOrErr = native.operators.get(\\"boolOrErr\\");
+const wrap = native.operators.get(\\"wrap\\");
+const unaryOp = native.operators.get(\\"unaryOp\\");
+const binaryOp = native.operators.get(\\"binaryOp\\");
+const throwIfTimeout = native.operators.get(\\"throwIfTimeout\\");
+const setProp = native.operators.get(\\"setProp\\");
+const getProp = native.operators.get(\\"getProp\\");
+let lastStatementResult = undefined;
+(( <globals redacted> ) => {
+  return (() => {
+    {
+      {
+        lastStatementResult = eval(\\"callIfFuncAndRightArgs(list_ref, 1, 0, callIfFuncAndRightArgs(list, 1, 9, 1, 2, 3), 3);\\");
+      }
+    }
+    return lastStatementResult;
+  })();
+})();
+",
   "visualiseListResult": Array [],
 }
 `;
@@ -190,6 +211,27 @@ Object {
   "parsedErrors": "Line 1: Error: list_ref(xs, n) expects a positive integer as argument n, but encountered -1",
   "result": undefined,
   "resultStatus": "error",
+  "transpiled": "const native = $$NATIVE_STORAGE;
+const callIfFuncAndRightArgs = native.operators.get(\\"callIfFuncAndRightArgs\\");
+const boolOrErr = native.operators.get(\\"boolOrErr\\");
+const wrap = native.operators.get(\\"wrap\\");
+const unaryOp = native.operators.get(\\"unaryOp\\");
+const binaryOp = native.operators.get(\\"binaryOp\\");
+const throwIfTimeout = native.operators.get(\\"throwIfTimeout\\");
+const setProp = native.operators.get(\\"setProp\\");
+const getProp = native.operators.get(\\"getProp\\");
+let lastStatementResult = undefined;
+(( <globals redacted> ) => {
+  return (() => {
+    {
+      {
+        lastStatementResult = eval(\\"callIfFuncAndRightArgs(list_ref, 1, 0, callIfFuncAndRightArgs(list, 1, 9, 1, 2, 3), unaryOp(\\\\\\"-\\\\\\", 1, 1, 24));\\");
+      }
+    }
+    return lastStatementResult;
+  })();
+})();
+",
   "visualiseListResult": Array [],
 }
 `;
@@ -219,6 +261,27 @@ Object {
   "parsedErrors": "Line 1: Error: list_ref(xs, n) expects a positive integer as argument n, but encountered 1.5",
   "result": undefined,
   "resultStatus": "error",
+  "transpiled": "const native = $$NATIVE_STORAGE;
+const callIfFuncAndRightArgs = native.operators.get(\\"callIfFuncAndRightArgs\\");
+const boolOrErr = native.operators.get(\\"boolOrErr\\");
+const wrap = native.operators.get(\\"wrap\\");
+const unaryOp = native.operators.get(\\"unaryOp\\");
+const binaryOp = native.operators.get(\\"binaryOp\\");
+const throwIfTimeout = native.operators.get(\\"throwIfTimeout\\");
+const setProp = native.operators.get(\\"setProp\\");
+const getProp = native.operators.get(\\"getProp\\");
+let lastStatementResult = undefined;
+(( <globals redacted> ) => {
+  return (() => {
+    {
+      {
+        lastStatementResult = eval(\\"callIfFuncAndRightArgs(list_ref, 1, 0, callIfFuncAndRightArgs(list, 1, 9, 1, 2, 3), 1.5);\\");
+      }
+    }
+    return lastStatementResult;
+  })();
+})();
+",
   "visualiseListResult": Array [],
 }
 `;
@@ -248,6 +311,27 @@ Object {
   "parsedErrors": "Line 1: Error: list_ref(xs, n) expects a positive integer as argument n, but encountered 1",
   "result": undefined,
   "resultStatus": "error",
+  "transpiled": "const native = $$NATIVE_STORAGE;
+const callIfFuncAndRightArgs = native.operators.get(\\"callIfFuncAndRightArgs\\");
+const boolOrErr = native.operators.get(\\"boolOrErr\\");
+const wrap = native.operators.get(\\"wrap\\");
+const unaryOp = native.operators.get(\\"unaryOp\\");
+const binaryOp = native.operators.get(\\"binaryOp\\");
+const throwIfTimeout = native.operators.get(\\"throwIfTimeout\\");
+const setProp = native.operators.get(\\"setProp\\");
+const getProp = native.operators.get(\\"getProp\\");
+let lastStatementResult = undefined;
+(( <globals redacted> ) => {
+  return (() => {
+    {
+      {
+        lastStatementResult = eval(\\"callIfFuncAndRightArgs(list_ref, 1, 0, callIfFuncAndRightArgs(list, 1, 9, 1, 2, 3), '1');\\");
+      }
+    }
+    return lastStatementResult;
+  })();
+})();
+",
   "visualiseListResult": Array [],
 }
 `;
@@ -277,6 +361,27 @@ Object {
   "parsedErrors": "Line 1: Error: build_list(n, fun) expects a positive integer as argument n, but encountered -1",
   "result": undefined,
   "resultStatus": "error",
+  "transpiled": "const native = $$NATIVE_STORAGE;
+const callIfFuncAndRightArgs = native.operators.get(\\"callIfFuncAndRightArgs\\");
+const boolOrErr = native.operators.get(\\"boolOrErr\\");
+const wrap = native.operators.get(\\"wrap\\");
+const unaryOp = native.operators.get(\\"unaryOp\\");
+const binaryOp = native.operators.get(\\"binaryOp\\");
+const throwIfTimeout = native.operators.get(\\"throwIfTimeout\\");
+const setProp = native.operators.get(\\"setProp\\");
+const getProp = native.operators.get(\\"getProp\\");
+let lastStatementResult = undefined;
+(( <globals redacted> ) => {
+  return (() => {
+    {
+      {
+        lastStatementResult = eval(\\"callIfFuncAndRightArgs(build_list, 1, 0, unaryOp(\\\\\\"-\\\\\\", 1, 1, 11), wrap(x => ({   isTail: false,   value: x }), \\\\\\"x => x\\\\\\"));\\");
+      }
+    }
+    return lastStatementResult;
+  })();
+})();
+",
   "visualiseListResult": Array [],
 }
 `;
@@ -306,6 +411,27 @@ Object {
   "parsedErrors": "Line 1: Error: build_list(n, fun) expects a positive integer as argument n, but encountered 1.5",
   "result": undefined,
   "resultStatus": "error",
+  "transpiled": "const native = $$NATIVE_STORAGE;
+const callIfFuncAndRightArgs = native.operators.get(\\"callIfFuncAndRightArgs\\");
+const boolOrErr = native.operators.get(\\"boolOrErr\\");
+const wrap = native.operators.get(\\"wrap\\");
+const unaryOp = native.operators.get(\\"unaryOp\\");
+const binaryOp = native.operators.get(\\"binaryOp\\");
+const throwIfTimeout = native.operators.get(\\"throwIfTimeout\\");
+const setProp = native.operators.get(\\"setProp\\");
+const getProp = native.operators.get(\\"getProp\\");
+let lastStatementResult = undefined;
+(( <globals redacted> ) => {
+  return (() => {
+    {
+      {
+        lastStatementResult = eval(\\"callIfFuncAndRightArgs(build_list, 1, 0, 1.5, wrap(x => ({   isTail: false,   value: x }), \\\\\\"x => x\\\\\\"));\\");
+      }
+    }
+    return lastStatementResult;
+  })();
+})();
+",
   "visualiseListResult": Array [],
 }
 `;
@@ -335,6 +461,27 @@ Object {
   "parsedErrors": "Line 1: Error: build_list(n, fun) expects a positive integer as argument n, but encountered 1",
   "result": undefined,
   "resultStatus": "error",
+  "transpiled": "const native = $$NATIVE_STORAGE;
+const callIfFuncAndRightArgs = native.operators.get(\\"callIfFuncAndRightArgs\\");
+const boolOrErr = native.operators.get(\\"boolOrErr\\");
+const wrap = native.operators.get(\\"wrap\\");
+const unaryOp = native.operators.get(\\"unaryOp\\");
+const binaryOp = native.operators.get(\\"binaryOp\\");
+const throwIfTimeout = native.operators.get(\\"throwIfTimeout\\");
+const setProp = native.operators.get(\\"setProp\\");
+const getProp = native.operators.get(\\"getProp\\");
+let lastStatementResult = undefined;
+(( <globals redacted> ) => {
+  return (() => {
+    {
+      {
+        lastStatementResult = eval(\\"callIfFuncAndRightArgs(build_list, 1, 0, '1', wrap(x => ({   isTail: false,   value: x }), \\\\\\"x => x\\\\\\"));\\");
+      }
+    }
+    return lastStatementResult;
+  })();
+})();
+",
   "visualiseListResult": Array [],
 }
 `;
@@ -364,6 +511,27 @@ Object {
   "parsedErrors": "Line 1: Error: enum_list(start, end) expects a number as argument start, but encountered 1",
   "result": undefined,
   "resultStatus": "error",
+  "transpiled": "const native = $$NATIVE_STORAGE;
+const callIfFuncAndRightArgs = native.operators.get(\\"callIfFuncAndRightArgs\\");
+const boolOrErr = native.operators.get(\\"boolOrErr\\");
+const wrap = native.operators.get(\\"wrap\\");
+const unaryOp = native.operators.get(\\"unaryOp\\");
+const binaryOp = native.operators.get(\\"binaryOp\\");
+const throwIfTimeout = native.operators.get(\\"throwIfTimeout\\");
+const setProp = native.operators.get(\\"setProp\\");
+const getProp = native.operators.get(\\"getProp\\");
+let lastStatementResult = undefined;
+(( <globals redacted> ) => {
+  return (() => {
+    {
+      {
+        lastStatementResult = eval(\\"callIfFuncAndRightArgs(enum_list, 1, 0, '1', '5');\\");
+      }
+    }
+    return lastStatementResult;
+  })();
+})();
+",
   "visualiseListResult": Array [],
 }
 `;
@@ -393,6 +561,27 @@ Object {
   "parsedErrors": "Line 1: Error: enum_list(start, end) expects a number as argument start, but encountered 1",
   "result": undefined,
   "resultStatus": "error",
+  "transpiled": "const native = $$NATIVE_STORAGE;
+const callIfFuncAndRightArgs = native.operators.get(\\"callIfFuncAndRightArgs\\");
+const boolOrErr = native.operators.get(\\"boolOrErr\\");
+const wrap = native.operators.get(\\"wrap\\");
+const unaryOp = native.operators.get(\\"unaryOp\\");
+const binaryOp = native.operators.get(\\"binaryOp\\");
+const throwIfTimeout = native.operators.get(\\"throwIfTimeout\\");
+const setProp = native.operators.get(\\"setProp\\");
+const getProp = native.operators.get(\\"getProp\\");
+let lastStatementResult = undefined;
+(( <globals redacted> ) => {
+  return (() => {
+    {
+      {
+        lastStatementResult = eval(\\"callIfFuncAndRightArgs(enum_list, 1, 0, '1', 5);\\");
+      }
+    }
+    return lastStatementResult;
+  })();
+})();
+",
   "visualiseListResult": Array [],
 }
 `;
@@ -422,6 +611,27 @@ Object {
   "parsedErrors": "Line 1: Error: enum_list(start, end) expects a number as argument start, but encountered 5",
   "result": undefined,
   "resultStatus": "error",
+  "transpiled": "const native = $$NATIVE_STORAGE;
+const callIfFuncAndRightArgs = native.operators.get(\\"callIfFuncAndRightArgs\\");
+const boolOrErr = native.operators.get(\\"boolOrErr\\");
+const wrap = native.operators.get(\\"wrap\\");
+const unaryOp = native.operators.get(\\"unaryOp\\");
+const binaryOp = native.operators.get(\\"binaryOp\\");
+const throwIfTimeout = native.operators.get(\\"throwIfTimeout\\");
+const setProp = native.operators.get(\\"setProp\\");
+const getProp = native.operators.get(\\"getProp\\");
+let lastStatementResult = undefined;
+(( <globals redacted> ) => {
+  return (() => {
+    {
+      {
+        lastStatementResult = eval(\\"callIfFuncAndRightArgs(enum_list, 1, 0, 1, '5');\\");
+      }
+    }
+    return lastStatementResult;
+  })();
+})();
+",
   "visualiseListResult": Array [],
 }
 `;
@@ -704,6 +914,37 @@ list(1, 'a string \\"\\"', () => a, f, true, 3.14);",
     ],
   ],
   "resultStatus": "finished",
+  "transpiled": "const native = $$NATIVE_STORAGE;
+const callIfFuncAndRightArgs = native.operators.get(\\"callIfFuncAndRightArgs\\");
+const boolOrErr = native.operators.get(\\"boolOrErr\\");
+const wrap = native.operators.get(\\"wrap\\");
+const unaryOp = native.operators.get(\\"unaryOp\\");
+const binaryOp = native.operators.get(\\"binaryOp\\");
+const throwIfTimeout = native.operators.get(\\"throwIfTimeout\\");
+const setProp = native.operators.get(\\"setProp\\");
+const getProp = native.operators.get(\\"getProp\\");
+let lastStatementResult = undefined;
+(( <globals redacted> ) => {
+  return (() => {
+    {
+      {
+        const f = wrap(() => {
+          return {
+            isTail: false,
+            value: 1
+          };
+        }, \\"function f() {\\\\n  return 1;\\\\n}\\");
+        lastStatementResult = eval(\\"callIfFuncAndRightArgs(list, 2, 0, 1, 'a string \\\\\\"\\\\\\"', wrap(() => ({   isTail: false,   value: a }), \\\\\\"() => a\\\\\\"), f, true, 3.14);\\");
+        native.globals.variables.set(\\"f\\", {
+          kind: \\"const\\",
+          value: f
+        });
+      }
+    }
+    return lastStatementResult;
+  })();
+})();
+",
   "visualiseListResult": Array [],
 }
 `;
@@ -871,6 +1112,27 @@ Object {
   "parsedErrors": "Line 1: Error: head(xs) expects a pair as argument xs, but encountered undefined",
   "result": undefined,
   "resultStatus": "error",
+  "transpiled": "const native = $$NATIVE_STORAGE;
+const callIfFuncAndRightArgs = native.operators.get(\\"callIfFuncAndRightArgs\\");
+const boolOrErr = native.operators.get(\\"boolOrErr\\");
+const wrap = native.operators.get(\\"wrap\\");
+const unaryOp = native.operators.get(\\"unaryOp\\");
+const binaryOp = native.operators.get(\\"binaryOp\\");
+const throwIfTimeout = native.operators.get(\\"throwIfTimeout\\");
+const setProp = native.operators.get(\\"setProp\\");
+const getProp = native.operators.get(\\"getProp\\");
+let lastStatementResult = undefined;
+(( <globals redacted> ) => {
+  return (() => {
+    {
+      {
+        lastStatementResult = eval(\\"callIfFuncAndRightArgs(accumulate, 1, 0, wrap((x, y) => ({   isTail: false,   value: binaryOp(\\\\\\"+\\\\\\", x, y, 1, 21) }), \\\\\\"(x, y) => x + y\\\\\\"), [1, 2, 3]);\\");
+      }
+    }
+    return lastStatementResult;
+  })();
+})();
+",
   "visualiseListResult": Array [],
 }
 `;
@@ -900,20 +1162,41 @@ Object {
   "parsedErrors": "Line 1: Error: head(xs) expects a pair as argument xs, but encountered undefined",
   "result": undefined,
   "resultStatus": "error",
-  "visualiseListResult": Array [],
-}
-`;
-
-exports[`non-list error append: expectParsedError 1`] = `
-Object {
-  "alertResult": Array [],
-  "code": "append([1, 2, 3], list(1, 2, 3));",
-  "displayResult": Array [],
-  "errors": Array [
-    ExceptionError {
-      "error": [Error: head(xs) expects a pair as argument xs, but encountered [1, 2, 3]],
-      "location": SourceLocation {
-        "end": Position {
+  "transpiled": "const native = $$NATIVE_STORAGE;
+const callIfFuncAndRightArgs = native.operators.get(\\"callIfFuncAndRightArgs\\");
+const boolOrErr = native.operators.get(\\"boolOrErr\\");
+const wrap = native.operators.get(\\"wrap\\");
+const unaryOp = native.operators.get(\\"unaryOp\\");
+const binaryOp = native.operators.get(\\"binaryOp\\");
+const throwIfTimeout = native.operators.get(\\"throwIfTimeout\\");
+const setProp = native.operators.get(\\"setProp\\");
+const getProp = native.operators.get(\\"getProp\\");
+let lastStatementResult = undefined;
+(( <globals redacted> ) => {
+  return (() => {
+    {
+      {
+        lastStatementResult = eval(\\"callIfFuncAndRightArgs(accumulate, 1, 0, wrap((x, y) => ({   isTail: false,   value: binaryOp(\\\\\\"+\\\\\\", x, y, 1, 21) }), \\\\\\"(x, y) => x + y\\\\\\"), [1, 2, 3]);\\");
+      }
+    }
+    return lastStatementResult;
+  })();
+})();
+",
+  "visualiseListResult": Array [],
+}
+`;
+
+exports[`non-list error append: expectParsedError 1`] = `
+Object {
+  "alertResult": Array [],
+  "code": "append([1, 2, 3], list(1, 2, 3));",
+  "displayResult": Array [],
+  "errors": Array [
+    ExceptionError {
+      "error": [Error: head(xs) expects a pair as argument xs, but encountered [1, 2, 3]],
+      "location": SourceLocation {
+        "end": Position {
           "column": 32,
           "line": 1,
         },
@@ -929,6 +1212,27 @@ Object {
   "parsedErrors": "Line 1: Error: head(xs) expects a pair as argument xs, but encountered [1, 2, 3]",
   "result": undefined,
   "resultStatus": "error",
+  "transpiled": "const native = $$NATIVE_STORAGE;
+const callIfFuncAndRightArgs = native.operators.get(\\"callIfFuncAndRightArgs\\");
+const boolOrErr = native.operators.get(\\"boolOrErr\\");
+const wrap = native.operators.get(\\"wrap\\");
+const unaryOp = native.operators.get(\\"unaryOp\\");
+const binaryOp = native.operators.get(\\"binaryOp\\");
+const throwIfTimeout = native.operators.get(\\"throwIfTimeout\\");
+const setProp = native.operators.get(\\"setProp\\");
+const getProp = native.operators.get(\\"getProp\\");
+let lastStatementResult = undefined;
+(( <globals redacted> ) => {
+  return (() => {
+    {
+      {
+        lastStatementResult = eval(\\"callIfFuncAndRightArgs(append, 1, 0, [1, 2, 3], callIfFuncAndRightArgs(list, 1, 18, 1, 2, 3));\\");
+      }
+    }
+    return lastStatementResult;
+  })();
+})();
+",
   "visualiseListResult": Array [],
 }
 `;
@@ -987,6 +1291,27 @@ Object {
   "parsedErrors": "Line 1: Error: head(xs) expects a pair as argument xs, but encountered [1, 2, 3]",
   "result": undefined,
   "resultStatus": "error",
+  "transpiled": "const native = $$NATIVE_STORAGE;
+const callIfFuncAndRightArgs = native.operators.get(\\"callIfFuncAndRightArgs\\");
+const boolOrErr = native.operators.get(\\"boolOrErr\\");
+const wrap = native.operators.get(\\"wrap\\");
+const unaryOp = native.operators.get(\\"unaryOp\\");
+const binaryOp = native.operators.get(\\"binaryOp\\");
+const throwIfTimeout = native.operators.get(\\"throwIfTimeout\\");
+const setProp = native.operators.get(\\"setProp\\");
+const getProp = native.operators.get(\\"getProp\\");
+let lastStatementResult = undefined;
+(( <globals redacted> ) => {
+  return (() => {
+    {
+      {
+        lastStatementResult = eval(\\"callIfFuncAndRightArgs(filter, 1, 0, wrap(x => ({   isTail: false,   value: true }), \\\\\\"x => true\\\\\\"), [1, 2, 3]);\\");
+      }
+    }
+    return lastStatementResult;
+  })();
+})();
+",
   "visualiseListResult": Array [],
 }
 `;
@@ -1016,6 +1341,27 @@ Object {
   "parsedErrors": "Line 1: Error: for_each expects a list as argument xs, but encountered 1,2,3",
   "result": undefined,
   "resultStatus": "error",
+  "transpiled": "const native = $$NATIVE_STORAGE;
+const callIfFuncAndRightArgs = native.operators.get(\\"callIfFuncAndRightArgs\\");
+const boolOrErr = native.operators.get(\\"boolOrErr\\");
+const wrap = native.operators.get(\\"wrap\\");
+const unaryOp = native.operators.get(\\"unaryOp\\");
+const binaryOp = native.operators.get(\\"binaryOp\\");
+const throwIfTimeout = native.operators.get(\\"throwIfTimeout\\");
+const setProp = native.operators.get(\\"setProp\\");
+const getProp = native.operators.get(\\"getProp\\");
+let lastStatementResult = undefined;
+(( <globals redacted> ) => {
+  return (() => {
+    {
+      {
+        lastStatementResult = eval(\\"callIfFuncAndRightArgs(for_each, 1, 0, wrap(x => ({   isTail: false,   value: x }), \\\\\\"x => x\\\\\\"), [1, 2, 3]);\\");
+      }
+    }
+    return lastStatementResult;
+  })();
+})();
+",
   "visualiseListResult": Array [],
 }
 `;
@@ -1045,6 +1391,27 @@ Object {
   "parsedErrors": "Line 1: Error: head(xs) expects a pair as argument xs, but encountered [1, 2, 3]",
   "result": undefined,
   "resultStatus": "error",
+  "transpiled": "const native = $$NATIVE_STORAGE;
+const callIfFuncAndRightArgs = native.operators.get(\\"callIfFuncAndRightArgs\\");
+const boolOrErr = native.operators.get(\\"boolOrErr\\");
+const wrap = native.operators.get(\\"wrap\\");
+const unaryOp = native.operators.get(\\"unaryOp\\");
+const binaryOp = native.operators.get(\\"binaryOp\\");
+const throwIfTimeout = native.operators.get(\\"throwIfTimeout\\");
+const setProp = native.operators.get(\\"setProp\\");
+const getProp = native.operators.get(\\"getProp\\");
+let lastStatementResult = undefined;
+(( <globals redacted> ) => {
+  return (() => {
+    {
+      {
+        lastStatementResult = eval(\\"callIfFuncAndRightArgs(head, 1, 0, [1, 2, 3]);\\");
+      }
+    }
+    return lastStatementResult;
+  })();
+})();
+",
   "visualiseListResult": Array [],
 }
 `;
@@ -1074,6 +1441,27 @@ Object {
   "parsedErrors": "Line 1: Error: tail(xs) expects a pair as argument xs, but encountered [1, 2, 3]",
   "result": undefined,
   "resultStatus": "error",
+  "transpiled": "const native = $$NATIVE_STORAGE;
+const callIfFuncAndRightArgs = native.operators.get(\\"callIfFuncAndRightArgs\\");
+const boolOrErr = native.operators.get(\\"boolOrErr\\");
+const wrap = native.operators.get(\\"wrap\\");
+const unaryOp = native.operators.get(\\"unaryOp\\");
+const binaryOp = native.operators.get(\\"binaryOp\\");
+const throwIfTimeout = native.operators.get(\\"throwIfTimeout\\");
+const setProp = native.operators.get(\\"setProp\\");
+const getProp = native.operators.get(\\"getProp\\");
+let lastStatementResult = undefined;
+(( <globals redacted> ) => {
+  return (() => {
+    {
+      {
+        lastStatementResult = eval(\\"callIfFuncAndRightArgs(length, 1, 0, [1, 2, 3]);\\");
+      }
+    }
+    return lastStatementResult;
+  })();
+})();
+",
   "visualiseListResult": Array [],
 }
 `;
@@ -1103,6 +1491,27 @@ Object {
   "parsedErrors": "Line 1: Error: head(xs) expects a pair as argument xs, but encountered [1, 2, 3]",
   "result": undefined,
   "resultStatus": "error",
+  "transpiled": "const native = $$NATIVE_STORAGE;
+const callIfFuncAndRightArgs = native.operators.get(\\"callIfFuncAndRightArgs\\");
+const boolOrErr = native.operators.get(\\"boolOrErr\\");
+const wrap = native.operators.get(\\"wrap\\");
+const unaryOp = native.operators.get(\\"unaryOp\\");
+const binaryOp = native.operators.get(\\"binaryOp\\");
+const throwIfTimeout = native.operators.get(\\"throwIfTimeout\\");
+const setProp = native.operators.get(\\"setProp\\");
+const getProp = native.operators.get(\\"getProp\\");
+let lastStatementResult = undefined;
+(( <globals redacted> ) => {
+  return (() => {
+    {
+      {
+        lastStatementResult = eval(\\"callIfFuncAndRightArgs(map, 1, 0, wrap(x => ({   isTail: false,   value: x }), \\\\\\"x => x\\\\\\"), [1, 2, 3]);\\");
+      }
+    }
+    return lastStatementResult;
+  })();
+})();
+",
   "visualiseListResult": Array [],
 }
 `;
@@ -1132,6 +1541,27 @@ Object {
   "parsedErrors": "Line 1: Error: head(xs) expects a pair as argument xs, but encountered [1, 2, 3]",
   "result": undefined,
   "resultStatus": "error",
+  "transpiled": "const native = $$NATIVE_STORAGE;
+const callIfFuncAndRightArgs = native.operators.get(\\"callIfFuncAndRightArgs\\");
+const boolOrErr = native.operators.get(\\"boolOrErr\\");
+const wrap = native.operators.get(\\"wrap\\");
+const unaryOp = native.operators.get(\\"unaryOp\\");
+const binaryOp = native.operators.get(\\"binaryOp\\");
+const throwIfTimeout = native.operators.get(\\"throwIfTimeout\\");
+const setProp = native.operators.get(\\"setProp\\");
+const getProp = native.operators.get(\\"getProp\\");
+let lastStatementResult = undefined;
+(( <globals redacted> ) => {
+  return (() => {
+    {
+      {
+        lastStatementResult = eval(\\"callIfFuncAndRightArgs(member, 1, 0, 1, [1, 2, 3]);\\");
+      }
+    }
+    return lastStatementResult;
+  })();
+})();
+",
   "visualiseListResult": Array [],
 }
 `;
@@ -1161,6 +1591,27 @@ Object {
   "parsedErrors": "Line 1: Error: head(xs) expects a pair as argument xs, but encountered [1, 2, 3]",
   "result": undefined,
   "resultStatus": "error",
+  "transpiled": "const native = $$NATIVE_STORAGE;
+const callIfFuncAndRightArgs = native.operators.get(\\"callIfFuncAndRightArgs\\");
+const boolOrErr = native.operators.get(\\"boolOrErr\\");
+const wrap = native.operators.get(\\"wrap\\");
+const unaryOp = native.operators.get(\\"unaryOp\\");
+const binaryOp = native.operators.get(\\"binaryOp\\");
+const throwIfTimeout = native.operators.get(\\"throwIfTimeout\\");
+const setProp = native.operators.get(\\"setProp\\");
+const getProp = native.operators.get(\\"getProp\\");
+let lastStatementResult = undefined;
+(( <globals redacted> ) => {
+  return (() => {
+    {
+      {
+        lastStatementResult = eval(\\"callIfFuncAndRightArgs(remove, 1, 0, 1, [1, 2, 3]);\\");
+      }
+    }
+    return lastStatementResult;
+  })();
+})();
+",
   "visualiseListResult": Array [],
 }
 `;
@@ -1190,6 +1641,27 @@ Object {
   "parsedErrors": "Line 1: Error: head(xs) expects a pair as argument xs, but encountered [1, 2, 3]",
   "result": undefined,
   "resultStatus": "error",
+  "transpiled": "const native = $$NATIVE_STORAGE;
+const callIfFuncAndRightArgs = native.operators.get(\\"callIfFuncAndRightArgs\\");
+const boolOrErr = native.operators.get(\\"boolOrErr\\");
+const wrap = native.operators.get(\\"wrap\\");
+const unaryOp = native.operators.get(\\"unaryOp\\");
+const binaryOp = native.operators.get(\\"binaryOp\\");
+const throwIfTimeout = native.operators.get(\\"throwIfTimeout\\");
+const setProp = native.operators.get(\\"setProp\\");
+const getProp = native.operators.get(\\"getProp\\");
+let lastStatementResult = undefined;
+(( <globals redacted> ) => {
+  return (() => {
+    {
+      {
+        lastStatementResult = eval(\\"callIfFuncAndRightArgs(remove_all, 1, 0, 1, [1, 2, 3]);\\");
+      }
+    }
+    return lastStatementResult;
+  })();
+})();
+",
   "visualiseListResult": Array [],
 }
 `;
@@ -1219,6 +1691,27 @@ Object {
   "parsedErrors": "Line 1: Error: reverse(xs) expects a list as argument xs, but encountered 1,2,3",
   "result": undefined,
   "resultStatus": "error",
+  "transpiled": "const native = $$NATIVE_STORAGE;
+const callIfFuncAndRightArgs = native.operators.get(\\"callIfFuncAndRightArgs\\");
+const boolOrErr = native.operators.get(\\"boolOrErr\\");
+const wrap = native.operators.get(\\"wrap\\");
+const unaryOp = native.operators.get(\\"unaryOp\\");
+const binaryOp = native.operators.get(\\"binaryOp\\");
+const throwIfTimeout = native.operators.get(\\"throwIfTimeout\\");
+const setProp = native.operators.get(\\"setProp\\");
+const getProp = native.operators.get(\\"getProp\\");
+let lastStatementResult = undefined;
+(( <globals redacted> ) => {
+  return (() => {
+    {
+      {
+        lastStatementResult = eval(\\"callIfFuncAndRightArgs(reverse, 1, 0, [1, 2, 3]);\\");
+      }
+    }
+    return lastStatementResult;
+  })();
+})();
+",
   "visualiseListResult": Array [],
 }
 `;
@@ -1248,6 +1741,27 @@ Object {
   "parsedErrors": "Line 1: Error: set_head(xs,x) expects a pair as argument xs, but encountered [1, 2, 3]",
   "result": undefined,
   "resultStatus": "error",
+  "transpiled": "const native = $$NATIVE_STORAGE;
+const callIfFuncAndRightArgs = native.operators.get(\\"callIfFuncAndRightArgs\\");
+const boolOrErr = native.operators.get(\\"boolOrErr\\");
+const wrap = native.operators.get(\\"wrap\\");
+const unaryOp = native.operators.get(\\"unaryOp\\");
+const binaryOp = native.operators.get(\\"binaryOp\\");
+const throwIfTimeout = native.operators.get(\\"throwIfTimeout\\");
+const setProp = native.operators.get(\\"setProp\\");
+const getProp = native.operators.get(\\"getProp\\");
+let lastStatementResult = undefined;
+(( <globals redacted> ) => {
+  return (() => {
+    {
+      {
+        lastStatementResult = eval(\\"callIfFuncAndRightArgs(set_head, 1, 0, [1, 2, 3], 4);\\");
+      }
+    }
+    return lastStatementResult;
+  })();
+})();
+",
   "visualiseListResult": Array [],
 }
 `;
@@ -1277,6 +1791,27 @@ Object {
   "parsedErrors": "Line 1: Error: set_tail(xs,x) expects a pair as argument xs, but encountered [1, 2, 3]",
   "result": undefined,
   "resultStatus": "error",
+  "transpiled": "const native = $$NATIVE_STORAGE;
+const callIfFuncAndRightArgs = native.operators.get(\\"callIfFuncAndRightArgs\\");
+const boolOrErr = native.operators.get(\\"boolOrErr\\");
+const wrap = native.operators.get(\\"wrap\\");
+const unaryOp = native.operators.get(\\"unaryOp\\");
+const binaryOp = native.operators.get(\\"binaryOp\\");
+const throwIfTimeout = native.operators.get(\\"throwIfTimeout\\");
+const setProp = native.operators.get(\\"setProp\\");
+const getProp = native.operators.get(\\"getProp\\");
+let lastStatementResult = undefined;
+(( <globals redacted> ) => {
+  return (() => {
+    {
+      {
+        lastStatementResult = eval(\\"callIfFuncAndRightArgs(set_tail, 1, 0, [1, 2, 3], 4);\\");
+      }
+    }
+    return lastStatementResult;
+  })();
+})();
+",
   "visualiseListResult": Array [],
 }
 `;
@@ -1306,6 +1841,27 @@ Object {
   "parsedErrors": "Line 1: Error: tail(xs) expects a pair as argument xs, but encountered [1, 2, 3]",
   "result": undefined,
   "resultStatus": "error",
+  "transpiled": "const native = $$NATIVE_STORAGE;
+const callIfFuncAndRightArgs = native.operators.get(\\"callIfFuncAndRightArgs\\");
+const boolOrErr = native.operators.get(\\"boolOrErr\\");
+const wrap = native.operators.get(\\"wrap\\");
+const unaryOp = native.operators.get(\\"unaryOp\\");
+const binaryOp = native.operators.get(\\"binaryOp\\");
+const throwIfTimeout = native.operators.get(\\"throwIfTimeout\\");
+const setProp = native.operators.get(\\"setProp\\");
+const getProp = native.operators.get(\\"getProp\\");
+let lastStatementResult = undefined;
+(( <globals redacted> ) => {
+  return (() => {
+    {
+      {
+        lastStatementResult = eval(\\"callIfFuncAndRightArgs(tail, 1, 0, [1, 2, 3]);\\");
+      }
+    }
+    return lastStatementResult;
+  })();
+})();
+",
   "visualiseListResult": Array [],
 }
 `;
diff --git a/src/stdlib/__tests__/list.ts b/src/stdlib/__tests__/list.ts
index 2a694380..e2bf7f9f 100644
--- a/src/stdlib/__tests__/list.ts
+++ b/src/stdlib/__tests__/list.ts
@@ -7,7 +7,7 @@ test('list creates list', () => {
     function f() { return 1; }
     list(1, 'a string ""', () => a, f, true, 3.14);
   `,
-    { chapter: 2 }
+    { chapter: 2, native: true }
   ).toMatchInlineSnapshot(`
 Array [
   1,
@@ -283,7 +283,7 @@ test('non-list error head', () => {
     stripIndent`
     head([1, 2, 3]);
   `,
-    { chapter: 3 }
+    { chapter: 3, native: true }
   ).toMatchInlineSnapshot(
     `"Line 1: Error: head(xs) expects a pair as argument xs, but encountered [1, 2, 3]"`
   )
@@ -294,7 +294,7 @@ test('non-list error tail', () => {
     stripIndent`
     tail([1, 2, 3]);
   `,
-    { chapter: 3 }
+    { chapter: 3, native: true }
   ).toMatchInlineSnapshot(
     `"Line 1: Error: tail(xs) expects a pair as argument xs, but encountered [1, 2, 3]"`
   )
@@ -305,7 +305,7 @@ test('non-list error length', () => {
     stripIndent`
     length([1, 2, 3]);
   `,
-    { chapter: 3 }
+    { chapter: 3, native: true }
   ).toMatchInlineSnapshot(
     `"Line 1: Error: tail(xs) expects a pair as argument xs, but encountered [1, 2, 3]"`
   )
@@ -316,7 +316,7 @@ test('non-list error map', () => {
     stripIndent`
     map(x=>x, [1, 2, 3]);
   `,
-    { chapter: 3 }
+    { chapter: 3, native: true }
   ).toMatchInlineSnapshot(
     `"Line 1: Error: head(xs) expects a pair as argument xs, but encountered [1, 2, 3]"`
   )
@@ -327,7 +327,7 @@ test('non-list error for_each', () => {
     stripIndent`
     for_each(x=>x, [1, 2, 3]);
   `,
-    { chapter: 3 }
+    { chapter: 3, native: true }
   ).toMatchInlineSnapshot(
     `"Line 1: Error: for_each expects a list as argument xs, but encountered 1,2,3"`
   )
@@ -338,7 +338,7 @@ test('non-list error reverse', () => {
     stripIndent`
     reverse([1, 2, 3]);
   `,
-    { chapter: 3 }
+    { chapter: 3, native: true }
   ).toMatchInlineSnapshot(
     `"Line 1: Error: reverse(xs) expects a list as argument xs, but encountered 1,2,3"`
   )
@@ -349,7 +349,7 @@ test('non-list error append', () => {
     stripIndent`
     append([1, 2, 3], list(1, 2, 3));
   `,
-    { chapter: 3 }
+    { chapter: 3, native: true }
   ).toMatchInlineSnapshot(
     `"Line 1: Error: head(xs) expects a pair as argument xs, but encountered [1, 2, 3]"`
   )
@@ -360,7 +360,7 @@ test('non-list error member', () => {
     stripIndent`
     member(1, [1, 2, 3]);
   `,
-    { chapter: 3 }
+    { chapter: 3, native: true }
   ).toMatchInlineSnapshot(
     `"Line 1: Error: head(xs) expects a pair as argument xs, but encountered [1, 2, 3]"`
   )
@@ -371,7 +371,7 @@ test('non-list error remove', () => {
     stripIndent`
     remove(1, [1, 2, 3]);
   `,
-    { chapter: 3 }
+    { chapter: 3, native: true }
   ).toMatchInlineSnapshot(
     `"Line 1: Error: head(xs) expects a pair as argument xs, but encountered [1, 2, 3]"`
   )
@@ -382,7 +382,7 @@ test('non-list error remove_all', () => {
     stripIndent`
     remove_all(1, [1, 2, 3]);
   `,
-    { chapter: 3 }
+    { chapter: 3, native: true }
   ).toMatchInlineSnapshot(
     `"Line 1: Error: head(xs) expects a pair as argument xs, but encountered [1, 2, 3]"`
   )
@@ -404,7 +404,7 @@ test('non-list error filter', () => {
     stripIndent`
     filter(x => true, [1, 2, 3]);
   `,
-    { chapter: 3 }
+    { chapter: 3, native: true }
   ).toMatchInlineSnapshot(
     `"Line 1: Error: head(xs) expects a pair as argument xs, but encountered [1, 2, 3]"`
   )
@@ -415,7 +415,7 @@ test('non-list error accumulate', () => {
     stripIndent`
     accumulate((x, y) => x + y, [1, 2, 3]);
   `,
-    { chapter: 3 }
+    { chapter: 3, native: true }
   ).toMatchInlineSnapshot(
     `"Line 1: Error: head(xs) expects a pair as argument xs, but encountered undefined"`
   )
@@ -426,7 +426,7 @@ test('non-list error accumulate', () => {
     stripIndent`
     accumulate((x, y) => x + y, [1, 2, 3]);
   `,
-    { chapter: 3 }
+    { chapter: 3, native: true }
   ).toMatchInlineSnapshot(
     `"Line 1: Error: head(xs) expects a pair as argument xs, but encountered undefined"`
   )
@@ -437,7 +437,7 @@ test('non-list error set_head', () => {
     stripIndent`
     set_head([1, 2, 3], 4);
   `,
-    { chapter: 3 }
+    { chapter: 3, native: true }
   ).toMatchInlineSnapshot(
     `"Line 1: Error: set_head(xs,x) expects a pair as argument xs, but encountered [1, 2, 3]"`
   )
@@ -448,7 +448,7 @@ test('non-list error set_tail', () => {
     stripIndent`
     set_tail([1, 2, 3], 4);
   `,
-    { chapter: 3 }
+    { chapter: 3, native: true }
   ).toMatchInlineSnapshot(
     `"Line 1: Error: set_tail(xs,x) expects a pair as argument xs, but encountered [1, 2, 3]"`
   )
@@ -459,7 +459,7 @@ test('bad number error build_list', () => {
     stripIndent`
     build_list(-1, x => x);
   `,
-    { chapter: 2 }
+    { chapter: 2, native: true }
   ).toMatchInlineSnapshot(
     `"Line 1: Error: build_list(n, fun) expects a positive integer as argument n, but encountered -1"`
   )
@@ -470,7 +470,7 @@ test('bad number error build_list', () => {
     stripIndent`
     build_list(1.5, x => x);
   `,
-    { chapter: 2 }
+    { chapter: 2, native: true }
   ).toMatchInlineSnapshot(
     `"Line 1: Error: build_list(n, fun) expects a positive integer as argument n, but encountered 1.5"`
   )
@@ -481,7 +481,7 @@ test('bad number error build_list', () => {
     stripIndent`
     build_list('1', x => x);
   `,
-    { chapter: 2 }
+    { chapter: 2, native: true }
   ).toMatchInlineSnapshot(
     `"Line 1: Error: build_list(n, fun) expects a positive integer as argument n, but encountered 1"`
   )
@@ -492,7 +492,7 @@ test('bad number error enum_list', () => {
     stripIndent`
     enum_list('1', '5');
   `,
-    { chapter: 2 }
+    { chapter: 2, native: true }
   ).toMatchInlineSnapshot(
     `"Line 1: Error: enum_list(start, end) expects a number as argument start, but encountered 1"`
   )
@@ -503,7 +503,7 @@ test('bad number error enum_list', () => {
     stripIndent`
     enum_list('1', 5);
   `,
-    { chapter: 2 }
+    { chapter: 2, native: true }
   ).toMatchInlineSnapshot(
     `"Line 1: Error: enum_list(start, end) expects a number as argument start, but encountered 1"`
   )
@@ -514,7 +514,7 @@ test('bad number error enum_list', () => {
     stripIndent`
     enum_list(1, '5');
   `,
-    { chapter: 2 }
+    { chapter: 2, native: true }
   ).toMatchInlineSnapshot(
     `"Line 1: Error: enum_list(start, end) expects a number as argument start, but encountered 5"`
   )
@@ -525,7 +525,7 @@ test('bad index error list_ref', () => {
     stripIndent`
     list_ref(list(1, 2, 3), 3);
   `,
-    { chapter: 2 }
+    { chapter: 2, native: true }
   ).toMatchInlineSnapshot(
     `"Line 1: Error: head(xs) expects a pair as argument xs, but encountered null"`
   )
@@ -536,7 +536,7 @@ test('bad index error list_ref', () => {
     stripIndent`
     list_ref(list(1, 2, 3), -1);
   `,
-    { chapter: 2 }
+    { chapter: 2, native: true }
   ).toMatchInlineSnapshot(
     `"Line 1: Error: list_ref(xs, n) expects a positive integer as argument n, but encountered -1"`
   )
@@ -547,7 +547,7 @@ test('bad index error list_ref', () => {
     stripIndent`
     list_ref(list(1, 2, 3), 1.5);
   `,
-    { chapter: 2 }
+    { chapter: 2, native: true }
   ).toMatchInlineSnapshot(
     `"Line 1: Error: list_ref(xs, n) expects a positive integer as argument n, but encountered 1.5"`
   )
@@ -558,7 +558,7 @@ test('bad index error list_ref', () => {
     stripIndent`
     list_ref(list(1, 2, 3), '1');
   `,
-    { chapter: 2 }
+    { chapter: 2, native: true }
   ).toMatchInlineSnapshot(
     `"Line 1: Error: list_ref(xs, n) expects a positive integer as argument n, but encountered 1"`
   )