Skip to content

Commit cc31e7d

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 cc31e7d

File tree

2 files changed

+46
-9
lines changed

2 files changed

+46
-9
lines changed

lib/internal/util/inspect.js

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,15 +1120,21 @@ 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);
11241124
if (proxy !== undefined) {
1125-
if (proxy === null || proxy[0] === null) {
1126-
return ctx.stylize('<Revoked Proxy>', 'special');
1127-
}
11281125
if (ctx.showProxy) {
1126+
if (proxy[0] === null) {
1127+
return ctx.stylize('<Revoked Proxy>', 'special');
1128+
}
11291129
return formatProxy(ctx, proxy, recurseTimes);
11301130
}
1131-
value = proxy;
1131+
do {
1132+
if (proxy === null) {
1133+
return ctx.stylize('<Revoked Proxy>', 'special');
1134+
}
1135+
value = proxy;
1136+
proxy = getProxyDetails(value, false);
1137+
} while (proxy !== undefined);
11321138
}
11331139

11341140
// Provide a hook for user-specified inspect functions.
@@ -1143,8 +1149,7 @@ function formatValue(ctx, value, recurseTimes, typedArray) {
11431149
// This makes sure the recurseTimes are reported as before while using
11441150
// a counter internally.
11451151
const depth = ctx.depth === null ? null : ctx.depth - recurseTimes;
1146-
const isCrossContext =
1147-
proxy !== undefined || !FunctionPrototypeSymbolHasInstance(Object, context);
1152+
const isCrossContext = context !== value || !FunctionPrototypeSymbolHasInstance(Object, context);
11481153
const ret = FunctionPrototypeCall(
11491154
maybeCustom,
11501155
context,
@@ -2685,7 +2690,7 @@ function hasBuiltInToString(value) {
26852690
if (proxyTarget === null) {
26862691
return true;
26872692
}
2688-
value = proxyTarget;
2693+
return hasBuiltInToString(proxyTarget);
26892694
}
26902695

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