Skip to content

Commit 01643c9

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 c57a40c commit 01643c9

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
@@ -1120,15 +1120,23 @@ function formatValue(ctx, value, recurseTimes, typedArray) {
11201120
const context = value;
11211121
// Always check for proxies to prevent side effects and to prevent triggering
11221122
// any proxy handlers.
1123-
const proxy = getProxyDetails(value, !!ctx.showProxy);
1123+
let proxy = getProxyDetails(value, !!ctx.showProxy);
1124+
let isProxyValue = false;
11241125
if (proxy !== undefined) {
1125-
if (proxy === null || proxy[0] === null) {
1126-
return ctx.stylize('<Revoked Proxy>', 'special');
1127-
}
11281126
if (ctx.showProxy) {
1127+
if (proxy[0] === null) {
1128+
return ctx.stylize('<Revoked Proxy>', 'special');
1129+
}
11291130
return formatProxy(ctx, proxy, recurseTimes);
11301131
}
1131-
value = proxy;
1132+
isProxyValue = true;
1133+
do {
1134+
if (proxy === null) {
1135+
return ctx.stylize('<Revoked Proxy>', 'special');
1136+
}
1137+
value = proxy;
1138+
proxy = getProxyDetails(value, false);
1139+
} while (proxy !== undefined);
11321140
}
11331141

11341142
// Provide a hook for user-specified inspect functions.
@@ -1144,7 +1152,7 @@ function formatValue(ctx, value, recurseTimes, typedArray) {
11441152
// a counter internally.
11451153
const depth = ctx.depth === null ? null : ctx.depth - recurseTimes;
11461154
const isCrossContext =
1147-
proxy !== undefined || !FunctionPrototypeSymbolHasInstance(Object, context);
1155+
isProxyValue || !FunctionPrototypeSymbolHasInstance(Object, context);
11481156
const ret = FunctionPrototypeCall(
11491157
maybeCustom,
11501158
context,
@@ -2685,7 +2693,7 @@ function hasBuiltInToString(value) {
26852693
if (proxyTarget === null) {
26862694
return true;
26872695
}
2688-
value = proxyTarget;
2696+
return hasBuiltInToString(proxyTarget);
26892697
}
26902698

26912699
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)