diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/bun.js/bindings/ZigGlobalObject.cpp | 188 | ||||
| -rw-r--r-- | src/bun.js/bindings/bindings.cpp | 28 | ||||
| -rw-r--r-- | src/bun.js/javascript.zig | 22 | 
3 files changed, 200 insertions, 38 deletions
| diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index b27e0aafc..4b4edf097 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -219,7 +219,9 @@ extern "C" void JSCInitialize(const char* envp[], size_t envc, void (*onCrash)(c          JSC::Options::useJITCage() = false;          JSC::Options::useShadowRealm() = true;          JSC::Options::useResizableArrayBuffer() = true; +#ifdef BUN_DEBUG          JSC::Options::showPrivateScriptsInStackTraces() = true; +#endif          JSC::Options::useSetMethods() = true;          /* @@ -312,6 +314,123 @@ extern "C" void JSCInitialize(const char* envp[], size_t envc, void (*onCrash)(c  }  extern "C" void* Bun__getVM(); +extern "C" JSGlobalObject* Bun__getDefaultGlobal(); + +static String computeErrorInfoWithoutPrepareStackTrace(JSC::VM& vm, Vector<StackFrame>& stackTrace, unsigned& line, unsigned& column, String& sourceURL, JSObject* errorInstance) +{ +    if (!errorInstance) { +        return String(); +    } + +    Zig::GlobalObject* globalObject = jsDynamicCast<Zig::GlobalObject*>(errorInstance->globalObject()); +    if (!globalObject) { +        // Happens in node:vm +        globalObject = jsDynamicCast<Zig::GlobalObject*>(Bun__getDefaultGlobal()); +    } + +    WTF::String name = "Error"_s; +    WTF::String message; + +    if (errorInstance) { +        // Note that we are not allowed to allocate memory in here. It's called inside a finalizer. +        if (auto* instance = jsDynamicCast<ErrorInstance*>(errorInstance)) { +            name = instance->sanitizedNameString(globalObject); +            message = instance->sanitizedMessageString(globalObject); +        } +    } + +    WTF::StringBuilder sb; + +    if (!name.isEmpty()) { +        sb.append(name); +        sb.append(": "_s); +    } + +    if (!message.isEmpty()) { +        sb.append(message); +    } + +    if (stackTrace.isEmpty()) { +        return sb.toString(); +    } + +    if ((!message.isEmpty() || !name.isEmpty())) { +        sb.append("\n"_s); +    } + +    size_t framesCount = stackTrace.size(); +    ZigStackFrame remappedFrames[framesCount]; +    bool hasSet = false; +    for (size_t i = 0; i < framesCount; i++) { +        StackFrame& frame = stackTrace.at(i); + +        sb.append("    at "_s); + +        WTF::String functionName = frame.functionName(vm); + +        if (auto codeblock = frame.codeBlock()) { +            if (codeblock->isConstructor()) { +                sb.append("new "_s); +            } + +            // TODO: async +        } + +        if (functionName.isEmpty()) { +            sb.append("<anonymous>"_s); +        } else { +            sb.append(functionName); +        } + +        sb.append(" ("_s); + +        if (frame.hasLineAndColumnInfo()) { +            unsigned int thisLine = 0; +            unsigned int thisColumn = 0; +            frame.computeLineAndColumn(thisLine, thisColumn); +            remappedFrames[i].position.line = thisLine; +            remappedFrames[i].position.column_start = thisColumn; +            remappedFrames[i].source_url = Zig::toZigString(frame.sourceURL(vm)); + +            // This ensures the lifetime of the sourceURL is accounted for correctly +            Bun__remapStackFramePositions(globalObject, remappedFrames + i, 1); + +            if (!hasSet) { +                hasSet = true; +                line = thisLine; +                column = thisColumn; +                sourceURL = frame.sourceURL(vm); + +                if (errorInstance) { +                    if (remappedFrames[i].remapped) { +                        errorInstance->putDirect(vm, Identifier::fromString(vm, "originalLine"_s), jsNumber(thisLine), 0); +                        errorInstance->putDirect(vm, Identifier::fromString(vm, "originalColumn"_s), jsNumber(thisColumn), 0); +                    } +                } +            } + +            sb.append(frame.sourceURL(vm)); +            sb.append(":"_s); +            sb.append(remappedFrames[i].position.line); +            sb.append(":"_s); +            sb.append(remappedFrames[i].position.column_start); +        } else { +            sb.append("native"_s); +        } +        sb.append(")"_s); + +        if (i != framesCount - 1) { +            sb.append("\n"_s); +        } +    } + +    return sb.toString(); +} + +static String computeErrorInfo(JSC::VM& vm, Vector<StackFrame>& stackTrace, unsigned& line, unsigned& column, String& sourceURL, JSObject* errorInstance) +{ +    return computeErrorInfoWithoutPrepareStackTrace(vm, stackTrace, line, column, sourceURL, errorInstance); +}  extern "C" JSC__JSGlobalObject* Zig__GlobalObject__create(JSClassRef* globalObjectClass, int count,      void* console_client) @@ -329,6 +448,9 @@ extern "C" JSC__JSGlobalObject* Zig__GlobalObject__create(JSClassRef* globalObje      Zig::GlobalObject* globalObject = Zig::GlobalObject::create(vm, Zig::GlobalObject::createStructure(vm, JSC::JSGlobalObject::create(vm, JSC::JSGlobalObject::createStructure(vm, JSC::jsNull())), JSC::jsNull()));      globalObject->setConsole(globalObject);      globalObject->isThreadLocalDefaultGlobalObject = true; +    globalObject->setStackTraceLimit(DEFAULT_ERROR_STACK_TRACE_LIMIT); // Node.js defaults to 10 +    vm.setOnComputeErrorInfo(computeErrorInfo); +      if (count > 0) {          globalObject->installAPIGlobals(globalObjectClass, count, vm);      } @@ -2585,7 +2707,32 @@ JSC::JSValue GlobalObject::formatStackTrace(JSC::VM& vm, JSC::JSGlobalObject* le  extern "C" EncodedJSValue JSPasswordObject__create(JSC::JSGlobalObject*, bool); -JSC_DECLARE_HOST_FUNCTION(errorConstructorFuncCaptureStackTrace); +JSC_DEFINE_HOST_FUNCTION(errorConstructorFuncAppendStackTrace, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame)) +{ +    GlobalObject* globalObject = reinterpret_cast<GlobalObject*>(lexicalGlobalObject); +    JSC::VM& vm = globalObject->vm(); +    auto scope = DECLARE_THROW_SCOPE(vm); + +    JSC::ErrorInstance* source = jsDynamicCast<JSC::ErrorInstance*>(callFrame->argument(0)); +    JSC::ErrorInstance* destination = jsDynamicCast<JSC::ErrorInstance*>(callFrame->argument(1)); + +    if (!source || !destination) { +        throwTypeError(lexicalGlobalObject, scope, "First & second argument must be an Error object"_s); +        return JSC::JSValue::encode(jsUndefined()); +    } + +    if (!destination->stackTrace()) { +        destination->captureStackTrace(vm, globalObject, 1); +    } + +    if (source->stackTrace()) { +        destination->stackTrace()->appendVector(*source->stackTrace()); +        source->stackTrace()->clear(); +    } + +    return JSC::JSValue::encode(jsUndefined()); +} +  JSC_DEFINE_HOST_FUNCTION(errorConstructorFuncCaptureStackTrace, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))  {      GlobalObject* globalObject = reinterpret_cast<GlobalObject*>(lexicalGlobalObject); @@ -2600,18 +2747,15 @@ JSC_DEFINE_HOST_FUNCTION(errorConstructorFuncCaptureStackTrace, (JSC::JSGlobalOb      JSC::JSObject* errorObject = objectArg.asCell()->getObject();      JSC::JSValue caller = callFrame->argument(1); +    // We cannot use our ErrorInstance::captureStackTrace() fast path here unfortunately. +    // We need to return these CallSite array objects which means we need to create them      JSValue errorValue = lexicalGlobalObject->get(lexicalGlobalObject, vm.propertyNames->Error);      auto* errorConstructor = jsDynamicCast<JSC::JSObject*>(errorValue); - -    size_t stackTraceLimit = DEFAULT_ERROR_STACK_TRACE_LIMIT; -    if (JSC::JSValue stackTraceLimitProp = errorConstructor->getIfPropertyExists(lexicalGlobalObject, vm.propertyNames->stackTraceLimit)) { -        if (stackTraceLimitProp.isNumber()) { -            stackTraceLimit = std::min(std::max(static_cast<size_t>(stackTraceLimitProp.toIntegerOrInfinity(lexicalGlobalObject)), 0ul), 2048ul); -            if (stackTraceLimit == 0) { -                stackTraceLimit = 2048; -            } -        } +    size_t stackTraceLimit = globalObject->stackTraceLimit().value(); +    if (stackTraceLimit == 0) { +        stackTraceLimit = DEFAULT_ERROR_STACK_TRACE_LIMIT;      } +      JSCStackTrace stackTrace = JSCStackTrace::captureCurrentJSStackTrace(globalObject, callFrame, stackTraceLimit, caller);      // Create an (uninitialized) array for our "call sites" @@ -2672,9 +2816,20 @@ JSC_DEFINE_HOST_FUNCTION(errorConstructorFuncCaptureStackTrace, (JSC::JSGlobalOb          errorObject->deleteProperty(lexicalGlobalObject, vm.propertyNames->stack);      }      if (formattedStackTrace.isUndefinedOrNull()) { -        errorObject->putDirect(vm, vm.propertyNames->stack, jsUndefined(), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); -    } else { -        errorObject->putDirect(vm, vm.propertyNames->stack, formattedStackTrace, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); +        formattedStackTrace = JSC::jsUndefined(); +    } + +    errorObject->putDirect(vm, vm.propertyNames->stack, formattedStackTrace, 0); + +    if (!(caller && caller.isObject())) { +        if (auto* instance = jsDynamicCast<JSC::ErrorInstance*>(errorObject)) { +            // we make a separate copy of the StackTrace unfortunately so that we +            // can later console.log it without losing the info +            // +            // This is not good. We should remove this in the future as it strictly makes this function +            // already slower than necessary. +            instance->captureStackTrace(vm, globalObject, 1, false); +        }      }      RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSValue {})); @@ -3118,11 +3273,8 @@ void GlobalObject::finishCreation(VM& vm)      RELEASE_ASSERT(classInfo());      JSC::JSObject* errorConstructor = this->errorConstructor(); -    errorConstructor->putDirectNativeFunctionWithoutTransition(vm, this, JSC::Identifier::fromString(vm, "captureStackTrace"_s), 2, errorConstructorFuncCaptureStackTrace, ImplementationVisibility::Public, JSC::NoIntrinsic, PropertyAttribute::DontEnum | 0); - -    // JSC default is 100 -    errorConstructor->putDirect(vm, vm.propertyNames->stackTraceLimit, jsNumber(DEFAULT_ERROR_STACK_TRACE_LIMIT), JSC::PropertyAttribute::DontEnum | 0); - +    errorConstructor->putDirectNativeFunction(vm, this, JSC::Identifier::fromString(vm, "captureStackTrace"_s), 2, errorConstructorFuncCaptureStackTrace, ImplementationVisibility::Public, JSC::NoIntrinsic, PropertyAttribute::DontEnum | 0); +    errorConstructor->putDirectNativeFunction(vm, this, JSC::Identifier::fromString(vm, "appendStackTrace"_s), 2, errorConstructorFuncAppendStackTrace, ImplementationVisibility::Private, JSC::NoIntrinsic, PropertyAttribute::DontEnum | 0);      JSC::JSValue console = this->get(this, JSC::Identifier::fromString(vm, "console"_s));      JSC::JSObject* consoleObject = console.getObject();      consoleObject->putDirectBuiltinFunction(vm, this, vm.propertyNames->asyncIteratorSymbol, consoleObjectAsyncIteratorCodeGenerator(vm), PropertyAttribute::Builtin | PropertyAttribute::DontDelete); diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index 3f3d82dc4..68aef29dd 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -3528,12 +3528,13 @@ static void fromErrorInstance(ZigException* except, JSC::JSGlobalObject* global,      JSC::JSValue val)  {      JSC::JSObject* obj = JSC::jsDynamicCast<JSC::JSObject*>(val); +    JSC::VM& vm = global->vm();      bool getFromSourceURL = false;      if (stackTrace != nullptr && stackTrace->size() > 0) { -        populateStackTrace(global->vm(), *stackTrace, &except->stack); +        populateStackTrace(vm, *stackTrace, &except->stack);      } else if (err->stackTrace() != nullptr && err->stackTrace()->size() > 0) { -        populateStackTrace(global->vm(), *err->stackTrace(), &except->stack); +        populateStackTrace(vm, *err->stackTrace(), &except->stack);      } else {          getFromSourceURL = true;      } @@ -3546,7 +3547,7 @@ static void fromErrorInstance(ZigException* except, JSC::JSGlobalObject* global,      }      if (except->code == SYNTAX_ERROR_CODE) {          except->message = Zig::toZigString(err->sanitizedMessageString(global)); -    } else if (JSC::JSValue message = obj->getIfPropertyExists(global, global->vm().propertyNames->message)) { +    } else if (JSC::JSValue message = obj->getIfPropertyExists(global, vm.propertyNames->message)) {          except->message = Zig::toZigString(message, global); @@ -3556,7 +3557,7 @@ static void fromErrorInstance(ZigException* except, JSC::JSGlobalObject* global,      except->name = Zig::toZigString(err->sanitizedNameString(global));      except->runtime_type = err->runtimeTypeForCause(); -    auto clientData = WebCore::clientData(global->vm()); +    auto clientData = WebCore::clientData(vm);      if (except->code != SYNTAX_ERROR_CODE) {          if (JSC::JSValue syscall = obj->getIfPropertyExists(global, clientData->builtinNames().syscallPublicName())) { @@ -3571,7 +3572,7 @@ static void fromErrorInstance(ZigException* except, JSC::JSGlobalObject* global,              except->path = Zig::toZigString(path, global);          } -        if (JSC::JSValue fd = obj->getIfPropertyExists(global, Identifier::fromString(global->vm(), "fd"_s))) { +        if (JSC::JSValue fd = obj->getIfPropertyExists(global, Identifier::fromString(vm, "fd"_s))) {              if (fd.isAnyInt()) {                  except->fd = fd.toInt32(global);              } @@ -3583,17 +3584,17 @@ static void fromErrorInstance(ZigException* except, JSC::JSGlobalObject* global,      }      if (getFromSourceURL) { -        if (JSC::JSValue sourceURL = obj->getIfPropertyExists(global, global->vm().propertyNames->sourceURL)) { +        if (JSC::JSValue sourceURL = obj->getIfPropertyExists(global, vm.propertyNames->sourceURL)) {              except->stack.frames_ptr[0].source_url = Zig::toZigString(sourceURL, global); -            if (JSC::JSValue column = obj->getIfPropertyExists(global, global->vm().propertyNames->column)) { +            if (JSC::JSValue column = obj->getIfPropertyExists(global, vm.propertyNames->column)) {                  except->stack.frames_ptr[0].position.column_start = column.toInt32(global);              } -            if (JSC::JSValue line = obj->getIfPropertyExists(global, global->vm().propertyNames->line)) { +            if (JSC::JSValue line = obj->getIfPropertyExists(global, vm.propertyNames->line)) {                  except->stack.frames_ptr[0].position.line = line.toInt32(global); -                if (JSC::JSValue lineText = obj->getIfPropertyExists(global, JSC::Identifier::fromString(global->vm(), "lineText"_s))) { +                if (JSC::JSValue lineText = obj->getIfPropertyExists(global, JSC::Identifier::fromString(vm, "lineText"_s))) {                      if (JSC::JSString* jsStr = lineText.toStringOrNull(global)) {                          auto str = jsStr->value(global);                          except->stack.source_lines_ptr[0] = Zig::toZigString(str); @@ -3603,7 +3604,9 @@ static void fromErrorInstance(ZigException* except, JSC::JSGlobalObject* global,                      }                  }              } +              except->stack.frames_len = 1; +            except->stack.frames_ptr[0].remapped = obj->hasProperty(global, JSC::Identifier::fromString(vm, "originalLine"_s));          }      } @@ -3654,7 +3657,12 @@ void exceptionFromString(ZigException* except, JSC::JSValue value, JSC::JSGlobal          if (JSC::JSValue line = obj->getIfPropertyExists(global, global->vm().propertyNames->line)) {              if (line) { -                except->stack.frames_ptr[0].position.line = line.toInt32(global); +                // TODO: don't sourcemap it twice +                if (auto originalLine = obj->getIfPropertyExists(global, JSC::Identifier::fromString(global->vm(), "originalLine"_s))) { +                    except->stack.frames_ptr[0].position.line = originalLine.toInt32(global); +                } else { +                    except->stack.frames_ptr[0].position.line = line.toInt32(global); +                }                  except->stack.frames_len = 1;              }          } diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig index cb1a50f1d..5c58eff60 100644 --- a/src/bun.js/javascript.zig +++ b/src/bun.js/javascript.zig @@ -1981,19 +1981,21 @@ pub const VirtualMachine = struct {      }      pub fn remapStackFramePositions(this: *VirtualMachine, frames: [*]JSC.ZigStackFrame, frames_count: usize) void { -        var i: usize = 0; -        while (i < frames_count) : (i += 1) { -            if (frames[i].position.isInvalid()) continue; +        for (frames[0..frames_count]) |*frame| { +            if (frame.position.isInvalid() or frame.remapped) continue; +            var sourceURL = frame.source_url.toSlice(bun.default_allocator); +            defer sourceURL.deinit(); +              if (this.source_mappings.resolveMapping( -                frames[i].source_url.slice(), -                @max(frames[i].position.line, 0), -                @max(frames[i].position.column_start, 0), +                sourceURL.slice(), +                @max(frame.position.line, 0), +                @max(frame.position.column_start, 0),              )) |mapping| { -                frames[i].position.line = mapping.original.lines; -                frames[i].position.column_start = mapping.original.columns; -                frames[i].remapped = true; +                frame.position.line = mapping.original.lines; +                frame.position.column_start = mapping.original.columns; +                frame.remapped = true;              } else { -                frames[i].remapped = true; +                frame.remapped = true;              }          }      } | 
