Skip to content

Commit 7e0bc7e

Browse files
evanlucasMichael Scovetta
authored andcommitted
util: make inspect more reliable
34a3591 added pretty printing for TypedArray, ArrayBuffer, and DataView. This change allows inspecting those across different contexts. Since instanceof does not work across contexts, we can use v8::Value::IsTypedArray, v8::Value::IsArrayBuffer, and v8::Value::IsDataView PR-URL: nodejs#4098 Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]>
1 parent 8a3b610 commit 7e0bc7e

File tree

3 files changed

+87
-17
lines changed

3 files changed

+87
-17
lines changed

lib/util.js

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ function formatValue(ctx, value, recurseTimes) {
290290
}
291291
// Fast path for ArrayBuffer. Can't do the same for DataView because it
292292
// has a non-primitive .buffer property that we need to recurse for.
293-
if (value instanceof ArrayBuffer) {
293+
if (binding.isArrayBuffer(value)) {
294294
return `${getConstructorOf(value).name}` +
295295
` { byteLength: ${formatNumber(ctx, value.byteLength)} }`;
296296
}
@@ -328,18 +328,18 @@ function formatValue(ctx, value, recurseTimes) {
328328
keys.unshift('size');
329329
empty = value.size === 0;
330330
formatter = formatMap;
331-
} else if (value instanceof ArrayBuffer) {
331+
} else if (binding.isArrayBuffer(value)) {
332332
braces = ['{', '}'];
333333
keys.unshift('byteLength');
334334
visibleKeys.byteLength = true;
335-
} else if (value instanceof DataView) {
335+
} else if (binding.isDataView(value)) {
336336
braces = ['{', '}'];
337337
// .buffer goes last, it's not a primitive like the others.
338338
keys.unshift('byteLength', 'byteOffset', 'buffer');
339339
visibleKeys.byteLength = true;
340340
visibleKeys.byteOffset = true;
341341
visibleKeys.buffer = true;
342-
} else if (isTypedArray(value)) {
342+
} else if (binding.isTypedArray(value)) {
343343
braces = ['[', ']'];
344344
formatter = formatTypedArray;
345345
if (ctx.showHidden) {
@@ -679,19 +679,6 @@ function reduceToSingleString(output, base, braces) {
679679
}
680680

681681

682-
function isTypedArray(value) {
683-
return value instanceof Float32Array ||
684-
value instanceof Float64Array ||
685-
value instanceof Int16Array ||
686-
value instanceof Int32Array ||
687-
value instanceof Int8Array ||
688-
value instanceof Uint16Array ||
689-
value instanceof Uint32Array ||
690-
value instanceof Uint8Array ||
691-
value instanceof Uint8ClampedArray;
692-
}
693-
694-
695682
// NOTE: These type checking functions intentionally don't use `instanceof`
696683
// because it is fragile and can be easily faked with `Object.create()`.
697684
exports.isArray = Array.isArray;

src/node_util.cc

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,24 @@ static void IsPromise(const FunctionCallbackInfo<Value>& args) {
3030
}
3131

3232

33+
static void IsTypedArray(const FunctionCallbackInfo<Value>& args) {
34+
CHECK_EQ(1, args.Length());
35+
args.GetReturnValue().Set(args[0]->IsTypedArray());
36+
}
37+
38+
39+
static void IsArrayBuffer(const FunctionCallbackInfo<Value>& args) {
40+
CHECK_EQ(1, args.Length());
41+
args.GetReturnValue().Set(args[0]->IsArrayBuffer());
42+
}
43+
44+
45+
static void IsDataView(const FunctionCallbackInfo<Value>& args) {
46+
CHECK_EQ(1, args.Length());
47+
args.GetReturnValue().Set(args[0]->IsDataView());
48+
}
49+
50+
3351
static void GetHiddenValue(const FunctionCallbackInfo<Value>& args) {
3452
Environment* env = Environment::GetCurrent(args);
3553

@@ -53,6 +71,9 @@ void Initialize(Local<Object> target,
5371
env->SetMethod(target, "isMapIterator", IsMapIterator);
5472
env->SetMethod(target, "isSetIterator", IsSetIterator);
5573
env->SetMethod(target, "isPromise", IsPromise);
74+
env->SetMethod(target, "isTypedArray", IsTypedArray);
75+
env->SetMethod(target, "isArrayBuffer", IsArrayBuffer);
76+
env->SetMethod(target, "isDataView", IsDataView);
5677
env->SetMethod(target, "getHiddenValue", GetHiddenValue);
5778
}
5879

test/parallel/test-util-inspect.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
var common = require('../common');
33
var assert = require('assert');
44
var util = require('util');
5+
const vm = require('vm');
56

67
assert.equal(util.inspect(1), '1');
78
assert.equal(util.inspect(false), 'false');
@@ -68,6 +69,35 @@ for (const showHidden of [true, false]) {
6869
' y: 1337 }');
6970
}
7071

72+
// Now do the same checks but from a different context
73+
for (const showHidden of [true, false]) {
74+
const ab = vm.runInNewContext('new ArrayBuffer(4)');
75+
const dv = vm.runInNewContext('new DataView(ab, 1, 2)', { ab: ab });
76+
assert.equal(util.inspect(ab, showHidden), 'ArrayBuffer { byteLength: 4 }');
77+
assert.equal(util.inspect(new DataView(ab, 1, 2), showHidden),
78+
'DataView {\n' +
79+
' byteLength: 2,\n' +
80+
' byteOffset: 1,\n' +
81+
' buffer: ArrayBuffer { byteLength: 4 } }');
82+
assert.equal(util.inspect(ab, showHidden), 'ArrayBuffer { byteLength: 4 }');
83+
assert.equal(util.inspect(dv, showHidden),
84+
'DataView {\n' +
85+
' byteLength: 2,\n' +
86+
' byteOffset: 1,\n' +
87+
' buffer: ArrayBuffer { byteLength: 4 } }');
88+
ab.x = 42;
89+
dv.y = 1337;
90+
assert.equal(util.inspect(ab, showHidden),
91+
'ArrayBuffer { byteLength: 4, x: 42 }');
92+
assert.equal(util.inspect(dv, showHidden),
93+
'DataView {\n' +
94+
' byteLength: 2,\n' +
95+
' byteOffset: 1,\n' +
96+
' buffer: ArrayBuffer { byteLength: 4, x: 42 },\n' +
97+
' y: 1337 }');
98+
}
99+
100+
71101
[ Float32Array,
72102
Float64Array,
73103
Int16Array,
@@ -94,6 +124,38 @@ for (const showHidden of [true, false]) {
94124
assert.equal(util.inspect(array, false), `${constructor.name} [ 65, 97 ]`);
95125
});
96126

127+
// Now check that declaring a TypedArray in a different context works the same
128+
[ Float32Array,
129+
Float64Array,
130+
Int16Array,
131+
Int32Array,
132+
Int8Array,
133+
Uint16Array,
134+
Uint32Array,
135+
Uint8Array,
136+
Uint8ClampedArray ].forEach(constructor => {
137+
const length = 2;
138+
const byteLength = length * constructor.BYTES_PER_ELEMENT;
139+
const array = vm.runInNewContext('new constructor(new ArrayBuffer(' +
140+
'byteLength), 0, length)',
141+
{ constructor: constructor,
142+
byteLength: byteLength,
143+
length: length
144+
});
145+
array[0] = 65;
146+
array[1] = 97;
147+
assert.equal(util.inspect(array, true),
148+
`${constructor.name} [\n` +
149+
` 65,\n` +
150+
` 97,\n` +
151+
` [BYTES_PER_ELEMENT]: ${constructor.BYTES_PER_ELEMENT},\n` +
152+
` [length]: ${length},\n` +
153+
` [byteLength]: ${byteLength},\n` +
154+
` [byteOffset]: 0,\n` +
155+
` [buffer]: ArrayBuffer { byteLength: ${byteLength} } ]`);
156+
assert.equal(util.inspect(array, false), `${constructor.name} [ 65, 97 ]`);
157+
});
158+
97159
// Due to the hash seed randomization it's not deterministic the order that
98160
// the following ways this hash is displayed.
99161
// See http://codereview.chromium.org/9124004/

0 commit comments

Comments
 (0)