aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2022-12-20 22:05:47 -0800
committerGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2022-12-20 22:05:47 -0800
commita98e0adc7dda63600ff811fc6928c69879334dc5 (patch)
tree7e8c0911f3cb4493b43300811bad3c6a4f50a970
parent06487ef64f6968c411420f3e8347144f3885d2ef (diff)
downloadbun-a98e0adc7dda63600ff811fc6928c69879334dc5.tar.gz
bun-a98e0adc7dda63600ff811fc6928c69879334dc5.tar.zst
bun-a98e0adc7dda63600ff811fc6928c69879334dc5.zip
[web] Support multiple arguments in `setTimeout`, `setInterval`, and `setImmediate`
-rw-r--r--src/bun.js/api/bun.zig63
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.cpp67
-rw-r--r--src/bun.js/bindings/bindings.zig11
-rw-r--r--src/bun.js/bindings/headers-cpp.h2
-rw-r--r--src/bun.js/bindings/headers.h6
5 files changed, 139 insertions, 10 deletions
diff --git a/src/bun.js/api/bun.zig b/src/bun.js/api/bun.zig
index 7d7c0ec7a..eb235da2f 100644
--- a/src/bun.js/api/bun.zig
+++ b/src/bun.js/api/bun.zig
@@ -2340,6 +2340,7 @@ pub const Timer = struct {
ref: JSC.Ref = JSC.Ref.init(),
globalThis: *JSC.JSGlobalObject,
callback: JSC.Strong = .{},
+ arguments: JSC.Strong = .{},
repeat: bool = false,
pub const Task = JSC.AnyTask.New(CallbackJob, perform);
@@ -2369,6 +2370,7 @@ pub const Timer = struct {
pub fn deinit(this: *CallbackJob) void {
this.callback.deinit();
+ this.arguments.deinit();
this.ref.unref(this.globalThis.bunVM());
bun.default_allocator.destroy(this);
}
@@ -2395,9 +2397,34 @@ pub const Timer = struct {
}
}
+ var args_buf: [8]JSC.JSValue = undefined;
+ var args: []JSC.JSValue = &.{};
+ var args_needs_deinit = false;
+ defer if (args_needs_deinit) bun.default_allocator.free(args);
+
const callback = this.callback.get() orelse @panic("Expected CallbackJob to have a callback function");
+ if (this.arguments.trySwap()) |arguments| {
+ const count = arguments.getLengthOfArray(globalThis);
+ if (count > 0) {
+ if (count > args_buf.len) {
+ args = bun.default_allocator.alloc(JSC.JSValue, count) catch unreachable;
+ args_needs_deinit = true;
+ } else {
+ args = args_buf[0..count];
+ }
+ var arg = args.ptr;
+ var i: u32 = 0;
+ while (i < count) : (i += 1) {
+ arg[0] = JSC.JSObject.getIndex(arguments, globalThis, @truncate(u32, i));
+ arg += 1;
+ }
+ }
+ }
- const result = callback.call(globalThis, &.{});
+ const result = callback.callWithGlobalThis(
+ globalThis,
+ args,
+ );
if (result.isEmptyOrUndefinedOrNull() or !result.isCell()) {
this.deinit();
@@ -2435,6 +2462,7 @@ pub const Timer = struct {
globalThis: *JSC.JSGlobalObject,
timer: *uws.Timer,
poll_ref: JSC.PollRef = JSC.PollRef.init(),
+ arguments: JSC.Strong = .{},
// this is sized to be the same as one pointer
pub const ID = extern struct {
@@ -2478,6 +2506,21 @@ pub const Timer = struct {
)
else
this.callback,
+ .arguments = if (repeats and this.arguments.has())
+ JSC.Strong.create(
+ this.arguments.get() orelse {
+ // if the arguments freed, that's an error
+ if (comptime Environment.allow_assert)
+ unreachable;
+
+ this.deinit();
+ _ = map.swapRemove(timer_id.id);
+ return;
+ },
+ globalThis,
+ )
+ else
+ this.arguments,
.globalThis = globalThis,
.id = timer_id.id,
.repeat = timer_id.repeat > 0,
@@ -2488,6 +2531,7 @@ pub const Timer = struct {
// - reuse the JSC.Strong
if (!repeats) {
this.callback = .{};
+ this.arguments = .{};
map.put(vm.allocator, timer_id.id, null) catch unreachable;
this.deinit();
}
@@ -2507,9 +2551,11 @@ pub const Timer = struct {
JSC.markBinding(@src());
var vm = this.globalThis.bunVM();
+
this.poll_ref.unref(vm);
this.timer.deinit();
this.callback.deinit();
+ this.arguments.deinit();
}
};
@@ -2518,6 +2564,7 @@ pub const Timer = struct {
globalThis: *JSGlobalObject,
callback: JSValue,
countdown: JSValue,
+ arguments_array_or_zero: JSValue,
repeat: bool,
) !void {
JSC.markBinding(@src());
@@ -2545,6 +2592,10 @@ pub const Timer = struct {
.repeat = false,
};
+ if (arguments_array_or_zero != .zero) {
+ cb.arguments = JSC.Strong.create(arguments_array_or_zero, globalThis);
+ }
+
var job = vm.allocator.create(CallbackJob) catch @panic(
"Out of memory while allocating Timeout",
);
@@ -2570,6 +2621,10 @@ pub const Timer = struct {
),
};
+ if (arguments_array_or_zero != .zero) {
+ timeout.arguments = JSC.Strong.create(arguments_array_or_zero, globalThis);
+ }
+
timeout.poll_ref.ref(vm);
map.put(vm.allocator, id, timeout) catch unreachable;
@@ -2588,12 +2643,13 @@ pub const Timer = struct {
globalThis: *JSGlobalObject,
callback: JSValue,
countdown: JSValue,
+ arguments: JSValue,
) callconv(.C) JSValue {
JSC.markBinding(@src());
const id = globalThis.bunVM().timer.last_id;
globalThis.bunVM().timer.last_id +%= 1;
- Timer.set(id, globalThis, callback, countdown, false) catch
+ Timer.set(id, globalThis, callback, countdown, arguments, false) catch
return JSValue.jsUndefined();
return JSValue.jsNumberWithType(i32, id);
@@ -2602,12 +2658,13 @@ pub const Timer = struct {
globalThis: *JSGlobalObject,
callback: JSValue,
countdown: JSValue,
+ arguments: JSValue,
) callconv(.C) JSValue {
JSC.markBinding(@src());
const id = globalThis.bunVM().timer.last_id;
globalThis.bunVM().timer.last_id +%= 1;
- Timer.set(id, globalThis, callback, countdown, true) catch
+ Timer.set(id, globalThis, callback, countdown, arguments, true) catch
return JSValue.jsUndefined();
return JSValue.jsNumberWithType(i32, id);
diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp
index e8ee74943..7b5490a0b 100644
--- a/src/bun.js/bindings/ZigGlobalObject.cpp
+++ b/src/bun.js/bindings/ZigGlobalObject.cpp
@@ -762,7 +762,27 @@ static JSC_DEFINE_HOST_FUNCTION(functionSetTimeout,
}
JSC::JSValue num = callFrame->argument(1);
- return Bun__Timer__setTimeout(globalObject, JSC::JSValue::encode(job), JSC::JSValue::encode(num));
+ JSC::JSValue arguments = {};
+ size_t argumentCount = callFrame->argumentCount() - 2;
+ if (argumentCount > 0) {
+ JSC::ObjectInitializationScope initializationScope(globalObject->vm());
+ JSC::JSArray* argumentsArray = argumentsArray = JSC::JSArray::tryCreateUninitializedRestricted(
+ initializationScope, nullptr,
+ globalObject->arrayStructureForIndexingTypeDuringAllocation(JSC::ArrayWithContiguous),
+ argumentCount);
+
+ if (!argumentsArray) {
+ auto scope = DECLARE_THROW_SCOPE(globalObject->vm());
+ JSC::throwOutOfMemoryError(globalObject, scope);
+ return JSC::JSValue::encode(JSC::JSValue {});
+ }
+
+ for (size_t i = 0; i < argumentCount; i++) {
+ argumentsArray->putDirectIndex(globalObject, i, callFrame->uncheckedArgument(i + 2));
+ }
+ arguments = JSValue(argumentsArray);
+ }
+ return Bun__Timer__setTimeout(globalObject, JSC::JSValue::encode(job), JSC::JSValue::encode(num), JSValue::encode(arguments));
}
static JSC_DEFINE_HOST_FUNCTION(functionSetInterval,
@@ -785,8 +805,29 @@ static JSC_DEFINE_HOST_FUNCTION(functionSetInterval,
}
JSC::JSValue num = callFrame->argument(1);
+
+ JSC::JSValue arguments = {};
+ size_t argumentCount = callFrame->argumentCount() - 2;
+ if (argumentCount > 0) {
+ JSC::ObjectInitializationScope initializationScope(globalObject->vm());
+ JSC::JSArray* argumentsArray = argumentsArray = JSC::JSArray::tryCreateUninitializedRestricted(
+ initializationScope, nullptr,
+ globalObject->arrayStructureForIndexingTypeDuringAllocation(JSC::ArrayWithContiguous),
+ argumentCount);
+
+ if (!argumentsArray) {
+ auto scope = DECLARE_THROW_SCOPE(globalObject->vm());
+ JSC::throwOutOfMemoryError(globalObject, scope);
+ return JSC::JSValue::encode(JSC::JSValue {});
+ }
+
+ for (size_t i = 0; i < argumentCount; i++) {
+ argumentsArray->putDirectIndex(globalObject, i, callFrame->uncheckedArgument(i + 2));
+ }
+ arguments = JSValue(argumentsArray);
+ }
return Bun__Timer__setInterval(globalObject, JSC::JSValue::encode(job),
- JSC::JSValue::encode(num));
+ JSC::JSValue::encode(num), JSValue::encode(arguments));
}
static JSC_DEFINE_HOST_FUNCTION(functionClearInterval,
@@ -2753,7 +2794,27 @@ static JSC_DEFINE_HOST_FUNCTION(functionSetImmediate,
return JSC::JSValue::encode(JSC::JSValue {});
}
- return Bun__Timer__setTimeout(globalObject, JSC::JSValue::encode(job), JSC::JSValue::encode(jsNumber(0)));
+ JSC::JSValue arguments = {};
+ size_t argumentCount = callFrame->argumentCount() - 1;
+ if (argumentCount > 0) {
+ JSC::ObjectInitializationScope initializationScope(globalObject->vm());
+ JSC::JSArray* argumentsArray = argumentsArray = JSC::JSArray::tryCreateUninitializedRestricted(
+ initializationScope, nullptr,
+ globalObject->arrayStructureForIndexingTypeDuringAllocation(JSC::ArrayWithContiguous),
+ argumentCount);
+
+ if (!argumentsArray) {
+ auto scope = DECLARE_THROW_SCOPE(globalObject->vm());
+ JSC::throwOutOfMemoryError(globalObject, scope);
+ return JSC::JSValue::encode(JSC::JSValue {});
+ }
+
+ for (size_t i = 0; i < argumentCount; i++) {
+ argumentsArray->putDirectIndex(globalObject, i, callFrame->uncheckedArgument(i + 1));
+ }
+ arguments = JSValue(argumentsArray);
+ }
+ return Bun__Timer__setTimeout(globalObject, JSC::JSValue::encode(job), JSC::JSValue::encode(jsNumber(0)), JSValue::encode(arguments));
}
JSC_DEFINE_CUSTOM_GETTER(JSModuleLoader_getter, (JSGlobalObject * globalObject, EncodedJSValue thisValue, PropertyName))
diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig
index add89ea77..1ce48da90 100644
--- a/src/bun.js/bindings/bindings.zig
+++ b/src/bun.js/bindings/bindings.zig
@@ -2427,6 +2427,17 @@ pub const JSValue = enum(JSValueReprInt) {
return callWithThis(this, globalThis, JSC.JSValue.jsUndefined(), args);
}
+ pub fn callWithGlobalThis(this: JSValue, globalThis: *JSGlobalObject, args: []const JSC.JSValue) JSC.JSValue {
+ JSC.markBinding(@src());
+ return JSC.C.JSObjectCallAsFunctionReturnValue(
+ globalThis,
+ this.asObjectRef(),
+ @ptrCast(JSC.C.JSValueRef, globalThis),
+ args.len,
+ @ptrCast(?[*]const JSC.C.JSValueRef, args.ptr),
+ );
+ }
+
pub fn callWithThis(this: JSValue, globalThis: *JSGlobalObject, thisValue: JSC.JSValue, args: []const JSC.JSValue) JSC.JSValue {
JSC.markBinding(@src());
return JSC.C.JSObjectCallAsFunctionReturnValue(
diff --git a/src/bun.js/bindings/headers-cpp.h b/src/bun.js/bindings/headers-cpp.h
index b8bf7c300..0e43e86de 100644
--- a/src/bun.js/bindings/headers-cpp.h
+++ b/src/bun.js/bindings/headers-cpp.h
@@ -1,4 +1,4 @@
-//-- AUTOGENERATED FILE -- 1671319165
+//-- AUTOGENERATED FILE -- 1671518642
// clang-format off
#pragma once
diff --git a/src/bun.js/bindings/headers.h b/src/bun.js/bindings/headers.h
index f29395eff..31f5dc22a 100644
--- a/src/bun.js/bindings/headers.h
+++ b/src/bun.js/bindings/headers.h
@@ -1,5 +1,5 @@
// clang-format off
-//-- AUTOGENERATED FILE -- 1671319165
+//-- AUTOGENERATED FILE -- 1671518642
#pragma once
#include <stddef.h>
@@ -726,8 +726,8 @@ ZIG_DECL void Zig__ConsoleClient__timeStamp(void* arg0, JSC__JSGlobalObject* arg
ZIG_DECL JSC__JSValue Bun__Timer__clearInterval(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1);
ZIG_DECL JSC__JSValue Bun__Timer__clearTimeout(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1);
ZIG_DECL int32_t Bun__Timer__getNextID();
-ZIG_DECL JSC__JSValue Bun__Timer__setInterval(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue JSValue2);
-ZIG_DECL JSC__JSValue Bun__Timer__setTimeout(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue JSValue2);
+ZIG_DECL JSC__JSValue Bun__Timer__setInterval(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue JSValue2, JSC__JSValue JSValue3);
+ZIG_DECL JSC__JSValue Bun__Timer__setTimeout(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue JSValue2, JSC__JSValue JSValue3);
#endif