Store RouteTree slots in a Map to keep slot access monomorphic (#96168)
### Root cause
Parallel route slot names are app-defined, so a `RouteTree.slots` stored
as a plain object has an unbounded set of hidden classes: every distinct
combination of slot names (`{children}`, `{children, side}`, ...)
produces a different shape. Keyed access over these objects makes the
inline cache megamorphic. This was the single `ic-megamorphic` finding
reported by `pnpm bench:deopt --scenario segment-cache`, at the keyed
store in `convertTreePrefetchToRouteTree` (`cache.ts:1531`, keys
`children`, `side`).
This PR converts `RouteTree.slots` from `{ [parallelRouteKey: string]:
RouteTree }` to `Map<string, RouteTree>`. All reads, writes, and
iterations over RouteTree slots now go through monomorphic `Map` method
calls, and iteration order is preserved (Map iterates in insertion
order, matching plain-object string-key order).
### Scope
This was an approved design decision: `RouteTree` is an in-memory-only
structure, so only it changes. `CacheNode.slots` (spread into JSX props)
and the wire formats (`TreePrefetch.slots`, `FlightRouterState[1]`)
remain plain objects; the existing conversion functions bridge them into
and out of the Map.
### Before/after findings diff
```diff
--- bench/deopt/artifacts/slots-map-before/findings.txt
+++ bench/deopt/artifacts/slots-map-after/findings.txt
@@
-high ic-megamorphic packages/next/src/client/components/segment-cache/cache.ts e keys: children, side
+high ic-megamorphic packages/next/src/client/components/segment-cache/scheduler.ts e keys: children, side
@@
+info deopt-dependency-change packages/next/src/client/components/segment-cache/scheduler.ts pingSharedPartOfCacheComponentsTree dependent prototype chain changed
+info deopt-eager packages/next/src/client/components/segment-cache/scheduler.ts _heapIndex Insufficient type feedback for generic named access
+info deopt-eager packages/next/src/client/components/segment-cache/scheduler.ts cancelPrefetchTask Insufficient type feedback for generic named access
+info ic-polymorphic packages/next/src/client/components/segment-cache/cache.ts eR keys: push
```
The targeted finding at `cache.ts:1531` is cleared. The remaining
megamorphic site (stable across two runs) is `scheduler.ts:1068:23` in
`pingSharedPartOfCacheComponentsTree` — the keyed load
`oldTreeChildren[parallelRouteKey]`, where `oldTreeChildren` is
`FlightRouterState[1]`, i.e. the wire-format plain object that is
intentionally out of scope for this change. That access existed before
this PR; previously the `for...in` loop key qualified the sibling load
for V8's fast enum-cache path, and with Map iteration keys it now
surfaces as a generic keyed load. Eliminating it would require changing
the `FlightRouterState` children format, which is a separate decision.
The new info-level lines are one-time warm-up/prototype-registration
deopts from the introduced `Map` usage, not steady-state IC misses.
### Tests
- `test/e2e/app-dir/segment-cache/basic` (start-turbo): 10 passed, 1
skipped
-
`test/e2e/app-dir/segment-cache/optimistic-routing-parallel-slot-catchall-regression`
(start-turbo): 1 passed
- `test/e2e/app-dir/segment-cache/cached-navigations` (start-turbo): 16
passed
- `pnpm --filter=next types` passes (also re-run after rebasing onto
canary)