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>
28 KiB
Story 3.6: Built-in Completeness -- Date/RegExp/Map/Set
Status: ready-for-dev
Story
As a web developer using JavaScript, I want Date, RegExp, Map, Set, WeakMap, and WeakSet to work completely, So that date handling, pattern matching, and collection types function correctly.
Acceptance Criteria
-
Date construction and methods:
new Date(),new Date(value),new Date(year, month, ...),Date.now(),Date.parse(),Date.UTC()all construct dates correctly. Getter methods (getFullYear,getMonth,getDate,getDay,getHours,getMinutes,getSeconds,getMilliseconds,getTime,getTimezoneOffset) and their UTC variants return correct values. Setter methods (setFullYear,setMonth,setDate,setHours,setMinutes,setSeconds,setMilliseconds,setTime) modify dates correctly. Conversion methods (toISOString,toJSON,toDateString,toTimeString,toString,valueOf) produce spec-compliant output. Invalid dates returnNaNfromgetTime()and"Invalid Date"fromtoString(). Per ECMAScript §21.4. -
RegExp completeness: Extend existing RegExp with: function replacers in
String.prototype.replace(), named capture groups(?<name>...)with$<name>substitution (requiresregresscrate support check),RegExp.prototype[Symbol.match],[Symbol.replace],[Symbol.search],[Symbol.split]well-known symbol methods. Per ECMAScript §22.2. -
Map complete:
new Map(iterable?)constructor. Methods:.get(key),.set(key, value),.has(key),.delete(key),.clear(),.forEach(callback, thisArg). Property:.size. Iterators:.entries(),.keys(),.values(),[Symbol.iterator]. Keys use SameValueZero equality (NaN === NaN,-0 === +0). Insertion order preserved. Per ECMAScript §24.1. -
Set complete:
new Set(iterable?)constructor. Methods:.add(value),.has(value),.delete(value),.clear(),.forEach(callback, thisArg). Property:.size. Iterators:.entries(),.keys(),.values(),[Symbol.iterator]. Values use SameValueZero equality. Insertion order preserved. Per ECMAScript §24.2. -
WeakSet complete:
new WeakSet(iterable?)constructor. Methods:.add(value),.has(value),.delete(value). Object-only keys (TypeError for primitives). Per ECMAScript §24.4. -
WeakMap enhancements: Constructor accepts iterable of
[key, value]pairs:new WeakMap(iterable?). Existingget/set/has/deletemethods already implemented -- verify function keys work correctly (current limitation: function keys may throw TypeError). -
Callback error propagation:
Map.forEach,Set.forEach, andArray.from(map/set)correctly propagate thrown errors from callbacks without corrupting collection internal state. -
Test262 tests promoted: All relevant vendored and full-suite Test262 tests for Date, RegExp, Map, Set, WeakMap, WeakSet are promoted.
docs/JavaScript_Implementation_Checklist.mdanddocs/js_feature_matrix.mdupdated.just cipasses.
What NOT to Implement
- No
Intl.DateTimeFormat-- locale-sensitive Date formatting (toLocaleDateString,toLocaleTimeString,toLocaleString) return English-only fixed format. Full Intl support out of scope. - No timezone database -- timezone offset uses the host system's local timezone via
chronoorstd::time. Named timezone support (IANA) deferred. - No RegExp lookbehind --
(?<=...)and(?<!...)depend onregresscrate support. Ifregressdoesn't support them, document as known limitation. - No RegExp
dflag (hasIndices) -- ES2022 match indices deferred unlessregressprovides match positions (it likely does viaMatch::start()/Match::end()-- investigate). - No
RegExp.prototype.compile()-- deprecated legacy method, skip. - No
Map/Setsubclassing --class MyMap extends MapandSymbol.speciesdeferred. - No garbage collection for WeakMap/WeakSet -- entries remain until explicit deletion. Documenting as known limitation (Rust's
Rc<RefCell<>>prevents true weak references without architectural changes).
Files to Modify
| File | Change |
|---|---|
crates/js_vm/src/value.rs |
Add PrimitiveValue::Date(f64) variant for Date internal time value. Add map_data: Option<OrderedMap> and set_data: Option<OrderedSet> and weak_set_data: Option<HashSet<usize>> fields to JsObjectData. |
crates/js_vm/src/interpreter/date_builtins.rs |
New file -- Date constructor, all Date prototype methods (getters, setters, conversion), Date static methods (now, parse, UTC). |
crates/js_vm/src/interpreter/map_builtins.rs |
New file -- Map constructor, prototype methods (get, set, has, delete, clear, forEach, entries, keys, values, size), setup function. |
crates/js_vm/src/interpreter/set_builtins.rs |
New file -- Set constructor, prototype methods (add, has, delete, clear, forEach, entries, keys, values, size), setup function. |
crates/js_vm/src/interpreter/weakmap_builtins.rs |
Add iterable constructor support. Fix function-as-key limitation. |
crates/js_vm/src/interpreter/weakset_builtins.rs |
New file -- WeakSet constructor (with iterable), add, has, delete. Same pointer-identity pattern as WeakMap. |
crates/js_vm/src/interpreter/regexp_builtins.rs |
Add Symbol.match/replace/search/split well-known symbol methods. |
crates/js_vm/src/interpreter/builtins.rs |
Add function replacer support to String.prototype.replace(). Add named capture group $<name> substitution. |
crates/js_vm/src/interpreter/mod.rs |
Add setup_date_builtins(), setup_map_builtins(), setup_set_builtins(), setup_weakset_builtins() calls. |
crates/js_vm/src/interpreter/expressions/calls.rs |
Add Map/Set/WeakSet method dispatch alongside existing WeakMap dispatch. |
crates/js_vm/src/interpreter/tests/date_tests.rs |
New file -- Date unit tests. |
crates/js_vm/src/interpreter/tests/map_tests.rs |
New file -- Map unit tests. |
crates/js_vm/src/interpreter/tests/set_tests.rs |
New file -- Set unit tests. |
crates/js_vm/src/interpreter/tests/weakset_tests.rs |
New file -- WeakSet unit tests. |
crates/js_vm/src/interpreter/tests/regexp_tests.rs |
Add tests for function replacers, Symbol methods. |
tests/external/js262/js262_manifest.toml |
Promote passing tests. |
docs/JavaScript_Implementation_Checklist.md |
Check off Phase 20 (Map, Set, WeakSet) and Phase 22 (Date) items. |
docs/old/js_feature_matrix.md |
Update built-in coverage. |
Tasks / Subtasks
-
Task 1: Map implementation (AC: #3, #7)
- 1.1 Add ordered map storage to
crates/js_vm/src/value.rs:- Add
map_data: Option<IndexMap<MapKey, JsValue>>field toJsObjectData MapKeyenum: wrapsJsValuewith customEq/Hashusing SameValueZero semantics- SameValueZero:
NaN == NaN(true),+0 == -0(true), otherwise strict equality - For object keys: use
Rc::as_ptr()identity (same as WeakMap) - For
NaN: canonical hash/eq so all NaN variants match - Use
indexmapcrate (already a workspace dependency) for insertion-order preservation - Add
init_map(),is_map()helpers onJsObject
- Add
- 1.2 Create
crates/js_vm/src/interpreter/map_builtins.rs:setup_map_builtins(env: &mut Environment)-- registersMapconstructor and prototype- Constructor:
new Map()creates empty map.new Map(iterable)iterates entries and calls.set()for each[key, value]pair. - TypeError if called without
new
- 1.3 Implement Map prototype methods:
.set(key, value)-- insert/update, returnthisfor chaining (ECMAScript §24.1.3.9).get(key)-- return value orundefined(ECMAScript §24.1.3.6).has(key)-- return boolean (ECMAScript §24.1.3.7).delete(key)-- remove and return boolean (ECMAScript §24.1.3.3).clear()-- remove all entries (ECMAScript §24.1.3.2).size-- getter returning entry count (ECMAScript §24.1.3.10)
- 1.4 Implement Map callback and iterator methods:
.forEach(callback, thisArg)-- callcallback(value, key, map)for each entry in insertion order. Propagate errors from callback without corrupting map state..entries()-- return iterator yielding[key, value]arrays (ECMAScript §24.1.3.4).keys()-- return iterator yielding keys (ECMAScript §24.1.3.8).values()-- return iterator yielding values (ECMAScript §24.1.3.11)Map.prototype[Symbol.iterator]=Map.prototype.entries(ECMAScript §24.1.3.12)- Iterators reuse generator/iterator protocol from Story 3.2
- 1.5 Wire Map dispatch in
crates/js_vm/src/interpreter/expressions/calls.rs:- Follow WeakMap dispatch pattern (check
obj.is_map(), then fast-path method resolution)
- Follow WeakMap dispatch pattern (check
- 1.6 Add
setup_map_builtins()call incrates/js_vm/src/interpreter/mod.rs - 1.7 Add unit tests in
crates/js_vm/src/interpreter/tests/map_tests.rs:- Basic CRUD operations, constructor with iterable, size, chaining
- NaN key equality (
map.set(NaN, 1); map.get(NaN) === 1) -0and+0treated as same key- Object key identity (two
{}are different keys) - Insertion order preservation in iteration
- forEach error propagation
- Iterator protocol compliance
- 1.1 Add ordered map storage to
-
Task 2: Set implementation (AC: #4, #7)
- 2.1 Add ordered set storage to
crates/js_vm/src/value.rs:- Add
set_data: Option<IndexSet<MapKey>>field toJsObjectData - Reuse
MapKeyfrom Map implementation (same SameValueZero equality) - Use
indexmap::IndexSetfor insertion-order preservation - Add
init_set(),is_set()helpers onJsObject
- Add
- 2.2 Create
crates/js_vm/src/interpreter/set_builtins.rs:setup_set_builtins(env: &mut Environment)-- registersSetconstructor and prototype- Constructor:
new Set()creates empty set.new Set(iterable)iterates and calls.add()for each value. - TypeError if called without
new
- 2.3 Implement Set prototype methods:
.add(value)-- insert value, returnthisfor chaining (ECMAScript §24.2.3.1).has(value)-- return boolean (ECMAScript §24.2.3.5).delete(value)-- remove and return boolean (ECMAScript §24.2.3.4).clear()-- remove all values (ECMAScript §24.2.3.2).size-- getter returning count (ECMAScript §24.2.3.9)
- 2.4 Implement Set callback and iterator methods:
.forEach(callback, thisArg)-- callcallback(value, value, set)(note: value passed as both args per spec). Propagate errors..entries()-- return iterator yielding[value, value](ECMAScript §24.2.3.5).keys()-- alias for.values()(ECMAScript §24.2.3.8).values()-- return iterator yielding values (ECMAScript §24.2.3.10)Set.prototype[Symbol.iterator]=Set.prototype.values(ECMAScript §24.2.3.11)
- 2.5 Wire Set dispatch in
crates/js_vm/src/interpreter/expressions/calls.rs - 2.6 Add
setup_set_builtins()call incrates/js_vm/src/interpreter/mod.rs - 2.7 Add unit tests in
crates/js_vm/src/interpreter/tests/set_tests.rs:- Basic add/has/delete/clear, constructor with iterable, size
- NaN equality,
-0/+0treated as same, object identity - Insertion order preservation
- forEach with
(value, value, set)args - Deduplication (adding same value twice keeps one entry)
- 2.1 Add ordered set storage to
-
Task 3: WeakSet implementation (AC: #5)
- 3.1 Add weak set storage to
crates/js_vm/src/value.rs:- Add
weak_set_data: Option<HashSet<usize>>field toJsObjectData - Use
Rc::as_ptr()identity (same pattern as WeakMap) - Add
init_weakset(),is_weakset()helpers onJsObject
- Add
- 3.2 Create
crates/js_vm/src/interpreter/weakset_builtins.rs:setup_weakset_builtins(env: &mut Environment)- Constructor:
new WeakSet()/new WeakSet(iterable)-- iterable items must be objects
- 3.3 Implement WeakSet prototype methods:
.add(value)-- TypeError if not object. Add by pointer identity. Returnthis..has(value)-- return boolean.delete(value)-- remove and return boolean
- 3.4 Wire WeakSet dispatch alongside WeakMap in
calls.rs - 3.5 Add
setup_weakset_builtins()call inmod.rs - 3.6 Add unit tests in
crates/js_vm/src/interpreter/tests/weakset_tests.rs:- Basic add/has/delete, TypeError on primitive values
- Constructor with iterable of objects
- Object identity semantics
- 3.1 Add weak set storage to
-
Task 4: WeakMap enhancements (AC: #6)
- 4.1 Add iterable constructor to WeakMap in
weakmap_builtins.rs:new WeakMap(iterable)-- iterate[key, value]pairs, key must be object
- 4.2 Fix function-as-key limitation:
JsValue::Functionshould be accepted as a WeakMap/WeakSet key (functions are objects per spec)- Use function pointer identity for keying
- 4.3 Add unit tests for iterable constructor and function keys
- 4.1 Add iterable constructor to WeakMap in
-
Task 5: Date implementation (AC: #1)
- 5.1 Add Date internal value storage to
crates/js_vm/src/value.rs:- Add
PrimitiveValue::Date(f64)variant -- stores milliseconds since epoch (orNaNfor invalid) - Add
init_date(time_value: f64),is_date(),date_value() -> f64helpers onJsObject
- Add
- 5.2 Create
crates/js_vm/src/interpreter/date_builtins.rs:setup_date_builtins(env: &mut Environment)-- registersDateconstructor and prototype
- 5.3 Implement Date constructors (ECMAScript §21.4.2):
Date()(no new) -- return current date as stringnew Date()-- current time in milliseconds (std::time::SystemTime::now())new Date(value)-- if number, use as milliseconds. If string, parse (ISO 8601 subset)new Date(year, month, date?, hours?, minutes?, seconds?, ms?)-- component constructor- Month is 0-indexed (0=January), year 0-99 maps to 1900-1999
- 5.4 Implement Date static methods:
Date.now()-- return current time in ms (ECMAScript §21.4.3.1)Date.parse(string)-- parse ISO 8601 date string, return ms or NaN (ECMAScript §21.4.3.2)Date.UTC(year, month, ...)-- like component constructor but in UTC (ECMAScript §21.4.3.4)
- 5.5 Implement Date prototype getter methods (ECMAScript §21.4.4):
- Local time getters:
getFullYear(),getMonth(),getDate(),getDay(),getHours(),getMinutes(),getSeconds(),getMilliseconds() getTime()-- return internal time value (ms since epoch)getTimezoneOffset()-- return local timezone offset in minutes- UTC getters:
getUTCFullYear(),getUTCMonth(),getUTCDate(),getUTCDay(),getUTCHours(),getUTCMinutes(),getUTCSeconds(),getUTCMilliseconds() - All getters return
NaNif internal time value isNaN(Invalid Date)
- Local time getters:
- 5.6 Implement Date prototype setter methods:
setTime(time),setMilliseconds(ms),setSeconds(sec, ms?),setMinutes(min, sec?, ms?),setHours(hour, min?, sec?, ms?),setDate(date),setMonth(month, date?),setFullYear(year, month?, date?)- UTC setters:
setUTCFullYear(),setUTCMonth(),setUTCDate(),setUTCHours(),setUTCMinutes(),setUTCSeconds(),setUTCMilliseconds() - Each setter recalculates the internal time value and returns it
- 5.7 Implement Date conversion methods:
toString()-- e.g."Sun Mar 16 2026 12:30:00 GMT-0400 (Eastern Daylight Time)"(simplified format acceptable)toDateString()-- e.g."Sun Mar 16 2026"toTimeString()-- e.g."12:30:00 GMT-0400"toISOString()-- e.g."2026-03-16T16:30:00.000Z"(always UTC, throws RangeError if invalid)toJSON()-- callstoISOString(), returns null if invalid (ECMAScript §21.4.4.37)toUTCString()-- e.g."Sun, 16 Mar 2026 16:30:00 GMT"valueOf()-- returngetTime()(enables numeric comparison with<,>)[Symbol.toPrimitive](hint)-- "default"/"number" returns valueOf, "string" returns toString
- 5.8 Implement Date internal helpers:
ms_to_components(time_value: f64) -> DateComponents-- decompose ms to year/month/day/hour/min/sec/ms in local timems_to_utc_components(time_value: f64) -> DateComponents-- same but UTCcomponents_to_ms(year, month, day, hour, min, sec, ms) -> f64-- recompose to mslocal_tz_offset_ms() -> f64-- get system local timezone offset- Use
std::time::SystemTimeforDate.now(). Use manual arithmetic for component decomposition (avoid addingchronodependency -- it's large). - Date math reference: ECMAScript §21.4.1 defines
Day(t),TimeWithinDay(t),DaysInYear(y),DayFromYear(y),TimeFromYear(y),YearFromTime(t),InLeapYear(t),MonthFromTime(t),DateFromTime(t),WeekDay(t),LocalTime(t),UTC(t),MakeTime(),MakeDay(),MakeDate(),TimeClip(). Implement these helper functions.
- 5.9 Wire Date dispatch in
calls.rsand addsetup_date_builtins()inmod.rs - 5.10 Add unit tests in
crates/js_vm/src/interpreter/tests/date_tests.rs:- All constructor forms (no args, ms, string, components)
- Invalid date detection (
new Date("invalid").getTime() === NaN) - All getters for a known date (e.g.,
new Date(2024, 0, 15, 10, 30, 45, 500)) - All setters modifying a date and reading back
toISOString()output formatDate.now()returns a reasonable valueDate.parse()for ISO stringsDate.UTC()constructor- Comparison operators work via
valueOf() - Edge cases: leap years, month overflow, year 0-99 mapping, negative timestamps
- 5.1 Add Date internal value storage to
-
Task 6: RegExp enhancements (AC: #2)
- 6.1 Add function replacer support to
String.prototype.replace()inbuiltins.rs:- When second argument is a function, call it with
(match, ...captures, index, fullString) - Return value of function becomes replacement string
- Works with both string and regex patterns
- For regex with
/gflag: call function for each match
- When second argument is a function, call it with
- 6.2 Add
$<name>named capture group substitution inString.prototype.replace():- Check if
regresscrate supports named capture groups first - If supported: extract group name, look up in match captures
- If not supported: document as known limitation, skip
- Check if
- 6.3 Add RegExp Symbol method implementations in
regexp_builtins.rs:RegExp.prototype[Symbol.match](string)-- same behavior as currentString.prototype.match()delegation (ECMAScript §22.2.6.8)RegExp.prototype[Symbol.replace](string, replaceValue)-- (ECMAScript §22.2.6.10)RegExp.prototype[Symbol.search](string)-- (ECMAScript §22.2.6.11)RegExp.prototype[Symbol.split](string, limit)-- (ECMAScript §22.2.6.12)- Wire these as Symbol-keyed properties on RegExp.prototype
- 6.4 Add unit tests for function replacers and Symbol methods in
regexp_tests.rs
- 6.1 Add function replacer support to
-
Task 7: Testing and validation (AC: #8)
- 7.1 Run vendored Test262 suite and promote passing tests:
cargo test -p rust_browser --test js262_harness js262_suite_matches_manifest_expectations -- --nocapturejust js262-status promote --id <test-id>for each newly passing test
- 7.2 Run full Test262 suite and triage:
just test262-fulljust triage-test262-full
- 7.3 Run all existing JS test suites to verify no regressions:
cargo test -p js_vm(all unit tests)cargo test -p rust_browser --test js_testscargo test -p rust_browser --test js_dom_testscargo test -p rust_browser --test js_eventscargo test -p rust_browser --test js_schedulingcargo test -p rust_browser --test js_asynccargo test -p rust_browser --test js_modules
- 7.4 Update
docs/JavaScript_Implementation_Checklist.md:- Check off Phase 20 items (Map, Set, WeakSet)
- Check off Phase 22 items (Date)
- Update RegExp section with new features
- 7.5 Update
docs/old/js_feature_matrix.mdwith expanded coverage - 7.6 Run
just ci-- full validation pass
- 7.1 Run vendored Test262 suite and promote passing tests:
Dev Notes
Key Architecture Decisions
Map/Set use indexmap crate (already a dependency). IndexMap and IndexSet preserve insertion order, which is required by the spec. Do NOT use HashMap/HashSet -- they don't preserve order. The indexmap crate is already in [workspace.dependencies].
Date stores f64 milliseconds since epoch. This is the internal [[DateValue]] per ECMAScript §21.4.1. Use PrimitiveValue::Date(f64) on the JsObject, following the same pattern as PrimitiveValue::RegExp(RegExpData). NaN represents an Invalid Date.
No chrono dependency for Date. Implement ECMAScript date math helpers directly per §21.4.1. The spec defines all the conversion functions (Day(t), YearFromTime(t), MonthFromTime(t), etc.) using simple arithmetic. std::time::SystemTime::now() provides milliseconds for Date.now(). This avoids adding a large dependency.
MapKey wrapper for SameValueZero equality. Map and Set keys need custom equality: NaN === NaN (unlike ===), +0 === -0 (like ===). Create a MapKey wrapper around JsValue that implements Hash and Eq with these semantics. For object keys, hash/eq by pointer identity (Rc::as_ptr()).
WeakSet follows WeakMap pattern exactly. WeakMap already uses HashMap<usize, JsValue> keyed by Rc::as_ptr(). WeakSet is simply HashSet<usize> with the same pattern. Both lack true weak reference semantics (no GC integration), which is a documented limitation.
Implementation Patterns from Existing Code
WeakMap dispatch pattern (in calls.rs:401-447):
// Check if object is a special type, dispatch method before prototype lookup
if obj.is_weakmap() {
match method_name.as_str() {
"get" => { /* ... */ }
"set" => { /* ... */ }
// ...
}
}
Map, Set, WeakSet, and Date should follow this same dispatch pattern.
Builtin setup pattern (in mod.rs):
regexp_builtins::setup_regexp_builtins(self.env);
weakmap_builtins::setup_weakmap_builtins(self.env);
// Add:
map_builtins::setup_map_builtins(self.env);
set_builtins::setup_set_builtins(self.env);
weakset_builtins::setup_weakset_builtins(self.env);
date_builtins::setup_date_builtins(self.env);
PrimitiveValue pattern (in value.rs):
pub enum PrimitiveValue {
Number(f64),
String(String),
Boolean(bool),
RegExp(RegExpData),
// Add: Date(f64),
}
Critical Implementation Details
Date math is pure arithmetic, not library calls. ECMAScript §21.4.1 defines:
msPerDay = 86_400_000Day(t) = floor(t / msPerDay)TimeWithinDay(t) = t % msPerDay(uset.rem_euclid(msPerDay)for negative timestamps)- Leap year calculation, month/day decomposition, etc. -- all spec-defined formulas
- For local time:
LocalTime(t) = t + local_tz_offset_ms. Get offset vialibc::localtime_ror by comparingSystemTimeepoch math.
Map/Set forEach must handle mutation during iteration. Per spec, entries added during forEach are visited; entries deleted before being visited are skipped. IndexMap's index-based iteration handles this naturally if you iterate by index rather than by iterator.
Constructor new enforcement. Map, Set, WeakSet, Date must throw TypeError when called without new. Check the new_target or use a flag in the NativeFunction setup. Follow the pattern used by existing constructors.
Map.prototype.size is a getter, not a data property. It must be defined via Object.defineProperty with a getter function (or the internal equivalent once property descriptors are available from Story 3.5). If property descriptors aren't landed yet, implement as a regular method call -- the dispatch in calls.rs can handle .size specially.
Dependency on Story 3.5
Property descriptors (Story 3.5 Task 1) are NOT required for this story's core functionality. Map/Set/Date can be implemented using the existing .set()/.get() API. However:
.sizeas a getter property ideally needs property descriptors. Workaround: handle in method dispatch.Object.freezeon Map/Set is a Story 3.5 concern, not this story.- Iterator methods depend on Story 3.2 (generators/iterator protocol) which is already done.
This story can proceed independently of Story 3.5.
Previous Story Intelligence
From Story 3.5 (Array/String/Object):
- Property descriptor changes are high-risk foundational work -- this story should NOT depend on them
- Iterator protocol from Story 3.2 is available and working
indexmapis already a workspace dependency -- confirmed available
From Story 3.4 (ES modules) review patterns:
- Don't create stub/dead code -- wire everything into actual execution paths
- Integration tests must test the right code paths
- Run
just ciafter each task
Risk Assessment
HIGH: Date math complexity. ECMAScript date arithmetic with local time conversions, leap years, month boundaries, and pre-epoch negative timestamps is intricate. Many edge cases (Feb 29, month overflow, year 0-99 mapping). Must implement per-spec formulas carefully.
MEDIUM: MapKey hash/eq for arbitrary JsValue types. Hashing JsValues (especially objects by pointer) requires careful Hash/Eq implementation. NaN handling must be special-cased. Consider that JsValue may not derive Hash natively -- MapKey wrapper handles this.
MEDIUM: Date.parse() string parsing. ISO 8601 parsing has many edge cases. Implement a focused subset: YYYY-MM-DDTHH:mm:ss.sssZ and common variants. Don't try to handle all date string formats browsers support (those are implementation-defined).
LOW: Map/Set core operations. Straightforward CRUD with IndexMap/IndexSet. Well-defined spec behavior.
LOW: WeakSet. Near-identical to WeakMap with simpler API.
Phased Implementation Strategy
Phase A -- Map + Set (Tasks 1-2): Implement together -- they share MapKey infrastructure and follow identical patterns. Can start immediately.
Phase B -- WeakSet + WeakMap fixes (Tasks 3-4): Quick additions. WeakSet mirrors WeakMap. Fix function-as-key for both.
Phase C -- Date (Task 5): Largest task. Independent of Map/Set. Implement date math helpers first, then constructors, then methods.
Phase D -- RegExp enhancements (Task 6): Independent of other tasks. Function replacers are the highest-value addition.
Phase E -- Testing + Validation (Task 7): After all implementations complete.
Project Structure Notes
- All implementation in
crates/js_vm/src/interpreter/(Layer 1) -- no layer violations - New files:
date_builtins.rs,map_builtins.rs,set_builtins.rs,weakset_builtins.rs(4 new) - New test files:
date_tests.rs,map_tests.rs,set_tests.rs,weakset_tests.rs(4 new) - Value type changes in
crates/js_vm/src/value.rs(Layer 1) - No
unsafecode needed - No new external dependencies (indexmap already available)
References
- ECMAScript §21.4 -- Date Objects -- Date constructor, methods, math
- ECMAScript §21.4.1 -- Time Values -- Date math helper definitions
- ECMAScript §22.2 -- RegExp Objects -- RegExp built-in
- ECMAScript §24.1 -- Map Objects -- Map constructor and prototype
- ECMAScript §24.2 -- Set Objects -- Set constructor and prototype
- ECMAScript §24.3 -- WeakMap Objects -- WeakMap
- ECMAScript §24.4 -- WeakSet Objects -- WeakSet
- [Source: crates/js_vm/src/interpreter/regexp_builtins.rs] -- Existing RegExp implementation (421 lines)
- [Source: crates/js_vm/src/interpreter/weakmap_builtins.rs] -- Existing WeakMap implementation
- [Source: crates/js_vm/src/interpreter/expressions/calls.rs:401-447] -- WeakMap method dispatch pattern
- [Source: crates/js_vm/src/interpreter/mod.rs:507-521] -- Existing builtin setup calls
- [Source: crates/js_vm/src/value.rs:602-622] -- JsValue enum and PrimitiveValue
- [Source: _bmad-output/planning-artifacts/epics.md#Story 3.6] -- Story requirements
- [Source: _bmad-output/planning-artifacts/architecture.md#JS Feature Implementation Order] -- Implementation order pattern
Dev Agent Record
Agent Model Used
{{agent_model_name_version}}