Skip to content

Commit 99bc57f

Browse files
BridgeARLiviaMedeiros
authored andcommitted
util: fix nested proxy inspection
Fixes: #61061 PR-URL: #61077 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: LiviaMedeiros <livia@cirno.name> Reviewed-By: Juan José Arboleda <soyjuanarbol@gmail.com> Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
1 parent d4dceb9 commit 99bc57f

File tree

2 files changed

+48
-8
lines changed

2 files changed

+48
-8
lines changed

lib/internal/util/inspect.js

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -869,15 +869,23 @@ function formatValue(ctx, value, recurseTimes, typedArray) {
869869
const context = value;
870870
// Always check for proxies to prevent side effects and to prevent triggering
871871
// any proxy handlers.
872-
const proxy = getProxyDetails(value, !!ctx.showProxy);
872+
let proxy = getProxyDetails(value, !!ctx.showProxy);
873+
let isProxyValue = false;
873874
if (proxy !== undefined) {
874-
if (proxy === null || proxy[0] === null) {
875-
return ctx.stylize('<Revoked Proxy>', 'special');
876-
}
877875
if (ctx.showProxy) {
876+
if (proxy[0] === null) {
877+
return ctx.stylize('<Revoked Proxy>', 'special');
878+
}
878879
return formatProxy(ctx, proxy, recurseTimes);
879880
}
880-
value = proxy;
881+
isProxyValue = true;
882+
do {
883+
if (proxy === null) {
884+
return ctx.stylize('<Revoked Proxy>', 'special');
885+
}
886+
value = proxy;
887+
proxy = getProxyDetails(value, false);
888+
} while (proxy !== undefined);
881889
}
882890

883891
// Provide a hook for user-specified inspect functions.
@@ -893,7 +901,7 @@ function formatValue(ctx, value, recurseTimes, typedArray) {
893901
// a counter internally.
894902
const depth = ctx.depth === null ? null : ctx.depth - recurseTimes;
895903
const isCrossContext =
896-
proxy !== undefined || !FunctionPrototypeSymbolHasInstance(Object, context);
904+
isProxyValue || !FunctionPrototypeSymbolHasInstance(Object, context);
897905
const ret = FunctionPrototypeCall(
898906
maybeCustom,
899907
context,
@@ -2433,7 +2441,7 @@ function hasBuiltInToString(value) {
24332441
if (proxyTarget === null) {
24342442
return true;
24352443
}
2436-
value = proxyTarget;
2444+
return hasBuiltInToString(proxyTarget);
24372445
}
24382446

24392447
let hasOwnToString = ObjectPrototypeHasOwnProperty;

test/parallel/test-util-inspect-proxy.js

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@ proxyObj = new Proxy(target, handler);
4242
util.inspect(proxyObj, opts);
4343

4444
// Make sure inspecting object does not trigger any proxy traps.
45-
util.format('%s', proxyObj);
45+
// %i%f%d use Symbol.toPrimitive to convert the value to a string.
46+
// %j uses JSON.stringify, accessing the value's toJSON and toString method.
47+
util.format('%s%o%O%c', proxyObj, proxyObj, proxyObj, proxyObj);
48+
const nestedProxy = new Proxy(new Proxy({}, handler), {});
49+
util.format('%s%o%O%c', nestedProxy, nestedProxy, nestedProxy, nestedProxy);
4650

4751
// getProxyDetails is an internal method, not intended for public use.
4852
// This is here to test that the internals are working correctly.
@@ -179,3 +183,31 @@ const expected10 = '[Function (anonymous)]';
179183
const expected11 = '[Function (anonymous)]';
180184
assert.strictEqual(util.inspect(proxy10), expected10);
181185
assert.strictEqual(util.inspect(proxy11), expected11);
186+
187+
const proxy12 = new Proxy([1, 2, 3], proxy5);
188+
assert.strictEqual(
189+
util.inspect(proxy12, { colors: true, breakLength: 1 }),
190+
'[\n \x1B[33m1\x1B[39m,\n \x1B[33m2\x1B[39m,\n \x1B[33m3\x1B[39m\n]'
191+
);
192+
assert.strictEqual(util.format('%s', proxy12), '[ 1, 2, 3 ]');
193+
194+
{
195+
// Nested proxies should not trigger any proxy handlers.
196+
const nestedProxy = new Proxy(new Proxy(new Proxy({}, handler), {}), {});
197+
198+
assert.strictEqual(
199+
util.inspect(nestedProxy, { showProxy: true }),
200+
'Proxy [ Proxy [ Proxy [ {}, [Object] ], {} ], {} ]'
201+
);
202+
assert.strictEqual(util.inspect(nestedProxy, { showProxy: false }), '{}');
203+
}
204+
205+
{
206+
// Nested revoked proxies should work as expected as well as custom inspection functions.
207+
const revocable = Proxy.revocable({}, handler);
208+
revocable.revoke();
209+
const nestedProxy = new Proxy(revocable.proxy, {});
210+
211+
assert.strictEqual(util.inspect(nestedProxy, { showProxy: true }), 'Proxy [ <Revoked Proxy>, {} ]');
212+
assert.strictEqual(util.inspect(nestedProxy, { showProxy: false }), '<Revoked Proxy>');
213+
}

0 commit comments

Comments
 (0)