aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/bun_js.zig5
-rw-r--r--src/javascript/jsc/JavascriptCore.zig2
-rw-r--r--src/javascript/jsc/api/router.zig4
-rw-r--r--src/javascript/jsc/base.zig2
-rw-r--r--src/javascript/jsc/bindings/ZigGlobalObject.cpp15
-rw-r--r--src/javascript/jsc/bindings/ZigGlobalObject.h28
-rw-r--r--src/javascript/jsc/bindings/bindings.zig20
-rw-r--r--src/javascript/jsc/bindings/exports.zig9
-rw-r--r--src/javascript/jsc/bindings/headers-cpp.h2
-rw-r--r--src/javascript/jsc/bindings/headers-handwritten.h1
-rw-r--r--src/javascript/jsc/bindings/headers.h8
-rw-r--r--src/javascript/jsc/bindings/headers.zig2
-rw-r--r--src/javascript/jsc/javascript.zig87
-rw-r--r--src/javascript/jsc/webcore/response.zig318
-rw-r--r--src/js_ast.zig2
-rw-r--r--src/pool.zig10
-rw-r--r--src/runtime.version2
-rw-r--r--src/thread_pool.zig2
18 files changed, 404 insertions, 115 deletions
diff --git a/src/bun_js.zig b/src/bun_js.zig
index e5fb2bcd7..cb1d6c219 100644
--- a/src/bun_js.zig
+++ b/src/bun_js.zig
@@ -68,11 +68,10 @@ pub const Run = struct {
pub fn start(this: *Run) !void {
var promise = try this.vm.loadEntryPoint(this.entry_path);
-
- this.vm.global.vm().drainMicrotasks();
+ this.vm.tick();
while (promise.status(this.vm.global.vm()) == .Pending) {
- this.vm.global.vm().drainMicrotasks();
+ this.vm.tick();
}
if (promise.status(this.vm.global.vm()) == .Rejected) {
diff --git a/src/javascript/jsc/JavascriptCore.zig b/src/javascript/jsc/JavascriptCore.zig
index e6ac23bb7..807ea0053 100644
--- a/src/javascript/jsc/JavascriptCore.zig
+++ b/src/javascript/jsc/JavascriptCore.zig
@@ -174,7 +174,7 @@ pub extern "c" fn JSObjectMakeArray(ctx: JSContextRef, argumentCount: usize, arg
pub extern "c" fn JSObjectMakeDate(ctx: JSContextRef, argumentCount: usize, arguments: [*c]const JSValueRef, exception: ExceptionRef) JSObjectRef;
pub extern "c" fn JSObjectMakeError(ctx: JSContextRef, argumentCount: usize, arguments: [*c]const JSValueRef, exception: ExceptionRef) JSObjectRef;
pub extern "c" fn JSObjectMakeRegExp(ctx: JSContextRef, argumentCount: usize, arguments: [*c]const JSValueRef, exception: ExceptionRef) JSObjectRef;
-pub extern "c" fn JSObjectMakeDeferredPromise(ctx: JSContextRef, resolve: [*c]JSObjectRef, reject: [*c]JSObjectRef, exception: ExceptionRef) JSObjectRef;
+pub extern "c" fn JSObjectMakeDeferredPromise(ctx: JSContextRef, resolve: ?*JSObjectRef, reject: ?*JSObjectRef, exception: ExceptionRef) JSObjectRef;
pub extern "c" fn JSObjectMakeFunction(ctx: JSContextRef, name: JSStringRef, parameterCount: c_uint, parameterNames: [*c]const JSStringRef, body: JSStringRef, sourceURL: JSStringRef, startingLineNumber: c_int, exception: ExceptionRef) JSObjectRef;
pub extern "c" fn JSObjectGetPrototype(ctx: JSContextRef, object: JSObjectRef) JSValueRef;
pub extern "c" fn JSObjectSetPrototype(ctx: JSContextRef, object: JSObjectRef, value: JSValueRef) void;
diff --git a/src/javascript/jsc/api/router.zig b/src/javascript/jsc/api/router.zig
index 35bd2a865..3b3cf8f99 100644
--- a/src/javascript/jsc/api/router.zig
+++ b/src/javascript/jsc/api/router.zig
@@ -31,7 +31,9 @@ pub fn importRoute(
exception: js.ExceptionRef,
) js.JSObjectRef {
const prom = JSModuleLoader.loadAndEvaluateModule(VirtualMachine.vm.global, ZigString.init(this.route.file_path));
- VirtualMachine.vm.global.vm().drainMicrotasks();
+
+ VirtualMachine.vm.tick();
+
return prom.result(VirtualMachine.vm.global.vm()).asRef();
}
diff --git a/src/javascript/jsc/base.zig b/src/javascript/jsc/base.zig
index 63e07b635..64a0f15a3 100644
--- a/src/javascript/jsc/base.zig
+++ b/src/javascript/jsc/base.zig
@@ -1552,6 +1552,7 @@ pub fn castObj(obj: js.JSObjectRef, comptime Type: type) *Type {
const JSNode = @import("../../js_ast.zig").Macro.JSNode;
const LazyPropertiesObject = @import("../../js_ast.zig").Macro.LazyPropertiesObject;
const ModuleNamespace = @import("../../js_ast.zig").Macro.ModuleNamespace;
+const FetchTaskletContext = Fetch.FetchTasklet.FetchTaskletContext;
pub const JSPrivateDataPtr = TaggedPointerUnion(.{
ResolveError,
BuildError,
@@ -1564,6 +1565,7 @@ pub const JSPrivateDataPtr = TaggedPointerUnion(.{
JSNode,
LazyPropertiesObject,
ModuleNamespace,
+ FetchTaskletContext,
});
pub inline fn GetJSPrivateData(comptime Type: type, ref: js.JSObjectRef) ?*Type {
diff --git a/src/javascript/jsc/bindings/ZigGlobalObject.cpp b/src/javascript/jsc/bindings/ZigGlobalObject.cpp
index b8470ce4b..4d8a2a6da 100644
--- a/src/javascript/jsc/bindings/ZigGlobalObject.cpp
+++ b/src/javascript/jsc/bindings/ZigGlobalObject.cpp
@@ -5,7 +5,7 @@
#include <JavaScriptCore/AggregateError.h>
#include <JavaScriptCore/BytecodeIndex.h>
#include <JavaScriptCore/CallFrameInlines.h>
-#include <JavaScriptCore/CatchScope.h>
+
#include <JavaScriptCore/ClassInfo.h>
#include <JavaScriptCore/CodeBlock.h>
#include <JavaScriptCore/CodeCache.h>
@@ -27,6 +27,7 @@
#include <JavaScriptCore/JSCast.h>
#include <JavaScriptCore/JSClassRef.h>
// #include <JavaScriptCore/JSContextInternal.h>
+#include <JavaScriptCore/CatchScope.h>
#include <JavaScriptCore/JSInternalPromise.h>
#include <JavaScriptCore/JSLock.h>
#include <JavaScriptCore/JSMap.h>
@@ -50,6 +51,7 @@
#include <JavaScriptCore/VM.h>
#include <JavaScriptCore/VMEntryScope.h>
#include <JavaScriptCore/WasmFaultSignalHandler.h>
+#include <wtf/Gigacage.h>
#include <wtf/StdLibExtras.h>
#include <wtf/URL.h>
#include <wtf/text/ExternalStringImpl.h>
@@ -58,8 +60,6 @@
#include <wtf/text/StringView.h>
#include <wtf/text/WTFString.h>
-#include <wtf/Gigacage.h>
-
#include <cstdlib>
#include <exception>
#include <iostream>
@@ -130,7 +130,7 @@ const JSC::GlobalObjectMethodTable GlobalObject::s_globalObjectMethodTable = {
&supportsRichSourceInfo,
&shouldInterruptScript,
&javaScriptRuntimeFlags,
- nullptr, // queueTaskToEventLoop
+ &queueMicrotaskToEventLoop, // queueTaskToEventLoop
nullptr, // &shouldInterruptScriptBeforeTimeout,
&moduleLoaderImportModule, // moduleLoaderImportModule
&moduleLoaderResolve, // moduleLoaderResolve
@@ -454,4 +454,11 @@ JSC::JSValue GlobalObject::moduleLoaderEvaluate(JSGlobalObject *globalObject,
return result;
}
+void GlobalObject::queueMicrotaskToEventLoop(JSC::JSGlobalObject &global,
+ Ref<JSC::Microtask> &&task) {
+
+ Zig__GlobalObject__queueMicrotaskToEventLoop(
+ &global, &JSMicrotaskCallback::create(global, WTFMove(task)).leakRef());
+}
+
} // namespace Zig \ No newline at end of file
diff --git a/src/javascript/jsc/bindings/ZigGlobalObject.h b/src/javascript/jsc/bindings/ZigGlobalObject.h
index 4b1e1e935..64ece0c64 100644
--- a/src/javascript/jsc/bindings/ZigGlobalObject.h
+++ b/src/javascript/jsc/bindings/ZigGlobalObject.h
@@ -10,10 +10,10 @@ class Identifier;
} // namespace JSC
#include "ZigConsoleClient.h"
+#include <JavaScriptCore/CatchScope.h>
#include <JavaScriptCore/JSGlobalObject.h>
#include <JavaScriptCore/JSTypeInfo.h>
#include <JavaScriptCore/Structure.h>
-
namespace Zig {
class GlobalObject : public JSC::JSGlobalObject {
@@ -45,6 +45,7 @@ class GlobalObject : public JSC::JSGlobalObject {
static void reportUncaughtExceptionAtEventLoop(JSGlobalObject *, JSC::Exception *);
+ static void queueMicrotaskToEventLoop(JSC::JSGlobalObject &global, Ref<JSC::Microtask> &&task);
static JSC::JSInternalPromise *moduleLoaderImportModule(JSGlobalObject *, JSC::JSModuleLoader *,
JSC::JSString *moduleNameValue,
JSC::JSValue parameters,
@@ -69,4 +70,29 @@ class GlobalObject : public JSC::JSGlobalObject {
: JSC::JSGlobalObject(vm, structure, &s_globalObjectMethodTable) {}
};
+class JSMicrotaskCallback : public RefCounted<JSMicrotaskCallback> {
+ public:
+ static Ref<JSMicrotaskCallback> create(JSC::JSGlobalObject &globalObject,
+ Ref<JSC::Microtask> &&task) {
+ return adoptRef(*new JSMicrotaskCallback(globalObject, WTFMove(task)));
+ }
+
+ void call() {
+ auto protectedThis{makeRef(*this)};
+ JSC::VM &vm = m_globalObject->vm();
+ JSC::JSLockHolder lock(vm);
+ auto scope = DECLARE_CATCH_SCOPE(vm);
+ auto task = &m_task.get();
+ task->run(m_globalObject.get());
+ scope.assertNoExceptionExceptTermination();
+ }
+
+ private:
+ JSMicrotaskCallback(JSC::JSGlobalObject &globalObject, Ref<JSC::Microtask> &&task)
+ : m_globalObject{globalObject.vm(), &globalObject}, m_task{WTFMove(task)} {}
+
+ JSC::Strong<JSC::JSGlobalObject> m_globalObject;
+ Ref<JSC::Microtask> m_task;
+};
+
} // namespace Zig
diff --git a/src/javascript/jsc/bindings/bindings.zig b/src/javascript/jsc/bindings/bindings.zig
index 02f026ea5..4973a0400 100644
--- a/src/javascript/jsc/bindings/bindings.zig
+++ b/src/javascript/jsc/bindings/bindings.zig
@@ -314,6 +314,12 @@ pub fn NewGlobalObject(comptime Type: type) type {
return JSValue.jsUndefined();
}
+ pub fn queueMicrotaskToEventLoop(global: *JSGlobalObject, microtask: *Microtask) callconv(.C) void {
+ if (comptime @hasDecl(Type, "queueMicrotaskToEventLoop")) {
+ @call(.{ .modifier = .always_inline }, Type.queueMicrotaskToEventLoop, .{ global, microtask });
+ }
+ }
+
pub fn onCrash() callconv(.C) void {
if (comptime @hasDecl(Type, "onCrash")) {
return @call(.{ .modifier = .always_inline }, Type.onCrash, .{});
@@ -1504,6 +1510,20 @@ pub const JSValue = enum(i64) {
pub const Extern = [_][]const u8{ "getLengthOfArray", "toZigString", "createStringArray", "createEmptyObject", "putRecord", "asPromise", "isClass", "getNameProperty", "getClassName", "getErrorsProperty", "toInt32", "toBoolean", "isInt32", "isIterable", "forEach", "isAggregateError", "toZigException", "isException", "toWTFString", "hasProperty", "getPropertyNames", "getDirect", "putDirect", "get", "getIfExists", "asString", "asObject", "asNumber", "isError", "jsNull", "jsUndefined", "jsTDZValue", "jsBoolean", "jsDoubleNumber", "jsNumberFromDouble", "jsNumberFromChar", "jsNumberFromU16", "jsNumberFromInt32", "jsNumberFromInt64", "jsNumberFromUint64", "isUndefined", "isNull", "isUndefinedOrNull", "isBoolean", "isAnyInt", "isUInt32AsAnyInt", "isInt32AsAnyInt", "isNumber", "isString", "isBigInt", "isHeapBigInt", "isBigInt32", "isSymbol", "isPrimitive", "isGetterSetter", "isCustomGetterSetter", "isObject", "isCell", "asCell", "toString", "toStringOrNull", "toPropertyKey", "toPropertyKeyValue", "toObject", "toString", "getPrototype", "getPropertyByPropertyName", "eqlValue", "eqlCell", "isCallable" };
};
+extern "c" fn Microtask__run(*Microtask, *JSGlobalObject) void;
+
+pub const Microtask = opaque {
+ pub const name = "Zig::JSMicrotaskCallback";
+
+ pub fn run(this: *Microtask, global_object: *JSGlobalObject) void {
+ if (comptime is_bindgen) {
+ return;
+ }
+
+ return Microtask__run(this, global_object);
+ }
+};
+
pub const PropertyName = extern struct {
pub const shim = Shimmer("JSC", "PropertyName", @This());
bytes: shim.Bytes,
diff --git a/src/javascript/jsc/bindings/exports.zig b/src/javascript/jsc/bindings/exports.zig
index b325a1208..00f58e745 100644
--- a/src/javascript/jsc/bindings/exports.zig
+++ b/src/javascript/jsc/bindings/exports.zig
@@ -109,6 +109,13 @@ pub const ZigGlobalObject = extern struct {
return @call(.{ .modifier = .always_inline }, Interface.onCrash, .{});
}
+ pub fn queueMicrotaskToEventLoop(global: *JSGlobalObject, microtask: *Microtask) callconv(.C) void {
+ if (comptime is_bindgen) {
+ unreachable;
+ }
+ return @call(.{ .modifier = .always_inline }, Interface.queueMicrotaskToEventLoop, .{ global, microtask });
+ }
+
pub const Export = shim.exportFunctions(
.{
.@"import" = import,
@@ -119,6 +126,7 @@ pub const ZigGlobalObject = extern struct {
.@"reportUncaughtException" = reportUncaughtException,
.@"createImportMetaProperties" = createImportMetaProperties,
.@"onCrash" = onCrash,
+ .@"queueMicrotaskToEventLoop" = queueMicrotaskToEventLoop,
},
);
@@ -132,6 +140,7 @@ pub const ZigGlobalObject = extern struct {
@export(reportUncaughtException, .{ .name = Export[4].symbol_name });
@export(createImportMetaProperties, .{ .name = Export[5].symbol_name });
@export(onCrash, .{ .name = Export[6].symbol_name });
+ @export(queueMicrotaskToEventLoop, .{ .name = Export[7].symbol_name });
}
};
diff --git a/src/javascript/jsc/bindings/headers-cpp.h b/src/javascript/jsc/bindings/headers-cpp.h
index 8b050bbfe..ec3a8cb2c 100644
--- a/src/javascript/jsc/bindings/headers-cpp.h
+++ b/src/javascript/jsc/bindings/headers-cpp.h
@@ -1,4 +1,4 @@
-//-- AUTOGENERATED FILE -- 1636158703
+//-- AUTOGENERATED FILE -- 1639731411
// clang-format off
#pragma once
diff --git a/src/javascript/jsc/bindings/headers-handwritten.h b/src/javascript/jsc/bindings/headers-handwritten.h
index a8d53f6e3..e07eca6b6 100644
--- a/src/javascript/jsc/bindings/headers-handwritten.h
+++ b/src/javascript/jsc/bindings/headers-handwritten.h
@@ -97,4 +97,5 @@ const JSErrorCode JSErrorCodeUserErrorCode = 254;
extern "C" ZigErrorCode Zig_ErrorCodeParserError;
extern "C" void ZigString__free(const unsigned char *ptr, size_t len, void *allocator);
+extern "C" void Microtask__run(void *ptr, void *global);
#endif
diff --git a/src/javascript/jsc/bindings/headers.h b/src/javascript/jsc/bindings/headers.h
index 8c2586ba8..3633c7ca1 100644
--- a/src/javascript/jsc/bindings/headers.h
+++ b/src/javascript/jsc/bindings/headers.h
@@ -1,5 +1,5 @@
// clang-format: off
-//-- AUTOGENERATED FILE -- 1636158703
+//-- AUTOGENERATED FILE -- 1639731411
#pragma once
#include <stddef.h>
@@ -108,6 +108,7 @@ typedef void* JSClassRef;
typedef ZigException ZigException;
typedef struct JSC__SetIteratorPrototype JSC__SetIteratorPrototype; // JSC::SetIteratorPrototype
typedef bJSC__SourceCode JSC__SourceCode; // JSC::SourceCode
+ typedef struct Zig__JSMicrotaskCallback Zig__JSMicrotaskCallback; // Zig::JSMicrotaskCallback
typedef bJSC__JSCell JSC__JSCell; // JSC::JSCell
typedef struct JSC__BigIntPrototype JSC__BigIntPrototype; // JSC::BigIntPrototype
typedef struct JSC__GeneratorFunctionPrototype JSC__GeneratorFunctionPrototype; // JSC::GeneratorFunctionPrototype
@@ -175,6 +176,9 @@ typedef void* JSClassRef;
class StringView;
class ExternalStringImpl;
}
+ namespace Zig {
+ class JSMicrotaskCallback;
+ }
namespace Inspector {
class ScriptArguments;
}
@@ -226,6 +230,7 @@ typedef void* JSClassRef;
using WTF__String = WTF::String;
using WTF__StringView = WTF::StringView;
using WTF__ExternalStringImpl = WTF::ExternalStringImpl;
+ using Zig__JSMicrotaskCallback = Zig::JSMicrotaskCallback;
using Inspector__ScriptArguments = Inspector::ScriptArguments;
#endif
@@ -588,6 +593,7 @@ ZIG_DECL void Zig__GlobalObject__fetch(ErrorableResolvedSource* arg0, JSC__JSGlo
ZIG_DECL ErrorableZigString Zig__GlobalObject__import(JSC__JSGlobalObject* arg0, ZigString* arg1, ZigString* arg2);
ZIG_DECL void Zig__GlobalObject__onCrash();
ZIG_DECL JSC__JSValue Zig__GlobalObject__promiseRejectionTracker(JSC__JSGlobalObject* arg0, JSC__JSPromise* arg1, uint32_t JSPromiseRejectionOperation2);
+ZIG_DECL void Zig__GlobalObject__queueMicrotaskToEventLoop(JSC__JSGlobalObject* arg0, Zig__JSMicrotaskCallback* arg1);
ZIG_DECL JSC__JSValue Zig__GlobalObject__reportUncaughtException(JSC__JSGlobalObject* arg0, JSC__Exception* arg1);
ZIG_DECL void Zig__GlobalObject__resolve(ErrorableZigString* arg0, JSC__JSGlobalObject* arg1, ZigString* arg2, ZigString* arg3);
diff --git a/src/javascript/jsc/bindings/headers.zig b/src/javascript/jsc/bindings/headers.zig
index e02044721..070d2ad05 100644
--- a/src/javascript/jsc/bindings/headers.zig
+++ b/src/javascript/jsc/bindings/headers.zig
@@ -48,6 +48,8 @@ pub const JSC__JSPromise = bJSC__JSPromise;
pub const JSC__SetIteratorPrototype = struct_JSC__SetIteratorPrototype;
pub const JSC__SourceCode = bJSC__SourceCode;
+
+pub const Zig__JSMicrotaskCallback = struct_Zig__JSMicrotaskCallback;
pub const JSC__JSCell = bJSC__JSCell;
pub const JSC__BigIntPrototype = struct_JSC__BigIntPrototype;
diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig
index e05153366..d864c2d17 100644
--- a/src/javascript/jsc/javascript.zig
+++ b/src/javascript/jsc/javascript.zig
@@ -728,8 +728,10 @@ pub const Module = struct {
};
const FetchTasklet = Fetch.FetchTasklet;
+const TaggedPointerUnion = @import("../../tagged_pointer.zig").TaggedPointerUnion;
pub const Task = TaggedPointerUnion(.{
FetchTasklet,
+ Microtask,
});
// If you read JavascriptCore/API/JSVirtualMachine.mm - https://github.com/WebKit/WebKit/blob/acff93fb303baa670c055cb24c2bad08691a01a0/Source/JavaScriptCore/API/JSVirtualMachine.mm#L101
@@ -768,6 +770,50 @@ pub const VirtualMachine = struct {
origin_timer: std.time.Timer = undefined,
ready_tasks_count: std.atomic.Atomic(u32) = std.atomic.Atomic(u32).init(0),
+ pending_tasks_count: std.atomic.Atomic(u32) = std.atomic.Atomic(u32).init(0),
+ microtasks_queue: std.ArrayList(Task) = std.ArrayList(Task).init(default_allocator),
+
+ pub fn enqueueTask(this: *VirtualMachine, task: Task) !void {
+ _ = this.pending_tasks_count.fetchAdd(1, .Monotonic);
+ try this.microtasks_queue.append(task);
+ }
+
+ pub fn tick(this: *VirtualMachine) void {
+ this.global.vm().drainMicrotasks();
+ _ = this.eventLoopTick();
+ }
+
+ // 👶👶👶 event loop 👶👶👶
+ pub fn eventLoopTick(this: *VirtualMachine) u32 {
+ var finished: u32 = 0;
+ var i: usize = 0;
+ while (i < this.microtasks_queue.items.len) {
+ var task: Task = this.microtasks_queue.items[i];
+ switch (task.tag()) {
+ .Microtask => {
+ var micro: *Microtask = task.get(Microtask).?;
+ _ = this.microtasks_queue.swapRemove(i);
+ micro.run(this.global);
+
+ finished += 1;
+ continue;
+ },
+ .FetchTasklet => {
+ var fetch_task: *Fetch.FetchTasklet = task.get(Fetch.FetchTasklet).?;
+ if (fetch_task.status == .done) {
+ _ = this.ready_tasks_count.fetchSub(1, .Monotonic);
+ _ = this.microtasks_queue.swapRemove(i);
+ fetch_task.onDone();
+ finished += 1;
+ continue;
+ }
+ },
+ else => unreachable,
+ }
+ i += 1;
+ }
+ return finished;
+ }
pub const MacroMap = std.AutoArrayHashMap(i32, js.JSObjectRef);
@@ -1219,7 +1265,15 @@ pub const VirtualMachine = struct {
ret.path = result_path.text;
}
+ pub fn queueMicrotaskToEventLoop(
+ global: *JSGlobalObject,
+ microtask: *Microtask,
+ ) void {
+ std.debug.assert(VirtualMachine.vm_loaded);
+ std.debug.assert(VirtualMachine.vm.global == global);
+ vm.enqueueTask(Task.init(microtask)) catch unreachable;
+ }
pub fn resolve(res: *ErrorableZigString, global: *JSGlobalObject, specifier: ZigString, source: ZigString) void {
var result = ResolveFunctionResult{ .path = "", .result = null };
@@ -1418,10 +1472,10 @@ pub const VirtualMachine = struct {
if (this.node_modules != null) {
promise = JSModuleLoader.loadAndEvaluateModule(this.global, ZigString.init(std.mem.span(bun_file_import_path)));
- this.global.vm().drainMicrotasks();
+ this.tick();
while (promise.status(this.global.vm()) == JSPromise.Status.Pending) {
- this.global.vm().drainMicrotasks();
+ this.tick();
}
if (promise.status(this.global.vm()) == JSPromise.Status.Rejected) {
@@ -1433,10 +1487,10 @@ pub const VirtualMachine = struct {
promise = JSModuleLoader.loadAndEvaluateModule(this.global, ZigString.init(std.mem.span(main_file_name)));
- this.global.vm().drainMicrotasks();
+ this.tick();
while (promise.status(this.global.vm()) == JSPromise.Status.Pending) {
- this.global.vm().drainMicrotasks();
+ this.tick();
}
return promise;
@@ -1453,30 +1507,13 @@ pub const VirtualMachine = struct {
var entry_point = entry_point_entry.value_ptr.*;
var promise: *JSInternalPromise = undefined;
- // We first import the node_modules bundle. This prevents any potential TDZ issues.
- // The contents of the node_modules bundle are lazy, so hopefully this should be pretty quick.
- // if (this.node_modules != null) {
- // promise = JSModuleLoader.loadAndEvaluateModule(this.global, ZigString.init(std.mem.span(bun_file_import_path)));
-
- // this.global.vm().drainMicrotasks();
-
- // while (promise.status(this.global.vm()) == JSPromise.Status.Pending) {
- // this.global.vm().drainMicrotasks();
- // }
-
- // if (promise.status(this.global.vm()) == JSPromise.Status.Rejected) {
- // return promise;
- // }
-
- // _ = promise.result(this.global.vm());
- // }
promise = JSModuleLoader.loadAndEvaluateModule(this.global, ZigString.init(entry_point.source.path.text));
- this.global.vm().drainMicrotasks();
+ this.tick();
while (promise.status(this.global.vm()) == JSPromise.Status.Pending) {
- this.global.vm().drainMicrotasks();
+ this.tick();
}
return promise;
@@ -1914,7 +1951,7 @@ pub const EventListenerMixin = struct {
var result = js.JSObjectCallAsFunctionReturnValue(vm.global.ref(), listener_ref, null, 1, &fetch_args);
var promise = JSPromise.resolvedPromise(vm.global, result);
- vm.global.vm().drainMicrotasks();
+ vm.tick();
if (fetch_event.rejected) return;
@@ -1925,7 +1962,7 @@ pub const EventListenerMixin = struct {
_ = promise.result(vm.global.vm());
}
- vm.global.vm().drainMicrotasks();
+ vm.tick();
if (fetch_event.request_context.has_called_done) {
break;
diff --git a/src/javascript/jsc/webcore/response.zig b/src/javascript/jsc/webcore/response.zig
index d4c5f5936..248c3a4a7 100644
--- a/src/javascript/jsc/webcore/response.zig
+++ b/src/javascript/jsc/webcore/response.zig
@@ -7,8 +7,11 @@ usingnamespace @import("../bindings/bindings.zig");
const ZigURL = @import("../../../query_string_map.zig").URL;
const HTTPClient = @import("http");
const NetworkThread = @import("network_thread");
+
const Method = @import("../../../http/method.zig").Method;
+const ObjectPool = @import("../../../pool.zig").ObjectPool;
+
const picohttp = @import("picohttp");
pub const Response = struct {
pub const Class = NewClass(
@@ -414,10 +417,28 @@ pub const Fetch = struct {
const fetch_error_cant_fetch_same_origin = "fetch to same-origin on the server is not supported yet - sorry! (it would just hang forever)";
pub const FetchTasklet = struct {
- promise: *JSPromise = undefined,
+ promise: *JSInternalPromise = undefined,
http: HTTPClient.AsyncHTTP = undefined,
status: Status = Status.pending,
javascript_vm: *VirtualMachine = undefined,
+ global_this: *JSGlobalObject = undefined,
+
+ empty_request_body: MutableString = undefined,
+ pooled_body: *BodyPool.Node = undefined,
+ this_object: js.JSObjectRef = null,
+ resolve: js.JSObjectRef = null,
+ reject: js.JSObjectRef = null,
+ context: FetchTaskletContext = undefined,
+
+ const Pool = ObjectPool(FetchTasklet, init);
+ const BodyPool = ObjectPool(MutableString, MutableString.init2048);
+ pub const FetchTaskletContext = struct {
+ tasklet: *FetchTasklet,
+ };
+
+ pub fn init(allocator: *std.mem.Allocator) anyerror!FetchTasklet {
+ return FetchTasklet{};
+ }
pub const Status = enum(u8) {
pending,
@@ -425,10 +446,172 @@ pub const Fetch = struct {
done,
};
+ pub fn onDone(this: *FetchTasklet) void {
+ var args = [1]js.JSValueRef{undefined};
+
+ var callback_object = switch (this.http.state.load(.Monotonic)) {
+ .success => this.resolve,
+ .fail => this.reject,
+ else => unreachable,
+ };
+
+ args[0] = switch (this.http.state.load(.Monotonic)) {
+ .success => this.onResolve().asObjectRef(),
+ .fail => this.onReject().asObjectRef(),
+ else => unreachable,
+ };
+
+ _ = js.JSObjectCallAsFunction(this.global_this.ref(), callback_object, null, 1, &args, null);
+
+ this.release();
+ }
+
+ pub fn reset(this: *FetchTasklet) void {}
+
+ pub fn release(this: *FetchTasklet) void {
+ js.JSValueUnprotect(this.global_this.ref(), this.resolve);
+ js.JSValueUnprotect(this.global_this.ref(), this.reject);
+ js.JSValueUnprotect(this.global_this.ref(), this.this_object);
+
+ this.global_this = undefined;
+ this.javascript_vm = undefined;
+ this.promise = undefined;
+ this.status = Status.pending;
+ var pooled = this.pooled_body;
+ BodyPool.release(pooled);
+ this.pooled_body = undefined;
+ this.http = undefined;
+ this.this_object = null;
+ this.resolve = null;
+ this.reject = null;
+ Pool.release(@fieldParentPtr(Pool.Node, "data", this));
+ }
+
+ pub const FetchResolver = struct {
+ pub fn call(
+ ctx: js.JSContextRef,
+ function: js.JSObjectRef,
+ thisObject: js.JSObjectRef,
+ arguments_len: usize,
+ arguments: [*c]const js.JSValueRef,
+ exception: js.ExceptionRef,
+ ) callconv(.C) js.JSObjectRef {
+ return JSPrivateDataPtr.from(js.JSObjectGetPrivate(arguments[0]))
+ .get(FetchTaskletContext).?.tasklet.onResolve().asObjectRef();
+ // return js.JSObjectGetPrivate(arguments[0]).? .tasklet.onResolve().asObjectRef();
+ }
+ };
+
+ pub const FetchRejecter = struct {
+ pub fn call(
+ ctx: js.JSContextRef,
+ function: js.JSObjectRef,
+ thisObject: js.JSObjectRef,
+ arguments_len: usize,
+ arguments: [*c]const js.JSValueRef,
+ exception: js.ExceptionRef,
+ ) callconv(.C) js.JSObjectRef {
+ return JSPrivateDataPtr.from(js.JSObjectGetPrivate(arguments[0]))
+ .get(FetchTaskletContext).?.tasklet.onReject().asObjectRef();
+ }
+ };
+
+ pub fn onReject(this: *FetchTasklet) JSValue {
+ const fetch_error = std.fmt.allocPrint(
+ default_allocator,
+ "Fetch error: {s}\nURL: \"{s}\"",
+ .{
+ @errorName(this.http.err orelse error.HTTPFail),
+ this.http.url.href,
+ },
+ ) catch unreachable;
+ return ZigString.init(fetch_error).toErrorInstance(this.global_this);
+ }
+
+ pub fn onResolve(this: *FetchTasklet) JSValue {
+ var allocator = default_allocator;
+ var http_response = this.http.response.?;
+ var response_headers = Headers.fromPicoHeaders(allocator, http_response.headers) catch unreachable;
+ response_headers.guard = .immutable;
+ var response = allocator.create(Response) catch unreachable;
+ var duped = allocator.dupe(u8, this.http.response_buffer.toOwnedSlice()) catch unreachable;
+
+ response.* = Response{
+ .allocator = allocator,
+ .status_text = allocator.dupe(u8, http_response.status) catch unreachable,
+ .body = .{
+ .init = .{
+ .headers = response_headers,
+ .status_code = @truncate(u16, http_response.status_code),
+ },
+ .value = .{
+ .Unconsumed = 0,
+ },
+ .ptr = duped.ptr,
+ .len = duped.len,
+ .ptr_allocator = allocator,
+ },
+ };
+ return JSValue.fromRef(Response.Class.make(@ptrCast(js.JSContextRef, this.global_this), response));
+ }
+
+ pub fn get(
+ allocator: *std.mem.Allocator,
+ method: Method,
+ url: ZigURL,
+ headers: Headers.Entries,
+ headers_buf: string,
+ request_body: ?*MutableString,
+ timeout: usize,
+ ) !*FetchTasklet.Pool.Node {
+ var linked_list = FetchTasklet.Pool.get(allocator);
+ linked_list.data.javascript_vm = VirtualMachine.vm;
+ linked_list.data.empty_request_body = MutableString.init(allocator, 0) catch unreachable;
+ linked_list.data.pooled_body = BodyPool.get(allocator);
+ linked_list.data.http = try HTTPClient.AsyncHTTP.init(
+ allocator,
+ method,
+ url,
+ headers,
+ headers_buf,
+ &linked_list.data.pooled_body.data,
+ request_body orelse &linked_list.data.empty_request_body,
+
+ timeout,
+ );
+ linked_list.data.context = .{ .tasklet = &linked_list.data };
+
+ return linked_list;
+ }
+
+ pub fn queue(
+ allocator: *std.mem.Allocator,
+ global: *JSGlobalObject,
+ method: Method,
+ url: ZigURL,
+ headers: Headers.Entries,
+ headers_buf: string,
+ request_body: ?*MutableString,
+ timeout: usize,
+ ) !*FetchTasklet.Pool.Node {
+ var node = try get(allocator, method, url, headers, headers_buf, request_body, timeout);
+ node.data.promise = JSInternalPromise.create(global);
+
+ node.data.global_this = global;
+ node.data.http.callback = callback;
+ var batch = NetworkThread.Batch{};
+ node.data.http.schedule(allocator, &batch);
+ NetworkThread.global.pool.schedule(batch);
+
+ try VirtualMachine.vm.enqueueTask(Task.init(&node.data));
+ return node;
+ }
+
pub fn callback(http_: *HTTPClient.AsyncHTTP, sender: *HTTPClient.AsyncHTTP.HTTPSender) void {
var task: *FetchTasklet = @fieldParentPtr(FetchTasklet, "http", http_);
- @atomicStore(Status.done, &task.status, Status.done, .Monotonic);
- task.javascript_vm.pending_tasks.fetchAdd(.Monotonic, 1);
+ @atomicStore(Status, &task.status, Status.done, .Monotonic);
+ _ = task.javascript_vm.ready_tasks_count.fetchAdd(1, .Monotonic);
+ _ = task.javascript_vm.pending_tasks_count.fetchSub(1, .Monotonic);
sender.release();
}
};
@@ -466,8 +649,6 @@ pub const Fetch = struct {
url_str = getAllocator(ctx).dupe(u8, url_str) catch unreachable;
}
- defer getAllocator(ctx).free(url_str);
-
NetworkThread.init() catch @panic("Failed to start network thread");
const url = ZigURL.parse(url_str);
@@ -478,6 +659,7 @@ pub const Fetch = struct {
var headers: ?Headers = null;
var body: string = "";
+ var method = Method.GET;
if (arguments.len >= 2 and js.JSValueIsObject(ctx, arguments[1])) {
var array = js.JSObjectCopyPropertyNames(ctx, arguments[1]);
@@ -524,7 +706,7 @@ pub const Fetch = struct {
defer js.JSStringRelease(string_ref);
var method_name_buf: [16]u8 = undefined;
var method_name = method_name_buf[0..js.JSStringGetUTF8CString(string_ref, &method_name_buf, method_name_buf.len)];
- http_client.method = Method.which(method_name) orelse http_client.method;
+ method = Method.which(method_name) orelse method;
}
}
},
@@ -533,56 +715,39 @@ pub const Fetch = struct {
}
}
+ var header_entries: Headers.Entries = .{};
+ var header_buf: string = "";
+
if (headers) |head| {
- http_client.header_entries = head.entries;
- http_client.header_buf = head.buf.items;
+ header_entries = head.entries;
+ header_buf = head.buf.items;
}
+ var resolve = js.JSObjectMakeFunctionWithCallback(ctx, null, Fetch.FetchTasklet.FetchResolver.call);
+ var reject = js.JSObjectMakeFunctionWithCallback(ctx, null, Fetch.FetchTasklet.FetchRejecter.call);
- if (fetch_body_string_loaded) {
- fetch_body_string.reset();
- } else {
- fetch_body_string = MutableString.init(VirtualMachine.vm.allocator, 0) catch unreachable;
- fetch_body_string_loaded = true;
- }
+ js.JSValueProtect(ctx, resolve);
+ js.JSValueProtect(ctx, reject);
- var http_response = http_client.send(body, &fetch_body_string) catch |err| {
- const fetch_error = std.fmt.allocPrint(
- getAllocator(ctx),
- "Fetch error: {s}\nURL: \"{s}\"",
- .{
- @errorName(err),
- url_str,
- },
- ) catch unreachable;
- return JSPromise.rejectedPromiseValue(VirtualMachine.vm.global, ZigString.init(fetch_error).toErrorInstance(VirtualMachine.vm.global)).asRef();
- };
+ // var resolve = FetchTasklet.FetchResolver.Class.make(ctx: js.JSContextRef, ptr: *ZigType)
+ var queued = FetchTasklet.queue(
+ default_allocator,
+ VirtualMachine.vm.global,
+ method,
+ url,
+ header_entries,
+ header_buf,
+ null,
+ std.time.ns_per_hour,
+ ) catch unreachable;
+ queued.data.this_object = js.JSObjectMake(ctx, null, JSPrivateDataPtr.init(&queued.data.context).ptr());
+ js.JSValueProtect(ctx, queued.data.this_object);
- var response_headers = Headers.fromPicoHeaders(getAllocator(ctx), http_response.headers) catch unreachable;
- response_headers.guard = .immutable;
- var response = getAllocator(ctx).create(Response) catch unreachable;
- var allocator = getAllocator(ctx);
- var duped = allocator.dupeZ(u8, fetch_body_string.list.items) catch unreachable;
- response.* = Response{
- .allocator = allocator,
- .status_text = allocator.dupe(u8, http_response.status) catch unreachable,
- .body = .{
- .init = .{
- .headers = response_headers,
- .status_code = @truncate(u16, http_response.status_code),
- },
- .value = .{
- .Unconsumed = 0,
- },
- .ptr = duped.ptr,
- .len = duped.len,
- .ptr_allocator = allocator,
- },
- };
+ var promise = js.JSObjectMakeDeferredPromise(ctx, &resolve, &reject, exception);
+ queued.data.reject = reject;
+ queued.data.resolve = resolve;
- return JSPromise.resolvedPromiseValue(
- VirtualMachine.vm.global,
- JSValue.fromRef(Response.Class.make(ctx, response)),
- ).asRef();
+ return promise;
+ // queued.data.promise.create(globalThis: *JSGlobalObject)
}
};
@@ -1511,6 +1676,7 @@ pub const FetchEvent = struct {
response: ?*Response = null,
request_context: *http.RequestContext,
request: Request,
+ pending_promise: ?*JSInternalPromise = null,
onPromiseRejectionCtx: *c_void = undefined,
onPromiseRejectionHandler: ?fn (ctx: *c_void, err: anyerror, fetch_event: *FetchEvent, value: JSValue) void = null,
@@ -1589,47 +1755,56 @@ pub const FetchEvent = struct {
if (this.request_context.has_called_done) return js.JSValueMakeUndefined(ctx);
// A Response or a Promise that resolves to a Response. Otherwise, a network error is returned to Fetch.
- if (arguments.len == 0 or !Response.Class.loaded) {
+ if (arguments.len == 0 or !Response.Class.loaded or !js.JSValueIsObject(ctx, arguments[0])) {
JSError(getAllocator(ctx), "event.respondWith() must be a Response or a Promise<Response>.", .{}, ctx, exception);
this.request_context.sendInternalError(error.respondWithWasEmpty) catch {};
return js.JSValueMakeUndefined(ctx);
}
- var resolved = JSInternalPromise.resolvedPromise(VirtualMachine.vm.global, JSValue.fromRef(arguments[0]));
-
- var status = resolved.status(VirtualMachine.vm.global.vm());
+ var arg = arguments[0];
- if (status == .Pending) {
- VirtualMachine.vm.global.vm().drainMicrotasks();
+ if (!js.JSValueIsObjectOfClass(ctx, arg, Response.Class.ref)) {
+ this.pending_promise = this.pending_promise orelse JSInternalPromise.resolvedPromise(VirtualMachine.vm.global, JSValue.fromRef(arguments[0]));
}
- status = resolved.status(VirtualMachine.vm.global.vm());
+ if (this.pending_promise) |promise| {
+ var status = promise.status(VirtualMachine.vm.global.vm());
- switch (status) {
- .Fulfilled => {},
- else => {
- this.rejected = true;
- this.onPromiseRejectionHandler.?(
- this.onPromiseRejectionCtx,
- error.PromiseRejection,
- this,
- resolved.result(VirtualMachine.vm.global.vm()),
- );
- return js.JSValueMakeUndefined(ctx);
- },
- }
+ if (status == .Pending) {
+ VirtualMachine.vm.tick();
+ status = promise.status(VirtualMachine.vm.global.vm());
+ }
+
+ switch (status) {
+ .Fulfilled => {},
+ else => {
+ this.rejected = true;
+ this.pending_promise = null;
+ this.onPromiseRejectionHandler.?(
+ this.onPromiseRejectionCtx,
+ error.PromiseRejection,
+ this,
+ promise.result(VirtualMachine.vm.global.vm()),
+ );
+ return js.JSValueMakeUndefined(ctx);
+ },
+ }
- var arg = resolved.result(VirtualMachine.vm.global.vm()).asObjectRef();
+ arg = promise.result(VirtualMachine.vm.global.vm()).asRef();
+ }
if (!js.JSValueIsObjectOfClass(ctx, arg, Response.Class.ref)) {
this.rejected = true;
+ this.pending_promise = null;
JSError(getAllocator(ctx), "event.respondWith() must be a Response or a Promise<Response>.", .{}, ctx, exception);
this.onPromiseRejectionHandler.?(this.onPromiseRejectionCtx, error.RespondWithInvalidType, this, JSValue.fromRef(exception.*));
+
return js.JSValueMakeUndefined(ctx);
}
var response: *Response = GetJSPrivateData(Response, arg) orelse {
this.rejected = true;
+ this.pending_promise = null;
JSError(getAllocator(ctx), "event.respondWith()'s Response object was invalid. This may be an internal error.", .{}, ctx, exception);
this.onPromiseRejectionHandler.?(this.onPromiseRejectionCtx, error.RespondWithInvalidTypeInternal, this, JSValue.fromRef(exception.*));
return js.JSValueMakeUndefined(ctx);
@@ -1650,6 +1825,7 @@ pub const FetchEvent = struct {
}
}
+ defer this.pending_promise = null;
var needs_mime_type = true;
var content_length: ?usize = null;
if (response.body.init.headers) |*headers| {
diff --git a/src/js_ast.zig b/src/js_ast.zig
index 860e980e9..cebad7115 100644
--- a/src/js_ast.zig
+++ b/src/js_ast.zig
@@ -7101,7 +7101,7 @@ pub const Macro = struct {
js.JSValueProtect(macro.vm.global.ref(), result.asRef());
defer js.JSValueUnprotect(macro.vm.global.ref(), result.asRef());
var promise = JSC.JSPromise.resolvedPromise(macro.vm.global, result);
- macro.vm.global.vm().drainMicrotasks();
+ _ = macro.vm.tick();
if (promise.status(macro.vm.global.vm()) == .Rejected) {
macro.vm.defaultErrorHandler(promise.result(macro.vm.global.vm()), null);
diff --git a/src/pool.zig b/src/pool.zig
index b4fe0eb29..f37b62162 100644
--- a/src/pool.zig
+++ b/src/pool.zig
@@ -6,7 +6,9 @@ pub fn ObjectPool(comptime Type: type, comptime Init: (fn (allocator: *std.mem.A
// mimalloc crashes on realloc across threads
threadlocal var list: LinkedList = undefined;
threadlocal var loaded: bool = false;
- pub fn get(allocator: *std.mem.Allocator) *LinkedList.Node {
+
+ pub const Node = LinkedList.Node;
+ pub fn get(allocator: *std.mem.Allocator) *Node {
if (loaded) {
if (list.popFirst()) |node| {
node.data.reset();
@@ -14,8 +16,8 @@ pub fn ObjectPool(comptime Type: type, comptime Init: (fn (allocator: *std.mem.A
}
}
- var new_node = allocator.create(LinkedList.Node) catch unreachable;
- new_node.* = LinkedList.Node{
+ var new_node = allocator.create(Node) catch unreachable;
+ new_node.* = Node{
.data = Init(
allocator,
) catch unreachable,
@@ -24,7 +26,7 @@ pub fn ObjectPool(comptime Type: type, comptime Init: (fn (allocator: *std.mem.A
return new_node;
}
- pub fn release(node: *LinkedList.Node) void {
+ pub fn release(node: *Node) void {
if (loaded) {
list.prepend(node);
return;
diff --git a/src/runtime.version b/src/runtime.version
index 9a96f51a3..936ae3d67 100644
--- a/src/runtime.version
+++ b/src/runtime.version
@@ -1 +1 @@
-54c050533edcf4b5 \ No newline at end of file
+b79c80cf594c185e \ No newline at end of file
diff --git a/src/thread_pool.zig b/src/thread_pool.zig
index 19ea14e2d..4df199006 100644
--- a/src/thread_pool.zig
+++ b/src/thread_pool.zig
@@ -247,7 +247,7 @@ noinline fn wait(self: *ThreadPool, _is_waking: bool) error{Shutdown}!bool {
}
} else {
if (self.io) |io| {
- const HTTP = @import("./http/http_client_async.zig");
+ const HTTP = @import("http");
io.run_for_ns(std.time.ns_per_us * 100) catch {};
while (HTTP.AsyncHTTP.active_requests_count.load(.Monotonic) > 255) {
io.tick() catch {};