Skip to content

metro-file-map: Identify TreeFS directory nodes by shape, not realm - #1934

Open
robhogan wants to merge 1 commit into
mainfrom
robhogan/treefs-realm-independent-directory-check
Open

robhogan wants to merge 1 commit into
mainfrom
robhogan/treefs-realm-independent-directory-check

Conversation

@robhogan

@robhogan robhogan commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator

TreeFS uses an internal instanceof Map check in its internal isDirectory function, to identify whether a given node represented a directory (Map) or file (Array/tuple).

The only problem with instanceof Map is under Jest, in tests where we're exercising the file map cache. In Jest, tests are executed in v8 contexts where Map gets a new prototype, such that a file map cache v8-serialised in one test is deserialised but can't be traversed in another test, because instanceof Map checks fail on the foreign maps.

I discovered this while writing some unrelated tests that failed unexpectedly. We already have at least three tests in main that pass by accident (isDirectory returns false despite the node being a Map, just a foreign one) and effectively weren't testing anything. (Luckily, they all pass anyway)

An easy fix is to invert the implementation of isDirectory so that it becomes "not null and not a file", using Array.isArray, which is portable across realms.

As it turns out, this is also marginally faster.

Changelog: [Internal]

Test plan

Microbenchmark

isDirectory is hot so just to confirm this doesn't regress:

const m = new Map([['a', 1]]);
const f = [0, 1, 2, 3, 0];
const nodes = [m, f, m, f, m, m, f, m];
const fn =
  process.argv[2] === 'instanceof'
    ? n => n instanceof Map
    : n => n != null && !Array.isArray(n);
let best = Infinity;
for (let r = 0; r < 5; r++) {
  let c = 0;
  const t = process.hrtime.bigint();
  for (let i = 0; i < 2e8; i++) c += fn(nodes[i & 7]) ? 1 : 0;
  best = Math.min(best, Number(process.hrtime.bigint() - t) / 1e6);
}
console.log(process.argv[2], (best | 0) + ' ms');
Check Run 1 Run 2 Per call
node instanceof Map 216 ms 216 ms 1.08 ns
node != null && !Array.isArray(node) 180 ms 179 ms 0.90 ns

Array.isArray is marginally faster with a mix of inputs - implemented with a slot read rather than a prototype walk.

Summary:
`TreeFS` told directory nodes apart from file nodes with `node instanceof Map`, which holds only when the tree was built in the same realm as the code reading it. A tree restored through `TreeFS.fromDeserializedSnapshot` is used as-is, so a snapshot deserialized in another realm traverses wrongly: every directory below the root reads as a symlink and lookups through it fail with `Expected symlink target to be populated`, while `hierarchicalLookup` treats the root as a non-directory and returns null without probing it.

Nothing in a Metro process crosses a realm, but Jest does: each test file runs in its own `vm` context while `node:v8` is a host module, so a file map cache read back within a test holds `Map`s from another realm. The `metro` tests that build two `DependencyGraph`s on one config have been traversing such a tree, and pass only because the walks they make happen to fail in the direction their expectations need.

File nodes are metadata tuples, so `isDirectory` checks `!Array.isArray(node)` instead, which reads an internal slot and is realm-independent. The new test round-trips a snapshot through `v8.serialize`/`deserialize` and looks up a nested file in the result.

Changelog: [Internal]
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Sep 16, 2026
@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Sep 16, 2026
@meta-codesync

meta-codesync Bot commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

@vzaidman has imported this pull request. If you are a Meta employee, you can view this in D120355345.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant