Skip to content

Commit f443ccc

Browse files
authored
1718 stepper built in function errors not caught and crashing the frontend (#1719)
* Refactor: BuiltInFunctionError to catch Built-In Function Error * Refactor: Change Error to BuiltInFunctionError * Fix: Tests
1 parent 0a284d2 commit f443ccc

File tree

4 files changed

+20
-13
lines changed

4 files changed

+20
-13
lines changed

src/errors/errors.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import { RuntimeSourceError } from './runtimeSourceError'
1010

1111
//Wrap build-in function error in SourceError
1212
export class BuiltInFunctionError extends RuntimeSourceError {
13-
constructor(node: Node, private explanation: String) {
14-
super(node)
13+
constructor(private explanation: String) {
14+
super(undefined)
1515
this.explanation = explanation
1616
}
1717

src/stepper/__tests__/__snapshots__/stepper.ts.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5507,12 +5507,12 @@ math_abs(-1);
55075507

55085508
exports[`Test catching errors from built in function Incorrect number of arguments 1`] = `
55095509
"Start of evaluation
5510-
"
5510+
Expected 2 arguments, but got 1"
55115511
`;
55125512

55135513
exports[`Test catching errors from built in function Incorrect type of argument for math function 1`] = `
55145514
"Start of evaluation
5515-
"
5515+
Math functions must be called with number arguments"
55165516
`;
55175517

55185518
exports[`Test catching errors from built in function Incorrect type of arguments for module function 1`] = `

src/stepper/lib.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as es from 'estree'
33
import * as misc from '../stdlib/misc'
44
import { Context, substituterNodes } from '../types'
55
import * as ast from '../utils/ast/astCreator'
6+
import { BuiltInFunctionError } from '../errors/errors'
67
import { nodeToValue, nodeToValueWithContext, valueToExpression } from './converter'
78
import { codify } from './stepper'
89
import { isBuiltinFunction, isNumber } from './util'
@@ -30,13 +31,13 @@ export function stringify(val: substituterNodes): es.Literal {
3031
// defineBuiltin(context, 'error(str)', misc.error_message)
3132
export function error(val: substituterNodes, str?: substituterNodes) {
3233
const output = (str === undefined ? '' : str + ' ') + stringify(val)
33-
throw new Error(output)
34+
throw new BuiltInFunctionError(output)
3435
}
3536

3637
// defineBuiltin(context, 'prompt(str)', prompt)
3738
export function prompt(str: substituterNodes): es.Literal {
3839
if (str.type !== 'Literal' || typeof str.value !== 'string') {
39-
throw new Error('Argument to error must be a string.')
40+
throw new BuiltInFunctionError('Argument to error must be a string.')
4041
}
4142
const result = window.prompt(str.value as string)
4243
return ast.literal((result ? result : null) as string)
@@ -99,9 +100,9 @@ export function parse_int(str: substituterNodes, radix: substituterNodes): es.Ex
99100
export function evaluateMath(mathFn: string, ...args: substituterNodes[]): es.Expression {
100101
const fn = Math[mathFn.split('_')[1]]
101102
if (!fn) {
102-
throw new Error(`Math function ${mathFn} not found.`)
103+
throw new BuiltInFunctionError(`Math function ${mathFn} not found.`)
103104
} else if (args.some(arg => !isNumber(arg))) {
104-
throw new Error(`Math functions must be called with number arguments`)
105+
throw new BuiltInFunctionError(`Math functions must be called with number arguments`)
105106
}
106107
const jsArgs = args.map(nodeToValue)
107108
return valueToExpression(fn(...jsArgs))
@@ -115,7 +116,7 @@ export function evaluateModuleFunction(
115116
): es.Expression {
116117
const fn = context.runtime.environments[0].head[moduleFn]
117118
if (!fn) {
118-
throw new Error(`Module function ${moduleFn} not found.`)
119+
throw new BuiltInFunctionError(`Module function ${moduleFn} not found.`)
119120
}
120121
const jsArgs = args.map(arg => nodeToValueWithContext(arg, context))
121122
return valueToExpression(fn(...jsArgs), context)
@@ -126,7 +127,7 @@ export function evaluateModuleFunction(
126127
// defineBuiltin(context, 'pair(left, right)', list.pair)
127128
export function pair(left: substituterNodes, right: substituterNodes): es.ArrayExpression {
128129
if (left == null || right == null) {
129-
throw new Error(
130+
throw new BuiltInFunctionError(
130131
//Count the number of arguments that are not undefined
131132
`Expected 2 arguments, but got ${[left, right].filter(x => x != undefined).length}`
132133
)
@@ -136,21 +137,24 @@ export function pair(left: substituterNodes, right: substituterNodes): es.ArrayE
136137

137138
// defineBuiltin(context, 'is_pair(val)', list.is_pair)
138139
export function is_pair(val: substituterNodes): es.Literal {
140+
if (val == null) {
141+
throw new BuiltInFunctionError('Expected 1 argument, but got 0')
142+
}
139143
return ast.literal(val.type === 'ArrayExpression' && val.elements.length === 2)
140144
}
141145

142146
// defineBuiltin(context, 'head(xs)', list.head)
143147
export function head(xs: substituterNodes): es.Expression {
144148
if (is_pair(xs).value === false) {
145-
throw new Error(`${codify(xs)} is not a pair`)
149+
throw new BuiltInFunctionError(`${codify(xs)} is not a pair`)
146150
}
147151
return (xs as es.ArrayExpression).elements[0] as es.Expression
148152
}
149153

150154
// defineBuiltin(context, 'tail(xs)', list.tail)
151155
export function tail(xs: substituterNodes): es.Expression {
152156
if (is_pair(xs).value === false) {
153-
throw new Error(`${codify(xs)} is not a pair`)
157+
throw new BuiltInFunctionError(`${codify(xs)} is not a pair`)
154158
}
155159
return (xs as es.ArrayExpression).elements[1] as es.Expression
156160
}

src/stepper/util.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@ import { Context } from '..'
44
import * as errors from '../errors/errors'
55
import { RuntimeSourceError } from '../errors/runtimeSourceError'
66
import { BlockExpression, Environment, Node, substituterNodes, Value } from '../types'
7+
import { UNKNOWN_LOCATION } from '../constants'
78
import * as builtin from './lib'
89

910
export function prettyPrintError(error: RuntimeSourceError): string {
10-
return `Line ${error.location.start.line}: ${error.explain()}`
11+
return error.location == UNKNOWN_LOCATION
12+
? error.explain()
13+
: `Line ${error.location.start.line}: ${error.explain()}`
1114
}
1215

1316
export function isBuiltinFunction(node: substituterNodes): boolean {

0 commit comments

Comments
 (0)