diff options
Diffstat (limited to 'src/javascript')
-rw-r--r-- | src/javascript/jsc/base.zig | 74 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/ZigGlobalObject.cpp | 15 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/bindings.cpp | 11 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/bindings.zig | 11 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/headers-cpp.h | 2 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/headers.h | 4 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/headers.zig | 33 | ||||
-rw-r--r-- | src/javascript/jsc/javascript.zig | 6 | ||||
-rw-r--r-- | src/javascript/jsc/webcore/response.zig | 514 |
9 files changed, 515 insertions, 155 deletions
diff --git a/src/javascript/jsc/base.zig b/src/javascript/jsc/base.zig index 58075dc38..3a0355503 100644 --- a/src/javascript/jsc/base.zig +++ b/src/javascript/jsc/base.zig @@ -726,11 +726,15 @@ pub fn NewClass( var static_functions = brk: { var funcs: [function_name_refs.len + 1]js.JSStaticFunction = undefined; - std.mem.set(js.JSStaticFunction, &funcs, js.JSStaticFunction{ - .name = @intToPtr([*c]const u8, 0), - .callAsFunction = null, - .attributes = js.JSPropertyAttributes.kJSPropertyAttributeNone, - },); + std.mem.set( + js.JSStaticFunction, + &funcs, + js.JSStaticFunction{ + .name = @intToPtr([*c]const u8, 0), + .callAsFunction = null, + .attributes = js.JSPropertyAttributes.kJSPropertyAttributeNone, + }, + ); break :brk funcs; }; var instance_functions = std.mem.zeroes([function_names.len]js.JSObjectRef); @@ -738,36 +742,40 @@ pub fn NewClass( var property_name_refs = std.mem.zeroes([property_names.len]js.JSStringRef); const property_name_literals = property_names; var static_properties = brk: { - var props: [property_names.len]js.JSStaticValue = undefined; - std.mem.set(js.JSStaticValue, &props, js.JSStaticValue{ - .name = @intToPtr([*c]const u8, 0), - .getProperty = null, - .setProperty = null, - .attributes = js.JSPropertyAttributes.kJSPropertyAttributeNone, - },); + var props: [property_names.len]js.JSStaticValue = undefined; + std.mem.set( + js.JSStaticValue, + &props, + js.JSStaticValue{ + .name = @intToPtr([*c]const u8, 0), + .getProperty = null, + .setProperty = null, + .attributes = js.JSPropertyAttributes.kJSPropertyAttributeNone, + }, + ); break :brk props; }; pub var ref: js.JSClassRef = null; pub var loaded = false; - pub var definition: js.JSClassDefinition =.{ - .version = 0, - .attributes = js.JSClassAttributes.kJSClassAttributeNone, - .className = name[0..:0].ptr, - .parentClass = null, - .staticValues = null, - .staticFunctions = null, - .initialize = null, - .finalize = null, - .hasProperty = null, - .getProperty = null, - .setProperty = null, - .deleteProperty = null, - .getPropertyNames = null, - .callAsFunction = null, - .callAsConstructor = null, - .hasInstance = null, - .convertToType = null, + pub var definition: js.JSClassDefinition = .{ + .version = 0, + .attributes = js.JSClassAttributes.kJSClassAttributeNone, + .className = name[0.. :0].ptr, + .parentClass = null, + .staticValues = null, + .staticFunctions = null, + .initialize = null, + .finalize = null, + .hasProperty = null, + .getProperty = null, + .setProperty = null, + .deleteProperty = null, + .getPropertyNames = null, + .callAsFunction = null, + .callAsConstructor = null, + .hasInstance = null, + .convertToType = null, }; const ConstructorWrapper = struct { pub fn rfn( @@ -1326,7 +1334,7 @@ pub fn NewClass( .callAsConstructor = null, .hasInstance = null, .convertToType = null, - }; + }; if (static_functions.len > 0) { std.mem.set(js.JSStaticFunction, &static_functions, std.mem.zeroes(js.JSStaticFunction)); @@ -1338,6 +1346,8 @@ pub fn NewClass( def.callAsConstructor = To.JS.Constructor(staticFunctions.constructor.rfn).rfn; } else if (comptime strings.eqlComptime(function_names[i], "finalize")) { def.finalize = To.JS.Finalize(ZigType, staticFunctions.finalize.rfn).rfn; + } else if (comptime strings.eqlComptime(function_names[i], "call")) { + def.callAsFunction = To.JS.Callback(ZigType, staticFunctions.call.rfn).rfn; } else if (comptime strings.eqlComptime(function_names[i], "callAsFunction")) { const ctxfn = @field(staticFunctions, function_names[i]).rfn; const Func: std.builtin.TypeInfo.Fn = @typeInfo(@TypeOf(ctxfn)).Fn; @@ -1379,6 +1389,8 @@ pub fn NewClass( def.callAsConstructor = To.JS.Constructor(staticFunctions.constructor).rfn; } else if (comptime strings.eqlComptime(function_names[i], "finalize")) { def.finalize = To.JS.Finalize(ZigType, staticFunctions.finalize).rfn; + } else if (comptime strings.eqlComptime(function_names[i], "call")) { + def.callAsFunction = To.JS.Callback(ZigType, staticFunctions.call).rfn; } else { var callback = To.JS.Callback( ZigType, diff --git a/src/javascript/jsc/bindings/ZigGlobalObject.cpp b/src/javascript/jsc/bindings/ZigGlobalObject.cpp index ea7e89141..8caf09662 100644 --- a/src/javascript/jsc/bindings/ZigGlobalObject.cpp +++ b/src/javascript/jsc/bindings/ZigGlobalObject.cpp @@ -165,7 +165,7 @@ void GlobalObject::setConsole(void *console) { // and any other objects available globally. void GlobalObject::installAPIGlobals(JSClassRef *globals, int count) { WTF::Vector<GlobalPropertyInfo> extraStaticGlobals; - extraStaticGlobals.reserveCapacity((size_t)count + 1); + extraStaticGlobals.reserveCapacity((size_t)count + 2); // This is not nearly a complete implementation. It's just enough to make some npm packages that // were compiled with Webpack to run without crashing in this environment. @@ -223,9 +223,7 @@ JSC::Identifier GlobalObject::moduleLoaderResolve(JSGlobalObject *globalObject, res.success = false; ZigString keyZ = toZigString(key, globalObject); ZigString referrerZ = referrer.isString() ? toZigString(referrer, globalObject) : ZigStringEmpty; - Zig__GlobalObject__resolve(&res, globalObject, &keyZ, - &referrerZ - ); + Zig__GlobalObject__resolve(&res, globalObject, &keyZ, &referrerZ); if (res.success) { return toIdentifier(res.result.value, globalObject); @@ -250,11 +248,9 @@ JSC::JSInternalPromise *GlobalObject::moduleLoaderImportModule(JSGlobalObject *g auto sourceURL = sourceOrigin.url(); ErrorableZigString resolved; auto moduleNameZ = toZigString(moduleNameValue, globalObject); - auto sourceOriginZ = sourceURL.isEmpty() ? ZigStringCwd - : toZigString(sourceURL.fileSystemPath()); + auto sourceOriginZ = sourceURL.isEmpty() ? ZigStringCwd : toZigString(sourceURL.fileSystemPath()); resolved.success = false; - Zig__GlobalObject__resolve(&resolved, globalObject, &moduleNameZ, &sourceOriginZ - ); + Zig__GlobalObject__resolve(&resolved, globalObject, &moduleNameZ, &sourceOriginZ); if (!resolved.success) { throwException(scope, resolved.result.err, globalObject); return promise->rejectWithCaughtException(globalObject, scope); @@ -382,8 +378,7 @@ JSC::JSInternalPromise *GlobalObject::moduleLoaderFetch(JSGlobalObject *globalOb res.result.err.code = 0; res.result.err.ptr = nullptr; - Zig__GlobalObject__fetch(&res, globalObject, &moduleKeyZig, - &source ); + Zig__GlobalObject__fetch(&res, globalObject, &moduleKeyZig, &source); if (!res.success) { throwException(scope, res.result.err, globalObject); diff --git a/src/javascript/jsc/bindings/bindings.cpp b/src/javascript/jsc/bindings/bindings.cpp index 766de863b..d5c5e057d 100644 --- a/src/javascript/jsc/bindings/bindings.cpp +++ b/src/javascript/jsc/bindings/bindings.cpp @@ -1765,4 +1765,15 @@ void WTF__URL__setQuery(WTF__URL *arg0, bWTF__StringView arg1) { void WTF__URL__setUser(WTF__URL *arg0, bWTF__StringView arg1) { arg0->setUser(*Wrap<WTF::StringView, bWTF__StringView>::unwrap(&arg1)); }; + +JSC__JSValue JSC__JSPromise__rejectedPromiseValue(JSC__JSGlobalObject *arg0, + JSC__JSValue JSValue1) { + return JSC::JSValue::encode( + JSC::JSPromise::rejectedPromise(arg0, JSC::JSValue::decode(JSValue1))); +} +JSC__JSValue JSC__JSPromise__resolvedPromiseValue(JSC__JSGlobalObject *arg0, + JSC__JSValue JSValue1) { + return JSC::JSValue::encode( + JSC::JSPromise::resolvedPromise(arg0, JSC::JSValue::decode(JSValue1))); } +}
\ No newline at end of file diff --git a/src/javascript/jsc/bindings/bindings.zig b/src/javascript/jsc/bindings/bindings.zig index 3ff10a285..4f9d5595e 100644 --- a/src/javascript/jsc/bindings/bindings.zig +++ b/src/javascript/jsc/bindings/bindings.zig @@ -438,10 +438,19 @@ pub const JSPromise = extern struct { pub fn resolvedPromise(globalThis: *JSGlobalObject, value: JSValue) *JSPromise { return cppFn("resolvedPromise", .{ globalThis, value }); } + + pub fn resolvedPromiseValue(globalThis: *JSGlobalObject, value: JSValue) JSValue { + return cppFn("resolvedPromiseValue", .{ globalThis, value }); + } + pub fn rejectedPromise(globalThis: *JSGlobalObject, value: JSValue) *JSPromise { return cppFn("rejectedPromise", .{ globalThis, value }); } + pub fn rejectedPromiseValue(globalThis: *JSGlobalObject, value: JSValue) JSValue { + return cppFn("rejectedPromiseValue", .{ globalThis, value }); + } + pub fn resolve(this: *JSPromise, globalThis: *JSGlobalObject, value: JSValue) void { cppFn("resolve", .{ this, globalThis, value }); } @@ -470,6 +479,8 @@ pub const JSPromise = extern struct { "rejectAsHandled", // "rejectException", "rejectAsHandledException", + "rejectedPromiseValue", + "resolvedPromiseValue", }; }; diff --git a/src/javascript/jsc/bindings/headers-cpp.h b/src/javascript/jsc/bindings/headers-cpp.h index 918138460..400697777 100644 --- a/src/javascript/jsc/bindings/headers-cpp.h +++ b/src/javascript/jsc/bindings/headers-cpp.h @@ -1,4 +1,4 @@ -//-- AUTOGENERATED FILE -- 1631085611 +//-- AUTOGENERATED FILE -- 1631179623 // clang-format off #pragma once diff --git a/src/javascript/jsc/bindings/headers.h b/src/javascript/jsc/bindings/headers.h index a26fecc2f..125937a59 100644 --- a/src/javascript/jsc/bindings/headers.h +++ b/src/javascript/jsc/bindings/headers.h @@ -1,4 +1,4 @@ -//-- AUTOGENERATED FILE -- 1631085611 +//-- AUTOGENERATED FILE -- 1631179623 // clang-format: off #pragma once @@ -285,9 +285,11 @@ CPP_DECL void JSC__JSPromise__reject(JSC__JSPromise* arg0, JSC__JSGlobalObject* CPP_DECL void JSC__JSPromise__rejectAsHandled(JSC__JSPromise* arg0, JSC__JSGlobalObject* arg1, JSC__JSValue JSValue2); CPP_DECL void JSC__JSPromise__rejectAsHandledException(JSC__JSPromise* arg0, JSC__JSGlobalObject* arg1, JSC__Exception* arg2); CPP_DECL JSC__JSPromise* JSC__JSPromise__rejectedPromise(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1); +CPP_DECL JSC__JSValue JSC__JSPromise__rejectedPromiseValue(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1); CPP_DECL void JSC__JSPromise__rejectWithCaughtException(JSC__JSPromise* arg0, JSC__JSGlobalObject* arg1, bJSC__ThrowScope arg2); CPP_DECL void JSC__JSPromise__resolve(JSC__JSPromise* arg0, JSC__JSGlobalObject* arg1, JSC__JSValue JSValue2); CPP_DECL JSC__JSPromise* JSC__JSPromise__resolvedPromise(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1); +CPP_DECL JSC__JSValue JSC__JSPromise__resolvedPromiseValue(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1); CPP_DECL JSC__JSValue JSC__JSPromise__result(const JSC__JSPromise* arg0, JSC__VM* arg1); CPP_DECL uint32_t JSC__JSPromise__status(const JSC__JSPromise* arg0, JSC__VM* arg1); diff --git a/src/javascript/jsc/bindings/headers.zig b/src/javascript/jsc/bindings/headers.zig index c1504a31f..f34816253 100644 --- a/src/javascript/jsc/bindings/headers.zig +++ b/src/javascript/jsc/bindings/headers.zig @@ -37,36 +37,7 @@ pub const __mbstate_t = extern union { pub const __darwin_mbstate_t = __mbstate_t; pub const __darwin_ptrdiff_t = c_long; pub const __darwin_size_t = c_ulong; -pub const __builtin_va_list = [*c]u8; -pub const __darwin_va_list = __builtin_va_list; -pub const __darwin_wchar_t = c_int; -pub const __darwin_rune_t = __darwin_wchar_t; -pub const __darwin_wint_t = c_int; -pub const __darwin_clock_t = c_ulong; -pub const __darwin_socklen_t = __uint32_t; -pub const __darwin_ssize_t = c_long; -pub const __darwin_time_t = c_long; -pub const __darwin_blkcnt_t = __int64_t; -pub const __darwin_blksize_t = __int32_t; -pub const __darwin_dev_t = __int32_t; -pub const __darwin_fsblkcnt_t = c_uint; -pub const __darwin_fsfilcnt_t = c_uint; -pub const __darwin_gid_t = __uint32_t; -pub const __darwin_id_t = __uint32_t; -pub const __darwin_ino64_t = __uint64_t; -pub const __darwin_ino_t = __darwin_ino64_t; -pub const __darwin_mach_port_name_t = __darwin_natural_t; -pub const __darwin_mach_port_t = __darwin_mach_port_name_t; -pub const __darwin_mode_t = __uint16_t; -pub const __darwin_off_t = __int64_t; -pub const __darwin_pid_t = __int32_t; -pub const __darwin_sigset_t = __uint32_t; -pub const __darwin_suseconds_t = __int32_t; -pub const __darwin_uid_t = __uint32_t; -pub const __darwin_useconds_t = __uint32_t; -pub const __darwin_uuid_t = [16]u8; -pub const __darwin_uuid_string_t = [37]u8; - + pub const JSC__RegExpPrototype = struct_JSC__RegExpPrototype; pub const JSC__GeneratorPrototype = struct_JSC__GeneratorPrototype; @@ -162,9 +133,11 @@ pub extern fn JSC__JSPromise__reject(arg0: [*c]JSC__JSPromise, arg1: [*c]JSC__JS pub extern fn JSC__JSPromise__rejectAsHandled(arg0: [*c]JSC__JSPromise, arg1: [*c]JSC__JSGlobalObject, JSValue2: JSC__JSValue) void; pub extern fn JSC__JSPromise__rejectAsHandledException(arg0: [*c]JSC__JSPromise, arg1: [*c]JSC__JSGlobalObject, arg2: [*c]JSC__Exception) void; pub extern fn JSC__JSPromise__rejectedPromise(arg0: [*c]JSC__JSGlobalObject, JSValue1: JSC__JSValue) [*c]JSC__JSPromise; +pub extern fn JSC__JSPromise__rejectedPromiseValue(arg0: [*c]JSC__JSGlobalObject, JSValue1: JSC__JSValue) JSC__JSValue; pub extern fn JSC__JSPromise__rejectWithCaughtException(arg0: [*c]JSC__JSPromise, arg1: [*c]JSC__JSGlobalObject, arg2: bJSC__ThrowScope) void; pub extern fn JSC__JSPromise__resolve(arg0: [*c]JSC__JSPromise, arg1: [*c]JSC__JSGlobalObject, JSValue2: JSC__JSValue) void; pub extern fn JSC__JSPromise__resolvedPromise(arg0: [*c]JSC__JSGlobalObject, JSValue1: JSC__JSValue) [*c]JSC__JSPromise; +pub extern fn JSC__JSPromise__resolvedPromiseValue(arg0: [*c]JSC__JSGlobalObject, JSValue1: JSC__JSValue) JSC__JSValue; pub extern fn JSC__JSPromise__result(arg0: [*c]const JSC__JSPromise, arg1: [*c]JSC__VM) JSC__JSValue; pub extern fn JSC__JSPromise__status(arg0: [*c]const JSC__JSPromise, arg1: [*c]JSC__VM) u32; pub extern fn JSC__JSInternalPromise__create(arg0: [*c]JSC__JSGlobalObject) [*c]JSC__JSInternalPromise; diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig index 1d8616ff7..5349aaec1 100644 --- a/src/javascript/jsc/javascript.zig +++ b/src/javascript/jsc/javascript.zig @@ -33,6 +33,7 @@ pub const GlobalClasses = [_]type{ BuildError.Class, ResolveError.Class, Bun.Class, + Fetch.Class, }; const Blob = @import("../../blob.zig"); @@ -276,6 +277,10 @@ pub const Bun = struct { .rfn = Router.match, .ts = Router.match_type_definition, }, + .fetch = .{ + .rfn = Fetch.call, + .ts = d.ts{}, + }, .getImportedStyles = .{ .rfn = Bun.getImportedStyles, .ts = d.ts{ @@ -1348,7 +1353,6 @@ pub const EventListenerMixin = struct { // Rely on JS finalizer var fetch_event = try vm.allocator.create(FetchEvent); - fetch_event.* = FetchEvent{ .request_context = request_context, .request = Request{ .request_context = request_context }, diff --git a/src/javascript/jsc/webcore/response.zig b/src/javascript/jsc/webcore/response.zig index cd4dff8c8..680e8aa06 100644 --- a/src/javascript/jsc/webcore/response.zig +++ b/src/javascript/jsc/webcore/response.zig @@ -4,19 +4,34 @@ const Api = @import("../../../api/schema.zig").Api; const http = @import("../../../http.zig"); usingnamespace @import("../javascript.zig"); usingnamespace @import("../bindings/bindings.zig"); - +const ZigURL = @import("../../../query_string_map.zig").URL; +const HTTPClient = @import("../../../http_client.zig"); +const picohttp = @import("picohttp"); pub const Response = struct { pub const Class = NewClass( Response, .{ .name = "Response" }, .{ .@"constructor" = constructor, + .@"text" = .{ + .rfn = getText, + .ts = d.ts{}, + }, + .@"json" = .{ + .rfn = getJson, + .ts = d.ts{}, + }, + .@"arrayBuffer" = .{ + .rfn = getArrayBuffer, + .ts = d.ts{}, + }, }, .{ // .@"url" = .{ // .@"get" = getURL, // .ro = true, // }, + .@"ok" = .{ .@"get" = getOK, .ro = true, @@ -30,6 +45,7 @@ pub const Response = struct { allocator: *std.mem.Allocator, body: Body, + status_text: string = "", pub const Props = struct {}; @@ -41,7 +57,174 @@ pub const Response = struct { exception: js.ExceptionRef, ) js.JSValueRef { // https://developer.mozilla.org/en-US/docs/Web/API/Response/ok - return js.JSValueMakeBoolean(ctx, this.body.init.status_code >= 200 and this.body.init.status_code <= 299); + return js.JSValueMakeBoolean(ctx, this.body.init.status_code == 304 or (this.body.init.status_code >= 200 and this.body.init.status_code <= 299)); + } + + pub fn getText( + this: *Response, + ctx: js.JSContextRef, + function: js.JSObjectRef, + thisObject: js.JSObjectRef, + arguments: []const js.JSValueRef, + exception: js.ExceptionRef, + ) js.JSValueRef { + // https://developer.mozilla.org/en-US/docs/Web/API/Response/text + defer this.body.value = .Empty; + return JSPromise.resolvedPromiseValue( + VirtualMachine.vm.global, + (brk: { + switch (this.body.value) { + .Unconsumed => { + if (this.body.ptr) |_ptr| { + break :brk ZigString.init(_ptr[0..this.body.len]).toValue(VirtualMachine.vm.global); + } + + break :brk ZigString.init("").toValue(VirtualMachine.vm.global); + }, + .Empty => { + break :brk ZigString.init("").toValue(VirtualMachine.vm.global); + }, + .String => |str| { + break :brk ZigString.init(str).toValue(VirtualMachine.vm.global); + }, + .ArrayBuffer => |buffer| { + break :brk ZigString.init(buffer.ptr[buffer.offset..buffer.byte_len]).toValue(VirtualMachine.vm.global); + }, + } + }), + ).asRef(); + } + + var temp_error_buffer: [4096]u8 = undefined; + var error_arg_list: [1]js.JSObjectRef = undefined; + pub fn getJson( + this: *Response, + ctx: js.JSContextRef, + function: js.JSObjectRef, + thisObject: js.JSObjectRef, + arguments: []const js.JSValueRef, + exception: js.ExceptionRef, + ) js.JSValueRef { + defer this.body.value = .Empty; + var zig_string = ZigString.init(""); + + var js_string = (js.JSValueCreateJSONString( + ctx, + brk: { + switch (this.body.value) { + .Unconsumed => { + if (this.body.ptr) |_ptr| { + zig_string = ZigString.init(_ptr[0..this.body.len]); + break :brk zig_string.toJSStringRef(); + } + + break :brk zig_string.toJSStringRef(); + }, + .Empty => { + break :brk zig_string.toJSStringRef(); + }, + .String => |str| { + zig_string = ZigString.init(str); + break :brk zig_string.toJSStringRef(); + }, + .ArrayBuffer => |buffer| { + zig_string = ZigString.init(buffer.ptr[buffer.offset..buffer.byte_len]); + break :brk zig_string.toJSStringRef(); + }, + } + }, + 0, + exception, + ) orelse { + var out = std.fmt.bufPrint(&temp_error_buffer, "Invalid JSON\n\n \"{s}\"", .{zig_string.slice()[0..std.math.min(zig_string.len, 4000)]}) catch unreachable; + error_arg_list[0] = ZigString.init(out).toValueGC(VirtualMachine.vm.global).asRef(); + return JSPromise.rejectedPromiseValue( + VirtualMachine.vm.global, + JSValue.fromRef( + js.JSObjectMakeError( + ctx, + 1, + &error_arg_list, + exception, + ), + ), + ).asRef(); + }); + defer js.JSStringRelease(js_string); + + return JSPromise.resolvedPromiseValue( + VirtualMachine.vm.global, + JSValue.fromRef( + js.JSValueMakeString( + ctx, + js_string, + ), + ), + ).asRef(); + } + pub fn getArrayBuffer( + this: *Response, + ctx: js.JSContextRef, + function: js.JSObjectRef, + thisObject: js.JSObjectRef, + arguments: []const js.JSValueRef, + exception: js.ExceptionRef, + ) js.JSValueRef { + defer this.body.value = .Empty; + return JSPromise.resolvedPromiseValue( + VirtualMachine.vm.global, + JSValue.fromRef( + (brk: { + switch (this.body.value) { + .Unconsumed => { + if (this.body.ptr) |_ptr| { + break :brk js.JSObjectMakeTypedArrayWithBytesNoCopy( + ctx, + js.JSTypedArrayType.kJSTypedArrayTypeUint8Array, + _ptr, + this.body.len, + null, + null, + exception, + ); + } + + break :brk js.JSObjectMakeTypedArray( + ctx, + js.JSTypedArrayType.kJSTypedArrayTypeUint8Array, + 0, + exception, + ); + }, + .Empty => { + break :brk js.JSObjectMakeTypedArray(ctx, js.JSTypedArrayType.kJSTypedArrayTypeUint8Array, 0, exception); + }, + .String => |str| { + break :brk js.JSObjectMakeTypedArrayWithBytesNoCopy( + ctx, + js.JSTypedArrayType.kJSTypedArrayTypeUint8Array, + @intToPtr([*]u8, @ptrToInt(str.ptr)), + str.len, + null, + null, + exception, + ); + }, + .ArrayBuffer => |buffer| { + break :brk js.JSObjectMakeTypedArrayWithBytesNoCopy( + ctx, + buffer.typed_array_type, + buffer.ptr, + buffer.byte_len, + null, + null, + exception, + ); + }, + } + }), + ), + ).asRef(); } pub fn getStatus( @@ -87,7 +270,7 @@ pub const Response = struct { return http.MimeType.html.value; }, - .ArrayBuffer => { + .Unconsumed, .ArrayBuffer => { return "application/octet-stream"; }, } @@ -134,6 +317,151 @@ pub const Response = struct { } }; +pub const Fetch = struct { + const headers_string = "headers"; + const method_string = "method"; + + var fetch_body_string: MutableString = undefined; + var fetch_body_string_loaded = false; + + pub const Class = NewClass( + void, + .{ .name = "fetch" }, + .{ + .@"call" = .{ + .rfn = Fetch.call, + .ts = d.ts{}, + }, + }, + .{}, + ); + + pub fn call( + this: void, + ctx: js.JSContextRef, + function: js.JSObjectRef, + thisObject: js.JSObjectRef, + arguments: []const js.JSValueRef, + exception: js.ExceptionRef, + ) js.JSObjectRef { + if (arguments.len == 0 or arguments.len > 2) return js.JSValueMakeNull(ctx); + var http_client = HTTPClient.init(getAllocator(ctx), .GET, ZigURL{}, .{}, ""); + var headers: ?Headers = null; + var body: string = ""; + + if (!js.JSValueIsString(ctx, arguments[0])) { + return js.JSValueMakeNull(ctx); + } + + var url_zig_str = ZigString.init(""); + JSValue.fromRef(arguments[0]).toZigString( + &url_zig_str, + VirtualMachine.vm.global, + ); + var url_str = url_zig_str.slice(); + if (url_str.len == 0) return js.JSValueMakeNull(ctx); + http_client.url = ZigURL.parse(url_str); + + if (arguments.len == 2 and js.JSValueIsObject(ctx, arguments[1])) { + var array = js.JSObjectCopyPropertyNames(ctx, arguments[1]); + defer js.JSPropertyNameArrayRelease(array); + const count = js.JSPropertyNameArrayGetCount(array); + var i: usize = 0; + while (i < count) : (i += 1) { + var property_name_ref = js.JSPropertyNameArrayGetNameAtIndex(array, i); + switch (js.JSStringGetLength(property_name_ref)) { + "headers".len => { + if (js.JSStringIsEqualToUTF8CString(property_name_ref, "headers")) { + if (js.JSObjectGetProperty(ctx, arguments[1], property_name_ref, null)) |value| { + if (GetJSPrivateData(Headers, value)) |headers_ptr| { + headers = headers_ptr.*; + } else if (Headers.JS.headersInit(ctx, value) catch null) |headers_| { + headers = headers_; + } + } + } + }, + "body".len => { + if (js.JSStringIsEqualToUTF8CString(property_name_ref, "body")) { + if (js.JSObjectGetProperty(ctx, arguments[1], property_name_ref, null)) |value| { + var body_ = Body.extractBody(ctx, value, false, null, exception); + if (exception != null) return js.JSValueMakeNull(ctx); + switch (body_.value) { + .ArrayBuffer => |arraybuffer| { + body = arraybuffer.ptr[0..arraybuffer.byte_len]; + }, + .String => |str| { + body = str; + }, + else => {}, + } + } + } + }, + "method".len => { + if (js.JSStringIsEqualToUTF8CString(property_name_ref, "method")) { + if (js.JSObjectGetProperty(ctx, arguments[1], property_name_ref, null)) |value| { + var string_ref = js.JSValueToStringCopy(ctx, value, exception); + + if (exception != null) return js.JSValueMakeNull(ctx); + 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 = http.Method.which(method_name) orelse http_client.method; + } + } + }, + else => {}, + } + } + } + + if (headers) |head| { + http_client.header_entries = head.entries; + http_client.header_buf = head.buf.items; + } + + 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; + } + + var http_response = http_client.send(body, &fetch_body_string) catch |err| { + const fetch_error = std.fmt.allocPrint(getAllocator(ctx), "Fetch error: {s}", .{@errorName(err)}) catch unreachable; + return JSPromise.rejectedPromiseValue(VirtualMachine.vm.global, ZigString.init(fetch_error).toErrorInstance(VirtualMachine.vm.global)).asRef(); + }; + + 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, + }, + }; + + return JSPromise.resolvedPromiseValue( + VirtualMachine.vm.global, + JSValue.fromRef(Response.Class.make(ctx, response)), + ).asRef(); + } +}; + // https://developer.mozilla.org/en-US/docs/Web/API/Headers pub const Headers = struct { pub const Kv = struct { @@ -272,6 +600,77 @@ pub const Headers = struct { return js.JSValueMakeNull(ctx); } + pub fn headersInit(ctx: js.JSContextRef, header_prop: js.JSObjectRef) !?Headers { + const header_keys = js.JSObjectCopyPropertyNames(ctx, header_prop); + defer js.JSPropertyNameArrayRelease(header_keys); + const total_header_count = js.JSPropertyNameArrayGetCount(header_keys); + if (total_header_count == 0) return null; + + // 2 passes through the headers + + // Pass #1: find the "real" count. + // The number of things which are strings or numbers. + // Anything else should be ignored. + // We could throw a TypeError, but ignoring silently is more JavaScript-like imo + var real_header_count: usize = 0; + var estimated_buffer_len: usize = 0; + var j: usize = 0; + while (j < total_header_count) : (j += 1) { + var key_ref = js.JSPropertyNameArrayGetNameAtIndex(header_keys, j); + var value_ref = js.JSObjectGetProperty(ctx, header_prop, key_ref, null); + + switch (js.JSValueGetType(ctx, value_ref)) { + js.JSType.kJSTypeNumber => { + const key_len = js.JSStringGetLength(key_ref); + if (key_len > 0) { + real_header_count += 1; + estimated_buffer_len += key_len; + estimated_buffer_len += std.fmt.count("{d}", .{js.JSValueToNumber(ctx, value_ref, null)}); + } + }, + js.JSType.kJSTypeString => { + const key_len = js.JSStringGetLength(key_ref); + const value_len = js.JSStringGetLength(value_ref); + if (key_len > 0 and value_len > 0) { + real_header_count += 1; + estimated_buffer_len += key_len + value_len; + } + }, + else => {}, + } + } + + if (real_header_count == 0 or estimated_buffer_len == 0) return null; + + j = 0; + var allocator = getAllocator(ctx); + var headers = Headers{ + .allocator = allocator, + .buf = try std.ArrayListUnmanaged(u8).initCapacity(allocator, estimated_buffer_len), + .entries = Headers.Entries{}, + }; + errdefer headers.deinit(); + try headers.entries.ensureTotalCapacity(allocator, real_header_count); + headers.buf.expandToCapacity(); + while (j < total_header_count) : (j += 1) { + var key_ref = js.JSPropertyNameArrayGetNameAtIndex(header_keys, j); + var value_ref = js.JSObjectGetProperty(ctx, header_prop, key_ref, null); + + switch (js.JSValueGetType(ctx, value_ref)) { + js.JSType.kJSTypeNumber => { + if (js.JSStringGetLength(key_ref) == 0) continue; + try headers.appendInit(ctx, key_ref, .kJSTypeNumber, value_ref); + }, + js.JSType.kJSTypeString => { + if (js.JSStringGetLength(value_ref) == 0 or js.JSStringGetLength(key_ref) == 0) continue; + try headers.appendInit(ctx, key_ref, .kJSTypeString, value_ref); + }, + else => {}, + } + } + return headers; + } + // https://developer.mozilla.org/en-US/docs/Web/API/Headers/Headers pub fn constructor( ctx: js.JSContextRef, @@ -283,6 +682,14 @@ pub const Headers = struct { if (arguments.len > 0 and js.JSValueIsObjectOfClass(ctx, arguments[0], Headers.Class.get().*)) { var other = castObj(arguments[0], Headers); other.clone(headers) catch unreachable; + } else if (arguments.len == 1 and js.JSValueIsObject(ctx, arguments[0])) { + headers.* = (JS.headersInit(ctx, arguments[0]) catch unreachable) orelse Headers{ + .entries = @TypeOf(headers.entries){}, + .buf = @TypeOf(headers.buf){}, + .used = 0, + .allocator = getAllocator(ctx), + .guard = Guard.none, + }; } else { headers.* = Headers{ .entries = @TypeOf(headers.entries){}, @@ -356,26 +763,25 @@ pub const Headers = struct { none, }; - // TODO: is it worth making this lazy? instead of copying all the request headers, should we just do it on get/put/iterator? - pub fn fromRequestCtx(allocator: *std.mem.Allocator, request: *http.RequestContext) !Headers { + pub fn fromPicoHeaders(allocator: *std.mem.Allocator, picohttp_headers: []const picohttp.Header) !Headers { var total_len: usize = 0; - for (request.request.headers) |header| { + for (picohttp_headers) |header| { total_len += header.name.len; total_len += header.value.len; } // for the null bytes - total_len += request.request.headers.len * 2; + total_len += picohttp_headers.len * 2; var headers = Headers{ .allocator = allocator, .entries = Entries{}, .buf = std.ArrayListUnmanaged(u8){}, }; - try headers.entries.ensureTotalCapacity(allocator, request.request.headers.len); + try headers.entries.ensureTotalCapacity(allocator, picohttp_headers.len); try headers.buf.ensureTotalCapacity(allocator, total_len); headers.buf.expandToCapacity(); headers.guard = Guard.request; - for (request.request.headers) |header| { + for (picohttp_headers) |header| { headers.entries.appendAssumeCapacity(Kv{ .name = headers.appendString( string, @@ -394,11 +800,14 @@ pub const Headers = struct { }); } - headers.guard = Guard.immutable; - return headers; } + // TODO: is it worth making this lazy? instead of copying all the request headers, should we just do it on get/put/iterator? + pub fn fromRequestCtx(allocator: *std.mem.Allocator, request: *http.RequestContext) !Headers { + return fromPicoHeaders(allocator, request.request.headers); + } + pub fn asStr(headers: *const Headers, ptr: Api.StringPointer) []u8 { return headers.buf.items[ptr.offset..][0..ptr.length]; } @@ -479,7 +888,7 @@ pub const Headers = struct { ), .value = headers.appendString( string, - key, + value, needs_lowercase, needs_normalize, append_null, @@ -577,6 +986,9 @@ pub const Headers = struct { pub const Body = struct { init: Init, value: Value, + ptr: ?[*]u8 = null, + len: usize = 0, + ptr_allocator: ?*std.mem.Allocator = null, pub fn deinit(this: *Body, allocator: *std.mem.Allocator) void { if (this.init.headers) |headers| { @@ -602,7 +1014,7 @@ pub const Body = struct { defer js.JSPropertyNameArrayRelease(array); const count = js.JSPropertyNameArrayGetCount(array); var i: usize = 0; - upper: while (i < count) : (i += 1) { + while (i < count) : (i += 1) { var property_name_ref = js.JSPropertyNameArrayGetNameAtIndex(array, i); switch (js.JSStringGetLength(property_name_ref)) { "headers".len => { @@ -611,73 +1023,7 @@ pub const Body = struct { if (js.JSObjectGetProperty(ctx, init_ref, property_name_ref, null)) |header_prop| { switch (js.JSValueGetType(ctx, header_prop)) { js.JSType.kJSTypeObject => { - const header_keys = js.JSObjectCopyPropertyNames(ctx, header_prop); - defer js.JSPropertyNameArrayRelease(header_keys); - const total_header_count = js.JSPropertyNameArrayGetCount(array); - if (total_header_count == 0) continue :upper; - - // 2 passes through the headers - - // Pass #1: find the "real" count. - // The number of things which are strings or numbers. - // Anything else should be ignored. - // We could throw a TypeError, but ignoring silently is more JavaScript-like imo - var real_header_count: usize = 0; - var estimated_buffer_len: usize = 0; - var j: usize = 0; - while (j < total_header_count) : (j += 1) { - var key_ref = js.JSPropertyNameArrayGetNameAtIndex(header_keys, j); - var value_ref = js.JSObjectGetProperty(ctx, header_prop, key_ref, null); - - switch (js.JSValueGetType(ctx, value_ref)) { - js.JSType.kJSTypeNumber => { - const key_len = js.JSStringGetLength(key_ref); - if (key_len > 0) { - real_header_count += 1; - estimated_buffer_len += key_len; - estimated_buffer_len += std.fmt.count("{d}", .{js.JSValueToNumber(ctx, value_ref, null)}); - } - }, - js.JSType.kJSTypeString => { - const key_len = js.JSStringGetLength(key_ref); - const value_len = js.JSStringGetLength(value_ref); - if (key_len > 0 and value_len > 0) { - real_header_count += 1; - estimated_buffer_len += key_len + value_len; - } - }, - else => {}, - } - } - - if (real_header_count == 0 or estimated_buffer_len == 0) continue :upper; - - j = 0; - var headers = Headers{ - .allocator = allocator, - .buf = try std.ArrayListUnmanaged(u8).initCapacity(allocator, estimated_buffer_len), - .entries = Headers.Entries{}, - }; - errdefer headers.deinit(); - try headers.entries.ensureTotalCapacity(allocator, real_header_count); - - while (j < total_header_count) : (j += 1) { - var key_ref = js.JSPropertyNameArrayGetNameAtIndex(header_keys, j); - var value_ref = js.JSObjectGetProperty(ctx, header_prop, key_ref, null); - - switch (js.JSValueGetType(ctx, value_ref)) { - js.JSType.kJSTypeNumber => { - if (js.JSStringGetLength(key_ref) == 0) continue; - try headers.appendInit(ctx, key_ref, .kJSTypeNumber, value_ref); - }, - js.JSType.kJSTypeString => { - if (js.JSStringGetLength(value_ref) == 0 or js.JSStringGetLength(key_ref) == 0) continue; - try headers.appendInit(ctx, key_ref, .kJSTypeString, value_ref); - }, - else => {}, - } - } - result.headers = headers; + result.headers = try Headers.JS.headersInit(ctx, header_prop); }, else => {}, } @@ -705,10 +1051,12 @@ pub const Body = struct { ArrayBuffer: ArrayBuffer, String: string, Empty: u0, + Unconsumed: u0, pub const Tag = enum { ArrayBuffer, String, Empty, + Unconsumed, }; pub fn length(value: *const Value) usize { @@ -719,7 +1067,7 @@ pub const Body = struct { .String => |str| { return str.len; }, - .Empty => { + else => { return 0; }, } @@ -783,6 +1131,8 @@ pub const Body = struct { } body.value = Value{ .String = str.characters8()[0..len] }; + body.ptr = @intToPtr([*]u8, @ptrToInt(body.value.String.ptr)); + body.len = body.value.String.len; return body; }, .kJSTypeObject => { @@ -807,6 +1157,8 @@ pub const Body = struct { } else |err| {} } body.value = Value{ .ArrayBuffer = buffer }; + body.ptr = buffer.ptr[buffer.offset..buffer.byte_len].ptr; + body.len = buffer.ptr[buffer.offset..buffer.byte_len].len; return body; }, } |