Repository navigation
Conversation
…nter An untyped write of a present declared field of a live record capsule now stores into the native record through a per-shape `set` hook (checked like the commit, refusing to the snapshot path for values the field cannot hold), so the capsule keeps answering from the record instead of turning into a permanent snapshot. Object.assign from a plain object takes the same path, and checked conversions and matches read a capsule through a fresh, uncached snapshot. A checked conversion of a record capsule into a record type whose storage is a same-offset prefix of the capsule's record (and whose extra fields hold no references) passes the pointer: both names keep one object, writes show through either, and the record remembers its own capsule parameters, so every later box of it and the narrower type's reflective surfaces answer for the whole object (decision D3). Other cross-layout conversions keep the refreshed copy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…chmark - forin-keys.test.ts matches the record-ops table with its eighth field, the store hook, which is null for a record no untyped code writes. - bench/record-untyped-writes writes typed records through `unknown` and `Object.assign` views, so it links dynamic-tier symbols; DYNAMIC_WORKLOADS lists it. - The sanitized self-hosting emission test gets a 600 s timeout. It takes about half of the default 300 s on an idle 16-core host, with or without these changes, and exceeded the default on a loaded CI shard.
… writes Object.freeze of a fresh record or array literal now sets the native frozen bit instead of compiling to the literal, so an untyped reference to the value sees it frozen: Object.isFrozen answers true and strict-mode writes throw Node's TypeErrors. Untyped property writes in sloppy-mode code (IrModule.sloppyDynWrites, decided per write by isStrictCode) call scr_dyn_key_set_sloppy and its computed form, which drop exactly the TypeError a refused assignment raises (a read-only or frozen property, a non-extensible object, a frozen array element) while a setter's exception still propagates. Computed-write dispatch helpers are shared only by writes of the same mode. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch was successfully deployed
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.
Stage S3 of the single object model, second PR (now also the frozen-record fix). Untyped writes of a typed record no longer turn its capsule into a permanent snapshot, and a cast into a record type whose storage is a prefix of the record's keeps one object (decision D3).
Writes through the record (descriptor-driven capsules)
ScrDynTypedRecordOps) gains asethook (liveRecordSetinexpr-stream-bridges.ts). It is emitted only when the program stores through an untyped view (!liveViewsReadOnly). An untyped write of a present declared field converts the value with the field's checked conversion and stores it in the native record in place. Before this PR, every such write materialized the whole view, wrote it, ran the full commit conversion, and left the capsule answering from the view from then on.TypeError: expected number at $.x, got string.Object.assignfrom a plain data-only object stores each key the same way.scr_dyn_typed_ref_peek), so casting a record no longer turns its capsule into a snapshot either.Prefix casts keep identity (S3g)
A checked conversion of a record capsule (shape A) into record type B passes the pointer when all of these hold:
The compiler decides this per pair (
prefixViewSourcesindyn.ts). B's release then frees the block correctly, because frees do not depend on the size class.At run time,
scr_dyn_typed_ref_prefix_viewrefuses the pass while A keeps undeclared source properties, and records A's capsule parameters in the record-source table (ScrRecordPrefix). That entry is freed with the record. Every later box of the object under any record type is then A's capsule, so(n as unknown) === srcholds. B's reflective surfaces also answer through that capsule:JSON.stringify,Object.keysand inspect show the whole object, as in Node.Copies remain in these cases:
{a,b,c,d}cast to{a,c}). Typed code reads fixed offsets, so two storages cannot share one object until records are read through their own layout (S3e: stamped layout id plus descriptors).Adoption
scr_dyn_decl_becomealready rewrites shared generic nodes into live capsules in place. With thesethook and peek, those capsules now stay live through writes and casts; this is the forwarding layout of DESIGN §4.5 step 4. Adoption itself needs no separate change.Tests
record-dynamic-writes.tsandrecord-prefix-identity.tswrite through one name and read through the other, and cover reflective views and identity.record-untyped-writes.pnpm test:sandboxfailed twice in sandbox preparation and completion probes, both infrastructure failures, so the lanes ran with the shard runner instead.self-hosting-frontend-lowering,self-hosting-native-driver(bootstrap: the rebuilt compiler emits itself),dyncheckanddecl-boundarypass.runtime-tree-shaking"static hello" fails identically on main: 99,008 B against a 98,304 B limit. This PR leaves the hello binary byte-identical in size to main. The excess also predates Store ordinary objects as a layout plus value slots #2353: the commit before it (9f47d74) builds hello at 99,048 B, and Store ordinary objects as a layout plus value slots #2353 shrank it by 48 B.self-hosting-native-frontend*suites fail at their Node-hosted compile step on main as well.sc_dc_*in 1,048 programs,sc_dm_*in 186) and the ops-table type (50). The other differences are executable-only, inside runtime functions (scr_dyn_key_set,scr_dispose_observers,sc_ldr_*_rops). No program gains a dynamic-tier symbol.--layouts=4 --runs=24):record-untyped-writesis 68.6% faster (4,759 ms to 1,494 ms, CI -69.3% .. -67.6%). dynamic-methods, dynamic-properties, json-records, json-unions, keyed-option-writes, optional-fields, record-boundaries, throw-chains and validate-errors are all neutral.Found, not fixed
An untyped write to a field of an
Object.freezed record literal or function result succeeds natively, while Node ignores or throws. Main behaves the same:scr_rc_frozenanswers false for these records.Frozen records seen through untyped references, and sloppy-mode writes
Object.freezeof a fresh record or array literal now sets the native frozen bit. Before, it compiled to the literal, so an untyped reference saw an unfrozen object:Object.isFrozenanswered false and writes succeeded.IrModule.sloppyDynWrites, decided byisStrictCode, and callscr_dyn_key_set_sloppyor its computed form. The refused assignments are a read-only or frozen property, a non-extensible object and a frozen array element. Only the TypeError that the refused assignment raised is dropped; a setter's exception still propagates. Computed-write dispatch helpers are shared only by writes of the same mode.frozen-record-untyped-writes.ts(strict) andfrozen-untyped-writes-sloppy.cjs(sloppy).Validation round (
bin/validate, HEAD 6878c5b against c5e8a87)record-untyped-writesis 68.8% faster,value-objectsis 2.4% slower (CI +0.1% .. +3.7%), and the other 50 are neutral.sc_dc_*in 1,048 programs,sc_dm_*in 186, and the ops-table type in 50.1982-freeze-resolve-passthroughandarray-sort-holes-published. Their frozen bit and guards are the fix.forin-keys,class-dynamic-dispatchandbench-dynamic-tier, all test expectations fixed in the commits after the validated head. Three others are not caused by this branch:three-libraryrelease timed out in the lane. Run alone, it passes on both trees: 572 s on the base and 560 s on the candidate, against its 600 s limit.runtime-tree-shakingfails on main too.lane-*-nativejobs failed at startup withSCRIPTC_TEST_SHARD must look like "2/3", got "native", a harness setup error, not a test failure.D3 instrumentation (full differential corpus, cast copies and prefix passes)
record-prefix-identity.tsThe extra copy is in a new corpus program. None of the existing 83 copies is a prefix cast whose extra fields hold no references, so S3g as it stands removes none of them. Retiring them needs either reference-holding prefixes (B's teardown would have to release the source storage's extra fields) or identity across incompatible layouts. The second conflicts with fixed-offset typed access (DESIGN §4.3); see NOTES.md, "S3e request".
🤖 Generated with Claude Code