aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--integration/bunjs-only-snippets/bun-jsc.test.js2
-rw-r--r--integration/bunjs-only-snippets/concat.test.js4
-rw-r--r--integration/bunjs-only-snippets/inspect.test.js1
-rw-r--r--integration/bunjs-only-snippets/streams.test.js93
-rw-r--r--integration/bunjs-only-snippets/transpiler.test.js7
-rw-r--r--src/javascript/jsc/bindings/ReadableByteStreamInternalsBuiltins.cpp5
-rw-r--r--src/javascript/jsc/bindings/ReadableStreamBuiltins.cpp57
-rw-r--r--src/javascript/jsc/bindings/ReadableStreamBuiltins.h24
-rw-r--r--src/javascript/jsc/bindings/ReadableStreamDefaultReaderBuiltins.cpp15
-rw-r--r--src/javascript/jsc/bindings/ZigGlobalObject.cpp116
-rw-r--r--src/javascript/jsc/bindings/ZigGlobalObject.h4
-rw-r--r--src/javascript/jsc/bindings/bindings.zig18
-rw-r--r--src/javascript/jsc/bindings/builtins/js/ReadableStream.js40
-rw-r--r--src/javascript/jsc/bindings/builtins/js/ReadableStreamDefaultReader.js13
-rw-r--r--src/javascript/jsc/webcore/response.zig114
-rw-r--r--src/javascript/jsc/webcore/streams.zig4
16 files changed, 478 insertions, 39 deletions
diff --git a/integration/bunjs-only-snippets/bun-jsc.test.js b/integration/bunjs-only-snippets/bun-jsc.test.js
index e329bc092..975003b77 100644
--- a/integration/bunjs-only-snippets/bun-jsc.test.js
+++ b/integration/bunjs-only-snippets/bun-jsc.test.js
@@ -77,7 +77,7 @@ describe("bun:jsc", () => {
count();
});
it("numberOfDFGCompiles", () => {
- expect(numberOfDFGCompiles(count)).toBe(3);
+ expect(numberOfDFGCompiles(count) > 0).toBe(true);
});
it("releaseWeakRefs", () => {
releaseWeakRefs();
diff --git a/integration/bunjs-only-snippets/concat.test.js b/integration/bunjs-only-snippets/concat.test.js
index 9f3e1f257..a965fdb94 100644
--- a/integration/bunjs-only-snippets/concat.test.js
+++ b/integration/bunjs-only-snippets/concat.test.js
@@ -1,6 +1,6 @@
import { describe, it, expect } from "bun:test";
import { gcTick } from "./gc";
-import { concat } from "bun";
+import { concatArrayBuffers } from "bun";
describe("concat", () => {
function polyfill(chunks) {
@@ -19,7 +19,7 @@ describe("concat", () => {
}
function concatToString(chunks) {
- return Array.from(new Uint8Array(concat(chunks))).join("");
+ return Array.from(new Uint8Array(concatArrayBuffers(chunks))).join("");
}
function polyfillToString(chunks) {
diff --git a/integration/bunjs-only-snippets/inspect.test.js b/integration/bunjs-only-snippets/inspect.test.js
index d110cd4b4..344fd7a78 100644
--- a/integration/bunjs-only-snippets/inspect.test.js
+++ b/integration/bunjs-only-snippets/inspect.test.js
@@ -91,4 +91,5 @@ it("inspect", () => {
).toBe(`<div hello="quoted">
<input type="text" value="123" />
</div>`);
+ expect(Bun.inspect(BigInt(32)), "32n");
});
diff --git a/integration/bunjs-only-snippets/streams.test.js b/integration/bunjs-only-snippets/streams.test.js
index d694be1ba..a3d4965ee 100644
--- a/integration/bunjs-only-snippets/streams.test.js
+++ b/integration/bunjs-only-snippets/streams.test.js
@@ -184,3 +184,96 @@ it("ReadableStream for empty file closes immediately", async () => {
expect(chunks.length).toBe(0);
});
+
+it("new Response(stream).arrayBuffer() (bytes)", async () => {
+ var queue = [Buffer.from("abdefgh")];
+ var stream = new ReadableStream({
+ pull(controller) {
+ var chunk = queue.shift();
+ if (chunk) {
+ controller.enqueue(chunk);
+ } else {
+ controller.close();
+ }
+ },
+ cancel() {},
+ type: "bytes",
+ });
+ const buffer = await new Response(stream).arrayBuffer();
+ expect(new TextDecoder().decode(new Uint8Array(buffer))).toBe("abdefgh");
+});
+
+it("new Response(stream).arrayBuffer() (default)", async () => {
+ var queue = [Buffer.from("abdefgh")];
+ var stream = new ReadableStream({
+ pull(controller) {
+ var chunk = queue.shift();
+ if (chunk) {
+ controller.enqueue(chunk);
+ } else {
+ controller.close();
+ }
+ },
+ cancel() {},
+ });
+ const buffer = await new Response(stream).arrayBuffer();
+ expect(new TextDecoder().decode(new Uint8Array(buffer))).toBe("abdefgh");
+});
+
+it("new Response(stream).text() (default)", async () => {
+ var queue = [Buffer.from("abdefgh")];
+ var stream = new ReadableStream({
+ pull(controller) {
+ var chunk = queue.shift();
+ if (chunk) {
+ controller.enqueue(chunk);
+ } else {
+ controller.close();
+ }
+ },
+ cancel() {},
+ });
+ const text = await new Response(stream).text();
+ expect(text).toBe("abdefgh");
+});
+
+it("new Response(stream).json() (default)", async () => {
+ var queue = [Buffer.from(JSON.stringify({ hello: true }))];
+ var stream = new ReadableStream({
+ pull(controller) {
+ var chunk = queue.shift();
+ if (chunk) {
+ controller.enqueue(chunk);
+ } else {
+ controller.close();
+ }
+ },
+ cancel() {},
+ });
+ const json = await new Response(stream).json();
+ expect(json.hello).toBe(true);
+});
+
+it("new Response(stream).blob() (default)", async () => {
+ var queue = [Buffer.from(JSON.stringify({ hello: true }))];
+ var stream = new ReadableStream({
+ pull(controller) {
+ var chunk = queue.shift();
+ if (chunk) {
+ controller.enqueue(chunk);
+ } else {
+ controller.close();
+ }
+ },
+ cancel() {},
+ });
+ const blob = await new Response(stream).blob();
+ expect(await blob.text()).toBe('{"hello":true}');
+});
+
+it("Blob.stream() -> new Response(stream).text()", async () => {
+ var blob = new Blob(["abdefgh"]);
+ var stream = blob.stream();
+ const text = await new Response(stream).text();
+ expect(text).toBe("abdefgh");
+});
diff --git a/integration/bunjs-only-snippets/transpiler.test.js b/integration/bunjs-only-snippets/transpiler.test.js
index 30fc2afde..f8da4c18c 100644
--- a/integration/bunjs-only-snippets/transpiler.test.js
+++ b/integration/bunjs-only-snippets/transpiler.test.js
@@ -358,12 +358,9 @@ export var ComponentThatChecksDefaultPropsAndHasChildren = {
type: Hello,
key: null,
ref: null,
- props: !Hello.defaultProps ? {
+ props: __merge({
children: "my child"
- } : {
- ...Hello.defaultProps,
- children: "my child"
- },
+ }, Hello.defaultProps),
_owner: null
};
export var ComponentThatHasSpreadCausesDeopt = jsx(Hello, {
diff --git a/src/javascript/jsc/bindings/ReadableByteStreamInternalsBuiltins.cpp b/src/javascript/jsc/bindings/ReadableByteStreamInternalsBuiltins.cpp
index 123874f1b..a1df2da1b 100644
--- a/src/javascript/jsc/bindings/ReadableByteStreamInternalsBuiltins.cpp
+++ b/src/javascript/jsc/bindings/ReadableByteStreamInternalsBuiltins.cpp
@@ -366,7 +366,7 @@ const char* const s_readableByteStreamInternalsReadableByteStreamControllerPullC
const JSC::ConstructAbility s_readableByteStreamInternalsReadableByteStreamControllerShouldCallPullCodeConstructAbility = JSC::ConstructAbility::CannotConstruct;
const JSC::ConstructorKind s_readableByteStreamInternalsReadableByteStreamControllerShouldCallPullCodeConstructorKind = JSC::ConstructorKind::None;
-const int s_readableByteStreamInternalsReadableByteStreamControllerShouldCallPullCodeLength = 872;
+const int s_readableByteStreamInternalsReadableByteStreamControllerShouldCallPullCodeLength = 873;
static const JSC::Intrinsic s_readableByteStreamInternalsReadableByteStreamControllerShouldCallPullCodeIntrinsic = JSC::NoIntrinsic;
const char* const s_readableByteStreamInternalsReadableByteStreamControllerShouldCallPullCode =
"(function (controller)\n" \
@@ -381,7 +381,8 @@ const char* const s_readableByteStreamInternalsReadableByteStreamControllerShoul
" return false;\n" \
" if (!@getByIdDirectPrivate(controller, \"started\"))\n" \
" return false;\n" \
- " const reader = @getByIdDirectPrivate(stream, \"reader\");\n" \
+ " const reader = @getByIdDirectPrivate(stream, \"reader\");\n" \
+ " \n" \
" if (reader && (@getByIdDirectPrivate(reader, \"readRequests\")?.isNotEmpty() || !!@getByIdDirectPrivate(reader, \"bunNativePtr\")))\n" \
" return true;\n" \
" if (@readableStreamHasBYOBReader(stream) && @getByIdDirectPrivate(@getByIdDirectPrivate(stream, \"reader\"), \"readIntoRequests\")?.isNotEmpty())\n" \
diff --git a/src/javascript/jsc/bindings/ReadableStreamBuiltins.cpp b/src/javascript/jsc/bindings/ReadableStreamBuiltins.cpp
index c7d9470c2..6db3451d3 100644
--- a/src/javascript/jsc/bindings/ReadableStreamBuiltins.cpp
+++ b/src/javascript/jsc/bindings/ReadableStreamBuiltins.cpp
@@ -119,7 +119,7 @@ const char* const s_readableStreamInitializeReadableStreamCode =
const JSC::ConstructAbility s_readableStreamReadableStreamToArrayCodeConstructAbility = JSC::ConstructAbility::CannotConstruct;
const JSC::ConstructorKind s_readableStreamReadableStreamToArrayCodeConstructorKind = JSC::ConstructorKind::None;
-const int s_readableStreamReadableStreamToArrayCodeLength = 884;
+const int s_readableStreamReadableStreamToArrayCodeLength = 883;
static const JSC::Intrinsic s_readableStreamReadableStreamToArrayCodeIntrinsic = JSC::NoIntrinsic;
const char* const s_readableStreamReadableStreamToArrayCode =
"(function (stream) {\n" \
@@ -129,7 +129,6 @@ const char* const s_readableStreamReadableStreamToArrayCode =
" return null;\n" \
" }\n" \
" var reader = stream.getReader();\n" \
- "\n" \
" var manyResult = reader.readMany();\n" \
" \n" \
" async function processManyResult(result) {\n" \
@@ -166,6 +165,60 @@ const char* const s_readableStreamReadableStreamToArrayCode =
"})\n" \
;
+const JSC::ConstructAbility s_readableStreamReadableStreamToTextCodeConstructAbility = JSC::ConstructAbility::CannotConstruct;
+const JSC::ConstructorKind s_readableStreamReadableStreamToTextCodeConstructorKind = JSC::ConstructorKind::None;
+const int s_readableStreamReadableStreamToTextCodeLength = 215;
+static const JSC::Intrinsic s_readableStreamReadableStreamToTextCodeIntrinsic = JSC::NoIntrinsic;
+const char* const s_readableStreamReadableStreamToTextCode =
+ "(function (stream) {\n" \
+ " \"use strict\";\n" \
+ "\n" \
+ " //\n" \
+ " return globalThis.Bun.readableStreamToArrayBuffer(stream).@then(function(arrayBuffer) {\n" \
+ " return new globalThis.TextDecoder().decode(arrayBuffer);\n" \
+ " });\n" \
+ "})\n" \
+;
+
+const JSC::ConstructAbility s_readableStreamReadableStreamToJSONCodeConstructAbility = JSC::ConstructAbility::CannotConstruct;
+const JSC::ConstructorKind s_readableStreamReadableStreamToJSONCodeConstructorKind = JSC::ConstructorKind::None;
+const int s_readableStreamReadableStreamToJSONCodeLength = 238;
+static const JSC::Intrinsic s_readableStreamReadableStreamToJSONCodeIntrinsic = JSC::NoIntrinsic;
+const char* const s_readableStreamReadableStreamToJSONCode =
+ "(function (stream) {\n" \
+ " \"use strict\";\n" \
+ "\n" \
+ " //\n" \
+ " return globalThis.Bun.readableStreamToArrayBuffer(stream).@then(function(arrayBuffer) {\n" \
+ " return globalThis.JSON.parse(new globalThis.TextDecoder().decode(arrayBuffer));\n" \
+ " });\n" \
+ "})\n" \
+;
+
+const JSC::ConstructAbility s_readableStreamReadableStreamToBlobCodeConstructAbility = JSC::ConstructAbility::CannotConstruct;
+const JSC::ConstructorKind s_readableStreamReadableStreamToBlobCodeConstructorKind = JSC::ConstructorKind::None;
+const int s_readableStreamReadableStreamToBlobCodeLength = 367;
+static const JSC::Intrinsic s_readableStreamReadableStreamToBlobCodeIntrinsic = JSC::NoIntrinsic;
+const char* const s_readableStreamReadableStreamToBlobCode =
+ "(function (stream) {\n" \
+ " \"use strict\";\n" \
+ "\n" \
+ " \n" \
+ " const array = @readableStreamToArray(stream);\n" \
+ " if (array === null) {\n" \
+ " return new globalThis.Blob();\n" \
+ " }\n" \
+ "\n" \
+ " return array.@then(function(chunks) {\n" \
+ " if (chunks === null || chunks.length === 0) {\n" \
+ " return new globalThis.Blob();\n" \
+ " }\n" \
+ "\n" \
+ " return new globalThis.Blob(chunks);\n" \
+ " });\n" \
+ "})\n" \
+;
+
const JSC::ConstructAbility s_readableStreamReadableStreamToArrayPublicCodeConstructAbility = JSC::ConstructAbility::CannotConstruct;
const JSC::ConstructorKind s_readableStreamReadableStreamToArrayPublicCodeConstructorKind = JSC::ConstructorKind::None;
const int s_readableStreamReadableStreamToArrayPublicCodeLength = 841;
diff --git a/src/javascript/jsc/bindings/ReadableStreamBuiltins.h b/src/javascript/jsc/bindings/ReadableStreamBuiltins.h
index 7733d4d0c..d009f8560 100644
--- a/src/javascript/jsc/bindings/ReadableStreamBuiltins.h
+++ b/src/javascript/jsc/bindings/ReadableStreamBuiltins.h
@@ -55,6 +55,18 @@ extern const char* const s_readableStreamReadableStreamToArrayCode;
extern const int s_readableStreamReadableStreamToArrayCodeLength;
extern const JSC::ConstructAbility s_readableStreamReadableStreamToArrayCodeConstructAbility;
extern const JSC::ConstructorKind s_readableStreamReadableStreamToArrayCodeConstructorKind;
+extern const char* const s_readableStreamReadableStreamToTextCode;
+extern const int s_readableStreamReadableStreamToTextCodeLength;
+extern const JSC::ConstructAbility s_readableStreamReadableStreamToTextCodeConstructAbility;
+extern const JSC::ConstructorKind s_readableStreamReadableStreamToTextCodeConstructorKind;
+extern const char* const s_readableStreamReadableStreamToJSONCode;
+extern const int s_readableStreamReadableStreamToJSONCodeLength;
+extern const JSC::ConstructAbility s_readableStreamReadableStreamToJSONCodeConstructAbility;
+extern const JSC::ConstructorKind s_readableStreamReadableStreamToJSONCodeConstructorKind;
+extern const char* const s_readableStreamReadableStreamToBlobCode;
+extern const int s_readableStreamReadableStreamToBlobCodeLength;
+extern const JSC::ConstructAbility s_readableStreamReadableStreamToBlobCodeConstructAbility;
+extern const JSC::ConstructorKind s_readableStreamReadableStreamToBlobCodeConstructorKind;
extern const char* const s_readableStreamReadableStreamToArrayPublicCode;
extern const int s_readableStreamReadableStreamToArrayPublicCodeLength;
extern const JSC::ConstructAbility s_readableStreamReadableStreamToArrayPublicCodeConstructAbility;
@@ -99,6 +111,9 @@ extern const JSC::ConstructorKind s_readableStreamLockedCodeConstructorKind;
#define WEBCORE_FOREACH_READABLESTREAM_BUILTIN_DATA(macro) \
macro(initializeReadableStream, readableStreamInitializeReadableStream, 2) \
macro(readableStreamToArray, readableStreamReadableStreamToArray, 1) \
+ macro(readableStreamToText, readableStreamReadableStreamToText, 1) \
+ macro(readableStreamToJSON, readableStreamReadableStreamToJSON, 1) \
+ macro(readableStreamToBlob, readableStreamReadableStreamToBlob, 1) \
macro(readableStreamToArrayPublic, readableStreamReadableStreamToArrayPublic, 1) \
macro(consumeReadableStream, readableStreamConsumeReadableStream, 3) \
macro(createEmptyReadableStream, readableStreamCreateEmptyReadableStream, 0) \
@@ -112,6 +127,9 @@ extern const JSC::ConstructorKind s_readableStreamLockedCodeConstructorKind;
#define WEBCORE_BUILTIN_READABLESTREAM_INITIALIZEREADABLESTREAM 1
#define WEBCORE_BUILTIN_READABLESTREAM_READABLESTREAMTOARRAY 1
+#define WEBCORE_BUILTIN_READABLESTREAM_READABLESTREAMTOTEXT 1
+#define WEBCORE_BUILTIN_READABLESTREAM_READABLESTREAMTOJSON 1
+#define WEBCORE_BUILTIN_READABLESTREAM_READABLESTREAMTOBLOB 1
#define WEBCORE_BUILTIN_READABLESTREAM_READABLESTREAMTOARRAYPUBLIC 1
#define WEBCORE_BUILTIN_READABLESTREAM_CONSUMEREADABLESTREAM 1
#define WEBCORE_BUILTIN_READABLESTREAM_CREATEEMPTYREADABLESTREAM 1
@@ -126,6 +144,9 @@ extern const JSC::ConstructorKind s_readableStreamLockedCodeConstructorKind;
#define WEBCORE_FOREACH_READABLESTREAM_BUILTIN_CODE(macro) \
macro(readableStreamInitializeReadableStreamCode, initializeReadableStream, ASCIILiteral(), s_readableStreamInitializeReadableStreamCodeLength) \
macro(readableStreamReadableStreamToArrayCode, readableStreamToArray, ASCIILiteral(), s_readableStreamReadableStreamToArrayCodeLength) \
+ macro(readableStreamReadableStreamToTextCode, readableStreamToText, ASCIILiteral(), s_readableStreamReadableStreamToTextCodeLength) \
+ macro(readableStreamReadableStreamToJSONCode, readableStreamToJSON, ASCIILiteral(), s_readableStreamReadableStreamToJSONCodeLength) \
+ macro(readableStreamReadableStreamToBlobCode, readableStreamToBlob, ASCIILiteral(), s_readableStreamReadableStreamToBlobCodeLength) \
macro(readableStreamReadableStreamToArrayPublicCode, readableStreamToArrayPublic, ASCIILiteral(), s_readableStreamReadableStreamToArrayPublicCodeLength) \
macro(readableStreamConsumeReadableStreamCode, consumeReadableStream, ASCIILiteral(), s_readableStreamConsumeReadableStreamCodeLength) \
macro(readableStreamCreateEmptyReadableStreamCode, createEmptyReadableStream, ASCIILiteral(), s_readableStreamCreateEmptyReadableStreamCodeLength) \
@@ -149,6 +170,9 @@ extern const JSC::ConstructorKind s_readableStreamLockedCodeConstructorKind;
macro(pipeTo) \
macro(readableStreamToArray) \
macro(readableStreamToArrayPublic) \
+ macro(readableStreamToBlob) \
+ macro(readableStreamToJSON) \
+ macro(readableStreamToText) \
macro(tee) \
#define DECLARE_BUILTIN_GENERATOR(codeName, functionName, overriddenName, argumentCount) \
diff --git a/src/javascript/jsc/bindings/ReadableStreamDefaultReaderBuiltins.cpp b/src/javascript/jsc/bindings/ReadableStreamDefaultReaderBuiltins.cpp
index 839550857..91689fb13 100644
--- a/src/javascript/jsc/bindings/ReadableStreamDefaultReaderBuiltins.cpp
+++ b/src/javascript/jsc/bindings/ReadableStreamDefaultReaderBuiltins.cpp
@@ -89,7 +89,7 @@ const char* const s_readableStreamDefaultReaderCancelCode =
const JSC::ConstructAbility s_readableStreamDefaultReaderReadManyCodeConstructAbility = JSC::ConstructAbility::CannotConstruct;
const JSC::ConstructorKind s_readableStreamDefaultReaderReadManyCodeConstructorKind = JSC::ConstructorKind::None;
-const int s_readableStreamDefaultReaderReadManyCodeLength = 2683;
+const int s_readableStreamDefaultReaderReadManyCodeLength = 3235;
static const JSC::Intrinsic s_readableStreamDefaultReaderReadManyCodeIntrinsic = JSC::NoIntrinsic;
const char* const s_readableStreamDefaultReaderReadManyCode =
"(function ()\n" \
@@ -121,6 +121,12 @@ const char* const s_readableStreamDefaultReaderReadManyCode =
" \n" \
"\n" \
" if (length > 0) {\n" \
+ " for (var i = 0; i < values.length; i++) {\n" \
+ " const buf = values[i];\n" \
+ " if (!(@ArrayBuffer.@isView(buf) || buf instanceof @ArrayBuffer)) {\n" \
+ " values[i] = new @Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);\n" \
+ " }\n" \
+ " }\n" \
" \n" \
" @resetQueue(@getByIdDirectPrivate(controller, \"queue\"));\n" \
"\n" \
@@ -143,6 +149,13 @@ const char* const s_readableStreamDefaultReaderReadManyCode =
" \n" \
" var queue = @getByIdDirectPrivate(controller, \"queue\");\n" \
" var value = [result.value].concat(queue.content.toArray(false));\n" \
+ " for (var i = 0; i < value.length; i++) {\n" \
+ " const buf = value[i];\n" \
+ " if (!(@ArrayBuffer.@isView(buf) || buf instanceof @ArrayBuffer)) {\n" \
+ " value[i] = new @Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);\n" \
+ " }\n" \
+ " }\n" \
+ "\n" \
" var size = queue.size;\n" \
" @resetQueue(queue);\n" \
"\n" \
diff --git a/src/javascript/jsc/bindings/ZigGlobalObject.cpp b/src/javascript/jsc/bindings/ZigGlobalObject.cpp
index 3282370bd..f0fb661a4 100644
--- a/src/javascript/jsc/bindings/ZigGlobalObject.cpp
+++ b/src/javascript/jsc/bindings/ZigGlobalObject.cpp
@@ -886,6 +886,19 @@ static JSC_DEFINE_HOST_FUNCTION(functionReportError,
return JSC::JSValue::encode(JSC::jsUndefined());
}
+extern "C" JSC__JSValue Bun__createUninitializedArrayBuffer(JSC::JSGlobalObject* globalObject, const void* ptr, size_t len)
+{
+ auto scope = DECLARE_THROW_SCOPE(globalObject->vm());
+ auto arrayBuffer = JSC::ArrayBuffer::tryCreateUninitialized(len, 1);
+
+ if (UNLIKELY(!arrayBuffer)) {
+ JSC::throwOutOfMemoryError(globalObject, scope);
+ return JSC::JSValue::encode(JSC::JSValue {});
+ }
+
+ RELEASE_AND_RETURN(scope, JSValue::encode(JSC::JSArrayBuffer::create(globalObject->vm(), globalObject->arrayBufferStructure(JSC::ArrayBufferSharingMode::Default), WTFMove(arrayBuffer))));
+}
+
JSC_DECLARE_HOST_FUNCTION(functionCreateUninitializedArrayBuffer);
JSC_DEFINE_HOST_FUNCTION(functionCreateUninitializedArrayBuffer,
(JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
@@ -1273,12 +1286,9 @@ extern "C" int32_t ReadableStreamTag__tagged(Zig::GlobalObject* globalObject, JS
auto* readableStream = jsCast<JSReadableStream*>(object);
auto& vm = globalObject->vm();
auto& builtinNames = WebCore::clientData(vm)->builtinNames();
-
- JSValue numberValue = readableStream->getDirect(vm, builtinNames.bunNativeTypePrivateName());
- int32_t num = numberValue.toInt32(globalObject);
- if (numberValue.isUndefined() || num == 0) {
- *ptr = JSC::JSValue();
- return 0;
+ int32_t num = 0;
+ if (JSValue numberValue = readableStream->getDirect(vm, builtinNames.bunNativeTypePrivateName())) {
+ num = numberValue.toInt32(globalObject);
}
// If this type is outside the expected range, it means something is wrong.
@@ -1533,6 +1543,78 @@ extern "C" JSC__JSValue ZigGlobalObject__readableStreamToArrayBuffer(Zig::Global
return ZigGlobalObject__readableStreamToArrayBufferBody(reinterpret_cast<Zig::GlobalObject*>(globalObject), readableStreamValue);
}
+extern "C" JSC__JSValue ZigGlobalObject__readableStreamToText(Zig::GlobalObject* globalObject, JSC__JSValue readableStreamValue);
+extern "C" JSC__JSValue ZigGlobalObject__readableStreamToText(Zig::GlobalObject* globalObject, JSC__JSValue readableStreamValue)
+{
+ auto& vm = globalObject->vm();
+
+ auto clientData = WebCore::clientData(vm);
+ auto& builtinNames = WebCore::builtinNames(vm);
+
+ JSC::JSFunction* function = nullptr;
+ if (auto readableStreamToText = globalObject->m_readableStreamToText.get()) {
+ function = readableStreamToText;
+ } else {
+ function = JSFunction::create(vm, static_cast<JSC::FunctionExecutable*>(readableStreamReadableStreamToTextCodeGenerator(vm)), globalObject);
+
+ globalObject->m_readableStreamToText.set(vm, globalObject, function);
+ }
+
+ JSC::MarkedArgumentBuffer arguments = JSC::MarkedArgumentBuffer();
+ arguments.append(JSValue::decode(readableStreamValue));
+
+ auto callData = JSC::getCallData(function);
+ return JSC::JSValue::encode(call(globalObject, function, callData, JSC::jsUndefined(), arguments));
+}
+
+extern "C" JSC__JSValue ZigGlobalObject__readableStreamToJSON(Zig::GlobalObject* globalObject, JSC__JSValue readableStreamValue);
+extern "C" JSC__JSValue ZigGlobalObject__readableStreamToJSON(Zig::GlobalObject* globalObject, JSC__JSValue readableStreamValue)
+{
+ auto& vm = globalObject->vm();
+
+ auto clientData = WebCore::clientData(vm);
+ auto& builtinNames = WebCore::builtinNames(vm);
+
+ JSC::JSFunction* function = nullptr;
+ if (auto readableStreamToJSON = globalObject->m_readableStreamToJSON.get()) {
+ function = readableStreamToJSON;
+ } else {
+ function = JSFunction::create(vm, static_cast<JSC::FunctionExecutable*>(readableStreamReadableStreamToJSONCodeGenerator(vm)), globalObject);
+
+ globalObject->m_readableStreamToJSON.set(vm, globalObject, function);
+ }
+
+ JSC::MarkedArgumentBuffer arguments = JSC::MarkedArgumentBuffer();
+ arguments.append(JSValue::decode(readableStreamValue));
+
+ auto callData = JSC::getCallData(function);
+ return JSC::JSValue::encode(call(globalObject, function, callData, JSC::jsUndefined(), arguments));
+}
+
+extern "C" JSC__JSValue ZigGlobalObject__readableStreamToBlob(Zig::GlobalObject* globalObject, JSC__JSValue readableStreamValue);
+extern "C" JSC__JSValue ZigGlobalObject__readableStreamToBlob(Zig::GlobalObject* globalObject, JSC__JSValue readableStreamValue)
+{
+ auto& vm = globalObject->vm();
+
+ auto clientData = WebCore::clientData(vm);
+ auto& builtinNames = WebCore::builtinNames(vm);
+
+ JSC::JSFunction* function = nullptr;
+ if (auto readableStreamToBlob = globalObject->m_readableStreamToBlob.get()) {
+ function = readableStreamToBlob;
+ } else {
+ function = JSFunction::create(vm, static_cast<JSC::FunctionExecutable*>(readableStreamReadableStreamToBlobCodeGenerator(vm)), globalObject);
+
+ globalObject->m_readableStreamToBlob.set(vm, globalObject, function);
+ }
+
+ JSC::MarkedArgumentBuffer arguments = JSC::MarkedArgumentBuffer();
+ arguments.append(JSValue::decode(readableStreamValue));
+
+ auto callData = JSC::getCallData(function);
+ return JSC::JSValue::encode(call(globalObject, function, callData, JSC::jsUndefined(), arguments));
+}
+
JSC_DECLARE_HOST_FUNCTION(functionReadableStreamToArrayBuffer);
JSC_DEFINE_HOST_FUNCTION(functionReadableStreamToArrayBuffer, (JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
{
@@ -1772,6 +1854,24 @@ void GlobalObject::installAPIGlobals(JSClassRef* globals, int count, JSC::VM& vm
}
{
+ JSC::Identifier identifier = JSC::Identifier::fromString(vm, "readableStreamToText"_s);
+ object->putDirectBuiltinFunction(vm, this, identifier, readableStreamReadableStreamToTextCodeGenerator(vm),
+ JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0);
+ }
+
+ {
+ JSC::Identifier identifier = JSC::Identifier::fromString(vm, "readableStreamToBlob"_s);
+ object->putDirectBuiltinFunction(vm, this, identifier, readableStreamReadableStreamToBlobCodeGenerator(vm),
+ JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0);
+ }
+
+ {
+ JSC::Identifier identifier = JSC::Identifier::fromString(vm, "readableStreamToJSON"_s);
+ object->putDirectBuiltinFunction(vm, this, identifier, readableStreamReadableStreamToJSONCodeGenerator(vm),
+ JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0);
+ }
+
+ {
JSC::Identifier identifier = JSC::Identifier::fromString(vm, "concatArrayBuffers"_s);
object->putDirectNativeFunction(vm, this, identifier, 1, functionConcatTypedArrays, NoIntrinsic,
JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0);
@@ -1871,7 +1971,9 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor)
thisObject->m_builtinInternalFunctions.visit(visitor);
thisObject->m_JSFFIFunctionStructure.visit(visitor);
visitor.append(thisObject->m_readableStreamToArrayBufferResolve);
- visitor.append(thisObject->m_readableStreamToTextResolve);
+ 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 ffed48909..54d511c94 100644
--- a/src/javascript/jsc/bindings/ZigGlobalObject.h
+++ b/src/javascript/jsc/bindings/ZigGlobalObject.h
@@ -153,7 +153,9 @@ public:
bool isThreadLocalDefaultGlobalObject = false;
mutable WriteBarrier<JSFunction> m_readableStreamToArrayBufferResolve;
- mutable WriteBarrier<JSFunction> m_readableStreamToTextResolve;
+ mutable WriteBarrier<JSFunction> m_readableStreamToText;
+ mutable WriteBarrier<JSFunction> m_readableStreamToBlob;
+ mutable WriteBarrier<JSFunction> m_readableStreamToJSON;
private:
void addBuiltinGlobals(JSC::VM&);
diff --git a/src/javascript/jsc/bindings/bindings.zig b/src/javascript/jsc/bindings/bindings.zig
index 4a01c702f..31f9b497d 100644
--- a/src/javascript/jsc/bindings/bindings.zig
+++ b/src/javascript/jsc/bindings/bindings.zig
@@ -1620,12 +1620,30 @@ pub const JSGlobalObject = extern struct {
}
extern fn ZigGlobalObject__readableStreamToArrayBuffer(*JSGlobalObject, JSValue) JSValue;
+ extern fn ZigGlobalObject__readableStreamToText(*JSGlobalObject, JSValue) JSValue;
+ extern fn ZigGlobalObject__readableStreamToJSON(*JSGlobalObject, JSValue) JSValue;
+ extern fn ZigGlobalObject__readableStreamToBlob(*JSGlobalObject, JSValue) JSValue;
pub fn readableStreamToArrayBuffer(this: *JSGlobalObject, value: JSValue) JSValue {
if (comptime is_bindgen) unreachable;
return ZigGlobalObject__readableStreamToArrayBuffer(this, value);
}
+ pub fn readableStreamToText(this: *JSGlobalObject, value: JSValue) JSValue {
+ if (comptime is_bindgen) unreachable;
+ return ZigGlobalObject__readableStreamToText(this, value);
+ }
+
+ pub fn readableStreamToJSON(this: *JSGlobalObject, value: JSValue) JSValue {
+ if (comptime is_bindgen) unreachable;
+ return ZigGlobalObject__readableStreamToJSON(this, value);
+ }
+
+ pub fn readableStreamToBlob(this: *JSGlobalObject, value: JSValue) JSValue {
+ if (comptime is_bindgen) unreachable;
+ return ZigGlobalObject__readableStreamToBlob(this, value);
+ }
+
pub const Extern = [_][]const u8{
"bunVM",
"putCachedObject",
diff --git a/src/javascript/jsc/bindings/builtins/js/ReadableStream.js b/src/javascript/jsc/bindings/builtins/js/ReadableStream.js
index 9695b0d82..4874067c3 100644
--- a/src/javascript/jsc/bindings/builtins/js/ReadableStream.js
+++ b/src/javascript/jsc/bindings/builtins/js/ReadableStream.js
@@ -96,7 +96,6 @@ function readableStreamToArray(stream) {
return null;
}
var reader = stream.getReader();
-
var manyResult = reader.readMany();
async function processManyResult(result) {
@@ -133,6 +132,45 @@ function readableStreamToArray(stream) {
}
@globalPrivate
+function readableStreamToText(stream) {
+ "use strict";
+
+ // TODO: optimize this to skip the extra ArrayBuffer
+ return globalThis.Bun.readableStreamToArrayBuffer(stream).@then(function(arrayBuffer) {
+ return new globalThis.TextDecoder().decode(arrayBuffer);
+ });
+}
+
+@globalPrivate
+function readableStreamToJSON(stream) {
+ "use strict";
+
+ // TODO: optimize this to skip the extra ArrayBuffer
+ return globalThis.Bun.readableStreamToArrayBuffer(stream).@then(function(arrayBuffer) {
+ return globalThis.JSON.parse(new globalThis.TextDecoder().decode(arrayBuffer));
+ });
+}
+
+@globalPrivate
+function readableStreamToBlob(stream) {
+ "use strict";
+
+
+ const array = @readableStreamToArray(stream);
+ if (array === null) {
+ return new globalThis.Blob();
+ }
+
+ return array.@then(function(chunks) {
+ if (chunks === null || chunks.length === 0) {
+ return new globalThis.Blob();
+ }
+
+ return new globalThis.Blob(chunks);
+ });
+}
+
+@globalPrivate
function readableStreamToArrayPublic(stream) {
"use strict";
diff --git a/src/javascript/jsc/bindings/builtins/js/ReadableStreamDefaultReader.js b/src/javascript/jsc/bindings/builtins/js/ReadableStreamDefaultReader.js
index 118376ffb..bdeaf1919 100644
--- a/src/javascript/jsc/bindings/builtins/js/ReadableStreamDefaultReader.js
+++ b/src/javascript/jsc/bindings/builtins/js/ReadableStreamDefaultReader.js
@@ -80,6 +80,12 @@ function readMany()
if (length > 0) {
+ for (var i = 0; i < values.length; i++) {
+ const buf = values[i];
+ if (!(@ArrayBuffer.@isView(buf) || buf instanceof @ArrayBuffer)) {
+ values[i] = new @Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
+ }
+ }
@resetQueue(@getByIdDirectPrivate(controller, "queue"));
@@ -102,6 +108,13 @@ function readMany()
var queue = @getByIdDirectPrivate(controller, "queue");
var value = [result.value].concat(queue.content.toArray(false));
+ for (var i = 0; i < value.length; i++) {
+ const buf = value[i];
+ if (!(@ArrayBuffer.@isView(buf) || buf instanceof @ArrayBuffer)) {
+ value[i] = new @Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
+ }
+ }
+
var size = queue.size;
@resetQueue(queue);
diff --git a/src/javascript/jsc/webcore/response.zig b/src/javascript/jsc/webcore/response.zig
index 19134b357..8d585f93c 100644
--- a/src/javascript/jsc/webcore/response.zig
+++ b/src/javascript/jsc/webcore/response.zig
@@ -3811,7 +3811,7 @@ pub const Body = struct {
pub const PendingValue = struct {
promise: ?JSValue = null,
- readable: JSValue = JSValue.zero,
+ stream: ?JSC.WebCore.ReadableStream = null,
global: *JSGlobalObject,
task: ?*anyopaque = null,
@@ -3825,14 +3825,47 @@ pub const Body = struct {
pub fn setPromise(value: *PendingValue, globalThis: *JSC.JSGlobalObject, action: Action) JSValue {
value.action = action;
- var promise = JSC.JSPromise.create(globalThis);
- const promise_value = promise.asValue(globalThis);
- value.promise = promise_value;
- if (value.onPull) |onPull| {
- value.onPull = null;
- onPull(value.task.?);
+
+ if (value.stream) |*stream| {
+ // switch (stream.ptr) {
+ // .JavaScript
+ // }
+ switch (action) {
+ .getText, .getJSON, .getBlob, .getArrayBuffer => {
+ switch (stream.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),
+ else => unreachable,
+ };
+ value.promise.?.ensureStillAlive();
+ stream.value.unprotect();
+
+ // js now owns the memory
+ value.stream = null;
+
+ return value.promise.?;
+ },
+ .none => {},
+ }
+ }
+
+ {
+ var promise = JSC.JSPromise.create(globalThis);
+ const promise_value = promise.asValue(globalThis);
+ value.promise = promise_value;
+
+ if (value.onPull) |onPull| {
+ value.onPull = null;
+ onPull(value.task.?);
+ }
+ return promise_value;
}
- return promise_value;
}
pub const Action = enum {
@@ -3861,9 +3894,28 @@ pub const Body = struct {
pub const empty = Value{ .Empty = .{} };
+ pub fn fromReadableStream(stream: JSC.WebCore.ReadableStream, globalThis: *JSGlobalObject) Value {
+ if (stream.isLocked(globalThis)) {
+ return .{ .Error = ZigString.init("Cannot use a locked ReadableStream").toErrorInstance(globalThis) };
+ }
+
+ stream.value.protect();
+ return .{
+ .Locked = .{
+ .stream = stream,
+ .global = globalThis,
+ },
+ };
+ }
+
pub fn resolve(this: *Value, new: *Value, global: *JSGlobalObject) void {
if (this.* == .Locked) {
- var locked = this.Locked;
+ var locked = &this.Locked;
+ if (locked.stream) |stream| {
+ stream.done();
+ locked.stream = null;
+ }
+
if (locked.callback) |callback| {
locked.callback = null;
callback(locked.task.?, new);
@@ -3936,6 +3988,11 @@ pub const Body = struct {
locked.promise = null;
}
+ if (locked.stream) |stream| {
+ stream.done();
+ locked.stream = null;
+ }
+
this.* = .{ .Error = error_instance };
if (locked.callback) |callback| {
locked.callback = null;
@@ -3967,6 +4024,10 @@ 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();
+ }
+
this.Locked.deinit = true;
return;
}
@@ -4051,10 +4112,28 @@ pub const Body = struct {
} else |_| {}
}
- // if (value.as(JSC.WebCore.ReadableStream)) |readable| {
- // body.value = Body.Value.fromReadableStream(ctx, readable);
- // return body;
- // }
+ if (JSC.WebCore.ReadableStream.fromJS(value, ctx)) |readable| {
+ switch (readable.ptr) {
+ .Blob => |blob| {
+ body.value = .{
+ .Blob = Blob.initWithStore(blob.store, ctx),
+ };
+ blob.store.ref();
+
+ readable.done();
+
+ if (!blob.done) {
+ blob.done = true;
+ blob.deinit();
+ }
+ return body;
+ },
+ else => {},
+ }
+
+ body.value = Body.Value.fromReadableStream(readable, ctx);
+ return body;
+ }
body.value = .{
.Blob = Blob.fromJS(ctx.ptr(), value, true, false) catch |err| {
@@ -4451,7 +4530,7 @@ fn BlobInterface(comptime Type: type) type {
_: []const js.JSValueRef,
_: js.ExceptionRef,
) js.JSValueRef {
- var value = this.getBodyValue();
+ var value: *Body.Value = this.getBodyValue();
if (value.* == .Locked) {
return value.Locked.setPromise(ctx.ptr(), .getText).asObjectRef();
}
@@ -4468,7 +4547,7 @@ fn BlobInterface(comptime Type: type) type {
_: []const js.JSValueRef,
exception: js.ExceptionRef,
) js.JSValueRef {
- var value = this.getBodyValue();
+ var value: *Body.Value = this.getBodyValue();
if (value.* == .Locked) {
return value.Locked.setPromise(ctx.ptr(), .getJSON).asObjectRef();
}
@@ -4484,7 +4563,7 @@ fn BlobInterface(comptime Type: type) type {
_: []const js.JSValueRef,
_: js.ExceptionRef,
) js.JSValueRef {
- var value = this.getBodyValue();
+ var value: *Body.Value = this.getBodyValue();
if (value.* == .Locked) {
return value.Locked.setPromise(ctx.ptr(), .getArrayBuffer).asObjectRef();
@@ -4502,7 +4581,8 @@ fn BlobInterface(comptime Type: type) type {
_: []const js.JSValueRef,
_: js.ExceptionRef,
) js.JSValueRef {
- var value = this.getBodyValue();
+ var value: *Body.Value = this.getBodyValue();
+
if (value.* == .Locked) {
return value.Locked.setPromise(ctx.ptr(), .getBlob).asObjectRef();
}
diff --git a/src/javascript/jsc/webcore/streams.zig b/src/javascript/jsc/webcore/streams.zig
index fea962e1e..bd0cfb133 100644
--- a/src/javascript/jsc/webcore/streams.zig
+++ b/src/javascript/jsc/webcore/streams.zig
@@ -51,6 +51,10 @@ pub const ReadableStream = struct {
value: JSValue,
ptr: Handle,
+ pub fn done(this: *const ReadableStream) void {
+ this.value.unprotect();
+ }
+
pub const Tag = enum(i32) {
Invalid = -1,