Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmake/sources/CxxSources.txt
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ src/bun.js/bindings/webcore/JSTextEncoderStream.cpp
src/bun.js/bindings/webcore/JSTransformStream.cpp
src/bun.js/bindings/webcore/JSTransformStreamDefaultController.cpp
src/bun.js/bindings/webcore/JSURLSearchParams.cpp
src/bun.js/bindings/webcore/JSWasmStreamingCompiler.cpp
src/bun.js/bindings/webcore/JSWebSocket.cpp
src/bun.js/bindings/webcore/JSWorker.cpp
src/bun.js/bindings/webcore/JSWorkerOptions.cpp
Expand Down
1 change: 1 addition & 0 deletions cmake/sources/JavaScriptSources.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ src/js/builtins/TransformStream.ts
src/js/builtins/TransformStreamDefaultController.ts
src/js/builtins/TransformStreamInternals.ts
src/js/builtins/UtilInspect.ts
src/js/builtins/WasmStreaming.ts
src/js/builtins/WritableStreamDefaultController.ts
src/js/builtins/WritableStreamDefaultWriter.ts
src/js/builtins/WritableStreamInternals.ts
Expand Down
1 change: 1 addition & 0 deletions src/bun.js/bindings/ErrorCode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ const errors: ErrorCodeMapping = [
["ERR_ZSTD_INVALID_PARAM", RangeError],
["ERR_USE_AFTER_CLOSE", Error],
["ERR_WASI_NOT_STARTED", Error],
["ERR_WEBASSEMBLY_RESPONSE", TypeError],
["ERR_WORKER_INIT_FAILED", Error],
["ERR_WORKER_NOT_RUNNING", Error],
["ERR_ZLIB_INITIALIZATION_FAILED", Error],
Expand Down
68 changes: 68 additions & 0 deletions src/bun.js/bindings/JSGlobalObject.zig
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,73 @@ pub const JSGlobalObject = opaque {
@panic("A C++ exception occurred");
}

extern fn JSC__Wasm__StreamingCompiler__addBytes(streaming_compiler: *anyopaque, bytes_ptr: [*]const u8, bytes_len: usize) void;

fn getBodyStreamOrBytesForWasmStreaming(
this: *jsc.JSGlobalObject,
response_value: jsc.JSValue,
streaming_compiler: *anyopaque,
) bun.JSError!jsc.JSValue {
const response = jsc.WebCore.Response.fromJS(response_value) orelse return this.throwInvalidArgumentTypeValue2(
"source",
"an instance of Response or an Promise resolving to Response",
response_value,
);

const content_type = if (try response.getContentType()) |content_type|
content_type.toZigString()
else
ZigString.static("null").*;

if (!content_type.eqlComptime("application/wasm")) {
return this.ERR(.WEBASSEMBLY_RESPONSE, "WebAssembly response has unsupported MIME type '{}'", .{content_type}).throw();
}

if (!response.isOK()) {
return this.ERR(.WEBASSEMBLY_RESPONSE, "WebAssembly response has status code {}", .{response.statusCode()}).throw();
}

if (response.getBodyUsed(this).toBoolean()) {
return this.ERR(.WEBASSEMBLY_RESPONSE, "WebAssembly response body has already been used", .{}).throw();
}

const body = response.getBodyValue();
if (body.* == .Error) {
return this.throwValue(body.Error.toJS(this));
}

// We're done validating. From now on, deal with extracting the body.
body.toBlobIfPossible();

var any_blob = switch (body.*) {
.Locked => body.tryUseAsAnyBlob() orelse return body.toReadableStream(this),
else => body.useAsAnyBlob(),
};

if (any_blob.store()) |store| {
if (store.data != .bytes) {
// This is a file or an S3 object, which aren't accessible synchronously.
// (using any_blob.slice() would return a bogus empty slice)

// Logic from JSC.WebCore.Body.Value.toReadableStream
var blob = any_blob.Blob;
defer blob.detach();

blob.resolveSize();
return jsc.WebCore.ReadableStream.fromBlobCopyRef(this, &blob, blob.size);
}
}

defer any_blob.detach();

// Push the blob contents into the streaming compiler by passing a pointer and
// length, and return null to signify this has been done.
const slice = any_blob.slice();
JSC__Wasm__StreamingCompiler__addBytes(streaming_compiler, slice.ptr, slice.len);

return .null;
}

pub fn createError(
globalThis: *jsc.JSGlobalObject,
comptime fmt: string,
Expand Down Expand Up @@ -875,6 +942,7 @@ pub const JSGlobalObject = opaque {
@export(&resolve, .{ .name = "Zig__GlobalObject__resolve" });
@export(&reportUncaughtException, .{ .name = "Zig__GlobalObject__reportUncaughtException" });
@export(&onCrash, .{ .name = "Zig__GlobalObject__onCrash" });
@export(&jsc.host_fn.wrap3(getBodyStreamOrBytesForWasmStreaming), .{ .name = "Zig__GlobalObject__getBodyStreamOrBytesForWasmStreaming" });
}
};

Expand Down
69 changes: 65 additions & 4 deletions src/bun.js/bindings/ZigGlobalObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@
#include "JSTransformStream.h"
#include "JSTransformStreamDefaultController.h"
#include "JSURLSearchParams.h"
#include "JSWasmStreamingCompiler.h"
#include "JSWebSocket.h"
#include "JSWorker.h"
#include "JSWritableStream.h"
Expand Down Expand Up @@ -1242,8 +1243,8 @@ const JSC::GlobalObjectMethodTable& GlobalObject::globalObjectMethodTable()
&scriptExecutionStatus,
nullptr, // reportViolationForUnsafeEval
nullptr, // defaultLanguage
nullptr, // compileStreaming
nullptr, // instantiateStreaming
&compileStreaming,
&instantiateStreaming,
&Zig::deriveShadowRealmGlobalObject,
&codeForEval, // codeForEval
&canCompileStrings, // canCompileStrings
Expand Down Expand Up @@ -1272,8 +1273,8 @@ const JSC::GlobalObjectMethodTable& EvalGlobalObject::globalObjectMethodTable()
&scriptExecutionStatus,
nullptr, // reportViolationForUnsafeEval
nullptr, // defaultLanguage
nullptr, // compileStreaming
nullptr, // instantiateStreaming
&compileStreaming,
&instantiateStreaming,
&Zig::deriveShadowRealmGlobalObject,
&codeForEval, // codeForEval
&canCompileStrings, // canCompileStrings
Expand Down Expand Up @@ -3077,6 +3078,11 @@ void GlobalObject::finishCreation(VM& vm)
init.set(JSC::JSFunction::create(init.vm, init.owner, utilInspectStylizeWithNoColorCodeGenerator(init.vm), init.owner));
});

