From 44f7ddd2ffec6ead05f955dc71172d12e4be25ed Mon Sep 17 00:00:00 2001 From: dave caruso Date: Wed, 21 Feb 2024 18:33:54 -0800 Subject: [PATCH] fix: ConsoleObject handles proxy better (#9042) * fix: ConsoleObject handles proxy better * [autofix.ci] apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- src/bun.js/ConsoleObject.zig | 79 +++++++++----------- src/bun.js/bindings/bindings.cpp | 6 ++ src/bun.js/bindings/bindings.zig | 12 +++ src/js/builtins/ProcessObjectInternals.ts | 8 ++ test/js/web/console/console-log.expected.txt | 5 ++ test/js/web/console/console-log.js | 31 ++++++++ 6 files changed, 97 insertions(+), 44 deletions(-) diff --git a/src/bun.js/ConsoleObject.zig b/src/bun.js/ConsoleObject.zig index e775e351d8..bf3c8b9067 100644 --- a/src/bun.js/ConsoleObject.zig +++ b/src/bun.js/ConsoleObject.zig @@ -915,7 +915,6 @@ pub const Formatter = struct { JSON, toJSON, NativeCode, - ArrayBuffer, JSX, Event, @@ -923,6 +922,9 @@ pub const Formatter = struct { GetterSetter, CustomGetterSetter, + Proxy, + RevokedProxy, + pub fn isPrimitive(this: Tag) bool { return switch (this) { .String, @@ -971,15 +973,20 @@ pub const Formatter = struct { JSON: void, toJSON: void, NativeCode: void, - ArrayBuffer: void, JSX: void, Event: void, GetterSetter: void, CustomGetterSetter: void, + Proxy: void, + RevokedProxy: void, pub fn isPrimitive(this: @This()) bool { return @as(Tag, this).isPrimitive(); } + + pub fn tag(this: @This()) Tag { + return @as(Tag, this); + } }, cell: JSValue.JSType = JSValue.JSType.Cell, }; @@ -1126,10 +1133,17 @@ pub const Formatter = struct { .Object, .FinalObject, - .ProxyObject, .ModuleNamespaceObject, => .Object, + .ProxyObject => tag: { + const handler = value.getProxyInternalField(.handler); + if (handler == .zero or handler == .undefined or handler == .null) { + break :tag .RevokedProxy; + } + break :tag .Proxy; + }, + .GlobalObject => if (!opts.hide_global) .Object else @@ -2877,7 +2891,20 @@ pub const Formatter = struct { writer.writeAll(" ]"); }, - else => {}, + .RevokedProxy => { + this.addForNewLine("".len); + writer.print(comptime Output.prettyFmt("\\", enable_ansi_colors), .{}); + }, + .Proxy => { + const target = value.getProxyInternalField(.target); + if (Environment.allow_assert) { + // Proxy does not allow non-objects here. + std.debug.assert(target.isCell()); + } + // TODO: if (options.showProxy), print like `Proxy { target: ..., handlers: ... }` + // this is default off so it is not used. + this.format(ConsoleObject.Formatter.Tag.get(target, this.globalThis), Writer, writer_, target, this.globalThis, enable_ansi_colors); + }, } } @@ -2908,9 +2935,6 @@ pub const Formatter = struct { } pub fn format(this: *ConsoleObject.Formatter, result: Tag.Result, comptime Writer: type, writer: Writer, value: JSValue, globalThis: *JSGlobalObject, comptime enable_ansi_colors: bool) void { - if (comptime is_bindgen) { - return; - } const prevGlobalThis = this.globalThis; defer this.globalThis = prevGlobalThis; this.globalThis = globalThis; @@ -2919,44 +2943,11 @@ pub const Formatter = struct { // comptime var so we have to repeat it here. The rationale there is // it _should_ limit the stack usage because each version of the // function will be relatively small - switch (result.tag) { - .StringPossiblyFormatted => this.printAs(.StringPossiblyFormatted, Writer, writer, value, result.cell, enable_ansi_colors), - .String => this.printAs(.String, Writer, writer, value, result.cell, enable_ansi_colors), - .Undefined => this.printAs(.Undefined, Writer, writer, value, result.cell, enable_ansi_colors), - .Double => this.printAs(.Double, Writer, writer, value, result.cell, enable_ansi_colors), - .Integer => this.printAs(.Integer, Writer, writer, value, result.cell, enable_ansi_colors), - .Null => this.printAs(.Null, Writer, writer, value, result.cell, enable_ansi_colors), - .Boolean => this.printAs(.Boolean, Writer, writer, value, result.cell, enable_ansi_colors), - .Array => this.printAs(.Array, Writer, writer, value, result.cell, enable_ansi_colors), - .Object => this.printAs(.Object, Writer, writer, value, result.cell, enable_ansi_colors), - .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), - .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), - .MapIterator => this.printAs(.MapIterator, Writer, writer, value, result.cell, enable_ansi_colors), - .SetIterator => this.printAs(.SetIterator, 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), - .BigInt => this.printAs(.BigInt, Writer, writer, value, result.cell, enable_ansi_colors), - .GlobalObject => this.printAs(.GlobalObject, Writer, writer, value, result.cell, enable_ansi_colors), - .Private => this.printAs(.Private, Writer, writer, value, result.cell, enable_ansi_colors), - .Promise => this.printAs(.Promise, Writer, writer, value, result.cell, enable_ansi_colors), + switch (result.tag.tag()) { + inline else => |tag| this.printAs(tag, Writer, writer, value, result.cell, enable_ansi_colors), - // Call JSON.stringify on the value - .JSON => this.printAs(.JSON, Writer, writer, value, result.cell, enable_ansi_colors), - - // Call value.toJSON() and print as an object - .toJSON => this.printAs(.toJSON, Writer, writer, value, result.cell, enable_ansi_colors), - - .NativeCode => this.printAs(.NativeCode, 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), - .GetterSetter => this.printAs(.GetterSetter, Writer, writer, value, result.cell, enable_ansi_colors), - .CustomGetterSetter => this.printAs(.CustomGetterSetter, Writer, writer, value, result.cell, enable_ansi_colors), - - .CustomFormattedObject => |callback| { - this.custom_formatted_object = callback; + .CustomFormattedObject => { + this.custom_formatted_object = result.tag.CustomFormattedObject; this.printAs(.CustomFormattedObject, Writer, writer, value, result.cell, enable_ansi_colors); }, } diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index da91128e75..47dcde7be7 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -32,6 +32,7 @@ #include "JavaScriptCore/JSObject.h" #include "JavaScriptCore/JSSet.h" #include "JavaScriptCore/JSString.h" +#include "JavaScriptCore/ProxyObject.h" #include "JavaScriptCore/Microtask.h" #include "JavaScriptCore/ObjectConstructor.h" #include "JavaScriptCore/ParserError.h" @@ -5433,3 +5434,8 @@ CPP_DECL bool JSC__CustomGetterSetter__isSetterNull(JSC__CustomGetterSetter* get { return gettersetter->setter() == nullptr; } + +CPP_DECL JSC__JSValue Bun__ProxyObject__getInternalField(JSC__JSValue value, uint32_t id) +{ + return JSValue::encode(jsCast(JSValue::decode(value))->internalField((ProxyObject::Field)id).get()); +} \ No newline at end of file diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig index 26bcd5d7f3..45a1428636 100644 --- a/src/bun.js/bindings/bindings.zig +++ b/src/bun.js/bindings/bindings.zig @@ -5310,6 +5310,18 @@ pub const JSValue = enum(JSValueReprInt) { else null; } + + extern fn Bun__ProxyObject__getInternalField(this: JSValue, field: ProxyInternalField) JSValue; + + const ProxyInternalField = enum(u32) { + target = 0, + handler = 1, + }; + + /// Asserts `this` is a proxy + pub fn getProxyInternalField(this: JSValue, field: ProxyInternalField) JSValue { + return Bun__ProxyObject__getInternalField(this, field); + } }; extern "c" fn AsyncContextFrame__withAsyncContextIfNeeded(global: *JSGlobalObject, callback: JSValue) JSValue; diff --git a/src/js/builtins/ProcessObjectInternals.ts b/src/js/builtins/ProcessObjectInternals.ts index 691f018df3..16676e2285 100644 --- a/src/js/builtins/ProcessObjectInternals.ts +++ b/src/js/builtins/ProcessObjectInternals.ts @@ -353,6 +353,14 @@ export function windowsEnv(internalEnv: InternalEnvMap, envMapList: Array { + let o = {}; + for (let k of envMapList) { + o[k] = internalEnv[k.toUpperCase()]; + } + return o; + }; + return new Proxy(internalEnv, { get(_, p) { return typeof p === "string" ? Reflect.get(internalEnv, p.toUpperCase()) : undefined; diff --git a/test/js/web/console/console-log.expected.txt b/test/js/web/console/console-log.expected.txt index 97ab621065..bcb85c590f 100644 --- a/test/js/web/console/console-log.expected.txt +++ b/test/js/web/console/console-log.expected.txt @@ -244,3 +244,8 @@ myCustomName { { "": "", } +{ + hello: 2, +} + +custom inspect diff --git a/test/js/web/console/console-log.js b/test/js/web/console/console-log.js index a45e4af242..34a744eee4 100644 --- a/test/js/web/console/console-log.js +++ b/test/js/web/console/console-log.js @@ -173,3 +173,34 @@ console.log(hole([1, 2, 3], 0)); // It appears to not be set and I don't know why. console.log({ "": "" }); + +{ + // proxy + const proxy = Proxy.revocable( + { hello: 2 }, + { + get(target, prop, receiver) { + console.log("FAILED: GET", prop); + return Reflect.get(target, prop, receiver); + }, + set(target, prop, value, receiver) { + console.log("FAILED: SET", prop, value); + return Reflect.set(target, prop, value, receiver); + }, + }, + ); + console.log(proxy.proxy); + proxy.revoke(); + console.log(proxy.proxy); +} + +{ + // proxy custom inspect + const proxy = new Proxy( + { + [Bun.inspect.custom]: () => "custom inspect", + }, + {}, + ); + console.log(proxy); +}