Skip to content

Commit bd64a44

Browse files
committed
[PortsJS] Impelemnts safe asString to prevent circular structures to break the interpreter
1 parent c20f2a0 commit bd64a44

File tree

1 file changed

+24
-9
lines changed

1 file changed

+24
-9
lines changed

ports-js/scheme.js

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,33 @@ const _let = Sym('let');
2828

2929

3030
export function asString(expr) {
31+
try {
3132
if (typeof expr === 'symbol') {
3233
return Symbol.keyFor(expr) || expr.toString();
3334
} else if (Array.isArray(expr)) {
34-
return `(${expr.map(asString).join(' ')})`;
35+
return `(${expr.map((ea) => asStringSafe(ea)).join(' ')})`;
3536
} else if (expr === true) {
3637
return '#t';
3738
} else if (expr === false) {
3839
return '#f';
3940
} else {
4041
return expr.toString();
4142
}
43+
} catch (e) {
44+
return '#<error>';
45+
}
46+
}
4247

48+
function asStringSafe(expr, covered_exprs = new Set()) {
49+
if (covered_exprs.has(expr)) {
50+
return '#<circular>';
51+
}
52+
covered_exprs.add(expr);
53+
if (Array.isArray(expr)) {
54+
return `(${expr.map((ea) => asStringSafe(ea, covered_exprs)).join(' ')})`;
55+
} else {
56+
return asString(expr);
57+
}
4358
}
4459

4560
// Procedure class
@@ -65,7 +80,7 @@ export class Env extends Map {
6580
this.set(parms, Array.from(args));
6681
} else {
6782
if (args.length !== parms.length) {
68-
throw new Error(`Expected ${parms}, given ${args}`);
83+
throw new Error(`Expected ${asString(parms)}, given ${asString(args)}`);
6984
}
7085
parms.forEach((param, i) => this.set(param, args[i]));
7186
}
@@ -271,17 +286,17 @@ function addGlobals(env) {
271286
env.set(Sym("raise"), (e) => { throw e; });
272287

273288
// String operations
274-
env.set(Sym('string-append'), (...strs) => strs.reduce((acc, s) => acc + String(s), ''));
275-
env.set(Sym('string-split'), (s, sep) => String(s).split(sep));
276-
env.set(Sym('string-replace'), (old, newStr, s) => String(s).replace(old, newStr));
289+
env.set(Sym('string-append'), (...strs) => strs.reduce((acc, s) => acc + asString(s), ''));
290+
env.set(Sym('string-split'), (s, sep) => asString(s).split(sep));
291+
env.set(Sym('string-replace'), (old, newStr, s) => asString(s).replace(old, newStr));
277292
env.set(Sym('string-index'), (str, substr) => {
278293
const idx = str.indexOf(substr);
279294
return idx !== -1 ? idx : false;
280295
});
281-
env.set(Sym('string-upcase'), s => String(s).toUpperCase());
282-
env.set(Sym('string-downcase'), s => String(s).toLowerCase());
283-
env.set(Sym('string-trim'), s => String(s).trim());
284-
env.set(Sym('number->string'), x => String(x));
296+
env.set(Sym('string-upcase'), s => asString(s).toUpperCase());
297+
env.set(Sym('string-downcase'), s => asString(s).toLowerCase());
298+
env.set(Sym('string-trim'), s => asString(s).trim());
299+
env.set(Sym('number->string'), x => asString(x));
285300

286301
// Type checking
287302
env.set(Sym('null?'), x => x === null || x === undefined || (Array.isArray(x) && x.length === 0));

0 commit comments

Comments
 (0)