diff options
author | 2023-01-12 19:10:41 -0800 | |
---|---|---|
committer | 2023-01-12 19:10:41 -0800 | |
commit | 766f8ceebc76dd749ba5c104f802c7ebda289db9 (patch) | |
tree | f84ee560938188261f7f5604a65b83aae354a646 /src/bun.js | |
parent | c03f7c998dd22689412658177e3a5736ce6b9034 (diff) | |
parent | 32f8cb31be6fb5b0b9aea1c6d4e95d118e8ef816 (diff) | |
download | bun-766f8ceebc76dd749ba5c104f802c7ebda289db9.tar.gz bun-766f8ceebc76dd749ba5c104f802c7ebda289db9.tar.zst bun-766f8ceebc76dd749ba5c104f802c7ebda289db9.zip |
Merge branch 'main' into dylan/github-dependencies
Diffstat (limited to 'src/bun.js')
-rw-r--r-- | src/bun.js/api/bun.zig | 21 | ||||
-rw-r--r-- | src/bun.js/api/bun/socket.zig | 71 | ||||
-rw-r--r-- | src/bun.js/api/server.zig | 130 | ||||
-rw-r--r-- | src/bun.js/bindings/ImportMetaObject.cpp | 26 | ||||
-rw-r--r-- | src/bun.js/bindings/ImportMetaObject.h | 4 | ||||
-rw-r--r-- | src/bun.js/bindings/JSBufferList.cpp | 22 | ||||
-rw-r--r-- | src/bun.js/bindings/JSBufferList.h | 9 | ||||
-rw-r--r-- | src/bun.js/bindings/JSStringDecoder.cpp | 23 | ||||
-rw-r--r-- | src/bun.js/bindings/JSStringDecoder.h | 6 | ||||
-rw-r--r-- | src/bun.js/bindings/bindings.zig | 16 | ||||
-rw-r--r-- | src/bun.js/bindings/headers-cpp.h | 2 | ||||
-rw-r--r-- | src/bun.js/bindings/headers.h | 2 | ||||
-rw-r--r-- | src/bun.js/javascript.zig | 81 | ||||
-rw-r--r-- | src/bun.js/modules/NodeModuleModule.cpp | 44 | ||||
-rw-r--r-- | src/bun.js/net.exports.js | 21 | ||||
-rw-r--r-- | src/bun.js/test/jest.zig | 49 |
16 files changed, 372 insertions, 155 deletions
diff --git a/src/bun.js/api/bun.zig b/src/bun.js/api/bun.zig index e9b760de5..32bd0a9ec 100644 --- a/src/bun.js/api/bun.zig +++ b/src/bun.js/api/bun.zig @@ -942,7 +942,17 @@ fn doResolve( return null; } - return doResolveWithArgs(ctx, specifier.getZigString(ctx.ptr()), from.getZigString(ctx.ptr()), exception, false); + var is_esm = true; + if (args.nextEat()) |next| { + if (next.isBoolean()) { + is_esm = next.toBoolean(); + } else { + JSC.throwInvalidArguments("esm must be a boolean", .{}, ctx, exception); + return null; + } + } + + return doResolveWithArgs(ctx, specifier.getZigString(ctx.ptr()), from.getZigString(ctx.ptr()), exception, is_esm, false); } fn doResolveWithArgs( @@ -950,6 +960,7 @@ fn doResolveWithArgs( specifier: ZigString, from: ZigString, exception: js.ExceptionRef, + is_esm: bool, comptime is_file_path: bool, ) ?JSC.JSValue { var errorable: ErrorableZigString = undefined; @@ -960,6 +971,7 @@ fn doResolveWithArgs( ctx.ptr(), specifier, from, + is_esm, ); } else { VirtualMachine.resolveForAPI( @@ -967,6 +979,7 @@ fn doResolveWithArgs( ctx.ptr(), specifier, from, + is_esm, ); } @@ -1010,10 +1023,11 @@ export fn Bun__resolve( global: *JSGlobalObject, specifier: JSValue, source: JSValue, + is_esm: bool, ) JSC.JSValue { var exception_ = [1]JSC.JSValueRef{null}; var exception = &exception_; - const value = doResolveWithArgs(global, specifier.getZigString(global), source.getZigString(global), exception, true) orelse { + const value = doResolveWithArgs(global, specifier.getZigString(global), source.getZigString(global), exception, is_esm, true) orelse { return JSC.JSPromise.rejectedPromiseValue(global, JSC.JSValue.fromRef(exception[0])); }; return JSC.JSPromise.resolvedPromiseValue(global, value); @@ -1023,10 +1037,11 @@ export fn Bun__resolveSync( global: *JSGlobalObject, specifier: JSValue, source: JSValue, + is_esm: bool, ) JSC.JSValue { var exception_ = [1]JSC.JSValueRef{null}; var exception = &exception_; - return doResolveWithArgs(global, specifier.getZigString(global), source.getZigString(global), exception, true) orelse { + return doResolveWithArgs(global, specifier.getZigString(global), source.getZigString(global), exception, is_esm, true) orelse { return JSC.JSValue.fromRef(exception[0]); }; } diff --git a/src/bun.js/api/bun/socket.zig b/src/bun.js/api/bun/socket.zig index f4dcfb01c..911dc9e89 100644 --- a/src/bun.js/api/bun/socket.zig +++ b/src/bun.js/api/bun/socket.zig @@ -232,38 +232,65 @@ pub const SocketConfig = struct { } } - if (opts.getTruthy(globalObject, "hostname")) |hostname| { - if (hostname.isEmptyOrUndefinedOrNull() or !hostname.isString()) { - exception.* = JSC.toInvalidArguments("Expected \"hostname\" to be a string", .{}, globalObject).asObjectRef(); - return null; - } + hostname_or_unix: { + if (opts.getTruthy(globalObject, "unix")) |unix_socket| { + if (!unix_socket.isString()) { + exception.* = JSC.toInvalidArguments("Expected \"unix\" to be a string", .{}, globalObject).asObjectRef(); + return null; + } - const port_value = opts.get(globalObject, "port") orelse JSValue.zero; - if (port_value.isEmptyOrUndefinedOrNull() or !port_value.isNumber() or port_value.toInt64() > std.math.maxInt(u16) or port_value.toInt64() < 0) { - exception.* = JSC.toInvalidArguments("Expected \"port\" to be a number between 0 and 65535", .{}, globalObject).asObjectRef(); - return null; - } + hostname_or_unix = unix_socket.getZigString(globalObject).toSlice(bun.default_allocator); - hostname_or_unix = hostname.getZigString(globalObject).toSlice(bun.default_allocator); - port = port_value.toU16(); + if (strings.hasPrefixComptime(hostname_or_unix.slice(), "file://") or strings.hasPrefixComptime(hostname_or_unix.slice(), "unix://") or strings.hasPrefixComptime(hostname_or_unix.slice(), "sock://")) { + hostname_or_unix.ptr += 7; + hostname_or_unix.len -|= 7; + } - if (hostname_or_unix.len == 0) { - exception.* = JSC.toInvalidArguments("Expected \"hostname\" to be a non-empty string", .{}, globalObject).asObjectRef(); - return null; - } - } else if (opts.getTruthy(globalObject, "unix")) |unix_socket| { - if (unix_socket.isEmptyOrUndefinedOrNull() or !unix_socket.isString()) { - exception.* = JSC.toInvalidArguments("Expected \"unix\" to be a string", .{}, globalObject).asObjectRef(); - return null; + if (hostname_or_unix.len > 0) { + break :hostname_or_unix; + } } - hostname_or_unix = unix_socket.getZigString(globalObject).toSlice(bun.default_allocator); + if (opts.getTruthy(globalObject, "hostname")) |hostname| { + if (!hostname.isString()) { + exception.* = JSC.toInvalidArguments("Expected \"hostname\" to be a string", .{}, globalObject).asObjectRef(); + return null; + } + + var port_value = opts.get(globalObject, "port") orelse JSValue.zero; + hostname_or_unix = hostname.getZigString(globalObject).toSlice(bun.default_allocator); + + if (port_value.isEmptyOrUndefinedOrNull() and hostname_or_unix.len > 0) { + const parsed_url = bun.URL.parse(hostname_or_unix.slice()); + if (parsed_url.getPort()) |port_num| { + port_value = JSValue.jsNumber(port_num); + hostname_or_unix.ptr = parsed_url.hostname.ptr; + hostname_or_unix.len = @truncate(u32, parsed_url.hostname.len); + } + } + + if (port_value.isEmptyOrUndefinedOrNull() or !port_value.isNumber() or port_value.toInt64() > std.math.maxInt(u16) or port_value.toInt64() < 0) { + exception.* = JSC.toInvalidArguments("Expected \"port\" to be a number between 0 and 65535", .{}, globalObject).asObjectRef(); + return null; + } + + port = port_value.toU16(); + + if (hostname_or_unix.len == 0) { + exception.* = JSC.toInvalidArguments("Expected \"hostname\" to be a non-empty string", .{}, globalObject).asObjectRef(); + return null; + } + + if (hostname_or_unix.len > 0) { + break :hostname_or_unix; + } + } if (hostname_or_unix.len == 0) { exception.* = JSC.toInvalidArguments("Expected \"unix\" to be a non-empty string", .{}, globalObject).asObjectRef(); return null; } - } else { + exception.* = JSC.toInvalidArguments("Expected either \"hostname\" or \"unix\"", .{}, globalObject).asObjectRef(); return null; } diff --git a/src/bun.js/api/server.zig b/src/bun.js/api/server.zig index f47ee9fc0..137d164f0 100644 --- a/src/bun.js/api/server.zig +++ b/src/bun.js/api/server.zig @@ -2135,8 +2135,8 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp const result = JSC.C.JSObjectCallAsFunctionReturnValue(this.server.globalThis, this.server.config.onError.asObjectRef(), this.server.thisObject.asObjectRef(), 1, &args); if (!result.isEmptyOrUndefinedOrNull()) { - if (result.isError() or result.isAggregateError(this.server.globalThis)) { - this.finishRunningErrorHandler(result, status); + if (result.toError()) |err| { + this.finishRunningErrorHandler(err, status); return; } else if (result.as(Response)) |response| { this.render(response); @@ -2514,7 +2514,10 @@ pub const WebSocketServer = struct { active_connections: usize = 0, /// used by publish() - ssl: bool = false, + flags: packed struct(u2) { + ssl: bool = false, + publish_to_self: bool = true, + } = .{}, pub fn fromJS(globalObject: *JSC.JSGlobalObject, object: JSC.JSValue) ?Handler { var handler = Handler{ .globalObject = globalObject }; @@ -2752,6 +2755,17 @@ pub const WebSocketServer = struct { } } + if (object.get(globalObject, "publishToSelf")) |value| { + if (!value.isUndefinedOrNull()) { + if (!value.isBoolean()) { + globalObject.throwInvalidArguments("websocket expects publishToSelf to be a boolean", .{}); + return null; + } + + server.handler.flags.publish_to_self = value.toBoolean(); + } + } + server.protect(); return server; } @@ -3025,7 +3039,9 @@ pub const ServerWebSocket = struct { log("publish() closed", .{}); return JSValue.jsNumber(0); }; - const ssl = this.handler.ssl; + const flags = this.handler.flags; + const ssl = flags.ssl; + const publish_to_self = flags.publish_to_self; const topic_value = args.ptr[0]; const message_value = args.ptr[1]; @@ -3051,16 +3067,23 @@ pub const ServerWebSocket = struct { return .zero; } - if (message_value.asArrayBuffer(globalThis)) |buffer| { + if (message_value.asArrayBuffer(globalThis)) |array_buffer| { + const buffer = array_buffer.slice(); + if (buffer.len == 0) { globalThis.throw("publish requires a non-empty message", .{}); return .zero; } + const result = if (!publish_to_self) + this.websocket.publish(topic_slice.slice(), buffer, .binary, compress) + else + uws.AnyWebSocket.publishWithOptions(ssl, app, topic_slice.slice(), buffer, .binary, compress); + return JSValue.jsNumber( // if 0, return 0 // else return number of bytes sent - @as(i32, @boolToInt(uws.AnyWebSocket.publishWithOptions(ssl, app, topic_slice.slice(), buffer.slice(), .binary, compress))) * @intCast(i32, @truncate(u31, buffer.len)), + if (result) @intCast(i32, @truncate(u31, buffer.len)) else @as(i32, 0), ); } @@ -3072,10 +3095,16 @@ pub const ServerWebSocket = struct { } const buffer = string_slice.slice(); + + const result = if (!publish_to_self) + this.websocket.publish(topic_slice.slice(), buffer, .text, compress) + else + uws.AnyWebSocket.publishWithOptions(ssl, app, topic_slice.slice(), buffer, .text, compress); + return JSValue.jsNumber( // if 0, return 0 // else return number of bytes sent - @as(i32, @boolToInt(uws.AnyWebSocket.publishWithOptions(ssl, app, topic_slice.slice(), buffer, .text, compress))) * @intCast(i32, @truncate(u31, buffer.len)), + if (result) @intCast(i32, @truncate(u31, buffer.len)) else @as(i32, 0), ); } @@ -3099,7 +3128,9 @@ pub const ServerWebSocket = struct { log("publish() closed", .{}); return JSValue.jsNumber(0); }; - const ssl = this.handler.ssl; + const flags = this.handler.flags; + const ssl = flags.ssl; + const publish_to_self = flags.publish_to_self; const topic_value = args.ptr[0]; const message_value = args.ptr[1]; @@ -3132,10 +3163,16 @@ pub const ServerWebSocket = struct { } const buffer = string_slice.slice(); + + const result = if (!publish_to_self) + this.websocket.publish(topic_slice.slice(), buffer, .text, compress) + else + uws.AnyWebSocket.publishWithOptions(ssl, app, topic_slice.slice(), buffer, .text, compress); + return JSValue.jsNumber( // if 0, return 0 // else return number of bytes sent - @as(i32, @boolToInt(uws.AnyWebSocket.publishWithOptions(ssl, app, topic_slice.slice(), buffer, .text, compress))) * @intCast(i32, @truncate(u31, buffer.len)), + if (result) @intCast(i32, @truncate(u31, buffer.len)) else @as(i32, 0), ); } @@ -3156,7 +3193,9 @@ pub const ServerWebSocket = struct { log("publish() closed", .{}); return JSValue.jsNumber(0); }; - const ssl = this.handler.ssl; + const flags = this.handler.flags; + const ssl = flags.ssl; + const publish_to_self = flags.publish_to_self; const topic_value = args.ptr[0]; const message_value = args.ptr[1]; const compress_value = args.ptr[2]; @@ -3180,19 +3219,25 @@ pub const ServerWebSocket = struct { globalThis.throw("publishBinary requires a non-empty message", .{}); return .zero; } - const buffer = message_value.asArrayBuffer(globalThis) orelse { + const array_buffer = message_value.asArrayBuffer(globalThis) orelse { globalThis.throw("publishBinary expects an ArrayBufferView", .{}); return .zero; }; + const buffer = array_buffer.slice(); if (buffer.len == 0) { return JSC.JSValue.jsNumber(0); } + const result = if (!publish_to_self) + this.websocket.publish(topic_slice.slice(), buffer, .binary, compress) + else + uws.AnyWebSocket.publishWithOptions(ssl, app, topic_slice.slice(), buffer, .binary, compress); + return JSValue.jsNumber( // if 0, return 0 // else return number of bytes sent - @as(i32, @boolToInt(uws.AnyWebSocket.publishWithOptions(ssl, app, topic_slice.slice(), buffer.slice(), .binary, compress))) * @intCast(i32, @truncate(u31, buffer.len)), + if (result) @intCast(i32, @truncate(u31, buffer.len)) else @as(i32, 0), ); } @@ -3200,13 +3245,15 @@ pub const ServerWebSocket = struct { this: *ServerWebSocket, globalThis: *JSC.JSGlobalObject, topic_str: *JSC.JSString, - buffer: *JSC.JSUint8Array, + array: *JSC.JSUint8Array, ) callconv(.C) JSC.JSValue { var app = this.handler.app orelse { log("publish() closed", .{}); return JSValue.jsNumber(0); }; - const ssl = this.handler.ssl; + const flags = this.handler.flags; + const ssl = flags.ssl; + const publish_to_self = flags.publish_to_self; var topic_slice = topic_str.toSlice(globalThis, bun.default_allocator); defer topic_slice.deinit(); @@ -3217,30 +3264,20 @@ pub const ServerWebSocket = struct { const compress = true; - const slice = buffer.slice(); - if (slice.len == 0) { + const buffer = array.slice(); + if (buffer.len == 0) { return JSC.JSValue.jsNumber(0); } + const result = if (!publish_to_self) + this.websocket.publish(topic_slice.slice(), buffer, .binary, compress) + else + uws.AnyWebSocket.publishWithOptions(ssl, app, topic_slice.slice(), buffer, .binary, compress); + return JSValue.jsNumber( // if 0, return 0 // else return number of bytes sent - @as( - i32, - @boolToInt( - uws.AnyWebSocket.publishWithOptions( - ssl, - app, - topic_slice.slice(), - slice, - .binary, - compress, - ), - ), - ) * @intCast( - i32, - @truncate(u31, slice.len), - ), + if (result) @intCast(i32, @truncate(u31, buffer.len)) else @as(i32, 0), ); } @@ -3254,7 +3291,9 @@ pub const ServerWebSocket = struct { log("publish() closed", .{}); return JSValue.jsNumber(0); }; - const ssl = this.handler.ssl; + const flags = this.handler.flags; + const ssl = flags.ssl; + const publish_to_self = flags.publish_to_self; var topic_slice = topic_str.toSlice(globalThis, bun.default_allocator); defer topic_slice.deinit(); @@ -3266,24 +3305,21 @@ pub const ServerWebSocket = struct { const compress = true; const slice = str.toSlice(globalThis, bun.default_allocator); - if (slice.len == 0) { + defer slice.deinit(); + const buffer = slice.slice(); + + if (buffer.len == 0) { return JSC.JSValue.jsNumber(0); } + const result = if (!publish_to_self) + this.websocket.publish(topic_slice.slice(), buffer, .text, compress) + else + uws.AnyWebSocket.publishWithOptions(ssl, app, topic_slice.slice(), buffer, .text, compress); return JSValue.jsNumber( // if 0, return 0 // else return number of bytes sent - @as(i32, @boolToInt(uws.AnyWebSocket.publishWithOptions( - ssl, - app, - topic_slice.slice(), - slice.slice(), - .text, - compress, - ))) * @intCast( - i32, - @truncate(u31, slice.len), - ), + if (result) @intCast(i32, @truncate(u31, buffer.len)) else @as(i32, 0), ); } @@ -4107,7 +4143,7 @@ pub fn NewServer(comptime ssl_enabled_: bool, comptime debug_mode_: bool) type { } if (new_config.websocket) |*ws| { - ws.handler.ssl = ssl_enabled; + ws.handler.flags.ssl = ssl_enabled; if (ws.handler.onMessage != .zero or ws.handler.onOpen != .zero) { if (this.config.websocket) |old_ws| { old_ws.unprotect(); @@ -4671,7 +4707,7 @@ pub fn NewServer(comptime ssl_enabled_: bool, comptime debug_mode_: bool) type { if (this.config.websocket) |*websocket| { websocket.globalObject = this.globalThis; websocket.handler.app = this.app; - websocket.handler.ssl = ssl_enabled; + websocket.handler.flags.ssl = ssl_enabled; this.app.ws( "/*", this, diff --git a/src/bun.js/bindings/ImportMetaObject.cpp b/src/bun.js/bindings/ImportMetaObject.cpp index 5838bde04..0247b5140 100644 --- a/src/bun.js/bindings/ImportMetaObject.cpp +++ b/src/bun.js/bindings/ImportMetaObject.cpp @@ -80,7 +80,7 @@ static EncodedJSValue functionRequireResolve(JSC::JSGlobalObject* globalObject, } } - auto result = Bun__resolveSync(globalObject, JSC::JSValue::encode(moduleName), from); + auto result = Bun__resolveSync(globalObject, JSC::JSValue::encode(moduleName), from, false); auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); if (!JSC::JSValue::decode(result).isString()) { @@ -156,17 +156,18 @@ JSObject* Zig::ImportMetaObject::createRequireFunction(VM& vm, JSGlobalObject* g JSFunction* requireFunction = JSFunction::create(vm, importMetaObjectRequireCodeGenerator(vm), globalObject); auto clientData = WebCore::clientData(vm); requireFunction->putDirectCustomAccessor(vm, clientData->builtinNames().resolvePublicName(), JSC::CustomGetterSetter::create(vm, functionRequireResolveLazyGetter, functionRequireResolveLazySetter), 0); - requireFunction->putDirect(vm, clientData->builtinNames().pathPrivateName(), jsOwnedString(vm, pathString), JSC::PropertyAttribute::DontEnum | 0); + requireFunction->putDirect(vm, clientData->builtinNames().pathPublicName(), jsString(vm, pathString), JSC::PropertyAttribute::DontEnum | 0); return requireFunction; } extern "C" EncodedJSValue functionImportMeta__resolveSync(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) { JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); switch (callFrame->argumentCount()) { case 0: { - auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); + // not "requires" because "require" could be confusing JSC::throwTypeError(globalObject, scope, "import.meta.resolveSync needs 1 argument (a string)"_s); scope.release(); @@ -176,13 +177,13 @@ extern "C" EncodedJSValue functionImportMeta__resolveSync(JSC::JSGlobalObject* g JSC::JSValue moduleName = callFrame->argument(0); if (moduleName.isUndefinedOrNull()) { - auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); JSC::throwTypeError(globalObject, scope, "import.meta.resolveSync expects a string"_s); scope.release(); return JSC::JSValue::encode(JSC::JSValue {}); } JSC__JSValue from; + bool isESM = true; if (callFrame->argumentCount() > 1) { JSC::JSValue fromValue = callFrame->argument(1); @@ -195,8 +196,20 @@ extern "C" EncodedJSValue functionImportMeta__resolveSync(JSC::JSGlobalObject* g fromValue = array->getIndex(globalObject, 0); } } + + if (callFrame->argumentCount() > 2) { + JSC::JSValue isESMValue = callFrame->argument(2); + if (isESMValue.isBoolean()) { + isESM = isESMValue.toBoolean(globalObject); + RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::JSValue {})); + } + } + } else if (fromValue.isBoolean()) { + isESM = fromValue.toBoolean(globalObject); + RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::JSValue {})); } from = JSC::JSValue::encode(fromValue); + } else { JSC::JSObject* thisObject = JSC::jsDynamicCast<JSC::JSObject*>(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { @@ -210,8 +223,7 @@ extern "C" EncodedJSValue functionImportMeta__resolveSync(JSC::JSGlobalObject* g from = JSC::JSValue::encode(thisObject->get(globalObject, clientData->builtinNames().pathPublicName())); } - auto result = Bun__resolveSync(globalObject, JSC::JSValue::encode(moduleName), from); - auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); + auto result = Bun__resolveSync(globalObject, JSC::JSValue::encode(moduleName), from, isESM); if (!JSC::JSValue::decode(result).isString()) { JSC::throwException(globalObject, scope, JSC::JSValue::decode(result)); @@ -266,7 +278,7 @@ JSC_DEFINE_HOST_FUNCTION(functionImportMeta__resolve, from = JSC::JSValue::encode(thisObject->get(globalObject, clientData->builtinNames().pathPublicName())); } - return Bun__resolve(globalObject, JSC::JSValue::encode(moduleName), from); + return Bun__resolve(globalObject, JSC::JSValue::encode(moduleName), from, true); } } } diff --git a/src/bun.js/bindings/ImportMetaObject.h b/src/bun.js/bindings/ImportMetaObject.h index 3ce50ebbb..ff32c85d4 100644 --- a/src/bun.js/bindings/ImportMetaObject.h +++ b/src/bun.js/bindings/ImportMetaObject.h @@ -9,8 +9,8 @@ #include "JSDOMWrapperCache.h" extern "C" JSC_DECLARE_HOST_FUNCTION(functionImportMeta__resolveSync); -extern "C" EncodedJSValue Bun__resolve(JSC::JSGlobalObject* global, JSC::EncodedJSValue specifier, JSC::EncodedJSValue from); -extern "C" EncodedJSValue Bun__resolveSync(JSC::JSGlobalObject* global, JSC::EncodedJSValue specifier, JSC::EncodedJSValue from); +extern "C" EncodedJSValue Bun__resolve(JSC::JSGlobalObject* global, JSC::EncodedJSValue specifier, JSC::EncodedJSValue from, bool is_esm); +extern "C" EncodedJSValue Bun__resolveSync(JSC::JSGlobalObject* global, JSC::EncodedJSValue specifier, JSC::EncodedJSValue from, bool is_esm); namespace Zig { diff --git a/src/bun.js/bindings/JSBufferList.cpp b/src/bun.js/bindings/JSBufferList.cpp index e54b433e5..9b9990598 100644 --- a/src/bun.js/bindings/JSBufferList.cpp +++ b/src/bun.js/bindings/JSBufferList.cpp @@ -32,7 +32,7 @@ void JSBufferList::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly); } -JSC::JSValue JSBufferList::concat(JSC::VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, int32_t n) +JSC::JSValue JSBufferList::concat(JSC::VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, size_t n) { auto throwScope = DECLARE_THROW_SCOPE(vm); auto* subclassStructure = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject)->JSBufferSubclassStructure(); @@ -75,6 +75,8 @@ JSC::JSValue JSBufferList::concat(JSC::VM& vm, JSC::JSGlobalObject* lexicalGloba i += length; } + memset(uint8Array->typedVector() + i, 0, n - i); + RELEASE_AND_RETURN(throwScope, uint8Array); } @@ -100,7 +102,7 @@ JSC::JSValue JSBufferList::join(JSC::VM& vm, JSC::JSGlobalObject* lexicalGlobalO RELEASE_AND_RETURN(throwScope, ropeBuilder.release()); } -JSC::JSValue JSBufferList::consume(JSC::VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, int32_t n, bool hasString) +JSC::JSValue JSBufferList::consume(JSC::VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, size_t n, bool hasString) { if (hasString) return _getString(vm, lexicalGlobalObject, n); @@ -108,7 +110,7 @@ JSC::JSValue JSBufferList::consume(JSC::VM& vm, JSC::JSGlobalObject* lexicalGlob return _getBuffer(vm, lexicalGlobalObject, n); } -JSC::JSValue JSBufferList::_getString(JSC::VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, int32_t total) +JSC::JSValue JSBufferList::_getString(JSC::VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, size_t total) { auto throwScope = DECLARE_THROW_SCOPE(vm); if (total <= 0 || length() == 0) { @@ -150,13 +152,14 @@ JSC::JSValue JSBufferList::_getString(JSC::VM& vm, JSC::JSGlobalObject* lexicalG if (!ropeBuilder.append(str)) return throwOutOfMemoryError(lexicalGlobalObject, throwScope); m_deque.removeFirst(); - if (n == len) break; + if (n == len) + break; n -= len; } RELEASE_AND_RETURN(throwScope, ropeBuilder.release()); } -JSC::JSValue JSBufferList::_getBuffer(JSC::VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, int32_t total) +JSC::JSValue JSBufferList::_getBuffer(JSC::VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, size_t total) { auto throwScope = DECLARE_THROW_SCOPE(vm); auto* subclassStructure = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject)->JSBufferSubclassStructure(); @@ -205,16 +208,23 @@ JSC::JSValue JSBufferList::_getBuffer(JSC::VM& vm, JSC::JSGlobalObject* lexicalG auto buffer = array->possiblySharedBuffer(); JSC::JSUint8Array* newArray = JSC::JSUint8Array::create(lexicalGlobalObject, subclassStructure, buffer, n, len - n); iter->set(vm, this, newArray); + offset += n; break; } if (UNLIKELY(!uint8Array->setFromTypedArray(lexicalGlobalObject, offset, array, 0, len, JSC::CopyType::Unobservable))) { return throwOutOfMemoryError(lexicalGlobalObject, throwScope); } m_deque.removeFirst(); - if (n == len) break; + if (n == len) { + offset += len; + break; + } n -= len; offset += len; } + + memset(uint8Array->typedVector() + offset, 0, total - offset); + RELEASE_AND_RETURN(throwScope, uint8Array); } diff --git a/src/bun.js/bindings/JSBufferList.h b/src/bun.js/bindings/JSBufferList.h index a9227e981..94a69c8d1 100644 --- a/src/bun.js/bindings/JSBufferList.h +++ b/src/bun.js/bindings/JSBufferList.h @@ -76,11 +76,11 @@ public: return JSC::JSValue(m_deque.first().get()); } - JSC::JSValue concat(JSC::VM&, JSC::JSGlobalObject*, int32_t); + JSC::JSValue concat(JSC::VM&, JSC::JSGlobalObject*, size_t); JSC::JSValue join(JSC::VM&, JSC::JSGlobalObject*, JSString*); - JSC::JSValue consume(JSC::VM&, JSC::JSGlobalObject*, int32_t, bool); - JSC::JSValue _getBuffer(JSC::VM&, JSC::JSGlobalObject*, int32_t); - JSC::JSValue _getString(JSC::VM&, JSC::JSGlobalObject*, int32_t); + JSC::JSValue consume(JSC::VM&, JSC::JSGlobalObject*, size_t, bool); + JSC::JSValue _getBuffer(JSC::VM&, JSC::JSGlobalObject*, size_t); + JSC::JSValue _getString(JSC::VM&, JSC::JSGlobalObject*, size_t); private: Deque<WriteBarrier<Unknown>> m_deque; @@ -134,6 +134,7 @@ public: // Must be defined for each specialization class. static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSC::JSGlobalObject*, JSC::CallFrame*); DECLARE_EXPORT_INFO; + private: JSBufferListConstructor(JSC::VM& vm, JSC::Structure* structure, JSC::NativeFunction nativeFunction) : Base(vm, structure, nativeFunction, nativeFunction) diff --git a/src/bun.js/bindings/JSStringDecoder.cpp b/src/bun.js/bindings/JSStringDecoder.cpp index 66f9cb654..ca79e9e1c 100644 --- a/src/bun.js/bindings/JSStringDecoder.cpp +++ b/src/bun.js/bindings/JSStringDecoder.cpp @@ -206,9 +206,30 @@ JSC::JSValue JSStringDecoder::write(JSC::VM& vm, JSC::JSGlobalObject* globalObje } } -JSC::JSValue JSStringDecoder::end(JSC::VM& vm, JSC::JSGlobalObject* globalObject, uint8_t* bufPtr, uint32_t length) +class ResetScope final { +public: + ResetScope(JSStringDecoder* decoder); + ~ResetScope(); + JSStringDecoder* m_decoder; +}; + +ResetScope::ResetScope(JSStringDecoder* decoder) +{ + m_decoder = decoder; +} + +ResetScope::~ResetScope() +{ + m_decoder->m_lastTotal = 0; + m_decoder->m_lastNeed = 0; + memset(m_decoder->m_lastChar, 0, 4); +} + +JSC::JSValue +JSStringDecoder::end(JSC::VM& vm, JSC::JSGlobalObject* globalObject, uint8_t* bufPtr, uint32_t length) { auto throwScope = DECLARE_THROW_SCOPE(vm); + auto resetScope = ResetScope(this); switch (m_encoding) { case BufferEncodingType::ucs2: case BufferEncodingType::utf16le: { diff --git a/src/bun.js/bindings/JSStringDecoder.h b/src/bun.js/bindings/JSStringDecoder.h index 299c2fb96..bce1ae05d 100644 --- a/src/bun.js/bindings/JSStringDecoder.h +++ b/src/bun.js/bindings/JSStringDecoder.h @@ -11,7 +11,10 @@ class JSStringDecoder : public JSC::JSDestructibleObject { public: JSStringDecoder(JSC::VM& vm, JSC::Structure* structure, BufferEncodingType encoding) - : Base(vm, structure), m_lastNeed(0), m_lastTotal(0), m_encoding(encoding) + : Base(vm, structure) + , m_lastNeed(0) + , m_lastTotal(0) + , m_encoding(encoding) { } @@ -108,6 +111,7 @@ public: // Must be defined for each specialization class. static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSC::JSGlobalObject*, JSC::CallFrame*); DECLARE_EXPORT_INFO; + private: JSStringDecoderConstructor(JSC::VM& vm, JSC::Structure* structure, JSC::NativeFunction nativeFunction) : Base(vm, structure, nativeFunction, nativeFunction) diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig index 41a52956d..1bee13fc0 100644 --- a/src/bun.js/bindings/bindings.zig +++ b/src/bun.js/bindings/bindings.zig @@ -1339,7 +1339,7 @@ pub fn NewGlobalObject(comptime Type: type) type { } pub fn resolve(res: *ErrorableZigString, global: *JSGlobalObject, specifier: *ZigString, source: *ZigString) callconv(.C) void { if (comptime @hasDecl(Type, "resolve")) { - @call(.always_inline, Type.resolve, .{ res, global, specifier.*, source.* }); + @call(.always_inline, Type.resolve, .{ res, global, specifier.*, source.*, true }); return; } res.* = ErrorableZigString.err(error.ResolveFailed, ZigString.init(resolveNotImpl).toErrorInstance(global).asVoid()); @@ -1506,7 +1506,7 @@ pub const JSPromise = extern struct { ) JSValue { if (value.isEmpty()) { return resolvedPromiseValue(globalObject, JSValue.jsUndefined()); - } else if (value.isUndefinedOrNull() or !value.isCell()) { + } else if (value.isEmptyOrUndefinedOrNull() or !value.isCell()) { return resolvedPromiseValue(globalObject, value); } @@ -2484,10 +2484,8 @@ pub const JSValue = enum(JSValueReprInt) { } pub fn isObject(this: JSType) bool { - return switch (this) { - .Object, .FinalObject => true, - else => false, - }; + // inline constexpr bool isObjectType(JSType type) { return type >= ObjectType; } + return @enumToInt(this) >= @enumToInt(JSType.Object); } pub fn isFunction(this: JSType) bool { @@ -2663,7 +2661,7 @@ pub const JSValue = enum(JSValueReprInt) { } pub fn isInstanceOf(this: JSValue, global: *JSGlobalObject, constructor: JSValue) bool { - if (this.isEmptyOrUndefinedOrNull()) + if (!this.isCell()) return false; return JSC.C.JSValueIsInstanceOfConstructor(global, this.asObjectRef(), constructor.asObjectRef(), null); @@ -3112,8 +3110,8 @@ pub const JSValue = enum(JSValueReprInt) { pub fn isCustomGetterSetter(this: JSValue) bool { return cppFn("isCustomGetterSetter", .{this}); } - pub fn isObject(this: JSValue) bool { - return cppFn("isObject", .{this}); + pub inline fn isObject(this: JSValue) bool { + return this.isCell() and this.jsType().isObject(); } pub fn isClass(this: JSValue, global: *JSGlobalObject) bool { diff --git a/src/bun.js/bindings/headers-cpp.h b/src/bun.js/bindings/headers-cpp.h index 065fd8caa..a05007fff 100644 --- a/src/bun.js/bindings/headers-cpp.h +++ b/src/bun.js/bindings/headers-cpp.h @@ -1,4 +1,4 @@ -//-- AUTOGENERATED FILE -- 1673374722 +//-- AUTOGENERATED FILE -- 1673376494 // clang-format off #pragma once diff --git a/src/bun.js/bindings/headers.h b/src/bun.js/bindings/headers.h index 4e5dabe4e..71a8d1034 100644 --- a/src/bun.js/bindings/headers.h +++ b/src/bun.js/bindings/headers.h @@ -1,5 +1,5 @@ // clang-format off -//-- AUTOGENERATED FILE -- 1673374722 +//-- AUTOGENERATED FILE -- 1673376494 #pragma once #include <stddef.h> diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig index fd2ded108..c2c43cf81 100644 --- a/src/bun.js/javascript.zig +++ b/src/bun.js/javascript.zig @@ -429,7 +429,7 @@ pub const VirtualMachine = struct { auto_install_dependencies: bool = false, load_builtins_from_path: []const u8 = "", - onUnhandledRejection: *const fn (*VirtualMachine, globalObject: *JSC.JSGlobalObject, JSC.JSValue) void = defaultOnUnhandledRejection, + onUnhandledRejection: *const OnUnhandledRejection = defaultOnUnhandledRejection, onUnhandledRejectionCtx: ?*anyopaque = null, unhandled_error_counter: usize = 0, @@ -438,6 +438,8 @@ pub const VirtualMachine = struct { gc_controller: JSC.GarbageCollectionController = .{}, + pub const OnUnhandledRejection = fn (*VirtualMachine, globalObject: *JSC.JSGlobalObject, JSC.JSValue) void; + const VMHolder = struct { pub threadlocal var vm: ?*VirtualMachine = null; }; @@ -454,6 +456,30 @@ pub const VirtualMachine = struct { pub threadlocal var is_main_thread_vm: bool = false; + pub const UnhandledRejectionScope = struct { + ctx: ?*anyopaque = null, + onUnhandledRejection: *const OnUnhandledRejection = undefined, + count: usize = 0, + + pub fn apply(this: *UnhandledRejectionScope, vm: *JSC.VirtualMachine) void { + vm.onUnhandledRejection = this.onUnhandledRejection; + vm.onUnhandledRejectionCtx = this.ctx; + vm.unhandled_error_counter = this.count; + } + }; + + pub fn onQuietUnhandledRejectionHandler(this: *VirtualMachine, _: *JSC.JSGlobalObject, _: JSC.JSValue) void { + this.unhandled_error_counter += 1; + } + + pub fn unhandledRejectionScope(this: *VirtualMachine) UnhandledRejectionScope { + return .{ + .onUnhandledRejection = this.onUnhandledRejection, + .ctx = this.onUnhandledRejectionCtx, + .count = this.unhandled_error_counter, + }; + } + pub fn resetUnhandledRejection(this: *VirtualMachine) void { this.onUnhandledRejection = defaultOnUnhandledRejection; } @@ -891,6 +917,7 @@ pub const VirtualMachine = struct { _: *JSGlobalObject, specifier: string, source: string, + is_esm: bool, comptime is_a_file_path: bool, comptime realpath: bool, ) !void { @@ -936,7 +963,7 @@ pub const VirtualMachine = struct { jsc_vm.bundler.fs.top_level_dir, // TODO: do we need to handle things like query string params? if (strings.hasPrefixComptime(specifier, "file://")) specifier["file://".len..] else specifier, - .stmt, + if (is_esm) .stmt else .require, .read_only, )) { .success => |r| r, @@ -1012,19 +1039,53 @@ pub const VirtualMachine = struct { } } - pub fn resolveForAPI(res: *ErrorableZigString, global: *JSGlobalObject, specifier: ZigString, source: ZigString) void { - resolveMaybeNeedsTrailingSlash(res, global, specifier, source, false, true); + pub fn resolveForAPI( + res: *ErrorableZigString, + global: *JSGlobalObject, + specifier: ZigString, + source: ZigString, + is_esm: bool, + ) void { + resolveMaybeNeedsTrailingSlash(res, global, specifier, source, is_esm, false, true); } - pub fn resolveFilePathForAPI(res: *ErrorableZigString, global: *JSGlobalObject, specifier: ZigString, source: ZigString) void { - resolveMaybeNeedsTrailingSlash(res, global, specifier, source, true, true); + pub fn resolveFilePathForAPI( + res: *ErrorableZigString, + global: *JSGlobalObject, + specifier: ZigString, + source: ZigString, + is_esm: bool, + ) void { + resolveMaybeNeedsTrailingSlash(res, global, specifier, source, is_esm, true, true); + } + + pub fn resolve( + res: *ErrorableZigString, + global: *JSGlobalObject, + specifier: ZigString, + source: ZigString, + is_esm: bool, + ) void { + resolveMaybeNeedsTrailingSlash(res, global, specifier, source, is_esm, true, false); } - pub fn resolve(res: *ErrorableZigString, global: *JSGlobalObject, specifier: ZigString, source: ZigString) void { - resolveMaybeNeedsTrailingSlash(res, global, specifier, source, true, false); + fn normalizeSource(source: []const u8) []const u8 { + if (strings.hasPrefixComptime(source, "file://")) { + return source["file://".len..]; + } + + return source; } - pub fn resolveMaybeNeedsTrailingSlash(res: *ErrorableZigString, global: *JSGlobalObject, specifier: ZigString, source: ZigString, comptime is_a_file_path: bool, comptime realpath: bool) void { + pub fn resolveMaybeNeedsTrailingSlash( + res: *ErrorableZigString, + global: *JSGlobalObject, + specifier: ZigString, + source: ZigString, + is_esm: bool, + comptime is_a_file_path: bool, + comptime realpath: bool, + ) void { var result = ResolveFunctionResult{ .path = "", .result = null }; var jsc_vm = VirtualMachine.get(); if (jsc_vm.plugin_runner) |plugin_runner| { @@ -1057,7 +1118,7 @@ pub const VirtualMachine = struct { jsc_vm.bundler.linker.log = old_log; jsc_vm.bundler.resolver.log = old_log; } - _resolve(&result, global, specifier.slice(), source.slice(), is_a_file_path, realpath) catch |err_| { + _resolve(&result, global, specifier.slice(), normalizeSource(source.slice()), is_esm, is_a_file_path, realpath) catch |err_| { var err = err_; const msg: logger.Msg = brk: { var msgs: []logger.Msg = log.msgs.items; diff --git a/src/bun.js/modules/NodeModuleModule.cpp b/src/bun.js/modules/NodeModuleModule.cpp index 02e4e3849..01e061499 100644 --- a/src/bun.js/modules/NodeModuleModule.cpp +++ b/src/bun.js/modules/NodeModuleModule.cpp @@ -19,36 +19,24 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionNodeModuleCreateRequire, return JSC::JSValue::encode(JSC::jsUndefined()); } - Zig::ImportMetaObject *importMetaObject = Zig::ImportMetaObject::create( - globalObject, callFrame->uncheckedArgument(0)); + auto str = callFrame->uncheckedArgument(0).toStringOrNull(globalObject); + RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::jsUndefined())); + WTF::String val = str->value(globalObject); + auto *meta = Zig::ImportMetaObject::create(globalObject, str); auto clientData = WebCore::clientData(vm); - - RETURN_IF_EXCEPTION(scope, {}); - - if (!importMetaObject) { - throwTypeError(globalObject, scope, "Invalid path"_s); - return JSC::JSValue::encode(JSC::jsUndefined()); - } - - auto requireFunctionValue = importMetaObject->get( - globalObject, clientData->builtinNames().requirePublicName()); - RETURN_IF_EXCEPTION(scope, {}); - - JSC::JSBoundFunction *boundRequireFunction = JSC::JSBoundFunction::create( - vm, globalObject, requireFunctionValue.getObject(), importMetaObject, - nullptr, 1, jsString(vm, String("require"_s))); - RETURN_IF_EXCEPTION(scope, {}); - auto resolveFunction = importMetaObject->get( - globalObject, clientData->builtinNames().resolveSyncPublicName()); - - JSC::JSBoundFunction *boundResolveFunction = JSC::JSBoundFunction::create( - vm, globalObject, resolveFunction.getObject(), importMetaObject, nullptr, - 1, jsString(vm, String("resolve"_s))); + auto requireFunction = + Zig::ImportMetaObject::createRequireFunction(vm, globalObject, val); + auto nameStr = jsCast<JSFunction *>(requireFunction)->name(vm); + JSC::JSBoundFunction *boundRequireFunction = + JSC::JSBoundFunction::create(vm, globalObject, requireFunction, meta, + nullptr, 0, jsString(vm, nameStr)); boundRequireFunction->putDirect( - vm, clientData->builtinNames().resolvePublicName(), boundResolveFunction, - JSC::PropertyAttribute::Function | 0); + vm, clientData->builtinNames().resolvePublicName(), + requireFunction->getDirect( + vm, clientData->builtinNames().resolvePublicName()), + 0); - RELEASE_AND_RETURN(scope, JSC::JSValue::encode(boundRequireFunction)); + RELEASE_AND_RETURN(scope, JSValue::encode(boundRequireFunction)); } JSC_DEFINE_HOST_FUNCTION(jsFunctionNodeModulePaths, (JSC::JSGlobalObject * globalObject, @@ -113,7 +101,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionResolveFileName, auto result = Bun__resolveSync(globalObject, JSC::JSValue::encode(moduleName), - JSValue::encode(callFrame->argument(1))); + JSValue::encode(callFrame->argument(1)), false); auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); if (!JSC::JSValue::decode(result).isString()) { diff --git a/src/bun.js/net.exports.js b/src/bun.js/net.exports.js index 8d283dd1e..f894cd5fa 100644 --- a/src/bun.js/net.exports.js +++ b/src/bun.js/net.exports.js @@ -86,11 +86,10 @@ export const Socket = (function (InternalSocket) { self.emit("error", error); }, - data(socket, buffer) { - const self = socket.data; - self.bytesRead += buffer.length; + data({ data: self }, { length, buffer }) { + self.bytesRead += length; const queue = self.#readQueue; - const ret = new Buffer(buffer.buffer); + const ret = new Buffer(buffer); if (queue.isEmpty()) { if (self.push(ret)) return; } @@ -197,7 +196,10 @@ export const Socket = (function (InternalSocket) { connect(port, host, connectListener) { // TODO support IPC sockets var path; - if (typeof host == "function") { + if (arguments.length === 1 && typeof port === "string") { + path = port; + port = undefined; + } else if (typeof host == "function") { if (typeof port === "string") { path = port; port = undefined; @@ -336,7 +338,14 @@ export const Socket = (function (InternalSocket) { } else if (this.#writeCallback) { callback(new Error("overlapping _write()")); } else { - if (written > 0) chunk = chunk.slice(written); + if (written > 0) { + if (typeof chunk == "string") { + chunk = chunk.slice(written); + } else { + chunk = chunk.subarray(written); + } + } + this.#writeCallback = callback; this.#writeChunk = chunk; } diff --git a/src/bun.js/test/jest.zig b/src/bun.js/test/jest.zig index 00c8148b2..4b31f7309 100644 --- a/src/bun.js/test/jest.zig +++ b/src/bun.js/test/jest.zig @@ -1124,12 +1124,38 @@ pub const Expect = struct { } const not = this.op.contains(.not); - const result_ = value.call(globalObject, &.{}).toError(); + + const result_: ?JSValue = brk: { + var vm = globalObject.bunVM(); + var scope = vm.unhandledRejectionScope(); + vm.onUnhandledRejection = &VirtualMachine.onQuietUnhandledRejectionHandler; + const return_value: JSValue = value.call(globalObject, &.{}); + + if (return_value.asAnyPromise()) |promise| { + globalObject.bunVM().waitForPromise(promise); + scope.apply(vm); + const promise_result = promise.result(globalObject.vm()); + + switch (promise.status(globalObject.vm())) { + .Fulfilled => { + break :brk null; + }, + .Rejected => { + // since we know for sure it rejected, we should always return the error + break :brk promise_result.toError() orelse promise_result; + }, + .Pending => unreachable, + } + } + scope.apply(vm); + + break :brk return_value.toError(); + }; + const did_throw = result_ != null; const matched_expectation = did_throw == !not; - if (matched_expectation) return thisValue; - if (expected_value.isEmptyOrUndefinedOrNull()) { + if (!matched_expectation) { if (!not) globalObject.throw("Expected function to throw", .{}) else { @@ -1139,7 +1165,14 @@ pub const Expect = struct { return .zero; } - const result = result_.?; + + // If you throw a string, it's treated as the message of an Error + // If you are expected not to throw and you didn't throw, then you pass + // If you are expected to throw a specific message and you throw a different one, then you fail. + if (matched_expectation and (!expected_value.isCell() or not)) + return thisValue; + + const result = result_ orelse JSC.JSValue.jsUndefined(); const expected_error = expected_value.toError(); @@ -1148,7 +1181,10 @@ pub const Expect = struct { if (expected_value.isString()) break :brk expected_value; break :brk expected_error.?.get(globalObject, "message"); }; - const actual = result.get(globalObject, "message"); + const actual: ?JSValue = if (result.isObject()) + result.get(globalObject, "message") + else + null; // TODO support partial match const pass = brk: { if (expected) |expected_message| @@ -1460,7 +1496,6 @@ pub const TestScope = struct { .Internal => vm.waitForPromise(promise), else => {}, } - switch (promise.status(vm.global.vm())) { .Rejected => { vm.runErrorHandler(promise.result(vm.global.vm()), null); @@ -1737,7 +1772,7 @@ pub const DescribeScope = struct { var scope = allocator.create(DescribeScope) catch unreachable; scope.* = .{ .label = (label.toSlice(allocator).cloneIfNeeded(allocator) catch unreachable).slice(), - .parent = this, + .parent = active, .file_id = this.file_id, }; var new_this = DescribeScope.Class.make(ctx, scope); |