refactor(core): move nextTick/immediate queues into core, replace ops with shared buffers (#32440)
## Summary
Major refactoring of the nextTick/immediate queue infrastructure, moving
it from Node polyfills into deno_core and replacing op-based
communication with shared memory buffers.
### Move nextTick queue into core
- Remove `macrotaskCallback` array and the polling return-value protocol
- Collapse macrotask + nextTick callback arrays into a single
`tickCallback` slot
- Simplify `__drainNextTickAndMacrotasks` to just call the tick callback
+ microtasks (no more while-loop polling)
- Move `FixedQueue`, tick queue, and drain loop
(`processTicksAndRejections`, `runNextTicks`, `queueNextTick`) from
`ext/node/polyfills/_next_tick.ts` into `libs/core/01_core.js`
- `ext/node/polyfills/_next_tick.ts` becomes a thin wrapper:
`nextTick()` handles Node-specific validation, async hooks, and exit
checks, then delegates to `core.queueNextTick()`
- `setTickHook()` allows Node polyfills to inject per-tick async hook
behavior without core depending on Node internals
- `process.ts` no longer registers
`setTickCallback`/`setMacrotaskCallback` — just calls `enableNextTick()`
which sets up the tick hook
### Move immediate queue into core
- Move `setImmediate`/`clearImmediate` queue management from
`ext/node/polyfills/timers.ts` into `libs/core/01_core.js`
- Add async hooks support
(`emitInit`/`emitBefore`/`emitAfter`/`emitDestroy`) for immediates via
`setImmediateHook()`
- Interleave promise rejection processing with nextTick drain loop
- Catch errors in nextTick callbacks and route through
`uncaughtException`
### Replace ops with shared typed array buffers
- Replace `op_has_tick_scheduled` / `op_set_has_tick_scheduled` with a
shared `Uint8Array` (`tickInfo`) backed by `ContextState::tick_info:
Box<[u8; 2]>`
- Replace `op_immediate_count` / `op_immediate_ref_count` /
`op_immediate_set_has_outstanding` / `op_immediate_has_ref_count` with a
shared `Uint32Array` (`immediateInfo`) backed by
`ContextState::immediate_info: Box<[u32; 3]>`
- JS reads/writes the shared buffers directly — no JS-to-Rust boundary
crossing needed
- Shared buffers are passed from Rust to JS via init-only
`__setTickInfo` / `__setImmediateInfo` setter functions during
`store_js_callbacks`
- `__drainNextTickAndMacrotasks` now takes no arguments (reads
`tickInfo` directly)
### Wire up `hasRejectionToWarn`
- Set `tickInfo[1]` in `promise_reject_callback` (Rust) when
`PromiseRejectWithNoHandler`
- `runNextTicks()` now checks `!hasTickScheduled() &&
!hasRejectionToWarn()`, so pending promise rejections are processed even
when no ticks are queued (matching Node.js behavior)
- `processPromiseRejections()` clears the flag after draining
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>