Compare commits

...

3 Commits

Author SHA1 Message Date
Jarred Sumner
7fd570914a Fix 2025-01-06 19:39:15 -08:00
Kyle Carberry
26eddcd99c Merge branch 'main' into main 2025-01-06 16:13:19 -05:00
Kyle Carberry
44368d07d7 Fix missing source URL when Error.prepareStackTrace is set
`source_url` was empty when eval'ing:

```js
const fn = new Function("throw new Error('example')" + "\n//# sourceURL=/example.js");
```

This copies code added in:
https://github.com/oven-sh/bun/pull/13073
2025-01-04 14:16:02 -05:00
3 changed files with 75 additions and 21 deletions

View File

@@ -80,6 +80,7 @@ public:
void setLineNumber(OrdinalNumber lineNumber) { m_lineNumber = lineNumber; }
void setColumnNumber(OrdinalNumber columnNumber) { m_columnNumber = columnNumber; }
void setSourceURL(JSC::VM& vm, JSC::JSValue sourceURL) { m_sourceURL.set(vm, this, sourceURL); }
void formatAsString(JSC::VM& vm, JSC::JSGlobalObject* globalObject, WTF::StringBuilder& sb);

View File

@@ -685,29 +685,55 @@ static JSValue computeErrorInfoWithPrepareStackTrace(JSC::VM& vm, Zig::GlobalObj
// We need to sourcemap it if it's a GlobalObject.
if (globalObject == lexicalGlobalObject) {
size_t framesCount = stackTrace.size();
ZigStackFrame remappedFrames[64];
framesCount = framesCount > 64 ? 64 : framesCount;
for (int i = 0; i < framesCount; i++) {
remappedFrames[i] = {};
remappedFrames[i].source_url = Bun::toStringRef(lexicalGlobalObject, stackTrace.at(i).sourceURL());
if (JSCStackFrame::SourcePositions* sourcePositions = stackTrace.at(i).getSourcePositions()) {
remappedFrames[i].position.line_zero_based = sourcePositions->line.zeroBasedInt();
remappedFrames[i].position.column_zero_based = sourcePositions->column.zeroBasedInt();
} else {
remappedFrames[i].position.line_zero_based = -1;
remappedFrames[i].position.column_zero_based = -1;
for (int i = 0; i < stackTrace.size(); i++) {
ZigStackFrame frame = {};
String sourceURLForFrame = stackFrames.at(i).sourceURL(vm);
if (sourceURLForFrame.isEmpty()) {
const auto& source = stackFrames.at(i).codeBlock()->source();
if (!source.isNull()) {
auto* provider = source.provider();
// I'm not 100% sure we should show sourceURLDirective here.
if (!provider->sourceURLDirective().isEmpty()) {
sourceURLForFrame = provider->sourceURLDirective();
} else if (!provider->sourceURL().isEmpty()) {
sourceURLForFrame = provider->sourceURL();
} else {
const auto& origin = provider->sourceOrigin();
if (!origin.isNull()) {
sourceURLForFrame = origin.string();
}
}
}
}
}
Bun__remapStackFramePositions(globalObject, remappedFrames, framesCount);
if (JSCStackFrame::SourcePositions* sourcePositions = stackTrace.at(i).getSourcePositions()) {
frame.position.line_zero_based = sourcePositions->line.zeroBasedInt();
frame.position.column_zero_based = sourcePositions->column.zeroBasedInt();
} else {
frame.position.line_zero_based = -1;
frame.position.column_zero_based = -1;
}
for (size_t i = 0; i < framesCount; i++) {
JSC::JSValue callSiteValue = callSites.at(i);
if (remappedFrames[i].remapped) {
CallSite* callSite = JSC::jsCast<CallSite*>(callSiteValue);
callSite->setColumnNumber(remappedFrames[i].position.column());
callSite->setLineNumber(remappedFrames[i].position.line());
if (!sourceURLForFrame.isEmpty()) {
frame.source_url = Bun::toStringRef(sourceURLForFrame);
// This ensures the lifetime of the sourceURL is accounted for correctly
Bun__remapStackFramePositions(globalObject, &frame, 1);
sourceURLForFrame = frame.source_url.toWTFString();
}
auto* callsite = jsCast<CallSite*>(callSites.at(i));
if (!sourceURLForFrame.isEmpty())
callsite->setSourceURL(vm, jsString(vm, sourceURLForFrame));
if (frame.remapped) {
callsite->setLineNumber(frame.position.line());
callsite->setColumnNumber(frame.position.column());
}
}
}

View File

@@ -1,6 +1,6 @@
import { nativeFrameForTesting } from "bun:internal-for-testing";
import { afterEach, expect, test } from "bun:test";
import { noInline } from "bun:jsc";
import { afterEach, expect, mock, test } from "bun:test";
const origPrepareStackTrace = Error.prepareStackTrace;
afterEach(() => {
Error.prepareStackTrace = origPrepareStackTrace;
@@ -697,3 +697,30 @@ test("Error.prepareStackTrace propagates exceptions", () => {
]),
).toThrow("hi");
});
test("CallFrame.p.getScriptNameOrSourceURL inside eval", () => {
let prevPrepareStackTrace = Error.prepareStackTrace;
const prepare = mock((e, s) => {
expect(s[0].getScriptNameOrSourceURL()).toBe("https://zombo.com/welcome-to-zombo.js");
expect(s[1].getScriptNameOrSourceURL()).toBe("https://zombo.com/welcome-to-zombo.js");
expect(s[2].getScriptNameOrSourceURL()).toBe("[native code]");
expect(s[3].getScriptNameOrSourceURL()).toBe(import.meta.path);
expect(s[4].getScriptNameOrSourceURL()).toBe(import.meta.path);
});
Error.prepareStackTrace = prepare;
let evalScript = `(function() {
throw new Error("bad error!");
})() //# sourceURL=https://zombo.com/welcome-to-zombo.js`;
try {
function insideAFunction() {
eval(evalScript);
}
insideAFunction();
} catch (e) {
e.stack;
}
Error.prepareStackTrace = prevPrepareStackTrace;
expect(prepare).toHaveBeenCalledTimes(1);
});