Files
rust_browser/_bmad-output/implementation-artifacts/3-7-weakref-finalizationregistry-and-strict-mode-edge-cases.md
Zachary D. Rowitsch fb64ca1d34
All checks were successful
ci / fast (linux) (push) Successful in 7m9s
Create story files for Epic 3 stories 3.5-3.10
Create comprehensive implementation-ready story files for the remaining
Epic 3 (JavaScript Engine Maturity) stories and update sprint status
from backlog to ready-for-dev:

- 3.5: Built-in Completeness (Array/String/Object)
- 3.6: Built-in Completeness (Date/RegExp/Map/Set)
- 3.7: WeakRef, FinalizationRegistry & Strict Mode Edge Cases
- 3.8: DOM Bindings via web_api
- 3.9: Event Dispatch Completeness
- 3.10: Web API Exposure (fetch, Math, setInterval, rAF)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 08:06:03 -04:00

371 lines
25 KiB
Markdown

# Story 3.7: WeakRef, FinalizationRegistry & Strict Mode Edge Cases
Status: ready-for-dev
<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
## Story
As a web developer using JavaScript,
I want advanced memory management APIs and complete strict mode support,
So that all ECMAScript edge cases are handled correctly.
## Acceptance Criteria
1. **WeakRef construction and deref:** `new WeakRef(target)` creates a weak reference to an object. `weakRef.deref()` returns the target object if still alive, or `undefined` if collected. TypeError if `target` is not an object. Per ECMAScript §26.1.
2. **FinalizationRegistry with cleanup callbacks:** `new FinalizationRegistry(callback)` creates a registry. `.register(target, heldValue, unregisterToken?)` registers a target. `.unregister(unregisterToken)` removes registrations. Cleanup callback is called with `heldValue` when target is collected. Per ECMAScript §26.2.
3. **GC-awareness caveat:** Since the engine uses `Rc<RefCell<>>` (reference counting, no cycle collection), `WeakRef.deref()` always returns the target as long as any strong reference exists. `FinalizationRegistry` cleanup callbacks are called during explicit `cleanupSome()` or at engine-defined points (e.g., between microtask checkpoints). This is spec-compliant -- the spec says collection timing is implementation-defined.
4. **Strict mode: `eval` and `arguments` as identifiers:** `var eval = 1` and `var arguments = 2` throw SyntaxError in strict mode. Assignment to `eval` or `arguments` (`eval = 1`) throws SyntaxError. Using as function names, parameter names, or catch binding names also throws. Per ECMAScript §13.1.1.
5. **Strict mode: `with` statement rejection:** `with (obj) { ... }` throws SyntaxError in strict mode. In sloppy mode, `with` executes the body with `obj` pushed onto the scope chain. Per ECMAScript §14.11.
6. **Strict mode: octal literal rejection:** Legacy octal literals (`010`, `077`) throw SyntaxError in strict mode. `0o10` (ES2015 octal) is allowed in both modes. Octal escape sequences in strings (`"\01"`, `"\77"`) throw SyntaxError in strict mode. `"\0"` alone (null character, not followed by digit) is allowed. Per ECMAScript §B.1.1.
7. **Strict mode: assignment to undeclared variables:** `"use strict"; x = 42;` throws ReferenceError at runtime (not creating a global). Verify this works correctly in the bytecode VM path.
8. **Strict mode: read-only property assignment:** In strict mode, assignment to a non-writable property throws TypeError. Assignment to a getter-only accessor property throws TypeError. Assignment to a new property on a non-extensible object throws TypeError. (Depends on property descriptor system from Story 3.5.)
9. **Test262 tests promoted:** All relevant Test262 tests for WeakRef, FinalizationRegistry, and strict mode edge cases are promoted. `docs/JavaScript_Implementation_Checklist.md` and `docs/js_feature_matrix.md` updated. `just ci` passes.
## What NOT to Implement
- **No cycle-detecting garbage collector** -- `Rc<RefCell<>>` reference counting is the existing memory model. WeakRef/FinalizationRegistry are implemented using Rust `std::rc::Weak<>` for genuine weak references, but cleanup timing is implementation-defined per spec. True cycle collection deferred.
- **No `with` statement in sloppy mode** -- Parse `with` only to reject it in strict mode with a clear SyntaxError. Full sloppy-mode `with` semantics (scope chain manipulation) are out of scope due to complexity and the feature being deprecated. Document as known limitation.
- **No `arguments` object mutations** -- Strict mode `arguments` is a snapshot (not linked to parameters). Full sloppy-mode `arguments` aliasing (where `arguments[0]` and the first parameter are linked) is not required.
- **No `caller`/`callee` restrictions** -- `arguments.caller` and `arguments.callee` throwing TypeError in strict mode are deferred.
- **No `Object.defineProperty` strict enforcement** -- AC #8 depends on Story 3.5's property descriptor system. If Story 3.5 is not yet landed, skip AC #8 and note it as a dependency.
## Files to Modify
| File | Change |
|------|--------|
| `crates/js_vm/src/value.rs` | Add `Weak<RefCell<JsObjectData>>` support for WeakRef. Add `weak_ref_target: Option<Weak<RefCell<JsObjectData>>>` field to `JsObjectData`. Add `finalization_registry_data` field. |
| `crates/js_vm/src/interpreter/weakref_builtins.rs` | **New file** -- WeakRef constructor, `.deref()` method, setup function. |
| `crates/js_vm/src/interpreter/finalization_registry_builtins.rs` | **New file** -- FinalizationRegistry constructor, `.register()`, `.unregister()`, cleanup scheduling. |
| `crates/js_vm/src/interpreter/mod.rs` | Add `setup_weakref_builtins()`, `setup_finalization_registry_builtins()` calls. |
| `crates/js_vm/src/interpreter/expressions/calls.rs` | Add WeakRef/FinalizationRegistry method dispatch. |
| `crates/js_parser/src/parser/mod.rs` | Add `eval`/`arguments` identifier checks in strict mode via `check_strict_identifier()`. |
| `crates/js_parser/src/parser/statements.rs` | Add `with` statement parsing: reject in strict mode with SyntaxError, reject in sloppy mode with "not supported" error. Add parameter name validation for `eval`/`arguments` in strict functions. |
| `crates/js_parser/src/parser/expressions.rs` | Add `eval`/`arguments` assignment validation in strict mode. Validate octal escape sequences in string literals when in strict mode. |
| `crates/js_parser/src/lexer.rs` | Add legacy octal literal (`0[0-7]+`) detection and rejection in strict mode. Ensure `0o` octal works in both modes. |
| `crates/js_vm/src/interpreter/bytecode_exec.rs` | Verify assignment to undeclared variable throws ReferenceError in strict mode (bytecode path). |
| `crates/js_parser/src/parser/tests/strict_mode_tests.rs` | Add tests for `eval`/`arguments` restrictions, `with` rejection, octal rejection. |
| `crates/js_vm/src/interpreter/tests/strict_mode_tests.rs` | Add runtime strict mode tests. |
| `crates/js_vm/src/interpreter/tests/weakref_tests.rs` | **New file** -- WeakRef unit tests. |
| `crates/js_vm/src/interpreter/tests/finalization_registry_tests.rs` | **New file** -- FinalizationRegistry unit tests. |
| `tests/external/js262/js262_manifest.toml` | Promote passing tests. |
| `docs/JavaScript_Implementation_Checklist.md` | Check off WeakRef/FinalizationRegistry items. Update strict mode items. |
| `docs/old/js_feature_matrix.md` | Update coverage. |
## Tasks / Subtasks
- [ ] Task 1: Strict mode -- `eval` and `arguments` restrictions (AC: #4)
- [ ] 1.1 Add `eval` and `arguments` to strict identifier checks in `crates/js_parser/src/parser/mod.rs`:
- Extend `check_strict_identifier()` (or create new check) to reject `eval` and `arguments` as:
- Variable declarations: `var eval = 1`, `let arguments = 2`, `const eval = 3`
- Function names: `function eval() {}`, `function arguments() {}`
- Parameter names: `function f(eval) {}`, `function f(arguments) {}`
- Catch binding: `catch (eval) {}`
- Assignment targets: `eval = 1`, `arguments = 2` (in assignment expression)
- Error: `"'{}' cannot be used as identifier in strict mode"` (ECMAScript §13.1.1)
- [ ] 1.2 Add validation in `parse_var_declaration()` in `statements.rs`:
- After extracting binding name, check if strict and name is `eval` or `arguments`
- [ ] 1.3 Add validation in function parameter parsing in `statements.rs`:
- In `parse_params()` or equivalent, check each parameter name
- [ ] 1.4 Add validation in assignment expressions in `expressions.rs`:
- In `parse_assignment_expression()`, if LHS is Identifier("eval") or Identifier("arguments") and strict → SyntaxError
- [ ] 1.5 Add parser tests in `strict_mode_tests.rs`:
- `var eval = 1` in strict → SyntaxError
- `var arguments = 1` in strict → SyntaxError
- `eval = 1` in strict → SyntaxError
- `arguments = 1` in strict → SyntaxError
- `function eval() {}` in strict → SyntaxError
- `function f(eval) {}` in strict → SyntaxError
- `function f(arguments) {}` in strict → SyntaxError
- `catch (eval) {}` in strict → SyntaxError
- All above work fine in sloppy mode (no error)
- [ ] Task 2: Strict mode -- `with` statement (AC: #5)
- [ ] 2.1 Add `with` keyword recognition in `crates/js_parser/src/token.rs`:
- Add `With` to `TokenKind` and `keyword_from_str()` (if not already present)
- [ ] 2.2 Add `with` statement parsing in `parse_statement()` in `statements.rs`:
- When `TokenKind::With` encountered:
- If strict mode → `CompileError: "'with' statement not allowed in strict mode"` (ECMAScript §14.11.1)
- If sloppy mode → `CompileError: "'with' statement is not supported"` (known limitation, not spec-compliant but acceptable)
- Parse just enough to give a good error: consume `with`, `(`, expression, `)`, statement
- [ ] 2.3 Add parser tests:
- `"use strict"; with (obj) { x; }` → SyntaxError
- `with (obj) { x; }` in sloppy → error (unsupported, not SyntaxError -- different message)
- [ ] Task 3: Strict mode -- octal literal and escape rejection (AC: #6)
- [ ] 3.1 Add legacy octal literal detection in `crates/js_parser/src/lexer.rs`:
- When number starts with `0` followed by `[0-7]` (e.g., `010`, `077`):
- If strict mode → `CompileError: "Octal literals are not allowed in strict mode"` (ECMAScript §B.1.1)
- If sloppy mode → parse as octal number (or decimal -- implementation choice)
- `0o10` (ES2015 explicit octal) allowed in both modes
- `0x10` (hex) allowed in both modes
- `0b10` (binary) allowed in both modes
- **IMPORTANT**: The lexer needs access to strict mode flag. Either pass it as parameter or check after lexing.
- [ ] 3.2 Add octal escape sequence rejection in string literals:
- In string literal parsing, when `\` followed by `[1-7]` or `\0` followed by `[0-9]`:
- If strict mode → `CompileError: "Octal escape sequences are not allowed in strict mode"` (ECMAScript §B.1.2)
- `\0` alone (null character escape, NOT followed by digit) is always allowed
- `\x41` (hex escape) and `\u0041` (unicode escape) always allowed
- [ ] 3.3 Handle strict mode flag in lexer:
- Option A: Lexer has `strict: bool` field, set by parser when `"use strict"` is detected, then re-lex
- Option B: Lexer always lexes octals, parser validates after receiving token
- **Recommendation**: Option B is simpler -- lexer produces `OctalLiteral(value)` or `OctalEscapeSequence` tokens, parser rejects in strict mode
- [ ] 3.4 Add tests:
- `"use strict"; var x = 010;` → SyntaxError
- `"use strict"; var s = "\1";` → SyntaxError
- `var x = 010;` in sloppy → OK (value 8)
- `var x = 0o10;` → OK in both modes (value 8)
- `"use strict"; var s = "\0";` → OK (null character, no digit following)
- `"use strict"; var s = "\01";` → SyntaxError
- [ ] Task 4: Strict mode -- undeclared variable assignment (AC: #7)
- [ ] 4.1 Verify bytecode VM path in `crates/js_vm/src/interpreter/bytecode_exec.rs`:
- When `SetGlobal` or `SetVar` targets an undeclared variable in strict mode:
- Must throw ReferenceError, NOT silently create a global
- Check `Environment::set()` behavior -- it may already throw for undeclared
- The AST interpreter path already works (env.set() throws ReferenceError)
- [ ] 4.2 Add explicit test in `crates/js_vm/src/interpreter/tests/strict_mode_tests.rs`:
- `"use strict"; undeclaredVar = 42;` → ReferenceError
- `undeclaredVar = 42;` in sloppy → creates global (or throws if not supported)
- [ ] 4.3 Verify strict flag propagation in bytecode compiler:
- `FunctionBlueprint.strict: bool` must be set correctly from `Program.strict` and `Function.strict`
- Check `crates/js_vm/src/bytecode/compiler/` for strict flag handling
- [ ] Task 5: WeakRef implementation (AC: #1, #3)
- [ ] 5.1 Add WeakRef infrastructure to `crates/js_vm/src/value.rs`:
- Add `weak_ref_target: Option<std::rc::Weak<RefCell<JsObjectData>>>` field to `JsObjectData`
- Add `init_weakref(target: &JsObject)`, `is_weakref()`, `deref_weakref() -> Option<JsObject>` helpers
- `JsObject` is `Rc<RefCell<JsObjectData>>` -- use `Rc::downgrade()` to create `Weak`
- `deref_weakref()` calls `Weak::upgrade()` -- returns `Some` if target still alive, `None` if all strong refs dropped
- [ ] 5.2 Create `crates/js_vm/src/interpreter/weakref_builtins.rs`:
- `setup_weakref_builtins(env: &mut Environment)` -- register WeakRef constructor and prototype
- Constructor: `new WeakRef(target)` -- TypeError if target is not an object. Store `Rc::downgrade(&target.inner)`.
- **TypeError** if called without `new`
- [ ] 5.3 Implement `WeakRef.prototype.deref()` (ECMAScript §26.1.3.2):
- Call `Weak::upgrade()` on stored weak reference
- If `Some(inner)` → return `JsValue::Object(JsObject { inner })`
- If `None` → return `JsValue::Undefined`
- **Note**: With `Rc<>`, this will almost always return the target unless all other strong references have been dropped (e.g., variable went out of scope). This is spec-compliant -- timing is implementation-defined.
- [ ] 5.4 Wire WeakRef dispatch in `calls.rs` and add `setup_weakref_builtins()` in `mod.rs`
- [ ] 5.5 Add unit tests in `crates/js_vm/src/interpreter/tests/weakref_tests.rs`:
- `new WeakRef({})` → object created
- `weakRef.deref()` returns target when still referenced
- TypeError on primitive target
- TypeError when called without `new`
- `.deref()` returns `undefined` after target is unreachable (test by dropping all strong refs in Rust test harness -- may not be testable from JS)
- [ ] Task 6: FinalizationRegistry implementation (AC: #2, #3)
- [ ] 6.1 Add FinalizationRegistry infrastructure to `crates/js_vm/src/value.rs`:
- `FinalizationRegistryData` struct:
```
cleanup_callback: JsValue, // The callback function
registrations: Vec<FinalizationEntry>,
```
- `FinalizationEntry` struct:
```
target: Weak<RefCell<JsObjectData>>, // Weak reference to watched object
held_value: JsValue, // Value passed to callback
unregister_token: Option<usize>, // Pointer identity of unregister token
```
- Add `finalization_registry_data: Option<FinalizationRegistryData>` to `JsObjectData`
- Add helpers: `init_finalization_registry()`, `is_finalization_registry()`
- [ ] 6.2 Create `crates/js_vm/src/interpreter/finalization_registry_builtins.rs`:
- `setup_finalization_registry_builtins(env: &mut Environment)`
- Constructor: `new FinalizationRegistry(callback)` -- TypeError if callback is not callable
- **TypeError** if called without `new`
- [ ] 6.3 Implement FinalizationRegistry prototype methods:
- `.register(target, heldValue, unregisterToken?)` (ECMAScript §26.2.3.2):
- TypeError if `target` is not an object
- TypeError if `target === unregisterToken` (same object)
- Store weak reference to target, heldValue, and unregisterToken identity
- `.unregister(unregisterToken)` (ECMAScript §26.2.3.3):
- Remove all registrations with matching unregisterToken
- Return boolean (true if any removed)
- [ ] 6.4 Implement cleanup scheduling:
- Add `cleanup_finalization_registries()` method to interpreter
- For each registry: iterate entries, check if `target.upgrade()` returns None
- If target is dead: call cleanup callback with `heldValue`, remove entry
- **Scheduling**: Call `cleanup_finalization_registries()` at microtask checkpoint (after each script execution, same point where microtasks are drained)
- **Note**: With Rc<>, targets are only "dead" when all strong refs are dropped. In practice, most JS objects will have strong refs until the VM shuts down. This is acceptable per spec (cleanup timing is implementation-defined).
- [ ] 6.5 Wire dispatch in `calls.rs` and setup in `mod.rs`
- [ ] 6.6 Add unit tests in `crates/js_vm/src/interpreter/tests/finalization_registry_tests.rs`:
- Constructor with callback
- `.register()` and `.unregister()` basic operations
- TypeError on primitive target
- TypeError on non-callable callback
- Cleanup callback invocation (test by manually dropping strong refs in Rust test harness)
- [ ] Task 7: Testing and validation (AC: #9)
- [ ] 7.1 Run vendored Test262 suite and promote passing tests:
- `cargo test -p rust_browser --test js262_harness js262_suite_matches_manifest_expectations -- --nocapture`
- `just js262-status promote --id <test-id>`
- [ ] 7.2 Run full Test262 suite and triage:
- `just test262-full`
- `just triage-test262-full`
- [ ] 7.3 Run all existing JS test suites to verify no regressions:
- `cargo test -p js_vm`
- `cargo test -p rust_browser --test js_tests`
- `cargo test -p rust_browser --test js_dom_tests`
- `cargo test -p rust_browser --test js_events`
- `cargo test -p rust_browser --test js_scheduling`
- `cargo test -p rust_browser --test js_async`
- `cargo test -p rust_browser --test js_modules`
- [ ] 7.4 Update `docs/JavaScript_Implementation_Checklist.md`:
- Check off WeakRef/FinalizationRegistry
- Update strict mode section with new checks
- [ ] 7.5 Update `docs/old/js_feature_matrix.md`
- [ ] 7.6 Run `just ci` -- full validation pass
## Dev Notes
### Key Architecture Decisions
**WeakRef uses `std::rc::Weak<RefCell<JsObjectData>>`.** This is the natural Rust equivalent of a weak reference in an `Rc<>`-based system. `Rc::downgrade()` creates a `Weak`, and `Weak::upgrade()` returns `Some` if any strong refs remain. This gives us real weak reference semantics within Rust's reference counting model -- objects that are truly unreachable (no more `Rc` clones anywhere) will return `None` from `deref()`.
**FinalizationRegistry cleanup is best-effort.** The spec explicitly says cleanup timing is implementation-defined (ECMAScript §9.10.3). Our implementation checks for dead targets during microtask checkpoints. In practice, with `Rc<>`, objects are rarely "dead" while JS code still runs (some `Rc` clone usually exists). This is acceptable and spec-compliant.
**`with` statement is parsed only to reject.** Implementing full `with` semantics (runtime scope chain manipulation) is complex and the feature is deprecated. Parse enough to give a clear error in both strict (SyntaxError) and sloppy (unsupported) modes.
**Strict mode octal handling needs lexer-parser coordination.** The lexer doesn't currently know about strict mode. Two approaches: (1) lexer always produces tokens, parser validates based on strict flag, or (2) parser feeds strict flag back to lexer. Approach (1) is simpler -- have the lexer tag octal literals and escape sequences, then the parser rejects them in strict context.
### Current Strict Mode State
Already implemented (Phase 9 complete):
- `"use strict"` directive prologue detection with escape sequence guard
- Function-scoped and inherited strict mode
- Reserved word enforcement (7 words + yield)
- Duplicate parameter rejection
- Non-simple parameter + `"use strict"` rejection
- `delete identifier` prohibition (parse-time)
- `this` is `undefined` in non-constructor strict calls
- `.call()` does not coerce non-object `this`
**Key files:**
- Parser strict checks: `crates/js_parser/src/parser/mod.rs:148-163` (reserved words), `mod.rs:360-382` (directive)
- Runtime strict: `crates/js_vm/src/interpreter/expressions/calls.rs:821-839` (this binding), `calls.rs:666-682` (.call coercion)
- Parser tests: `crates/js_parser/src/parser/tests/strict_mode_tests.rs` (381 lines)
- VM tests: `crates/js_vm/src/interpreter/tests/strict_mode_tests.rs` (338 lines)
### Implementation Patterns
**WeakMap/WeakSet dispatch pattern** (in `calls.rs`):
```rust
if obj.is_weakmap() {
match method_name.as_str() {
"get" => { ... }
"set" => { ... }
_ => {}
}
}
```
WeakRef and FinalizationRegistry follow this same dispatch pattern.
**Strict identifier check** (in `parser/mod.rs`):
```rust
fn check_strict_identifier(&self, name: &str) -> Result<(), CompileError> {
if self.strict && is_strict_reserved_word(name) {
Err(CompileError::new(...))
} else { Ok(()) }
}
```
Extend this to also check for `"eval"` and `"arguments"`.
### Critical Implementation Details
**`Weak<RefCell<JsObjectData>>` requires JsObject to expose its `Rc`.** Currently `JsObject` wraps `Rc<RefCell<JsObjectData>>` as `inner`. WeakRef needs `Rc::downgrade(&obj.inner)`. This should work directly since `inner` is accessible within the `js_vm` crate.
**Octal literal edge cases:**
- `0` alone is decimal zero, NOT octal -- don't reject
- `08`, `09` are decimal (invalid octal digits) -- don't reject as octal
- `010` is legacy octal = 8 -- reject in strict
- `0o10` is ES2015 octal = 8 -- allowed in strict
- `"\0"` is null char escape -- always allowed
- `"\01"` is octal escape -- reject in strict
- `"\8"` and `"\9"` are non-octal -- per spec, these are identity escapes (allowed even in strict per ES2021 Annex B revision)
**FinalizationRegistry target === unregisterToken check.** The spec says `register(target, heldValue, unregisterToken)` must throw if `target` is the same as `unregisterToken`. Use `Rc::ptr_eq()` for object identity comparison.
### Dependencies
**Story 3.5 (property descriptors):** AC #8 (strict mode property assignment errors) depends on the property descriptor system from Story 3.5. If Story 3.5 is not yet complete, skip AC #8 and document it as a follow-up.
**No dependency on Story 3.6.** WeakRef/FinalizationRegistry are independent of Date/Map/Set.
### Previous Story Intelligence
From Story 3.6 (Date/RegExp/Map/Set):
- New builtin types follow consistent pattern: `value.rs` data fields, `*_builtins.rs` file, `setup_*_builtins()` call, dispatch in `calls.rs`
- `indexmap` available as dependency
- No `unsafe` code needed
From Story 3.4 (ES modules) review lessons:
- Don't leave stub code -- wire everything into execution paths
- Tests must test actual code paths
- Run `just ci` after each task
### Risk Assessment
**LOW: WeakRef.** Straightforward wrapper around `Rc::downgrade()`/`Weak::upgrade()`. Small API surface.
**LOW: Strict mode `eval`/`arguments` checks.** Simple parse-time validation. Well-defined spec behavior. Existing `check_strict_identifier()` pattern to follow.
**MEDIUM: FinalizationRegistry cleanup scheduling.** Need to integrate cleanup checks into the microtask/event loop. Finding the right hook point requires understanding the execution pipeline in `app_browser`/`browser_runtime`.
**MEDIUM: Octal literal/escape handling.** Lexer-parser coordination for strict mode. Multiple edge cases (`\0` vs `\01`, `08` vs `010`). Need careful testing.
**LOW: `with` statement.** Parse-only, reject in all modes. No runtime semantics to implement.
### Phased Implementation Strategy
**Phase A -- Strict Mode Fixes (Tasks 1-4):** Quick wins. Parse-time checks with existing infrastructure. Can be completed independently.
**Phase B -- WeakRef (Task 5):** Small, focused implementation using `Rc::downgrade()`.
**Phase C -- FinalizationRegistry (Task 6):** More complex due to cleanup scheduling. Depends on understanding microtask checkpoint locations.
**Phase D -- Testing + Validation (Task 7):** After all implementations.
### Project Structure Notes
- Parser changes in `crates/js_parser/src/` (Layer 1) -- no layer violations
- VM changes in `crates/js_vm/src/interpreter/` (Layer 1) -- new files for WeakRef, FinalizationRegistry
- Value type changes in `crates/js_vm/src/value.rs` (Layer 1)
- No `unsafe` code needed
- No new external dependencies
### References
- [ECMAScript §26.1 -- WeakRef Objects](https://tc39.es/ecma262/#sec-weak-ref-objects) -- WeakRef constructor and prototype
- [ECMAScript §26.2 -- FinalizationRegistry Objects](https://tc39.es/ecma262/#sec-finalization-registry-objects) -- FinalizationRegistry
- [ECMAScript §9.10 -- CleanupFinalizationRegistry](https://tc39.es/ecma262/#sec-cleanup-finalization-registry) -- Cleanup abstract operation
- [ECMAScript §13.1.1 -- Static Semantics: Early Errors](https://tc39.es/ecma262/#sec-identifiers-static-semantics-early-errors) -- eval/arguments restrictions
- [ECMAScript §14.11 -- The with Statement](https://tc39.es/ecma262/#sec-with-statement) -- with statement and strict mode
- [ECMAScript §B.1.1 -- Numeric Literals](https://tc39.es/ecma262/#sec-additional-syntax-numeric-literals) -- Legacy octal
- [ECMAScript §B.1.2 -- String Literals](https://tc39.es/ecma262/#sec-additional-syntax-string-literals) -- Octal escape sequences
- [Source: crates/js_parser/src/parser/mod.rs:148-163] -- Existing strict reserved word check
- [Source: crates/js_parser/src/parser/mod.rs:360-382] -- "use strict" directive detection
- [Source: crates/js_vm/src/interpreter/expressions/calls.rs:821-839] -- Strict this binding
- [Source: crates/js_vm/src/interpreter/weakmap_builtins.rs] -- WeakMap pattern to follow
- [Source: crates/js_vm/src/value.rs] -- JsObject is Rc<RefCell<JsObjectData>>
- [Source: _bmad-output/planning-artifacts/epics.md#Story 3.7] -- Story requirements
## Dev Agent Record
### Agent Model Used
{{agent_model_name_version}}
### Debug Log References
### Completion Notes List
### File List