Repository: denoland/deno
Pull request: fix(ext/node): return all own properties from VM context property enumerator

## Summary

- VM context's property enumerator callbacks were using `GetPropertyNamesArgs::default()` which only returns enumerable, non-symbol properties
- This caused `Reflect.ownKeys()`, `Object.getOwnPropertyNames()`, and `Object.getOwnPropertySymbols()` to return incomplete results inside VM contexts
- Fix by using `OwnOnly` + `ALL_PROPERTIES` filter so non-enumerable properties and symbols are included
- Also fixes a V8 crash in `test-vm-global-property-enumerator` (was a fatal `Check failed: IsName` panic)

## Test plan

- [x] `./x test-compat parallel::test-vm-ownkeys` passes (was failing)
- [x] `./x test-compat parallel::test-vm-ownpropertynames` passes (was failing)
- [x] `./x test-compat parallel::test-vm-ownpropertysymbols` passes (was failing)
- [x] `./x test-compat parallel::test-vm-global-get-own` passes (was failing)
- [x] All 4 tests enabled in `config.jsonc`
- [x] No regressions in other `parallel::test-vm-` tests (55 pass vs 51 before)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Changed files:
filename: ext/node/ops/vm.rs
patch: @@ -1027,9 +1027,13 @@ fn property_enumerator<'s>(
   };
 
   let context_scope = &mut v8::ContextScope::new(scope, context);
-  let Some(properties) = sandbox
-    .get_property_names(context_scope, v8::GetPropertyNamesArgs::default())
-  else {
+  let args = v8::GetPropertyNamesArgsBuilder::new()
+    .mode(v8::KeyCollectionMode::OwnOnly)
+    .property_filter(v8::PropertyFilter::ALL_PROPERTIES)
+    .index_filter(v8::IndexFilter::SkipIndices)
+    .key_conversion(v8::KeyConversionMode::ConvertToString)
+    .build();
+  let Some(properties) = sandbox.get_property_names(context_scope, args) else {
     return;
   };
 
@@ -1050,11 +1054,14 @@ fn indexed_property_enumerator<'s>(
     return;
   };
 
-  // By default, GetPropertyNames returns string and number property names, and
-  // doesn't convert the numbers to strings.
-  let Some(properties) =
-    sandbox.get_property_names(scope, v8::GetPropertyNamesArgs::default())
-  else {
+  // Return only indexed (numeric) own properties, including non-enumerable.
+  let args = v8::GetPropertyNamesArgsBuilder::new()
+    .mode(v8::KeyCollectionMode::OwnOnly)
+    .property_filter(v8::PropertyFilter::ALL_PROPERTIES)
+    .index_filter(v8::IndexFilter::IncludeIndices)
+    .key_conversion(v8::KeyConversionMode::KeepNumbers)
+    .build();
+  let Some(properties) = sandbox.get_property_names(scope, args) else {
     return;
   };
 

filename: tests/node_compat/config.jsonc
patch: @@ -2266,6 +2266,7 @@
     "parallel/test-vm-global-assignment.js": {},
     "parallel/test-vm-global-configurable-properties.js": {},
     "parallel/test-vm-global-define-property.js": {},
+    "parallel/test-vm-global-get-own.js": {},
     "parallel/test-vm-global-identity.js": {},
     "parallel/test-vm-global-setter.js": {},
     "parallel/test-vm-harmony-symbols.js": {},
@@ -2277,6 +2278,9 @@
     "parallel/test-vm-new-script-this-context.js": {},
     "parallel/test-vm-not-strict.js": {},
     "parallel/test-vm-options-validation.js": {},
+    "parallel/test-vm-ownkeys.js": {},
+    "parallel/test-vm-ownpropertynames.js": {},
+    "parallel/test-vm-ownpropertysymbols.js": {},
     "parallel/test-vm-parse-abort-on-uncaught-exception.js": {},
     "parallel/test-vm-preserves-property.js": {},
     "parallel/test-vm-property-not-on-sandbox.js": {},