From 3d3842003cf8b59b301fd62dbe00b3637fac1860 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Sun, 13 Oct 2024 06:51:17 -0700 Subject: [PATCH] Support setting error stack JSValue directly --- .../JavaScriptCore/runtime/ErrorInstance.cpp | 44 +++++++++++++++---- Source/JavaScriptCore/runtime/ErrorInstance.h | 2 + Source/JavaScriptCore/runtime/VM.h | 21 ++++++--- 3 files changed, 53 insertions(+), 14 deletions(-) diff --git a/Source/JavaScriptCore/runtime/ErrorInstance.cpp b/Source/JavaScriptCore/runtime/ErrorInstance.cpp index 83ae5423cc5ef..06ce8ca43d96c 100644 --- a/Source/JavaScriptCore/runtime/ErrorInstance.cpp +++ b/Source/JavaScriptCore/runtime/ErrorInstance.cpp @@ -28,6 +28,7 @@ #include "JSCInlines.h" #include "ParseInt.h" #include "StackFrame.h" +#include "VM.h" #include namespace JSC { @@ -107,6 +108,15 @@ String appendSourceToErrorMessage(CodeBlock* codeBlock, BytecodeIndex bytecodeIn return appender(message, codeBlock->source().provider()->getRange(start, stop), type, ErrorInstance::FoundApproximateSource); } +void ErrorInstance::setStackFrames(VM& vm, WTF::Vector&& stackFrames) +{ + std::unique_ptr> stackTrace = makeUnique>(WTFMove(stackFrames)); + + Locker locker { cellLock() }; + m_stackTrace = WTFMove(stackTrace); + vm.writeBarrier(this); +} + void ErrorInstance::captureStackTrace(VM& vm, JSGlobalObject* globalObject, size_t framesToSkip, bool append) { { @@ -127,7 +137,7 @@ void ErrorInstance::captureStackTrace(VM& vm, JSGlobalObject* globalObject, size remaining = std::min(remaining, m_stackTrace->size()); if (remaining > 0) { ASSERT(m_stackTrace->size() >= remaining); - stackTrace->append(std::span { m_stackTrace->data(), remaining } ); + stackTrace->append(std::span { m_stackTrace->data(), remaining }); } } @@ -310,14 +320,8 @@ void ErrorInstance::computeErrorInfo(VM& vm, bool allocationAllowed) if (m_stackTrace && !m_stackTrace->isEmpty()) { auto& fn = vm.onComputeErrorInfo(); - if (fn && allocationAllowed) { - // This function may call `globalObject` or potentially even execute arbitrary JS code. - // We cannot gurantee the lifetime of this stack trace to continue to be valid. - // We have to move it out of the ErrorInstance. - WTF::Vector stackTrace = WTFMove(*m_stackTrace.get()); - m_stackString = fn(vm, stackTrace, m_lineColumn.line, m_lineColumn.column, m_sourceURL, allocationAllowed ? this : nullptr); - } else if (fn && !allocationAllowed) { - m_stackString = fn(vm, *m_stackTrace.get(), m_lineColumn.line, m_lineColumn.column, m_sourceURL, nullptr); + if (fn) { + m_stackString = fn(vm, *m_stackTrace.get(), m_lineColumn.line, m_lineColumn.column, m_sourceURL); } else { getLineColumnAndSource(vm, m_stackTrace.get(), m_lineColumn, m_sourceURL); m_stackString = Interpreter::stackTraceAsString(vm, *m_stackTrace.get()); @@ -331,6 +335,28 @@ bool ErrorInstance::materializeErrorInfoIfNeeded(VM& vm) if (m_errorInfoMaterialized) return false; +#if USE(BUN_JSC_ADDITIONS) + + auto& fn = vm.onComputeErrorInfoJSValue(); + if (fn && m_stackTrace && !m_stackTrace->isEmpty()) { + DeferGCForAWhile deferGC(vm); + + JSValue stack = fn(vm, *m_stackTrace.get(), m_lineColumn.line, m_lineColumn.column, m_sourceURL, this); + m_stackTrace = nullptr; + + auto attributes = static_cast(PropertyAttribute::DontEnum); + + putDirect(vm, vm.propertyNames->line, jsNumber(m_lineColumn.line), attributes); + putDirect(vm, vm.propertyNames->column, jsNumber(m_lineColumn.column), attributes); + if (!m_sourceURL.isEmpty()) + putDirect(vm, vm.propertyNames->sourceURL, jsString(vm, WTFMove(m_sourceURL)), attributes); + + putDirect(vm, vm.propertyNames->stack, stack, attributes); + m_errorInfoMaterialized = true; + return true; + } + +#endif computeErrorInfo(vm, true); if (!m_stackString.isNull()) { diff --git a/Source/JavaScriptCore/runtime/ErrorInstance.h b/Source/JavaScriptCore/runtime/ErrorInstance.h index 625194c9eb004..d142940adb83a 100644 --- a/Source/JavaScriptCore/runtime/ErrorInstance.h +++ b/Source/JavaScriptCore/runtime/ErrorInstance.h @@ -104,6 +104,8 @@ class ErrorInstance : public JSNonFinalObject { unsigned column() const { return m_lineColumn.column; } void setColumn(unsigned column) { m_lineColumn.column = column; } + void setStackFrames(VM& vm, WTF::Vector&& stackFrames); + #endif JS_EXPORT_PRIVATE String sanitizedToString(JSGlobalObject*); diff --git a/Source/JavaScriptCore/runtime/VM.h b/Source/JavaScriptCore/runtime/VM.h index 97c4498b76d86..9187644bbe2bf 100644 --- a/Source/JavaScriptCore/runtime/VM.h +++ b/Source/JavaScriptCore/runtime/VM.h @@ -161,6 +161,11 @@ class Waiter; constexpr bool validateDFGDoesGC = ENABLE_DFG_DOES_GC_VALIDATION; +#if USE(BUN_JSC_ADDITIONS) +using ErrorInfoFunction = WTF::Function& stackTrace, unsigned& line, unsigned& column, String& sourceURL)>; +using ErrorInfoFunctionJSValue = WTF::Function& stackTrace, unsigned& line, unsigned& column, String& sourceURL, JSC::JSObject*)>; +#endif + #if ENABLE(FTL_JIT) namespace FTL { class Thunks; @@ -934,10 +939,13 @@ class VM : public ThreadSafeRefCounted, public DoublyLinkedListNode { WTF::Function& computeLineColumnWithSourcemap() { return m_computeLineColumnWithSourcemap; } void setComputeLineColumnWithSourcemap(WTF::Function&& func) { m_computeLineColumnWithSourcemap = WTFMove(func); } - - WTF::Function& stackTrace, unsigned &line, unsigned &column, String& sourceURL, JSC::JSObject*)>& onComputeErrorInfo() { return m_onComputeErrorInfo; } - void setOnComputeErrorInfo(WTF::Function& stackTrace, unsigned &line, unsigned &column, String& sourceURL, JSC::JSObject*)>&& func) { m_onComputeErrorInfo = WTFMove(func); } - + +#if USE(BUN_JSC_ADDITIONS) + const ErrorInfoFunction& onComputeErrorInfo() const { return m_onComputeErrorInfo; } + const ErrorInfoFunctionJSValue& onComputeErrorInfoJSValue() const { return m_onComputeErrorInfoJSValue; } + void setOnComputeErrorInfo(ErrorInfoFunction&& func) { m_onComputeErrorInfo = WTFMove(func); } + void setOnComputeErrorInfoJSValue(ErrorInfoFunctionJSValue&& func) { m_onComputeErrorInfoJSValue = WTFMove(func); } +#endif void finalizeSynchronousJSExecution() { ASSERT(currentThreadIsHoldingAPILock()); @@ -1135,8 +1143,11 @@ class VM : public ThreadSafeRefCounted, public DoublyLinkedListNode { Vector> m_aboutToBeNotifiedRejectedPromises; WTF::Function m_onEachMicrotaskTick; - WTF::Function& stackTrace, unsigned &line, unsigned &column, String& sourceURL, JSC::JSObject*)> m_onComputeErrorInfo; +#if USE(BUN_JSC_ADDITIONS) + ErrorInfoFunction m_onComputeErrorInfo; + ErrorInfoFunctionJSValue m_onComputeErrorInfoJSValue; WTF::Function m_computeLineColumnWithSourcemap; +#endif uintptr_t m_currentWeakRefVersion { 0 }; bool m_hasSideData { false };