@geastack/compiler 1.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +149 -0
- package/dist/cli-analyze.d.ts +1 -0
- package/dist/cli-analyze.js +61 -0
- package/dist/cli-coverage.d.ts +1 -0
- package/dist/cli-coverage.js +329 -0
- package/dist/cli-emit.d.ts +10 -0
- package/dist/cli-emit.js +398 -0
- package/dist/cli-module-graph.d.ts +18 -0
- package/dist/cli-module-graph.js +123 -0
- package/dist/cli-package-source.d.ts +5 -0
- package/dist/cli-package-source.js +116 -0
- package/dist/cli-project.d.ts +2 -0
- package/dist/cli-project.js +60 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +199 -0
- package/dist/compiler.d.ts +228 -0
- package/dist/compiler.js +1029 -0
- package/dist/conversion/algebra.d.ts +218 -0
- package/dist/conversion/algebra.js +155 -0
- package/dist/conversion/build.d.ts +67 -0
- package/dist/conversion/build.js +1378 -0
- package/dist/conversion/derive.d.ts +37 -0
- package/dist/conversion/derive.js +260 -0
- package/dist/conversion/graph-validation.d.ts +8 -0
- package/dist/conversion/graph-validation.js +97 -0
- package/dist/conversion/native-callable-adapter.d.ts +13 -0
- package/dist/conversion/native-callable-adapter.js +53 -0
- package/dist/conversion/native-class-reference.d.ts +12 -0
- package/dist/conversion/native-class-reference.js +39 -0
- package/dist/conversion/native-payload-transport.d.ts +25 -0
- package/dist/conversion/native-payload-transport.js +33 -0
- package/dist/conversion/native-selection.d.ts +49 -0
- package/dist/conversion/native-selection.js +135 -0
- package/dist/conversion/native-sum.d.ts +42 -0
- package/dist/conversion/native-sum.js +115 -0
- package/dist/conversion/nodes.d.ts +55 -0
- package/dist/conversion/nodes.js +91 -0
- package/dist/conversion/operand-view.d.ts +14 -0
- package/dist/conversion/operand-view.js +20 -0
- package/dist/conversion/record-view.d.ts +157 -0
- package/dist/conversion/record-view.js +273 -0
- package/dist/conversion/registry.d.ts +217 -0
- package/dist/conversion/registry.js +1 -0
- package/dist/diagnostics/model.d.ts +76 -0
- package/dist/diagnostics/model.js +60 -0
- package/dist/diagnostics/sweep.d.ts +31 -0
- package/dist/diagnostics/sweep.js +112 -0
- package/dist/identity/ids.d.ts +179 -0
- package/dist/identity/ids.js +212 -0
- package/dist/ir/absent-class-arm.d.ts +48 -0
- package/dist/ir/absent-class-arm.js +157 -0
- package/dist/ir/borrow-effects.d.ts +23 -0
- package/dist/ir/borrow-effects.js +151 -0
- package/dist/ir/borrowed-call-arguments.d.ts +40 -0
- package/dist/ir/borrowed-call-arguments.js +117 -0
- package/dist/ir/build.d.ts +168 -0
- package/dist/ir/build.js +459 -0
- package/dist/ir/call-dispatch.d.ts +38 -0
- package/dist/ir/call-dispatch.js +502 -0
- package/dist/ir/call-entry.d.ts +142 -0
- package/dist/ir/call-entry.js +386 -0
- package/dist/ir/callable-class-flow.d.ts +27 -0
- package/dist/ir/callable-class-flow.js +1386 -0
- package/dist/ir/callable-field-slots.d.ts +22 -0
- package/dist/ir/callable-field-slots.js +166 -0
- package/dist/ir/callable-identity-demand.d.ts +52 -0
- package/dist/ir/callable-identity-demand.js +460 -0
- package/dist/ir/callable-member-candidates.d.ts +9 -0
- package/dist/ir/callable-member-candidates.js +39 -0
- package/dist/ir/callable-records.d.ts +21 -0
- package/dist/ir/callable-records.js +411 -0
- package/dist/ir/captures.d.ts +26 -0
- package/dist/ir/captures.js +335 -0
- package/dist/ir/certificate.d.ts +45 -0
- package/dist/ir/certificate.js +47 -0
- package/dist/ir/certify/carrier-keys.d.ts +73 -0
- package/dist/ir/certify/carrier-keys.js +221 -0
- package/dist/ir/certify/function-source.d.ts +4 -0
- package/dist/ir/certify/function-source.js +28 -0
- package/dist/ir/certify/has-property-key.d.ts +88 -0
- package/dist/ir/certify/has-property-key.js +116 -0
- package/dist/ir/certify/instanceof-key.d.ts +24 -0
- package/dist/ir/certify/instanceof-key.js +35 -0
- package/dist/ir/certify/property-access-keys.d.ts +172 -0
- package/dist/ir/certify/property-access-keys.js +291 -0
- package/dist/ir/certify/property-access.d.ts +3 -0
- package/dist/ir/certify/property-access.js +597 -0
- package/dist/ir/certify/runtime-helper.d.ts +3 -0
- package/dist/ir/certify/runtime-helper.js +326 -0
- package/dist/ir/certify/typeof-operand.d.ts +14 -0
- package/dist/ir/certify/typeof-operand.js +16 -0
- package/dist/ir/certify.d.ts +123 -0
- package/dist/ir/certify.js +481 -0
- package/dist/ir/class-evaluation.d.ts +26 -0
- package/dist/ir/class-evaluation.js +50 -0
- package/dist/ir/class-static-fields.d.ts +60 -0
- package/dist/ir/class-static-fields.js +108 -0
- package/dist/ir/construct-entry.d.ts +26 -0
- package/dist/ir/construct-entry.js +117 -0
- package/dist/ir/dead-values.d.ts +90 -0
- package/dist/ir/dead-values.js +184 -0
- package/dist/ir/deferral.d.ts +99 -0
- package/dist/ir/deferral.js +409 -0
- package/dist/ir/dense-loops.d.ts +230 -0
- package/dist/ir/dense-loops.js +697 -0
- package/dist/ir/dominance.d.ts +82 -0
- package/dist/ir/dominance.js +268 -0
- package/dist/ir/emission-representations.d.ts +71 -0
- package/dist/ir/emission-representations.js +350 -0
- package/dist/ir/facts.d.ts +218 -0
- package/dist/ir/facts.js +181 -0
- package/dist/ir/fixed-data-definition.d.ts +19 -0
- package/dist/ir/fixed-data-definition.js +49 -0
- package/dist/ir/generator-split.d.ts +38 -0
- package/dist/ir/generator-split.js +329 -0
- package/dist/ir/hoist.d.ts +55 -0
- package/dist/ir/hoist.js +375 -0
- package/dist/ir/instantiation.d.ts +61 -0
- package/dist/ir/instantiation.js +137 -0
- package/dist/ir/integer-storage.d.ts +170 -0
- package/dist/ir/integer-storage.js +822 -0
- package/dist/ir/integers.d.ts +161 -0
- package/dist/ir/integers.js +539 -0
- package/dist/ir/integrity-restrictions.d.ts +50 -0
- package/dist/ir/integrity-restrictions.js +118 -0
- package/dist/ir/local-iterators.d.ts +4 -0
- package/dist/ir/local-iterators.js +27 -0
- package/dist/ir/lower-allocation.d.ts +4 -0
- package/dist/ir/lower-allocation.js +325 -0
- package/dist/ir/lower-completion.d.ts +15 -0
- package/dist/ir/lower-completion.js +25 -0
- package/dist/ir/lower-destructuring.d.ts +5 -0
- package/dist/ir/lower-destructuring.js +693 -0
- package/dist/ir/lower-element.d.ts +29 -0
- package/dist/ir/lower-element.js +111 -0
- package/dist/ir/lower-exceptions.d.ts +47 -0
- package/dist/ir/lower-exceptions.js +92 -0
- package/dist/ir/lower-flow.d.ts +137 -0
- package/dist/ir/lower-flow.js +600 -0
- package/dist/ir/lower-graph.d.ts +218 -0
- package/dist/ir/lower-graph.js +687 -0
- package/dist/ir/lower-invocation.d.ts +4 -0
- package/dist/ir/lower-invocation.js +514 -0
- package/dist/ir/lower-narrow.d.ts +53 -0
- package/dist/ir/lower-narrow.js +320 -0
- package/dist/ir/lower-operands.d.ts +272 -0
- package/dist/ir/lower-operands.js +461 -0
- package/dist/ir/lower-property.d.ts +21 -0
- package/dist/ir/lower-property.js +132 -0
- package/dist/ir/lower-protocol.d.ts +11 -0
- package/dist/ir/lower-protocol.js +118 -0
- package/dist/ir/lower-short-circuit.d.ts +41 -0
- package/dist/ir/lower-short-circuit.js +63 -0
- package/dist/ir/lower.d.ts +82 -0
- package/dist/ir/lower.js +1346 -0
- package/dist/ir/model.d.ts +1474 -0
- package/dist/ir/model.js +6 -0
- package/dist/ir/native-absent-property.d.ts +3 -0
- package/dist/ir/native-absent-property.js +13 -0
- package/dist/ir/native-array-transport.d.ts +7 -0
- package/dist/ir/native-array-transport.js +46 -0
- package/dist/ir/native-callable-bind.d.ts +9 -0
- package/dist/ir/native-callable-bind.js +30 -0
- package/dist/ir/native-carrier-predicate.d.ts +34 -0
- package/dist/ir/native-carrier-predicate.js +49 -0
- package/dist/ir/native-class-accessor.d.ts +18 -0
- package/dist/ir/native-class-accessor.js +52 -0
- package/dist/ir/native-class-construction.d.ts +16 -0
- package/dist/ir/native-class-construction.js +71 -0
- package/dist/ir/native-class-initialization.d.ts +15 -0
- package/dist/ir/native-class-initialization.js +56 -0
- package/dist/ir/native-class-origins.d.ts +13 -0
- package/dist/ir/native-class-origins.js +237 -0
- package/dist/ir/native-dictionary-transport.d.ts +6 -0
- package/dist/ir/native-dictionary-transport.js +37 -0
- package/dist/ir/native-enumeration.d.ts +14 -0
- package/dist/ir/native-enumeration.js +26 -0
- package/dist/ir/native-equality.d.ts +10 -0
- package/dist/ir/native-equality.js +18 -0
- package/dist/ir/native-field-owner.d.ts +37 -0
- package/dist/ir/native-field-owner.js +183 -0
- package/dist/ir/native-fixed-layout.d.ts +41 -0
- package/dist/ir/native-fixed-layout.js +232 -0
- package/dist/ir/native-host-construction.d.ts +53 -0
- package/dist/ir/native-host-construction.js +62 -0
- package/dist/ir/native-key-query.d.ts +4 -0
- package/dist/ir/native-key-query.js +38 -0
- package/dist/ir/native-merge-transport.d.ts +25 -0
- package/dist/ir/native-merge-transport.js +71 -0
- package/dist/ir/native-record-index-transport.d.ts +11 -0
- package/dist/ir/native-record-index-transport.js +34 -0
- package/dist/ir/native-record-index.d.ts +12 -0
- package/dist/ir/native-record-index.js +27 -0
- package/dist/ir/native-reflect-field.d.ts +5 -0
- package/dist/ir/native-reflect-field.js +33 -0
- package/dist/ir/native-sequence-transport.d.ts +7 -0
- package/dist/ir/native-sequence-transport.js +53 -0
- package/dist/ir/numeric-intrinsics.d.ts +13 -0
- package/dist/ir/numeric-intrinsics.js +57 -0
- package/dist/ir/object-value-conversions.d.ts +20 -0
- package/dist/ir/object-value-conversions.js +50 -0
- package/dist/ir/physical-class-reflection.d.ts +9 -0
- package/dist/ir/physical-class-reflection.js +57 -0
- package/dist/ir/presence-proof.d.ts +42 -0
- package/dist/ir/presence-proof.js +490 -0
- package/dist/ir/program-facts.d.ts +106 -0
- package/dist/ir/program-facts.js +319 -0
- package/dist/ir/proven-branches.d.ts +16 -0
- package/dist/ir/proven-branches.js +219 -0
- package/dist/ir/queries.d.ts +41 -0
- package/dist/ir/queries.js +213 -0
- package/dist/ir/reflection-demand.d.ts +64 -0
- package/dist/ir/reflection-demand.js +1394 -0
- package/dist/ir/refusal.d.ts +43 -0
- package/dist/ir/refusal.js +28 -0
- package/dist/ir/shake.d.ts +37 -0
- package/dist/ir/shake.js +1078 -0
- package/dist/ir/static-callables.d.ts +11 -0
- package/dist/ir/static-callables.js +163 -0
- package/dist/ir/string-layout-reuse.d.ts +13 -0
- package/dist/ir/string-layout-reuse.js +39 -0
- package/dist/ir/string-length-reuse.d.ts +8 -0
- package/dist/ir/string-length-reuse.js +47 -0
- package/dist/ir/transfer.d.ts +78 -0
- package/dist/ir/transfer.js +270 -0
- package/dist/ir/type-query-results.d.ts +25 -0
- package/dist/ir/type-query-results.js +96 -0
- package/dist/ir/typed-property-access.d.ts +100 -0
- package/dist/ir/typed-property-access.js +161 -0
- package/dist/ir/verify.d.ts +19 -0
- package/dist/ir/verify.js +355 -0
- package/dist/plugins/apple/constructors.d.ts +112 -0
- package/dist/plugins/apple/constructors.js +217 -0
- package/dist/plugins/apple/host.d.ts +181 -0
- package/dist/plugins/apple/host.js +262 -0
- package/dist/plugins/apple/jsx.d.ts +2 -0
- package/dist/plugins/apple/jsx.js +293 -0
- package/dist/plugins/apple/members.d.ts +16 -0
- package/dist/plugins/apple/members.js +190 -0
- package/dist/plugins/apple/plugin.d.ts +2 -0
- package/dist/plugins/apple/plugin.js +156 -0
- package/dist/plugins/gea/component-classes.d.ts +25 -0
- package/dist/plugins/gea/component-classes.js +162 -0
- package/dist/plugins/gea/component-inline.d.ts +13 -0
- package/dist/plugins/gea/component-inline.js +532 -0
- package/dist/plugins/gea/constructors.d.ts +23 -0
- package/dist/plugins/gea/constructors.js +100 -0
- package/dist/plugins/gea/contract.d.ts +150 -0
- package/dist/plugins/gea/contract.js +195 -0
- package/dist/plugins/gea/element-ref.d.ts +31 -0
- package/dist/plugins/gea/element-ref.js +45 -0
- package/dist/plugins/gea/host.d.ts +520 -0
- package/dist/plugins/gea/host.js +762 -0
- package/dist/plugins/gea/lower.d.ts +6 -0
- package/dist/plugins/gea/lower.js +195 -0
- package/dist/plugins/gea/members.d.ts +22 -0
- package/dist/plugins/gea/members.js +221 -0
- package/dist/plugins/gea/plugin.d.ts +22 -0
- package/dist/plugins/gea/plugin.js +307 -0
- package/dist/plugins/gea/prelude.d.ts +78 -0
- package/dist/plugins/gea/prelude.js +153 -0
- package/dist/plugins/gea/producer.d.ts +5 -0
- package/dist/plugins/gea/producer.js +52 -0
- package/dist/plugins/gea/reactive-slots.d.ts +49 -0
- package/dist/plugins/gea/reactive-slots.js +578 -0
- package/dist/plugins/gea/render-bridge.d.ts +6 -0
- package/dist/plugins/gea/render-bridge.js +281 -0
- package/dist/plugins/gea/slots.d.ts +15 -0
- package/dist/plugins/gea/slots.js +36 -0
- package/dist/plugins/gea/template-analysis.d.ts +151 -0
- package/dist/plugins/gea/template-analysis.js +346 -0
- package/dist/plugins/gea/user-agent-styles.d.ts +9 -0
- package/dist/plugins/gea/user-agent-styles.js +94 -0
- package/dist/plugins/installed.d.ts +19 -0
- package/dist/plugins/installed.js +21 -0
- package/dist/plugins/load.d.ts +7 -0
- package/dist/plugins/load.js +207 -0
- package/dist/plugins/model.d.ts +735 -0
- package/dist/plugins/model.js +47 -0
- package/dist/plugins/webgl/host.d.ts +77 -0
- package/dist/plugins/webgl/host.js +162 -0
- package/dist/plugins/webgl/plugin.d.ts +40 -0
- package/dist/plugins/webgl/plugin.js +99 -0
- package/dist/preflight/obligations.d.ts +206 -0
- package/dist/preflight/obligations.js +36 -0
- package/dist/preflight/report.d.ts +20 -0
- package/dist/preflight/report.js +61 -0
- package/dist/preflight/run.d.ts +39 -0
- package/dist/preflight/run.js +403 -0
- package/dist/project-preparation.d.ts +33 -0
- package/dist/project-preparation.js +314 -0
- package/dist/projection/abi.d.ts +82 -0
- package/dist/projection/abi.js +349 -0
- package/dist/projection/apply-arguments.d.ts +12 -0
- package/dist/projection/apply-arguments.js +20 -0
- package/dist/projection/bindings.d.ts +250 -0
- package/dist/projection/bindings.js +340 -0
- package/dist/projection/callee.d.ts +196 -0
- package/dist/projection/callee.js +444 -0
- package/dist/projection/class-property-presence.d.ts +28 -0
- package/dist/projection/class-property-presence.js +58 -0
- package/dist/projection/class-prototype.d.ts +5 -0
- package/dist/projection/class-prototype.js +23 -0
- package/dist/projection/class-storage.d.ts +26 -0
- package/dist/projection/class-storage.js +236 -0
- package/dist/projection/classes.d.ts +283 -0
- package/dist/projection/classes.js +944 -0
- package/dist/projection/coercions.d.ts +20 -0
- package/dist/projection/coercions.js +33 -0
- package/dist/projection/dispatch.d.ts +167 -0
- package/dist/projection/dispatch.js +379 -0
- package/dist/projection/fields.d.ts +123 -0
- package/dist/projection/fields.js +383 -0
- package/dist/projection/instance-test.d.ts +78 -0
- package/dist/projection/instance-test.js +156 -0
- package/dist/projection/slots.d.ts +146 -0
- package/dist/projection/slots.js +663 -0
- package/dist/projection/synthesized-abi.d.ts +61 -0
- package/dist/projection/synthesized-abi.js +94 -0
- package/dist/representation/array-index.d.ts +14 -0
- package/dist/representation/array-index.js +17 -0
- package/dist/representation/callable-object.d.ts +3 -0
- package/dist/representation/callable-object.js +6 -0
- package/dist/representation/collections.d.ts +84 -0
- package/dist/representation/collections.js +139 -0
- package/dist/representation/derive.d.ts +159 -0
- package/dist/representation/derive.js +2546 -0
- package/dist/representation/difference.d.ts +30 -0
- package/dist/representation/difference.js +240 -0
- package/dist/representation/field-descriptor-policy.d.ts +19 -0
- package/dist/representation/field-descriptor-policy.js +39 -0
- package/dist/representation/function-constructor.d.ts +13 -0
- package/dist/representation/function-constructor.js +9 -0
- package/dist/representation/host-abi.d.ts +32 -0
- package/dist/representation/host-abi.js +97 -0
- package/dist/representation/host-templates.d.ts +67 -0
- package/dist/representation/host-templates.js +94 -0
- package/dist/representation/intersection.d.ts +84 -0
- package/dist/representation/intersection.js +149 -0
- package/dist/representation/literal-destination.d.ts +43 -0
- package/dist/representation/literal-destination.js +133 -0
- package/dist/representation/merge.d.ts +18 -0
- package/dist/representation/merge.js +83 -0
- package/dist/representation/model.d.ts +1160 -0
- package/dist/representation/model.js +1019 -0
- package/dist/representation/numeric-index.d.ts +13 -0
- package/dist/representation/numeric-index.js +29 -0
- package/dist/representation/object-shape.d.ts +109 -0
- package/dist/representation/object-shape.js +123 -0
- package/dist/representation/optional.d.ts +12 -0
- package/dist/representation/optional.js +87 -0
- package/dist/representation/plan.d.ts +60 -0
- package/dist/representation/plan.js +78 -0
- package/dist/representation/policies.d.ts +589 -0
- package/dist/representation/policies.js +107 -0
- package/dist/representation/primitive-domain.d.ts +1 -0
- package/dist/representation/primitive-domain.js +1 -0
- package/dist/representation/primitives.d.ts +44 -0
- package/dist/representation/primitives.js +78 -0
- package/dist/representation/publish.d.ts +49 -0
- package/dist/representation/publish.js +648 -0
- package/dist/representation/record-fields.d.ts +96 -0
- package/dist/representation/record-fields.js +116 -0
- package/dist/representation/template-object.d.ts +11 -0
- package/dist/representation/template-object.js +26 -0
- package/dist/representation/union.d.ts +30 -0
- package/dist/representation/union.js +324 -0
- package/dist/representation/value-records.d.ts +3 -0
- package/dist/representation/value-records.js +307 -0
- package/dist/representation/verify.d.ts +17 -0
- package/dist/representation/verify.js +221 -0
- package/dist/semantics/alias-this-field-declaration-transform.d.ts +4 -0
- package/dist/semantics/alias-this-field-declaration-transform.js +207 -0
- package/dist/semantics/ambient-type-realization-transform.d.ts +62 -0
- package/dist/semantics/ambient-type-realization-transform.js +168 -0
- package/dist/semantics/ambient.d.ts +24 -0
- package/dist/semantics/ambient.js +119 -0
- package/dist/semantics/borrowed-builtin-call-bind-source-transform.d.ts +4 -0
- package/dist/semantics/borrowed-builtin-call-bind-source-transform.js +341 -0
- package/dist/semantics/callable-origins.d.ts +46 -0
- package/dist/semantics/callable-origins.js +354 -0
- package/dist/semantics/class-alias.d.ts +7 -0
- package/dist/semantics/class-alias.js +37 -0
- package/dist/semantics/class-heritage.d.ts +34 -0
- package/dist/semantics/class-heritage.js +104 -0
- package/dist/semantics/commonjs-wrapper.d.ts +69 -0
- package/dist/semantics/commonjs-wrapper.js +231 -0
- package/dist/semantics/constructor-slot-subclasses.d.ts +28 -0
- package/dist/semantics/constructor-slot-subclasses.js +112 -0
- package/dist/semantics/declaration-overlay-transform.d.ts +35 -0
- package/dist/semantics/declaration-overlay-transform.js +2157 -0
- package/dist/semantics/define-property-source-transform.d.ts +4 -0
- package/dist/semantics/define-property-source-transform.js +437 -0
- package/dist/semantics/diagnostic-source-preparation.d.ts +24 -0
- package/dist/semantics/diagnostic-source-preparation.js +82 -0
- package/dist/semantics/documentation-ranges.d.ts +22 -0
- package/dist/semantics/documentation-ranges.js +36 -0
- package/dist/semantics/dynamic-fallback.d.ts +86 -0
- package/dist/semantics/dynamic-fallback.js +249 -0
- package/dist/semantics/frontend-timing.d.ts +6 -0
- package/dist/semantics/frontend-timing.js +35 -0
- package/dist/semantics/frontend.d.ts +504 -0
- package/dist/semantics/frontend.js +1157 -0
- package/dist/semantics/host-declaration-provenance.d.ts +11 -0
- package/dist/semantics/host-declaration-provenance.js +18 -0
- package/dist/semantics/host-methods.d.ts +24 -0
- package/dist/semantics/host-methods.js +74 -0
- package/dist/semantics/host-protocols.d.ts +429 -0
- package/dist/semantics/host-protocols.js +1501 -0
- package/dist/semantics/inherited-accessor.d.ts +7 -0
- package/dist/semantics/inherited-accessor.js +48 -0
- package/dist/semantics/interface-families.d.ts +70 -0
- package/dist/semantics/interface-families.js +180 -0
- package/dist/semantics/interface-implementors.d.ts +53 -0
- package/dist/semantics/interface-implementors.js +87 -0
- package/dist/semantics/jsdoc-namepath-transform.d.ts +4 -0
- package/dist/semantics/jsdoc-namepath-transform.js +85 -0
- package/dist/semantics/model/coverage.d.ts +68 -0
- package/dist/semantics/model/coverage.js +73 -0
- package/dist/semantics/model/edges.d.ts +103 -0
- package/dist/semantics/model/edges.js +20 -0
- package/dist/semantics/model/graph.d.ts +55 -0
- package/dist/semantics/model/graph.js +129 -0
- package/dist/semantics/model/operands.d.ts +159 -0
- package/dist/semantics/model/operands.js +26 -0
- package/dist/semantics/model/operations.d.ts +496 -0
- package/dist/semantics/model/operations.js +2 -0
- package/dist/semantics/model/primitive-domain.d.ts +26 -0
- package/dist/semantics/model/primitive-domain.js +38 -0
- package/dist/semantics/model/selected-signature.d.ts +261 -0
- package/dist/semantics/model/selected-signature.js +55 -0
- package/dist/semantics/model/structural-type-table.d.ts +93 -0
- package/dist/semantics/model/structural-type-table.js +237 -0
- package/dist/semantics/model/structural-types.d.ts +367 -0
- package/dist/semantics/model/structural-types.js +200 -0
- package/dist/semantics/module-resolution.d.ts +23 -0
- package/dist/semantics/module-resolution.js +227 -0
- package/dist/semantics/new-callee-class-tag-source-transform.d.ts +4 -0
- package/dist/semantics/new-callee-class-tag-source-transform.js +64 -0
- package/dist/semantics/node-project.d.ts +13 -0
- package/dist/semantics/node-project.js +107 -0
- package/dist/semantics/node-source-hooks.d.ts +1 -0
- package/dist/semantics/node-source-hooks.js +63 -0
- package/dist/semantics/normalize/absent-globals.d.ts +143 -0
- package/dist/semantics/normalize/absent-globals.js +191 -0
- package/dist/semantics/normalize/arguments-objects.d.ts +43 -0
- package/dist/semantics/normalize/arguments-objects.js +60 -0
- package/dist/semantics/normalize/asserted-arm-absence.d.ts +28 -0
- package/dist/semantics/normalize/asserted-arm-absence.js +61 -0
- package/dist/semantics/normalize/assignment-patterns.d.ts +70 -0
- package/dist/semantics/normalize/assignment-patterns.js +147 -0
- package/dist/semantics/normalize/bag-absence.d.ts +8 -0
- package/dist/semantics/normalize/bag-absence.js +42 -0
- package/dist/semantics/normalize/binding-fixpoint.d.ts +36 -0
- package/dist/semantics/normalize/binding-fixpoint.js +140 -0
- package/dist/semantics/normalize/callback-parameter-contracts.d.ts +16 -0
- package/dist/semantics/normalize/callback-parameter-contracts.js +91 -0
- package/dist/semantics/normalize/cells/agreement.d.ts +46 -0
- package/dist/semantics/normalize/cells/agreement.js +34 -0
- package/dist/semantics/normalize/cells/candidates.d.ts +17 -0
- package/dist/semantics/normalize/cells/candidates.js +35 -0
- package/dist/semantics/normalize/cells/facts.d.ts +19 -0
- package/dist/semantics/normalize/cells/facts.js +90 -0
- package/dist/semantics/normalize/cells/index.d.ts +10 -0
- package/dist/semantics/normalize/cells/index.js +6 -0
- package/dist/semantics/normalize/cells/model.d.ts +98 -0
- package/dist/semantics/normalize/cells/model.js +2 -0
- package/dist/semantics/normalize/cells/policies/bag.d.ts +44 -0
- package/dist/semantics/normalize/cells/policies/bag.js +114 -0
- package/dist/semantics/normalize/cells/policies/collection.d.ts +42 -0
- package/dist/semantics/normalize/cells/policies/collection.js +218 -0
- package/dist/semantics/normalize/cells/policies/field.d.ts +28 -0
- package/dist/semantics/normalize/cells/policies/field.js +40 -0
- package/dist/semantics/normalize/cells/policies/index.d.ts +49 -0
- package/dist/semantics/normalize/cells/policies/index.js +58 -0
- package/dist/semantics/normalize/cells/policies/local.d.ts +18 -0
- package/dist/semantics/normalize/cells/policies/local.js +37 -0
- package/dist/semantics/normalize/cells/policies/parameter.d.ts +28 -0
- package/dist/semantics/normalize/cells/policies/parameter.js +39 -0
- package/dist/semantics/normalize/cells/policies/return.d.ts +26 -0
- package/dist/semantics/normalize/cells/policies/return.js +45 -0
- package/dist/semantics/normalize/cells/policies/shared.d.ts +33 -0
- package/dist/semantics/normalize/cells/policies/shared.js +45 -0
- package/dist/semantics/normalize/cells/policy.d.ts +72 -0
- package/dist/semantics/normalize/cells/policy.js +18 -0
- package/dist/semantics/normalize/cells/registry.d.ts +14 -0
- package/dist/semantics/normalize/cells/registry.js +17 -0
- package/dist/semantics/normalize/census-refusal.d.ts +62 -0
- package/dist/semantics/normalize/census-refusal.js +27 -0
- package/dist/semantics/normalize/census.d.ts +109 -0
- package/dist/semantics/normalize/census.js +667 -0
- package/dist/semantics/normalize/collection-bindings.d.ts +278 -0
- package/dist/semantics/normalize/collection-bindings.js +677 -0
- package/dist/semantics/normalize/commonjs-module-record.d.ts +21 -0
- package/dist/semantics/normalize/commonjs-module-record.js +289 -0
- package/dist/semantics/normalize/commonjs-require.d.ts +13 -0
- package/dist/semantics/normalize/commonjs-require.js +1527 -0
- package/dist/semantics/normalize/contribution.d.ts +63 -0
- package/dist/semantics/normalize/contribution.js +172 -0
- package/dist/semantics/normalize/dead-typeof-guards.d.ts +64 -0
- package/dist/semantics/normalize/dead-typeof-guards.js +152 -0
- package/dist/semantics/normalize/deferred-intrinsic-protocols.d.ts +57 -0
- package/dist/semantics/normalize/deferred-intrinsic-protocols.js +109 -0
- package/dist/semantics/normalize/derived-expression-type.d.ts +1070 -0
- package/dist/semantics/normalize/derived-expression-type.js +3463 -0
- package/dist/semantics/normalize/evaluation-order.d.ts +16 -0
- package/dist/semantics/normalize/evaluation-order.js +37 -0
- package/dist/semantics/normalize/field-bindings.d.ts +167 -0
- package/dist/semantics/normalize/field-bindings.js +1125 -0
- package/dist/semantics/normalize/flow/array-element-continuation.d.ts +27 -0
- package/dist/semantics/normalize/flow/array-element-continuation.js +1452 -0
- package/dist/semantics/normalize/flow/callable-array-origins.d.ts +42 -0
- package/dist/semantics/normalize/flow/callable-array-origins.js +318 -0
- package/dist/semantics/normalize/flow/callable-completions.d.ts +45 -0
- package/dist/semantics/normalize/flow/callable-completions.js +82 -0
- package/dist/semantics/normalize/flow/callable-reach.d.ts +237 -0
- package/dist/semantics/normalize/flow/callable-reach.js +7174 -0
- package/dist/semantics/normalize/flow/class-family-member-read.d.ts +16 -0
- package/dist/semantics/normalize/flow/class-family-member-read.js +992 -0
- package/dist/semantics/normalize/flow/collection-value-continuation.d.ts +21 -0
- package/dist/semantics/normalize/flow/collection-value-continuation.js +192 -0
- package/dist/semantics/normalize/flow/component-solver.d.ts +199 -0
- package/dist/semantics/normalize/flow/component-solver.js +587 -0
- package/dist/semantics/normalize/flow/computed-key-set.d.ts +84 -0
- package/dist/semantics/normalize/flow/computed-key-set.js +660 -0
- package/dist/semantics/normalize/flow/export-importers.d.ts +24 -0
- package/dist/semantics/normalize/flow/export-importers.js +269 -0
- package/dist/semantics/normalize/flow/host-text-sink.d.ts +25 -0
- package/dist/semantics/normalize/flow/host-text-sink.js +87 -0
- package/dist/semantics/normalize/flow/intrinsic-array-callback-frames.d.ts +84 -0
- package/dist/semantics/normalize/flow/intrinsic-array-callback-frames.js +180 -0
- package/dist/semantics/normalize/flow/intrinsic-data-definition.d.ts +35 -0
- package/dist/semantics/normalize/flow/intrinsic-data-definition.js +140 -0
- package/dist/semantics/normalize/flow/invocation-facts.d.ts +98 -0
- package/dist/semantics/normalize/flow/invocation-facts.js +257 -0
- package/dist/semantics/normalize/flow/member-call-forwarding.d.ts +122 -0
- package/dist/semantics/normalize/flow/member-call-forwarding.js +835 -0
- package/dist/semantics/normalize/flow/model.d.ts +357 -0
- package/dist/semantics/normalize/flow/model.js +177 -0
- package/dist/semantics/normalize/flow/native-collection-protocol.d.ts +24 -0
- package/dist/semantics/normalize/flow/native-collection-protocol.js +252 -0
- package/dist/semantics/normalize/flow/node-path-token.d.ts +3 -0
- package/dist/semantics/normalize/flow/node-path-token.js +53 -0
- package/dist/semantics/normalize/flow/object-binding-continuation.d.ts +11 -0
- package/dist/semantics/normalize/flow/object-binding-continuation.js +42 -0
- package/dist/semantics/normalize/flow/origin-authority.d.ts +56 -0
- package/dist/semantics/normalize/flow/origin-authority.js +2 -0
- package/dist/semantics/normalize/flow/owned-class-receivers.d.ts +35 -0
- package/dist/semantics/normalize/flow/owned-class-receivers.js +191 -0
- package/dist/semantics/normalize/flow/parameter-values.d.ts +27 -0
- package/dist/semantics/normalize/flow/parameter-values.js +251 -0
- package/dist/semantics/normalize/flow/proof-hypotheses.d.ts +66 -0
- package/dist/semantics/normalize/flow/proof-hypotheses.js +162 -0
- package/dist/semantics/normalize/flow/record-alias-closure.d.ts +27 -0
- package/dist/semantics/normalize/flow/record-alias-closure.js +298 -0
- package/dist/semantics/normalize/flow/seeded-origins.d.ts +43 -0
- package/dist/semantics/normalize/flow/seeded-origins.js +54 -0
- package/dist/semantics/normalize/flow/source-class-data.d.ts +132 -0
- package/dist/semantics/normalize/flow/source-class-data.js +838 -0
- package/dist/semantics/normalize/flow/source-class-instanceof.d.ts +13 -0
- package/dist/semantics/normalize/flow/source-class-instanceof.js +37 -0
- package/dist/semantics/normalize/flow/source-class-static-data.d.ts +16 -0
- package/dist/semantics/normalize/flow/source-class-static-data.js +47 -0
- package/dist/semantics/normalize/flow/source-construction-frames.d.ts +12 -0
- package/dist/semantics/normalize/flow/source-construction-frames.js +118 -0
- package/dist/semantics/normalize/flow/source-invocation-frame-layout.d.ts +34 -0
- package/dist/semantics/normalize/flow/source-invocation-frame-layout.js +78 -0
- package/dist/semantics/normalize/flow/source-invocation-receiver.d.ts +37 -0
- package/dist/semantics/normalize/flow/source-invocation-receiver.js +95 -0
- package/dist/semantics/normalize/flow/source-prototype-method-identity.d.ts +23 -0
- package/dist/semantics/normalize/flow/source-prototype-method-identity.js +81 -0
- package/dist/semantics/normalize/flow/source-record-data.d.ts +29 -0
- package/dist/semantics/normalize/flow/source-record-data.js +626 -0
- package/dist/semantics/normalize/flow/source-value-session.d.ts +77 -0
- package/dist/semantics/normalize/flow/source-value-session.js +3089 -0
- package/dist/semantics/normalize/flow/targets.d.ts +118 -0
- package/dist/semantics/normalize/flow/targets.js +405 -0
- package/dist/semantics/normalize/flow/value-flow.d.ts +36 -0
- package/dist/semantics/normalize/flow/value-flow.js +823 -0
- package/dist/semantics/normalize/flow/value-provenance.d.ts +31 -0
- package/dist/semantics/normalize/flow/value-provenance.js +112 -0
- package/dist/semantics/normalize/gating.d.ts +51 -0
- package/dist/semantics/normalize/gating.js +560 -0
- package/dist/semantics/normalize/generic-function-choice.d.ts +31 -0
- package/dist/semantics/normalize/generic-function-choice.js +115 -0
- package/dist/semantics/normalize/global-host-mutations.d.ts +51 -0
- package/dist/semantics/normalize/global-host-mutations.js +5989 -0
- package/dist/semantics/normalize/host-effect-contracts.d.ts +77 -0
- package/dist/semantics/normalize/host-effect-contracts.js +143 -0
- package/dist/semantics/normalize/host-mutation-computed-keys.d.ts +38 -0
- package/dist/semantics/normalize/host-mutation-computed-keys.js +26 -0
- package/dist/semantics/normalize/host-mutation-key-reader.d.ts +66 -0
- package/dist/semantics/normalize/host-mutation-key-reader.js +192 -0
- package/dist/semantics/normalize/host-mutation-keys.d.ts +114 -0
- package/dist/semantics/normalize/host-mutation-keys.js +183 -0
- package/dist/semantics/normalize/identities.d.ts +172 -0
- package/dist/semantics/normalize/identities.js +426 -0
- package/dist/semantics/normalize/implicit-arguments-tuple.d.ts +97 -0
- package/dist/semantics/normalize/implicit-arguments-tuple.js +197 -0
- package/dist/semantics/normalize/implicit-arguments.d.ts +11 -0
- package/dist/semantics/normalize/implicit-arguments.js +39 -0
- package/dist/semantics/normalize/instantiation.d.ts +73 -0
- package/dist/semantics/normalize/instantiation.js +399 -0
- package/dist/semantics/normalize/intrinsic-property-call.d.ts +18 -0
- package/dist/semantics/normalize/intrinsic-property-call.js +69 -0
- package/dist/semantics/normalize/intrinsic-prototype.d.ts +41 -0
- package/dist/semantics/normalize/intrinsic-prototype.js +87 -0
- package/dist/semantics/normalize/intrinsic-static-member.d.ts +14 -0
- package/dist/semantics/normalize/intrinsic-static-member.js +23 -0
- package/dist/semantics/normalize/jsdoc-type-names.d.ts +32 -0
- package/dist/semantics/normalize/jsdoc-type-names.js +512 -0
- package/dist/semantics/normalize/keyof.d.ts +32 -0
- package/dist/semantics/normalize/keyof.js +110 -0
- package/dist/semantics/normalize/local-bindings.d.ts +176 -0
- package/dist/semantics/normalize/local-bindings.js +1826 -0
- package/dist/semantics/normalize/logical-result-type.d.ts +16 -0
- package/dist/semantics/normalize/logical-result-type.js +102 -0
- package/dist/semantics/normalize/namespace-paths.d.ts +57 -0
- package/dist/semantics/normalize/namespace-paths.js +108 -0
- package/dist/semantics/normalize/normalize.d.ts +57 -0
- package/dist/semantics/normalize/normalize.js +68 -0
- package/dist/semantics/normalize/object-bag-bindings.d.ts +305 -0
- package/dist/semantics/normalize/object-bag-bindings.js +1290 -0
- package/dist/semantics/normalize/omitted-stated-parameter.d.ts +53 -0
- package/dist/semantics/normalize/omitted-stated-parameter.js +59 -0
- package/dist/semantics/normalize/open-type-form.d.ts +10 -0
- package/dist/semantics/normalize/open-type-form.js +32 -0
- package/dist/semantics/normalize/parameter-bindings.d.ts +361 -0
- package/dist/semantics/normalize/parameter-bindings.js +3339 -0
- package/dist/semantics/normalize/parameter-slot.d.ts +264 -0
- package/dist/semantics/normalize/parameter-slot.js +491 -0
- package/dist/semantics/normalize/physical-overload-result.d.ts +77 -0
- package/dist/semantics/normalize/physical-overload-result.js +99 -0
- package/dist/semantics/normalize/primitive-property-absence.d.ts +12 -0
- package/dist/semantics/normalize/primitive-property-absence.js +60 -0
- package/dist/semantics/normalize/producer-context.d.ts +376 -0
- package/dist/semantics/normalize/producer-context.js +1 -0
- package/dist/semantics/normalize/producers/allocations.d.ts +3 -0
- package/dist/semantics/normalize/producers/allocations.js +863 -0
- package/dist/semantics/normalize/producers/binding-kind.d.ts +37 -0
- package/dist/semantics/normalize/producers/binding-kind.js +36 -0
- package/dist/semantics/normalize/producers/bindings.d.ts +85 -0
- package/dist/semantics/normalize/producers/bindings.js +710 -0
- package/dist/semantics/normalize/producers/boundary.d.ts +34 -0
- package/dist/semantics/normalize/producers/boundary.js +123 -0
- package/dist/semantics/normalize/producers/class-lifecycle.d.ts +3 -0
- package/dist/semantics/normalize/producers/class-lifecycle.js +902 -0
- package/dist/semantics/normalize/producers/computations.d.ts +3 -0
- package/dist/semantics/normalize/producers/computations.js +613 -0
- package/dist/semantics/normalize/producers/control-loops.d.ts +30 -0
- package/dist/semantics/normalize/producers/control-loops.js +91 -0
- package/dist/semantics/normalize/producers/control.d.ts +30 -0
- package/dist/semantics/normalize/producers/control.js +923 -0
- package/dist/semantics/normalize/producers/declaration-lifecycle.d.ts +3 -0
- package/dist/semantics/normalize/producers/declaration-lifecycle.js +390 -0
- package/dist/semantics/normalize/producers/destructuring.d.ts +45 -0
- package/dist/semantics/normalize/producers/destructuring.js +1180 -0
- package/dist/semantics/normalize/producers/dynamic-language.d.ts +3 -0
- package/dist/semantics/normalize/producers/dynamic-language.js +70 -0
- package/dist/semantics/normalize/producers/erasure.d.ts +51 -0
- package/dist/semantics/normalize/producers/erasure.js +90 -0
- package/dist/semantics/normalize/producers/installed.d.ts +3 -0
- package/dist/semantics/normalize/producers/installed.js +55 -0
- package/dist/semantics/normalize/producers/invocations.d.ts +35 -0
- package/dist/semantics/normalize/producers/invocations.js +1796 -0
- package/dist/semantics/normalize/producers/iteration-yield.d.ts +59 -0
- package/dist/semantics/normalize/producers/iteration-yield.js +235 -0
- package/dist/semantics/normalize/producers/jsx.d.ts +3 -0
- package/dist/semantics/normalize/producers/jsx.js +360 -0
- package/dist/semantics/normalize/producers/mint.d.ts +39 -0
- package/dist/semantics/normalize/producers/mint.js +44 -0
- package/dist/semantics/normalize/producers/nullish.d.ts +16 -0
- package/dist/semantics/normalize/producers/nullish.js +27 -0
- package/dist/semantics/normalize/producers/object-assignment.d.ts +8 -0
- package/dist/semantics/normalize/producers/object-assignment.js +204 -0
- package/dist/semantics/normalize/producers/object-tag.d.ts +9 -0
- package/dist/semantics/normalize/producers/object-tag.js +119 -0
- package/dist/semantics/normalize/producers/optional-chain.d.ts +44 -0
- package/dist/semantics/normalize/producers/optional-chain.js +75 -0
- package/dist/semantics/normalize/producers/properties.d.ts +21 -0
- package/dist/semantics/normalize/producers/properties.js +1053 -0
- package/dist/semantics/normalize/producers/protocol.d.ts +67 -0
- package/dist/semantics/normalize/producers/protocol.js +695 -0
- package/dist/semantics/normalize/producers/references.d.ts +53 -0
- package/dist/semantics/normalize/producers/references.js +1027 -0
- package/dist/semantics/normalize/producers/shared.d.ts +561 -0
- package/dist/semantics/normalize/producers/shared.js +1683 -0
- package/dist/semantics/normalize/producers/spread-arguments.d.ts +72 -0
- package/dist/semantics/normalize/producers/spread-arguments.js +375 -0
- package/dist/semantics/normalize/producers/tagged-template.d.ts +5 -0
- package/dist/semantics/normalize/producers/tagged-template.js +307 -0
- package/dist/semantics/normalize/producers/tuple-spread.d.ts +189 -0
- package/dist/semantics/normalize/producers/tuple-spread.js +279 -0
- package/dist/semantics/normalize/property-key-domain.d.ts +36 -0
- package/dist/semantics/normalize/property-key-domain.js +150 -0
- package/dist/semantics/normalize/reachability.d.ts +189 -0
- package/dist/semantics/normalize/reachability.js +1057 -0
- package/dist/semantics/normalize/reassigned-bindings.d.ts +13 -0
- package/dist/semantics/normalize/reassigned-bindings.js +41 -0
- package/dist/semantics/normalize/record-storage-families.d.ts +11 -0
- package/dist/semantics/normalize/record-storage-families.js +109 -0
- package/dist/semantics/normalize/return-bindings.d.ts +260 -0
- package/dist/semantics/normalize/return-bindings.js +1311 -0
- package/dist/semantics/normalize/return-paths.d.ts +2 -0
- package/dist/semantics/normalize/return-paths.js +28 -0
- package/dist/semantics/normalize/script-global-redefinition.d.ts +44 -0
- package/dist/semantics/normalize/script-global-redefinition.js +75 -0
- package/dist/semantics/normalize/specialization.d.ts +201 -0
- package/dist/semantics/normalize/specialization.js +1824 -0
- package/dist/semantics/normalize/structural-array-element.d.ts +176 -0
- package/dist/semantics/normalize/structural-array-element.js +700 -0
- package/dist/semantics/normalize/structural-array-read.d.ts +13 -0
- package/dist/semantics/normalize/structural-array-read.js +124 -0
- package/dist/semantics/normalize/structural-callable.d.ts +12 -0
- package/dist/semantics/normalize/structural-callable.js +82 -0
- package/dist/semantics/normalize/structural-creation-order.d.ts +32 -0
- package/dist/semantics/normalize/structural-creation-order.js +111 -0
- package/dist/semantics/normalize/structural-declarations.d.ts +395 -0
- package/dist/semantics/normalize/structural-declarations.js +968 -0
- package/dist/semantics/normalize/structural-declared-body.d.ts +25 -0
- package/dist/semantics/normalize/structural-declared-body.js +147 -0
- package/dist/semantics/normalize/structural-generics.d.ts +56 -0
- package/dist/semantics/normalize/structural-generics.js +120 -0
- package/dist/semantics/normalize/structural-indexed-access.d.ts +11 -0
- package/dist/semantics/normalize/structural-indexed-access.js +126 -0
- package/dist/semantics/normalize/structural-instantiated-member.d.ts +64 -0
- package/dist/semantics/normalize/structural-instantiated-member.js +407 -0
- package/dist/semantics/normalize/structural-joins.d.ts +44 -0
- package/dist/semantics/normalize/structural-joins.js +143 -0
- package/dist/semantics/normalize/structural-layout-relevance.d.ts +29 -0
- package/dist/semantics/normalize/structural-layout-relevance.js +391 -0
- package/dist/semantics/normalize/structural-layout-type.d.ts +11 -0
- package/dist/semantics/normalize/structural-layout-type.js +1468 -0
- package/dist/semantics/normalize/structural-leaves.d.ts +49 -0
- package/dist/semantics/normalize/structural-leaves.js +125 -0
- package/dist/semantics/normalize/structural-local-union.d.ts +13 -0
- package/dist/semantics/normalize/structural-local-union.js +133 -0
- package/dist/semantics/normalize/structural-members.d.ts +26 -0
- package/dist/semantics/normalize/structural-members.js +84 -0
- package/dist/semantics/normalize/structural-mutable-method.d.ts +15 -0
- package/dist/semantics/normalize/structural-mutable-method.js +109 -0
- package/dist/semantics/normalize/structural-parts.d.ts +131 -0
- package/dist/semantics/normalize/structural-parts.js +737 -0
- package/dist/semantics/normalize/structural-receiver.d.ts +66 -0
- package/dist/semantics/normalize/structural-receiver.js +551 -0
- package/dist/semantics/normalize/structural-rules.d.ts +112 -0
- package/dist/semantics/normalize/structural-rules.js +93 -0
- package/dist/semantics/normalize/structural-self-reference.d.ts +150 -0
- package/dist/semantics/normalize/structural-self-reference.js +494 -0
- package/dist/semantics/normalize/structural.d.ts +202 -0
- package/dist/semantics/normalize/structural.js +4218 -0
- package/dist/semantics/normalize/unreduced-type-form.d.ts +41 -0
- package/dist/semantics/normalize/unreduced-type-form.js +53 -0
- package/dist/semantics/normalize/unresolvable-names.d.ts +56 -0
- package/dist/semantics/normalize/unresolvable-names.js +140 -0
- package/dist/semantics/package-sources.d.ts +16 -0
- package/dist/semantics/package-sources.js +365 -0
- package/dist/semantics/program.d.ts +151 -0
- package/dist/semantics/program.js +667 -0
- package/dist/semantics/prototype-install-source-transform.d.ts +4 -0
- package/dist/semantics/prototype-install-source-transform.js +341 -0
- package/dist/semantics/prototype-object-class-source-transform.d.ts +4 -0
- package/dist/semantics/prototype-object-class-source-transform.js +525 -0
- package/dist/semantics/prototype-reparenting.d.ts +50 -0
- package/dist/semantics/prototype-reparenting.js +317 -0
- package/dist/semantics/stable-checker.d.ts +10 -0
- package/dist/semantics/stable-checker.js +148 -0
- package/dist/semantics/subclass-member-overlay-transform.d.ts +35 -0
- package/dist/semantics/subclass-member-overlay-transform.js +1418 -0
- package/dist/semantics/symbol-keyed-expando-source-transform.d.ts +4 -0
- package/dist/semantics/symbol-keyed-expando-source-transform.js +66 -0
- package/dist/semantics/this-constructor-source-transform.d.ts +4 -0
- package/dist/semantics/this-constructor-source-transform.js +122 -0
- package/dist/semantics/uninstantiable-classes.d.ts +22 -0
- package/dist/semantics/uninstantiable-classes.js +75 -0
- package/dist/targets/cpp/balanced-units.d.ts +3 -0
- package/dist/targets/cpp/balanced-units.js +58 -0
- package/dist/targets/cpp/borrowed-call-entry.d.ts +30 -0
- package/dist/targets/cpp/borrowed-call-entry.js +68 -0
- package/dist/targets/cpp/captures.d.ts +68 -0
- package/dist/targets/cpp/captures.js +269 -0
- package/dist/targets/cpp/class-layout.d.ts +313 -0
- package/dist/targets/cpp/class-layout.js +483 -0
- package/dist/targets/cpp/class-properties/computed-method-value.d.ts +25 -0
- package/dist/targets/cpp/class-properties/computed-method-value.js +22 -0
- package/dist/targets/cpp/class-properties/emit-class-properties.d.ts +216 -0
- package/dist/targets/cpp/class-properties/emit-class-properties.js +917 -0
- package/dist/targets/cpp/class-properties/native-prototype.d.ts +11 -0
- package/dist/targets/cpp/class-properties/native-prototype.js +101 -0
- package/dist/targets/cpp/class-ref-transport.d.ts +12 -0
- package/dist/targets/cpp/class-ref-transport.js +32 -0
- package/dist/targets/cpp/conversions.d.ts +3 -0
- package/dist/targets/cpp/conversions.js +2027 -0
- package/dist/targets/cpp/deferral-safety.d.ts +82 -0
- package/dist/targets/cpp/deferral-safety.js +86 -0
- package/dist/targets/cpp/direct-call-receivers.d.ts +37 -0
- package/dist/targets/cpp/direct-call-receivers.js +79 -0
- package/dist/targets/cpp/document.d.ts +74 -0
- package/dist/targets/cpp/document.js +106 -0
- package/dist/targets/cpp/emit-allocation.d.ts +60 -0
- package/dist/targets/cpp/emit-allocation.js +573 -0
- package/dist/targets/cpp/emit-arrays.d.ts +119 -0
- package/dist/targets/cpp/emit-arrays.js +1047 -0
- package/dist/targets/cpp/emit-binding-reference.d.ts +17 -0
- package/dist/targets/cpp/emit-binding-reference.js +120 -0
- package/dist/targets/cpp/emit-bindings.d.ts +158 -0
- package/dist/targets/cpp/emit-bindings.js +769 -0
- package/dist/targets/cpp/emit-buffers.d.ts +156 -0
- package/dist/targets/cpp/emit-buffers.js +573 -0
- package/dist/targets/cpp/emit-callable.d.ts +230 -0
- package/dist/targets/cpp/emit-callable.js +2154 -0
- package/dist/targets/cpp/emit-carrier-members.d.ts +406 -0
- package/dist/targets/cpp/emit-carrier-members.js +1224 -0
- package/dist/targets/cpp/emit-coercion.d.ts +26 -0
- package/dist/targets/cpp/emit-coercion.js +39 -0
- package/dist/targets/cpp/emit-context.d.ts +1882 -0
- package/dist/targets/cpp/emit-context.js +904 -0
- package/dist/targets/cpp/emit-dynamic-properties.d.ts +174 -0
- package/dist/targets/cpp/emit-dynamic-properties.js +1249 -0
- package/dist/targets/cpp/emit-equality.d.ts +39 -0
- package/dist/targets/cpp/emit-equality.js +472 -0
- package/dist/targets/cpp/emit-exceptions.d.ts +104 -0
- package/dist/targets/cpp/emit-exceptions.js +513 -0
- package/dist/targets/cpp/emit-field-owner.d.ts +4 -0
- package/dist/targets/cpp/emit-field-owner.js +27 -0
- package/dist/targets/cpp/emit-in.d.ts +31 -0
- package/dist/targets/cpp/emit-in.js +426 -0
- package/dist/targets/cpp/emit-instanceof.d.ts +19 -0
- package/dist/targets/cpp/emit-instanceof.js +601 -0
- package/dist/targets/cpp/emit-integers.d.ts +68 -0
- package/dist/targets/cpp/emit-integers.js +184 -0
- package/dist/targets/cpp/emit-iterator.d.ts +100 -0
- package/dist/targets/cpp/emit-iterator.js +1137 -0
- package/dist/targets/cpp/emit-json.d.ts +73 -0
- package/dist/targets/cpp/emit-json.js +823 -0
- package/dist/targets/cpp/emit-jsx.d.ts +74 -0
- package/dist/targets/cpp/emit-jsx.js +806 -0
- package/dist/targets/cpp/emit-mixed-binary.d.ts +51 -0
- package/dist/targets/cpp/emit-mixed-binary.js +60 -0
- package/dist/targets/cpp/emit-namespaces.d.ts +101 -0
- package/dist/targets/cpp/emit-namespaces.js +133 -0
- package/dist/targets/cpp/emit-narrowing.d.ts +738 -0
- package/dist/targets/cpp/emit-narrowing.js +3967 -0
- package/dist/targets/cpp/emit-native-equality.d.ts +3 -0
- package/dist/targets/cpp/emit-native-equality.js +14 -0
- package/dist/targets/cpp/emit-native-selection.d.ts +5 -0
- package/dist/targets/cpp/emit-native-selection.js +37 -0
- package/dist/targets/cpp/emit-object-tag.d.ts +4 -0
- package/dist/targets/cpp/emit-object-tag.js +108 -0
- package/dist/targets/cpp/emit-owned-record.d.ts +2 -0
- package/dist/targets/cpp/emit-owned-record.js +15 -0
- package/dist/targets/cpp/emit-presence.d.ts +78 -0
- package/dist/targets/cpp/emit-presence.js +376 -0
- package/dist/targets/cpp/emit-properties.d.ts +105 -0
- package/dist/targets/cpp/emit-properties.js +1640 -0
- package/dist/targets/cpp/emit-record-view.d.ts +20 -0
- package/dist/targets/cpp/emit-record-view.js +354 -0
- package/dist/targets/cpp/emit-return.d.ts +5 -0
- package/dist/targets/cpp/emit-return.js +237 -0
- package/dist/targets/cpp/emit-sum-widening.d.ts +10 -0
- package/dist/targets/cpp/emit-sum-widening.js +69 -0
- package/dist/targets/cpp/emit-tonumber.d.ts +38 -0
- package/dist/targets/cpp/emit-tonumber.js +177 -0
- package/dist/targets/cpp/emit-tostring.d.ts +92 -0
- package/dist/targets/cpp/emit-tostring.js +556 -0
- package/dist/targets/cpp/emit-typeof.d.ts +49 -0
- package/dist/targets/cpp/emit-typeof.js +317 -0
- package/dist/targets/cpp/emit-union-properties.d.ts +181 -0
- package/dist/targets/cpp/emit-union-properties.js +1281 -0
- package/dist/targets/cpp/emit.d.ts +45 -0
- package/dist/targets/cpp/emit.js +2225 -0
- package/dist/targets/cpp/error-types.d.ts +4 -0
- package/dist/targets/cpp/error-types.js +11 -0
- package/dist/targets/cpp/function-source-reads.d.ts +72 -0
- package/dist/targets/cpp/function-source-reads.js +148 -0
- package/dist/targets/cpp/host/atomics.d.ts +25 -0
- package/dist/targets/cpp/host/atomics.js +63 -0
- package/dist/targets/cpp/host/core-globals.d.ts +108 -0
- package/dist/targets/cpp/host/core-globals.js +158 -0
- package/dist/targets/cpp/host/dynamic-argument-members.d.ts +42 -0
- package/dist/targets/cpp/host/dynamic-argument-members.js +61 -0
- package/dist/targets/cpp/host/emit-host-arity.d.ts +139 -0
- package/dist/targets/cpp/host/emit-host-arity.js +232 -0
- package/dist/targets/cpp/host/emit-host-invoke.d.ts +91 -0
- package/dist/targets/cpp/host/emit-host-invoke.js +1105 -0
- package/dist/targets/cpp/host/emit-host-object.d.ts +55 -0
- package/dist/targets/cpp/host/emit-host-object.js +2206 -0
- package/dist/targets/cpp/host/emit-host-properties.d.ts +69 -0
- package/dist/targets/cpp/host/emit-host-properties.js +533 -0
- package/dist/targets/cpp/host/emit-host-reflect.d.ts +4 -0
- package/dist/targets/cpp/host/emit-host-reflect.js +180 -0
- package/dist/targets/cpp/host/emit-host-value.d.ts +120 -0
- package/dist/targets/cpp/host/emit-host-value.js +392 -0
- package/dist/targets/cpp/host/host-members.d.ts +505 -0
- package/dist/targets/cpp/host/host-members.js +819 -0
- package/dist/targets/cpp/host/host-method-aliases.d.ts +30 -0
- package/dist/targets/cpp/host/host-method-aliases.js +76 -0
- package/dist/targets/cpp/host/native-protocols.d.ts +47 -0
- package/dist/targets/cpp/host/native-protocols.js +183 -0
- package/dist/targets/cpp/host/object-protocol.d.ts +351 -0
- package/dist/targets/cpp/host/object-protocol.js +671 -0
- package/dist/targets/cpp/host-namespace-reads.d.ts +83 -0
- package/dist/targets/cpp/host-namespace-reads.js +113 -0
- package/dist/targets/cpp/manifest/capabilities.d.ts +39 -0
- package/dist/targets/cpp/manifest/capabilities.js +1484 -0
- package/dist/targets/cpp/manifest.d.ts +23 -0
- package/dist/targets/cpp/manifest.js +113 -0
- package/dist/targets/cpp/native-error-base.d.ts +38 -0
- package/dist/targets/cpp/native-error-base.js +74 -0
- package/dist/targets/cpp/native-narrowing-transport.d.ts +12 -0
- package/dist/targets/cpp/native-narrowing-transport.js +151 -0
- package/dist/targets/cpp/native-selection-helpers.d.ts +11 -0
- package/dist/targets/cpp/native-selection-helpers.js +46 -0
- package/dist/targets/cpp/native-symbol-keys.d.ts +8 -0
- package/dist/targets/cpp/native-symbol-keys.js +34 -0
- package/dist/targets/cpp/ownership.d.ts +43 -0
- package/dist/targets/cpp/ownership.js +38 -0
- package/dist/targets/cpp/owning-conversion-input.d.ts +14 -0
- package/dist/targets/cpp/owning-conversion-input.js +12 -0
- package/dist/targets/cpp/property-read-claims.d.ts +62 -0
- package/dist/targets/cpp/property-read-claims.js +23 -0
- package/dist/targets/cpp/property-store-claims.d.ts +62 -0
- package/dist/targets/cpp/property-store-claims.js +26 -0
- package/dist/targets/cpp/prototype/emit-prototype-array.d.ts +111 -0
- package/dist/targets/cpp/prototype/emit-prototype-array.js +602 -0
- package/dist/targets/cpp/prototype/emit-prototype-date.d.ts +133 -0
- package/dist/targets/cpp/prototype/emit-prototype-date.js +515 -0
- package/dist/targets/cpp/prototype/emit-prototype-error.d.ts +13 -0
- package/dist/targets/cpp/prototype/emit-prototype-error.js +54 -0
- package/dist/targets/cpp/prototype/emit-prototype-invoke.d.ts +34 -0
- package/dist/targets/cpp/prototype/emit-prototype-invoke.js +1003 -0
- package/dist/targets/cpp/prototype/emit-prototype-iterator.d.ts +47 -0
- package/dist/targets/cpp/prototype/emit-prototype-iterator.js +250 -0
- package/dist/targets/cpp/prototype/emit-prototype-promise.d.ts +46 -0
- package/dist/targets/cpp/prototype/emit-prototype-promise.js +143 -0
- package/dist/targets/cpp/prototype/emit-prototype-regexp.d.ts +335 -0
- package/dist/targets/cpp/prototype/emit-prototype-regexp.js +853 -0
- package/dist/targets/cpp/prototype/emit-prototype-string.d.ts +42 -0
- package/dist/targets/cpp/prototype/emit-prototype-string.js +252 -0
- package/dist/targets/cpp/prototype/prototype-method-reads.d.ts +25 -0
- package/dist/targets/cpp/prototype/prototype-method-reads.js +182 -0
- package/dist/targets/cpp/reactive-dependencies.d.ts +140 -0
- package/dist/targets/cpp/reactive-dependencies.js +432 -0
- package/dist/targets/cpp/reactive-origins.d.ts +44 -0
- package/dist/targets/cpp/reactive-origins.js +82 -0
- package/dist/targets/cpp/records.d.ts +277 -0
- package/dist/targets/cpp/records.js +2427 -0
- package/dist/targets/cpp/recursive-containers.d.ts +43 -0
- package/dist/targets/cpp/recursive-containers.js +144 -0
- package/dist/targets/cpp/regexp-types.d.ts +56 -0
- package/dist/targets/cpp/regexp-types.js +59 -0
- package/dist/targets/cpp/substrate.d.ts +125 -0
- package/dist/targets/cpp/substrate.js +12 -0
- package/dist/targets/cpp/translation-unit.d.ts +219 -0
- package/dist/targets/cpp/translation-unit.js +2063 -0
- package/dist/targets/cpp/types.d.ts +280 -0
- package/dist/targets/cpp/types.js +1135 -0
- package/dist/targets/cpp/virtual-methods.d.ts +95 -0
- package/dist/targets/cpp/virtual-methods.js +264 -0
- package/package.json +95 -0
- package/src/targets/cpp/runtime/gea_dynamic_proxy.h +824 -0
- package/src/targets/cpp/runtime/gea_eval.h +1376 -0
- package/src/targets/cpp/runtime/gea_native_class_prototype.h +44 -0
- package/src/targets/cpp/runtime/gea_runtime.h +29222 -0
- package/src/targets/cpp/runtime/gea_runtime_builtins.cpp +55 -0
|
@@ -0,0 +1,2427 @@
|
|
|
1
|
+
import { declaredFieldRepresentationOf, declaredRecordFieldOf, nativeBaseFieldOf, recordAccessorBodiesOf, recordAccessorsOfShape, recordFieldsOfShape } from '../../projection/fields.js';
|
|
2
|
+
export { declaredFieldRepresentationOf, declaredRecordFieldOf, nativeBaseFieldOf, recordAccessorBodiesOf, recordAccessorsOfShape, recordFieldsOfShape };
|
|
3
|
+
import { lazyArrowFieldPlansForClass } from './class-layout.js';
|
|
4
|
+
import { representationKey, walkRepresentation } from '../../representation/model.js';
|
|
5
|
+
import { dynamicFieldAbsencePolicy } from '../../representation/field-descriptor-policy.js';
|
|
6
|
+
import { integerStorageSlot } from '../../ir/integer-storage.js';
|
|
7
|
+
import { recordIndexDomainContainsKey } from '../../ir/native-record-index.js';
|
|
8
|
+
import { cppBodyName, cppClassName, cppNarrowedIntegerType, cppRecordFieldKeyIsSymbol, cppRecordFieldAttributesName, cppRecordFieldName, cppRecordAccessorEnvironmentName, cppRecordFieldPresenceName, cppArrayExtensionStructName, cppRecordStructName, cppStringLiteral, cppTypeOf } from './types.js';
|
|
9
|
+
import { storedEnvironmentText } from './emit-context.js';
|
|
10
|
+
import { boxedText, dynamicCarrierBoxText, dynamicTagFor } from './emit-narrowing.js';
|
|
11
|
+
/**
|
|
12
|
+
* The physical Optional carrier does not remember whether its empty state is
|
|
13
|
+
* JavaScript null or undefined. NativeFieldWrite therefore carries the
|
|
14
|
+
* representation's absence policy at every optional and union node.
|
|
15
|
+
*/
|
|
16
|
+
const nativeFieldPolicyType = (representation) => {
|
|
17
|
+
switch (representation.kind) {
|
|
18
|
+
case 'optional':
|
|
19
|
+
return `gea::NativeFieldOptionalPolicy<${representation.absence === 'null' ? 'true' : 'false'}, ${nativeFieldPolicyType(representation.payload)}>`;
|
|
20
|
+
case 'tagged-union':
|
|
21
|
+
return `gea::NativeFieldUnionPolicy<${representation.arms.map((arm) => nativeFieldPolicyType(arm.value)).join(', ')}>`;
|
|
22
|
+
default:
|
|
23
|
+
return 'gea::NativeFieldLeafPolicy';
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const nativeFieldPolicyValue = (representation) => `${nativeFieldPolicyType(representation)}{}`;
|
|
27
|
+
const nativeFieldPolicyArgument = (representation) => representation.kind === 'optional' || representation.kind === 'tagged-union' ? `, ${nativeFieldPolicyValue(representation)}` : '';
|
|
28
|
+
const nativeFieldPolicyTemplate = (representation) => representation.kind === 'optional' || representation.kind === 'tagged-union' ? `<${nativeFieldPolicyType(representation)}>` : '';
|
|
29
|
+
/**
|
|
30
|
+
* Every class struct's base link, keyed by the struct name that derives.
|
|
31
|
+
*
|
|
32
|
+
* The base's shape is read off its *instance carrier* rather than re-derived
|
|
33
|
+
* from a declaration, because that carrier is what the class's own construction
|
|
34
|
+
* publishes as the thing it returns -- the same value `constructDefinitionsOf`
|
|
35
|
+
* spells as the construct's result. A class that states a base whose instance
|
|
36
|
+
* carrier is absent or is not a class reference names no shape to subtract; the
|
|
37
|
+
* reason is recorded rather than dropped, so the struct refuses by name instead
|
|
38
|
+
* of silently emitting flat and colliding with a base body later.
|
|
39
|
+
*/
|
|
40
|
+
const classBaseLinks = (classes) => {
|
|
41
|
+
const links = new Map();
|
|
42
|
+
const unlinkable = new Map();
|
|
43
|
+
for (const layout of classes.values()) {
|
|
44
|
+
if (layout.nativeBase !== null) {
|
|
45
|
+
const structName = cppClassName(layout.declaration);
|
|
46
|
+
if (layout.nativeBase.instance.native === null) {
|
|
47
|
+
unlinkable.set(structName, `its native base ${layout.nativeBase.protocol} published no native C++ layout`);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
links.set(structName, {
|
|
51
|
+
structName: layout.nativeBase.instance.native,
|
|
52
|
+
shapeId: layout.nativeBase.instance.shapeId,
|
|
53
|
+
native: true
|
|
54
|
+
});
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (layout.base === null)
|
|
58
|
+
continue;
|
|
59
|
+
const structName = cppClassName(layout.declaration);
|
|
60
|
+
const instance = classes.get(layout.base)?.instance;
|
|
61
|
+
if (!instance || instance.kind !== 'class-ref') {
|
|
62
|
+
unlinkable.set(structName, `its base ${layout.base} published no class-reference instance carrier to take a layout from`);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
links.set(structName, { structName: cppClassName(layout.base), shapeId: instance.shapeId, native: false });
|
|
66
|
+
}
|
|
67
|
+
return { links, unlinkable };
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* The one shape a class's struct is built from, by struct name.
|
|
71
|
+
*
|
|
72
|
+
* A class's C++ struct is named by its DECLARATION (`cppClassName`), and the
|
|
73
|
+
* projection collapses a generic class's copies onto one declaration
|
|
74
|
+
* (`projection/classes.ts`'s `collapseSpecializations`). Its `class-ref`
|
|
75
|
+
* carriers, however, still name whichever instance SHAPE the site that built
|
|
76
|
+
* them derived -- an uninstantiated root's `Context<E>` and a monomorphized
|
|
77
|
+
* copy's `Context<{ ... }>` are two shapes under one struct name.
|
|
78
|
+
*
|
|
79
|
+
* `collectRequiredStructs`'s cycle guard then rendered the struct from
|
|
80
|
+
* whichever of them the plan happened to walk first, and every operation typed
|
|
81
|
+
* against the other one compiled against members that were not there: hono's
|
|
82
|
+
* `Context.env` was declared `gea::Value` from the unbound root while its own
|
|
83
|
+
* constructor stored an `Optional<Ref<...>>` into it, and clang rejected the
|
|
84
|
+
* assignment. Silently, from the compiler's side -- both halves certified.
|
|
85
|
+
*
|
|
86
|
+
* `ClassLayout.instance` is already the authority for the OTHER half of this
|
|
87
|
+
* question: `classBaseLinks` above subtracts a base's members using the base
|
|
88
|
+
* class's instance shape, not whatever shape a `class-ref` to it carried. This
|
|
89
|
+
* makes the struct itself, and every field lookup against it, read the same
|
|
90
|
+
* one, so a class has exactly one layout everywhere.
|
|
91
|
+
*/
|
|
92
|
+
export const classInstanceShapes = (classes) => {
|
|
93
|
+
const byStruct = new Map();
|
|
94
|
+
for (const layout of classes.values()) {
|
|
95
|
+
const instance = layout.instance;
|
|
96
|
+
if (instance && instance.kind === 'class-ref')
|
|
97
|
+
byStruct.set(cppClassName(layout.declaration), instance.shapeId);
|
|
98
|
+
}
|
|
99
|
+
return byStruct;
|
|
100
|
+
};
|
|
101
|
+
const visitAbi = (abi, visitStruct) => {
|
|
102
|
+
for (const parameter of abi.parameters)
|
|
103
|
+
visitRepresentation(parameter.value, visitStruct);
|
|
104
|
+
visitRepresentation(abi.result, visitStruct);
|
|
105
|
+
if (abi.receiver !== null)
|
|
106
|
+
visitRepresentation(abi.receiver, visitStruct);
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* Walks one representation for every nested `record`/`record-with-index`/
|
|
110
|
+
* `native-record-ref`, reporting each to `visitStruct` with its layout when it
|
|
111
|
+
* carries one (a `record` or `record-with-index`) or `null` when it does not
|
|
112
|
+
* (a `native-record-ref`, which only ever names a shape -- see the model
|
|
113
|
+
* comment on that variant).
|
|
114
|
+
*
|
|
115
|
+
* Exhaustive over `Representation['kind']` with no `default`, matching
|
|
116
|
+
* `cppTypeOf` and `representationIsRenderable` (document.ts): a new carrier
|
|
117
|
+
* kind that can hold a record must be taught to this walk, not silently
|
|
118
|
+
* skipped by falling through a catch-all.
|
|
119
|
+
*/
|
|
120
|
+
const visitRepresentation = (representation, visitStruct) => {
|
|
121
|
+
switch (representation.kind) {
|
|
122
|
+
case 'unresolved':
|
|
123
|
+
case 'void':
|
|
124
|
+
case 'string':
|
|
125
|
+
case 'null':
|
|
126
|
+
case 'undefined':
|
|
127
|
+
case 'scalar':
|
|
128
|
+
case 'native-handle':
|
|
129
|
+
case 'typed-array':
|
|
130
|
+
// No struct to visit: an ArrayBuffer is a byte block the runtime owns
|
|
131
|
+
// whole, exactly as a typed array view is.
|
|
132
|
+
case 'array-buffer':
|
|
133
|
+
case 'shared-array-buffer':
|
|
134
|
+
case 'data-view':
|
|
135
|
+
case 'dynamic':
|
|
136
|
+
return;
|
|
137
|
+
case 'class-ref':
|
|
138
|
+
visitStruct(cppClassName(representation.declaration), representation.shapeId, null);
|
|
139
|
+
return;
|
|
140
|
+
case 'record':
|
|
141
|
+
visitStruct(cppRecordStructName(representation.shapeId), representation.shapeId, {
|
|
142
|
+
fields: representation.fields,
|
|
143
|
+
indexes: [],
|
|
144
|
+
accessors: representation.accessors
|
|
145
|
+
});
|
|
146
|
+
return;
|
|
147
|
+
case 'record-with-index':
|
|
148
|
+
visitStruct(cppRecordStructName(representation.shapeId), representation.shapeId, {
|
|
149
|
+
fields: representation.fields,
|
|
150
|
+
indexes: representation.indexes,
|
|
151
|
+
accessors: []
|
|
152
|
+
});
|
|
153
|
+
return;
|
|
154
|
+
case 'native-record-ref':
|
|
155
|
+
// A stated-native record is defined by the engine header, so this
|
|
156
|
+
// backend must not emit a struct of its own for it -- two definitions of
|
|
157
|
+
// one type is an ODR violation, and the compiler's would be the wrong
|
|
158
|
+
// one. Its members are still reached by name, which is what makes clang
|
|
159
|
+
// the check on any name the host type does not actually have.
|
|
160
|
+
if (representation.native)
|
|
161
|
+
return;
|
|
162
|
+
visitStruct(cppRecordStructName(representation.shapeId), representation.shapeId, null);
|
|
163
|
+
return;
|
|
164
|
+
case 'proxy-object':
|
|
165
|
+
visitRepresentation(representation.target, visitStruct);
|
|
166
|
+
visitRepresentation(representation.handler, visitStruct);
|
|
167
|
+
return;
|
|
168
|
+
case 'borrowed-ref':
|
|
169
|
+
visitRepresentation(representation.referent, visitStruct);
|
|
170
|
+
return;
|
|
171
|
+
case 'array-object':
|
|
172
|
+
case 'dense-buffer':
|
|
173
|
+
case 'native-sequence':
|
|
174
|
+
visitRepresentation(representation.element, visitStruct);
|
|
175
|
+
return;
|
|
176
|
+
case 'iterator':
|
|
177
|
+
visitRepresentation(representation.element, visitStruct);
|
|
178
|
+
visitRepresentation(representation.resume, visitStruct);
|
|
179
|
+
visitRepresentation(representation.completion, visitStruct);
|
|
180
|
+
return;
|
|
181
|
+
case 'promise':
|
|
182
|
+
visitRepresentation(representation.value, visitStruct);
|
|
183
|
+
return;
|
|
184
|
+
case 'keyed-collection':
|
|
185
|
+
// Both positions, and the value position especially: a `Map<string,
|
|
186
|
+
// Entry>` whose value is a `record` needs that struct DEFINED, and a
|
|
187
|
+
// walk that stopped at the collection would leave the emitted
|
|
188
|
+
// `gea::Map<std::string, gea_record_type_7>` naming a struct no
|
|
189
|
+
// translation unit declares.
|
|
190
|
+
visitRepresentation(representation.key, visitStruct);
|
|
191
|
+
if (representation.value)
|
|
192
|
+
visitRepresentation(representation.value, visitStruct);
|
|
193
|
+
return;
|
|
194
|
+
case 'dictionary':
|
|
195
|
+
visitRepresentation(representation.value, visitStruct);
|
|
196
|
+
return;
|
|
197
|
+
case 'function-and-constructor':
|
|
198
|
+
visitAbi(representation.call, visitStruct);
|
|
199
|
+
visitAbi(representation.construct, visitStruct);
|
|
200
|
+
return;
|
|
201
|
+
case 'function':
|
|
202
|
+
case 'function-family':
|
|
203
|
+
case 'constructor-family':
|
|
204
|
+
case 'constructor-value-dispatch':
|
|
205
|
+
case 'function-value-family':
|
|
206
|
+
case 'function-value-dispatch':
|
|
207
|
+
visitAbi(representation.abi, visitStruct);
|
|
208
|
+
return;
|
|
209
|
+
case 'generic-function-set':
|
|
210
|
+
return;
|
|
211
|
+
case 'optional':
|
|
212
|
+
visitRepresentation(representation.payload, visitStruct);
|
|
213
|
+
return;
|
|
214
|
+
case 'tagged-union':
|
|
215
|
+
for (const arm of representation.arms)
|
|
216
|
+
visitRepresentation(arm.value, visitStruct);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
/**
|
|
221
|
+
* Whether a representation names any struct this compilation mints --
|
|
222
|
+
* `gea_record_*` or `gea_class_*`, from `cppRecordStructName`/`cppClassName`
|
|
223
|
+
* above -- anywhere in its type, including nested inside a callable ABI, a
|
|
224
|
+
* collection element, or an optional/union payload.
|
|
225
|
+
*
|
|
226
|
+
* `translation-unit.ts` asks this of a cell's `extern`/absent declaration to
|
|
227
|
+
* decide whether the declaration can be hoisted ahead of the unnamed
|
|
228
|
+
* namespace an isolated unit wraps its definitions in: a struct minted by
|
|
229
|
+
* this compilation is defined INSIDE that namespace, so a declaration naming
|
|
230
|
+
* one cannot be placed ahead of it. The question is answered by walking the
|
|
231
|
+
* representation `visitRepresentation` already walks for struct dependency
|
|
232
|
+
* ordering -- the same one, not a second one -- rather than by searching the
|
|
233
|
+
* declaration's own rendered C++ text for the name prefixes, which is a
|
|
234
|
+
* source-shaped approximation of a fact this compilation already knows from
|
|
235
|
+
* the representation it rendered that text from. A `native-record-ref` with a
|
|
236
|
+
* stated `.native` name is excluded by `visitRepresentation` itself (it never
|
|
237
|
+
* calls back for one), which is correct here too: that struct is the host's,
|
|
238
|
+
* declared in a header already included, not one this compilation defines.
|
|
239
|
+
*/
|
|
240
|
+
export const representationNamesMintedStruct = (representation) => {
|
|
241
|
+
let found = false;
|
|
242
|
+
visitRepresentation(representation, () => {
|
|
243
|
+
found = true;
|
|
244
|
+
});
|
|
245
|
+
return found;
|
|
246
|
+
};
|
|
247
|
+
/**
|
|
248
|
+
* Every distinct struct name the plan's carriers require, and the layout to
|
|
249
|
+
* render for it where a `record`/`record-with-index` supplied one.
|
|
250
|
+
*
|
|
251
|
+
* `fieldsByStruct` is also this walk's cycle guard: a struct is only expanded
|
|
252
|
+
* -- its fields recursed into -- the first time a layout for it is seen. A
|
|
253
|
+
* layout is set into the map *before* that recursion runs, so a
|
|
254
|
+
* self-referential shape that reaches its own struct name again (directly, or
|
|
255
|
+
* through a field that is itself a `record`/`record-with-index` rather than
|
|
256
|
+
* the usual `native-record-ref`) finds the entry already present and stops
|
|
257
|
+
* instead of looping. A recursive type is expected to break the cycle with
|
|
258
|
+
* `native-record-ref` (model.ts), which carries no fields to recurse into at
|
|
259
|
+
* all, but the guard does not depend on that discipline being honoured.
|
|
260
|
+
*/
|
|
261
|
+
const collectRequiredStructs = (representations, deriver, bases, unlinkable, classShapes, classFields) => {
|
|
262
|
+
const requiredStructs = new Set();
|
|
263
|
+
const fieldsByStruct = new Map();
|
|
264
|
+
// Why a struct has no layout, kept per struct name so the refusal can name
|
|
265
|
+
// the gap instead of only the symptom.
|
|
266
|
+
const unlayoutable = new Map(unlinkable);
|
|
267
|
+
const visitStruct = (structName, requestedShapeId, layout) => {
|
|
268
|
+
// See `classInstanceShapes`: a class has one struct, so it has one layout,
|
|
269
|
+
// and which one is the projection's answer rather than the walk order's.
|
|
270
|
+
const shapeId = classShapes.get(structName) ?? requestedShapeId;
|
|
271
|
+
requiredStructs.add(structName);
|
|
272
|
+
if (fieldsByStruct.has(structName))
|
|
273
|
+
return;
|
|
274
|
+
if (unlayoutable.has(structName))
|
|
275
|
+
return;
|
|
276
|
+
// A nominal reference carries no layout of its own: a declared type is
|
|
277
|
+
// carried by name precisely so `interface Node { next: Node }` has a finite
|
|
278
|
+
// carrier. The layout is not missing, it is in the sealed structural table,
|
|
279
|
+
// and deriving the shape the reference names is how it is read back. Nothing
|
|
280
|
+
// in `plan.selected` can substitute: no producer ever publishes a result
|
|
281
|
+
// whose carrier is a declared type's own body, so waiting for one to appear
|
|
282
|
+
// means never emitting a struct for an ordinary named interface.
|
|
283
|
+
const raw = layout ?? recordLayoutOfShape(deriver, shapeId);
|
|
284
|
+
const nativeFields = classFields.get(structName);
|
|
285
|
+
const resolved = typeof raw === 'string' || nativeFields === undefined ? raw : { ...raw, fields: nativeFields };
|
|
286
|
+
if (typeof resolved === 'string') {
|
|
287
|
+
unlayoutable.set(structName, `${shapeId} carries ${resolved}`);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
// A field or sidecar derived here is invisible to every guard that ran
|
|
291
|
+
// before this point. `verify.ts` walks `plan.selected`, and no producer
|
|
292
|
+
// ever publishes a result whose carrier is a declared type's own body --
|
|
293
|
+
// so a nominal reference can be perfectly resolved while the layout
|
|
294
|
+
// behind its name is not. Letting that through renders
|
|
295
|
+
// `cppTypeOf(unresolved)`, which is a throw from the type layer with no
|
|
296
|
+
// program context attached; naming the field here is the same refusal
|
|
297
|
+
// one stage earlier, where the shape and the field are both still in
|
|
298
|
+
// hand.
|
|
299
|
+
const bottom = resolved.fields.find((field) => field.value.kind === 'unresolved');
|
|
300
|
+
if (bottom && bottom.value.kind === 'unresolved') {
|
|
301
|
+
unlayoutable.set(structName, `${shapeId} field "${bottom.key}" carries unresolved(${bottom.value.reason})`);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
const unresolvedIndex = resolved.indexes.find((index) => index.value.kind === 'unresolved');
|
|
305
|
+
if (unresolvedIndex?.value.kind === 'unresolved') {
|
|
306
|
+
unlayoutable.set(structName, `${shapeId} ${unresolvedIndex.key} index sidecar carries unresolved(${unresolvedIndex.value.reason})`);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
// A derived struct declares only what its base does not already have. The
|
|
310
|
+
// shape resolved above is the checker's flattened view -- it lists every
|
|
311
|
+
// inherited member -- so declaring it verbatim under a `: Base` clause
|
|
312
|
+
// would give the object two `name` members, and a base method writing
|
|
313
|
+
// through its own would be invisible to a derived reader. Which fields the
|
|
314
|
+
// base has is the base *shape's* answer, taken from the deriver exactly as
|
|
315
|
+
// this struct's own was, so the two are never two opinions.
|
|
316
|
+
const base = bases.get(structName);
|
|
317
|
+
const own = base ? ownFieldsOf(deriver, resolved, base, classFields.get(base.structName)) : resolved;
|
|
318
|
+
if (typeof own === 'string') {
|
|
319
|
+
unlayoutable.set(structName, own);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
fieldsByStruct.set(structName, own);
|
|
323
|
+
// Every field the shape has, inherited ones included: the carriers they name
|
|
324
|
+
// still have to be required here, and the base struct's own expansion is not
|
|
325
|
+
// guaranteed to reach a nested shape this one holds by a different route.
|
|
326
|
+
for (const field of resolved.fields)
|
|
327
|
+
visitRepresentation(field.value, visitStruct);
|
|
328
|
+
for (const index of resolved.indexes)
|
|
329
|
+
visitRepresentation(index.value, visitStruct);
|
|
330
|
+
if (base && !base.native)
|
|
331
|
+
visitStruct(base.structName, base.shapeId, null);
|
|
332
|
+
};
|
|
333
|
+
for (const representation of representations)
|
|
334
|
+
visitRepresentation(representation, visitStruct);
|
|
335
|
+
return { requiredStructs, fieldsByStruct, unlayoutable };
|
|
336
|
+
};
|
|
337
|
+
/**
|
|
338
|
+
* The half of a derived class's layout that it declares itself, or the reason
|
|
339
|
+
* the subtraction could not be made.
|
|
340
|
+
*
|
|
341
|
+
* A key the base already declares is dropped, and dropped whatever carrier the
|
|
342
|
+
* derived's own shape gave it. An object has ONE property per key (ECMA-262
|
|
343
|
+
* 10.1: an ordinary object's [[OwnPropertyKeys]] is a set, and a derived
|
|
344
|
+
* constructor writes into the very same slot the base's does), so a key both
|
|
345
|
+
* shapes carry is one storage member and the base is where it lives -- a
|
|
346
|
+
* derived member of the same name would be a second slot C++ would happily let
|
|
347
|
+
* shadow the first, leaving a base body writing one copy while a derived reader
|
|
348
|
+
* saw the other.
|
|
349
|
+
*
|
|
350
|
+
* This used to refuse when the two carriers DIFFERED, on the theory that a
|
|
351
|
+
* difference meant a real override. It does not. The common cause is one
|
|
352
|
+
* INHERITED member interned twice: `class Hono<E, S, P> extends HonoBase<E, S,
|
|
353
|
+
* P>` gives the two classes distinct type-parameter symbols, so `HonoBase`'s
|
|
354
|
+
* own `readonly getPath: GetPath<E>` derives one record for `E`-as-declared-in-
|
|
355
|
+
* `HonoBase` and another for `E`-as-declared-in-`Hono`, and the structural view
|
|
356
|
+
* of the derived class lists the inherited member under the second. Neither
|
|
357
|
+
* class declared anything twice -- `Hono` declares no fields at all -- so
|
|
358
|
+
* refusing here reported a conflict that does not exist in the program.
|
|
359
|
+
*
|
|
360
|
+
* A genuine TypeScript override that narrows a field's declared type is not a
|
|
361
|
+
* second slot either: it is a type-level claim about the one slot, and a
|
|
362
|
+
* derived read that wants the narrower carrier narrows at the read, which
|
|
363
|
+
* `narrowedFieldReadText` (emit-properties.ts) already does and still refuses
|
|
364
|
+
* by name when it cannot.
|
|
365
|
+
*/
|
|
366
|
+
const ownFieldsOf = (deriver, derived, base, nativeFields) => {
|
|
367
|
+
const baseLayout = recordLayoutOfShape(deriver, base.shapeId);
|
|
368
|
+
if (typeof baseLayout === 'string')
|
|
369
|
+
return `its base struct ${base.structName} (${base.shapeId}) carries ${baseLayout}`;
|
|
370
|
+
const inherited = new Map((nativeFields ?? baseLayout.fields).map((field) => [field.key, field]));
|
|
371
|
+
const inheritedAccessors = new Set(baseLayout.accessors.map((accessor) => accessor.key));
|
|
372
|
+
return {
|
|
373
|
+
fields: derived.fields.filter((field) => !inherited.has(field.key)),
|
|
374
|
+
indexes: derived.indexes,
|
|
375
|
+
accessors: derived.accessors.filter((accessor) => !inheritedAccessors.has(accessor.key))
|
|
376
|
+
};
|
|
377
|
+
};
|
|
378
|
+
/**
|
|
379
|
+
* The struct name a carrier IS, when holding one by value embeds it.
|
|
380
|
+
*
|
|
381
|
+
* `native-record-ref` with a stated `native` name is the host's own struct and
|
|
382
|
+
* this module emits no definition for it, so it can never be a name this order
|
|
383
|
+
* has to place.
|
|
384
|
+
*/
|
|
385
|
+
export const cppStructNameOf = (representation) => {
|
|
386
|
+
if (representation.kind === 'record' || representation.kind === 'record-with-index')
|
|
387
|
+
return cppRecordStructName(representation.shapeId);
|
|
388
|
+
if (representation.kind === 'native-record-ref')
|
|
389
|
+
return representation.native === null ? cppRecordStructName(representation.shapeId) : null;
|
|
390
|
+
if (representation.kind === 'class-ref')
|
|
391
|
+
return cppClassName(representation.declaration);
|
|
392
|
+
return null;
|
|
393
|
+
};
|
|
394
|
+
const isRepresentationNode = (value) => typeof value === 'object' && value !== null && typeof value.kind === 'string';
|
|
395
|
+
/**
|
|
396
|
+
* The struct names a carrier requires the COMPLETE definition of.
|
|
397
|
+
*
|
|
398
|
+
* Two stopping rules, and they are the whole correctness argument. A carrier
|
|
399
|
+
* whose `ownership` is not `owned` is a pointer or a reference
|
|
400
|
+
* (`cppOwnershipWrap`, types.ts), which a forward declaration satisfies -- so
|
|
401
|
+
* it is not a dependency, and descending past it would manufacture a cycle out
|
|
402
|
+
* of two structs that legitimately point at each other. A callable -- spelled
|
|
403
|
+
* by carrying an `abi` -- never requires its parameter or result types to be
|
|
404
|
+
* complete either, for the same reason and with the same cycle risk.
|
|
405
|
+
*
|
|
406
|
+
* Everything else is walked STRUCTURALLY rather than by enumerating the thirty
|
|
407
|
+
* representation kinds: a kind added to `model.ts` tomorrow is then ordered by
|
|
408
|
+
* this walk automatically instead of silently falling out of it, which is the
|
|
409
|
+
* failure mode a `switch` over kinds would have. The walk stops at each struct
|
|
410
|
+
* it names -- that struct's own dependencies are placed when IT is ordered.
|
|
411
|
+
*/
|
|
412
|
+
const valueHeldStructNames = (representation, into, seen) => {
|
|
413
|
+
if (seen.has(representation))
|
|
414
|
+
return;
|
|
415
|
+
seen.add(representation);
|
|
416
|
+
const node = representation;
|
|
417
|
+
const ownership = node['ownership'];
|
|
418
|
+
if (typeof ownership === 'string' && ownership !== 'owned')
|
|
419
|
+
return;
|
|
420
|
+
if (node['abi'] !== undefined) {
|
|
421
|
+
// A bare record in a function-pointer signature may remain incomplete,
|
|
422
|
+
// but a by-value carrier BUILT AROUND that record may not. In
|
|
423
|
+
// `CallableObject<TaggedUnion<Record, Ref<T>>()>`, naming the callable
|
|
424
|
+
// instantiates `TaggedUnion`, whose inline storage immediately asks for
|
|
425
|
+
// `sizeof(Record)`. Walk those composite parameter/result/receiver
|
|
426
|
+
// carriers while continuing to skip a record named directly by the ABI.
|
|
427
|
+
const abi = node['abi'];
|
|
428
|
+
const nested = [...abi.parameters.map((parameter) => parameter.value), abi.result, ...(abi.receiver ? [abi.receiver] : [])];
|
|
429
|
+
for (const value of nested) {
|
|
430
|
+
if (cppStructNameOf(value) === null)
|
|
431
|
+
valueHeldStructNames(value, into, seen);
|
|
432
|
+
}
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
const named = cppStructNameOf(representation);
|
|
436
|
+
if (named !== null) {
|
|
437
|
+
into.add(named);
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
const descend = (value) => {
|
|
441
|
+
if (isRepresentationNode(value)) {
|
|
442
|
+
valueHeldStructNames(value, into, seen);
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
if (Array.isArray(value)) {
|
|
446
|
+
for (const entry of value)
|
|
447
|
+
descend(entry);
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
// A plain carrier-holding record -- `RecordField`, a tagged-union arm --
|
|
451
|
+
// is not itself a representation, so reaching the carriers inside it is
|
|
452
|
+
// what keeps a struct nested one level down from being missed.
|
|
453
|
+
if (typeof value === 'object' && value !== null && !seen.has(value)) {
|
|
454
|
+
seen.add(value);
|
|
455
|
+
for (const nested of Object.values(value))
|
|
456
|
+
descend(nested);
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
for (const value of Object.values(node))
|
|
460
|
+
descend(value);
|
|
461
|
+
};
|
|
462
|
+
/** Every struct one struct's own storage embeds by value. */
|
|
463
|
+
const structValueDependencies = (layout) => {
|
|
464
|
+
const into = new Set();
|
|
465
|
+
const seen = new Set();
|
|
466
|
+
for (const field of layout.fields)
|
|
467
|
+
valueHeldStructNames(field.value, into, seen);
|
|
468
|
+
for (const index of layout.indexes)
|
|
469
|
+
valueHeldStructNames(index.value, into, seen);
|
|
470
|
+
return into;
|
|
471
|
+
};
|
|
472
|
+
/**
|
|
473
|
+
* Struct names ordered so every struct a definition needs COMPLETE is defined
|
|
474
|
+
* before it.
|
|
475
|
+
*
|
|
476
|
+
* Two dependencies need completeness and a forward declaration cannot supply
|
|
477
|
+
* either. A base: `struct D : B` names `B` in a position that has to be a
|
|
478
|
+
* complete type. And a field held by value: `struct A { gea::Optional<B> b; }`
|
|
479
|
+
* embeds a `B`, so `B`'s definition -- not its declaration -- has to precede
|
|
480
|
+
* `A`'s. The second one used to be left to a sort by NAME, which is not an
|
|
481
|
+
* order at all: `gea_record_type_1173` sorts before `gea_record_type_695`
|
|
482
|
+
* because `'1' < '6'`, so an app whose struct numbering fell that way emitted a
|
|
483
|
+
* struct with an incomplete member and failed to compile (`sky-hop`,
|
|
484
|
+
* `sky-hop-jsx`) while every other app happened to be numbered the lucky way.
|
|
485
|
+
*
|
|
486
|
+
* The walk is depth-first and emits each name once. A name reached twice within
|
|
487
|
+
* one chain is a cycle -- a class extending itself, or two structs embedding
|
|
488
|
+
* each other by value, neither of which any well-formed C++ program has -- and
|
|
489
|
+
* stopping there emits the enclosing struct first, leaving the C++ compiler to
|
|
490
|
+
* name the incomplete type rather than this walk running forever. That is
|
|
491
|
+
* exactly what the previous name sort did for every struct, so a cycle is no
|
|
492
|
+
* worse than before and everything else is now right.
|
|
493
|
+
*/
|
|
494
|
+
const definitionOrder = (structNames, bases, valueDependencies) => {
|
|
495
|
+
const ordered = [];
|
|
496
|
+
const emitted = new Set();
|
|
497
|
+
const placeable = new Set(structNames);
|
|
498
|
+
const visit = (structName, chain) => {
|
|
499
|
+
if (emitted.has(structName) || chain.has(structName))
|
|
500
|
+
return;
|
|
501
|
+
const deeper = new Set([...chain, structName]);
|
|
502
|
+
const base = bases.get(structName);
|
|
503
|
+
if (base && placeable.has(base.structName))
|
|
504
|
+
visit(base.structName, deeper);
|
|
505
|
+
for (const dependency of valueDependencies.get(structName) ?? []) {
|
|
506
|
+
// A name this run emits no definition for -- a host struct, or a carrier
|
|
507
|
+
// naming a shape outside `requiredStructs` -- is not this order's to place.
|
|
508
|
+
if (placeable.has(dependency))
|
|
509
|
+
visit(dependency, deeper);
|
|
510
|
+
}
|
|
511
|
+
if (emitted.has(structName))
|
|
512
|
+
return;
|
|
513
|
+
emitted.add(structName);
|
|
514
|
+
ordered.push(structName);
|
|
515
|
+
};
|
|
516
|
+
for (const structName of structNames)
|
|
517
|
+
visit(structName, new Set());
|
|
518
|
+
return ordered;
|
|
519
|
+
};
|
|
520
|
+
/**
|
|
521
|
+
* The layout of the shape a nominal record reference names.
|
|
522
|
+
*
|
|
523
|
+
* The deriver is the one authority on what a structural type physically is, so
|
|
524
|
+
* this asks it rather than reading the table's shapes directly -- a second
|
|
525
|
+
* reading would be a second opinion about layout, and the two would eventually
|
|
526
|
+
* disagree about exactly the types that are hardest to check by eye.
|
|
527
|
+
*
|
|
528
|
+
* When the shape has no record layout the *carrier it does have* is what says
|
|
529
|
+
* why, so it is returned rather than discarded: "the shape derives
|
|
530
|
+
* `unresolved(no primitive for a record carrier with a symbol-keyed index
|
|
531
|
+
* signature)`" names a gap someone can close, where "no reachable record
|
|
532
|
+
* carrier defines their fields" names a search that failed and points at the
|
|
533
|
+
* wrong stage entirely.
|
|
534
|
+
*/
|
|
535
|
+
const recordLayoutOfShape = (deriver, shapeId) => {
|
|
536
|
+
const carrier = deriver.layoutOf(shapeId);
|
|
537
|
+
if (carrier.kind === 'record')
|
|
538
|
+
return { fields: carrier.fields, indexes: [], accessors: carrier.accessors };
|
|
539
|
+
if (carrier.kind === 'record-with-index')
|
|
540
|
+
return { fields: carrier.fields, indexes: carrier.indexes, accessors: [] };
|
|
541
|
+
return representationKey(carrier);
|
|
542
|
+
};
|
|
543
|
+
/**
|
|
544
|
+
* A shape's own dynamic-property sidecars, or an empty list when its layout
|
|
545
|
+
* has none.
|
|
546
|
+
*
|
|
547
|
+
* `native-record-ref` (model.ts) names a shape rather than carrying its own
|
|
548
|
+
* `indexes` inline the way `record-with-index` does -- it is a
|
|
549
|
+
* reference, resolved by `recordLayoutOfShape` exactly as
|
|
550
|
+
* `recordFieldsOfShape` already resolves the named half. A computed-key
|
|
551
|
+
* `[[Get]]`/`[[Set]]` on a `native-record-ref` receiver (`emit-properties.ts`)
|
|
552
|
+
* needs this to find the identical sidecar members `renderStructDefinition`
|
|
553
|
+
* gives the struct when its shape has index
|
|
554
|
+
* signatures -- an `ArrayLike<number>`-shaped interface (`{ length: number;
|
|
555
|
+
* [index: number]: number }`) derives `native-record-ref` here the same way
|
|
556
|
+
* any other named interface does, and its computed reads have nowhere else to
|
|
557
|
+
* go. An empty result is not a gap to fill: there is no sidecar member to route
|
|
558
|
+
* to, and `preflight/run.ts` checks this same
|
|
559
|
+
* question per site before certifying one (see its own comment for why an
|
|
560
|
+
* unconditional claim would over-claim).
|
|
561
|
+
*/
|
|
562
|
+
export const recordIndexesOfShape = (deriver, shapeId) => {
|
|
563
|
+
const layout = recordLayoutOfShape(deriver, shapeId);
|
|
564
|
+
return typeof layout === 'string' ? [] : layout.indexes;
|
|
565
|
+
};
|
|
566
|
+
/**
|
|
567
|
+
* The reserved member name for a record's dynamic-property sidecar.
|
|
568
|
+
*
|
|
569
|
+
* `cppRecordFieldName` (types.ts) throws for any source key inside the
|
|
570
|
+
* `gea_` prefix, so no field a program actually declares can collide with
|
|
571
|
+
* this name -- the sidecar and an ordinary field are drawn from disjoint
|
|
572
|
+
* namespaces by construction, not by hoping no one names a property this.
|
|
573
|
+
*/
|
|
574
|
+
export const cppRecordIndexSidecarName = 'gea_dynamic';
|
|
575
|
+
/** Domain-specific name when a record carries both text and symbol tables. */
|
|
576
|
+
export const cppRecordIndexSidecarNameFor = (index, indexes) => indexes.length > 1 && index.key === 'symbol' ? 'gea_symbol_dynamic' : cppRecordIndexSidecarName;
|
|
577
|
+
/**
|
|
578
|
+
* Attribute state for entries in `gea_dynamic`.
|
|
579
|
+
*
|
|
580
|
+
* The index dictionary deliberately remains typed: this companion table holds
|
|
581
|
+
* only the three data-property bits that an ordinary assignment cannot carry.
|
|
582
|
+
* It is therefore not a second value store and cannot turn an indexed record
|
|
583
|
+
* into a boxed object merely because one entry is non-enumerable or sealed.
|
|
584
|
+
*/
|
|
585
|
+
export const cppRecordIndexAttributesName = 'gea_dynamic_attributes';
|
|
586
|
+
export const cppRecordIndexAttributesNameFor = (index, indexes) => indexes.length > 1 && index.key === 'symbol' ? 'gea_symbol_dynamic_attributes' : cppRecordIndexAttributesName;
|
|
587
|
+
/**
|
|
588
|
+
* The C++ type each `gea::Value::Tag` pins down exactly.
|
|
589
|
+
*
|
|
590
|
+
* `Value::as<T>()` is an unchecked `static_pointer_cast`: the tag says which
|
|
591
|
+
* alternative was written, and the reader has to name the same C++ type the
|
|
592
|
+
* writer boxed. For an object or a function the payload type travels with the
|
|
593
|
+
* box (`nativeFieldOpsFor` is instantiated from the payload's static type at
|
|
594
|
+
* the `box` call site), so a read is always type-correct. For a PRIMITIVE it
|
|
595
|
+
* does not: `Tag::Number` is boxed from whatever numeric C++ type the source
|
|
596
|
+
* carrier had, and this header's own `toBoolean` already reads every such box
|
|
597
|
+
* back as `double`. So the three primitive tags are addressable only when the
|
|
598
|
+
* field's own C++ type is the canonical one -- an `int32_t` field would box a
|
|
599
|
+
* payload no reader could name, and that field refuses instead.
|
|
600
|
+
*/
|
|
601
|
+
const canonicalDynamicPayloads = new Map([
|
|
602
|
+
['Boolean', 'bool'],
|
|
603
|
+
['Number', 'double'],
|
|
604
|
+
['String', 'std::string']
|
|
605
|
+
]);
|
|
606
|
+
/**
|
|
607
|
+
* The accessors this renderer answers for.
|
|
608
|
+
*
|
|
609
|
+
* Symbol-keyed accessors are excluded, not forgotten: a unique symbol's id
|
|
610
|
+
* only exists once the program runs, so the string-keyed dispatcher below has
|
|
611
|
+
* nothing to compare against, and the well-known-symbol path
|
|
612
|
+
* (`symbolBranchFor`) reads its bodies out of the per-key maps a FIELD fills.
|
|
613
|
+
* Wiring accessors into that is a separate step; excluding them here keeps the
|
|
614
|
+
* bits and the branches agreeing about which keys exist, which is the property
|
|
615
|
+
* everything else in this file depends on.
|
|
616
|
+
*/
|
|
617
|
+
const renderableAccessors = (layout) => layout.accessors.filter((accessor) => !cppRecordFieldKeyIsSymbol(accessor.key));
|
|
618
|
+
/**
|
|
619
|
+
* The boxed spelling of one accessor's value, or `null` when no static tag
|
|
620
|
+
* names its carrier.
|
|
621
|
+
*
|
|
622
|
+
* The same two questions `dynamicFieldAccessOf` asks of a field's carrier,
|
|
623
|
+
* asked of the accessor's -- `RecordAccessor.value`, derived by the identical
|
|
624
|
+
* `deriveStored` call. `null` here means the dispatcher emits no read arm, so
|
|
625
|
+
* a boxed read answers `undefined`: exactly what an unaddressable FIELD
|
|
626
|
+
* already does, rather than a new refusal for a shape that compiles today.
|
|
627
|
+
*/
|
|
628
|
+
const accessorBoxedText = (accessor, call) => {
|
|
629
|
+
if (accessor.value.kind === 'dynamic')
|
|
630
|
+
return call;
|
|
631
|
+
// A member the checker types `undefined` (a getter that only ever throws
|
|
632
|
+
// reaches here as one) is compiled with a `void` ABI, so there is no value to
|
|
633
|
+
// cast -- but the CALL still has to happen, because running the body is the
|
|
634
|
+
// whole observable effect of reading the member.
|
|
635
|
+
if (accessor.value.kind === 'undefined')
|
|
636
|
+
return `((void)(${call}), gea::Value())`;
|
|
637
|
+
// The three carriers a FIELD reaches through the runtime's typed adapter
|
|
638
|
+
// rather than through a box (`dynamicFieldAccessOf`'s `dynamicCarrier` arm).
|
|
639
|
+
// `boxedText` would `static_cast` them into a payload no reader can name, so
|
|
640
|
+
// this refuses instead and the dispatcher emits a named runtime refusal.
|
|
641
|
+
if (accessor.value.kind === 'optional' || accessor.value.kind === 'tagged-union' || accessor.value.kind === 'dictionary')
|
|
642
|
+
return null;
|
|
643
|
+
const tag = dynamicTagFor(accessor.value);
|
|
644
|
+
if (tag === null)
|
|
645
|
+
return null;
|
|
646
|
+
const canonical = canonicalDynamicPayloads.get(tag);
|
|
647
|
+
if (canonical !== undefined && canonical !== cppTypeOf(accessor.value))
|
|
648
|
+
return null;
|
|
649
|
+
return boxedText(accessor.value, tag, call);
|
|
650
|
+
};
|
|
651
|
+
/** The other direction: what a boxed write hands the setter body, or `null` when no static tag names the carrier. */
|
|
652
|
+
const accessorUnboxText = (accessor, structName, text) => {
|
|
653
|
+
if (accessor.value.kind === 'dynamic')
|
|
654
|
+
return text;
|
|
655
|
+
if (accessor.value.kind === 'optional' || accessor.value.kind === 'tagged-union' || accessor.value.kind === 'dictionary')
|
|
656
|
+
return null;
|
|
657
|
+
const tag = dynamicTagFor(accessor.value);
|
|
658
|
+
if (tag === null)
|
|
659
|
+
return null;
|
|
660
|
+
const type = cppTypeOf(accessor.value);
|
|
661
|
+
const canonical = canonicalDynamicPayloads.get(tag);
|
|
662
|
+
if (canonical !== undefined && canonical !== type)
|
|
663
|
+
return null;
|
|
664
|
+
return `gea::detail::unboxField<${type}>(${text}, gea::Value::Tag::${tag}, ${cppStringLiteral(structName)}, ${cppStringLiteral(accessor.key)})`;
|
|
665
|
+
};
|
|
666
|
+
const dynamicFieldAccessOf = (field, storageType) => {
|
|
667
|
+
// Whether this field can be reached dynamically is a question about its
|
|
668
|
+
// VALUE's carrier, not about whether the key that names it is a string or a
|
|
669
|
+
// symbol -- a `[Symbol.iterator]() {}` method boxes exactly the way a
|
|
670
|
+
// same-shaped `next() {}` method does. This function used to refuse every
|
|
671
|
+
// symbol-keyed field outright, which is what `renderFieldDispatcher` relied
|
|
672
|
+
// on to justify treating EVERY symbol key as unaddressable; that conflated
|
|
673
|
+
// "the key is a symbol" with "the id is unknowable", which is false for the
|
|
674
|
+
// fifteen well-known symbols (`Symbol.iterator` and its siblings are minted
|
|
675
|
+
// once, before any user `Symbol()` call, at a fixed low id --
|
|
676
|
+
// `gea::wellKnownSymbol`). The dispatcher now decides addressability by
|
|
677
|
+
// symbol identity itself, so this function computes the same access facts
|
|
678
|
+
// for every field regardless of its key's shape.
|
|
679
|
+
// A field whose own carrier is the box is the one case that needs no tag at
|
|
680
|
+
// all: `dynamicTagFor` answers `null` for `dynamic` because no STATIC tag
|
|
681
|
+
// describes it, and reading that as "unaddressable" refused a member the
|
|
682
|
+
// dynamic path can reach more directly than any other -- by copying the
|
|
683
|
+
// `gea::Value` it already is. hono's `HonoBase` is the case: `this[method] =
|
|
684
|
+
// handler` over `allMethods` writes nine such fields, and every one of them
|
|
685
|
+
// aborted the program on the first route registration.
|
|
686
|
+
if (field.value.kind === 'dynamic' && storageType === 'gea::Value') {
|
|
687
|
+
return { key: field.key, member: cppRecordFieldName(field.key), tag: '', type: '', readable: true, writable: true, boxed: true };
|
|
688
|
+
}
|
|
689
|
+
// Optional callable traps and union fields carry their own live tag. An open
|
|
690
|
+
// dictionary likewise keeps one exact native reference type on both sides of
|
|
691
|
+
// a dynamic property operation: its values are already the declared dynamic
|
|
692
|
+
// boundary, while the dictionary object itself retains identity. Let the
|
|
693
|
+
// runtime's typed adapter move these carriers across the transient Value
|
|
694
|
+
// without replacing the field's native storage with a box.
|
|
695
|
+
if (field.value.kind === 'optional' || field.value.kind === 'tagged-union' || field.value.kind === 'dictionary') {
|
|
696
|
+
return {
|
|
697
|
+
key: field.key,
|
|
698
|
+
member: cppRecordFieldName(field.key),
|
|
699
|
+
tag: '',
|
|
700
|
+
type: storageType,
|
|
701
|
+
readable: true,
|
|
702
|
+
writable: true,
|
|
703
|
+
boxed: false,
|
|
704
|
+
dynamicCarrier: true
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
const tag = dynamicTagFor(field.value);
|
|
708
|
+
if (tag === null) {
|
|
709
|
+
return { key: field.key, member: cppRecordFieldName(field.key), tag: '', type: '', readable: false, writable: false, boxed: false };
|
|
710
|
+
}
|
|
711
|
+
// The type the MEMBER is declared with, not the one its representation would
|
|
712
|
+
// spell: a narrowed `number` member is a `long long`, and boxing one under
|
|
713
|
+
// `Tag::Number` would hand every reader a payload it names `double`. That is
|
|
714
|
+
// precisely the case the canonical table below exists to refuse, so it is
|
|
715
|
+
// shown the storage and refuses -- loudly -- rather than punning.
|
|
716
|
+
const type = storageType;
|
|
717
|
+
const canonical = canonicalDynamicPayloads.get(tag);
|
|
718
|
+
const readable = canonical === undefined || canonical === type;
|
|
719
|
+
return {
|
|
720
|
+
key: field.key,
|
|
721
|
+
member: cppRecordFieldName(field.key),
|
|
722
|
+
tag,
|
|
723
|
+
type,
|
|
724
|
+
readable,
|
|
725
|
+
writable: readable && canonical !== undefined,
|
|
726
|
+
boxed: false
|
|
727
|
+
};
|
|
728
|
+
};
|
|
729
|
+
const refuseFieldText = (structName, key) => `gea::detail::refuseUnaddressableField(${cppStringLiteral(structName)}, ${cppStringLiteral(key)});`;
|
|
730
|
+
/**
|
|
731
|
+
* The `gea::detail::WellKnownSymbol` enum member a symbol-keyed record
|
|
732
|
+
* field's declaration denotes, or `null` when the field's key names a
|
|
733
|
+
* genuinely unique symbol (`Symbol()`/`Symbol('x')`) whose id only exists
|
|
734
|
+
* once the program runs and can never be compared against here.
|
|
735
|
+
*
|
|
736
|
+
* `key` is the `sym(<declaration>)` marker `derive.ts`'s `recordFieldKeyOf`
|
|
737
|
+
* mints for the field; `wellKnownSymbols` is the frontend's own
|
|
738
|
+
* declaration-identity census of `SymbolConstructor`'s members
|
|
739
|
+
* (`host-protocols.ts`'s `wellKnownSymbolDeclarationsOf`) -- the SAME
|
|
740
|
+
* authority `emit-context.ts`'s `wellKnownSymbolMemberOf` asks for a static
|
|
741
|
+
* key read off an `IrOperand`, restated here because a record field's key
|
|
742
|
+
* arrives as plain text, never as an operand. The member name TypeScript
|
|
743
|
+
* reports (`iterator`, `asyncIterator`, ...) and the runtime's enum member
|
|
744
|
+
* spelling (`Iterator`, `AsyncIterator`, ...) differ only by their first
|
|
745
|
+
* letter's case -- `gea_runtime.h`'s `enum class WellKnownSymbol` is
|
|
746
|
+
* generated to match `SymbolConstructor`'s own member names one for one.
|
|
747
|
+
*/
|
|
748
|
+
const wellKnownSymbolEnumNameOf = (wellKnownSymbols, key) => {
|
|
749
|
+
for (const [declaration, member] of wellKnownSymbols) {
|
|
750
|
+
if (key === `sym(${declaration})`)
|
|
751
|
+
return member.charAt(0).toUpperCase() + member.slice(1);
|
|
752
|
+
}
|
|
753
|
+
return null;
|
|
754
|
+
};
|
|
755
|
+
/**
|
|
756
|
+
* The field dispatcher every emitted struct carries: how a `gea::Value` that
|
|
757
|
+
* boxes this struct reads, writes and enumerates the members C++ declares.
|
|
758
|
+
*
|
|
759
|
+
* This is the native answer to "a statically typed object is used
|
|
760
|
+
* dynamically". The forbidden shortcut is to box the receiver and let its
|
|
761
|
+
* declared fields disappear into an opaque payload; the answer here is that
|
|
762
|
+
* the struct keeps its native layout and *states* how to reach it, so
|
|
763
|
+
* `obj[key] = v` through an `any` writes the real member and every native read
|
|
764
|
+
* of that member sees it. `gea::Value::box` finds these three by concept
|
|
765
|
+
* (`detail::NativeFieldTable`) and stores a table of function pointers, so a
|
|
766
|
+
* struct that is never boxed pays nothing and none of this changes any
|
|
767
|
+
* struct's layout, size or triviality.
|
|
768
|
+
*
|
|
769
|
+
* A field the dispatcher cannot address REFUSES OUT LOUD rather than
|
|
770
|
+
* answering `false`. The distinction is load-bearing: `false` means "this
|
|
771
|
+
* struct does not declare that key", which is what routes a key to the box's
|
|
772
|
+
* expando table, and a declared field answering `false` would let the expando
|
|
773
|
+
* shadow it -- a write landing in a side table while every native read still
|
|
774
|
+
* saw the old value. That is the silently-wrong answer, so it is spelled as a
|
|
775
|
+
* loud one instead.
|
|
776
|
+
*/
|
|
777
|
+
/**
|
|
778
|
+
* ECMA-262 10.1.11.1 OrdinaryOwnPropertyKeys: array-index keys first in
|
|
779
|
+
* ascending numeric order, then the rest in creation order. Observable, not a
|
|
780
|
+
* layout preference -- `Object.keys`, `for...in`, `Object.entries` and
|
|
781
|
+
* `JSON.stringify` all read it. Source order answered `b,10,a,2` for
|
|
782
|
+
* `{ b: 1, 10: 2, a: 3, 2: 4 }` where the specification says `2,10,b,a`.
|
|
783
|
+
*
|
|
784
|
+
* A stable partition, not one sort: non-index keys keep creation order. A
|
|
785
|
+
* leading zero disqualifies a key (7.1.21 wants `ToString(ToUint32(P)) === P`).
|
|
786
|
+
* Scoped to one struct's OWN keys -- a derived struct delegates to its base
|
|
787
|
+
* first, so an array-index field on a DERIVED class does not hoist; not fixed.
|
|
788
|
+
*/
|
|
789
|
+
const isArrayIndexKey = (key) => {
|
|
790
|
+
if (key.length === 0 || key.length > 10)
|
|
791
|
+
return false;
|
|
792
|
+
if (key.length > 1 && key.charCodeAt(0) === 48)
|
|
793
|
+
return false;
|
|
794
|
+
for (let at = 0; at < key.length; at += 1) {
|
|
795
|
+
const code = key.charCodeAt(at);
|
|
796
|
+
if (code < 48 || code > 57)
|
|
797
|
+
return false;
|
|
798
|
+
}
|
|
799
|
+
return Number(key) < 4294967295;
|
|
800
|
+
};
|
|
801
|
+
export const enumerationOrdered = (entries, keyOf) => [
|
|
802
|
+
...entries.filter((entry) => isArrayIndexKey(keyOf(entry))).sort((left, right) => Number(keyOf(left)) - Number(keyOf(right))),
|
|
803
|
+
...entries.filter((entry) => !isArrayIndexKey(keyOf(entry)))
|
|
804
|
+
];
|
|
805
|
+
/** Whether `text` contains `name` as a whole identifier rather than inside a longer one. */
|
|
806
|
+
const mentionsIdentifier = (text, name) => {
|
|
807
|
+
const isIdentifierCharacter = (character) => (character >= 'a' && character <= 'z') ||
|
|
808
|
+
(character >= 'A' && character <= 'Z') ||
|
|
809
|
+
(character >= '0' && character <= '9') ||
|
|
810
|
+
character === '_';
|
|
811
|
+
for (let at = text.indexOf(name); at >= 0; at = text.indexOf(name, at + 1)) {
|
|
812
|
+
const before = at === 0 ? '' : (text[at - 1] ?? '');
|
|
813
|
+
const after = text[at + name.length] ?? '';
|
|
814
|
+
if (!isIdentifierCharacter(before) && !isIdentifierCharacter(after))
|
|
815
|
+
return true;
|
|
816
|
+
}
|
|
817
|
+
return false;
|
|
818
|
+
};
|
|
819
|
+
/** One parameter list split on the commas that separate parameters, never on one inside a template argument or a function type. */
|
|
820
|
+
const parameterListOf = (text) => {
|
|
821
|
+
const parameters = [];
|
|
822
|
+
let depth = 0;
|
|
823
|
+
let start = 0;
|
|
824
|
+
for (let at = 0; at < text.length; at += 1) {
|
|
825
|
+
const character = text[at];
|
|
826
|
+
if (character === '<' || character === '(')
|
|
827
|
+
depth += 1;
|
|
828
|
+
else if (character === '>' || character === ')')
|
|
829
|
+
depth -= 1;
|
|
830
|
+
else if (character === ',' && depth === 0) {
|
|
831
|
+
parameters.push(text.slice(start, at));
|
|
832
|
+
start = at + 1;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
if (text.trim().length > 0)
|
|
836
|
+
parameters.push(text.slice(start));
|
|
837
|
+
return parameters;
|
|
838
|
+
};
|
|
839
|
+
/**
|
|
840
|
+
* The same hook declaration with every parameter its body never reads left
|
|
841
|
+
* UNNAMED.
|
|
842
|
+
*
|
|
843
|
+
* A protocol hook ignores its parameters entirely or reads them all, according
|
|
844
|
+
* to what the record declares -- a struct with no index properties reads none
|
|
845
|
+
* of `gea_ownIndexPresent`'s. Both the declaration and the body are generated
|
|
846
|
+
* side by side here, so the body's own text is the only authority on which
|
|
847
|
+
* names it reads: there is no earlier fact to consult, because the body is
|
|
848
|
+
* already rendered C++ by the time the two meet. C++ spells "deliberately
|
|
849
|
+
* unused" by omitting the name, which is the same reason `nameOf` above
|
|
850
|
+
* withholds `gea_name` from a hook with no fields.
|
|
851
|
+
*
|
|
852
|
+
* Only the emitter's own `gea_`-prefixed names are touched, so a parameter
|
|
853
|
+
* this file did not name is left exactly as written.
|
|
854
|
+
*/
|
|
855
|
+
export const withUnreadParametersUnnamed = (declaration, body) => {
|
|
856
|
+
const close = declaration.lastIndexOf(')');
|
|
857
|
+
// A callable return type has its own parenthesized ABI before the body's
|
|
858
|
+
// formals. Match the final list from its closing delimiter so its parameter
|
|
859
|
+
// types cannot be mistaken for unused names in this function.
|
|
860
|
+
let open = close;
|
|
861
|
+
let depth = 0;
|
|
862
|
+
for (; open >= 0; open -= 1) {
|
|
863
|
+
if (declaration[open] === ')')
|
|
864
|
+
depth += 1;
|
|
865
|
+
else if (declaration[open] === '(' && --depth === 0)
|
|
866
|
+
break;
|
|
867
|
+
}
|
|
868
|
+
if (open < 0 || close < open)
|
|
869
|
+
return declaration;
|
|
870
|
+
const text = body.join('\n');
|
|
871
|
+
const rewritten = parameterListOf(declaration.slice(open + 1, close)).map((parameter) => {
|
|
872
|
+
const defaulted = parameter.indexOf('=');
|
|
873
|
+
const head = defaulted < 0 ? parameter : parameter.slice(0, defaulted);
|
|
874
|
+
const tail = defaulted < 0 ? '' : parameter.slice(defaulted);
|
|
875
|
+
const trimmed = head.trimEnd();
|
|
876
|
+
let start = trimmed.length;
|
|
877
|
+
while (start > 0) {
|
|
878
|
+
const character = trimmed[start - 1] ?? '';
|
|
879
|
+
if (!((character >= 'a' && character <= 'z') ||
|
|
880
|
+
(character >= 'A' && character <= 'Z') ||
|
|
881
|
+
(character >= '0' && character <= '9') ||
|
|
882
|
+
character === '_'))
|
|
883
|
+
break;
|
|
884
|
+
start -= 1;
|
|
885
|
+
}
|
|
886
|
+
const name = trimmed.slice(start);
|
|
887
|
+
if (trimmed.slice(0, start).trim().length === 0 || !name.startsWith('gea_') || mentionsIdentifier(text, name))
|
|
888
|
+
return parameter;
|
|
889
|
+
return trimmed.slice(0, start) + tail;
|
|
890
|
+
});
|
|
891
|
+
return declaration.slice(0, open + 1) + rewritten.join(',') + declaration.slice(close);
|
|
892
|
+
};
|
|
893
|
+
const renderFieldDispatcher = (structName, layout, base, classDispatch, storageTypeOf, wellKnownSymbols, definitions, dynamicProtocol = true, fieldOperations = undefined, accessorCarriesEnvironment = () => false,
|
|
894
|
+
/**
|
|
895
|
+
* Whether the class is a root nothing derives from. Its protocol members
|
|
896
|
+
* then dispatch statically: the only concrete type a handle to it can hold
|
|
897
|
+
* is itself, so `virtual` would buy a vptr in every instance and nothing
|
|
898
|
+
* else. (`gea::Ref` destroys through its own operations table, never a
|
|
899
|
+
* virtual destructor.)
|
|
900
|
+
*/
|
|
901
|
+
leafRoot = false,
|
|
902
|
+
/**
|
|
903
|
+
* This class's own lazily-materialized arrow fields, by key
|
|
904
|
+
* (`class-layout.ts`'s `censusLazyArrowFields`) -- `undefined` for a
|
|
905
|
+
* structural record, which cannot declare one (the design is per-class,
|
|
906
|
+
* keyed on the declaring class's identity).
|
|
907
|
+
*/
|
|
908
|
+
lazyArrowFields = undefined) => {
|
|
909
|
+
const accesses = layout.fields.map((field) => ({ field, access: dynamicFieldAccessOf(field, storageTypeOf(field)) }));
|
|
910
|
+
const symbolKeys = layout.fields.filter((field) => cppRecordFieldKeyIsSymbol(field.key));
|
|
911
|
+
const baseRead = base ? [` if (this->${base.structName}::gea_readOwnField(gea_key, gea_out)) return true;`] : [];
|
|
912
|
+
const baseWrite = base ? [` if (this->${base.structName}::gea_writeOwnField(gea_key, gea_value, gea_extensible)) return true;`] : [];
|
|
913
|
+
const baseDelete = base && !base.native
|
|
914
|
+
? [` if (this->${base.structName}::gea_matchesOwnField(gea_key)) return this->${base.structName}::gea_deleteOwnField(gea_key);`]
|
|
915
|
+
: [];
|
|
916
|
+
const baseMatches = base && !base.native ? [` if (this->${base.structName}::gea_matchesOwnField(gea_key)) return true;`] : [];
|
|
917
|
+
const baseFreezeFields = base && !base.native ? [` this->${base.structName}::gea_freezeOwnFields();`] : [];
|
|
918
|
+
const baseSealFields = base && !base.native ? [` this->${base.structName}::gea_sealOwnFields();`] : [];
|
|
919
|
+
const baseFrozenFields = base && !base.native ? [` if (!this->${base.structName}::gea_ownFieldsFrozen()) return false;`] : [];
|
|
920
|
+
const baseSealedFields = base && !base.native ? [` if (!this->${base.structName}::gea_ownFieldsSealed()) return false;`] : [];
|
|
921
|
+
// Native host bases have no generated index-sidecar hook. A generated class
|
|
922
|
+
// base does, and its sidecar must remain visible through the derived view
|
|
923
|
+
// without being confused with the derived class's fixed fields.
|
|
924
|
+
const baseIndexRead = base && !base.native ? [` if (this->${base.structName}::gea_readOwnIndex(gea_key, gea_out)) return true;`] : [];
|
|
925
|
+
const baseIndexWrite = base && !base.native
|
|
926
|
+
? [
|
|
927
|
+
` if (this->${base.structName}::gea_matchesOwnIndex(gea_key)) return this->${base.structName}::gea_writeOwnIndex(gea_key, gea_value, gea_extensible);`
|
|
928
|
+
]
|
|
929
|
+
: [];
|
|
930
|
+
const baseIndexDelete = base && !base.native
|
|
931
|
+
? [` if (this->${base.structName}::gea_matchesOwnIndex(gea_key)) return this->${base.structName}::gea_deleteOwnIndex(gea_key);`]
|
|
932
|
+
: [];
|
|
933
|
+
const baseIndexDescriptor = base && !base.native
|
|
934
|
+
? [
|
|
935
|
+
` if (this->${base.structName}::gea_matchesOwnIndex(gea_key)) return this->${base.structName}::gea_ownIndexDescriptor(gea_key, gea_out);`
|
|
936
|
+
]
|
|
937
|
+
: [];
|
|
938
|
+
const baseIndexDefine = base && !base.native
|
|
939
|
+
? [
|
|
940
|
+
` if (this->${base.structName}::gea_matchesOwnIndex(gea_key)) return this->${base.structName}::gea_defineOwnIndex(gea_key, gea_descriptor, gea_extensible);`
|
|
941
|
+
]
|
|
942
|
+
: [];
|
|
943
|
+
const baseIndexMatches = base && !base.native ? [` if (this->${base.structName}::gea_matchesOwnIndex(gea_key)) return true;`] : [];
|
|
944
|
+
const baseIndexFreeze = base && !base.native ? [` this->${base.structName}::gea_freezeOwnIndex();`] : [];
|
|
945
|
+
const baseKeys = base ? [` this->${base.structName}::gea_ownFieldKeys(gea_declared);`] : [];
|
|
946
|
+
const hasSymbolIndex = layout.indexes.some((index) => index.key === 'symbol');
|
|
947
|
+
// A symbol-keyed field splits into two cases that look identical from the
|
|
948
|
+
// field's OWN declaration but are not: a well-known symbol (`Symbol.iterator`
|
|
949
|
+
// and its fourteen siblings) is minted once, before any user `Symbol()` call,
|
|
950
|
+
// at a fixed low id (`gea::wellKnownSymbol`), so its identity is exactly as
|
|
951
|
+
// knowable at compile time as a string key's spelling is. A genuinely unique
|
|
952
|
+
// symbol (`Symbol()`/`Symbol('x')`) is not: its id exists only once the
|
|
953
|
+
// program runs. Refusing every symbol key alike conflated the two -- the
|
|
954
|
+
// fixture this was written against was `{ [Symbol.iterator]() {...} }`, an
|
|
955
|
+
// object literal exposing exactly the iterator protocol every `for...of` and
|
|
956
|
+
// destructuring source needs to invoke DYNAMICALLY (`source: any`) through
|
|
957
|
+
// `gea::Value::getProperty(PropertyKey::symbol(...))`, and the blanket
|
|
958
|
+
// refusal made every dynamic iteration over such a value abort at runtime --
|
|
959
|
+
// `gea::runtime::iterator::getIterator`'s own `source.getProperty(key)`
|
|
960
|
+
// could never reach a method that was RIGHT THERE, natively stored.
|
|
961
|
+
//
|
|
962
|
+
// `bodyOf` below reuses the exact per-field statement the string-keyed path
|
|
963
|
+
// already renders for `gea_name == "<the field's own sym(...) marker>"` --
|
|
964
|
+
// built once, by key, alongside `reads`/`writes`/`descriptors`/`defines`/
|
|
965
|
+
// `matches`/`deletes` further down -- so there is exactly one place that
|
|
966
|
+
// decides how one field is read, written or described, asked from two
|
|
967
|
+
// different guards (a name equality and a symbol-id equality) rather than a
|
|
968
|
+
// second opinion that could drift from the first.
|
|
969
|
+
const symbolBranchFor = (hook, generatedOnly = false) => {
|
|
970
|
+
// An open symbol index is the runtime answer for a symbol key the checker
|
|
971
|
+
// did not resolve to one declared member. It has to reach the typed
|
|
972
|
+
// sidecar below before the field dispatcher considers text names; routing
|
|
973
|
+
// it into DynamicObject would box the index value and create a second
|
|
974
|
+
// store invisible to native indexed reads.
|
|
975
|
+
if (hasSymbolIndex)
|
|
976
|
+
return [];
|
|
977
|
+
if (symbolKeys.length === 0) {
|
|
978
|
+
if (base && (!generatedOnly || !base.native))
|
|
979
|
+
return [` if (gea_key.isSymbol()) return this->${base.structName}::${hook};`];
|
|
980
|
+
return [' if (gea_key.isSymbol()) return false;'];
|
|
981
|
+
}
|
|
982
|
+
const bodyOf = hookBodyByKey(hook);
|
|
983
|
+
const resolved = symbolKeys.map((field) => ({ field, wellKnown: wellKnownSymbolEnumNameOf(wellKnownSymbols, field.key) }));
|
|
984
|
+
const unresolved = resolved.filter((entry) => entry.wellKnown === null);
|
|
985
|
+
const matchedBranches = resolved.flatMap((entry) => {
|
|
986
|
+
if (entry.wellKnown === null)
|
|
987
|
+
return [];
|
|
988
|
+
const body = bodyOf?.get(entry.field.key);
|
|
989
|
+
// No entry means this exact field has no dynamic access at all (an
|
|
990
|
+
// uninhabited or otherwise unboxable value) -- refuse it by name rather
|
|
991
|
+
// than silently falling through to "not this field".
|
|
992
|
+
const action = body ?? refuseFieldText(structName, `[symbol] ${entry.field.key}`);
|
|
993
|
+
return [
|
|
994
|
+
` if (gea_key.symbolId() == static_cast<std::uint32_t>(gea::detail::WellKnownSymbol::${entry.wellKnown})) { ` +
|
|
995
|
+
`const std::string& gea_name = ${cppStringLiteral(entry.field.key)}; (void)gea_name; ${action} }`
|
|
996
|
+
];
|
|
997
|
+
});
|
|
998
|
+
// A program's own symbol is compared by the id its cell registered
|
|
999
|
+
// (`gea::detail::registerDeclaredSymbol`); a key matching none of them is
|
|
1000
|
+
// not this struct's, so it continues exactly as a struct with no symbol
|
|
1001
|
+
// fields would.
|
|
1002
|
+
const declaredBranches = unresolved.map((entry) => {
|
|
1003
|
+
const body = bodyOf?.get(entry.field.key);
|
|
1004
|
+
const action = body ?? refuseFieldText(structName, `[symbol] ${entry.field.key}`);
|
|
1005
|
+
return (` if (gea_key.symbolId() == gea::detail::declaredSymbolId(${cppStringLiteral(entry.field.key)})) { ` +
|
|
1006
|
+
`const std::string& gea_name = ${cppStringLiteral(entry.field.key)}; (void)gea_name; ${action} }`);
|
|
1007
|
+
});
|
|
1008
|
+
const fallback = [
|
|
1009
|
+
...declaredBranches,
|
|
1010
|
+
...(base && (!generatedOnly || !base.native) ? [` return this->${base.structName}::${hook};`] : [' return false;'])
|
|
1011
|
+
];
|
|
1012
|
+
return [' if (gea_key.isSymbol()) {', ...matchedBranches, ...fallback, ' }'];
|
|
1013
|
+
};
|
|
1014
|
+
// An index signature accepts every key of its domain, so its sidecar is the
|
|
1015
|
+
// struct's own answer for keys the named fields do not claim -- and reaching
|
|
1016
|
+
// it here is what keeps a dynamic write and a native indexed read looking at
|
|
1017
|
+
// the same storage. Number keys retain their original numeric source and
|
|
1018
|
+
// accept only a reflected string that is that number's canonical ECMAScript
|
|
1019
|
+
// spelling; every other text key remains an expando rather than becoming an
|
|
1020
|
+
// invisible second numeric store. Symbol sidecars are reachable directly:
|
|
1021
|
+
// PropertyKey carries the symbol id without passing through text.
|
|
1022
|
+
const sidecarRead = [];
|
|
1023
|
+
const sidecarWrite = [];
|
|
1024
|
+
const sidecarDelete = [];
|
|
1025
|
+
const sidecarDescriptor = [];
|
|
1026
|
+
const sidecarDefine = [];
|
|
1027
|
+
const sidecarMatches = [];
|
|
1028
|
+
const sidecarFreeze = [];
|
|
1029
|
+
const sidecarSeal = [];
|
|
1030
|
+
const sidecarFrozen = [];
|
|
1031
|
+
const sidecarSealed = [];
|
|
1032
|
+
const sidecarNativeWrites = new Map();
|
|
1033
|
+
const sidecarKeys = [];
|
|
1034
|
+
const sidecarPresent = [];
|
|
1035
|
+
const sidecarEnumerable = [];
|
|
1036
|
+
// Key discovery does not depend on whether the sidecar's VALUE can cross a
|
|
1037
|
+
// dynamic `gea::Value` boundary. `Reflect.ownKeys` needs to retain a symbol
|
|
1038
|
+
// property even when its payload is optional or another fully-native type
|
|
1039
|
+
// with no box recipe; string-only consumers filter the resulting
|
|
1040
|
+
// `PropertyKey` list in the runtime. Conflating discovery with readability
|
|
1041
|
+
// made such properties disappear from the object's own-key semantics.
|
|
1042
|
+
for (const index of layout.indexes) {
|
|
1043
|
+
const member = cppRecordIndexSidecarNameFor(index, layout.indexes);
|
|
1044
|
+
const attributes = cppRecordIndexAttributesNameFor(index, layout.indexes);
|
|
1045
|
+
const sidecarTag = dynamicTagFor(index.value);
|
|
1046
|
+
const sidecarValueType = cppTypeOf(index.value);
|
|
1047
|
+
const sidecarCanonical = sidecarTag === null ? undefined : canonicalDynamicPayloads.get(sidecarTag);
|
|
1048
|
+
// Optional and tagged sidecar values carry their own live dynamic tag.
|
|
1049
|
+
// Treat them exactly as a fixed field of the same carrier: an absent
|
|
1050
|
+
// Optional is a PRESENT property whose value is `undefined`, not a missing
|
|
1051
|
+
// sidecar entry.
|
|
1052
|
+
const sidecarDynamicCarrier = index.value.kind === 'dynamic' ||
|
|
1053
|
+
index.value.kind === 'optional' ||
|
|
1054
|
+
index.value.kind === 'tagged-union' ||
|
|
1055
|
+
index.value.kind === 'dictionary';
|
|
1056
|
+
const sidecarReadable = sidecarDynamicCarrier || (sidecarTag !== null && (sidecarCanonical === undefined || sidecarCanonical === sidecarValueType));
|
|
1057
|
+
const sidecarWritable = sidecarDynamicCarrier || (sidecarReadable && sidecarCanonical !== undefined);
|
|
1058
|
+
const sidecarPropertyKey = index.key === 'symbol'
|
|
1059
|
+
? 'gea::PropertyKey::symbol(gea_entry.first)'
|
|
1060
|
+
: index.key === 'number'
|
|
1061
|
+
? 'gea::PropertyKey::string(gea_entry.first)'
|
|
1062
|
+
: 'gea::PropertyKey::string(gea_entry.first)';
|
|
1063
|
+
sidecarKeys.push(` for (const auto& gea_entry : ${member}) gea_declared.push_back(${sidecarPropertyKey});`);
|
|
1064
|
+
const sidecarKey = index.key === 'symbol'
|
|
1065
|
+
? 'gea::Symbol(static_cast<std::uint32_t>(gea_key.symbolId()))'
|
|
1066
|
+
: index.key === 'number'
|
|
1067
|
+
? 'gea_key.text()'
|
|
1068
|
+
: 'gea_key.text()';
|
|
1069
|
+
const sidecarGuard = index.key === 'symbol' ? 'gea_key.isSymbol()' : index.key === 'number' ? 'gea_key.isCanonicalNumberKey()' : '!gea_key.isSymbol()';
|
|
1070
|
+
sidecarPresent.push(` if (${sidecarGuard}) { gea_out = ${member}.has(${sidecarKey}); return true; }`);
|
|
1071
|
+
sidecarEnumerable.push(` if (${sidecarGuard}) { gea_out = ${member}.has(${sidecarKey}) && ${attributes}.attributes(${sidecarKey}).enumerable; return true; }`);
|
|
1072
|
+
if (sidecarReadable) {
|
|
1073
|
+
const sidecarReadValue = sidecarDynamicCarrier
|
|
1074
|
+
? `gea::detail::readDynamicField<${sidecarValueType}>(${member}.read(${sidecarKey}), ${cppStringLiteral(structName)}, ${cppStringLiteral(`[${index.key} index]`)})`
|
|
1075
|
+
: `gea::Value::box(gea::Value::Tag::${sidecarTag}, static_cast<${sidecarValueType}>(${member}.read(${sidecarKey})))`;
|
|
1076
|
+
sidecarRead.push(` if (${sidecarGuard} && ${member}.has(${sidecarKey})) { gea_out = ${sidecarReadValue}; return true; }`);
|
|
1077
|
+
sidecarDescriptor.push(` if (${sidecarGuard} && ${member}.has(${sidecarKey})) { ` +
|
|
1078
|
+
`gea_out = gea::PropertyDescriptor::assignment(${sidecarReadValue}); ` +
|
|
1079
|
+
`const gea::NativeIndexAttributes gea_attributes = ${attributes}.attributes(${sidecarKey}); ` +
|
|
1080
|
+
`gea_out.writable = gea_attributes.writable; gea_out.enumerable = gea_attributes.enumerable; gea_out.configurable = gea_attributes.configurable; return true; }`);
|
|
1081
|
+
}
|
|
1082
|
+
if (!sidecarReadable) {
|
|
1083
|
+
sidecarRead.push(` if (${sidecarGuard}) ${refuseFieldText(structName, `[${index.key} index]`)}`);
|
|
1084
|
+
sidecarDescriptor.push(` if (${sidecarGuard}) ${refuseFieldText(structName, `[${index.key} index]`)}`);
|
|
1085
|
+
}
|
|
1086
|
+
if (sidecarWritable) {
|
|
1087
|
+
const sidecarWriteValue = sidecarDynamicCarrier
|
|
1088
|
+
? `gea::detail::writeDynamicField<${sidecarValueType}>(${member}[${sidecarKey}], gea_value, ${cppStringLiteral(structName)}, ${cppStringLiteral(`[${index.key} index]`)})`
|
|
1089
|
+
: `${member}[${sidecarKey}] = gea::detail::unboxField<${sidecarValueType}>(gea_value, gea::Value::Tag::${sidecarTag}, ${cppStringLiteral(structName)}, ${cppStringLiteral(`[${index.key} index]`)})`;
|
|
1090
|
+
const sidecarAccepts = sidecarDynamicCarrier
|
|
1091
|
+
? `gea::detail::dynamicFieldAccepts<${sidecarValueType}>(gea_value)`
|
|
1092
|
+
: `gea_value.tag() == gea::Value::Tag::${sidecarTag} && gea_value.payloadType() == gea::detail::payloadTypeTagFor<${sidecarValueType}>()`;
|
|
1093
|
+
sidecarWrite.push(` if (${sidecarGuard}) { const bool gea_exists = ${member}.has(${sidecarKey}); ` +
|
|
1094
|
+
`if ((!gea_exists && !gea_extensible) || (gea_exists && !${attributes}.attributes(${sidecarKey}).writable) || !(${sidecarAccepts})) return false; ` +
|
|
1095
|
+
`${sidecarWriteValue}; return true; }`);
|
|
1096
|
+
const appliedWrite = sidecarDynamicCarrier
|
|
1097
|
+
? `gea::detail::writeDynamicField<${sidecarValueType}>(${member}[${sidecarKey}], gea_applied.value, ${cppStringLiteral(structName)}, ${cppStringLiteral(`[${index.key} index]`)})`
|
|
1098
|
+
: `${member}[${sidecarKey}] = gea_applied.value.as<${sidecarValueType}>()`;
|
|
1099
|
+
sidecarDefine.push(` if (${sidecarGuard}) { const bool gea_exists = ${member}.has(${sidecarKey}); gea::PropertyDescriptor gea_current; ` +
|
|
1100
|
+
`if (gea_exists && !gea_ownIndexDescriptor(gea_key, gea_current)) return false; gea::PropertyDescriptor gea_applied; ` +
|
|
1101
|
+
`if (!gea::applyNativeIndexDataDescriptor(gea_exists, gea_extensible, gea_current, gea_descriptor, gea_applied)) return false; ` +
|
|
1102
|
+
`if (!(${sidecarAccepts.replaceAll('gea_value', 'gea_applied.value')})) return false; ${appliedWrite}; ` +
|
|
1103
|
+
`${attributes}.set(${sidecarKey}, gea::NativeIndexAttributes{gea_applied.writable, gea_applied.enumerable, gea_applied.configurable}); return true; }`);
|
|
1104
|
+
}
|
|
1105
|
+
else {
|
|
1106
|
+
sidecarWrite.push(` if (${sidecarGuard}) ${refuseFieldText(structName, `[${index.key} index]`)}`);
|
|
1107
|
+
sidecarDefine.push(` if (${sidecarGuard}) ${refuseFieldText(structName, `[${index.key} index]`)}`);
|
|
1108
|
+
}
|
|
1109
|
+
sidecarMatches.push(` if (${sidecarGuard}) return true;`);
|
|
1110
|
+
sidecarDelete.push(` if (${sidecarGuard}) { if (${member}.has(${sidecarKey}) && !${attributes}.deleteAllowed(${sidecarKey})) return false; ` +
|
|
1111
|
+
`${member}.erase(${sidecarKey}); ${attributes}.erase(${sidecarKey}); return true; }`);
|
|
1112
|
+
sidecarFreeze.push(` for (const auto& gea_entry : ${member}) ${attributes}.freeze(gea_entry.first);`);
|
|
1113
|
+
sidecarSeal.push(` for (const auto& gea_entry : ${member}) { auto gea_attributes = ${attributes}.attributes(gea_entry.first); gea_attributes.configurable = false; ${attributes}.set(gea_entry.first, gea_attributes); }`);
|
|
1114
|
+
sidecarFrozen.push(` for (const auto& gea_entry : ${member}) { const auto gea_attributes = ${attributes}.attributes(gea_entry.first); if (gea_attributes.configurable || gea_attributes.writable) return false; }`);
|
|
1115
|
+
sidecarSealed.push(` for (const auto& gea_entry : ${member}) if (${attributes}.attributes(gea_entry.first).configurable) return false;`);
|
|
1116
|
+
const nativeWrites = sidecarNativeWrites.get(sidecarValueType) ?? [];
|
|
1117
|
+
const mayAddressFixed = base !== null || layout.fields.some((field) => recordIndexDomainContainsKey(index.key, field.key));
|
|
1118
|
+
const fixedNativeWrite = !mayAddressFixed
|
|
1119
|
+
? ''
|
|
1120
|
+
: index.value.kind === 'dynamic'
|
|
1121
|
+
? `if (gea_matchesOwnField(gea_key)) return gea_writeOwnField(gea_key, gea_value, gea_extensible);`
|
|
1122
|
+
: `if (gea_matchesOwnField(gea_key)) return gea_writeOwnFieldNative(gea_key, gea::NativeFieldWrite(gea_value${nativeFieldPolicyArgument(index.value)}), gea_extensible);`;
|
|
1123
|
+
// A `gea::Value` sidecar has no native caller (`nativeRecordIndexTransportOf`
|
|
1124
|
+
// refuses a dynamic value), and its fixed-field routing names
|
|
1125
|
+
// `gea_writeOwnField`, which only the dynamic protocol declares.
|
|
1126
|
+
if (index.value.kind !== 'dynamic' || dynamicProtocol)
|
|
1127
|
+
nativeWrites.push(` if (${sidecarGuard}) { ${fixedNativeWrite} const bool gea_exists = ${member}.has(${sidecarKey}); ` +
|
|
1128
|
+
`if ((!gea_exists && !gea_extensible) || (gea_exists && !${attributes}.attributes(${sidecarKey}).writable)) return false; ` +
|
|
1129
|
+
`${member}[${sidecarKey}] = gea_value; return true; }`);
|
|
1130
|
+
sidecarNativeWrites.set(sidecarValueType, nativeWrites);
|
|
1131
|
+
}
|
|
1132
|
+
const reads = [];
|
|
1133
|
+
const nativeReads = [];
|
|
1134
|
+
const nativeReadsByKey = new Map();
|
|
1135
|
+
const nativeFieldWrites = [];
|
|
1136
|
+
const nativeFieldWritesByKey = new Map();
|
|
1137
|
+
const writes = [];
|
|
1138
|
+
const descriptors = [];
|
|
1139
|
+
// The same fields' descriptor answers, minus the VALUE: presence and the
|
|
1140
|
+
// three attribute bits, which are plain bools. `gea_ownFieldDescriptor` used
|
|
1141
|
+
// to render one boxing expression per field -- the identical expression
|
|
1142
|
+
// `gea_readOwnField` already renders for that field -- so every fixed field
|
|
1143
|
+
// of every unrestricted carrier was boxed exactly twice, and the descriptor
|
|
1144
|
+
// half was half of all reflection boxing in the program. It is composed from
|
|
1145
|
+
// the read hook instead: this chain decides presence and attributes without
|
|
1146
|
+
// boxing anything, and one `gea::Value` per STRUCT carries the value.
|
|
1147
|
+
const descriptorAttributes = [];
|
|
1148
|
+
/**
|
|
1149
|
+
* The plain data fields' reads, grouped by the CARRIER they box into.
|
|
1150
|
+
*
|
|
1151
|
+
* Every one of them renders `gea::Value::box(Tag::X, static_cast<T>(member))`
|
|
1152
|
+
* -- the same two-token recipe, differing only in which member it names. So a
|
|
1153
|
+
* struct with 341 numeric constants emitted 341 `gea::Value` construction
|
|
1154
|
+
* sites to do one thing. Grouped, each `(tag, type)` pair needs exactly one:
|
|
1155
|
+
* the key chain selects a member into a local, and the group boxes once.
|
|
1156
|
+
*
|
|
1157
|
+
* This is a code-size and template-instantiation win, and deliberately not
|
|
1158
|
+
* claimed as more: a dynamic read still boxes exactly one value at run time,
|
|
1159
|
+
* the same as before. What goes away is the emitted duplication.
|
|
1160
|
+
*/
|
|
1161
|
+
const readGroups = new Map();
|
|
1162
|
+
const dataReadKeys = [];
|
|
1163
|
+
const defines = [];
|
|
1164
|
+
// Keyed the same way `matches`/`deletes` already read one field's statement
|
|
1165
|
+
// back below: by the field's own key text. `symbolBranchFor` (above) reads
|
|
1166
|
+
// these to run the identical per-field body a matched well-known symbol
|
|
1167
|
+
// needs, so a symbol-keyed field's read/write/descriptor/define is decided
|
|
1168
|
+
// in exactly the one place a string-keyed field's already is -- never a
|
|
1169
|
+
// second, symbol-shaped copy of the same logic that could drift from it.
|
|
1170
|
+
const readsByKey = new Map();
|
|
1171
|
+
const writesByKey = new Map();
|
|
1172
|
+
const descriptorsByKey = new Map();
|
|
1173
|
+
const definesByKey = new Map();
|
|
1174
|
+
// Every reader of a lazy field's storage that OBSERVES its current value
|
|
1175
|
+
// (rather than only its presence/attributes, which the empty sentinel
|
|
1176
|
+
// answers identically to a real one) must materialize first -- the same
|
|
1177
|
+
// requirement `emit-properties.ts`'s static choke point and this file's own
|
|
1178
|
+
// `gea_readOwnField`/`gea_ownFieldDescriptor` arms (below, sharing `boxed`)
|
|
1179
|
+
// already meet. `access.member` is a bare field name, valid as an lvalue
|
|
1180
|
+
// only where `this` is non-const -- every one of these hooks is `const`
|
|
1181
|
+
// (`renderableAccessors`'s `selfText`, further below, needs the identical
|
|
1182
|
+
// `const_cast` for the same reason). Reading the member back afterward
|
|
1183
|
+
// needs no cast: only the write does.
|
|
1184
|
+
const materializeText = (plan, member) => `void(${member}.invoke == nullptr ? void(const_cast<${structName}*>(this)->${member} = ${cppBodyName(plan.initializer)}(gea::Ref<${structName}>::adopt(const_cast<${structName}*>(this), true))) : void(0))`;
|
|
1185
|
+
for (const { field, access } of accesses) {
|
|
1186
|
+
if (access === null)
|
|
1187
|
+
continue;
|
|
1188
|
+
const literal = cppStringLiteral(access.key);
|
|
1189
|
+
// Required is a checker fact, not a claim that the ECMAScript own
|
|
1190
|
+
// property can never be removed. Every fixed slot therefore carries the
|
|
1191
|
+
// same physical presence bit; construction starts required slots present,
|
|
1192
|
+
// while delete can clear either kind when its descriptor permits it.
|
|
1193
|
+
const presence = cppRecordFieldPresenceName(field.key);
|
|
1194
|
+
// `censusLazyArrowFields` only ever selects a field whose carrier is a
|
|
1195
|
+
// plain callable representation (an arrow/function-expression literal),
|
|
1196
|
+
// so this is `undefined` for every OTHER access shape below (`boxed`,
|
|
1197
|
+
// `dynamicCarrier`, and the generic non-`Function` tag) -- they never
|
|
1198
|
+
// consult it.
|
|
1199
|
+
const lazyPlan = lazyArrowFields?.get(field.key);
|
|
1200
|
+
// A typed NATIVE read (`gea_readOwnFieldNative`, a host-interop path
|
|
1201
|
+
// distinct from the ordinary boxed `gea_readOwnField` below) reads
|
|
1202
|
+
// `access.member` just as directly, so it gets the same guard.
|
|
1203
|
+
const nativeRead = lazyPlan !== undefined
|
|
1204
|
+
? ` if (gea_name == ${literal}) { if (!${presence}) return false; ${materializeText(lazyPlan, access.member)}; return gea_out.assign(${access.member}); }`
|
|
1205
|
+
: ` if (gea_name == ${literal}) return ${presence} && gea_out.assign(${access.member});`;
|
|
1206
|
+
if (fieldOperations === undefined ||
|
|
1207
|
+
fieldOperations.get(field.key)?.has('read') ||
|
|
1208
|
+
fieldOperations.get(field.key)?.has('native-read')) {
|
|
1209
|
+
nativeReads.push(nativeRead);
|
|
1210
|
+
nativeReadsByKey.set(field.key, nativeRead);
|
|
1211
|
+
}
|
|
1212
|
+
const attributes = cppRecordFieldAttributesName(field.key);
|
|
1213
|
+
if (fieldOperations === undefined ||
|
|
1214
|
+
fieldOperations.get(field.key)?.has('write') ||
|
|
1215
|
+
fieldOperations.get(field.key)?.has('native-write')) {
|
|
1216
|
+
const nativeWrite = ` if (gea_name == ${literal}) { if (${presence} ? !${attributes}.writable : !gea_extensible) return false; ` +
|
|
1217
|
+
`if (!gea_value.assign${nativeFieldPolicyTemplate(field.value)}(${access.member})) return false; if (!${presence}) ${attributes} = gea::NativeIndexAttributes{}; ${presence} = true; return true; }`;
|
|
1218
|
+
nativeFieldWrites.push(nativeWrite);
|
|
1219
|
+
nativeFieldWritesByKey.set(field.key, nativeWrite);
|
|
1220
|
+
}
|
|
1221
|
+
const descriptorAttributeText = () => ` if (gea_name == ${literal}) { if (!${presence}) return false; ` +
|
|
1222
|
+
`gea_descriptor_writable = ${attributes}.writable; gea_descriptor_enumerable = ${attributes}.enumerable; ` +
|
|
1223
|
+
`gea_descriptor_configurable = ${attributes}.configurable; gea_descriptor_own = true; }`;
|
|
1224
|
+
const descriptorText = (value) => ` if (gea_name == ${literal}) { if (!${presence}) return false; ` +
|
|
1225
|
+
`gea_out = gea::PropertyDescriptor::assignment(${value}); gea_out.writable = ${attributes}.writable; ` +
|
|
1226
|
+
`gea_out.enumerable = ${attributes}.enumerable; gea_out.configurable = ${attributes}.configurable; return true; }`;
|
|
1227
|
+
const defineText = (value, accepts, write) => ` if (gea_name == ${literal}) { const bool gea_exists = ${presence}; gea::PropertyDescriptor gea_current; ` +
|
|
1228
|
+
`if (gea_exists) { gea_current = gea::PropertyDescriptor::assignment(${value}); gea_current.writable = ${attributes}.writable; ` +
|
|
1229
|
+
`gea_current.enumerable = ${attributes}.enumerable; gea_current.configurable = ${attributes}.configurable; } ` +
|
|
1230
|
+
`gea::PropertyDescriptor gea_applied; if (!gea::applyNativeIndexDataDescriptor(gea_exists, gea_extensible, gea_current, gea_descriptor, gea_applied)) return false; ` +
|
|
1231
|
+
`if (!(${accepts.replaceAll('gea_value', 'gea_applied.value')})) return false; ${write}; ` +
|
|
1232
|
+
`${attributes} = gea::NativeIndexAttributes{gea_applied.writable, gea_applied.enumerable, gea_applied.configurable}; ${presence} = true; return true; }`;
|
|
1233
|
+
const record = (into, byKey, statement) => {
|
|
1234
|
+
const operation = byKey === readsByKey ? 'read' : byKey === writesByKey ? 'write' : byKey === descriptorsByKey ? 'descriptor' : 'define';
|
|
1235
|
+
if (fieldOperations !== undefined && !fieldOperations.get(field.key)?.has(operation))
|
|
1236
|
+
return;
|
|
1237
|
+
into.push(statement);
|
|
1238
|
+
byKey.set(field.key, statement);
|
|
1239
|
+
// `descriptorsByKey` keeps the per-field body regardless: the symbol
|
|
1240
|
+
// branch inlines it BY KEY, and a well-known symbol field has no name to
|
|
1241
|
+
// match in a text chain. What composition replaces is the text chain, and
|
|
1242
|
+
// it mirrors that chain exactly -- symbol-keyed fields included, because
|
|
1243
|
+
// `symbolBranchFor` returns nothing when the layout has an open symbol
|
|
1244
|
+
// index, and a symbol key then falls through to the text compare like any
|
|
1245
|
+
// other. Dropping them here would answer differently in exactly that case.
|
|
1246
|
+
if (byKey === descriptorsByKey)
|
|
1247
|
+
descriptorAttributes.push(descriptorAttributeText());
|
|
1248
|
+
// Index-aligned with the data-field half of `reads`, so the emitted chain
|
|
1249
|
+
// can drop exactly the fields a group already answers for.
|
|
1250
|
+
if (byKey === readsByKey)
|
|
1251
|
+
dataReadKeys.push(field.key);
|
|
1252
|
+
};
|
|
1253
|
+
if (!access.readable) {
|
|
1254
|
+
record(reads, readsByKey, ` if (gea_name == ${literal}) ${refuseFieldText(structName, access.key)}`);
|
|
1255
|
+
record(writes, writesByKey, ` if (gea_name == ${literal}) ${refuseFieldText(structName, access.key)}`);
|
|
1256
|
+
record(defines, definesByKey, ` if (gea_name == ${literal}) ${refuseFieldText(structName, access.key)}`);
|
|
1257
|
+
continue;
|
|
1258
|
+
}
|
|
1259
|
+
if (access.dynamicCarrier) {
|
|
1260
|
+
const boxed = dynamicCarrierBoxText(field.value, access.member) ??
|
|
1261
|
+
`gea::detail::readDynamicField(${access.member}, ${cppStringLiteral(structName)}, ${literal})`;
|
|
1262
|
+
// An optional or union carrier boxes through a CONDITIONAL over its live
|
|
1263
|
+
// arms -- one `gea::Value` construction per arm, so a `Texture | null |
|
|
1264
|
+
// undefined` field spends three and an optional `number` spends two.
|
|
1265
|
+
// `dynamicCarrierBoxText` is a pure function of the representation and
|
|
1266
|
+
// the text naming the storage, so asking it about a SLOT rather than a
|
|
1267
|
+
// member yields the identical conditional for every field that spells
|
|
1268
|
+
// the same carrier: the key chain selects the storage into the slot and
|
|
1269
|
+
// the arms are rendered once. The fallback route is deliberately not
|
|
1270
|
+
// grouped -- `readDynamicField` names the struct and the field key, so
|
|
1271
|
+
// its text is not a function of the storage alone.
|
|
1272
|
+
const groupedBox = dynamicCarrierBoxText(field.value, '(*gea_slot)');
|
|
1273
|
+
if (groupedBox !== null && (fieldOperations === undefined || fieldOperations.get(field.key)?.has('read'))) {
|
|
1274
|
+
const groupKey = `carrier|${representationKey(field.value)}`;
|
|
1275
|
+
const group = readGroups.get(groupKey) ?? { type: access.type, box: groupedBox, keys: [], branches: [] };
|
|
1276
|
+
group.keys.push(field.key);
|
|
1277
|
+
group.branches.push(` if (gea_name == ${literal}) { if (!${presence}) return false; gea_slot = ${access.member}; }`);
|
|
1278
|
+
readGroups.set(groupKey, group);
|
|
1279
|
+
}
|
|
1280
|
+
record(reads, readsByKey, ` if (gea_name == ${literal}) { if (!${presence}) return false; gea_out = ${boxed}; return true; }`);
|
|
1281
|
+
record(writes, writesByKey, ` if (gea_name == ${literal}) { if (${presence} ? !${attributes}.writable : !gea_extensible) return false; ` +
|
|
1282
|
+
`gea::detail::writeDynamicField(${access.member}, gea_value, ${cppStringLiteral(structName)}, ${literal}); if (!${presence}) ${attributes} = gea::NativeIndexAttributes{}; ${presence} = true; return true; }`);
|
|
1283
|
+
record(descriptors, descriptorsByKey, descriptorText(boxed));
|
|
1284
|
+
const absencePolicy = dynamicFieldAbsencePolicy(field.value);
|
|
1285
|
+
record(defines, definesByKey, absencePolicy === null
|
|
1286
|
+
? defineText(boxed, `gea::detail::dynamicFieldAccepts<${access.type}>(gea_value)`, `gea::detail::writeDynamicField(${access.member}, gea_applied.value, ${cppStringLiteral(structName)}, ${literal})`)
|
|
1287
|
+
: ` if (gea_name == ${literal}) return gea::detail::applyNativeDynamicFieldDescriptor(` +
|
|
1288
|
+
`${access.member}, ${attributes}, ${presence}, gea_descriptor, gea_extensible, ${absencePolicy.allowsUndefined}, ${absencePolicy.allowsNull}, ${cppStringLiteral(structName)}, ${literal});`);
|
|
1289
|
+
continue;
|
|
1290
|
+
}
|
|
1291
|
+
if (access.boxed) {
|
|
1292
|
+
record(reads, readsByKey, ` if (gea_name == ${literal}) { if (!${presence}) return false; gea_out = ${access.member}; return true; }`);
|
|
1293
|
+
record(writes, writesByKey, ` if (gea_name == ${literal}) { if (${presence} ? !${attributes}.writable : !gea_extensible) return false; ${access.member} = gea_value; if (!${presence}) ${attributes} = gea::NativeIndexAttributes{}; ${presence} = true; return true; }`);
|
|
1294
|
+
record(descriptors, descriptorsByKey, descriptorText(access.member));
|
|
1295
|
+
record(defines, definesByKey, defineText(access.member, 'true', `${access.member} = gea_applied.value`));
|
|
1296
|
+
continue;
|
|
1297
|
+
}
|
|
1298
|
+
// This dispatcher is the SAME `gea_readOwnField` the design's "dynamic
|
|
1299
|
+
// (`any`) read" requirement names, reached independently of
|
|
1300
|
+
// `emit-properties.ts`'s own choke point (a plain `c.json` never lowers
|
|
1301
|
+
// to a dynamic operation at all). Materializing here, before `boxedText`
|
|
1302
|
+
// reads the member, is what makes a lazy field safe to expose through the
|
|
1303
|
+
// SAME dynamic protocol every other field already answers through -- see
|
|
1304
|
+
// `selfText` below, this file's own precedent for recovering a retained
|
|
1305
|
+
// `this` handle inside one of these `const` dispatch methods.
|
|
1306
|
+
const boxed = access.tag === 'Function'
|
|
1307
|
+
? lazyPlan !== undefined
|
|
1308
|
+
? `(${materializeText(lazyPlan, access.member)}, ${boxedText(field.value, access.tag, access.member)})`
|
|
1309
|
+
: boxedText(field.value, access.tag, access.member)
|
|
1310
|
+
: `gea::Value::box(gea::Value::Tag::${access.tag}, static_cast<${access.type}>(${access.member}))`;
|
|
1311
|
+
// A `Function` tag boxes through `boxedText`, whose recipe is not a tag and
|
|
1312
|
+
// a cast but a choice among three `gea::Value` constructors made from the
|
|
1313
|
+
// field's own REPRESENTATION -- a method thunk, a rest-aware callable, or
|
|
1314
|
+
// the plain box. So a callable field groups by that representation rather
|
|
1315
|
+
// than by the `(tag, type)` pair, and the group states the chosen recipe
|
|
1316
|
+
// once over its own slot. Two fields spelling the same representation
|
|
1317
|
+
// spell the same recipe by construction: `boxedText` reads nothing else.
|
|
1318
|
+
// A lazy field never joins the group: the shared box function takes only a
|
|
1319
|
+
// slot pointer, with no room for the per-field initializer call the
|
|
1320
|
+
// materialize step above needs, so it keeps its own individual arm instead.
|
|
1321
|
+
if (lazyPlan === undefined && (fieldOperations === undefined || fieldOperations.get(field.key)?.has('read'))) {
|
|
1322
|
+
const callable = access.tag === 'Function';
|
|
1323
|
+
const slotType = callable ? cppTypeOf(field.value) : access.type;
|
|
1324
|
+
const groupKey = callable ? `Function|${representationKey(field.value)}` : `${access.tag}|${access.type}`;
|
|
1325
|
+
const group = readGroups.get(groupKey) ?? {
|
|
1326
|
+
type: slotType,
|
|
1327
|
+
box: callable ? boxedText(field.value, access.tag, '*gea_slot') : `gea::Value::box(gea::Value::Tag::${access.tag}, *gea_slot)`,
|
|
1328
|
+
keys: [],
|
|
1329
|
+
branches: []
|
|
1330
|
+
};
|
|
1331
|
+
group.keys.push(field.key);
|
|
1332
|
+
group.branches.push(` if (gea_name == ${literal}) { if (!${presence}) return false; gea_slot = static_cast<${slotType}>(${access.member}); }`);
|
|
1333
|
+
readGroups.set(groupKey, group);
|
|
1334
|
+
}
|
|
1335
|
+
record(reads, readsByKey, ` if (gea_name == ${literal}) { if (!${presence}) return false; gea_out = ${boxed}; return true; }`);
|
|
1336
|
+
record(descriptors, descriptorsByKey, descriptorText(boxed));
|
|
1337
|
+
if (access.writable) {
|
|
1338
|
+
record(writes, writesByKey, ` if (gea_name == ${literal}) { if (${presence} ? !${attributes}.writable : !gea_extensible) return false; ` +
|
|
1339
|
+
`${access.member} = gea::detail::unboxField<${access.type}>(gea_value, gea::Value::Tag::${access.tag}, ${cppStringLiteral(structName)}, ${literal}); if (!${presence}) ${attributes} = gea::NativeIndexAttributes{}; ${presence} = true; return true; }`);
|
|
1340
|
+
// `applyNativeFieldDescriptor` reads the CURRENT value through this
|
|
1341
|
+
// same `access.member` reference when the field is present,
|
|
1342
|
+
// non-configurable and non-writable (a SameValue check against the
|
|
1343
|
+
// incoming descriptor's value) -- an un-materialized lazy field would
|
|
1344
|
+
// hand it the empty sentinel there instead of the real callable, so it
|
|
1345
|
+
// materializes first, unconditionally, the same as every other reader
|
|
1346
|
+
// in this file that observes rather than replaces the current value.
|
|
1347
|
+
record(defines, definesByKey, lazyPlan !== undefined
|
|
1348
|
+
? ` if (gea_name == ${literal}) { ${materializeText(lazyPlan, access.member)}; return gea::applyNativeFieldDescriptor(` +
|
|
1349
|
+
`${access.member}, ${attributes}, ${presence}, gea_descriptor, gea_extensible, gea::Value::Tag::${access.tag}); }`
|
|
1350
|
+
: ` if (gea_name == ${literal}) return gea::applyNativeFieldDescriptor(` +
|
|
1351
|
+
`${access.member}, ${attributes}, ${presence}, gea_descriptor, gea_extensible, gea::Value::Tag::${access.tag});`);
|
|
1352
|
+
}
|
|
1353
|
+
else {
|
|
1354
|
+
record(writes, writesByKey, ` if (gea_name == ${literal}) ${refuseFieldText(structName, access.key)}`);
|
|
1355
|
+
record(defines, definesByKey, ` if (gea_name == ${literal}) ${refuseFieldText(structName, access.key)}`);
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
// An accessor is an own property with NO storage, so the boxed protocol has
|
|
1359
|
+
// to call its body where a field's arm reads a member. The body called is the
|
|
1360
|
+
// same free function `emit-properties.ts` calls for a native read -- one
|
|
1361
|
+
// authority for "what answers this member", reached from the two places that
|
|
1362
|
+
// can ask it. Its receiver is rebuilt from `this`: these hooks are only ever
|
|
1363
|
+
// reached through a `gea::Value` whose payload IS this object, and
|
|
1364
|
+
// `Ref::adopt(pointer, true)` takes the retained handle the body's parameter
|
|
1365
|
+
// is declared with.
|
|
1366
|
+
//
|
|
1367
|
+
// Before this, the dispatcher knew only `layout.fields`, so a boxed read of
|
|
1368
|
+
// `{ get g() { ... } }` found no arm, fell through to `return false`, and the
|
|
1369
|
+
// box answered `undefined` -- a silently wrong answer that also made every
|
|
1370
|
+
// `for...of` over a dynamic iterator whose `next` is an accessor loop forever
|
|
1371
|
+
// (`test/runtime/dynamic-iterator-header-close.ts`).
|
|
1372
|
+
// Everything pushed into `descriptors` from here on is an ACCESSOR's, and an
|
|
1373
|
+
// accessor descriptor is a pair of callable halves rather than a stored value
|
|
1374
|
+
// -- it has no read-hook answer to compose from and stays exactly as it was.
|
|
1375
|
+
const dataDescriptorCount = descriptors.length;
|
|
1376
|
+
const dataReadCount = reads.length;
|
|
1377
|
+
const accessors = renderableAccessors(layout);
|
|
1378
|
+
const selfText = `gea::Ref<${structName}>::adopt(const_cast<${structName}*>(this), true)`;
|
|
1379
|
+
for (const accessor of accessors) {
|
|
1380
|
+
const literal = cppStringLiteral(accessor.key);
|
|
1381
|
+
const presence = cppRecordFieldPresenceName(accessor.key);
|
|
1382
|
+
const attributes = cppRecordFieldAttributesName(accessor.key);
|
|
1383
|
+
// A capturing accessor's environment travels on the object, so the boxed
|
|
1384
|
+
// protocol unpacks it exactly where the native read does -- one helper,
|
|
1385
|
+
// both callers, so the two spellings of one call cannot come apart. Read
|
|
1386
|
+
// off whichever spelling of the object is in scope: the hooks run as
|
|
1387
|
+
// member functions, while a descriptor's `get`/`set` runs inside a lambda
|
|
1388
|
+
// that captured the handle and has no `this` at all.
|
|
1389
|
+
const environmentOf = (half, body, holder) => body === null || !accessorCarriesEnvironment(body)
|
|
1390
|
+
? ''
|
|
1391
|
+
: `${storedEnvironmentText(body, `${holder}${cppRecordAccessorEnvironmentName(accessor.key, half)}`)}, `;
|
|
1392
|
+
const getEnvironment = environmentOf('getter', accessor.getter, '');
|
|
1393
|
+
const setEnvironment = environmentOf('setter', accessor.setter, '');
|
|
1394
|
+
const descriptorGetEnvironment = environmentOf('getter', accessor.getter, 'gea_self->');
|
|
1395
|
+
const descriptorSetEnvironment = environmentOf('setter', accessor.setter, 'gea_self->');
|
|
1396
|
+
const getText = (receiver, environment) => accessor.getter === null ? 'gea::Value()' : accessorBoxedText(accessor, `${cppBodyName(accessor.getter)}(${environment}${receiver})`);
|
|
1397
|
+
const setText = (receiver, written, environment) => {
|
|
1398
|
+
if (accessor.setter === null)
|
|
1399
|
+
return null;
|
|
1400
|
+
const unboxed = accessorUnboxText(accessor, structName, written);
|
|
1401
|
+
return unboxed === null ? null : `${cppBodyName(accessor.setter)}(${environment}${receiver}, ${unboxed})`;
|
|
1402
|
+
};
|
|
1403
|
+
const read = getText(selfText, getEnvironment);
|
|
1404
|
+
reads.push(read === null
|
|
1405
|
+
? ` if (gea_name == ${literal}) ${refuseFieldText(structName, accessor.key)}`
|
|
1406
|
+
: ` if (gea_name == ${literal}) { if (!${presence}) return false; gea_out = ${read}; return true; }`);
|
|
1407
|
+
// A `[[Set]]` on a getter-only accessor FAILS -- `false` here, never a
|
|
1408
|
+
// fallthrough, which would let the box grow an expando that shadows the
|
|
1409
|
+
// getter from then on.
|
|
1410
|
+
const write = setText(selfText, 'gea_value', setEnvironment);
|
|
1411
|
+
writes.push(accessor.setter === null
|
|
1412
|
+
? ` if (gea_name == ${literal}) return false;`
|
|
1413
|
+
: write === null
|
|
1414
|
+
? ` if (gea_name == ${literal}) ${refuseFieldText(structName, accessor.key)}`
|
|
1415
|
+
: ` if (gea_name == ${literal}) { if (!${presence}) return false; ${write}; return true; }`);
|
|
1416
|
+
const descriptorGet = getText('gea_self', descriptorGetEnvironment);
|
|
1417
|
+
const descriptorSet = setText('gea_self', 'gea_written', descriptorSetEnvironment);
|
|
1418
|
+
const halves = [
|
|
1419
|
+
...(accessor.getter !== null && descriptorGet !== null
|
|
1420
|
+
? [`gea_out.hasGet = true; gea_out.get = [gea_self = ${selfText}](const gea::Value&) { return ${descriptorGet}; };`]
|
|
1421
|
+
: []),
|
|
1422
|
+
...(accessor.setter !== null && descriptorSet !== null
|
|
1423
|
+
? [
|
|
1424
|
+
`gea_out.hasSet = true; gea_out.set = [gea_self = ${selfText}](const gea::Value&, const gea::Value& gea_written) { ${descriptorSet}; };`
|
|
1425
|
+
]
|
|
1426
|
+
: [])
|
|
1427
|
+
];
|
|
1428
|
+
descriptors.push(halves.length === 0
|
|
1429
|
+
? ` if (gea_name == ${literal}) ${refuseFieldText(structName, accessor.key)}`
|
|
1430
|
+
: ` if (gea_name == ${literal}) { if (!${presence}) return false; gea_out = gea::PropertyDescriptor{}; ${halves.join(' ')} ` +
|
|
1431
|
+
`gea_out.hasEnumerable = true; gea_out.enumerable = ${attributes}.enumerable; ` +
|
|
1432
|
+
`gea_out.hasConfigurable = true; gea_out.configurable = ${attributes}.configurable; return true; }`);
|
|
1433
|
+
// Redefining an accessor-backed member would have to replace the BODY that
|
|
1434
|
+
// answers it, and a struct has no slot to put another one in. Failing the
|
|
1435
|
+
// define is the honest answer -- `Object.defineProperty` throws on it --
|
|
1436
|
+
// where returning `false` from `gea_matchesOwnField` instead would let the
|
|
1437
|
+
// definition land in the expando table and shadow the accessor silently.
|
|
1438
|
+
defines.push(` if (gea_name == ${literal}) return false;`);
|
|
1439
|
+
}
|
|
1440
|
+
// Enumeration order is the specification's, not the declaration's, and is a
|
|
1441
|
+
// property of the whole key set rather than of any one access.
|
|
1442
|
+
// Enumeration does not read a property's value. A field whose carrier has
|
|
1443
|
+
// no dynamic box recipe is still an own enumerable key, so filtering this
|
|
1444
|
+
// list through `dynamicFieldAccessOf` would make Object.keys lose exactly
|
|
1445
|
+
// those fields. Symbol fields are excluded because this hook supplies the
|
|
1446
|
+
// string-key list consumed by Object.keys/for-in; their runtime identity is
|
|
1447
|
+
// not a string the dispatcher can reconstruct.
|
|
1448
|
+
// Accessor keys follow the fields rather than interleaving with them. The two
|
|
1449
|
+
// lists come from one `shape.members` walk and each keeps its order, but the
|
|
1450
|
+
// split does not record where one sat relative to the other, so a literal
|
|
1451
|
+
// mixing `get g()` with `plain` enumerates `plain,g` where the engine says
|
|
1452
|
+
// `g,plain`. That is a narrower divergence than the one it replaces -- an
|
|
1453
|
+
// accessor used to be absent from `Object.keys` entirely -- and closing it
|
|
1454
|
+
// means carrying a member ordinal, not reordering here.
|
|
1455
|
+
const ownKeys = [...layout.fields.map((field) => field.key), ...accessors.map((accessor) => accessor.key)];
|
|
1456
|
+
const keys = ownKeys.flatMap((key) => {
|
|
1457
|
+
if (!cppRecordFieldKeyIsSymbol(key))
|
|
1458
|
+
return [` if (${cppRecordFieldPresenceName(key)}) gea_declared.push_back(gea::PropertyKey::string(${cppStringLiteral(key)}));`];
|
|
1459
|
+
const wellKnown = wellKnownSymbolEnumNameOf(wellKnownSymbols, key);
|
|
1460
|
+
if (wellKnown === null)
|
|
1461
|
+
return [];
|
|
1462
|
+
return [
|
|
1463
|
+
` if (${cppRecordFieldPresenceName(key)}) gea_declared.push_back(gea::PropertyKey::symbol(gea::wellKnownSymbol(gea::detail::WellKnownSymbol::${wellKnown})));`
|
|
1464
|
+
];
|
|
1465
|
+
});
|
|
1466
|
+
const matches = ownKeys.map((key) => ` if (gea_name == ${cppStringLiteral(key)}) return true;`);
|
|
1467
|
+
const matchesByKey = new Map(layout.fields.map((field, index) => [field.key, matches[index]]));
|
|
1468
|
+
const presentFields = ownKeys.map((key) => ` if (gea_name == ${cppStringLiteral(key)}) { gea_out = ${cppRecordFieldPresenceName(key)}; return true; }`);
|
|
1469
|
+
const enumerableFields = ownKeys.map((key) => ` if (gea_name == ${cppStringLiteral(key)}) { gea_out = ${cppRecordFieldPresenceName(key)} && ${cppRecordFieldAttributesName(key)}.enumerable; return true; }`);
|
|
1470
|
+
const presentByKey = new Map(layout.fields.map((field, index) => [field.key, presentFields[index]]));
|
|
1471
|
+
const enumerableByKey = new Map(layout.fields.map((field, index) => [field.key, enumerableFields[index]]));
|
|
1472
|
+
const deletes = ownKeys.map((key) => {
|
|
1473
|
+
const presence = cppRecordFieldPresenceName(key);
|
|
1474
|
+
const attributes = cppRecordFieldAttributesName(key);
|
|
1475
|
+
return ` if (gea_name == ${cppStringLiteral(key)}) { if (!${presence}) return true; if (!${attributes}.configurable) return false; ${presence} = false; return true; }`;
|
|
1476
|
+
});
|
|
1477
|
+
const deletesByKey = new Map(layout.fields.map((field, index) => [field.key, deletes[index]]));
|
|
1478
|
+
// The one place `symbolBranchFor` (above) reads a matched field's
|
|
1479
|
+
// already-rendered body from, by hook signature -- so the symbol path and
|
|
1480
|
+
// the string path share one statement per field per hook, never two.
|
|
1481
|
+
const hooksByBodyKey = {
|
|
1482
|
+
'gea_readOwnFieldNative(gea_key, gea_out)': nativeReadsByKey,
|
|
1483
|
+
'gea_writeOwnFieldNative(gea_key, gea_value, gea_extensible)': nativeFieldWritesByKey,
|
|
1484
|
+
'gea_readOwnField(gea_key, gea_out)': readsByKey,
|
|
1485
|
+
'gea_writeOwnField(gea_key, gea_value, gea_extensible)': writesByKey,
|
|
1486
|
+
'gea_ownFieldDescriptor(gea_key, gea_out)': descriptorsByKey,
|
|
1487
|
+
'gea_defineOwnField(gea_key, gea_descriptor, gea_extensible)': definesByKey,
|
|
1488
|
+
'gea_matchesOwnField(gea_key)': matchesByKey,
|
|
1489
|
+
'gea_deleteOwnField(gea_key)': deletesByKey,
|
|
1490
|
+
'gea_ownFieldPresent(gea_key, gea_out)': presentByKey,
|
|
1491
|
+
'gea_ownFieldEnumerable(gea_key, gea_out)': enumerableByKey
|
|
1492
|
+
};
|
|
1493
|
+
const hookBodyByKey = (hook) => hooksByBodyKey[hook];
|
|
1494
|
+
const freezeFields = ownKeys.map((key) => {
|
|
1495
|
+
const presence = cppRecordFieldPresenceName(key);
|
|
1496
|
+
const attributes = cppRecordFieldAttributesName(key);
|
|
1497
|
+
return ` if (${presence}) ${attributes} = gea::NativeIndexAttributes{false, ${attributes}.enumerable, false};`;
|
|
1498
|
+
});
|
|
1499
|
+
const sealFields = ownKeys.map((key) => {
|
|
1500
|
+
const presence = cppRecordFieldPresenceName(key);
|
|
1501
|
+
const attributes = cppRecordFieldAttributesName(key);
|
|
1502
|
+
return ` if (${presence}) ${attributes}.configurable = false;`;
|
|
1503
|
+
});
|
|
1504
|
+
const frozenFields = ownKeys.map((key) => {
|
|
1505
|
+
const presence = cppRecordFieldPresenceName(key);
|
|
1506
|
+
const attributes = cppRecordFieldAttributesName(key);
|
|
1507
|
+
return ` if (${presence} && (${attributes}.configurable || ${attributes}.writable)) return false;`;
|
|
1508
|
+
});
|
|
1509
|
+
const sealedFields = ownKeys.map((key) => {
|
|
1510
|
+
const presence = cppRecordFieldPresenceName(key);
|
|
1511
|
+
const attributes = cppRecordFieldAttributesName(key);
|
|
1512
|
+
return ` if (${presence} && ${attributes}.configurable) return false;`;
|
|
1513
|
+
});
|
|
1514
|
+
// A struct with no addressable field of its own never reads `gea_name`, and
|
|
1515
|
+
// an unused reference is a warning in every build that enables one.
|
|
1516
|
+
const nameOf = (body) => body.length > 0 ? [' const std::string& gea_name = gea_key.text();'] : [];
|
|
1517
|
+
const accessorDescriptors = descriptors.slice(dataDescriptorCount);
|
|
1518
|
+
const accessorReads = reads.slice(dataReadCount);
|
|
1519
|
+
// A group of ONE is not a group: its block spends a slot declaration and a
|
|
1520
|
+
// presence test to save nothing, so the lone field keeps the site it had and
|
|
1521
|
+
// only shared carriers are hoisted.
|
|
1522
|
+
const emittedReadGroups = [...readGroups.values()].filter((group) => group.branches.length > 1);
|
|
1523
|
+
const groupedReadKeys = new Set(emittedReadGroups.flatMap((group) => group.keys));
|
|
1524
|
+
// The fields no group answers for: an already-boxed slot, a dynamic carrier,
|
|
1525
|
+
// a refused one, and any carrier only one field on this layout spells.
|
|
1526
|
+
const ungroupedDataReads = reads.slice(0, dataReadCount).filter((_, index) => !groupedReadKeys.has(dataReadKeys[index] ?? ''));
|
|
1527
|
+
const readGroupBlocks = emittedReadGroups.flatMap((group) => [
|
|
1528
|
+
' {',
|
|
1529
|
+
` std::optional<${group.type}> gea_slot;`,
|
|
1530
|
+
...group.branches,
|
|
1531
|
+
// `std::optional`'s own emptiness, never a sentinel value: a `bool` field
|
|
1532
|
+
// holding `false` and an absent field must not be the same answer.
|
|
1533
|
+
` if (gea_slot) { gea_out = ${group.box}; return true; }`,
|
|
1534
|
+
' }'
|
|
1535
|
+
]);
|
|
1536
|
+
/**
|
|
1537
|
+
* Whether the descriptor hook may compose its VALUE from the read hook.
|
|
1538
|
+
*
|
|
1539
|
+
* It may exactly when every data field this hook answers for is a field the
|
|
1540
|
+
* read hook also answers for, which an unrestricted carrier satisfies by
|
|
1541
|
+
* construction: both cover the whole layout. A SEALED per-field demand can
|
|
1542
|
+
* name a field for `descriptor` and not for `read`, and composing there would
|
|
1543
|
+
* answer `false` for a field that has a descriptor -- so those keep the
|
|
1544
|
+
* per-field rendering they already had. That is also where there is nothing
|
|
1545
|
+
* to win: the boxes are on the unrestricted carriers.
|
|
1546
|
+
*/
|
|
1547
|
+
const descriptorsComposed = fieldOperations === undefined;
|
|
1548
|
+
// A dynamic property operation retains the receiver's static `Ref<T>` type,
|
|
1549
|
+
// but JavaScript property lookup observes the concrete object. A call through
|
|
1550
|
+
// `Ref<Base>` must therefore enter the most-derived field table before that
|
|
1551
|
+
// table explicitly walks its bases. Records have no subtype identity and keep
|
|
1552
|
+
// these hooks non-virtual, preserving their plain aggregate layout.
|
|
1553
|
+
const classMember = (stated, body) => {
|
|
1554
|
+
const declaration = withUnreadParametersUnnamed(stated, body);
|
|
1555
|
+
const prefix = classDispatch && !leafRoot && (base === undefined || base.native) ? 'virtual ' : '';
|
|
1556
|
+
const suffix = classDispatch && base !== undefined && !base.native ? ' override' : '';
|
|
1557
|
+
const signature = ` ${prefix}${declaration}${suffix}`;
|
|
1558
|
+
if (definitions === undefined)
|
|
1559
|
+
return [`${signature} {`, ...body, ' }'];
|
|
1560
|
+
// The definition's qualified name and omitted default argument are C++
|
|
1561
|
+
// placement syntax. Its body is the identical field-table recipe above.
|
|
1562
|
+
const returnTypeEnd = declaration.indexOf(' ') + 1;
|
|
1563
|
+
const qualified = declaration.slice(0, returnTypeEnd) + structName + '::' + declaration.slice(returnTypeEnd);
|
|
1564
|
+
definitions.push([`${qualified.split(' = true').join('')} {`, ...body, '}'].join('\n'));
|
|
1565
|
+
return [`${signature};`];
|
|
1566
|
+
};
|
|
1567
|
+
const dynamicMember = (declaration, body) => dynamicProtocol ? classMember(declaration, body) : [];
|
|
1568
|
+
// A store through an index sidecar is TYPED transport, not reflection:
|
|
1569
|
+
// `emit-carrier-members.ts`'s `emitRecordIndexSidecarStore` calls
|
|
1570
|
+
// `gea_writeOwnIndexNative` for every `m[k] = v` a record with an index
|
|
1571
|
+
// signature receives, and `reflection-demand.ts` correctly counts that store
|
|
1572
|
+
// as no reflection demand at all (`nativeRecordIndexTransportOf`). So these
|
|
1573
|
+
// members cannot hang off `dynamicProtocol`: a keys-only struct with an
|
|
1574
|
+
// index sidecar still has to carry the index write, and the fixed-field
|
|
1575
|
+
// routing inside it (`gea_writeOwnFieldNative`, for a key the sidecar's
|
|
1576
|
+
// domain shares with a declared field). Gating both behind the protocol is
|
|
1577
|
+
// how record-index-sidecar.ts's own `m[2] = 'two'` named a member its struct
|
|
1578
|
+
// never declared.
|
|
1579
|
+
// `gea_matchesOwnField` is one third of the runtime's native own-field
|
|
1580
|
+
// protocol (`nativeOwnFieldProtocolIsWhole`: predicate, definition and
|
|
1581
|
+
// deletion are stated together or not at all), and the sidecar's
|
|
1582
|
+
// fixed-field routing needs the predicate. So an index sidecar states the
|
|
1583
|
+
// whole trio; the definition and deletion bodies are the same demand-gated
|
|
1584
|
+
// recipes the dynamic protocol would emit, unreachable until reflection
|
|
1585
|
+
// demand turns the protocol on. Stating only the predicate tripped the
|
|
1586
|
+
// fail-closed static_assert in native-symbol-property-identity.
|
|
1587
|
+
const nativeIndexMember = sidecarNativeWrites.size > 0 ? classMember : dynamicMember;
|
|
1588
|
+
const nativeWriteMethods = [...sidecarNativeWrites].flatMap(([valueType, writes]) => [
|
|
1589
|
+
...classMember(`bool gea_writeOwnIndexNative(const gea::PropertyKey& gea_key, const ${valueType}& gea_value, bool gea_extensible = true)`, [...writes, ' return false;'])
|
|
1590
|
+
]);
|
|
1591
|
+
return [
|
|
1592
|
+
" /** This struct's declared fields, as a boxed `gea::Value` reads them. `false` means the struct does not declare the key, which sends it to the box's expando table. */",
|
|
1593
|
+
...classMember('bool gea_ownFieldPresent(const gea::PropertyKey& gea_key, bool& gea_out) const', [
|
|
1594
|
+
...symbolBranchFor('gea_ownFieldPresent(gea_key, gea_out)', true),
|
|
1595
|
+
...nameOf(presentFields),
|
|
1596
|
+
...presentFields,
|
|
1597
|
+
...(base && !base.native ? [` if (this->${base.structName}::gea_ownFieldPresent(gea_key, gea_out)) return true;`] : []),
|
|
1598
|
+
' return false;'
|
|
1599
|
+
]),
|
|
1600
|
+
...classMember('bool gea_ownFieldEnumerable(const gea::PropertyKey& gea_key, bool& gea_out) const', [
|
|
1601
|
+
...symbolBranchFor('gea_ownFieldEnumerable(gea_key, gea_out)', true),
|
|
1602
|
+
...nameOf(enumerableFields),
|
|
1603
|
+
...enumerableFields,
|
|
1604
|
+
...(base && !base.native ? [` if (this->${base.structName}::gea_ownFieldEnumerable(gea_key, gea_out)) return true;`] : []),
|
|
1605
|
+
' return false;'
|
|
1606
|
+
]),
|
|
1607
|
+
...classMember('bool gea_ownIndexPresent(const gea::PropertyKey& gea_key, bool& gea_out) const', [
|
|
1608
|
+
...(base && !base.native ? [` if (this->${base.structName}::gea_ownIndexPresent(gea_key, gea_out)) return true;`] : []),
|
|
1609
|
+
...sidecarPresent,
|
|
1610
|
+
' return false;'
|
|
1611
|
+
]),
|
|
1612
|
+
...classMember('bool gea_ownIndexEnumerable(const gea::PropertyKey& gea_key, bool& gea_out) const', [
|
|
1613
|
+
...(base && !base.native ? [` if (this->${base.structName}::gea_ownIndexEnumerable(gea_key, gea_out)) return true;`] : []),
|
|
1614
|
+
...sidecarEnumerable,
|
|
1615
|
+
' return false;'
|
|
1616
|
+
]),
|
|
1617
|
+
...dynamicMember('bool gea_readOwnField(const gea::PropertyKey& gea_key, gea::Value& gea_out) const', [
|
|
1618
|
+
...symbolBranchFor('gea_readOwnField(gea_key, gea_out)'),
|
|
1619
|
+
...nameOf(reads),
|
|
1620
|
+
...readGroupBlocks,
|
|
1621
|
+
...ungroupedDataReads,
|
|
1622
|
+
...accessorReads,
|
|
1623
|
+
...baseRead,
|
|
1624
|
+
' return false;'
|
|
1625
|
+
]),
|
|
1626
|
+
...dynamicMember('bool gea_readOwnFieldNative(const gea::PropertyKey& gea_key, gea::NativeFieldRead& gea_out) const', [
|
|
1627
|
+
...symbolBranchFor('gea_readOwnFieldNative(gea_key, gea_out)', true),
|
|
1628
|
+
...nameOf([...nativeReads, ...accessors.map((accessor) => accessor.key)]),
|
|
1629
|
+
...nativeReads,
|
|
1630
|
+
// Accessors must run exactly once through the existing getter path.
|
|
1631
|
+
// A derived accessor also shadows a base data field of the same name.
|
|
1632
|
+
...accessors.map((accessor) => ` if (gea_name == ${cppStringLiteral(accessor.key)}) return false;`),
|
|
1633
|
+
...(base && !base.native ? [` return this->${base.structName}::gea_readOwnFieldNative(gea_key, gea_out);`] : [' return false;'])
|
|
1634
|
+
]),
|
|
1635
|
+
...nativeIndexMember('bool gea_writeOwnFieldNative(const gea::PropertyKey& gea_key, const gea::NativeFieldWrite& gea_value, bool gea_extensible = true)', [
|
|
1636
|
+
...symbolBranchFor('gea_writeOwnFieldNative(gea_key, gea_value, gea_extensible)', true),
|
|
1637
|
+
...nameOf([...nativeFieldWrites, ...accessors.map((accessor) => accessor.key)]),
|
|
1638
|
+
...nativeFieldWrites,
|
|
1639
|
+
...accessors.map((accessor) => ` if (gea_name == ${cppStringLiteral(accessor.key)}) return false;`),
|
|
1640
|
+
...(base && !base.native
|
|
1641
|
+
? [` return this->${base.structName}::gea_writeOwnFieldNative(gea_key, gea_value, gea_extensible);`]
|
|
1642
|
+
: [' return false;'])
|
|
1643
|
+
]),
|
|
1644
|
+
' /** The complete data descriptor for one present fixed field. */',
|
|
1645
|
+
...dynamicMember('bool gea_ownFieldDescriptor(const gea::PropertyKey& gea_key, gea::PropertyDescriptor& gea_out) const', [
|
|
1646
|
+
// Descriptor hooks belong to generated bases, just like the text-key chain
|
|
1647
|
+
// below. A native base's field reader is not a descriptor hook.
|
|
1648
|
+
...symbolBranchFor('gea_ownFieldDescriptor(gea_key, gea_out)', true),
|
|
1649
|
+
...nameOf(descriptorsComposed ? [...descriptorAttributes, ...accessorDescriptors] : descriptors),
|
|
1650
|
+
...(descriptorsComposed
|
|
1651
|
+
? [
|
|
1652
|
+
...(descriptorAttributes.length === 0
|
|
1653
|
+
? []
|
|
1654
|
+
: [
|
|
1655
|
+
' bool gea_descriptor_own = false;',
|
|
1656
|
+
' bool gea_descriptor_writable = false;',
|
|
1657
|
+
' bool gea_descriptor_enumerable = false;',
|
|
1658
|
+
' bool gea_descriptor_configurable = false;'
|
|
1659
|
+
]),
|
|
1660
|
+
...descriptorAttributes,
|
|
1661
|
+
...(descriptorAttributes.length === 0
|
|
1662
|
+
? []
|
|
1663
|
+
: [
|
|
1664
|
+
' if (gea_descriptor_own) {',
|
|
1665
|
+
' gea::Value gea_descriptor_value;',
|
|
1666
|
+
' if (!this->gea_readOwnField(gea_key, gea_descriptor_value)) return false;',
|
|
1667
|
+
' gea_out = gea::PropertyDescriptor::assignment(gea_descriptor_value);',
|
|
1668
|
+
' gea_out.writable = gea_descriptor_writable; gea_out.enumerable = gea_descriptor_enumerable;',
|
|
1669
|
+
' gea_out.configurable = gea_descriptor_configurable;',
|
|
1670
|
+
' return true;',
|
|
1671
|
+
' }'
|
|
1672
|
+
]),
|
|
1673
|
+
...accessorDescriptors
|
|
1674
|
+
]
|
|
1675
|
+
: descriptors),
|
|
1676
|
+
...(base && !base.native ? [` if (this->${base.structName}::gea_ownFieldDescriptor(gea_key, gea_out)) return true;`] : []),
|
|
1677
|
+
' return false;'
|
|
1678
|
+
]),
|
|
1679
|
+
' /** The same, for a write. A declared field is written IN PLACE, so every native read of it sees the change. */',
|
|
1680
|
+
...dynamicMember('bool gea_writeOwnField(const gea::PropertyKey& gea_key, const gea::Value& gea_value, bool gea_extensible = true)', [
|
|
1681
|
+
...symbolBranchFor('gea_writeOwnField(gea_key, gea_value, gea_extensible)'),
|
|
1682
|
+
...nameOf(writes),
|
|
1683
|
+
...writes,
|
|
1684
|
+
...baseWrite,
|
|
1685
|
+
' return false;'
|
|
1686
|
+
]),
|
|
1687
|
+
' /** ValidateAndApplyPropertyDescriptor for a fixed native data property. */',
|
|
1688
|
+
...nativeIndexMember('bool gea_defineOwnField(const gea::PropertyKey& gea_key, const gea::PropertyDescriptor& gea_descriptor, bool gea_extensible = true)', [
|
|
1689
|
+
...symbolBranchFor('gea_defineOwnField(gea_key, gea_descriptor, gea_extensible)', true),
|
|
1690
|
+
...nameOf(defines),
|
|
1691
|
+
...defines,
|
|
1692
|
+
...(base && !base.native
|
|
1693
|
+
? [
|
|
1694
|
+
` if (this->${base.structName}::gea_matchesOwnField(gea_key)) ` +
|
|
1695
|
+
`return this->${base.structName}::gea_defineOwnField(gea_key, gea_descriptor, gea_extensible);`
|
|
1696
|
+
]
|
|
1697
|
+
: []),
|
|
1698
|
+
' return false;'
|
|
1699
|
+
]),
|
|
1700
|
+
' /** A field may be declared by the native layout but currently absent after delete. */',
|
|
1701
|
+
...nativeIndexMember('bool gea_matchesOwnField(const gea::PropertyKey& gea_key) const', [
|
|
1702
|
+
...symbolBranchFor('gea_matchesOwnField(gea_key)', true),
|
|
1703
|
+
...nameOf(matches),
|
|
1704
|
+
...matches,
|
|
1705
|
+
...baseMatches,
|
|
1706
|
+
' return false;'
|
|
1707
|
+
]),
|
|
1708
|
+
' /** OrdinaryDelete for one declared data property; false preserves a non-configurable field. */',
|
|
1709
|
+
...nativeIndexMember('bool gea_deleteOwnField(const gea::PropertyKey& gea_key)', [
|
|
1710
|
+
...symbolBranchFor('gea_deleteOwnField(gea_key)', true),
|
|
1711
|
+
...nameOf(deletes),
|
|
1712
|
+
...deletes,
|
|
1713
|
+
...baseDelete,
|
|
1714
|
+
' return false;'
|
|
1715
|
+
]),
|
|
1716
|
+
" /** SetIntegrityLevel(frozen) changes descriptors, never the fixed fields' native carriers. */",
|
|
1717
|
+
...classMember('void gea_freezeOwnFields()', [...baseFreezeFields, ...freezeFields]),
|
|
1718
|
+
' /** SetIntegrityLevel(sealed) retains writability while making present fields non-configurable. */',
|
|
1719
|
+
...classMember('void gea_sealOwnFields()', [...baseSealFields, ...sealFields]),
|
|
1720
|
+
' /** TestIntegrityLevel(frozen) over present fixed data properties. */',
|
|
1721
|
+
...classMember('bool gea_ownFieldsFrozen() const', [...baseFrozenFields, ...frozenFields, ' return true;']),
|
|
1722
|
+
' /** TestIntegrityLevel(sealed) over present fixed data properties. */',
|
|
1723
|
+
...classMember('bool gea_ownFieldsSealed() const', [...baseSealedFields, ...sealedFields, ' return true;']),
|
|
1724
|
+
" /** A present entry of this record's index-signature sidecar. It is deliberately separate from fixed C++ fields: the former is configurable, the latter is not. */",
|
|
1725
|
+
...dynamicMember('bool gea_readOwnIndex(const gea::PropertyKey& gea_key, gea::Value& gea_out) const', [
|
|
1726
|
+
...baseIndexRead,
|
|
1727
|
+
...sidecarRead,
|
|
1728
|
+
' return false;'
|
|
1729
|
+
]),
|
|
1730
|
+
' /** Whether a key belongs to this typed index domain, even when no entry is present. */',
|
|
1731
|
+
...dynamicMember('bool gea_matchesOwnIndex(const gea::PropertyKey& gea_key) const', [
|
|
1732
|
+
...baseIndexMatches,
|
|
1733
|
+
...sidecarMatches,
|
|
1734
|
+
' return false;'
|
|
1735
|
+
]),
|
|
1736
|
+
' /** Store one index-signature entry without making it masquerade as a fixed field. */',
|
|
1737
|
+
...dynamicMember('bool gea_writeOwnIndex(const gea::PropertyKey& gea_key, const gea::Value& gea_value, bool gea_extensible = true)', [
|
|
1738
|
+
...baseIndexWrite,
|
|
1739
|
+
...sidecarWrite,
|
|
1740
|
+
' return false;'
|
|
1741
|
+
]),
|
|
1742
|
+
' /** Complete descriptor reflection for one present typed index entry. */',
|
|
1743
|
+
...dynamicMember('bool gea_ownIndexDescriptor(const gea::PropertyKey& gea_key, gea::PropertyDescriptor& gea_out) const', [
|
|
1744
|
+
...baseIndexDescriptor,
|
|
1745
|
+
...sidecarDescriptor,
|
|
1746
|
+
' return false;'
|
|
1747
|
+
]),
|
|
1748
|
+
' /** Data-descriptor-only ValidateAndApplyPropertyDescriptor for the typed index store. */',
|
|
1749
|
+
...dynamicMember('bool gea_defineOwnIndex(const gea::PropertyKey& gea_key, const gea::PropertyDescriptor& gea_descriptor, bool gea_extensible)', [...baseIndexDefine, ...sidecarDefine, ' return false;']),
|
|
1750
|
+
' /** Typed indexed assignment keeps its value native while honoring the same descriptor bits. */',
|
|
1751
|
+
...nativeWriteMethods,
|
|
1752
|
+
' /** Delete an index-signature entry. Fixed fields intentionally never reach this hook. */',
|
|
1753
|
+
...dynamicMember('bool gea_deleteOwnIndex(const gea::PropertyKey& gea_key)', [
|
|
1754
|
+
...baseIndexDelete,
|
|
1755
|
+
...sidecarDelete,
|
|
1756
|
+
' return false;'
|
|
1757
|
+
]),
|
|
1758
|
+
' /** Freeze the sidecar entries without changing their native value carrier. */',
|
|
1759
|
+
...classMember('void gea_freezeOwnIndex()', [...baseIndexFreeze, ...sidecarFreeze]),
|
|
1760
|
+
' /** Seal keeps typed index values writable while making their present entries non-configurable. */',
|
|
1761
|
+
...classMember('void gea_sealOwnIndex()', [
|
|
1762
|
+
...(base && !base.native ? [` this->${base.structName}::gea_sealOwnIndex();`] : []),
|
|
1763
|
+
...sidecarSeal
|
|
1764
|
+
]),
|
|
1765
|
+
' /** TestIntegrityLevel(frozen) over present typed index entries. */',
|
|
1766
|
+
...classMember('bool gea_ownIndexFrozen() const', [
|
|
1767
|
+
...(base && !base.native ? [` if (!this->${base.structName}::gea_ownIndexFrozen()) return false;`] : []),
|
|
1768
|
+
...sidecarFrozen,
|
|
1769
|
+
' return true;'
|
|
1770
|
+
]),
|
|
1771
|
+
' /** TestIntegrityLevel(sealed) over present typed index entries. */',
|
|
1772
|
+
...classMember('bool gea_ownIndexSealed() const', [
|
|
1773
|
+
...(base && !base.native ? [` if (!this->${base.structName}::gea_ownIndexSealed()) return false;`] : []),
|
|
1774
|
+
...sidecarSealed,
|
|
1775
|
+
' return true;'
|
|
1776
|
+
]),
|
|
1777
|
+
' /** The full string-and-symbol own-key list for this native struct. String-only consumers filter symbols; reflective consumers retain them. */',
|
|
1778
|
+
...classMember('void gea_ownFieldKeys(std::vector<gea::PropertyKey>& gea_out) const', [
|
|
1779
|
+
' std::vector<gea::PropertyKey> gea_declared;',
|
|
1780
|
+
...baseKeys,
|
|
1781
|
+
...keys,
|
|
1782
|
+
...sidecarKeys,
|
|
1783
|
+
' std::vector<gea::PropertyKey> gea_indices;',
|
|
1784
|
+
' std::vector<gea::PropertyKey> gea_strings;',
|
|
1785
|
+
' std::vector<gea::PropertyKey> gea_symbols;',
|
|
1786
|
+
' for (const auto& gea_key : gea_declared) {',
|
|
1787
|
+
' if (gea_key.isSymbol()) gea_symbols.push_back(gea_key);',
|
|
1788
|
+
' else if (gea::DynamicObject::arrayIndexOf(gea_key.text()) != gea::DynamicObject::kNotAnArrayIndex) gea_indices.push_back(gea_key);',
|
|
1789
|
+
' else gea_strings.push_back(gea_key);',
|
|
1790
|
+
' }',
|
|
1791
|
+
' std::stable_sort(gea_indices.begin(), gea_indices.end(), [](const gea::PropertyKey& gea_left, const gea::PropertyKey& gea_right) {',
|
|
1792
|
+
' return gea::DynamicObject::arrayIndexOf(gea_left.text()) < gea::DynamicObject::arrayIndexOf(gea_right.text());',
|
|
1793
|
+
' });',
|
|
1794
|
+
' gea_out.insert(gea_out.end(), gea_indices.begin(), gea_indices.end());',
|
|
1795
|
+
' gea_out.insert(gea_out.end(), gea_strings.begin(), gea_strings.end());',
|
|
1796
|
+
' gea_out.insert(gea_out.end(), gea_symbols.begin(), gea_symbols.end());'
|
|
1797
|
+
])
|
|
1798
|
+
];
|
|
1799
|
+
};
|
|
1800
|
+
/**
|
|
1801
|
+
* The C++ storage one field of this struct occupies.
|
|
1802
|
+
*
|
|
1803
|
+
* Ordinarily that is exactly the carrier's own type. A field the installed
|
|
1804
|
+
* plugin marked reactive is instead held in that plugin's cell, wrapping the
|
|
1805
|
+
* SAME carrier -- `Signal<double>` for a `double`, never a box. The cell is
|
|
1806
|
+
* storage and nothing else: it converts to `const T&` on read and assigns on
|
|
1807
|
+
* write, so every read and write this emitter renders keeps the spelling it
|
|
1808
|
+
* had, and no value in the program becomes dynamic. That is the whole reason a
|
|
1809
|
+
* cell is admissible under the no-boxing rule where a `gea::Value` would not
|
|
1810
|
+
* be.
|
|
1811
|
+
*
|
|
1812
|
+
* Only three carriers are celled, and the restriction is the cell's own
|
|
1813
|
+
* requirements rather than a policy: it default-constructs its value, copy-
|
|
1814
|
+
* assigns it, and compares it with `==` to skip a no-op write. A number, a
|
|
1815
|
+
* boolean and a string all satisfy that. An array, a record, a handle or a
|
|
1816
|
+
* class reference may not -- a `std::shared_ptr` compares by identity, so a
|
|
1817
|
+
* mutation through it would compare EQUAL to itself and notify nobody, which
|
|
1818
|
+
* is a silent failure to re-render rather than a loud one. Those fields stay
|
|
1819
|
+
* plain members here; a template that reads one is refused by name at the JSX
|
|
1820
|
+
* site (`emit-jsx.ts`) rather than being wired to a cell that cannot tell it
|
|
1821
|
+
* changed.
|
|
1822
|
+
*/
|
|
1823
|
+
export const representationCanCell = (representation) => representation.kind === 'scalar' || representation.kind === 'string';
|
|
1824
|
+
/**
|
|
1825
|
+
* The companion revision cell a reactive field gets when its own carrier
|
|
1826
|
+
* cannot be a cell.
|
|
1827
|
+
*
|
|
1828
|
+
* `count = 0` becomes `Signal<double> count` and every write notifies through
|
|
1829
|
+
* the cell itself. `cells = [...]` cannot: the cell compares with `==` to skip
|
|
1830
|
+
* unchanged writes, and a `shared_ptr` to a vector compares equal to itself
|
|
1831
|
+
* after the vector is mutated, so it would notify nobody. v1 hit this and
|
|
1832
|
+
* answered it by leaving the vector a plain member and declaring a companion
|
|
1833
|
+
* `<field>__rev` Signal beside it that mutations tick
|
|
1834
|
+
* (`cpp-reactive-component.ts`'s `arrayRevFieldName`); this is that answer,
|
|
1835
|
+
* emitted rather than spliced into generated text.
|
|
1836
|
+
*
|
|
1837
|
+
* `cppRecordFieldName` throws for any source key in the `gea_` prefix, and a
|
|
1838
|
+
* TypeScript identifier cannot contain `__rev` as a suffix without being a
|
|
1839
|
+
* different key -- but a program CAN declare both `cells` and `cells__rev`, so
|
|
1840
|
+
* the two would collide. That is a real, narrow collision and it is left
|
|
1841
|
+
* visible rather than papered over with a mangled name nothing else can spell:
|
|
1842
|
+
* the emitter names this member from this one function, so a future guard has
|
|
1843
|
+
* exactly one place to check.
|
|
1844
|
+
*/
|
|
1845
|
+
export const cppReactiveRevisionFieldName = (key) => `${cppRecordFieldName(key)}__rev`;
|
|
1846
|
+
/**
|
|
1847
|
+
* The C++ type one member is declared with -- the ONE spelling of a field's
|
|
1848
|
+
* storage, which is why the integer census reaches the struct through here.
|
|
1849
|
+
*
|
|
1850
|
+
* `narrowed` is the whole-program answer from `ir/integer-storage.ts`: a
|
|
1851
|
+
* `number` member every write proves integral and bounded is declared
|
|
1852
|
+
* `long long`, exactly as the hand-written baselines declare theirs. The
|
|
1853
|
+
* member's REPRESENTATION is untouched -- it stays `scalar('number')`, so
|
|
1854
|
+
* nothing outside this file's own readers sees a carrier that disagrees with
|
|
1855
|
+
* the plan -- and every other spelling of the same member (`dynamicFieldAccessOf`
|
|
1856
|
+
* below) is passed this answer rather than re-deriving one.
|
|
1857
|
+
*/
|
|
1858
|
+
const fieldStorageType = (field, reactive, celled, narrowed) => {
|
|
1859
|
+
const inner = narrowed ? cppNarrowedIntegerType : cppTypeOf(field.value);
|
|
1860
|
+
if (reactive.cell === null || !celled.has(field.key))
|
|
1861
|
+
return inner;
|
|
1862
|
+
if (!representationCanCell(field.value))
|
|
1863
|
+
return inner;
|
|
1864
|
+
return `${reactive.cell}<${inner}>`;
|
|
1865
|
+
};
|
|
1866
|
+
/**
|
|
1867
|
+
* Which of a struct's fields this compilation holds in a cell, by struct name.
|
|
1868
|
+
*
|
|
1869
|
+
* The plugin states the answer per CLASS DECLARATION, and this renderer works
|
|
1870
|
+
* in struct names, so the two are joined here -- once, against the same
|
|
1871
|
+
* `cppClassName` that `classBaseLinks` uses, so a struct and its base link can
|
|
1872
|
+
* never disagree about which name a declaration has.
|
|
1873
|
+
*
|
|
1874
|
+
* Own fields only, and that is the correct scope rather than a limitation: a
|
|
1875
|
+
* derived struct really does inherit its base's members (`ClassBaseLink`
|
|
1876
|
+
* subtracts them), so a base's reactive field has its storage -- and therefore
|
|
1877
|
+
* its cell -- declared once, in the base's own struct, which is where the
|
|
1878
|
+
* plugin recorded it.
|
|
1879
|
+
*/
|
|
1880
|
+
const reactiveFieldsByStruct = (classes, reactive, fieldsByStruct, bound) => {
|
|
1881
|
+
const byStruct = new Map();
|
|
1882
|
+
if (reactive.cell === null)
|
|
1883
|
+
return byStruct;
|
|
1884
|
+
for (const layout of classes.values()) {
|
|
1885
|
+
const owned = reactive.fields.get(layout.declaration);
|
|
1886
|
+
if (!owned || owned.size === 0)
|
|
1887
|
+
continue;
|
|
1888
|
+
const structName = cppClassName(layout.declaration);
|
|
1889
|
+
byStruct.set(structName, owned);
|
|
1890
|
+
// The ELEMENTS of a reactive array are reactive too.
|
|
1891
|
+
//
|
|
1892
|
+
// `cells = [{ left, top, color, filled }]` is one reactive field, but what
|
|
1893
|
+
// a program writes is `cell.filled = 1` -- through a reference to one
|
|
1894
|
+
// element, never through the array. Celling the array itself cannot see
|
|
1895
|
+
// that write (a `shared_ptr` compares equal to itself after its pointee
|
|
1896
|
+
// changes), and rebuilding the whole list on it is both wasteful and the
|
|
1897
|
+
// wrong granularity: one cell changed, one row's props should change.
|
|
1898
|
+
//
|
|
1899
|
+
// Celling the ELEMENT's own fields makes the write notify exactly the
|
|
1900
|
+
// props that read it, through the same `Signal<T>` storage every reactive
|
|
1901
|
+
// scalar already uses -- so `cell.filled = 1` needs no notify emitted
|
|
1902
|
+
// beside it at all, and the row is never rebuilt.
|
|
1903
|
+
for (const field of fieldsByStruct.get(structName)?.fields ?? []) {
|
|
1904
|
+
if (!owned.has(field.key) || field.value.kind !== 'array-object')
|
|
1905
|
+
continue;
|
|
1906
|
+
const element = field.value.element;
|
|
1907
|
+
if (element.kind !== 'record' && element.kind !== 'native-record-ref')
|
|
1908
|
+
continue;
|
|
1909
|
+
//
|
|
1910
|
+
// Only the members a JSX slot actually BINDS, never every cellable one.
|
|
1911
|
+
// `Signal<T>` is not the field's declared carrier, so celling a member
|
|
1912
|
+
// nothing renders can stop the struct compiling for its other readers:
|
|
1913
|
+
// `gea-bench` holds `rows` (rendered) beside `items` (parsed out of
|
|
1914
|
+
// JSON), and celling `Item.id` made `gea_json_read(reader, out.id)` an
|
|
1915
|
+
// error. `reactiveBoundRecordFields` answers which members are bound.
|
|
1916
|
+
const elementStruct = cppRecordStructName(element.shapeId);
|
|
1917
|
+
const renders = bound.get(elementStruct);
|
|
1918
|
+
if (renders === undefined)
|
|
1919
|
+
continue;
|
|
1920
|
+
const cellable = fieldsByStruct.get(elementStruct)?.fields.filter((member) => renders.has(member.key) && representationCanCell(member.value)) ?? [];
|
|
1921
|
+
if (cellable.length > 0)
|
|
1922
|
+
byStruct.set(elementStruct, new Set(cellable.map((member) => member.key)));
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
return byStruct;
|
|
1926
|
+
};
|
|
1927
|
+
const renderStructDefinition = (structName, layout, base, reactive, celled,
|
|
1928
|
+
/** The members `ir/integer-storage.ts` proved may be held in a `long long`, by slot. */
|
|
1929
|
+
narrowedSlots,
|
|
1930
|
+
/** Dispatch members for the keys this class's family overrides -- see `virtual-methods.ts`. */
|
|
1931
|
+
virtualMembers,
|
|
1932
|
+
/** Whether this struct is a class whose dynamic property table must dispatch through its concrete runtime type. */
|
|
1933
|
+
classDispatch,
|
|
1934
|
+
/** Whether a descendant introduces virtual dispatch, requiring this base to own the hierarchy's primary vptr so every `Ref` upcast preserves the allocation address. */
|
|
1935
|
+
stabilizeRefAddress,
|
|
1936
|
+
/**
|
|
1937
|
+
* Whether nothing in this unit derives from the struct.
|
|
1938
|
+
*
|
|
1939
|
+
* Stated in the C++ because `gea::Ref<T>::release` reads it: a handle to a
|
|
1940
|
+
* FINAL type is already the exact type, so it destroys and frees directly
|
|
1941
|
+
* instead of through the operations table two indirect calls away. Nothing
|
|
1942
|
+
* else consults it -- `final` is a fact about the hierarchy this renderer
|
|
1943
|
+
* already knows, written down where the runtime can act on it.
|
|
1944
|
+
*/
|
|
1945
|
+
isFinal,
|
|
1946
|
+
/** The frontend's declaration-identity census of `SymbolConstructor`'s own members -- see `wellKnownSymbolEnumNameOf`. */
|
|
1947
|
+
wellKnownSymbols, definitions, dynamicProtocol = true, fieldOperations = undefined, accessorCarriesEnvironment = () => false,
|
|
1948
|
+
/**
|
|
1949
|
+
* Whether presence bits and attribute triples are program-wide constants
|
|
1950
|
+
* (`program-facts.ts`'s `fixedFieldStateConstant`: nothing freezes, seals,
|
|
1951
|
+
* redefines or deletes a declared field). They are then `static` members --
|
|
1952
|
+
* the same names every reader and writer already spells, stated once per
|
|
1953
|
+
* struct instead of carried by every instance. An OPTIONAL field's presence
|
|
1954
|
+
* is still per instance: it starts absent and each instance's first store
|
|
1955
|
+
* makes it present.
|
|
1956
|
+
*/
|
|
1957
|
+
fixedFieldStateConstant = false,
|
|
1958
|
+
/**
|
|
1959
|
+
* Whether the class's method state is one object for the whole program
|
|
1960
|
+
* (`ir/class-evaluation.ts`: the class is evaluated once, and it is a root
|
|
1961
|
+
* nothing derives from, so no other class's instances share the member).
|
|
1962
|
+
* The handle is then a `static` member the construct function fills in,
|
|
1963
|
+
* not eight bytes of every instance.
|
|
1964
|
+
*/
|
|
1965
|
+
staticMethodState = false,
|
|
1966
|
+
/** This class's own lazily-materialized arrow fields, by key -- see `renderFieldDispatcher`'s parameter of the same name. */
|
|
1967
|
+
lazyArrowFields = undefined) => {
|
|
1968
|
+
const isNarrowed = (field) => narrowedSlots.has(integerStorageSlot(structName, field.key));
|
|
1969
|
+
const lines = [`struct ${structName}${isFinal ? ' final' : ''}${base ? ` : ${base.structName}` : ''} {`];
|
|
1970
|
+
const ownsMethodState = classDispatch && (!base || base.native);
|
|
1971
|
+
if (ownsMethodState)
|
|
1972
|
+
lines.push(` ${staticMethodState ? 'static inline ' : ''}gea::Ref<gea::NativeClassMethodState> gea_method_state;`);
|
|
1973
|
+
if (stabilizeRefAddress && virtualMembers.length === 0 && !classDispatch) {
|
|
1974
|
+
// A derived class that is the first polymorphic class in a hierarchy may
|
|
1975
|
+
// place its vptr before a non-polymorphic base subobject. That moves the
|
|
1976
|
+
// base away from the allocation address and breaks `gea::Ref`'s one-pointer
|
|
1977
|
+
// header lookup on an ordinary derived-to-base conversion. Give every
|
|
1978
|
+
// ancestor of an emitted virtual family one harmless virtual member, so
|
|
1979
|
+
// the hierarchy has a primary vptr from its root and every single base
|
|
1980
|
+
// subobject remains at offset zero under the target C++ ABI.
|
|
1981
|
+
lines.push(' virtual void gea_ref_address_anchor() {}');
|
|
1982
|
+
}
|
|
1983
|
+
// NO `gea_ref_standalone` marker. `final && !base` is NOT the condition that
|
|
1984
|
+
// licenses it, and asserting it here was a silent miscompile.
|
|
1985
|
+
//
|
|
1986
|
+
// `refStandalone`'s contract is that EVERY `gea::Ref` naming the object names
|
|
1987
|
+
// its exact allocated type -- and `gea::Value::box` breaks it for any record,
|
|
1988
|
+
// because a box holds its payload as a `Ref<void>` (`held_`, gea_runtime.h).
|
|
1989
|
+
// The two are incompatible in the block's ADDRESSING, not merely in its
|
|
1990
|
+
// destruction: `refCountsOf` subtracts `refStride<T>`, which is 8 for a
|
|
1991
|
+
// standalone type and 16 for `void`, so an erased handle reads and writes the
|
|
1992
|
+
// refcount 8 bytes outside the block, and `release` then calls through an
|
|
1993
|
+
// operations pointer the block never had. Measured: a two-field record passed
|
|
1994
|
+
// to an `any` parameter certified clean, emitted, compiled under clang, and
|
|
1995
|
+
// segfaulted with PC=0. `for...in` was NOT required to trigger it -- boxing
|
|
1996
|
+
// alone was, so this reached every program that boxes a record.
|
|
1997
|
+
//
|
|
1998
|
+
// This renderer cannot prove the record is never boxed: whether a shape
|
|
1999
|
+
// reaches a `dynamic` carrier is a whole-program conversion fact, and only
|
|
2000
|
+
// `isFinal` (pure hierarchy) arrives here. Re-earning the optimization means
|
|
2001
|
+
// threading that fact in and marking only shapes no conversion ever boxes;
|
|
2002
|
+
// until then the sound answer is not to claim it. `Ref`'s converting
|
|
2003
|
+
// constructor and `staticCast` now `static_assert` against erasing a
|
|
2004
|
+
// standalone handle, so a future attempt fails to COMPILE rather than to run.
|
|
2005
|
+
for (const field of layout.fields) {
|
|
2006
|
+
lines.push(` ${fieldStorageType(field, reactive, celled, isNarrowed(field))} ${cppRecordFieldName(field.key)};`);
|
|
2007
|
+
// A reactive field whose own carrier cannot be a cell gets the companion
|
|
2008
|
+
// revision cell instead -- see `cppReactiveRevisionFieldName`.
|
|
2009
|
+
if (reactive.cell !== null && celled.has(field.key) && !representationCanCell(field.value)) {
|
|
2010
|
+
lines.push(` ${reactive.cell}<double> ${cppReactiveRevisionFieldName(field.key)};`);
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
// A capturing accessor's environment. Declared beside the fields because it
|
|
2014
|
+
// IS storage -- see `cppRecordAccessorEnvironmentName` -- and type-erased
|
|
2015
|
+
// (`gea::PackedEnvironment` is a `void*` plus the handle that owns it), so
|
|
2016
|
+
// this struct never has to name, or be ordered after, the environment struct
|
|
2017
|
+
// of a body it merely carries state for.
|
|
2018
|
+
for (const accessor of layout.accessors) {
|
|
2019
|
+
for (const half of ['getter', 'setter']) {
|
|
2020
|
+
const body = half === 'getter' ? accessor.getter : accessor.setter;
|
|
2021
|
+
if (body === null || !accessorCarriesEnvironment(body))
|
|
2022
|
+
continue;
|
|
2023
|
+
lines.push(` gea::PackedEnvironment ${cppRecordAccessorEnvironmentName(accessor.key, half)};`);
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
for (const index of layout.indexes) {
|
|
2027
|
+
// One dictionary member carries every dynamic-keyed property the named
|
|
2028
|
+
// fields above do not, embedded by value so it shares the struct's own
|
|
2029
|
+
// ownership instead of needing one of its own. Spelling it by building the
|
|
2030
|
+
// identical `dictionary` representation a bare dictionary carrier would be
|
|
2031
|
+
// and handing it to `cppTypeOf` (types.ts) -- rather than picking
|
|
2032
|
+
// `gea::Dictionary`/`gea::NumericDictionary` here too -- keeps the
|
|
2033
|
+
// string-vs-number container choice a fact `cppTypeOf` states once, not a
|
|
2034
|
+
// second opinion this renderer could drift from.
|
|
2035
|
+
const sidecar = { kind: 'dictionary', key: index.key, value: index.value, ownership: 'owned' };
|
|
2036
|
+
lines.push(` ${cppTypeOf(sidecar)} ${cppRecordIndexSidecarNameFor(index, layout.indexes)};`);
|
|
2037
|
+
const attributeKey = index.key === 'symbol' ? 'gea::Symbol' : 'std::string';
|
|
2038
|
+
lines.push(` gea::NativeIndexAttributeTable<${attributeKey}> ${cppRecordIndexAttributesNameFor(index, layout.indexes)};`);
|
|
2039
|
+
}
|
|
2040
|
+
// Presence is separate from the value carrier for EVERY fixed field.
|
|
2041
|
+
// `value === undefined` is present, and a required TypeScript member starts
|
|
2042
|
+
// present, but either physical property can later be deleted while
|
|
2043
|
+
// configurable. Bits follow all value/index members so existing aggregate
|
|
2044
|
+
// initializers keep their positional field layout.
|
|
2045
|
+
const constantPresence = (required) => (required && fixedFieldStateConstant ? 'static inline ' : '');
|
|
2046
|
+
const constantAttributes = fixedFieldStateConstant ? 'static inline ' : '';
|
|
2047
|
+
for (const field of layout.fields) {
|
|
2048
|
+
lines.push(` ${constantPresence(field.required)}bool ${cppRecordFieldPresenceName(field.key)} = ${field.required ? 'true' : 'false'};`);
|
|
2049
|
+
}
|
|
2050
|
+
for (const field of layout.fields)
|
|
2051
|
+
lines.push(` ${constantAttributes}gea::NativeIndexAttributes ${cppRecordFieldAttributesName(field.key)};`);
|
|
2052
|
+
// An accessor stores no VALUE, but it is still an own property, so it needs
|
|
2053
|
+
// the two bits every own property has: whether it is currently present (a
|
|
2054
|
+
// configurable one can be deleted) and its attributes. Without them a boxed
|
|
2055
|
+
// `delete o.g` would have to answer by lying in one direction or the other.
|
|
2056
|
+
for (const accessor of renderableAccessors(layout)) {
|
|
2057
|
+
lines.push(` ${constantPresence(true)}bool ${cppRecordFieldPresenceName(accessor.key)} = true;`);
|
|
2058
|
+
lines.push(` ${constantAttributes}gea::NativeIndexAttributes ${cppRecordFieldAttributesName(accessor.key)};`);
|
|
2059
|
+
}
|
|
2060
|
+
// Trace the actual stored fields, including inherited storage and the
|
|
2061
|
+
// index sidecar. The collector counts ownership edges, never a reflective
|
|
2062
|
+
// property read (which could allocate, box, or run a getter).
|
|
2063
|
+
// A STATIC method state is not an edge the instance owns -- the collector's
|
|
2064
|
+
// trial deletion would subtract one reference per instance from an object
|
|
2065
|
+
// only the class holds -- so it is traced by nobody.
|
|
2066
|
+
const traceableFields = [
|
|
2067
|
+
...(ownsMethodState && !staticMethodState ? ['true'] : []),
|
|
2068
|
+
...(base ? [`gea::detail::TraceEdges<${base.structName}>::supported`] : []),
|
|
2069
|
+
...layout.fields.map((field) => `gea::detail::TraceEdges<decltype(${cppRecordFieldName(field.key)})>::supported`),
|
|
2070
|
+
...layout.indexes.map((index) => `gea::detail::TraceEdges<decltype(${cppRecordIndexSidecarNameFor(index, layout.indexes)})>::supported`)
|
|
2071
|
+
];
|
|
2072
|
+
// `geaTraceRefs`'s only caller is `gea::detail::traceRefs`'s `if constexpr
|
|
2073
|
+
// (supported)` -- reachable only when SOME field here traces to a ref, which
|
|
2074
|
+
// is a fact about every OTHER declaration in the program that could hold
|
|
2075
|
+
// this struct, not about this struct in isolation. A shape with no
|
|
2076
|
+
// ref-reaching field (every member a plain scalar/string/bool) makes
|
|
2077
|
+
// `supported` a compile-time `false`, so the branch is discarded and this
|
|
2078
|
+
// friend is called nowhere except the unevaluated `requires(...)` that
|
|
2079
|
+
// detects `supported` in the first place -- which typechecks it without
|
|
2080
|
+
// requiring it to be emitted. Clang then reports the whole function as dead
|
|
2081
|
+
// (`-Wunused-function` / `-Wunneeded-internal-declaration`) for every class
|
|
2082
|
+
// this program happens not to need it for. `[[maybe_unused]]` is the same
|
|
2083
|
+
// answer `linkagePrefix` already gives every minted function for the same
|
|
2084
|
+
// reason: whether a minted entity has a caller is a program-wide fact this
|
|
2085
|
+
// renderer cannot see from one struct, so it never tries to predict it.
|
|
2086
|
+
lines.push(` [[maybe_unused]] friend auto geaTraceRefs(const ${structName}& value, gea::detail::RefVisitor& visitor)`);
|
|
2087
|
+
lines.push(` -> std::bool_constant<${traceableFields.length ? traceableFields.join(' || ') : 'false'}> {`);
|
|
2088
|
+
if (base)
|
|
2089
|
+
lines.push(` gea::detail::traceRefs(static_cast<const ${base.structName}&>(value), visitor);`);
|
|
2090
|
+
if (ownsMethodState && !staticMethodState)
|
|
2091
|
+
lines.push(' gea::detail::traceRefs(value.gea_method_state, visitor);');
|
|
2092
|
+
for (const field of layout.fields)
|
|
2093
|
+
lines.push(` gea::detail::traceRefs(value.${cppRecordFieldName(field.key)}, visitor);`);
|
|
2094
|
+
for (const index of layout.indexes)
|
|
2095
|
+
lines.push(` gea::detail::traceRefs(value.${cppRecordIndexSidecarNameFor(index, layout.indexes)}, visitor);`);
|
|
2096
|
+
lines.push(' return {};');
|
|
2097
|
+
lines.push(' }');
|
|
2098
|
+
for (const line of renderFieldDispatcher(structName, layout, base, classDispatch, (field) => fieldStorageType(field, reactive, celled, isNarrowed(field)), wellKnownSymbols, definitions, dynamicProtocol, fieldOperations, accessorCarriesEnvironment, isFinal && base === undefined, lazyArrowFields))
|
|
2099
|
+
lines.push(line);
|
|
2100
|
+
// Declared only; the definitions go out of line, after every body has been
|
|
2101
|
+
// forward-declared -- a member cannot forward to a body the file has not
|
|
2102
|
+
// named yet.
|
|
2103
|
+
for (const line of virtualMembers)
|
|
2104
|
+
lines.push(line);
|
|
2105
|
+
lines.push('};');
|
|
2106
|
+
return lines.join('\n');
|
|
2107
|
+
};
|
|
2108
|
+
/**
|
|
2109
|
+
* Every struct definition the selected carriers require, in a deterministic
|
|
2110
|
+
* order, plus the ones no layout could be rendered for.
|
|
2111
|
+
*
|
|
2112
|
+
* A gap here is reported, never thrown. A struct is required by every carrier
|
|
2113
|
+
* that names it, so a missing layout is discovered in the middle of assembling
|
|
2114
|
+
* a file -- and an exception escaping from there takes down a compilation that
|
|
2115
|
+
* has a perfectly good report to give, which is the same objection
|
|
2116
|
+
* `renderTranslationUnit` already answers for a refused body.
|
|
2117
|
+
*/
|
|
2118
|
+
export const cppRecordDeclarations = (plan, deriver, classes, reactive,
|
|
2119
|
+
/** Which record members a JSX slot binds -- `reactiveBoundRecordFields`. */
|
|
2120
|
+
boundRecordFields,
|
|
2121
|
+
/** Dispatch member declarations by struct name -- `virtualMethodEmission().membersByStruct`. */
|
|
2122
|
+
virtualMembersByStruct = new Map(),
|
|
2123
|
+
/** The members that may be declared `long long` -- `integerStorageCensusOf().slots`. */
|
|
2124
|
+
narrowedSlots = new Set(),
|
|
2125
|
+
/**
|
|
2126
|
+
* The frontend's declaration-identity census of `SymbolConstructor`'s own
|
|
2127
|
+
* members (`host-protocols.ts`'s `wellKnownSymbolDeclarationsOf`) -- what
|
|
2128
|
+
* lets a symbol-keyed field's dispatcher tell `Symbol.iterator` and its
|
|
2129
|
+
* fourteen siblings, whose runtime identity is fixed at a known low id,
|
|
2130
|
+
* apart from a genuinely unique symbol whose id only exists once the
|
|
2131
|
+
* program runs. See `wellKnownSymbolEnumNameOf`.
|
|
2132
|
+
*/
|
|
2133
|
+
wellKnownSymbols = new Map(), splitFieldDefinitions = false, reflection, representations = [...plan.selected.values()], physicalClasses = classes,
|
|
2134
|
+
/**
|
|
2135
|
+
* Whether an accessor body carries an environment -- `captures.ts`'s
|
|
2136
|
+
* admission, asked rather than re-derived, so the member this struct
|
|
2137
|
+
* declares and the formal that body takes cannot come apart.
|
|
2138
|
+
*/
|
|
2139
|
+
accessorCarriesEnvironment = () => false,
|
|
2140
|
+
/** `program-facts.ts`'s `fixedFieldStateConstant` -- see `renderStructDefinition`'s parameter of the same name. */
|
|
2141
|
+
fixedFieldStateConstant = false,
|
|
2142
|
+
/** `program-facts.ts`'s `singleEvaluationClasses` -- which classes may hold their method state statically. */
|
|
2143
|
+
singleEvaluationClasses = new Set()) => {
|
|
2144
|
+
const { links, unlinkable } = classBaseLinks(physicalClasses);
|
|
2145
|
+
const { requiredStructs, fieldsByStruct, unlayoutable } = collectRequiredStructs(representations, deriver, links, unlinkable, classInstanceShapes(physicalClasses), new Map([...physicalClasses.values()].flatMap((layout) => layout.nativeStorage === undefined ? [] : [[cppClassName(layout.declaration), layout.nativeStorage.fields]])));
|
|
2146
|
+
const celledByStruct = reactiveFieldsByStruct(classes, reactive, fieldsByStruct, boundRecordFields);
|
|
2147
|
+
// A `native-record-ref` never carries its own layout (model.ts): the shape
|
|
2148
|
+
// it names is expanded wherever a `record`/`record-with-index` for that
|
|
2149
|
+
// same struct name is reachable in the plan. When none is, emitting the
|
|
2150
|
+
// struct anyway would mean emitting an empty body for a type that is not
|
|
2151
|
+
// actually empty -- silently wrong C++ that happens to compile. Refusing
|
|
2152
|
+
// names the gap.
|
|
2153
|
+
//
|
|
2154
|
+
// A purely nominal type -- every use going through the `declared` wrapper
|
|
2155
|
+
// in derive.ts, never through a standalone `object` shape -- has no
|
|
2156
|
+
// `record`/`record-with-index` anywhere in `plan.selected` to find, because
|
|
2157
|
+
// nothing independently derives a declared type's own body as its own
|
|
2158
|
+
// result (architecture.md: "the layout is resolved from the sealed
|
|
2159
|
+
// structural table at emission"). That is exactly why this function takes
|
|
2160
|
+
// `types`, not just `plan`: `collectRequiredStructs` hands the deriver built
|
|
2161
|
+
// from it to `recordLayoutOfShape`, which re-derives the named shape's body
|
|
2162
|
+
// directly and closes the gap for the ordinary case. What lands in `missing`
|
|
2163
|
+
// here is the residual the re-derivation itself could not resolve --
|
|
2164
|
+
// `unlayoutable` already names which, by shape id and reason (a field or the
|
|
2165
|
+
// index sidecar bottoming out at `unresolved`, or the body itself not
|
|
2166
|
+
// deriving a record shape at all) -- so emitting the struct anyway would
|
|
2167
|
+
// still mean guessing a layout nothing actually computed. Refusing here
|
|
2168
|
+
// names that gap instead of rendering an empty or partial body that happens
|
|
2169
|
+
// to compile.
|
|
2170
|
+
const missing = [...requiredStructs].filter((structName) => !fieldsByStruct.has(structName)).sort();
|
|
2171
|
+
if (missing.length > 0) {
|
|
2172
|
+
return {
|
|
2173
|
+
declarations: [],
|
|
2174
|
+
fieldDefinitionsByStruct: new Map(),
|
|
2175
|
+
runtimeClassBases: [],
|
|
2176
|
+
refused: missing.map((structName) => ({
|
|
2177
|
+
structName,
|
|
2178
|
+
reason: `no record layout: ${unlayoutable.get(structName) ?? 'named by no reachable shape'}`
|
|
2179
|
+
})),
|
|
2180
|
+
revisionFields: new Map(),
|
|
2181
|
+
celledFields: new Map(),
|
|
2182
|
+
staticMethodStateClasses: new Set()
|
|
2183
|
+
};
|
|
2184
|
+
}
|
|
2185
|
+
const structNames = [...requiredStructs].sort();
|
|
2186
|
+
const classStructNames = new Set([...physicalClasses.values()].map((layout) => cppClassName(layout.declaration)));
|
|
2187
|
+
// Every struct some other struct derives FROM. Its complement is the leaves,
|
|
2188
|
+
// and a leaf is spelled `final` so `gea::Ref<T>::release` can skip the
|
|
2189
|
+
// operations table. `links` is the one place a base clause is decided, so
|
|
2190
|
+
// reading the set off it is what keeps the two from ever disagreeing.
|
|
2191
|
+
const baseStructNames = new Set([...links.values()].map((link) => link.structName));
|
|
2192
|
+
// C++ may put the vptr ahead of a non-polymorphic base when a descendant is
|
|
2193
|
+
// the first class to declare a virtual member. `Ref<Derived> -> Ref<Base>`
|
|
2194
|
+
// cannot tolerate that adjustment because both handles find one allocation
|
|
2195
|
+
// header by subtracting from the object address. Mark every ancestor of an
|
|
2196
|
+
// emitted virtual-dispatch root; `renderStructDefinition` gives ancestors
|
|
2197
|
+
// without their own virtual member a vptr anchor.
|
|
2198
|
+
const refAddressStableStructs = new Set();
|
|
2199
|
+
for (const [root, members] of virtualMembersByStruct) {
|
|
2200
|
+
if (members.length === 0)
|
|
2201
|
+
continue;
|
|
2202
|
+
const walked = new Set();
|
|
2203
|
+
for (let link = links.get(root); link !== undefined && !walked.has(link.structName); link = links.get(link.structName)) {
|
|
2204
|
+
walked.add(link.structName);
|
|
2205
|
+
refAddressStableStructs.add(link.structName);
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
// Forward declarations make ordering irrelevant for a struct held through a
|
|
2209
|
+
// pointer or reference (`shared_ptr<T>`, `T&` -- any non-`owned` ownership
|
|
2210
|
+
// in cppOwnershipWrap, types.ts). They do not make ordering irrelevant for
|
|
2211
|
+
// one held by value: `struct A { struct B b; };` still needs B's full
|
|
2212
|
+
// definition, not just its forward declaration, before A's. Both of those
|
|
2213
|
+
// dependencies -- the base clause and the by-value field -- are what
|
|
2214
|
+
// `definitionOrder` walks; `structNames` stays sorted only so the order is
|
|
2215
|
+
// deterministic where nothing constrains it.
|
|
2216
|
+
const forwardDeclarations = structNames.map((structName) => `struct ${structName};`);
|
|
2217
|
+
const valueDependencies = new Map();
|
|
2218
|
+
for (const [structName, layout] of fieldsByStruct)
|
|
2219
|
+
valueDependencies.set(structName, structValueDependencies(layout));
|
|
2220
|
+
const fieldDefinitionsByStruct = new Map();
|
|
2221
|
+
const dynamicProtocolByStruct = new Map();
|
|
2222
|
+
const fieldOperationsByStruct = new Map();
|
|
2223
|
+
if (reflection?.complete) {
|
|
2224
|
+
for (const [declaration, demand] of reflection.classes)
|
|
2225
|
+
if (demand.fieldOperations)
|
|
2226
|
+
fieldOperationsByStruct.set(cppClassName(declaration), demand.fieldOperations);
|
|
2227
|
+
for (const [shape, demand] of reflection.records)
|
|
2228
|
+
if (demand.fieldOperations)
|
|
2229
|
+
fieldOperationsByStruct.set(cppRecordStructName(shape), demand.fieldOperations);
|
|
2230
|
+
for (const [declaration, demand] of reflection.classes)
|
|
2231
|
+
dynamicProtocolByStruct.set(cppClassName(declaration), demand.level === 'full');
|
|
2232
|
+
for (const [shape, demand] of reflection.records)
|
|
2233
|
+
dynamicProtocolByStruct.set(cppRecordStructName(shape), demand.level === 'full');
|
|
2234
|
+
}
|
|
2235
|
+
// A derived class reaches its dynamic protocol through the hooks its base
|
|
2236
|
+
// emits, so a base keeps the full protocol whenever any descendant needs it,
|
|
2237
|
+
// however little the base itself is used dynamically.
|
|
2238
|
+
for (const structName of classStructNames) {
|
|
2239
|
+
if (dynamicProtocolByStruct.get(structName) === false)
|
|
2240
|
+
continue;
|
|
2241
|
+
let base = links.get(structName);
|
|
2242
|
+
while (base && !base.native) {
|
|
2243
|
+
dynamicProtocolByStruct.set(base.structName, true);
|
|
2244
|
+
base = links.get(base.structName);
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
// A class holds its method state statically when the program evaluates it
|
|
2248
|
+
// once AND it is a root nothing derives from: a derived class's instances
|
|
2249
|
+
// store THEIR evaluation's state into the base's member, so a base with a
|
|
2250
|
+
// descendant needs the member per instance however many times either is
|
|
2251
|
+
// evaluated. `links`/`baseStructNames` are the one authority on both.
|
|
2252
|
+
const declarationByStruct = new Map([...physicalClasses.values()].map((layout) => [cppClassName(layout.declaration), layout.declaration]));
|
|
2253
|
+
const staticMethodStateClasses = new Set();
|
|
2254
|
+
const staticMethodStateStructs = new Set();
|
|
2255
|
+
for (const [structName, declaration] of declarationByStruct) {
|
|
2256
|
+
if (!requiredStructs.has(structName) || baseStructNames.has(structName) || links.has(structName))
|
|
2257
|
+
continue;
|
|
2258
|
+
if (!singleEvaluationClasses.has(declaration))
|
|
2259
|
+
continue;
|
|
2260
|
+
staticMethodStateClasses.add(declaration);
|
|
2261
|
+
staticMethodStateStructs.add(structName);
|
|
2262
|
+
}
|
|
2263
|
+
const rendered = definitionOrder(structNames, links, valueDependencies).map((structName) => {
|
|
2264
|
+
const layout = fieldsByStruct.get(structName);
|
|
2265
|
+
// Unreachable given the check above, and stated rather than assumed: an
|
|
2266
|
+
// entry that got past `missing` with no layout would render a struct with
|
|
2267
|
+
// no body, which compiles and is wrong.
|
|
2268
|
+
if (!layout)
|
|
2269
|
+
return { structName, reason: 'has no layout recorded despite passing the missing-struct check' };
|
|
2270
|
+
const declaration = declarationByStruct.get(structName);
|
|
2271
|
+
const lazyArrowFields = declaration !== undefined ? lazyArrowFieldPlansForClass(classes, declaration) : undefined;
|
|
2272
|
+
// An accessor arm calls a body function, and a struct is defined long
|
|
2273
|
+
// before any body is even forward-declared. So a struct that answers for
|
|
2274
|
+
// one puts its whole field dispatcher out of line, where the names it
|
|
2275
|
+
// needs already exist -- the identical mechanism `--translation-units
|
|
2276
|
+
// per-file` already uses, switched on per struct rather than per file.
|
|
2277
|
+
// Class structs are otherwise excluded: their definitions are placed by
|
|
2278
|
+
// source file rather than into the program unit, and a class's accessors
|
|
2279
|
+
// are answered through `classMemberOf`, not through this layout -- but a
|
|
2280
|
+
// lazy arrow field's dynamic-read arm (`renderFieldDispatcher`'s
|
|
2281
|
+
// `materializeText`) is the SAME "calls a body from inline struct text"
|
|
2282
|
+
// problem an accessor has, for a class this time, and gets the identical
|
|
2283
|
+
// answer: this one struct's dispatcher, and only this one, goes out of
|
|
2284
|
+
// line too, however `splitFieldDefinitions`/`perFile` would otherwise
|
|
2285
|
+
// treat classes.
|
|
2286
|
+
const needsOutOfLineFields = (renderableAccessors(layout).length > 0 && !classStructNames.has(structName)) ||
|
|
2287
|
+
(lazyArrowFields !== undefined && lazyArrowFields.size > 0);
|
|
2288
|
+
const definitions = splitFieldDefinitions || needsOutOfLineFields ? [] : undefined;
|
|
2289
|
+
if (definitions)
|
|
2290
|
+
fieldDefinitionsByStruct.set(structName, definitions);
|
|
2291
|
+
return renderStructDefinition(structName, layout, links.get(structName), reactive, celledByStruct.get(structName) ?? new Set(), narrowedSlots, virtualMembersByStruct.get(structName) ?? [], classStructNames.has(structName), refAddressStableStructs.has(structName), !baseStructNames.has(structName), wellKnownSymbols, definitions, dynamicProtocolByStruct.get(structName) ?? true, fieldOperationsByStruct.get(structName), accessorCarriesEnvironment, fixedFieldStateConstant, staticMethodStateStructs.has(structName), lazyArrowFields);
|
|
2292
|
+
});
|
|
2293
|
+
const refused = rendered.filter((entry) => typeof entry !== 'string');
|
|
2294
|
+
if (refused.length > 0)
|
|
2295
|
+
return {
|
|
2296
|
+
declarations: [],
|
|
2297
|
+
fieldDefinitionsByStruct: new Map(),
|
|
2298
|
+
runtimeClassBases: [],
|
|
2299
|
+
refused,
|
|
2300
|
+
revisionFields: new Map(),
|
|
2301
|
+
celledFields: new Map(),
|
|
2302
|
+
staticMethodStateClasses: new Set()
|
|
2303
|
+
};
|
|
2304
|
+
// `Value` can only project a genuinely dynamic class back to a nominal
|
|
2305
|
+
// carrier when the allocation itself states an authenticated ancestry. The
|
|
2306
|
+
// C++ base clause is not enough after a Ref was erased, and RTTI is not an
|
|
2307
|
+
// available fallback on embedded targets. Publish exactly the checker
|
|
2308
|
+
// proven, emitted-program links to the runtime table; native bases are
|
|
2309
|
+
// intentionally absent because this compiler has no authenticated native
|
|
2310
|
+
// ancestry contract for them.
|
|
2311
|
+
const runtimeClassBases = definitionOrder(structNames, links, valueDependencies).flatMap((structName) => {
|
|
2312
|
+
const link = links.get(structName);
|
|
2313
|
+
if (!classStructNames.has(structName) || !link || link.native || !classStructNames.has(link.structName))
|
|
2314
|
+
return [];
|
|
2315
|
+
return [{ derived: structName, base: link.structName }];
|
|
2316
|
+
});
|
|
2317
|
+
// The same predicate `renderStructDefinition` just applied, split and read
|
|
2318
|
+
// back per struct for the emitter -- see `revisionFields` on the return type
|
|
2319
|
+
// for why it is reported rather than recomputed there. By STRUCT NAME rather
|
|
2320
|
+
// than by class declaration because a reactive struct is no longer only a
|
|
2321
|
+
// class: an element record of a reactive array is one too, and it has no
|
|
2322
|
+
// declaration to be keyed by.
|
|
2323
|
+
const revisionFields = new Map();
|
|
2324
|
+
const celledFields = new Map();
|
|
2325
|
+
for (const [structName, owned] of celledByStruct) {
|
|
2326
|
+
const shape = fieldsByStruct.get(structName);
|
|
2327
|
+
if (!shape)
|
|
2328
|
+
continue;
|
|
2329
|
+
const celled = new Set(shape.fields.filter((field) => owned.has(field.key) && representationCanCell(field.value)).map((field) => field.key));
|
|
2330
|
+
const revisions = new Set(shape.fields.filter((field) => owned.has(field.key) && !representationCanCell(field.value)).map((field) => field.key));
|
|
2331
|
+
if (celled.size > 0)
|
|
2332
|
+
celledFields.set(structName, celled);
|
|
2333
|
+
if (revisions.size > 0)
|
|
2334
|
+
revisionFields.set(structName, revisions);
|
|
2335
|
+
}
|
|
2336
|
+
return {
|
|
2337
|
+
// The extension structs last: a field of one may name a record struct
|
|
2338
|
+
// above, and nothing above names an extension -- the array's own C++ type
|
|
2339
|
+
// does not mention it; only the member bodies reading the fields do.
|
|
2340
|
+
declarations: [
|
|
2341
|
+
...forwardDeclarations,
|
|
2342
|
+
...rendered.filter((entry) => typeof entry === 'string'),
|
|
2343
|
+
...arrayExtensionDeclarations(representations)
|
|
2344
|
+
],
|
|
2345
|
+
fieldDefinitionsByStruct,
|
|
2346
|
+
runtimeClassBases,
|
|
2347
|
+
refused: [],
|
|
2348
|
+
revisionFields,
|
|
2349
|
+
celledFields,
|
|
2350
|
+
staticMethodStateClasses
|
|
2351
|
+
};
|
|
2352
|
+
};
|
|
2353
|
+
/**
|
|
2354
|
+
* One struct per distinct extension an `array-object` carrier anywhere in the
|
|
2355
|
+
* plan declares (`array-object.extension`): the typed fields an interface
|
|
2356
|
+
* adds to the Array it extends, held by the runtime array's own sidecar
|
|
2357
|
+
* (`gea::ArrayObject::extensionFields`, gea_runtime.h). Named by the fields'
|
|
2358
|
+
* identity (`cppArrayExtensionStructName`), so two interfaces with one field
|
|
2359
|
+
* list share one struct; tagged for the runtime's own type check
|
|
2360
|
+
* (`gea::arrayExtensionTagOf`) instead of RTTI, which the embedded targets
|
|
2361
|
+
* build without; traced like a record's fields so a `Ref` a field holds is
|
|
2362
|
+
* reachable to the cycle collector. An optional field carries the same
|
|
2363
|
+
* presence bit a record's does (`cppRecordFieldPresenceName`), false until
|
|
2364
|
+
* the first store, so a read before any write answers `undefined`.
|
|
2365
|
+
*/
|
|
2366
|
+
const arrayExtensionDeclarations = (representations) => {
|
|
2367
|
+
const extensions = new Map();
|
|
2368
|
+
for (const representation of representations) {
|
|
2369
|
+
for (const nested of walkRepresentation(representation)) {
|
|
2370
|
+
if (nested.kind !== 'array-object' || nested.extension === null)
|
|
2371
|
+
continue;
|
|
2372
|
+
const name = cppArrayExtensionStructName(nested.extension);
|
|
2373
|
+
if (!extensions.has(name))
|
|
2374
|
+
extensions.set(name, nested.extension);
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2377
|
+
return [...extensions.entries()]
|
|
2378
|
+
.sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0))
|
|
2379
|
+
.map(([name, fields]) => [
|
|
2380
|
+
`struct ${name} final : gea::ArrayExtension {`,
|
|
2381
|
+
` ${name}() : gea::ArrayExtension(gea::arrayExtensionTagOf<${name}>()) {}`,
|
|
2382
|
+
...fields.map((field) => ` ${cppTypeOf(field.value)} ${cppRecordFieldName(field.key)}{};`),
|
|
2383
|
+
...fields.filter((field) => !field.required).map((field) => ` bool ${cppRecordFieldPresenceName(field.key)} = false;`),
|
|
2384
|
+
' void traceRefs(gea::detail::RefVisitor& visitor) const override {',
|
|
2385
|
+
...fields.map((field) => ` gea::detail::traceRefs(${cppRecordFieldName(field.key)}, visitor);`),
|
|
2386
|
+
' }',
|
|
2387
|
+
'};'
|
|
2388
|
+
].join('\n'));
|
|
2389
|
+
};
|
|
2390
|
+
/**
|
|
2391
|
+
* Real `static` storage for the whole program's `ClassName.KEY = value`
|
|
2392
|
+
* sites (`class-layout.ts`'s `censusClassStaticFieldStorage`), as bare
|
|
2393
|
+
* translation-unit-scope definitions -- zero-initialized, exactly the way
|
|
2394
|
+
* `translation-unit.ts`'s own `globalDefinitions` declares a module-scope
|
|
2395
|
+
* cell's storage. A bare definition rather than one with an inline
|
|
2396
|
+
* initializer on purpose: the ACTUAL value (`new Vector3(0, 1, 0)`, a
|
|
2397
|
+
* `WeakMap`-closed-over reference, ...) is whatever expression the source
|
|
2398
|
+
* assignment itself computed, and that expression already runs -- once, at
|
|
2399
|
+
* the point the enclosing module body reaches that statement -- through the
|
|
2400
|
+
* ordinary `[[Set]]` this storage exists to give a home to. Initializing it a
|
|
2401
|
+
* second time here would run that expression twice, or (for one with a
|
|
2402
|
+
* side effect, or one that reads another module-scope cell not yet set)
|
|
2403
|
+
* run it too EARLY.
|
|
2404
|
+
*
|
|
2405
|
+
* Emitted after every struct declaration and before any body, mirroring
|
|
2406
|
+
* `translation-unit.ts`'s own ordering for `globalDefinitions`: a static
|
|
2407
|
+
* whose declared carrier is itself a struct (`Object3D.DEFAULT_UP`'s
|
|
2408
|
+
* `Vector3`) needs that struct to already be a complete type.
|
|
2409
|
+
*
|
|
2410
|
+
* Sorted by name for the same reason every other whole-program list in this
|
|
2411
|
+
* file is: byte-identical output from one compile to the next, so a diff
|
|
2412
|
+
* between two builds is never just iteration order.
|
|
2413
|
+
*/
|
|
2414
|
+
export const classStaticFieldDefinitions = (storage) => classStaticFieldStorageRows(storage).map((row) => `${row.type} ${row.name};`);
|
|
2415
|
+
/**
|
|
2416
|
+
* The same storage as (type, name) pairs, for a layout that has to spell each
|
|
2417
|
+
* one twice -- `extern` in a shared header, defined in exactly one unit.
|
|
2418
|
+
*/
|
|
2419
|
+
export const classStaticFieldStorageRows = (storage) => {
|
|
2420
|
+
const rows = [];
|
|
2421
|
+
for (const perClass of storage.values())
|
|
2422
|
+
rows.push(...perClass.values());
|
|
2423
|
+
return rows
|
|
2424
|
+
.filter((entry) => entry.representation.kind !== 'unresolved' && entry.representation.kind !== 'void')
|
|
2425
|
+
.sort((left, right) => left.name.localeCompare(right.name))
|
|
2426
|
+
.map((entry) => ({ type: cppTypeOf(entry.representation), name: entry.name }));
|
|
2427
|
+
};
|