Compare commits

...

14 Commits

Author SHA1 Message Date
Meghan Denny
f9dcf55e28 Merge branch 'main' into nektro-patch-14462 2025-04-22 16:40:43 -08:00
Meghan Denny
41a667d7cc catch strays 2025-03-10 16:57:23 -07:00
Meghan Denny
ad549bce6e go back to ints 2025-03-10 14:43:50 -07:00
Meghan Denny
6540359766 undo 2025-03-07 01:38:34 -08:00
Meghan Denny
5c4eee5716 tidy 2025-03-06 23:51:20 -08:00
Meghan Denny
2ffc4596f3 make Bun.inspect have the correct value 2025-03-06 23:51:15 -08:00
Meghan Denny
a8cdd45a19 fix test failure 2025-03-06 23:45:44 -08:00
Meghan Denny
04c627467c node: add test-webstream-encoding-inspect.js 2025-03-06 22:07:09 -08:00
Meghan Denny
27cac7ce70 add custom inspect for TextDecoderStream 2025-03-06 22:06:39 -08:00
Meghan Denny
1836a05470 add custom inspect for TextEncoderStream 2025-03-06 22:05:54 -08:00
Meghan Denny
4eba34f816 tidy 2025-03-06 22:05:08 -08:00
Meghan Denny
e58736635e add custom inspect function to WritableStream 2025-03-06 21:25:08 -08:00
Meghan Denny
faedf7e4bd add custom inspect function for ReadableStream 2025-03-06 21:23:14 -08:00
Meghan Denny
2fab5d3d16 zig: console: name this field better 2025-03-06 17:07:03 -08:00
17 changed files with 251 additions and 98 deletions

View File

