10000
We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
There was an error while loading. Please reload this page.
1 parent d674493 commit 0bca959Copy full SHA for 0bca959
benchmark/util/inspect-proxy.js
@@ -0,0 +1,44 @@
1
+'use strict';
2
+
3
+const util = require('util');
4
+const common = require('../common.js');
5
6
+const bench = common.createBenchmark(main, {
7
+ v: [1, 2],
8
+ n: [1e6]
9
+});
10
11
+function twoDifferentProxies(n) {
12
+ // This one should be slower between we're looking up multiple proxies.
13
+ const proxyA = new Proxy({}, {get: () => {}});
14
+ const proxyB = new Proxy({}, {get: () => {}});
15
+ bench.start();
16
+ for (var i = 0; i < n; i += 1)
17
+ util.inspect({a: proxyA, b: proxyB}, {showProxy: true});
18
+ bench.end(n);
19
+}
20
21
+function oneProxy(n) {
22
+ // This one should be a bit faster because of the internal caching.
23
+ const proxy = new Proxy({}, {get: () => {}});
24
25
26
+ util.inspect({a: proxy, b: proxy}, {showProxy: true});
27
28
29
30
+function main(conf) {
31
+ const n = conf.n | 0;
32
+ const v = conf.v | 0;
33
34
+ switch (v) {
35
+ case 1:
36
+ oneProxy(n);
37
+ break;
38
+ case 2:
39
+ twoDifferentProxies(n);
40
41
+ default:
42
+ throw new Error('Should not get to here');
43
+ }
44
doc/api/util.md
@@ -179,6 +179,10 @@ formatted string:
179
- `customInspect` - if `false`, then custom `inspect(depth, opts)` functions
180
defined on the objects being inspected won't be called. Defaults to `true`.
181
182
+ - `showProxy` - if `true`, then objects and functions that are Proxy objects
183
+ will be introspected to show their `target` and `hander` objects. Defaults to
184
+ `false`.
185
186
Example of inspecting all properties of the `util` object:
187
188
```js
lib/util.js
@@ -139,6 +139,7 @@ function inspect(obj, opts) {
139
if (ctx.depth === undefined) ctx.depth = 2;
140
if (ctx.colors === undefined) ctx.colors = false;
141
if (ctx.customInspect === undefined) ctx.customInspect = true;
142
+ if (ctx.showProxy === undefined) ctx.showProxy = false;
143
if (ctx.colors) ctx.stylize = stylizeWithColor;
144
return formatValue(ctx, obj, ctx.depth);
145
}
@@ -241,6 +242,46 @@ function inspectPromise(p) {
241
242
243
244
function formatValue(ctx, value, recurseTimes) {
245
+ if (ctx.showProxy &&
246
+ ((typeof value === 'object' && value !== null) ||
247
+ typeof value === 'function')) {
248
+ var proxy = undefined;
249
+ var proxyCache = ctx.proxyCache;
250
+ if (!proxyCache)
251
+ proxyCache = ctx.proxyCache = new Map();
252
+ // Determine if we've already seen this object and have
253
+ // determined that it either is or is not a proxy.
254
+ if (proxyCache.has(value)) {
255
+ // We've seen it, if the value is not undefined, it's a Proxy.
256
+ proxy = proxyCache.get(value);
257
+ } else {
258
+ // Haven't seen it. Need to check.
259
+ // If it's not a Proxy, this will return undefined.
260
+ // Otherwise, it'll return an array. The first item
261
+ // is the target, the second item is the handler.
262
+ // We ignore (and do not return) the Proxy isRevoked property.
263
+ proxy = binding.getProxyDetails(value);
264
+ if (proxy) {
265
+ // We know for a fact that this isn't a Proxy.
266
+ // Mark it as having already been evaluated.
267
+ // We do this because this object is passed
268
+ // recursively to formatValue below in order
269
+ // for it to get proper formatting, and because
270
+ // the target and handle objects also might be
271
+ // proxies... it's unfortunate but necessary.
272
+ proxyCache.set(proxy, undefined);
273
274
+ // If the object is not a Proxy, then this stores undefined.
275
+ // This tells the code above that we've already checked and
276
+ // ruled it out. If the object is a proxy, this caches the
277
+ // results of the getProxyDetails call.
278
+ proxyCache.set(value, proxy);
279
280
281
+ return 'Proxy ' + formatValue(ctx, proxy, recurseTimes);
282
283
284
285
// Provide a hook for user-specified inspect functions.
286
// Check that value is an object with an inspect function on it
287
if (ctx.customInspect &&
@@ -785,7 +826,6 @@ exports.isPrimitive = isPrimitive;
785
826
786
827
exports.isBuffer = Buffer.isBuffer;
787
828
788
-
789
829
function pad(n) {
790
830
return n < 10 ? '0' + n.toString(10) : n.toString(10);
791
831
src/node_util.cc
@@ -6,11 +6,13 @@
namespace node {
namespace util {
+using v8::Array;
using v8::Context;
using v8::FunctionCallbackInfo;
using v8::Local;
using v8::Object;
using v8::Private;
+using v8::Proxy;
using v8::String;
using v8::Value;
@@ -37,6 +39,19 @@ using v8::Value;
VALUE_METHOD_MAP(V)
#undef V
+static void GetProxyDetails(const FunctionCallbackInfo<Value>& args) {
+ // Return undefined if it's not a proxy.
+ if (!args[0]->IsProxy())
45
+ return;
46
47
+ Local<Proxy> proxy = args[0].As<Proxy>();
48
49
+ Local<Array> ret = Array::New(args.GetIsolate(), 2);
50
+ ret->Set(0, proxy->GetTarget());
51
+ ret->Set(1, proxy->GetHandler());
52
53
+ args.GetReturnValue().Set(ret);
54
55
56
static void GetHiddenValue(const FunctionCallbackInfo<Value>& args) {
57
Environment* env = Environment::GetCurrent(args);
@@ -84,6 +99,7 @@ void Initialize(Local<Object> target,
84
99
85
100
env->SetMethod(target, "getHiddenValue", GetHiddenValue);
86
101
env->SetMethod(target, "setHiddenValue", SetHiddenValue);
102
+ env->SetMethod(target, "getProxyDetails", GetProxyDetails);
87
103
88
104
89
105
} // namespace util
test/parallel/test-util-inspect-proxy.js
@@ -0,0 +1,84 @@
+require('../common');
+const assert = require('assert');
+const processUtil = process.binding('util');
+const opts = {showProxy: true};
+const target = {};
+const handler = {
+ get: function() { throw new Error('Getter should not be called'); }
+};
+const proxyObj = new Proxy(target, handler);
+// Inspecting the proxy should not actually walk it's properties
+assert.doesNotThrow(() => util.inspect(proxyObj, opts));
+// getProxyDetails is an internal method, not intended for public use.
+// This is here to test that the internals are working correctly.
+const details = processUtil.getProxyDetails(proxyObj);
+assert.strictEqual(target, details[0]);
+assert.strictEqual(handler, details[1]);
+assert.strictEqual(util.inspect(proxyObj, opts),
+ 'Proxy [ {}, { get: [Function] } ]');
+// Using getProxyDetails with non-proxy returns undefined
+assert.strictEqual(processUtil.getProxyDetails({}), undefined);
+// This will throw because the showProxy option is not used
+// and the get function on the handler object defined above
+// is actually invoked.
+assert.throws(
+ () => util.inspect(proxyObj)
+);
+// Yo dawg, I heard you liked Proxy so I put a Proxy
+// inside your Proxy that proxies your Proxy's Proxy.
+const proxy1 = new Proxy({}, {});
+const proxy2 = new Proxy(proxy1, {});
+const proxy3 = new Proxy(proxy2, proxy1);
+const proxy4 = new Proxy(proxy1, proxy2);
+const proxy5 = new Proxy(proxy3, proxy4);
+const proxy6 = new Proxy(proxy5, proxy5);
+const expected0 = '{}';
+const expected1 = 'Proxy [ {}, {} ]';
+const expected2 = 'Proxy [ Proxy [ {}, {} ], {} ]';
+const expected3 = 'Proxy [ Proxy [ Proxy [ {}, {} ], {} ], Proxy [ {}, {} ] ]';
+const expected4 = 'Proxy [ Proxy [ {}, {} ], Proxy [ Proxy [ {}, {} ], {} ] ]';
+const expected5 = 'Proxy [ Proxy [ Proxy [ Proxy [Object], {} ],' +
+ ' Proxy [ {}, {} ] ],\n Proxy [ Proxy [ {}, {} ]' +
+ ', Proxy [ Proxy [Object], {} ] ] ]';
+const expected6 = 'Proxy [ Proxy [ Proxy [ Proxy [Object], Proxy [Object]' +
+ ' ],\n Proxy [ Proxy [Object], Proxy [Object] ] ],\n' +
+ ' Proxy [ Proxy [ Proxy [Object], Proxy [Object] ],\n' +
+ ' Proxy [ Proxy [Object], Proxy [Object] ] ] ]';
+assert.strictEqual(util.inspect(proxy1, opts), expected1);
58
+assert.strictEqual(util.inspect(proxy2, opts), expected2);
59
+assert.strictEqual(util.inspect(proxy3, opts), expected3);
60
+assert.strictEqual(util.inspect(proxy4, opts), expected4);
61
+assert.strictEqual(util.inspect(proxy5, opts), expected5);
62
+assert.strictEqual(util.inspect(proxy6, opts), expected6);
63
+assert.strictEqual(util.inspect(proxy1), expected0);
64
+assert.strictEqual(util.inspect(proxy2), expected0);
65
+assert.strictEqual(util.inspect(proxy3), expected0);
66
+assert.strictEqual(util.inspect(proxy4), expected0);
67
+assert.strictEqual(util.inspect(proxy5), expected0);
68
+assert.strictEqual(util.inspect(proxy6), expected0);
69
70
+// Just for fun, let's create a Proxy using Arrays.
71
+const proxy7 = new Proxy([], []);
72
+const expected7 = 'Proxy [ [], [] ]';
73
+assert.strictEqual(util.inspect(proxy7, opts), expected7);
74
+assert.strictEqual(util.inspect(proxy7), '[]');
75
76
+// Now we're just getting silly, right?
77
+const proxy8 = new Proxy(Date, []);
78
+const proxy9 = new Proxy(Date, String);
79
+const expected8 = 'Proxy [ [Function: Date], [] ]';
80
+const expected9 = 'Proxy [ [Function: Date], [Function: String] ]';
81
+assert.strictEqual(util.inspect(proxy8, opts), expected8);
82
+assert.strictEqual(util.inspect(proxy9, opts), expected9);
83
+assert.strictEqual(util.inspect(proxy8), '[Function: Date]');
+assert.strictEqual(util.inspect(proxy9), '[Function: Date]');