diff --git a/src/intrinsics/ecma262/Error.js b/src/intrinsics/ecma262/Error.js index 4d8afbfa8..9f2af66a8 100644 --- a/src/intrinsics/ecma262/Error.js +++ b/src/intrinsics/ecma262/Error.js @@ -11,10 +11,18 @@ import type { Realm } from "../../realm.js"; import type { LexicalEnvironment } from "../../environment.js"; -import { ObjectValue, FunctionValue, NativeFunctionValue, StringValue } from "../../values/index.js"; +import { + AbstractValue, + ObjectValue, + FunctionValue, + NativeFunctionValue, + StringValue, + Value, +} from "../../values/index.js"; import { Get } from "../../methods/index.js"; import { Create, Properties, To } from "../../singletons.js"; import invariant from "../../invariant.js"; +import buildExpressionTemplate from "../../utils/builder.js"; import type { BabelNodeSourceLocation } from "@babel/types"; export default function(realm: Realm): NativeFunctionValue { @@ -76,23 +84,23 @@ export function describeLocation( return location; } -function buildStack(realm: Realm, context: ObjectValue) { +const buildStackTemplateSrc = 'A + (B ? ": " + B : "") + C'; +const buildStackTemplate = buildExpressionTemplate(buildStackTemplateSrc); + +function buildStack(realm: Realm, context: ObjectValue): Value { invariant(context.$ErrorData); let stack = context.$ErrorData.contextStack; if (!stack) return realm.intrinsics.undefined; let lines = []; - let header = ""; + let header = To.ToStringPartial(realm, Get(realm, context, "name")); - header += To.ToStringPartial(realm, Get(realm, context, "name")); - - let msg = Get(realm, context, "message"); - if (!msg.mightBeUndefined()) { - msg = To.ToStringPartial(realm, msg); - if (msg) header += `: ${msg}`; + let message = Get(realm, context, "message"); + if (!message.mightBeUndefined()) { + message = To.ToStringValue(realm, message); } else { - msg.throwIfNotConcrete(); + message.throwIfNotConcrete(); } for (let executionContext of stack.reverse()) { @@ -106,8 +114,17 @@ function buildStack(realm: Realm, context: ObjectValue) { ); if (locString !== undefined) lines.push(locString); } + let footer = `\n ${lines.join("\n ")}`; - return new StringValue(realm, `${header}\n ${lines.join("\n ")}`); + return message instanceof StringValue + ? new StringValue(realm, `${header}${message.value ? `: ${message.value}` : ""}${footer}`) + : AbstractValue.createFromTemplate( + realm, + buildStackTemplate, + StringValue, + [new StringValue(realm, header), message, new StringValue(realm, footer)], + buildStackTemplateSrc + ); } export function build(name: string, realm: Realm, inheritError?: boolean = true): NativeFunctionValue { @@ -122,15 +139,6 @@ export function build(name: string, realm: Realm, inheritError?: boolean = true) locationData: undefined, }; - // Build a text description of the stack. - let stackDesc = { - value: buildStack(realm, O), - enumerable: false, - configurable: true, - writable: true, - }; - Properties.DefinePropertyOrThrow(realm, O, "stack", stackDesc); - // 3. If message is not undefined, then if (!message.mightBeUndefined()) { // a. Let msg be ? ToString(message). @@ -150,6 +158,15 @@ export function build(name: string, realm: Realm, inheritError?: boolean = true) message.throwIfNotConcrete(); } + // Build a text description of the stack. + let stackDesc = { + value: buildStack(realm, O), + enumerable: false, + configurable: true, + writable: true, + }; + Properties.DefinePropertyOrThrow(realm, O, "stack", stackDesc); + // 4. Return O. return O; });