[console.log] Improve Object formatting and error handling

This commit is contained in:
Jarred Sumner
2022-11-27 07:27:21 -08:00
parent 964c4037de
commit 5e6e8ece4e
3 changed files with 398 additions and 181 deletions

View File

@@ -47,7 +47,7 @@
#include "wtf/text/StringImpl.h"
#include "wtf/text/StringView.h"
#include "wtf/text/WTFString.h"
#include "JavaScriptCore/FunctionPrototype.h"
#include "JSFetchHeaders.h"
#include "FetchHeaders.h"
#include "DOMURL.h"
@@ -128,7 +128,7 @@ static void handlePromise(PromiseType* promise, JSC__JSGlobalObject* globalObjec
}
}
static bool canPerformFastPropertyEnumerationForObjectAssignBun(Structure* s)
static bool canPerformFastPropertyEnumerationForIterationBun(Structure* s)
{
if (s->typeInfo().overridesGetOwnPropertySlot())
return false;
@@ -448,7 +448,6 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2,
case JSFunctionType: {
return false;
}
default: {
break;
}
@@ -547,14 +546,14 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2,
}
JSC::Structure* o1Structure = o1->structure();
if (canPerformFastPropertyEnumerationForObjectAssignBun(o1Structure)) {
if (canPerformFastPropertyEnumerationForIterationBun(o1Structure)) {
JSC::Structure* o2Structure = o2->structure();
if (canPerformFastPropertyEnumerationForObjectAssignBun(o2Structure)) {
if (canPerformFastPropertyEnumerationForIterationBun(o2Structure)) {
size_t count1 = 0;
bool result = true;
if (o2Structure->maxOffset() != o1Structure->maxOffset()) {
if (o2Structure->inlineSize() + o2Structure->outOfLineSize() != o1Structure->inlineSize() + o1Structure->outOfLineSize()) {
return false;
}
@@ -1819,7 +1818,15 @@ void JSC__JSValue__toZigString(JSC__JSValue JSValue0, ZigString* arg1, JSC__JSGl
// return;
// }
auto str = value.toWTFString(arg2);
auto* strValue = value.toStringOrNull(arg2);
if (UNLIKELY(!strValue)) {
arg1->len = 0;
arg1->ptr = nullptr;
return;
}
auto str = strValue->value(arg2);
if (str.is8Bit()) {
arg1->ptr = str.characters8();
@@ -3724,3 +3731,159 @@ bool JSC__JSValue__toBooleanSlow(JSC__JSValue JSValue0, JSC__JSGlobalObject* glo
{
return JSValue::decode(JSValue0).toBoolean(globalObject);
}
void JSC__JSValue__forEachProperty(JSC__JSValue JSValue0, JSC__JSGlobalObject* globalObject, void* arg2, void (*iter)(JSC__JSGlobalObject* arg0, void* ctx, ZigString* arg2, JSC__JSValue JSValue3, bool isSymbol))
{
JSC::JSValue value = JSC::JSValue::decode(JSValue0);
JSC::JSObject* object = value.getObject();
if (!object)
return;
JSC::VM& vm = globalObject->vm();
auto scope = DECLARE_CATCH_SCOPE(vm);
JSC::Structure* structure = object->structure();
bool fast = canPerformFastPropertyEnumerationForIterationBun(structure);
if (fast) {
if (structure->outOfLineSize() == 0 && structure->inlineSize() == 0) {
fast = false;
if (JSValue proto = object->getPrototype(vm, globalObject)) {
if (structure = proto.structureOrNull()) {
fast = canPerformFastPropertyEnumerationForIterationBun(structure);
}
}
}
}
if (fast) {
bool anyHits = false;
structure->forEachProperty(vm, [&](const PropertyTableEntry& entry) -> bool {
if (entry.attributes() & PropertyAttribute::DontEnum) {
if ((entry.attributes() & PropertyAttribute::Accessor) != 0) {
return true;
}
if (!(entry.attributes() & (PropertyAttribute::BuiltinOrFunction | PropertyAttribute::CustomAccessorOrValue))) {
return true;
}
// ignore constructor
if (entry.key() == vm.propertyNames->constructor) {
return true;
}
}
ZigString key = toZigString(entry.key());
JSC::JSValue propertyValue = object->getDirect(entry.offset());
if (!propertyValue) {
propertyValue = object->get(globalObject, entry.key());
}
if (scope.exception())
scope.clearException();
if (!propertyValue)
return true;
anyHits = true;
JSC::EnsureStillAliveScope ensureStillAliveScope(propertyValue);
iter(globalObject, arg2, &key, JSC::JSValue::encode(propertyValue), entry.key()->isSymbol());
return true;
});
if (anyHits) {
if (scope.exception()) {
scope.clearException();
return;
}
return;
}
}
JSC::PropertyNameArray properties(vm, PropertyNameMode::StringsAndSymbols, PrivateSymbolMode::Exclude);
{
auto scope = DECLARE_THROW_SCOPE(vm);
JSObject* iterating = object;
unsigned prototypeCount = 0;
size_t count = 0;
while (count == 0) {
iterating->methodTable()->getOwnPropertyNames(iterating, globalObject, properties, DontEnumPropertiesMode::Include);
RETURN_IF_EXCEPTION(scope, void());
for (auto& property : properties) {
if (UNLIKELY(property.isEmpty() || property.isNull()))
continue;
// ignore constructor
if (property == vm.propertyNames->constructor) {
continue;
}
JSC::PropertySlot slot(object, PropertySlot::InternalMethodType::Get);
if (!object->getPropertySlot(globalObject, property, slot))
continue;
if ((slot.attributes() & PropertyAttribute::Accessor) != 0) {
continue;
}
if ((slot.attributes() & PropertyAttribute::DontEnum) != 0) {
if (property == vm.propertyNames->length || property == vm.propertyNames->name || property == vm.propertyNames->underscoreProto)
continue;
}
ZigString key = toZigString(property.isSymbol() ? property.impl() : property.string());
JSC::JSValue propertyValue = jsUndefined();
if ((slot.attributes() & PropertyAttribute::DontEnum) != 0) {
if (slot.attributes() & PropertyAttribute::BuiltinOrFunction) {
propertyValue = slot.getValue(globalObject, property);
} else if (slot.isCustom()) {
propertyValue = slot.internalMethodType() == PropertySlot::InternalMethodType::Get || slot.internalMethodType() == PropertySlot::InternalMethodType::HasProperty ? slot.getValue(globalObject, property) : jsUndefined();
} else if (slot.isValue()) {
propertyValue = slot.getValue(globalObject, property);
} else if (object->getOwnPropertySlot(object, globalObject, property, slot)) {
propertyValue = slot.getValue(globalObject, property);
}
} else {
propertyValue = slot.getValue(globalObject, property);
}
if (scope.exception()) {
scope.clearException();
propertyValue = jsUndefined();
}
JSC::EnsureStillAliveScope ensureStillAliveScope(propertyValue);
count++;
iter(globalObject, arg2, &key, JSC::JSValue::encode(propertyValue), property.isSymbol());
}
// reuse memory
properties.data()->propertyNameVector().shrink(0);
JSValue prototype = iterating->getPrototype(vm, globalObject);
RETURN_IF_EXCEPTION(scope, void());
if (prototype.isNull() || prototype == JSValue(globalObject->functionPrototype()) || prototype == JSValue(globalObject->objectPrototype()) || prototype == JSValue(globalObject->objectPrototype()))
break;
if (++prototypeCount > 1) {
break;
}
iterating = asObject(prototype);
if (iterating->structure()->typeInfo().overridesAnyFormOfGetOwnPropertyNames())
break;
}
}
properties.releaseData();
if (scope.exception()) {
scope.clearException();
return;
}
}

View File

@@ -2692,7 +2692,7 @@ pub const JSValue = enum(JSValueReprInt) {
pub fn isFunction(this: JSType) bool {
return switch (this) {
.FunctionExecutable, .InternalFunction => true,
.JSFunction, .FunctionExecutable, .InternalFunction => true,
else => false,
};
}
@@ -2805,6 +2805,23 @@ pub const JSValue = enum(JSValueReprInt) {
return cppFn("coerceToInt32", .{ this, globalThis });
}
const PropertyIteratorFn = fn (
globalObject_: [*c]JSGlobalObject,
ctx_ptr: ?*anyopaque,
key: *ZigString,
value: JSValue,
is_symbol: bool,
) callconv(.C) void;
pub fn forEachProperty(
this: JSValue,
globalThis: *JSC.JSGlobalObject,
ctx: ?*anyopaque,
callback: PropertyIteratorFn,
) void {
cppFn("forEachProperty", .{ this, globalThis, ctx, callback });
}
pub fn coerce(this: JSValue, comptime T: type, globalThis: *JSC.JSGlobalObject) T {
return switch (T) {
ZigString => this.getZigString(globalThis),
@@ -3208,6 +3225,10 @@ pub const JSValue = enum(JSValueReprInt) {
}
pub fn getNameProperty(this: JSValue, global: *JSGlobalObject, ret: *ZigString) void {
if (this.isEmptyOrUndefinedOrNull()) {
return;
}
cppFn("getNameProperty", .{ this, global, ret });
}
@@ -3615,7 +3636,7 @@ pub const JSValue = enum(JSValueReprInt) {
return this.asNullableVoid().?;
}
pub const Extern = [_][]const u8{ "coerceToInt32", "fastGet_", "getStaticProperty", "createUninitializedUint8Array", "fromInt64NoTruncate", "fromUInt64NoTruncate", "toUInt64NoTruncate", "asPromise", "toInt64", "_then", "put", "makeWithNameAndPrototype", "parseJSON", "symbolKeyFor", "symbolFor", "getSymbolDescription", "createInternalPromise", "asInternalPromise", "asArrayBuffer_", "fromEntries", "createTypeError", "createRangeError", "createObject2", "getIfPropertyExistsImpl", "jsType", "jsonStringify", "kind_", "isTerminationException", "isSameValue", "getLengthOfArray", "toZigString", "createStringArray", "createEmptyObject", "putRecord", "asPromise", "isClass", "getNameProperty", "getClassName", "getErrorsProperty", "toInt32", "toBoolean", "isInt32", "isIterable", "forEach", "isAggregateError", "toError", "toZigException", "isException", "toWTFString", "hasProperty", "getPropertyNames", "getDirect", "putDirect", "getIfExists", "asString", "asObject", "asNumber", "isError", "jsNull", "jsUndefined", "jsTDZValue", "jsBoolean", "jsDoubleNumber", "jsNumberFromDouble", "jsNumberFromChar", "jsNumberFromU16", "jsNumberFromInt64", "isBoolean", "isAnyInt", "isUInt32AsAnyInt", "isInt32AsAnyInt", "isNumber", "isString", "isBigInt", "isHeapBigInt", "isBigInt32", "isSymbol", "isPrimitive", "isGetterSetter", "isCustomGetterSetter", "isObject", "isCell", "asCell", "toString", "toStringOrNull", "toPropertyKey", "toPropertyKeyValue", "toObject", "toString", "getPrototype", "getPropertyByPropertyName", "eqlValue", "eqlCell", "isCallable", "toBooleanSlow", "deepEquals" };
pub const Extern = [_][]const u8{ "forEachProperty", "coerceToInt32", "fastGet_", "getStaticProperty", "createUninitializedUint8Array", "fromInt64NoTruncate", "fromUInt64NoTruncate", "toUInt64NoTruncate", "asPromise", "toInt64", "_then", "put", "makeWithNameAndPrototype", "parseJSON", "symbolKeyFor", "symbolFor", "getSymbolDescription", "createInternalPromise", "asInternalPromise", "asArrayBuffer_", "fromEntries", "createTypeError", "createRangeError", "createObject2", "getIfPropertyExistsImpl", "jsType", "jsonStringify", "kind_", "isTerminationException", "isSameValue", "getLengthOfArray", "toZigString", "createStringArray", "createEmptyObject", "putRecord", "asPromise", "isClass", "getNameProperty", "getClassName", "getErrorsProperty", "toInt32", "toBoolean", "isInt32", "isIterable", "forEach", "isAggregateError", "toError", "toZigException", "isException", "toWTFString", "hasProperty", "getPropertyNames", "getDirect", "putDirect", "getIfExists", "asString", "asObject", "asNumber", "isError", "jsNull", "jsUndefined", "jsTDZValue", "jsBoolean", "jsDoubleNumber", "jsNumberFromDouble", "jsNumberFromChar", "jsNumberFromU16", "jsNumberFromInt64", "isBoolean", "isAnyInt", "isUInt32AsAnyInt", "isInt32AsAnyInt", "isNumber", "isString", "isBigInt", "isHeapBigInt", "isBigInt32", "isSymbol", "isPrimitive", "isGetterSetter", "isCustomGetterSetter", "isObject", "isCell", "asCell", "toString", "toStringOrNull", "toPropertyKey", "toPropertyKeyValue", "toObject", "toString", "getPrototype", "getPropertyByPropertyName", "eqlValue", "eqlCell", "isCallable", "toBooleanSlow", "deepEquals" };
};
extern "c" fn Microtask__run(*Microtask, *JSGlobalObject) void;

View File

@@ -1680,6 +1680,156 @@ pub const ZigConsoleClient = struct {
};
}
pub fn PropertyIterator(comptime Writer: type, comptime enable_ansi_colors_: bool) type {
return struct {
formatter: *ZigConsoleClient.Formatter,
writer: Writer,
i: usize = 0,
always_newline: bool = false,
parent: JSValue,
const enable_ansi_colors = enable_ansi_colors_;
pub fn handleFirstProperty(this: *@This(), globalThis: *JSC.JSGlobalObject, value: JSValue) void {
if (!value.jsType().isFunction() and !value.isClass(globalThis)) {
var name_str = ZigString.init("");
value.getPrototype(globalThis).getNameProperty(globalThis, &name_str);
var writer = WrappedWriter(Writer){
.ctx = this.writer,
.failed = false,
};
if (name_str.len > 0 and !strings.eqlComptime(name_str.slice(), "Object")) {
writer.print("{} ", .{name_str});
} else {
value.getNameProperty(globalThis, &name_str);
if (name_str.len > 0 and !strings.eqlComptime(name_str.slice(), "Object")) {
writer.print("{} ", .{name_str});
}
}
}
this.always_newline = true;
this.formatter.estimated_line_length = this.formatter.indent * 2 + 1;
this.writer.writeAll("{\n") catch {};
this.formatter.indent += 1;
this.formatter.writeIndent(Writer, this.writer) catch {};
}
pub fn forEach(
globalObject_: [*c]JSGlobalObject,
ctx_ptr: ?*anyopaque,
key: *ZigString,
value: JSValue,
is_symbol: bool,
) callconv(.C) void {
if (key.eqlComptime("constructor")) return;
if (key.eqlComptime("call")) return;
var globalThis = globalObject_.?;
var ctx: *@This() = bun.cast(*@This(), ctx_ptr orelse return);
var this = ctx.formatter;
var writer_ = ctx.writer;
var writer = WrappedWriter(Writer){
.ctx = writer_,
.failed = false,
};
const tag = Tag.get(value, globalThis);
if (tag.cell.isHidden()) return;
if (ctx.i == 0) {
handleFirstProperty(ctx, globalThis, ctx.parent);
} else {
this.printComma(Writer, writer_, enable_ansi_colors) catch unreachable;
}
defer ctx.i += 1;
if (ctx.i > 0) {
if (ctx.always_newline or this.always_newline_scope or this.goodTimeForANewLine()) {
writer.writeAll("\n");
this.writeIndent(Writer, writer_) catch {};
this.resetLine();
} else {
this.estimated_line_length += 1;
writer.writeAll(" ");
}
}
if (!is_symbol) {
// TODO: make this one pass?
if (!key.is16Bit() and JSLexer.isLatin1Identifier(@TypeOf(key.slice()), key.slice())) {
this.addForNewLine(key.len + 1);
writer.print(
comptime Output.prettyFmt("{}<d>:<r> ", enable_ansi_colors),
.{key},
);
} else if (key.is16Bit() and JSLexer.isLatin1Identifier(@TypeOf(key.utf16SliceAligned()), key.utf16SliceAligned())) {
this.addForNewLine(key.len + 1);
writer.print(
comptime Output.prettyFmt("{}<d>:<r> ", enable_ansi_colors),
.{key},
);
} else if (key.is16Bit()) {
var utf16Slice = key.utf16SliceAligned();
this.addForNewLine(utf16Slice.len + 2);
if (comptime enable_ansi_colors) {
writer.writeAll(comptime Output.prettyFmt("<r><green>", true));
}
writer.writeAll("'");
while (strings.indexOfAny16(utf16Slice, "\"")) |j| {
writer.write16Bit(utf16Slice[0..j]);
writer.writeAll("\"");
utf16Slice = utf16Slice[j + 1 ..];
}
writer.write16Bit(utf16Slice);
writer.print(
comptime Output.prettyFmt("\"<r><d>:<r> ", enable_ansi_colors),
.{},
);
} else {
this.addForNewLine(key.len + 1);
writer.print(
comptime Output.prettyFmt("{s}<d>:<r> ", enable_ansi_colors),
.{JSPrinter.formatJSONString(key.slice())},
);
}
} else {
this.addForNewLine(1 + "[Symbol()]:".len + key.len);
writer.print(
comptime Output.prettyFmt("<r><d>[<r><blue>Symbol({any})<r><d>]:<r> ", enable_ansi_colors),
.{
key.*,
},
);
}
if (tag.cell.isStringLike()) {
if (comptime enable_ansi_colors) {
writer.writeAll(comptime Output.prettyFmt("<r><green>", true));
}
}
this.format(tag, Writer, ctx.writer, value, globalThis, enable_ansi_colors);
if (tag.cell.isStringLike()) {
if (comptime enable_ansi_colors) {
writer.writeAll(comptime Output.prettyFmt("<r>", true));
}
}
}
};
}
pub fn printAs(
this: *ZigConsoleClient.Formatter,
comptime Format: ZigConsoleClient.Formatter.Tag,
@@ -1822,12 +1972,13 @@ pub const ZigConsoleClient = struct {
},
.Symbol => {
const description = value.getDescription(this.globalThis);
this.addForNewLine(description.len + "Symbol".len);
this.addForNewLine("Symbol".len);
if (description.len > 0) {
writer.print(comptime Output.prettyFmt("<r><cyan>Symbol<r><d>(<green>{}<r><d>)<r>", enable_ansi_colors), .{description});
this.addForNewLine(description.len + "()".len);
writer.print(comptime Output.prettyFmt("<r><blue>Symbol({any})<r>", enable_ansi_colors), .{description});
} else {
writer.print(comptime Output.prettyFmt("<r><cyan>Symbol<r>", enable_ansi_colors), .{});
writer.print(comptime Output.prettyFmt("<r><blue>Symbol<r>", enable_ansi_colors), .{});
}
},
.Error => {
@@ -1970,6 +2121,12 @@ pub const ZigConsoleClient = struct {
else => {},
}
}
if (value.isCallable(this.globalThis.vm())) {
return this.printAs(.Function, Writer, writer_, value, jsType, enable_ansi_colors);
}
return this.printAs(.Object, Writer, writer_, value, jsType, enable_ansi_colors);
}
return this.printAs(.Object, Writer, writer_, value, .Event, enable_ansi_colors);
},
@@ -2361,178 +2518,55 @@ pub const ZigConsoleClient = struct {
writer.writeAll(" />");
},
.Object => {
var object = value.asObjectRef();
const prev_quote_strings = this.quote_strings;
this.quote_strings = true;
defer this.quote_strings = prev_quote_strings;
{
var props_iter = JSC.JSPropertyIterator(.{
.skip_empty_name = true,
.include_value = true,
}).init(this.globalThis, object);
defer props_iter.deinit();
const Iterator = PropertyIterator(Writer, enable_ansi_colors);
const prev_quote_strings = this.quote_strings;
this.quote_strings = true;
defer this.quote_strings = prev_quote_strings;
// We want to figure out if we should print this object
// on one line or multiple lines
//
// The 100% correct way would be to print everything to
// a temporary buffer and then check how long each line was
//
// But it's important that console.log() is fast. So we
// do a small compromise to avoid multiple passes over input
//
// We say:
//
// If the object has at least 2 properties and ANY of the following conditions are met:
// - total length of all the property names is more than
// 14 characters
// - the parent object is printing each property on a new line
// - The first property is a DOM object, ESM namespace, Map, Set, or Blob
//
// Then, we print it each property on a new line, recursively.
//
const prev_always_newline_scope = this.always_newline_scope;
defer this.always_newline_scope = prev_always_newline_scope;
var iter = Iterator{
.formatter = this,
.writer = writer_,
.always_newline = this.always_newline_scope or this.goodTimeForANewLine(),
.parent = value,
};
var name_str = ZigString.init("");
value.getPrototype(this.globalThis).getNameProperty(this.globalThis, &name_str);
value.forEachProperty(this.globalThis, &iter, Iterator.forEach);
if (name_str.len > 0 and !strings.eqlComptime(name_str.slice(), "Object")) {
writer.print("{} ", .{name_str});
} else {
value.getNameProperty(this.globalThis, &name_str);
if (name_str.len > 0 and !strings.eqlComptime(name_str.slice(), "Object")) {
writer.print("{} ", .{name_str});
}
if (iter.i == 0) {
if (value.isClass(this.globalThis) and !value.isCallable(this.globalThis.vm()))
this.printAs(.Class, Writer, writer_, value, jsType, enable_ansi_colors)
else if (value.isCallable(this.globalThis.vm()))
this.printAs(.Function, Writer, writer_, value, jsType, enable_ansi_colors)
else
writer.writeAll("{}");
} else {
if (iter.always_newline) {
this.indent -|= 1;
}
if (props_iter.len == 0) {
writer.writeAll("{ }");
return;
}
// We want to figure out if we should print this object
// on one line or multiple lines
//
// The 100% correct way would be to print everything to
// a temporary buffer and then check how long each line was
//
// But it's important that console.log() is fast. So we
// do a small compromise to avoid multiple passes over input
//
// We say:
//
// If the object has at least 2 properties and ANY of the following conditions are met:
// - total length of all the property names is more than
// 14 characters
// - the parent object is printing each property on a new line
// - The first property is a DOM object, ESM namespace, Map, Set, or Blob
//
// Then, we print it each property on a new line, recursively.
//
const prev_always_newline_scope = this.always_newline_scope;
const first_value = JSC.JSValue.fromRef(
JSC.C.JSObjectGetProperty(
this.globalThis,
value.asObjectRef(),
props_iter.array_ref.?.at(0),
null,
),
);
const always_newline = props_iter.len > 1 and
(prev_always_newline_scope or
props_iter.hasLongNames() or
switch (first_value.jsTypeLoose()) {
.DOMWrapper, .ModuleNamespaceObject, .JSMap, .JSSet, .Blob => true,
else => false,
});
defer this.always_newline_scope = prev_always_newline_scope;
this.always_newline_scope = always_newline;
const had_newline = this.always_newline_scope or this.goodTimeForANewLine();
if (had_newline) {
this.estimated_line_length = this.indent * 2 + 1;
writer.writeAll("{\n");
this.indent += 1;
this.writeIndent(Writer, writer_) catch {};
} else {
writer.writeAll("{ ");
this.estimated_line_length += 3;
}
{
defer {
if (had_newline) {
this.indent -|= 1;
}
}
while (props_iter.nextMaybeFirstValue(first_value)) |key| {
const property_value = props_iter.value;
const tag = Tag.get(property_value, this.globalThis);
if (tag.cell.isHidden()) continue;
if (props_iter.iter_i > 1) {
if (always_newline or this.always_newline_scope or this.goodTimeForANewLine()) {
writer.writeAll("\n");
this.writeIndent(Writer, writer_) catch {};
this.resetLine();
} else {
this.estimated_line_length += 1;
writer.writeAll(" ");
}
}
// TODO: make this one pass?
if (!key.is16Bit() and JSLexer.isLatin1Identifier(@TypeOf(key.slice()), key.slice())) {
this.addForNewLine(key.len + 1);
writer.print(
comptime Output.prettyFmt("{}<d>:<r> ", enable_ansi_colors),
.{key},
);
} else if (key.is16Bit() and JSLexer.isLatin1Identifier(@TypeOf(key.utf16SliceAligned()), key.utf16SliceAligned())) {
this.addForNewLine(key.len + 1);
writer.print(
comptime Output.prettyFmt("{}<d>:<r> ", enable_ansi_colors),
.{key},
);
} else if (key.is16Bit()) {
var utf16Slice = key.utf16SliceAligned();
this.addForNewLine(utf16Slice.len + 2);
if (comptime enable_ansi_colors) {
writer.writeAll(comptime Output.prettyFmt("<r><green>", true));
}
writer.writeAll("'");
while (strings.indexOfAny16(utf16Slice, "'")) |j| {
writer.write16Bit(utf16Slice[0..j]);
writer.writeAll("\\'");
utf16Slice = utf16Slice[j + 1 ..];
}
writer.write16Bit(utf16Slice);
writer.print(
comptime Output.prettyFmt("'<r><d>:<r> ", enable_ansi_colors),
.{},
);
} else {
this.addForNewLine(key.len + 1);
writer.print(
comptime Output.prettyFmt("{s}<d>:<r> ", enable_ansi_colors),
.{JSPrinter.formatJSONString(key.slice())},
);
}
if (tag.cell.isStringLike()) {
if (comptime enable_ansi_colors) {
writer.writeAll(comptime Output.prettyFmt("<r><green>", true));
}
}
this.format(tag, Writer, writer_, property_value, this.globalThis, enable_ansi_colors);
if (tag.cell.isStringLike()) {
if (comptime enable_ansi_colors) {
writer.writeAll(comptime Output.prettyFmt("<r>", true));
}
}
if (props_iter.i + 1 < props_iter.len) {
this.printComma(Writer, writer_, enable_ansi_colors) catch unreachable;
}
}
}
if (had_newline) {
if (iter.always_newline) {
writer.writeAll("\n");
this.writeIndent(Writer, writer_) catch {};
writer.writeAll("}");
@@ -2616,7 +2650,7 @@ pub const ZigConsoleClient = struct {
enable_ansi_colors,
),
// Uint8Array, Uint8ClampedArray, DataView
// Uint8Array, Uint8ClampedArray, DataView, ArrayBuffer
else => this.writeTypedArray(*@TypeOf(writer), &writer, u8, slice, enable_ansi_colors),
}
}
@@ -2678,7 +2712,7 @@ pub const ZigConsoleClient = struct {
.Function => this.printAs(.Function, Writer, writer, value, result.cell, enable_ansi_colors),
.Class => this.printAs(.Class, Writer, writer, value, result.cell, enable_ansi_colors),
.Error => this.printAs(.Error, Writer, writer, value, result.cell, enable_ansi_colors),
.TypedArray => this.printAs(.TypedArray, Writer, writer, value, result.cell, enable_ansi_colors),
.ArrayBuffer, .TypedArray => this.printAs(.TypedArray, Writer, writer, value, result.cell, enable_ansi_colors),
.Map => this.printAs(.Map, Writer, writer, value, result.cell, enable_ansi_colors),
.Set => this.printAs(.Set, Writer, writer, value, result.cell, enable_ansi_colors),
.Symbol => this.printAs(.Symbol, Writer, writer, value, result.cell, enable_ansi_colors),
@@ -2688,7 +2722,6 @@ pub const ZigConsoleClient = struct {
.Promise => this.printAs(.Promise, Writer, writer, value, result.cell, enable_ansi_colors),
.JSON => this.printAs(.JSON, Writer, writer, value, result.cell, enable_ansi_colors),
.NativeCode => this.printAs(.NativeCode, Writer, writer, value, result.cell, enable_ansi_colors),
.ArrayBuffer => this.printAs(.ArrayBuffer, Writer, writer, value, result.cell, enable_ansi_colors),
.JSX => this.printAs(.JSX, Writer, writer, value, result.cell, enable_ansi_colors),
.Event => this.printAs(.Event, Writer, writer, value, result.cell, enable_ansi_colors),
};