Skip to content

Commit 9120924

Browse files
authored
util: fix nested proxy inspection
Fixes: #61061 PR-URL: #61077 Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: LiviaMedeiros <[email protected]> Reviewed-By: Juan José Arboleda <[email protected]> Reviewed-By: Gürgün Dayıoğlu <[email protected]>
1 parent 799feac commit 9120924

File tree

2 files changed

+59
-16
lines changed

2 files changed

+59
-16
lines changed

lib/internal/util/inspect.js

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,17 +1118,29 @@ function formatValue(ctx, value, recurseTimes, typedArray) {
11181118

11191119
// Memorize the context for custom inspection on proxies.
11201120
const context = value;
1121+
let proxies = 0;
11211122
// Always check for proxies to prevent side effects and to prevent triggering
11221123
// any proxy handlers.
1123-
const proxy = getProxyDetails(value, !!ctx.showProxy);
1124+
let proxy = getProxyDetails(value, !!ctx.showProxy);
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+
do {
1133+
if (proxy === null) {
1134+
let formatted = ctx.stylize('<Revoked Proxy>', 'special');
1135+
for (let i = 0; i < proxies; i++) {
1136+
formatted = `${ctx.stylize('Proxy(', 'special')}${formatted}${ctx.stylize(')', 'special')}`;
1137+
}
1138+
return formatted;
1139+
}
1140+
value = proxy;
1141+
proxy = getProxyDetails(value, false);
1142+
proxies += 1;
1143+
} while (proxy !== undefined);
11321144
}
11331145

11341146
// Provide a hook for user-specified inspect functions.
@@ -1144,7 +1156,7 @@ function formatValue(ctx, value, recurseTimes, typedArray) {
11441156
// a counter internally.
11451157
const depth = ctx.depth === null ? null : ctx.depth - recurseTimes;
11461158
const isCrossContext =
1147-
proxy !== undefined || !FunctionPrototypeSymbolHasInstance(Object, context);
1159+
proxies !== 0 || !FunctionPrototypeSymbolHasInstance(Object, context);
11481160
const ret = FunctionPrototypeCall(
11491161
maybeCustom,
11501162
context,
@@ -1180,10 +1192,12 @@ function formatValue(ctx, value, recurseTimes, typedArray) {
11801192
return ctx.stylize(`[Circular *${index}]`, 'special');
11811193
}
11821194

1183-
const formatted = formatRaw(ctx, value, recurseTimes, typedArray);
1195+
let formatted = formatRaw(ctx, value, recurseTimes, typedArray);
11841196

1185-
if (proxy !== undefined) {
1186-
return `${ctx.stylize('Proxy(', 'special')}${formatted}${ctx.stylize(')', 'special')}`;
1197+
if (proxies !== 0) {
1198+
for (let i = 0; i < proxies; i++) {
1199+
formatted = `${ctx.stylize('Proxy(', 'special')}${formatted}${ctx.stylize(')', 'special')}`;
1200+
}
11871201
}
11881202

11891203
return formatted;
@@ -2691,7 +2705,7 @@ function hasBuiltInToString(value) {
26912705
if (proxyTarget === null) {
26922706
return true;
26932707
}
2694-
value = proxyTarget;
2708+
return hasBuiltInToString(proxyTarget);
26952709
}
26962710

26972711
let hasOwnToString = ObjectPrototypeHasOwnProperty;

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

Lines changed: 35 additions & 6 deletions
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.
@@ -135,6 +139,10 @@ const expected6 = 'Proxy [\n' +
135139
' Proxy [ Proxy [Array], Proxy [Array] ]\n' +
136140
' ]\n' +
137141
']';
142+
const expected2NoShowProxy = 'Proxy(Proxy({}))';
143+
const expected3NoShowProxy = 'Proxy(Proxy(Proxy({})))';
144+
const expected4NoShowProxy = 'Proxy(Proxy(Proxy(Proxy({}))))';
145+
const expected5NoShowProxy = 'Proxy(Proxy(Proxy(Proxy(Proxy({})))))';
138146
assert.strictEqual(
139147
util.inspect(proxy1, { showProxy: 1, depth: null }),
140148
expected1);
@@ -144,11 +152,11 @@ assert.strictEqual(util.inspect(proxy4, opts), expected4);
144152
assert.strictEqual(util.inspect(proxy5, opts), expected5);
145153
assert.strictEqual(util.inspect(proxy6, opts), expected6);
146154
assert.strictEqual(util.inspect(proxy1), expected0);
147-
assert.strictEqual(util.inspect(proxy2), expected0);
148-
assert.strictEqual(util.inspect(proxy3), expected0);
149-
assert.strictEqual(util.inspect(proxy4), expected0);
150-
assert.strictEqual(util.inspect(proxy5), expected0);
151-
assert.strictEqual(util.inspect(proxy6), expected0);
155+
assert.strictEqual(util.inspect(proxy2), expected2NoShowProxy);
156+
assert.strictEqual(util.inspect(proxy3), expected3NoShowProxy);
157+
assert.strictEqual(util.inspect(proxy4), expected2NoShowProxy);
158+
assert.strictEqual(util.inspect(proxy5), expected4NoShowProxy);
159+
assert.strictEqual(util.inspect(proxy6), expected5NoShowProxy);
152160

153161
// Just for fun, let's create a Proxy using Arrays.
154162
const proxy7 = new Proxy([], []);
@@ -188,3 +196,24 @@ assert.strictEqual(
188196
')\x1B[39m'
189197
);
190198
assert.strictEqual(util.format('%s', proxy12), 'Proxy([ 1, 2, 3 ])');
199+
200+
{
201+
// Nested proxies should not trigger any proxy handlers.
202+
const nestedProxy = new Proxy(new Proxy(new Proxy({}, handler), {}), {});
203+
204+
assert.strictEqual(
205+
util.inspect(nestedProxy, { showProxy: true }),
206+
'Proxy [ Proxy [ Proxy [ {}, [Object] ], {} ], {} ]'
207+
);
208+
assert.strictEqual(util.inspect(nestedProxy, { showProxy: false }), expected3NoShowProxy);
209+
}
210+
211+
{
212+
// Nested revoked proxies should work as expected as well as custom inspection functions.
213+
const revocable = Proxy.revocable({}, handler);
214+
revocable.revoke();
215+
const nestedProxy = new Proxy(revocable.proxy, {});
216+
217+
assert.strictEqual(util.inspect(nestedProxy, { showProxy: true }), 'Proxy [ <Revoked Proxy>, {} ]');
218+
assert.strictEqual(util.inspect(nestedProxy, { showProxy: false }), 'Proxy(<Revoked Proxy>)');
219+
}

0 commit comments

Comments
 (0)