diff options
Diffstat (limited to 'src/javascript/jsc/javascript.zig')
-rw-r--r-- | src/javascript/jsc/javascript.zig | 516 |
1 files changed, 492 insertions, 24 deletions
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; |