Skip to content

Commit

Permalink
lib,src: use V8 API for collection inspection
Browse files Browse the repository at this point in the history
Use a new public V8 API for inspecting weak collections and
collection iterators, rather than using V8-internal functions
to achieve this. This currently comes with a slight modification of
the output for inspecting iterators generated by `Set().entries()`.

Fixes: #20409
  • Loading branch information
addaleax committed May 18, 2018
1 parent 0babe5a commit 1d660fe
Show file tree
Hide file tree
Showing 8 changed files with 47 additions and 92 deletions.
6 changes: 3 additions & 3 deletions lib/console.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const {
ERR_INVALID_ARG_VALUE,
},
} = require('internal/errors');
const { previewMapIterator, previewSetIterator } = require('internal/v8');
const { previewEntries } = process.binding('util');
const { Buffer: { isBuffer } } = require('buffer');
const cliTable = require('internal/cli_table');
const util = require('util');
Expand Down Expand Up @@ -343,7 +343,7 @@ Console.prototype.table = function(tabularData, properties) {

const mapIter = isMapIterator(tabularData);
if (mapIter)
tabularData = previewMapIterator(tabularData);
tabularData = previewEntries(tabularData);

if (mapIter || isMap(tabularData)) {
const keys = [];
Expand All @@ -365,7 +365,7 @@ Console.prototype.table = function(tabularData, properties) {

const setIter = isSetIterator(tabularData);
if (setIter)
tabularData = previewSetIterator(tabularData);
tabularData = previewEntries(tabularData);

const setlike = setIter || isSet(tabularData);
if (setlike) {
Expand Down
17 changes: 0 additions & 17 deletions lib/internal/bootstrap/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
// do this good and early, since it handles errors.
setupProcessFatal();

setupV8();
setupProcessICUVersions();

setupGlobalVariables();
Expand Down Expand Up @@ -471,22 +470,6 @@
};
}

function setupV8() {
// Warm up the map and set iterator preview functions. V8 compiles
// functions lazily (unless --nolazy is set) so we need to do this
// before we turn off --allow_natives_syntax again.
const v8 = NativeModule.require('internal/v8');
v8.previewMapIterator(new Map().entries());
v8.previewSetIterator(new Set().entries());
v8.previewWeakMap(new WeakMap(), 1);
v8.previewWeakSet(new WeakSet(), 1);
// Disable --allow_natives_syntax again unless it was explicitly
// specified on the command line.
const re = /^--allow[-_]natives[-_]syntax$/;
if (!process.execArgv.some((s) => re.test(s)))
process.binding('v8').setFlagsFromString('--noallow_natives_syntax');
}

function setupProcessICUVersions() {
const icu = process.binding('config').hasIntl ?
process.binding('icu') : undefined;
Expand Down
39 changes: 0 additions & 39 deletions lib/internal/v8.js

This file was deleted.

32 changes: 11 additions & 21 deletions lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,12 @@ const {
const { TextDecoder, TextEncoder } = require('internal/encoding');
const { isBuffer } = require('buffer').Buffer;

const {
previewMapIterator,
previewSetIterator,
previewWeakMap,
previewWeakSet
} = require('internal/v8');

const {
getPromiseDetails,
getProxyDetails,
kPending,
kRejected,
previewEntries
} = process.binding('util');

const { internalBinding } = require('internal/bootstrap/loaders');
Expand Down Expand Up @@ -915,7 +909,7 @@ function formatMap(ctx, value, recurseTimes, keys) {

function formatWeakSet(ctx, value, recurseTimes, keys) {
const maxArrayLength = Math.max(ctx.maxArrayLength, 0);
const entries = previewWeakSet(value, maxArrayLength + 1);
const entries = previewEntries(value).slice(0, maxArrayLength + 1);
const maxLength = Math.min(maxArrayLength, entries.length);
let output = new Array(maxLength);
for (var i = 0; i < maxLength; ++i)
Expand All @@ -932,16 +926,14 @@ function formatWeakSet(ctx, value, recurseTimes, keys) {

function formatWeakMap(ctx, value, recurseTimes, keys) {
const maxArrayLength = Math.max(ctx.maxArrayLength, 0);
const entries = previewWeakMap(value, maxArrayLength + 1);
// Entries exist as [key1, val1, key2, val2, ...]
const remainder = entries.length / 2 > maxArrayLength;
const len = entries.length / 2 - (remainder ? 1 : 0);
const entries = previewEntries(value).slice(0, maxArrayLength + 1);
const remainder = entries.length > maxArrayLength;
const len = entries.length - (remainder ? 1 : 0);
const maxLength = Math.min(maxArrayLength, len);
let output = new Array(maxLength);
for (var i = 0; i < len; i++) {
const pos = i * 2;
output[i] = `${formatValue(ctx, entries[pos], recurseTimes)} => ` +
formatValue(ctx, entries[pos + 1], recurseTimes);
output[i] = `${formatValue(ctx, entries[i][0], recurseTimes)} => ` +
formatValue(ctx, entries[i][1], recurseTimes);
}
// Sort all entries to have a halfway reliable output (if more entries than
// retrieved ones exist, we can not reliably return the same output).
Expand All @@ -953,9 +945,9 @@ function formatWeakMap(ctx, value, recurseTimes, keys) {
return output;
}

function formatCollectionIterator(preview, ctx, value, recurseTimes, keys) {
function formatCollectionIterator(ctx, value, recurseTimes, keys) {
const output = [];
for (const entry of preview(value)) {
for (const entry of previewEntries(value)) {
if (ctx.maxArrayLength === output.length) {
output.push('... more items');
break;
Expand All @@ -969,13 +961,11 @@ function formatCollectionIterator(preview, ctx, value, recurseTimes, keys) {
}

function formatMapIterator(ctx, value, recurseTimes, keys) {
return formatCollectionIterator(previewMapIterator, ctx, value, recurseTimes,
keys);
return formatCollectionIterator(ctx, value, recurseTimes, keys);
}

function formatSetIterator(ctx, value, recurseTimes, keys) {
return formatCollectionIterator(previewSetIterator, ctx, value, recurseTimes,
keys);
return formatCollectionIterator(ctx, value, recurseTimes, keys);
}

function formatPromise(ctx, value, recurseTimes, keys) {
Expand Down
1 change: 0 additions & 1 deletion node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,6 @@
'lib/internal/http2/core.js',
'lib/internal/http2/compat.js',
'lib/internal/http2/util.js',
'lib/internal/v8.js',
'lib/internal/v8_prof_polyfill.js',
'lib/internal/v8_prof_processor.js',
'lib/internal/validators.js',
Expand Down
6 changes: 0 additions & 6 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4355,12 +4355,6 @@ void Init(int* argc,
}
#endif

// Needed for access to V8 intrinsics. Disabled again during bootstrapping,
// see lib/internal/bootstrap/node.js.
const char allow_natives_syntax[] = "--allow_natives_syntax";
V8::SetFlagsFromString(allow_natives_syntax,
sizeof(allow_natives_syntax) - 1);

// We should set node_is_initialized here instead of in node::Start,
// otherwise embedders using node::Init to initialize everything will not be
// able to set it and native modules will not load for them.
Expand Down
30 changes: 30 additions & 0 deletions src/node_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,35 @@ static void GetProxyDetails(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(ret);
}

static void PreviewEntries(const FunctionCallbackInfo<Value>& args) {
if (!args[0]->IsObject())
return;

bool is_key_value;
Local<Array> entries;
if (!args[0].As<Object>()->PreviewEntries(&is_key_value).ToLocal(&entries))
return;
if (!is_key_value)
return args.GetReturnValue().Set(entries);

uint32_t length = entries->Length();
CHECK_EQ(length % 2, 0);

Environment* env = Environment::GetCurrent(args);
Local<Context> context = env->context();

Local<Array> pairs = Array::New(env->isolate(), length / 2);
for (uint32_t i = 0; i < length / 2; i++) {
Local<Array> pair = Array::New(env->isolate(), 2);
pair->Set(context, 0, entries->Get(context, i * 2).ToLocalChecked())
.FromJust();
pair->Set(context, 1, entries->Get(context, i * 2 + 1).ToLocalChecked())
.FromJust();
pairs->Set(context, i, pair).FromJust();
}
args.GetReturnValue().Set(pairs);
}

// Side effect-free stringification that will never throw exceptions.
static void SafeToString(const FunctionCallbackInfo<Value>& args) {
auto context = args.GetIsolate()->GetCurrentContext();
Expand Down Expand Up @@ -188,6 +217,7 @@ void Initialize(Local<Object> target,
env->SetMethod(target, "getPromiseDetails", GetPromiseDetails);
env->SetMethod(target, "getProxyDetails", GetProxyDetails);
env->SetMethod(target, "safeToString", SafeToString);
env->SetMethod(target, "previewEntries", PreviewEntries);

env->SetMethod(target, "startSigintWatchdog", StartSigintWatchdog);
env->SetMethod(target, "stopSigintWatchdog", StopSigintWatchdog);
Expand Down
8 changes: 3 additions & 5 deletions test/parallel/test-util-inspect.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,13 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

// Flags: --expose_internals
'use strict';
const common = require('../common');
const assert = require('assert');
const JSStream = process.binding('js_stream').JSStream;
const util = require('util');
const vm = require('vm');
const { previewMapIterator } = require('internal/v8');
const { previewEntries } = process.binding('util');

assert.strictEqual(util.inspect(1), '1');
assert.strictEqual(util.inspect(false), 'false');
Expand Down Expand Up @@ -448,7 +447,7 @@ assert.strictEqual(util.inspect(-5e-324), '-5e-324');
{
const map = new Map();
map.set(1, 2);
const vals = previewMapIterator(map.entries());
const vals = previewEntries(map.entries());
const valsOutput = [];
for (const o of vals) {
valsOutput.push(o);
Expand Down Expand Up @@ -935,8 +934,7 @@ if (typeof Symbol !== 'undefined') {
const aSet = new Set([1, 3]);
assert.strictEqual(util.inspect(aSet.keys()), '[Set Iterator] { 1, 3 }');
assert.strictEqual(util.inspect(aSet.values()), '[Set Iterator] { 1, 3 }');
assert.strictEqual(util.inspect(aSet.entries()),
'[Set Iterator] { [ 1, 1 ], [ 3, 3 ] }');
assert.strictEqual(util.inspect(aSet.entries()), '[Set Iterator] { 1, 3 }');
// Make sure the iterator doesn't get consumed.
const keys = aSet.keys();
assert.strictEqual(util.inspect(keys), '[Set Iterator] { 1, 3 }');
Expand Down

0 comments on commit 1d660fe

Please sign in to comment.