Repository: denoland/deno
Pull request: perf(node): implement getOwnNonIndexProperties in native code

This speeds up assertions on typed arrays by a ton. This unblocks one of the node compat tests for advanced serialization, which times out because the assertion is too slow.

Bench:
```
import assert from "node:assert";

const iters = 10;
let total = 0;
for (let i = 0; i < iters; i++) {
  const start = performance.now();
  assert.deepStrictEqual(
    new Uint8Array(8 * 1024 * 1024).fill(0),
    new Uint8Array(8 * 1024 * 1024).fill(0),
  );
  const end = performance.now();
  total += end - start;
}

console.log("took", total / iters, "per iter");
```

Before:

```
took 2634.9984669 per iter
```

After:

```
took 15.0644873 per iter
```

So over 100x faster

Changed files:
filename: ext/node/lib.rs
patch: @@ -360,6 +360,7 @@ deno_core::extension!(deno_node,
     ops::require::op_require_break_on_next_statement,
     ops::util::op_node_guess_handle_type,
     ops::util::op_node_view_has_buffer,
+    ops::util::op_node_get_own_non_index_properties,
     ops::util::op_node_call_is_from_dependency<TInNpmPackageChecker, TNpmPackageFolderResolver, TSys>,
     ops::util::op_node_in_npm_package<TInNpmPackageChecker, TNpmPackageFolderResolver, TSys>,
     ops::worker_threads::op_worker_threads_filename<TSys>,

filename: ext/node/ops/util.rs
patch: @@ -7,6 +7,7 @@ use deno_core::ResourceHandle;
 use deno_core::ResourceHandleFd;
 use deno_core::op2;
 use deno_core::v8;
+use deno_error::JsErrorBox;
 use node_resolver::InNpmPackageChecker;
 use node_resolver::NpmPackageFolderResolver;
 
@@ -176,3 +177,41 @@ pub fn op_node_in_npm_package<
     TSys,
   >>().in_npm_package(&specifier)
 }
+
+#[op2]
+pub fn op_node_get_own_non_index_properties<'s>(
+  scope: &mut v8::PinScope<'s, '_>,
+  obj: v8::Local<'s, v8::Object>,
+  #[smi] filter: u32,
+) -> Result<v8::Local<'s, v8::Array>, JsErrorBox> {
+  let mut property_filter = v8::PropertyFilter::ALL_PROPERTIES;
+  if filter & 1 << 0 != 0 {
+    property_filter = property_filter | v8::PropertyFilter::ONLY_WRITABLE;
+  }
+  if filter & 1 << 1 != 0 {
+    property_filter = property_filter | v8::PropertyFilter::ONLY_ENUMERABLE;
+  }
+  if filter & 1 << 2 != 0 {
+    property_filter = property_filter | v8::PropertyFilter::ONLY_CONFIGURABLE;
+  }
+  if filter & 1 << 3 != 0 {
+    property_filter = property_filter | v8::PropertyFilter::SKIP_STRINGS;
+  }
+  if filter & 1 << 4 != 0 {
+    property_filter = property_filter | v8::PropertyFilter::SKIP_SYMBOLS;
+  }
+
+  obj
+    .get_property_names(
+      scope,
+      v8::GetPropertyNamesArgs {
+        index_filter: v8::IndexFilter::SkipIndices,
+        property_filter,
+        key_conversion: v8::KeyConversionMode::NoNumbers,
+        mode: v8::KeyCollectionMode::OwnOnly,
+      },
+    )
+    .ok_or_else(|| {
+      JsErrorBox::type_error("Failed to get own non-index properties")
+    })
+}

filename: ext/node/polyfills/internal_binding/util.ts
patch: @@ -29,6 +29,7 @@
 // deno-lint-ignore-file prefer-primordials
 
 import {
+  op_node_get_own_non_index_properties,
   op_node_guess_handle_type,
   op_node_view_has_buffer,
 } from "ext:core/ops";
@@ -93,43 +94,7 @@ export function getOwnNonIndexProperties(
   obj: object,
   filter: number,
 ): (string | symbol)[] {
-  let allProperties = [
-    ...Object.getOwnPropertyNames(obj),
-    ...Object.getOwnPropertySymbols(obj),
-  ];
-
-  if (Array.isArray(obj) || ArrayBuffer.isView(obj)) {
-    allProperties = allProperties.filter((k) => !isArrayIndex(k));
-  }
-
-  if (filter === ALL_PROPERTIES) {
-    return allProperties;
-  }
-
-  const result: (string | symbol)[] = [];
-  for (const key of allProperties) {
-    const desc = Object.getOwnPropertyDescriptor(obj, key);
-    if (desc === undefined) {
-      continue;
-    }
-    if (filter & ONLY_WRITABLE && !desc.writable) {
-      continue;
-    }
-    if (filter & ONLY_ENUMERABLE && !desc.enumerable) {
-      continue;
-    }
-    if (filter & ONLY_CONFIGURABLE && !desc.configurable) {
-      continue;
-    }
-    if (filter & SKIP_STRINGS && typeof key === "string") {
-      continue;
-    }
-    if (filter & SKIP_SYMBOLS && typeof key === "symbol") {
-      continue;
-    }
-    result.push(key);
-  }
-  return result;
+  return op_node_get_own_non_index_properties(obj, filter);
 }
 
 export function arrayBufferViewHasBuffer(