From ff635551436123022ba3980b39580d53973c80a2 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Sat, 24 Jun 2023 06:02:16 -0700 Subject: Rewrite Bun's runtime CommonJS loader (#3379) * wip changes for CommonJS * this rewrite is almost complete * even more code * wip * Remove usages of `import.meta.require` from builtins * Remove usages of require * Regenerate * :scissors: builtin rewrite commonjs in printer * Use lazy custom getters for import.meta * fixups * Remove depd * ugh * still crashing * fixup undici * comment out import.meta.require.resolve temporarily not a real solution but it stops the crashes * Redo import.meta.primordials * Builtins now have a `builtin://` protocol in source origin * Seems to work? * Finsih getting rid of primordials * switcharoo * No more function * just one more bug * Update launch.json * Implement `require.main` * :scissors: * Bump WebKit * Fixup import cycles * Fixup improt cycles * export more things * Implement `createCommonJSModule` builtin * More exports * regenerate * i broke some stuff * some of these tests work now * We lost the encoding * Sort of fix zlib * Sort of fix util * Update events.js * bump * bump * bump * Fix missing export in fs * fix some bugs with builtin esm modules (stream, worker_threads, events). its not perfect yet. * fix some other internal module bugs * oops * fix some extra require default stuff * uncomment this file but it crsahes on my machine * tidy code here * fixup tls exports * make simdutf happier * Add hasPrefix binding * Add test for `require.main` * Fix CommonJS evaluation order race condition * Make node:http load faster * Add missing exports to tls.js * Use the getter * Regenerate builtins * Fix assertion failure in Bun.write() * revamp dotEnv parser (#3347) - fixes `strings.indexOfAny()` - fixes OOB array access fixes #411 fixes #2823 fixes #3042 * fix tests for `expect()` (#3384) - extend test job time-out for `darwin-aarch64` * `expect().resolves` and `expect().rejects` (#3318) * Move expect and snapshots to their own files * expect().resolves and expect().rejects * Fix promise being added to unhandled rejection list * Handle timeouts in expect() * wip merge * Fix merge issue --------- Co-authored-by: Jarred Sumner Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> * fixup min/memcopy (#3388) * Fix crash in builtins * Don't attempt to evaluate modules with no source code * Update WebCoreJSBuiltins.cpp * Update WebCoreJSBuiltins.cpp * Update WebCoreJSBuiltins.cpp * Fix crash * cleanup * Fix test cc @paperdave * Fixup Undici * Fix issue in node:http * Create util-deprecate.mjs * Fix several bugs * Use the identifier * Support error.code in `util.deprecate` * make the CJs loader slightly more resilient * Update WebCoreJSBuiltins.cpp * Fix macros --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> Co-authored-by: dave caruso Co-authored-by: Alex Lam S.L Co-authored-by: Ashcon Partovi Co-authored-by: Ciro Spaciari --- src/string.zig | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/string.zig') diff --git a/src/string.zig b/src/string.zig index 2c75dcc33..54af2ba68 100644 --- a/src/string.zig +++ b/src/string.zig @@ -164,8 +164,13 @@ pub const WTFStringImplStruct = extern struct { return std.mem.Allocator{ .ptr = self, .vtable = StringImplAllocator.VTablePtr }; } + pub fn hasPrefix(self: WTFStringImpl, text: []const u8) bool { + return Bun__WTFStringImpl__hasPrefix(self, text.ptr, text.len); + } + extern fn Bun__WTFStringImpl__deref(self: WTFStringImpl) void; extern fn Bun__WTFStringImpl__ref(self: WTFStringImpl) void; + extern fn Bun__WTFStringImpl__hasPrefix(self: *const WTFStringImplStruct, offset: [*]const u8, length: usize) bool; }; pub const StringImplAllocator = struct { @@ -616,6 +621,10 @@ pub const String = extern struct { } pub fn hasPrefixComptime(this: String, comptime value: []const u8) bool { + if (this.tag == .WTFStringImpl) { + return this.value.WTFStringImpl.hasPrefix(value); + } + return this.toZigString().substring(0, value.len).eqlComptime(value); } -- cgit v1.2.3 From 68e6fe00a4be7857f474cb3aa80e0d876499e3f8 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Wed, 28 Jun 2023 21:11:06 -0700 Subject: Use `bun.String` for `ZigException` (#3451) * Use `bun.String` for `ZigException` * woopsie --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> --- src/bun.js/bindings/ZigGlobalObject.cpp | 4 +- src/bun.js/bindings/bindings.cpp | 69 +++++----------- src/bun.js/bindings/exports.zig | 130 ++++++++++++++++++++---------- src/bun.js/bindings/headers-handwritten.h | 16 ++-- src/bun.js/javascript.zig | 72 ++++++++++------- src/bun.js/test/jest.zig | 18 ++--- src/js/out/modules/node/path.js | 2 +- src/js/out/modules/node/stream.web.js | 2 +- src/logger.zig | 4 +- src/string.zig | 24 ++++++ 10 files changed, 198 insertions(+), 143 deletions(-) (limited to 'src/string.zig') diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index 4b4edf097..9ff3ebf93 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -390,7 +390,7 @@ static String computeErrorInfoWithoutPrepareStackTrace(JSC::VM& vm, Vectorline.zeroBasedInt(); remappedFrames[i].position.column_start = sourcePositions->startColumn.zeroBasedInt() + 1; diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index 68aef29dd..141215ebe 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -3303,7 +3303,7 @@ bool JSC__JSValue__stringIncludes(JSC__JSValue value, JSC__JSGlobalObject* globa static void populateStackFrameMetadata(JSC::VM& vm, const JSC::StackFrame* stackFrame, ZigStackFrame* frame) { - frame->source_url = Zig::toZigString(stackFrame->sourceURL(vm)); + frame->source_url = Bun::toString(stackFrame->sourceURL(vm)); if (stackFrame->isWasmFrame()) { frame->code_type = ZigStackFrameCodeWasm; @@ -3340,37 +3340,11 @@ static void populateStackFrameMetadata(JSC::VM& vm, const JSC::StackFrame* stack JSC::JSObject* callee = JSC::jsCast(calleeCell); - // Does the code block have a user-defined name property? - JSC::JSValue name = callee->getDirect(vm, 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(callee)) { - - WTF::String actualName = function->name(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(callee)) { - // Based on JSC::InternalFunction::calculatedDisplayName, skipping the "displayName" property - frame->function_name = Zig::toZigString(function->name()); - } + frame->function_name = Bun::toString(JSC::getCalculatedDisplayName(vm, callee)); } // 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, +static void populateStackFramePosition(const JSC::StackFrame* stackFrame, BunString* source_lines, int32_t* source_line_numbers, uint8_t source_lines_count, ZigStackFramePosition* position) { @@ -3440,7 +3414,7 @@ static void populateStackFramePosition(const JSC::StackFrame* stackFrame, ZigStr // 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_lines[0] = Bun::toString(sourceString.substring(lineStart, lineStop - lineStart).toStringWithoutCopying()); source_line_numbers[0] = line; if (lineStart > 0) { @@ -3457,8 +3431,7 @@ static void populateStackFramePosition(const JSC::StackFrame* stackFrame, ZigStr } // 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_lines[source_line_i] = Bun::toString(sourceString.substring(byte_offset_in_source_string, end_of_line_offset - byte_offset_in_source_string + 1).toStringWithoutCopying()); source_line_numbers[source_line_i] = line - source_line_i; source_line_i++; @@ -3546,30 +3519,30 @@ static void fromErrorInstance(ZigException* except, JSC::JSGlobalObject* global, except->code = 8; } if (except->code == SYNTAX_ERROR_CODE) { - except->message = Zig::toZigString(err->sanitizedMessageString(global)); + except->message = Bun::toString(err->sanitizedMessageString(global)); } else if (JSC::JSValue message = obj->getIfPropertyExists(global, vm.propertyNames->message)) { - except->message = Zig::toZigString(message, global); + except->message = Bun::toString(global, message); } else { - except->message = Zig::toZigString(err->sanitizedMessageString(global)); + except->message = Bun::toString(err->sanitizedMessageString(global)); } - except->name = Zig::toZigString(err->sanitizedNameString(global)); + except->name = Bun::toString(err->sanitizedNameString(global)); except->runtime_type = err->runtimeTypeForCause(); auto clientData = WebCore::clientData(vm); if (except->code != SYNTAX_ERROR_CODE) { if (JSC::JSValue syscall = obj->getIfPropertyExists(global, clientData->builtinNames().syscallPublicName())) { - except->syscall = Zig::toZigString(syscall, global); + except->syscall = Bun::toString(global, syscall); } if (JSC::JSValue code = obj->getIfPropertyExists(global, clientData->builtinNames().codePublicName())) { - except->code_ = Zig::toZigString(code, global); + except->code_ = Bun::toString(global, code); } if (JSC::JSValue path = obj->getIfPropertyExists(global, clientData->builtinNames().pathPublicName())) { - except->path = Zig::toZigString(path, global); + except->path = Bun::toString(global, path); } if (JSC::JSValue fd = obj->getIfPropertyExists(global, Identifier::fromString(vm, "fd"_s))) { @@ -3585,7 +3558,7 @@ static void fromErrorInstance(ZigException* except, JSC::JSGlobalObject* global, if (getFromSourceURL) { if (JSC::JSValue sourceURL = obj->getIfPropertyExists(global, vm.propertyNames->sourceURL)) { - except->stack.frames_ptr[0].source_url = Zig::toZigString(sourceURL, global); + except->stack.frames_ptr[0].source_url = Bun::toString(global, sourceURL); if (JSC::JSValue column = obj->getIfPropertyExists(global, vm.propertyNames->column)) { except->stack.frames_ptr[0].position.column_start = column.toInt32(global); @@ -3597,7 +3570,7 @@ static void fromErrorInstance(ZigException* except, JSC::JSGlobalObject* global, if (JSC::JSValue lineText = obj->getIfPropertyExists(global, JSC::Identifier::fromString(vm, "lineText"_s))) { if (JSC::JSString* jsStr = lineText.toStringOrNull(global)) { auto str = jsStr->value(global); - except->stack.source_lines_ptr[0] = Zig::toZigString(str); + except->stack.source_lines_ptr[0] = Bun::toString(str); except->stack.source_lines_numbers[0] = except->stack.frames_ptr[0].position.line; except->stack.source_lines_len = 1; except->remapped = true; @@ -3620,7 +3593,7 @@ void exceptionFromString(ZigException* except, JSC::JSValue value, JSC::JSGlobal if (JSC::JSObject* obj = JSC::jsDynamicCast(value)) { if (obj->hasProperty(global, global->vm().propertyNames->name)) { auto name_str = obj->getIfPropertyExists(global, global->vm().propertyNames->name).toWTFString(global); - except->name = Zig::toZigString(name_str); + except->name = Bun::toString(name_str); if (name_str == "Error"_s) { except->code = JSErrorCodeError; } else if (name_str == "EvalError"_s) { @@ -3642,14 +3615,14 @@ void exceptionFromString(ZigException* except, JSC::JSValue value, JSC::JSGlobal if (JSC::JSValue message = obj->getIfPropertyExists(global, global->vm().propertyNames->message)) { if (message) { - except->message = Zig::toZigString( + except->message = Bun::toString( message.toWTFString(global)); } } if (JSC::JSValue sourceURL = obj->getIfPropertyExists(global, global->vm().propertyNames->sourceURL)) { if (sourceURL) { - except->stack.frames_ptr[0].source_url = Zig::toZigString( + except->stack.frames_ptr[0].source_url = Bun::toString( sourceURL.toWTFString(global)); except->stack.frames_len = 1; } @@ -3678,9 +3651,7 @@ void exceptionFromString(ZigException* except, JSC::JSValue value, JSC::JSGlobal } scope.release(); - auto ref = OpaqueJSString::tryCreate(str); - except->message = ZigString { ref->characters8(), ref->length() }; - ref->ref(); + except->message = Bun::toString(str); } void JSC__VM__releaseWeakRefs(JSC__VM* arg0) @@ -3790,8 +3761,8 @@ void JSC__JSValue__toZigException(JSC__JSValue JSValue0, JSC__JSGlobalObject* ar JSC::JSValue value = JSC::JSValue::decode(JSValue0); if (value == JSC::JSValue {}) { exception->code = JSErrorCodeError; - exception->name = Zig::toZigString("Error"_s); - exception->message = Zig::toZigString("Unknown error"_s); + exception->name = Bun::toString("Error"_s); + exception->message = Bun::toString("Unknown error"_s); return; } diff --git a/src/bun.js/bindings/exports.zig b/src/bun.js/bindings/exports.zig index 213291e7b..73a26e4be 100644 --- a/src/bun.js/bindings/exports.zig +++ b/src/bun.js/bindings/exports.zig @@ -29,6 +29,7 @@ const Backtrace = @import("../../crash_reporter.zig"); const JSPrinter = bun.js_printer; const JSLexer = bun.js_lexer; const typeBaseName = @import("../../meta.zig").typeBaseName; +const String = bun.String; pub const ZigGlobalObject = extern struct { pub const shim = Shimmer("Zig", "GlobalObject", @This()); @@ -438,7 +439,7 @@ pub const Process = extern struct { }; pub const ZigStackTrace = extern struct { - source_lines_ptr: [*c]ZigString, + source_lines_ptr: [*c]bun.String, source_lines_numbers: [*c]i32, source_lines_len: u8, source_lines_to_collect: u8, @@ -456,23 +457,24 @@ pub const ZigStackTrace = extern struct { { var source_lines_iter = this.sourceLineIterator(); - var source_line_len: usize = 0; - var count: usize = 0; - while (source_lines_iter.next()) |source| { - count += 1; - source_line_len += source.text.len; - } + var source_line_len = source_lines_iter.getLength(); - if (count > 0 and source_line_len > 0) { - var source_lines = try allocator.alloc(Api.SourceLine, count); + if (source_line_len > 0) { + var source_lines = try allocator.alloc(Api.SourceLine, @intCast(usize, @max(source_lines_iter.i, 0))); var source_line_buf = try allocator.alloc(u8, source_line_len); source_lines_iter = this.sourceLineIterator(); var remain_buf = source_line_buf[0..]; var i: usize = 0; while (source_lines_iter.next()) |source| { - bun.copy(u8, remain_buf, source.text); - const copied_line = remain_buf[0..source.text.len]; - remain_buf = remain_buf[source.text.len..]; + const text = source.text.slice(); + defer source.text.deinit(); + defer bun.copy( + u8, + remain_buf, + text, + ); + const copied_line = remain_buf[0..text.len]; + remain_buf = remain_buf[text.len..]; source_lines[i] = .{ .text = copied_line, .line = source.line }; i += 1; } @@ -508,9 +510,18 @@ pub const ZigStackTrace = extern struct { pub const SourceLine = struct { line: i32, - text: string, + text: ZigString.Slice, }; + pub fn getLength(this: *SourceLineIterator) usize { + var count: usize = 0; + for (this.trace.source_lines_ptr[0..@intCast(usize, this.i)]) |*line| { + count += line.length(); + } + + return count; + } + pub fn untilLast(this: *SourceLineIterator) ?SourceLine { if (this.i < 1) return null; return this.next(); @@ -522,7 +533,7 @@ pub const ZigStackTrace = extern struct { const source_line = this.trace.source_lines_ptr[@intCast(usize, this.i)]; const result = SourceLine{ .line = this.trace.source_lines_numbers[@intCast(usize, this.i)], - .text = source_line.slice(), + .text = source_line.toUTF8(bun.default_allocator), }; this.i -= 1; return result; @@ -541,21 +552,28 @@ pub const ZigStackTrace = extern struct { }; pub const ZigStackFrame = extern struct { - function_name: ZigString, - source_url: ZigString, + function_name: String, + source_url: String, position: ZigStackFramePosition, code_type: ZigStackFrameCode, /// This informs formatters whether to display as a blob URL or not remapped: bool = false, + pub fn deinit(this: *ZigStackFrame) void { + this.function_name.deref(); + this.source_url.deref(); + } + pub fn toAPI(this: *const ZigStackFrame, root_path: string, origin: ?*const ZigURL, allocator: std.mem.Allocator) !Api.StackFrame { var frame: Api.StackFrame = comptime std.mem.zeroes(Api.StackFrame); - if (this.function_name.len > 0) { - frame.function_name = try allocator.dupe(u8, this.function_name.slice()); + if (!this.function_name.isEmpty()) { + var slicer = this.function_name.toUTF8(allocator); + defer slicer.deinit(); + frame.function_name = (try slicer.clone(allocator)).slice(); } - if (this.source_url.len > 0) { + if (!this.source_url.isEmpty()) { frame.file = try std.fmt.allocPrint(allocator, "{any}", .{this.sourceURLFormatter(root_path, origin, true, false)}); } @@ -576,7 +594,7 @@ pub const ZigStackFrame = extern struct { } pub const SourceURLFormatter = struct { - source_url: ZigString, + source_url: bun.String, position: ZigStackFramePosition, enable_color: bool, origin: ?*const ZigURL, @@ -588,7 +606,9 @@ pub const ZigStackFrame = extern struct { try writer.writeAll(Output.prettyFmt("", true)); } - var source_slice = this.source_url.slice(); + var source_slice_ = this.source_url.toUTF8(bun.default_allocator); + var source_slice = source_slice_.slice(); + defer source_slice_.deinit(); if (!this.remapped) { if (this.origin) |origin| { @@ -647,12 +667,12 @@ pub const ZigStackFrame = extern struct { }; pub const NameFormatter = struct { - function_name: ZigString, + function_name: String, code_type: ZigStackFrameCode, enable_color: bool, pub fn format(this: NameFormatter, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { - const name = this.function_name.slice(); + const name = this.function_name; switch (this.code_type) { .Eval => { @@ -662,26 +682,26 @@ pub const ZigStackFrame = extern struct { // try writer.writeAll("(esm)"); }, .Function => { - if (name.len > 0) { + if (!name.isEmpty()) { if (this.enable_color) { - try std.fmt.format(writer, comptime Output.prettyFmt("{s}", true), .{name}); + try std.fmt.format(writer, comptime Output.prettyFmt("{}", true), .{name}); } else { - try std.fmt.format(writer, "{s}", .{name}); + try std.fmt.format(writer, "{}", .{name}); } } }, .Global => { - if (name.len > 0) { - try std.fmt.format(writer, "globalThis {s}", .{name}); + if (!name.isEmpty()) { + try std.fmt.format(writer, "globalThis {}", .{name}); } else { try writer.writeAll("globalThis"); } }, .Wasm => { - try std.fmt.format(writer, "WASM {s}", .{name}); + try std.fmt.format(writer, "WASM {}", .{name}); }, .Constructor => { - try std.fmt.format(writer, "new {s}", .{name}); + try std.fmt.format(writer, "new {}", .{name}); }, else => {}, } @@ -689,9 +709,9 @@ pub const ZigStackFrame = extern struct { }; pub const Zero: ZigStackFrame = ZigStackFrame{ - .function_name = ZigString{ ._unsafe_ptr_do_not_use = "", .len = 0 }, + .function_name = String.empty, .code_type = ZigStackFrameCode.None, - .source_url = ZigString{ ._unsafe_ptr_do_not_use = "", .len = 0 }, + .source_url = String.empty, .position = ZigStackFramePosition.Invalid, }; @@ -744,14 +764,14 @@ pub const ZigException = extern struct { /// SystemError only errno: c_int = 0, /// SystemError only - syscall: ZigString = ZigString.Empty, + syscall: String = String.empty, /// SystemError only - system_code: ZigString = ZigString.Empty, + system_code: String = String.empty, /// SystemError only - path: ZigString = ZigString.Empty, + path: String = String.empty, - name: ZigString, - message: ZigString, + name: String, + message: String, stack: ZigStackTrace, exception: ?*anyopaque, @@ -760,6 +780,19 @@ pub const ZigException = extern struct { fd: i32 = -1, + pub fn deinit(this: *ZigException) void { + this.syscall.deref(); + this.system_code.deref(); + this.path.deref(); + + this.name.deref(); + this.message.deref(); + + for (this.stack.frames_ptr[0..this.stack.frames_len]) |*frame| { + frame.deinit(); + } + } + pub const shim = Shimmer("Zig", "Exception", @This()); pub const name = "ZigException"; pub const namespace = shim.namespace; @@ -768,7 +801,7 @@ pub const ZigException = extern struct { const frame_count = 32; pub const source_lines_count = 6; source_line_numbers: [source_lines_count]i32, - source_lines: [source_lines_count]ZigString, + source_lines: [source_lines_count]String, frames: [frame_count]ZigStackFrame, loaded: bool, zig_exception: ZigException, @@ -786,8 +819,8 @@ pub const ZigException = extern struct { }, .source_lines = brk: { - var lines: [source_lines_count]ZigString = undefined; - @memset(&lines, ZigString.Empty); + var lines: [source_lines_count]String = undefined; + @memset(&lines, String.empty); break :brk lines; }, .zig_exception = undefined, @@ -798,13 +831,17 @@ pub const ZigException = extern struct { return Holder.Zero; } + pub fn deinit(this: *Holder) void { + this.zigException().deinit(); + } + pub fn zigException(this: *Holder) *ZigException { if (!this.loaded) { this.zig_exception = ZigException{ .code = @enumFromInt(JSErrorCode, 255), .runtime_type = JSRuntimeType.Nothing, - .name = ZigString.Empty, - .message = ZigString.Empty, + .name = String.empty, + .message = String.empty, .exception = null, .stack = ZigStackTrace{ .source_lines_ptr = &this.source_lines, @@ -832,8 +869,13 @@ pub const ZigException = extern struct { root_path: string, origin: ?*const ZigURL, ) !void { - const _name: string = @field(this, "name").slice(); - const message: string = @field(this, "message").slice(); + const name_slice = @field(this, "name").toUTF8(bun.default_allocator); + const message_slice = @field(this, "message").toUTF8(bun.default_allocator); + + const _name = name_slice.slice(); + defer name_slice.deinit(); + const message = message_slice.slice(); + defer message_slice.deinit(); var is_empty = true; var api_exception = Api.JsException{ diff --git a/src/bun.js/bindings/headers-handwritten.h b/src/bun.js/bindings/headers-handwritten.h index db1e38d3e..c7429b633 100644 --- a/src/bun.js/bindings/headers-handwritten.h +++ b/src/bun.js/bindings/headers-handwritten.h @@ -120,15 +120,15 @@ typedef struct ZigStackFramePosition { } ZigStackFramePosition; typedef struct ZigStackFrame { - ZigString function_name; - ZigString source_url; + BunString function_name; + BunString source_url; ZigStackFramePosition position; ZigStackFrameCode code_type; bool remapped; } ZigStackFrame; typedef struct ZigStackTrace { - ZigString* source_lines_ptr; + BunString* source_lines_ptr; int32_t* source_lines_numbers; uint8_t source_lines_len; uint8_t source_lines_to_collect; @@ -140,11 +140,11 @@ typedef struct ZigException { unsigned char code; uint16_t runtime_type; int errno_; - ZigString syscall; - ZigString code_; - ZigString path; - ZigString name; - ZigString message; + BunString syscall; + BunString code_; + BunString path; + BunString name; + BunString message; ZigStackTrace stack; void* exception; bool remapped; diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig index cf6a65841..605cc0c25 100644 --- a/src/bun.js/javascript.zig +++ b/src/bun.js/javascript.zig @@ -1805,6 +1805,7 @@ pub const VirtualMachine = struct { if (exception) |exception_| { var holder = ZigException.Holder.init(); var zig_exception: *ZigException = holder.zigException(); + defer zig_exception.deinit(); exception_.getStackTrace(&zig_exception.stack); if (zig_exception.stack.frames_len > 0) { if (allow_ansi_color) { @@ -1932,8 +1933,14 @@ pub const VirtualMachine = struct { while (i < stack.len) : (i += 1) { const frame = stack[@intCast(usize, i)]; - const file = frame.source_url.slice(); - const func = frame.function_name.slice(); + const file_slice = frame.source_url.toSlice(bun.default_allocator); + defer file_slice.deinit(); + const func_slice = frame.function_name.toSlice(bun.default_allocator); + defer func_slice.deinit(); + + const file = file_slice.slice(); + const func = func_slice.slice(); + if (file.len == 0 and func.len == 0) continue; const has_name = std.fmt.count("{any}", .{frame.nameFormatter( @@ -1985,7 +1992,7 @@ pub const VirtualMachine = struct { pub fn remapStackFramePositions(this: *VirtualMachine, frames: [*]JSC.ZigStackFrame, frames_count: usize) void { for (frames[0..frames_count]) |*frame| { if (frame.position.isInvalid() or frame.remapped) continue; - var sourceURL = frame.source_url.toSlice(bun.default_allocator); + var sourceURL = frame.source_url.toUTF8(bun.default_allocator); defer sourceURL.deinit(); if (this.source_mappings.resolveMapping( @@ -2049,8 +2056,10 @@ pub const VirtualMachine = struct { if (frames.len == 0) return; var top = &frames[0]; + var top_source_url = top.source_url.toUTF8(bun.default_allocator); + defer top_source_url.deinit(); if (this.source_mappings.resolveMapping( - top.source_url.slice(), + top_source_url.slice(), @max(top.position.line, 0), @max(top.position.column_start, 0), )) |mapping| { @@ -2078,18 +2087,18 @@ pub const VirtualMachine = struct { )) |lines| { var source_lines = exception.stack.source_lines_ptr[0..JSC.ZigException.Holder.source_lines_count]; var source_line_numbers = exception.stack.source_lines_numbers[0..JSC.ZigException.Holder.source_lines_count]; - @memset(source_lines, ZigString.Empty); + @memset(source_lines, String.empty); @memset(source_line_numbers, 0); var lines_ = lines[0..@min(lines.len, source_lines.len)]; for (lines_, 0..) |line, j| { - source_lines[(lines_.len - 1) - j] = ZigString.init(line); + source_lines[(lines_.len - 1) - j] = String.init(line); source_line_numbers[j] = top.position.line - @intCast(i32, j) + 1; } exception.stack.source_lines_len = @intCast(u8, lines_.len); - top.position.column_stop = @intCast(i32, source_lines[lines_.len - 1].len); + top.position.column_stop = @intCast(i32, source_lines[lines_.len - 1].length()); top.position.line_stop = top.position.column_stop; // This expression range is no longer accurate @@ -2101,8 +2110,10 @@ pub const VirtualMachine = struct { if (frames.len > 1) { for (frames[1..]) |*frame| { if (frame.position.isInvalid()) continue; + const source_url = frame.source_url.toUTF8(bun.default_allocator); + defer source_url.deinit(); if (this.source_mappings.resolveMapping( - frame.source_url.slice(), + source_url.slice(), @max(frame.position.line, 0), @max(frame.position.column_start, 0), )) |mapping| { @@ -2117,6 +2128,7 @@ pub const VirtualMachine = struct { pub fn printErrorInstance(this: *VirtualMachine, error_instance: JSValue, exception_list: ?*ExceptionList, comptime Writer: type, writer: Writer, comptime allow_ansi_color: bool, comptime allow_side_effects: bool) !void { var exception_holder = ZigException.Holder.init(); var exception = exception_holder.zigException(); + defer exception_holder.deinit(); this.remapZigException(exception, error_instance, exception_list); this.had_errors = true; @@ -2134,15 +2146,18 @@ pub const VirtualMachine = struct { var source_lines = exception.stack.sourceLineIterator(); var last_pad: u64 = 0; while (source_lines.untilLast()) |source| { + defer source.text.deinit(); + const int_size = std.fmt.count("{d}", .{source.line}); const pad = max_line_number_pad - int_size; last_pad = pad; try writer.writeByteNTimes(' ', pad); + try writer.print( comptime Output.prettyFmt("{d} | {s}\n", allow_ansi_color), .{ source.line, - std.mem.trim(u8, source.text, "\n"), + std.mem.trim(u8, source.text.slice(), "\n"), }, ); } @@ -2158,7 +2173,8 @@ pub const VirtualMachine = struct { const top_frame = if (exception.stack.frames_len > 0) exception.stack.frames()[0] else null; if (top_frame == null or top_frame.?.position.isInvalid()) { defer did_print_name = true; - var text = std.mem.trim(u8, source.text, "\n"); + defer source.text.deinit(); + var text = std.mem.trim(u8, source.text.slice(), "\n"); try writer.print( comptime Output.prettyFmt( @@ -2176,7 +2192,9 @@ pub const VirtualMachine = struct { const int_size = std.fmt.count("{d}", .{source.line}); const pad = max_line_number_pad - int_size; try writer.writeByteNTimes(' ', pad); - var remainder = std.mem.trim(u8, source.text, "\n"); + defer source.text.deinit(); + const text = source.text.slice(); + var remainder = std.mem.trim(u8, text, "\n"); try writer.print( comptime Output.prettyFmt( @@ -2188,7 +2206,7 @@ pub const VirtualMachine = struct { if (!top.position.isInvalid()) { var first_non_whitespace = @intCast(u32, top.position.column_start); - while (first_non_whitespace < source.text.len and source.text[first_non_whitespace] == ' ') { + while (first_non_whitespace < text.len and text[first_non_whitespace] == ' ') { first_non_whitespace += 1; } const indent = @intCast(usize, pad) + " | ".len + first_non_whitespace; @@ -2219,10 +2237,10 @@ pub const VirtualMachine = struct { }; var show = Show{ - .system_code = exception.system_code.len > 0 and !strings.eql(exception.system_code.slice(), name.slice()), - .syscall = exception.syscall.len > 0, + .system_code = !exception.system_code.eql(name) and !exception.system_code.isEmpty(), + .syscall = !exception.syscall.isEmpty(), .errno = exception.errno < 0, - .path = exception.path.len > 0, + .path = !exception.path.isEmpty(), .fd = exception.fd != -1, }; @@ -2262,7 +2280,7 @@ pub const VirtualMachine = struct { } else if (show.errno) { try writer.writeAll(" "); } - try writer.print(comptime Output.prettyFmt(" path: \"{s}\"\n", allow_ansi_color), .{exception.path}); + try writer.print(comptime Output.prettyFmt(" path: \"{}\"\n", allow_ansi_color), .{exception.path}); } if (show.fd) { @@ -2281,12 +2299,12 @@ pub const VirtualMachine = struct { } else if (show.errno) { try writer.writeAll(" "); } - try writer.print(comptime Output.prettyFmt(" code: \"{s}\"\n", allow_ansi_color), .{exception.system_code}); + try writer.print(comptime Output.prettyFmt(" code: \"{}\"\n", allow_ansi_color), .{exception.system_code}); add_extra_line = true; } if (show.syscall) { - try writer.print(comptime Output.prettyFmt(" syscall: \"{s}\"\n", allow_ansi_color), .{exception.syscall}); + try writer.print(comptime Output.prettyFmt(" syscall: \"{}\"\n", allow_ansi_color), .{exception.syscall}); add_extra_line = true; } @@ -2303,22 +2321,22 @@ pub const VirtualMachine = struct { try printStackTrace(@TypeOf(writer), writer, exception.stack, allow_ansi_color); } - fn printErrorNameAndMessage(_: *VirtualMachine, name: ZigString, message: ZigString, comptime Writer: type, writer: Writer, comptime allow_ansi_color: bool) !void { - if (name.len > 0 and message.len > 0) { - const display_name: ZigString = if (!name.is16Bit() and strings.eqlComptime(name.slice(), "Error")) ZigString.init("error") else name; + fn printErrorNameAndMessage(_: *VirtualMachine, name: String, message: String, comptime Writer: type, writer: Writer, comptime allow_ansi_color: bool) !void { + if (!name.isEmpty() and !message.isEmpty()) { + const display_name: String = if (name.eqlComptime("Error")) String.init("error") else name; try writer.print(comptime Output.prettyFmt("{any}: {s}\n", allow_ansi_color), .{ display_name, message, }); - } else if (name.len > 0) { - if (name.is16Bit() or !strings.hasPrefixComptime(name.slice(), "error")) { - try writer.print(comptime Output.prettyFmt("error: {s}\n", allow_ansi_color), .{name}); + } else if (!name.isEmpty()) { + if (!name.hasPrefixComptime("error")) { + try writer.print(comptime Output.prettyFmt("error: {}\n", allow_ansi_color), .{name}); } else { - try writer.print(comptime Output.prettyFmt("{s}\n", allow_ansi_color), .{name}); + try writer.print(comptime Output.prettyFmt("{}\n", allow_ansi_color), .{name}); } - } else if (message.len > 0) { - try writer.print(comptime Output.prettyFmt("error: {s}\n", allow_ansi_color), .{message}); + } else if (!message.isEmpty()) { + try writer.print(comptime Output.prettyFmt("error: {}\n", allow_ansi_color), .{message}); } else { try writer.print(comptime Output.prettyFmt("error\n", allow_ansi_color), .{}); } diff --git a/src/bun.js/test/jest.zig b/src/bun.js/test/jest.zig index d3bb90747..5ae2337f9 100644 --- a/src/bun.js/test/jest.zig +++ b/src/bun.js/test/jest.zig @@ -1532,8 +1532,8 @@ inline fn createIfScope( // In Github Actions, emit an annotation that renders the error and location. // https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-error-message pub fn printGithubAnnotation(exception: *JSC.ZigException) void { - const name = exception.name; - const message = exception.message; + const name = @field(exception, "name"); + const message = @field(exception, "message"); const frames = exception.stack.frames(); const top_frame = if (frames.len > 0) frames[0] else null; const dir = bun.getenvZ("GITHUB_WORKSPACE") orelse bun.fs.FileSystem.instance.top_level_dir; @@ -1543,7 +1543,7 @@ pub fn printGithubAnnotation(exception: *JSC.ZigException) void { if (top_frame) |frame| { if (!frame.position.isInvalid()) { - const source_url = frame.source_url.toSlice(allocator); + const source_url = frame.source_url.toUTF8(allocator); defer source_url.deinit(); const file = bun.path.relative(dir, source_url.slice()); Output.printError("\n::error file={s},line={d},col={d},title=", .{ @@ -1559,14 +1559,14 @@ pub fn printGithubAnnotation(exception: *JSC.ZigException) void { Output.printError("\n::error title=", .{}); } - if (name.len == 0 or name.eqlComptime("Error")) { + if (name.isEmpty() or name.eqlComptime("Error")) { Output.printError("error", .{}); } else { Output.printError("{s}", .{name.githubAction()}); } - if (message.len > 0) { - const message_slice = message.toSlice(allocator); + if (!message.isEmpty()) { + const message_slice = message.toUTF8(allocator); defer message_slice.deinit(); const msg = message_slice.slice(); @@ -1574,7 +1574,7 @@ pub fn printGithubAnnotation(exception: *JSC.ZigException) void { while (strings.indexOfNewlineOrNonASCIIOrANSI(msg, cursor)) |i| { cursor = i + 1; if (msg[i] == '\n') { - const first_line = ZigString.init(msg[0..i]); + const first_line = bun.String.fromUTF8(msg[0..i]); Output.printError(": {s}::", .{first_line.githubAction()}); break; } @@ -1605,10 +1605,10 @@ pub fn printGithubAnnotation(exception: *JSC.ZigException) void { var i: i16 = 0; while (i < frames.len) : (i += 1) { const frame = frames[@intCast(usize, i)]; - const source_url = frame.source_url.toSlice(allocator); + const source_url = frame.source_url.toUTF8(allocator); defer source_url.deinit(); const file = bun.path.relative(dir, source_url.slice()); - const func = frame.function_name.toSlice(allocator); + const func = frame.function_name.toUTF8(allocator); if (file.len == 0 and func.len == 0) continue; diff --git a/src/js/out/modules/node/path.js b/src/js/out/modules/node/path.js index f8cc1ec08..3a3a75207 100644 --- a/src/js/out/modules/node/path.js +++ b/src/js/out/modules/node/path.js @@ -1 +1 @@ -var g=function(i){var f=m({basename:i.basename.bind(i),dirname:i.dirname.bind(i),extname:i.extname.bind(i),format:i.format.bind(i),isAbsolute:i.isAbsolute.bind(i),join:i.join.bind(i),normalize:i.normalize.bind(i),parse:i.parse.bind(i),relative:i.relative.bind(i),resolve:i.resolve.bind(i),toNamespacedPath:i.toNamespacedPath.bind(i),sep:i.sep,delimiter:i.delimiter});return f.default=f,f},m=(i)=>Object.assign(Object.create(null),i),k=g(Bun._Path()),q=g(Bun._Path(!1)),v=g(Bun._Path(!0));k.win32=v;k.posix=q;var{basename:y,dirname:z,extname:A,format:B,isAbsolute:C,join:D,normalize:E,parse:F,relative:G,resolve:H,toNamespacedPath:I,sep:J,delimiter:K,__esModule:L}=k;k[Symbol.for("CommonJS")]=0;k.__esModule=!0;var O=k;export{v as win32,I as toNamespacedPath,J as sep,H as resolve,G as relative,q as posix,F as parse,E as normalize,D as join,C as isAbsolute,B as format,A as extname,z as dirname,K as delimiter,O as default,m as createModule,y as basename,L as __esModule}; +var i=function(N){var m=g({basename:N.basename.bind(N),dirname:N.dirname.bind(N),extname:N.extname.bind(N),format:N.format.bind(N),isAbsolute:N.isAbsolute.bind(N),join:N.join.bind(N),normalize:N.normalize.bind(N),parse:N.parse.bind(N),relative:N.relative.bind(N),resolve:N.resolve.bind(N),toNamespacedPath:N.toNamespacedPath.bind(N),sep:N.sep,delimiter:N.delimiter});return m.default=m,m},g=(N)=>Object.assign(Object.create(null),N),f=i(Bun._Path()),J=i(Bun._Path(!1)),k=i(Bun._Path(!0));f.win32=k;f.posix=J;var{basename:q,dirname:v,extname:y,format:z,isAbsolute:A,join:B,normalize:K,parse:C,relative:D,resolve:E,toNamespacedPath:F,sep:G,delimiter:H,__esModule:I}=f;f[Symbol.for("CommonJS")]=0;f.__esModule=!0;var O=f;export{k as win32,F as toNamespacedPath,G as sep,E as resolve,D as relative,J as posix,C as parse,K as normalize,B as join,A as isAbsolute,z as format,y as extname,v as dirname,H as delimiter,O as default,g as createModule,q as basename,I as __esModule}; diff --git a/src/js/out/modules/node/stream.web.js b/src/js/out/modules/node/stream.web.js index bb906418c..f91ee03b4 100644 --- a/src/js/out/modules/node/stream.web.js +++ b/src/js/out/modules/node/stream.web.js @@ -1 +1 @@ -var{ReadableStream:c,ReadableStreamDefaultController:j,WritableStream:k,WritableStreamDefaultController:p,WritableStreamDefaultWriter:v,TransformStream:w,TransformStreamDefaultController:x,ByteLengthQueuingStrategy:z,CountQueuingStrategy:A,ReadableStreamBYOBReader:E,ReadableStreamBYOBRequest:F,ReadableStreamDefaultReader:G}=globalThis,H={ReadableStream:c,ReadableStreamDefaultController:j,WritableStream:k,WritableStreamDefaultController:p,WritableStreamDefaultWriter:v,TransformStream:w,TransformStreamDefaultController:x,ByteLengthQueuingStrategy:z,CountQueuingStrategy:A,ReadableStreamBYOBReader:E,ReadableStreamBYOBRequest:F,ReadableStreamDefaultReader:G,[Symbol.for("CommonJS")]:0};export{H as default,v as WritableStreamDefaultWriter,p as WritableStreamDefaultController,k as WritableStream,x as TransformStreamDefaultController,w as TransformStream,G as ReadableStreamDefaultReader,j as ReadableStreamDefaultController,F as ReadableStreamBYOBRequest,E as ReadableStreamBYOBReader,c as ReadableStream,A as CountQueuingStrategy,z as ByteLengthQueuingStrategy}; +var{ReadableStream:k,ReadableStreamDefaultController:v,WritableStream:w,WritableStreamDefaultController:x,WritableStreamDefaultWriter:z,TransformStream:A,TransformStreamDefaultController:E,ByteLengthQueuingStrategy:F,CountQueuingStrategy:c,ReadableStreamBYOBReader:j,ReadableStreamBYOBRequest:G,ReadableStreamDefaultReader:p}=globalThis,H={ReadableStream:k,ReadableStreamDefaultController:v,WritableStream:w,WritableStreamDefaultController:x,WritableStreamDefaultWriter:z,TransformStream:A,TransformStreamDefaultController:E,ByteLengthQueuingStrategy:F,CountQueuingStrategy:c,ReadableStreamBYOBReader:j,ReadableStreamBYOBRequest:G,ReadableStreamDefaultReader:p,[Symbol.for("CommonJS")]:0};export{H as default,z as WritableStreamDefaultWriter,x as WritableStreamDefaultController,w as WritableStream,E as TransformStreamDefaultController,A as TransformStream,p as ReadableStreamDefaultReader,v as ReadableStreamDefaultController,G as ReadableStreamBYOBRequest,j as ReadableStreamBYOBReader,k as ReadableStream,c as CountQueuingStrategy,F as ByteLengthQueuingStrategy}; diff --git a/src/logger.zig b/src/logger.zig index 3279e9fd5..fc25541de 100644 --- a/src/logger.zig +++ b/src/logger.zig @@ -421,12 +421,12 @@ pub const Msg = struct { if (err.toError()) |value| { value.toZigException(globalObject, zig_exception_holder.zigException()); } else { - zig_exception_holder.zig_exception.message = JSC.ZigString.fromUTF8(err.toSlice(globalObject, allocator).slice()); + zig_exception_holder.zig_exception.message = err.toBunString(globalObject); } return Msg{ .data = .{ - .text = zig_exception_holder.zigException().message.toSliceClone(allocator).slice(), + .text = try zig_exception_holder.zigException().message.toOwnedSlice(allocator), .location = Location{ .file = file, }, diff --git a/src/string.zig b/src/string.zig index 54af2ba68..3c0c99ce5 100644 --- a/src/string.zig +++ b/src/string.zig @@ -247,6 +247,26 @@ pub const String = extern struct { extern fn BunString__fromLatin1(bytes: [*]const u8, len: usize) String; extern fn BunString__fromBytes(bytes: [*]const u8, len: usize) String; + pub fn toOwnedSlice(this: String, allocator: std.mem.Allocator) ![]u8 { + switch (this.tag) { + .ZigString => return try this.value.ZigString.toOwnedSlice(allocator), + .WTFStringImpl => { + var utf8_slice = this.value.WTFStringImpl.toUTF8(allocator); + + if (utf8_slice.allocator.get()) |alloc| { + if (isWTFAllocator(alloc)) { + return @constCast((try utf8_slice.clone(allocator)).slice()); + } + } + + return @constCast(utf8_slice.slice()); + }, + .StaticZigString => return try this.value.StaticZigString.toOwnedSlice(allocator), + .Empty => return &[_]u8{}, + else => unreachable, + } + } + pub fn createLatin1(bytes: []const u8) String { JSC.markBinding(@src()); return BunString__fromLatin1(bytes.ptr, bytes.len); @@ -429,6 +449,10 @@ pub const String = extern struct { return .latin1; } + pub fn githubAction(self: String) ZigString.GithubActionFormatter { + return self.toZigString().githubAction(); + } + pub fn byteSlice(this: String) []const u8 { return switch (this.tag) { .ZigString, .StaticZigString => this.value.ZigString.byteSlice(), -- cgit v1.2.3 From 983039a18afccb2f7d78dfdb06724d1ea58edde6 Mon Sep 17 00:00:00 2001 From: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> Date: Mon, 3 Jul 2023 13:16:45 -0700 Subject: Fixes #3508 --- src/bun.js/bindings/bindings.zig | 22 +++++++++- src/bun.js/webcore/encoding.zig | 8 ++++ src/napi/napi.zig | 94 +++++++++++----------------------------- src/string.zig | 52 +++++++++++++++++++++- 4 files changed, 105 insertions(+), 71 deletions(-) (limited to 'src/string.zig') diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig index 777860d3c..277172b81 100644 --- a/src/bun.js/bindings/bindings.zig +++ b/src/bun.js/bindings/bindings.zig @@ -291,7 +291,27 @@ pub const ZigString = extern struct { return this.len * 2; } - /// Count the number of code points in the string. + pub fn utf16ByteLength(this: ZigString) usize { + if (this.isUTF8()) { + return bun.simdutf.length.utf16.from.utf8.le(this.slice()); + } + + if (this.is16Bit()) { + return this.len * 2; + } + + return JSC.WebCore.Encoder.byteLengthU8(this.slice().ptr, this.slice().len, .utf16le); + } + + pub fn latin1ByteLength(this: ZigString) usize { + if (this.isUTF8()) { + @panic("TODO"); + } + + return this.len; + } + + /// Count the number of bytes in the UTF-8 version of the string. /// This function is slow. Use maxUITF8ByteLength() to get a quick estimate pub fn utf8ByteLength(this: ZigString) usize { if (this.isUTF8()) { diff --git a/src/bun.js/webcore/encoding.zig b/src/bun.js/webcore/encoding.zig index 061a25eed..bb1180acb 100644 --- a/src/bun.js/webcore/encoding.zig +++ b/src/bun.js/webcore/encoding.zig @@ -985,6 +985,14 @@ pub const Encoder = struct { } } + pub fn encodeIntoFrom16(input: []const u16, to: []u8, comptime encoding: JSC.Node.Encoding, comptime allow_partial_write: bool) !usize { + return writeU16(input.ptr, input.len, to.ptr, to.len, encoding, allow_partial_write); + } + + pub fn encodeIntoFrom8(input: []const u8, to: []u8, comptime encoding: JSC.Node.Encoding) !usize { + return writeU8(input.ptr, input.len, to.ptr, to.len, encoding); + } + pub fn writeU16(input: [*]const u16, len: usize, to: [*]u8, to_len: usize, comptime encoding: JSC.Node.Encoding, comptime allow_partial_write: bool) !usize { if (len == 0) return 0; diff --git a/src/napi/napi.zig b/src/napi/napi.zig index 439319489..d9c7b5993 100644 --- a/src/napi/napi.zig +++ b/src/napi/napi.zig @@ -289,7 +289,10 @@ pub export fn napi_create_string_utf8(env: napi_env, str: [*]const u8, length: u log("napi_create_string_utf8: {s}", .{slice}); - setNapiValue(result, JSC.ZigString.fromUTF8(slice).toValueGC(env)); + var string = bun.String.create(slice); + defer string.deref(); + + setNapiValue(result, string.toJS(env)); return .ok; } pub export fn napi_create_string_utf16(env: napi_env, str: [*]const char16_t, length: usize, result: *napi_value) napi_status { @@ -340,18 +343,14 @@ inline fn maybeAppendNull(ptr: anytype, doit: bool) void { pub export fn napi_get_value_string_latin1(env: napi_env, value: napi_value, buf_ptr: [*c]u8, bufsize: usize, result: *usize) napi_status { log("napi_get_value_string_latin1", .{}); defer value.ensureStillAlive(); - const zig_str = value.getZigString(env); + const str = value.toBunString(env); var buf = buf_ptr orelse { - result.* = if (!zig_str.is16Bit()) - zig_str.len - else - // should be same length if valid latin1 - strings.elementLengthUTF16IntoUTF8([]const u16, zig_str.utf16SliceAligned()); + result.* = str.latin1ByteLength(); return .ok; }; - if (zig_str.len == 0) { + if (str.isEmpty()) { result.* = 0; buf[0] = 0; @@ -367,18 +366,7 @@ pub export fn napi_get_value_string_latin1(env: napi_env, value: napi_value, buf return .ok; } } - - if (zig_str.is16Bit()) { - const utf16 = zig_str.utf16SliceAligned(); - const wrote = JSC.WebCore.Encoder.writeU16(utf16.ptr, utf16.len, buf, buf_.len, .latin1, false) catch return genericFailure(); - maybeAppendNull(&buf[wrote], bufsize == 0); - // if zero terminated, report the length of the string without the null - result.* = @intCast(@TypeOf(result.*), wrote); - return .ok; - } - const to_copy = @min(zig_str.len, buf_.len); - @memcpy(buf[0..to_copy], zig_str.slice().ptr[0..to_copy]); - buf[to_copy] = 0; + const to_copy = str.encodeInto(buf_, .latin1) catch unreachable; // if zero terminated, report the length of the string without the null result.* = to_copy; return .ok; @@ -399,24 +387,22 @@ pub export fn napi_get_value_string_utf8(env: napi_env, value: napi_value, buf_p return .string_expected; } - const zig_str = value.getZigString(env); - var buf = buf_ptr orelse { + const str = value.toBunString(env); + + if (str.isEmpty()) { if (result_ptr) |result| { - result.* = if (!zig_str.is16Bit()) - zig_str.len - else - JSC.WebCore.Encoder.byteLengthU16(zig_str.utf16SliceAligned().ptr, zig_str.utf16SliceAligned().len, .utf8); + result.* = 0; } - return .ok; - }; + } - if (zig_str.len == 0) { + var buf = buf_ptr orelse { if (result_ptr) |result| { - result.* = 0; + result.* = str.utf8ByteLength(); } + return .ok; - } + }; var buf_ = buf[0..bufsize]; @@ -430,44 +416,29 @@ pub export fn napi_get_value_string_utf8(env: napi_env, value: napi_value, buf_p } } - if (zig_str.is16Bit()) { - const utf16 = zig_str.utf16SliceAligned(); - const wrote = JSC.WebCore.Encoder.writeU16(utf16.ptr, utf16.len, buf, buf_.len, .utf8, false) catch return genericFailure(); - buf[wrote] = 0; - if (result_ptr) |result| { - result.* = @intCast(@TypeOf(result.*), wrote); - } - - return .ok; - } + const written = str.encodeInto(buf_, .utf8) catch unreachable; - const to_copy = @min(zig_str.len, buf_.len); - @memcpy(buf[0..to_copy], zig_str.slice().ptr[0..to_copy]); - buf[to_copy] = 0; if (result_ptr) |result| { - result.* = @intCast(@TypeOf(result.*), to_copy); + result.* = written; } - log("napi_get_value_string_utf8: {s}", .{buf[0..to_copy]}); + log("napi_get_value_string_utf8: {s}", .{buf[0..written]}); return .ok; } pub export fn napi_get_value_string_utf16(env: napi_env, value: napi_value, buf_ptr: [*c]char16_t, bufsize: usize, result_ptr: ?*usize) napi_status { log("napi_get_value_string_utf16", .{}); defer value.ensureStillAlive(); - const zig_str = value.getZigString(env); + const str = value.toBunString(env); var buf = buf_ptr orelse { if (result_ptr) |result| { - result.* = if (zig_str.is16Bit()) - zig_str.len - else - JSC.WebCore.Encoder.byteLengthU16(zig_str.utf16SliceAligned().ptr, zig_str.utf16SliceAligned().len, .latin1); + result.* = str.utf16ByteLength(); } return .ok; }; - if (zig_str.len == 0) { + if (str.isEmpty()) { if (result_ptr) |result| { result.* = 0; } @@ -487,20 +458,7 @@ pub export fn napi_get_value_string_utf16(env: napi_env, value: napi_value, buf_ return .ok; } } - - if (!zig_str.is16Bit()) { - const slice = zig_str.slice(); - const encode_into_result = strings.copyLatin1IntoUTF16([]char16_t, buf_, []const u8, slice); - buf[@intCast(usize, encode_into_result.written)] = 0; - - if (result_ptr) |result| { - result.* = encode_into_result.written; - } - return .ok; - } - - const to_copy = @min(zig_str.len, buf_.len) * 2; - @memcpy(std.mem.sliceAsBytes(buf_)[0..to_copy], std.mem.sliceAsBytes(zig_str.utf16SliceAligned())[0..to_copy]); + const to_copy = (str.encodeInto(std.mem.sliceAsBytes(buf_), .utf16le) catch unreachable) >> 1; buf[to_copy] = 0; // if zero terminated, report the length of the string without the null if (result_ptr) |result| { @@ -509,9 +467,9 @@ pub export fn napi_get_value_string_utf16(env: napi_env, value: napi_value, buf_ return .ok; } -pub export fn napi_coerce_to_bool(_: napi_env, value: napi_value, result: *napi_value) napi_status { +pub export fn napi_coerce_to_bool(env: napi_env, value: napi_value, result: *napi_value) napi_status { log("napi_coerce_to_bool", .{}); - result.* = JSValue.jsBoolean(value.to(bool)); + result.* = JSValue.jsBoolean(value.coerce(bool, env)); return .ok; } pub export fn napi_coerce_to_number(env: napi_env, value: napi_value, result: *napi_value) napi_status { diff --git a/src/string.zig b/src/string.zig index 3c0c99ce5..166a0a6f7 100644 --- a/src/string.zig +++ b/src/string.zig @@ -160,6 +160,17 @@ pub const WTFStringImplStruct = extern struct { } } + pub fn utf16ByteLength(this: WTFStringImpl) usize { + // All latin1 characters fit in a single UTF-16 code unit. + return this.length() * 2; + } + + pub fn latin1ByteLength(this: WTFStringImpl) usize { + // Not all UTF-16 characters fit are representable in latin1. + // Those get truncated? + return this.length(); + } + pub fn refCountAllocator(self: WTFStringImpl) std.mem.Allocator { return std.mem.Allocator{ .ptr = self, .vtable = StringImplAllocator.VTablePtr }; } @@ -286,6 +297,31 @@ pub const String = extern struct { return this; } + pub fn utf8ByteLength(this: String) usize { + return switch (this.tag) { + .WTFStringImpl => this.value.WTFStringImpl.utf8ByteLength(), + .ZigString => this.value.ZigString.utf8ByteLength(), + .StaticZigString => this.value.StaticZigString.utf8ByteLength(), + .Dead, .Empty => 0, + }; + } + + pub fn utf16ByteLength(this: String) usize { + return switch (this.tag) { + .WTFStringImpl => this.value.WTFStringImpl.utf16ByteLength(), + .StaticZigString, .ZigString => this.value.ZigString.utf16ByteLength(), + .Dead, .Empty => 0, + }; + } + + pub fn latin1ByteLength(this: String) usize { + return switch (this.tag) { + .WTFStringImpl => this.value.WTFStringImpl.latin1ByteLength(), + .StaticZigString, .ZigString => this.value.ZigString.latin1ByteLength(), + .Dead, .Empty => 0, + }; + } + pub fn initWithType(comptime Type: type, value: Type) String { switch (comptime Type) { ZigString => return String{ .tag = .ZigString, .value = .{ .ZigString = value } }, @@ -431,7 +467,7 @@ pub const String = extern struct { } pub fn isUTF8(self: String) bool { - if (!self.tag == .ZigString or self.tag == .StaticZigString) + if (!(self.tag == .ZigString or self.tag == .StaticZigString)) return false; return self.value.ZigString.isUTF8(); @@ -466,11 +502,23 @@ pub const String = extern struct { return !self.value.WTFStringImpl.is8Bit(); if (self.tag == .ZigString or self.tag == .StaticZigString) - return self.value.ZigString.isUTF16(); + return self.value.ZigString.is16Bit(); return false; } + pub fn encodeInto(self: String, out: []u8, comptime enc: JSC.Node.Encoding) !usize { + if (self.isUTF16()) { + return JSC.WebCore.Encoder.encodeIntoFrom16(self.utf16(), out, enc, true); + } + + if (self.isUTF8()) { + @panic("TODO"); + } + + return JSC.WebCore.Encoder.encodeIntoFrom8(self.latin1(), out, enc); + } + pub inline fn utf8(self: String) []const u8 { if (comptime bun.Environment.allow_assert) std.debug.assert(self.canBeUTF8()); -- cgit v1.2.3 From 963d4311e614ac197427104b9cf265bbe2a890af Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Sun, 9 Jul 2023 22:36:24 -0700 Subject: Fixes #3530 (#3587) * Fixes #3530 * Handle OOM * Add test --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> --- src/bun.js/bindings/BunString.cpp | 23 +++++++++++++++++ src/bun.js/node/types.zig | 30 +++++++++++++++++----- src/bun.js/webcore/encoding.zig | 36 ++++++++++++--------------- src/string.zig | 26 +++++++++++++++++++ test/js/node/crypto/node-crypto.test.js | 44 +++++++++++++++++++++++++++++++++ 5 files changed, 133 insertions(+), 26 deletions(-) (limited to 'src/string.zig') diff --git a/src/bun.js/bindings/BunString.cpp b/src/bun.js/bindings/BunString.cpp index 4c8ff384e..21541d711 100644 --- a/src/bun.js/bindings/BunString.cpp +++ b/src/bun.js/bindings/BunString.cpp @@ -169,6 +169,29 @@ extern "C" JSC::EncodedJSValue BunString__toJS(JSC::JSGlobalObject* globalObject return JSValue::encode(Bun::toJS(globalObject, *bunString)); } +extern "C" BunString BunString__fromUTF16Unitialized(size_t length) +{ + unsigned utf16Length = length; + UChar* ptr; + auto impl = WTF::StringImpl::createUninitialized(utf16Length, ptr); + if (UNLIKELY(!ptr)) + return { BunStringTag::Dead }; + + impl->ref(); + return { BunStringTag::WTFStringImpl, { .wtf = &impl.leakRef() } }; +} + +extern "C" BunString BunString__fromLatin1Unitialized(size_t length) +{ + unsigned latin1Length = length; + LChar* ptr; + auto impl = WTF::StringImpl::createUninitialized(latin1Length, ptr); + if (UNLIKELY(!ptr)) + return { BunStringTag::Dead }; + impl->ref(); + return { BunStringTag::WTFStringImpl, { .wtf = &impl.leakRef() } }; +} + extern "C" BunString BunString__fromUTF8(const char* bytes, size_t length) { if (simdutf::validate_utf8(bytes, length)) { diff --git a/src/bun.js/node/types.zig b/src/bun.js/node/types.zig index 553b292d6..642039ba5 100644 --- a/src/bun.js/node/types.zig +++ b/src/bun.js/node/types.zig @@ -541,9 +541,18 @@ pub const Encoding = enum(u8) { const result = JSC.ZigString.init(out).toValueGC(globalThis); return result; }, - else => { - globalThis.throwInvalidArguments("Unexpected encoding", .{}); - return JSC.JSValue.zero; + .buffer => { + return JSC.ArrayBuffer.createBuffer(globalThis, input); + }, + + inline else => |enc| { + const res = JSC.WebCore.Encoder.toString(input.ptr, size, globalThis, enc); + if (res.isError()) { + globalThis.throwValue(res); + return .zero; + } + + return res; }, } } @@ -571,9 +580,18 @@ pub const Encoding = enum(u8) { const result = JSC.ZigString.init(out).toValueGC(globalThis); return result; }, - else => { - globalThis.throwInvalidArguments("Unexpected encoding", .{}); - return JSC.JSValue.zero; + .buffer => { + return JSC.ArrayBuffer.createBuffer(globalThis, input); + }, + inline else => |enc| { + const res = JSC.WebCore.Encoder.toString(input.ptr, input.len, globalThis, enc); + + if (res.isError()) { + globalThis.throwValue(res); + return .zero; + } + + return res; }, } } diff --git a/src/bun.js/webcore/encoding.zig b/src/bun.js/webcore/encoding.zig index bb1180acb..dd47ccc29 100644 --- a/src/bun.js/webcore/encoding.zig +++ b/src/bun.js/webcore/encoding.zig @@ -829,23 +829,18 @@ pub const Encoder = struct { return ZigString.init(input).toValueGC(global); } - if (input.len < 512) { - var buf: [512]u8 = undefined; - var to = buf[0..input.len]; - strings.copyLatin1IntoASCII(to, input); - return ZigString.init(to).toValueGC(global); - } - - var to = allocator.alloc(u8, len) catch return ZigString.init("Out of memory").toErrorInstance(global); - strings.copyLatin1IntoASCII(to, input); - return ZigString.init(to).toExternalValue(global); + var str = bun.String.createUninitialized(.latin1, len) orelse return ZigString.init("Out of memory").toErrorInstance(global); + defer str.deref(); + strings.copyLatin1IntoASCII(@constCast(str.latin1()), input); + return str.toJS(global); }, .latin1 => { - var to = allocator.alloc(u8, len) catch return ZigString.init("Out of memory").toErrorInstance(global); + var str = bun.String.createUninitialized(.latin1, len) orelse return ZigString.init("Out of memory").toErrorInstance(global); + defer str.deref(); - @memcpy(to, input_ptr[0..to.len]); + @memcpy(@constCast(str.latin1()), input_ptr[0..len]); - return ZigString.init(to).toExternalValue(global); + return str.toJS(global); }, .buffer, .utf8 => { const converted = strings.toUTF16Alloc(allocator, input, false) catch return ZigString.init("Out of memory").toErrorInstance(global); @@ -861,21 +856,22 @@ pub const Encoder = struct { // Avoid incomplete characters if (len / 2 == 0) return ZigString.Empty.toValue(global); - var output = allocator.alloc(u16, len / 2) catch return ZigString.init("Out of memory").toErrorInstance(global); - var output_bytes = std.mem.sliceAsBytes(output); + var output = bun.String.createUninitialized(.utf16, len / 2) orelse return ZigString.init("Out of memory").toErrorInstance(global); + defer output.deref(); + var output_bytes = std.mem.sliceAsBytes(@constCast(output.utf16())); output_bytes[output_bytes.len - 1] = 0; @memcpy(output_bytes, input_ptr[0..output_bytes.len]); - return ZigString.toExternalU16(output.ptr, output.len, global); + return output.toJS(global); }, .hex => { - var output = allocator.alloc(u8, input.len * 2) catch return ZigString.init("Out of memory").toErrorInstance(global); + var str = bun.String.createUninitialized(.latin1, len * 2) orelse return ZigString.init("Out of memory").toErrorInstance(global); + defer str.deref(); + var output = @constCast(str.latin1()); const wrote = strings.encodeBytesToHex(output, input); std.debug.assert(wrote == output.len); - var val = ZigString.init(output); - val.mark(); - return val.toExternalValue(global); + return str.toJS(global); }, .base64url => { diff --git a/src/string.zig b/src/string.zig index 166a0a6f7..5f107197f 100644 --- a/src/string.zig +++ b/src/string.zig @@ -257,6 +257,8 @@ pub const String = extern struct { extern fn BunString__fromLatin1(bytes: [*]const u8, len: usize) String; extern fn BunString__fromBytes(bytes: [*]const u8, len: usize) String; + extern fn BunString__fromLatin1Unitialized(len: usize) String; + extern fn BunString__fromUTF16Unitialized(len: usize) String; pub fn toOwnedSlice(this: String, allocator: std.mem.Allocator) ![]u8 { switch (this.tag) { @@ -278,6 +280,30 @@ pub const String = extern struct { } } + pub fn createUninitializedLatin1(len: usize) String { + JSC.markBinding(@src()); + return BunString__fromLatin1Unitialized(len); + } + + pub fn createUninitializedUTF16(len: usize) String { + JSC.markBinding(@src()); + return BunString__fromUTF16Unitialized(len); + } + + pub fn createUninitialized(comptime kind: @Type(.EnumLiteral), len: usize) ?String { + const without_check = switch (comptime kind) { + .latin1 => createUninitializedLatin1(len), + .utf16 => createUninitializedUTF16(len), + else => @compileError("Invalid string kind"), + }; + + if (without_check.tag == .Dead) { + return null; + } + + return without_check; + } + pub fn createLatin1(bytes: []const u8) String { JSC.markBinding(@src()); return BunString__fromLatin1(bytes.ptr, bytes.len); diff --git a/test/js/node/crypto/node-crypto.test.js b/test/js/node/crypto/node-crypto.test.js index 5a68540cf..2489f96c7 100644 --- a/test/js/node/crypto/node-crypto.test.js +++ b/test/js/node/crypto/node-crypto.test.js @@ -43,6 +43,50 @@ describe("createHash", () => { expect(Buffer.isBuffer(hash.digest())).toBeTrue(); }); + const otherEncodings = { + ucs2: [ + 11626, 2466, 37699, 38942, 64564, 53010, 48101, 47943, 44761, 18499, 12442, 26994, 46434, 62582, 39395, 20542, + ], + latin1: [ + 106, 45, 162, 9, 67, 147, 30, 152, 52, 252, 18, 207, 229, 187, 71, 187, 217, 174, 67, 72, 154, 48, 114, 105, 98, + 181, 118, 244, 227, 153, 62, 80, + ], + binary: [ + 106, 45, 162, 9, 67, 147, 30, 152, 52, 252, 18, 207, 229, 187, 71, 187, 217, 174, 67, 72, 154, 48, 114, 105, 98, + 181, 118, 244, 227, 153, 62, 80, + ], + base64: [ + 97, 105, 50, 105, 67, 85, 79, 84, 72, 112, 103, 48, 47, 66, 76, 80, 53, 98, 116, 72, 117, 57, 109, 117, 81, 48, + 105, 97, 77, 72, 74, 112, 89, 114, 86, 50, 57, 79, 79, 90, 80, 108, 65, 61, + ], + hex: [ + 54, 97, 50, 100, 97, 50, 48, 57, 52, 51, 57, 51, 49, 101, 57, 56, 51, 52, 102, 99, 49, 50, 99, 102, 101, 53, 98, + 98, 52, 55, 98, 98, 100, 57, 97, 101, 52, 51, 52, 56, 57, 97, 51, 48, 55, 50, 54, 57, 54, 50, 98, 53, 55, 54, 102, + 52, 101, 51, 57, 57, 51, 101, 53, 48, + ], + ascii: [ + 106, 45, 34, 9, 67, 19, 30, 24, 52, 124, 18, 79, 101, 59, 71, 59, 89, 46, 67, 72, 26, 48, 114, 105, 98, 53, 118, + 116, 99, 25, 62, 80, + ], + utf8: [ + 106, 45, 65533, 9, 67, 65533, 30, 65533, 52, 65533, 18, 65533, 65533, 71, 65533, 1646, 67, 72, 65533, 48, 114, + 105, 98, 65533, 118, 65533, 65533, 62, 80, + ], + }; + + for (let encoding in otherEncodings) { + it("digest " + encoding, () => { + const hash = crypto.createHash("sha256"); + hash.update("some data to hash"); + expect( + hash + .digest(encoding) + .split("") + .map(a => a.charCodeAt(0)), + ).toEqual(otherEncodings[encoding]); + }); + } + it("stream (sync)", () => { const hash = crypto.createHash("sha256"); hash.write("some data to hash"); -- cgit v1.2.3