Skip to content

Commit 66475b7

Browse files
committed
vm: return all own names and symbols in property enumerator interceptor
Property enumerator methods like `Object.getOwnPropertyNames`, `Object.getOwnPropertySymbols`, and `Object.keys` all invokes the named property enumerator interceptor. V8 will filter the result based on the invoked enumerator variant. Fix the enumerator interceptor to return all potential properties.
1 parent cef2047 commit 66475b7

File tree

2 files changed

+74
-21
lines changed

2 files changed

+74
-21
lines changed

src/node_contextify.cc

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -290,18 +290,6 @@ BaseObjectPtr<ContextifyContext> ContextifyContext::New(
290290
Local<Object> wrapper;
291291
{
292292
Context::Scope context_scope(v8_context);
293-
Local<String> ctor_name = sandbox_obj->GetConstructorName();
294-
if (!ctor_name->Equals(v8_context, env->object_string()).FromMaybe(false) &&
295-
new_context_global
296-
->DefineOwnProperty(
297-
v8_context,
298-
v8::Symbol::GetToStringTag(env->isolate()),
299-
ctor_name,
300-
static_cast<v8::PropertyAttribute>(v8::DontEnum))
301-
.IsNothing()) {
302-
return BaseObjectPtr<ContextifyContext>();
303-
}
304-
305293
// Assign host_defined_options_id to the global object so that in the
306294
// callback of ImportModuleDynamically, we can get the
307295
// host_defined_options_id from the v8::Context without accessing the
@@ -742,19 +730,25 @@ Intercepted ContextifyContext::PropertyDeleterCallback(
742730
// static
743731
void ContextifyContext::PropertyEnumeratorCallback(
744732
const PropertyCallbackInfo<Array>& args) {
733+
// Named enumerator will be invoked on Object.keys,
734+
// Object.getOwnPropertyNames, Object.getOwnPropertySymbols,
735+
// Object.getOwnPropertyDescriptors, for...in, etc. operations.
736+
// Named enumerator should return all own non-indices property names,
737+
// including string properties and symbol properties. V8 will filter the
738+
// result array to match the expected symbol-only, enumerable-only with
739+
// NamedPropertyQueryCallback.
745740
ContextifyContext* ctx = ContextifyContext::Get(args);
746741

747742
// Still initializing
748743
if (IsStillInitializing(ctx)) return;
749744

750745
Local<Array> properties;
751-
// Only get named properties, exclude symbols and indices.
746+
// Only get own named properties, exclude indices.
752747
if (!ctx->sandbox()
753748
->GetPropertyNames(
754749
ctx->context(),
755-
KeyCollectionMode::kIncludePrototypes,
756-
static_cast<PropertyFilter>(PropertyFilter::ONLY_ENUMERABLE |
757-
PropertyFilter::SKIP_SYMBOLS),
750+
KeyCollectionMode::kOwnOnly,
751+
static_cast<PropertyFilter>(PropertyFilter::ALL_PROPERTIES),
758752
IndexFilter::kSkipIndices)
759753
.ToLocal(&properties))
760754
return;
@@ -765,6 +759,12 @@ void ContextifyContext::PropertyEnumeratorCallback(
765759
// static
766760
void ContextifyContext::IndexedPropertyEnumeratorCallback(
767761
const PropertyCallbackInfo<Array>& args) {
762+
// Indexed enumerator will be invoked on Object.keys,
763+
// Object.getOwnPropertyNames, Object.getOwnPropertyDescriptors, for...in,
764+
// etc. operations. Indexed enumerator should return all own non-indices index
765+
// properties. V8 will filter the result array to match the expected
766+
// enumerable-only with IndexedPropertyQueryCallback.
767+
768768
Isolate* isolate = args.GetIsolate();
769769
HandleScope scope(isolate);
770770
ContextifyContext* ctx = ContextifyContext::Get(args);
@@ -775,9 +775,15 @@ void ContextifyContext::IndexedPropertyEnumeratorCallback(
775775

776776
Local<Array> properties;
777777

778-
// By default, GetPropertyNames returns string and number property names, and
779-
// doesn't convert the numbers to strings.
780-
if (!ctx->sandbox()->GetPropertyNames(context).ToLocal(&properties)) return;
778+
// Only get own index properties.
779+
if (!ctx->sandbox()
780+
->GetPropertyNames(
781+
context,
782+
KeyCollectionMode::kOwnOnly,
783+
static_cast<PropertyFilter>(PropertyFilter::SKIP_SYMBOLS),
784+
IndexFilter::kIncludeIndices)
785+
.ToLocal(&properties))
786+
return;
781787

782788
std::vector<v8::Global<Value>> properties_vec;
783789
if (FromV8Array(context, properties, &properties_vec).IsNothing()) {

test/parallel/test-vm-global-property-enumerator.js

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
'use strict';
22
require('../common');
3+
const globalNames = require('../common/globals');
34
const vm = require('vm');
45
const assert = require('assert');
56

@@ -39,11 +40,57 @@ const cases = [
3940
key: 'value',
4041
},
4142
},
43+
(() => {
44+
const obj = {
45+
__proto__: {
46+
[Symbol.toStringTag]: 'proto',
47+
},
48+
};
49+
Object.defineProperty(obj, '1', {
50+
value: 'value',
51+
enumerable: false,
52+
configurable: true,
53+
});
54+
Object.defineProperty(obj, 'key', {
55+
value: 'value',
56+
enumerable: false,
57+
configurable: true,
58+
});
59+
Object.defineProperty(obj, Symbol('symbol'), {
60+
value: 'value',
61+
enumerable: false,
62+
configurable: true,
63+
});
64+
Object.defineProperty(obj, Symbol('symbol-enumerable'), {
65+
value: 'value',
66+
enumerable: true,
67+
configurable: true,
68+
});
69+
return obj;
70+
})(),
4271
];
4372

4473
for (const [idx, obj] of cases.entries()) {
4574
const ctx = vm.createContext(obj);
4675
const globalObj = vm.runInContext('this', ctx);
47-
const keys = Object.keys(globalObj);
48-
assert.deepStrictEqual(keys, Object.keys(obj), `Case ${idx} failed`);
76+
assert.deepStrictEqual(Object.keys(globalObj), Object.keys(obj), `Case ${idx} failed: Object.keys`);
77+
78+
const ownPropertyNamesInner = difference(Object.getOwnPropertyNames(globalObj), globalNames.intrinsics);
79+
const ownPropertyNamesOuter = Object.getOwnPropertyNames(obj);
80+
assert.deepStrictEqual(
81+
ownPropertyNamesInner,
82+
ownPropertyNamesOuter,
83+
`Case ${idx} failed: Object.getOwnPropertyNames`
84+
);
85+
86+
assert.deepStrictEqual(
87+
Object.getOwnPropertySymbols(globalObj),
88+
Object.getOwnPropertySymbols(obj),
89+
`Case ${idx} failed: Object.getOwnPropertySymbols`
90+
);
4991
}
92+
93+
function difference(arrA, arrB) {
94+
const setB = new Set(arrB);
95+
return arrA.filter((x) => !setB.has(x));
96+
};

0 commit comments

Comments
 (0)