From f6d8e2a08686d86b23177264be0120c615c75c28 Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Tue, 11 Aug 2026 04:36:43 +0000 Subject: [PATCH] ErrorInstance: record that addErrorInfo() attached a parser location (hasParseLocation) Bun's stack formatter renders a synthetic "at (url:line)" frame for SyntaxErrors whose line/sourceURL were recorded by the parser via addErrorInfo(). Until now it recognized those errors by the sourceURL being set, which is also true of structured-cloned errors (created with their original's sourceURL) and of errors whose frames the GC finalizer already flushed to a string (computeErrorInfo records the first frame's URL), so giving either of those new frames with Error.captureStackTrace() produced a bogus frame. isParseError() cannot serve as the discriminator: it is also set for eval-code syntax errors, which never go through addErrorInfo() and so record no location, and toErrorObject() sets it only after addErrorInfo() has already materialized the stack through the host hook. Add a Bun-only bit that addErrorInfo() sets right where it records the location, before materializing, and expose it as hasParseLocation(). --- Source/JavaScriptCore/runtime/Error.cpp | 3 +++ Source/JavaScriptCore/runtime/ErrorInstance.cpp | 3 +++ Source/JavaScriptCore/runtime/ErrorInstance.h | 9 +++++++++ 3 files changed, 15 insertions(+) diff --git a/Source/JavaScriptCore/runtime/Error.cpp b/Source/JavaScriptCore/runtime/Error.cpp index b5514f770a289..1cc8c72a85add 100644 --- a/Source/JavaScriptCore/runtime/Error.cpp +++ b/Source/JavaScriptCore/runtime/Error.cpp @@ -262,6 +262,9 @@ JSObject* addErrorInfo(VM& vm, JSObject* error, int line, const SourceCode& sour if (!sourceURL.isEmpty()) { errorInstance->setSourceURL(sourceURL); } + + // materializeErrorInfoIfNeeded() formats the stack through the host, which reads this. + errorInstance->setHasParseLocation(); #endif errorInstance->materializeErrorInfoIfNeeded(vm); diff --git a/Source/JavaScriptCore/runtime/ErrorInstance.cpp b/Source/JavaScriptCore/runtime/ErrorInstance.cpp index 8cb916db573db..6f4afd6ef084f 100644 --- a/Source/JavaScriptCore/runtime/ErrorInstance.cpp +++ b/Source/JavaScriptCore/runtime/ErrorInstance.cpp @@ -48,6 +48,9 @@ ErrorInstance::ErrorInstance(VM& vm, Structure* structure, ErrorType errorType) #if ENABLE(WEBASSEMBLY) , m_catchableFromWasm(true) #endif // ENABLE(WEBASSEMBLY) +#if USE(BUN_JSC_ADDITIONS) + , m_hasParseLocation(false) +#endif { } diff --git a/Source/JavaScriptCore/runtime/ErrorInstance.h b/Source/JavaScriptCore/runtime/ErrorInstance.h index c28ce88fd7914..05d1bc01ac860 100644 --- a/Source/JavaScriptCore/runtime/ErrorInstance.h +++ b/Source/JavaScriptCore/runtime/ErrorInstance.h @@ -116,6 +116,12 @@ class ErrorInstance : public JSNonFinalObject { void setStackFrames(VM& vm, WTF::Vector&& stackFrames); bool hasMaterializedErrorInfo() const { return m_errorInfoMaterialized; } + // Whether line()/sourceURL() were recorded by addErrorInfo() for a source the parser rejected, as + // opposed to copied from another error (structured clone) or derived from the stack frames. Unlike + // isParseError(), this is not set for parse errors that record no location (eval code), and it is + // set before the stack is materialized, so the host's stack formatter can rely on it. + bool hasParseLocation() const { return m_hasParseLocation; } + void setHasParseLocation() { m_hasParseLocation = true; } #endif JS_EXPORT_PRIVATE String sanitizedToString(JSGlobalObject*); @@ -172,6 +178,9 @@ class ErrorInstance : public JSNonFinalObject { #if ENABLE(WEBASSEMBLY) bool m_catchableFromWasm : 1; #endif +#if USE(BUN_JSC_ADDITIONS) + bool m_hasParseLocation : 1; +#endif }; String appendSourceToErrorMessage(CodeBlock*, BytecodeIndex, const String&, RuntimeType, ErrorInstance::SourceAppender);