aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2023-05-26 03:32:28 -0700
committerGravatar GitHub <noreply@github.com> 2023-05-26 03:32:28 -0700
commit0f2a79b9c1a5b0142f8099e94712799607e8990f (patch)
tree99077706398898df3abda3a9a45a9abc0093e8fc
parent62f05ec1037009cc1724674277df82f1a9ec75ab (diff)
downloadbun-0f2a79b9c1a5b0142f8099e94712799607e8990f.tar.gz
bun-0f2a79b9c1a5b0142f8099e94712799607e8990f.tar.zst
bun-0f2a79b9c1a5b0142f8099e94712799607e8990f.zip
Fix crash in test.todo + remove JSC C API usages in bun:test (#3079)
* Fix crash in test.todo * remove usages of JSC C API in bun:test * Remove additional JSC-C API usages * fix `make headers` * URLSearchParams.length * FormData length * URLSearchParams length * Fix `make headers` * very fancy length * Fix bug with exceptions being ignored sometimes * Add tests for extension toHaveLength --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
-rw-r--r--src/bun.js/api/JSTranspiler.zig6
-rw-r--r--src/bun.js/api/bun.zig2
-rw-r--r--src/bun.js/api/filesystem_router.zig2
-rw-r--r--src/bun.js/api/server.zig6
-rw-r--r--src/bun.js/base.zig1
-rw-r--r--src/bun.js/bindings/URLSearchParams.h1
-rw-r--r--src/bun.js/bindings/ZigGeneratedClasses.h60
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.cpp32
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.h4
-rw-r--r--src/bun.js/bindings/bindings.cpp76
-rw-r--r--src/bun.js/bindings/bindings.zig57
-rw-r--r--src/bun.js/bindings/exports.zig4
-rw-r--r--src/bun.js/bindings/headers-handwritten.h2
-rw-r--r--src/bun.js/bindings/headers.h10
-rw-r--r--src/bun.js/bindings/headers.zig2
-rw-r--r--src/bun.js/bindings/webcore/JSDOMFormData.cpp13
-rw-r--r--src/bun.js/bindings/webcore/JSFetchHeaders.h2
-rw-r--r--src/bun.js/bindings/webcore/JSURLSearchParams.cpp11
-rw-r--r--src/bun.js/bindings/webcore/WebSocket.cpp16
-rw-r--r--src/bun.js/node/node_os.zig2
-rw-r--r--src/bun.js/test/jest.zig590
-rw-r--r--src/bun.js/test/pretty_format.zig4
-rw-r--r--src/bun.js/webcore/blob.zig31
-rw-r--r--src/cli/test_command.zig2
-rw-r--r--src/http/websocket_http_client.zig19
-rw-r--r--src/js_ast.zig4
-rw-r--r--src/napi/napi.zig4
-rw-r--r--test/js/bun/test/test-test.test.ts46
28 files changed, 645 insertions, 364 deletions
diff --git a/src/bun.js/api/JSTranspiler.zig b/src/bun.js/api/JSTranspiler.zig
index fecce60cd..8a59f59e7 100644
--- a/src/bun.js/api/JSTranspiler.zig
+++ b/src/bun.js/api/JSTranspiler.zig
@@ -385,7 +385,7 @@ fn transformOptionsFromJSC(globalObject: JSC.C.JSContextRef, temp_allocator: std
single_external[0] = std.fmt.allocPrint(allocator, "{}", .{external}) catch unreachable;
transpiler.transform.external = single_external;
} else if (toplevel_type.isArray()) {
- const count = external.getLengthOfArray(globalThis);
+ const count = external.getLength(globalThis);
if (count == 0) break :external;
var externals = allocator.alloc(string, count) catch unreachable;
@@ -606,7 +606,7 @@ fn transformOptionsFromJSC(globalObject: JSC.C.JSContextRef, temp_allocator: std
var length_iter = iter;
while (length_iter.next()) |value| {
if (value.isString()) {
- const length = @truncate(u32, value.getLengthOfArray(globalThis));
+ const length = @truncate(u32, value.getLength(globalThis));
string_count += @as(u32, @boolToInt(length > 0));
total_name_buf_len += length;
}
@@ -679,7 +679,7 @@ fn transformOptionsFromJSC(globalObject: JSC.C.JSContextRef, temp_allocator: std
continue;
}
- if (value.isObject() and value.getLengthOfArray(globalObject) == 2) {
+ if (value.isObject() and value.getLength(globalObject) == 2) {
const replacementValue = JSC.JSObject.getIndex(value, globalThis, 1);
if (exportReplacementValue(replacementValue, globalThis)) |to_replace| {
const replacementKey = JSC.JSObject.getIndex(value, globalThis, 0);
diff --git a/src/bun.js/api/bun.zig b/src/bun.js/api/bun.zig
index 298c41e63..f4900d0d2 100644
--- a/src/bun.js/api/bun.zig
+++ b/src/bun.js/api/bun.zig
@@ -2933,7 +2933,7 @@ pub const Timer = struct {
args_buf[0] = arguments;
args = args_buf[0..1];
} else {
- const count = arguments.getLengthOfArray(globalThis);
+ const count = arguments.getLength(globalThis);
if (count > 0) {
if (count > args_buf.len) {
args = bun.default_allocator.alloc(JSC.JSValue, count) catch unreachable;
diff --git a/src/bun.js/api/filesystem_router.zig b/src/bun.js/api/filesystem_router.zig
index ec95ec373..d926ca7b3 100644
--- a/src/bun.js/api/filesystem_router.zig
+++ b/src/bun.js/api/filesystem_router.zig
@@ -233,7 +233,7 @@ pub const FileSystemRouter = struct {
globalThis.allocator().destroy(arena);
return null;
}
- if (val.getLengthOfArray(globalThis) == 0) continue;
+ if (val.getLength(globalThis) == 0) continue;
extensions.appendAssumeCapacity((val.toSlice(globalThis, allocator).clone(allocator) catch unreachable).slice()[1..]);
}
}
diff --git a/src/bun.js/api/server.zig b/src/bun.js/api/server.zig
index bb48f5786..9af44deba 100644
--- a/src/bun.js/api/server.zig
+++ b/src/bun.js/api/server.zig
@@ -288,7 +288,7 @@ pub const ServerConfig = struct {
if (obj.getTruthy(global, "key")) |js_obj| {
if (js_obj.jsType().isArray()) {
- const count = js_obj.getLengthOfArray(global);
+ const count = js_obj.getLength(global);
if (count > 0) {
const native_array = bun.default_allocator.alloc([*c]const u8, count) catch unreachable;
@@ -389,7 +389,7 @@ pub const ServerConfig = struct {
if (obj.getTruthy(global, "cert")) |js_obj| {
if (js_obj.jsType().isArray()) {
- const count = js_obj.getLengthOfArray(global);
+ const count = js_obj.getLength(global);
if (count > 0) {
const native_array = bun.default_allocator.alloc([*c]const u8, count) catch unreachable;
@@ -504,7 +504,7 @@ pub const ServerConfig = struct {
if (obj.getTruthy(global, "ca")) |js_obj| {
if (js_obj.jsType().isArray()) {
- const count = js_obj.getLengthOfArray(global);
+ const count = js_obj.getLength(global);
if (count > 0) {
const native_array = bun.default_allocator.alloc([*c]const u8, count) catch unreachable;
diff --git a/src/bun.js/base.zig b/src/bun.js/base.zig
index 66c83dd5f..9b9cacbe7 100644
--- a/src/bun.js/base.zig
+++ b/src/bun.js/base.zig
@@ -2202,7 +2202,6 @@ pub const JSPrivateDataPtr = TaggedPointerUnion(.{
Comment,
DebugServer,
DebugSSLServer,
- DescribeScope,
DocEnd,
DocType,
Element,
diff --git a/src/bun.js/bindings/URLSearchParams.h b/src/bun.js/bindings/URLSearchParams.h
index 486098adc..7f9926a3a 100644
--- a/src/bun.js/bindings/URLSearchParams.h
+++ b/src/bun.js/bindings/URLSearchParams.h
@@ -52,6 +52,7 @@ public:
String toString() const;
void updateFromAssociatedURL();
void sort();
+ size_t size() const { return m_pairs.size(); }
class Iterator {
public:
diff --git a/src/bun.js/bindings/ZigGeneratedClasses.h b/src/bun.js/bindings/ZigGeneratedClasses.h
index cf5446a1a..a27d2bae9 100644
--- a/src/bun.js/bindings/ZigGeneratedClasses.h
+++ b/src/bun.js/bindings/ZigGeneratedClasses.h
@@ -56,6 +56,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSBlob(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
@@ -112,6 +114,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSBuildArtifact(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
@@ -173,6 +177,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSBuildMessage(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
@@ -231,6 +237,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSCryptoHasher(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
@@ -288,6 +296,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSDirent(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
@@ -344,6 +354,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSExpect(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
@@ -401,6 +413,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSExpectAny(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
@@ -457,6 +471,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSFileSystemRouter(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
@@ -515,6 +531,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSListener(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
@@ -572,6 +590,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSMD4(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
@@ -622,6 +642,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSMD5(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
@@ -672,6 +694,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSMatchedRoute(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
@@ -734,6 +758,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSNodeJSFS(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
@@ -784,6 +810,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSRequest(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
@@ -843,6 +871,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSResolveMessage(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
@@ -904,6 +934,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSResponse(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
@@ -963,6 +995,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSSHA1(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
@@ -1013,6 +1047,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSSHA224(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
@@ -1063,6 +1099,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSSHA256(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
@@ -1113,6 +1151,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSSHA384(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
@@ -1163,6 +1203,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSSHA512(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
@@ -1213,6 +1255,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSSHA512_256(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
@@ -1263,6 +1307,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSServerWebSocket(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
@@ -1320,6 +1366,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSStats(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
@@ -1378,6 +1426,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSSubprocess(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
@@ -1463,6 +1513,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSTCPSocket(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
@@ -1547,6 +1599,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSTLSSocket(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
@@ -1631,6 +1685,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSTextDecoder(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
@@ -1687,6 +1743,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSTimeout(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
@@ -1744,6 +1802,8 @@ public:
void* m_ctx { nullptr };
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
+
JSTranspiler(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp
index 92568f1a9..f0ff9cf16 100644
--- a/src/bun.js/bindings/ZigGlobalObject.cpp
+++ b/src/bun.js/bindings/ZigGlobalObject.cpp
@@ -2041,6 +2041,17 @@ JSC_DEFINE_HOST_FUNCTION(functionConcatTypedArraysFromIterator, (JSGlobalObject
return flattenArrayOfBuffersIntoArrayBuffer(globalObject, iter->getDirect(vm, vm.propertyNames->value));
}
+extern "C" JSC__JSValue Bun__Jest__createTestModuleObject(JSC::JSGlobalObject*);
+extern "C" JSC__JSValue Bun__Jest__createTestPreloadObject(JSC::JSGlobalObject*);
+extern "C" JSC__JSValue Bun__Jest__testPreloadObject(Zig::GlobalObject* globalObject)
+{
+ return JSValue::encode(globalObject->lazyPreloadTestModuleObject());
+}
+extern "C" JSC__JSValue Bun__Jest__testModuleObject(Zig::GlobalObject* globalObject)
+{
+ return JSValue::encode(globalObject->lazyTestModuleObject());
+}
+
static inline JSC__JSValue ZigGlobalObject__readableStreamToArrayBufferBody(Zig::GlobalObject* globalObject, JSC__JSValue readableStreamValue);
static inline JSC__JSValue ZigGlobalObject__readableStreamToArrayBufferBody(Zig::GlobalObject* globalObject, JSC__JSValue readableStreamValue)
{
@@ -2578,6 +2589,24 @@ void GlobalObject::finishCreation(VM& vm)
init.set(result.toObject(globalObject));
});
+ m_lazyTestModuleObject.initLater(
+ [](const Initializer<JSObject>& init) {
+ JSC::VM& vm = init.vm;
+ JSC::JSGlobalObject* globalObject = init.owner;
+
+ JSValue result = JSValue::decode(Bun__Jest__createTestModuleObject(globalObject));
+ init.set(result.toObject(globalObject));
+ });
+
+ m_lazyPreloadTestModuleObject.initLater(
+ [](const Initializer<JSObject>& init) {
+ JSC::VM& vm = init.vm;
+ JSC::JSGlobalObject* globalObject = init.owner;
+
+ JSValue result = JSValue::decode(Bun__Jest__createTestPreloadObject(globalObject));
+ init.set(result.toObject(globalObject));
+ });
+
// Change prototype from null to object for synthetic modules.
m_moduleNamespaceObjectStructure.initLater(
[](const Initializer<Structure>& init) {
@@ -3767,7 +3796,8 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor)
thisObject->m_lazyRequireCacheObject.visit(visitor);
thisObject->m_vmModuleContextMap.visit(visitor);
thisObject->m_bunSleepThenCallback.visit(visitor);
-
+ thisObject->m_lazyTestModuleObject.visit(visitor);
+ thisObject->m_lazyPreloadTestModuleObject.visit(visitor);
thisObject->m_cachedGlobalObjectStructure.visit(visitor);
thisObject->m_cachedGlobalProxyStructure.visit(visitor);
diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h
index c7c792a2c..67c3196c7 100644
--- a/src/bun.js/bindings/ZigGlobalObject.h
+++ b/src/bun.js/bindings/ZigGlobalObject.h
@@ -258,6 +258,8 @@ public:
Structure* globalObjectStructure() { return m_cachedGlobalObjectStructure.getInitializedOnMainThread(this); }
Structure* globalProxyStructure() { return m_cachedGlobalProxyStructure.getInitializedOnMainThread(this); }
+ JSObject* lazyTestModuleObject() { return m_lazyTestModuleObject.getInitializedOnMainThread(this); }
+ JSObject* lazyPreloadTestModuleObject() { return m_lazyPreloadTestModuleObject.getInitializedOnMainThread(this); }
JSWeakMap* vmModuleContextMap() { return m_vmModuleContextMap.getInitializedOnMainThread(this); }
@@ -469,6 +471,8 @@ private:
LazyProperty<JSGlobalObject, JSObject> m_dnsObject;
LazyProperty<JSGlobalObject, JSWeakMap> m_vmModuleContextMap;
LazyProperty<JSGlobalObject, JSObject> m_lazyRequireCacheObject;
+ LazyProperty<JSGlobalObject, JSObject> m_lazyTestModuleObject;
+ LazyProperty<JSGlobalObject, JSObject> m_lazyPreloadTestModuleObject;
LazyProperty<JSGlobalObject, JSFunction> m_bunSleepThenCallback;
LazyProperty<JSGlobalObject, Structure> m_cachedGlobalObjectStructure;
diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp
index a72fba992..1e6da1e71 100644
--- a/src/bun.js/bindings/bindings.cpp
+++ b/src/bun.js/bindings/bindings.cpp
@@ -92,6 +92,9 @@
#include "ZigGeneratedClasses.h"
#include "JavaScriptCore/JSMapInlines.h"
+#include <JavaScriptCore/JSWeakMap.h>
+#include "JSURLSearchParams.h"
+
template<typename UWSResponse>
static void copyToUWS(WebCore::FetchHeaders* headers, UWSResponse* res)
{
@@ -1118,11 +1121,78 @@ JSC__JSValue JSC__JSValue__createEmptyObject(JSC__JSGlobalObject* globalObject,
JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), initialCapacity));
}
-uint64_t JSC__JSValue__getLengthOfArray(JSC__JSValue value, JSC__JSGlobalObject* globalObject)
+extern "C" uint64_t Bun__Blob__getSizeForBindings(void* blob);
+
+double JSC__JSValue__getLengthIfPropertyExistsInternal(JSC__JSValue value, JSC__JSGlobalObject* globalObject)
{
JSC::JSValue jsValue = JSC::JSValue::decode(value);
- JSC::JSObject* object = jsValue.toObject(globalObject);
- return JSC::toLength(globalObject, object);
+ if (!jsValue || !jsValue.isCell())
+ return 0;
+ JSCell* cell = jsValue.asCell();
+ JSC::JSType type = cell->type();
+
+ switch (type) {
+ case JSC::JSType::StringType:
+ return static_cast<double>(jsValue.toString(globalObject)->length());
+ case JSC::JSType::ArrayType:
+ return static_cast<double>(jsCast<JSC::JSArray*>(cell)->length());
+
+ case JSC::JSType::Int8ArrayType:
+ case JSC::JSType::Uint8ArrayType:
+ case JSC::JSType::Uint8ClampedArrayType:
+ case JSC::JSType::Int16ArrayType:
+ case JSC::JSType::Uint16ArrayType:
+ case JSC::JSType::Int32ArrayType:
+ case JSC::JSType::Uint32ArrayType:
+ case JSC::JSType::Float32ArrayType:
+ case JSC::JSType::Float64ArrayType:
+ case JSC::JSType::BigInt64ArrayType:
+ case JSC::JSType::BigUint64ArrayType:
+ return static_cast<double>(jsCast<JSC::JSArrayBufferView*>(cell)->length());
+
+ case JSC::JSType::JSMapType:
+ return static_cast<double>(jsCast<JSC::JSMap*>(cell)->size());
+
+ case JSC::JSType::JSSetType:
+ return static_cast<double>(jsCast<JSC::JSSet*>(cell)->size());
+
+ case JSC::JSType::JSWeakMapType:
+ return static_cast<double>(jsCast<JSC::JSWeakMap*>(cell)->size());
+
+ case JSC::JSType::ArrayBufferType: {
+ auto* arrayBuffer = jsCast<JSC::JSArrayBuffer*>(cell);
+ if (auto* impl = arrayBuffer->impl()) {
+ return static_cast<double>(impl->byteLength());
+ }
+
+ return 0;
+ }
+
+ case JSC::JSType(JSDOMWrapperType): {
+ if (auto* headers = jsDynamicCast<WebCore::JSFetchHeaders*>(cell))
+ return static_cast<double>(jsCast<WebCore::JSFetchHeaders*>(cell)->wrapped().size());
+
+ if (auto* blob = jsDynamicCast<WebCore::JSBlob*>(cell)) {
+ uint64_t size = Bun__Blob__getSizeForBindings(blob->wrapped());
+ if (size == std::numeric_limits<uint64_t>::max())
+ return std::numeric_limits<double>::max();
+ return static_cast<double>(size);
+ }
+ }
+
+ default: {
+
+ if (auto* object = jsDynamicCast<JSObject*>(cell)) {
+ auto scope = DECLARE_THROW_SCOPE(globalObject->vm());
+ if (JSValue lengthValue = object->getIfPropertyExists(globalObject, globalObject->vm().propertyNames->length)) {
+ RETURN_IF_EXCEPTION(scope, 0);
+ RELEASE_AND_RETURN(scope, lengthValue.toNumber(globalObject));
+ }
+ }
+ }
+ }
+
+ return std::numeric_limits<double>::infinity();
}
void JSC__JSObject__putRecord(JSC__JSObject* object, JSC__JSGlobalObject* global, ZigString* key,
diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig
index 038a81b1e..42f0c5344 100644
--- a/src/bun.js/bindings/bindings.zig
+++ b/src/bun.js/bindings/bindings.zig
@@ -2401,7 +2401,7 @@ pub const JSGlobalObject = extern struct {
) JSC.JSValue {
return JSC.toTypeErrorWithCode(
"NOT_ENOUGH_ARGUMENTS",
- "Not enough arguments to '" ++ name_ ++ "''. Expected {d}, got {d}.",
+ "Not enough arguments to '" ++ name_ ++ "'. Expected {d}, got {d}.",
.{ expected, got },
this,
);
@@ -2778,7 +2778,7 @@ pub const JSArrayIterator = struct {
return .{
.array = value,
.global = global,
- .len = @truncate(u32, value.getLengthOfArray(global)),
+ .len = @truncate(u32, value.getLength(global)),
};
}
@@ -4140,7 +4140,7 @@ pub const JSValue = enum(JSValueReprInt) {
return error.JSError;
}
- if (prop.getLengthOfArray(globalThis) == 0) {
+ if (prop.getLength(globalThis) == 0) {
return null;
}
@@ -4366,8 +4366,53 @@ pub const JSValue = enum(JSValueReprInt) {
return @intCast(u32, @max(this.toInt32(), 0));
}
- pub fn getLengthOfArray(this: JSValue, globalThis: *JSGlobalObject) u64 {
- return cppFn("getLengthOfArray", .{
+ /// This function supports:
+ /// - Array, DerivedArray & friends
+ /// - String, DerivedString & friends
+ /// - TypedArray
+ /// - Map (size)
+ /// - WeakMap (size)
+ /// - Set (size)
+ /// - WeakSet (size)
+ /// - ArrayBuffer (byteLength)
+ /// - anything with a .length property returning a number
+ ///
+ /// If the "length" property does not exist, this function will return 0.
+ pub fn getLength(this: JSValue, globalThis: *JSGlobalObject) u64 {
+ const len = this.getLengthIfPropertyExistsInternal(globalThis);
+ if (len == std.math.f64_max) {
+ return 0;
+ }
+
+ return @floatToInt(u64, @max(len, 0));
+ }
+
+ /// This function supports:
+ /// - Array, DerivedArray & friends
+ /// - String, DerivedString & friends
+ /// - TypedArray
+ /// - Map (size)
+ /// - WeakMap (size)
+ /// - Set (size)
+ /// - WeakSet (size)
+ /// - ArrayBuffer (byteLength)
+ /// - anything with a .length property returning a number
+ ///
+ /// If the "length" property does not exist, this function will return null.
+ pub fn tryGetLength(this: JSValue, globalThis: *JSGlobalObject) ?f64 {
+ const len = this.getLengthIfPropertyExistsInternal(globalThis);
+ if (len == std.math.f64_max) {
+ return null;
+ }
+
+ return @floatToInt(u64, @max(len, 0));
+ }
+
+ /// Do not use this directly!
+ ///
+ /// If the property does not exist, this function will return max(f64) instead of 0.
+ pub fn getLengthIfPropertyExistsInternal(this: JSValue, globalThis: *JSGlobalObject) f64 {
+ return cppFn("getLengthIfPropertyExistsInternal", .{
this,
globalThis,
});
@@ -4479,7 +4524,7 @@ pub const JSValue = enum(JSValueReprInt) {
"getIfExists",
"getIfPropertyExistsFromPath",
"getIfPropertyExistsImpl",
- "getLengthOfArray",
+ "getLengthIfPropertyExistsInternal",
"getNameProperty",
"getPropertyByPropertyName",
"getPropertyNames",
diff --git a/src/bun.js/bindings/exports.zig b/src/bun.js/bindings/exports.zig
index 666adf595..bfea93306 100644
--- a/src/bun.js/bindings/exports.zig
+++ b/src/bun.js/bindings/exports.zig
@@ -2061,7 +2061,7 @@ pub const ZigConsoleClient = struct {
writer.print(comptime Output.prettyFmt("<cyan>[Getter]<r>", enable_ansi_colors), .{});
},
.Array => {
- const len = @truncate(u32, value.getLengthOfArray(this.globalThis));
+ const len = @truncate(u32, value.getLength(this.globalThis));
if (len == 0) {
writer.writeAll("[]");
this.addForNewLine(2);
@@ -2573,7 +2573,7 @@ pub const ZigConsoleClient = struct {
this.writeIndent(Writer, writer_) catch unreachable;
},
.Array => {
- const length = children.getLengthOfArray(this.globalThis);
+ const length = children.getLength(this.globalThis);
if (length == 0) break :print_children;
writer.writeAll(">\n");
diff --git a/src/bun.js/bindings/headers-handwritten.h b/src/bun.js/bindings/headers-handwritten.h
index 24a540320..673b366d1 100644
--- a/src/bun.js/bindings/headers-handwritten.h
+++ b/src/bun.js/bindings/headers-handwritten.h
@@ -2,6 +2,8 @@
typedef uint16_t ZigErrorCode;
typedef struct VirtualMachine VirtualMachine;
+// exists to make headers.h happy
+typedef struct CppWebSocket CppWebSocket;
typedef struct ZigString {
const unsigned char* ptr;
diff --git a/src/bun.js/bindings/headers.h b/src/bun.js/bindings/headers.h
index c7612938c..5034b7652 100644
--- a/src/bun.js/bindings/headers.h
+++ b/src/bun.js/bindings/headers.h
@@ -330,7 +330,7 @@ CPP_DECL void JSC__JSValue__getClassName(JSC__JSValue JSValue0, JSC__JSGlobalObj
CPP_DECL JSC__JSValue JSC__JSValue__getErrorsProperty(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1);
CPP_DECL JSC__JSValue JSC__JSValue__getIfPropertyExistsFromPath(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, JSC__JSValue JSValue2);
CPP_DECL JSC__JSValue JSC__JSValue__getIfPropertyExistsImpl(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, const unsigned char* arg2, uint32_t arg3);
-CPP_DECL uint64_t JSC__JSValue__getLengthOfArray(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1);
+CPP_DECL double JSC__JSValue__getLengthIfPropertyExistsInternal(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1);
CPP_DECL void JSC__JSValue__getNameProperty(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, ZigString* arg2);
CPP_DECL JSC__JSValue JSC__JSValue__getPrototype(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1);
CPP_DECL void JSC__JSValue__getSymbolDescription(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, ZigString* arg2);
@@ -708,7 +708,7 @@ ZIG_DECL JSC__JSValue FileSink__write(JSC__JSGlobalObject* arg0, JSC__CallFrame*
#ifdef __cplusplus
ZIG_DECL void Bun__WebSocketHTTPClient__cancel(WebSocketHTTPClient* arg0);
-ZIG_DECL WebSocketHTTPClient* Bun__WebSocketHTTPClient__connect(JSC__JSGlobalObject* arg0, void* arg1, void* arg2, const ZigString* arg3, uint16_t arg4, const ZigString* arg5, const ZigString* arg6, ZigString* arg7, ZigString* arg8, size_t arg9);
+ZIG_DECL WebSocketHTTPClient* Bun__WebSocketHTTPClient__connect(JSC__JSGlobalObject* arg0, void* arg1, CppWebSocket* arg2, const ZigString* arg3, uint16_t arg4, const ZigString* arg5, const ZigString* arg6, ZigString* arg7, ZigString* arg8, size_t arg9);
ZIG_DECL void Bun__WebSocketHTTPClient__register(JSC__JSGlobalObject* arg0, void* arg1, void* arg2);
#endif
@@ -716,7 +716,7 @@ ZIG_DECL void Bun__WebSocketHTTPClient__register(JSC__JSGlobalObject* arg0, void
#ifdef __cplusplus
ZIG_DECL void Bun__WebSocketHTTPSClient__cancel(WebSocketHTTPSClient* arg0);
-ZIG_DECL WebSocketHTTPSClient* Bun__WebSocketHTTPSClient__connect(JSC__JSGlobalObject* arg0, void* arg1, void* arg2, const ZigString* arg3, uint16_t arg4, const ZigString* arg5, const ZigString* arg6, ZigString* arg7, ZigString* arg8, size_t arg9);
+ZIG_DECL WebSocketHTTPSClient* Bun__WebSocketHTTPSClient__connect(JSC__JSGlobalObject* arg0, void* arg1, CppWebSocket* arg2, const ZigString* arg3, uint16_t arg4, const ZigString* arg5, const ZigString* arg6, ZigString* arg7, ZigString* arg8, size_t arg9);
ZIG_DECL void Bun__WebSocketHTTPSClient__register(JSC__JSGlobalObject* arg0, void* arg1, void* arg2);
#endif
@@ -725,7 +725,7 @@ ZIG_DECL void Bun__WebSocketHTTPSClient__register(JSC__JSGlobalObject* arg0, voi
ZIG_DECL void Bun__WebSocketClient__close(WebSocketClient* arg0, uint16_t arg1, const ZigString* arg2);
ZIG_DECL void Bun__WebSocketClient__finalize(WebSocketClient* arg0);
-ZIG_DECL void* Bun__WebSocketClient__init(void* arg0, void* arg1, void* arg2, JSC__JSGlobalObject* arg3, unsigned char* arg4, size_t arg5);
+ZIG_DECL void* Bun__WebSocketClient__init(CppWebSocket* arg0, void* arg1, void* arg2, JSC__JSGlobalObject* arg3, unsigned char* arg4, size_t arg5);
ZIG_DECL void Bun__WebSocketClient__register(JSC__JSGlobalObject* arg0, void* arg1, void* arg2);
ZIG_DECL void Bun__WebSocketClient__writeBinaryData(WebSocketClient* arg0, const unsigned char* arg1, size_t arg2);
ZIG_DECL void Bun__WebSocketClient__writeString(WebSocketClient* arg0, const ZigString* arg1);
@@ -736,7 +736,7 @@ ZIG_DECL void Bun__WebSocketClient__writeString(WebSocketClient* arg0, const Zig
ZIG_DECL void Bun__WebSocketClientTLS__close(WebSocketClientTLS* arg0, uint16_t arg1, const ZigString* arg2);
ZIG_DECL void Bun__WebSocketClientTLS__finalize(WebSocketClientTLS* arg0);
-ZIG_DECL void* Bun__WebSocketClientTLS__init(void* arg0, void* arg1, void* arg2, JSC__JSGlobalObject* arg3, unsigned char* arg4, size_t arg5);
+ZIG_DECL void* Bun__WebSocketClientTLS__init(CppWebSocket* arg0, void* arg1, void* arg2, JSC__JSGlobalObject* arg3, unsigned char* arg4, size_t arg5);
ZIG_DECL void Bun__WebSocketClientTLS__register(JSC__JSGlobalObject* arg0, void* arg1, void* arg2);
ZIG_DECL void Bun__WebSocketClientTLS__writeBinaryData(WebSocketClientTLS* arg0, const unsigned char* arg1, size_t arg2);
ZIG_DECL void Bun__WebSocketClientTLS__writeString(WebSocketClientTLS* arg0, const ZigString* arg1);
diff --git a/src/bun.js/bindings/headers.zig b/src/bun.js/bindings/headers.zig
index 3f943ab27..b1f1a1974 100644
--- a/src/bun.js/bindings/headers.zig
+++ b/src/bun.js/bindings/headers.zig
@@ -231,7 +231,7 @@ pub extern fn JSC__JSValue__getClassName(JSValue0: JSC__JSValue, arg1: *bindings
pub extern fn JSC__JSValue__getErrorsProperty(JSValue0: JSC__JSValue, arg1: *bindings.JSGlobalObject) JSC__JSValue;
pub extern fn JSC__JSValue__getIfPropertyExistsFromPath(JSValue0: JSC__JSValue, arg1: *bindings.JSGlobalObject, JSValue2: JSC__JSValue) JSC__JSValue;
pub extern fn JSC__JSValue__getIfPropertyExistsImpl(JSValue0: JSC__JSValue, arg1: *bindings.JSGlobalObject, arg2: [*c]const u8, arg3: u32) JSC__JSValue;
-pub extern fn JSC__JSValue__getLengthOfArray(JSValue0: JSC__JSValue, arg1: *bindings.JSGlobalObject) u64;
+pub extern fn JSC__JSValue__getLengthIfPropertyExistsInternal(JSValue0: JSC__JSValue, arg1: *bindings.JSGlobalObject) f64;
pub extern fn JSC__JSValue__getNameProperty(JSValue0: JSC__JSValue, arg1: *bindings.JSGlobalObject, arg2: [*c]ZigString) void;
pub extern fn JSC__JSValue__getPrototype(JSValue0: JSC__JSValue, arg1: *bindings.JSGlobalObject) JSC__JSValue;
pub extern fn JSC__JSValue__getSymbolDescription(JSValue0: JSC__JSValue, arg1: *bindings.JSGlobalObject, arg2: [*c]ZigString) void;
diff --git a/src/bun.js/bindings/webcore/JSDOMFormData.cpp b/src/bun.js/bindings/webcore/JSDOMFormData.cpp
index e221288c1..181b20e45 100644
--- a/src/bun.js/bindings/webcore/JSDOMFormData.cpp
+++ b/src/bun.js/bindings/webcore/JSDOMFormData.cpp
@@ -108,6 +108,18 @@ static JSC_DECLARE_HOST_FUNCTION(jsDOMFormDataPrototypeFunction_toJSON);
static JSC_DECLARE_CUSTOM_GETTER(jsDOMFormDataConstructor);
+JSC_DEFINE_CUSTOM_GETTER(jsDOMFormDataPrototype_getLength, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName))
+{
+ VM& vm = JSC::getVM(lexicalGlobalObject);
+ auto* thisObject = jsDynamicCast<JSDOMFormData*>(JSValue::decode(thisValue));
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ if (UNLIKELY(!thisObject))
+ return throwVMTypeError(lexicalGlobalObject, throwScope);
+
+ size_t length = thisObject->wrapped().count();
+ return JSValue::encode(jsNumber(length));
+}
+
class JSDOMFormDataPrototype final : public JSC::JSNonFinalObject {
public:
using Base = JSC::JSNonFinalObject;
@@ -196,6 +208,7 @@ static const HashTableValue JSDOMFormDataPrototypeTableValues[] = {
{ "values"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsDOMFormDataPrototypeFunction_values, 0 } },
{ "forEach"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsDOMFormDataPrototypeFunction_forEach, 1 } },
{ "toJSON"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::NativeFunctionType, jsDOMFormDataPrototypeFunction_toJSON, 1 } },
+ { "length"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::DontEnum), NoIntrinsic, { HashTableValue::GetterSetterType, jsDOMFormDataPrototype_getLength, 0 } }
};
const ClassInfo JSDOMFormDataPrototype::s_info = { "FormData"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSDOMFormDataPrototype) };
diff --git a/src/bun.js/bindings/webcore/JSFetchHeaders.h b/src/bun.js/bindings/webcore/JSFetchHeaders.h
index 74fe24601..772adc546 100644
--- a/src/bun.js/bindings/webcore/JSFetchHeaders.h
+++ b/src/bun.js/bindings/webcore/JSFetchHeaders.h
@@ -45,7 +45,7 @@ public:
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
{
- return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info(), JSC::NonArray);
+ return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::JSType(JSDOMWrapperType), StructureFlags), info(), JSC::NonArray);
}
static JSC::JSValue getConstructor(JSC::VM&, const JSC::JSGlobalObject*);
diff --git a/src/bun.js/bindings/webcore/JSURLSearchParams.cpp b/src/bun.js/bindings/webcore/JSURLSearchParams.cpp
index 5d3ed056a..d80304684 100644
--- a/src/bun.js/bindings/webcore/JSURLSearchParams.cpp
+++ b/src/bun.js/bindings/webcore/JSURLSearchParams.cpp
@@ -152,6 +152,16 @@ template<> void JSURLSearchParamsDOMConstructor::initializeProperties(VM& vm, JS
putDirect(vm, vm.propertyNames->prototype, JSURLSearchParams::prototype(vm, globalObject), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete);
}
+JSC_DEFINE_CUSTOM_GETTER(jsURLSearchParamsPrototype_getLength, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName))
+{
+ VM& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ auto* thisObject = jsDynamicCast<JSURLSearchParams*>(JSValue::decode(thisValue));
+ if (UNLIKELY(!thisObject))
+ return throwVMTypeError(lexicalGlobalObject, throwScope);
+ return JSValue::encode(jsNumber(thisObject->wrapped().size()));
+}
+
/* Hash table for prototype */
static const HashTableValue JSURLSearchParamsPrototypeTableValues[] = {
@@ -168,6 +178,7 @@ static const HashTableValue JSURLSearchParamsPrototypeTableValues[] = {
{ "values"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsURLSearchParamsPrototypeFunction_values, 0 } },
{ "forEach"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsURLSearchParamsPrototypeFunction_forEach, 1 } },
{ "toString"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsURLSearchParamsPrototypeFunction_toString, 0 } },
+ { "length"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::DontEnum), NoIntrinsic, { HashTableValue::GetterSetterType, jsURLSearchParamsPrototype_getLength, 0 } },
};
const ClassInfo JSURLSearchParamsPrototype::s_info = { "URLSearchParams"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSURLSearchParamsPrototype) };
diff --git a/src/bun.js/bindings/webcore/WebSocket.cpp b/src/bun.js/bindings/webcore/WebSocket.cpp
index 4af556a7b..a93bb8726 100644
--- a/src/bun.js/bindings/webcore/WebSocket.cpp
+++ b/src/bun.js/bindings/webcore/WebSocket.cpp
@@ -169,19 +169,19 @@ WebSocket::~WebSocket()
if (m_upgradeClient != nullptr) {
void* upgradeClient = m_upgradeClient;
if (m_isSecure) {
- Bun__WebSocketHTTPSClient__cancel(upgradeClient);
+ Bun__WebSocketHTTPSClient__cancel(reinterpret_cast<void*>(upgradeClient));
} else {
- Bun__WebSocketHTTPClient__cancel(upgradeClient);
+ Bun__WebSocketHTTPClient__cancel(reinterpret_cast<void*>(upgradeClient));
}
}
switch (m_connectedWebSocketKind) {
case ConnectedWebSocketKind::Client: {
- Bun__WebSocketClient__finalize(this->m_connectedWebSocket.client);
+ Bun__WebSocketClient__finalize(reinterpret_cast<void*>(this->m_connectedWebSocket.client));
break;
}
case ConnectedWebSocketKind::ClientSSL: {
- Bun__WebSocketClientTLS__finalize(this->m_connectedWebSocket.clientSSL);
+ Bun__WebSocketClientTLS__finalize(reinterpret_cast<void*>(this->m_connectedWebSocket.clientSSL));
break;
}
// case ConnectedWebSocketKind::Server: {
@@ -409,11 +409,11 @@ ExceptionOr<void> WebSocket::connect(const String& url, const Vector<String>& pr
if (is_secure) {
us_socket_context_t* ctx = scriptExecutionContext()->webSocketContext<true>();
RELEASE_ASSERT(ctx);
- this->m_upgradeClient = Bun__WebSocketHTTPSClient__connect(scriptExecutionContext()->jsGlobalObject(), ctx, this, &host, port, &path, &clientProtocolString, headerNames.data(), headerValues.data(), headerNames.size());
+ this->m_upgradeClient = Bun__WebSocketHTTPSClient__connect(scriptExecutionContext()->jsGlobalObject(), ctx, reinterpret_cast<CppWebSocket*>(this), &host, port, &path, &clientProtocolString, headerNames.data(), headerValues.data(), headerNames.size());
} else {
us_socket_context_t* ctx = scriptExecutionContext()->webSocketContext<false>();
RELEASE_ASSERT(ctx);
- this->m_upgradeClient = Bun__WebSocketHTTPClient__connect(scriptExecutionContext()->jsGlobalObject(), ctx, this, &host, port, &path, &clientProtocolString, headerNames.data(), headerValues.data(), headerNames.size());
+ this->m_upgradeClient = Bun__WebSocketHTTPClient__connect(scriptExecutionContext()->jsGlobalObject(), ctx, reinterpret_cast<CppWebSocket*>(this), &host, port, &path, &clientProtocolString, headerNames.data(), headerValues.data(), headerNames.size());
}
headerValues.clear();
@@ -1022,11 +1022,11 @@ void WebSocket::didConnect(us_socket_t* socket, char* bufferedData, size_t buffe
this->m_upgradeClient = nullptr;
if (m_isSecure) {
us_socket_context_t* ctx = (us_socket_context_t*)this->scriptExecutionContext()->connectedWebSocketContext<true, false>();
- this->m_connectedWebSocket.clientSSL = Bun__WebSocketClientTLS__init(this, socket, ctx, this->scriptExecutionContext()->jsGlobalObject(), reinterpret_cast<unsigned char*>(bufferedData), bufferedDataSize);
+ this->m_connectedWebSocket.clientSSL = Bun__WebSocketClientTLS__init(reinterpret_cast<CppWebSocket*>(this), socket, ctx, this->scriptExecutionContext()->jsGlobalObject(), reinterpret_cast<unsigned char*>(bufferedData), bufferedDataSize);
this->m_connectedWebSocketKind = ConnectedWebSocketKind::ClientSSL;
} else {
us_socket_context_t* ctx = (us_socket_context_t*)this->scriptExecutionContext()->connectedWebSocketContext<false, false>();
- this->m_connectedWebSocket.client = Bun__WebSocketClient__init(this, socket, ctx, this->scriptExecutionContext()->jsGlobalObject(), reinterpret_cast<unsigned char*>(bufferedData), bufferedDataSize);
+ this->m_connectedWebSocket.client = Bun__WebSocketClient__init(reinterpret_cast<CppWebSocket*>(this), socket, ctx, this->scriptExecutionContext()->jsGlobalObject(), reinterpret_cast<unsigned char*>(bufferedData), bufferedDataSize);
this->m_connectedWebSocketKind = ConnectedWebSocketKind::Client;
}
diff --git a/src/bun.js/node/node_os.zig b/src/bun.js/node/node_os.zig
index 7b292ae36..4b37640cf 100644
--- a/src/bun.js/node/node_os.zig
+++ b/src/bun.js/node/node_os.zig
@@ -531,7 +531,7 @@ pub const Os = struct {
// Does this entry already exist?
if (ret.get(globalThis, interface_name)) |array| {
// Add this interface entry to the existing array
- const next_index = @intCast(u32, array.getLengthOfArray(globalThis));
+ const next_index = @intCast(u32, array.getLength(globalThis));
array.putIndex(globalThis, next_index, interface);
} else {
// Add it as an array with this interface as an element
diff --git a/src/bun.js/test/jest.zig b/src/bun.js/test/jest.zig
index e22c2a0fd..ad9cf9b5b 100644
--- a/src/bun.js/test/jest.zig
+++ b/src/bun.js/test/jest.zig
@@ -28,12 +28,9 @@ const default_allocator = @import("root").bun.default_allocator;
const FeatureFlags = @import("root").bun.FeatureFlags;
const ArrayBuffer = @import("../base.zig").ArrayBuffer;
const Properties = @import("../base.zig").Properties;
-const NewClass = @import("../base.zig").NewClass;
const d = @import("../base.zig").d;
const castObj = @import("../base.zig").castObj;
const getAllocator = @import("../base.zig").getAllocator;
-const JSPrivateDataPtr = @import("../base.zig").JSPrivateDataPtr;
-const GetJSPrivateData = @import("../base.zig").GetJSPrivateData;
const ZigString = JSC.ZigString;
const JSInternalPromise = JSC.JSInternalPromise;
@@ -49,22 +46,6 @@ const Task = @import("../javascript.zig").Task;
const Fs = @import("../../fs.zig");
const is_bindgen: bool = std.meta.globalOption("bindgen", bool) orelse false;
-fn notImplementedFn(_: *anyopaque, ctx: js.JSContextRef, _: js.JSObjectRef, _: js.JSObjectRef, _: []const js.JSValueRef, exception: js.ExceptionRef) js.JSValueRef {
- JSError(getAllocator(ctx), "Not implemented yet!", .{}, ctx, exception);
- return null;
-}
-
-fn notImplementedProp(
- _: anytype,
- ctx: js.JSContextRef,
- _: js.JSObjectRef,
- _: js.JSStringRef,
- exception: js.ExceptionRef,
-) js.JSValueRef {
- JSError(getAllocator(ctx), "Property not implemented yet!", .{}, ctx, exception);
- return null;
-}
-
pub const DiffFormatter = struct {
received_string: ?string = null,
expected_string: ?string = null,
@@ -837,7 +818,7 @@ pub const Jest = struct {
return .zero;
}
- if (function.getLengthOfArray(globalThis) > 0) {
+ if (function.getLength(globalThis) > 0) {
globalThis.throw("done() callback is not implemented in global hooks yet. Please make your function take no arguments", .{});
return .zero;
}
@@ -852,6 +833,118 @@ pub const Jest = struct {
}.appendGlobalFunctionCallback;
}
+ pub fn Bun__Jest__createTestPreloadObject(globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue {
+ JSC.markBinding(@src());
+
+ var global_hooks_object = JSC.JSValue.createEmptyObject(globalObject, 8);
+ global_hooks_object.ensureStillAlive();
+
+ const notSupportedHereFn = struct {
+ pub fn notSupportedHere(
+ globalThis: *JSC.JSGlobalObject,
+ _: *JSC.CallFrame,
+ ) callconv(.C) JSValue {
+ globalThis.throw("This function can only be used in a test.", .{});
+ return .zero;
+ }
+ }.notSupportedHere;
+ const notSupportedHere = JSC.NewFunction(globalObject, null, 0, notSupportedHereFn, false);
+ notSupportedHere.ensureStillAlive();
+
+ inline for (.{
+ "expect",
+ "describe",
+ "it",
+ "test",
+ }) |name| {
+ global_hooks_object.put(globalObject, ZigString.static(name), notSupportedHere);
+ }
+
+ inline for (.{ "beforeAll", "beforeEach", "afterAll", "afterEach" }) |name| {
+ const function = JSC.NewFunction(globalObject, null, 1, globalHook(name), false);
+ function.ensureStillAlive();
+ global_hooks_object.put(globalObject, ZigString.static(name), function);
+ }
+ return global_hooks_object;
+ }
+
+ pub fn Bun__Jest__createTestModuleObject(globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue {
+ JSC.markBinding(@src());
+
+ const module = JSC.JSValue.createEmptyObject(globalObject, 7);
+
+ const test_fn = JSC.NewFunction(globalObject, ZigString.static("test"), 2, TestScope.call, false);
+ module.put(
+ globalObject,
+ ZigString.static("test"),
+ test_fn,
+ );
+ test_fn.put(
+ globalObject,
+ ZigString.static("todo"),
+ JSC.NewFunction(globalObject, ZigString.static("todo"), 2, TestScope.todo, false),
+ );
+ test_fn.put(
+ globalObject,
+ ZigString.static("skip"),
+ JSC.NewFunction(globalObject, ZigString.static("skip"), 2, TestScope.skip, false),
+ );
+ test_fn.put(
+ globalObject,
+ ZigString.static("only"),
+ JSC.NewFunction(globalObject, ZigString.static("only"), 2, TestScope.only, false),
+ );
+
+ module.put(
+ globalObject,
+ ZigString.static("it"),
+ test_fn,
+ );
+ const describe = JSC.NewFunction(globalObject, ZigString.static("describe"), 2, DescribeScope.describe, false);
+ describe.put(
+ globalObject,
+ ZigString.static("skip"),
+ JSC.NewFunction(globalObject, ZigString.static("skip"), 2, DescribeScope.skip, false),
+ );
+
+ module.put(
+ globalObject,
+ ZigString.static("describe"),
+ describe,
+ );
+
+ module.put(
+ globalObject,
+ ZigString.static("beforeAll"),
+ JSC.NewRuntimeFunction(globalObject, ZigString.static("beforeAll"), 1, DescribeScope.beforeAll, false),
+ );
+ module.put(
+ globalObject,
+ ZigString.static("beforeEach"),
+ JSC.NewRuntimeFunction(globalObject, ZigString.static("beforeEach"), 1, DescribeScope.beforeEach, false),
+ );
+ module.put(
+ globalObject,
+ ZigString.static("afterAll"),
+ JSC.NewRuntimeFunction(globalObject, ZigString.static("afterAll"), 1, DescribeScope.afterAll, false),
+ );
+ module.put(
+ globalObject,
+ ZigString.static("afterEach"),
+ JSC.NewRuntimeFunction(globalObject, ZigString.static("afterEach"), 1, DescribeScope.afterEach, false),
+ );
+ module.put(
+ globalObject,
+ ZigString.static("expect"),
+ Expect.getConstructor(globalObject),
+ );
+
+ return module;
+ }
+
+ extern fn Bun__Jest__testPreloadObject(*JSC.JSGlobalObject) JSC.JSValue;
+ extern fn Bun__Jest__testModuleObject(*JSC.JSGlobalObject) JSC.JSValue;
+
pub fn call(
_: void,
ctx: js.JSContextRef,
@@ -860,6 +953,7 @@ pub const Jest = struct {
arguments_: []const js.JSValueRef,
exception: js.ExceptionRef,
) js.JSValueRef {
+ JSC.markBinding(@src());
var runner_ = runner orelse {
JSError(getAllocator(ctx), "Run \"bun test\" to run a test", .{}, ctx, exception);
return js.JSValueMakeUndefined(ctx);
@@ -880,36 +974,7 @@ pub const Jest = struct {
}
var vm = ctx.bunVM();
if (vm.is_in_preload) {
- var global_hooks_object = JSC.JSValue.createEmptyObject(ctx, 8);
- global_hooks_object.ensureStillAlive();
-
- const notSupportedHereFn = struct {
- pub fn notSupportedHere(
- globalThis: *JSC.JSGlobalObject,
- _: *JSC.CallFrame,
- ) callconv(.C) JSValue {
- globalThis.throw("This function can only be used in a test.", .{});
- return .zero;
- }
- }.notSupportedHere;
- const notSupportedHere = JSC.NewFunction(ctx, null, 0, notSupportedHereFn, false);
- notSupportedHere.ensureStillAlive();
-
- inline for (.{
- "expect",
- "describe",
- "it",
- "test",
- }) |name| {
- global_hooks_object.put(ctx, ZigString.static(name), notSupportedHere);
- }
-
- inline for (.{ "beforeAll", "beforeEach", "afterAll", "afterEach" }) |name| {
- const function = JSC.NewFunction(ctx, null, 1, globalHook(name), false);
- function.ensureStillAlive();
- global_hooks_object.put(ctx, ZigString.static(name), function);
- }
- return global_hooks_object.asObjectRef();
+ return Bun__Jest__testPreloadObject(ctx).asObjectRef();
}
var filepath = Fs.FileSystem.instance.filename_store.append([]const u8, slice) catch unreachable;
@@ -917,7 +982,15 @@ pub const Jest = struct {
var scope = runner_.getOrPutFile(filepath);
DescribeScope.active = scope;
DescribeScope.module = scope;
- return DescribeScope.Class.make(ctx, scope);
+
+ return Bun__Jest__testModuleObject(ctx).asObjectRef();
+ }
+
+ comptime {
+ if (!JSC.is_bindgen) {
+ @export(Bun__Jest__createTestModuleObject, .{ .name = "Bun__Jest__createTestModuleObject" });
+ @export(Bun__Jest__createTestPreloadObject, .{ .name = "Bun__Jest__createTestPreloadObject" });
+ }
}
};
@@ -1229,27 +1302,19 @@ pub const Expect = struct {
const not = this.op.contains(.not);
var pass = false;
- var actual_length: f64 = undefined;
- if (value.jsType() == .String) {
- actual_length = @intToFloat(f64, value.asString().length());
- if (actual_length == expected_length) pass = true;
- } else {
- const length_value: JSValue = value.getIfPropertyExistsImpl(globalObject, "length", "length".len);
+ const actual_length = value.getLengthIfPropertyExistsInternal(globalObject);
- if (length_value.isEmpty()) {
- var fmt = JSC.ZigConsoleClient.Formatter{ .globalThis = globalObject, .quote_strings = true };
- globalObject.throw("Received value does not have a length property: {any}", .{value.toFmt(globalObject, &fmt)});
- return .zero;
- } else if (!length_value.isNumber()) {
- var fmt = JSC.ZigConsoleClient.Formatter{ .globalThis = globalObject, .quote_strings = true };
- globalObject.throw("Received value has non-number length property: {any}", .{length_value.toFmt(globalObject, &fmt)});
- return .zero;
- }
+ if (actual_length == std.math.f64_max) {
+ var fmt = JSC.ZigConsoleClient.Formatter{ .globalThis = globalObject, .quote_strings = true };
+ globalObject.throw("Received value does not have a length property: {any}", .{value.toFmt(globalObject, &fmt)});
+ return .zero;
+ } else if (std.math.isNan(actual_length)) {
+ globalObject.throw("Received value has non-number length property: {}", .{actual_length});
+ return .zero;
+ }
- actual_length = length_value.asNumber();
- if (@round(actual_length) == actual_length) {
- if (actual_length == expected_length) pass = true;
- }
+ if (actual_length == expected_length) {
+ pass = true;
}
if (not) pass = !pass;
@@ -3232,7 +3297,7 @@ pub const Expect = struct {
pub const TestScope = struct {
label: string = "",
parent: *DescribeScope,
- callback: js.JSValueRef,
+ callback: JSC.JSValue,
id: TestRunner.Test.ID = 0,
promise: ?*JSInternalPromise = null,
ran: bool = false,
@@ -3242,81 +3307,59 @@ pub const TestScope = struct {
snapshot_count: usize = 0,
timeout_millis: u32 = 0,
- pub const Class = NewClass(
- void,
- .{ .name = "test" },
- .{
- .call = call,
- .only = only,
- .skip = skip,
- .todo = todo,
- },
- .{},
- );
-
pub const Counter = struct {
expected: u32 = 0,
actual: u32 = 0,
};
pub fn only(
- // the DescribeScope here is the top of the file, not the real one
- _: void,
- ctx: js.JSContextRef,
- this: js.JSObjectRef,
- _: js.JSObjectRef,
- arguments: []const js.JSValueRef,
- exception: js.ExceptionRef,
- ) js.JSObjectRef {
- return prepare(this, ctx, arguments, exception, .only);
+ globalThis: *JSC.JSGlobalObject,
+ callframe: *JSC.CallFrame,
+ ) callconv(.C) JSC.JSValue {
+ const thisValue = callframe.this();
+ const args = callframe.arguments(3);
+ prepare(globalThis, args.ptr[0..args.len], .only);
+ return thisValue;
}
pub fn skip(
- // the DescribeScope here is the top of the file, not the real one
- _: void,
- ctx: js.JSContextRef,
- this: js.JSObjectRef,
- _: js.JSObjectRef,
- arguments: []const js.JSValueRef,
- exception: js.ExceptionRef,
- ) js.JSObjectRef {
- return prepare(this, ctx, arguments, exception, .skip);
+ globalThis: *JSC.JSGlobalObject,
+ callframe: *JSC.CallFrame,
+ ) callconv(.C) JSC.JSValue {
+ const thisValue = callframe.this();
+ const args = callframe.arguments(3);
+ prepare(globalThis, args.ptr[0..args.len], .skip);
+ return thisValue;
}
pub fn call(
- // the DescribeScope here is the top of the file, not the real one
- _: void,
- ctx: js.JSContextRef,
- this: js.JSObjectRef,
- _: js.JSObjectRef,
- arguments: []const js.JSValueRef,
- exception: js.ExceptionRef,
- ) js.JSObjectRef {
- return prepare(this, ctx, arguments, exception, .call);
+ globalThis: *JSC.JSGlobalObject,
+ callframe: *JSC.CallFrame,
+ ) callconv(.C) JSC.JSValue {
+ const thisValue = callframe.this();
+ const args = callframe.arguments(3);
+ prepare(globalThis, args.ptr[0..args.len], .call);
+ return thisValue;
}
pub fn todo(
- _: void,
- ctx: js.JSContextRef,
- this: js.JSObjectRef,
- _: js.JSObjectRef,
- arguments: []const js.JSValueRef,
- exception: js.ExceptionRef,
- ) js.JSObjectRef {
- return prepare(this, ctx, arguments, exception, .todo);
+ globalThis: *JSC.JSGlobalObject,
+ callframe: *JSC.CallFrame,
+ ) callconv(.C) JSC.JSValue {
+ const thisValue = callframe.this();
+ const args = callframe.arguments(3);
+ prepare(globalThis, args.ptr[0..args.len], .todo);
+ return thisValue;
}
- fn prepare(
- this: js.JSObjectRef,
- ctx: js.JSContextRef,
- arguments: []const js.JSValueRef,
- exception: js.ExceptionRef,
+ inline fn prepare(
+ globalThis: *JSC.JSGlobalObject,
+ args: []const JSC.JSValue,
comptime tag: @Type(.EnumLiteral),
- ) js.JSObjectRef {
- var args = bun.cast([]const JSC.JSValue, arguments[0..@min(arguments.len, 3)]);
+ ) void {
var label: string = "";
if (args.len == 0) {
- return this;
+ return;
}
var label_value = args[0];
@@ -3328,21 +3371,21 @@ pub const TestScope = struct {
}
if (label_value != .zero) {
- const allocator = getAllocator(ctx);
- label = (label_value.toSlice(ctx, allocator).cloneIfNeeded(allocator) catch unreachable).slice();
+ const allocator = getAllocator(globalThis);
+ label = (label_value.toSlice(globalThis, allocator).cloneIfNeeded(allocator) catch unreachable).slice();
}
if (tag == .todo and label_value == .zero) {
- JSError(getAllocator(ctx), "test.todo() requires a description", .{}, ctx, exception);
- return this;
+ globalThis.throw("test.todo() requires a description", .{});
+ return;
}
const function = function_value;
- if (function.isEmptyOrUndefinedOrNull() or !function.isCell() or !function.isCallable(ctx.vm())) {
+ if (function.isEmptyOrUndefinedOrNull() or !function.isCell() or !function.isCallable(globalThis.vm())) {
// a callback is not required for .todo
if (tag != .todo) {
- JSError(getAllocator(ctx), "test() expects a function", .{}, ctx, exception);
- return this;
+ globalThis.throw("test() expects a function", .{});
+ return;
}
}
@@ -3351,35 +3394,37 @@ pub const TestScope = struct {
}
if (tag == .todo) {
+ if (function != .zero)
+ function.protect();
DescribeScope.active.todo_counter += 1;
- DescribeScope.active.tests.append(getAllocator(ctx), TestScope{
+ DescribeScope.active.tests.append(getAllocator(globalThis), TestScope{
.label = label,
.parent = DescribeScope.active,
.is_todo = true,
- .callback = if (function == .zero) null else function.asObjectRef(),
+ .callback = function,
}) catch unreachable;
- return this;
+ return;
}
if (tag == .skip or (tag != .only and Jest.runner.?.only)) {
DescribeScope.active.skipped_counter += 1;
- DescribeScope.active.tests.append(getAllocator(ctx), TestScope{
+ DescribeScope.active.tests.append(getAllocator(globalThis), TestScope{
.label = label,
.parent = DescribeScope.active,
.skipped = true,
- .callback = null,
+ .callback = .zero,
}) catch unreachable;
- return this;
+ return;
}
- js.JSValueProtect(ctx, function.asObjectRef());
+ function.protect();
- DescribeScope.active.tests.append(getAllocator(ctx), TestScope{
+ DescribeScope.active.tests.append(getAllocator(globalThis), TestScope{
.label = label,
- .callback = function.asObjectRef(),
+ .callback = function,
.parent = DescribeScope.active,
- .timeout_millis = if (arguments.len > 2) @intCast(u32, @max(args[2].coerce(i32, ctx), 0)) else Jest.runner.?.default_timeout_ms,
+ .timeout_millis = if (args.len > 2) @intCast(u32, @max(args[2].coerce(i32, globalThis), 0)) else Jest.runner.?.default_timeout_ms,
}) catch unreachable;
if (test_elapsed_timer == null) create_tiemr: {
@@ -3387,8 +3432,6 @@ pub const TestScope = struct {
timer.* = std.time.Timer.start() catch break :create_tiemr;
test_elapsed_timer = timer;
}
-
- return this;
}
pub fn onReject(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue {
@@ -3442,16 +3485,16 @@ pub const TestScope = struct {
) Result {
if (comptime is_bindgen) return undefined;
var vm = VirtualMachine.get();
- var callback = this.callback;
+ const callback = this.callback;
Jest.runner.?.did_pending_test_fail = false;
defer {
- js.JSValueUnprotect(vm.global, callback);
- this.callback = null;
+ callback.unprotect();
+ this.callback = .zero;
vm.autoGarbageCollect();
}
JSC.markBinding(@src());
- const callback_length = JSValue.fromRef(callback).getLengthOfArray(vm.global);
+ const callback_length = callback.getLength(vm.global);
var initial_value = JSValue.zero;
if (test_elapsed_timer) |timer| {
@@ -3474,9 +3517,9 @@ pub const TestScope = struct {
task,
);
task.done_callback_state = .pending;
- initial_value = JSValue.fromRef(callback).call(vm.global, &.{callback_func});
+ initial_value = callback.call(vm.global, &.{callback_func});
} else {
- initial_value = js.JSObjectCallAsFunctionReturnValue(vm.global, callback, null, 0, null);
+ initial_value = callback.call(vm.global, &.{});
}
if (initial_value.isAnyError()) {
@@ -3539,8 +3582,6 @@ pub const TestScope = struct {
return .{ .pending = {} };
}
- this.callback = null;
-
if (active_test_expectation_counter.expected > 0 and active_test_expectation_counter.expected < active_test_expectation_counter.actual) {
Output.prettyErrorln("Test fail: {d} / {d} expectations\n (make this better!)", .{
active_test_expectation_counter.actual,
@@ -3614,73 +3655,36 @@ pub const DescribeScope = struct {
afterAll,
};
- pub const TestEntry = struct {
- label: string,
- callback: js.JSValueRef,
-
- pub const List = std.MultiArrayList(TestEntry);
- };
-
pub threadlocal var active: *DescribeScope = undefined;
pub threadlocal var module: *DescribeScope = undefined;
const CallbackFn = *const fn (
- void,
- js.JSContextRef,
- js.JSObjectRef,
- js.JSObjectRef,
- []const js.JSValueRef,
- js.ExceptionRef,
- ) js.JSObjectRef;
+ *JSC.JSGlobalObject,
+ *JSC.CallFrame,
+ ) callconv(.C) JSC.JSValue;
fn createCallback(comptime hook: LifecycleHook) CallbackFn {
return struct {
const this_hook = hook;
pub fn run(
- _: void,
- ctx: js.JSContextRef,
- _: js.JSObjectRef,
- _: js.JSObjectRef,
- arguments: []const js.JSValueRef,
- exception: js.ExceptionRef,
- ) js.JSObjectRef {
- if (arguments.len == 0 or !JSC.JSValue.c(arguments[0]).isObject() or !JSC.JSValue.c(arguments[0]).isCallable(ctx.vm())) {
- JSC.throwInvalidArguments("Expected callback", .{}, ctx, exception);
- return null;
+ globalThis: *JSC.JSGlobalObject,
+ callframe: *JSC.CallFrame,
+ ) callconv(.C) JSC.JSValue {
+ const arguments_ = callframe.arguments(2);
+ const arguments = arguments_.ptr[0..arguments_.len];
+ if (arguments.len == 0 or !arguments[0].isObject() or !arguments[0].isCallable(globalThis.vm())) {
+ globalThis.throwInvalidArgumentType(@tagName(this_hook), "callback", "function");
+ return .zero;
}
- JSC.JSValue.c(arguments[0]).protect();
+ arguments[0].protect();
const name = comptime @as(string, @tagName(this_hook));
- @field(DescribeScope.active, name).append(getAllocator(ctx), JSC.JSValue.c(arguments[0])) catch unreachable;
- return JSC.JSValue.jsBoolean(true).asObjectRef();
+ @field(DescribeScope.active, name).append(getAllocator(globalThis), arguments[0]) catch unreachable;
+ return JSC.JSValue.jsBoolean(true);
}
}.run;
}
- pub const Class = NewClass(
- DescribeScope,
- .{
- .name = "describe",
- .read_only = true,
- },
- .{
- .call = describe,
- .afterAll = .{ .rfn = createCallback(.afterAll), .name = "afterAll" },
- .afterEach = .{ .rfn = createCallback(.afterEach), .name = "afterEach" },
- .beforeAll = .{ .rfn = createCallback(.beforeAll), .name = "beforeAll" },
- .beforeEach = .{ .rfn = createCallback(.beforeEach), .name = "beforeEach" },
- .skip = skip,
- },
- .{
- .expect = .{ .get = createExpect, .name = "expect" },
- // kind of a mindfuck but
- // describe("foo", () => {}).describe("bar") will wrok
- .describe = .{ .get = createDescribe, .name = "describe" },
- .it = .{ .get = createTest, .name = "it" },
- .@"test" = .{ .get = createTest, .name = "test" },
- },
- );
-
pub fn onDone(
ctx: js.JSContextRef,
callframe: *JSC.CallFrame,
@@ -3704,7 +3708,12 @@ pub const DescribeScope = struct {
return JSValue.jsUndefined();
}
- pub fn execCallback(this: *DescribeScope, ctx: js.JSContextRef, comptime hook: LifecycleHook) JSValue {
+ pub const afterAll = createCallback(.afterAll);
+ pub const afterEach = createCallback(.afterEach);
+ pub const beforeAll = createCallback(.beforeAll);
+ pub const beforeEach = createCallback(.beforeEach);
+
+ pub fn execCallback(this: *DescribeScope, globalObject: *JSC.JSGlobalObject, comptime hook: LifecycleHook) JSValue {
const name = comptime @as(string, @tagName(hook));
var hooks: []JSC.JSValue = @field(this, name).items;
for (hooks, 0..) |cb, i| {
@@ -3718,28 +3727,28 @@ pub const DescribeScope = struct {
Jest.runner.?.did_pending_test_fail = false;
const vm = VirtualMachine.get();
- var result: JSC.JSValue = if (cb.getLengthOfArray(ctx) > 0) brk: {
+ var result: JSC.JSValue = if (cb.getLength(globalObject) > 0) brk: {
this.done = false;
const done_func = JSC.NewFunctionWithData(
- ctx,
+ globalObject,
ZigString.static("done"),
0,
DescribeScope.onDone,
false,
this,
);
- var result = cb.call(ctx, &.{done_func});
+ var result = cb.call(globalObject, &.{done_func});
vm.waitFor(&this.done);
break :brk result;
- } else cb.call(ctx, &.{});
+ } else cb.call(globalObject, &.{});
if (result.asAnyPromise()) |promise| {
- if (promise.status(ctx.vm()) == .Pending) {
+ if (promise.status(globalObject.vm()) == .Pending) {
result.protect();
vm.waitForPromise(promise);
result.unprotect();
}
- result = promise.result(ctx.vm());
+ result = promise.result(globalObject.vm());
}
Jest.runner.?.pending_test = pending_test;
@@ -3791,71 +3800,64 @@ pub const DescribeScope = struct {
return null;
}
- pub fn runCallback(this: *DescribeScope, ctx: js.JSContextRef, comptime hook: LifecycleHook) JSValue {
- if (runGlobalCallbacks(ctx, hook)) |err| {
+ pub fn runCallback(this: *DescribeScope, globalObject: *JSC.JSGlobalObject, comptime hook: LifecycleHook) JSValue {
+ if (runGlobalCallbacks(globalObject, hook)) |err| {
return err;
}
var parent = this.parent;
while (parent) |scope| {
- const ret = scope.execCallback(ctx, hook);
+ const ret = scope.execCallback(globalObject, hook);
if (!ret.isEmpty()) {
return ret;
}
parent = scope.parent;
}
- return this.execCallback(ctx, hook);
+ return this.execCallback(globalObject, hook);
}
pub fn skip(
- this: *DescribeScope,
- ctx: js.JSContextRef,
- _: js.JSObjectRef,
- _: js.JSObjectRef,
- arguments: []const js.JSValueRef,
- exception: js.ExceptionRef,
- ) js.JSObjectRef {
- return this.runDescribe(ctx, null, null, arguments, exception, true);
+ globalThis: *JSC.JSGlobalObject,
+ callframe: *JSC.CallFrame,
+ ) callconv(.C) JSC.JSValue {
+ const arguments = callframe.arguments(3);
+ var this: *DescribeScope = DescribeScope.module;
+ return runDescribe(this, globalThis, arguments.ptr[0..arguments.len], true);
}
pub fn describe(
- this: *DescribeScope,
- ctx: js.JSContextRef,
- _: js.JSObjectRef,
- _: js.JSObjectRef,
- arguments: []const js.JSValueRef,
- exception: js.ExceptionRef,
- ) js.JSObjectRef {
- return runDescribe(this, ctx, null, null, arguments, exception, false);
+ globalThis: *JSC.JSGlobalObject,
+ callframe: *JSC.CallFrame,
+ ) callconv(.C) JSC.JSValue {
+ const arguments = callframe.arguments(3);
+ var this: *DescribeScope = DescribeScope.module;
+ return runDescribe(this, globalThis, arguments.ptr[0..arguments.len], false);
}
fn runDescribe(
this: *DescribeScope,
- ctx: js.JSContextRef,
- _: js.JSObjectRef,
- _: js.JSObjectRef,
- arguments: []const js.JSValueRef,
- exception: js.ExceptionRef,
+ globalThis: *JSC.JSGlobalObject,
+ arguments: []const JSC.JSValue,
skipped: bool,
- ) js.JSObjectRef {
+ ) JSC.JSValue {
if (arguments.len == 0 or arguments.len > 2) {
- JSError(getAllocator(ctx), "describe() requires 1-2 arguments", .{}, ctx, exception);
- return js.JSValueMakeUndefined(ctx);
+ globalThis.throwNotEnoughArguments("describe", 2, arguments.len);
+ return .zero;
}
var label = ZigString.init("");
var args = arguments;
- const allocator = getAllocator(ctx);
+ const allocator = getAllocator(globalThis);
- if (js.JSValueIsString(ctx, arguments[0])) {
- JSC.JSValue.fromRef(arguments[0]).toZigString(&label, ctx.ptr());
+ if (arguments[0].isString()) {
+ arguments[0].toZigString(&label, globalThis);
args = args[1..];
}
- if (args.len == 0 or !js.JSObjectIsFunction(ctx, args[0])) {
- JSError(allocator, "describe() requires a callback function", .{}, ctx, exception);
- return js.JSValueMakeUndefined(ctx);
+ if (args.len == 0 or !args[0].isCallable(globalThis.vm())) {
+ globalThis.throwInvalidArgumentType("describe", "callback", "function");
+ return .zero;
}
var callback = args[0];
@@ -3867,15 +3869,14 @@ pub const DescribeScope = struct {
.file_id = this.file_id,
.skipped = skipped or active.skipped,
};
- var new_this = DescribeScope.Class.make(ctx, scope);
- return scope.run(new_this, ctx, callback, exception);
+ return scope.run(globalThis, callback);
}
- pub fn run(this: *DescribeScope, thisObject: js.JSObjectRef, ctx: js.JSContextRef, callback: js.JSObjectRef, _: js.ExceptionRef) js.JSObjectRef {
+ pub fn run(this: *DescribeScope, globalObject: *JSC.JSGlobalObject, callback: JSC.JSValue) JSC.JSValue {
if (comptime is_bindgen) return undefined;
- js.JSValueProtect(ctx, callback);
- defer js.JSValueUnprotect(ctx, callback);
+ callback.protect();
+ defer callback.unprotect();
var original_active = active;
defer active = original_active;
if (this != module)
@@ -3884,34 +3885,34 @@ pub const DescribeScope = struct {
{
JSC.markBinding(@src());
- ctx.clearTerminationException();
- var result = js.JSObjectCallAsFunctionReturnValue(ctx, callback, thisObject, 0, null);
+ globalObject.clearTerminationException();
+ var result = callback.call(globalObject, &.{});
if (result.asAnyPromise()) |prom| {
- ctx.bunVM().waitForPromise(prom);
- switch (prom.status(ctx.ptr().vm())) {
+ globalObject.bunVM().waitForPromise(prom);
+ switch (prom.status(globalObject.ptr().vm())) {
JSPromise.Status.Fulfilled => {},
else => {
- ctx.bunVM().runErrorHandlerWithDedupe(prom.result(ctx.ptr().vm()), null);
- return JSC.JSValue.jsUndefined().asObjectRef();
+ globalObject.bunVM().runErrorHandlerWithDedupe(prom.result(globalObject.ptr().vm()), null);
+ return .undefined;
},
}
} else if (result.toError()) |err| {
- ctx.bunVM().runErrorHandlerWithDedupe(err, null);
- return JSC.JSValue.jsUndefined().asObjectRef();
+ globalObject.bunVM().runErrorHandlerWithDedupe(err, null);
+ return .undefined;
}
}
- this.runTests(thisObject.?.value(), ctx);
- return js.JSValueMakeUndefined(ctx);
+ this.runTests(globalObject);
+ return .undefined;
}
- pub fn runTests(this: *DescribeScope, this_object: JSC.JSValue, ctx: js.JSContextRef) void {
+ pub fn runTests(this: *DescribeScope, globalObject: *JSC.JSGlobalObject) void {
// Step 1. Initialize the test block
- ctx.clearTerminationException();
+ globalObject.clearTerminationException();
const file = this.file_id;
- const allocator = getAllocator(ctx);
+ const allocator = getAllocator(globalObject);
var tests: []TestScope = this.tests.items;
const end = @truncate(TestRunner.Test.ID, tests.len);
this.pending_tests = std.DynamicBitSetUnmanaged.initFull(allocator, end) catch unreachable;
@@ -3926,8 +3927,8 @@ pub const DescribeScope = struct {
var i: TestRunner.Test.ID = 0;
if (!this.isAllSkipped()) {
- const beforeAll = this.runCallback(ctx, .beforeAll);
- if (!beforeAll.isEmpty()) {
+ const beforeAllCallback = this.runCallback(globalObject, .beforeAll);
+ if (!beforeAllCallback.isEmpty()) {
while (i < end) {
Jest.runner.?.reportFailure(i + this.test_id_start, source.path.text, tests[i].label, 0, 0, this);
i += 1;
@@ -3943,11 +3944,10 @@ pub const DescribeScope = struct {
runner.* = .{
.test_id = i,
.describe = this,
- .globalThis = ctx,
+ .globalThis = globalObject,
.source_file_path = source.path.text,
- .value = JSC.Strong.create(this_object, ctx),
};
- runner.ref.ref(ctx.bunVM());
+ runner.ref.ref(globalObject.bunVM());
Jest.runner.?.enqueue(runner);
}
@@ -3959,9 +3959,9 @@ pub const DescribeScope = struct {
this.pending_tests.unset(test_id);
if (!skipped) {
- const afterEach = this.runCallback(globalThis, .afterEach);
- if (!afterEach.isEmpty()) {
- globalThis.bunVM().runErrorHandler(afterEach, null);
+ const afterEach_result = this.runCallback(globalThis, .afterEach);
+ if (!afterEach_result.isEmpty()) {
+ globalThis.bunVM().runErrorHandler(afterEach_result, null);
}
}
@@ -3972,9 +3972,9 @@ pub const DescribeScope = struct {
if (!this.isAllSkipped()) {
// Run the afterAll callbacks, in reverse order
// unless there were no tests for this scope
- const afterAll = this.execCallback(globalThis, .afterAll);
- if (!afterAll.isEmpty()) {
- globalThis.bunVM().runErrorHandler(afterAll, null);
+ const afterAll_result = this.execCallback(globalThis, .afterAll);
+ if (!afterAll_result.isEmpty()) {
+ globalThis.bunVM().runErrorHandler(afterAll_result, null);
}
}
@@ -3999,35 +3999,6 @@ pub const DescribeScope = struct {
// // }
// }
- pub fn createExpect(
- _: *DescribeScope,
- ctx: js.JSContextRef,
- _: js.JSValueRef,
- _: js.JSStringRef,
- _: js.ExceptionRef,
- ) js.JSObjectRef {
- return JSC.Jest.Expect.getConstructor(ctx).asObjectRef();
- }
-
- pub fn createTest(
- _: *DescribeScope,
- ctx: js.JSContextRef,
- _: js.JSValueRef,
- _: js.JSStringRef,
- _: js.ExceptionRef,
- ) js.JSObjectRef {
- return js.JSObjectMake(ctx, TestScope.Class.get().*, null);
- }
-
- pub fn createDescribe(
- this: *DescribeScope,
- ctx: js.JSContextRef,
- _: js.JSValueRef,
- _: js.JSStringRef,
- _: js.ExceptionRef,
- ) js.JSObjectRef {
- return DescribeScope.Class.make(ctx, this);
- }
};
var active_test_expectation_counter: TestScope.Counter = undefined;
@@ -4037,7 +4008,6 @@ pub const TestRunnerTask = struct {
describe: *DescribeScope,
globalThis: *JSC.JSGlobalObject,
source_file_path: string = "",
- value: JSC.Strong = .{},
needs_before_each: bool = true,
ref: JSC.Ref = JSC.Ref.init(),
@@ -4081,18 +4051,20 @@ pub const TestRunnerTask = struct {
pub fn run(this: *TestRunnerTask) bool {
var describe = this.describe;
+ var globalThis = this.globalThis;
+ var jsc_vm = globalThis.bunVM();
// reset the global state for each test
// prior to the run
DescribeScope.active = describe;
active_test_expectation_counter = .{};
+ jsc_vm.last_reported_error_for_dedupe = .zero;
const test_id = this.test_id;
var test_: TestScope = this.describe.tests.items[test_id];
describe.current_test_id = test_id;
- var globalThis = this.globalThis;
- if (!describe.skipped and test_.is_todo and test_.callback == null) {
+ if (!describe.skipped and test_.is_todo and test_.callback.isEmpty()) {
this.processTestResult(globalThis, .{ .todo = {} }, test_, test_id, describe);
this.deinit();
return false;
@@ -4104,7 +4076,7 @@ pub const TestRunnerTask = struct {
return false;
}
- globalThis.bunVM().onUnhandledRejectionCtx = this;
+ jsc_vm.onUnhandledRejectionCtx = this;
if (this.needs_before_each) {
this.needs_before_each = false;
@@ -4114,7 +4086,7 @@ pub const TestRunnerTask = struct {
if (!beforeEach.isEmpty()) {
Jest.runner.?.reportFailure(test_id, this.source_file_path, label, 0, 0, this.describe);
- globalThis.bunVM().runErrorHandler(beforeEach, null);
+ jsc_vm.runErrorHandler(beforeEach, null);
return false;
}
}
@@ -4129,7 +4101,6 @@ pub const TestRunnerTask = struct {
if (result == .pending and this.sync_state == .pending and (this.done_callback_state == .pending or this.promise_state == .pending)) {
this.sync_state = .fulfilled;
- this.value.set(globalThis, this.describe.value);
return true;
}
@@ -4254,7 +4225,6 @@ pub const TestRunnerTask = struct {
}
}
- this.value.deinit();
this.ref.unref(vm);
// there is a double free here involving async before/after callbacks
diff --git a/src/bun.js/test/pretty_format.zig b/src/bun.js/test/pretty_format.zig
index 400b0b4e1..47a267b7f 100644
--- a/src/bun.js/test/pretty_format.zig
+++ b/src/bun.js/test/pretty_format.zig
@@ -1137,7 +1137,7 @@ pub const JestPrettyFormat = struct {
writer.writeAll("[Function]");
},
.Array => {
- const len = @truncate(u32, value.getLengthOfArray(this.globalThis));
+ const len = @truncate(u32, value.getLength(this.globalThis));
if (len == 0) {
writer.writeAll("[]");
this.addForNewLine(2);
@@ -1614,7 +1614,7 @@ pub const JestPrettyFormat = struct {
this.writeIndent(Writer, writer_) catch unreachable;
},
.Array => {
- const length = children.getLengthOfArray(this.globalThis);
+ const length = children.getLength(this.globalThis);
if (length == 0) break :print_children;
writer.writeAll(">\n");
diff --git a/src/bun.js/webcore/blob.zig b/src/bun.js/webcore/blob.zig
index 547dc73f1..591150e12 100644
--- a/src/bun.js/webcore/blob.zig
+++ b/src/bun.js/webcore/blob.zig
@@ -593,7 +593,7 @@ pub const Blob = struct {
var needs_async = false;
if (data.isString()) {
- const len = data.getLengthOfArray(ctx);
+ const len = data.getLength(ctx);
if (len < 256 * 1024 or bun.isMissingIOUring()) {
const str = data.getZigString(ctx);
@@ -2596,6 +2596,35 @@ pub const Blob = struct {
return JSValue.jsNumber(init_timestamp);
}
+ pub fn getSizeForBindings(this: *Blob) u64 {
+ if (this.size == Blob.max_size) {
+ this.resolveSize();
+ }
+
+ // If the file doesn't exist or is not seekable
+ // signal that the size is unknown.
+ if (this.store != null and this.store.?.data == .file and
+ !(this.store.?.data.file.seekable orelse false))
+ {
+ return std.math.maxInt(u64);
+ }
+
+ if (this.size == Blob.max_size)
+ return std.math.maxInt(u64);
+
+ return this.size;
+ }
+
+ export fn Bun__Blob__getSizeForBindings(this: *Blob) callconv(.C) u64 {
+ return this.getSizeForBindings();
+ }
+
+ comptime {
+ if (!JSC.is_bindgen) {
+ _ = Bun__Blob__getSizeForBindings;
+ }
+ }
+
pub fn getSize(this: *Blob, _: *JSC.JSGlobalObject) callconv(.C) JSValue {
if (this.size == Blob.max_size) {
this.resolveSize();
diff --git a/src/cli/test_command.zig b/src/cli/test_command.zig
index d4a26d0f8..0e337ebf0 100644
--- a/src/cli/test_command.zig
+++ b/src/cli/test_command.zig
@@ -755,7 +755,7 @@ pub const TestCommand = struct {
vm.onUnhandledRejectionCtx = null;
vm.onUnhandledRejection = jest.TestRunnerTask.onUnhandledRejection;
- module.runTests(JSC.JSValue.zero, vm.global);
+ module.runTests(vm.global);
vm.eventLoop().tick();
var prev_unhandled_count = vm.unhandled_error_counter;
diff --git a/src/http/websocket_http_client.zig b/src/http/websocket_http_client.zig
index 3ce8f9118..9f83e550e 100644
--- a/src/http/websocket_http_client.zig
+++ b/src/http/websocket_http_client.zig
@@ -130,16 +130,16 @@ const ErrorCode = enum(i32) {
invalid_utf8,
};
-pub const JSWebSocket = opaque {
+const CppWebSocket = opaque {
extern fn WebSocket__didConnect(
- websocket_context: *JSWebSocket,
+ websocket_context: *CppWebSocket,
socket: *uws.Socket,
buffered_data: ?[*]u8,
buffered_len: usize,
) void;
- extern fn WebSocket__didCloseWithErrorCode(websocket_context: *JSWebSocket, reason: ErrorCode) void;
- extern fn WebSocket__didReceiveText(websocket_context: *JSWebSocket, clone: bool, text: *const JSC.ZigString) void;
- extern fn WebSocket__didReceiveBytes(websocket_context: *JSWebSocket, bytes: [*]const u8, byte_len: usize) void;
+ extern fn WebSocket__didCloseWithErrorCode(websocket_context: *CppWebSocket, reason: ErrorCode) void;
+ extern fn WebSocket__didReceiveText(websocket_context: *CppWebSocket, clone: bool, text: *const JSC.ZigString) void;
+ extern fn WebSocket__didReceiveBytes(websocket_context: *CppWebSocket, bytes: [*]const u8, byte_len: usize) void;
pub const didConnect = WebSocket__didConnect;
pub const didCloseWithErrorCode = WebSocket__didCloseWithErrorCode;
@@ -157,7 +157,7 @@ pub fn NewHTTPUpgradeClient(comptime ssl: bool) type {
return struct {
pub const Socket = uws.NewSocketHandler(ssl);
tcp: Socket,
- outgoing_websocket: ?*JSWebSocket,
+ outgoing_websocket: ?*CppWebSocket,
input_body_buf: []u8 = &[_]u8{},
client_protocol: []const u8 = "",
to_send: []const u8 = "",
@@ -210,7 +210,7 @@ pub fn NewHTTPUpgradeClient(comptime ssl: bool) type {
pub fn connect(
global: *JSC.JSGlobalObject,
socket_ctx: *anyopaque,
- websocket: *JSWebSocket,
+ websocket: *CppWebSocket,
host: *const JSC.ZigString,
port: u16,
pathname: *const JSC.ZigString,
@@ -844,7 +844,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
return struct {
pub const Socket = uws.NewSocketHandler(ssl);
tcp: Socket,
- outgoing_websocket: ?*JSWebSocket = null,
+ outgoing_websocket: ?*CppWebSocket = null,
receive_state: ReceiveState = ReceiveState.need_header,
receive_header: WebsocketHeader = @bitCast(WebsocketHeader, @as(u16, 0)),
@@ -934,6 +934,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
_ = socket;
_ = ssl_error;
log("WebSocket.onHandshake({d})", .{success});
+ JSC.markBinding(@src());
if (success == 0) {
if (this.outgoing_websocket) |ws| {
this.outgoing_websocket = null;
@@ -1520,7 +1521,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
}
pub fn init(
- outgoing: *JSWebSocket,
+ outgoing: *CppWebSocket,
input_socket: *anyopaque,
socket_ctx: *anyopaque,
globalThis: *JSC.JSGlobalObject,
diff --git a/src/js_ast.zig b/src/js_ast.zig
index 9bf76a37f..83a035a4c 100644
--- a/src/js_ast.zig
+++ b/src/js_ast.zig
@@ -8986,7 +8986,7 @@ pub const Macro = struct {
while (i < count) {
var nextArg = writer.eatArg() orelse return false;
if (js.JSValueIsArray(writer.ctx, nextArg.asRef())) {
- const extras = @truncate(u32, nextArg.getLengthOfArray(writer.ctx.ptr()));
+ const extras = @truncate(u32, nextArg.getLength(writer.ctx.ptr()));
count += std.math.max(@truncate(@TypeOf(count), extras), 1) - 1;
items.ensureUnusedCapacity(extras) catch unreachable;
items.expandToCapacity();
@@ -9367,7 +9367,7 @@ pub const Macro = struct {
.allocator = JSCBase.getAllocator(ctx),
.exception = exception,
.args_value = args_value,
- .args_len = @truncate(u32, args_value.getLengthOfArray(ctx.ptr())),
+ .args_len = @truncate(u32, args_value.getLength(ctx.ptr())),
.args_i = 0,
.errored = false,
};
diff --git a/src/napi/napi.zig b/src/napi/napi.zig
index ceee2caa4..227a80d05 100644
--- a/src/napi/napi.zig
+++ b/src/napi/napi.zig
@@ -571,7 +571,7 @@ pub export fn napi_has_element(env: napi_env, object: napi_value, index: c_uint,
return .array_expected;
}
- result.* = object.getLengthOfArray(env) > index;
+ result.* = object.getLength(env) > index;
return .ok;
}
pub export fn napi_get_element(env: napi_env, object: napi_value, index: u32, result: *napi_value) napi_status {
@@ -595,7 +595,7 @@ pub export fn napi_get_array_length(env: napi_env, value: napi_value, result: [*
return .array_expected;
}
- result.* = @truncate(u32, value.getLengthOfArray(env));
+ result.* = @truncate(u32, value.getLength(env));
return .ok;
}
pub export fn napi_strict_equals(env: napi_env, lhs: napi_value, rhs: napi_value, result: *bool) napi_status {
diff --git a/test/js/bun/test/test-test.test.ts b/test/js/bun/test/test-test.test.ts
index 5d45ef48e..4d31c9cb4 100644
--- a/test/js/bun/test/test-test.test.ts
+++ b/test/js/bun/test/test-test.test.ts
@@ -1765,6 +1765,52 @@ test("toHaveLength()", () => {
expect("123").not.toHaveLength(-0);
});
+test("toHaveLength() extended", () => {
+ // Headers
+ expect(new Headers()).toHaveLength(0);
+ expect(new Headers({ a: "1" })).toHaveLength(1);
+
+ // FormData
+ const form = new FormData();
+ expect(form).toHaveLength(0);
+ form.append("a", "1");
+ expect(form).toHaveLength(1);
+
+ // URLSearchParams
+ expect(new URLSearchParams()).toHaveLength(0);
+ expect(new URLSearchParams("a=1")).toHaveLength(1);
+ expect(new URLSearchParams([["a", "1"]])).toHaveLength(1);
+
+ // files
+ const thisFile = Bun.file(import.meta.path);
+ const thisFileSize = thisFile.size;
+
+ expect(thisFile).toHaveLength(thisFileSize);
+ expect(thisFile).toHaveLength(Bun.file(import.meta.path).size);
+
+ // empty file should have length 0
+ writeFileSync("/tmp/empty.txt", "");
+ expect(Bun.file("/tmp/empty.txt")).toHaveLength(0);
+
+ // if a file doesn't exist, it should throw (not return 0 size)
+ expect(() => expect(Bun.file("/does-not-exist/file.txt")).toHaveLength(0)).toThrow();
+
+ // Blob
+ expect(new Blob([1, 2, 3])).toHaveLength(3);
+ expect(new Blob()).toHaveLength(0);
+
+ // Set
+ expect(new Set()).toHaveLength(0);
+ expect(new Set([1, 2, 3])).toHaveLength(3);
+
+ // Map
+ expect(new Map()).toHaveLength(0);
+ expect(new Map([["a", 1]])).toHaveLength(1);
+
+ // WeakMap
+ expect(new WeakMap([[globalThis, 1]])).toHaveLength(1);
+});
+
test("toContain()", () => {
const s1 = new String("123");
expect(s1).not.toContain("12");