@@ -939,7 +939,7 @@ pub fn format2(
tag = ConsoleObject.Formatter.Tag.get(this_value, global);
if (tag.tag == .String and fmt.remaining_values.len > 0) {
tag.tag = .{ .StringPossiblyFormatted = {} };
tag.tag = .StringPossiblyFormatted;
}
try fmt.format(tag, Writer, writer, this_value, global, true);
@@ -961,7 +961,7 @@ pub fn format2(
any = true;
tag = ConsoleObject.Formatter.Tag.get(this_value, global);
if (tag.tag == .String and fmt.remaining_values.len > 0) {
tag.tag = .{ .StringPossiblyFormatted = {} };
tag.tag = .StringPossiblyFormatted;
}
try fmt.format(tag, Writer, writer, this_value, global, false);
@@ -1178,7 +1178,7 @@ pub const Formatter = struct {
return @as(Tag, this);
}
},
cell: JSValue.JSType = JSValue.JSType.Cell,
type: JSValue.JSType = .Cell,
};
pub fn get(value: JSValue, globalThis: *JSGlobalObject) Result {
@@ -1194,44 +1194,44 @@ pub const Formatter = struct {
pub fn getAdvanced(value: JSValue, globalThis: *JSGlobalObject, opts: Options) Result {
switch (@intFromEnum(value)) {
0, 0xa => return Result{
.tag = .{ .Undefined = {} },
.tag = .Undefined,
},
0x2 => return Result{
.tag = .{ .Null = {} },
.tag = .Null,
},
else => {},
}
if (value.isInt32()) {
return .{
.tag = .{ .Integer = {} },
.tag = .Integer,
};
} else if (value.isNumber()) {
return .{
.tag = .{ .Double = {} },
.tag = .Double,
};
} else if (value.isBoolean()) {
return .{
.tag = .{ .Boolean = {} },
.tag = .Boolean,
};
}
if (!value.isCell())
return .{
.tag = .{ .NativeCode = {} },
.tag = .NativeCode,
};
const js_type = value.jsType();
if (js_type.isHidden()) return .{
.tag = .{ .NativeCode = {} },
.cell = js_type,
.tag = .NativeCode,
.type = js_type,
};
if (js_type == .Cell) {
return .{
.tag = .{ .NativeCode = {} },
.cell = js_type,
.tag = .NativeCode,
.type = js_type,
};
}
@@ -1246,7 +1246,7 @@ pub const Formatter = struct {
.this = value,
},
},
.cell = js_type,
.type = js_type,
};
}
}
@@ -1255,8 +1255,8 @@ pub const Formatter = struct {
if (js_type == .DOMWrapper) {
return .{
.tag = .{ .Private = {} },
.cell = js_type,
.tag = .Private,
.type = js_type,
};
}
@@ -1265,8 +1265,8 @@ pub const Formatter = struct {
if (js_type != .Object and js_type != .ProxyObject and value.isCallable()) {
if (value.isClass(globalThis)) {
return .{
.tag = .{ .Class = {} },
.cell = js_type,
.tag = .Class,
.type = js_type,
};
}
@@ -1276,7 +1276,7 @@ pub const Formatter = struct {
// ideally, we would print [Function: namespace] { ... } on all functions, internal and js.
// what we'll do later is rid of .Function and .Class and handle the prefix in the .Object formatter
.tag = if (js_type == .InternalFunction) .Object else .Function,
.cell = js_type,
.type = js_type,
};
}
@@ -1288,8 +1288,8 @@ pub const Formatter = struct {
);
}
return .{
.tag = .{ .GlobalObject = {} },
.cell = js_type,
.tag = .GlobalObject,
.type = js_type,
};
}
@@ -1306,7 +1306,7 @@ pub const Formatter = struct {
JSValue.isSameValue(typeof_symbol, JSValue.symbolFor(globalThis, &react_element_transitional), globalThis) or
JSValue.isSameValue(typeof_symbol, JSValue.symbolFor(globalThis, &react_fragment), globalThis))
{
return .{ .tag = .{ .JSX = {} }, .cell = js_type };
return .{ .tag = .JSX, .type = js_type };
}
}
}
@@ -1404,7 +1404,7 @@ pub const Formatter = struct {
else => .JSON,
},
.cell = js_type,
.type = js_type,
};
}
};
@@ -1927,7 +1927,7 @@ pub const Formatter = struct {
.disable_inspect_custom = this.disable_inspect_custom,
});
if (tag.cell.isHidden()) return;
if (tag.type.isHidden()) return;
if (ctx.i == 0) {
handleFirstProperty(ctx, globalThis, ctx.parent);
} else {
@@ -2010,7 +2010,7 @@ pub const Formatter = struct {
);
}
if (tag.cell.isStringLike()) {
if (tag.type.isStringLike()) {
if (comptime enable_ansi_colors) {
writer.writeAll(comptime Output.prettyFmt("<r><green>", true));
}
@@ -2018,7 +2018,7 @@ pub const Formatter = struct {
this.format(tag, Writer, ctx.writer, value, globalThis, enable_ansi_colors) catch {}; // TODO:
if (tag.cell.isStringLike()) {
if (tag.type.isStringLike()) {
if (comptime enable_ansi_colors) {
writer.writeAll(comptime Output.prettyFmt("<r>", true));
}
@@ -2430,7 +2430,7 @@ pub const Formatter = struct {
try this.format(tag, Writer, writer_, element, this.globalThis, enable_ansi_colors);
if (tag.cell.isStringLike()) {
if (tag.type.isStringLike()) {
if (comptime enable_ansi_colors) {
writer.writeAll(comptime Output.prettyFmt("<r>", true));
}
@@ -2495,7 +2495,7 @@ pub const Formatter = struct {
try this.format(tag, Writer, writer_, element, this.globalThis, enable_ansi_colors);
if (tag.cell.isStringLike()) {
if (tag.type.isStringLike()) {
if (comptime enable_ansi_colors) {
writer.writeAll(comptime Output.prettyFmt("<r>", true));
}
@@ -3035,10 +3035,10 @@ pub const Formatter = struct {
.disable_inspect_custom = this.disable_inspect_custom,
});
if (_tag.cell == .Symbol) {} else if (_tag.cell.isStringLike()) {
if (_tag.type == .Symbol) {} else if (_tag.type.isStringLike()) {
try type_value.toZigString(&tag_name_str, this.globalThis);
is_tag_kind_primitive = true;
} else if (_tag.cell.isObject() or type_value.isCallable()) {
} else if (_tag.type.isObject() or type_value.isCallable()) {
type_value.getNameProperty(this.globalThis, &tag_name_str);
if (tag_name_str.len == 0) {
tag_name_str = ZigString.init("NoName");
@@ -3112,7 +3112,7 @@ pub const Formatter = struct {
.disable_inspect_custom = this.disable_inspect_custom,
});
if (tag.cell.isHidden()) continue;
if (tag.type.isHidden()) continue;
if (needs_space) writer.space();
needs_space = false;
@@ -3122,7 +3122,7 @@ pub const Formatter = struct {
.{prop.trunc(128)},
);
if (tag.cell.isStringLike()) {
if (tag.type.isStringLike()) {
if (comptime enable_ansi_colors) {
writer.writeAll(comptime Output.prettyFmt("<r><green>", true));
}
@@ -3130,7 +3130,7 @@ pub const Formatter = struct {
try this.format(tag, Writer, writer_, property_value, this.globalThis, enable_ansi_colors);
if (tag.cell.isStringLike()) {
if (tag.type.isStringLike()) {
if (comptime enable_ansi_colors) {
writer.writeAll(comptime Output.prettyFmt("<r>", true));
}
@@ -3507,11 +3507,11 @@ pub const Formatter = struct {
// it _should_ limit the stack usage because each version of the
// function will be relatively small
switch (result.tag.tag()) {
inline else => |tag| try this.printAs(tag, Writer, writer, value, result.cell, enable_ansi_colors),
inline else => |tag| try this.printAs(tag, Writer, writer, value, result.type, enable_ansi_colors),
.CustomFormattedObject => {
this.custom_formatted_object = result.tag.CustomFormattedObject;
try this.printAs(.CustomFormattedObject, Writer, writer, value, result.cell, enable_ansi_colors);
try this.printAs(.CustomFormattedObject, Writer, writer, value, result.type, enable_ansi_colors);
},
}
}

View File

@@ -1541,6 +1541,12 @@ JSC::JSObject* Bun::createInvalidThisError(JSC::JSGlobalObject* globalObject, JS
return Bun::createError(globalObject, Bun::ErrorCode::ERR_INVALID_THIS, builder.toString());
}
EncodedJSValue Bun::throwInvalidThisError(JSC::JSGlobalObject* globalObject, JSC::ThrowScope& scope, const ASCIILiteral expectedTypeName, JSC::JSValue actualThisValue)
{
scope.throwException(globalObject, createInvalidThisError(globalObject, actualThisValue, expectedTypeName));
return {};
}
JSC::EncodedJSValue Bun::throwError(JSC::JSGlobalObject* globalObject, JSC::ThrowScope& scope, Bun::ErrorCode code, const WTF::String& message)
{
scope.throwException(globalObject, createError(globalObject, code, message));

View File

@@ -56,6 +56,7 @@ JSC::JSObject* createError(VM& vm, Zig::GlobalObject* globalObject, ErrorCode co
JSC::JSValue toJS(JSC::JSGlobalObject*, ErrorCode);
JSObject* createInvalidThisError(JSGlobalObject* globalObject, JSValue thisValue, const ASCIILiteral typeName);
JSObject* createInvalidThisError(JSGlobalObject* globalObject, const String& message);
EncodedJSValue throwInvalidThisError(JSC::JSGlobalObject* globalObject, JSC::ThrowScope& scope, const ASCIILiteral expectedTypeName, JSC::JSValue actualThisValue);
JSC_DECLARE_HOST_FUNCTION(jsFunctionMakeErrorWithCode);

View File

@@ -40,6 +40,8 @@
#include <wtf/PointerPreparations.h>
#include "ZigGeneratedClasses.h"
#include "JavaScriptCore/BuiltinNames.h"
#include "ErrorCode.h"
#include "JSReadableByteStreamController.h"
namespace WebCore {
using namespace JSC;
@@ -118,7 +120,6 @@ static const HashTableValue JSReadableStreamPrototypeTableValues[] = {
{ "pipeTo"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, readableStreamPipeToCodeGenerator, 1 } },
{ "pipeThrough"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, readableStreamPipeThroughCodeGenerator, 2 } },
{ "tee"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, readableStreamTeeCodeGenerator, 0 } },
};
const ClassInfo JSReadableStreamPrototype::s_info = { "ReadableStream"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSReadableStreamPrototype) };
@@ -168,6 +169,23 @@ static JSC_DEFINE_CUSTOM_GETTER(JSReadableStreamPrototype__disturbedGetterWrap,
return JSValue::encode(jsBoolean(thisObject->disturbed()));
}
// keep this in sync with src/codegen/replacements.ts
static const ASCIILiteral stateNames[] = { ""_s, "closed"_s, "closing"_s, "errored"_s, "readable"_s, "waiting"_s, "writable"_s };
JSC_DEFINE_HOST_FUNCTION(JSReadableStreamPrototype__customInspect, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
{
auto& vm = JSC::getVM(lexicalGlobalObject);
auto scope = DECLARE_THROW_SCOPE(vm);
JSReadableStream* thisObject = jsDynamicCast<JSReadableStream*>(callFrame->thisValue());
if (!thisObject) return JSValue::encode(jsUndefined());
auto& builtinNames = WebCore::builtinNames(vm);
auto locked = thisObject->get(lexicalGlobalObject, Identifier::fromString(vm, "locked"_s)).toBoolean(lexicalGlobalObject) ? "true"_s : "false"_s;
auto state = stateNames[thisObject->getDirect(vm, builtinNames.statePrivateName()).toInt32(lexicalGlobalObject)];
auto supportsBYOB = thisObject->getDirect(vm, builtinNames.readableStreamControllerPrivateName()).inherits<JSReadableByteStreamController>() ? "true"_s : "false"_s;
return JSValue::encode(jsString(vm, WTF::makeString("ReadableStream { locked: "_s, locked, ", state: '"_s, state, "', supportsBYOB: "_s, supportsBYOB, " }"_s)));
}
void JSReadableStreamPrototype::finishCreation(VM& vm)
{
Base::finishCreation(vm);
@@ -181,6 +199,7 @@ void JSReadableStreamPrototype::finishCreation(VM& vm)
this->putDirectBuiltinFunction(vm, globalObject(), vm.propertyNames->asyncIteratorSymbol, readableStreamLazyAsyncIteratorCodeGenerator(vm), JSC::PropertyAttribute::DontDelete | 0);
this->putDirectBuiltinFunction(vm, globalObject(), vm.propertyNames->builtinNames().valuesPublicName(), readableStreamValuesCodeGenerator(vm), JSC::PropertyAttribute::DontDelete | 0);
JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
this->putDirect(vm, Identifier::fromUid(vm.symbolRegistry().symbolForKey("nodejs.util.inspect.custom"_s)), JSFunction::create(vm, globalObject(), 0, String("[nodejs.util.inspect.custom]"_s), JSReadableStreamPrototype__customInspect, ImplementationVisibility::Private), PropertyAttribute::Builtin | 0);
}
const ClassInfo JSReadableStream::s_info = { "ReadableStream"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSReadableStream) };

View File

@@ -38,13 +38,17 @@
#include <JavaScriptCore/SubspaceInlines.h>
#include <wtf/GetPtr.h>
#include <wtf/PointerPreparations.h>
#include "ErrorCode.h"
namespace WebCore {
using namespace JSC;
extern "C" BunString Bun__inspect(JSC::JSGlobalObject* globalObject, JSValue value);
// Attributes
static JSC_DECLARE_CUSTOM_GETTER(jsTextDecoderStreamConstructor);
static JSC_DECLARE_HOST_FUNCTION(JSTextDecoderStreamPrototype__customInspect);
class JSTextDecoderStreamPrototype final : public JSC::JSNonFinalObject {
public:
@@ -120,6 +124,7 @@ void JSTextDecoderStreamPrototype::finishCreation(VM& vm)
Base::finishCreation(vm);
reifyStaticProperties(vm, JSTextDecoderStream::info(), JSTextDecoderStreamPrototypeTableValues, *this);
JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
this->putDirect(vm, Identifier::fromUid(vm.symbolRegistry().symbolForKey("nodejs.util.inspect.custom"_s)), JSFunction::create(vm, globalObject(), 0, String("[nodejs.util.inspect.custom]"_s), JSTextDecoderStreamPrototype__customInspect, ImplementationVisibility::Private), PropertyAttribute::Builtin | 0);
}
const ClassInfo JSTextDecoderStream::s_info = { "TextDecoderStream"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSTextDecoderStream) };
@@ -162,6 +167,23 @@ JSC_DEFINE_CUSTOM_GETTER(jsTextDecoderStreamConstructor, (JSGlobalObject * lexic
return JSValue::encode(JSTextDecoderStream::getConstructor(vm, prototype->globalObject()));
}
JSC_DEFINE_HOST_FUNCTION(JSTextDecoderStreamPrototype__customInspect, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
{
auto& vm = JSC::getVM(lexicalGlobalObject);
auto scope = DECLARE_THROW_SCOPE(vm);
JSTextDecoderStream* thisObject = jsDynamicCast<JSTextDecoderStream*>(callFrame->thisValue());
if (!thisObject) return Bun::throwInvalidThisError(lexicalGlobalObject, scope, "TextDecoderStream", callFrame->thisValue());
auto& builtinNames = WebCore::builtinNames(vm);
auto encoding = thisObject->getDirect(vm, builtinNames.encodingPrivateName()).toWTFString(lexicalGlobalObject);
auto fatal = thisObject->getDirect(vm, builtinNames.fatalPrivateName()).toBoolean(lexicalGlobalObject) ? "true"_s : "false"_s;
auto ignoreBOM = thisObject->getDirect(vm, builtinNames.ignoreBOMPrivateName()).toBoolean(lexicalGlobalObject) ? "true"_s : "false"_s;
auto transform = thisObject->getDirect(vm, builtinNames.textDecoderStreamTransformPrivateName());
auto readable = Bun__inspect(lexicalGlobalObject, transform.getObject()->getDirect(vm, builtinNames.readablePrivateName())).transferToWTFString();
auto writable = Bun__inspect(lexicalGlobalObject, transform.getObject()->getDirect(vm, builtinNames.writablePrivateName())).transferToWTFString();
return JSValue::encode(jsString(vm, WTF::makeString("TextDecoderStream {\n encoding: '"_s, encoding, "',\n fatal: "_s, fatal, ",\n ignoreBOM: "_s, ignoreBOM, ",\n readable: "_s, readable, ",\n writable: "_s, writable, "\n}"_s)));
}
JSC::GCClient::IsoSubspace* JSTextDecoderStream::subspaceForImpl(JSC::VM& vm)
{
return WebCore::subspaceForImpl<JSTextDecoderStream, UseCustomHeapCellType::No>(

View File

@@ -38,13 +38,17 @@
#include <JavaScriptCore/SubspaceInlines.h>
#include <wtf/GetPtr.h>
#include <wtf/PointerPreparations.h>
#include "ErrorCode.h"
namespace WebCore {
using namespace JSC;
extern "C" BunString Bun__inspect(JSC::JSGlobalObject* globalObject, JSValue value);
// Attributes
static JSC_DECLARE_CUSTOM_GETTER(jsTextEncoderStreamConstructor);
static JSC_DECLARE_HOST_FUNCTION(JSTextEncoderStreamPrototype__customInspect);
class JSTextEncoderStreamPrototype final : public JSC::JSNonFinalObject {
public:
@@ -118,6 +122,7 @@ void JSTextEncoderStreamPrototype::finishCreation(VM& vm)
Base::finishCreation(vm);
reifyStaticProperties(vm, JSTextEncoderStream::info(), JSTextEncoderStreamPrototypeTableValues, *this);
JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
this->putDirect(vm, Identifier::fromUid(vm.symbolRegistry().symbolForKey("nodejs.util.inspect.custom"_s)), JSFunction::create(vm, globalObject(), 0, String("[nodejs.util.inspect.custom]"_s), JSTextEncoderStreamPrototype__customInspect, ImplementationVisibility::Private), PropertyAttribute::Builtin | 0);
}
const ClassInfo JSTextEncoderStream::s_info = { "TextEncoderStream"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSTextEncoderStream) };
@@ -160,6 +165,21 @@ JSC_DEFINE_CUSTOM_GETTER(jsTextEncoderStreamConstructor, (JSGlobalObject * lexic
return JSValue::encode(JSTextEncoderStream::getConstructor(vm, prototype->globalObject()));
}
JSC_DEFINE_HOST_FUNCTION(JSTextEncoderStreamPrototype__customInspect, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
{
auto& vm = JSC::getVM(lexicalGlobalObject);
auto scope = DECLARE_THROW_SCOPE(vm);
JSTextEncoderStream* thisObject = jsDynamicCast<JSTextEncoderStream*>(callFrame->thisValue());
if (!thisObject) return Bun::throwInvalidThisError(lexicalGlobalObject, scope, "TextEncoderStream", callFrame->thisValue());
auto& builtinNames = WebCore::builtinNames(vm);
auto encoding = "utf-8"_s;
auto transform = thisObject->getDirect(vm, builtinNames.textEncoderStreamTransformPrivateName());
auto readable = Bun__inspect(lexicalGlobalObject, transform.getObject()->getDirect(vm, builtinNames.readablePrivateName())).transferToWTFString();
auto writable = Bun__inspect(lexicalGlobalObject, transform.getObject()->getDirect(vm, builtinNames.writablePrivateName())).transferToWTFString();
return JSValue::encode(jsString(vm, WTF::makeString("TextEncoderStream {\n encoding: '"_s, encoding, "',\n readable: "_s, readable, ",\n writable: "_s, writable, "\n}"_s)));
}
JSC::GCClient::IsoSubspace* JSTextEncoderStream::subspaceForImpl(JSC::VM& vm)
{
return WebCore::subspaceForImpl<JSTextEncoderStream, UseCustomHeapCellType::No>(

View File

@@ -46,6 +46,7 @@
#include <wtf/GetPtr.h>
#include <wtf/PointerPreparations.h>
#include <wtf/URL.h>
#include "ErrorCode.h"
namespace WebCore {
using namespace JSC;
@@ -55,6 +56,7 @@ using namespace JSC;
static JSC_DECLARE_HOST_FUNCTION(jsWritableStreamPrototypeFunction_abort);
static JSC_DECLARE_HOST_FUNCTION(jsWritableStreamPrototypeFunction_close);
static JSC_DECLARE_HOST_FUNCTION(jsWritableStreamPrototypeFunction_getWriter);
static JSC_DECLARE_HOST_FUNCTION(jsWritableStreamPrototypeFunction_customInspect);
// Attributes
@@ -154,6 +156,7 @@ void JSWritableStreamPrototype::finishCreation(VM& vm)
Base::finishCreation(vm);
reifyStaticProperties(vm, JSWritableStream::info(), JSWritableStreamPrototypeTableValues, *this);
JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
this->putDirect(vm, Identifier::fromUid(vm.symbolRegistry().symbolForKey("nodejs.util.inspect.custom"_s)), JSFunction::create(vm, globalObject(), 0, String("[nodejs.util.inspect.custom]"_s), jsWritableStreamPrototypeFunction_customInspect, ImplementationVisibility::Private), PropertyAttribute::Builtin | 0);
}
const ClassInfo JSWritableStream::s_info = { "WritableStream"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSWritableStream) };
@@ -259,6 +262,22 @@ JSC_DEFINE_HOST_FUNCTION(jsWritableStreamPrototypeFunction_getWriter, (JSGlobalO
return IDLOperation<JSWritableStream>::call<jsWritableStreamPrototypeFunction_getWriterBody>(*lexicalGlobalObject, *callFrame, "getWriter");
}
// keep this in sync with src/codegen/replacements.ts
static const ASCIILiteral stateNames[] = { ""_s, "closed"_s, "closing"_s, "errored"_s, "readable"_s, "waiting"_s, "writable"_s, "erroring"_s };
JSC_DEFINE_HOST_FUNCTION(jsWritableStreamPrototypeFunction_customInspect, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
{
auto& vm = JSC::getVM(lexicalGlobalObject);
auto scope = DECLARE_THROW_SCOPE(vm);
JSWritableStream* thisObject = jsDynamicCast<JSWritableStream*>(callFrame->thisValue());
if (!thisObject) return JSValue::encode(jsUndefined());
auto& builtinNames = WebCore::builtinNames(vm);
auto locked = !thisObject->get(lexicalGlobalObject, WebCore::builtinNames(vm).writerPrivateName()).isUndefined() ? "true"_s : "false"_s;
auto state = stateNames[thisObject->wrapped().internalWritableStream().guardedObject().getObject()->getDirect(vm, builtinNames.statePrivateName()).toInt32(lexicalGlobalObject)];
return JSValue::encode(jsString(vm, WTF::makeString("WritableStream { locked: "_s, locked, ", state: '"_s, state, "' }"_s)));
}
JSC::GCClient::IsoSubspace* JSWritableStream::subspaceForImpl(JSC::VM& vm)
{
return WebCore::subspaceForImpl<JSWritableStream, UseCustomHeapCellType::No>(

View File

@@ -63,7 +63,7 @@ export function registerNativeCall(
call_type: NativeCallType,
filename: string,
symbol: string,
create_fn_len: null | number,
create_fn_len: null | number | undefined,
) {
const resolved_filename = resolveNativeFileId(call_type, filename);

View File

@@ -122,12 +122,15 @@ export const define: Record<string, string> = {
"process.env.NODE_ENV": JSON.stringify(debug ? "development" : "production"),
"IS_BUN_DEVELOPMENT": String(debug),
// keep this in sync with src/bun.js/bindings/webcore/JSReadableStream.cpp customInspect
// keep this in sync with src/bun.js/bindings/webcore/JSWritableStream.cpp customInspect
$streamClosed: "1",
$streamClosing: "2",
$streamErrored: "3",
$streamReadable: "4",
$streamWaiting: "5",
$streamWritable: "6",
$streamErroring: "7",
"process.platform": JSON.stringify(Bun.env.TARGET_PLATFORM ?? process.platform),
"process.arch": JSON.stringify(Bun.env.TARGET_ARCH ?? process.arch),

13
src/js/builtins.d.ts vendored
View File

@@ -509,12 +509,13 @@ declare function $strategy(): TODO;
declare function $strategyHWM(): TODO;
declare function $strategySizeAlgorithm(): TODO;
declare function $stream(): TODO;
declare function $streamClosed(): TODO;
declare function $streamClosing(): TODO;
declare function $streamErrored(): TODO;
declare function $streamReadable(): TODO;
declare function $streamWaiting(): TODO;
declare function $streamWritable(): TODO;
const $streamClosed: unknown;
const $streamClosing: unknown;
const $streamErrored: unknown;
const $streamErroring: unknown;
const $streamReadable: unknown;
const $streamWaiting: unknown;
const $streamWritable: unknown;
declare function $structuredCloneForStream(): TODO;
declare function $syscall(): TODO;
declare function $textDecoderStreamDecoder(): TODO;

View File

@@ -262,7 +262,7 @@ export function readableStreamPipeToWritableStream(
pipeState,
() => {
const shouldAbortDestination =
!pipeState.preventAbort && $getByIdDirectPrivate(pipeState.destination, "state") === "writable";
!pipeState.preventAbort && $getByIdDirectPrivate(pipeState.destination, "state") === $streamWritable;
const promiseDestination = shouldAbortDestination
? $writableStreamAbort(pipeState.destination, reason)
: Promise.$resolve();
@@ -381,7 +381,7 @@ export function pipeToErrorsMustBePropagatedBackward(pipeState) {
}
$pipeToShutdown(pipeState, error);
};
if ($getByIdDirectPrivate(pipeState.destination, "state") === "errored") {
if ($getByIdDirectPrivate(pipeState.destination, "state") === $streamErrored) {
action();
return;
}
@@ -410,7 +410,7 @@ export function pipeToClosingMustBePropagatedForward(pipeState) {
export function pipeToClosingMustBePropagatedBackward(pipeState) {
if (
!$writableStreamCloseQueuedOrInFlight(pipeState.destination) &&
$getByIdDirectPrivate(pipeState.destination, "state") !== "closed"
$getByIdDirectPrivate(pipeState.destination, "state") !== $streamClosed
)
return;
@@ -445,7 +445,7 @@ export function pipeToShutdownWithAction(pipeState, action) {
};
if (
$getByIdDirectPrivate(pipeState.destination, "state") === "writable" &&
$getByIdDirectPrivate(pipeState.destination, "state") === $streamWritable &&
!$writableStreamCloseQueuedOrInFlight(pipeState.destination)
) {
pipeState.pendingReadPromiseCapability.promise.$then(
@@ -473,7 +473,7 @@ export function pipeToShutdown(pipeState) {
};
if (
$getByIdDirectPrivate(pipeState.destination, "state") === "writable" &&
$getByIdDirectPrivate(pipeState.destination, "state") === $streamWritable &&
!$writableStreamCloseQueuedOrInFlight(pipeState.destination)
) {
pipeState.pendingReadPromiseCapability.promise.$then(

View File

@@ -266,7 +266,7 @@ export function transformStreamDefaultControllerTerminate(controller) {
export function transformStreamDefaultSinkWriteAlgorithm(stream, chunk) {
const writable = $getByIdDirectPrivate(stream, "internalWritable");
$assert($getByIdDirectPrivate(writable, "state") === "writable");
$assert($getByIdDirectPrivate(writable, "state") === $streamWritable);
const controller = $getByIdDirectPrivate(stream, "controller");
@@ -278,12 +278,12 @@ export function transformStreamDefaultSinkWriteAlgorithm(stream, chunk) {
backpressureChangePromise.promise.$then(
() => {
const state = $getByIdDirectPrivate(writable, "state");
if (state === "erroring") {
if (state === $streamErroring) {
promiseCapability.reject.$call(undefined, $getByIdDirectPrivate(writable, "storedError"));
return;
}
$assert(state === "writable");
$assert(state === $streamWritable);
$transformStreamDefaultControllerPerformTransform(controller, chunk).$then(
() => {
promiseCapability.resolve();

View File

@@ -43,6 +43,6 @@ export function error(this, e) {
throw $ERR_INVALID_THIS("WritableStreamDefaultController");
const stream = $getByIdDirectPrivate(this, "stream");
if ($getByIdDirectPrivate(stream, "state") !== "writable") return;
if ($getByIdDirectPrivate(stream, "state") !== $streamWritable) return;
$writableStreamDefaultControllerError(this, e);
}

View File

@@ -112,7 +112,7 @@ export function createInternalWritableStreamFromUnderlyingSink(underlyingSink, s
}
export function initializeWritableStreamSlots(stream, underlyingSink) {
$putByIdDirectPrivate(stream, "state", "writable");
$putByIdDirectPrivate(stream, "state", $streamWritable);
$putByIdDirectPrivate(stream, "storedError", undefined);
$putByIdDirectPrivate(stream, "writer", undefined);
$putByIdDirectPrivate(stream, "controller", undefined);
@@ -160,17 +160,17 @@ export function setUpWritableStreamDefaultWriter(writer, stream) {
$putByIdDirectPrivate(writer, "closedPromise", closedPromiseCapability);
const state = $getByIdDirectPrivate(stream, "state");
if (state === "writable") {
if (state === $streamWritable) {
if ($writableStreamCloseQueuedOrInFlight(stream) || !$getByIdDirectPrivate(stream, "backpressure"))
readyPromiseCapability.resolve.$call();
} else if (state === "erroring") {
} else if (state === $streamErroring) {
readyPromiseCapability.reject.$call(undefined, $getByIdDirectPrivate(stream, "storedError"));
$markPromiseAsHandled(readyPromiseCapability.promise);
} else if (state === "closed") {
} else if (state === $streamClosed) {
readyPromiseCapability.resolve.$call();
closedPromiseCapability.resolve.$call();
} else {
$assert(state === "errored");
$assert(state === $streamErrored);
const storedError = $getByIdDirectPrivate(stream, "storedError");
readyPromiseCapability.reject.$call(undefined, storedError);
$markPromiseAsHandled(readyPromiseCapability.promise);
@@ -181,14 +181,14 @@ export function setUpWritableStreamDefaultWriter(writer, stream) {
export function writableStreamAbort(stream, reason) {
const state = $getByIdDirectPrivate(stream, "state");
if (state === "closed" || state === "errored") return Promise.$resolve();
if (state === $streamClosed || state === $streamErrored) return Promise.$resolve();
const pendingAbortRequest = $getByIdDirectPrivate(stream, "pendingAbortRequest");
if (pendingAbortRequest !== undefined) return pendingAbortRequest.promise.promise;
$assert(state === "writable" || state === "erroring");
$assert(state === $streamWritable || state === $streamErroring);
let wasAlreadyErroring = false;
if (state === "erroring") {
if (state === $streamErroring) {
wasAlreadyErroring = true;
reason = undefined;
}
@@ -206,17 +206,17 @@ export function writableStreamAbort(stream, reason) {
export function writableStreamClose(stream) {
const state = $getByIdDirectPrivate(stream, "state");
if (state === "closed" || state === "errored")
if (state === $streamClosed || state === $streamErrored)
return Promise.$reject($makeTypeError("Cannot close a writable stream that is closed or errored"));
$assert(state === "writable" || state === "erroring");
$assert(state === $streamWritable || state === $streamErroring);
$assert(!$writableStreamCloseQueuedOrInFlight(stream));
const closePromiseCapability = $newPromiseCapability(Promise);
$putByIdDirectPrivate(stream, "closeRequest", closePromiseCapability);
const writer = $getByIdDirectPrivate(stream, "writer");
if (writer !== undefined && $getByIdDirectPrivate(stream, "backpressure") && state === "writable")
if (writer !== undefined && $getByIdDirectPrivate(stream, "backpressure") && state === $streamWritable)
$getByIdDirectPrivate(writer, "readyPromise").resolve.$call();
$writableStreamDefaultControllerClose($getByIdDirectPrivate(stream, "controller"));
@@ -226,7 +226,7 @@ export function writableStreamClose(stream) {
export function writableStreamAddWriteRequest(stream) {
$assert($isWritableStreamLocked(stream));
$assert($getByIdDirectPrivate(stream, "state") === "writable");
$assert($getByIdDirectPrivate(stream, "state") === $streamWritable);
const writePromiseCapability = $newPromiseCapability(Promise);
const writeRequests = $getByIdDirectPrivate(stream, "writeRequests");
@@ -243,20 +243,20 @@ export function writableStreamCloseQueuedOrInFlight(stream) {
export function writableStreamDealWithRejection(stream, error) {
const state = $getByIdDirectPrivate(stream, "state");
if (state === "writable") {
if (state === $streamWritable) {
$writableStreamStartErroring(stream, error);
return;
}
$assert(state === "erroring");
$assert(state === $streamErroring);
$writableStreamFinishErroring(stream);
}
export function writableStreamFinishErroring(stream) {
$assert($getByIdDirectPrivate(stream, "state") === "erroring");
$assert($getByIdDirectPrivate(stream, "state") === $streamErroring);
$assert(!$writableStreamHasOperationMarkedInFlight(stream));
$putByIdDirectPrivate(stream, "state", "errored");
$putByIdDirectPrivate(stream, "state", $streamErrored);
const controller = $getByIdDirectPrivate(stream, "controller");
$getByIdDirectPrivate(controller, "errorSteps").$call();
@@ -303,9 +303,9 @@ export function writableStreamFinishInFlightClose(stream) {
$putByIdDirectPrivate(stream, "inFlightCloseRequest", undefined);
const state = $getByIdDirectPrivate(stream, "state");
$assert(state === "writable" || state === "erroring");
$assert(state === $streamWritable || state === $streamErroring);
if (state === "erroring") {
if (state === $streamErroring) {
$putByIdDirectPrivate(stream, "storedError", undefined);
const abortRequest = $getByIdDirectPrivate(stream, "pendingAbortRequest");
if (abortRequest !== undefined) {
@@ -314,7 +314,7 @@ export function writableStreamFinishInFlightClose(stream) {
}
}
$putByIdDirectPrivate(stream, "state", "closed");
$putByIdDirectPrivate(stream, "state", $streamClosed);
const writer = $getByIdDirectPrivate(stream, "writer");
if (writer !== undefined) $getByIdDirectPrivate(writer, "closedPromise").resolve.$call();
@@ -331,7 +331,7 @@ export function writableStreamFinishInFlightCloseWithError(stream, error) {
$putByIdDirectPrivate(stream, "inFlightCloseRequest", undefined);
const state = $getByIdDirectPrivate(stream, "state");
$assert(state === "writable" || state === "erroring");
$assert(state === $streamWritable || state === $streamErroring);
const abortRequest = $getByIdDirectPrivate(stream, "pendingAbortRequest");
if (abortRequest !== undefined) {
@@ -358,7 +358,7 @@ export function writableStreamFinishInFlightWriteWithError(stream, error) {
$putByIdDirectPrivate(stream, "inFlightWriteRequest", undefined);
const state = $getByIdDirectPrivate(stream, "state");
$assert(state === "writable" || state === "erroring");
$assert(state === $streamWritable || state === $streamErroring);
$writableStreamDealWithRejection(stream, error);
}
@@ -389,7 +389,7 @@ export function writableStreamMarkFirstWriteRequestInFlight(stream) {
}
export function writableStreamRejectCloseAndClosedPromiseIfNeeded(stream) {
$assert($getByIdDirectPrivate(stream, "state") === "errored");
$assert($getByIdDirectPrivate(stream, "state") === $streamErrored);
const storedError = $getByIdDirectPrivate(stream, "storedError");
@@ -410,12 +410,12 @@ export function writableStreamRejectCloseAndClosedPromiseIfNeeded(stream) {
export function writableStreamStartErroring(stream, reason) {
$assert($getByIdDirectPrivate(stream, "storedError") === undefined);
$assert($getByIdDirectPrivate(stream, "state") === "writable");
$assert($getByIdDirectPrivate(stream, "state") === $streamWritable);
const controller = $getByIdDirectPrivate(stream, "controller");
$assert(controller !== undefined);
$putByIdDirectPrivate(stream, "state", "erroring");
$putByIdDirectPrivate(stream, "state", $streamErroring);
$putByIdDirectPrivate(stream, "storedError", reason);
const writer = $getByIdDirectPrivate(stream, "writer");
@@ -426,7 +426,7 @@ export function writableStreamStartErroring(stream, reason) {
}
export function writableStreamUpdateBackpressure(stream, backpressure) {
$assert($getByIdDirectPrivate(stream, "state") === "writable");
$assert($getByIdDirectPrivate(stream, "state") === $streamWritable);
$assert(!$writableStreamCloseQueuedOrInFlight(stream));
const writer = $getByIdDirectPrivate(stream, "writer");
@@ -455,11 +455,11 @@ export function writableStreamDefaultWriterCloseWithErrorPropagation(writer) {
const state = $getByIdDirectPrivate(stream, "state");
if ($writableStreamCloseQueuedOrInFlight(stream) || state === "closed") return Promise.$resolve();
if ($writableStreamCloseQueuedOrInFlight(stream) || state === $streamClosed) return Promise.$resolve();
if (state === "errored") return Promise.$reject($getByIdDirectPrivate(stream, "storedError"));
if (state === $streamErrored) return Promise.$reject($getByIdDirectPrivate(stream, "storedError"));
$assert(state === "writable" || state === "erroring");
$assert(state === $streamWritable || state === $streamErroring);
return $writableStreamDefaultWriterClose(writer);
}
@@ -497,9 +497,9 @@ export function writableStreamDefaultWriterGetDesiredSize(writer) {
const state = $getByIdDirectPrivate(stream, "state");
if (state === "errored" || state === "erroring") return null;
if (state === $streamErrored || state === $streamErroring) return null;
if (state === "closed") return 0;
if (state === $streamClosed) return 0;
return $writableStreamDefaultControllerGetDesiredSize($getByIdDirectPrivate(stream, "controller"));
}
@@ -530,17 +530,17 @@ export function writableStreamDefaultWriterWrite(writer, chunk) {
return Promise.$reject($makeTypeError("writer is not stream's writer"));
const state = $getByIdDirectPrivate(stream, "state");
if (state === "errored") return Promise.$reject($getByIdDirectPrivate(stream, "storedError"));
if (state === $streamErrored) return Promise.$reject($getByIdDirectPrivate(stream, "storedError"));
if ($writableStreamCloseQueuedOrInFlight(stream) || state === "closed")
if ($writableStreamCloseQueuedOrInFlight(stream) || state === $streamClosed)
return Promise.$reject($makeTypeError("stream is closing or closed"));
if ($writableStreamCloseQueuedOrInFlight(stream) || state === "closed")
if ($writableStreamCloseQueuedOrInFlight(stream) || state === $streamClosed)
return Promise.$reject($makeTypeError("stream is closing or closed"));
if (state === "erroring") return Promise.$reject($getByIdDirectPrivate(stream, "storedError"));
if (state === $streamErroring) return Promise.$reject($getByIdDirectPrivate(stream, "storedError"));
$assert(state === "writable");
$assert(state === $streamWritable);
const promise = $writableStreamAddWriteRequest(stream);
$writableStreamDefaultControllerWrite(controller, chunk, chunkSize);
@@ -590,13 +590,13 @@ export function writableStreamDefaultControllerStart(controller) {
return Promise.$resolve(startAlgorithm.$call()).$then(
() => {
const state = $getByIdDirectPrivate(stream, "state");
$assert(state === "writable" || state === "erroring");
$assert(state === $streamWritable || state === $streamErroring);
$putByIdDirectPrivate(controller, "started", 1);
$writableStreamDefaultControllerAdvanceQueueIfNeeded(controller);
},
error => {
const state = $getByIdDirectPrivate(stream, "state");
$assert(state === "writable" || state === "erroring");
$assert(state === $streamWritable || state === $streamErroring);
$putByIdDirectPrivate(controller, "started", 1);
$writableStreamDealWithRejection(stream, error);
},
@@ -662,8 +662,8 @@ export function writableStreamDefaultControllerAdvanceQueueIfNeeded(controller)
if ($getByIdDirectPrivate(stream, "inFlightWriteRequest") !== undefined) return;
const state = $getByIdDirectPrivate(stream, "state");
$assert(state !== "closed" || state !== "errored");
if (state === "erroring") {
$assert(state !== $streamClosed || state !== $streamErrored);
if (state === $streamErroring) {
$writableStreamFinishErroring(stream);
return;
}
@@ -694,7 +694,7 @@ export function writableStreamDefaultControllerClose(controller) {
export function writableStreamDefaultControllerError(controller, error) {
const stream = $getByIdDirectPrivate(controller, "stream");
$assert(stream !== undefined);
$assert($getByIdDirectPrivate(stream, "state") === "writable");
$assert($getByIdDirectPrivate(stream, "state") === $streamWritable);
$writableStreamDefaultControllerClearAlgorithms(controller);
$writableStreamStartErroring(stream, error);
@@ -702,7 +702,8 @@ export function writableStreamDefaultControllerError(controller, error) {
export function writableStreamDefaultControllerErrorIfNeeded(controller, error) {
const stream = $getByIdDirectPrivate(controller, "stream");
if ($getByIdDirectPrivate(stream, "state") === "writable") $writableStreamDefaultControllerError(controller, error);
if ($getByIdDirectPrivate(stream, "state") === $streamWritable)
$writableStreamDefaultControllerError(controller, error);
}
export function writableStreamDefaultControllerGetBackpressure(controller) {
@@ -755,10 +756,10 @@ export function writableStreamDefaultControllerProcessWrite(controller, chunk) {
() => {
$writableStreamFinishInFlightWrite(stream);
const state = $getByIdDirectPrivate(stream, "state");
$assert(state === "writable" || state === "erroring");
$assert(state === $streamWritable || state === $streamErroring);
$dequeueValue($getByIdDirectPrivate(controller, "queue"));
if (!$writableStreamCloseQueuedOrInFlight(stream) && state === "writable") {
if (!$writableStreamCloseQueuedOrInFlight(stream) && state === $streamWritable) {
const backpressure = $writableStreamDefaultControllerGetBackpressure(controller);
$writableStreamUpdateBackpressure(stream, backpressure);
}
@@ -766,7 +767,7 @@ export function writableStreamDefaultControllerProcessWrite(controller, chunk) {
},
reason => {
const state = $getByIdDirectPrivate(stream, "state");
if (state === "writable") $writableStreamDefaultControllerClearAlgorithms(controller);
if (state === $streamWritable) $writableStreamDefaultControllerClearAlgorithms(controller);
$writableStreamFinishInFlightWriteWithError(stream, reason);
},
@@ -780,7 +781,7 @@ export function writableStreamDefaultControllerWrite(controller, chunk, chunkSiz
const stream = $getByIdDirectPrivate(controller, "stream");
const state = $getByIdDirectPrivate(stream, "state");
if (!$writableStreamCloseQueuedOrInFlight(stream) && state === "writable") {
if (!$writableStreamCloseQueuedOrInFlight(stream) && state === $streamWritable) {
const backpressure = $writableStreamDefaultControllerGetBackpressure(controller);
$writableStreamUpdateBackpressure(stream, backpressure);
}

View File

@@ -774,7 +774,7 @@ fn ScopedLogger(comptime tagname: []const u8, comptime disabled: bool) type {
/// BUN_DEBUG_foo=1
/// To enable all logs, set the environment variable
/// BUN_DEBUG_ALL=1
pub fn log(comptime fmt: string, args: anytype) void {
pub fn log(comptime fmt: string, args: anytype) callconv(bun.callconv_inline) void {
if (!source_set) return;
if (fmt.len == 0 or fmt[fmt.len - 1] != '\n') {
return log(fmt ++ "\n", args);

View File

@@ -0,0 +1,35 @@
'use strict';
require('../common');
const { TextEncoderStream, TextDecoderStream } = require('stream/web');
const util = require('util');
const assert = require('assert');
const textEncoderStream = new TextEncoderStream();
assert.strictEqual(
util.inspect(textEncoderStream),
`TextEncoderStream {
encoding: 'utf-8',
readable: ReadableStream { locked: false, state: 'readable', supportsBYOB: false },
writable: WritableStream { locked: false, state: 'writable' }
}`
);
assert.throws(() => textEncoderStream[util.inspect.custom].call(), {
code: 'ERR_INVALID_THIS',
});
const textDecoderStream = new TextDecoderStream();
assert.strictEqual(
util.inspect(textDecoderStream),
`TextDecoderStream {
encoding: 'utf-8',
fatal: false,
ignoreBOM: false,
readable: ReadableStream { locked: false, state: 'readable', supportsBYOB: false },
writable: WritableStream { locked: false, state: 'writable' }
}`
);
assert.throws(() => textDecoderStream[util.inspect.custom].call(), {
code: 'ERR_INVALID_THIS',
});

View File

@@ -20,6 +20,7 @@
// USE OR OTHER DEALINGS IN THE SOFTWARE.
import assert from "assert";
import { test, expect } from "bun:test";
import { isWindows } from "harness";
import util, { inspect } from "util";
import vm from "vm";
@@ -3263,3 +3264,28 @@ function mustCall(fn, criteria = 1) {
});
return _return;
}
test("ReadableStream", () => {
expect(util.inspect(new ReadableStream())).toEqual(
`ReadableStream { locked: false, state: 'readable', supportsBYOB: false }`,
);
});
test("WritableStream", () => {
expect(util.inspect(new WritableStream())).toEqual(`WritableStream { locked: false, state: 'writable' }`);
});
test("TextEncoderStream", () => {
expect(util.inspect(new TextEncoderStream())).toEqual(`TextEncoderStream {
encoding: 'utf-8',
readable: ReadableStream { locked: false, state: 'readable', supportsBYOB: false },
writable: WritableStream { locked: false, state: 'writable' }
}`);
});
test("TextDecoderStream", () => {
expect(util.inspect(new TextDecoderStream())).toEqual(`TextDecoderStream {
encoding: 'utf-8',
fatal: false,
ignoreBOM: false,
readable: ReadableStream { locked: false, state: 'readable', supportsBYOB: false },
writable: WritableStream { locked: false, state: 'writable' }
}`);
});