this kind of works, but there is a crash when bundling. I think its missing a Stmt.Data.Store.reset()

Former-commit-id: 88aad6aeb1
This commit is contained in:
Jarred Sumner
2021-07-01 05:12:15 -07:00
parent a1d3191b89
commit b7d7fac635
21 changed files with 1547 additions and 485 deletions

View File

@@ -246,3 +246,26 @@ pub const OpaqueJSValue = struct_OpaqueJSValue;
// https://github.com/WebKit/webkit/blob/main/Source/JavaScriptCore/API/JSStringRef.cpp#L62
pub extern fn JSStringCreateWithCharactersNoCopy(string: [*c]const JSChar, numChars: size_t) JSStringRef;
const size_t = usize;
const then_key = "then";
var thenable_string: JSStringRef = null;
pub fn isObjectOfClassAndResolveIfNeeded(ctx: JSContextRef, obj: JSObjectRef, class: JSClassRef) ?JSObjectRef {
if (JSValueIsObjectOfClass(ctx, obj, class)) {
return obj;
}
if (!JSValueIsObject(ctx, obj)) {
return null;
}
if (thenable_string == null) {
thenable_string = JSStringCreateWithUTF8CString(then_key[0.. :0]);
}
var prop = JSObjectGetPropertyForKey(ctx, obj, JSValueMakeString(ctx, thenable_string), null);
if (prop == null) {
return null;
}
}

View File

