util: correctly inspect Map/Set Iterators
Previously, a MapIterator or SetIterator would not be inspected properly. This change makes it possible to inspect them by creating a Debug Mirror and previewing the iterators to not consume the actual iterator that we are trying to inspect. This change also adds a node_util binding that uses v8's Value::IsSetIterator and Value::IsMapIterator to verify that the values passed in are actual iterators. Fixes: https://github.com/nodejs/node/issues/3107 PR-URL: https://github.com/nodejs/node/pull/3119 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
This commit is contained in:
parent
089d688617
commit
88533881dd
35
lib/util.js
35
lib/util.js
@ -3,6 +3,7 @@
|
|||||||
const uv = process.binding('uv');
|
const uv = process.binding('uv');
|
||||||
const Buffer = require('buffer').Buffer;
|
const Buffer = require('buffer').Buffer;
|
||||||
const internalUtil = require('internal/util');
|
const internalUtil = require('internal/util');
|
||||||
|
const binding = process.binding('util');
|
||||||
|
|
||||||
var Debug;
|
var Debug;
|
||||||
var ObjectIsPromise;
|
var ObjectIsPromise;
|
||||||
@ -323,11 +324,23 @@ function formatValue(ctx, value, recurseTimes) {
|
|||||||
braces = ['{', '}'];
|
braces = ['{', '}'];
|
||||||
formatter = formatPromise;
|
formatter = formatPromise;
|
||||||
} else {
|
} else {
|
||||||
if (constructor === Object)
|
if (binding.isMapIterator(value)) {
|
||||||
constructor = null;
|
constructor = { name: 'MapIterator' };
|
||||||
braces = ['{', '}'];
|
braces = ['{', '}'];
|
||||||
empty = true; // No other data than keys.
|
empty = false;
|
||||||
formatter = formatObject;
|
formatter = formatCollectionIterator;
|
||||||
|
} else if (binding.isSetIterator(value)) {
|
||||||
|
constructor = { name: 'SetIterator' };
|
||||||
|
braces = ['{', '}'];
|
||||||
|
empty = false;
|
||||||
|
formatter = formatCollectionIterator;
|
||||||
|
} else {
|
||||||
|
if (constructor === Object)
|
||||||
|
constructor = null;
|
||||||
|
braces = ['{', '}'];
|
||||||
|
empty = true; // No other data than keys.
|
||||||
|
formatter = formatObject;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -501,6 +514,18 @@ function formatMap(ctx, value, recurseTimes, visibleKeys, keys) {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatCollectionIterator(ctx, value, recurseTimes, visibleKeys, keys) {
|
||||||
|
ensureDebugIsInitialized();
|
||||||
|
const mirror = Debug.MakeMirror(value, true);
|
||||||
|
var nextRecurseTimes = recurseTimes === null ? null : recurseTimes - 1;
|
||||||
|
var vals = mirror.preview();
|
||||||
|
var output = [];
|
||||||
|
for (let o of vals) {
|
||||||
|
output.push(formatValue(ctx, o, nextRecurseTimes));
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
function formatPromise(ctx, value, recurseTimes, visibleKeys, keys) {
|
function formatPromise(ctx, value, recurseTimes, visibleKeys, keys) {
|
||||||
var output = [];
|
var output = [];
|
||||||
var internals = inspectPromise(value);
|
var internals = inspectPromise(value);
|
||||||
|
1
node.gyp
1
node.gyp
@ -115,6 +115,7 @@
|
|||||||
'src/node_javascript.cc',
|
'src/node_javascript.cc',
|
||||||
'src/node_main.cc',
|
'src/node_main.cc',
|
||||||
'src/node_os.cc',
|
'src/node_os.cc',
|
||||||
|
'src/node_util.cc',
|
||||||
'src/node_v8.cc',
|
'src/node_v8.cc',
|
||||||
'src/node_stat_watcher.cc',
|
'src/node_stat_watcher.cc',
|
||||||
'src/node_watchdog.cc',
|
'src/node_watchdog.cc',
|
||||||
|
38
src/node_util.cc
Normal file
38
src/node_util.cc
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#include "node.h"
|
||||||
|
#include "v8.h"
|
||||||
|
#include "env.h"
|
||||||
|
#include "env-inl.h"
|
||||||
|
|
||||||
|
namespace node {
|
||||||
|
namespace util {
|
||||||
|
|
||||||
|
using v8::Context;
|
||||||
|
using v8::FunctionCallbackInfo;
|
||||||
|
using v8::Local;
|
||||||
|
using v8::Object;
|
||||||
|
using v8::Value;
|
||||||
|
|
||||||
|
static void IsMapIterator(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
CHECK_EQ(1, args.Length());
|
||||||
|
args.GetReturnValue().Set(args[0]->IsMapIterator());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void IsSetIterator(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
CHECK_EQ(1, args.Length());
|
||||||
|
args.GetReturnValue().Set(args[0]->IsSetIterator());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Initialize(Local<Object> target,
|
||||||
|
Local<Value> unused,
|
||||||
|
Local<Context> context) {
|
||||||
|
Environment* env = Environment::GetCurrent(context);
|
||||||
|
env->SetMethod(target, "isMapIterator", IsMapIterator);
|
||||||
|
env->SetMethod(target, "isSetIterator", IsSetIterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace util
|
||||||
|
} // namespace node
|
||||||
|
|
||||||
|
NODE_MODULE_CONTEXT_AWARE_BUILTIN(util, node::util::Initialize)
|
@ -287,6 +287,26 @@ global.Promise = function() { this.bar = 42; };
|
|||||||
assert.equal(util.inspect(new Promise()), '{ bar: 42 }');
|
assert.equal(util.inspect(new Promise()), '{ bar: 42 }');
|
||||||
global.Promise = oldPromise;
|
global.Promise = oldPromise;
|
||||||
|
|
||||||
|
// Map/Set Iterators
|
||||||
|
var m = new Map([['foo', 'bar']]);
|
||||||
|
assert.strictEqual(util.inspect(m.keys()), 'MapIterator { \'foo\' }');
|
||||||
|
assert.strictEqual(util.inspect(m.values()), 'MapIterator { \'bar\' }');
|
||||||
|
assert.strictEqual(util.inspect(m.entries()),
|
||||||
|
'MapIterator { [ \'foo\', \'bar\' ] }');
|
||||||
|
// make sure the iterator doesn't get consumed
|
||||||
|
var keys = m.keys();
|
||||||
|
assert.strictEqual(util.inspect(keys), 'MapIterator { \'foo\' }');
|
||||||
|
assert.strictEqual(util.inspect(keys), 'MapIterator { \'foo\' }');
|
||||||
|
|
||||||
|
var s = new Set([1, 3]);
|
||||||
|
assert.strictEqual(util.inspect(s.keys()), 'SetIterator { 1, 3 }');
|
||||||
|
assert.strictEqual(util.inspect(s.values()), 'SetIterator { 1, 3 }');
|
||||||
|
assert.strictEqual(util.inspect(s.entries()),
|
||||||
|
'SetIterator { [ 1, 1 ], [ 3, 3 ] }');
|
||||||
|
// make sure the iterator doesn't get consumed
|
||||||
|
keys = s.keys();
|
||||||
|
assert.strictEqual(util.inspect(keys), 'SetIterator { 1, 3 }');
|
||||||
|
assert.strictEqual(util.inspect(keys), 'SetIterator { 1, 3 }');
|
||||||
|
|
||||||
// Test alignment of items in container
|
// Test alignment of items in container
|
||||||
// Assumes that the first numeric character is the start of an item.
|
// Assumes that the first numeric character is the start of an item.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user