diff options
27 files changed, 1744 insertions, 325 deletions
diff --git a/.vscode/launch.json b/.vscode/launch.json index 590185982..41b9d64af 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -53,6 +53,20 @@ { "type": "lldb", "request": "launch", + "name": "Eval Error", + "program": "${workspaceFolder}/build/debug/macos-x86_64/spjs", + "args": [ + "error.js", + "--resolve=dev", + "--outdir=outcss" + // "--public-url=https://localhost:9000/" + ], + "cwd": "${workspaceFolder}", + "console": "internalConsole" + }, + { + "type": "lldb", + "request": "launch", "name": "Eval", "program": "${workspaceFolder}/build/debug/macos-x86_64/spjs", "args": [ @@ -223,14 +223,13 @@ pub fn build(b: *std.build.Builder) void { translate_c.out_basename = "headers"; translate_c.output_dir = b.pathFromRoot("src/javascript/jsc/bindings/"); headers_step.dependOn(&translate_c.step); - headers_zig_file = b.pathFromRoot("src/javascript/jsc/bindings/headers.zig"); original_make_fn = headers_step.makeFn; headers_step.makeFn = HeadersMaker.make; b.default_step.dependOn(&exe.step); - var steps = [_]*std.build.LibExeObjStep{ exe, javascript, typings_exe }; + var steps = [_]*std.build.LibExeObjStep{ exe, javascript, typings_exe, headers_exec }; for (steps) |step| { step.linkLibC(); diff --git a/src/bundler.zig b/src/bundler.zig index b31fb8308..d2488be89 100644 --- a/src/bundler.zig +++ b/src/bundler.zig @@ -539,8 +539,20 @@ pub fn NewBundler(cache_files: bool) type { // Since we trim the prefixes, we must also compare the package name pub fn sortJavascriptModuleByPath(ctx: *GenerateNodeModuleBundle, a: Api.JavascriptBundledModule, b: Api.JavascriptBundledModule) bool { - return switch (std.mem.order(u8, ctx.metadataStringPointer(ctx.package_list.items[a.package_id].name), ctx.metadataStringPointer(ctx.package_list.items[b.package_id].name))) { - .eq => std.mem.order(u8, ctx.metadataStringPointer(a.path), ctx.metadataStringPointer(b.path)) == .lt, + return switch (std.mem.order( + u8, + ctx.metadataStringPointer( + ctx.package_list.items[a.package_id].name, + ), + ctx.metadataStringPointer( + ctx.package_list.items[b.package_id].name, + ), + )) { + .eq => std.mem.order( + u8, + ctx.metadataStringPointer(a.path), + ctx.metadataStringPointer(b.path), + ) == .lt, .lt => true, else => false, }; diff --git a/src/css_scanner.zig b/src/css_scanner.zig index d26fbf1a1..e39515292 100644 --- a/src/css_scanner.zig +++ b/src/css_scanner.zig @@ -316,7 +316,13 @@ pub const Scanner = struct { return text; } - pub fn next(scanner: *Scanner, comptime import_behavior: ImportBehavior, comptime WriterType: type, writer: WriterType, writeChunk: (fn (ctx: WriterType, Chunk) anyerror!void)) !void { + pub fn next( + scanner: *Scanner, + comptime import_behavior: ImportBehavior, + comptime WriterType: type, + writer: WriterType, + writeChunk: (fn (ctx: WriterType, Chunk) anyerror!void), + ) !void { scanner.has_newline_before = scanner.end == 0; scanner.has_delimiter_before = false; scanner.step(); diff --git a/src/global.zig b/src/global.zig index daa30e676..496136a29 100644 --- a/src/global.zig +++ b/src/global.zig @@ -62,6 +62,10 @@ pub const Output = struct { return source.error_stream.writer(); } + pub fn errorStream() Source.StreamType { + return source.error_stream; + } + pub fn writer() WriterType { return source.stream.writer(); } @@ -134,7 +138,7 @@ pub const Output = struct { // <d> - dim // </r> - reset // <r> - reset - fn _pretty(comptime fmt: string, args: anytype, comptime printer: anytype, comptime is_enabled: bool) void { + pub fn prettyFmt(comptime fmt: string, comptime is_enabled: bool) string { comptime var new_fmt: [fmt.len * 4]u8 = undefined; comptime var new_fmt_i: usize = 0; const ED = comptime "\x1b["; @@ -205,7 +209,7 @@ pub const Output = struct { is_reset = true; break :color_picker ""; } else { - @compileError("Invalid color name passed:" ++ color_name); + @compileError("Invalid color name passed: " ++ color_name); } }; var orig = new_fmt_i; @@ -233,18 +237,19 @@ pub const Output = struct { }, } }; - printer(new_fmt[0..new_fmt_i], args); + + return comptime new_fmt[0..new_fmt_i]; } - pub fn prettyWithPrinter(comptime fmt: string, args: anytype, printer: anytype, comptime l: Level) void { + pub fn prettyWithPrinter(comptime fmt: string, args: anytype, comptime printer: anytype, comptime l: Level) void { if (comptime l == .Warn) { if (level == .Error) return; } if (enable_ansi_colors) { - _pretty(fmt, args, printer, true); + printer(comptime prettyFmt(fmt, true), args); } else { - _pretty(fmt, args, printer, false); + printer(comptime prettyFmt(fmt, false), args); } } @@ -254,9 +259,9 @@ pub const Output = struct { pub fn prettyln(comptime fmt: string, args: anytype) void { if (enable_ansi_colors) { - _pretty(fmt, args, println, true); + println(comptime prettyFmt(fmt, true), args); } else { - _pretty(fmt, args, println, false); + println(comptime prettyFmt(fmt, false), args); } } diff --git a/src/javascript/jsc/JavascriptCore.zig b/src/javascript/jsc/JavascriptCore.zig index f5299cd08..25bb40c5a 100644 --- a/src/javascript/jsc/JavascriptCore.zig +++ b/src/javascript/jsc/JavascriptCore.zig @@ -18,9 +18,9 @@ pub const JSPropertyNameArrayRef = ?*struct_OpaqueJSPropertyNameArray; pub const struct_OpaqueJSPropertyNameAccumulator = generic; pub const JSPropertyNameAccumulatorRef = ?*struct_OpaqueJSPropertyNameAccumulator; pub const JSTypedArrayBytesDeallocator = ?fn (?*c_void, ?*c_void) callconv(.C) void; -pub const struct_OpaqueJSValue = generic; -pub const JSValueRef = ?*struct_OpaqueJSValue; -pub const JSObjectRef = ?*struct_OpaqueJSValue; +pub const OpaqueJSValue = opaque {}; +pub const JSValueRef = ?*OpaqueJSValue; +pub const JSObjectRef = ?*OpaqueJSValue; pub extern fn JSEvaluateScript(ctx: JSContextRef, script: JSStringRef, thisObject: JSObjectRef, sourceURL: JSStringRef, startingLineNumber: c_int, exception: ExceptionRef) JSValueRef; pub extern fn JSCheckScriptSyntax(ctx: JSContextRef, script: JSStringRef, sourceURL: JSStringRef, startingLineNumber: c_int, exception: ExceptionRef) bool; pub extern fn JSGarbageCollect(ctx: JSContextRef) void; @@ -242,7 +242,6 @@ pub const OpaqueJSString = struct_OpaqueJSString; pub const OpaqueJSClass = struct_OpaqueJSClass; pub const OpaqueJSPropertyNameArray = struct_OpaqueJSPropertyNameArray; pub const OpaqueJSPropertyNameAccumulator = struct_OpaqueJSPropertyNameAccumulator; -pub const OpaqueJSValue = struct_OpaqueJSValue; // const JSProcessID = ; pub extern fn JSRemoteInspectorDisableAutoStart() void; diff --git a/src/javascript/jsc/WebKit b/src/javascript/jsc/WebKit -Subproject 4eb8a5a13ba6f023d9dddf975996052a6f8b5fe +Subproject e17b749830a4e3faf87f751630fdcc149906ee9 diff --git a/src/javascript/jsc/base.zig b/src/javascript/jsc/base.zig index 7bd9b96a4..6f39349a9 100644 --- a/src/javascript/jsc/base.zig +++ b/src/javascript/jsc/base.zig @@ -1,7 +1,12 @@ pub const js = @import("./JavaScriptCore.zig"); const std = @import("std"); pub usingnamespace @import("../../global.zig"); -const javascript = @import("./javascript.zig"); +usingnamespace @import("./javascript.zig"); +usingnamespace @import("./webcore/response.zig"); + +const TaggedPointerTypes = @import("../../tagged_pointer.zig"); +const TaggedPointerUnion = TaggedPointerTypes.TaggedPointerUnion; + pub const ExceptionValueRef = [*c]js.JSValueRef; pub const JSValueRef = js.JSValueRef; @@ -26,10 +31,10 @@ pub const To = struct { ) js.JSValueRef, ) js.JSObjectRef { var function = js.JSObjectMakeFunctionWithCallback(ctx, name, Callback(ZigContextType, callback).rfn); - _ = js.JSObjectSetPrivate( + std.debug.assert(js.JSObjectSetPrivate( function, - @ptrCast(*c_void, @alignCast(@alignOf(*c_void), zig)), - ); + JSPrivateDataPtr.init(zig).ptr(), + )); return function; } @@ -44,12 +49,8 @@ pub const To = struct { pub fn rfn( object: js.JSObjectRef, ) callconv(.C) void { - var object_ptr_ = js.JSObjectGetPrivate(object); - if (object_ptr_ == null) return; - return ctxfn( - @ptrCast(*ZigContextType, @alignCast(@alignOf(*ZigContextType), object_ptr_.?)), - object, + GetJSPrivateData(ZigContextType, object) orelse return, ); } }; @@ -128,26 +129,25 @@ pub const To = struct { ) callconv(.C) js.JSValueRef { var object_ptr: *c_void = undefined; - if (comptime ZigContextType != c_void) { - var object_ptr_ = js.JSObjectGetPrivate(function); - if (object_ptr_ == null) { - object_ptr_ = js.JSObjectGetPrivate(thisObject); - } - - if (object_ptr_ == null) { - return js.JSValueMakeUndefined(ctx); - } - object_ptr = object_ptr_.?; + if (comptime ZigContextType == c_void) { + return ctxfn( + js.JSObjectGetPrivate(function) or js.jsObjectGetPrivate(thisObject), + ctx, + function, + thisObject, + if (arguments) |args| args[0..argumentCount] else &[_]js.JSValueRef{}, + exception, + ); + } else { + return ctxfn( + GetJSPrivateData(ZigContextType, function) orelse GetJSPrivateData(ZigContextType, thisObject) orelse return js.JSValueMakeUndefined(ctx), + ctx, + function, + thisObject, + if (arguments) |args| args[0..argumentCount] else &[_]js.JSValueRef{}, + exception, + ); } - - return ctxfn( - @ptrCast(*ZigContextType, @alignCast(@alignOf(*ZigContextType), object_ptr)), - ctx, - function, - thisObject, - if (arguments) |args| args[0..argumentCount] else &[_]js.JSValueRef{}, - exception, - ); } }; } @@ -164,15 +164,7 @@ pub const To = struct { return buf[0..js.JSStringGetUTF8CString(Ref.str(ref), buf.ptr, buf.len)]; } pub inline fn ptr(comptime StructType: type, obj: js.JSObjectRef) *StructType { - return @ptrCast( - *StructType, - @alignCast( - @alignOf( - *StructType, - ), - js.JSObjectGetPrivate(obj).?, - ), - ); + return GetJSPrivateData(StructType, obj).?; } }; }; @@ -764,10 +756,6 @@ pub fn NewClass( pub const static_value_count = static_properties.len; - pub fn new(ctx: js.JSContextRef, ptr: ?*ZigType) js.JSObjectRef { - return js.JSObjectMake(ctx, get().*, ptr); - } - pub fn get() callconv(.C) [*c]js.JSClassRef { if (!loaded) { loaded = true; @@ -795,6 +783,17 @@ pub fn NewClass( return ClassGetter; } + pub fn customHasInstance(ctx: js.JSContextRef, obj: js.JSObjectRef, value: js.JSValueRef, exception: ExceptionRef) callconv(.C) bool { + return js.JSValueIsObjectOfClass(ctx, obj, get().*); + } + + pub fn make(ctx: js.JSContextRef, ptr: *ZigType) callconv(.C) js.JSObjectRef { + return js.JSObjectMake( + ctx, + get().*, + JSPrivateDataPtr.init(ptr).ptr(), + ); + } pub fn GetClass(comptime ReceiverType: type) type { const ClassGetter = struct { get: fn ( @@ -825,21 +824,7 @@ pub fn NewClass( prop: js.JSStringRef, exception: js.ExceptionRef, ) callconv(.C) js.JSValueRef { - var instance_pointer_ = js.JSObjectGetPrivate(obj); - if (comptime ZigType != c_void) { - if (instance_pointer_ == null) return null; - } - - var instance_pointer: *c_void = if (comptime ZigType == c_void) undefined else instance_pointer_.?; - var ptr = @ptrCast( - *ZigType, - @alignCast( - @alignOf( - *ZigType, - ), - instance_pointer, - ), - ); + var pointer = GetJSPrivateData(ZigType, obj) orelse return js.JSValueMakeUndefined(ctx); if (singleton) { inline for (function_names) |propname, i| { @@ -876,17 +861,7 @@ pub fn NewClass( prop: js.JSStringRef, exception: js.ExceptionRef, ) callconv(.C) js.JSValueRef { - var instance_pointer_ = js.JSObjectGetPrivate(obj); - if (instance_pointer_ == null) return null; - var this: *ZigType = @ptrCast( - *ZigType, - @alignCast( - @alignOf( - *ZigType, - ), - instance_pointer_.?, - ), - ); + var this = GetJSPrivateData(ZigType, obj) orelse return js.JSValueMakeUndefined(ctx); var exc: js.JSValueRef = null; const Field = @TypeOf(@field( @@ -958,17 +933,7 @@ pub fn NewClass( value: js.JSValueRef, exception: js.ExceptionRef, ) callconv(.C) bool { - var instance_pointer_ = js.JSObjectGetPrivate(obj); - if (instance_pointer_ == null) return false; - var this: *ZigType = @ptrCast( - *ZigType, - @alignCast( - @alignOf( - *ZigType, - ), - instance_pointer_.?, - ), - ); + var this = GetJSPrivateData(ZigType, obj) orelse return js.JSValueMakeUndefined(ctx); var exc: js.ExceptionRef = null; @@ -1375,13 +1340,14 @@ pub fn NewClass( } static_properties[i].name = property_names[i][0.. :0].ptr; } - def.staticValues = (&static_properties); } def.className = class_name_str; // def.getProperty = getPropertyCallback; + if (!singleton) + def.hasInstance = customHasInstance; return def; } }; @@ -1430,8 +1396,19 @@ pub const ArrayBuffer = struct { }; pub fn castObj(obj: js.JSObjectRef, comptime Type: type) *Type { - return @ptrCast( - *Type, - @alignCast(@alignOf(*Type), js.JSObjectGetPrivate(obj).?), - ); + return JSPrivateDataPtr.from(js.JSObjectGetPrivate(obj)).as(Type); +} + +pub const JSPrivateDataPtr = TaggedPointerUnion(.{ + ResolveError, + BuildError, + Response, + Request, + FetchEvent, + Headers, + Body, +}); + +pub inline fn GetJSPrivateData(comptime Type: type, ref: js.JSObjectRef) ?*Type { + return JSPrivateDataPtr.from(js.JSObjectGetPrivate(ref)).get(Type); } diff --git a/src/javascript/jsc/bindings/ZigGlobalObject.cpp b/src/javascript/jsc/bindings/ZigGlobalObject.cpp index 8897b8d21..4a4c3ec96 100644 --- a/src/javascript/jsc/bindings/ZigGlobalObject.cpp +++ b/src/javascript/jsc/bindings/ZigGlobalObject.cpp @@ -147,7 +147,7 @@ JSC::Identifier GlobalObject::moduleLoaderResolve(JSGlobalObject *globalObject, return toIdentifier(res.result.value, globalObject); } else { auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); - throwException(scope, res.result.err.message, globalObject); + throwException(scope, res.result.err, globalObject); return globalObject->vm().propertyNames->emptyIdentifier; } } @@ -168,7 +168,7 @@ JSC::JSInternalPromise *GlobalObject::moduleLoaderImportModule(JSGlobalObject *g globalObject, toZigString(moduleNameValue, globalObject), sourceURL.isEmpty() ? ZigStringCwd : toZigString(sourceURL.fileSystemPath())); if (!resolved.success) { - throwException(scope, resolved.result.err.message, globalObject); + throwException(scope, resolved.result.err, globalObject); return promise->rejectWithCaughtException(globalObject, scope); } @@ -196,11 +196,15 @@ JSC::JSInternalPromise *GlobalObject::moduleLoaderFetch(JSGlobalObject *globalOb auto moduleKey = key.toWTFString(globalObject); RETURN_IF_EXCEPTION(scope, promise->rejectWithCaughtException(globalObject, scope)); auto moduleKeyZig = toZigString(moduleKey); + ErrorableResolvedSource res; + res.success = false; + res.result.err.code = 0; + res.result.err.ptr = nullptr; - auto res = Zig__GlobalObject__fetch(globalObject, moduleKeyZig, ZigStringEmpty); + Zig__GlobalObject__fetch(&res, globalObject, moduleKeyZig, ZigStringEmpty); if (!res.success) { - throwException(scope, res.result.err.message, globalObject); + throwException(scope, res.result.err, globalObject); RETURN_IF_EXCEPTION(scope, promise->rejectWithCaughtException(globalObject, scope)); } diff --git a/src/javascript/jsc/bindings/bindings.cpp b/src/javascript/jsc/bindings/bindings.cpp index 9afe9a00b..a7e2849ad 100644 --- a/src/javascript/jsc/bindings/bindings.cpp +++ b/src/javascript/jsc/bindings/bindings.cpp @@ -1,10 +1,14 @@ #include "helpers.h" #include "root.h" +#include <JavaScriptCore/AggregateError.h> +#include <JavaScriptCore/BytecodeIndex.h> +#include <JavaScriptCore/CodeBlock.h> #include <JavaScriptCore/Completion.h> #include <JavaScriptCore/ErrorInstance.h> #include <JavaScriptCore/ExceptionScope.h> #include <JavaScriptCore/FunctionConstructor.h> #include <JavaScriptCore/Identifier.h> +#include <JavaScriptCore/JSArray.h> #include <JavaScriptCore/JSCInlines.h> #include <JavaScriptCore/JSCallbackObject.h> #include <JavaScriptCore/JSClassRef.h> @@ -16,15 +20,17 @@ #include <JavaScriptCore/JSSet.h> #include <JavaScriptCore/JSString.h> #include <JavaScriptCore/ParserError.h> +#include <JavaScriptCore/ScriptExecutable.h> #include <JavaScriptCore/StackFrame.h> +#include <JavaScriptCore/StackVisitor.h> #include <JavaScriptCore/VM.h> #include <JavaScriptCore/WasmFaultSignalHandler.h> #include <wtf/text/ExternalStringImpl.h> #include <wtf/text/StringCommon.h> #include <wtf/text/StringImpl.h> #include <wtf/text/StringView.h> -#include <wtf/text/WTFString.h> +#include <wtf/text/WTFString.h> extern "C" { // #pragma mark - JSC::PropertyNameArray @@ -131,18 +137,49 @@ static JSC::JSValue doLink(JSC__JSGlobalObject *globalObject, JSC::JSValue modul return JSC::linkAndEvaluateModule(globalObject, moduleKey, JSC::JSValue()); } +JSC__JSValue JSC__JSGlobalObject__createAggregateError(JSC__JSGlobalObject *globalObject, + JSC__JSValue *errors, uint16_t errors_count, + ZigString arg3) { + JSC::VM &vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSC::JSValue message = JSC::JSValue(JSC::jsOwnedString(vm, Zig::toString(arg3))); + JSC::JSValue options = JSC::jsUndefined(); + JSC::JSArray *array = nullptr; + { + JSC::ObjectInitializationScope initializationScope(vm); + if ((array = JSC::JSArray::tryCreateUninitializedRestricted( + initializationScope, nullptr, + globalObject->arrayStructureForIndexingTypeDuringAllocation(JSC::ArrayWithContiguous), + errors_count))) { + + for (uint16_t i = 0; i < errors_count; ++i) { + array->initializeIndexWithoutBarrier(initializationScope, i, + JSC::JSValue::decode(errors[i])); + } + } + } + if (!array) { + JSC::throwOutOfMemoryError(globalObject, scope); + return JSC::JSValue::encode(JSC::JSValue()); + } + + JSC::Structure *errorStructure = globalObject->errorStructure(JSC::ErrorType::AggregateError); + return RELEASE_AND_RETURN(scope, JSC::JSValue::encode(JSC::createAggregateError( + globalObject, vm, errorStructure, array, message, options, + nullptr, JSC::TypeNothing, false))); +} // static JSC::JSNativeStdFunction* resolverFunction; // static JSC::JSNativeStdFunction* rejecterFunction; // static bool resolverFunctionInitialized = false; -static JSC::EncodedJSValue resolverFunctionCallback(JSC::JSGlobalObject *globalObject, - JSC::CallFrame *callFrame) { - return JSC::JSValue::encode(doLink(globalObject, callFrame->argument(0))); +JSC__JSValue ZigString__toValue(ZigString arg0, JSC__JSGlobalObject *arg1) { + return JSC::JSValue::encode(JSC::JSValue(JSC::jsOwnedString(arg1->vm(), Zig::toString(arg0)))); } -static JSC::EncodedJSValue rejecterFunctionCallback(JSC::JSGlobalObject *globalObject, +static JSC::EncodedJSValue resolverFunctionCallback(JSC::JSGlobalObject *globalObject, JSC::CallFrame *callFrame) { - return JSC::JSValue::encode(callFrame->argument(0)); + return JSC::JSValue::encode(doLink(globalObject, callFrame->argument(0))); } JSC__JSInternalPromise * @@ -157,9 +194,14 @@ JSC__JSModuleLoader__loadAndEvaluateModule(JSC__JSGlobalObject *globalObject, Zi JSC::JSNativeStdFunction *resolverFunction = JSC::JSNativeStdFunction::create( globalObject->vm(), globalObject, 1, String(), resolverFunctionCallback); JSC::JSNativeStdFunction *rejecterFunction = JSC::JSNativeStdFunction::create( - globalObject->vm(), globalObject, 1, String(), rejecterFunctionCallback); + globalObject->vm(), globalObject, 1, String(), + [&arg1](JSC::JSGlobalObject *globalObject, JSC::CallFrame *callFrame) -> JSC::EncodedJSValue { + return JSC::JSValue::encode( + JSC::JSInternalPromise::rejectedPromise(globalObject, callFrame->argument(0))); + }); + globalObject->vm().drainMicrotasks(); - auto result = promise->then(globalObject, resolverFunction, nullptr); + auto result = promise->then(globalObject, resolverFunction, rejecterFunction); globalObject->vm().drainMicrotasks(); // if (promise->status(globalObject->vm()) == @@ -687,100 +729,321 @@ bWTF__String JSC__JSValue__toWTFString(JSC__JSValue JSValue0, JSC__JSGlobalObjec return Wrap<WTF::String, bWTF__String>::wrap(value.toWTFString(arg1)); }; -static ZigException fromErrorInstance(JSC::JSGlobalObject *global, JSC::ErrorInstance *err, - JSC::JSValue val) { - ZigException except = Zig::ZigExceptionNone; - JSC::JSObject *obj = JSC::jsDynamicCast<JSC::JSObject *>(global->vm(), val); - if (err->stackTrace() != nullptr && err->stackTrace()->size() > 0) { - JSC::StackFrame *stack = &err->stackTrace()->first(); - except.sourceURL = Zig::toZigString(stack->sourceURL()); - - if (stack->hasLineAndColumnInfo()) { - unsigned lineNumber; - unsigned column; - stack->computeLineAndColumn(lineNumber, column); - except.line = lineNumber; - except.column = column; +static void populateStackFrameMetadata(const JSC::StackFrame *stackFrame, ZigStackFrame *frame) { + frame->source_url = Zig::toZigString(stackFrame->sourceURL()); + + if (stackFrame->isWasmFrame()) { + frame->code_type = ZigStackFrameCodeWasm; + return; + } + + auto m_codeBlock = stackFrame->codeBlock(); + if (m_codeBlock) { + switch (m_codeBlock->codeType()) { + case JSC::EvalCode: { + frame->code_type = ZigStackFrameCodeEval; + return; + } + case JSC::ModuleCode: { + frame->code_type = ZigStackFrameCodeModule; + return; + } + case JSC::GlobalCode: { + frame->code_type = ZigStackFrameCodeGlobal; + return; + } + case JSC::FunctionCode: { + frame->code_type = + !m_codeBlock->isConstructor() ? ZigStackFrameCodeFunction : ZigStackFrameCodeConstructor; + break; + } + default: ASSERT_NOT_REACHED(); } + } + + auto calleeCell = stackFrame->callee(); + if (!calleeCell || !calleeCell->isObject()) return; + + JSC::JSObject *callee = JSC::jsCast<JSC::JSObject *>(calleeCell); + // Does the code block have a user-defined name property? + JSC::JSValue name = callee->getDirect(m_codeBlock->vm(), m_codeBlock->vm().propertyNames->name); + if (name && name.isString()) { + auto str = name.toWTFString(m_codeBlock->globalObject()); + frame->function_name = Zig::toZigString(str); + return; + } + + /* For functions (either JSFunction or InternalFunction), fallback to their "native" name + * property. Based on JSC::getCalculatedDisplayName, "inlining" the + * JSFunction::calculatedDisplayName\InternalFunction::calculatedDisplayName calls */ + if (JSC::JSFunction *function = + JSC::jsDynamicCast<JSC::JSFunction *>(m_codeBlock->vm(), callee)) { + + WTF::String actualName = function->name(m_codeBlock->vm()); + if (!actualName.isEmpty() || function->isHostOrBuiltinFunction()) { + frame->function_name = Zig::toZigString(actualName); + return; + } + + auto inferred_name = function->jsExecutable()->name(); + frame->function_name = Zig::toZigString(inferred_name.string()); + } + + if (JSC::InternalFunction *function = + JSC::jsDynamicCast<JSC::InternalFunction *>(m_codeBlock->vm(), callee)) { + // Based on JSC::InternalFunction::calculatedDisplayName, skipping the "displayName" property + frame->function_name = Zig::toZigString(function->name()); + } +} +// Based on +// https://github.com/mceSystems/node-jsc/blob/master/deps/jscshim/src/shim/JSCStackTrace.cpp#L298 +static void populateStackFramePosition(const JSC::StackFrame *stackFrame, ZigString *source_lines, + int32_t *source_line_numbers, uint8_t source_lines_count, + ZigStackFramePosition *position) { + auto m_codeBlock = stackFrame->codeBlock(); + if (!m_codeBlock) return; + + JSC::BytecodeIndex bytecodeOffset = + stackFrame->hasBytecodeIndex() ? stackFrame->bytecodeIndex() : JSC::BytecodeIndex(); + + /* Get the "raw" position info. + * Note that we're using m_codeBlock->unlinkedCodeBlock()->expressionRangeForBytecodeOffset + * rather than m_codeBlock->expressionRangeForBytecodeOffset in order get the "raw" offsets and + * avoid the CodeBlock's expressionRangeForBytecodeOffset modifications to the line and column + * numbers, (we don't need the column number from it, and we'll calculate the line "fixes" + * ourselves). */ + int startOffset = 0; + int endOffset = 0; + int divotPoint = 0; + unsigned line = 0; + unsigned unusedColumn = 0; + m_codeBlock->unlinkedCodeBlock()->expressionRangeForBytecodeIndex( + bytecodeOffset, divotPoint, startOffset, endOffset, line, unusedColumn); + divotPoint += m_codeBlock->sourceOffset(); + + // TODO: evaluate if using the API from UnlinkedCodeBlock can be used instead of iterating + // through source text. + + /* On the first line of the source code, it seems that we need to "fix" the column with the + * starting offset. We currently use codeBlock->source()->startPosition().m_column.oneBasedInt() + * as the offset in the first line rather than codeBlock->firstLineColumnOffset(), which seems + * simpler (and what CodeBlock::expressionRangeForBytecodeOffset does). This is because + * firstLineColumnOffset values seems different from what we expect (according to v8's tests) + * and I haven't dove into the relevant parts in JSC (yet) to figure out why. */ + unsigned columnOffset = line ? 0 : m_codeBlock->source().startColumn().zeroBasedInt(); + + // "Fix" the line number + JSC::ScriptExecutable *executable = m_codeBlock->ownerExecutable(); + if (std::optional<int> overrideLine = executable->overrideLineNumber(m_codeBlock->vm())) { + line = overrideLine.value(); } else { - // JSC::ErrorInstance marks these as protected. - // To work around that, we cast as a JSC::JSObject - // This code path triggers when there was an exception before the code was executed - // For example, ParserError becomes one of these - auto source_url_value = obj->getDirect(global->vm(), global->vm().propertyNames->sourceURL); - auto str = source_url_value.toWTFString(global); - except.sourceURL = Zig::toZigString(str); - except.line = obj->getDirect(global->vm(), global->vm().propertyNames->line).toInt32(global); - except.column = - obj->getDirect(global->vm(), global->vm().propertyNames->column).toInt32(global); + line += executable->firstLine(); + } + + // Calculate the staring\ending offsets of the entire expression + int expressionStart = divotPoint - startOffset; + int expressionStop = divotPoint + endOffset; + + // Make sure the range is valid + WTF::StringView sourceString = m_codeBlock->source().provider()->source(); + if (!expressionStop || expressionStart > static_cast<int>(sourceString.length())) { return; } + + // Search for the beginning of the line + unsigned int lineStart = expressionStart; + while ((lineStart > 0) && ('\n' != sourceString[lineStart - 1])) { lineStart--; } + // Search for the end of the line + unsigned int lineStop = expressionStop; + unsigned int sourceLength = sourceString.length(); + while ((lineStop < sourceLength) && ('\n' != sourceString[lineStop])) { lineStop++; } + if (source_lines_count > 1 && source_lines != nullptr) { + auto chars = sourceString.characters8(); + + // Most of the time, when you look at a stack trace, you want a couple lines above + + source_lines[0] = {&chars[lineStart], lineStop - lineStart}; + source_line_numbers[0] = line; + + if (lineStart > 0) { + auto byte_offset_in_source_string = lineStart - 1; + uint8_t source_line_i = 1; + auto remaining_lines_to_grab = source_lines_count - 1; + + while (byte_offset_in_source_string > 0 && remaining_lines_to_grab > 0) { + unsigned int end_of_line_offset = byte_offset_in_source_string; + + // This should probably be code points instead of newlines + while (byte_offset_in_source_string > 0 && chars[byte_offset_in_source_string] != '\n') { + byte_offset_in_source_string--; + } + + // We are at the beginning of the line + source_lines[source_line_i] = {&chars[byte_offset_in_source_string], + end_of_line_offset - byte_offset_in_source_string + 1}; + + source_line_numbers[source_line_i] = line - source_line_i; + source_line_i++; + + remaining_lines_to_grab--; + + byte_offset_in_source_string -= byte_offset_in_source_string > 0; + } + } } - if (obj->hasProperty(global, global->vm().propertyNames->stack)) { - except.stack = Zig::toZigString( - obj->getDirect(global->vm(), global->vm().propertyNames->stack).toWTFString(global)); + /* Finally, store the source "positions" info. + * Notes: + * - The retrieved column seem to point the "end column". To make sure we're current, we'll + *calculate the columns ourselves, since we've already found where the line starts. Note that in + *v8 it should be 0-based here (in contrast the 1-based column number in v8::StackFrame). + * - The static_casts are ugly, but comes from differences between JSC and v8's api, and should + *be OK since no source should be longer than "max int" chars. + * TODO: If expressionStart == expressionStop, then m_endColumn will be equal to m_startColumn. + *Should we handle this case? + */ + position->expression_start = expressionStart; + position->expression_stop = expressionStop; + position->line = WTF::OrdinalNumber::fromOneBasedInt(static_cast<int>(line)).zeroBasedInt(); + position->column_start = (expressionStart - lineStart) + columnOffset; + position->column_stop = position->column_start + (expressionStop - expressionStart); + position->line_start = lineStart; + position->line_stop = lineStop; + + return; +} +static void populateStackFrame(ZigStackTrace *trace, const JSC::StackFrame *stackFrame, + ZigStackFrame *frame, bool is_top) { + populateStackFrameMetadata(stackFrame, frame); + populateStackFramePosition(stackFrame, is_top ? trace->source_lines_ptr : nullptr, + is_top ? trace->source_lines_numbers : nullptr, + is_top ? trace->source_lines_to_collect : 0, &frame->position); +} +static void populateStackTrace(const WTF::Vector<JSC::StackFrame> &frames, ZigStackTrace *trace) { + uint8_t frame_i = 0; + size_t stack_frame_i = 0; + const size_t total_frame_count = frames.size(); + const uint8_t frame_count = + total_frame_count < trace->frames_len ? total_frame_count : trace->frames_len; + + while (frame_i < frame_count && stack_frame_i < total_frame_count) { + // Skip native frames + while (stack_frame_i < total_frame_count && !(&frames.at(stack_frame_i))->codeBlock() && + !(&frames.at(stack_frame_i))->isWasmFrame()) { + stack_frame_i++; + } + if (stack_frame_i >= total_frame_count) break; + + ZigStackFrame *frame = &trace->frames_ptr[frame_i]; + populateStackFrame(trace, &frames[stack_frame_i], frame, frame_i == 0); + stack_frame_i++; + frame_i++; + } + trace->frames_len = frame_i; +} +static void fromErrorInstance(ZigException *except, JSC::JSGlobalObject *global, + JSC::ErrorInstance *err, const Vector<JSC::StackFrame> *stackTrace, + JSC::JSValue val) { + JSC::JSObject *obj = JSC::jsDynamicCast<JSC::JSObject *>(global->vm(), val); + if (stackTrace != nullptr && stackTrace->size() > 0) { + populateStackTrace(*stackTrace, &except->stack); + } else if (err->stackTrace() != nullptr && err->stackTrace()->size() > 0) { + populateStackTrace(*err->stackTrace(), &except->stack); } - except.code = (unsigned char)err->errorType(); - if (err->isStackOverflowError()) { except.code = 253; } - if (err->isOutOfMemoryError()) { except.code = 8; } + except->code = (unsigned char)err->errorType(); + if (err->isStackOverflowError()) { except->code = 253; } + if (err->isOutOfMemoryError()) { except->code = 8; } if (obj->hasProperty(global, global->vm().propertyNames->message)) { - except.message = Zig::toZigString( + except->message = Zig::toZigString( obj->getDirect(global->vm(), global->vm().propertyNames->message).toWTFString(global)); } else { - except.message = Zig::toZigString(err->sanitizedMessageString(global)); + except->message = Zig::toZigString(err->sanitizedMessageString(global)); } - except.name = Zig::toZigString(err->sanitizedNameString(global)); - except.runtime_type = err->runtimeTypeForCause(); - - except.exception = err; + except->name = Zig::toZigString(err->sanitizedNameString(global)); + except->runtime_type = err->runtimeTypeForCause(); + + except->exception = err; +} + +void exceptionFromString(ZigException *except, JSC::JSValue value, JSC::JSGlobalObject *global) { + // Fallback case for when it's a user-defined ErrorLike-object that doesn't inherit from + // ErrorInstance + if (JSC::JSObject *obj = JSC::jsDynamicCast<JSC::JSObject *>(global->vm(), value)) { + if (obj->hasProperty(global, global->vm().propertyNames->name)) { + auto name_str = + obj->getDirect(global->vm(), global->vm().propertyNames->name).toWTFString(global); + except->name = Zig::toZigString(name_str); + if (name_str == "Error"_s) { + except->code = JSErrorCodeError; + } else if (name_str == "EvalError"_s) { + except->code = JSErrorCodeEvalError; + } else if (name_str == "RangeError"_s) { + except->code = JSErrorCodeRangeError; + } else if (name_str == "ReferenceError"_s) { + except->code = JSErrorCodeReferenceError; + } else if (name_str == "SyntaxError"_s) { + except->code = JSErrorCodeSyntaxError; + } else if (name_str == "TypeError"_s) { + except->code = JSErrorCodeTypeError; + } else if (name_str == "URIError"_s) { + except->code = JSErrorCodeURIError; + } else if (name_str == "AggregateError"_s) { + except->code = JSErrorCodeAggregateError; + } + } - return except; -} + if (obj->hasProperty(global, global->vm().propertyNames->message)) { + except->message = Zig::toZigString( + obj->getDirect(global->vm(), global->vm().propertyNames->message).toWTFString(global)); + } -static ZigException exceptionFromString(WTF::String &str) { - ZigException except = Zig::ZigExceptionNone; - auto ref = OpaqueJSString::tryCreate(str); - except.message = ZigString{ref->characters8(), ref->length()}; - ref->ref(); + if (obj->hasProperty(global, global->vm().propertyNames->sourceURL)) { + except->stack.frames_ptr[0].source_url = Zig::toZigString( + obj->getDirect(global->vm(), global->vm().propertyNames->sourceURL).toWTFString(global)); + except->stack.frames_len = 1; + } - return except; -} + if (obj->hasProperty(global, global->vm().propertyNames->line)) { + except->stack.frames_ptr[0].position.line = + obj->getDirect(global->vm(), global->vm().propertyNames->line).toInt32(global); + except->stack.frames_len = 1; + } -static ZigException exceptionFromString(JSC::JSValue value, JSC::JSGlobalObject *global) { + return; + } auto scope = DECLARE_THROW_SCOPE(global->vm()); auto str = value.toWTFString(global); if (scope.exception()) { scope.clearException(); scope.release(); - return Zig::ZigExceptionNone; + return; } scope.release(); - ZigException except = Zig::ZigExceptionNone; auto ref = OpaqueJSString::tryCreate(str); - except.message = ZigString{ref->characters8(), ref->length()}; + except->message = ZigString{ref->characters8(), ref->length()}; ref->ref(); - - return except; } -ZigException JSC__JSValue__toZigException(JSC__JSValue JSValue0, JSC__JSGlobalObject *arg1) { +void JSC__JSValue__toZigException(JSC__JSValue JSValue0, JSC__JSGlobalObject *arg1, + ZigException *exception) { JSC::JSValue value = JSC::JSValue::decode(JSValue0); - if (JSC::ErrorInstance *error = JSC::jsDynamicCast<JSC::ErrorInstance *>(arg1->vm(), value)) { - return fromErrorInstance(arg1, error, value); - } - - if (JSC::Exception *exception = JSC::jsDynamicCast<JSC::Exception *>(arg1->vm(), value)) { + if (JSC::Exception *jscException = JSC::jsDynamicCast<JSC::Exception *>(arg1->vm(), value)) { if (JSC::ErrorInstance *error = - JSC::jsDynamicCast<JSC::ErrorInstance *>(arg1->vm(), exception->value())) { - return fromErrorInstance(arg1, error, value); + JSC::jsDynamicCast<JSC::ErrorInstance *>(arg1->vm(), jscException->value())) { + fromErrorInstance(exception, arg1, error, &jscException->stack(), value); + return; } } - return exceptionFromString(value, arg1); + if (JSC::ErrorInstance *error = JSC::jsDynamicCast<JSC::ErrorInstance *>(arg1->vm(), value)) { + fromErrorInstance(exception, arg1, error, nullptr, value); + return; + } + + exceptionFromString(exception, value, arg1); } #pragma mark - JSC::PropertyName diff --git a/src/javascript/jsc/bindings/bindings.zig b/src/javascript/jsc/bindings/bindings.zig index 4055c26bb..1b7205aa9 100644 --- a/src/javascript/jsc/bindings/bindings.zig +++ b/src/javascript/jsc/bindings/bindings.zig @@ -2,6 +2,7 @@ usingnamespace @import("./shared.zig"); usingnamespace @import("./headers.zig"); pub const Shimmer = @import("./shimmer.zig").Shimmer; const hasRef = std.meta.trait.hasField("ref"); +const C_API = @import("../JavaScriptCore.zig"); pub const JSObject = extern struct { pub const shim = Shimmer("JSC", "JSObject", @This()); @@ -37,6 +38,10 @@ pub const JSObject = extern struct { pub const ZigString = extern struct { ptr: [*]const u8, len: usize, + pub const shim = Shimmer("Zig", "ZigString", @This()); + + pub const name = "ZigString"; + pub const namespace = "Zig"; pub fn init(slice_: []const u8) ZigString { return ZigString{ .ptr = slice_.ptr, .len = slice_.len }; @@ -47,6 +52,18 @@ pub const ZigString = extern struct { pub fn slice(this: *const ZigString) []const u8 { return this.ptr[0..std.math.min(this.len, 4096)]; } + + pub fn toValue(this: ZigString, global: *JSGlobalObject) JSValue { + return shim.cppFn("toValue", .{ this, global }); + } + + pub fn toJSStringRef(this: *const ZigString) C_API.JSStringRef { + return C_API.JSStringCreateStatic(this.ptr, this.len); + } + + pub const Extern = [_][]const u8{ + "toValue", + }; }; pub const JSCell = extern struct { @@ -184,11 +201,12 @@ pub fn NewGlobalObject(comptime Type: type) type { } return ErrorableZigString.err(error.ResolveFailed, "resolve not implemented"); } - pub fn fetch(global: *JSGlobalObject, specifier: ZigString, source: ZigString) callconv(.C) ErrorableResolvedSource { + pub fn fetch(ret: *ErrorableResolvedSource, global: *JSGlobalObject, specifier: ZigString, source: ZigString) callconv(.C) void { if (comptime @hasDecl(Type, "fetch")) { - return @call(.{ .modifier = .always_inline }, Type.fetch, .{ global, specifier, source }); + @call(.{ .modifier = .always_inline }, Type.fetch, .{ ret, global, specifier, source }); + return; } - return ErrorableResolvedSource.err(error.FetchFailed, "Module fetch not implemented"); + ret.* = ErrorableResolvedSource.err(error.FetchFailed, "Module fetch not implemented"); } pub fn promiseRejectionTracker(global: *JSGlobalObject, promise: *JSPromise, rejection: JSPromiseRejectionOperation) callconv(.C) JSValue { if (comptime @hasDecl(Type, "promiseRejectionTracker")) { @@ -776,11 +794,16 @@ pub const JSGlobalObject = extern struct { return cppFn("asyncGeneratorFunctionPrototype", .{this}); } + pub fn createAggregateError(globalObject: *JSGlobalObject, errors: [*]JSValue, errors_len: u16, message: ZigString) JSValue { + return cppFn("createAggregateError", .{ globalObject, errors, errors_len, message }); + } + pub fn vm(this: *JSGlobalObject) *VM { return cppFn("vm", .{this}); } pub const Extern = [_][]const u8{ + "createAggregateError", "objectPrototype", "functionPrototype", "arrayPrototype", @@ -1087,6 +1110,7 @@ pub const JSValue = enum(i64) { pub fn jsNumberFromInt32(i: i32) JSValue { return cppFn("jsNumberFromInt32", .{i}); } + pub fn jsNumberFromInt64(i: i64) JSValue { return cppFn("jsNumberFromInt64", .{i}); } @@ -1165,8 +1189,8 @@ pub const JSValue = enum(i64) { return cppFn("isException", .{ this, vm }); } - pub fn toZigException(this: JSValue, global: *JSGlobalObject) ZigException { - return cppFn("toZigException", .{ this, global }); + pub fn toZigException(this: JSValue, global: *JSGlobalObject, exception: *ZigException) void { + return cppFn("toZigException", .{ this, global, exception }); } // On exception, this returns the empty string. diff --git a/src/javascript/jsc/bindings/exports.zig b/src/javascript/jsc/bindings/exports.zig index 1c2dae1f9..8813870b2 100644 --- a/src/javascript/jsc/bindings/exports.zig +++ b/src/javascript/jsc/bindings/exports.zig @@ -3,6 +3,7 @@ usingnamespace @import("./shared.zig"); const Fs = @import("../../../fs.zig"); const CAPI = @import("../JavaScriptCore.zig"); const JS = @import("../javascript.zig"); +const JSBase = @import("../base.zig"); const Handler = struct { pub export fn global_signal_handler_fn(sig: i32, info: *const std.os.siginfo_t, ctx_ptr: ?*const c_void) callconv(.C) void { Global.panic("C++ Crash!!", .{}); @@ -47,11 +48,11 @@ pub const ZigGlobalObject = extern struct { } return @call(.{ .modifier = .always_inline }, Interface.resolve, .{ global, specifier, source }); } - pub fn fetch(global: *JSGlobalObject, specifier: ZigString, source: ZigString) callconv(.C) ErrorableResolvedSource { + pub fn fetch(ret: *ErrorableResolvedSource, global: *JSGlobalObject, specifier: ZigString, source: ZigString) callconv(.C) void { if (comptime is_bindgen) { unreachable; } - return @call(.{ .modifier = .always_inline }, Interface.fetch, .{ global, specifier, source }); + @call(.{ .modifier = .always_inline }, Interface.fetch, .{ ret, global, specifier, source }); } pub fn promiseRejectionTracker(global: *JSGlobalObject, promise: *JSPromise, rejection: JSPromiseRejectionOperation) callconv(.C) JSValue { @@ -106,7 +107,8 @@ pub const ZigGlobalObject = extern struct { } }; -const ErrorCodeInt = std.meta.Int(.unsigned, @sizeOf(anyerror) * 8); +const ErrorCodeInt = usize; + pub const ErrorCode = enum(ErrorCodeInt) { _, @@ -114,18 +116,33 @@ pub const ErrorCode = enum(ErrorCodeInt) { return @intToEnum(ErrorCode, @errorToInt(code)); } - pub const Type = switch (@sizeOf(anyerror)) { - 0, 1 => u8, - 2 => u16, - 3 => u32, - 4 => u64, - else => @compileError("anyerror is too big"), - }; + pub const ParserError = @enumToInt(ErrorCode.from(error.ParserError)); + pub const JSErrorObject = @enumToInt(ErrorCode.from(error.JSErrorObject)); + + pub const Type = ErrorCodeInt; }; pub const ZigErrorType = extern struct { + pub const shim = Shimmer("Zig", "ErrorType", @This()); + pub const name = "ErrorType"; + pub const namespace = shim.namespace; + code: ErrorCode, - message: ZigString, + ptr: ?*c_void, + + pub fn isPrivateData(ptr: ?*c_void) callconv(.C) bool { + return JSBase.JSPrivateDataPtr.isValidPtr(ptr); + } + + pub const Export = shim.exportFunctions(.{ + .@"isPrivateData" = isPrivateData, + }); + + comptime { + @export(isPrivateData, .{ + .name = Export[0].symbol_name, + }); + } }; pub const JSErrorCode = enum(u8) { @@ -140,6 +157,7 @@ pub const JSErrorCode = enum(u8) { // StackOverflow & OutOfMemoryError is not an ErrorType in <JavaScriptCore/ErrorType.h> within JSC, so the number here is just totally made up OutOfMemoryError = 8, + BundlerError = 252, StackOverflow = 253, UserErrorCode = 254, _, @@ -181,18 +199,12 @@ pub fn Errorable(comptime Type: type) type { } threadlocal var err_buf: [4096]u8 = undefined; - pub fn errFmt(code: anyerror, comptime fmt: []const u8, args: anytype) @This() { - const message = std.fmt.bufPrint(&err_buf, fmt, args) catch @as([]const u8, @errorName(code)[0..]); - - return @call(.{ .modifier = .always_inline }, err, .{ code, message }); - } - - pub fn err(code: anyerror, msg: []const u8) @This() { + pub fn err(code: anyerror, ptr: *c_void) @This() { return @This(){ .result = .{ .err = .{ .code = ErrorCode.from(code), - .message = ZigString.init(msg), + .ptr = ptr, }, }, .success = false, @@ -215,10 +227,200 @@ pub const ResolvedSource = extern struct { bytecodecache_fd: u64, }; -pub const ErrorableResolvedSource = Errorable(ResolvedSource); +pub const ZigStackFrameCode = enum(u8) { + None = 0, + // 🏃 + Eval = 1, + // 📦 + Module = 2, + // λ + Function = 3, + // 🌎 + Global = 4, + // ⚙️ + Wasm = 5, + // 👷 + Constructor = 6, + _, -pub const ErrorableZigString = Errorable(ZigString); -pub const ErrorableJSValue = Errorable(JSValue); + pub fn emoji(this: ZigStackFrameCode) u21 { + return switch (this) { + .Eval => 0x1F3C3, + .Module => 0x1F4E6, + .Function => 0x03BB, + .Global => 0x1F30E, + .Wasm => 0xFE0F, + .Constructor => 0xF1477, + else => ' ', + }; + } + + pub fn ansiColor(this: ZigStackFrameCode) string { + return switch (this) { + .Eval => "\x1b[31m", + .Module => "\x1b[36m", + .Function => "\x1b[32m", + .Global => "\x1b[35m", + .Wasm => "\x1b[37m", + .Constructor => "\x1b[33m", + else => "", + }; + } +}; + +pub const ZigStackTrace = extern struct { + source_lines_ptr: [*c]ZigString, + source_lines_numbers: [*c]i32, + source_lines_len: u8, + source_lines_to_collect: u8, + + frames_ptr: [*c]ZigStackFrame, + frames_len: u8, + + pub fn frames(this: *const ZigStackTrace) []const ZigStackFrame { + return this.frames_ptr[0..this.frames_len]; + } + + pub const SourceLineIterator = struct { + trace: *const ZigStackTrace, + i: i16, + + pub const SourceLine = struct { + line: i32, + text: string, + }; + + pub fn untilLast(this: *SourceLineIterator) ?SourceLine { + if (this.i < 1) return null; + return this.next(); + } + + pub fn next(this: *SourceLineIterator) ?SourceLine { + if (this.i < 0) return null; + + const result = SourceLine{ + .line = this.trace.source_lines_numbers[@intCast(usize, this.i)], + .text = this.trace.source_lines_ptr[@intCast(usize, this.i)].slice(), + }; + this.i -= 1; + return result; + } + }; + + pub fn sourceLineIterator(this: *const ZigStackTrace) SourceLineIterator { + var i: usize = 0; + for (this.source_lines_numbers[0..this.source_lines_len]) |num, j| { + if (num > 0) { + i = j; + } + } + return SourceLineIterator{ .trace = this, .i = @intCast(i16, i) }; + } +}; + +pub const ZigStackFrame = extern struct { + pub const SourceURLFormatter = struct { + source_url: ZigString, + position: ZigStackFramePosition, + enable_color: bool, + + pub fn format(this: SourceURLFormatter, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { + try writer.writeAll(this.source_url.slice()); + if (this.position.line > -1 and this.position.column_start > -1) { + try std.fmt.format(writer, ":{d}:{d}", .{ this.position.line, this.position.column_start }); + } else if (this.position.line > -1) { + try std.fmt.format(writer, ":{d}", .{ + this.position.line, + }); + } + } + }; + + pub const NameFormatter = struct { + function_name: ZigString, + code_type: ZigStackFrameCode, + enable_color: bool, + + pub fn format(this: NameFormatter, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { + const name = this.function_name.slice(); + + switch (this.code_type) { + .Eval => { + try writer.writeAll("(eval)"); + }, + .Module => { + try writer.writeAll("(esm)"); + }, + .Function => { + if (name.len > 0) { + try std.fmt.format(writer, "{s}", .{name}); + } else { + try writer.writeAll("(anonymous)"); + } + }, + .Global => { + if (name.len > 0) { + try std.fmt.format(writer, "globalThis {s}", .{name}); + } else { + try writer.writeAll("globalThis"); + } + }, + .Wasm => { + try std.fmt.format(writer, "WASM {s}", .{name}); + }, + .Constructor => { + try std.fmt.format(writer, "new {s}", .{name}); + }, + else => {}, + } + } + }; + + pub const Zero: ZigStackFrame = ZigStackFrame{ + .function_name = ZigString{ .ptr = "", .len = 0 }, + .code_type = ZigStackFrameCode.None, + .source_url = ZigString{ .ptr = "", .len = 0 }, + .position = ZigStackFramePosition.Invalid, + }; + + function_name: ZigString, + source_url: ZigString, + position: ZigStackFramePosition, + code_type: ZigStackFrameCode, + + pub fn nameFormatter(this: *const ZigStackFrame, comptime enable_color: bool) NameFormatter { + return NameFormatter{ .function_name = this.function_name, .code_type = this.code_type, .enable_color = enable_color }; + } + + pub fn sourceURLFormatter(this: *const ZigStackFrame, comptime enable_color: bool) SourceURLFormatter { + return SourceURLFormatter{ .source_url = this.source_url, .position = this.position, .enable_color = enable_color }; + } +}; + +pub const ZigStackFramePosition = extern struct { + source_offset: i32, + line: i32, + line_start: i32, + line_stop: i32, + column_start: i32, + column_stop: i32, + expression_start: i32, + expression_stop: i32, + + pub const Invalid = ZigStackFramePosition{ + .source_offset = -1, + .line = -1, + .line_start = -1, + .line_stop = -1, + .column_start = -1, + .column_stop = -1, + .expression_start = -1, + .expression_stop = -1, + }; + pub fn isInvalid(this: *const ZigStackFramePosition) bool { + return std.mem.eql(u8, std.mem.asBytes(this), std.mem.asBytes(&Invalid)); + } +}; pub const ZigException = extern struct { pub const shim = Shimmer("Zig", "Exception", @This()); @@ -229,12 +431,68 @@ pub const ZigException = extern struct { runtime_type: JSRuntimeType, name: ZigString, message: ZigString, - sourceURL: ZigString, - line: i32, - column: i32, - stack: ZigString, + stack: ZigStackTrace, + exception: ?*c_void, + pub const Holder = extern struct { + const frame_count = 24; + const source_lines_count = 6; + source_line_numbers: [source_lines_count]i32, + source_lines: [source_lines_count]ZigString, + frames: [frame_count]ZigStackFrame, + loaded: bool, + zig_exception: ZigException, + + pub const Zero: Holder = Holder{ + .frames = brk: { + var _frames: [frame_count]ZigStackFrame = undefined; + std.mem.set(ZigStackFrame, &_frames, ZigStackFrame.Zero); + break :brk _frames; + }, + .source_line_numbers = brk: { + var lines: [source_lines_count]i32 = undefined; + std.mem.set(i32, &lines, -1); + break :brk lines; + }, + + .source_lines = brk: { + var lines: [source_lines_count]ZigString = undefined; + std.mem.set(ZigString, &lines, ZigString.Empty); + break :brk lines; + }, + .zig_exception = undefined, + .loaded = false, + }; + + pub fn init() Holder { + return Holder.Zero; + } + + pub fn zigException(this: *Holder) *ZigException { + if (!this.loaded) { + this.zig_exception = ZigException{ + .code = @intToEnum(JSErrorCode, 255), + .runtime_type = JSRuntimeType.Nothing, + .name = ZigString.Empty, + .message = ZigString.Empty, + .exception = null, + .stack = ZigStackTrace{ + .source_lines_ptr = &this.source_lines, + .source_lines_numbers = &this.source_line_numbers, + .source_lines_len = source_lines_count, + .source_lines_to_collect = source_lines_count, + .frames_ptr = &this.frames, + .frames_len = this.frames.len, + }, + }; + this.loaded = true; + } + + return &this.zig_exception; + } + }; + pub fn fromException(exception: *Exception) ZigException { return shim.cppFn("fromException", .{exception}); } @@ -242,6 +500,10 @@ pub const ZigException = extern struct { pub const Extern = [_][]const u8{"fromException"}; }; +pub const ErrorableResolvedSource = Errorable(ResolvedSource); +pub const ErrorableZigString = Errorable(ZigString); +pub const ErrorableJSValue = Errorable(JSValue); + pub const ZigConsoleClient = struct { pub const shim = Shimmer("Zig", "ConsoleClient", @This()); pub const Type = *c_void; @@ -463,3 +725,8 @@ pub const ZigConsoleClient = struct { pub inline fn toGlobalContextRef(ptr: *JSGlobalObject) CAPI.JSGlobalContextRef { return @ptrCast(CAPI.JSGlobalContextRef, ptr); } + +comptime { + @export(ErrorCode.ParserError, .{ .name = "Zig_ErrorCodeParserError" }); + @export(ErrorCode.JSErrorObject, .{ .name = "Zig_ErrorCodeJSErrorObject" }); +} diff --git a/src/javascript/jsc/bindings/header-gen.zig b/src/javascript/jsc/bindings/header-gen.zig index e912bf4f8..8b9bb504e 100644 --- a/src/javascript/jsc/bindings/header-gen.zig +++ b/src/javascript/jsc/bindings/header-gen.zig @@ -577,7 +577,7 @@ pub fn HeaderGen(comptime import: type, comptime fname: []const u8) type { const Generator = C_Generator; validateGenerator(Generator); var file_writer = file.writer(); - file_writer.print("//-- AUTOGENERATED FILE -- {d}\n", .{std.time.timestamp()}) catch unreachable; + file_writer.print("//-- AUTOGENERATED FILE -- {d}\n// clang-format: off\n", .{std.time.timestamp()}) catch unreachable; file.writeAll( \\#pragma once \\ @@ -594,18 +594,15 @@ pub fn HeaderGen(comptime import: type, comptime fname: []const u8) type { \\#define CPP_DECL AUTO_EXTERN_C \\#define CPP_SIZE AUTO_EXTERN_C \\ - \\typedef uint16_t ZigErrorCode; \\#ifndef __cplusplus \\typedef void* JSClassRef; \\#endif - \\typedef struct ZigString { const unsigned char* ptr; size_t len; } ZigString; - \\typedef struct ZigErrorType { ZigErrorCode code; ZigString message; } ZigErrorType; - \\typedef struct ZigException { unsigned char code; uint16_t runtime_type; ZigString name; ZigString message; ZigString sourceURL; int32_t line; int32_t column; ZigString stack; void* exception; } ZigException; - \\typedef union ErrorableZigStringResult { ZigString value; ZigErrorType err; } ErrorableZigStringResult; - \\typedef struct ErrorableZigString { ErrorableZigStringResult result; bool success; } ErrorableZigString; - \\typedef struct ResolvedSource { ZigString specifier; ZigString source_code; ZigString source_url; uint32_t hash; uint32_t bytecodecache_fd; } ResolvedSource; - \\typedef union ErrorableResolvedSourceResult { ResolvedSource value; ZigErrorType err; } ErrorableResolvedSourceResult; - \\typedef struct ErrorableResolvedSource { ErrorableResolvedSourceResult result; bool success; } ErrorableResolvedSource; + \\ + \\#ifdef __cplusplus + \\#include "root.h" + \\#include <JavaScriptCore/JSClassRef.h> + \\#endif + \\#include "headers-handwritten.h" \\ ) catch {}; diff --git a/src/javascript/jsc/bindings/headers-cpp.h b/src/javascript/jsc/bindings/headers-cpp.h index e8b096bb2..d51bfdfc2 100644 --- a/src/javascript/jsc/bindings/headers-cpp.h +++ b/src/javascript/jsc/bindings/headers-cpp.h @@ -1,4 +1,4 @@ -//-- AUTOGENERATED FILE -- 1627603981 +//-- AUTOGENERATED FILE -- 1627867755 // clang-format off #pragma once @@ -224,8 +224,8 @@ extern "C" const size_t Zig__GlobalObject_object_align_ = alignof(Zig::GlobalObj extern "C" const size_t Zig__ConsoleClient_object_size_ = sizeof(Zig::ConsoleClient); extern "C" const size_t Zig__ConsoleClient_object_align_ = alignof(Zig::ConsoleClient); -const size_t sizes[27] = {sizeof(JSC::JSObject), sizeof(JSC::JSCell), sizeof(JSC::JSString), sizeof(Inspector::ScriptArguments), sizeof(JSC::JSModuleLoader), sizeof(JSC::JSModuleRecord), sizeof(JSC::JSPromise), sizeof(JSC::JSInternalPromise), sizeof(JSC::SourceOrigin), sizeof(JSC::SourceCode), sizeof(JSC::JSFunction), sizeof(JSC::JSGlobalObject), sizeof(WTF::URL), sizeof(WTF::String), sizeof(JSC::JSValue), sizeof(JSC::PropertyName), sizeof(JSC::Exception), sizeof(JSC::VM), sizeof(JSC::ThrowScope), sizeof(JSC::CatchScope), sizeof(JSC::CallFrame), sizeof(JSC::Identifier), sizeof(WTF::StringImpl), sizeof(WTF::ExternalStringImpl), sizeof(WTF::StringView), sizeof(Zig::GlobalObject), sizeof(ZigException)}; +const size_t sizes[28] = {sizeof(JSC::JSObject), sizeof(ZigString), sizeof(JSC::JSCell), sizeof(JSC::JSString), sizeof(Inspector::ScriptArguments), sizeof(JSC::JSModuleLoader), sizeof(JSC::JSModuleRecord), sizeof(JSC::JSPromise), sizeof(JSC::JSInternalPromise), sizeof(JSC::SourceOrigin), sizeof(JSC::SourceCode), sizeof(JSC::JSFunction), sizeof(JSC::JSGlobalObject), sizeof(WTF::URL), sizeof(WTF::String), sizeof(JSC::JSValue), sizeof(JSC::PropertyName), sizeof(JSC::Exception), sizeof(JSC::VM), sizeof(JSC::ThrowScope), sizeof(JSC::CatchScope), sizeof(JSC::CallFrame), sizeof(JSC::Identifier), sizeof(WTF::StringImpl), sizeof(WTF::ExternalStringImpl), sizeof(WTF::StringView), sizeof(Zig::GlobalObject), sizeof(ZigException)}; -const char* names[27] = {"JSC__JSObject", "JSC__JSCell", "JSC__JSString", "Inspector__ScriptArguments", "JSC__JSModuleLoader", "JSC__JSModuleRecord", "JSC__JSPromise", "JSC__JSInternalPromise", "JSC__SourceOrigin", "JSC__SourceCode", "JSC__JSFunction", "JSC__JSGlobalObject", "WTF__URL", "WTF__String", "JSC__JSValue", "JSC__PropertyName", "JSC__Exception", "JSC__VM", "JSC__ThrowScope", "JSC__CatchScope", "JSC__CallFrame", "JSC__Identifier", "WTF__StringImpl", "WTF__ExternalStringImpl", "WTF__StringView", "Zig__GlobalObject", "ZigException"}; +const char* names[28] = {"JSC__JSObject", "ZigString", "JSC__JSCell", "JSC__JSString", "Inspector__ScriptArguments", "JSC__JSModuleLoader", "JSC__JSModuleRecord", "JSC__JSPromise", "JSC__JSInternalPromise", "JSC__SourceOrigin", "JSC__SourceCode", "JSC__JSFunction", "JSC__JSGlobalObject", "WTF__URL", "WTF__String", "JSC__JSValue", "JSC__PropertyName", "JSC__Exception", "JSC__VM", "JSC__ThrowScope", "JSC__CatchScope", "JSC__CallFrame", "JSC__Identifier", "WTF__StringImpl", "WTF__ExternalStringImpl", "WTF__StringView", "Zig__GlobalObject", "ZigException"}; -const size_t aligns[27] = {alignof(JSC::JSObject), alignof(JSC::JSCell), alignof(JSC::JSString), alignof(Inspector::ScriptArguments), alignof(JSC::JSModuleLoader), alignof(JSC::JSModuleRecord), alignof(JSC::JSPromise), alignof(JSC::JSInternalPromise), alignof(JSC::SourceOrigin), alignof(JSC::SourceCode), alignof(JSC::JSFunction), alignof(JSC::JSGlobalObject), alignof(WTF::URL), alignof(WTF::String), alignof(JSC::JSValue), alignof(JSC::PropertyName), alignof(JSC::Exception), alignof(JSC::VM), alignof(JSC::ThrowScope), alignof(JSC::CatchScope), alignof(JSC::CallFrame), alignof(JSC::Identifier), alignof(WTF::StringImpl), alignof(WTF::ExternalStringImpl), alignof(WTF::StringView), alignof(Zig::GlobalObject), alignof(ZigException)}; +const size_t aligns[28] = {alignof(JSC::JSObject), alignof(ZigString), alignof(JSC::JSCell), alignof(JSC::JSString), alignof(Inspector::ScriptArguments), alignof(JSC::JSModuleLoader), alignof(JSC::JSModuleRecord), alignof(JSC::JSPromise), alignof(JSC::JSInternalPromise), alignof(JSC::SourceOrigin), alignof(JSC::SourceCode), alignof(JSC::JSFunction), alignof(JSC::JSGlobalObject), alignof(WTF::URL), alignof(WTF::String), alignof(JSC::JSValue), alignof(JSC::PropertyName), alignof(JSC::Exception), alignof(JSC::VM), alignof(JSC::ThrowScope), alignof(JSC::CatchScope), alignof(JSC::CallFrame), alignof(JSC::Identifier), alignof(WTF::StringImpl), alignof(WTF::ExternalStringImpl), alignof(WTF::StringView), alignof(Zig::GlobalObject), alignof(ZigException)}; diff --git a/src/javascript/jsc/bindings/headers-handwritten.h b/src/javascript/jsc/bindings/headers-handwritten.h new file mode 100644 index 000000000..133222b39 --- /dev/null +++ b/src/javascript/jsc/bindings/headers-handwritten.h @@ -0,0 +1,95 @@ +typedef uint16_t ZigErrorCode; + +typedef struct ZigString { + const unsigned char *ptr; + size_t len; +} ZigString; +typedef struct ZigErrorType { + ZigErrorCode code; + void *ptr; +} ZigErrorType; +typedef union ErrorableZigStringResult { + ZigString value; + ZigErrorType err; +} ErrorableZigStringResult; +typedef struct ErrorableZigString { + ErrorableZigStringResult result; + bool success; +} ErrorableZigString; +typedef struct ResolvedSource { + ZigString specifier; + ZigString source_code; + ZigString source_url; + uint32_t hash; + uint32_t bytecodecache_fd; +} ResolvedSource; +typedef union ErrorableResolvedSourceResult { + ResolvedSource value; + ZigErrorType err; +} ErrorableResolvedSourceResult; +typedef struct ErrorableResolvedSource { + ErrorableResolvedSourceResult result; + bool success; +} ErrorableResolvedSource; + +typedef uint8_t ZigStackFrameCode; +const ZigStackFrameCode ZigStackFrameCodeNone = 0; +const ZigStackFrameCode ZigStackFrameCodeEval = 1; +const ZigStackFrameCode ZigStackFrameCodeModule = 2; +const ZigStackFrameCode ZigStackFrameCodeFunction = 3; +const ZigStackFrameCode ZigStackFrameCodeGlobal = 4; +const ZigStackFrameCode ZigStackFrameCodeWasm = 5; +const ZigStackFrameCode ZigStackFrameCodeConstructor = 6; + +typedef struct ZigStackFramePosition { + int32_t source_offset; + int32_t line; + int32_t line_start; + int32_t line_stop; + int32_t column_start; + int32_t column_stop; + int32_t expression_start; + int32_t expression_stop; +} ZigStackFramePosition; + +typedef struct ZigStackFrame { + ZigString function_name; + ZigString source_url; + ZigStackFramePosition position; + ZigStackFrameCode code_type; +} ZigStackFrame; + +typedef struct ZigStackTrace { + ZigString *source_lines_ptr; + int32_t *source_lines_numbers; + uint8_t source_lines_len; + uint8_t source_lines_to_collect; + ZigStackFrame *frames_ptr; + uint8_t frames_len; +} ZigStackTrace; + +typedef struct ZigException { + unsigned char code; + uint16_t runtime_type; + ZigString name; + ZigString message; + ZigStackTrace stack; + void *exception; +} ZigException; + +typedef uint8_t JSErrorCode; +const JSErrorCode JSErrorCodeError = 0; +const JSErrorCode JSErrorCodeEvalError = 1; +const JSErrorCode JSErrorCodeRangeError = 2; +const JSErrorCode JSErrorCodeReferenceError = 3; +const JSErrorCode JSErrorCodeSyntaxError = 4; +const JSErrorCode JSErrorCodeTypeError = 5; +const JSErrorCode JSErrorCodeURIError = 6; +const JSErrorCode JSErrorCodeAggregateError = 7; +const JSErrorCode JSErrorCodeOutOfMemoryError = 8; +const JSErrorCode JSErrorCodeStackOverflow = 253; +const JSErrorCode JSErrorCodeUserErrorCode = 254; + +#ifdef __cplusplus +extern "C" ZigErrorCode Zig_ErrorCodeParserError; +#endif
\ No newline at end of file diff --git a/src/javascript/jsc/bindings/headers.zig b/src/javascript/jsc/bindings/headers.zig index 2dfc7eb11..80a048443 100644 --- a/src/javascript/jsc/bindings/headers.zig +++ b/src/javascript/jsc/bindings/headers.zig @@ -37,7 +37,7 @@ pub const __mbstate_t = extern union { pub const __darwin_mbstate_t = __mbstate_t; pub const __darwin_ptrdiff_t = c_long; pub const __darwin_size_t = c_ulong; - + pub const JSC__RegExpPrototype = struct_JSC__RegExpPrototype; pub const JSC__GeneratorPrototype = struct_JSC__GeneratorPrototype; @@ -100,6 +100,7 @@ pub const JSC__MapIteratorPrototype = struct_JSC__MapIteratorPrototype; pub extern fn JSC__JSObject__getArrayLength(arg0: [*c]JSC__JSObject) usize; pub extern fn JSC__JSObject__getAtIndex(arg0: [*c]JSC__JSObject, arg1: [*c]JSC__JSGlobalObject, arg2: [*c]JSC__PropertyName, arg3: u32) JSC__JSValue; pub extern fn JSC__JSObject__putAtIndex(arg0: [*c]JSC__JSObject, arg1: [*c]JSC__JSGlobalObject, arg2: [*c]JSC__PropertyName, arg3: u32) bool; +pub extern fn ZigString__toValue(arg0: ZigString, arg1: [*c]JSC__JSGlobalObject) JSC__JSValue; pub extern fn JSC__JSCell__getObject(arg0: [*c]JSC__JSCell) [*c]JSC__JSObject; pub extern fn JSC__JSCell__getString(arg0: [*c]JSC__JSCell, arg1: [*c]JSC__JSGlobalObject) bWTF__String; pub extern fn JSC__JSCell__getType(arg0: [*c]JSC__JSCell) u8; @@ -167,6 +168,7 @@ pub extern fn JSC__JSGlobalObject__asyncGeneratorPrototype(arg0: [*c]JSC__JSGlob pub extern fn JSC__JSGlobalObject__asyncIteratorPrototype(arg0: [*c]JSC__JSGlobalObject) ?*JSC__AsyncIteratorPrototype; pub extern fn JSC__JSGlobalObject__bigIntPrototype(arg0: [*c]JSC__JSGlobalObject) ?*JSC__BigIntPrototype; pub extern fn JSC__JSGlobalObject__booleanPrototype(arg0: [*c]JSC__JSGlobalObject) [*c]JSC__JSObject; +pub extern fn JSC__JSGlobalObject__createAggregateError(arg0: [*c]JSC__JSGlobalObject, arg1: JSC__JSValue, arg2: u16, arg3: ZigString) JSC__JSValue; pub extern fn JSC__JSGlobalObject__datePrototype(arg0: [*c]JSC__JSGlobalObject) [*c]JSC__JSObject; pub extern fn JSC__JSGlobalObject__errorPrototype(arg0: [*c]JSC__JSGlobalObject) [*c]JSC__JSObject; pub extern fn JSC__JSGlobalObject__functionPrototype(arg0: [*c]JSC__JSGlobalObject) ?*JSC__FunctionPrototype; @@ -271,7 +273,7 @@ pub extern fn JSC__JSValue__toPropertyKeyValue(JSValue0: JSC__JSValue, arg1: [*c pub extern fn JSC__JSValue__toString(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject) [*c]JSC__JSString; pub extern fn JSC__JSValue__toStringOrNull(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject) [*c]JSC__JSString; pub extern fn JSC__JSValue__toWTFString(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject) bWTF__String; -pub extern fn JSC__JSValue__toZigException(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject) ZigException; +pub extern fn JSC__JSValue__toZigException(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, arg2: [*c]ZigException) void; pub extern fn JSC__PropertyName__eqlToIdentifier(arg0: [*c]JSC__PropertyName, arg1: [*c]const JSC__Identifier) bool; pub extern fn JSC__PropertyName__eqlToPropertyName(arg0: [*c]JSC__PropertyName, arg1: [*c]const JSC__PropertyName) bool; pub extern fn JSC__PropertyName__publicName(arg0: [*c]JSC__PropertyName) [*c]const WTF__StringImpl; diff --git a/src/javascript/jsc/bindings/helpers.h b/src/javascript/jsc/bindings/helpers.h index a24ecefcb..e35dd34f4 100644 --- a/src/javascript/jsc/bindings/helpers.h +++ b/src/javascript/jsc/bindings/helpers.h @@ -124,9 +124,9 @@ static ZigString toZigString(JSC::Identifier *str, JSC::JSGlobalObject *global) static WTF::StringView toStringView(ZigString str) { return WTF::StringView(str.ptr, str.len); } -static void throwException(JSC::ThrowScope &scope, ZigString msg, JSC::JSGlobalObject *global) { - auto str = toJSString(msg, global); - scope.throwException(global, JSC::Exception::create(global->vm(), JSC::JSValue(str))); +static void throwException(JSC::ThrowScope &scope, ZigErrorType err, JSC::JSGlobalObject *global) { + scope.throwException( + global, JSC::Exception::create(global->vm(), JSC::JSValue((JSC::EncodedJSValue)err.ptr))); } static ZigString toZigString(JSC::JSValue val, JSC::JSGlobalObject *global) { @@ -144,6 +144,4 @@ static ZigString toZigString(JSC::JSValue val, JSC::JSGlobalObject *global) { return toZigString(str); } -static ZigException ZigExceptionNone = ZigException{ - 0, 0, ZigStringEmpty, ZigStringEmpty, ZigStringEmpty, -1, -1, ZigStringEmpty, nullptr}; -} // namespace Zig +}; // namespace Zig diff --git a/src/javascript/jsc/bindings/static_export.zig b/src/javascript/jsc/bindings/static_export.zig index 83707b984..f8936f0df 100644 --- a/src/javascript/jsc/bindings/static_export.zig +++ b/src/javascript/jsc/bindings/static_export.zig @@ -4,6 +4,7 @@ symbol_name: []const u8, local_name: []const u8, Parent: type, + pub fn Decl(comptime this: *const @This()) std.builtin.TypeInfo.Declaration { return comptime std.meta.declarationInfo(this.Parent, this.local_name); } diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig index e63cee40f..50c40e746 100644 --- a/src/javascript/jsc/javascript.zig +++ b/src/javascript/jsc/javascript.zig @@ -11,7 +11,7 @@ const Bundler = @import("../../bundler.zig").ServeBundler; const js_printer = @import("../../js_printer.zig"); const hash_map = @import("../../hash_map.zig"); const http = @import("../../http.zig"); - +const ImportKind = ast.ImportKind; usingnamespace @import("./node_env_buf_map.zig"); usingnamespace @import("./base.zig"); usingnamespace @import("./webcore/response.zig"); @@ -26,8 +26,12 @@ pub const GlobalClasses = [_]type{ Response.Class, Headers.Class, EventListenerMixin.addEventListener(VirtualMachine), + BuildError.Class, + ResolveError.Class, }; +pub const LazyClasses = [_]type{}; + pub const Module = struct { reload_pending: bool = false, }; @@ -106,7 +110,12 @@ pub const VirtualMachine = struct { threadlocal var source_code_printer: js_printer.BufferPrinter = undefined; threadlocal var source_code_printer_loaded: bool = false; - inline fn _fetch(global: *JSGlobalObject, specifier: string, source: string) !ResolvedSource { + inline fn _fetch( + global: *JSGlobalObject, + specifier: string, + source: string, + log: *logger.Log, + ) !ResolvedSource { std.debug.assert(VirtualMachine.vm_loaded); std.debug.assert(VirtualMachine.vm.global == global); @@ -158,6 +167,10 @@ pub const VirtualMachine = struct { } } + var old = vm.bundler.log; + vm.bundler.log = log; + defer vm.bundler.log = old; + var parse_result = vm.bundler.parse( vm.bundler.allocator, path, @@ -202,7 +215,7 @@ pub const VirtualMachine = struct { return ResolvedSource{ .source_code = ZigString.init(vm.allocator.dupe(u8, source_code_printer.ctx.written) catch unreachable), .specifier = ZigString.init(specifier), - .source_url = ZigString.init(path.pretty), + .source_url = ZigString.init(path.text), .hash = 0, .bytecodecache_fd = 0, }; @@ -211,7 +224,7 @@ pub const VirtualMachine = struct { return ResolvedSource{ .source_code = ZigString.init(try strings.quotedAlloc(VirtualMachine.vm.allocator, path.pretty)), .specifier = ZigString.init(path.text), - .source_url = ZigString.init(path.pretty), + .source_url = ZigString.init(path.text), .hash = 0, .bytecodecache_fd = 0, }; @@ -295,15 +308,32 @@ pub const VirtualMachine = struct { return ErrorableZigString.ok(ZigString.init(result)); } - pub fn fetch(global: *JSGlobalObject, specifier: ZigString, source: ZigString) callconv(.C) ErrorableResolvedSource { + pub fn fetch(ret: *ErrorableResolvedSource, global: *JSGlobalObject, specifier: ZigString, source: ZigString) callconv(.C) void { + var log = logger.Log.init(vm.bundler.allocator); const result = _fetch(global, specifier.slice(), source.slice()) catch |err| { - return ErrorableResolvedSource.errFmt(err, "{s}: \"{s}\"", .{ - @errorName(err), - specifier.slice(), - }); + switch (err) { + error.ParserError => { + std.debug.assert(log.msgs.items.len > 0); + + switch (log.msgs.items.len) { + 1 => { + return ErrorableResolvedSource.err(error.ParserError, BuildError.create(vm.bundler.allocator, &log.msgs.items[0])); + }, + else => { + + }, + } + }, + else => { + ret.* = ErrorableResolvedSource.errFmt(err, "{s}: \"{s}\"", .{ + @errorName(err), + specifier.slice(), + }); + }, + } }; - return ErrorableResolvedSource.ok(result); + ret.* = ErrorableResolvedSource.ok(result); } pub fn loadEntryPoint(this: *VirtualMachine, entry_point: string) !void { @@ -318,15 +348,179 @@ pub const VirtualMachine = struct { } if (promise.status(this.global.vm()) == JSPromise.Status.Rejected) { - var exception = promise.result(this.global.vm()).toZigException(this.global); - Output.prettyErrorln("<r><red>{s}<r><d>:<r> <b>{s}<r>\n<blue>{s}<r>:{d}:{d}\n{s}", .{ - exception.name.slice(), - exception.message.slice(), - exception.sourceURL.slice(), - exception.line, - exception.column, - exception.stack.slice(), - }); + var exception_holder = ZigException.Holder.init(); + var exception = exception_holder.zigException(); + promise.result(this.global.vm()).toZigException(vm.global, exception); + var stderr: std.fs.File = Output.errorStream(); + var buffered = std.io.bufferedWriter(stderr.writer()); + var writer = buffered.writer(); + defer buffered.flush() catch unreachable; + // We are going to print the stack trace backwards + const stack = exception.stack.frames(); + if (stack.len > 0) { + var i = @intCast(i16, stack.len - 1); + + var func_name_pad: usize = 0; + while (i >= 0) : (i -= 1) { + const frame = stack[@intCast(usize, i)]; + func_name_pad = std.math.max(func_name_pad, std.fmt.count("{any}", .{ + frame.nameFormatter(true), + })); + } + + i = @intCast(i16, stack.len - 1); + + while (i >= 0) : (i -= 1) { + const frame = stack[@intCast(usize, i)]; + const file = frame.source_url.slice(); + const func = frame.function_name.slice(); + + try writer.print(" {any}", .{frame.sourceURLFormatter(true)}); + try writer.writeAll(" in "); + try writer.print(" {any}\n", .{frame.nameFormatter(true)}); + + // if (!frame.position.isInvalid()) { + // if (func.len > 0) { + // writer.print( + // comptime Output.prettyFmt("<r><d>{s}<r> {s}{s} - {s}:{d}:{d}\n", true), + // .{ + // if (i > 1) "↓" else "↳", + // frame.code_type.ansiColor(), + // func, + // file, + // frame.position.line, + // frame.position.column_start, + // }, + // ) catch unreachable; + // } else { + // writer.print(comptime Output.prettyFmt("<r><d>{s}<r> {u} - {s}{s}:{d}:{d}\n", true), .{ + // if (i > 1) "↓" else "↳", + // frame.code_type.emoji(), + + // frame.code_type.ansiColor(), + // file, + // frame.position.line, + // frame.position.column_start, + // }) catch unreachable; + // } + // } else { + // if (func.len > 0) { + // writer.print( + // comptime Output.prettyFmt("<r><d>{s}<r> {s}{s} - {s}\n", true), + // .{ + // if (i > 1) "↓" else "↳", + // frame.code_type.ansiColor(), + // func, + // file, + // }, + // ) catch unreachable; + // } else { + // writer.print( + // comptime Output.prettyFmt("<r><d>{s}<r> {u} - {s}{s}\n", true), + // .{ + // if (i > 1) "↓" else "↳", + // frame.code_type.emoji(), + // frame.code_type.ansiColor(), + // file, + // }, + // ) catch unreachable; + // } + // } + } + } + + var line_numbers = exception.stack.source_lines_numbers[0..exception.stack.source_lines_len]; + var max_line: i32 = -1; + for (line_numbers) |line| max_line = std.math.max(max_line, line); + const max_line_number_pad = std.fmt.count("{d}", .{max_line}); + + var source_lines = exception.stack.sourceLineIterator(); + var last_pad: u64 = 0; + while (source_lines.untilLast()) |source| { + const int_size = std.fmt.count("{d}", .{source.line}); + const pad = max_line_number_pad - int_size; + last_pad = pad; + writer.writeByteNTimes(' ', pad) catch unreachable; + writer.print( + comptime Output.prettyFmt("<r><d>{d} | <r>{s}\n", true), + .{ + source.line, + std.mem.trim(u8, source.text, "\n"), + }, + ) catch unreachable; + } + + const name = exception.name.slice(); + const message = exception.message.slice(); + var did_print_name = false; + if (source_lines.next()) |source| { + const int_size = std.fmt.count("{d}", .{source.line}); + const pad = max_line_number_pad - int_size; + writer.writeByteNTimes(' ', pad) catch unreachable; + + std.debug.assert(!stack[0].position.isInvalid()); + var remainder = std.mem.trim(u8, source.text, "\n"); + const prefix = remainder[0..@intCast(usize, stack[0].position.column_start)]; + const underline = remainder[@intCast(usize, stack[0].position.column_start)..@intCast(usize, stack[0].position.column_stop)]; + const suffix = remainder[@intCast(usize, stack[0].position.column_stop)..]; + + writer.print( + comptime Output.prettyFmt("<r><d>{d} |<r> {s}<red>{s}<r>{s}<r>\n<r>", true), + .{ + source.line, + prefix, + underline, + suffix, + }, + ) catch unreachable; + var first_non_whitespace = @intCast(u32, stack[0].position.column_start); + while (first_non_whitespace < source.text.len and source.text[first_non_whitespace] == ' ') { + first_non_whitespace += 1; + } + std.debug.assert(stack.len > 0); + const indent = @intCast(usize, pad) + " | ".len + first_non_whitespace + 1; + + writer.writeByteNTimes(' ', indent) catch unreachable; + writer.print(comptime Output.prettyFmt( + "<red><b>^<r>\n", + true, + ), .{}) catch unreachable; + + if (name.len > 0 and message.len > 0) { + writer.print(comptime Output.prettyFmt(" <r><red><b>{s}<r><d>:<r> <b>{s}<r>\n", true), .{ + name, + message, + }) catch unreachable; + } else if (name.len > 0) { + writer.print(comptime Output.prettyFmt(" <r><b>{s}<r>\n", true), .{name}) catch unreachable; + } else if (message.len > 0) { + writer.print(comptime Output.prettyFmt(" <r><b>{s}<r>\n", true), .{message}) catch unreachable; + } + + did_print_name = true; + } + + if (!did_print_name) { + if (name.len > 0 and message.len > 0) { + writer.print(comptime Output.prettyFmt("<r><red><b>{s}<r><d>:<r> <b>{s}<r>\n", true), .{ + name, + message, + }) catch unreachable; + } else if (name.len > 0) { + writer.print(comptime Output.prettyFmt("<r><b>{s}<r>\n", true), .{name}) catch unreachable; + } else if (message.len > 0) { + writer.print(comptime Output.prettyFmt("<r><b>{s}<r>\n", true), .{name}) catch unreachable; + } + } + + // Output.prettyErrorln("<r><red>{s}<r><d>:<r> <b>{s}<r>\n<blue>{s}<r>:{d}:{d}\n{s}", .{ + // exception.name.slice(), + // exception.message.slice(), + // exception.sourceURL.slice(), + // exception.line, + // exception.column, + // exception.stack.slice(), + // }); } } }; @@ -411,11 +605,7 @@ pub const EventListenerMixin = struct { var fetch_args: [1]js.JSObjectRef = undefined; for (listeners.items) |listener| { - fetch_args[0] = js.JSObjectMake( - vm.ctx, - FetchEvent.Class.get().*, - fetch_event, - ); + fetch_args[0] = FetchEvent.Class.make(vm.global, fetch_event); _ = js.JSObjectCallAsFunction( vm.ctx, @@ -510,3 +700,281 @@ pub const EventListenerMixin = struct { ); } }; + +pub const ResolveError = struct { + msg: logger.Msg, + allocator: *std.mem.Allocator, + referrer: ?Fs.Path = null, + + pub const Class = NewClass( + ResolveError, + .{ + .name = "ResolveError", + .read_only = true, + }, + .{ + .@"referrer" = .{ + .@"get" = getReferrer, + .ro = true, + .ts = d.ts{ .@"return" = "string" }, + }, + .@"message" = .{ + .@"get" = getMessage, + .ro = true, + .ts = d.ts{ .@"return" = "string" }, + }, + .@"name" = .{ + .@"get" = getName, + .ro = true, + .ts = d.ts{ .@"return" = "string" }, + }, + .@"specifier" = .{ + .@"get" = getSpecifier, + .ro = true, + .ts = d.ts{ .@"return" = "string" }, + }, + .@"importKind" = .{ + .@"get" = getImportKind, + .ro = true, + .ts = d.ts{ .@"return" = "string" }, + }, + .@"position" = .{ + .@"get" = getPosition, + .ro = true, + .ts = d.ts{ .@"return" = "string" }, + }, + }, + .{}, + ); + + pub fn create( + msg: logger.Msg, + allocator: *std.mem.Allocator, + ) js.JSObjectRef { + var resolve_error = allocator.create(ResolveError) catch unreachable; + resolve_error.* = ResolveError{ + .msg = msg, + .allocator = allocator, + }; + + return Class.make(VirtualMachine.vm.global, resolve_error); + } + + pub fn getPosition( + this: *ResolveError, + ctx: js.JSContextRef, + thisObject: js.JSObjectRef, + prop: js.JSStringRef, + exception: js.ExceptionRef, + ) js.JSValueRef { + return BuildError.generatePositionObject(this.msg, ctx, exception); + } + + pub fn getMessage( + this: *BuildError, + ctx: js.JSContextRef, + thisObject: js.JSObjectRef, + prop: js.JSStringRef, + exception: js.ExceptionRef, + ) js.JSValueRef { + return ZigString.init(this.msg.data.text).toValue(VirtualMachine.vm.global); + } + + pub fn getSpecifier( + this: *BuildError, + ctx: js.JSContextRef, + thisObject: js.JSObjectRef, + prop: js.JSStringRef, + exception: js.ExceptionRef, + ) js.JSValueRef { + return ZigString.init(this.msg.metadata.Resolve.specifier.slice(this.msg.data.text)).toValue(VirtualMachine.vm.global); + } + + pub fn getImportKind( + this: *BuildError, + ctx: js.JSContextRef, + thisObject: js.JSObjectRef, + prop: js.JSStringRef, + exception: js.ExceptionRef, + ) js.JSValueRef { + return ZigString.init(@tagName(this.msg.metadata.Resolve.import_kind)).toValue(VirtualMachine.vm.global); + } + + pub fn getReferrer( + this: *BuildError, + ctx: js.JSContextRef, + thisObject: js.JSObjectRef, + prop: js.JSStringRef, + exception: js.ExceptionRef, + ) js.JSValueRef { + if (this.referrer) |referrer| { + return ZigString.init(referrer.text).toValue(VirtualMachine.vm.global); + } else { + return js.JSValueMakeNull(ctx); + } + } + + const BuildErrorName = "ResolveError"; + pub fn getName( + this: *BuildError, + ctx: js.JSContextRef, + thisObject: js.JSObjectRef, + prop: js.JSStringRef, + exception: js.ExceptionRef, + ) js.JSValueRef { + return ZigString.init(BuildErrorName).toValue(VirtualMachine.vm.global); + } +}; + +pub const BuildError = struct { + msg: logger.Msg, + // resolve_result: resolver.Result, + allocator: *std.mem.Allocator, + + pub const Class = NewClass( + BuildError, + .{ + .name = "BuildError", + .read_only = true, + }, + .{}, + .{ + .@"message" = .{ + .@"get" = getMessage, + .ro = true, + }, + .@"name" = .{ + .@"get" = getName, + .ro = true, + }, + // This is called "position" instead of "location" because "location" may be confused with Location. + .@"position" = .{ + .@"get" = getPosition, + .ro = true, + }, + }, + ); + + pub fn create( + allocator: *std.mem.Allocator, + msg: *const logger.Msg, + // resolve_result: *const resolver.Result, + ) js.JSObjectRef { + var build_error = allocator.create(BuildError) catch unreachable; + build_error.* = BuildError{ + .msg = msg.*, + // .resolve_result = resolve_result.*, + .allocator = allocator, + }; + + return Class.make(VirtualMachine.vm.global, build_error); + } + + pub fn getPosition( + this: *BuildError, + ctx: js.JSContextRef, + thisObject: js.JSObjectRef, + prop: js.JSStringRef, + exception: js.ExceptionRef, + ) js.JSValueRef { + return generatePositionObject(this.msg, ctx, exception); + } + + pub const PositionProperties = struct { + const _file = ZigString.init("file"); + var file_ptr: js.JSStringRef = null; + pub fn file() js.JSStringRef { + if (file_ptr == null) { + file_ptr = _file.toJSStringRef(); + } + return file_ptr.?; + } + const _namespace = ZigString.init("namespace"); + var namespace_ptr: js.JSStringRef = null; + pub fn namespace() js.JSStringRef { + if (namespace_ptr == null) { + namespace_ptr = _namespace.toJSStringRef(); + } + return namespace_ptr.?; + } + const _line = ZigString.init("line"); + var line_ptr: js.JSStringRef = null; + pub fn line() js.JSStringRef { + if (line_ptr == null) { + line_ptr = _line.toJSStringRef(); + } + return line_ptr.?; + } + const _column = ZigString.init("column"); + var column_ptr: js.JSStringRef = null; + pub fn column() js.JSStringRef { + if (column_ptr == null) { + column_ptr = _column.toJSStringRef(); + } + return column_ptr.?; + } + const _length = ZigString.init("length"); + var length_ptr: js.JSStringRef = null; + pub fn length() js.JSStringRef { + if (length_ptr == null) { + length_ptr = _length.toJSStringRef(); + } + return length_ptr.?; + } + const _lineText = ZigString.init("lineText"); + var lineText_ptr: js.JSStringRef = null; + pub fn lineText() js.JSStringRef { + if (lineText_ptr == null) { + lineText_ptr = _lineText.toJSStringRef(); + } + return lineText_ptr.?; + } + const _offset = ZigString.init("offset"); + var offset_ptr: js.JSStringRef = null; + pub fn offset() js.JSStringRef { + if (offset_ptr == null) { + offset_ptr = _offset.toJSStringRef(); + } + return offset_ptr.?; + } + }; + + pub fn generatePositionObject(msg: logger.Msg, ctx: js.JSContextRef, exception: ExceptionValueRef) js.JSValue { + if (msg.data.location) |location| { + const ref = js.JSObjectMake(ctx, null, null); + js.JSObjectSetProperty(ctx, ref, PositionProperties.lineText(), ZigString.init(location.line_text orelse "").toJSStringRef(), 0, exception); + js.JSObjectSetProperty(ctx, ref, PositionProperties.file(), ZigString.init(location.file).toJSStringRef(), 0, exception); + js.JSObjectSetProperty(ctx, ref, PositionProperties.namespace(), ZigString.init(location.namespace).toJSStringRef(), 0, exception); + js.JSObjectSetProperty(ctx, ref, PositionProperties.line(), js.JSValueMakeNumber(ctx, location.line), 0, exception); + js.JSObjectSetProperty(ctx, ref, PositionProperties.column(), js.JSValueMakeNumber(ctx, location.column), 0, exception); + js.JSObjectSetProperty(ctx, ref, PositionProperties.length(), js.JSValueMakeNumber(ctx, location.length), 0, exception); + js.JSObjectSetProperty(ctx, ref, PositionProperties.offset(), js.JSValueMakeNumber(ctx, location.offset), 0, exception); + return ref; + } + + return js.JSValueMakeNull(ctx); + } + + pub fn getMessage( + this: *BuildError, + ctx: js.JSContextRef, + thisObject: js.JSObjectRef, + prop: js.JSStringRef, + exception: js.ExceptionRef, + ) js.JSValueRef { + return ZigString.init(this.msg.data.text).toValue(VirtualMachine.vm.global); + } + + const BuildErrorName = "BuildError"; + pub fn getName( + this: *BuildError, + ctx: js.JSContextRef, + thisObject: js.JSObjectRef, + prop: js.JSStringRef, + exception: js.ExceptionRef, + ) js.JSValueRef { + return ZigString.init(BuildErrorName).toValue(VirtualMachine.vm.global); + } +}; + +pub const JSPrivateDataTag = JSPrivateDataPtr.Tag; diff --git a/src/javascript/jsc/webcore/response.zig b/src/javascript/jsc/webcore/response.zig index 36c6528b5..06ca76d2f 100644 --- a/src/javascript/jsc/webcore/response.zig +++ b/src/javascript/jsc/webcore/response.zig @@ -121,13 +121,16 @@ pub const Response = struct { // return null; // } - var response = getAllocator(ctx).create(Response) catch return null; + var tup = Repsonse.Class.makeObject( + ctx, + getAllocator(ctx), + ); - response.* = Response{ + tup.ptr.* = Response{ .body = body, .allocator = getAllocator(ctx), }; - return js.JSObjectMake(ctx, Response.Class.get().*, response); + return tup.ref; } }; @@ -290,7 +293,7 @@ pub const Headers = struct { }; } - return js.JSObjectMake(ctx, Headers.Class.get().*, headers); + return Headers.Class.make(ctx, headers); } pub fn finalize( @@ -922,7 +925,7 @@ pub const Request = struct { this.headers = Headers.fromRequestCtx(getAllocator(ctx), this.request_context) catch unreachable; } - return js.JSObjectMake(ctx, Headers.Class.get().*, &this.headers.?); + return Headers.Class.make(ctx, &this.headers.?); } pub fn getIntegrity( this: *Request, @@ -1068,7 +1071,7 @@ pub const FetchEvent = struct { prop: js.JSStringRef, exception: js.ExceptionRef, ) js.JSValueRef { - return js.JSObjectMake(ctx, Request.Class.get().*, &this.request); + return Request.Class.make(ctx, &this.request); } // https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent/respondWith @@ -1096,14 +1099,11 @@ pub const FetchEvent = struct { return js.JSValueMakeUndefined(ctx); } - var ptr = js.JSObjectGetPrivate(arg); - if (ptr == null) { + var response: *Response = GetJSPrivateData(Response, arg) orelse { JSError(getAllocator(ctx), "event.respondWith()'s Response object was invalid. This may be an internal error.", .{}, ctx, exception); this.request_context.sendInternalError(error.respondWithWasInvalid) catch {}; return js.JSValueMakeUndefined(ctx); - } - - var response = @ptrCast(*Response, @alignCast(@alignOf(*Response), ptr.?)); + }; var needs_mime_type = true; var content_length: ?usize = null; diff --git a/src/js_parser/js_parser.zig b/src/js_parser/js_parser.zig index a987351c7..5ca0b3f57 100644 --- a/src/js_parser/js_parser.zig +++ b/src/js_parser/js_parser.zig @@ -17,17 +17,22 @@ usingnamespace @import("imports.zig"); const TemplatePartTuple = std.meta.Tuple(&[_]type{ []E.TemplatePart, logger.Loc }); const ScopeOrderList = std.ArrayListUnmanaged(?ScopeOrder); -pub fn ExpressionTransposer(comptime ctx: type, visitor: fn (ptr: *ctx, arg: Expr, state: anytype) Expr) type { +pub fn ExpressionTransposer( + comptime Kontext: type, + visitor: fn (ptr: *Kontext, arg: Expr, state: anytype) Expr, +) type { return struct { + pub const Context = Kontext; + pub const This = @This(); context: *Context, - pub fn init(c: *Context) @This() { - return @This(){ + pub fn init(c: *Context) This { + return This{ .context = c, }; } - pub fn maybeTransposeIf(self: *@This(), arg: Expr, state: anytype) Expr { + pub fn maybeTransposeIf(self: *This, arg: Expr, state: anytype) Expr { switch (arg.data) { .e_if => |ex| { ex.yes = self.maybeTransposeIf(ex.yes, state); @@ -39,7 +44,6 @@ pub fn ExpressionTransposer(comptime ctx: type, visitor: fn (ptr: *ctx, arg: Exp }, } } - pub const Context = ctx; }; } diff --git a/src/js_printer.zig b/src/js_printer.zig index 2f02e4208..727a849b6 100644 --- a/src/js_printer.zig +++ b/src/js_printer.zig @@ -127,10 +127,6 @@ pub fn NewPrinter( comptime rewrite_esm_to_cjs: bool, comptime speedy: bool, ) type { - // comptime const comptime_buf_len = 64; - // comptime var comptime_buf = [comptime_buf_len]u8{}; - // comptime var comptime_buf_i: usize = 0; - return struct { symbols: Symbol.Map, import_records: []importRecord.ImportRecord, @@ -153,53 +149,6 @@ pub fn NewPrinter( prev_stmt_tag: Stmt.Tag = .s_empty, const Printer = @This(); - pub fn comptime_flush(p: *Printer) void {} - - // pub fn comptime_flush(p: *Printer) callconv(.Inline) void { - // const result = comptime { - // if (comptime_buf_i > 0) { - // return comptime_buf[0..comptime_buf_i]; - // } else { - // return ""; - // } - // }; - - // if (result.len) { - // p.print(result); - // comptime { - // if (comptime_buf_i > 0) { - // comptime_buf_i = 0; - // while (comptime_buf_i < comptime_buf_i) { - // comptime_buf[comptime_buf_i] = 0; - // comptime_buf_i += 1; - // } - // comptime_buf_i = 0; - // } - // } - // } - // } - // pub fn comptime_print(p: *Printer, str: comptime []const u8) callconv(.Inline) void { - // comptime const needsFlush = (str.len + comptime_buf_i >= comptime_buf_len - 1); - // if (needsFlush) { - // p.comptime_flush(); - // } - - // comptime { - // if (str.len > 63) { - // @compileError("comptime_print buffer overflow"); - // return; - // } - // } - - // comptime { - // comptime str_i = 0; - // while (str_i < str.len) { - // comptime_buf[comptime_buf_i] = str[str_i]; - // comptime_buf_i += 1; - // str_i += 1; - // } - // } - // } pub fn writeAll(p: *Printer, bytes: anytype) anyerror!void { p.print(bytes); @@ -234,8 +183,6 @@ pub fn NewPrinter( } pub fn printIndent(p: *Printer) void { - comptime_flush(p); - if (p.options.indent == 0) { return; } @@ -2149,7 +2096,6 @@ pub fn NewPrinter( debug("<printStmt>: {s}\n", .{stmt}); defer debug("</printStmt>: {s}\n", .{stmt}); - p.comptime_flush(); p.addSourceMapping(stmt.loc); switch (stmt.data) { diff --git a/src/linker.zig b/src/linker.zig index 44439a0ec..4e90d0072 100644 --- a/src/linker.zig +++ b/src/linker.zig @@ -160,12 +160,10 @@ pub fn NewLinker(comptime BundlerType: type) type { unreachable; } - pub inline fn nodeModuleBundleImportPath(this: *const ThisLinker) string { return if (this.options.node_modules_bundle_url.len > 0) this.options.node_modules_bundle_url else this.options.node_modules_bundle.?.bundle.import_from_name; } - // pub const Scratch = struct { // threadlocal var externals: std.ArrayList(u32) = undefined; // threadlocal var has_externals: std.ArrayList(u32) = undefined; @@ -317,24 +315,26 @@ pub fn NewLinker(comptime BundlerType: type) type { error.ModuleNotFound => { if (Resolver.isPackagePath(import_record.path.text)) { if (linker.options.platform.isWebLike() and Options.ExternalModules.isNodeBuiltin(import_record.path.text)) { - try linker.log.addRangeErrorFmt( + try linker.log.addResolveError( &result.source, import_record.range, linker.allocator, "Could not resolve: \"{s}\". Try setting --platform=\"node\"", .{import_record.path.text}, + import_record.kind, ); } else { - try linker.log.addRangeErrorFmt( + try linker.log.addResolveError( &result.source, import_record.range, linker.allocator, "Could not resolve: \"{s}\". Maybe you need to \"npm install\" (or yarn/pnpm)?", .{import_record.path.text}, + import_record.kind, ); } } else { - try linker.log.addRangeErrorFmt( + try linker.log.addResolveError( &result.source, import_record.range, linker.allocator, @@ -342,6 +342,7 @@ pub fn NewLinker(comptime BundlerType: type) type { .{ import_record.path.text, }, + import_record.kind, ); continue; } diff --git a/src/logger.zig b/src/logger.zig index 9ab04b398..fdf628ef7 100644 --- a/src/logger.zig +++ b/src/logger.zig @@ -1,6 +1,7 @@ const std = @import("std"); const Api = @import("./api/schema.zig").Api; - +const js = @import("./javascript/jsc/bindings/bindings.zig"); +const ImportKind = @import("./import_record.zig").ImportKind; usingnamespace @import("global.zig"); const fs = @import("fs.zig"); @@ -161,11 +162,42 @@ pub const Data = struct { } }; +pub const BabyString = packed struct { + offset: u16, + len: u16, + + pub fn in(parent: string, text: string) BabyString { + return BabyString{ + .offset = @truncate(u16, std.mem.indexOf(u8, parent, text) orelse unreachable), + .len = @truncate(u16, text.len), + }; + } + + pub fn slice(container: string) string { + return container[offset..][0..len]; + } +}; + pub const Msg = struct { kind: Kind = Kind.err, data: Data, + metadata: Metadata = .{ .build = 0 }, notes: ?[]Data = null, + pub const Metadata = union(Tag) { + build: u0, + resolve: Resolve, + pub const Tag = enum(u8) { + build = 1, + resolve = 2, + }; + + pub const Resolve = struct { + specifier: BabyString, + import_kind: ImportKind, + }; + }; + pub fn toAPI(this: *const Msg, allocator: *std.mem.Allocator) Api.Message { var msg = Api.Message{ .kind = this.kind.toAPI(), @@ -183,6 +215,7 @@ pub const Msg = struct { return msg; } + pub fn toAPIFromList(comptime ListType: type, list: ListType, allocator: *std.mem.Allocator) ![]Api.Message { var out_list = try allocator.alloc(Api.Msg, list.items.len); for (list.items) |item, i| { @@ -316,6 +349,32 @@ pub const Log = struct { }); } + pub fn addResolveError( + log: *Log, + source: *const Source, + r: Range, + allocator: *std.mem.Allocator, + comptime fmt: string, + args: anytype, + import_kind: ImportKind, + ) !void { + const text = try std.fmt.allocPrint(allocator, fmt, args); + // TODO: fix this. this is stupid, it should be returned in allocPrint. + const specifier = BabyString.in(text, args.@"0"); + log.errors += 1; + try log.addMsg( + Msg{ + .kind = .err, + .data = rangeData( + source, + r, + text, + ), + .metadata = .{ .resolve = Msg.Metadata.Resolve{ .specifier = specifier, .import_kind = import_kind } }, + }, + ); + } + pub fn addRangeError(log: *Log, source: ?*const Source, r: Range, text: string) !void { log.errors += 1; try log.addMsg(Msg{ @@ -432,6 +491,15 @@ pub const Log = struct { try msg.writeFormat(to); } } + + pub fn toZigException(this: *const Log, allocator: *std.mem.Allocator) *js.ZigException.Holder { + var holder = try allocator.create(js.ZigException.Holder); + holder.* = js.ZigException.Holder.init(); + var zig_exception: *js.ZigException = holder.zigException(); + zig_exception.exception = this; + zig_exception.code = js.JSErrorCode.BundlerError; + return holder; + } }; pub inline fn usize2Loc(loc: usize) Loc { diff --git a/src/runtime/errors.ts b/src/runtime/errors.ts new file mode 100644 index 000000000..ad54cc322 --- /dev/null +++ b/src/runtime/errors.ts @@ -0,0 +1,82 @@ +var __BuildError; +var __ResolveError; +var __ImportKind; +{ + enum ImportKind { + entry_point = 0, + stmt = 1, + require = 2, + dynamic = 3, + require_resolve = 4, + at = 5, + at_conditional = 6, + url = 7, + } + + type ErrorPosition = { + file: string; + namespace: string; + line: number; // 1-based + column: number; // 0-based, byte offset relative to lineText + length: number; // in bytes + /** line of text, possibly empty */ + lineText: string; + /** byte offset relative to the start of the file */ + offset: number; + }; + + interface BuildErrorImplementation { + position: ErrorPosition; + name: string; + message: string; + } + + interface ResolveErrorImplementation extends BuildErrorImplementation { + specifier: string; + importKind: ImportKind; + } + + class BuildError extends Error { + constructor(data: BuildErrorImplementation) { + super(data.message); + this.name = data.name; + this.data = data; + } + data: BuildErrorImplementation; + + get position() { + return this.data.position; + } + + get [Symbol.toStringTag]() { + return `${this.name}: ${this.message}`; + } + } + + class ResolveError extends BuildError { + constructor(data: ResolveErrorImplementation) { + super(data); + this.name = data.name; + this.data = data; + } + data: ResolveErrorImplementation; + + get importKind() { + return this.data.importKind; + } + + get specifier() { + return this.data.specifier || ""; + } + } + + __ResolveError = ResolveError; + __BuildError = BuildError; + __ImportKind = ImportKind; +} + +export { + __ResolveError as ResolveError, + __BuildError as BuildError, + __ImportKind as ImportKind, +}; diff --git a/src/runtime/index.ts b/src/runtime/index.ts index 873666412..1566c70b8 100644 --- a/src/runtime/index.ts +++ b/src/runtime/index.ts @@ -1,2 +1,3 @@ export * from "./hmr"; +export * from "./errors"; export * from "../runtime.js"; diff --git a/src/tagged_pointer.zig b/src/tagged_pointer.zig new file mode 100644 index 000000000..332464007 --- /dev/null +++ b/src/tagged_pointer.zig @@ -0,0 +1,186 @@ +const std = @import("std"); + +const TagSize = u15; +const AddressableSize = u49; + +const TaggedPointer = packed struct { + _ptr: AddressableSize, + data: TagSize, + + pub inline fn init(ptr: anytype, data: TagSize) TaggedPointer { + const Ptr = @TypeOf(ptr); + + if (comptime @typeInfo(Ptr) != .Pointer and Ptr != ?*c_void) { + @compileError(@typeName(Ptr) ++ " must be a ptr, received: " ++ @tagName(@typeInfo(Ptr))); + } + + const address = @ptrToInt(ptr); + + return TaggedPointer{ + ._ptr = @truncate(AddressableSize, address), + .data = data, + }; + } + + pub inline fn get(this: TaggedPointer, comptime Type: type) *Type { + return @intToPtr(*Type, @intCast(usize, this._ptr)); + } + + pub inline fn from(val: anytype) TaggedPointer { + const ValueType = @TypeOf(val); + return switch (ValueType) { + f64, i64, u64 => @bitCast(TaggedPointer, val), + ?*c_void, *c_void => @bitCast(TaggedPointer, @ptrToInt(val)), + else => @compileError("Unsupported type: " ++ @typeName(ValueType)), + }; + } + + pub inline fn to(this: TaggedPointer) *c_void { + return @intToPtr(*c_void, @bitCast(u64, this)); + } +}; + +pub fn TaggedPointerUnion(comptime Types: anytype) type { + const TagType: type = tag_break: { + var enumFields: [Types.len]std.builtin.TypeInfo.EnumField = undefined; + var decls = [_]std.builtin.TypeInfo.Declaration{}; + + inline for (Types) |field, i| { + enumFields[i] = .{ + .name = @typeName(field), + .value = std.math.maxInt(TagSize) - 1 - i, + }; + } + + break :tag_break @Type(.{ + .Enum = .{ + .layout = .Auto, + .tag_type = TagSize, + .fields = &enumFields, + .decls = &decls, + .is_exhaustive = false, + }, + }); + }; + + return struct { + pub const Tag = TagType; + repr: TaggedPointer, + const This = @This(); + fn assert_type(comptime Type: type) void { + if (!comptime @hasField(Tag, @typeName(Type))) { + @compileError("TaggedPointerUnion does not have " ++ @typeName(Type) ++ "."); + } + } + pub inline fn get(this: This, comptime Type: anytype) ?*Type { + comptime assert_type(Type); + + return if (this.is(Type)) this.as(Type) else null; + } + + pub inline fn tag(this: This) TagType { + return @intToEnum(TagType, this.repr.data); + } + + /// unsafely cast a tagged pointer to a specific type, without checking that it's really that type + pub inline fn as(this: This, comptime Type: type) *Type { + comptime assert_type(Type); + return this.repr.get(Type); + } + + pub inline fn is(this: This, comptime Type: type) bool { + comptime assert_type(Type); + return this.repr.data == comptime @enumToInt(@field(Tag, @typeName(Type))); + } + + pub inline fn isValidPtr(_ptr: ?*c_void) bool { + return This.isValid(This.from(_ptr)); + } + + pub inline fn isValid(this: This) bool { + return switch (this.repr.data) { + @enumToInt( + @field(Tag, @typeName(Types[Types.len - 1])), + )...@enumToInt( + @field(Tag, @typeName(Types[0])), + ) => true, + else => false, + }; + } + + pub inline fn from(_ptr: ?*c_void) This { + return This{ .repr = TaggedPointer.from(_ptr) }; + } + + pub inline fn ptr(this: This) *c_void { + return this.repr.to(); + } + + pub inline fn init(_ptr: anytype) This { + const Type = std.meta.Child(@TypeOf(_ptr)); + + // there will be a compiler error if the passed in type doesn't exist in the enum + return This{ .repr = TaggedPointer.init(_ptr, @enumToInt(@field(Tag, @typeName(Type)))) }; + } + }; +} + +test "TaggedPointerUnion" { + const IntPrimtiive = struct { val: u32 = 0 }; + const StringPrimitive = struct { val: []const u8 = "" }; + const Object = struct { blah: u32, val: u32 }; + // const Invalid = struct { + // wrong: bool = true, + // }; + const Union = TaggedPointerUnion(.{ IntPrimtiive, StringPrimitive, Object }); + var str = try std.heap.c_allocator.create(StringPrimitive); + str.* = StringPrimitive{ .val = "hello!" }; + var un = Union.init(str); + try std.testing.expect(un.is(StringPrimitive)); + try std.testing.expectEqualStrings(un.as(StringPrimitive).val, "hello!"); + try std.testing.expect(!un.is(IntPrimtiive)); + const num = try std.heap.c_allocator.create(IntPrimtiive); + num.val = 9999; + + var un2 = Union.init(num); + + try std.testing.expect(un2.as(IntPrimtiive).val == 9999); + + try std.testing.expect(un.tag() == .StringPrimitive); + try std.testing.expect(un2.tag() == .IntPrimtiive); + + un2.repr.data = 0; + try std.testing.expect(un2.tag() != .IntPrimtiive); + try std.testing.expect(un2.get(IntPrimtiive) == null); + // try std.testing.expect(un2.is(Invalid) == false); +} + +test "TaggedPointer" { + const Hello = struct { + what: []const u8, + }; + + var hello_struct_ptr = try std.heap.c_allocator.create(Hello); + hello_struct_ptr.* = Hello{ .what = "hiiii" }; + var tagged = TaggedPointer.init(hello_struct_ptr, 0); + try std.testing.expectEqual(tagged.get(Hello), hello_struct_ptr); + try std.testing.expectEqualStrings(tagged.get(Hello).what, hello_struct_ptr.what); + tagged = TaggedPointer.init(hello_struct_ptr, 100); + try std.testing.expectEqual(tagged.get(Hello), hello_struct_ptr); + try std.testing.expectEqualStrings(tagged.get(Hello).what, hello_struct_ptr.what); + tagged = TaggedPointer.init(hello_struct_ptr, std.math.maxInt(TagSize) - 500); + try std.testing.expectEqual(tagged.get(Hello), hello_struct_ptr); + try std.testing.expectEqual(tagged.data, std.math.maxInt(TagSize) - 500); + try std.testing.expectEqualStrings(tagged.get(Hello).what, hello_struct_ptr.what); + + var i: TagSize = 0; + while (i < std.math.maxInt(TagSize) - 1) : (i += 1) { + hello_struct_ptr = try std.heap.c_allocator.create(Hello); + const what = try std.fmt.allocPrint(std.heap.c_allocator, "hiiii {d}", .{i}); + hello_struct_ptr.* = Hello{ .what = what }; + try std.testing.expectEqualStrings(TaggedPointer.from(TaggedPointer.init(hello_struct_ptr, i).to()).get(Hello).what, what); + var this = TaggedPointer.from(TaggedPointer.init(hello_struct_ptr, i).to()); + try std.testing.expect(this.data == i); + try std.testing.expect(this.data != i + 1); + } +} |