diff options
26 files changed, 2054 insertions, 170 deletions
@@ -1210,7 +1210,7 @@ wasm-return1: EMIT_LLVM_FOR_RELEASE=-emit-llvm -flto="full" EMIT_LLVM_FOR_DEBUG= -EMIT_LLVM=$(EMIT_LLVM_FOR_RELEASE) +EMIT_LLVM=$(EMIT_LLVM_FOR_DEBUG) # We do this outside of build.zig for performance reasons # The C compilation stuff with build.zig is really slow and we don't need to run this as often as the rest diff --git a/examples/lambda.ts b/examples/lambda.ts new file mode 100644 index 000000000..ab2d5bb2b --- /dev/null +++ b/examples/lambda.ts @@ -0,0 +1,214 @@ +const { AWS_LAMBDA_RUNTIME_API, LAMBDA_TASK_ROOT, _HANDLER } = process.env; + +if (!AWS_LAMBDA_RUNTIME_API || AWS_LAMBDA_RUNTIME_API === "") { + throw new Error("AWS_LAMBDA_RUNTIME_API is not set"); +} + +const nextURL = `http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next`; +const sourceDir = LAMBDA_TASK_ROOT; +if (!sourceDir) { + throw new Error("handler is not set"); +} + +// don't care if this fails +if (process.cwd() !== sourceDir) { + try { + process.chdir(sourceDir); + } catch (e) {} +} + +var handlerDot = _HANDLER.lastIndexOf("."); +var sourcefile = handlerDot > 0 ? _HANDLER.substring(0, handlerDot) : _HANDLER; +if (sourcefile.length === 0) { + throw new Error("handler is not set"); +} +if (!sourcefile.startsWith("/")) { + sourcefile = `./${sourcefile}`; +} +function noop() {} +const method = (handlerDot > 0 ? _HANDLER.substring(handlerDot) : "") || "GET"; + +if (typeof process.env.VERBOSE !== "undefined") { + console.time(`Loaded ${sourcefile}`); +} +var Handler; + +try { + Handler = await import(sourcefile); +} catch (e) { + console.error("Error loading sourcefile:", e); + try { + await fetch( + new URL(`http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/init/error`) + .href, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + errorMessage: e.message, + errorType: e.name, + stackTrace: e?.stack?.split("\n") ?? [], + }), + } + ); + } catch (e2) { + console.error("Error sending error to runtime:", e2); + } + process.exit(1); +} + +if (typeof process.env.VERBOSE !== "undefined") { + console.timeEnd(`Loaded ${sourcefile}`); +} + +const handlerFunction = Handler.default?.fetch; +if (typeof handlerFunction !== "function") { + const e = new Error(`${sourcefile} must export default a function called fetch + +Here is an example: + +export default { + fetch(req) { + return new Response("Hello World"); + } +} +`); + + console.error(e); + + try { + await fetch( + new URL(`http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/init/error`) + .href, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + errorMessage: e.message, + errorType: e.name, + stackTrace: e?.stack?.split("\n") ?? [], + }), + } + ); + } catch (e2) { + console.error("Error sending error to runtime:", e2); + } + + process.exit(1); +} + +var baseURLString = AWS_LAMBDA_RUNTIME_API; +if ("baseURI" in Handler.default) { + baseURLString = Handler.default.baseURI?.toString(); +} + +var baseURL; +try { + baseURL = new URL(baseURLString); +} catch (e) { + console.error("Error parsing baseURI:", e); + try { + await fetch( + new URL(`http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/init/error`) + .href, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + errorMessage: e.message, + errorType: e.name, + stackTrace: e?.stack?.split("\n") || [], + }), + } + ); + } catch (e2) { + console.error("Error sending error to runtime:", e2); + } + + process.exit(1); +} + +async function runHandler(response: Response) { + const traceID = response.headers.get("Lambda-Runtime-Trace-Id"); + const requestID = response.headers.get("Lambda-Runtime-Aws-Request-Id"); + var request = new Request(baseURL.href, { + method, + headers: response.headers, + body: + parseInt(response.headers.get("Content-Length") || "0", 10) > 0 + ? await response.blob() + : undefined, + }); + // we are done with the Response object here + // allow it to be GC'd + response = undefined; + + var result: Response; + try { + if (typeof process.env.VERBOSE !== "undefined") { + console.time(`[${traceID}] Run ${request.url}`); + } + result = handlerFunction(request, {}); + if (result && result.then) { + await result; + } + } catch (e1) { + if (typeof process.env.VERBOSE !== "undefined") { + console.error(`[${traceID}] Error running handler:`, e1); + } + fetch( + `http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/${requestID}/error`, + { + method: "POST", + + body: JSON.stringify({ + errorMessage: e1.message, + errorType: e1.name, + stackTrace: e1?.stack?.split("\n") ?? [], + }), + } + ).finally(noop); + return; + } finally { + if (typeof process.env.VERBOSE !== "undefined") { + console.timeEnd(`[${traceID}] Run ${request.url}`); + } + } + + if (!result || !("headers" in result)) { + await fetch( + `http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/${requestID}/error`, + { + method: "POST", + body: JSON.stringify({ + errorMessage: "Expected Response object", + errorType: "ExpectedResponseObject", + stackTrace: [], + }), + } + ); + return; + } + + await fetch( + `http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/${requestID}/response`, + { + method: "POST", + headers: result.headers, + body: await result.blob(), + } + ); + result = undefined; +} + +while (true) { + fetch(nextURL).then(runHandler, console.error); +} + +export {}; diff --git a/src/baby_list.zig b/src/baby_list.zig index b1bfc1786..dcb808936 100644 --- a/src/baby_list.zig +++ b/src/baby_list.zig @@ -1,5 +1,6 @@ const std = @import("std"); const Environment = @import("./env.zig"); +const strings = @import("./string_immutable.zig"); /// This is like ArrayList except it stores the length and capacity as u32 /// In practice, it is very unusual to have lengths above 4 GB @@ -100,5 +101,53 @@ pub fn BabyList(comptime Type: type) type { @setRuntimeSafety(false); return this.ptr[0..this.len]; } + + pub fn write(this: *@This(), allocator: std.mem.Allocator, str: []const u8) !u32 { + if (comptime Type != u8) + @compileError("Unsupported for type " ++ @typeName(Type)); + const initial = this.len; + var list_ = this.listManaged(allocator); + try list_.appendSlice(str); + this.update(list_); + return this.len - initial; + } + pub fn writeLatin1(this: *@This(), allocator: std.mem.Allocator, str: []const u8) !u32 { + if (comptime Type != u8) + @compileError("Unsupported for type " ++ @typeName(Type)); + const initial = this.len; + var list_ = this.listManaged(allocator); + defer this.update(list_); + const start = list_.items.len; + try list_.appendSlice(str); + + strings.replaceLatin1WithUTF8(list_.items[start..list_.items.len]); + + return this.len - initial; + } + pub fn writeUTF16(this: *@This(), allocator: std.mem.Allocator, str: []const u16) !u32 { + if (comptime Type != u8) + @compileError("Unsupported for type " ++ @typeName(Type)); + + var list_ = this.listManaged(allocator); + defer this.update(list_); + try list_.ensureTotalCapacityPrecise(list_.items.len + str.len); + const initial = this.len; + var remain = str; + while (remain.len > 0) { + const orig_len = list_.items.len; + + var slice_ = list_.items.ptr[orig_len..list_.capacity]; + const result = strings.copyUTF16IntoUTF8(slice_, []const u16, remain); + remain = remain[result.read..]; + list_.items.len += @as(usize, result.written); + if (remain.len > 0) { + try list_.ensureTotalCapacityPrecise(list_.items.len + strings.elementLengthUTF16IntoUTF8([]const u16, remain)); + continue; + } + if (result.read == 0 or result.written == 0) break; + } + + return this.len - initial; + } }; } diff --git a/src/javascript/jsc/bindings/JSSink+custom.h b/src/javascript/jsc/bindings/JSSink+custom.h new file mode 100644 index 000000000..744691f53 --- /dev/null +++ b/src/javascript/jsc/bindings/JSSink+custom.h @@ -0,0 +1,10 @@ + +static const HashTableValue JSArrayBufferSinkPrototypeTableValues[] + = { + { "close"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(ArrayBufferSink__close), (intptr_t)(0) } }, + { "closeWithError"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(ArrayBufferSink__closeWithError), (intptr_t)(1) } }, + { "drain"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(ArrayBufferSink__drain), (intptr_t)(1) } }, + { "end"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(ArrayBufferSink__end), (intptr_t)(0) } }, + { "start"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(ArrayBufferSink__start), (intptr_t)(1) } }, + { "write"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(ArrayBufferSink__write), (intptr_t)(1) } }, + }; diff --git a/src/javascript/jsc/bindings/JSSink.cpp b/src/javascript/jsc/bindings/JSSink.cpp new file mode 100644 index 000000000..041f03fa3 --- /dev/null +++ b/src/javascript/jsc/bindings/JSSink.cpp @@ -0,0 +1,220 @@ + +// AUTO-GENERATED FILE. DO NOT EDIT. +// Generated by /Users/jarred/Code/bun/src/javascript/jsc/generate-jssink.js at 2022-06-13T03:43:14.001Z +// To regenerate this file, run: +// +// bun src/javascript/jsc/generate-jssink.js +// +#include "root.h" +#include "JSSink.h" + +#include "ActiveDOMObject.h" +#include "ExtendedDOMClientIsoSubspaces.h" +#include "ExtendedDOMIsoSubspaces.h" +#include "IDLTypes.h" +// #include "JSBlob.h" +#include "JSDOMAttribute.h" +#include "JSDOMBinding.h" +#include "JSDOMConstructor.h" +#include "JSDOMConvertBase.h" +#include "JSDOMConvertInterface.h" +#include "JSDOMConvertStrings.h" +#include "JSDOMExceptionHandling.h" +#include "JSDOMGlobalObject.h" +#include "JSDOMGlobalObjectInlines.h" +#include "JSDOMOperation.h" +#include "JSDOMWrapperCache.h" +#include "ScriptExecutionContext.h" +#include "WebCoreJSClientData.h" +#include "JavaScriptCore/FunctionPrototype.h" +#include "JavaScriptCore/HeapAnalyzer.h" + +#include "JavaScriptCore/JSDestructibleObjectHeapCellType.h" +#include "JavaScriptCore/SlotVisitorMacros.h" +#include "JavaScriptCore/SubspaceInlines.h" +#include "wtf/GetPtr.h" +#include "wtf/PointerPreparations.h" +#include "wtf/URL.h" +#include "JavaScriptCore/BuiltinNames.h" + +#include "JSBufferEncodingType.h" +#include "JSBufferPrototypeBuiltins.h" +#include "JSBufferConstructorBuiltins.h" +#include "JavaScriptCore/JSBase.h" +#if ENABLE(MEDIA_SOURCE) +#include "BufferMediaSource.h" +#include "JSMediaSource.h" +#endif + +// #include "JavaScriptCore/JSTypedArrayViewPrototype.h" +#include "JavaScriptCore/JSArrayBufferViewInlines.h" + +namespace WebCore { +using namespace JSC; + + + +JSC_DEFINE_CUSTOM_GETTER(functionArrayBufferSink__getter, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName)) +{ + auto& vm = lexicalGlobalObject->vm(); + Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject); + + return JSC::JSValue::encode(globalObject->ArrayBufferSink()); +} + +static const HashTableValue JSArrayBufferSinkPrototypeTableValues[] + = { + { "close"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(ArrayBufferSink__close), (intptr_t)(0) } }, + { "closeWithError"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(ArrayBufferSink__closeWithError), (intptr_t)(1) } }, + { "drain"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(ArrayBufferSink__drain), (intptr_t)(1) } }, + { "end"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(ArrayBufferSink__end), (intptr_t)(0) } }, + { "start"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(ArrayBufferSink__start), (intptr_t)(1) } }, + { "write"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(ArrayBufferSink__write), (intptr_t)(1) } }, + }; + +#pragma mark - ArrayBufferSink + +class JSArrayBufferSinkPrototype final : public JSC::JSNonFinalObject { +public: + using Base = JSC::JSNonFinalObject; + + static JSArrayBufferSinkPrototype* create(JSC::VM& vm, JSGlobalObject* globalObject, JSC::Structure* structure) + { + JSArrayBufferSinkPrototype* ptr = new (NotNull, JSC::allocateCell<JSArrayBufferSinkPrototype>(vm)) JSArrayBufferSinkPrototype(vm, globalObject, structure); + ptr->finishCreation(vm, globalObject); + return ptr; + } + + DECLARE_INFO; + template<typename CellType, JSC::SubspaceAccess> + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + return &vm.plainObjectSpace(); + } + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); + } + +private: + JSArrayBufferSinkPrototype(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure) + : Base(vm, structure) + { + } + + void finishCreation(JSC::VM&, JSC::JSGlobalObject*); +}; +STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSArrayBufferSinkPrototype, JSArrayBufferSinkPrototype::Base); + + + +const ClassInfo JSArrayBufferSinkPrototype::s_info = { "ArrayBufferSink"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSArrayBufferSinkPrototype) }; +const ClassInfo JSArrayBufferSink::s_info = { "ArrayBufferSink"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSArrayBufferSink) }; +const ClassInfo JSArrayBufferSinkConstructor::s_info = { "ArrayBufferSink"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSArrayBufferSinkConstructor) }; + +JSArrayBufferSink::~JSArrayBufferSink() +{ + if (m_sinkPtr) { + ArrayBufferSink__finalize(m_sinkPtr); + } +} + + + + +JSArrayBufferSinkConstructor* JSArrayBufferSinkConstructor::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSObject* prototype) +{ + JSArrayBufferSinkConstructor* ptr = new (NotNull, JSC::allocateCell<JSArrayBufferSinkConstructor>(vm)) JSArrayBufferSinkConstructor(vm, structure, ArrayBufferSink__construct); + ptr->finishCreation(vm, globalObject, prototype); + return ptr; +} + +JSArrayBufferSink* JSArrayBufferSink::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr) +{ + JSArrayBufferSink* ptr = new (NotNull, JSC::allocateCell<JSArrayBufferSink>(vm)) JSArrayBufferSink(vm, structure, sinkPtr); + ptr->finishCreation(vm); + return ptr; +} + +void JSArrayBufferSinkConstructor::finishCreation(VM& vm, JSC::JSGlobalObject* globalObject, JSObject* prototype) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + initializeProperties(vm, globalObject, prototype); +} + +JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSArrayBufferSinkConstructor::construct(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) { + return ArrayBufferSink__construct(globalObject, callFrame); +} + + +void JSArrayBufferSinkConstructor::initializeProperties(VM& vm, JSC::JSGlobalObject* globalObject, JSObject* prototype) +{ + putDirect(vm, vm.propertyNames->length, jsNumber(0), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); + JSString* nameString = jsNontrivialString(vm, "ArrayBufferSink"_s); + m_originalName.set(vm, this, nameString); + putDirect(vm, vm.propertyNames->name, nameString, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); +} + +void JSArrayBufferSinkPrototype::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject) +{ + Base::finishCreation(vm); + reifyStaticProperties(vm, JSArrayBufferSink::info(), JSArrayBufferSinkPrototypeTableValues, *this); + JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); +} + +void JSArrayBufferSink::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); +} + +void JSArrayBufferSink::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) +{ + auto* thisObject = jsCast<JSArrayBufferSink*>(cell); + if (void* wrapped = thisObject->wrapped()) { + analyzer.setWrappedObjectForCell(cell, wrapped); + // if (thisObject->scriptExecutionContext()) + // analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + } + Base::analyzeHeap(cell, analyzer); +} + +void JSArrayBufferSink::destroy(JSCell* cell) +{ + static_cast<JSArrayBufferSink*>(cell)->JSArrayBufferSink::~JSArrayBufferSink(); +} + + + + JSObject* createJSSinkPrototype(JSC::VM& vm, JSC::JSGlobalObject* globalObject, SinkID sinkID) + { + switch (sinkID) { + + case ArrayBufferSink: + return JSArrayBufferSinkPrototype::create(vm, globalObject, JSArrayBufferSinkPrototype::createStructure(vm, globalObject, globalObject->objectPrototype())); + +default: + RELEASE_ASSERT_NOT_REACHED(); + } +} +} // namespace WebCore + + +extern "C" JSC__JSValue ArrayBufferSink__createObject(JSC__JSGlobalObject* arg0, void* sinkPtr) +{ + auto& vm = arg0->vm(); + Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(arg0); + JSC::JSValue prototype = globalObject->ArrayBufferSinkPrototype(); + JSC::Structure* structure = WebCore::JSArrayBufferSink::createStructure(vm, globalObject, prototype); + return JSC::JSValue::encode(WebCore::JSArrayBufferSink::create(vm, globalObject, structure, sinkPtr)); +} + +extern "C" void* ArrayBufferSink__fromJS(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1) +{ + JSC::VM& vm = WebCore::getVM(arg0); + if (auto* sink = JSC::jsDynamicCast<WebCore::JSArrayBufferSink*>(JSC::JSValue::decode(JSValue1))) + return sink->wrapped(); + + return nullptr; +} diff --git a/src/javascript/jsc/bindings/JSSink.h b/src/javascript/jsc/bindings/JSSink.h new file mode 100644 index 000000000..c8e935c8e --- /dev/null +++ b/src/javascript/jsc/bindings/JSSink.h @@ -0,0 +1,106 @@ + +// AUTO-GENERATED FILE. DO NOT EDIT. +// Generated by /Users/jarred/Code/bun/src/javascript/jsc/generate-jssink.js at 2022-06-13T03:43:13.999Z +// +#pragma once + +#include "root.h" + +#include "JSDOMWrapper.h" +#include "wtf/NeverDestroyed.h" + +#include "Sink.h" + +extern "C" bool JSSink_isSink(JSC::JSGlobalObject*, JSC::EncodedJSValue); + +namespace WebCore { +using namespace JSC; +class JSArrayBufferSinkConstructor final : public JSC::InternalFunction { + public: + using Base = JSC::InternalFunction; + static JSArrayBufferSinkConstructor* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSC::JSObject* prototype); + static constexpr SinkID Sink = SinkID::ArrayBufferSink; + + static constexpr unsigned StructureFlags = Base::StructureFlags; + static constexpr bool needsDestruction = false; + + DECLARE_EXPORT_INFO; + template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return WebCore::subspaceForImpl<JSArrayBufferSinkConstructor, WebCore::UseCustomHeapCellType::No>( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForJSSinkConstructor.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForJSSinkConstructor = WTFMove(space); }, + [](auto& spaces) { return spaces.m_subspaceForJSSinkConstructor.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForJSSinkConstructor = WTFMove(space); }); + } + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), info()); + } + void initializeProperties(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSObject* prototype); + + + // Must be defined for each specialization class. + static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSC::JSGlobalObject*, JSC::CallFrame*); + + private: + JSArrayBufferSinkConstructor(JSC::VM& vm, JSC::Structure* structure, JSC::NativeFunction nativeFunction) + : Base(vm, structure, nativeFunction, nativeFunction) + + { + } + + void finishCreation(JSC::VM&, JSC::JSGlobalObject* globalObject, JSC::JSObject* prototype); + }; + + class JSArrayBufferSink final : public JSC::JSDestructibleObject { + public: + using Base = JSC::JSDestructibleObject; + static JSArrayBufferSink* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr); + static constexpr SinkID Sink = SinkID::ArrayBufferSink; + + DECLARE_EXPORT_INFO; + template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return WebCore::subspaceForImpl<JSArrayBufferSink, WebCore::UseCustomHeapCellType::No>( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForJSSink.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForJSSink = WTFMove(space); }, + [](auto& spaces) { return spaces.m_subspaceForJSSink.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForJSSink = WTFMove(space); }); + } + + static void destroy(JSC::JSCell*); + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); + } + + ~JSArrayBufferSink(); + + void* wrapped() const { return m_sinkPtr; } + + static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&); + + void* m_sinkPtr; + + JSArrayBufferSink(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr) + : Base(vm, structure) + { + m_sinkPtr = sinkPtr; + } + + void finishCreation(JSC::VM&); + }; +JSC_DECLARE_CUSTOM_GETTER(functionArrayBufferSink__getter); + + +JSObject* createJSSinkPrototype(JSC::VM& vm, JSC::JSGlobalObject* globalObject, WebCore::SinkID sinkID); + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/Sink.h b/src/javascript/jsc/bindings/Sink.h new file mode 100644 index 000000000..3d7435b29 --- /dev/null +++ b/src/javascript/jsc/bindings/Sink.h @@ -0,0 +1,15 @@ +#include "root.h" + +namespace WebCore { + +enum SinkID : uint8_t { + ArrayBufferSink = 0, + TextSink = 1, + FileSink = 2, + HTMLRewriterSink = 3, + +}; +static constexpr unsigned numberOfSinkIDs + = 4; + +}
\ No newline at end of file diff --git a/src/javascript/jsc/bindings/ZigGlobalObject.cpp b/src/javascript/jsc/bindings/ZigGlobalObject.cpp index f0fb661a4..ba1965c9a 100644 --- a/src/javascript/jsc/bindings/ZigGlobalObject.cpp +++ b/src/javascript/jsc/bindings/ZigGlobalObject.cpp @@ -136,6 +136,8 @@ using JSBuffer = WebCore::JSBuffer; #include "StructuredClone.h" #include "ReadableStream.h" +#include "JSSink.h" + // #include <iostream> static bool has_loaded_jsc = false; @@ -1635,6 +1637,26 @@ void GlobalObject::finishCreation(VM& vm) Base::finishCreation(vm); ASSERT(inherits(info())); + m_NapiClassStructure.initLater( + [](LazyClassStructure::Initializer& init) { + init.setStructure(Zig::NapiClass::createStructure(init.vm, init.global, init.global->functionPrototype())); + }); + + m_JSArrayBufferSinkClassStructure.initLater( + [](LazyClassStructure::Initializer& init) { + auto* prototype = createJSSinkPrototype(init.vm, init.global, WebCore::SinkID::ArrayBufferSink); + auto* structure = JSArrayBufferSink::createStructure(init.vm, init.global, prototype); + auto* constructor = JSArrayBufferSinkConstructor::create(init.vm, init.global, JSArrayBufferSinkConstructor::createStructure(init.vm, init.global, init.global->functionPrototype()), jsCast<JSObject*>(prototype)); + init.setPrototype(prototype); + init.setStructure(structure); + init.setConstructor(constructor); + }); + + m_JSFFIFunctionStructure.initLater( + [](LazyClassStructure::Initializer& init) { + init.setStructure(Zig::JSFFIFunction::createStructure(init.vm, init.global, init.global->functionPrototype())); + }); + addBuiltinGlobals(vm); RELEASE_ASSERT(classInfo()); @@ -1878,6 +1900,12 @@ void GlobalObject::installAPIGlobals(JSClassRef* globals, int count, JSC::VM& vm } { + JSC::Identifier identifier = JSC::Identifier::fromString(vm, "ArrayBufferSink"_s); + object->putDirectCustomAccessor(vm, identifier, JSC::CustomGetterSetter::create(vm, functionArrayBufferSink__getter, nullptr), + JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly); + } + + { object->putDirectBuiltinFunction(vm, this, builtinNames.readableStreamToArrayPublicName(), readableStreamReadableStreamToArrayCodeGenerator(vm), PropertyAttribute::Builtin | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly); } @@ -1926,16 +1954,6 @@ void GlobalObject::installAPIGlobals(JSClassRef* globals, int count, JSC::VM& vm this->addStaticGlobals(extraStaticGlobals.data(), extraStaticGlobals.size()); - m_NapiClassStructure.initLater( - [](LazyClassStructure::Initializer& init) { - init.setStructure(Zig::NapiClass::createStructure(init.vm, init.global, init.global->m_functionPrototype.get())); - }); - - m_JSFFIFunctionStructure.initLater( - [](LazyClassStructure::Initializer& init) { - init.setStructure(Zig::JSFFIFunction::createStructure(init.vm, init.global, init.global->m_functionPrototype.get())); - }); - // putDirectCustomAccessor(vm, JSC::Identifier::fromString(vm, "SQL"_s), JSC::CustomGetterSetter::create(vm, JSSQLStatement_getter, nullptr), // JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly); @@ -1970,10 +1988,13 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor) thisObject->m_builtinInternalFunctions.visit(visitor); thisObject->m_JSFFIFunctionStructure.visit(visitor); + thisObject->m_JSArrayBufferSinkClassStructure.visit(visitor); + visitor.append(thisObject->m_readableStreamToArrayBufferResolve); visitor.append(thisObject->m_readableStreamToText); visitor.append(thisObject->m_readableStreamToJSON); visitor.append(thisObject->m_readableStreamToBlob); + ScriptExecutionContext* context = thisObject->scriptExecutionContext(); visitor.addOpaqueRoot(context); } diff --git a/src/javascript/jsc/bindings/ZigGlobalObject.h b/src/javascript/jsc/bindings/ZigGlobalObject.h index 54d511c94..34c9ef412 100644 --- a/src/javascript/jsc/bindings/ZigGlobalObject.h +++ b/src/javascript/jsc/bindings/ZigGlobalObject.h @@ -148,6 +148,9 @@ public: WebCore::JSBuiltinInternalFunctions& builtinInternalFunctions() { return m_builtinInternalFunctions; } JSC::Structure* FFIFunctionStructure() { return m_JSFFIFunctionStructure.getInitializedOnMainThread(this); } JSC::Structure* NapiClassStructure() { return m_NapiClassStructure.getInitializedOnMainThread(this); } + JSC::Structure* ArrayBufferSinkStructure() { return m_JSArrayBufferSinkClassStructure.getInitializedOnMainThread(this); } + JSC::JSObject* ArrayBufferSink() { return m_JSArrayBufferSinkClassStructure.constructorInitializedOnMainThread(this); } + JSC::JSValue ArrayBufferSinkPrototype() { return m_JSArrayBufferSinkClassStructure.prototypeInitializedOnMainThread(this); } void* bunVM() { return m_bunVM; } bool isThreadLocalDefaultGlobalObject = false; @@ -171,6 +174,7 @@ private: Ref<WebCore::DOMWrapperWorld> m_world; LazyClassStructure m_JSFFIFunctionStructure; LazyClassStructure m_NapiClassStructure; + LazyClassStructure m_JSArrayBufferSinkClassStructure; DOMGuardedObjectSet m_guardedObjects WTF_GUARDED_BY_LOCK(m_gcLock); void* m_bunVM; diff --git a/src/javascript/jsc/bindings/bindings.zig b/src/javascript/jsc/bindings/bindings.zig index 31f9b497d..cb606ba39 100644 --- a/src/javascript/jsc/bindings/bindings.zig +++ b/src/javascript/jsc/bindings/bindings.zig @@ -3236,6 +3236,8 @@ pub const CallFrame = opaque { const argumentsCount_offset = thisValue_offset - 1; const alignment = std.meta.alignment([]const JSC.JSValue); + pub const name = "JSC::CallFrame"; + pub fn argumentsPtr(self: *const CallFrame) [*]const JSC.JSValue { return @ptrCast([*]const JSC.JSValue, @alignCast(alignment, self)) + arguments_offset; } diff --git a/src/javascript/jsc/bindings/builtins/js/ReadableStream.js b/src/javascript/jsc/bindings/builtins/js/ReadableStream.js index 4874067c3..57a683162 100644 --- a/src/javascript/jsc/bindings/builtins/js/ReadableStream.js +++ b/src/javascript/jsc/bindings/builtins/js/ReadableStream.js @@ -313,6 +313,8 @@ function consumeReadableStream(nativePtr, nativeType, inputStream) { if (!this.#ptr) return @throwTypeError("ReadableStreamSink is already closed"); return this.processResult(this.#reader.readMany()); } + + }; const minlength = nativeType + 1; @@ -326,7 +328,7 @@ function consumeReadableStream(nativePtr, nativeType, inputStream) { @throwTypeError("Cannot start reading from a locked stream"); } - return new Prototype(inputStream.getReader(), nativePtr); + return new Prototype(inputStream.getReader(), nativePtr); } @globalPrivate diff --git a/src/javascript/jsc/bindings/exports.zig b/src/javascript/jsc/bindings/exports.zig index 4311eed61..47cd3488d 100644 --- a/src/javascript/jsc/bindings/exports.zig +++ b/src/javascript/jsc/bindings/exports.zig @@ -183,6 +183,9 @@ pub const NodePath = JSC.Node.Path; pub const JSReadableStreamBlob = JSC.WebCore.ByteBlobLoader.Source.JSReadableStreamSource; pub const JSReadableStreamFile = JSC.WebCore.FileBlobLoader.Source.JSReadableStreamSource; +// Sinks +pub const JSArrayBufferSink = JSC.WebCore.ArrayBufferSink.JSArrayBufferSink; + pub fn Errorable(comptime Type: type) type { return extern struct { result: Result, @@ -1599,9 +1602,8 @@ pub const ZigConsoleClient = struct { writer.print(comptime Output.prettyFmt("<r><yellow>{d}<r>", enable_ansi_colors), .{value.toInt64()}); }, .BigInt => { - var sliced = value.toSlice(this.globalThis, bun.default_allocator); - defer sliced.deinit(); - writer.print(comptime Output.prettyFmt("<r><yellow>{s}<r>", enable_ansi_colors), .{sliced.slice()}); + var wtf = value.toWTFString(this.globalThis); + writer.print(comptime Output.prettyFmt("<r><yellow>{s}n<r>", enable_ansi_colors), .{wtf.slice()}); }, .Double => { writer.print(comptime Output.prettyFmt("<r><yellow>{d}<r>", enable_ansi_colors), .{value.asNumber()}); @@ -2507,12 +2509,14 @@ comptime { _ = Process.setTitle; _ = Zig__getAPIGlobals; _ = Zig__getAPIConstructors; - std.testing.refAllDecls(NodeReadableStream); - std.testing.refAllDecls(Bun.Timer); - std.testing.refAllDecls(NodeWritableStream); - std.testing.refAllDecls(NodePath); - std.testing.refAllDecls(JSReadableStreamBlob); - std.testing.refAllDecls(JSReadableStreamFile); + NodeReadableStream.shim.ref(); + Bun.Timer.shim.ref(); + NodeWritableStream.shim.ref(); + NodePath.shim.ref(); + JSReadableStreamBlob.shim.ref(); + JSArrayBufferSink.shim.ref(); + + JSReadableStreamFile.shim.ref(); _ = ZigString__free; _ = ZigString__free_global; } diff --git a/src/javascript/jsc/bindings/headers-cpp.h b/src/javascript/jsc/bindings/headers-cpp.h index 28db16511..c2c68bc60 100644 --- a/src/javascript/jsc/bindings/headers-cpp.h +++ b/src/javascript/jsc/bindings/headers-cpp.h @@ -1,4 +1,4 @@ -//-- AUTOGENERATED FILE -- 1654055796 +//-- AUTOGENERATED FILE -- 1655075078 // clang-format off #pragma once @@ -256,8 +256,8 @@ extern "C" const size_t Zig__ConsoleClient_object_align_ = alignof(Zig::ConsoleC extern "C" const size_t Bun__Timer_object_size_ = sizeof(Bun__Timer); extern "C" const size_t Bun__Timer_object_align_ = alignof(Bun__Timer); -const size_t sizes[31] = {sizeof(JSC::JSObject), sizeof(WebCore::DOMURL), sizeof(WebCore::FetchHeaders), sizeof(SystemError), sizeof(JSC::JSCell), sizeof(JSC::JSString), sizeof(Inspector::ScriptArguments), sizeof(JSC::JSModuleLoader), sizeof(JSC::JSModuleRecord), sizeof(JSC::JSPromise), sizeof(JSC::JSInternalPromise), sizeof(JSC::SourceOrigin), sizeof(JSC::SourceCode), sizeof(JSC::JSFunction), sizeof(JSC::JSGlobalObject), sizeof(WTF::URL), sizeof(WTF::String), sizeof(JSC::JSValue), sizeof(JSC::PropertyName), sizeof(JSC::Exception), sizeof(JSC::VM), sizeof(JSC::ThrowScope), sizeof(JSC::CatchScope), sizeof(JSC::Identifier), sizeof(WTF::StringImpl), sizeof(WTF::ExternalStringImpl), sizeof(WTF::StringView), sizeof(Zig::GlobalObject), sizeof(Bun__Readable), sizeof(Bun__Writable), sizeof(Bun__Path)}; +const size_t sizes[32] = {sizeof(JSC::JSObject), sizeof(WebCore::DOMURL), sizeof(WebCore::FetchHeaders), sizeof(SystemError), sizeof(JSC::JSCell), sizeof(JSC::JSString), sizeof(Inspector::ScriptArguments), sizeof(JSC::JSModuleLoader), sizeof(JSC::JSModuleRecord), sizeof(JSC::JSPromise), sizeof(JSC::JSInternalPromise), sizeof(JSC::SourceOrigin), sizeof(JSC::SourceCode), sizeof(JSC::JSFunction), sizeof(JSC::JSGlobalObject), sizeof(WTF::URL), sizeof(WTF::String), sizeof(JSC::JSValue), sizeof(JSC::PropertyName), sizeof(JSC::Exception), sizeof(JSC::VM), sizeof(JSC::ThrowScope), sizeof(JSC::CatchScope), sizeof(JSC::Identifier), sizeof(WTF::StringImpl), sizeof(WTF::ExternalStringImpl), sizeof(WTF::StringView), sizeof(Zig::GlobalObject), sizeof(Bun__Readable), sizeof(Bun__Writable), sizeof(Bun__Path), sizeof(ArrayBufferSink)}; -const char* names[31] = {"JSC__JSObject", "WebCore__DOMURL", "WebCore__FetchHeaders", "SystemError", "JSC__JSCell", "JSC__JSString", "Inspector__ScriptArguments", "JSC__JSModuleLoader", "JSC__JSModuleRecord", "JSC__JSPromise", "JSC__JSInternalPromise", "JSC__SourceOrigin", "JSC__SourceCode", "JSC__JSFunction", "JSC__JSGlobalObject", "WTF__URL", "WTF__String", "JSC__JSValue", "JSC__PropertyName", "JSC__Exception", "JSC__VM", "JSC__ThrowScope", "JSC__CatchScope", "JSC__Identifier", "WTF__StringImpl", "WTF__ExternalStringImpl", "WTF__StringView", "Zig__GlobalObject", "Bun__Readable", "Bun__Writable", "Bun__Path"}; +const char* names[32] = {"JSC__JSObject", "WebCore__DOMURL", "WebCore__FetchHeaders", "SystemError", "JSC__JSCell", "JSC__JSString", "Inspector__ScriptArguments", "JSC__JSModuleLoader", "JSC__JSModuleRecord", "JSC__JSPromise", "JSC__JSInternalPromise", "JSC__SourceOrigin", "JSC__SourceCode", "JSC__JSFunction", "JSC__JSGlobalObject", "WTF__URL", "WTF__String", "JSC__JSValue", "JSC__PropertyName", "JSC__Exception", "JSC__VM", "JSC__ThrowScope", "JSC__CatchScope", "JSC__Identifier", "WTF__StringImpl", "WTF__ExternalStringImpl", "WTF__StringView", "Zig__GlobalObject", "Bun__Readable", "Bun__Writable", "Bun__Path", "ArrayBufferSink"}; -const size_t aligns[31] = {alignof(JSC::JSObject), alignof(WebCore::DOMURL), alignof(WebCore::FetchHeaders), alignof(SystemError), alignof(JSC::JSCell), alignof(JSC::JSString), alignof(Inspector::ScriptArguments), alignof(JSC::JSModuleLoader), alignof(JSC::JSModuleRecord), alignof(JSC::JSPromise), alignof(JSC::JSInternalPromise), alignof(JSC::SourceOrigin), alignof(JSC::SourceCode), alignof(JSC::JSFunction), alignof(JSC::JSGlobalObject), alignof(WTF::URL), alignof(WTF::String), alignof(JSC::JSValue), alignof(JSC::PropertyName), alignof(JSC::Exception), alignof(JSC::VM), alignof(JSC::ThrowScope), alignof(JSC::CatchScope), alignof(JSC::Identifier), alignof(WTF::StringImpl), alignof(WTF::ExternalStringImpl), alignof(WTF::StringView), alignof(Zig::GlobalObject), alignof(Bun__Readable), alignof(Bun__Writable), alignof(Bun__Path)}; +const size_t aligns[32] = {alignof(JSC::JSObject), alignof(WebCore::DOMURL), alignof(WebCore::FetchHeaders), alignof(SystemError), alignof(JSC::JSCell), alignof(JSC::JSString), alignof(Inspector::ScriptArguments), alignof(JSC::JSModuleLoader), alignof(JSC::JSModuleRecord), alignof(JSC::JSPromise), alignof(JSC::JSInternalPromise), alignof(JSC::SourceOrigin), alignof(JSC::SourceCode), alignof(JSC::JSFunction), alignof(JSC::JSGlobalObject), alignof(WTF::URL), alignof(WTF::String), alignof(JSC::JSValue), alignof(JSC::PropertyName), alignof(JSC::Exception), alignof(JSC::VM), alignof(JSC::ThrowScope), alignof(JSC::CatchScope), alignof(JSC::Identifier), alignof(WTF::StringImpl), alignof(WTF::ExternalStringImpl), alignof(WTF::StringView), alignof(Zig::GlobalObject), alignof(Bun__Readable), alignof(Bun__Writable), alignof(Bun__Path), alignof(ArrayBufferSink)}; diff --git a/src/javascript/jsc/bindings/headers-handwritten.h b/src/javascript/jsc/bindings/headers-handwritten.h index 7409427ac..a2f15cb74 100644 --- a/src/javascript/jsc/bindings/headers-handwritten.h +++ b/src/javascript/jsc/bindings/headers-handwritten.h @@ -44,6 +44,8 @@ typedef struct SystemError { ZigString syscall; } SystemError; +typedef void* ArrayBufferSink; + typedef uint8_t ZigStackFrameCode; const ZigStackFrameCode ZigStackFrameCodeNone = 0; const ZigStackFrameCode ZigStackFrameCodeEval = 1; diff --git a/src/javascript/jsc/bindings/headers-replacements.zig b/src/javascript/jsc/bindings/headers-replacements.zig index fb5cf1496..4491c79af 100644 --- a/src/javascript/jsc/bindings/headers-replacements.zig +++ b/src/javascript/jsc/bindings/headers-replacements.zig @@ -1,6 +1,6 @@ // GENERATED FILE - do not modify! const bindings = @import("../../../jsc.zig"); - +pub const struct_JSC__CallFrame = bindings.CallFrame; pub const struct_JSC__StringPrototype = bindings.StringPrototype; pub const struct_JSC__SetIteratorPrototype = bindings.SetIteratorPrototype; pub const struct_JSC__RegExpPrototype = bindings.RegExpPrototype; @@ -17,6 +17,7 @@ pub const struct_JSC__AsyncGeneratorPrototype = bindings.AsyncGeneratorPrototype pub const struct_JSC__AsyncGeneratorFunctionPrototype = bindings.AsyncGeneratorFunctionPrototype; pub const struct_JSC__AsyncFunctionPrototype = bindings.AsyncFunctionPrototype; pub const struct_JSC__ArrayPrototype = bindings.ArrayPrototype; + pub const struct_JSC__ArrayIteratorPrototype = bindings.ArrayIteratorPrototype; pub const bWTF__URL = bindings.URL; pub const bWTF__StringView = bindings.StringView; @@ -63,3 +64,4 @@ pub const struct_WebCore__DOMURL = bindings.DOMURL; pub const struct_WebCore__FetchHeaders = bindings.FetchHeaders; pub const StringPointer = @import("../../../api/schema.zig").Api.StringPointer; pub const struct_VirtualMachine = bindings.VirtualMachine; +pub const ArrayBufferSink = @import("../webcore/streams.zig").ArrayBufferSink; diff --git a/src/javascript/jsc/bindings/headers.h b/src/javascript/jsc/bindings/headers.h index 217fdd884..e3d642676 100644 --- a/src/javascript/jsc/bindings/headers.h +++ b/src/javascript/jsc/bindings/headers.h @@ -1,5 +1,5 @@ // clang-format: off -//-- AUTOGENERATED FILE -- 1654055796 +//-- AUTOGENERATED FILE -- 1655075078 #pragma once #include <stddef.h> @@ -105,6 +105,7 @@ typedef void* JSClassRef; typedef struct JSC__RegExpPrototype JSC__RegExpPrototype; // JSC::RegExpPrototype typedef struct JSC__MapIteratorPrototype JSC__MapIteratorPrototype; // JSC::MapIteratorPrototype typedef struct WebCore__FetchHeaders WebCore__FetchHeaders; // WebCore::FetchHeaders + typedef struct JSC__CallFrame JSC__CallFrame; // JSC::CallFrame typedef bWTF__StringView WTF__StringView; // WTF::StringView typedef bJSC__ThrowScope JSC__ThrowScope; // JSC::ThrowScope typedef bWTF__StringImpl WTF__StringImpl; // WTF::StringImpl @@ -165,6 +166,7 @@ typedef void* JSClassRef; class SourceCode; class FunctionPrototype; class IteratorPrototype; + class CallFrame; class ObjectPrototype; } namespace WTF { @@ -228,6 +230,7 @@ typedef void* JSClassRef; using JSC__SourceCode = JSC::SourceCode; using JSC__FunctionPrototype = JSC::FunctionPrototype; using JSC__IteratorPrototype = JSC::IteratorPrototype; + using JSC__CallFrame = JSC::CallFrame; using JSC__ObjectPrototype = JSC::ObjectPrototype; using WTF__URL = WTF::URL; using WTF__StringImpl = WTF::StringImpl; @@ -727,6 +730,21 @@ ZIG_DECL JSC__JSValue ByteBlob__JSReadableStreamSource__load(JSC__JSGlobalObject ZIG_DECL JSC__JSValue FileBlobLoader__JSReadableStreamSource__load(JSC__JSGlobalObject* arg0); #endif +CPP_DECL JSC__JSValue ArrayBufferSink__createObject(JSC__JSGlobalObject* arg0, void* arg1); +CPP_DECL void* ArrayBufferSink__fromJS(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1); + +#ifdef __cplusplus + +ZIG_DECL JSC__JSValue ArrayBufferSink__close(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); +ZIG_DECL JSC__JSValue ArrayBufferSink__closeWithError(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); +ZIG_DECL JSC__JSValue ArrayBufferSink__construct(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); +ZIG_DECL JSC__JSValue ArrayBufferSink__drain(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); +ZIG_DECL JSC__JSValue ArrayBufferSink__end(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); +ZIG_DECL void ArrayBufferSink__finalize(void* arg0); +ZIG_DECL JSC__JSValue ArrayBufferSink__start(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); +ZIG_DECL JSC__JSValue ArrayBufferSink__write(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); + +#endif #ifdef __cplusplus diff --git a/src/javascript/jsc/bindings/headers.zig b/src/javascript/jsc/bindings/headers.zig index ce7cbee1e..e6417ab0f 100644 --- a/src/javascript/jsc/bindings/headers.zig +++ b/src/javascript/jsc/bindings/headers.zig @@ -1,6 +1,6 @@ // GENERATED FILE - do not modify! const bindings = @import("../../../jsc.zig"); - +pub const struct_JSC__CallFrame = bindings.CallFrame; pub const struct_JSC__StringPrototype = bindings.StringPrototype; pub const struct_JSC__SetIteratorPrototype = bindings.SetIteratorPrototype; pub const struct_JSC__RegExpPrototype = bindings.RegExpPrototype; @@ -17,6 +17,7 @@ pub const struct_JSC__AsyncGeneratorPrototype = bindings.AsyncGeneratorPrototype pub const struct_JSC__AsyncGeneratorFunctionPrototype = bindings.AsyncGeneratorFunctionPrototype; pub const struct_JSC__AsyncFunctionPrototype = bindings.AsyncFunctionPrototype; pub const struct_JSC__ArrayPrototype = bindings.ArrayPrototype; + pub const struct_JSC__ArrayIteratorPrototype = bindings.ArrayIteratorPrototype; pub const bWTF__URL = bindings.URL; pub const bWTF__StringView = bindings.StringView; @@ -63,6 +64,7 @@ pub const struct_WebCore__DOMURL = bindings.DOMURL; pub const struct_WebCore__FetchHeaders = bindings.FetchHeaders; pub const StringPointer = @import("../../../api/schema.zig").Api.StringPointer; pub const struct_VirtualMachine = bindings.VirtualMachine; +pub const ArrayBufferSink = @import("../webcore/streams.zig").ArrayBufferSink; // GENERATED CODE - DO NOT MODIFY BY HAND pub const ptrdiff_t = c_long; @@ -116,6 +118,8 @@ pub const JSC__RegExpPrototype = struct_JSC__RegExpPrototype; pub const JSC__MapIteratorPrototype = struct_JSC__MapIteratorPrototype; pub const WebCore__FetchHeaders = struct_WebCore__FetchHeaders; + +pub const JSC__CallFrame = struct_JSC__CallFrame; pub const WTF__StringView = bWTF__StringView; pub const JSC__ThrowScope = bJSC__ThrowScope; pub const WTF__StringImpl = bWTF__StringImpl; @@ -460,4 +464,6 @@ pub extern fn Zig__GlobalObject__resetModuleRegistryMap(arg0: [*c]JSC__JSGlobalO pub extern fn Bun__Readable__create(arg0: [*c]Bun__Readable, arg1: [*c]JSC__JSGlobalObject) JSC__JSValue; pub extern fn Bun__Writable__create(arg0: [*c]Bun__Writable, arg1: [*c]JSC__JSGlobalObject) JSC__JSValue; pub extern fn Bun__Path__create(arg0: [*c]JSC__JSGlobalObject, arg1: bool) JSC__JSValue; +pub extern fn ArrayBufferSink__createObject(arg0: [*c]JSC__JSGlobalObject, arg1: ?*anyopaque) JSC__JSValue; +pub extern fn ArrayBufferSink__fromJS(arg0: [*c]JSC__JSGlobalObject, JSValue1: JSC__JSValue) ?*anyopaque; pub extern fn ZigException__fromException(arg0: [*c]JSC__Exception) ZigException; diff --git a/src/javascript/jsc/bindings/shimmer.zig b/src/javascript/jsc/bindings/shimmer.zig index 116327207..8d8fda591 100644 --- a/src/javascript/jsc/bindings/shimmer.zig +++ b/src/javascript/jsc/bindings/shimmer.zig @@ -13,6 +13,20 @@ pub fn Shimmer(comptime _namespace: []const u8, comptime _name: []const u8, comp pub const namespace = _namespace; pub const name = _name; + pub fn ref() void { + if (comptime @hasDecl(Parent, "Export")) { + inline for (Parent.Export) |exp| { + _ = exp; + } + } + + if (comptime @hasDecl(Parent, "Extern")) { + inline for (Parent.Extern) |exp| { + _ = @field(Parent, exp); + } + } + } + // fn toCppType(comptime FromType: type) type { // var NewReturnType = FromType; diff --git a/src/javascript/jsc/bindings/webcore/DOMClientIsoSubspaces.h b/src/javascript/jsc/bindings/webcore/DOMClientIsoSubspaces.h index 14e748a9a..876b6843f 100644 --- a/src/javascript/jsc/bindings/webcore/DOMClientIsoSubspaces.h +++ b/src/javascript/jsc/bindings/webcore/DOMClientIsoSubspaces.h @@ -22,6 +22,8 @@ public: std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForNapiPrototype; std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSSQLStatement; std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSSQLStatementConstructor; + std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSSinkConstructor; + std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSSink; /* --- bun --- */ std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForDOMException; diff --git a/src/javascript/jsc/bindings/webcore/DOMIsoSubspaces.h b/src/javascript/jsc/bindings/webcore/DOMIsoSubspaces.h index b434ff771..6b55f05e7 100644 --- a/src/javascript/jsc/bindings/webcore/DOMIsoSubspaces.h +++ b/src/javascript/jsc/bindings/webcore/DOMIsoSubspaces.h @@ -22,6 +22,8 @@ public: std::unique_ptr<IsoSubspace> m_subspaceForNapiPrototype; std::unique_ptr<IsoSubspace> m_subspaceForJSSQLStatement; std::unique_ptr<IsoSubspace> m_subspaceForJSSQLStatementConstructor; + std::unique_ptr<IsoSubspace> m_subspaceForJSSinkConstructor; + std::unique_ptr<IsoSubspace> m_subspaceForJSSink; /*-- BUN --*/ // std::unique_ptr<IsoSubspace> m_subspaceForTouch; diff --git a/src/javascript/jsc/generate-jssink.js b/src/javascript/jsc/generate-jssink.js new file mode 100644 index 000000000..ba1bbf7ab --- /dev/null +++ b/src/javascript/jsc/generate-jssink.js @@ -0,0 +1,380 @@ +const classes = ["ArrayBufferSink"]; + +function header() { + function classTemplate(idName) { + const name = `JS${idName}`; + const constructor = `${name}Constructor`; + const constructorName = `JS${name}Constructor`; + + return `class ${constructor} final : public JSC::InternalFunction { + public: + using Base = JSC::InternalFunction; + static ${constructor}* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSC::JSObject* prototype); + static constexpr SinkID Sink = SinkID::${idName}; + + static constexpr unsigned StructureFlags = Base::StructureFlags; + static constexpr bool needsDestruction = false; + + DECLARE_EXPORT_INFO; + template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return WebCore::subspaceForImpl<${constructor}, WebCore::UseCustomHeapCellType::No>( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForJSSinkConstructor.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForJSSinkConstructor = WTFMove(space); }, + [](auto& spaces) { return spaces.m_subspaceForJSSinkConstructor.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForJSSinkConstructor = WTFMove(space); }); + } + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), info()); + } + void initializeProperties(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSObject* prototype); + + + // Must be defined for each specialization class. + static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSC::JSGlobalObject*, JSC::CallFrame*); + + private: + ${constructor}(JSC::VM& vm, JSC::Structure* structure, JSC::NativeFunction nativeFunction) + : Base(vm, structure, nativeFunction, nativeFunction) + + { + } + + void finishCreation(JSC::VM&, JSC::JSGlobalObject* globalObject, JSC::JSObject* prototype); + }; + + class ${name} final : public JSC::JSDestructibleObject { + public: + using Base = JSC::JSDestructibleObject; + static ${name}* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr); + static constexpr SinkID Sink = SinkID::${idName}; + + DECLARE_EXPORT_INFO; + template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return WebCore::subspaceForImpl<${name}, WebCore::UseCustomHeapCellType::No>( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForJSSink.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForJSSink = WTFMove(space); }, + [](auto& spaces) { return spaces.m_subspaceForJSSink.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForJSSink = WTFMove(space); }); + } + + static void destroy(JSC::JSCell*); + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); + } + + ~${name}(); + + void* wrapped() const { return m_sinkPtr; } + + static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&); + + void* m_sinkPtr; + + ${name}(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr) + : Base(vm, structure) + { + m_sinkPtr = sinkPtr; + } + + void finishCreation(JSC::VM&); + }; +JSC_DECLARE_CUSTOM_GETTER(function${idName}__getter); + + `; + } + + const outer = ` +// AUTO-GENERATED FILE. DO NOT EDIT. +// Generated by ${import.meta.path} at ${new Date().toISOString()} +// +#pragma once + +#include "root.h" + +#include "JSDOMWrapper.h" +#include "wtf/NeverDestroyed.h" + +#include "Sink.h" + +extern "C" bool JSSink_isSink(JSC::JSGlobalObject*, JSC::EncodedJSValue); + +namespace WebCore { +using namespace JSC; +`; + + const bottom = `JSObject* createJSSinkPrototype(JSC::VM& vm, JSC::JSGlobalObject* globalObject, WebCore::SinkID sinkID); + +} // namespace WebCore +`; + var templ = outer; + for (let name of classes) { + templ += classTemplate(name) + "\n"; + } + templ += bottom; + return templ; +} + +async function implementation() { + const head = ` +// AUTO-GENERATED FILE. DO NOT EDIT. +// Generated by ${import.meta.path} at ${new Date().toISOString()} +// To regenerate this file, run: +// +// bun src/javascript/jsc/generate-jssink.js +// +#include "root.h" +#include "JSSink.h" + +#include "ActiveDOMObject.h" +#include "ExtendedDOMClientIsoSubspaces.h" +#include "ExtendedDOMIsoSubspaces.h" +#include "IDLTypes.h" +// #include "JSBlob.h" +#include "JSDOMAttribute.h" +#include "JSDOMBinding.h" +#include "JSDOMConstructor.h" +#include "JSDOMConvertBase.h" +#include "JSDOMConvertInterface.h" +#include "JSDOMConvertStrings.h" +#include "JSDOMExceptionHandling.h" +#include "JSDOMGlobalObject.h" +#include "JSDOMGlobalObjectInlines.h" +#include "JSDOMOperation.h" +#include "JSDOMWrapperCache.h" +#include "ScriptExecutionContext.h" +#include "WebCoreJSClientData.h" +#include "JavaScriptCore/FunctionPrototype.h" +#include "JavaScriptCore/HeapAnalyzer.h" + +#include "JavaScriptCore/JSDestructibleObjectHeapCellType.h" +#include "JavaScriptCore/SlotVisitorMacros.h" +#include "JavaScriptCore/SubspaceInlines.h" +#include "wtf/GetPtr.h" +#include "wtf/PointerPreparations.h" +#include "wtf/URL.h" +#include "JavaScriptCore/BuiltinNames.h" + +#include "JSBufferEncodingType.h" +#include "JSBufferPrototypeBuiltins.h" +#include "JSBufferConstructorBuiltins.h" +#include "JavaScriptCore/JSBase.h" +#if ENABLE(MEDIA_SOURCE) +#include "BufferMediaSource.h" +#include "JSMediaSource.h" +#endif + +// #include "JavaScriptCore/JSTypedArrayViewPrototype.h" +#include "JavaScriptCore/JSArrayBufferViewInlines.h" + +namespace WebCore { +using namespace JSC; + + +`; + var templ = head; + + for (let name of classes) { + templ += ` +JSC_DEFINE_CUSTOM_GETTER(function${name}__getter, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName)) +{ + auto& vm = lexicalGlobalObject->vm(); + Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject); + + return JSC::JSValue::encode(globalObject->${name}()); +} +`; + } + + templ += ` +${(await Bun.file(import.meta.dir + "/bindings/JSSink+custom.h").text()).trim()} +`; + + const footer = ` +} // namespace WebCore + +`; + + for (let name of classes) { + const constructorName = `JS${name}Constructor`; + const className = `JS${name}`; + const prototypeName = `JS${name}Prototype`; + + templ += ` +#pragma mark - ${name} + +class ${prototypeName} final : public JSC::JSNonFinalObject { +public: + using Base = JSC::JSNonFinalObject; + + static ${prototypeName}* create(JSC::VM& vm, JSGlobalObject* globalObject, JSC::Structure* structure) + { + ${prototypeName}* ptr = new (NotNull, JSC::allocateCell<${prototypeName}>(vm)) ${prototypeName}(vm, globalObject, structure); + ptr->finishCreation(vm, globalObject); + return ptr; + } + + DECLARE_INFO; + template<typename CellType, JSC::SubspaceAccess> + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + return &vm.plainObjectSpace(); + } + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); + } + +private: + ${prototypeName}(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure) + : Base(vm, structure) + { + } + + void finishCreation(JSC::VM&, JSC::JSGlobalObject*); +}; +STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(${prototypeName}, ${prototypeName}::Base); + + + +const ClassInfo ${prototypeName}::s_info = { "${name}"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(${prototypeName}) }; +const ClassInfo ${className}::s_info = { "${name}"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(${className}) }; +const ClassInfo ${constructorName}::s_info = { "${name}"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(${constructorName}) }; + +${className}::~${className}() +{ + if (m_sinkPtr) { + ${name}__finalize(m_sinkPtr); + } +} + + +`; + + templ += ` + +${constructorName}* ${constructorName}::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSObject* prototype) +{ + ${constructorName}* ptr = new (NotNull, JSC::allocateCell<${constructorName}>(vm)) ${constructorName}(vm, structure, ${name}__construct); + ptr->finishCreation(vm, globalObject, prototype); + return ptr; +} + +${className}* ${className}::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr) +{ + ${className}* ptr = new (NotNull, JSC::allocateCell<${className}>(vm)) ${className}(vm, structure, sinkPtr); + ptr->finishCreation(vm); + return ptr; +} + +void ${constructorName}::finishCreation(VM& vm, JSC::JSGlobalObject* globalObject, JSObject* prototype) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + initializeProperties(vm, globalObject, prototype); +} + +JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES ${constructorName}::construct(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) { + return ${name}__construct(globalObject, callFrame); +} + + +void ${constructorName}::initializeProperties(VM& vm, JSC::JSGlobalObject* globalObject, JSObject* prototype) +{ + putDirect(vm, vm.propertyNames->length, jsNumber(0), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); + JSString* nameString = jsNontrivialString(vm, "${name}"_s); + m_originalName.set(vm, this, nameString); + putDirect(vm, vm.propertyNames->name, nameString, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); +} + +void ${prototypeName}::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject) +{ + Base::finishCreation(vm); + reifyStaticProperties(vm, ${className}::info(), ${className}PrototypeTableValues, *this); + JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); +} + +void ${className}::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); +} + +void ${className}::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) +{ + auto* thisObject = jsCast<${className}*>(cell); + if (void* wrapped = thisObject->wrapped()) { + analyzer.setWrappedObjectForCell(cell, wrapped); + // if (thisObject->scriptExecutionContext()) + // analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + } + Base::analyzeHeap(cell, analyzer); +} + +void ${className}::destroy(JSCell* cell) +{ + static_cast<${className}*>(cell)->${className}::~${className}(); +} + + +`; + } + + templ += ` + JSObject* createJSSinkPrototype(JSC::VM& vm, JSC::JSGlobalObject* globalObject, SinkID sinkID) + { + switch (sinkID) { + `; + for (let name of classes) { + templ += ` + case ${name}: + return JS${name}Prototype::create(vm, globalObject, JS${name}Prototype::createStructure(vm, globalObject, globalObject->objectPrototype())); +`; + } + templ += ` +default: + RELEASE_ASSERT_NOT_REACHED(); + } +}`; + + templ += footer; + + for (let name of classes) { + templ += ` +extern "C" JSC__JSValue ${name}__createObject(JSC__JSGlobalObject* arg0, void* sinkPtr) +{ + auto& vm = arg0->vm(); + Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(arg0); + JSC::JSValue prototype = globalObject->${name}Prototype(); + JSC::Structure* structure = WebCore::JS${name}::createStructure(vm, globalObject, prototype); + return JSC::JSValue::encode(WebCore::JS${name}::create(vm, globalObject, structure, sinkPtr)); +} + +extern "C" void* ${name}__fromJS(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1) +{ + JSC::VM& vm = WebCore::getVM(arg0); + if (auto* sink = JSC::jsDynamicCast<WebCore::JS${name}*>(JSC::JSValue::decode(JSValue1))) + return sink->wrapped(); + + return nullptr; +} +`; + return templ; + } +} + +await Bun.write(import.meta.dir + "/bindings/JSSink.h", header()); +await Bun.write( + import.meta.dir + "/bindings/JSSink.cpp", + await implementation() +); diff --git a/src/javascript/jsc/node/types.zig b/src/javascript/jsc/node/types.zig index 3906da392..0f9c31683 100644 --- a/src/javascript/jsc/node/types.zig +++ b/src/javascript/jsc/node/types.zig @@ -85,6 +85,55 @@ pub fn Maybe(comptime ResultType: type) type { pub const todo: @This() = @This(){ .err = Syscall.Error.todo }; + pub fn toJS(this: @This(), globalThis: *JSC.JSGlobalObject) JSC.JSValue { + switch (this) { + .err => |e| { + return e.toJSC(globalThis); + }, + .result => |r| { + if (comptime ReturnType == void) { + return JSC.JSValue.jsUndefined(); + } + + if (comptime ReturnType == JSC.ArrayBuffer) { + return r.toJS(globalThis, null); + } + + if (comptime std.meta.trait.isNumber(ResultType) or std.meta.trait.isFloat(ResultType)) { + return JSC.JSValue.jsNumber(r); + } + + if (comptime std.meta.trait.isZigString(ResultType)) { + if (ResultType == []u8) { + return JSC.ArrayBuffer.fromBytes(r, .ArrayBuffer).toJS(globalThis, null); + } + return JSC.ZigString.init(std.mem.span(r)).withEncoding().toValueAuto(globalThis); + } + + if (comptime @typeInfo(ReturnType) == .Bool) { + return JSC.JSValue.jsBoolean(r); + } + + if (comptime std.meta.trait.isContainer(ReturnType)) { + return r.toJS(globalThis); + } + + @compileError("toJS Not implemented for type " ++ @typeName(ReturnType)); + }, + } + } + + pub fn toArrayBuffer(this: @This(), globalThis: *JSC.JSGlobalObject) JSC.JSValue { + switch (this) { + .err => |e| { + return e.toJSC(globalThis); + }, + .result => |r| { + return JSC.ArrayBuffer.fromBytes(r, .ArrayBuffer).toJS(globalThis, null); + }, + } + } + pub inline fn getErrno(this: @This()) os.E { return switch (this) { .result => os.E.SUCCESS, diff --git a/src/javascript/jsc/webcore.zig b/src/javascript/jsc/webcore.zig index 1ed585018..799b408f2 100644 --- a/src/javascript/jsc/webcore.zig +++ b/src/javascript/jsc/webcore.zig @@ -5,6 +5,14 @@ pub usingnamespace @import("./webcore/streams.zig"); const JSC = @import("../../jsc.zig"); const std = @import("std"); +pub const Lifetime = enum { + clone, + transfer, + share, + /// When reading from a fifo like STDIN/STDERR + temporary, +}; + pub const Crypto = struct { const UUID = @import("./uuid.zig"); diff --git a/src/javascript/jsc/webcore/response.zig b/src/javascript/jsc/webcore/response.zig index 8d585f93c..75c9f8d98 100644 --- a/src/javascript/jsc/webcore/response.zig +++ b/src/javascript/jsc/webcore/response.zig @@ -3122,14 +3122,7 @@ pub const Blob = struct { return this.store.?.sharedView()[this.offset..][0..this.size]; } - pub const Lifetime = enum { - clone, - transfer, - share, - /// When reading from a fifo like STDIN/STDERR - temporary, - }; - + pub const Lifetime = JSC.WebCore.Lifetime; pub fn setIsASCIIFlag(this: *Blob, is_all_ascii: bool) void { this.is_all_ascii = is_all_ascii; // if this Blob represents the entire binary data @@ -3811,7 +3804,8 @@ pub const Body = struct { pub const PendingValue = struct { promise: ?JSValue = null, - stream: ?JSC.WebCore.ReadableStream = null, + readable: ?JSC.WebCore.ReadableStream = null, + // writable: JSC.WebCore.Sink global: *JSGlobalObject, task: ?*anyopaque = null, @@ -3826,28 +3820,28 @@ pub const Body = struct { pub fn setPromise(value: *PendingValue, globalThis: *JSC.JSGlobalObject, action: Action) JSValue { value.action = action; - if (value.stream) |*stream| { - // switch (stream.ptr) { + if (value.readable) |*readable| { + // switch (readable.ptr) { // .JavaScript // } switch (action) { .getText, .getJSON, .getBlob, .getArrayBuffer => { - switch (stream.ptr) { + switch (readable.ptr) { .Blob => unreachable, else => {}, } value.promise = switch (action) { - .getJSON => globalThis.readableStreamToJSON(stream.value), - .getArrayBuffer => globalThis.readableStreamToArrayBuffer(stream.value), - .getText => globalThis.readableStreamToText(stream.value), - .getBlob => globalThis.readableStreamToBlob(stream.value), + .getJSON => globalThis.readableStreamToJSON(readable.value), + .getArrayBuffer => globalThis.readableStreamToArrayBuffer(readable.value), + .getText => globalThis.readableStreamToText(readable.value), + .getBlob => globalThis.readableStreamToBlob(readable.value), else => unreachable, }; value.promise.?.ensureStillAlive(); - stream.value.unprotect(); + readable.value.unprotect(); // js now owns the memory - value.stream = null; + value.readable = null; return value.promise.?; }, @@ -3894,15 +3888,15 @@ pub const Body = struct { pub const empty = Value{ .Empty = .{} }; - pub fn fromReadableStream(stream: JSC.WebCore.ReadableStream, globalThis: *JSGlobalObject) Value { - if (stream.isLocked(globalThis)) { + pub fn fromReadableStream(readable: JSC.WebCore.ReadableStream, globalThis: *JSGlobalObject) Value { + if (readable.isLocked(globalThis)) { return .{ .Error = ZigString.init("Cannot use a locked ReadableStream").toErrorInstance(globalThis) }; } - stream.value.protect(); + readable.value.protect(); return .{ .Locked = .{ - .stream = stream, + .readable = readable, .global = globalThis, }, }; @@ -3911,9 +3905,9 @@ pub const Body = struct { pub fn resolve(this: *Value, new: *Value, global: *JSGlobalObject) void { if (this.* == .Locked) { var locked = &this.Locked; - if (locked.stream) |stream| { - stream.done(); - locked.stream = null; + if (locked.readable) |readable| { + readable.done(); + locked.readable = null; } if (locked.callback) |callback| { @@ -3988,9 +3982,9 @@ pub const Body = struct { locked.promise = null; } - if (locked.stream) |stream| { - stream.done(); - locked.stream = null; + if (locked.readable) |readable| { + readable.done(); + locked.readable = null; } this.* = .{ .Error = error_instance }; @@ -4024,8 +4018,8 @@ pub const Body = struct { pub fn deinit(this: *Value) void { const tag = @as(Tag, this.*); if (tag == .Locked) { - if (this.Locked.stream) |*stream| { - stream.done(); + if (this.Locked.readable) |*readable| { + readable.done(); } this.Locked.deinit = true; @@ -4593,6 +4587,21 @@ fn BlobInterface(comptime Type: type) type { blob.allocator = getAllocator(ctx); return JSC.JSPromise.resolvedPromiseValue(ctx.ptr(), JSValue.fromRef(Blob.Class.make(ctx, ptr))).asObjectRef(); } + + // pub fn getBody( + // this: *Type, + // ctx: js.JSContextRef, + // _: js.JSObjectRef, + // _: js.JSObjectRef, + // _: []const js.JSValueRef, + // _: js.ExceptionRef, + // ) js.JSValueRef { + // var value: *Body.Value = this.getBodyValue(); + + // switch (value.*) { + // .Empty => {}, + // } + // } }; } diff --git a/src/javascript/jsc/webcore/streams.zig b/src/javascript/jsc/webcore/streams.zig index cb7791e0f..e78bd5e48 100644 --- a/src/javascript/jsc/webcore/streams.zig +++ b/src/javascript/jsc/webcore/streams.zig @@ -49,7 +49,7 @@ const Request = JSC.WebCore.Request; pub const ReadableStream = struct { value: JSValue, - ptr: Handle, + ptr: Source, pub fn done(this: *const ReadableStream) void { this.value.unprotect(); @@ -63,14 +63,22 @@ pub const ReadableStream = struct { File = 2, HTTPRequest = 3, HTTPSRequest = 4, + HTTPResponse = 5, + HTTPSResponse = 6, }; - pub const Handle = union(Tag) { + pub const Source = union(Tag) { Invalid: void, JavaScript: void, Blob: *ByteBlobLoader, File: *FileBlobLoader, + // HTTPRequest: *HTTPRequest, HTTPRequest: void, + // HTTPSRequest: *HTTPSRequest, HTTPSRequest: void, + // HTTPRequest: *HTTPRequest, + HTTPResponse: void, + // HTTPSRequest: *HTTPSRequest, + HTTPSResponse: void, }; extern fn ReadableStreamTag__tagged(globalObject: *JSGlobalObject, possibleReadableStream: JSValue, ptr: *JSValue) Tag; @@ -116,6 +124,19 @@ pub const ReadableStream = struct { .File = ptr.asPtr(FileBlobLoader), }, }, + + // .HTTPRequest => ReadableStream{ + // .value = value, + // .ptr = .{ + // .HTTPRequest = ptr.asPtr(HTTPRequest), + // }, + // }, + // .HTTPSRequest => ReadableStream{ + // .value = value, + // .ptr = .{ + // .HTTPSRequest = ptr.asPtr(HTTPSRequest), + // }, + // }, else => null, }; } @@ -181,81 +202,6 @@ pub const ReadableStream = struct { return @intCast(JSC.Node.FileDescriptor, out); } }; - - pub fn NewNativeReader( - comptime Context: type, - comptime onEnqueue: anytype, - comptime onEnqueueMany: anytype, - comptime onClose: anytype, - comptime onError: anytype, - comptime name_: []const u8, - ) type { - return struct { - pub const JSReadableStreamReaderNative = struct { - pub const shim = JSC.Shimmer(std.mem.span(name_), "JSReadableStreamReaderNative", @This()); - pub const tag = Context.tag; - pub const name = std.fmt.comptimePrint("{s}_JSReadableStreamReaderNative", .{std.mem.span(name_)}); - - pub fn enqueue(globalThis: *JSGlobalObject, callframe: *const JSC.CallFrame) callconv(.C) JSC.JSValue { - var this = callframe.argument(0).asPtr(*Context); - var buffer = callframe.argument(1).asArrayBuffer(globalThis) orelse { - globalThis.vm().throwError(globalThis, JSC.toInvalidArguments("Expected TypedArray or ArrayBuffer", .{}, globalThis)); - return JSC.JSValue.jsUndefined(); - }; - return onEnqueue(this, globalThis, buffer.slice(), callframe.argument(1)); - } - - pub fn enqueueMany(globalThis: *JSGlobalObject, callframe: *const JSC.CallFrame) callconv(.C) JSC.JSValue { - var this = callframe.argument(0).asPtr(*Context); - return onEnqueueMany(this, globalThis, callframe.argument(1)); - } - - pub fn close(globalThis: *JSGlobalObject, callframe: *const JSC.CallFrame) callconv(.C) JSC.JSValue { - var this = callframe.argument(0).asPtr(*Context); - return onClose(this, globalThis, callframe.argument(1)); - } - - pub fn @"error"(globalThis: *JSGlobalObject, callframe: *const JSC.CallFrame) callconv(.C) JSC.JSValue { - var this = callframe.argument(0).asPtr(*Context); - return onError(this, globalThis, callframe.argument(1)); - } - - pub fn load(globalThis: *JSGlobalObject) callconv(.C) JSC.JSValue { - if (comptime JSC.is_bindgen) unreachable; - if (comptime Environment.allow_assert) { - // this should be cached per globals object - const OnlyOnce = struct { - pub threadlocal var last_globals: ?*JSGlobalObject = null; - }; - if (OnlyOnce.last_globals) |last_globals| { - std.debug.assert(last_globals != globalThis); - } - OnlyOnce.last_globals = globalThis; - } - return JSC.JSArray.from(globalThis, &.{ - JSC.NewFunction(globalThis, null, 2, JSReadableStreamReaderNative.enqueue), - JSC.NewFunction(globalThis, null, 2, JSReadableStreamReaderNative.enqueueMany), - JSC.NewFunction(globalThis, null, 2, JSReadableStreamReaderNative.close), - JSC.NewFunction(globalThis, null, 2, JSReadableStreamReaderNative.@"error"), - }); - } - - pub const Export = shim.exportFunctions(.{ - .@"load" = load, - }); - - comptime { - if (!JSC.is_bindgen) { - @export(load, .{ .name = Export[0].symbol_name }); - _ = JSReadableStreamReaderNative.enqueue; - _ = JSReadableStreamReaderNative.enqueueMany; - _ = JSReadableStreamReaderNative.close; - _ = JSReadableStreamReaderNative.@"error"; - } - } - }; - }; - } }; pub const StreamStart = union(enum) { @@ -263,9 +209,36 @@ pub const StreamStart = union(enum) { err: JSC.Node.Syscall.Error, chunk_size: Blob.SizeType, ready: void, + + pub fn toJS(this: StreamStart, globalThis: *JSGlobalObject) JSC.JSValue { + switch (this) { + .empty, .ready => { + return JSC.JSValue.jsUndefined(); + }, + .chunk_size => |chunk| { + return JSC.JSValue.jsNumber(@intCast(Blob.SizeType, chunk)); + }, + .err => |err| { + globalThis.vm().throwError(globalThis, err.toJSC(globalThis)); + return JSC.JSValue.jsUndefined(); + }, + } + } + + pub fn fromJS(globalThis: *JSGlobalObject, value: JSValue) StreamStart { + if (value.isEmptyOrUndefinedOrNull() or !value.isObject()) { + return .{ .empty = {} }; + } + + if (value.get(globalThis, "chunkSize")) |chunkSize| { + return .{ .chunk_size = @intCast(Blob.SizeType, @truncate(i52, chunkSize.toInt64())) }; + } + + return .{ .empty = {} }; + } }; -pub const StreamResult = union(enum) { +pub const StreamResult = union(Tag) { owned: bun.ByteList, owned_and_done: bun.ByteList, temporary_and_done: bun.ByteList, @@ -276,6 +249,117 @@ pub const StreamResult = union(enum) { err: JSC.Node.Syscall.Error, done: void, + pub const Tag = enum { + owned, + owned_and_done, + temporary_and_done, + temporary, + into_array, + into_array_and_done, + pending, + err, + done, + }; + + pub fn slice(this: *const StreamResult) []const u8 { + return switch (this.*) { + .owned => |owned| owned.slice(), + .owned_and_done => |owned_and_done| owned_and_done.slice(), + .temporary_and_done => |temporary_and_done| temporary_and_done.slice(), + .temporary => |temporary| temporary.slice(), + else => "", + }; + } + + pub const Writable = union(StreamResult.Tag) { + owned: Blob.SizeType, + owned_and_done: Blob.SizeType, + temporary_and_done: Blob.SizeType, + temporary: Blob.SizeType, + into_array: Blob.SizeType, + into_array_and_done: Blob.SizeType, + pending: *Writable.Pending, + err: JSC.Node.Syscall.Error, + done: void, + + pub const Pending = struct { + frame: anyframe, + result: Writable, + consumed: Blob.SizeType = 0, + used: bool = false, + }; + + pub fn toPromised(globalThis: *JSGlobalObject, promise: *JSPromise, pending: *Writable.Pending) void { + var frame = bun.default_allocator.create(@Frame(Writable.toPromisedWrap)) catch unreachable; + frame.* = async Writable.toPromisedWrap(globalThis, promise, pending); + pending.frame = frame; + } + + pub fn isDone(this: *const Writable) bool { + return switch (this.*) { + .owned_and_done, .temporary_and_done, .into_array_and_done, .done, .err => true, + else => false, + }; + } + fn toPromisedWrap(globalThis: *JSGlobalObject, promise: *JSPromise, pending: *Writable.Pending) void { + suspend {} + + pending.used = true; + const result: Writable = pending.result; + + switch (result) { + .err => |err| { + promise.reject(globalThis, err.toJSC(globalThis)); + }, + .done => { + promise.resolve(globalThis, JSValue.jsBoolean(false)); + }, + else => { + promise.resolve(globalThis, result.toJS(globalThis)); + }, + } + } + + pub fn toJS(this: *const Writable, globalThis: *JSGlobalObject) JSValue { + switch (this.*) { + .pending => |pending| { + var promise = JSC.JSPromise.create(globalThis); + Writable.toPromised(globalThis, promise, pending); + return promise.asValue(globalThis); + }, + + .err => |err| { + return JSC.JSPromise.rejectedPromise(globalThis, JSValue.c(err.toJS(globalThis.ref()))).asValue(globalThis); + }, + + .owned => |len| { + return JSC.JSValue.jsNumber(len); + }, + .owned_and_done => |len| { + return JSC.JSValue.jsNumber(len); + }, + .temporary_and_done => |len| { + return JSC.JSValue.jsNumber(len); + }, + .temporary => |len| { + return JSC.JSValue.jsNumber(len); + }, + .into_array => |len| { + return JSC.JSValue.jsNumber(len); + }, + .into_array_and_done => |len| { + return JSC.JSValue.jsNumber(len); + }, + + // false == controller.close() + // undefined == noop, but we probably won't send it + .done => { + return JSC.JSValue.jsBoolean(true); + }, + } + } + }; + pub const IntoArray = struct { value: JSValue = JSValue.zero, len: Blob.SizeType = std.math.maxInt(Blob.SizeType), @@ -329,14 +413,14 @@ pub const StreamResult = union(enum) { }, .temporary => |temp| { var array = JSC.JSValue.createUninitializedUint8Array(globalThis, temp.len); - var slice = array.asArrayBuffer(globalThis).?.slice(); - @memcpy(slice.ptr, temp.ptr, temp.len); + var slice_ = array.asArrayBuffer(globalThis).?.slice(); + @memcpy(slice_.ptr, temp.ptr, temp.len); return array; }, .temporary_and_done => |temp| { var array = JSC.JSValue.createUninitializedUint8Array(globalThis, temp.len); - var slice = array.asArrayBuffer(globalThis).?.slice(); - @memcpy(slice.ptr, temp.ptr, temp.len); + var slice_ = array.asArrayBuffer(globalThis).?.slice(); + @memcpy(slice_.ptr, temp.ptr, temp.len); return array; }, .into_array => |array| { @@ -364,6 +448,680 @@ pub const StreamResult = union(enum) { } }; +pub const Signal = struct { + ptr: *anyopaque = @intToPtr(*anyopaque, 0xaaaaaaaa), + vtable: VTable = VTable.Dead, + + pub fn isDead(this: Signal) bool { + return this.ptr == @intToPtr(*anyopaque, 0xaaaaaaaa); + } + + pub fn initWithType(comptime Type: type, handler: *Type) Sink { + return .{ + .ptr = handler, + .vtable = VTable.wrap(Type), + }; + } + + pub fn init(handler: anytype) Signal { + return initWithType(std.meta.Child(@TypeOf(handler)), handler); + } + + pub fn close(this: Signal, err: ?JSC.Node.Syscall.Error) void { + if (this.isDead()) + return; + this.vtable.close(this.ptr, err); + } + pub fn ready(this: Signal, amount: ?Blob.SizeType, offset: ?Blob.SizeType) void { + if (this.isDead()) + return; + this.vtable.ready(this.ptr, amount, offset); + } + pub fn start(this: Signal) void { + if (this.isDead()) + return; + this.vtable.start(this.ptr); + } + + pub const VTable = struct { + pub const OnCloseFn = fn (this: *anyopaque, err: ?JSC.Node.Syscall.Error) void; + pub const OnReadyFn = fn (this: *anyopaque, amount: ?Blob.SizeType, offset: ?Blob.SizeType) void; + pub const OnStartFn = fn (this: *anyopaque) void; + close: OnCloseFn, + ready: OnReadyFn, + start: OnStartFn, + + const DeadFns = struct { + pub fn close(_: *anyopaque, _: ?JSC.Node.Syscall.Error) void { + unreachable; + } + pub fn ready(_: *anyopaque, _: ?Blob.SizeType, _: ?Blob.SizeType) void { + unreachable; + } + + pub fn start(_: *anyopaque) void { + unreachable; + } + }; + + pub const Dead = VTable{ .close = DeadFns.close, .ready = DeadFns.ready, .start = DeadFns.start }; + + pub fn wrap( + comptime Wrapped: type, + ) VTable { + const Functions = struct { + fn onClose(this: *anyopaque, err: ?JSC.Node.Syscall.Error) void { + Wrapped.close(@ptrCast(*Wrapped, @alignCast(std.meta.alignment(Wrapped), this)), err); + } + fn onReady(this: *anyopaque, amount: ?Blob.SizeType, offset: ?Blob.SizeType) void { + Wrapped.ready(@ptrCast(*Wrapped, @alignCast(std.meta.alignment(Wrapped), this)), amount, offset); + } + fn onStart(this: *anyopaque) void { + Wrapped.start(@ptrCast(*Wrapped, @alignCast(std.meta.alignment(Wrapped), this))); + } + }; + + return VTable{ + .close = Functions.onClose, + .ready = Functions.onReady, + .start = Functions.onStart, + }; + } + }; +}; + +pub const Sink = struct { + ptr: *anyopaque, + vtable: VTable, + status: Status = Status.closed, + used: bool = false, + + pub const Status = enum { + ready, + closed, + }; + + pub const Data = union(enum) { + utf16: StreamResult, + latin1: StreamResult, + bytes: StreamResult, + }; + + pub fn initWithType(comptime Type: type, handler: *Type) Sink { + return .{ + .ptr = handler, + .vtable = VTable.wrap(Type), + .status = .ready, + .used = false, + }; + } + + pub fn init(handler: anytype) Sink { + return initWithType(std.meta.Child(@TypeOf(handler)), handler); + } + + pub const UTF8Fallback = struct { + const stack_size = 1024; + pub fn writeLatin1(comptime Ctx: type, ctx: *Ctx, input: StreamResult, comptime writeFn: anytype) StreamResult.Writable { + var str = input.slice(); + if (strings.isAllASCII(str)) { + return writeFn( + ctx, + input, + ); + } + + if (stack_size >= str.len) { + var buf: [stack_size]u8 = undefined; + @memcpy(&buf, str.ptr, str.len); + strings.replaceLatin1WithUTF8(buf[0..str.len]); + if (input.isDone()) { + const result = writeFn(ctx, .{ .temporary_and_done = bun.ByteList.init(buf[0..str.len]) }); + return result; + } else { + const result = writeFn(ctx, .{ .temporary = bun.ByteList.init(buf[0..str.len]) }); + return result; + } + } + + { + var slice = bun.default_allocator.alloc(u8, str.len) catch return .{ .err = JSC.Node.Syscall.Error.oom }; + @memcpy(slice.ptr, str.ptr, str.len); + strings.replaceLatin1WithUTF8(slice[0..str.len]); + if (input.isDone()) { + return writeFn(ctx, .{ .owned_and_done = bun.ByteList.init(slice) }); + } else { + return writeFn(ctx, .{ .owned = bun.ByteList.init(slice) }); + } + } + } + + pub fn writeUTF16(comptime Ctx: type, ctx: *Ctx, input: StreamResult, comptime writeFn: anytype) StreamResult.Writable { + var str: []const u16 = std.mem.bytesAsSlice(u16, input.slice()); + + if (stack_size >= str.len * 2) { + var buf: [stack_size]u8 = undefined; + const copied = strings.copyUTF16IntoUTF8(&buf, []const u16, str); + std.debug.assert(copied.written <= stack_size); + std.debug.assert(copied.read <= stack_size); + if (input.isDone()) { + const result = writeFn(ctx, .{ .temporary_and_done = bun.ByteList.init(buf[0..copied.written]) }); + return result; + } else { + const result = writeFn(ctx, .{ .temporary = bun.ByteList.init(buf[0..copied.written]) }); + return result; + } + } + + { + var allocated = strings.toUTF8Alloc(bun.default_allocator, str) catch return .{ .err = JSC.Node.Syscall.Error.oom }; + if (input.isDone()) { + return writeFn(ctx, .{ .owned_and_done = bun.ByteList.init(allocated) }); + } else { + return writeFn(ctx, .{ .owned = bun.ByteList.init(allocated) }); + } + } + } + }; + + pub const VTable = struct { + pub const WriteUTF16Fn = fn (this: *anyopaque, data: StreamResult) StreamResult.Writable; + pub const WriteUTF8Fn = fn (this: *anyopaque, data: StreamResult) StreamResult.Writable; + pub const WriteLatin1Fn = fn (this: *anyopaque, data: StreamResult) StreamResult.Writable; + pub const EndFn = fn (this: *anyopaque, err: ?JSC.Node.Syscall.Error) JSC.Node.Maybe(void); + pub const ConnectFn = fn (this: *anyopaque, signal: Signal) JSC.Node.Maybe(void); + + connect: ConnectFn, + write: WriteUTF8Fn, + writeLatin1: WriteLatin1Fn, + writeUTF16: WriteUTF16Fn, + end: EndFn, + + pub fn wrap( + comptime Wrapped: type, + ) VTable { + const Functions = struct { + pub fn onWrite(this: *anyopaque, data: StreamResult) StreamResult.Writable { + return Wrapped.write(@ptrCast(*Wrapped, @alignCast(std.meta.alignment(Wrapped), this)), data); + } + pub fn onConnect(this: *anyopaque, signal: Signal) JSC.Node.Maybe(void) { + return Wrapped.connect(@ptrCast(*Wrapped, @alignCast(std.meta.alignment(Wrapped), this)), signal); + } + pub fn onWriteLatin1(this: *anyopaque, data: StreamResult) StreamResult.Writable { + return Wrapped.writeLatin1(@ptrCast(*Wrapped, @alignCast(std.meta.alignment(Wrapped), this)), data); + } + pub fn onWriteUTF16(this: *anyopaque, data: StreamResult) StreamResult.Writable { + return Wrapped.writeUTF16(@ptrCast(*Wrapped, @alignCast(std.meta.alignment(Wrapped), this)), data); + } + pub fn onEnd(this: *anyopaque, err: ?JSC.Node.Syscall.Error) JSC.Node.Maybe(void) { + return Wrapped.end(@ptrCast(*Wrapped, @alignCast(std.meta.alignment(Wrapped), this)), err); + } + }; + + return VTable{ + .write = Functions.onWrite, + .writeLatin1 = Functions.onWriteLatin1, + .writeUTF16 = Functions.onWriteUTF16, + .end = Functions.onEnd, + .connect = Functions.onConnect, + }; + } + }; + + pub fn end(this: *Sink, err: ?JSC.Node.Syscall.Error) JSC.Node.Maybe(void) { + if (this.status == .closed) { + return .{ .result = {} }; + } + + this.status = .closed; + return this.vtable.end(this.ptr, err); + } + + pub fn writeLatin1(this: *Sink, data: StreamResult) StreamResult.Writable { + if (this.status == .closed) { + return .{ .done = {} }; + } + + const res = this.vtable.writeLatin1(this.ptr, data); + this.status = if ((res.isDone()) or this.status == .closed) + Status.closed + else + Status.ready; + this.used = true; + return res; + } + + pub fn writeBytes(this: *Sink, data: StreamResult) StreamResult.Writable { + if (this.status == .closed) { + return .{ .done = {} }; + } + + const res = this.vtable.write(this.ptr, data); + this.status = if ((res.isDone()) or this.status == .closed) + Status.closed + else + Status.ready; + this.used = true; + return res; + } + + pub fn writeUTF16(this: *Sink, data: StreamResult) StreamResult.Writable { + if (this.status == .closed) { + return .{ .done = {} }; + } + + const res = this.vtable.writeUTF16(this.ptr, data); + this.status = if ((res.isDone()) or this.status == .closed) + Status.closed + else + Status.ready; + this.used = true; + return res; + } + + pub fn write(this: *Sink, data: Data) StreamResult.Writable { + switch (data) { + .utf16 => |str| { + return this.writeUTF16(str); + }, + .latin1 => |str| { + return this.writeLatin1(str); + }, + .bytes => |bytes| { + return this.writeBytes(bytes); + }, + } + } +}; + +pub const ArrayBufferSink = struct { + bytes: bun.ByteList, + allocator: std.mem.Allocator, + done: bool = false, + signal: Signal = .{}, + next: ?Sink = null, + + pub fn connect(this: *ArrayBufferSink, signal: Signal) void { + std.debug.assert(this.reader == null); + this.signal = signal; + } + + pub fn start(this: *ArrayBufferSink, config: StreamStart) JSC.Node.Maybe(void) { + var list = this.bytes.listManaged(this.allocator); + list.clearAndFree(); + + switch (config) { + .chunk_size => |chunk_size| { + if (chunk_size > 0) { + list.ensureTotalCapacityPrecise(chunk_size) catch return .{ .err = JSC.Node.Syscall.Error.oom }; + this.bytes.update(list); + } + }, + else => {}, + } + + this.done = false; + + this.signal.start(); + return .{ .result = {} }; + } + + pub fn drain(_: *ArrayBufferSink) JSC.Node.Maybe(void) { + return .{ .result = {} }; + } + + pub fn finalize(this: *ArrayBufferSink) void { + if (this.bytes.len > 0) { + this.bytes.listManaged(this.allocator).deinit(); + this.bytes = bun.ByteList.init(""); + this.done = true; + } + } + + pub fn init(allocator: std.mem.Allocator, next: ?Sink) !*ArrayBufferSink { + var this = try allocator.create(ArrayBufferSink); + this.* = ArrayBufferSink{ + .bytes = bun.ByteList.init(&.{}), + .allocator = allocator, + .next = next, + }; + return this; + } + + pub fn construct(this: *ArrayBufferSink, allocator: std.mem.Allocator) void { + this.* = ArrayBufferSink{ + .bytes = bun.ByteList.init(&.{}), + .allocator = allocator, + .next = null, + }; + } + + pub fn write(this: *@This(), data: StreamResult) StreamResult.Writable { + if (this.next) |*next| { + return next.writeBytes(data); + } + + const len = this.bytes.write(this.allocator, data.slice()) catch { + return .{ .err = JSC.Node.Syscall.Error.oom }; + }; + this.signal.ready(null, null); + return .{ .owned = len }; + } + pub const writeBytes = write; + pub fn writeLatin1(this: *@This(), data: StreamResult) StreamResult.Writable { + if (this.next) |*next| { + return next.writeLatin1(data); + } + const len = this.bytes.writeLatin1(this.allocator, data.slice()) catch { + return .{ .err = JSC.Node.Syscall.Error.oom }; + }; + this.signal.ready(null, null); + return .{ .owned = len }; + } + pub fn writeUTF16(this: *@This(), data: StreamResult) StreamResult.Writable { + if (this.next) |*next| { + return next.writeUTF16(data); + } + const len = this.bytes.writeUTF16(this.allocator, @ptrCast([*]const u16, @alignCast(@alignOf(u16), data.slice().ptr))[0..std.mem.bytesAsSlice(u16, data.slice()).len]) catch { + return .{ .err = JSC.Node.Syscall.Error.oom }; + }; + this.signal.ready(null, null); + return .{ .owned = len }; + } + + pub fn end(this: *ArrayBufferSink, err: ?JSC.Node.Syscall.Error) JSC.Node.Maybe(void) { + if (this.next) |*next| { + return next.end(err); + } + return .{ .result = {} }; + } + + pub fn toJS(this: *ArrayBufferSink, err: ?JSC.Node.Syscall.Error) JSC.Node.Maybe(void) { + if (this.next) |*next| { + return next.end(err); + } + return .{ .result = {} }; + } + + pub fn endFromJS(this: *ArrayBufferSink, _: *JSGlobalObject) JSC.Node.Maybe(ArrayBuffer) { + if (this.done) { + return .{ .result = ArrayBuffer.fromBytes(&[_]u8{}, .ArrayBuffer) }; + } + + std.debug.assert(this.next == null); + var list = this.bytes.listManaged(this.allocator); + this.bytes = bun.ByteList.init(""); + this.done = true; + this.signal.close(null); + return .{ .result = ArrayBuffer.fromBytes(list.toOwnedSlice(), .ArrayBuffer) }; + } + + pub fn sink(this: *ArrayBufferSink) Sink { + return Sink.init(this); + } + + pub const JSArrayBufferSink = NewJSSink(@This(), "ArrayBufferSink"); +}; + +pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { + return struct { + sink: SinkType, + + const ThisSink = @This(); + + pub const shim = JSC.Shimmer("", std.mem.span(name_), @This()); + pub const name = std.fmt.comptimePrint("{s}", .{std.mem.span(name_)}); + + pub fn createObject(globalThis: *JSGlobalObject, object: *anyopaque) callconv(.C) JSValue { + JSC.markBinding(); + + return shim.cppFn("createObject", .{ globalThis, object }); + } + + pub fn fromJS(globalThis: *JSGlobalObject, value: JSValue) ?*anyopaque { + JSC.markBinding(); + + return shim.cppFn("fromJS", .{ globalThis, value }); + } + + pub fn construct(globalThis: *JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSValue { + JSC.markBinding(); + var allocator = globalThis.bunVM().allocator; + var this = allocator.create(ThisSink) catch { + globalThis.vm().throwError(globalThis, JSC.Node.Syscall.Error.oom.toJSC( + globalThis, + )); + return JSC.JSValue.jsUndefined(); + }; + this.sink.construct(allocator); + return createObject(globalThis, this); + } + + pub fn finalize(ptr: *anyopaque) callconv(.C) void { + var this = @ptrCast(*ThisSink, @alignCast(std.meta.alignment(ThisSink), ptr)); + + this.sink.finalize(); + } + + pub fn write(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + JSC.markBinding(); + var this = @ptrCast(*ThisSink, @alignCast(std.meta.alignment(ThisSink), fromJS(globalThis, callframe.this()) orelse { + const err = JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_THIS, "Expected Sink", .{}, globalThis); + globalThis.vm().throwError(globalThis, err); + return JSC.JSValue.jsUndefined(); + })); + + if (comptime @hasDecl(SinkType, "getPendingError")) { + if (this.sink.getPendingError()) |err| { + globalThis.vm().throwError(globalThis, err); + return JSC.JSValue.jsUndefined(); + } + } + + const args = callframe.arguments(); + if (args.len == 0 or args[0].isEmptyOrUndefinedOrNull() or args[0].isNumber()) { + const err = JSC.toTypeError( + if (args.len == 0) JSC.Node.ErrorCode.ERR_MISSING_ARGS else JSC.Node.ErrorCode.ERR_INVALID_ARG_TYPE, + "write() expects a string, ArrayBufferView, or ArrayBuffer", + .{}, + globalThis, + ); + globalThis.vm().throwError(globalThis, err); + return JSC.JSValue.jsUndefined(); + } + + const arg = args[0]; + if (arg.asArrayBuffer(globalThis)) |buffer| { + const slice = buffer.slice(); + if (slice.len == 0) { + return JSC.JSValue.jsNumber(0); + } + + return this.sink.writeBytes(.{ .temporary = bun.ByteList.init(slice) }).toJS(globalThis); + } + + const str = arg.getZigString(globalThis); + if (str.len == 0) { + return JSC.JSValue.jsNumber(0); + } + + if (str.is16Bit()) { + return this.sink.writeUTF16(.{ .temporary = bun.ByteList.init(std.mem.sliceAsBytes(str.utf16SliceAligned())) }).toJS(globalThis); + } + + return this.sink.writeLatin1(.{ .temporary = bun.ByteList.init(str.slice()) }).toJS(globalThis); + } + + pub fn writeString(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + JSC.markBinding(); + + var this = @ptrCast(*ThisSink, @alignCast(std.meta.alignment(ThisSink), fromJS(globalThis, callframe.this()) orelse { + const err = JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_THIS, "Expected Sink", .{}, globalThis); + globalThis.vm().throwError(globalThis, err); + return JSC.JSValue.jsUndefined(); + })); + + if (comptime @hasDecl(SinkType, "getPendingError")) { + if (this.sink.getPendingError()) |err| { + globalThis.vm().throwError(globalThis, err); + return JSC.JSValue.jsUndefined(); + } + } + + const args = callframe.arguments(); + if (args.len == 0 or args[0].isEmptyOrUndefinedOrNull() or args[0].isNumber()) { + const err = JSC.toTypeError( + if (args.len == 0) JSC.Node.ErrorCode.ERR_MISSING_ARGS else JSC.Node.ErrorCode.ERR_INVALID_ARG_TYPE, + "write() expects a string, ArrayBufferView, or ArrayBuffer", + .{}, + globalThis, + ); + globalThis.vm().throwError(globalThis, err); + return JSC.JSValue.jsUndefined(); + } + + const arg = args[0]; + + const str = arg.getZigString(globalThis); + if (str.len == 0) { + return JSC.JSValue.jsNumber(0); + } + + if (str.is16Bit()) { + return this.sink.writeUTF16(.{ .temporary = str.utf16SliceAligned() }).toJS(globalThis); + } + + return this.sink.writeLatin1(.{ .temporary = str.slice() }).toJS(globalThis); + } + + pub fn close(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + JSC.markBinding(); + var this = @ptrCast(*ThisSink, @alignCast(std.meta.alignment(ThisSink), fromJS(globalThis, callframe.this()) orelse { + const err = JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_THIS, "Expected Sink", .{}, globalThis); + globalThis.vm().throwError(globalThis, err); + return JSC.JSValue.jsUndefined(); + })); + + if (comptime @hasDecl(SinkType, "getPendingError")) { + if (this.sink.getPendingError()) |err| { + globalThis.vm().throwError(globalThis, err); + return JSC.JSValue.jsUndefined(); + } + } + + return this.sink.end(null).toJS(globalThis); + } + pub fn closeWithError(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + JSC.markBinding(); + var this = @ptrCast(*ThisSink, @alignCast(std.meta.alignment(ThisSink), fromJS(globalThis, callframe.this()) orelse { + const err = JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_THIS, "Expected Sink", .{}, globalThis); + globalThis.vm().throwError(globalThis, err); + return JSC.JSValue.jsUndefined(); + })); + + if (comptime @hasDecl(SinkType, "getPendingError")) { + if (this.sink.getPendingError()) |err| { + globalThis.vm().throwError(globalThis, err); + return JSC.JSValue.jsUndefined(); + } + } + + if (callframe.argumentsCount() == 0) { + const err = JSC.toTypeError(JSC.Node.ErrorCode.ERR_MISSING_ARGS, "closeWithError() expects an error", .{}, globalThis); + globalThis.vm().throwError(globalThis, err); + return JSC.JSValue.jsUndefined(); + } + + return this.sink.end(null).toJS(globalThis); + } + + pub fn drain(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + JSC.markBinding(); + + var this = @ptrCast(*ThisSink, @alignCast(std.meta.alignment(ThisSink), fromJS(globalThis, callframe.this()) orelse { + const err = JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_THIS, "Expected Sink", .{}, globalThis); + globalThis.vm().throwError(globalThis, err); + return JSC.JSValue.jsUndefined(); + })); + + if (comptime @hasDecl(SinkType, "getPendingError")) { + if (this.sink.getPendingError()) |err| { + globalThis.vm().throwError(globalThis, err); + return JSC.JSValue.jsUndefined(); + } + } + + return this.sink.drain().toJS(globalThis); + } + + pub fn start(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + JSC.markBinding(); + + var this = @ptrCast(*ThisSink, @alignCast(std.meta.alignment(ThisSink), fromJS(globalThis, callframe.this()) orelse { + const err = JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_THIS, "Expected Sink", .{}, globalThis); + globalThis.vm().throwError(globalThis, err); + return JSC.JSValue.jsUndefined(); + })); + + if (comptime @hasDecl(SinkType, "getPendingError")) { + if (this.sink.getPendingError()) |err| { + globalThis.vm().throwError(globalThis, err); + return JSC.JSValue.jsUndefined(); + } + } + + return this.sink.start( + if (callframe.argumentsCount() > 0) StreamStart.fromJS(globalThis, callframe.argument(0)) else StreamStart{ .empty = {} }, + ).toJS(globalThis); + } + + pub fn end(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + JSC.markBinding(); + + var this = @ptrCast(*ThisSink, @alignCast(std.meta.alignment(ThisSink), fromJS(globalThis, callframe.this()) orelse { + const err = JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_THIS, "Expected Sink", .{}, globalThis); + globalThis.vm().throwError(globalThis, err); + return JSC.JSValue.jsUndefined(); + })); + + if (comptime @hasDecl(SinkType, "getPendingError")) { + if (this.sink.getPendingError()) |err| { + globalThis.vm().throwError(globalThis, err); + return JSC.JSValue.jsUndefined(); + } + } + + return this.sink.endFromJS(globalThis).toJS(globalThis); + } + + pub const Export = shim.exportFunctions(.{ + .@"finalize" = finalize, + .@"write" = write, + .@"close" = close, + .@"closeWithError" = closeWithError, + .@"drain" = drain, + .@"start" = start, + .@"end" = end, + .@"construct" = construct, + }); + + comptime { + if (!JSC.is_bindgen) { + @export(finalize, .{ .name = Export[0].symbol_name }); + @export(write, .{ .name = Export[1].symbol_name }); + @export(close, .{ .name = Export[2].symbol_name }); + @export(closeWithError, .{ .name = Export[3].symbol_name }); + @export(drain, .{ .name = Export[4].symbol_name }); + @export(start, .{ .name = Export[5].symbol_name }); + @export(end, .{ .name = Export[6].symbol_name }); + @export(construct, .{ .name = Export[7].symbol_name }); + } + } + + pub const Extern = [_][]const u8{ "createObject", "fromJS" }; + }; +} + pub fn WritableStreamSink( comptime Context: type, comptime onStart: ?fn (this: Context) void, @@ -824,23 +1582,6 @@ pub fn RequestBodyStreamer( else if (is_ssl) ReadableStream.Tag.HTTPSRequest; - pub fn setup( - this: *ByteBlobLoader, - blob: *const Blob, - user_chunk_size: Blob.SizeType, - ) void { - blob.store.?.ref(); - var blobe = blob.*; - blobe.resolveSize(); - this.* = ByteBlobLoader{ - .offset = blobe.offset, - .store = blobe.store.?, - .chunk_size = if (user_chunk_size > 0) @minimum(user_chunk_size, blobe.size) else @minimum(1024 * 1024 * 2, blobe.size), - .remain = blobe.size, - .done = false, - }; - } - pub fn onStart(this: *ByteBlobLoader) StreamStart { return .{ .chunk_size = this.chunk_size }; } @@ -910,6 +1651,7 @@ pub const FileBlobLoader = struct { user_chunk_size: Blob.SizeType = 0, scheduled_count: u32 = 0, concurrent: Concurrent = Concurrent{}, + input_tag: StreamResult.Tag = StreamResult.Tag.done, const FileReader = @This(); @@ -1195,8 +1937,9 @@ pub const FileBlobLoader = struct { @minimum(available_to_read, chunk_size); } - pub fn onPull(this: *FileBlobLoader, buffer: []u8, view: JSC.JSValue) StreamResult { + pub fn onPullInto(this: *FileBlobLoader, buffer: []u8, view: JSC.JSValue) StreamResult { const chunk_size = this.calculateChunkSize(std.math.maxInt(usize)); + this.input_tag = .into_array; switch (chunk_size) { 0 => { @@ -1362,16 +2105,17 @@ pub const FileBlobLoader = struct { bun.default_allocator.destroy(this); } - pub const Source = ReadableStreamSource(@This(), "FileBlobLoader", onStart, onPull, onCancel, deinit); + pub const Source = ReadableStreamSource(@This(), "FileBlobLoader", onStart, onPullInto, onCancel, deinit); }; -pub const StreamSource = struct { - ptr: ?*anyopaque = null, - vtable: VTable, - - pub const VTable = struct { - onStart: fn (this: StreamSource) JSC.WebCore.StreamStart, - onPull: fn (this: StreamSource) JSC.WebCore.StreamResult, - onError: fn (this: StreamSource) void, - }; -}; +// pub const HTTPRequest = RequestBodyStreamer(false); +// pub const HTTPSRequest = RequestBodyStreamer(true); +// pub fn ResponseBodyStreamer(comptime is_ssl: bool) type { +// return struct { +// const Streamer = @This(); +// pub fn onEnqueue(this: *Streamer, buffer: []u8, ): anytype, +// pub fn onEnqueueMany(this: *Streamer): anytype, +// pub fn onClose(this: *Streamer): anytype, +// pub fn onError(this: *Streamer): anytype, +// }; +// } diff --git a/src/main.zig b/src/main.zig index f70642819..4fd2b8234 100644 --- a/src/main.zig +++ b/src/main.zig @@ -37,7 +37,8 @@ pub fn PLCrashReportHandler() void { pub var start_time: i128 = 0; pub fn main() void { - CrashReporter.start(null, Report.CrashReportWriter.printFrame, Report.handleCrash); + if (comptime Environment.isRelease) + CrashReporter.start(null, Report.CrashReportWriter.printFrame, Report.handleCrash); start_time = std.time.nanoTimestamp(); |