Repository navigation
P3 B4a: delete the INT32 Number producer API and the prototype-word decoders - #14372
Merged
Merged
Conversation
DESIGN-p3 rev 3.1 §3 B4a deletes the producer API. B1 audited the NUM declarations and the exact-int32 predicate confirmed that nothing produces an INT32-tagged JS Number; this removes the means to make one. - `JSValue::int32` is gone. - Its one non-test caller, the builtin constructor identity capture (`alloc_builtin_constructor`), writes `builtin_constructor_identity_word`: the same runtime-internal 0x7FFE word the realm image writes for `RealmValue::Int32`, named as an internal word, never a Number (R4). - The realm schema labels `RealmValue::Int32` (the constructor identity capture) `Other`, not `Number` (`realm.rs:293`). - The test helpers that built INT32 Numbers now build canonical doubles. `value/tests.rs` keeps raw 0x7FFE words where it tests the tag space, and drops `test_int32` (the deleted constructor).
…t; delete the JS-path decoders of the prototype word (P3 B4a) After B2 a `C.prototype` read returns the prototype object. The last place the 0x7FFE prototype word still sat in a JS-value position was the receiver of an evaluation's by-name method value (`class_evaluation_method_value_for_name`, `class_method_values.rs:42`), a BOUND_METHOD closure whose slot 0 is read as a receiver. - That receiver is now the evaluation's prototype object, the value a `C.prototype` read of the evaluation returns. The prototype fill passes the prototype it is filling; other callers build it first, which fills and finds the value. - Bound-method dispatch reads the owner off the prototype object (`class_prototype_object_id`, structural: `class_id_for_decl_prototype_object`) and runs the owner's body with the call-time `this`. - The word stays only as a method's internal home capture, under a new name: `class_method_home_word` / `class_method_home_id` (R4). Its readers are `js_class_method_home` (super) and `class_method_value_target`; the check-mode classifier in contracts.rs keeps it until B4b. - Every other decoder of the word is deleted: the `class_prototype_ref_id` calls in descriptors, defineProperty, hasOwn, delete, symbol get/own symbols, proxy, weakref, instanceof, property get/set, enumeration, bind and others, and the 0x7FFE arms of `class_ref_id` and `class_ref_word`, which now decode only a class function object (both delegate to `class_value_id`). Helpers left without callers are deleted with them (`canonical_bound_method_receiver`, `delete_class_prototype_key`, `decl_prototype_own_accessor`, `class_chain_getter_value`, `class_static_prototype_is_nulled`), as is the class-word branch of `Function.prototype.bind` and of `Object.getPrototypeOf`. - Tests that fed the word to a JS operation use the prototype object; `synthetic_prototype_ref_folds_onto_constructor_key` tested only the deleted fold and is removed. - Controls: tests/parity/language/class/test_p3_b4a_prototype_paths.ts, one block per path plus bound methods of declared and per-evaluation classes, against node.
The store-side canonicalization tests (array push/set/from/raw helpers) and the typed-array fast-set decline test pin what a live consumer does with a tag word. Those consumers stay until B4b, so their tests keep a raw word; a canonical double does not exercise them.
…the declaration, not the template holder (P3 B4a, S7b) A by-name evaluation method value now carries the evaluation's prototype object, so a method read before that prototype exists builds it. Its [[Prototype]] for an evaluation with no pinned heritage was copied from the template's declaration holder, which built that holder: the S7b invariant forbids exactly that. The link now comes from the declaration's static parent facts: `static_parent_prototype_bits`, split out of `decl_prototype_parent_bits`, so both prototypes read one function. It answers with the declared parent class's prototype, a reserved native parent's, or Object.prototype, and never reads the class's own holder.
|
Caution Review failedThe pull request is closed.
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. |
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.
P3 B4a: delete the INT32 Number producer API and the JS-path decoders of the prototype word
Part of P3 "canonical Numbers" (DESIGN-p3 rev 3.1 §3 B4a, plus CWN's additions). After B1 (#14354), the exact-int32 predicate (#14353) and B2 (#14358), nothing produces an INT32-tagged JS Number, and
C.prototypereads return the prototype object. This PR removes the means to make an INT32 Number, moves the last JS-visible use of the 0x7FFE prototype word (a bound method's receiver) to the prototype object, and deletes every decoder of that word in a JS-operation path.Changes
1. Producer API (
335c8f500b, builds on its own)JSValue::int32is deleted.alloc_builtin_constructor). It now writesbuiltin_constructor_identity_word: the same runtime-internal 0x7FFE word the realm image writes forRealmValue::Int32, named as an internal word (R4).perry-runtime-manifest/src/realm.rs:293:RealmValue::Int32(the constructor identity capture) is labelledOther, notNumber.crypto/random.rs).value/tests.rskeeps raw 0x7FFE words where it tests the tag space and dropstest_int32.96da3dd424: the tests of live INT32 consumers (array store canonicalization, the typed-array fast-set decline) still feed a raw 0x7FFE word. A double would not exercise those consumers, which B4b deletes.2. The bound-method producer (CWN)
class_method_values.rs:42(class_evaluation_method_value_for_name_bytes) built the BOUND_METHOD with the prototype word as its receiver (slot 0). The receiver is now the evaluation's prototype object, the value aC.prototyperead returns.finish_class_evaluation_prototype) passes the prototype it is filling.bound.rs:39,:145,:177,:224) reads the owner off the prototype object withclass_prototype_object_id. That is a structural answer fromclass_id_for_decl_prototype_object, not a table. Dispatch then runs the owner's body with the call-timethis.class_method_home_word/class_method_home_id.73395de176, CWN ruling (b) on S7b).C.prototyperead builds that prototype.class_evaluation_prototype_value(class_object_props.rs:127) copied the parent link from the template's declaration holder. That built the holder, which the S7b invariant forbids.static_parent_prototype_bits, split out ofdecl_prototype_parent_bits(state.rs:1743) so both prototypes read one function. It returns the declared parent class's prototype, a reserved native parent's, or Object.prototype, and never reads the class's own holder.3. Deleted decoders
Line numbers are main
7776d5f491.is_proto_ref_receiverflag; the dynamic- and builtin-parent arms are now unconditionalor_else(class_ref_word)fallback, plus the now-dead non-pointer class tailtarget_is_closurewith it (a class is always its function object)is_prototype_ref: the write throughclass_prototype_set_bytes, and its guardsclass_value_id_bitsclass_value_idclass_prototype_ref_idre-export (replaced byclass_method_home_id)&& class_prototype_ref_id(..).is_none()#xpublic static field, dynamic static accessor, synthesizedname, own static fieldconstructordata descriptorsreceiver_is_constructor_refclass_method_slot_valueshortcut for the worddefinePropertyto the decl prototypeis_staticargument (and the instance arm ofdefine_declared_class_accessor)inis_prototype_ref: the prototypeconstructor, method and accessor reads, and every!is_prototype_refguard; the parameter ofclass_value_get_wtf8_field&& …is_none()class_ref_wordblock (unreachable after theclass_value_idblock above it), and theis_class_refprimitive guard&& …is_none()Helpers left without callers, deleted:
canonical_bound_method_receiverdelete_class_prototype_keydecl_prototype_own_accessor(_bytes)class_chain_getter_value(_bytes)class_static_prototype_is_nulledclass_prototype_set_bytesre-exportParameters removed:
thisfromdispatch_symbol_bound_method(only static symbol methods are built)is_staticfromdefine_declared_class_accessoris_prototype_reffromclass_value_get_wtf8_fieldkey_valuefromclass_ref_wtf8_own_propertyKept (R4):
class_method_home_id, atjs_class_method_home(super,class_super_chain.rs:34) andclass_method_value_target(class_ref_values.rs). These are the readers of the method-home capture.contracts.rs:409proto_class_id, the check-mode classifier, which B4b deletes with the other classifiers.Tests:
bound_method_receiver_tests,object/tests.rslex method).class_value/tests.rsasserts that the home word decodes to no class.synthetic_prototype_ref_folds_onto_constructor_keytested only the deleted fold and is removed.Evidence the decoders were dead
Before deleting anything, a probe build made
class_prototype_ref_idprintP3B4A-PROBE <caller>on every hit (#[track_caller]). The home readers were already onclass_method_home_id.--nocapture): 2 hits, both from unit tests that build the word by hand and pass it in: the metadata fold test (deleted) and theclass_ref_wordtest (now assertingNone).Controls
tests/parity/language/class/test_p3_b4a_prototype_paths.tshas one block per path:Symbol.hasInstance);C.prototype.m.bind(x)andconst f = C.prototype.m; f.call(o)/apply/ a bare call;superthrough a method value.Its output equals node, as does B2's control. One field was dropped from the control:
C.prototype.m.bind(o).lengthis 0 on main and 1 in node. That bug predates this PR and does not sit on a B4a path.Gates
01d23ecbebagainst main8d5b774210(qb9, x86 Linux): the S7b fix plus a merge of main. The rows marked † were re-run there.de1d335128against mainc30a986394. The S7b fix touches only the evaluation-prototype parent link.-D warningsper cratewebassembly_wrapper_state.rs,cell_meta.rs,gc/mark.rs,shapes.rs, plusglobal_this.rsand others under stdlib)tests/large_object_barriers.rs::large_local_array_push_inbounds_store_emits_precise_slot_barrierfails on mainc30a986394too (pre-existing; this PR changes no codegen file).texthello †.texthello2 †.textclasses (300-class fixture) †.textgrowth, per symbol († run; hello2 and classes are the same)apply_property_descriptor::{closure#42}+12394 is the same closure renumbered:{closure#43}is −12394, net 0.class_evaluation_method_value_for_name+852: the home prototype is built and rooted here now.js_array_set_index_or_string+606,js_object_get_own_property_descriptors+536,js_object_keys_value+536,class_prototype_addr+477, …) are LLVM inlining shifts: smaller callees from the deletions now inline. They are outweighed by the removals:class_value_get_field−2035,get_prototype_of_resolved−1635,class_prototype_set_bytes−1040,define_declared_class_accessor−929,js_object_get_own_property_names_shape−844, …Startup Ir, classes
memcmp(+2617) andHashMap<_, _, RandomState>::insert(+365): seeded-hash noise.class_closure_id/class_value_idnow reached through the delegatingclass_ref_id, which is −21).IR census output diffs
Every one is a compile failure in both arms or nondeterministic output:
elapsed: 7 msvs6 msfromDate.now(), and is equal on rerun.Follow-ups
C.prototype.m.bind(o).lengthis 0 in perry and 1 in node, on main as well. It is not a B4a path, so the control does not print it.perry-codegen/tests/large_object_barriers.rs::large_local_array_push_inbounds_store_emits_precise_slot_barrieralso fails on main.contracts.rs:409, the lastclass_method_home_idreader outside the home paths), and the 0x7FFE rename toINTERNAL_WORD_TAG.