mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
[console.log] Improve Object formatting and error handling
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user