GeezerVison is showing github.com — vm: return all own names and symbols in property enumerator interceptor · nodejs/node@fe5666f
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: src/node_contextify.cc +25-7Lines changed: 25 additions & 7 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -769,19 +769,25 @@ Intercepted ContextifyContext::PropertyDeleterCallback( | 769 | 769 | // static | 770 | 770 | void ContextifyContext::PropertyEnumeratorCallback( | 771 | 771 | const PropertyCallbackInfo<Array>& args) { | | 772 | + // Named enumerator will be invoked on Object.keys, | | 773 | + // Object.getOwnPropertyNames, Object.getOwnPropertySymbols, | | 774 | + // Object.getOwnPropertyDescriptors, for...in, etc. operations. | | 775 | + // Named enumerator should return all own non-indices property names, | | 776 | + // including string properties and symbol properties. V8 will filter the | | 777 | + // result array to match the expected symbol-only, enumerable-only with | | 778 | + // NamedPropertyQueryCallback. | 772 | 779 | ContextifyContext* ctx = ContextifyContext::Get(args); | 773 | 780 |
| 774 | 781 | // Still initializing | 775 | 782 | if (IsStillInitializing(ctx)) return; | 776 | 783 |
| 777 | 784 | Local<Array> properties; | 778 | | - // Only get named properties, exclude symbols and indices. | | 785 | + // Only get own named properties, exclude indices. | 779 | 786 | if (!ctx->sandbox() | 780 | 787 | ->GetPropertyNames( | 781 | 788 | ctx->context(), | 782 | | - KeyCollectionMode::kIncludePrototypes, | 783 | | - static_cast<PropertyFilter>(PropertyFilter::ONLY_ENUMERABLE | | 784 | | - PropertyFilter::SKIP_SYMBOLS), | | 789 | + KeyCollectionMode::kOwnOnly, | | 790 | + static_cast<PropertyFilter>(PropertyFilter::ALL_PROPERTIES), | 785 | 791 | IndexFilter::kSkipIndices) | 786 | 792 | .ToLocal(&properties)) | 787 | 793 | return; | @@ -792,6 +798,12 @@ void ContextifyContext::PropertyEnumeratorCallback( | 792 | 798 | // static | 793 | 799 | void ContextifyContext::IndexedPropertyEnumeratorCallback( | 794 | 800 | const PropertyCallbackInfo<Array>& args) { | | 801 | + // Indexed enumerator will be invoked on Object.keys, | | 802 | + // Object.getOwnPropertyNames, Object.getOwnPropertyDescriptors, for...in, | | 803 | + // etc. operations. Indexed enumerator should return all own non-indices index | | 804 | + // properties. V8 will filter the result array to match the expected | | 805 | + // enumerable-only with IndexedPropertyQueryCallback. | | 806 | + | 795 | 807 | Isolate* isolate = args.GetIsolate(); | 796 | 808 | HandleScope scope(isolate); | 797 | 809 | ContextifyContext* ctx = ContextifyContext::Get(args); | @@ -802,9 +814,15 @@ void ContextifyContext::IndexedPropertyEnumeratorCallback( | 802 | 814 |
| 803 | 815 | Local<Array> properties; | 804 | 816 |
| 805 | | - // By default, GetPropertyNames returns string and number property names, and | 806 | | - // doesn't convert the numbers to strings. | 807 | | - if (!ctx->sandbox()->GetPropertyNames(context).ToLocal(&properties)) return; | | 817 | + // Only get own index properties. | | 818 | + if (!ctx->sandbox() | | 819 | + ->GetPropertyNames( | | 820 | + context, | | 821 | + KeyCollectionMode::kOwnOnly, | | 822 | + static_cast<PropertyFilter>(PropertyFilter::SKIP_SYMBOLS), | | 823 | + IndexFilter::kIncludeIndices) | | 824 | + .ToLocal(&properties)) | | 825 | + return; | 808 | 826 |
| 809 | 827 | std::vector<v8::Global<Value>> properties_vec; | 810 | 828 | if (FromV8Array(context, properties, &properties_vec).IsNothing()) { | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-vm-global-property-enumerator.js +54-2Lines changed: 54 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 1 | 1 | 'use strict'; | 2 | 2 | require('../common'); | | 3 | +const globalNames = require('../common/globals'); | 3 | 4 | const vm = require('vm'); | 4 | 5 | const assert = require('assert'); | 5 | 6 |
| @@ -39,11 +40,62 @@ const cases = [ | 39 | 40 | key: 'value', | 40 | 41 | }, | 41 | 42 | }, | | 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 | + })(), | 42 | 71 | ]; | 43 | 72 |
| 44 | 73 | for (const [idx, obj] of cases.entries()) { | 45 | 74 | const ctx = vm.createContext(obj); | 46 | 75 | 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 | + // FIXME(legendecas): globalThis[@@toStringTag] is unconditionally | | 87 | + // initialized to the sandbox's constructor name, even if it does not exist | | 88 | + // on the sandbox object. This may incorrectly initialize the prototype | | 89 | + // @@toStringTag on the globalThis as an own property, like | | 90 | + // Window.prototype[@@toStringTag] should be a property on the prototype. | | 91 | + assert.deepStrictEqual( | | 92 | + Object.getOwnPropertySymbols(globalObj).filter((it) => it !== Symbol.toStringTag), | | 93 | + Object.getOwnPropertySymbols(obj), | | 94 | + `Case ${idx} failed: Object.getOwnPropertySymbols` | | 95 | + ); | 49 | 96 | } | | 97 | + | | 98 | +function difference(arrA, arrB) { | | 99 | + const setB = new Set(arrB); | | 100 | + return arrA.filter((x) => !setB.has(x)); | | 101 | +}; |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-vm-ownkeys.js +3-5Lines changed: 3 additions & 5 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -15,11 +15,9 @@ Object.defineProperty(sandbox, sym2, { value: true }); | 15 | 15 |
| 16 | 16 | const ctx = vm.createContext(sandbox); | 17 | 17 |
| 18 | | -// Sanity check | 19 | | -// Please uncomment these when the test is no longer broken | 20 | | -// assert.deepStrictEqual(Reflect.ownKeys(sandbox), ['a', 'b', sym1, sym2]); | 21 | | -// assert.deepStrictEqual(Object.getOwnPropertyNames(sandbox), ['a', 'b']); | 22 | | -// assert.deepStrictEqual(Object.getOwnPropertySymbols(sandbox), [sym1, sym2]); | | 18 | +assert.deepStrictEqual(Reflect.ownKeys(sandbox), ['a', 'b', sym1, sym2]); | | 19 | +assert.deepStrictEqual(Object.getOwnPropertyNames(sandbox), ['a', 'b']); | | 20 | +assert.deepStrictEqual(Object.getOwnPropertySymbols(sandbox), [sym1, sym2]); | 23 | 21 |
| 24 | 22 | const nativeKeys = vm.runInNewContext('Reflect.ownKeys(this);'); | 25 | 23 | const ownKeys = vm.runInContext('Reflect.ownKeys(this);', ctx); | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-vm-ownpropertynames.js +3-5Lines changed: 3 additions & 5 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -15,11 +15,9 @@ Object.defineProperty(sandbox, sym2, { value: true }); | 15 | 15 |
| 16 | 16 | const ctx = vm.createContext(sandbox); | 17 | 17 |
| 18 | | -// Sanity check | 19 | | -// Please uncomment these when the test is no longer broken | 20 | | -// assert.deepStrictEqual(Reflect.ownKeys(sandbox), ['a', 'b', sym1, sym2]); | 21 | | -// assert.deepStrictEqual(Object.getOwnPropertyNames(sandbox), ['a', 'b']); | 22 | | -// assert.deepStrictEqual(Object.getOwnPropertySymbols(sandbox), [sym1, sym2]); | | 18 | +assert.deepStrictEqual(Reflect.ownKeys(sandbox), ['a', 'b', sym1, sym2]); | | 19 | +assert.deepStrictEqual(Object.getOwnPropertyNames(sandbox), ['a', 'b']); | | 20 | +assert.deepStrictEqual(Object.getOwnPropertySymbols(sandbox), [sym1, sym2]); | 23 | 21 |
| 24 | 22 | const nativeNames = vm.runInNewContext('Object.getOwnPropertyNames(this);'); | 25 | 23 | const ownNames = vm.runInContext('Object.getOwnPropertyNames(this);', ctx); | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-vm-ownpropertysymbols.js +3-5Lines changed: 3 additions & 5 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -15,11 +15,9 @@ Object.defineProperty(sandbox, sym2, { value: true }); | 15 | 15 |
| 16 | 16 | const ctx = vm.createContext(sandbox); | 17 | 17 |
| 18 | | -// Sanity check | 19 | | -// Please uncomment these when the test is no longer broken | 20 | | -// assert.deepStrictEqual(Reflect.ownKeys(sandbox), ['a', 'b', sym1, sym2]); | 21 | | -// assert.deepStrictEqual(Object.getOwnPropertyNames(sandbox), ['a', 'b']); | 22 | | -// assert.deepStrictEqual(Object.getOwnPropertySymbols(sandbox), [sym1, sym2]); | | 18 | +assert.deepStrictEqual(Reflect.ownKeys(sandbox), ['a', 'b', sym1, sym2]); | | 19 | +assert.deepStrictEqual(Object.getOwnPropertyNames(sandbox), ['a', 'b']); | | 20 | +assert.deepStrictEqual(Object.getOwnPropertySymbols(sandbox), [sym1, sym2]); | 23 | 21 |
| 24 | 22 | const nativeSym = vm.runInNewContext('Object.getOwnPropertySymbols(this);'); | 25 | 23 | const ownSym = vm.runInContext('Object.getOwnPropertySymbols(this);', ctx); | |
|