Repository navigation
Added support for WeakMap and WeakSet - #21
Merged
Merged
Conversation
WeakMap and WeakSet were rejected as host-only names. They are now standard globals backed by host weak collections, with the spec surface and V8 messages. Any object is a valid key. A weak collection cannot enter a run, and leaves it as undefined, like an iterator.
There was a problem hiding this comment.
🟡 Changes recommended
WeakMap object-entry handling must match JavaScript semantics, and the new wrapper types must be added to the documented value model.
1 open issue
What changed in this PR
Adds sandbox support for WeakMap and WeakSet, including JavaScript-like constructors and methods, weak-key validation, boundary handling, tests, parity coverage, and documentation.
Changes:
- Adds weak collection wrappers, built-ins, methods, globals, and dispatch registration.
- Prevents weak collections from crossing unsupported host boundaries.
- Adds escape, boundary, parity, and documentation coverage.
- Requires correction of
WeakMapiterable entry handling and completion of wrapper-model documentation.
| File | Description |
|---|---|
packages/maple/src/values/wrappers/wrapper.ts |
Adds weak collection wrappers to the wrapper model. |
packages/maple/src/values/wrappers/weak-set-wrapper.ts |
Defines the sandbox WeakSet wrapper. |
packages/maple/src/values/wrappers/weak-map-wrapper.ts |
Defines the sandbox WeakMap wrapper. |
packages/maple/src/support/can-be-held-weakly.ts |
Validates allowable weak collection keys and entries. |
packages/maple/src/host-functions.spec.ts |
Tests rejection of weak collections as host-function arguments. |
packages/maple/src/evaluator/language/references.ts |
Removes weak collection names from blocked globals. |
packages/maple/src/evaluator/host-bridge.ts |
Reports weak collections as unconvertible host arguments. |
packages/maple/src/escapes.spec.ts |
Tests prototype and constructor escape protections. |
packages/maple/src/builtins/weak-set/weak-set.ts |
Implements the WeakSet constructor and branding. |
packages/maple/src/builtins/weak-set/weak-set.spec.ts |
Covers WeakSet behavior and errors. |
packages/maple/src/builtins/weak-set/weak-set-prototype.ts |
Registers WeakSet methods. |
packages/maple/src/builtins/weak-set/methods/has.ts |
Implements WeakSet.has. |
packages/maple/src/builtins/weak-set/methods/delete.ts |
Implements WeakSet.delete. |
packages/maple/src/builtins/weak-set/methods/add.ts |
Implements WeakSet.add. |
packages/maple/src/builtins/weak-map/weak-map.ts |
Implements the WeakMap constructor and branding. |
packages/maple/src/builtins/weak-map/weak-map.spec.ts |
Covers WeakMap behavior and errors. |
packages/maple/src/builtins/weak-map/weak-map-prototype.ts |
Registers WeakMap methods. |
packages/maple/src/builtins/weak-map/methods/set.ts |
Implements WeakMap.set. |
packages/maple/src/builtins/weak-map/methods/has.ts |
Implements WeakMap.has. |
packages/maple/src/builtins/weak-map/methods/get.ts |
Implements WeakMap.get. |
packages/maple/src/builtins/weak-map/methods/delete.ts |
Implements WeakMap.delete. |
packages/maple/src/builtins/standard-globals.ts |
Exposes WeakMap and WeakSet globally. |
packages/maple/src/builtins/method-dispatch.ts |
Adds weak collection method dispatch. |
packages/maple/src/boundary/to-host.ts |
Drops or rejects weak collections during output conversion. |
packages/maple/src/boundary/to-host.spec.ts |
Tests weak collection output conversion. |
packages/maple/src/boundary/from-host.spec.ts |
Tests rejection of host weak collections as input. |
packages/maple/docs/language.md |
Documents supported constructors and globals. |
packages/maple/docs/builtins.md |
Documents the new weak collection APIs. |
packages/maple/docs/boundary.md |
Documents weak collection boundary behavior. |
packages/maple-parity/src/divergences.spec.ts |
Adds parity coverage for weak collections. |
packages/maple-parity/README.md |
Documents the interpreter parity divergence. |
Review effort: Lite
new Map(iterable) refused every entry that was not an array, and
read entry[0] and entry[1] off the host array directly. It now reads
both through the member model, as Object.fromEntries does, so
{ 0: key, 1: value } and Maple's own objects work as in JavaScript.
Merged
|
🎉 This PR is included in version 0.6.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

What changes
Scripts can now use
WeakMapandWeakSet, for example to cache results per object or to track which objects a traversal has already visited. Before this change, reading either name threwDISALLOWED_SYNTAX.WeakMapsupportsget,set,hasanddelete.WeakSetsupportsadd,hasanddelete.Both behave as in JavaScript:
newinstanceofandString(...)give[object WeakMap]/[object WeakSet]new WeakMap(iterable)reads each entry likeentry[0]andentry[1], so{ 0: key, 1: value }works as a pairTypeError: Invalid value used as weak map key/Invalid value used in weak setget,hasanddeletewith a primitive returnundefined/falseinstead of throwingAny object can be a key, including functions and built-ins such as
Math. Scripts have no symbols, so nothing else can be.At the boundary, a weak collection's entries can't be listed, so it can't be copied:
dataor globals, or returned from a host function, it is rejected like any other unsupported value.undefined.TypeError: A WeakMap cannot be passed to the host.In all three cases it is handled the way an iterator is.
No new divergence from JavaScript. As with every built-in,
WeakMap.prototypeand.constructorstay blocked.The same entry reading fixes
new Map(iterable), which used to throw for every entry that was not an array:new Map([{ 0: 'a', 1: 1 }]).get('a')now gives1, as in JavaScript.How
WeakMapWrapperandWeakSetWrapperhold a real hostWeakMap/WeakSet, so entries keyed by throwaway objects can be garbage-collected during a long run. Scripts have noWeakReforFinalizationRegistry, so they can't observe when that happens. Neither wrapper has areadOnlyflag, because a weak collection can never arrive with frozen input.builtins/weak-map/andbuiltins/weak-set/follow the layout ofmap/andset/.support/can-be-held-weakly.tsdecides which values are valid keys.standard-globals.tsandmethod-dispatch.ts, and removed fromHOST_ONLY_NAMES.new WeakMapandnew Mapreadentry[0]andentry[1]throughgetMember, asObject.fromEntriesdoes. Any object works as a pair, and properties a script added to Maple's own objects are seen. Before this, theMapconstructor read the indexes off the host array directly.toHostdrops both wrappers. In strict mode, which is used for host-function arguments, it throwsUnconvertibleValueErrorinstead, andhost-bridge.tsreports that as aTypeErrorin the script.Tests, parity and docs
weak-map.spec.tsandweak-set.spec.tscover every method, the invalid-key messages, the constructor and its errors, and that set algebra refuses aWeakSet.map.spec.tsandweak-map.spec.tscover entries that are not arrays. Every expected value was checked against Node.escapes.spec.tschecks that.constructor,__proto__andWeakMap.prototypestay blocked.to-host.spec.ts,from-host.spec.tsandhost-functions.spec.tscover the boundary in both directions.Map,Set,WeakMaporWeakSet, and the parity package only covers scripts it can run.language.md(blocked names, thenewallowlist, theinstanceofcount, the wrappers in the value model), a new "WeakMap and WeakSet" section and the standard-globals list inbuiltins.md, and a new row in theboundary.mdtable.contact-summary.jsorcontact-ten-lines.js(measure-run, three alternating rounds).