@@ -33,6 +33,79 @@ pub const To = struct {
return function;
}
pub fn Finalize(
comptime ZigContextType: type,
comptime ctxfn: fn (
this: *ZigContextType,
object: js.JSObjectRef,
) void,
) type {
return struct {
pub fn rfn(
object: js.JSObjectRef,
) callconv(.C) void {
var object_ptr_ = js.JSObjectGetPrivate(object);
if (object_ptr_ == null) return;
return ctxfn(
@ptrCast(*ZigContextType, @alignCast(@alignOf(*ZigContextType), object_ptr_.?)),
object,
);
}
};
}
pub fn Constructor(
comptime ctxfn: fn (
ctx: js.JSContextRef,
function: js.JSObjectRef,
arguments: []const js.JSValueRef,
exception: js.ExceptionRef,
) js.JSValueRef,
) type {
return struct {
pub fn rfn(
ctx: js.JSContextRef,
function: js.JSObjectRef,
argumentCount: usize,
arguments: [*c]const js.JSValueRef,
exception: js.ExceptionRef,
) callconv(.C) js.JSValueRef {
return ctxfn(
ctx,
function,
if (arguments) |args| args[0..argumentCount] else &[_]js.JSValueRef{},
exception,
);
}
};
}
pub fn ConstructorCallback(
comptime ctxfn: fn (
ctx: js.JSContextRef,
function: js.JSObjectRef,
arguments: []const js.JSValueRef,
exception: js.ExceptionRef,
) js.JSValueRef,
) type {
return struct {
pub fn rfn(
ctx: js.JSContextRef,
function: js.JSObjectRef,
argumentCount: usize,
arguments: [*c]const js.JSValueRef,
exception: js.ExceptionRef,
) callconv(.C) js.JSValueRef {
return ctxfn(
ctx,
function,
if (arguments) |args| args[0..argumentCount] else &[_]js.JSValueRef{},
exception,
);
}
};
}
pub fn Callback(
comptime ZigContextType: type,
comptime ctxfn: fn (
@@ -107,6 +180,18 @@ pub const Properties = struct {
pub const initialize_bundled_module = "$$m";
pub const load_module_function = "$lOaDuRcOdE$";
pub const window = "window";
pub const default = "default";
pub const include = "include";
pub const GET = "GET";
pub const PUT = "PUT";
pub const POST = "POST";
pub const PATCH = "PATCH";
pub const HEAD = "HEAD";
pub const OPTIONS = "OPTIONS";
pub const navigate = "navigate";
pub const follow = "follow";
};
pub const UTF16 = struct {
@@ -125,24 +210,50 @@ pub const Properties = struct {
pub const initialize_bundled_module = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.initialize_bundled_module);
pub const load_module_function: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.load_module_function);
pub const window: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.window);
pub const default: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.default);
pub const include: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.include);
pub const GET: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.GET);
pub const PUT: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.PUT);
pub const POST: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.POST);
pub const PATCH: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.PATCH);
pub const HEAD: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.HEAD);
pub const OPTIONS: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.OPTIONS);
pub const navigate: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.navigate);
pub const follow: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.follow);
};
pub const Refs = struct {
pub var module: js.JSStringRef = null;
pub var globalThis: js.JSStringRef = null;
pub var exports: js.JSStringRef = null;
pub var log: js.JSStringRef = null;
pub var debug: js.JSStringRef = null;
pub var info: js.JSStringRef = null;
pub var error_: js.JSStringRef = null;
pub var warn: js.JSStringRef = null;
pub var console: js.JSStringRef = null;
pub var require: js.JSStringRef = null;
pub var description: js.JSStringRef = null;
pub var name: js.JSStringRef = null;
pub var initialize_bundled_module: js.JSStringRef = null;
pub var load_module_function: js.JSStringRef = null;
pub var window: js.JSStringRef = null;
pub var module: js.JSStringRef = undefined;
pub var globalThis: js.JSStringRef = undefined;
pub var exports: js.JSStringRef = undefined;
pub var log: js.JSStringRef = undefined;
pub var debug: js.JSStringRef = undefined;
pub var info: js.JSStringRef = undefined;
pub var error_: js.JSStringRef = undefined;
pub var warn: js.JSStringRef = undefined;
pub var console: js.JSStringRef = undefined;
pub var require: js.JSStringRef = undefined;
pub var description: js.JSStringRef = undefined;
pub var name: js.JSStringRef = undefined;
pub var initialize_bundled_module: js.JSStringRef = undefined;
pub var load_module_function: js.JSStringRef = undefined;
pub var window: js.JSStringRef = undefined;
pub var default: js.JSStringRef = undefined;
pub var include: js.JSStringRef = undefined;
pub var GET: js.JSStringRef = undefined;
pub var PUT: js.JSStringRef = undefined;
pub var POST: js.JSStringRef = undefined;
pub var PATCH: js.JSStringRef = undefined;
pub var HEAD: js.JSStringRef = undefined;
pub var OPTIONS: js.JSStringRef = undefined;
pub var empty_string_ptr = [_]u8{0};
pub var empty_string: js.JSStringRef = undefined;
pub var navigate: js.JSStringRef = undefined;
pub var follow: js.JSStringRef = undefined;
};
pub fn init() void {
@@ -160,9 +271,13 @@ pub const Properties = struct {
);
}
}
Refs.empty_string = js.JSStringCreateWithUTF8CString(&Refs.empty_string_ptr);
}
};
const hasSetter = std.meta.trait.hasField("set");
const hasFinalize = std.meta.trait.hasField("finalize");
pub fn NewClass(
comptime ZigType: type,
comptime name: string,
@@ -215,6 +330,65 @@ pub fn NewClass(
};
var static_properties: [property_names.len]js.JSStaticValue = undefined;
pub var ref: js.JSClassRef = null;
pub var loaded = false;
pub var definition: js.JSClassDefinition = undefined;
const ConstructorWrapper = struct {
pub fn rfn(
ctx: js.JSContextRef,
function: js.JSObjectRef,
thisObject: js.JSObjectRef,
argumentCount: usize,
arguments: [*c]const js.JSValueRef,
exception: js.ExceptionRef,
) callconv(.C) js.JSValueRef {
return definition.callAsConstructor.?(ctx, function, argumentCount, arguments, exception);
}
};
pub const Constructor = ConstructorWrapper.rfn;
pub const static_value_count = static_properties.len;
pub fn get() callconv(.C) [*c]js.JSClassRef {
if (!loaded) {
loaded = true;
definition = define();
ref = js.JSClassRetain(js.JSClassCreate(&definition));
}
return &ref;
}
pub fn RawGetter(comptime ReceiverType: type) type {
const ClassGetter = struct {
pub fn getter(
ctx: js.JSContextRef,
obj: js.JSObjectRef,
prop: js.JSStringRef,
exception: js.ExceptionRef,
) callconv(.C) js.JSValueRef {
return js.JSObjectMake(ctx, get().*, null);
}
};
return ClassGetter;
}
pub fn GetClass(comptime ReceiverType: type) type {
const ClassGetter = struct {
pub fn getter(
receiver: *ReceiverType,
ctx: js.JSContextRef,
obj: js.JSObjectRef,
exception: js.ExceptionRef,
) js.JSValueRef {
return js.JSObjectMake(ctx, get().*, null);
}
};
return ClassGetter;
}
pub fn getPropertyCallback(
ctx: js.JSContextRef,
obj: js.JSObjectRef,
@@ -281,7 +455,7 @@ pub fn NewClass(
),
);
var exc: js.ExceptionRef = null;
var exc: js.JSValueRef = null;
switch (comptime @typeInfo(@TypeOf(@field(
properties,
@@ -372,15 +546,18 @@ pub fn NewClass(
var count: usize = 0;
inline for (function_name_literals) |function_name, i| {
if (comptime strings.eqlComptime(function_names[i], "constructor")) {
def.callAsConstructor = @field(staticFunctions, function_names[i]);
def.callAsConstructor = To.JS.Constructor(@field(staticFunctions, function_names[i])).rfn;
} else if (comptime strings.eqlComptime(function_names[i], "finalize")) {
def.finalize = To.JS.Finalize(ZigType, staticFunctions.finalize).rfn;
} else {
count += 1;
var callback = To.JS.Callback(ZigType, @field(staticFunctions, function_names[i])).rfn;
static_functions[count] = js.JSStaticFunction{
.name = (function_names[i][0.. :0]).ptr,
.callAsFunction = callback,
.attributes = comptime if (read_only) js.JSPropertyAttributes.kJSPropertyAttributeReadOnly else js.JSPropertyAttributes.kJSPropertyAttributeNone,
};
count += 1;
}
// if (singleton) {
@@ -402,7 +579,7 @@ pub fn NewClass(
static_properties[i].getProperty = StaticProperty(i).getter;
const field = comptime @field(properties, property_names[i]);
const hasSetter = std.meta.trait.hasField("set");
if (comptime hasSetter(@TypeOf(field))) {
static_properties[i].setProperty = StaticProperty(i).setter;
}
@@ -415,8 +592,6 @@ pub fn NewClass(
def.className = class_name_str;
// def.getProperty = getPropertyCallback;
def.finalize
return def;
}
};
@@ -437,20 +612,18 @@ pub fn JSError(
exception.* = js.JSObjectMakeError(ctx, 1, &error_args, null);
} else {
var buf = std.fmt.allocPrintZ(allocator, fmt, args) catch unreachable;
defer allocator.free(buf);
var message = js.JSStringCreateWithUTF8CString(buf);
defer js.JSStringRelease(message);
defer allocator.free(buf);
error_args[0] = js.JSValueMakeString(ctx, message);
exception.* = js.JSObjectMakeError(ctx, 1, &error_args, null);
}
}
pub fn getAllocator(ctx: js.JSContextRef) *std.mem.Allocator {
var global_obj = js.JSContextGetGlobalObject(ctx);
var priv = js.JSObjectGetPrivate(global_obj).?;
var global = @ptrCast(*javascript.GlobalObject, @alignCast(@alignOf(*javascript.GlobalObject), priv));
return global.vm.allocator;
return std.heap.c_allocator;
}
pub const JSStringList = std.ArrayList(js.JSStringRef);
@@ -465,3 +638,10 @@ pub const ArrayBuffer = struct {
typed_array_type: js.JSTypedArrayType,
};
pub fn castObj(obj: js.JSObjectRef, comptime Type: type) *Type {
return @ptrCast(
*Type,
@alignCast(@alignOf(*Type), js.JSObjectGetPrivate(obj).?),
);
}

View File

@@ -14,6 +14,7 @@ const http = @import("../../http.zig");
usingnamespace @import("./node_env_buf_map.zig");
usingnamespace @import("./base.zig");
usingnamespace @import("./webcore/response.zig");
const DefaultSpeedyDefines = struct {
pub const Keys = struct {
@@ -138,6 +139,15 @@ pub const VirtualMachine = struct {
log: *logger.Log,
watcher: ?*http.Watcher = null,
event_listeners: EventListenerMixin.Map,
pub threadlocal var instance: *VirtualMachine = undefined;
pub fn deinit(vm: *VirtualMachine) void {
js.JSGlobalContextRelease(vm.ctx);
js.JSContextGroupRelease(vm.group);
}
pub fn init(
allocator: *std.mem.Allocator,
_args: Api.TransformOptions,
@@ -167,6 +177,8 @@ pub const VirtualMachine = struct {
.log = log,
.group = group,
.event_listeners = EventListenerMixin.Map.init(allocator),
.require_cache = RequireCacheType.init(allocator),
.global = global,
};
@@ -191,8 +203,6 @@ pub const VirtualMachine = struct {
}
};
pub const Object = struct {
ref: js.jsObjectRef,
};
@@ -525,13 +535,41 @@ pub const Module = struct {
size += this_size;
longest_size = std.math.max(this_size, longest_size);
}
var static_properties = try vm.allocator.alloc(js.JSStaticValue, bundle.bundle.modules.len + 2);
static_properties[static_properties.len - 2] = js.JSStaticValue{
var static_properties = try vm.allocator.alloc(js.JSStaticValue, bundle.bundle.modules.len + 1 + GlobalObject.GlobalClass.static_value_count);
var copied_static_values = static_properties[bundle.bundle.modules.len..];
copied_static_values = copied_static_values[0 .. copied_static_values.len - 1];
copied_static_values[0] = js.JSStaticValue{
.name = Properties.UTF8.console[0.. :0],
.getProperty = getConsole,
.setProperty = null,
.attributes = .kJSPropertyAttributeNone,
};
copied_static_values[1] = js.JSStaticValue{
.name = Response.Class.definition.className,
.getProperty = Response.Class.RawGetter(NodeModuleList).getter,
.setProperty = null,
.attributes = .kJSPropertyAttributeNone,
};
copied_static_values[2] = js.JSStaticValue{
.name = Headers.Class.definition.className,
.getProperty = Headers.Class.RawGetter(NodeModuleList).getter,
.setProperty = null,
.attributes = .kJSPropertyAttributeNone,
};
copied_static_values[3] = js.JSStaticValue{
.name = Request.Class.definition.className,
.getProperty = Request.Class.RawGetter(NodeModuleList).getter,
.setProperty = null,
.attributes = .kJSPropertyAttributeNone,
};
// copied_static_values must match GlobalObject.GlobalClass.static_value_count
std.debug.assert(copied_static_values.len == 4);
static_properties[static_properties.len - 1] = std.mem.zeroes(js.JSStaticValue);
var utf8 = try vm.allocator.alloc(u8, size + std.math.max(longest_size, 32));
std.mem.set(u8, utf8, 0);
@@ -571,6 +609,7 @@ pub const Module = struct {
var node_module_global_class_def = js.kJSClassDefinitionEmpty;
node_module_global_class_def.staticValues = static_properties.ptr;
node_module_global_class_def.className = node_module_global_class_name[0.. :0];
node_module_global_class_def.staticFunctions = GlobalObject.GlobalClass.definition.staticFunctions;
// node_module_global_class_def.parentClass = vm.global.global_class;
var property_getters = try vm.allocator.alloc(js.JSObjectRef, bundle.bundle.modules.len);
@@ -1145,6 +1184,142 @@ pub const Module = struct {
pub const RequireObject = struct {};
};
pub const EventListenerMixin = struct {
threadlocal var event_listener_names_buf: [128]u8 = undefined;
pub const List = std.ArrayList(js.JSObjectRef);
pub const Map = std.AutoHashMap(EventListenerMixin.EventType, EventListenerMixin.List);
pub const EventType = enum {
fetch,
pub fn match(str: string) ?EventType {
if (strings.eqlComptime(str, "fetch")) {
return EventType.fetch;
}
return null;
}
};
pub fn emitFetchEventError(
request: *http.RequestContext,
comptime fmt: string,
args: anytype,
) void {
Output.prettyErrorln(fmt, args);
request.sendInternalError(error.FetchEventError) catch {};
}
pub fn emitFetchEvent(
vm: *VirtualMachine,
request_context: *http.RequestContext,
) !void {
var listeners = vm.event_listeners.get(EventType.fetch) orelse return emitFetchEventError(
request_context,
"Missing \"fetch\" handler. Did you run \"addEventListener(\"fetch\", (event) => {{}})\"?",
.{},
);
if (listeners.items.len == 0) return emitFetchEventError(
request_context,
"Missing \"fetch\" handler. Did you run \"addEventListener(\"fetch\", (event) => {{}})\"?",
.{},
);
var exception: js.JSValueRef = null;
var fetch_event = try vm.allocator.create(FetchEvent);
fetch_event.* = FetchEvent{
.request_context = request_context,
.request = Request{ .request_context = request_context },
};
var fetch_args: [1]js.JSObjectRef = undefined;
for (listeners.items) |listener| {
fetch_args[0] = js.JSObjectMake(
vm.ctx,
FetchEvent.Class.get().*,
fetch_event,
);
_ = js.JSObjectCallAsFunction(
vm.ctx,
listener,
js.JSContextGetGlobalObject(vm.ctx),
1,
&fetch_args,
&exception,
);
if (request_context.has_called_done) {
break;
}
}
if (exception != null) {
var message = js.JSValueToStringCopy(vm.ctx, exception, null);
defer js.JSStringRelease(message);
var buf = vm.allocator.alloc(u8, js.JSStringGetLength(message) + 1) catch unreachable;
defer vm.allocator.free(buf);
var note = buf[0 .. js.JSStringGetUTF8CString(message, buf.ptr, buf.len) - 1];
Output.prettyErrorln("<r><red>error<r>: <b>{s}<r>", .{note});
Output.flush();
if (!request_context.has_called_done) {
request_context.sendInternalError(error.JavaScriptError) catch {};
}
return;
}
if (!request_context.has_called_done) {
return emitFetchEventError(
request_context,
"\"fetch\" handler never called event.respondWith()",
.{},
);
}
}
pub fn addEventListener(
comptime Struct: type,
) type {
const Handler = struct {
pub fn addListener(
ptr: *Struct,
ctx: js.JSContextRef,
function: js.JSObjectRef,
thisObject: js.JSObjectRef,
arguments: []const js.JSValueRef,
exception: js.ExceptionRef,
) js.JSValueRef {
if (arguments.len == 0 or arguments.len == 1 or !js.JSValueIsString(ctx, arguments[0]) or !js.JSValueIsObject(ctx, arguments[arguments.len - 1]) or !js.JSObjectIsFunction(ctx, arguments[arguments.len - 1])) {
return js.JSValueMakeUndefined(ctx);
}
const name_len = js.JSStringGetLength(arguments[0]);
if (name_len > event_listener_names_buf.len) {
return js.JSValueMakeUndefined(ctx);
}
const name_used_len = js.JSStringGetUTF8CString(arguments[0], &event_listener_names_buf, event_listener_names_buf.len);
const name = event_listener_names_buf[0 .. name_used_len - 1];
const event = EventType.match(name) orelse return js.JSValueMakeUndefined(ctx);
var entry = VirtualMachine.instance.event_listeners.getOrPut(event) catch unreachable;
if (!entry.found_existing) {
entry.value_ptr.* = List.initCapacity(VirtualMachine.instance.allocator, 1) catch unreachable;
}
var callback = arguments[arguments.len - 1];
js.JSValueProtect(ctx, callback);
entry.value_ptr.append(callback) catch unreachable;
return js.JSValueMakeUndefined(ctx);
}
};
return Handler;
}
};
pub const GlobalObject = struct {
ref: js.JSObjectRef = undefined,
vm: *VirtualMachine,
@@ -1176,9 +1351,14 @@ pub const GlobalObject = struct {
pub const GlobalClass = NewClass(
GlobalObject,
"Global",
.{},
.{
.@"addEventListener" = EventListenerMixin.addEventListener(GlobalObject).addListener,
},
.{
.@"console" = getConsole,
.@"Request" = Request.Class.GetClass(GlobalObject).getter,
.@"Response" = Response.Class.GetClass(GlobalObject).getter,
.@"Headers" = Headers.Class.GetClass(GlobalObject).getter,
},
false,
false,
@@ -1195,18 +1375,11 @@ pub const GlobalObject = struct {
// js.JSValueProtect(js.JSContextGetGlobalContext(ctx), global.console);
// }
return js.JSObjectMake(js.JSContextGetGlobalContext(ctx), global.console_class, global);
return js.JSObjectMake(js.JSContextGetGlobalContext(ctx), ConsoleClass.get().*, global);
}
pub fn boot(global: *GlobalObject) !void {
global.console_definition = ConsoleClass.define();
global.console_class = js.JSClassRetain(js.JSClassCreate(&global.console_definition));
global.global_class_def = GlobalClass.define();
global.global_class = js.JSClassRetain(js.JSClassCreate(&global.global_class_def));
global.ctx = js.JSGlobalContextRetain(js.JSGlobalContextCreateInGroup(global.vm.group, global.global_class));
global.ctx = js.JSGlobalContextRetain(js.JSGlobalContextCreateInGroup(global.vm.group, GlobalObject.GlobalClass.get().*));
std.debug.assert(js.JSObjectSetPrivate(js.JSContextGetGlobalObject(global.ctx), global));
if (!printer_buf_loaded) {

File diff suppressed because it is too large Load Diff