diff --git a/scripts/check-node-all.sh b/scripts/check-node-all.sh new file mode 100755 index 00000000000000..9358e55ae97a23 --- /dev/null +++ b/scripts/check-node-all.sh @@ -0,0 +1,41 @@ +#!/bin/sh + +# How to use this script: +# 1. Pick a module from node's standard library (e.g. 'assert', 'fs') +# 2. Copy over relevant tests from node's parallel test suite into test/js/node/test/parallel +# 3. Run this script, e.g. `./scripts/check-node.sh fs` +# 4. Tests that passed get staged for commit + +i=0 +j=0 + +if [ -z "$1" ] +then + echo "Usage: $0 " + exit 1 +fi + +case $1 in + -h|--help) + echo "Usage: $0 " + echo "Run all parallel tests for a single module in node's standard library" + exit 0 + ;; +esac + +export BUN_DEBUG_QUIET_LOGS=1 + +for x in $(find test/js/node/test/parallel -type f -name "test-$1*.js") +do + i=$((i+1)) + echo ./$x + if timeout 2 $PWD/build/debug/bun-debug ./$x + then + j=$((j+1)) + git add ./$x + fi + echo +done + +echo $i tests tested +echo $j tests passed diff --git a/scripts/check-node.sh b/scripts/check-node.sh index 47c2787186df73..a3c3159525469b 100755 --- a/scripts/check-node.sh +++ b/scripts/check-node.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # How to use this script: # 1. Pick a module from node's standard library (e.g. 'assert', 'fs') @@ -9,7 +9,7 @@ i=0 j=0 -if [[ -z $1 ]] +if [ -z "$1" ] then echo "Usage: $0 " exit 1 @@ -18,13 +18,12 @@ fi case $1 in -h|--help) echo "Usage: $0 " - echo "Run all parallel tests for a single module in node's standard library" + echo "Run all unstaged parallel tests for a single module in node's standard library" exit 0 ;; esac export BUN_DEBUG_QUIET_LOGS=1 -export NO_COLOR=1 for x in $(git ls-files test/js/node/test/parallel --exclude-standard --others | grep test-$1) do @@ -36,7 +35,6 @@ do git add ./$x fi echo - echo done echo $i tests tested diff --git a/src/bake/BakeProduction.cpp b/src/bake/BakeProduction.cpp index d7a1fb984237d4..1db8e9ee5b8021 100644 --- a/src/bake/BakeProduction.cpp +++ b/src/bake/BakeProduction.cpp @@ -1,5 +1,6 @@ #include "BakeProduction.h" #include "BunBuiltinNames.h" +#include "JavaScriptCore/CallData.h" #include "WebCoreJSBuiltins.h" #include "JavaScriptCore/JSPromise.h" #include "JavaScriptCore/Exception.h" @@ -38,7 +39,7 @@ extern "C" JSC::JSPromise* BakeRenderRoutesForProdStatic( args.append(styles); NakedPtr returnedException = nullptr; - auto result = JSC::call(global, cb, callData, JSC::jsUndefined(), args, returnedException); + auto result = JSC::profiledCall(global, JSC::ProfilingReason::API, cb, callData, JSC::jsUndefined(), args, returnedException); if (UNLIKELY(returnedException)) { // This should be impossible because it returns a promise. return JSC::JSPromise::rejectedPromise(global, returnedException->value()); diff --git a/src/bake/BakeSourceProvider.cpp b/src/bake/BakeSourceProvider.cpp index 2a51ffa0aa4825..2b1c4a9d14e6d0 100644 --- a/src/bake/BakeSourceProvider.cpp +++ b/src/bake/BakeSourceProvider.cpp @@ -1,6 +1,7 @@ // clang-format off #include "BakeSourceProvider.h" #include "BakeGlobalObject.h" +#include "JavaScriptCore/CallData.h" #include "JavaScriptCore/Completion.h" #include "JavaScriptCore/Identifier.h" #include "JavaScriptCore/JSCJSValue.h" @@ -41,7 +42,7 @@ extern "C" JSC::EncodedJSValue BakeLoadInitialServerCode(GlobalObject* global, B args.append(JSC::jsBoolean(separateSSRGraph)); // separateSSRGraph args.append(Zig::ImportMetaObject::create(global, "bake://server-runtime.js"_s)); // importMeta - return JSC::JSValue::encode(JSC::call(global, fn, callData, JSC::jsUndefined(), args)); + return JSC::JSValue::encode(JSC::profiledCall(global, JSC::ProfilingReason::API, fn, callData, JSC::jsUndefined(), args)); } extern "C" JSC::JSInternalPromise* BakeLoadModuleByKey(GlobalObject* global, JSC::JSString* key) { @@ -61,7 +62,7 @@ extern "C" JSC::EncodedJSValue BakeLoadServerHmrPatch(GlobalObject* global, BunS WTF::TextPosition(), JSC::SourceProviderSourceType::Program )); - + JSC::JSValue result = vm.interpreter.executeProgram(sourceCode, global, global); RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode({})); @@ -124,7 +125,7 @@ extern "C" JSC::EncodedJSValue BakeRegisterProductionChunk(JSC::JSGlobalObject* WTF::TextPosition(), JSC::SourceProviderSourceType::Module )); - + global->moduleLoader()->provideFetch(global, key, sourceCode); RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode({})); diff --git a/src/bun.js/ConsoleObject.zig b/src/bun.js/ConsoleObject.zig index b6f05770b60443..33ebd2fa143599 100644 --- a/src/bun.js/ConsoleObject.zig +++ b/src/bun.js/ConsoleObject.zig @@ -226,7 +226,7 @@ fn messageWithTypeAndLevel_( } if (print_length > 0) - format2( + try format2( level, global, vals, @@ -344,7 +344,7 @@ pub const TablePrinter = struct { value, this.globalObject, false, - ); + ) catch {}; // TODO: return @truncate(width); } @@ -492,7 +492,7 @@ pub const TablePrinter = struct { node.release(); } } - value_formatter.format( + try value_formatter.format( tag, Writer, writer, @@ -772,7 +772,7 @@ pub fn format2( comptime Writer: type, writer: Writer, options: FormatOptions, -) void { +) bun.JSError!void { if (len == 1) { // initialized later in this function. var fmt = ConsoleObject.Formatter{ @@ -795,7 +795,7 @@ pub fn format2( if (level == .Error) { writer.writeAll(comptime Output.prettyFmt("", true)) catch {}; } - fmt.format( + try fmt.format( tag, Writer, writer, @@ -807,7 +807,7 @@ pub fn format2( writer.writeAll(comptime Output.prettyFmt("", true)) catch {}; } } else { - fmt.format( + try fmt.format( tag, Writer, writer, @@ -828,7 +828,7 @@ pub fn format2( } } if (options.enable_colors) { - fmt.format( + try fmt.format( tag, Writer, writer, @@ -837,7 +837,7 @@ pub fn format2( true, ); } else { - fmt.format( + try fmt.format( tag, Writer, writer, @@ -890,7 +890,7 @@ pub fn format2( tag.tag = .{ .StringPossiblyFormatted = {} }; } - fmt.format(tag, Writer, writer, this_value, global, true); + try fmt.format(tag, Writer, writer, this_value, global, true); if (fmt.remaining_values.len == 0) { break; } @@ -912,7 +912,7 @@ pub fn format2( tag.tag = .{ .StringPossiblyFormatted = {} }; } - fmt.format(tag, Writer, writer, this_value, global, false); + try fmt.format(tag, Writer, writer, this_value, global, false); if (fmt.remaining_values.len == 0) break; @@ -988,7 +988,7 @@ pub const Formatter = struct { defer { self.formatter.remaining_values = &[_]JSValue{}; } - self.formatter.format( + try self.formatter.format( Tag.get(self.value, self.formatter.globalThis), @TypeOf(writer), writer, @@ -1367,7 +1367,7 @@ pub const Formatter = struct { slice_: Slice, global: *JSGlobalObject, comptime enable_ansi_colors: bool, - ) void { + ) bun.JSError!void { var writer = WrappedWriter(Writer){ .ctx = writer_, .estimated_line_length = &this.estimated_line_length, @@ -1425,7 +1425,7 @@ pub const Formatter = struct { const max_before_e_notation = 1000000000000000000000; const min_before_e_notation = 0.000001; switch (token) { - .s => this.printAs(Tag.String, Writer, writer_, next_value, next_value.jsType(), enable_ansi_colors), + .s => try this.printAs(Tag.String, Writer, writer_, next_value, next_value.jsType(), enable_ansi_colors), .i => { // 1. If Type(current) is Symbol, let converted be NaN // 2. Otherwise, let converted be the result of Call(%parseInt%, undefined, current, 10) @@ -1551,7 +1551,7 @@ pub const Formatter = struct { // > implementation-specific, potentially-interactive representation // > of an object judged to be maximally useful and informative. } - this.format(Tag.get(next_value, global), Writer, writer_, next_value, global, enable_ansi_colors); + try this.format(Tag.get(next_value, global), Writer, writer_, next_value, global, enable_ansi_colors); }, .c => { @@ -1722,7 +1722,7 @@ pub const Formatter = struct { key, this.formatter.globalThis, enable_ansi_colors, - ); + ) catch {}; // TODO: this.writer.writeAll(": ") catch unreachable; const value_tag = Tag.getAdvanced(value, globalObject, .{ .hide_global = true, @@ -1735,7 +1735,7 @@ pub const Formatter = struct { value, this.formatter.globalThis, enable_ansi_colors, - ); + ) catch {}; // TODO: } else { if (!single_line) { this.writer.writeAll("\n") catch unreachable; @@ -1752,7 +1752,7 @@ pub const Formatter = struct { nextValue, this.formatter.globalThis, enable_ansi_colors, - ); + ) catch {}; // TODO: } this.count += 1; if (!single_line) { @@ -1793,7 +1793,7 @@ pub const Formatter = struct { nextValue, this.formatter.globalThis, enable_ansi_colors, - ); + ) catch {}; // TODO: if (!single_line) { this.formatter.printComma(Writer, this.writer, enable_ansi_colors) catch unreachable; @@ -1953,7 +1953,7 @@ pub const Formatter = struct { } } - this.format(tag, Writer, ctx.writer, value, globalThis, enable_ansi_colors); + this.format(tag, Writer, ctx.writer, value, globalThis, enable_ansi_colors) catch {}; // TODO: if (tag.cell.isStringLike()) { if (comptime enable_ansi_colors) { @@ -1976,7 +1976,6 @@ pub const Formatter = struct { } extern fn JSC__JSValue__callCustomInspectFunction( - *JSC.JSGlobalObject, *JSC.JSGlobalObject, JSValue, JSValue, @@ -1994,12 +1993,11 @@ pub const Formatter = struct { value: JSValue, jsType: JSValue.JSType, comptime enable_ansi_colors: bool, - ) void { + ) bun.JSError!void { if (this.failed) return; if (this.globalThis.hasException()) { - this.failed = true; - return; + return error.JSError; } var writer = WrappedWriter(Writer){ .ctx = writer_, .estimated_line_length = &this.estimated_line_length }; @@ -2042,14 +2040,11 @@ pub const Formatter = struct { defer str.deinit(); this.addForNewLine(str.len); const slice = str.slice(); - this.writeWithFormatting(Writer, writer_, @TypeOf(slice), slice, this.globalThis, enable_ansi_colors); + try this.writeWithFormatting(Writer, writer_, @TypeOf(slice), slice, this.globalThis, enable_ansi_colors); }, .String => { // This is called from the '%s' formatter, so it can actually be any value - const str: bun.String = bun.String.tryFromJS(value, this.globalThis) orelse { - writer.failed = true; - return; - }; + const str: bun.String = try bun.String.fromJS2(value, this.globalThis); defer str.deref(); this.addForNewLine(str.length()); @@ -2067,7 +2062,7 @@ pub const Formatter = struct { writer.writeAll(Output.prettyFmt("", true)); if (str.isUTF16()) { - this.printAs(.JSON, Writer, writer_, value, .StringObject, enable_ansi_colors); + try this.printAs(.JSON, Writer, writer_, value, .StringObject, enable_ansi_colors); return; } @@ -2179,7 +2174,6 @@ pub const Formatter = struct { // Call custom inspect function. Will return the error if there is one // we'll need to pass the callback through to the "this" value in here const result = JSC__JSValue__callCustomInspectFunction( - JSC.VirtualMachine.get().global, this.globalThis, this.custom_formatted_object.function, this.custom_formatted_object.this, @@ -2189,16 +2183,13 @@ pub const Formatter = struct { &is_exception, ); if (is_exception) { - // Previously, this printed [native code] - // TODO: in the future, should this throw when in Bun.inspect? - writer.print("[custom formatter threw an exception]", .{}); - return; + return error.JSError; } // Strings are printed directly, otherwise we recurse. It is possible to end up in an infinite loop. if (result.isString()) { writer.print("{}", .{result.fmtString(this.globalThis)}); } else { - this.format(ConsoleObject.Formatter.Tag.get(result, this.globalThis), Writer, writer_, result, this.globalThis, enable_ansi_colors); + try this.format(ConsoleObject.Formatter.Tag.get(result, this.globalThis), Writer, writer_, result, this.globalThis, enable_ansi_colors); } }, .Symbol => { @@ -2355,11 +2346,7 @@ pub const Formatter = struct { break :first; } - this.format(tag, Writer, writer_, element, this.globalThis, enable_ansi_colors); - if (this.globalThis.hasException()) { - this.failed = true; - } - if (this.failed) return; + try this.format(tag, Writer, writer_, element, this.globalThis, enable_ansi_colors); if (tag.cell.isStringLike()) { if (comptime enable_ansi_colors) { @@ -2424,8 +2411,7 @@ pub const Formatter = struct { .disable_inspect_custom = this.disable_inspect_custom, }); - this.format(tag, Writer, writer_, element, this.globalThis, enable_ansi_colors); - if (this.failed) return; + try this.format(tag, Writer, writer_, element, this.globalThis, enable_ansi_colors); if (tag.cell.isStringLike()) { if (comptime enable_ansi_colors) { @@ -2469,7 +2455,7 @@ pub const Formatter = struct { }; value.forEachPropertyNonIndexed(this.globalThis, &iter, Iterator.forEach); if (this.globalThis.hasException()) { - this.failed = true; + return error.JSError; } if (this.failed) return; } @@ -2508,7 +2494,7 @@ pub const Formatter = struct { this.quote_keys = true; defer this.quote_keys = prev_quote_keys; - return this.printAs( + return try this.printAs( .Object, Writer, writer_, @@ -2524,7 +2510,7 @@ pub const Formatter = struct { this.quote_keys = true; defer this.quote_keys = prev_quote_keys; - return this.printAs( + return try this.printAs( .Object, Writer, writer_, @@ -2536,7 +2522,7 @@ pub const Formatter = struct { } // this case should never happen - return this.printAs(.Undefined, Writer, writer_, .undefined, .Cell, enable_ansi_colors); + return try this.printAs(.Undefined, Writer, writer_, .undefined, .Cell, enable_ansi_colors); } else if (value.as(JSC.API.Bun.Timer.TimerObject)) |timer| { this.addForNewLine("Timeout(# ) ".len + bun.fmt.fastDigitCount(@as(u64, @intCast(@max(timer.id, 0))))); if (timer.kind == .setInterval) { @@ -2561,12 +2547,12 @@ pub const Formatter = struct { return; } else if (jsType != .DOMWrapper) { if (value.isCallable(this.globalThis.vm())) { - return this.printAs(.Function, Writer, writer_, value, jsType, enable_ansi_colors); + return try this.printAs(.Function, Writer, writer_, value, jsType, enable_ansi_colors); } - return this.printAs(.Object, Writer, writer_, value, jsType, enable_ansi_colors); + return try this.printAs(.Object, Writer, writer_, value, jsType, enable_ansi_colors); } - return this.printAs(.Object, Writer, writer_, value, .Event, enable_ansi_colors); + return try this.printAs(.Object, Writer, writer_, value, .Event, enable_ansi_colors); }, .NativeCode => { if (value.getClassInfoName()) |class_name| { @@ -2790,7 +2776,7 @@ pub const Formatter = struct { const prev_quote_keys = this.quote_keys; this.quote_keys = true; defer this.quote_keys = prev_quote_keys; - this.printAs(.Object, Writer, writer_, result, value.jsType(), enable_ansi_colors); + try this.printAs(.Object, Writer, writer_, result, value.jsType(), enable_ansi_colors); return; } @@ -2833,7 +2819,7 @@ pub const Formatter = struct { const event_type = switch (EventType.map.fromJS(this.globalThis, event_type_value) orelse .unknown) { .MessageEvent, .ErrorEvent => |evt| evt, else => { - return this.printAs(.Object, Writer, writer_, value, .Event, enable_ansi_colors); + return try this.printAs(.Object, Writer, writer_, value, .Event, enable_ansi_colors); }, }; @@ -2879,7 +2865,7 @@ pub const Formatter = struct { .hide_global = true, .disable_inspect_custom = this.disable_inspect_custom, }); - this.format(tag, Writer, writer_, message_value, this.globalThis, enable_ansi_colors); + try this.format(tag, Writer, writer_, message_value, this.globalThis, enable_ansi_colors); if (this.failed) return; this.printComma(Writer, writer_, enable_ansi_colors) catch unreachable; if (!this.single_line) { @@ -2903,7 +2889,7 @@ pub const Formatter = struct { .hide_global = true, .disable_inspect_custom = this.disable_inspect_custom, }); - this.format(tag, Writer, writer_, data, this.globalThis, enable_ansi_colors); + try this.format(tag, Writer, writer_, data, this.globalThis, enable_ansi_colors); if (this.failed) return; this.printComma(Writer, writer_, enable_ansi_colors) catch unreachable; if (!this.single_line) { @@ -2925,7 +2911,7 @@ pub const Formatter = struct { .hide_global = true, .disable_inspect_custom = this.disable_inspect_custom, }); - this.format(tag, Writer, writer_, error_value, this.globalThis, enable_ansi_colors); + try this.format(tag, Writer, writer_, error_value, this.globalThis, enable_ansi_colors); if (this.failed) return; this.printComma(Writer, writer_, enable_ansi_colors) catch unreachable; if (!this.single_line) { @@ -2999,7 +2985,7 @@ pub const Formatter = struct { this.quote_strings = true; defer this.quote_strings = old_quote_strings; - this.format(Tag.getAdvanced(key_value, this.globalThis, .{ + try this.format(Tag.getAdvanced(key_value, this.globalThis, .{ .hide_global = true, .disable_inspect_custom = this.disable_inspect_custom, }), Writer, writer_, key_value, this.globalThis, enable_ansi_colors); @@ -3053,7 +3039,7 @@ pub const Formatter = struct { } } - this.format(tag, Writer, writer_, property_value, this.globalThis, enable_ansi_colors); + try this.format(tag, Writer, writer_, property_value, this.globalThis, enable_ansi_colors); if (tag.cell.isStringLike()) { if (comptime enable_ansi_colors) { @@ -3116,7 +3102,7 @@ pub const Formatter = struct { this.indent += 1; this.writeIndent(Writer, writer_) catch unreachable; defer this.indent -|= 1; - this.format(Tag.get(children, this.globalThis), Writer, writer_, children, this.globalThis, enable_ansi_colors); + try this.format(Tag.get(children, this.globalThis), Writer, writer_, children, this.globalThis, enable_ansi_colors); } writer.writeAll("\n"); @@ -3139,7 +3125,7 @@ pub const Formatter = struct { var j: usize = 0; while (j < length) : (j += 1) { const child = children.getIndex(this.globalThis, @as(u32, @intCast(j))); - this.format(Tag.getAdvanced(child, this.globalThis, .{ + try this.format(Tag.getAdvanced(child, this.globalThis, .{ .hide_global = true, .disable_inspect_custom = this.disable_inspect_custom, }), Writer, writer_, child, this.globalThis, enable_ansi_colors); @@ -3235,15 +3221,15 @@ pub const Formatter = struct { } if (this.globalThis.hasException()) { - this.failed = true; + return error.JSError; } if (this.failed) return; if (iter.i == 0) { if (value.isClass(this.globalThis)) - this.printAs(.Class, Writer, writer_, value, jsType, enable_ansi_colors) + try this.printAs(.Class, Writer, writer_, value, jsType, enable_ansi_colors) else if (value.isCallable(this.globalThis.vm())) - this.printAs(.Function, Writer, writer_, value, jsType, enable_ansi_colors) + try this.printAs(.Function, Writer, writer_, value, jsType, enable_ansi_colors) else { if (getObjectName(this.globalThis, value)) |name_str| { writer.print("{} ", .{name_str}); @@ -3373,7 +3359,7 @@ pub const Formatter = struct { } // TODO: if (options.showProxy), print like `Proxy { target: ..., handlers: ... }` // this is default off so it is not used. - this.format(ConsoleObject.Formatter.Tag.get(target, this.globalThis), Writer, writer_, target, this.globalThis, enable_ansi_colors); + try this.format(ConsoleObject.Formatter.Tag.get(target, this.globalThis), Writer, writer_, target, this.globalThis, enable_ansi_colors); }, } } @@ -3408,7 +3394,7 @@ pub const Formatter = struct { } } - pub fn format(this: *ConsoleObject.Formatter, result: Tag.Result, comptime Writer: type, writer: Writer, value: JSValue, globalThis: *JSGlobalObject, comptime enable_ansi_colors: bool) void { + pub fn format(this: *ConsoleObject.Formatter, result: Tag.Result, comptime Writer: type, writer: Writer, value: JSValue, globalThis: *JSGlobalObject, comptime enable_ansi_colors: bool) bun.JSError!void { const prevGlobalThis = this.globalThis; defer this.globalThis = prevGlobalThis; this.globalThis = globalThis; @@ -3418,11 +3404,11 @@ pub const Formatter = struct { // it _should_ limit the stack usage because each version of the // function will be relatively small switch (result.tag.tag()) { - inline else => |tag| this.printAs(tag, Writer, writer, value, result.cell, enable_ansi_colors), + inline else => |tag| try this.printAs(tag, Writer, writer, value, result.cell, enable_ansi_colors), .CustomFormattedObject => { this.custom_formatted_object = result.tag.CustomFormattedObject; - this.printAs(.CustomFormattedObject, Writer, writer, value, result.cell, enable_ansi_colors); + try this.printAs(.CustomFormattedObject, Writer, writer, value, result.cell, enable_ansi_colors); }, } } @@ -3566,9 +3552,9 @@ pub fn timeLog( const tag = ConsoleObject.Formatter.Tag.get(arg, global); _ = writer.write(" ") catch 0; if (Output.enable_ansi_colors_stderr) { - fmt.format(tag, Writer, writer, arg, global, true); + fmt.format(tag, Writer, writer, arg, global, true) catch {}; // TODO: } else { - fmt.format(tag, Writer, writer, arg, global, false); + fmt.format(tag, Writer, writer, arg, global, false) catch {}; // TODO: } } _ = writer.write("\n") catch 0; diff --git a/src/bun.js/api/BunObject.zig b/src/bun.js/api/BunObject.zig index 259e741f33b6a0..3d06db43682941 100644 --- a/src/bun.js/api/BunObject.zig +++ b/src/bun.js/api/BunObject.zig @@ -515,7 +515,7 @@ pub fn inspect(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.J const Writer = @TypeOf(writer); // we buffer this because it'll almost always be < 4096 // when it's under 4096, we want to avoid the dynamic allocation - ConsoleObject.format2( + try ConsoleObject.format2( .Debug, globalThis, @as([*]const JSValue, @ptrCast(&value)), @@ -525,13 +525,8 @@ pub fn inspect(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.J writer, formatOptions, ); - if (globalThis.hasException()) { - return .zero; - } - - buffered_writer.flush() catch { - return globalThis.throwOutOfMemory(); - }; + if (globalThis.hasException()) return error.JSError; + buffered_writer.flush() catch return globalThis.throwOutOfMemory(); // we are going to always clone to keep things simple for now // the common case here will be stack-allocated, so it should be fine @@ -541,6 +536,19 @@ pub fn inspect(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.J return ret; } +export fn Bun__inspect(globalThis: *JSGlobalObject, value: JSValue) ZigString { + // very stable memory address + var array = MutableString.init(getAllocator(globalThis), 0) catch unreachable; + var buffered_writer = MutableString.BufferedWriter{ .context = &array }; + const writer = buffered_writer.writer(); + + var formatter = ConsoleObject.Formatter{ .globalThis = globalThis }; + writer.print("{}", .{value.toFmt(&formatter)}) catch return ZigString.Empty; + buffered_writer.flush() catch return ZigString.Empty; + + return ZigString.init(array.slice()).withEncoding(); +} + pub fn getInspect(globalObject: *JSC.JSGlobalObject, _: *JSC.JSObject) JSC.JSValue { const fun = JSC.createCallback(globalObject, ZigString.static("inspect"), 2, inspect); var str = ZigString.init("nodejs.util.inspect.custom"); diff --git a/src/bun.js/bindings/BunPlugin.cpp b/src/bun.js/bindings/BunPlugin.cpp index 0f56b920d949b0..1c183820685946 100644 --- a/src/bun.js/bindings/BunPlugin.cpp +++ b/src/bun.js/bindings/BunPlugin.cpp @@ -1,5 +1,6 @@ #include "BunPlugin.h" +#include "JavaScriptCore/CallData.h" #include "JavaScriptCore/ExceptionScope.h" #include "JavaScriptCore/JSCast.h" #include "headers-handwritten.h" @@ -465,7 +466,7 @@ JSObject* JSModuleMock::executeOnce(JSC::JSGlobalObject* lexicalGlobalObject) } JSObject* callback = callbackValue.getObject(); - JSC::JSValue result = JSC::call(lexicalGlobalObject, callback, JSC::getCallData(callback), JSC::jsUndefined(), ArgList()); + JSC::JSValue result = JSC::profiledCall(lexicalGlobalObject, ProfilingReason::API, callback, JSC::getCallData(callback), JSC::jsUndefined(), ArgList()); RETURN_IF_EXCEPTION(scope, {}); if (!result.isObject()) { diff --git a/src/bun.js/bindings/BunProcess.cpp b/src/bun.js/bindings/BunProcess.cpp index fdc1bf72c7f76c..269e860d07f0a7 100644 --- a/src/bun.js/bindings/BunProcess.cpp +++ b/src/bun.js/bindings/BunProcess.cpp @@ -4,6 +4,7 @@ #include #include #include "CommonJSModuleRecord.h" +#include "JavaScriptCore/CallData.h" #include "JavaScriptCore/CatchScope.h" #include "JavaScriptCore/JSCJSValue.h" #include "JavaScriptCore/JSCast.h" @@ -1143,11 +1144,11 @@ JSC_DEFINE_HOST_FUNCTION(Process_emitWarning, (JSGlobalObject * lexicalGlobalObj } WTF::String str = arg0.toWTFString(globalObject); - return createError(globalObject, str); + auto err = createError(globalObject, str); + err->putDirect(vm, vm.propertyNames->name, jsString(vm, String("warn"_s)), JSC::PropertyAttribute::DontEnum | 0); + return err; })(); - errorInstance->putDirect(vm, vm.propertyNames->name, jsString(vm, String("warn"_s)), JSC::PropertyAttribute::DontEnum | 0); - auto ident = Identifier::fromString(vm, "warning"_s); if (process->wrapped().hasEventListeners(ident)) { JSC::MarkedArgumentBuffer args; @@ -1158,8 +1159,8 @@ JSC_DEFINE_HOST_FUNCTION(Process_emitWarning, (JSGlobalObject * lexicalGlobalObj } auto jsArgs = JSValue::encode(errorInstance); - Bun__ConsoleObject__messageWithTypeAndLevel(reinterpret_cast(globalObject->consoleClient().get())->m_client, static_cast(MessageType::Log), - static_cast(MessageLevel::Warning), globalObject, &jsArgs, 1); + Bun__ConsoleObject__messageWithTypeAndLevel(reinterpret_cast(globalObject->consoleClient().get())->m_client, static_cast(MessageType::Log), static_cast(MessageLevel::Warning), globalObject, &jsArgs, 1); + RETURN_IF_EXCEPTION(scope, {}); return JSValue::encode(jsUndefined()); } @@ -1905,7 +1906,7 @@ static JSValue constructStdioWriteStream(JSC::JSGlobalObject* globalObject, int JSC::CallData callData = JSC::getCallData(getStdioWriteStream); NakedPtr returnedException = nullptr; - auto result = JSC::call(globalObject, getStdioWriteStream, callData, globalObject->globalThis(), args, returnedException); + auto result = JSC::profiledCall(globalObject, ProfilingReason::API, getStdioWriteStream, callData, globalObject->globalThis(), args, returnedException); RETURN_IF_EXCEPTION(scope, {}); if (auto* exception = returnedException.get()) { @@ -1958,7 +1959,7 @@ static JSValue constructStdin(VM& vm, JSObject* processObject) JSC::CallData callData = JSC::getCallData(getStdioWriteStream); NakedPtr returnedException = nullptr; - auto result = JSC::call(globalObject, getStdioWriteStream, callData, globalObject, args, returnedException); + auto result = JSC::profiledCall(globalObject, ProfilingReason::API, getStdioWriteStream, callData, globalObject, args, returnedException); RETURN_IF_EXCEPTION(scope, {}); if (auto* exception = returnedException.get()) { @@ -2028,7 +2029,7 @@ static JSValue constructProcessChannel(VM& vm, JSObject* processObject) JSC::CallData callData = JSC::getCallData(getControl); NakedPtr returnedException = nullptr; - auto result = JSC::call(globalObject, getControl, callData, globalObject->globalThis(), args, returnedException); + auto result = JSC::profiledCall(globalObject, ProfilingReason::API, getControl, callData, globalObject->globalThis(), args, returnedException); RETURN_IF_EXCEPTION(scope, {}); if (auto* exception = returnedException.get()) { @@ -2195,7 +2196,7 @@ inline JSValue processBindingUtil(Zig::GlobalObject* globalObject, JSC::VM& vm) auto callData = JSC::getCallData(fn); JSC::MarkedArgumentBuffer args; args.append(jsString(vm, String("util/types"_s))); - return JSC::call(globalObject, fn, callData, globalObject, args); + return JSC::profiledCall(globalObject, ProfilingReason::API, fn, callData, globalObject, args); } inline JSValue processBindingConfig(Zig::GlobalObject* globalObject, JSC::VM& vm) @@ -2322,37 +2323,11 @@ static Structure* constructMemoryUsageStructure(JSC::VM& vm, JSC::JSGlobalObject { JSC::Structure* structure = globalObject->structureCache().emptyObjectStructureForPrototype(globalObject, globalObject->objectPrototype(), 5); PropertyOffset offset; - structure = structure->addPropertyTransition( - vm, - structure, - JSC::Identifier::fromString(vm, "rss"_s), - 0, - offset); - structure = structure->addPropertyTransition( - vm, - structure, - JSC::Identifier::fromString(vm, "heapTotal"_s), - 0, - offset); - structure = structure->addPropertyTransition( - vm, - structure, - JSC::Identifier::fromString(vm, "heapUsed"_s), - 0, - offset); - structure = structure->addPropertyTransition( - vm, - structure, - JSC::Identifier::fromString(vm, "external"_s), - 0, - offset); - structure = structure->addPropertyTransition( - vm, - structure, - JSC::Identifier::fromString(vm, "arrayBuffers"_s), - 0, - offset); - + structure = structure->addPropertyTransition(vm, structure, JSC::Identifier::fromString(vm, "rss"_s), 0, offset); + structure = structure->addPropertyTransition(vm, structure, JSC::Identifier::fromString(vm, "heapTotal"_s), 0, offset); + structure = structure->addPropertyTransition(vm, structure, JSC::Identifier::fromString(vm, "heapUsed"_s), 0, offset); + structure = structure->addPropertyTransition(vm, structure, JSC::Identifier::fromString(vm, "external"_s), 0, offset); + structure = structure->addPropertyTransition(vm, structure, JSC::Identifier::fromString(vm, "arrayBuffers"_s), 0, offset); return structure; } @@ -2639,7 +2614,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionOpenStdin, (JSGlobalObject * globalObje auto callData = getCallData(resumeFunction); MarkedArgumentBuffer args; - JSC::call(globalObject, resumeFunction, callData, stdinValue, args); + JSC::profiledCall(globalObject, ProfilingReason::API, resumeFunction, callData, stdinValue, args); RETURN_IF_EXCEPTION(throwScope, {}); } @@ -2673,11 +2648,9 @@ static JSValue Process_stubEmptySet(VM& vm, JSObject* processObject) static JSValue constructMemoryUsage(VM& vm, JSObject* processObject) { auto* globalObject = processObject->globalObject(); - JSC::JSFunction* memoryUsage = JSC::JSFunction::create(vm, globalObject, 0, - String("memoryUsage"_s), Process_functionMemoryUsage, ImplementationVisibility::Public); + JSC::JSFunction* memoryUsage = JSC::JSFunction::create(vm, globalObject, 0, String("memoryUsage"_s), Process_functionMemoryUsage, ImplementationVisibility::Public); - JSC::JSFunction* rss = JSC::JSFunction::create(vm, globalObject, 0, - String("rss"_s), Process_functionMemoryUsageRSS, ImplementationVisibility::Public); + JSC::JSFunction* rss = JSC::JSFunction::create(vm, globalObject, 0, String("rss"_s), Process_functionMemoryUsageRSS, ImplementationVisibility::Public); memoryUsage->putDirect(vm, JSC::Identifier::fromString(vm, "rss"_s), rss, 0); return memoryUsage; @@ -2758,7 +2731,7 @@ JSValue Process::constructNextTickFn(JSC::VM& vm, Zig::GlobalObject* globalObjec args.append(JSC::JSFunction::create(vm, globalObject, 1, String(), jsFunctionDrainMicrotaskQueue, ImplementationVisibility::Private)); args.append(JSC::JSFunction::create(vm, globalObject, 1, String(), jsFunctionReportUncaughtException, ImplementationVisibility::Private)); - JSValue nextTickFunction = JSC::call(globalObject, initializer, JSC::getCallData(initializer), globalObject->globalThis(), args); + JSValue nextTickFunction = JSC::profiledCall(globalObject, ProfilingReason::API, initializer, JSC::getCallData(initializer), globalObject->globalThis(), args); if (nextTickFunction && nextTickFunction.isObject()) { this->m_nextTickFunction.set(vm, this, nextTickFunction.getObject()); } @@ -2981,7 +2954,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionKill, JSC::CallData callData = JSC::getCallData(_killFn); NakedPtr returnedException = nullptr; - auto result = JSC::call(globalObject, _killFn, callData, globalObject->globalThis(), args, returnedException); + auto result = JSC::profiledCall(globalObject, ProfilingReason::API, _killFn, callData, globalObject->globalThis(), args, returnedException); RETURN_IF_EXCEPTION(scope, {}); if (auto* exception = returnedException.get()) { diff --git a/src/bun.js/bindings/CallSite.cpp b/src/bun.js/bindings/CallSite.cpp index f1a51812655a58..6d2d15e09d70f7 100644 --- a/src/bun.js/bindings/CallSite.cpp +++ b/src/bun.js/bindings/CallSite.cpp @@ -6,6 +6,7 @@ #include "config.h" #include "CallSite.h" +#include "JavaScriptCore/CallData.h" #include "helpers.h" #include @@ -89,8 +90,7 @@ JSC_DEFINE_HOST_FUNCTION(nativeFrameForTesting, (JSC::JSGlobalObject * globalObj auto scope = DECLARE_THROW_SCOPE(vm); JSC::JSFunction* function = jsCast(callFrame->argument(0)); - return JSValue::encode( - JSC::call(globalObject, function, JSC::ArgList(), "nativeFrameForTesting"_s)); + return JSValue::encode(JSC::call(globalObject, function, JSC::ArgList(), "nativeFrameForTesting"_s)); } JSValue createNativeFrameForTesting(Zig::GlobalObject* globalObject) diff --git a/src/bun.js/bindings/CommonJSModuleRecord.cpp b/src/bun.js/bindings/CommonJSModuleRecord.cpp index 6ab63c26cb5fcd..9573cfa46a84d0 100644 --- a/src/bun.js/bindings/CommonJSModuleRecord.cpp +++ b/src/bun.js/bindings/CommonJSModuleRecord.cpp @@ -31,6 +31,7 @@ #include "headers.h" +#include "JavaScriptCore/CallData.h" #include "JavaScriptCore/Synchronousness.h" #include "JavaScriptCore/JSCast.h" #include @@ -199,7 +200,7 @@ static bool evaluateCommonJSModuleOnce(JSC::VM& vm, Zig::GlobalObject* globalObj // // fn(exports, require, module, __filename, __dirname) { /* code */ }(exports, require, module, __filename, __dirname) // - JSC::call(globalObject, fn, callData, moduleObject, args, exception); + JSC::profiledCall(globalObject, ProfilingReason::API, fn, callData, moduleObject, args, exception); return exception.get() == nullptr; } diff --git a/src/bun.js/bindings/ConsoleObject.cpp b/src/bun.js/bindings/ConsoleObject.cpp index 1645511c7f2cad..2d4653beefbfff 100644 --- a/src/bun.js/bindings/ConsoleObject.cpp +++ b/src/bun.js/bindings/ConsoleObject.cpp @@ -55,11 +55,7 @@ void ConsoleObject::messageWithTypeAndLevel(MessageType type, MessageLevel level return; } - auto scope = DECLARE_CATCH_SCOPE(vm); - Bun__ConsoleObject__messageWithTypeAndLevel(this->m_client, static_cast(type), - static_cast(level), globalObject, jsArgs, - count); - scope.clearException(); + Bun__ConsoleObject__messageWithTypeAndLevel(this->m_client, static_cast(type), static_cast(level), globalObject, jsArgs, count); } void ConsoleObject::count(JSGlobalObject* globalObject, const String& label) { diff --git a/src/bun.js/bindings/ErrorCode.cpp b/src/bun.js/bindings/ErrorCode.cpp index cd2ad0ee1fe16e..1fc96a4da228f3 100644 --- a/src/bun.js/bindings/ErrorCode.cpp +++ b/src/bun.js/bindings/ErrorCode.cpp @@ -175,6 +175,10 @@ JSObject* createError(Zig::JSGlobalObject* globalObject, ErrorCode code, JSC::JS return createError(vm, globalObject, code, message); } +// export fn Bun__inspect(globalThis: *JSGlobalObject, value: JSValue) ZigString +extern "C" ZigString Bun__inspect(JSC::JSGlobalObject* globalObject, JSValue value); + +// WTF::String JSValueToStringSafe(JSC::JSGlobalObject* globalObject, JSValue arg) { ASSERT(!arg.isEmpty()); @@ -187,7 +191,6 @@ WTF::String JSValueToStringSafe(JSC::JSGlobalObject* globalObject, JSValue arg) return arg.toWTFStringForConsole(globalObject); } case JSC::JSType::SymbolType: { - auto symbol = jsCast(cell); auto result = symbol->tryGetDescriptiveString(); if (result.has_value()) @@ -201,14 +204,14 @@ WTF::String JSValueToStringSafe(JSC::JSGlobalObject* globalObject, JSValue arg) auto name = JSC::getCalculatedDisplayName(vm, cell->getObject()); if (catchScope.exception()) { catchScope.clearException(); - name = "Function"_s; + name = ""_s; } if (!name.isNull() && name.length() > 0) { return makeString("[Function: "_s, name, ']'); } - return "Function"_s; + return "[Function: (anonymous)]"_s; break; } @@ -217,47 +220,142 @@ WTF::String JSValueToStringSafe(JSC::JSGlobalObject* globalObject, JSValue arg) } } - return arg.toWTFStringForConsole(globalObject); + ZigString zstring = Bun__inspect(globalObject, arg); + BunString bstring(BunStringTag::ZigString, BunStringImpl(zstring)); + return bstring.toWTFString(); +} + +WTF::String determineSpecificType(JSC::JSGlobalObject* globalObject, JSValue value) +{ + auto& vm = globalObject->vm(); + auto scope = DECLARE_CATCH_SCOPE(vm); + + ASSERT(!value.isEmpty()); + + if (value.isNull()) { + return String("null"_s); + } + if (value.isUndefined()) { + return String("undefined"_s); + } + if (value.isNumber()) { + double d = value.asNumber(); + double infinity = std::numeric_limits::infinity(); + if (value == 0) return (1 / d == -infinity) ? String("type number (-0)"_s) : String("type number (0)"_s); + if (d != d) return String("type number (NaN)"_s); + if (d == infinity) return String("type number (Infinity)"_s); + if (d == -infinity) return String("type number (-Infinity)"_s); + auto str = value.toStringOrNull(globalObject); + if (!str) return {}; + return makeString("type number ("_s, str->getString(globalObject), ")"_s); + } + if (value.isBoolean()) { + return value.asBoolean() ? String("type boolean (true)"_s) : String("type boolean (false)"_s); + } + if (value.isBigInt()) { + auto str = value.toString(globalObject); + if (!str) return {}; + return makeString("type bigint ("_s, str->getString(globalObject), "n)"_s); + } + + ASSERT(value.isCell()); + auto cell = value.asCell(); + + if (cell->isSymbol()) { + auto symbol = jsCast(cell); + auto result = symbol->tryGetDescriptiveString(); + auto description = result.has_value() ? result.value() : String("Symbol()"_s); + return makeString("type symbol ("_s, description, ")"_s); + } + if (cell->isCallable()) { + auto name = JSC::getCalculatedDisplayName(vm, cell->getObject()); + if (scope.exception()) { + scope.clearException(); + name = String(""_s); + } + if (!name.isNull() && name.length() > 0) { + return makeString("function "_s, name); + } + return String("function"_s); + } + if (cell->isString()) { + auto str = value.toString(globalObject)->getString(globalObject); + if (str.length() > 28) { + str = str.substring(0, 25); + str = makeString(str, "..."_s); + if (!str.contains('\'')) { + return makeString("type string ('"_s, str, "')"_s); + } + } + // return `type string (${JSONStringify(value)})`; + str = value.toWTFStringForConsole(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + return makeString("type string ("_s, str, ")"_s); + } + if (cell->isObject()) { + auto constructor = value.get(globalObject, vm.propertyNames->constructor); + RETURN_IF_EXCEPTION(scope, {}); + if (constructor.toBoolean(globalObject)) { + auto name = constructor.get(globalObject, vm.propertyNames->name); + RETURN_IF_EXCEPTION(scope, {}); + auto str = name.toString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + return makeString("an instance of "_s, str->getString(globalObject)); + } + // return `${lazyInternalUtilInspect().inspect(value, { depth: -1 })}`; + auto str = JSValueToStringSafe(globalObject, value); + RETURN_IF_EXCEPTION(scope, {}); + return str; + } + + // value = lazyInternalUtilInspect().inspect(value, { colors: false }); + auto str = JSValueToStringSafe(globalObject, value); + RETURN_IF_EXCEPTION(scope, {}); + return str; } namespace Message { WTF::String ERR_INVALID_ARG_TYPE(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, const StringView& arg_name, const StringView& expected_type, JSValue actual_value) { - auto actual_value_string = JSValueToStringSafe(globalObject, actual_value); + auto actual_value_string = determineSpecificType(globalObject, actual_value); RETURN_IF_EXCEPTION(scope, {}); - return makeString("The \""_s, arg_name, "\" argument must be of type "_s, expected_type, ". Received: "_s, actual_value_string); + return makeString("The \""_s, arg_name, "\" argument must be of type "_s, expected_type, ". Received "_s, actual_value_string); } WTF::String ERR_INVALID_ARG_TYPE(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, const StringView& arg_name, ArgList expected_types, JSValue actual_value) { WTF::StringBuilder result; - auto actual_value_string = JSValueToStringSafe(globalObject, actual_value); + auto actual_value_string = determineSpecificType(globalObject, actual_value); RETURN_IF_EXCEPTION(scope, {}); - result.append("The \""_s, arg_name, "\" argument must be of type "_s); + result.append("The "_s); + + if (arg_name.contains(' ')) { + result.append(arg_name); + } else { + result.append("\""_s); + result.append(arg_name); + result.append("\" argument"_s); + } + result.append(" must be of type "_s); unsigned length = expected_types.size(); if (length == 1) { result.append(expected_types.at(0).toWTFString(globalObject)); - } else if (length == 2) { - result.append(expected_types.at(0).toWTFString(globalObject)); - result.append(" or "_s); - result.append(expected_types.at(1).toWTFString(globalObject)); } else { for (unsigned i = 0; i < length - 1; i++) { - if (i > 0) - result.append(", "_s); JSValue expected_type = expected_types.at(i); result.append(expected_type.toWTFString(globalObject)); + result.append(", "_s); } - result.append(" or "_s); + result.append("or "_s); result.append(expected_types.at(length - 1).toWTFString(globalObject)); } - result.append(". Received: "_s, actual_value_string); + result.append(". Received "_s, actual_value_string); return result.toString(); } @@ -295,7 +393,7 @@ WTF::String ERR_OUT_OF_RANGE(JSC::ThrowScope& scope, JSC::JSGlobalObject* global auto input = JSValueToStringSafe(globalObject, val_input); RETURN_IF_EXCEPTION(scope, {}); - return makeString("The value of \""_s, arg_name, "\" is out of range. It must be "_s, range, ". Received: "_s, input); + return makeString("The value of \""_s, arg_name, "\" is out of range. It must be "_s, range, ". Received "_s, input); } } @@ -308,7 +406,7 @@ JSC::EncodedJSValue INVALID_ARG_TYPE(JSC::ThrowScope& throwScope, JSC::JSGlobalO auto ty_first_char = expected_type[0]; auto ty_kind = ty_first_char >= 'A' && ty_first_char <= 'Z' ? "an instance of"_s : "of type"_s; - auto actual_value = JSValueToStringSafe(globalObject, val_actual_value); + auto actual_value = determineSpecificType(globalObject, val_actual_value); RETURN_IF_EXCEPTION(throwScope, {}); auto message = makeString("The \""_s, arg_name, "\" "_s, arg_kind, " must be "_s, ty_kind, " "_s, expected_type, ". Received "_s, actual_value); @@ -324,7 +422,7 @@ JSC::EncodedJSValue INVALID_ARG_TYPE(JSC::ThrowScope& throwScope, JSC::JSGlobalO auto ty_first_char = expected_type[0]; auto ty_kind = ty_first_char >= 'A' && ty_first_char <= 'Z' ? "an instance of"_s : "of type"_s; - auto actual_value = JSValueToStringSafe(globalObject, val_actual_value); + auto actual_value = determineSpecificType(globalObject, val_actual_value); RETURN_IF_EXCEPTION(throwScope, {}); auto message = makeString("The \""_s, arg_name, "\" "_s, arg_kind, " must be "_s, ty_kind, " "_s, expected_type, ". Received "_s, actual_value); @@ -600,6 +698,30 @@ JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_BUFFER_OUT_OF_BOUNDS, (JSC::JSGlobalObje return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_BUFFER_OUT_OF_BOUNDS, "Attempt to access memory outside buffer bounds"_s)); } +JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_UNHANDLED_ERROR, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto err = callFrame->argument(0); + + if (err.isUndefined()) { + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_UNHANDLED_ERROR, "Unhandled error."_s)); + } + if (err.isString()) { + auto err_str = err.getString(globalObject); + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_UNHANDLED_ERROR, makeString("Unhandled error. ("_s, err_str, ")"_s))); + } + if (err.isCell()) { + auto cell = err.asCell(); + if (cell->inherits()) { + return JSC::JSValue::encode(jsCast(cell)->value()); + } + } + auto err_str = err.toWTFString(globalObject); + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_UNHANDLED_ERROR, makeString("Unhandled error. ("_s, err_str, ")"_s))); +} + } // namespace Bun JSC::JSValue WebCore::toJS(JSC::JSGlobalObject* globalObject, CommonAbortReason abortReason) diff --git a/src/bun.js/bindings/ErrorCode.h b/src/bun.js/bindings/ErrorCode.h index a3f4f8a67eba91..6a4387e51c6193 100644 --- a/src/bun.js/bindings/ErrorCode.h +++ b/src/bun.js/bindings/ErrorCode.h @@ -67,6 +67,7 @@ JSC_DECLARE_HOST_FUNCTION(jsFunction_ERR_BROTLI_INVALID_PARAM); JSC_DECLARE_HOST_FUNCTION(jsFunction_ERR_BUFFER_TOO_LARGE); JSC_DECLARE_HOST_FUNCTION(jsFunction_ERR_ZLIB_INITIALIZATION_FAILED); JSC_DECLARE_HOST_FUNCTION(jsFunction_ERR_BUFFER_OUT_OF_BOUNDS); +JSC_DECLARE_HOST_FUNCTION(jsFunction_ERR_UNHANDLED_ERROR); enum Bound { LOWER, diff --git a/src/bun.js/bindings/ErrorCode.ts b/src/bun.js/bindings/ErrorCode.ts index a4b54170546a4d..ce57deaac52cc5 100644 --- a/src/bun.js/bindings/ErrorCode.ts +++ b/src/bun.js/bindings/ErrorCode.ts @@ -51,6 +51,7 @@ export default [ ["ERR_INVALID_URI", URIError, "URIError"], ["ERR_SCRIPT_EXECUTION_TIMEOUT", Error, "Error"], ["ERR_SCRIPT_EXECUTION_INTERRUPTED", Error, "Error"], + ["ERR_UNHANDLED_ERROR", Error], // Bun-specific ["ERR_FORMDATA_PARSE_ERROR", TypeError], diff --git a/src/bun.js/bindings/ImportMetaObject.cpp b/src/bun.js/bindings/ImportMetaObject.cpp index 61b64f0229da44..561ae2d213b20b 100644 --- a/src/bun.js/bindings/ImportMetaObject.cpp +++ b/src/bun.js/bindings/ImportMetaObject.cpp @@ -23,6 +23,7 @@ #include "WebCoreJSClientData.h" #include #include +#include #include #include @@ -325,7 +326,7 @@ extern "C" JSC::EncodedJSValue functionImportMeta__resolveSyncPrivate(JSC::JSGlo auto bunStr = Bun::toString(parentIdStr); args.append(jsBoolean(Bun__isBunMain(lexicalGlobalObject, &bunStr))); - return JSValue::encode(JSC::call(lexicalGlobalObject, overrideHandler, JSC::getCallData(overrideHandler), parentModuleObject, args)); + return JSValue::encode(JSC::profiledCall(lexicalGlobalObject, ProfilingReason::API, overrideHandler, JSC::getCallData(overrideHandler), parentModuleObject, args)); } } } diff --git a/src/bun.js/bindings/JSBundlerPlugin.cpp b/src/bun.js/bindings/JSBundlerPlugin.cpp index fea22a07a47b9d..559023efb6b174 100644 --- a/src/bun.js/bindings/JSBundlerPlugin.cpp +++ b/src/bun.js/bindings/JSBundlerPlugin.cpp @@ -2,6 +2,7 @@ #include "BunProcess.h" #include "../../../packages/bun-native-bundler-plugin-api/bundler_plugin.h" +#include "JavaScriptCore/CallData.h" #include "headers-handwritten.h" #include #include @@ -618,7 +619,7 @@ extern "C" JSC::EncodedJSValue JSBundlerPlugin__runSetupFunction( arguments.append(JSValue::decode(encodedIsBake)); auto* lexicalGlobalObject = jsCast(JSValue::decode(encodedSetupFunction))->globalObject(); - return JSC::JSValue::encode(JSC::call(lexicalGlobalObject, setupFunction, callData, plugin, arguments)); + return JSC::JSValue::encode(JSC::profiledCall(lexicalGlobalObject, ProfilingReason::API, setupFunction, callData, plugin, arguments)); } extern "C" void JSBundlerPlugin__setConfig(Bun::JSBundlerPlugin* plugin, void* config) diff --git a/src/bun.js/bindings/JSEnvironmentVariableMap.cpp b/src/bun.js/bindings/JSEnvironmentVariableMap.cpp index daac63987850bd..53463a05611bfb 100644 --- a/src/bun.js/bindings/JSEnvironmentVariableMap.cpp +++ b/src/bun.js/bindings/JSEnvironmentVariableMap.cpp @@ -380,7 +380,7 @@ JSValue createEnvironmentVariablesMap(Zig::GlobalObject* globalObject) auto clientData = WebCore::clientData(vm); JSC::CallData callData = JSC::getCallData(getSourceEvent); NakedPtr returnedException = nullptr; - auto result = JSC::call(globalObject, getSourceEvent, callData, globalObject->globalThis(), args, returnedException); + auto result = JSC::profiledCall(globalObject, JSC::ProfilingReason::API, getSourceEvent, callData, globalObject->globalThis(), args, returnedException); RETURN_IF_EXCEPTION(scope, {}); if (returnedException) { diff --git a/src/bun.js/bindings/JSFFIFunction.cpp b/src/bun.js/bindings/JSFFIFunction.cpp index b58d4ed14487d7..e3a1fc3239a128 100644 --- a/src/bun.js/bindings/JSFFIFunction.cpp +++ b/src/bun.js/bindings/JSFFIFunction.cpp @@ -30,6 +30,7 @@ #include #include "ZigGlobalObject.h" +#include #include #include "DOMJITIDLConvert.h" #include "DOMJITIDLType.h" @@ -202,7 +203,7 @@ FFI_Callback_call(FFICallbackFunctionWrapper& wrapper, size_t argCount, JSC::Enc for (size_t i = 0; i < argCount; ++i) arguments.appendWithCrashOnOverflow(JSC::JSValue::decode(args[i])); WTF::NakedPtr exception; - auto result = JSC::call(globalObject, function, JSC::getCallData(function), JSC::jsUndefined(), arguments, exception); + auto result = JSC::profiledCall(globalObject, JSC::ProfilingReason::API, function, JSC::getCallData(function), JSC::jsUndefined(), arguments, exception); if (UNLIKELY(exception)) { auto scope = DECLARE_THROW_SCOPE(vm); scope.throwException(globalObject, exception); @@ -229,7 +230,7 @@ FFI_Callback_threadsafe_call(FFICallbackFunctionWrapper& wrapper, size_t argCoun for (size_t i = 0; i < argsVec.size(); ++i) arguments.appendWithCrashOnOverflow(JSC::JSValue::decode(argsVec[i])); WTF::NakedPtr exception; - JSC::call(globalObject, function, JSC::getCallData(function), JSC::jsUndefined(), arguments, exception); + JSC::profiledCall(globalObject, JSC::ProfilingReason::API, function, JSC::getCallData(function), JSC::jsUndefined(), arguments, exception); if (UNLIKELY(exception)) { auto scope = DECLARE_THROW_SCOPE(vm); scope.throwException(globalObject, exception); @@ -248,7 +249,7 @@ FFI_Callback_call_0(FFICallbackFunctionWrapper& wrapper, size_t argCount, JSC::E JSC::MarkedArgumentBuffer arguments; WTF::NakedPtr exception; - auto result = JSC::call(globalObject, function, JSC::getCallData(function), JSC::jsUndefined(), arguments, exception); + auto result = JSC::profiledCall(globalObject, JSC::ProfilingReason::API, function, JSC::getCallData(function), JSC::jsUndefined(), arguments, exception); if (UNLIKELY(exception)) { auto scope = DECLARE_THROW_SCOPE(vm); scope.throwException(globalObject, exception); @@ -269,7 +270,7 @@ FFI_Callback_call_1(FFICallbackFunctionWrapper& wrapper, size_t argCount, JSC::E arguments.append(JSC::JSValue::decode(args[0])); WTF::NakedPtr exception; - auto result = JSC::call(globalObject, function, JSC::getCallData(function), JSC::jsUndefined(), arguments, exception); + auto result = JSC::profiledCall(globalObject, JSC::ProfilingReason::API, function, JSC::getCallData(function), JSC::jsUndefined(), arguments, exception); if (UNLIKELY(exception)) { auto scope = DECLARE_THROW_SCOPE(vm); scope.throwException(globalObject, exception); @@ -291,7 +292,7 @@ FFI_Callback_call_2(FFICallbackFunctionWrapper& wrapper, size_t argCount, JSC::E arguments.append(JSC::JSValue::decode(args[1])); WTF::NakedPtr exception; - auto result = JSC::call(globalObject, function, JSC::getCallData(function), JSC::jsUndefined(), arguments, exception); + auto result = JSC::profiledCall(globalObject, JSC::ProfilingReason::API, function, JSC::getCallData(function), JSC::jsUndefined(), arguments, exception); if (UNLIKELY(exception)) { auto scope = DECLARE_THROW_SCOPE(vm); scope.throwException(globalObject, exception); @@ -313,7 +314,7 @@ extern "C" JSC::EncodedJSValue FFI_Callback_call_3(FFICallbackFunctionWrapper& w arguments.append(JSC::JSValue::decode(args[2])); WTF::NakedPtr exception; - auto result = JSC::call(globalObject, function, JSC::getCallData(function), JSC::jsUndefined(), arguments, exception); + auto result = JSC::profiledCall(globalObject, JSC::ProfilingReason::API, function, JSC::getCallData(function), JSC::jsUndefined(), arguments, exception); if (UNLIKELY(exception)) { auto scope = DECLARE_THROW_SCOPE(vm); scope.throwException(globalObject, exception); @@ -336,7 +337,7 @@ extern "C" JSC::EncodedJSValue FFI_Callback_call_4(FFICallbackFunctionWrapper& w arguments.append(JSC::JSValue::decode(args[3])); WTF::NakedPtr exception; - auto result = JSC::call(globalObject, function, JSC::getCallData(function), JSC::jsUndefined(), arguments, exception); + auto result = JSC::profiledCall(globalObject, JSC::ProfilingReason::API, function, JSC::getCallData(function), JSC::jsUndefined(), arguments, exception); if (UNLIKELY(exception)) { auto scope = DECLARE_THROW_SCOPE(vm); scope.throwException(globalObject, exception); @@ -360,7 +361,7 @@ extern "C" JSC::EncodedJSValue FFI_Callback_call_5(FFICallbackFunctionWrapper& w arguments.append(JSC::JSValue::decode(args[4])); WTF::NakedPtr exception; - auto result = JSC::call(globalObject, function, JSC::getCallData(function), JSC::jsUndefined(), arguments, exception); + auto result = JSC::profiledCall(globalObject, JSC::ProfilingReason::API, function, JSC::getCallData(function), JSC::jsUndefined(), arguments, exception); if (UNLIKELY(exception)) { auto scope = DECLARE_THROW_SCOPE(vm); scope.throwException(globalObject, exception); @@ -386,7 +387,7 @@ FFI_Callback_call_6(FFICallbackFunctionWrapper& wrapper, size_t argCount, JSC::E arguments.append(JSC::JSValue::decode(args[5])); WTF::NakedPtr exception; - auto result = JSC::call(globalObject, function, JSC::getCallData(function), JSC::jsUndefined(), arguments, exception); + auto result = JSC::profiledCall(globalObject, JSC::ProfilingReason::API, function, JSC::getCallData(function), JSC::jsUndefined(), arguments, exception); if (UNLIKELY(exception)) { auto scope = DECLARE_THROW_SCOPE(vm); scope.throwException(globalObject, exception); @@ -413,7 +414,7 @@ FFI_Callback_call_7(FFICallbackFunctionWrapper& wrapper, size_t argCount, JSC::E arguments.append(JSC::JSValue::decode(args[6])); WTF::NakedPtr exception; - auto result = JSC::call(globalObject, function, JSC::getCallData(function), JSC::jsUndefined(), arguments, exception); + auto result = JSC::profiledCall(globalObject, JSC::ProfilingReason::API, function, JSC::getCallData(function), JSC::jsUndefined(), arguments, exception); if (UNLIKELY(exception)) { auto scope = DECLARE_THROW_SCOPE(vm); scope.throwException(globalObject, exception); diff --git a/src/bun.js/bindings/UtilInspect.cpp b/src/bun.js/bindings/UtilInspect.cpp index 9df5b95eada7e3..5e72a31557d173 100644 --- a/src/bun.js/bindings/UtilInspect.cpp +++ b/src/bun.js/bindings/UtilInspect.cpp @@ -37,7 +37,6 @@ JSObject* createInspectOptionsObject(VM& vm, Zig::GlobalObject* globalObject, un extern "C" JSC::EncodedJSValue JSC__JSValue__callCustomInspectFunction( Zig::GlobalObject* globalObject, - JSC::JSGlobalObject* lexicalGlobalObject, JSC__JSValue encodedFunctionValue, JSC__JSValue encodedThisValue, unsigned depth, @@ -59,11 +58,10 @@ extern "C" JSC::EncodedJSValue JSC__JSValue__callCustomInspectFunction( arguments.append(options); arguments.append(inspectFn); - auto inspectRet = JSC::call(lexicalGlobalObject, functionToCall, callData, thisValue, arguments); + auto inspectRet = JSC::profiledCall(globalObject, ProfilingReason::API, functionToCall, callData, thisValue, arguments); if (auto exe = scope.exception()) { *is_exception = true; - scope.clearException(); - return JSValue::encode(exe); + return {}; } RELEASE_AND_RETURN(scope, JSValue::encode(inspectRet)); } diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index a8a8ea91cebb12..fe179e0de38f37 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -1885,7 +1885,7 @@ JSC_DEFINE_HOST_FUNCTION(functionCallback, (JSC::JSGlobalObject * globalObject, { JSFunction* callback = jsCast(callFrame->uncheckedArgument(0)); JSC::CallData callData = JSC::getCallData(callback); - return JSC::JSValue::encode(JSC::call(globalObject, callback, callData, JSC::jsUndefined(), JSC::MarkedArgumentBuffer())); + return JSC::JSValue::encode(JSC::profiledCall(globalObject, ProfilingReason::API, callback, callData, JSC::jsUndefined(), JSC::MarkedArgumentBuffer())); } JSC_DEFINE_CUSTOM_GETTER(noop_getter, (JSGlobalObject*, EncodedJSValue, PropertyName)) @@ -2563,7 +2563,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionPerformMicrotask, (JSGlobalObject * globalObj break; } - JSC::call(globalObject, job, callData, jsUndefined(), arguments, exceptionPtr); + JSC::profiledCall(globalObject, ProfilingReason::API, job, callData, jsUndefined(), arguments, exceptionPtr); if (asyncContextData) { asyncContextData->putInternalField(vm, 0, restoreAsyncContext); @@ -2615,7 +2615,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionPerformMicrotaskVariadic, (JSGlobalObject * g asyncContextData->putInternalField(vm, 0, setAsyncContext); } - JSC::call(globalObject, job, callData, thisValue, arguments, exceptionPtr); + JSC::profiledCall(globalObject, ProfilingReason::API, job, callData, thisValue, arguments, exceptionPtr); if (asyncContextData) { asyncContextData->putInternalField(vm, 0, restoreAsyncContext); @@ -2852,7 +2852,7 @@ void GlobalObject::finishCreation(VM& vm) auto* function = JSFunction::create(vm, globalObject, static_cast(importMetaObjectCreateRequireCacheCodeGenerator(vm)), globalObject); NakedPtr returnedException = nullptr; - auto result = JSC::call(globalObject, function, JSC::getCallData(function), globalObject, ArgList(), returnedException); + auto result = JSC::profiledCall(globalObject, ProfilingReason::API, function, JSC::getCallData(function), globalObject, ArgList(), returnedException); init.set(result.toObject(globalObject)); }); @@ -2978,7 +2978,7 @@ void GlobalObject::finishCreation(VM& vm) JSC::CallData callData = JSC::getCallData(getStylize); NakedPtr returnedException = nullptr; - auto result = JSC::call(init.owner, getStylize, callData, jsNull(), args, returnedException); + auto result = JSC::profiledCall(init.owner, ProfilingReason::API, getStylize, callData, jsNull(), args, returnedException); // RETURN_IF_EXCEPTION(scope, {}); if (returnedException) { @@ -3431,7 +3431,7 @@ JSC_DEFINE_CUSTOM_GETTER(getConsoleConstructor, (JSGlobalObject * globalObject, args.append(console); JSC::CallData callData = JSC::getCallData(createConsoleConstructor); NakedPtr returnedException = nullptr; - auto result = JSC::call(globalObject, createConsoleConstructor, callData, console, args, returnedException); + auto result = JSC::profiledCall(globalObject, ProfilingReason::API, createConsoleConstructor, callData, console, args, returnedException); if (returnedException) { auto scope = DECLARE_THROW_SCOPE(vm); throwException(globalObject, scope, returnedException.get()); diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig index 94a0698a29842f..ef989ab0d13837 100644 --- a/src/bun.js/bindings/bindings.zig +++ b/src/bun.js/bindings/bindings.zig @@ -4482,33 +4482,6 @@ pub const JSValue = enum(i64) { try buffered_writer.flush(); } - pub fn jestPrettyFormat(this: JSValue, out: *MutableString, globalObject: *JSGlobalObject) !void { - var buffered_writer = MutableString.BufferedWriter{ .context = out }; - const writer = buffered_writer.writer(); - const Writer = @TypeOf(writer); - - const fmt_options = JSC.ConsoleObject.FormatOptions{ - .enable_colors = false, - .add_newline = false, - .flush = false, - .ordered_properties = true, - .quote_strings = true, - }; - - JSC.ConsoleObject.format2( - .Debug, - globalObject, - @as([*]const JSValue, @ptrCast(&this)), - 1, - Writer, - Writer, - writer, - fmt_options, - ); - - try buffered_writer.flush(); - } - extern fn JSBuffer__bufferFromLength(*JSGlobalObject, i64) JSValue; /// Must come from globally-allocated memory if allocator is not null @@ -6803,10 +6776,7 @@ pub const JSHostZigFunction = fn (*JSGlobalObject, *CallFrame) bun.JSError!JSVal pub fn toJSHostFunction(comptime Function: JSHostZigFunction) JSC.JSHostFunctionType { return struct { - pub fn function( - globalThis: *JSC.JSGlobalObject, - callframe: *JSC.CallFrame, - ) callconv(JSC.conv) JSC.JSValue { + pub fn function(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { if (bun.Environment.allow_assert and bun.Environment.is_canary) { const value = Function(globalThis, callframe) catch |err| switch (err) { error.JSError => .zero, @@ -6815,14 +6785,17 @@ pub fn toJSHostFunction(comptime Function: JSHostZigFunction) JSC.JSHostFunction if (comptime bun.Environment.isDebug) { if (value != .zero) { if (globalThis.hasException()) { + var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis }; bun.Output.prettyErrorln( \\Assertion failed: Native function returned a non-zero JSValue while an exception is pending \\ - \\Did you forget to check if an exception is pending? - \\ - \\ if (globalThis.hasException()) return .zero; + \\ fn: {s} + \\ value: {} \\ - , .{}); + , .{ + &Function, // use `(lldb) image lookup --address 0x1ec4` to discover what function failed + value.toFmt(&formatter), + }); Output.flush(); } } diff --git a/src/bun.js/bindings/webcore/JSDOMIterator.h b/src/bun.js/bindings/webcore/JSDOMIterator.h index 4f60337af08918..cb3becd31303e9 100644 --- a/src/bun.js/bindings/webcore/JSDOMIterator.h +++ b/src/bun.js/bindings/webcore/JSDOMIterator.h @@ -31,6 +31,7 @@ #include #include #include "ErrorCode.h" +#include "JavaScriptCore/CallData.h" #include "JavaScriptCore/Interpreter.h" namespace WebCore { @@ -228,7 +229,7 @@ template JSC::JSValue iteratorForEach(JSC::JSGlobalObject& throwOutOfMemoryError(&lexicalGlobalObject, scope); return {}; } - JSC::call(&lexicalGlobalObject, callback, callData, thisValue, arguments); + JSC::profiledCall(&lexicalGlobalObject, ProfilingReason::API, callback, callData, thisValue, arguments); if (UNLIKELY(scope.exception())) break; } diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig index b5a2aa70482e46..6c5274dd0a618e 100644 --- a/src/bun.js/javascript.zig +++ b/src/bun.js/javascript.zig @@ -4025,7 +4025,7 @@ pub const VirtualMachine = struct { value, this.global, allow_ansi_color, - ); + ) catch {}; if (allow_side_effects) { // When we're printing errors for a top-level uncaught eception / rejection, suppress further errors here. @@ -4051,7 +4051,7 @@ pub const VirtualMachine = struct { .{ .disable_inspect_custom = true, .hide_global = true }, ); if (tag.tag != .NativeCode) { - formatter.format( + try formatter.format( tag, Writer, writer, diff --git a/src/bun.js/modules/BunJSCModule.h b/src/bun.js/modules/BunJSCModule.h index 55926dde59b191..557cdc2f6fad83 100644 --- a/src/bun.js/modules/BunJSCModule.h +++ b/src/bun.js/modules/BunJSCModule.h @@ -1,6 +1,7 @@ #include "_NativeModule.h" #include "ExceptionOr.h" +#include "JavaScriptCore/CallData.h" #include "JavaScriptCore/ArgList.h" #include "JavaScriptCore/ExceptionScope.h" #include "JavaScriptCore/JSCJSValue.h" @@ -693,7 +694,7 @@ JSC_DEFINE_HOST_FUNCTION(functionRunProfiler, (JSGlobalObject * globalObject, Ca samplingProfiler.noticeCurrentThreadAsJSCExecutionThread(); samplingProfiler.start(); - JSValue returnValue = JSC::call(globalObject, function, callData, JSC::jsUndefined(), args); + JSValue returnValue = JSC::profiledCall(globalObject, ProfilingReason::API, function, callData, JSC::jsUndefined(), args); if (returnValue.isEmpty() || throwScope.exception()) { return JSValue::encode(reportFailure(vm)); @@ -976,7 +977,7 @@ DEFINE_NATIVE_MODULE(BunJSC) putNativeFn(Identifier::fromString(vm, "serialize"_s), functionSerialize); putNativeFn(Identifier::fromString(vm, "deserialize"_s), functionDeserialize); putNativeFn(Identifier::fromString(vm, "estimateShallowMemoryUsageOf"_s), functionEstimateDirectMemoryUsageOf); - + // Deprecated putNativeFn(Identifier::fromString(vm, "describe"_s), functionDescribe); putNativeFn(Identifier::fromString(vm, "describeArray"_s), functionDescribeArray); diff --git a/src/bun.js/modules/NodeUtilTypesModule.h b/src/bun.js/modules/NodeUtilTypesModule.h index cc4117701e2a78..219665c958ffc8 100644 --- a/src/bun.js/modules/NodeUtilTypesModule.h +++ b/src/bun.js/modules/NodeUtilTypesModule.h @@ -1,6 +1,8 @@ #pragma once #include "BunClientData.h" +#include "JSDOMWrapper.h" +#include "JSEventTarget.h" #include "JavaScriptCore/CatchScope.h" #include "_NativeModule.h" @@ -452,89 +454,67 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionIsCryptoKey, GET_FIRST_CELL return JSValue::encode(jsBoolean(cell->inherits())); } +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsEventTarget, + (JSC::JSGlobalObject * globalObject, + JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(cell->inherits())); +} namespace Zig { + +// Hardcoded module "node:util/types" DEFINE_NATIVE_MODULE(NodeUtilTypes) { - INIT_NATIVE_MODULE(43); + INIT_NATIVE_MODULE(44); putNativeFn(Identifier::fromString(vm, "isExternal"_s), jsFunctionIsExternal); putNativeFn(Identifier::fromString(vm, "isDate"_s), jsFunctionIsDate); - putNativeFn(Identifier::fromString(vm, "isArgumentsObject"_s), - jsFunctionIsArgumentsObject); - putNativeFn(Identifier::fromString(vm, "isBigIntObject"_s), - jsFunctionIsBigIntObject); - putNativeFn(Identifier::fromString(vm, "isBooleanObject"_s), - jsFunctionIsBooleanObject); - putNativeFn(Identifier::fromString(vm, "isNumberObject"_s), - jsFunctionIsNumberObject); - putNativeFn(Identifier::fromString(vm, "isStringObject"_s), - jsFunctionIsStringObject); - putNativeFn(Identifier::fromString(vm, "isSymbolObject"_s), - jsFunctionIsSymbolObject); - putNativeFn(Identifier::fromString(vm, "isNativeError"_s), - jsFunctionIsNativeError); + putNativeFn(Identifier::fromString(vm, "isArgumentsObject"_s), jsFunctionIsArgumentsObject); + putNativeFn(Identifier::fromString(vm, "isBigIntObject"_s), jsFunctionIsBigIntObject); + putNativeFn(Identifier::fromString(vm, "isBooleanObject"_s), jsFunctionIsBooleanObject); + putNativeFn(Identifier::fromString(vm, "isNumberObject"_s), jsFunctionIsNumberObject); + putNativeFn(Identifier::fromString(vm, "isStringObject"_s), jsFunctionIsStringObject); + putNativeFn(Identifier::fromString(vm, "isSymbolObject"_s), jsFunctionIsSymbolObject); + putNativeFn(Identifier::fromString(vm, "isNativeError"_s), jsFunctionIsNativeError); putNativeFn(Identifier::fromString(vm, "isRegExp"_s), jsFunctionIsRegExp); - putNativeFn(Identifier::fromString(vm, "isAsyncFunction"_s), - jsFunctionIsAsyncFunction); - putNativeFn(Identifier::fromString(vm, "isGeneratorFunction"_s), - jsFunctionIsGeneratorFunction); - putNativeFn(Identifier::fromString(vm, "isGeneratorObject"_s), - jsFunctionIsGeneratorObject); + putNativeFn(Identifier::fromString(vm, "isAsyncFunction"_s), jsFunctionIsAsyncFunction); + putNativeFn(Identifier::fromString(vm, "isGeneratorFunction"_s), jsFunctionIsGeneratorFunction); + putNativeFn(Identifier::fromString(vm, "isGeneratorObject"_s), jsFunctionIsGeneratorObject); putNativeFn(Identifier::fromString(vm, "isPromise"_s), jsFunctionIsPromise); putNativeFn(Identifier::fromString(vm, "isMap"_s), jsFunctionIsMap); putNativeFn(Identifier::fromString(vm, "isSet"_s), jsFunctionIsSet); - putNativeFn(Identifier::fromString(vm, "isMapIterator"_s), - jsFunctionIsMapIterator); - putNativeFn(Identifier::fromString(vm, "isSetIterator"_s), - jsFunctionIsSetIterator); + putNativeFn(Identifier::fromString(vm, "isMapIterator"_s), jsFunctionIsMapIterator); + putNativeFn(Identifier::fromString(vm, "isSetIterator"_s), jsFunctionIsSetIterator); putNativeFn(Identifier::fromString(vm, "isWeakMap"_s), jsFunctionIsWeakMap); putNativeFn(Identifier::fromString(vm, "isWeakSet"_s), jsFunctionIsWeakSet); - putNativeFn(Identifier::fromString(vm, "isArrayBuffer"_s), - jsFunctionIsArrayBuffer); + putNativeFn(Identifier::fromString(vm, "isArrayBuffer"_s), jsFunctionIsArrayBuffer); putNativeFn(Identifier::fromString(vm, "isDataView"_s), jsFunctionIsDataView); - putNativeFn(Identifier::fromString(vm, "isSharedArrayBuffer"_s), - jsFunctionIsSharedArrayBuffer); + putNativeFn(Identifier::fromString(vm, "isSharedArrayBuffer"_s), jsFunctionIsSharedArrayBuffer); putNativeFn(Identifier::fromString(vm, "isProxy"_s), jsFunctionIsProxy); - putNativeFn(Identifier::fromString(vm, "isModuleNamespaceObject"_s), - jsFunctionIsModuleNamespaceObject); - putNativeFn(Identifier::fromString(vm, "isAnyArrayBuffer"_s), - jsFunctionIsAnyArrayBuffer); - putNativeFn(Identifier::fromString(vm, "isBoxedPrimitive"_s), - jsFunctionIsBoxedPrimitive); - putNativeFn(Identifier::fromString(vm, "isArrayBufferView"_s), - jsFunctionIsArrayBufferView); - putNativeFn(Identifier::fromString(vm, "isTypedArray"_s), - jsFunctionIsTypedArray); - putNativeFn(Identifier::fromString(vm, "isUint8Array"_s), - jsFunctionIsUint8Array); - putNativeFn(Identifier::fromString(vm, "isUint8ClampedArray"_s), - jsFunctionIsUint8ClampedArray); - putNativeFn(Identifier::fromString(vm, "isUint16Array"_s), - jsFunctionIsUint16Array); - putNativeFn(Identifier::fromString(vm, "isUint32Array"_s), - jsFunctionIsUint32Array); - putNativeFn(Identifier::fromString(vm, "isInt8Array"_s), - jsFunctionIsInt8Array); - putNativeFn(Identifier::fromString(vm, "isInt16Array"_s), - jsFunctionIsInt16Array); - putNativeFn(Identifier::fromString(vm, "isInt32Array"_s), - jsFunctionIsInt32Array); - putNativeFn(Identifier::fromString(vm, "isFloat16Array"_s), - jsFunctionIsFloat16Array); - putNativeFn(Identifier::fromString(vm, "isFloat32Array"_s), - jsFunctionIsFloat32Array); - putNativeFn(Identifier::fromString(vm, "isFloat64Array"_s), - jsFunctionIsFloat64Array); - putNativeFn(Identifier::fromString(vm, "isBigInt64Array"_s), - jsFunctionIsBigInt64Array); - putNativeFn(Identifier::fromString(vm, "isBigUint64Array"_s), - jsFunctionIsBigUint64Array); - putNativeFn(Identifier::fromString(vm, "isKeyObject"_s), - jsFunctionIsKeyObject); - putNativeFn(Identifier::fromString(vm, "isCryptoKey"_s), - jsFunctionIsCryptoKey); + putNativeFn(Identifier::fromString(vm, "isModuleNamespaceObject"_s), jsFunctionIsModuleNamespaceObject); + putNativeFn(Identifier::fromString(vm, "isAnyArrayBuffer"_s), jsFunctionIsAnyArrayBuffer); + putNativeFn(Identifier::fromString(vm, "isBoxedPrimitive"_s), jsFunctionIsBoxedPrimitive); + putNativeFn(Identifier::fromString(vm, "isArrayBufferView"_s), jsFunctionIsArrayBufferView); + putNativeFn(Identifier::fromString(vm, "isTypedArray"_s), jsFunctionIsTypedArray); + putNativeFn(Identifier::fromString(vm, "isUint8Array"_s), jsFunctionIsUint8Array); + putNativeFn(Identifier::fromString(vm, "isUint8ClampedArray"_s), jsFunctionIsUint8ClampedArray); + putNativeFn(Identifier::fromString(vm, "isUint16Array"_s), jsFunctionIsUint16Array); + putNativeFn(Identifier::fromString(vm, "isUint32Array"_s), jsFunctionIsUint32Array); + putNativeFn(Identifier::fromString(vm, "isInt8Array"_s), jsFunctionIsInt8Array); + putNativeFn(Identifier::fromString(vm, "isInt16Array"_s), jsFunctionIsInt16Array); + putNativeFn(Identifier::fromString(vm, "isInt32Array"_s), jsFunctionIsInt32Array); + putNativeFn(Identifier::fromString(vm, "isFloat16Array"_s), jsFunctionIsFloat16Array); + putNativeFn(Identifier::fromString(vm, "isFloat32Array"_s), jsFunctionIsFloat32Array); + putNativeFn(Identifier::fromString(vm, "isFloat64Array"_s), jsFunctionIsFloat64Array); + putNativeFn(Identifier::fromString(vm, "isBigInt64Array"_s), jsFunctionIsBigInt64Array); + putNativeFn(Identifier::fromString(vm, "isBigUint64Array"_s), jsFunctionIsBigUint64Array); + putNativeFn(Identifier::fromString(vm, "isKeyObject"_s), jsFunctionIsKeyObject); + putNativeFn(Identifier::fromString(vm, "isCryptoKey"_s), jsFunctionIsCryptoKey); + putNativeFn(Identifier::fromString(vm, "isEventTarget"_s), jsFunctionIsEventTarget); RETURN_NATIVE_MODULE(); } + } // namespace Zig diff --git a/src/bun.js/test/diff_format.zig b/src/bun.js/test/diff_format.zig index fc04a74e33f573..e198e474cda160 100644 --- a/src/bun.js/test/diff_format.zig +++ b/src/bun.js/test/diff_format.zig @@ -111,7 +111,7 @@ pub const DiffFormatter = struct { Writer, buf_writer, fmt_options, - ); + ) catch {}; // TODO: buffered_writer.flush() catch unreachable; buffered_writer_.context = &expected_buf; @@ -125,7 +125,7 @@ pub const DiffFormatter = struct { Writer, buf_writer, fmt_options, - ); + ) catch {}; // TODO: buffered_writer.flush() catch unreachable; } diff --git a/src/bun.js/test/pretty_format.zig b/src/bun.js/test/pretty_format.zig index 7cfeb3e212ed03..2934577d494638 100644 --- a/src/bun.js/test/pretty_format.zig +++ b/src/bun.js/test/pretty_format.zig @@ -574,10 +574,10 @@ pub const JestPrettyFormat = struct { const next_value = this.remaining_values[0]; this.remaining_values = this.remaining_values[1..]; switch (token) { - Tag.String => this.printAs(Tag.String, Writer, writer_, next_value, next_value.jsType(), enable_ansi_colors), - Tag.Double => this.printAs(Tag.Double, Writer, writer_, next_value, next_value.jsType(), enable_ansi_colors), - Tag.Object => this.printAs(Tag.Object, Writer, writer_, next_value, next_value.jsType(), enable_ansi_colors), - Tag.Integer => this.printAs(Tag.Integer, Writer, writer_, next_value, next_value.jsType(), enable_ansi_colors), + Tag.String => this.printAs(Tag.String, Writer, writer_, next_value, next_value.jsType(), enable_ansi_colors) catch {}, // TODO: + Tag.Double => this.printAs(Tag.Double, Writer, writer_, next_value, next_value.jsType(), enable_ansi_colors) catch {}, // TODO: + Tag.Object => this.printAs(Tag.Object, Writer, writer_, next_value, next_value.jsType(), enable_ansi_colors) catch {}, // TODO: + Tag.Integer => this.printAs(Tag.Integer, Writer, writer_, next_value, next_value.jsType(), enable_ansi_colors) catch {}, // TODO: // undefined is overloaded to mean the '%o" field Tag.Undefined => this.format(Tag.get(next_value, globalThis), Writer, writer_, next_value, globalThis, enable_ansi_colors), @@ -899,7 +899,7 @@ pub const JestPrettyFormat = struct { value: JSValue, jsType: JSValue.JSType, comptime enable_ansi_colors: bool, - ) void { + ) error{}!void { if (this.failed) return; var writer = WrappedWriter(Writer){ .ctx = writer_, .estimated_line_length = &this.estimated_line_length }; @@ -1060,7 +1060,7 @@ pub const JestPrettyFormat = struct { }, .Double => { if (value.isCell()) { - this.printAs(.Object, Writer, writer_, value, .Object, enable_ansi_colors); + try this.printAs(.Object, Writer, writer_, value, .Object, enable_ansi_colors); return; } @@ -1240,12 +1240,11 @@ pub const JestPrettyFormat = struct { this.addForNewLine("FormData (entries) ".len); writer.writeAll(comptime Output.prettyFmt("FormData (entries) ", enable_ansi_colors)); - return this.printAs( + return try this.printAs( .Object, Writer, writer_, - toJSONFunction.call(this.globalThis, value, &.{}) catch |err| - this.globalThis.takeException(err), + toJSONFunction.call(this.globalThis, value, &.{}) catch |err| this.globalThis.takeException(err), .Object, enable_ansi_colors, ); @@ -1273,12 +1272,12 @@ pub const JestPrettyFormat = struct { return; } else if (jsType != .DOMWrapper) { if (value.isCallable(this.globalThis.vm())) { - return this.printAs(.Function, Writer, writer_, value, jsType, enable_ansi_colors); + return try this.printAs(.Function, Writer, writer_, value, jsType, enable_ansi_colors); } - return this.printAs(.Object, Writer, writer_, value, jsType, enable_ansi_colors); + return try this.printAs(.Object, Writer, writer_, value, jsType, enable_ansi_colors); } - return this.printAs(.Object, Writer, writer_, value, .Event, enable_ansi_colors); + return try this.printAs(.Object, Writer, writer_, value, .Event, enable_ansi_colors); }, .NativeCode => { this.addForNewLine("[native code]".len); @@ -1293,7 +1292,7 @@ pub const JestPrettyFormat = struct { }, .Boolean => { if (value.isCell()) { - this.printAs(.Object, Writer, writer_, value, .Object, enable_ansi_colors); + try this.printAs(.Object, Writer, writer_, value, .Object, enable_ansi_colors); return; } if (value.toBoolean()) { @@ -1401,7 +1400,7 @@ pub const JestPrettyFormat = struct { const event_type = switch (EventType.map.fromJS(this.globalThis, event_type_value) orelse .unknown) { .MessageEvent, .ErrorEvent => |evt| evt, else => { - return this.printAs(.Object, Writer, writer_, value, .Event, enable_ansi_colors); + return try this.printAs(.Object, Writer, writer_, value, .Event, enable_ansi_colors); }, }; @@ -1962,7 +1961,7 @@ pub const JestPrettyFormat = struct { // comptime var so we have to repeat it here. The rationale there is // it _should_ limit the stack usage because each version of the // function will be relatively small - return switch (result.tag) { + return try switch (result.tag) { .StringPossiblyFormatted => this.printAs(.StringPossiblyFormatted, Writer, writer, value, result.cell, enable_ansi_colors), .String => this.printAs(.String, Writer, writer, value, result.cell, enable_ansi_colors), .Undefined => this.printAs(.Undefined, Writer, writer, value, result.cell, enable_ansi_colors), @@ -2076,7 +2075,7 @@ pub const JestPrettyFormat = struct { this.addForNewLine("ObjectContaining ".len); writer.writeAll("ObjectContaining "); } - this.printAs(.Object, @TypeOf(writer_), writer_, object_value, .Object, enable_ansi_colors); + this.printAs(.Object, @TypeOf(writer_), writer_, object_value, .Object, enable_ansi_colors) catch {}; // TODO: } else if (value.as(expect.ExpectStringContaining)) |matcher| { const substring_value = expect.ExpectStringContaining.stringValueGetCached(value) orelse return true; @@ -2088,7 +2087,7 @@ pub const JestPrettyFormat = struct { this.addForNewLine("StringContaining ".len); writer.writeAll("StringContaining "); } - this.printAs(.String, @TypeOf(writer_), writer_, substring_value, .String, enable_ansi_colors); + this.printAs(.String, @TypeOf(writer_), writer_, substring_value, .String, enable_ansi_colors) catch {}; // TODO: } else if (value.as(expect.ExpectStringMatching)) |matcher| { const test_value = expect.ExpectStringMatching.testValueGetCached(value) orelse return true; @@ -2103,7 +2102,7 @@ pub const JestPrettyFormat = struct { const original_quote_strings = this.quote_strings; if (test_value.isRegExp()) this.quote_strings = false; - this.printAs(.String, @TypeOf(writer_), writer_, test_value, .String, enable_ansi_colors); + this.printAs(.String, @TypeOf(writer_), writer_, test_value, .String, enable_ansi_colors) catch {}; // TODO: this.quote_strings = original_quote_strings; } else if (value.as(expect.ExpectCustomAsymmetricMatcher)) |instance| { const printed = instance.customPrint(value, this.globalThis, writer_, true) catch unreachable; @@ -2121,7 +2120,7 @@ pub const JestPrettyFormat = struct { this.addForNewLine(matcher_name.length() + 1); writer.print("{s}", .{matcher_name}); writer.writeAll(" "); - this.printAs(.Array, @TypeOf(writer_), writer_, args_value, .Array, enable_ansi_colors); + this.printAs(.Array, @TypeOf(writer_), writer_, args_value, .Array, enable_ansi_colors) catch {}; // TODO: } } else { return false; diff --git a/src/bun.js/web_worker.zig b/src/bun.js/web_worker.zig index 506d818dc3a929..c8ae517d906542 100644 --- a/src/bun.js/web_worker.zig +++ b/src/bun.js/web_worker.zig @@ -337,7 +337,7 @@ pub const WebWorker = struct { // Prevent recursion vm.onUnhandledRejection = &JSC.VirtualMachine.onQuietUnhandledRejectionHandlerCaptureValue; - const error_instance = error_instance_or_exception.toError() orelse error_instance_or_exception; + var error_instance = error_instance_or_exception.toError() orelse error_instance_or_exception; var array = bun.MutableString.init(bun.default_allocator, 0) catch unreachable; defer array.deinit(); @@ -364,7 +364,13 @@ pub const WebWorker = struct { .flush = false, .max_depth = 32, }, - ); + ) catch |err| { + switch (err) { + error.JSError => {}, + error.OutOfMemory => globalObject.throwOutOfMemory() catch {}, + } + error_instance = globalObject.tryTakeException().?; + }; buffered_writer.flush() catch { bun.outOfMemory(); }; diff --git a/src/bun.js/webcore/body.zig b/src/bun.js/webcore/body.zig index 2290def2c9da8d..7b1534091d96f7 100644 --- a/src/bun.js/webcore/body.zig +++ b/src/bun.js/webcore/body.zig @@ -72,7 +72,7 @@ pub const Body = struct { try formatter.writeIndent(Writer, writer); try writer.writeAll(comptime Output.prettyFmt("bodyUsed: ", enable_ansi_colors)); - formatter.printAs(.Boolean, Writer, writer, JSC.JSValue.jsBoolean(this.value == .Used), .BooleanObject, enable_ansi_colors); + try formatter.printAs(.Boolean, Writer, writer, JSC.JSValue.jsBoolean(this.value == .Used), .BooleanObject, enable_ansi_colors); if (this.value == .Blob) { try formatter.printComma(Writer, writer, enable_ansi_colors); @@ -89,7 +89,7 @@ pub const Body = struct { try formatter.printComma(Writer, writer, enable_ansi_colors); try writer.writeAll("\n"); try formatter.writeIndent(Writer, writer); - formatter.printAs(.Object, Writer, writer, stream.value, stream.value.jsType(), enable_ansi_colors); + try formatter.printAs(.Object, Writer, writer, stream.value, stream.value.jsType(), enable_ansi_colors); } } } diff --git a/src/bun.js/webcore/request.zig b/src/bun.js/webcore/request.zig index 45a9f3fec3612d..7274373be0bfb1 100644 --- a/src/bun.js/webcore/request.zig +++ b/src/bun.js/webcore/request.zig @@ -227,7 +227,7 @@ pub const Request = struct { try formatter.writeIndent(Writer, writer); try writer.writeAll(comptime Output.prettyFmt("headers: ", enable_ansi_colors)); - formatter.printAs(.Private, Writer, writer, this.getHeaders(formatter.globalThis), .DOMWrapper, enable_ansi_colors); + try formatter.printAs(.Private, Writer, writer, this.getHeaders(formatter.globalThis), .DOMWrapper, enable_ansi_colors); if (this.body.value == .Blob) { try writer.writeAll("\n"); @@ -247,7 +247,7 @@ pub const Request = struct { if (this.body.value.Locked.readable.get()) |stream| { try writer.writeAll("\n"); try formatter.writeIndent(Writer, writer); - formatter.printAs(.Object, Writer, writer, stream.value, stream.value.jsType(), enable_ansi_colors); + try formatter.printAs(.Object, Writer, writer, stream.value, stream.value.jsType(), enable_ansi_colors); } } } diff --git a/src/bun.js/webcore/response.zig b/src/bun.js/webcore/response.zig index 76e6bd4993e1e7..13955dbf520965 100644 --- a/src/bun.js/webcore/response.zig +++ b/src/bun.js/webcore/response.zig @@ -143,7 +143,7 @@ pub const Response = struct { try formatter.writeIndent(Writer, writer); try writer.writeAll(comptime Output.prettyFmt("ok: ", enable_ansi_colors)); - formatter.printAs(.Boolean, Writer, writer, JSC.JSValue.jsBoolean(this.isOK()), .BooleanObject, enable_ansi_colors); + try formatter.printAs(.Boolean, Writer, writer, JSC.JSValue.jsBoolean(this.isOK()), .BooleanObject, enable_ansi_colors); formatter.printComma(Writer, writer, enable_ansi_colors) catch bun.outOfMemory(); try writer.writeAll("\n"); @@ -156,7 +156,7 @@ pub const Response = struct { try formatter.writeIndent(Writer, writer); try writer.writeAll(comptime Output.prettyFmt("status: ", enable_ansi_colors)); - formatter.printAs(.Double, Writer, writer, JSC.JSValue.jsNumber(this.init.status_code), .NumberObject, enable_ansi_colors); + try formatter.printAs(.Double, Writer, writer, JSC.JSValue.jsNumber(this.init.status_code), .NumberObject, enable_ansi_colors); formatter.printComma(Writer, writer, enable_ansi_colors) catch bun.outOfMemory(); try writer.writeAll("\n"); @@ -168,13 +168,13 @@ pub const Response = struct { try formatter.writeIndent(Writer, writer); try writer.writeAll(comptime Output.prettyFmt("headers: ", enable_ansi_colors)); - formatter.printAs(.Private, Writer, writer, this.getHeaders(formatter.globalThis), .DOMWrapper, enable_ansi_colors); + try formatter.printAs(.Private, Writer, writer, this.getHeaders(formatter.globalThis), .DOMWrapper, enable_ansi_colors); formatter.printComma(Writer, writer, enable_ansi_colors) catch bun.outOfMemory(); try writer.writeAll("\n"); try formatter.writeIndent(Writer, writer); try writer.writeAll(comptime Output.prettyFmt("redirected: ", enable_ansi_colors)); - formatter.printAs(.Boolean, Writer, writer, JSC.JSValue.jsBoolean(this.redirected), .BooleanObject, enable_ansi_colors); + try formatter.printAs(.Boolean, Writer, writer, JSC.JSValue.jsBoolean(this.redirected), .BooleanObject, enable_ansi_colors); formatter.printComma(Writer, writer, enable_ansi_colors) catch bun.outOfMemory(); try writer.writeAll("\n"); diff --git a/src/js/internal/errors.ts b/src/js/internal/errors.ts index fd5036ec865bef..739958bf03d148 100644 --- a/src/js/internal/errors.ts +++ b/src/js/internal/errors.ts @@ -10,4 +10,5 @@ export default { ERR_BUFFER_TOO_LARGE: $newCppFunction("ErrorCode.cpp", "jsFunction_ERR_BUFFER_TOO_LARGE", 0), ERR_ZLIB_INITIALIZATION_FAILED: $newCppFunction("ErrorCode.cpp", "jsFunction_ERR_ZLIB_INITIALIZATION_FAILED", 0), ERR_BUFFER_OUT_OF_BOUNDS: $newCppFunction("ErrorCode.cpp", "jsFunction_ERR_BUFFER_OUT_OF_BOUNDS", 0), + ERR_UNHANDLED_ERROR: $newCppFunction("ErrorCode.cpp", "jsFunction_ERR_UNHANDLED_ERROR", 0), }; diff --git a/src/js/node/domain.ts b/src/js/node/domain.ts index 7bed189e5313b3..6a712a0a3f50a3 100644 --- a/src/js/node/domain.ts +++ b/src/js/node/domain.ts @@ -1,5 +1,8 @@ // Import Events var EventEmitter = require("node:events"); +const { ERR_UNHANDLED_ERROR } = require("internal/errors"); + +const ObjectDefineProperty = Object.defineProperty; // Export Domain var domain: any = {}; @@ -7,6 +10,18 @@ domain.createDomain = domain.create = function () { var d = new EventEmitter(); function emitError(e) { + e ||= ERR_UNHANDLED_ERROR(); + if (typeof e === "object") { + e.domainEmitter = this; + ObjectDefineProperty(e, "domain", { + __proto__: null, + configurable: true, + enumerable: false, + value: domain, + writable: true, + }); + e.domainThrown = false; + } d.emit("error", e); } diff --git a/src/js/node/events.ts b/src/js/node/events.ts index 7759a0d18ca558..85a5c7707cfca3 100644 --- a/src/js/node/events.ts +++ b/src/js/node/events.ts @@ -23,16 +23,22 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -const { ERR_INVALID_ARG_TYPE } = require("internal/errors"); +const { ERR_INVALID_ARG_TYPE, ERR_UNHANDLED_ERROR } = require("internal/errors"); const { validateObject, validateInteger, validateAbortSignal, validateNumber, validateBoolean, + validateFunction, } = require("internal/validators"); +const { inspect, types } = require("node:util"); + const SymbolFor = Symbol.for; +const ArrayPrototypeSlice = Array.prototype.slice; +const ArrayPrototypeSplice = Array.prototype.splice; +const ReflectOwnKeys = Reflect.ownKeys; const kCapture = Symbol("kCapture"); const kErrorMonitor = SymbolFor("events.errorMonitor"); @@ -42,7 +48,6 @@ const kWatermarkData = SymbolFor("nodejs.watermarkData"); const kRejection = SymbolFor("nodejs.rejection"); const kFirstEventParam = SymbolFor("nodejs.kFirstEventParam"); const captureRejectionSymbol = SymbolFor("nodejs.rejection"); -const ArrayPrototypeSlice = Array.prototype.slice; let FixedQueue; const kEmptyObject = Object.freeze({ __proto__: null }); @@ -77,26 +82,48 @@ Object.defineProperty(EventEmitterPrototype.setMaxListeners, "name", { value: "s EventEmitterPrototype.constructor = EventEmitter; EventEmitterPrototype.getMaxListeners = function getMaxListeners() { - return this?._maxListeners ?? defaultMaxListeners; + return _getMaxListeners(this); }; Object.defineProperty(EventEmitterPrototype.getMaxListeners, "name", { value: "getMaxListeners" }); function emitError(emitter, args) { var { _events: events } = emitter; - args[0] ??= new Error("Unhandled error."); - if (!events) throw args[0]; - var errorMonitor = events[kErrorMonitor]; - if (errorMonitor) { - for (var handler of ArrayPrototypeSlice.$call(errorMonitor)) { - handler.$apply(emitter, args); + + if (events !== undefined) { + const errorMonitor = events[kErrorMonitor]; + if (errorMonitor) { + for (const handler of ArrayPrototypeSlice.$call(errorMonitor)) { + handler.$apply(emitter, args); + } + } + + const handlers = events.error; + if (handlers) { + for (var handler of ArrayPrototypeSlice.$call(handlers)) { + handler.$apply(emitter, args); + } + return true; } } - var handlers = events.error; - if (!handlers) throw args[0]; - for (var handler of ArrayPrototypeSlice.$call(handlers)) { - handler.$apply(emitter, args); + + let er; + if (args.length > 0) er = args[0]; + + if (er instanceof Error) { + throw er; // Unhandled 'error' event } - return true; + + let stringifiedEr; + try { + stringifiedEr = inspect(er); + } catch { + stringifiedEr = er; + } + + // At least give some kind of context to the user + const err = ERR_UNHANDLED_ERROR(stringifiedEr); + err.context = er; + throw err; // Unhandled 'error' event } function addCatch(emitter, promise, type, args) { @@ -215,7 +242,7 @@ EventEmitterPrototype.addListener = function addListener(type, fn) { this._eventsCount++; } else { handlers.push(fn); - var m = this._maxListeners ?? defaultMaxListeners; + var m = _getMaxListeners(this); if (m > 0 && handlers.length > m && !handlers.warned) { overflowWarning(this, type, handlers); } @@ -240,7 +267,7 @@ EventEmitterPrototype.prependListener = function prependListener(type, fn) { this._eventsCount++; } else { handlers.unshift(fn); - var m = this._maxListeners ?? defaultMaxListeners; + var m = _getMaxListeners(this); if (m > 0 && handlers.length > m && !handlers.warned) { overflowWarning(this, type, handlers); } @@ -251,8 +278,7 @@ EventEmitterPrototype.prependListener = function prependListener(type, fn) { function overflowWarning(emitter, type, handlers) { handlers.warned = true; const warn = new Error( - `Possible EventEmitter memory leak detected. ${handlers.length} ${String(type)} listeners ` + - `added to [${emitter.constructor.name}]. Use emitter.setMaxListeners() to increase limit`, + `Possible EventTarget memory leak detected. ${handlers.length} ${String(type)} listeners added to ${inspect(emitter, { depth: -1 })}. MaxListeners is ${emitter._maxListeners}. Use events.setMaxListeners() to increase limit`, ); warn.name = "MaxListenersExceededWarning"; warn.emitter = emitter; @@ -291,45 +317,70 @@ EventEmitterPrototype.prependOnceListener = function prependOnceListener(type, f return this; }; -EventEmitterPrototype.removeListener = function removeListener(type, fn) { - checkListener(fn); - var { _events: events } = this; - if (!events) return this; - var handlers = events[type]; - if (!handlers) return this; - var length = handlers.length; +EventEmitterPrototype.removeListener = function removeListener(type, listener) { + checkListener(listener); + + const events = this._events; + if (events === undefined) return this; + + const list = events[type]; + if (list === undefined) return this; + let position = -1; - for (let i = length - 1; i >= 0; i--) { - if (handlers[i] === fn || handlers[i].listener === fn) { + for (let i = list.length - 1; i >= 0; i--) { + if (list[i] === listener || list[i].listener === listener) { position = i; break; } } if (position < 0) return this; - if (position === 0) { - handlers.shift(); - } else { - handlers.splice(position, 1); - } - if (handlers.length === 0) { + + if (position === 0) list.shift(); + else ArrayPrototypeSplice.$call(list, position, 1); + + if (list.length === 0) { delete events[type]; this._eventsCount--; } + + if (events.removeListener !== undefined) this.emit("removeListener", type, listener.listener || listener); + return this; }; EventEmitterPrototype.off = EventEmitterPrototype.removeListener; EventEmitterPrototype.removeAllListeners = function removeAllListeners(type) { - var { _events: events } = this; - if (type && events) { - if (events[type]) { - delete events[type]; - this._eventsCount--; + const events = this._events; + if (events === undefined) return this; + + if (events.removeListener === undefined) { + if (type) { + if (events[type]) { + delete events[type]; + this._eventsCount--; + } + } else { + this._events = { __proto__: null }; } - } else { + return this; + } + + // Emit removeListener for all listeners on all events + if (!type) { + for (const key of ReflectOwnKeys(events)) { + if (key === "removeListener") continue; + this.removeAllListeners(key); + } + this.removeAllListeners("removeListener"); this._events = { __proto__: null }; + this._eventsCount = 0; + return this; } + + // emit in LIFO order + const listeners = events[type]; + for (let i = listeners.length - 1; i >= 0; i--) this.removeListener(type, listeners[i]); return this; }; @@ -599,20 +650,20 @@ function getEventListeners(emitter, type) { // https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/lib/events.js#L315-L339 function setMaxListeners(n = defaultMaxListeners, ...eventTargets) { validateNumber(n, "setMaxListeners", 0); - const length = eventTargets?.length; - if (length) { - for (let eventTargetOrEmitter of eventTargets) { - // TODO: EventTarget setMaxListeners is not implemented yet. - // Only EventEmitter has it. - if ($isCallable(eventTargetOrEmitter?.setMaxListeners)) { - eventTargetOrEmitter.setMaxListeners(n); - } else if ($isObject(eventTargetOrEmitter) && eventTargetOrEmitter instanceof EventTarget) { - // This is a fake number so that the number can be checked against with getMaxListeners() - eventTargetOrEmitter[eventTargetMaxListenersSymbol] = n; + if (eventTargets.length === 0) { + defaultMaxListeners = n; + } else { + for (let i = 0; i < eventTargets.length; i++) { + const target = eventTargets[i]; + if (types.isEventTarget(target)) { + target[kMaxEventTargetListeners] = n; + target[kMaxEventTargetListenersWarned] = false; + } else if (typeof target.setMaxListeners === "function") { + target.setMaxListeners(n); + } else { + throw ERR_INVALID_ARG_TYPE("eventTargets", ["EventEmitter", "EventTarget"], target); } } - } else { - defaultMaxListeners = n; } } Object.defineProperty(setMaxListeners, "name", { value: "setMaxListeners" }); @@ -674,16 +725,23 @@ function ERR_OUT_OF_RANGE(name, range, value) { } function checkListener(listener) { - if (typeof listener !== "function") { - throw new TypeError("The listener must be a function"); - } + validateFunction(listener, "listener"); +} + +function _getMaxListeners(emitter) { + return emitter?._maxListeners ?? defaultMaxListeners; } let AsyncResource = null; -const eventTargetMaxListenersSymbol = Symbol("EventTarget.maxListeners"); function getMaxListeners(emitterOrTarget) { - return emitterOrTarget?.[eventTargetMaxListenersSymbol] ?? emitterOrTarget?._maxListeners ?? defaultMaxListeners; + if (typeof emitterOrTarget?.getMaxListeners === "function") { + return _getMaxListeners(emitterOrTarget); + } else if (types.isEventTarget(emitterOrTarget)) { + emitterOrTarget[kMaxEventTargetListeners] ??= defaultMaxListeners; + return emitterOrTarget[kMaxEventTargetListeners]; + } + throw ERR_INVALID_ARG_TYPE("emitter", ["EventEmitter", "EventTarget"], emitterOrTarget); } Object.defineProperty(getMaxListeners, "name", { value: "getMaxListeners" }); diff --git a/test/js/bun/net/socket.test.ts b/test/js/bun/net/socket.test.ts index c60c267ceead1c..e3735148f3339f 100644 --- a/test/js/bun/net/socket.test.ts +++ b/test/js/bun/net/socket.test.ts @@ -372,7 +372,7 @@ it("should allow large amounts of data to be sent and received", async () => { it("it should not crash when getting a ReferenceError on client socket open", async () => { using server = Bun.serve({ - port: 8080, + port: 0, hostname: "localhost", fetch() { return new Response("Hello World"); @@ -413,7 +413,7 @@ it("it should not crash when getting a ReferenceError on client socket open", as it("it should not crash when returning a Error on client socket open", async () => { using server = Bun.serve({ - port: 8080, + port: 0, hostname: "localhost", fetch() { return new Response("Hello World"); diff --git a/test/js/node/child_process/child_process-node.test.js b/test/js/node/child_process/child_process-node.test.js index 42931a8dcf0a06..1d4a7a430557a6 100644 --- a/test/js/node/child_process/child_process-node.test.js +++ b/test/js/node/child_process/child_process-node.test.js @@ -659,7 +659,7 @@ describe("fork", () => { code: "ERR_INVALID_ARG_TYPE", name: "TypeError", message: expect.stringContaining( - `The "modulePath" argument must be of type string, Buffer or URL. Received: `, + `The "modulePath" argument must be of type string, Buffer, or URL. Received `, ), }), ); @@ -718,7 +718,7 @@ describe("fork", () => { expect.objectContaining({ code: "ERR_INVALID_ARG_TYPE", name: "TypeError", - message: expect.stringContaining(`The "options" argument must be of type object. Received: `), + message: expect.stringContaining(`The "options" argument must be of type object. Received `), }), ); }); diff --git a/test/js/node/child_process/child_process.test.ts b/test/js/node/child_process/child_process.test.ts index f70772d4218971..a259c6897da066 100644 --- a/test/js/node/child_process/child_process.test.ts +++ b/test/js/node/child_process/child_process.test.ts @@ -96,7 +96,7 @@ describe("spawn()", () => { it("should disallow invalid filename", () => { // @ts-ignore expect(() => spawn(123)).toThrow({ - message: 'The "file" argument must be of type string. Received 123', + message: 'The "file" argument must be of type string. Received type number (123)', code: "ERR_INVALID_ARG_TYPE", }); }); diff --git a/test/js/node/test/parallel/test-async-hooks-vm-gc.js b/test/js/node/test/parallel/test-async-hooks-vm-gc.js new file mode 100644 index 00000000000000..da95e3579dcca4 --- /dev/null +++ b/test/js/node/test/parallel/test-async-hooks-vm-gc.js @@ -0,0 +1,15 @@ +// Flags: --expose-gc +'use strict'; + +require('../common'); +const asyncHooks = require('async_hooks'); +const vm = require('vm'); + +// This is a regression test for https://github.com/nodejs/node/issues/39019 +// +// It should not segfault. + +const hook = asyncHooks.createHook({ init() {} }).enable(); +vm.createContext(); +globalThis.gc(); +hook.disable(); diff --git a/test/js/node/test/parallel/test-dgram-send-address-types.js b/test/js/node/test/parallel/test-dgram-send-address-types.js new file mode 100644 index 00000000000000..a31e53f9038fbb --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-send-address-types.js @@ -0,0 +1,47 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const buf = Buffer.from('test'); + +const defaultCases = ['', null, undefined]; + +const onMessage = common.mustSucceed((bytes) => { + assert.strictEqual(bytes, buf.length); +}, defaultCases.length + 1); + +const client = dgram.createSocket('udp4').bind(0, () => { + const port = client.address().port; + + // Check valid addresses + defaultCases.forEach((address) => { + client.send(buf, port, address, onMessage); + }); + + // Valid address: not provided + client.send(buf, port, onMessage); + + // Check invalid addresses + [ + [], + 0, + 1, + true, + false, + 0n, + 1n, + {}, + Symbol(), + ].forEach((invalidInput) => { + const expectedError = { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "address" argument must be of type string.' + + `${common.invalidArgTypeHelper(invalidInput)}` + }; + assert.throws(() => client.send(buf, port, invalidInput), expectedError); + }); +}); + +client.unref(); diff --git a/test/js/node/test/parallel/test-event-emitter-errors.js b/test/js/node/test/parallel/test-event-emitter-errors.js new file mode 100644 index 00000000000000..f22fc3bd58c2b1 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-errors.js @@ -0,0 +1,37 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); +const util = require('util'); + +const EE = new EventEmitter(); + +assert.throws( + () => EE.emit('error', 'Accepts a string'), + { + code: 'ERR_UNHANDLED_ERROR', + name: 'Error', + message: "Unhandled error. ('Accepts a string')", + } +); + +assert.throws( + () => EE.emit('error', { message: 'Error!' }), + { + code: 'ERR_UNHANDLED_ERROR', + name: 'Error', + message: "Unhandled error. ({ message: 'Error!' })", + } +); + +assert.throws( + () => EE.emit('error', { + message: 'Error!', + [util.inspect.custom]() { throw new Error(); }, + }), + { + code: 'ERR_UNHANDLED_ERROR', + name: 'Error', + message: 'Unhandled error. ([object Object])', + } +); diff --git a/test/js/node/test/parallel/test-event-emitter-invalid-listener.js b/test/js/node/test/parallel/test-event-emitter-invalid-listener.js new file mode 100644 index 00000000000000..f05766c72e3917 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-invalid-listener.js @@ -0,0 +1,20 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); + +const eventsMethods = ['on', 'once', 'removeListener', 'prependOnceListener']; + +// Verify that the listener must be a function for events methods +for (const method of eventsMethods) { + assert.throws(() => { + const ee = new EventEmitter(); + ee[method]('foo', null); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "listener" argument must be of type function. ' + + 'Received null', + }); +} diff --git a/test/js/node/test/parallel/test-event-emitter-listeners-side-effects.js b/test/js/node/test/parallel/test-event-emitter-listeners-side-effects.js new file mode 100644 index 00000000000000..f1a9e659e0aa08 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-listeners-side-effects.js @@ -0,0 +1,60 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +require('../common'); +const assert = require('assert'); + +const EventEmitter = require('events').EventEmitter; + +const e = new EventEmitter(); +let fl; // foo listeners + +fl = e.listeners('foo'); +assert(Array.isArray(fl)); +assert.strictEqual(fl.length, 0); +assert(!(e._events instanceof Object)); +assert.deepStrictEqual(Object.keys(e._events), []); + +e.on('foo', assert.fail); +fl = e.listeners('foo'); +assert.deepEqual(e._events.foo, [assert.fail]); +assert(Array.isArray(fl)); +assert.strictEqual(fl.length, 1); +assert.strictEqual(fl[0], assert.fail); + +e.listeners('bar'); + +e.on('foo', assert.ok); +fl = e.listeners('foo'); + +assert(Array.isArray(e._events.foo)); +assert.strictEqual(e._events.foo.length, 2); +assert.strictEqual(e._events.foo[0], assert.fail); +assert.strictEqual(e._events.foo[1], assert.ok); + +assert(Array.isArray(fl)); +assert.strictEqual(fl.length, 2); +assert.strictEqual(fl[0], assert.fail); +assert.strictEqual(fl[1], assert.ok); + +console.log('ok'); diff --git a/test/js/node/test/parallel/test-event-emitter-max-listeners-warning-for-null.js b/test/js/node/test/parallel/test-event-emitter-max-listeners-warning-for-null.js new file mode 100644 index 00000000000000..673b42336e7d13 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-max-listeners-warning-for-null.js @@ -0,0 +1,22 @@ +// Flags: --no-warnings +// The flag suppresses stderr output but the warning event will still emit +'use strict'; + +const common = require('../common'); +const events = require('events'); +const assert = require('assert'); + +const e = new events.EventEmitter(); +e.setMaxListeners(1); + +process.on('warning', common.mustCall((warning) => { + assert.ok(warning instanceof Error); + assert.strictEqual(warning.name, 'MaxListenersExceededWarning'); + assert.strictEqual(warning.emitter, e); + assert.strictEqual(warning.count, 2); + assert.strictEqual(warning.type, null); + assert.ok(warning.message.includes('2 null listeners added to [EventEmitter]. MaxListeners is 1.')); +})); + +e.on(null, () => {}); +e.on(null, () => {}); diff --git a/test/js/node/test/parallel/test-event-emitter-max-listeners-warning-for-symbol.js b/test/js/node/test/parallel/test-event-emitter-max-listeners-warning-for-symbol.js new file mode 100644 index 00000000000000..e654b7697cee0b --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-max-listeners-warning-for-symbol.js @@ -0,0 +1,24 @@ +// Flags: --no-warnings +// The flag suppresses stderr output but the warning event will still emit +'use strict'; + +const common = require('../common'); +const events = require('events'); +const assert = require('assert'); + +const symbol = Symbol('symbol'); + +const e = new events.EventEmitter(); +e.setMaxListeners(1); + +process.on('warning', common.mustCall((warning) => { + assert.ok(warning instanceof Error); + assert.strictEqual(warning.name, 'MaxListenersExceededWarning'); + assert.strictEqual(warning.emitter, e); + assert.strictEqual(warning.count, 2); + assert.strictEqual(warning.type, symbol); + assert.ok(warning.message.includes('2 Symbol(symbol) listeners added to [EventEmitter]. MaxListeners is 1.')); +})); + +e.on(symbol, () => {}); +e.on(symbol, () => {}); diff --git a/test/js/node/test/parallel/test-event-emitter-max-listeners-warning.js b/test/js/node/test/parallel/test-event-emitter-max-listeners-warning.js new file mode 100644 index 00000000000000..31bd8d0712ce4d --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-max-listeners-warning.js @@ -0,0 +1,30 @@ +// Flags: --no-warnings +// The flag suppresses stderr output but the warning event will still emit +'use strict'; + +const common = require('../common'); +const events = require('events'); +const assert = require('assert'); + +class FakeInput extends events.EventEmitter { + resume() {} + pause() {} + write() {} + end() {} +} + +const e = new FakeInput(); +e.setMaxListeners(1); + +process.on('warning', common.mustCall((warning) => { + assert.ok(warning instanceof Error); + assert.strictEqual(warning.name, 'MaxListenersExceededWarning'); + assert.strictEqual(warning.emitter, e); + assert.strictEqual(warning.count, 2); + assert.strictEqual(warning.type, 'event-type'); + assert.ok(warning.message.includes('2 event-type listeners added to [FakeInput]. MaxListeners is 1.')); +})); + +e.on('event-type', () => {}); +e.on('event-type', () => {}); // Trigger warning. +e.on('event-type', () => {}); // Verify that warning is emitted only once. diff --git a/test/js/node/test/parallel/test-event-emitter-max-listeners.js b/test/js/node/test/parallel/test-event-emitter-max-listeners.js new file mode 100644 index 00000000000000..9b9c2ad0d5d403 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-max-listeners.js @@ -0,0 +1,88 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const events = require('events'); +const e = new events.EventEmitter(); + +e.on('maxListeners', common.mustCall()); + +// Should not corrupt the 'maxListeners' queue. +e.setMaxListeners(42); + +const rangeErrorObjs = [NaN, -1]; +const typeErrorObj = 'and even this'; + +for (const obj of rangeErrorObjs) { + assert.throws( + () => e.setMaxListeners(obj), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + } + ); + + assert.throws( + () => events.defaultMaxListeners = obj, + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + } + ); +} + +assert.throws( + () => e.setMaxListeners(typeErrorObj), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + } +); + +assert.throws( + () => events.defaultMaxListeners = typeErrorObj, + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + } +); + +e.emit('maxListeners'); + +{ + const { EventEmitter, defaultMaxListeners } = events; + for (const obj of rangeErrorObjs) { + assert.throws(() => EventEmitter.setMaxListeners(obj), { + code: 'ERR_OUT_OF_RANGE', + }); + } + + assert.throws(() => EventEmitter.setMaxListeners(typeErrorObj), { + code: 'ERR_INVALID_ARG_TYPE', + }); + + assert.throws( + () => EventEmitter.setMaxListeners(defaultMaxListeners, 'INVALID_EMITTER'), + { code: 'ERR_INVALID_ARG_TYPE' } + ); +} diff --git a/test/js/node/test/parallel/test-event-emitter-no-error-provided-to-error-event.js b/test/js/node/test/parallel/test-event-emitter-no-error-provided-to-error-event.js new file mode 100644 index 00000000000000..5c30b54533133d --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-no-error-provided-to-error-event.js @@ -0,0 +1,56 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const events = require('events'); +const domain = require('domain'); + +{ + const e = new events.EventEmitter(); + const d = domain.create(); + d.add(e); + d.on('error', common.mustCall((er) => { + assert(er instanceof Error, 'error created'); + })); + e.emit('error'); +} + +for (const arg of [false, null, undefined]) { + const e = new events.EventEmitter(); + const d = domain.create(); + d.add(e); + d.on('error', common.mustCall((er) => { + assert(er instanceof Error, 'error created'); + })); + e.emit('error', arg); +} + +for (const arg of [42, 'fortytwo', true]) { + const e = new events.EventEmitter(); + const d = domain.create(); + d.add(e); + d.on('error', common.mustCall((er) => { + assert.strictEqual(er, arg); + })); + e.emit('error', arg); +} diff --git a/test/js/node/test/parallel/test-event-emitter-remove-all-listeners.js b/test/js/node/test/parallel/test-event-emitter-remove-all-listeners.js new file mode 100644 index 00000000000000..c62183fd08c203 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-remove-all-listeners.js @@ -0,0 +1,123 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const events = require('events'); + + +function expect(expected) { + const actual = []; + process.on('exit', function() { + assert.deepStrictEqual(actual.sort(), expected.sort()); + }); + function listener(name) { + actual.push(name); + } + return common.mustCall(listener, expected.length); +} + +{ + const ee = new events.EventEmitter(); + const noop = common.mustNotCall(); + ee.on('foo', noop); + ee.on('bar', noop); + ee.on('baz', noop); + ee.on('baz', noop); + const fooListeners = ee.listeners('foo'); + const barListeners = ee.listeners('bar'); + const bazListeners = ee.listeners('baz'); + ee.on('removeListener', expect(['bar', 'baz', 'baz'])); + ee.removeAllListeners('bar'); + ee.removeAllListeners('baz'); + assert.deepStrictEqual(ee.listeners('foo'), [noop]); + assert.deepStrictEqual(ee.listeners('bar'), []); + assert.deepStrictEqual(ee.listeners('baz'), []); + // After calling removeAllListeners(), + // the old listeners array should stay unchanged. + assert.deepStrictEqual(fooListeners, [noop]); + assert.deepStrictEqual(barListeners, [noop]); + assert.deepStrictEqual(bazListeners, [noop, noop]); + // After calling removeAllListeners(), + // new listeners arrays is different from the old. + assert.notStrictEqual(ee.listeners('bar'), barListeners); + assert.notStrictEqual(ee.listeners('baz'), bazListeners); +} + +{ + const ee = new events.EventEmitter(); + ee.on('foo', common.mustNotCall()); + ee.on('bar', common.mustNotCall()); + // Expect LIFO order + ee.on('removeListener', expect(['foo', 'bar', 'removeListener'])); + ee.on('removeListener', expect(['foo', 'bar'])); + ee.removeAllListeners(); + assert.deepStrictEqual([], ee.listeners('foo')); + assert.deepStrictEqual([], ee.listeners('bar')); +} + +{ + const ee = new events.EventEmitter(); + ee.on('removeListener', common.mustNotCall()); + // Check for regression where removeAllListeners() throws when + // there exists a 'removeListener' listener, but there exists + // no listeners for the provided event type. + ee.removeAllListeners.bind(ee, 'foo'); +} + +{ + const ee = new events.EventEmitter(); + let expectLength = 2; + ee.on('removeListener', function(name, noop) { + assert.strictEqual(expectLength--, this.listeners('baz').length); + }); + ee.on('baz', common.mustNotCall()); + ee.on('baz', common.mustNotCall()); + ee.on('baz', common.mustNotCall()); + assert.strictEqual(ee.listeners('baz').length, expectLength + 1); + ee.removeAllListeners('baz'); + assert.strictEqual(ee.listeners('baz').length, 0); +} + +{ + const ee = new events.EventEmitter(); + assert.deepStrictEqual(ee, ee.removeAllListeners()); +} + +{ + const ee = new events.EventEmitter(); + ee._events = undefined; + assert.strictEqual(ee, ee.removeAllListeners()); +} + +{ + const ee = new events.EventEmitter(); + const symbol = Symbol('symbol'); + const noop = common.mustNotCall(); + ee.on(symbol, noop); + + ee.on('removeListener', common.mustCall((...args) => { + assert.deepStrictEqual(args, [symbol, noop]); + })); + + ee.removeAllListeners(); +} diff --git a/test/js/node/test/parallel/test-event-emitter-remove-listeners.js b/test/js/node/test/parallel/test-event-emitter-remove-listeners.js new file mode 100644 index 00000000000000..5ab52b8320c8dd --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-remove-listeners.js @@ -0,0 +1,170 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); + +function listener1() {} + +function listener2() {} + +{ + const ee = new EventEmitter(); + ee.on('hello', listener1); + ee.on('removeListener', common.mustCall((name, cb) => { + assert.strictEqual(name, 'hello'); + assert.strictEqual(cb, listener1); + })); + ee.removeListener('hello', listener1); + assert.deepStrictEqual([], ee.listeners('hello')); +} + +{ + const ee = new EventEmitter(); + ee.on('hello', listener1); + ee.on('removeListener', common.mustNotCall()); + ee.removeListener('hello', listener2); + assert.deepStrictEqual([listener1], ee.listeners('hello')); +} + +{ + const ee = new EventEmitter(); + ee.on('hello', listener1); + ee.on('hello', listener2); + ee.once('removeListener', common.mustCall((name, cb) => { + assert.strictEqual(name, 'hello'); + assert.strictEqual(cb, listener1); + assert.deepStrictEqual([listener2], ee.listeners('hello')); + })); + ee.removeListener('hello', listener1); + assert.deepStrictEqual([listener2], ee.listeners('hello')); + ee.once('removeListener', common.mustCall((name, cb) => { + assert.strictEqual(name, 'hello'); + assert.strictEqual(cb, listener2); + assert.deepStrictEqual([], ee.listeners('hello')); + })); + ee.removeListener('hello', listener2); + assert.deepStrictEqual([], ee.listeners('hello')); +} + +{ + const ee = new EventEmitter(); + + function remove1() { + assert.fail('remove1 should not have been called'); + } + + function remove2() { + assert.fail('remove2 should not have been called'); + } + + ee.on('removeListener', common.mustCall(function(name, cb) { + if (cb !== remove1) return; + this.removeListener('quux', remove2); + this.emit('quux'); + }, 2)); + ee.on('quux', remove1); + ee.on('quux', remove2); + ee.removeListener('quux', remove1); +} + +{ + const ee = new EventEmitter(); + ee.on('hello', listener1); + ee.on('hello', listener2); + ee.once('removeListener', common.mustCall((name, cb) => { + assert.strictEqual(name, 'hello'); + assert.strictEqual(cb, listener1); + assert.deepStrictEqual([listener2], ee.listeners('hello')); + ee.once('removeListener', common.mustCall((name, cb) => { + assert.strictEqual(name, 'hello'); + assert.strictEqual(cb, listener2); + assert.deepStrictEqual([], ee.listeners('hello')); + })); + ee.removeListener('hello', listener2); + assert.deepStrictEqual([], ee.listeners('hello')); + })); + ee.removeListener('hello', listener1); + assert.deepStrictEqual([], ee.listeners('hello')); +} + +{ + const ee = new EventEmitter(); + const listener3 = common.mustCall(() => { + ee.removeListener('hello', listener4); + }, 2); + const listener4 = common.mustCall(); + + ee.on('hello', listener3); + ee.on('hello', listener4); + + // listener4 will still be called although it is removed by listener 3. + ee.emit('hello'); + // This is so because the internal listener array at time of emit + // was [listener3,listener4] + + // Internal listener array [listener3] + ee.emit('hello'); +} + +{ + const ee = new EventEmitter(); + + ee.once('hello', listener1); + ee.on('removeListener', common.mustCall((eventName, listener) => { + assert.strictEqual(eventName, 'hello'); + assert.strictEqual(listener, listener1); + })); + ee.emit('hello'); +} + +{ + const ee = new EventEmitter(); + + assert.deepStrictEqual(ee, ee.removeListener('foo', () => {})); +} + +{ + const ee = new EventEmitter(); + const listener = () => {}; + ee._events = undefined; + const e = ee.removeListener('foo', listener); + assert.strictEqual(e, ee); +} + +{ + const ee = new EventEmitter(); + + ee.on('foo', listener1); + ee.on('foo', listener2); + assert.deepStrictEqual(ee.listeners('foo'), [listener1, listener2]); + + ee.removeListener('foo', listener1); + assert.deepEqual(ee._events.foo, [listener2]); + + ee.on('foo', listener1); + assert.deepStrictEqual(ee.listeners('foo'), [listener2, listener1]); + + ee.removeListener('foo', listener1); + assert.deepEqual(ee._events.foo, [listener2]); +} diff --git a/test/js/node/test/parallel/test-require-delete-array-iterator.js b/test/js/node/test/parallel/test-require-delete-array-iterator.js new file mode 100644 index 00000000000000..5424ef8b75da9d --- /dev/null +++ b/test/js/node/test/parallel/test-require-delete-array-iterator.js @@ -0,0 +1,13 @@ +'use strict'; + +const common = require('../common'); + + +const ArrayIteratorPrototype = + Object.getPrototypeOf(Array.prototype[Symbol.iterator]()); + +delete Array.prototype[Symbol.iterator]; +delete ArrayIteratorPrototype.next; + +require('../common/fixtures'); +import('../fixtures/es-modules/test-esm-ok.mjs').then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-stream-readable-setEncoding-null.js b/test/js/node/test/parallel/test-stream-readable-setEncoding-null.js new file mode 100644 index 00000000000000..b95b26bb795728 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-setEncoding-null.js @@ -0,0 +1,15 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const { Readable } = require('stream'); + + +{ + const readable = new Readable({ encoding: 'hex' }); + assert.strictEqual(readable._readableState.encoding, 'hex'); + + readable.setEncoding(null); + + assert.strictEqual(readable._readableState.encoding, 'utf8'); +} diff --git a/test/js/node/test/parallel/test-stream-readable-unshift.js b/test/js/node/test/parallel/test-stream-readable-unshift.js new file mode 100644 index 00000000000000..cccc834fc1947b --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-unshift.js @@ -0,0 +1,170 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Readable } = require('stream'); + +{ + // Check that strings are saved as Buffer + const readable = new Readable({ read() {} }); + + const string = 'abc'; + + readable.on('data', common.mustCall((chunk) => { + assert(Buffer.isBuffer(chunk)); + assert.strictEqual(chunk.toString('utf8'), string); + }, 1)); + + readable.unshift(string); + +} + +{ + // Check that data goes at the beginning + const readable = new Readable({ read() {} }); + const unshift = 'front'; + const push = 'back'; + + const expected = [unshift, push]; + readable.on('data', common.mustCall((chunk) => { + assert.strictEqual(chunk.toString('utf8'), expected.shift()); + }, 2)); + + + readable.push(push); + readable.unshift(unshift); +} + +{ + // Check that buffer is saved with correct encoding + const readable = new Readable({ read() {} }); + + const encoding = 'base64'; + const string = Buffer.from('abc').toString(encoding); + + readable.on('data', common.mustCall((chunk) => { + assert.strictEqual(chunk.toString(encoding), string); + }, 1)); + + readable.unshift(string, encoding); + +} + +{ + + const streamEncoding = 'base64'; + + function checkEncoding(readable) { + + // chunk encodings + const encodings = ['utf8', 'binary', 'hex', 'base64']; + const expected = []; + + readable.on('data', common.mustCall((chunk) => { + const { encoding, string } = expected.pop(); + assert.strictEqual(chunk.toString(encoding), string); + }, encodings.length)); + + for (const encoding of encodings) { + const string = 'abc'; + + // If encoding is the same as the state.encoding the string is + // saved as is + const expect = encoding !== streamEncoding ? + Buffer.from(string, encoding).toString(streamEncoding) : string; + + expected.push({ encoding, string: expect }); + + readable.unshift(string, encoding); + } + } + + const r1 = new Readable({ read() {} }); + r1.setEncoding(streamEncoding); + checkEncoding(r1); + + const r2 = new Readable({ read() {}, encoding: streamEncoding }); + checkEncoding(r2); + +} + +{ + // Both .push & .unshift should have the same behaviour + // When setting an encoding, each chunk should be emitted with that encoding + const encoding = 'base64'; + + function checkEncoding(readable) { + const string = 'abc'; + readable.on('data', common.mustCall((chunk) => { + assert.strictEqual(chunk, Buffer.from(string).toString(encoding)); + }, 2)); + + readable.push(string); + readable.unshift(string); + } + + const r1 = new Readable({ read() {} }); + r1.setEncoding(encoding); + checkEncoding(r1); + + const r2 = new Readable({ read() {}, encoding }); + checkEncoding(r2); + +} + +{ + // Check that ObjectMode works + const readable = new Readable({ objectMode: true, read() {} }); + + const chunks = ['a', 1, {}, []]; + + readable.on('data', common.mustCall((chunk) => { + assert.strictEqual(chunk, chunks.pop()); + }, chunks.length)); + + for (const chunk of chunks) { + readable.unshift(chunk); + } +} + +{ + + // Should not throw: https://github.com/nodejs/node/issues/27192 + const highWaterMark = 50; + class ArrayReader extends Readable { + constructor(opt) { + super({ highWaterMark }); + // The error happened only when pushing above hwm + this.buffer = new Array(highWaterMark * 2).fill(0).map(String); + } + _read(size) { + while (this.buffer.length) { + const chunk = this.buffer.shift(); + if (!this.buffer.length) { + this.push(chunk); + this.push(null); + return true; + } + if (!this.push(chunk)) + return; + } + } + } + + function onRead() { + while (null !== (stream.read())) { + // Remove the 'readable' listener before unshifting + stream.removeListener('readable', onRead); + stream.unshift('a'); + stream.on('data', (chunk) => { + console.log(chunk.length); + }); + break; + } + } + + const stream = new ArrayReader(); + stream.once('readable', common.mustCall(onRead)); + stream.on('end', common.mustCall()); + +} diff --git a/test/js/node/test/parallel/test-util-styletext.js b/test/js/node/test/parallel/test-util-styletext.js new file mode 100644 index 00000000000000..6baa6a60eac8ac --- /dev/null +++ b/test/js/node/test/parallel/test-util-styletext.js @@ -0,0 +1,43 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const util = require('util'); + +[ + undefined, + null, + false, + 5n, + 5, + Symbol(), + () => {}, + {}, +].forEach((invalidOption) => { + assert.throws(() => { + util.styleText(invalidOption, 'test'); + }, { + code: 'ERR_INVALID_ARG_VALUE', + }); + assert.throws(() => { + util.styleText('red', invalidOption); + }, { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +assert.throws(() => { + util.styleText('invalid', 'text'); +}, { + code: 'ERR_INVALID_ARG_VALUE', +}); + +assert.strictEqual(util.styleText('red', 'test'), '\u001b[31mtest\u001b[39m'); + +assert.strictEqual(util.styleText(['bold', 'red'], 'test'), '\u001b[1m\u001b[31mtest\u001b[39m\u001b[22m'); +assert.strictEqual(util.styleText(['bold', 'red'], 'test'), util.styleText('bold', util.styleText('red', 'test'))); + +assert.throws(() => { + util.styleText(['invalid'], 'text'); +}, { + code: 'ERR_INVALID_ARG_VALUE', +}); diff --git a/test/js/node/test/parallel/test-vm-global-property-enumerator.js b/test/js/node/test/parallel/test-vm-global-property-enumerator.js new file mode 100644 index 00000000000000..7b37c2af41094d --- /dev/null +++ b/test/js/node/test/parallel/test-vm-global-property-enumerator.js @@ -0,0 +1,49 @@ +'use strict'; +require('../common'); +const vm = require('vm'); +const assert = require('assert'); + +// Regression of https://github.com/nodejs/node/issues/53346 + +const cases = [ + { + get key() { + return 'value'; + }, + }, + { + // Intentionally single setter. + // eslint-disable-next-line accessor-pairs + set key(value) {}, + }, + {}, + { + key: 'value', + }, + (new class GetterObject { + get key() { + return 'value'; + } + }()), + (new class SetterObject { + // Intentionally single setter. + // eslint-disable-next-line accessor-pairs + set key(value) { + // noop + } + }()), + [], + [['key', 'value']], + { + __proto__: { + key: 'value', + }, + }, +]; + +for (const [idx, obj] of cases.entries()) { + const ctx = vm.createContext(obj); + const globalObj = vm.runInContext('this', ctx); + const keys = Object.keys(globalObj); + assert.deepStrictEqual(keys, Object.keys(obj), `Case ${idx} failed`); +} diff --git a/test/js/node/test/parallel/test-vm-global-property-prototype.js b/test/js/node/test/parallel/test-vm-global-property-prototype.js new file mode 100644 index 00000000000000..fe8abc8be45ee2 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-global-property-prototype.js @@ -0,0 +1,83 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +const sandbox = { + onSelf: 'onSelf', +}; + +function onSelfGetter() { + return 'onSelfGetter'; +} + +Object.defineProperty(sandbox, 'onSelfGetter', { + get: onSelfGetter, +}); + +Object.defineProperty(sandbox, 1, { + value: 'onSelfIndexed', + writable: false, + enumerable: false, + configurable: true, +}); + +const ctx = vm.createContext(sandbox); + +const result = vm.runInContext(` +Object.prototype.onProto = 'onProto'; +Object.defineProperty(Object.prototype, 'onProtoGetter', { + get() { + return 'onProtoGetter'; + }, +}); +Object.defineProperty(Object.prototype, 2, { + value: 'onProtoIndexed', + writable: false, + enumerable: false, + configurable: true, +}); + +const resultHasOwn = { + onSelf: Object.hasOwn(this, 'onSelf'), + onSelfGetter: Object.hasOwn(this, 'onSelfGetter'), + onSelfIndexed: Object.hasOwn(this, 1), + onProto: Object.hasOwn(this, 'onProto'), + onProtoGetter: Object.hasOwn(this, 'onProtoGetter'), + onProtoIndexed: Object.hasOwn(this, 2), +}; + +const getDesc = (prop) => Object.getOwnPropertyDescriptor(this, prop); +const resultDesc = { + onSelf: getDesc('onSelf'), + onSelfGetter: getDesc('onSelfGetter'), + onSelfIndexed: getDesc(1), + onProto: getDesc('onProto'), + onProtoGetter: getDesc('onProtoGetter'), + onProtoIndexed: getDesc(2), +}; +({ + resultHasOwn, + resultDesc, +}); +`, ctx); + +// eslint-disable-next-line no-restricted-properties +assert.deepEqual(result, { + resultHasOwn: { + onSelf: true, + onSelfGetter: true, + onSelfIndexed: true, + onProto: false, + onProtoGetter: false, + onProtoIndexed: false, + }, + resultDesc: { + onSelf: { value: 'onSelf', writable: true, enumerable: true, configurable: true }, + onSelfGetter: { get: onSelfGetter, set: undefined, enumerable: false, configurable: false }, + onSelfIndexed: { value: 'onSelfIndexed', writable: false, enumerable: false, configurable: true }, + onProto: undefined, + onProtoGetter: undefined, + onProtoIndexed: undefined, + }, +}); diff --git a/test/js/node/test/parallel/test-weakref.js b/test/js/node/test/parallel/test-weakref.js new file mode 100644 index 00000000000000..ca7485aaa6a40c --- /dev/null +++ b/test/js/node/test/parallel/test-weakref.js @@ -0,0 +1,13 @@ +'use strict'; + +// Flags: --expose-gc + +require('../common'); +const assert = require('assert'); + +const w = new globalThis.WeakRef({}); + +setTimeout(() => { + globalThis.gc(); + assert.strictEqual(w.deref(), undefined); +}, 200); diff --git a/test/js/node/test/parallel/test-worker-message-channel-sharedarraybuffer.js b/test/js/node/test/parallel/test-worker-message-channel-sharedarraybuffer.js new file mode 100644 index 00000000000000..220aa978b12051 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-message-channel-sharedarraybuffer.js @@ -0,0 +1,28 @@ +// Flags: --expose-gc +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +{ + const sharedArrayBuffer = new SharedArrayBuffer(12); + const local = Buffer.from(sharedArrayBuffer); + + const w = new Worker(` + const { parentPort } = require('worker_threads'); + parentPort.on('message', ({ sharedArrayBuffer }) => { + const local = Buffer.from(sharedArrayBuffer); + local.write('world!', 6); + parentPort.postMessage('written!'); + }); + `, { eval: true }); + w.on('message', common.mustCall(() => { + assert.strictEqual(local.toString(), 'Hello world!'); + global.gc(); + w.terminate(); + })); + w.postMessage({ sharedArrayBuffer }); + // This would be a race condition if the memory regions were overlapping + local.write('Hello '); +} diff --git a/test/js/node/test/parallel/test-worker-workerdata-sharedarraybuffer.js b/test/js/node/test/parallel/test-worker-workerdata-sharedarraybuffer.js new file mode 100644 index 00000000000000..4e3d508ac94941 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-workerdata-sharedarraybuffer.js @@ -0,0 +1,32 @@ +// Flags: --expose-gc +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +{ + const sharedArrayBuffer = new SharedArrayBuffer(12); + const local = Buffer.from(sharedArrayBuffer); + + const w = new Worker(` + const { parentPort, workerData } = require('worker_threads'); + const local = Buffer.from(workerData.sharedArrayBuffer); + + parentPort.on('message', () => { + local.write('world!', 6); + parentPort.postMessage('written!'); + }); + `, { + eval: true, + workerData: { sharedArrayBuffer } + }); + w.on('message', common.mustCall(() => { + assert.strictEqual(local.toString(), 'Hello world!'); + global.gc(); + w.terminate(); + })); + w.postMessage({}); + // This would be a race condition if the memory regions were overlapping + local.write('Hello '); +} diff --git a/test/js/node/test/parallel/test-zlib-deflate-constructors.js b/test/js/node/test/parallel/test-zlib-deflate-constructors.js index 6a5d4100862e9e..bf4b6d337443ff 100644 --- a/test/js/node/test/parallel/test-zlib-deflate-constructors.js +++ b/test/js/node/test/parallel/test-zlib-deflate-constructors.js @@ -19,7 +19,7 @@ assert.throws( code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError', message: 'The "options.chunkSize" property must be of type number. ' + - 'Received "test"' + 'Received type string ("test")' } ); @@ -39,7 +39,7 @@ assert.throws( code: 'ERR_OUT_OF_RANGE', name: 'RangeError', message: 'The value of "options.chunkSize" is out of range. It must ' + - 'be >= 64. Received: 0' + 'be >= 64. Received 0' } ); @@ -53,7 +53,7 @@ assert.throws( code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError', message: 'The "options.windowBits" property must be of type number. ' + - 'Received "test"' + 'Received type string ("test")' } ); @@ -94,7 +94,7 @@ assert.throws( code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError', message: 'The "options.level" property must be of type number. ' + - 'Received "test"' + 'Received type string ("test")' } ); @@ -135,7 +135,7 @@ assert.throws( code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError', message: 'The "level" argument must be of type number. ' + - 'Received "test"' + 'Received type string ("test")' } ); @@ -176,7 +176,7 @@ assert.throws( code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError', message: 'The "options.memLevel" property must be of type number. ' + - 'Received "test"' + 'Received type string ("test")' } ); @@ -224,7 +224,7 @@ assert.throws( code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError', message: 'The "options.strategy" property must be of type number. ' + - 'Received "test"' + 'Received type string ("test")' } ); @@ -265,7 +265,7 @@ assert.throws( code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError', message: 'The "strategy" argument must be of type number. ' + - 'Received "test"' + 'Received type string ("test")' } ); @@ -306,4 +306,4 @@ assert.throws( code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError', } -); \ No newline at end of file +); diff --git a/test/js/node/test/parallel/test-zlib-failed-init.js b/test/js/node/test/parallel/test-zlib-failed-init.js index d47b61de662d77..95f401a3718f30 100644 --- a/test/js/node/test/parallel/test-zlib-failed-init.js +++ b/test/js/node/test/parallel/test-zlib-failed-init.js @@ -11,7 +11,7 @@ assert.throws( code: 'ERR_OUT_OF_RANGE', name: 'RangeError', message: 'The value of "options.chunkSize" is out of range. It must ' + - 'be >= 64. Received: 0' + 'be >= 64. Received 0' } ); @@ -43,4 +43,4 @@ assert.throws( { const stream = zlib.createGzip({ strategy: NaN }); assert.strictEqual(stream._strategy, zlib.constants.Z_DEFAULT_STRATEGY); -} \ No newline at end of file +} diff --git a/test/js/node/test/parallel/test-zlib-flush-flags.js b/test/js/node/test/parallel/test-zlib-flush-flags.js index 1cb552390c1864..f3392b7a411363 100644 --- a/test/js/node/test/parallel/test-zlib-flush-flags.js +++ b/test/js/node/test/parallel/test-zlib-flush-flags.js @@ -11,7 +11,7 @@ assert.throws( code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError', message: 'The "options.flush" property must be of type number. ' + - 'Received "foobar"' + 'Received type string ("foobar")' } ); @@ -33,7 +33,7 @@ assert.throws( code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError', message: 'The "options.finishFlush" property must be of type number. ' + - 'Received "foobar"' + 'Received type string ("foobar")' } ); @@ -45,4 +45,4 @@ assert.throws( message: 'The value of "options.finishFlush" is out of range. It must ' + 'be >= 0 and <= 5. Received 10000' } -); \ No newline at end of file +); diff --git a/test/js/node/test/parallel/test-zlib-not-string-or-buffer.js b/test/js/node/test/parallel/test-zlib-not-string-or-buffer.js new file mode 100644 index 00000000000000..35e969737f925c --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-not-string-or-buffer.js @@ -0,0 +1,30 @@ +'use strict'; + +// Check the error condition testing for passing something other than a string +// or buffer. + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +[ + undefined, + null, + true, + false, + 0, + 1, + [1, 2, 3], + { foo: 'bar' }, +].forEach((input) => { + assert.throws( + () => zlib.deflateSync(input), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "buffer" argument must be of type string, ' + + 'Buffer, TypedArray, DataView, or ArrayBuffer.' + + common.invalidArgTypeHelper(input) + } + ); +}); diff --git a/test/js/node/util/bun-inspect.test.ts b/test/js/node/util/bun-inspect.test.ts index 115ad1c500a885..65de3b0ed4a8f7 100644 --- a/test/js/node/util/bun-inspect.test.ts +++ b/test/js/node/util/bun-inspect.test.ts @@ -3,13 +3,13 @@ import stripAnsi from "strip-ansi"; describe("Bun.inspect", () => { it("reports error instead of [native code]", () => { - expect( + expect(() => Bun.inspect({ [Symbol.for("nodejs.util.inspect.custom")]() { throw new Error("custom inspect"); }, }), - ).toBe("[custom formatter threw an exception]"); + ).toThrow("custom inspect"); }); it("supports colors: false", () => { diff --git a/test/js/node/util/custom-inspect.test.js b/test/js/node/util/custom-inspect.test.js index 040f0abb8d08bd..bc8763b214167b 100644 --- a/test/js/node/util/custom-inspect.test.js +++ b/test/js/node/util/custom-inspect.test.js @@ -155,11 +155,6 @@ for (const [name, inspect] of process.versions.bun }, }; - if (Bun.inspect === inspect) { - // make sure this doesnt crash - expect(inspect(obj)).toBeString(); - } else { - expect(() => inspect(obj)).toThrow(); - } + expect(() => inspect(obj)).toThrow(); }); } diff --git a/test/js/node/util/test-util-types.test.js b/test/js/node/util/test-util-types.test.js index 031d18ca20e6aa..084da3bfaceaf9 100644 --- a/test/js/node/util/test-util-types.test.js +++ b/test/js/node/util/test-util-types.test.js @@ -47,6 +47,7 @@ for (const [value, _method] of [ [new DataView(new ArrayBuffer())], [new SharedArrayBuffer()], [new Proxy({}, {}), "isProxy"], + [new EventTarget()], ]) { const method = _method || `is${value.constructor.name}`; test(method, () => {