m_wasmStreamingConsumeStreamFunction.initLater(
[](const Initializer<JSFunction>& init) {
init.set(JSC::JSFunction::create(init.vm, init.owner, wasmStreamingConsumeStreamCodeGenerator(init.vm), init.owner));
});

m_nativeMicrotaskTrampoline.initLater(
[](const Initializer<JSFunction>& init) {
init.set(JSFunction::create(init.vm, init.owner, 2, ""_s, functionNativeMicrotaskTrampoline, ImplementationVisibility::Public));
Expand Down Expand Up @@ -4443,6 +4449,61 @@ JSC::JSValue EvalGlobalObject::moduleLoaderEvaluate(JSGlobalObject* lexicalGloba
return result;
}

extern "C" JSC::EncodedJSValue Zig__GlobalObject__getBodyStreamOrBytesForWasmStreaming(JSGlobalObject*, EncodedJSValue response, JSC::Wasm::StreamingCompiler* compiler);

extern "C" void JSC__Wasm__StreamingCompiler__addBytes(JSC::Wasm::StreamingCompiler* compiler, const uint8_t* spanPtr, size_t spanSize)
{
compiler->addBytes(std::span(spanPtr, spanSize));
}

static JSC::JSPromise* handleResponseOnStreamingAction(JSGlobalObject* lexicalGlobalObject, JSC::JSValue source, JSC::Wasm::CompilerMode mode, JSC::JSObject* importObject)
{
auto globalObject = defaultGlobalObject(lexicalGlobalObject);
auto& vm = JSC::getVM(globalObject);
auto scope = DECLARE_THROW_SCOPE(vm);
JSC::JSLockHolder locker(vm);

auto promise = JSC::JSPromise::create(vm, globalObject->promiseStructure());
auto compiler = JSC::Wasm::StreamingCompiler::create(vm, mode, globalObject, promise, importObject);

// getBodyStreamOrBytesForWasmStreaming throws the proper exception. Since this is being
// executed in a .then(...) callback, throwing is perfectly fine.

auto readableStreamMaybe = JSC::JSValue::decode(Zig__GlobalObject__getBodyStreamOrBytesForWasmStreaming(
globalObject, JSC::JSValue::encode(source), compiler.ptr()));

RETURN_IF_EXCEPTION(scope, nullptr);

// We were able to get the slice synchronously.
if (readableStreamMaybe.isNull()) {
compiler->finalize(globalObject);

// Apparently rejecting a Promise (done in JSC::Wasm::StreamingCompiler#fail) can throw
RETURN_IF_EXCEPTION(scope, nullptr);
return promise;
}

auto wrapper = WebCore::toJSNewlyCreated(globalObject, globalObject, WTFMove(compiler));
auto builtin = globalObject->wasmStreamingConsumeStreamFunction();
auto callData = JSC::getCallData(builtin);
MarkedArgumentBuffer arguments;

arguments.append(readableStreamMaybe);
JSC::call(globalObject, builtin, callData, wrapper, arguments);
scope.assertNoException();
return promise;
}

JSC::JSPromise* GlobalObject::compileStreaming(JSGlobalObject* globalObject, JSC::JSValue source)
{
return handleResponseOnStreamingAction(globalObject, source, JSC::Wasm::CompilerMode::Validation, nullptr);
}

JSC::JSPromise* GlobalObject::instantiateStreaming(JSGlobalObject* globalObject, JSC::JSValue source, JSC::JSObject* importObject)
{
return handleResponseOnStreamingAction(globalObject, source, JSC::Wasm::CompilerMode::FullCompile, importObject);
}

GlobalObject::PromiseFunctions GlobalObject::promiseHandlerID(Zig::FFIFunction handler)
{
if (handler == BunServe__onResolvePlugins) {
Expand Down
5 changes: 5 additions & 0 deletions src/bun.js/bindings/ZigGlobalObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ class GlobalObject : public Bun::GlobalScope {
static JSC::JSInternalPromise* moduleLoaderFetch(JSGlobalObject*, JSC::JSModuleLoader*, JSC::JSValue key, JSC::JSValue parameters, JSC::JSValue script);
static JSC::JSObject* moduleLoaderCreateImportMetaProperties(JSGlobalObject*, JSC::JSModuleLoader*, JSC::JSValue key, JSC::JSModuleRecord*, JSC::JSValue val);
static JSC::JSValue moduleLoaderEvaluate(JSGlobalObject*, JSC::JSModuleLoader*, JSValue key, JSValue moduleRecordValue, JSValue scriptFetcher, JSValue sentValue, JSValue resumeMode);
static JSC::JSPromise* compileStreaming(JSGlobalObject*, JSC::JSValue source);
static JSC::JSPromise* instantiateStreaming(JSGlobalObject*, JSC::JSValue source, JSC::JSObject* importObject);

static ScriptExecutionStatus scriptExecutionStatus(JSGlobalObject*, JSObject*);
static void promiseRejectionTracker(JSGlobalObject*, JSC::JSPromise*, JSC::JSPromiseRejectionOperation);
Expand Down Expand Up @@ -274,6 +276,8 @@ class GlobalObject : public Bun::GlobalScope {
JSC::JSFunction* utilInspectStylizeColorFunction() const { return m_utilInspectStylizeColorFunction.getInitializedOnMainThread(this); }
JSC::JSFunction* utilInspectStylizeNoColorFunction() const { return m_utilInspectStylizeNoColorFunction.getInitializedOnMainThread(this); }

JSC::JSFunction* wasmStreamingConsumeStreamFunction() const { return m_wasmStreamingConsumeStreamFunction.getInitializedOnMainThread(this); }

JSObject* requireFunctionUnbound() const { return m_requireFunctionUnbound.getInitializedOnMainThread(this); }
JSObject* requireResolveFunctionUnbound() const { return m_requireResolveFunctionUnbound.getInitializedOnMainThread(this); }
Bun::InternalModuleRegistry* internalModuleRegistry() const { return m_internalModuleRegistry.getInitializedOnMainThread(this); }
Expand Down Expand Up @@ -561,6 +565,7 @@ class GlobalObject : public Bun::GlobalScope {
V(private, LazyPropertyOfGlobalObject<Structure>, m_utilInspectOptionsStructure) \
V(private, LazyPropertyOfGlobalObject<JSFunction>, m_utilInspectStylizeColorFunction) \
V(private, LazyPropertyOfGlobalObject<JSFunction>, m_utilInspectStylizeNoColorFunction) \
V(private, LazyPropertyOfGlobalObject<JSFunction>, m_wasmStreamingConsumeStreamFunction) \
V(private, LazyPropertyOfGlobalObject<JSMap>, m_lazyReadableStreamPrototypeMap) \
V(private, LazyPropertyOfGlobalObject<JSMap>, m_requireMap) \
V(private, LazyPropertyOfGlobalObject<JSMap>, m_esmRegistryMap) \
Expand Down
2 changes: 2 additions & 0 deletions src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ class DOMClientIsoSubspaces {
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSS3File;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSX509Certificate;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSNodePerformanceHooksHistogram;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForWasmStreamingCompiler;

#include "ZigGeneratedClasses+DOMClientIsoSubspaces.h"
/* --- bun --- */

Expand Down
1 change: 1 addition & 0 deletions src/bun.js/bindings/webcore/DOMIsoSubspaces.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class DOMIsoSubspaces {
std::unique_ptr<IsoSubspace> m_subspaceForJSS3File;
std::unique_ptr<IsoSubspace> m_subspaceForJSX509Certificate;
std::unique_ptr<IsoSubspace> m_subspaceForJSNodePerformanceHooksHistogram;
std::unique_ptr<IsoSubspace> m_subspaceForWasmStreamingCompiler;
#include "ZigGeneratedClasses+DOMIsoSubspaces.h"
/*-- BUN --*/

Expand Down
Loading
Loading