Repository navigation
Let scripts delete and replace built-in properties where JavaScript allows it - #22
Merged
Merged
Conversation
Node runs each case in the test process, so a case that replaces or deletes a built-in changes it for js-interpreter and Maple too. Every helper now runs Node last, and such a case restores the built-in in a finally before anything else runs.
A Map's or Set's size and a RegExp's source and flags are accessors on the prototype in JavaScript, not own properties. Every value still answers to them and in finds them, but Object.hasOwn and Object.getOwnPropertyNames now leave them out, and deleting one changes nothing. lastIndex stays a RegExp's one own property.
… properties As in JavaScript, a function's name and length can be deleted, which leaves what Function.prototype has, and the methods of a built-in such as Math.max can be replaced and deleted, while constants such as Math.PI and a RegExp's lastIndex cannot be deleted. An error's name, message and stack can be deleted too. What a run changes on a shared built-in stays in that run.
📋 Release PreviewChangelog0.6.0 (2026-10-24)🚀 Features
🩹 Bug Fixes
🧪 Tests
|
There was a problem hiding this comment.
🟡 Changes recommended
Address the forbidden-key validation bypass in Object.hasOwn and ensure deleted or reassigned error stacks are reflected during host error conversion.
2 open issues
What changed in this PR
This pull request updates Maple’s sandbox object model so built-in properties follow JavaScript-compatible rules for deletion, replacement, inheritance, ownership, and per-run isolation.
Changes:
- Adds configurable, replaceable built-in methods while preserving protected constants.
- Models prototype-backed properties for functions, errors, Maps, Sets, and RegExps.
- Updates error conversion, parity isolation, tests, documentation, and integrity behavior.
| File | Description |
|---|---|
packages/maple/src/values/wrappers/set-wrapper.ts |
Models Set.size as a prototype-backed accessor. |
packages/maple/src/values/wrappers/regexp-wrapper.ts |
Separates lastIndex from prototype-backed RegExp accessors and updates delete descriptions. |
packages/maple/src/values/wrappers/map-wrapper.ts |
Models Map.size as a prototype-backed accessor. |
packages/maple/src/values/wrappers/date-wrapper.ts |
Adds JavaScript-compatible Date descriptions for delete errors. |
packages/maple/src/values/sandbox-object.ts |
Implements fixed-property replacement, deletion, inheritance, ownership, and integrity rules. |
packages/maple/src/values/sandbox-error.ts |
Adds deletion-aware error properties and host conversion handling. |
packages/maple/src/values/run-state.ts |
Adds non-allocating lookups for per-run shared-property changes. |
packages/maple/src/values/own-properties.ts |
Tracks deleted and replaced fixed properties. |
packages/maple/src/values/members.ts |
Updates member operations and own-property handling. |
packages/maple/src/values/coercion.ts |
Delegates failed-delete descriptions to sandbox values. |
packages/maple/src/values/builtin.ts |
Assigns JavaScript-like descriptors to built-in methods and constants. |
packages/maple/src/support/noop.ts |
Provides a no-op method lookup callback for error conversion. |
packages/maple/src/evaluator/language/functions.spec.ts |
Tests deletion and inherited behavior for function properties. |
packages/maple/src/escapes.spec.ts |
Tests isolation of shared built-in changes across runs. |
packages/maple/src/errors.spec.ts |
Tests deletion and reassignment of error properties. |
packages/maple/src/builtins/regexp/regexp.spec.ts |
Tests RegExp own-property and prototype-accessor behavior. |
packages/maple/src/builtins/object/statics/has-own.ts |
Routes Object.hasOwn through sandbox-aware logic. |
packages/maple/src/builtins/object/object.spec.ts |
Tests own names, deletion errors, and integrity levels. |
packages/maple/src/builtins/math/math.spec.ts |
Tests replacement and deletion of Math methods. |
packages/maple/src/builtins/map/map.spec.ts |
Tests Map and Set prototype-backed size. |
packages/maple/src/builtins/json/json.spec.ts |
Tests replacement of JSON methods. |
packages/maple/docs/language.md |
Documents function, built-in, and wrapper property behavior. |
packages/maple/docs/builtins.md |
Documents deletable error properties. |
packages/maple-parity/src/parity.ts |
Ensures Node runs last to isolate built-in mutations. |
packages/maple-parity/src/divergences.spec.ts |
Adds documented ES2015 property-model divergences. |
packages/maple-parity/src/cases/math.spec.ts |
Adds parity tests for Math method replacement and deletion. |
packages/maple-parity/src/cases/functions.spec.ts |
Adds function-length deletion parity coverage. |
packages/maple-parity/src/cases/errors.spec.ts |
Adds error-message deletion and reassignment parity coverage. |
packages/maple-parity/README.md |
Documents new parity differences. |
AGENTS.md |
Records parity execution ordering and restoration requirements. |
Review effort: Lite
…uilt-in properties
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
Built-in properties now follow JavaScript's rules for what a script can delete, replace and list as its own. Before this change, most of them could never be changed, and some were listed as own properties when JavaScript keeps them on the prototype.
nameandlengthcan be deleted, as in JavaScript. Afterwards they read''and0, which is whatFunction.prototypehas, and'name' in fis stilltrue. Assigning to them still throws, before and after the delete.Math.max,JSON.parseorObject.keys:Math.max = fn); the replacement is not enumerable, soObject.keys(Math)stays[];Math.PIandNumber.EPSILONcan still be neither written nor deleted. Deleting one now throws V8's message,Cannot delete property 'PI' of #<Object>, instead of "of a read-only object".name,messageandstackcan be deleted.messagereads'',namethe constructor's name andstackundefined, as in JavaScript.String(e)and the error the host receives use these values.Object.keyslists.sizeand a RegExp'ssourceand flags live on the prototype in JavaScript.infinds them.Object.hasOwnandObject.getOwnPropertyNamesnow leave them out:new Map()lists[]and/a/glists['lastIndex'].lastIndex:delete r.lastIndexthrows, as it does in Node.Object.preventExtensions(Math)no longer makesObject.isSealed(Math)andObject.isFrozen(Math)reporttrue.[object RegExp]/[object Date]and a namespace as#<Object>, as V8 does.Everything a run replaces or deletes on a shared built-in stays in that run. Nested runs and the next run see the originals.
Scripts that relied on the old behaviour see these changes:
delete f.nameis no longer a no-op.No new divergence from JavaScript.
How
FixedPropertygets an explicitwritable, an optionalown: falsefor accessors JavaScript keeps on the prototype, and an optionalinheritedvalue for what the prototype has once the own property is deleted.FixedChange, the per-value record next to the property table, getsdeletedandreplaced. A replaced value is kept there because the built-in is shared by every run.SandboxObjectapplies these rules ingetProperty,setProperty,deleteProperty,in,Object.hasOwn, the own-key listing andseal/freeze. Deleting a configurable fixed property marks it deleted; deleting a non-configurable one throws.Builtin.deleteProperty, which refused every delete, is gone.nameandmessagethrough the same path, so a deletedmessageconverts as''.SandboxObject.describe(), overridden for namespaces, errors, RegExps and Dates, replaces the tag checks indescribeObject.finallybefore anything else runs. This is noted inAGENTS.md.Tests, parity and docs
name/length, replacing and deletingMath.max, the delete messages,preventExtensionson shared values, and errors.map.spec.ts,regexp.spec.tsandobject.spec.tscover the type accessors that are no longer own.New cases for replacing and deleting
Math.max(each restores the original descriptor in afinally),delete f.length, and deleting and re-adding an error'smessage.New divergences where Maple follows JavaScript and js-interpreter (ES5) does not:
delete f.name;Math.PIorlastIndex;Object.getOwnPropertyNameson a RegExp anddelete r.source.Each has a README row.
language.md(Functions, and the wrappers' own properties in the value model) andbuiltins.md(Errors).measure-run, 1000 runs, four alternating rounds againstincrement):contact-summary.jsandcontact-ten-lines.jsare unchanged.