Skip to content

Commit 276ad5e

Browse files
committed
src: fix vm enumerator callbacks
Some of the choices here are odd, including that symbols are missing. However, that matches previous behaviour. What had to be changed was that inherited properties are no longer included; the alternative would be to also refactor the descriptor callbacks to provide data for inherited properties. Fixes: nodejs#22723 Refs: nodejs#22390
1 parent cdb7d2f commit 276ad5e

File tree

4 files changed

+91
-2
lines changed

4 files changed

+91
-2
lines changed

src/node_contextify.cc

+44-2
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ Local<Context> ContextifyContext::CreateV8Context(
155155
IndexedPropertySetterCallback,
156156
IndexedPropertyQueryCallback,
157157
IndexedPropertyDeleterCallback,
158-
PropertyEnumeratorCallback,
158+
IndexedPropertyEnumeratorCallback,
159159
IndexedPropertyDefinerCallback,
160160
IndexedPropertyDescriptorCallback,
161161
CreateDataWrapper(env));
@@ -528,7 +528,16 @@ void ContextifyContext::PropertyEnumeratorCallback(
528528
if (ctx->context_.IsEmpty())
529529
return;
530530

531-
args.GetReturnValue().Set(ctx->sandbox()->GetPropertyNames());
531+
Local<Array> ret;
532+
if (!ctx->sandbox()->GetPropertyNames(
533+
ctx->context(),
534+
v8::KeyCollectionMode::kOwnOnly,
535+
v8::SKIP_SYMBOLS,
536+
v8::IndexFilter::kSkipIndices).ToLocal(&ret)) {
537+
return;
538+
}
539+
540+
args.GetReturnValue().Set(ret);
532541
}
533542

534543
// static
@@ -623,6 +632,39 @@ void ContextifyContext::IndexedPropertyDeleterCallback(
623632
args.GetReturnValue().Set(false);
624633
}
625634

635+
// static
636+
void ContextifyContext::IndexedPropertyEnumeratorCallback(
637+
const PropertyCallbackInfo<Array>& args) {
638+
ContextifyContext* ctx = ContextifyContext::Get(args);
639+
640+
// Still initializing
641+
if (ctx->context_.IsEmpty())
642+
return;
643+
644+
Local<Array> all_keys;
645+
if (!ctx->sandbox()->GetPropertyNames(
646+
ctx->context(),
647+
v8::KeyCollectionMode::kOwnOnly,
648+
v8::SKIP_SYMBOLS,
649+
v8::IndexFilter::kIncludeIndices).ToLocal(&all_keys)) {
650+
return;
651+
}
652+
uint32_t all_keys_len = all_keys->Length();
653+
654+
Local<Array> ret = Array::New(ctx->env()->isolate());
655+
for (uint32_t i = 0, j = 0; i < all_keys_len; ++i) {
656+
Local<Value> entry;
657+
if (!all_keys->Get(ctx->context(), i).ToLocal(&entry))
658+
return;
659+
if (!entry->IsUint32())
660+
continue;
661+
if (!ret->Set(ctx->context(), j++, entry).FromMaybe(false))
662+
return;
663+
}
664+
665+
args.GetReturnValue().Set(ret);
666+
}
667+
626668
class ContextifyScript : public BaseObject {
627669
private:
628670
Persistent<UnboundScript> script_;

src/node_contextify.h

+2
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ class ContextifyContext {
104104
static void IndexedPropertyDeleterCallback(
105105
uint32_t index,
106106
const v8::PropertyCallbackInfo<v8::Boolean>& args);
107+
static void IndexedPropertyEnumeratorCallback(
108+
const v8::PropertyCallbackInfo<v8::Array>& args);
107109
Environment* const env_;
108110
Persistent<v8::Context> context_;
109111
};

test/parallel/test-vm-object-keys.js

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
'use strict';
2+
require('../common');
3+
const assert = require('assert');
4+
const vm = require('vm');
5+
6+
// Regression test for https://github.com/nodejs/node/issues/22723.
7+
8+
const kFoo = Symbol('kFoo');
9+
10+
const test = {
11+
'not': 'empty',
12+
'0': 0,
13+
'0.5': 0.5,
14+
'1': 1,
15+
'-1': -1,
16+
[kFoo]: kFoo
17+
};
18+
vm.createContext(test);
19+
const proxied = vm.runInContext('this', test);
20+
21+
// TODO(addaleax): The .sort() calls should not be necessary; the property
22+
// order should be indices, then other properties, then symbols.
23+
assert.deepStrictEqual(
24+
Object.keys(proxied).sort(),
25+
['0', '1', 'not', '0.5', '-1'].sort());
26+
assert.deepStrictEqual(
27+
// Filter out names shared by all global objects, i.e. JS builtins.
28+
Object.getOwnPropertyNames(proxied)
29+
.filter((name) => !(name in global))
30+
.sort(),
31+
['0', '1', 'not', '0.5', '-1'].sort());
32+
assert.deepStrictEqual(Object.getOwnPropertySymbols(proxied), []);
33+
34+
Object.setPrototypeOf(test, { inherited: 'true', 17: 42 });
35+
36+
assert.deepStrictEqual(
37+
Object.keys(proxied).sort(),
38+
['0', '1', 'not', '0.5', '-1'].sort());
39+
assert.deepStrictEqual(
40+
// Filter out names shared by all global objects, i.e. JS builtins.
41+
Object.getOwnPropertyNames(proxied)
42+
.filter((name) => !(name in global))
43+
.sort(),
44+
['0', '1', 'not', '0.5', '-1'].sort());
45+
assert.deepStrictEqual(Object.getOwnPropertySymbols(proxied), []);

0 commit comments

Comments
 (0)