@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,2225 @@
|
|
|
1
|
+
import { objectTagExpression, objectTagCapability } from './emit-object-tag.js';
|
|
2
|
+
import { stableBorrowActualsOf } from '../../ir/borrowed-call-arguments.js';
|
|
3
|
+
import { owningConversionInputText } from './owning-conversion-input.js';
|
|
4
|
+
import { boxedValueText } from './emit-dynamic-properties.js';
|
|
5
|
+
import { ownedDyingValuesOf, ownedFormalInputsOf, transfersFormalConversion } from '../../ir/transfer.js';
|
|
6
|
+
import { constructedBaseOf } from '../../projection/classes.js';
|
|
7
|
+
import { allOperationsOf } from '../../ir/model.js';
|
|
8
|
+
import { resultOfIrOperation } from '../../ir/queries.js';
|
|
9
|
+
import { representationKey } from '../../representation/model.js';
|
|
10
|
+
import { cppFormalName, cppReceiverName, createCppEmitBlockedError, createEmitContext, defineValue, defineValueAlias, directCalleesOf, emptyCaptureIndex, isIntegerStorageValue, isCppEmitBlockedError, operandText, storageTypeOf, cppThunkName, sealFactFieldsForRender } from './emit-context.js';
|
|
11
|
+
import { hostMemberReadsOf } from './host/emit-host-properties.js';
|
|
12
|
+
import { hostNamespaceReadsOf } from './host-namespace-reads.js';
|
|
13
|
+
import { functionSourceReadsOf } from './function-source-reads.js';
|
|
14
|
+
import { prototypeMethodReadsOf } from './prototype/prototype-method-reads.js';
|
|
15
|
+
import { directCallReceiversOf, virtualCalleesOf } from './direct-call-receivers.js';
|
|
16
|
+
import { unionMemberTypeofReadsOf, unionMethodReadsOf } from './emit-union-properties.js';
|
|
17
|
+
import { reactiveOriginsOf } from './reactive-origins.js';
|
|
18
|
+
import { renderTryRegion } from './emit-exceptions.js';
|
|
19
|
+
import { emitReturn } from './emit-return.js';
|
|
20
|
+
import { collectDirectBindingSinks, classObjectReadsOf, hostClassReadsOf, collectFormalCells, earlyCapturedCellPrologue, emitBindingRead, emitBindingWrite } from './emit-bindings.js';
|
|
21
|
+
import { alignedValueText, emitMergeLiveArmRebuild, namedConversionText, widenedStoreText } from './emit-narrowing.js';
|
|
22
|
+
import { admitDenseWindows, collectCapacityHints, emitAllocateArrayObject, emitDenseSetup, emitFillLoop } from './emit-arrays.js';
|
|
23
|
+
import { noInstantiationFacts } from '../../ir/instantiation.js';
|
|
24
|
+
import { observesEveryCallableIdentity } from '../../ir/callable-identity-demand.js';
|
|
25
|
+
import { stringConstantsOf } from '../../ir/dead-values.js';
|
|
26
|
+
import { bodyValueOriginsOf, irBodyCensusOf } from '../../ir/facts.js';
|
|
27
|
+
import { emitGetIterator, emitIteratorClose, emitIteratorDone, emitIteratorNext, renderIteratorCloseRegion } from './emit-iterator.js';
|
|
28
|
+
import { absenceComparisonText, booleanTestText, definedTestText, presenceTestText } from './emit-presence.js';
|
|
29
|
+
import { callableIdentityEqualityText, constantStringComparisonText, strictEqualityText } from './emit-equality.js';
|
|
30
|
+
import { nativeEqualityText } from './emit-native-equality.js';
|
|
31
|
+
import { denseRemainderCompanion, integerBitwiseOperators, integerBoundedComparison, remainderText } from './emit-integers.js';
|
|
32
|
+
import { instanceofText } from './emit-instanceof.js';
|
|
33
|
+
import { typeofTagLiteral, typeofTagText, typeofText, typeofTextFor, unionMemberTypeofTagText, unionMemberTypeofText } from './emit-typeof.js';
|
|
34
|
+
import { emitHasProperty } from './emit-in.js';
|
|
35
|
+
import { mergeWritesOf } from './emit-namespaces.js';
|
|
36
|
+
import { templateText, toStringRefusal } from './emit-tostring.js';
|
|
37
|
+
import { mixedDynamicPlusText } from './emit-mixed-binary.js';
|
|
38
|
+
import { emitFieldStore, emitGet, isPlainMemberRead } from './emit-properties.js';
|
|
39
|
+
import { directClassMethodBody } from './class-properties/emit-class-properties.js';
|
|
40
|
+
import { classMemberOf, lazyCalleeReadsOf, structNameOfReceiver } from './class-layout.js';
|
|
41
|
+
import { emitAllocateRecord, emitAllocateRegExp, emitAllocateTemplateObject, emitToNumericCoercion, emitSpreadCopy } from './emit-allocation.js';
|
|
42
|
+
import { alignedText, receiverBoundCallableText, emitAllocateCallable, emitAllocateConstructor, emitBindCallable, emitCall, emitConstruct, emitSuperInitialize } from './emit-callable.js';
|
|
43
|
+
import { cppClassName, cppCommonJsModuleName, cppCommonJsRecordName, cppConstantLiteral, cppStringLiteral, cppTypeOf, cppUndefinedValue } from './types.js';
|
|
44
|
+
import { awaitedText } from './prototype/emit-prototype-promise.js';
|
|
45
|
+
import { classTableRootsOf, emitElement, emitElementChild, emitElementProp } from './emit-jsx.js';
|
|
46
|
+
import { emitDeleteOperation, emitUnaryDelete } from './emit-dynamic-properties.js';
|
|
47
|
+
/**
|
|
48
|
+
* The execution region this section belongs to.
|
|
49
|
+
*
|
|
50
|
+
* `IrBody` carries it directly (`sourceOwner`), which is the only honest route:
|
|
51
|
+
* the `PhysicalBodyId` folds an owner and a variant key into one string, and
|
|
52
|
+
* taking that apart here would make this file a second authority over a
|
|
53
|
+
* structure `identity/ids.ts` alone defines -- the same objection that forbids
|
|
54
|
+
* recovering a decision by parsing rendered C++.
|
|
55
|
+
*/
|
|
56
|
+
const sectionOwnerOf = (body) => body.sourceOwner;
|
|
57
|
+
const emitConstant = (ctx, lines, operation) => {
|
|
58
|
+
// Already spelled at every use by `spellConstants`; nothing is left to render.
|
|
59
|
+
if (ctx.deferredTexts.has(operation.result.id))
|
|
60
|
+
return;
|
|
61
|
+
const name = defineValue(ctx, operation.result);
|
|
62
|
+
ctx.constantTexts.set(operation.result.id, operation.text);
|
|
63
|
+
lines.push(`${name} = ${cppConstantLiteral(operation.text, operation.literal, operation.result.representation)};`);
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Every literal of a body, spelled inline at each of its uses instead of once
|
|
67
|
+
* into a temporary.
|
|
68
|
+
*
|
|
69
|
+
* A literal reads no state and has no operands, so the two conditions the
|
|
70
|
+
* deferral census imposes on everything else -- one use, in the same block --
|
|
71
|
+
* protect nothing here: the text `("cities")` means the same thing wherever
|
|
72
|
+
* it lands, and there is no definition a `goto` could skip. What a temporary
|
|
73
|
+
* cost was real: a string literal used three times was one construction and
|
|
74
|
+
* two copies (three allocations) where three inline spellings are three
|
|
75
|
+
* constructions (three allocations, no worse), and a literal whose uses are
|
|
76
|
+
* all member keys -- spelled `->cities` by the access, never as text -- was
|
|
77
|
+
* a temporary nothing read at all. `examples/apps/weather` carried 142 of
|
|
78
|
+
* those. Recorded before any block renders so a use in an earlier block of
|
|
79
|
+
* `blockOrder` than its definition still finds the text.
|
|
80
|
+
*/
|
|
81
|
+
const spellConstants = (ctx, body) => {
|
|
82
|
+
for (const block of body.blocks.values()) {
|
|
83
|
+
for (const operation of block.operations) {
|
|
84
|
+
if (operation.kind !== 'constant')
|
|
85
|
+
continue;
|
|
86
|
+
ctx.constantTexts.set(operation.result.id, operation.text);
|
|
87
|
+
ctx.deferredTexts.set(operation.result.id, `(${cppConstantLiteral(operation.text, operation.literal, operation.result.representation)})`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* The C++ spelling of one language operator on one carrier.
|
|
93
|
+
*
|
|
94
|
+
* Keyed by the carrier as well as the operator, because the same token is a
|
|
95
|
+
* different machine operation on different carriers -- and on several carriers
|
|
96
|
+
* it is not a machine operation at all. `null` means no spelling exists here,
|
|
97
|
+
* which is refused rather than approximated.
|
|
98
|
+
*
|
|
99
|
+
* `%` and `**` have no infix spelling on `double` in C++, so they are library
|
|
100
|
+
* calls (`gea::remainder`/`std::pow`, below) -- and so are `&`/`|`/`^`/`<<`/`>>`/
|
|
101
|
+
* `>>>`, for a different reason: ECMAScript defines every one of them through
|
|
102
|
+
* `ToInt32`/`ToUint32` (ECMA-262 7.1.6/7.1.7) of the operand, never through the
|
|
103
|
+
* IEEE-754 double the plan actually carries. `a & b` truncates and wraps each
|
|
104
|
+
* operand into a 32-bit two's-complement integer first (ECMA-262 6.1.6.1.16-19
|
|
105
|
+
* `NumberBitwiseOp`/`Number::bitwiseAND`/`bitwiseXOR`/`bitwiseOR`; 6.1.6.1.2
|
|
106
|
+
* `bitwiseNOT` for `~`), and the three shifts additionally reduce the shift
|
|
107
|
+
* count with `ToUint32(rhs) modulo 32` (6.1.6.1.9-11 `leftShift`/
|
|
108
|
+
* `signedRightShift`/`unsignedRightShift`) before shifting. A bare C++ `&`/
|
|
109
|
+
* `<<` on `double` would not even compile, and doing the ToInt32/ToUint32
|
|
110
|
+
* conversion inline at each of these six call sites would be six copies of
|
|
111
|
+
* the identical wraparound to keep in step, so `gea::toInt32`/`gea::toUint32`
|
|
112
|
+
* and the `gea::bitwise*`/`*Shift` wrappers built from them live once in
|
|
113
|
+
* `runtime/gea_runtime.h` -- reusing the identical modulo-2**32 wraparound the
|
|
114
|
+
* TypedArray element store already performs there
|
|
115
|
+
* (`detail::typedArrayIntegerModulo`) -- and are called here as
|
|
116
|
+
* `CppOperatorSpelling`s of kind `'call'`, exactly like `gea::remainder`/`std::pow`.
|
|
117
|
+
*
|
|
118
|
+
* Only the `number`/`float64` domain gets a bitwise spelling. `bigint` looks
|
|
119
|
+
* like it belongs in the same table -- ECMAScript spells the identical six
|
|
120
|
+
* tokens over it -- but it is a genuinely different operation: arbitrary-
|
|
121
|
+
* precision two's complement, no `ToInt32` anywhere in the algorithm
|
|
122
|
+
* (ECMA-262 6.1.6.2.2/.9/.18-19 `BigInt::bitwiseNOT`/`leftShift`/`bitwiseAND`/
|
|
123
|
+
* `bitwiseOR`), and `ApplyStringOrNumericBinaryOperator` (13.15.3) step 5
|
|
124
|
+
* throws a `TypeError` rather than ever converting a `BigInt` through the
|
|
125
|
+
* `Number` path. So it stays refused here by the same `null` this table
|
|
126
|
+
* already returns for every other unimplemented `bigint` operator, not routed
|
|
127
|
+
* through `ToInt32` as an approximation -- which would silently truncate any
|
|
128
|
+
* BigInt outside the 32-bit range. `int32`/`uint32` are listed in
|
|
129
|
+
* `ScalarDomain` but `representation/derive.ts` never actually selects either
|
|
130
|
+
* for an ordinary value -- every plain `number` reaches this table as domain
|
|
131
|
+
* `'number'` -- so there is, today, no carrier for which the round trip could
|
|
132
|
+
* be skipped; see `.scratch/port/bitwise/citations.md` section 5.
|
|
133
|
+
*
|
|
134
|
+
* `===` maps to `==` and `!==` to `!=`: on a single, non-dynamic carrier the
|
|
135
|
+
* strict comparison has no type check left to perform, because both sides were
|
|
136
|
+
* already proven to hold the same carrier before this is reached.
|
|
137
|
+
*
|
|
138
|
+
* `==` and `!=` map to the same spellings, and for the same reason -- not as a
|
|
139
|
+
* shortcut, but because that identical-carrier proof is precisely the premise
|
|
140
|
+
* ECMA-262 7.2.15 (IsLooselyEqual) tests first: "If Type(x) is Type(y), return
|
|
141
|
+
* IsStrictlyEqual(x, y)". Loose equality only diverges from strict when it has
|
|
142
|
+
* a type difference to coerce across, and `emitBinary` refuses a mixed-carrier
|
|
143
|
+
* operator by name immediately above this lookup, so no such pair can reach a
|
|
144
|
+
* table. The one other divergence -- `x == null` matching both `null` and
|
|
145
|
+
* `undefined` -- never reaches here either: `absenceComparisonText` settles a
|
|
146
|
+
* comparison against an absent value earlier, and distinguishes the loose form
|
|
147
|
+
* from the strict one itself (`emit-presence.ts`). What is left is the case the
|
|
148
|
+
* spec collapses, so collapsing it here is the language's own answer rather
|
|
149
|
+
* than this emitter's approximation of it.
|
|
150
|
+
*/
|
|
151
|
+
const numericBinaryOperators = new Map([
|
|
152
|
+
['+', { kind: 'infix', text: '+' }],
|
|
153
|
+
['-', { kind: 'infix', text: '-' }],
|
|
154
|
+
['*', { kind: 'infix', text: '*' }],
|
|
155
|
+
['/', { kind: 'infix', text: '/' }],
|
|
156
|
+
['%', { kind: 'call', text: 'gea::remainder' }],
|
|
157
|
+
['**', { kind: 'call', text: 'std::pow' }],
|
|
158
|
+
['<', { kind: 'infix', text: '<' }],
|
|
159
|
+
['>', { kind: 'infix', text: '>' }],
|
|
160
|
+
['<=', { kind: 'infix', text: '<=' }],
|
|
161
|
+
['>=', { kind: 'infix', text: '>=' }],
|
|
162
|
+
['===', { kind: 'infix', text: '==' }],
|
|
163
|
+
['!==', { kind: 'infix', text: '!=' }],
|
|
164
|
+
['==', { kind: 'infix', text: '==' }],
|
|
165
|
+
['!=', { kind: 'infix', text: '!=' }],
|
|
166
|
+
// ECMA-262 6.1.6.1.16-19: both operands go through ToInt32 (7.1.6), the 32-bit
|
|
167
|
+
// two's-complement bits combine, and the result widens back to Number as signed.
|
|
168
|
+
['&', { kind: 'call', text: 'gea::bitwiseAnd' }],
|
|
169
|
+
['|', { kind: 'call', text: 'gea::bitwiseOr' }],
|
|
170
|
+
['^', { kind: 'call', text: 'gea::bitwiseXor' }],
|
|
171
|
+
// ECMA-262 6.1.6.1.9-11: the shift count is ToUint32(rhs) (7.1.7) modulo 32;
|
|
172
|
+
// `<<`/`>>` keep the left signed (ToInt32), `>>>` makes it unsigned (ToUint32)
|
|
173
|
+
// -- the one place these diverge from AND/OR/XOR, so each gets its own wrapper.
|
|
174
|
+
['<<', { kind: 'call', text: 'gea::leftShift' }],
|
|
175
|
+
['>>', { kind: 'call', text: 'gea::signedRightShift' }],
|
|
176
|
+
['>>>', { kind: 'call', text: 'gea::unsignedRightShift' }]
|
|
177
|
+
]);
|
|
178
|
+
/** The relational operators `mixedRelationalText` answers -- IsLessThan's own four spellings, never `===`/`==` (a different rule, `emit-equality.ts`). */
|
|
179
|
+
const relationalOperators = new Set(['<', '>', '<=', '>=']);
|
|
180
|
+
const stringBinaryOperators = new Map([
|
|
181
|
+
['+', { kind: 'infix', text: '+' }],
|
|
182
|
+
['<', { kind: 'infix', text: '<' }],
|
|
183
|
+
['>', { kind: 'infix', text: '>' }],
|
|
184
|
+
['<=', { kind: 'infix', text: '<=' }],
|
|
185
|
+
['>=', { kind: 'infix', text: '>=' }],
|
|
186
|
+
['===', { kind: 'infix', text: '==' }],
|
|
187
|
+
['!==', { kind: 'infix', text: '!=' }],
|
|
188
|
+
['==', { kind: 'infix', text: '==' }],
|
|
189
|
+
['!=', { kind: 'infix', text: '!=' }]
|
|
190
|
+
]);
|
|
191
|
+
/**
|
|
192
|
+
* Symbols compare and do nothing else.
|
|
193
|
+
*
|
|
194
|
+
* `<`/`>`/`+` on a symbol are TypeErrors in the language, so they have no
|
|
195
|
+
* spelling here rather than a wrong one. `==` is listed beside `===` because
|
|
196
|
+
* this table is only ever reached with BOTH sides already carrying `symbol`
|
|
197
|
+
* (`emitCompute` refuses a mixed-carrier operator above it), and for two
|
|
198
|
+
* symbols the loose and strict comparisons are the same operation.
|
|
199
|
+
*/
|
|
200
|
+
const symbolBinaryOperators = new Map([
|
|
201
|
+
['===', { kind: 'infix', text: '==' }],
|
|
202
|
+
['!==', { kind: 'infix', text: '!=' }],
|
|
203
|
+
['==', { kind: 'infix', text: '==' }],
|
|
204
|
+
['!=', { kind: 'infix', text: '!=' }]
|
|
205
|
+
]);
|
|
206
|
+
/**
|
|
207
|
+
* `||` is here and `logical` is not the form that reaches it.
|
|
208
|
+
*
|
|
209
|
+
* A source `a || b` is a `logical` computation -- ECMA-262 13.13.1 evaluates
|
|
210
|
+
* the right operand only when the left is falsy -- and `ir/lower.ts` lowers
|
|
211
|
+
* that one through a merge of two arms, never through this table. What arrives
|
|
212
|
+
* here as a `binary` `||` is a disjunction whose operands are both already
|
|
213
|
+
* evaluated values, which is what `producers/control.ts` mints to give a group
|
|
214
|
+
* of `case` clauses one guard. Rendering it `a || b` is exact: C++ would
|
|
215
|
+
* short-circuit a computation that has already happened.
|
|
216
|
+
*/
|
|
217
|
+
const booleanBinaryOperators = new Map([
|
|
218
|
+
['===', { kind: 'infix', text: '==' }],
|
|
219
|
+
['!==', { kind: 'infix', text: '!=' }],
|
|
220
|
+
['==', { kind: 'infix', text: '==' }],
|
|
221
|
+
['!=', { kind: 'infix', text: '!=' }],
|
|
222
|
+
['||', { kind: 'infix', text: '||' }]
|
|
223
|
+
]);
|
|
224
|
+
/**
|
|
225
|
+
* ECMAScript OBJECT IDENTITY, for the two carriers that are exactly a pointer.
|
|
226
|
+
*
|
|
227
|
+
* `IsStrictlyEqual` on two objects (ECMA-262 7.2.16, via SameValueNonNumber
|
|
228
|
+
* 7.2.12) is "the same object reference", and both of these carriers are a
|
|
229
|
+
* `std::shared_ptr` to an object nothing copies behind the program's back --
|
|
230
|
+
* so `==` on the pointers answers precisely that question, with no conversion
|
|
231
|
+
* invented. `view.buffer === buffer` is the idiom this exists for: it is how a
|
|
232
|
+
* program asks whether two views alias the same block, and the only way to ask
|
|
233
|
+
* it that does not involve writing through one view and reading another.
|
|
234
|
+
*
|
|
235
|
+
* It is deliberately NOT generalized to every reference-shaped carrier.
|
|
236
|
+
* `borrowed-ref` and `native-record-ref` are views this compiler mints rather
|
|
237
|
+
* than objects the program created, and a `record` may be carried by value --
|
|
238
|
+
* pointer identity on any of those would answer a different question than the
|
|
239
|
+
* language asks, so they keep refusing by name.
|
|
240
|
+
*/
|
|
241
|
+
const referenceIdentityOperators = new Map([
|
|
242
|
+
['===', { kind: 'infix', text: '==' }],
|
|
243
|
+
['!==', { kind: 'infix', text: '!=' }],
|
|
244
|
+
['==', { kind: 'infix', text: '==' }],
|
|
245
|
+
['!=', { kind: 'infix', text: '!=' }]
|
|
246
|
+
]);
|
|
247
|
+
const binaryOperatorFor = (operator, carrier) => {
|
|
248
|
+
if (carrier.kind === 'string')
|
|
249
|
+
return stringBinaryOperators.get(operator) ?? null;
|
|
250
|
+
// `CallableObject` stores the ECMAScript function allocation's stable
|
|
251
|
+
// identity, and its `operator==` compares exactly that identity. Copies and
|
|
252
|
+
// ABI adapters preserve it, while separately evaluated capture-free arrows
|
|
253
|
+
// receive distinct identities. The carrier therefore owns the same strict
|
|
254
|
+
// and loose same-type equality rule as Symbol and the pointer-backed object
|
|
255
|
+
// carriers below.
|
|
256
|
+
if (carrier.kind === 'function-value-dispatch')
|
|
257
|
+
return referenceIdentityOperators.get(operator) ?? null;
|
|
258
|
+
// The identity carrier IS that stable identity, reached through the same
|
|
259
|
+
// handle -- so equality on it is the very comparison the paragraph above
|
|
260
|
+
// describes, with the convention stripped away.
|
|
261
|
+
if (carrier.kind === 'callable-identity')
|
|
262
|
+
return referenceIdentityOperators.get(operator) ?? null;
|
|
263
|
+
// Two references to one generic source function are one function object,
|
|
264
|
+
// and the set's tag IS which function: equal tags, same function.
|
|
265
|
+
if (carrier.kind === 'generic-function-set')
|
|
266
|
+
return referenceIdentityOperators.get(operator) ?? null;
|
|
267
|
+
// A native handle with no host-stated carrier is `gea::NativeHandle<Tag>`,
|
|
268
|
+
// whose whole content is the `int id_` indexing the native store -- so two
|
|
269
|
+
// handles name the same host object exactly when their ids agree, and `==`
|
|
270
|
+
// over that id is the entirety of SameValueNonNumeric for the carrier. This
|
|
271
|
+
// is what answers `const m = Math; m === Math` for a namespace-shaped
|
|
272
|
+
// protocol, where the handle has exactly one inhabitant.
|
|
273
|
+
//
|
|
274
|
+
// A protocol whose plugin DID state a carrier is spelled as that host type
|
|
275
|
+
// instead (`cppTypeOf`'s `representation.native ?? ...`), and this emitter
|
|
276
|
+
// has been told nothing about its equality -- so it keeps refusing rather
|
|
277
|
+
// than emitting a comparison whose meaning belongs to the host.
|
|
278
|
+
if (carrier.kind === 'native-handle')
|
|
279
|
+
return carrier.native === null ? (referenceIdentityOperators.get(operator) ?? null) : null;
|
|
280
|
+
if (carrier.kind === 'array-buffer' ||
|
|
281
|
+
carrier.kind === 'shared-array-buffer' ||
|
|
282
|
+
carrier.kind === 'data-view' ||
|
|
283
|
+
(carrier.kind === 'typed-array' && carrier.ownership === 'shared-refcount'))
|
|
284
|
+
return referenceIdentityOperators.get(operator) ?? null;
|
|
285
|
+
if (carrier.kind === 'symbol')
|
|
286
|
+
return symbolBinaryOperators.get(operator) ?? null;
|
|
287
|
+
if (carrier.kind !== 'scalar')
|
|
288
|
+
return null;
|
|
289
|
+
if (carrier.domain === 'boolean')
|
|
290
|
+
return booleanBinaryOperators.get(operator) ?? null;
|
|
291
|
+
if (carrier.domain === 'bigint') {
|
|
292
|
+
if (operator === '**')
|
|
293
|
+
return { kind: 'call', text: 'gea::BigInt::pow' };
|
|
294
|
+
if (operator === '>>>')
|
|
295
|
+
return null;
|
|
296
|
+
const spelling = numericBinaryOperators.get(operator);
|
|
297
|
+
return spelling ? { kind: 'infix', text: ['%', '&', '|', '^', '<<', '>>'].includes(operator) ? operator : spelling.text } : null;
|
|
298
|
+
}
|
|
299
|
+
if (carrier.domain === 'number' || carrier.domain === 'float64')
|
|
300
|
+
return numericBinaryOperators.get(operator) ?? null;
|
|
301
|
+
// int32/uint32/bigint have exact range semantics that a bare C++ operator does
|
|
302
|
+
// not reproduce (wrapping, promotion, arbitrary precision); no spelling here.
|
|
303
|
+
return null;
|
|
304
|
+
};
|
|
305
|
+
const updateStepOperators = new Map([
|
|
306
|
+
['++', '+'],
|
|
307
|
+
['--', '-']
|
|
308
|
+
]);
|
|
309
|
+
const numericUnaryOperators = new Map([
|
|
310
|
+
['-', { kind: 'infix', text: '-' }],
|
|
311
|
+
['+', { kind: 'infix', text: '+' }],
|
|
312
|
+
// ECMA-262 6.1.6.1.2 Number::bitwiseNOT: ToInt32(x) (7.1.6), bitwise
|
|
313
|
+
// complement, widened back to Number as signed -- the same round trip
|
|
314
|
+
// `gea::bitwiseAnd`/... perform, so it goes through the identical helper.
|
|
315
|
+
['~', { kind: 'call', text: 'gea::bitwiseNot' }]
|
|
316
|
+
]);
|
|
317
|
+
const unaryOperatorFor = (operator, carrier) => {
|
|
318
|
+
if (carrier.kind === 'scalar' && carrier.domain === 'bigint')
|
|
319
|
+
return operator === '-' || operator === '~' ? { kind: 'infix', text: operator } : null;
|
|
320
|
+
if (carrier.kind === 'scalar' && carrier.domain === 'boolean')
|
|
321
|
+
return operator === '!' ? { kind: 'infix', text: '!' } : null;
|
|
322
|
+
if (carrier.kind === 'scalar' && (carrier.domain === 'number' || carrier.domain === 'float64')) {
|
|
323
|
+
return numericUnaryOperators.get(operator) ?? null;
|
|
324
|
+
}
|
|
325
|
+
return null;
|
|
326
|
+
};
|
|
327
|
+
const emitCompute = (ctx, lines, operation) => {
|
|
328
|
+
// `ctx.computeOrigins` already names this operation for this result --
|
|
329
|
+
// settled in `EmitBodyFacts` before any spelling is chosen, so
|
|
330
|
+
// `emit-bindings.ts` can recognize a cell's own compound update regardless
|
|
331
|
+
// of which of the returns below spells this same operation.
|
|
332
|
+
const queryComparison = ctx.typeQueryComparisons.get(operation);
|
|
333
|
+
if (queryComparison) {
|
|
334
|
+
const left = queryComparison.left.kind === 'tag' ? operandText(ctx, queryComparison.left.operand) : typeofTagLiteral(queryComparison.left.text);
|
|
335
|
+
const right = queryComparison.right.kind === 'tag' ? operandText(ctx, queryComparison.right.operand) : typeofTagLiteral(queryComparison.right.text);
|
|
336
|
+
const answer = left === null || right === null ? String(queryComparison.unequal) : `${left} ${queryComparison.unequal ? '!=' : '=='} ${right}`;
|
|
337
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${answer};`);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
const operands = operation.operands;
|
|
341
|
+
if (operation.nativeEquality) {
|
|
342
|
+
const left = operands[0];
|
|
343
|
+
const right = operands[1];
|
|
344
|
+
if (!left || !right)
|
|
345
|
+
throw createCppEmitBlockedError('runtime-helper:computation:binary:arity', 'a native equality recipe requires two operands');
|
|
346
|
+
const answer = nativeEqualityText(operation.nativeEquality, [operandText(ctx, left), operandText(ctx, right)]);
|
|
347
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${answer};`);
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
// `void x` evaluates its operand and yields `undefined` (ECMA-262 13.5.2).
|
|
351
|
+
// Nothing about that answer depends on what the operand carried, so it is
|
|
352
|
+
// settled before any carrier lookup -- like `!` below, and unlike `-`/`+`,
|
|
353
|
+
// which run ToNumber and so must consult one. The operand's own evaluation
|
|
354
|
+
// is already its own operation in this graph, so discarding its value here
|
|
355
|
+
// is the whole of the semantics -- which is also why this is the one form
|
|
356
|
+
// that legitimately arrives with NO operand at all: `void f()` for an `f`
|
|
357
|
+
// that returns nothing has no SSA name to cite, and the lowering cites none
|
|
358
|
+
// rather than inventing one (`ir/lower.ts`).
|
|
359
|
+
if (operation.form === 'unary' && operation.operator === 'void') {
|
|
360
|
+
const produced = operation.result.representation;
|
|
361
|
+
if (produced.kind !== 'undefined') {
|
|
362
|
+
throw createCppEmitBlockedError(`physical-cpp-type:${representationKey(produced)}`, `unary "void" published a "${produced.kind}" carrier, but the language gives the expression undefined`);
|
|
363
|
+
}
|
|
364
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${cppUndefinedValue};`);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
const first = operands[0];
|
|
368
|
+
if (!first)
|
|
369
|
+
throw createCppEmitBlockedError('runtime-helper:computation:no-operands', 'a computation with no operands has no C++ spelling');
|
|
370
|
+
if (operation.form === 'require-object-coercible') {
|
|
371
|
+
const text = operandText(ctx, first);
|
|
372
|
+
lines.push(`if (!(${presenceTestText(text, first.representation)})) gea::host::throwGetPropertyOfNullish<void>();`);
|
|
373
|
+
defineValueAlias(ctx, operation.result, text);
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
// The presence half of an array pattern's own possibly-absent source
|
|
377
|
+
// (`ir/lower-destructuring.ts`'s `lowerArrayPatternSource`): the identical
|
|
378
|
+
// `gea::detail::requireIterablePresent` the general `for`-`of` protocol's
|
|
379
|
+
// own absent sources already call in front of their cursor constructor
|
|
380
|
+
// (`emit-iterator.ts`), reused here in front of the array-pattern fast
|
|
381
|
+
// path's positional reads instead.
|
|
382
|
+
if (operation.form === 'require-iterable-present') {
|
|
383
|
+
const text = operandText(ctx, first);
|
|
384
|
+
const name = defineValue(ctx, operation.result);
|
|
385
|
+
lines.push(`${name} = gea::detail::requireIterablePresent(${text}, ${cppStringLiteral('a nested array-destructuring position')});`);
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
// The arm half of the same source, once unwrapped: `soleArrayPatternCapableArm`
|
|
389
|
+
// (`representation/model.ts`) proved exactly one arm of this tagged union
|
|
390
|
+
// supports the pattern's positional reads at compile time, but which VALUE
|
|
391
|
+
// this program actually holds at runtime is still an open question -- the
|
|
392
|
+
// other arm is a real value ECMA-262 7.4.2's `GetIterator` would throw a
|
|
393
|
+
// `TypeError` over, and this is that throw. The arm index was carried
|
|
394
|
+
// through the `operator` field as a decimal string (see `ir/model.ts`'s
|
|
395
|
+
// own doc for why) rather than a real language operator, since `.is<N>()`/
|
|
396
|
+
// `.get<N>()` need it as a compile-time template argument.
|
|
397
|
+
if (operation.form === 'require-tagged-union-arm') {
|
|
398
|
+
const text = operandText(ctx, first);
|
|
399
|
+
const index = operation.operator;
|
|
400
|
+
const name = defineValue(ctx, operation.result);
|
|
401
|
+
lines.push(`if (!(${text}).is<${index}>()) gea::detail::refuseTaggedUnionArmMismatch(${cppStringLiteral('a nested array-destructuring position')});`);
|
|
402
|
+
lines.push(`${name} = (${text}).get<${index}>();`);
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
if (operation.form === 'unary') {
|
|
406
|
+
if (operation.operator === 'ObjectTag') {
|
|
407
|
+
const answer = objectTagExpression(first.representation, operandText(ctx, first), ctx);
|
|
408
|
+
if (answer === null)
|
|
409
|
+
throw createCppEmitBlockedError(`runtime-helper:${objectTagCapability(first.representation)}`, 'Object.prototype.toString has no native tag algorithm for this carrier');
|
|
410
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${answer};`);
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
// `!x` is `ToBoolean(x)` negated, over whatever carrier `x` arrived in --
|
|
414
|
+
// the same abstract operation a conditional's guard runs, so it is spelled
|
|
415
|
+
// by the same authority rather than by a second rule that could disagree
|
|
416
|
+
// with it. Only `!` is a coercion this way: `-x`/`+x` run ToNumber, which
|
|
417
|
+
// is a different table and stays on the operator map below.
|
|
418
|
+
if (operation.operator === '!') {
|
|
419
|
+
const name = defineValue(ctx, operation.result);
|
|
420
|
+
lines.push(`${name} = !${booleanTestText(operandText(ctx, first), first.representation, isIntegerStorageValue(ctx, first.value))};`);
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
// `delete o.p`'s value is the boolean its own `[[Delete]]` published.
|
|
424
|
+
if (operation.operator === 'delete') {
|
|
425
|
+
emitUnaryDelete(ctx, lines, defineValue(ctx, operation.result), first);
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
if (operation.operator === 'ToNumeric')
|
|
429
|
+
return emitToNumericCoercion(ctx, lines, first, operation.result);
|
|
430
|
+
const spelling = unaryOperatorFor(operation.operator, first.representation);
|
|
431
|
+
if (!spelling) {
|
|
432
|
+
throw createCppEmitBlockedError(`runtime-helper:computation:unary:${operation.operator}:${first.representation.kind}`, `unary "${operation.operator}" on a "${first.representation.kind}" carrier has no C++ spelling in this emitter`);
|
|
433
|
+
}
|
|
434
|
+
const name = defineValue(ctx, operation.result);
|
|
435
|
+
const operandRendered = operandText(ctx, first);
|
|
436
|
+
const narrowedNot = operation.operator === '~' && ctx.integerValues.has(first.value);
|
|
437
|
+
const call = narrowedNot ? `gea::integerBitwiseNot(${operandRendered})` : `${spelling.text}(${operandRendered})`;
|
|
438
|
+
// C++ integer zero has no negative representation: `-(0)` is still the
|
|
439
|
+
// integer `0`, and converting it to double afterward loses JavaScript's
|
|
440
|
+
// observable -0 (`Object.is`, reciprocal sign, BSON number width). Give a
|
|
441
|
+
// constant zero a floating spelling before negation; every nonzero unary
|
|
442
|
+
// operation keeps the ordinary carrier-directed spelling.
|
|
443
|
+
const constantText = ctx.constantTexts.get(first.value);
|
|
444
|
+
const negativeZero = operation.operator === '-' &&
|
|
445
|
+
first.representation.kind === 'scalar' &&
|
|
446
|
+
(first.representation.domain === 'number' || first.representation.domain === 'float64') &&
|
|
447
|
+
constantText !== undefined &&
|
|
448
|
+
Number(constantText) === 0;
|
|
449
|
+
const expression = negativeZero ? '-0.0' : spelling.kind === 'infix' ? `${spelling.text}${operandRendered}` : call;
|
|
450
|
+
lines.push(`${name} = ${expression};`);
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
// `typeof` is a question about the carrier, and for every carrier but a sum
|
|
454
|
+
// the answer is settled before the program runs. `emit-typeof.ts` owns both
|
|
455
|
+
// the mapping and the refusal.
|
|
456
|
+
if (operation.form === 'typeof') {
|
|
457
|
+
// A member read off a union whose arms disagree about the member: the
|
|
458
|
+
// answer is per-arm and the read itself rendered nothing, so the
|
|
459
|
+
// discriminant answers here. See `EmitContext.unionMemberTypeofReads`.
|
|
460
|
+
const memberRead = ctx.unionMemberTypeofReads.get(first.value);
|
|
461
|
+
if (memberRead !== undefined) {
|
|
462
|
+
const receiver = operandText(ctx, memberRead.receiver);
|
|
463
|
+
const answer = ctx.typeQueryValues.has(operation.result.id)
|
|
464
|
+
? unionMemberTypeofTagText(memberRead.answers, receiver, memberRead.member)
|
|
465
|
+
: unionMemberTypeofText(memberRead.answers, receiver, memberRead.member);
|
|
466
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${answer};`);
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
const namespacePath = ctx.hostNamespaceReads.get(first.value) ?? ctx.hostNamespaceValues.get(first.value);
|
|
470
|
+
const namespaceTypeof = namespacePath === undefined ? undefined : ctx.hosts.namespaces.typeofs.get(namespacePath);
|
|
471
|
+
if (ctx.typeQueryValues.has(operation.result.id)) {
|
|
472
|
+
const tag = namespaceTypeof === undefined
|
|
473
|
+
? typeofTagText(first.representation, () => operandText(ctx, first))
|
|
474
|
+
: typeofTagLiteral(namespaceTypeof);
|
|
475
|
+
if (tag === null)
|
|
476
|
+
throw createCppEmitBlockedError(`runtime-helper:computation:typeof:${representationKey(first.representation)}`, `host typeof published an unknown result ${namespaceTypeof}`);
|
|
477
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${tag};`);
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
if (namespaceTypeof !== undefined) {
|
|
481
|
+
ctx.constantTexts.set(operation.result.id, namespaceTypeof);
|
|
482
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${cppConstantLiteral(namespaceTypeof, 'string', operation.result.representation)};`);
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
// A settled answer is a string CONSTANT, and saying so is what lets every
|
|
486
|
+
// later rule that reasons about constants see it -- the comparison fold
|
|
487
|
+
// above included. Without this the two halves of `typeof x !== 'undefined'`
|
|
488
|
+
// were a folded literal and a recorded constant, so the fold did not fire
|
|
489
|
+
// and the pair reached C++ as `("object") != ("undefined")`: two literal
|
|
490
|
+
// ADDRESSES. Only recorded when the answer really is static; a sum answers
|
|
491
|
+
// at runtime and is no constant.
|
|
492
|
+
const settled = typeofTextFor(first.representation);
|
|
493
|
+
if (settled !== null)
|
|
494
|
+
ctx.constantTexts.set(operation.result.id, settled);
|
|
495
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${typeofText(first.representation, () => operandText(ctx, first))};`);
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
// `instanceof` is `[[HasInstance]]`, which this backend answers only for the
|
|
499
|
+
// error constructors -- see `emit-instanceof.ts` for why the general case is
|
|
500
|
+
// refused rather than approximated.
|
|
501
|
+
if (operation.form === 'instanceof') {
|
|
502
|
+
const constructor = operands[1];
|
|
503
|
+
if (!constructor || operands.length !== 2) {
|
|
504
|
+
throw createCppEmitBlockedError('runtime-helper:computation:instanceof:arity', `an "instanceof" with ${operands.length} operand(s) has no C++ spelling`);
|
|
505
|
+
}
|
|
506
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${instanceofText(ctx, first, constructor, operation.classInstanceTest)};`);
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
// A template is a concatenation of its pieces, each through ToString.
|
|
510
|
+
if (operation.form === 'template') {
|
|
511
|
+
const rendered = templateText(ctx, operation);
|
|
512
|
+
if ('refused' in rendered)
|
|
513
|
+
throw createCppEmitBlockedError('runtime-helper:computation:template', toStringRefusal(rendered.refused, ctx.classes, ctx.deriver));
|
|
514
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${rendered.text};`);
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
// `x++` is `x + 1` over the value ToNumeric already produced, and `x--` is
|
|
518
|
+
// `x - 1`. The store is a separate operation; this instruction only computes
|
|
519
|
+
// the number that gets stored, so it never appears as a C++ `++`.
|
|
520
|
+
if (operation.form === 'update') {
|
|
521
|
+
const step = updateStepOperators.get(operation.operator);
|
|
522
|
+
if (!step || !binaryOperatorFor(step, first.representation)) {
|
|
523
|
+
throw createCppEmitBlockedError(`runtime-helper:computation:update:${operation.operator}:${first.representation.kind}`, `update "${operation.operator}" on a "${first.representation.kind}" carrier has no C++ spelling in this emitter`);
|
|
524
|
+
}
|
|
525
|
+
const name = defineValue(ctx, operation.result), raw = `${operandText(ctx, first)} ${step} 1`;
|
|
526
|
+
lines.push(`${name} = ${widenedStoreText(operation.result.representation, first.representation, raw) ?? raw};`);
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
const second = operands[1];
|
|
530
|
+
if (!second || operands.length !== 2) {
|
|
531
|
+
throw createCppEmitBlockedError('runtime-helper:computation:binary:arity', `a binary computation with ${operands.length} operand(s) has no C++ spelling`);
|
|
532
|
+
}
|
|
533
|
+
// Equality against `null`/`undefined` is settled by the carriers, not by a
|
|
534
|
+
// conversion between them, so it is answered before the mixed-carrier refusal
|
|
535
|
+
// below -- which would otherwise reject the language's most common absence
|
|
536
|
+
// check as a coercion with no recipe.
|
|
537
|
+
// Native host singleton/class handles are compile-time identity tags. Do
|
|
538
|
+
// not ask them for a runtime value before answering identity: authenticated
|
|
539
|
+
// `process === globalThis.process` has two routes to the same Process
|
|
540
|
+
// protocol and neither route needs to box or materialize that singleton.
|
|
541
|
+
const firstHostIdentity = ctx.hostClassReads.get(first.value);
|
|
542
|
+
const secondHostIdentity = ctx.hostClassReads.get(second.value);
|
|
543
|
+
if (firstHostIdentity && secondHostIdentity && referenceIdentityOperators.has(operation.operator)) {
|
|
544
|
+
const equal = firstHostIdentity.declaration === secondHostIdentity.declaration;
|
|
545
|
+
const negated = operation.operator === '!==' || operation.operator === '!=';
|
|
546
|
+
const answer = equal !== negated ? 'true' : 'false';
|
|
547
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${answer};`);
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
const sides = [first, second].map((operand) => ({ text: operandText(ctx, operand), representation: operand.representation }));
|
|
551
|
+
const absent = sides[0] && sides[1] ? absenceComparisonText(operation.operator, sides[0], sides[1]) : null;
|
|
552
|
+
if (absent !== null) {
|
|
553
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${absent};`);
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
// Function equality is allocation identity even when the two statically
|
|
557
|
+
// visible call signatures differ. The rule itself is
|
|
558
|
+
// `callableIdentityEqualityText`, asked here and from inside
|
|
559
|
+
// `strictEqualityText`'s own recursion so that a callable wrapped in an
|
|
560
|
+
// OPTIONAL gets the identical answer -- it used to live inline here, where
|
|
561
|
+
// that recursion could not reach it. `Alias === Routed` in
|
|
562
|
+
// `dynamic-callable-abi-recovery.runtime.js` is the bare pair;
|
|
563
|
+
// `stateStack[stackIndex] !== State.done` in `generic-state-function-array.ts`
|
|
564
|
+
// is the wrapped one.
|
|
565
|
+
const callableIdentity = sides[0] && sides[1] ? callableIdentityEqualityText(operation.operator, sides[0], sides[1]) : null;
|
|
566
|
+
if (callableIdentity !== null) {
|
|
567
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${callableIdentity};`);
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
// A sum on either side is a discriminant test plus one arm's comparison,
|
|
571
|
+
// which the generic path below cannot reach: two different carriers hit its
|
|
572
|
+
// mixed-carrier refusal, and two identical sums hit `binaryOperatorFor`,
|
|
573
|
+
// which has no `==` for a byte buffer. Answered here, before both.
|
|
574
|
+
const equality = sides[0] && sides[1] ? strictEqualityText(operation.operator, sides[0], sides[1]) : null;
|
|
575
|
+
if (equality !== null) {
|
|
576
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${equality};`);
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
// Two string constants compared: answered in the source language rather than
|
|
580
|
+
// by a C++ operator, which would compare the two literals' ADDRESSES. See
|
|
581
|
+
// `constantStringComparisonText`. Before the `+` fold below because `+` on
|
|
582
|
+
// two strings is concatenation, which that fold already owns.
|
|
583
|
+
if (first.representation.kind === 'string' && second.representation.kind === 'string') {
|
|
584
|
+
const leftConstant = ctx.constantTexts.get(first.value);
|
|
585
|
+
const rightConstant = ctx.constantTexts.get(second.value);
|
|
586
|
+
if (leftConstant !== undefined && rightConstant !== undefined) {
|
|
587
|
+
const folded = constantStringComparisonText(operation.operator, leftConstant, rightConstant);
|
|
588
|
+
if (folded !== null) {
|
|
589
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${folded};`);
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
// `+` is string concatenation whenever either operand is a string, and the
|
|
595
|
+
// plan has already settled which `+` this is: the result carrier. Both sides
|
|
596
|
+
// then go through ToString, folded by the same authority a template
|
|
597
|
+
// expression uses -- `'n: ' + count` and `` `n: ${count}` `` are the same
|
|
598
|
+
// value, and spelling them by two rules that could disagree is how one of
|
|
599
|
+
// them ends up wrong. A carrier with no ToString refuses by name here exactly
|
|
600
|
+
// as it does in a template, rather than being boxed.
|
|
601
|
+
if (operation.operator === '+' && operation.result.representation.kind === 'string') {
|
|
602
|
+
const rendered = templateText(ctx, operation);
|
|
603
|
+
if ('refused' in rendered)
|
|
604
|
+
throw createCppEmitBlockedError('runtime-helper:computation:template', toStringRefusal(rendered.refused, ctx.classes, ctx.deriver));
|
|
605
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${rendered.text};`);
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
// Exactly one side already `dynamic` and the other an ordinary typed
|
|
609
|
+
// carrier -- `parsed + 1`, `1 + parsed` where `parsed` came from
|
|
610
|
+
// `JSON.parse` or a declared `any` never narrowed -- is answered without
|
|
611
|
+
// boxing the typed side (`mixedDynamicPlusText`'s own comment says why),
|
|
612
|
+
// and UNCONDITIONALLY: a `dynamic` carrier this ordinary is not the opt-in
|
|
613
|
+
// `ctx.deriver.dynamicFallback` feature (that flag gates a different,
|
|
614
|
+
// riskier fallback below), and gating this recipe behind it left the most
|
|
615
|
+
// common dynamic-boundary program -- `JSON.parse(...) + 1` -- refusing by
|
|
616
|
+
// name with exactly this file's own "mixes a dynamic and a scalar carrier"
|
|
617
|
+
// message whenever the flag was off, which is the ordinary case.
|
|
618
|
+
if (operation.operator === '+') {
|
|
619
|
+
const firstDynamic = first.representation.kind === 'dynamic';
|
|
620
|
+
const secondDynamic = second.representation.kind === 'dynamic';
|
|
621
|
+
if (firstDynamic !== secondDynamic) {
|
|
622
|
+
const dynamicOperand = firstDynamic ? sides[0] : sides[1];
|
|
623
|
+
const typedOperand = firstDynamic ? sides[1] : sides[0];
|
|
624
|
+
const native = dynamicOperand && typedOperand ? mixedDynamicPlusText(dynamicOperand, typedOperand, firstDynamic, ctx.classes, ctx.deriver) : null;
|
|
625
|
+
if (native !== null) {
|
|
626
|
+
const dynamicResult = { kind: 'dynamic', reason: 'opt-in-fallback' };
|
|
627
|
+
const text = alignedValueText(ctx, 'emit.ts:711', dynamicResult, operation.result.representation, native);
|
|
628
|
+
if (text === null)
|
|
629
|
+
throw createCppEmitBlockedError(`conversion:dynamic->${representationKey(operation.result.representation)}`, 'dynamic addition result has no checked conversion');
|
|
630
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${text};`);
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
// Two dynamic sides, or a `tagged-union` on either side, still need
|
|
636
|
+
// `gea::dynamicAdd`'s full ToPrimitive dispatch -- a tagged union's live
|
|
637
|
+
// arm is exactly as unknown at compile time as a dynamic operand's tag, and
|
|
638
|
+
// two genuinely dynamic operands may each be a real JS object whose own
|
|
639
|
+
// `valueOf`/`toString` only `gea::dynamicAdd` runs. This IS the boxing this
|
|
640
|
+
// file's header describes as correct to keep, and stays behind the opt-in
|
|
641
|
+
// flag: it is the one case left that boxes an operand this backend could
|
|
642
|
+
// otherwise have kept native (the `firstDynamic !== secondDynamic` branch
|
|
643
|
+
// just above, when it applies).
|
|
644
|
+
if (ctx.deriver.dynamicFallback &&
|
|
645
|
+
operation.operator === '+' &&
|
|
646
|
+
(first.representation.kind === 'dynamic' ||
|
|
647
|
+
second.representation.kind === 'dynamic' ||
|
|
648
|
+
first.representation.kind === 'tagged-union' ||
|
|
649
|
+
second.representation.kind === 'tagged-union')) {
|
|
650
|
+
const left = boxedValueText(ctx, first, 'dynamic addition');
|
|
651
|
+
const right = boxedValueText(ctx, second, 'dynamic addition');
|
|
652
|
+
const dynamic = { kind: 'dynamic', reason: 'opt-in-fallback' };
|
|
653
|
+
const text = alignedValueText(ctx, 'emit.ts:738', dynamic, operation.result.representation, `gea::dynamicAdd(${left}, ${right})`);
|
|
654
|
+
if (text === null)
|
|
655
|
+
throw createCppEmitBlockedError(`conversion:dynamic->${representationKey(operation.result.representation)}`, 'dynamic addition result has no checked conversion');
|
|
656
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${text};`);
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
// A function compared against a function IDENTITY. Not a coercion this file
|
|
660
|
+
// invents: the identity IS what `CallableObject::operator==` compares, so
|
|
661
|
+
// converting the callable side to its own identity handle (the very field
|
|
662
|
+
// that comparison reads) and comparing the two handles is the same answer
|
|
663
|
+
// written one step earlier. Restricted to the four same-type equality
|
|
664
|
+
// operators, which is all `referenceIdentityOperators` spells.
|
|
665
|
+
const identitySpelling = referenceIdentityOperators.get(operation.operator);
|
|
666
|
+
const identitySide = first.representation.kind === 'callable-identity' ? 0 : second.representation.kind === 'callable-identity' ? 1 : null;
|
|
667
|
+
if (identitySpelling && identitySide !== null) {
|
|
668
|
+
const callable = identitySide === 0 ? second : first;
|
|
669
|
+
const identityCarrier = { kind: 'callable-identity' };
|
|
670
|
+
const converted = alignedValueText(ctx, 'emit.ts:764', callable.representation, identityCarrier, operandText(ctx, callable));
|
|
671
|
+
if (converted !== null) {
|
|
672
|
+
const held = operandText(ctx, identitySide === 0 ? first : second);
|
|
673
|
+
const [left, right] = identitySide === 0 ? [held, converted] : [converted, held];
|
|
674
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${left} ${identitySpelling.text} ${right};`);
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
// Two carriers reaching an operator is not a decision this printer makes
|
|
679
|
+
// any more: ECMA-262's ToNumeric/ToPrimitive before a binary operator is a
|
|
680
|
+
// conversion, and the slot census states it (`projection/slots.ts`'s
|
|
681
|
+
// `binarySlot`) -- lowering coerces both operands into one carrier, or the
|
|
682
|
+
// coercion node is `never` and the operands arrive as they were. What is
|
|
683
|
+
// left here is exactly that `never`: BigInt mixed with Number, a Date's
|
|
684
|
+
// `valueOf`-first ToPrimitive, an object whose own `toString` would have
|
|
685
|
+
// to be called. Refused by name, never spelled by a second table.
|
|
686
|
+
if (representationKey(first.representation) !== representationKey(second.representation)) {
|
|
687
|
+
throw createCppEmitBlockedError(`conversion:${representationKey(first.representation)}->${representationKey(second.representation)}`, `binary "${operation.operator}" mixes a "${first.representation.kind}" and a "${second.representation.kind}" carrier, ` +
|
|
688
|
+
'and the census names no coercion that brings them together');
|
|
689
|
+
}
|
|
690
|
+
const spelling = binaryOperatorFor(operation.operator, first.representation);
|
|
691
|
+
if (!spelling) {
|
|
692
|
+
throw createCppEmitBlockedError(`runtime-helper:computation:binary:${operation.operator}:${first.representation.kind}`, `binary "${operation.operator}" on a "${first.representation.kind}" carrier has no C++ spelling in this emitter`);
|
|
693
|
+
}
|
|
694
|
+
const name = defineValue(ctx, operation.result);
|
|
695
|
+
const integral = ctx.integerValues.has(first.value) && ctx.integerValues.has(second.value);
|
|
696
|
+
// `/` is the one arithmetic operator whose C++ meaning CHANGES when both
|
|
697
|
+
// operands are integers: `7 / 2` would become 3 rather than 3.5, so two
|
|
698
|
+
// narrowed operands are widened back for it. `%` goes the other way -- C++
|
|
699
|
+
// truncates toward zero just as ECMA-262 6.1.6.1.6 does, so an integer
|
|
700
|
+
// remainder is the same answer without the fmod round trip, under its own
|
|
701
|
+
// name rather than an overload (a mixed call would make a pair ambiguous).
|
|
702
|
+
const widenForDivision = integral && operation.operator === '/';
|
|
703
|
+
const left = widenForDivision ? `static_cast<double>(${operandText(ctx, first)})` : operandText(ctx, first);
|
|
704
|
+
const right = widenForDivision ? `static_cast<double>(${operandText(ctx, second)})` : operandText(ctx, second);
|
|
705
|
+
const bounded = integerBoundedComparison(ctx.integerValues, ctx.loopInvariantValues, operation, first, second, left, right);
|
|
706
|
+
if (bounded !== null) {
|
|
707
|
+
lines.push(`${name} = ${bounded};`);
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
// The cheaper spellings of `%`, chosen by the integer census (`ir/integers.ts`)
|
|
711
|
+
// and written out by `emit-integers.ts`; `null` keeps `gea::remainder`.
|
|
712
|
+
const form = integral && ctx.integerValues.has(operation.result.id) ? 'narrowed' : ctx.remainderForms.get(operation.result.id);
|
|
713
|
+
const hoisted = operation.operator === '%' ? ctx.denseLengths.get(second.value) : undefined;
|
|
714
|
+
const remainder = operation.operator !== '%' ? null : remainderText(form, left, right, ctx.declarations, hoisted);
|
|
715
|
+
if (remainder !== null) {
|
|
716
|
+
// A wrapped window's subscript is computed in the INTEGERS (`denseRemainderCompanion`).
|
|
717
|
+
const window = hoisted === undefined || !ctx.integerValues.has(first.value) ? undefined : hoisted;
|
|
718
|
+
const dense = window === undefined ? null : denseRemainderCompanion(window, left, ctx.declarations, lines);
|
|
719
|
+
if (dense !== null)
|
|
720
|
+
ctx.denseIndices.set(operation.result.id, dense.index);
|
|
721
|
+
lines.push(`${name} = ${dense === null ? remainder : dense.value};`);
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
const integerBitwise = integral ? integerBitwiseOperators.get(operation.operator) : undefined;
|
|
725
|
+
if (integerBitwise !== undefined) {
|
|
726
|
+
lines.push(`${name} = ${integerBitwise}(${left}, ${right});`);
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
const expression = spelling.kind === 'infix' ? `${left} ${spelling.text} ${right}` : `${spelling.text}(${left}, ${right})`;
|
|
730
|
+
// Coerced operands compute the operator's own result -- a Number, a String
|
|
731
|
+
// for `+` over strings, a Boolean for a relational -- but the CELL is what
|
|
732
|
+
// the checker published for the expression, which is `dynamic` whenever it
|
|
733
|
+
// could not type the mixed operands (`number + null` is a strictNullChecks
|
|
734
|
+
// error recovered as `any`). The census converts the operator's result
|
|
735
|
+
// into that cell like any other store; a scalar or string cell is the
|
|
736
|
+
// operator's own carrier already (an integer-narrowed cell included).
|
|
737
|
+
const result = operation.result.representation;
|
|
738
|
+
const natural = relationalOperators.has(operation.operator)
|
|
739
|
+
? { kind: 'scalar', domain: 'boolean' }
|
|
740
|
+
: first.representation.kind === 'string'
|
|
741
|
+
? { kind: 'string' }
|
|
742
|
+
: { kind: 'scalar', domain: 'number' };
|
|
743
|
+
const stored = result.kind === 'scalar' || result.kind === 'string' || representationKey(natural) === representationKey(result)
|
|
744
|
+
? expression
|
|
745
|
+
: alignedValueText(ctx, 'emit.ts:compute-result', natural, result, expression);
|
|
746
|
+
if (stored === null) {
|
|
747
|
+
throw createCppEmitBlockedError(`conversion:${representationKey(natural)}->${representationKey(result)}`, `binary "${operation.operator}" produces a ${representationKey(natural)} that no installed conversion stores into ${representationKey(result)}`);
|
|
748
|
+
}
|
|
749
|
+
lines.push(`${name} = ${stored};`);
|
|
750
|
+
};
|
|
751
|
+
const emitParameter = (ctx, _lines, operation) => {
|
|
752
|
+
const declared = ctx.abi?.parameters[operation.ordinal];
|
|
753
|
+
if (!declared) {
|
|
754
|
+
throw createCppEmitBlockedError('call-abi:parameter', `reads ABI position ${operation.ordinal}, which this body's calling convention does not declare`);
|
|
755
|
+
}
|
|
756
|
+
// A formal the whole-program census settled is declared `long long` by this
|
|
757
|
+
// body's own signature (`formalsOf`, translation-unit.ts). The ABI is
|
|
758
|
+
// untouched -- it is what every caller, thunk and `CallableObject` agrees on
|
|
759
|
+
// -- and the thunk converts across; here the formal simply already has the
|
|
760
|
+
// carrier the body works in.
|
|
761
|
+
if (ctx.narrowedFormals.has(operation.ordinal)) {
|
|
762
|
+
// The formal already IS the narrowed carrier, so this is the alias below,
|
|
763
|
+
// not a conversion: the signature carried the census's answer.
|
|
764
|
+
defineValueAlias(ctx, operation.result, cppFormalName(operation.ordinal));
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
// Otherwise the formal itself, for the reason `emitReceiver` states: it is a
|
|
768
|
+
// local of this frame that nothing ever writes, so naming it is naming the
|
|
769
|
+
// value the caller passed, and the copy that stood here was a retain and a
|
|
770
|
+
// release (or a string's heap copy) for a name the frame already had.
|
|
771
|
+
defineValueAlias(ctx, operation.result, cppFormalName(operation.ordinal));
|
|
772
|
+
};
|
|
773
|
+
/**
|
|
774
|
+
* One JSX element, constructed through the host's element protocol.
|
|
775
|
+
*
|
|
776
|
+
* The emitter states only what the language states -- create with this tag,
|
|
777
|
+
* then these props in order, then these children in order -- and hands each
|
|
778
|
+
* step to an overloaded host entry point. What a prop *means* is genuinely not
|
|
779
|
+
* decidable here: a style object, a string attribute, and an event handler are
|
|
780
|
+
* three different operations on the host's node, and the thing that can tell
|
|
781
|
+
* them apart is the C++ type of the value, which the host's own overload set
|
|
782
|
+
* already dispatches on. Encoding that decision here instead would put the
|
|
783
|
+
* framework's prop taxonomy inside the compiler, where it would immediately be
|
|
784
|
+
* a second authority against the runtime's.
|
|
785
|
+
*
|
|
786
|
+
* The result must be a `native-handle`: a host element is opaque, and rendering
|
|
787
|
+
* construction into a record carrier would produce a struct that compiles and
|
|
788
|
+
* renders nothing.
|
|
789
|
+
*/
|
|
790
|
+
/**
|
|
791
|
+
* `ToBoolean` of one value, per carrier.
|
|
792
|
+
*
|
|
793
|
+
* The table is the language's, not a convenience: `0` and `NaN` are false and
|
|
794
|
+
* every other number true, an empty string is false and a one-space string
|
|
795
|
+
* true, `null` and `undefined` are false, an absent optional is false, and
|
|
796
|
+
* *every* object is true no matter what it holds. Writing any of these as a
|
|
797
|
+
* C++ implicit conversion would get several of them wrong -- `NaN` converts to
|
|
798
|
+
* `true`, and a `std::string` does not convert at all -- so each carrier gets
|
|
799
|
+
* the expression that is exactly its rule, and a carrier with no stated rule
|
|
800
|
+
* is refused rather than left to whatever `if (x)` happens to mean in C++.
|
|
801
|
+
*/
|
|
802
|
+
const emitTest = (ctx, lines, operation) => {
|
|
803
|
+
const name = defineValue(ctx, operation.result);
|
|
804
|
+
const hostNamespace = ctx.hostNamespaceReads.get(operation.value.value) ?? ctx.hostNamespaceValues.get(operation.value.value);
|
|
805
|
+
const hostSingleton = ctx.hostClassReads.get(operation.value.value);
|
|
806
|
+
const hostMethod = ctx.hostMemberReads.get(operation.value.value);
|
|
807
|
+
if (hostNamespace !== undefined || hostSingleton !== undefined || hostMethod !== undefined) {
|
|
808
|
+
// A plugin-declared namespace segment is a real, present object in the
|
|
809
|
+
// source language even though the target fuses its member path and never
|
|
810
|
+
// materializes that intermediate object. Host singletons and exact host
|
|
811
|
+
// methods are the same kind of authenticated, always-present identity:
|
|
812
|
+
// testing either for an optional-chain guard must not materialize a class
|
|
813
|
+
// or method value. All three tests agree for every one: present, defined
|
|
814
|
+
// and true.
|
|
815
|
+
lines.push(`${name} = true;`);
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
const text = operandText(ctx, operation.value);
|
|
819
|
+
const carrier = operation.value.representation;
|
|
820
|
+
// Which question was asked travels on the operation, from the census that
|
|
821
|
+
// recorded it, and is never re-derived from the carrier here: `if (x)` and
|
|
822
|
+
// `x ?? y` over one `x` ask different questions and a carrier cannot tell
|
|
823
|
+
// them apart.
|
|
824
|
+
const answer = operation.predicate === 'is-present'
|
|
825
|
+
? presenceTestText(text, carrier)
|
|
826
|
+
: operation.predicate === 'is-defined'
|
|
827
|
+
? definedTestText(text, carrier)
|
|
828
|
+
: booleanTestText(text, carrier, isIntegerStorageValue(ctx, operation.value.value));
|
|
829
|
+
lines.push(`${name} = ${answer};`);
|
|
830
|
+
};
|
|
831
|
+
/**
|
|
832
|
+
* A conversion the graph made explicit, rendered by the one authority on what
|
|
833
|
+
* a narrowing loads.
|
|
834
|
+
*
|
|
835
|
+
* `emit-narrowing.ts` already answers "given a value carried as X and wanted as
|
|
836
|
+
* Y, what is the load" -- it is what a narrowed binding read renders -- and
|
|
837
|
+
* asking it here rather than writing a second dereference is what keeps the two
|
|
838
|
+
* from disagreeing. A pair it has no load for is refused by name: preflight
|
|
839
|
+
* censused the conversion, so reaching this with no answer means the census and
|
|
840
|
+
* the renderer disagree, which is worth saying out loud rather than papering
|
|
841
|
+
* over with an implicit C++ conversion.
|
|
842
|
+
*/
|
|
843
|
+
const emitConvert = (ctx, lines, operation) => {
|
|
844
|
+
// A deferred host method has no standalone C++ value. Optional chaining can
|
|
845
|
+
// narrow/widen the callee between its property read, presence test and call;
|
|
846
|
+
// carry the authenticated deferred invocation through those SSA views
|
|
847
|
+
// without asking `operandText` to materialize a function the host never
|
|
848
|
+
// supplied. Any non-call consumer still reaches `operandText` later and
|
|
849
|
+
// fails closed as a host-method-value.
|
|
850
|
+
if (ctx.hostMemberReads.has(operation.source.value))
|
|
851
|
+
return;
|
|
852
|
+
// A host singleton is a compile-time identity, including after the
|
|
853
|
+
// statically-present arm of `globalThis?.singleton` is narrowed for a
|
|
854
|
+
// following optional call. No object is converted or boxed here, so the
|
|
855
|
+
// conversion renders nothing; the identity's travel onto the new SSA view is
|
|
856
|
+
// stated by `emit-bindings.ts`'s `hostClassReadsOf`.
|
|
857
|
+
if (ctx.hostClassReads.has(operation.source.value) && operation.result.representation.kind === 'native-handle')
|
|
858
|
+
return;
|
|
859
|
+
// A host NAMESPACE is a path, not a value (`emit-namespaces.ts`), and a path
|
|
860
|
+
// does not change by being viewed under another carrier. `export const
|
|
861
|
+
// Accelerometer = typeof __gea_Accelerometer !== 'undefined' ?
|
|
862
|
+
// __gea_Accelerometer : (undefined as unknown as typeof __gea_Accelerometer)`
|
|
863
|
+
// merges the namespace with the host-absent arm into one path, then converts
|
|
864
|
+
// that merge into the cell's own carrier before the binding write; the write
|
|
865
|
+
// recognises the path and renders nothing (`emitBindingWrite`), so the
|
|
866
|
+
// convert in between has to carry it rather than ask `operandText` for a
|
|
867
|
+
// value the namespace never had.
|
|
868
|
+
// The propagation itself is settled before this body rendered a line, by
|
|
869
|
+
// `host-namespace-reads.ts`'s `hostNamespaceReadsOf`, which folds `convert`
|
|
870
|
+
// into the same fixed point that finds the path in the first place. All this
|
|
871
|
+
// has to ask is whether THIS conversion's result is one of the paths it
|
|
872
|
+
// found -- a conversion of a namespace still renders nothing.
|
|
873
|
+
if (ctx.hostNamespaceReads.has(operation.result.id))
|
|
874
|
+
return;
|
|
875
|
+
// Converting an expression to `void` evaluates the source and discards its
|
|
876
|
+
// value. Evaluation has already happened in the source operation, so this
|
|
877
|
+
// conversion only needs to publish the C++ void expression for any later
|
|
878
|
+
// SSA use; it must not try to allocate storage for a carrier that has none.
|
|
879
|
+
if (operation.result.representation.kind === 'void') {
|
|
880
|
+
defineValueAlias(ctx, operation.result, '(void)0');
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
const input = owningConversionInputText(ctx, operation.source, operation.result.representation, operandText(ctx, operation.source));
|
|
884
|
+
// A load no program fact proves present (`ir/presence-proof.ts`) tests the
|
|
885
|
+
// cell first: `presentOrThrow` hands back the same cell or raises.
|
|
886
|
+
const sourceText = operation.presence === 'checked' ? `gea::host::presentOrThrow(${input})` : input;
|
|
887
|
+
// The node lowering named on this instruction, rendered by its recipe
|
|
888
|
+
// (`alignedValueText` asks the census for the same pair and renders the
|
|
889
|
+
// same node; a pair the census refused is its drift row, and the chain's).
|
|
890
|
+
const named = ctx.conversions.nodeById(operation.conversionUse);
|
|
891
|
+
const text = (named?.capability.kind === 'coercion'
|
|
892
|
+
? namedConversionText(ctx, 'emit.ts:1026', named, sourceText)
|
|
893
|
+
: alignedValueText(ctx, 'emit.ts:1026', operation.source.representation, operation.result.representation, sourceText)) ??
|
|
894
|
+
// A method value escaping into a receiver-less slot: the receiver is
|
|
895
|
+
// recovered from the read itself (`emit-callable.ts`'s
|
|
896
|
+
// `receiverBoundCallableText`), a fact of this context, not of the pair.
|
|
897
|
+
receiverBoundCallableText(ctx, operation.source, operation.result.representation, sourceText);
|
|
898
|
+
// A storage-free source converting into a real carrier is a branch flow
|
|
899
|
+
// analysis proved dead, not a missing load: `undefined` is also `never`'s
|
|
900
|
+
// carrier (`representation/primitives.ts`), and a pair the loads refuse can
|
|
901
|
+
// only be reached when the source holds nothing. `var [d = 7] = []` is the
|
|
902
|
+
// standing case -- the extraction past an empty tuple is `undefined`
|
|
903
|
+
// outright, the default's present arm converts it to the bound `number`,
|
|
904
|
+
// and the `is-defined` test guarding that arm is the constant `false`.
|
|
905
|
+
// Rendered as the throw the call-site argument path already renders
|
|
906
|
+
// (`emit-callable.ts`) so the dead arm does not block the live one.
|
|
907
|
+
if (text === null && (operation.source.representation.kind === 'undefined' || operation.source.representation.kind === 'void')) {
|
|
908
|
+
defineValueAlias(ctx, operation.result, `gea::host::unreachableValue<${cppTypeOf(operation.result.representation)}>()`);
|
|
909
|
+
return;
|
|
910
|
+
}
|
|
911
|
+
if (text === null) {
|
|
912
|
+
throw createCppEmitBlockedError(`conversion:${representationKey(operation.source.representation)}->${representationKey(operation.result.representation)}`, `converts ${representationKey(operation.source.representation)} to ${representationKey(operation.result.representation)}, ` +
|
|
913
|
+
`which no installed load performs (conversion use ${operation.conversionUse})`);
|
|
914
|
+
}
|
|
915
|
+
const consumed = transfersFormalConversion(ctx.consumingFormalConversions, operation.result.id) === 'move' ? `std::move(${text})` : text;
|
|
916
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${consumed};`);
|
|
917
|
+
};
|
|
918
|
+
/**
|
|
919
|
+
* `await p`.
|
|
920
|
+
*
|
|
921
|
+
* `gea::Promise<V>` is a settled-value box with no job queue
|
|
922
|
+
* (`runtime/gea_runtime.h`'s `Promise` doc comment): the only promise this
|
|
923
|
+
* runtime can ever construct is one whose result is already known. `await`
|
|
924
|
+
* therefore renders as reading that value right now -- `.awaited()`, the
|
|
925
|
+
* same synchronous-immediate answer `.then()` already commits to for the
|
|
926
|
+
* identical reason (see that method's own doc comment) -- rather than a real
|
|
927
|
+
* suspension, which this substrate has no coroutine primitive to express.
|
|
928
|
+
*
|
|
929
|
+
* The operand is not always a `promise` carrier: `await` accepts any
|
|
930
|
+
* expression (`Awaited<T>` is `T` unchanged for a non-thenable `T`), and a
|
|
931
|
+
* non-promise operand is already its own resolution, so it passes through
|
|
932
|
+
* unchanged rather than calling a method that carrier does not have.
|
|
933
|
+
*
|
|
934
|
+
* And it is not always one of those two, either. A union of both --
|
|
935
|
+
* `string | Promise<string>`, what every conditionally-async helper returns --
|
|
936
|
+
* is a discriminant test away from either, and asking only `kind === 'promise'`
|
|
937
|
+
* passed it through untouched, handing a `TaggedUnion<...>` to a consumer told
|
|
938
|
+
* it would get the payload. `awaitedText` (prototype/emit-prototype-promise.ts)
|
|
939
|
+
* is ECMA-262 27.2.4.7.1 `PromiseResolve` stated once, for all three shapes,
|
|
940
|
+
* and shared with `Promise.all`, which resolves each element by the same rule.
|
|
941
|
+
*/
|
|
942
|
+
const emitAwait = (ctx, lines, operation) => {
|
|
943
|
+
const text = operandText(ctx, operation.operand);
|
|
944
|
+
const expression = awaitedText(ctx, 'emit.ts:emitAwait', operation.operand.representation, text, operation.result?.representation ?? null) ?? text;
|
|
945
|
+
if (!operation.result) {
|
|
946
|
+
lines.push(`${expression};`);
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
949
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${expression};`);
|
|
950
|
+
};
|
|
951
|
+
/**
|
|
952
|
+
* `yield x` -- a real suspension, unlike `await` above.
|
|
953
|
+
*
|
|
954
|
+
* The enclosing body's ABI result is `gea::Iterator<T>`, whose nested
|
|
955
|
+
* `promise_type` (`runtime/gea_runtime.h`) is what makes this function a C++20
|
|
956
|
+
* coroutine; `co_yield` stores the value in the frame and suspends, and the
|
|
957
|
+
* `next()` that resumes it reads it back. That is v1's own mechanism
|
|
958
|
+
* (`value_09_generator.h`'s `yield_value`/`yield_awaiter`), re-typed over the
|
|
959
|
+
* concrete `E` the checker proved instead of v1's `gea_cpp_value`.
|
|
960
|
+
*
|
|
961
|
+
* The value is aligned to the cursor's element carrier on the way in, by the
|
|
962
|
+
* same rule an argument written into a parameter slot is: a `Generator<number>`
|
|
963
|
+
* yielding an `int`-carried expression must store the `double` the cursor
|
|
964
|
+
* holds.
|
|
965
|
+
*
|
|
966
|
+
* `yield` with no operand is `yield undefined`, and this backend has no
|
|
967
|
+
* `undefined` to yield into a typed cursor, so it is refused rather than
|
|
968
|
+
* yielding a default-constructed element the program never wrote.
|
|
969
|
+
*/
|
|
970
|
+
const emitYield = (ctx, lines, operation) => {
|
|
971
|
+
const cursor = ctx.abi?.result;
|
|
972
|
+
if (cursor?.kind !== 'iterator') {
|
|
973
|
+
throw createCppEmitBlockedError('abrupt-edge:suspend', `appears in a body whose calling convention returns "${cursor ? representationKey(cursor) : 'nothing'}"; ` +
|
|
974
|
+
'only a body returning an "iterator" cursor is emitted as a coroutine');
|
|
975
|
+
}
|
|
976
|
+
// A bare `yield` yields `undefined`; it is spelled only when the cursor's
|
|
977
|
+
// element carrier has an `undefined` to hold (`Iterator<Undefined>` for a
|
|
978
|
+
// generator that yields nothing else, an optional or a sum carrying the
|
|
979
|
+
// absence otherwise), never as a default-constructed element the program
|
|
980
|
+
// did not write.
|
|
981
|
+
const bareUndefined = operation.operand
|
|
982
|
+
? null
|
|
983
|
+
: alignedValueText(ctx, 'emit.ts:1128', { kind: 'undefined' }, cursor.element, cppUndefinedValue);
|
|
984
|
+
if (!operation.operand && bareUndefined === null) {
|
|
985
|
+
throw createCppEmitBlockedError('abrupt-edge:suspend', `yields no value; a bare \`yield\` yields \`undefined\`, which this cursor's "${representationKey(cursor.element)}" element carrier cannot hold`);
|
|
986
|
+
}
|
|
987
|
+
const yieldText = `(co_yield ${operation.operand ? alignedText(ctx, cursor.element, operation.operand, 'yield') : bareUndefined})`;
|
|
988
|
+
// Statement form: nobody reads what `next(v)` sent back, so the resumed
|
|
989
|
+
// value -- whatever it turns out to be -- is evaluated and discarded, the
|
|
990
|
+
// same as any other unread expression statement.
|
|
991
|
+
if (operation.result === null) {
|
|
992
|
+
lines.push(`${yieldText};`);
|
|
993
|
+
return;
|
|
994
|
+
}
|
|
995
|
+
// A yield whose own value IS read needs a real resume channel to read it
|
|
996
|
+
// from: `TNext` collapsed to `undefined` (`representation/derive.ts`) means
|
|
997
|
+
// there is no storage behind `yieldText`'s own value to convert, so this
|
|
998
|
+
// refuses by name rather than converting a `void`/`undefined` expression
|
|
999
|
+
// into whatever type the read expects.
|
|
1000
|
+
if (cursor.resume.kind === 'void' || cursor.resume.kind === 'undefined') {
|
|
1001
|
+
throw createCppEmitBlockedError('abrupt-edge:suspend', "a yield whose own value is read needs the generator's resume channel (what `next(v)`/an abrupt `.return`/`.throw` sends back), " +
|
|
1002
|
+
'and this generator\'s own resume channel is "undefined" -- either TNext never resolved to a native carrier, or this ' +
|
|
1003
|
+
'generator is not annotated as one');
|
|
1004
|
+
}
|
|
1005
|
+
const text = alignedValueText(ctx, 'emit.ts:1156', cursor.resume, operation.result.representation, yieldText);
|
|
1006
|
+
if (text === null) {
|
|
1007
|
+
throw createCppEmitBlockedError(`conversion:${representationKey(cursor.resume)}->${representationKey(operation.result.representation)}`, `resumes with "${representationKey(cursor.resume)}" where this read expects "${representationKey(operation.result.representation)}", ` +
|
|
1008
|
+
'and no installed conversion reconciles them');
|
|
1009
|
+
}
|
|
1010
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${text};`);
|
|
1011
|
+
};
|
|
1012
|
+
const emitReceiver = (ctx, _lines, operation) => {
|
|
1013
|
+
const declared = ctx.abi?.receiver;
|
|
1014
|
+
if (!declared) {
|
|
1015
|
+
throw createCppEmitBlockedError('call-abi:receiver', "reads the frame's receiver, which this body's calling convention does not declare");
|
|
1016
|
+
}
|
|
1017
|
+
// The formal IS the receiver, so the read names it rather than copying it --
|
|
1018
|
+
// the same argument `emit-bindings.ts` makes for a cell written once, and
|
|
1019
|
+
// stronger here: a formal is written zero times, by anyone, so no later value
|
|
1020
|
+
// exists for the alias to drift onto.
|
|
1021
|
+
//
|
|
1022
|
+
// The copy it removes is real work on every carrier a receiver can have. A
|
|
1023
|
+
// `gea::Ref` receiver pays a retain and a release per call for a name the
|
|
1024
|
+
// frame already had; measured on `bench/comparison/fixtures/method_calls.ts`,
|
|
1025
|
+
// whose loop is one method call, that pair alone was a third of the program
|
|
1026
|
+
// (57.4ms to 38.4ms).
|
|
1027
|
+
defineValueAlias(ctx, operation.result, cppReceiverName);
|
|
1028
|
+
};
|
|
1029
|
+
const emitGlobalThis = (ctx, lines, operation) => {
|
|
1030
|
+
const name = defineValue(ctx, operation.result);
|
|
1031
|
+
lines.push(`${name} = gea::runtime::globalThis();`);
|
|
1032
|
+
};
|
|
1033
|
+
/**
|
|
1034
|
+
* A name with no declaration anywhere -- reading it is a ReferenceError, not
|
|
1035
|
+
* a value this backend approximates or boxes (`ir/lower.ts`'s
|
|
1036
|
+
* `unresolvableThrows`). `throwReferenceError<T>()` is `[[noreturn]]`,
|
|
1037
|
+
* templated on this result's declared type, so it type-checks without
|
|
1038
|
+
* producing one -- the `[[noreturn]] void` idiom generalized to a value spot.
|
|
1039
|
+
*/
|
|
1040
|
+
const emitUnresolvableReference = (ctx, lines, operation) => {
|
|
1041
|
+
const name = defineValue(ctx, operation.result);
|
|
1042
|
+
const type = storageTypeOf(ctx, operation.result.id, operation.result.representation);
|
|
1043
|
+
lines.push(`${name} = gea::host::detail::throwReferenceError<${type}>();`);
|
|
1044
|
+
};
|
|
1045
|
+
/** Every non-terminator kind this file lowers. Anything else falls to the `default` and refuses, including kinds added after this file was written. */
|
|
1046
|
+
const emitOperationStatements = (ctx, lines, operation) => {
|
|
1047
|
+
switch (operation.kind) {
|
|
1048
|
+
case 'constant':
|
|
1049
|
+
emitConstant(ctx, lines, operation);
|
|
1050
|
+
return;
|
|
1051
|
+
case 'binding-read':
|
|
1052
|
+
emitBindingRead(ctx, lines, operation);
|
|
1053
|
+
return;
|
|
1054
|
+
case 'binding-write':
|
|
1055
|
+
emitBindingWrite(ctx, lines, operation);
|
|
1056
|
+
return;
|
|
1057
|
+
case 'parameter':
|
|
1058
|
+
emitParameter(ctx, lines, operation);
|
|
1059
|
+
return;
|
|
1060
|
+
case 'receiver':
|
|
1061
|
+
emitReceiver(ctx, lines, operation);
|
|
1062
|
+
return;
|
|
1063
|
+
case 'global-this':
|
|
1064
|
+
emitGlobalThis(ctx, lines, operation);
|
|
1065
|
+
return;
|
|
1066
|
+
case 'unresolvable-reference':
|
|
1067
|
+
emitUnresolvableReference(ctx, lines, operation);
|
|
1068
|
+
return;
|
|
1069
|
+
case 'await':
|
|
1070
|
+
emitAwait(ctx, lines, operation);
|
|
1071
|
+
return;
|
|
1072
|
+
case 'yield':
|
|
1073
|
+
emitYield(ctx, lines, operation);
|
|
1074
|
+
return;
|
|
1075
|
+
case 'compute':
|
|
1076
|
+
emitCompute(ctx, lines, operation);
|
|
1077
|
+
return;
|
|
1078
|
+
case 'call':
|
|
1079
|
+
emitCall(ctx, lines, operation);
|
|
1080
|
+
return;
|
|
1081
|
+
case 'commonjs-require':
|
|
1082
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${cppCommonJsModuleName(operation.target)}();`);
|
|
1083
|
+
return;
|
|
1084
|
+
case 'commonjs-binding':
|
|
1085
|
+
// Publication forces every unproved wrapper read to `dynamic`; a
|
|
1086
|
+
// non-dynamic module result is therefore the consumed source proof, not
|
|
1087
|
+
// a second backend shape heuristic.
|
|
1088
|
+
if (operation.result.representation.kind !== 'dynamic') {
|
|
1089
|
+
if (operation.global !== 'module') {
|
|
1090
|
+
throw createCppEmitBlockedError('native-boundary:commonjs-binding', `native CommonJS record proof reached the "${operation.global}" wrapper; only the exact module record has a native carrier`);
|
|
1091
|
+
}
|
|
1092
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${cppCommonJsRecordName(operation.owner)}();`);
|
|
1093
|
+
}
|
|
1094
|
+
else {
|
|
1095
|
+
lines.push(`${defineValue(ctx, operation.result)} = gea::commonjs::${operation.global}();`);
|
|
1096
|
+
}
|
|
1097
|
+
return;
|
|
1098
|
+
case 'commonjs-binding-set':
|
|
1099
|
+
lines.push(`gea::commonjs::setBinding("${operation.global}", ${operandText(ctx, operation.value)});`);
|
|
1100
|
+
return;
|
|
1101
|
+
case 'super-initialize':
|
|
1102
|
+
emitSuperInitialize(ctx, lines, operation);
|
|
1103
|
+
return;
|
|
1104
|
+
case 'reparent-constructor':
|
|
1105
|
+
lines.push(`gea::reparentNativeClass<${cppClassName(operation.derived)}, ${cppClassName(operation.base)}>(` +
|
|
1106
|
+
`${operandText(ctx, operation.classValue)}.environment, ${operandText(ctx, operation.heritage)}.environment);`);
|
|
1107
|
+
return;
|
|
1108
|
+
case 'allocate-callable':
|
|
1109
|
+
emitAllocateCallable(ctx, lines, operation);
|
|
1110
|
+
return;
|
|
1111
|
+
case 'bind-callable':
|
|
1112
|
+
emitBindCallable(ctx, lines, operation);
|
|
1113
|
+
return;
|
|
1114
|
+
case 'allocate-constructor':
|
|
1115
|
+
emitAllocateConstructor(ctx, lines, operation);
|
|
1116
|
+
return;
|
|
1117
|
+
case 'construct':
|
|
1118
|
+
emitConstruct(ctx, lines, operation);
|
|
1119
|
+
return;
|
|
1120
|
+
case 'get': {
|
|
1121
|
+
const priorLength = ctx.reusedStringLengths.get(operation.result.id);
|
|
1122
|
+
if (priorLength !== undefined) {
|
|
1123
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${operandText(ctx, priorLength)};`);
|
|
1124
|
+
return;
|
|
1125
|
+
}
|
|
1126
|
+
emitGet(ctx, lines, operation);
|
|
1127
|
+
return;
|
|
1128
|
+
}
|
|
1129
|
+
case 'set':
|
|
1130
|
+
emitFieldStore(ctx, classTableLines(ctx, lines, operation.receiver.value), operation, 'set');
|
|
1131
|
+
return;
|
|
1132
|
+
case 'define-own-property':
|
|
1133
|
+
emitFieldStore(ctx, classTableLines(ctx, lines, operation.receiver.value), operation, 'define-own-property');
|
|
1134
|
+
return;
|
|
1135
|
+
case 'spread-copy':
|
|
1136
|
+
emitSpreadCopy(ctx, lines, operation);
|
|
1137
|
+
return;
|
|
1138
|
+
// Answered for the one receiver with a runtime property table, refused by
|
|
1139
|
+
// name for every other carrier; both rules live with the dynamic machinery.
|
|
1140
|
+
case 'delete':
|
|
1141
|
+
emitDeleteOperation(ctx, lines, operation);
|
|
1142
|
+
return;
|
|
1143
|
+
case 'has-property':
|
|
1144
|
+
emitHasProperty(ctx, lines, operation);
|
|
1145
|
+
return;
|
|
1146
|
+
case 'allocate-ordinary-object':
|
|
1147
|
+
lines.push(`${defineValue(ctx, operation.result)} = gea::Value::object();`);
|
|
1148
|
+
return;
|
|
1149
|
+
case 'allocate-record':
|
|
1150
|
+
emitAllocateRecord(ctx, classTableLines(ctx, lines, operation.result.id), operation);
|
|
1151
|
+
return;
|
|
1152
|
+
case 'allocate-regexp':
|
|
1153
|
+
emitAllocateRegExp(ctx, lines, operation);
|
|
1154
|
+
return;
|
|
1155
|
+
case 'allocate-template-object':
|
|
1156
|
+
emitAllocateTemplateObject(ctx, lines, operation);
|
|
1157
|
+
return;
|
|
1158
|
+
case 'allocate-array-object':
|
|
1159
|
+
emitAllocateArrayObject(ctx, lines, operation);
|
|
1160
|
+
return;
|
|
1161
|
+
case 'get-iterator':
|
|
1162
|
+
emitGetIterator(ctx, lines, operation);
|
|
1163
|
+
return;
|
|
1164
|
+
case 'iterator-next':
|
|
1165
|
+
emitIteratorNext(ctx, lines, operation);
|
|
1166
|
+
return;
|
|
1167
|
+
case 'iterator-done':
|
|
1168
|
+
emitIteratorDone(ctx, lines, operation);
|
|
1169
|
+
return;
|
|
1170
|
+
case 'iterator-close':
|
|
1171
|
+
emitIteratorClose(ctx, lines, operation);
|
|
1172
|
+
return;
|
|
1173
|
+
case 'element':
|
|
1174
|
+
emitElement(ctx, lines, operation);
|
|
1175
|
+
return;
|
|
1176
|
+
case 'element-prop':
|
|
1177
|
+
emitElementProp(ctx, lines, operation);
|
|
1178
|
+
return;
|
|
1179
|
+
case 'element-child':
|
|
1180
|
+
emitElementChild(ctx, lines, operation);
|
|
1181
|
+
return;
|
|
1182
|
+
case 'test':
|
|
1183
|
+
emitTest(ctx, lines, operation);
|
|
1184
|
+
return;
|
|
1185
|
+
case 'convert':
|
|
1186
|
+
emitConvert(ctx, lines, operation);
|
|
1187
|
+
return;
|
|
1188
|
+
case 'merge-live-arm-rebuild':
|
|
1189
|
+
emitMergeLiveArmRebuild(ctx, lines, operation);
|
|
1190
|
+
return;
|
|
1191
|
+
case 'phi':
|
|
1192
|
+
// The variable is declared with every other local and written by each
|
|
1193
|
+
// predecessor; there is nothing left to render where it is read.
|
|
1194
|
+
return;
|
|
1195
|
+
default:
|
|
1196
|
+
throw createCppEmitBlockedError(`runtime-helper:operation:${operation.kind}`, `no C++ rendering is implemented for the "${operation.kind}" operation yet`);
|
|
1197
|
+
}
|
|
1198
|
+
};
|
|
1199
|
+
/** A block's assigned `goto` label. Absence means a jump names a block outside this body's own `blockOrder`, an IR-verification bug, not a rendering gap. */
|
|
1200
|
+
const requireBlockLabel = (labels, block) => {
|
|
1201
|
+
const label = labels.get(block);
|
|
1202
|
+
if (label === undefined)
|
|
1203
|
+
throw new Error(`ir block ${block} has no assigned label; every block in this body must be labeled before emission`);
|
|
1204
|
+
return label;
|
|
1205
|
+
};
|
|
1206
|
+
/**
|
|
1207
|
+
* `text` with one outermost parenthesis layer removed, when that layer wraps
|
|
1208
|
+
* the whole of it.
|
|
1209
|
+
*
|
|
1210
|
+
* `operandText` renders a binary expression fully parenthesized, because it
|
|
1211
|
+
* does not know what it will be spliced into and precedence is not something a
|
|
1212
|
+
* renderer may guess at. A branch condition is the one place that layer is
|
|
1213
|
+
* always redundant: `if (...)` is itself a grouping, so `if (((b0) == (3)))`
|
|
1214
|
+
* says exactly what `if ((b0) == (3))` says -- and clang reads the first as a
|
|
1215
|
+
* person who meant `=` and wrote `==`, raising -Wparentheses-equality on every
|
|
1216
|
+
* one. Measured across the emitted units in this tree: roughly 350 of them,
|
|
1217
|
+
* 61 in `button-tetris` alone. Not a large number, but a warning that is always
|
|
1218
|
+
* present is a warning nobody reads.
|
|
1219
|
+
*
|
|
1220
|
+
* Only the OUTER layer, and only when it really is one: the scan below returns
|
|
1221
|
+
* the text unchanged for `(a) == (b)`, whose first parenthesis closes at index
|
|
1222
|
+
* 2, and for a leading cast like `(bool)(x)`. Quoted runs are skipped whole,
|
|
1223
|
+
* because an emitted string literal may contain either parenthesis --
|
|
1224
|
+
* `gea_class_decl_f3_545` and every other name literal proves nothing about
|
|
1225
|
+
* nesting.
|
|
1226
|
+
*/
|
|
1227
|
+
const unwrapConditionParentheses = (text) => {
|
|
1228
|
+
if (text.length < 2 || text[0] !== '(' || text[text.length - 1] !== ')')
|
|
1229
|
+
return text;
|
|
1230
|
+
let depth = 0;
|
|
1231
|
+
let quote = '';
|
|
1232
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
1233
|
+
const character = text[index];
|
|
1234
|
+
if (quote !== '') {
|
|
1235
|
+
if (character === '\\')
|
|
1236
|
+
index += 1;
|
|
1237
|
+
else if (character === quote)
|
|
1238
|
+
quote = '';
|
|
1239
|
+
continue;
|
|
1240
|
+
}
|
|
1241
|
+
if (character === '"' || character === "'") {
|
|
1242
|
+
quote = character;
|
|
1243
|
+
continue;
|
|
1244
|
+
}
|
|
1245
|
+
if (character === '(')
|
|
1246
|
+
depth += 1;
|
|
1247
|
+
else if (character === ')') {
|
|
1248
|
+
depth -= 1;
|
|
1249
|
+
// Closed before the end, so the opening parenthesis wrapped a part and
|
|
1250
|
+
// not the whole -- there is nothing redundant to take off.
|
|
1251
|
+
if (depth === 0 && index !== text.length - 1)
|
|
1252
|
+
return text;
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
// An unbalanced count means the text is not what this reads it as (an
|
|
1256
|
+
// unterminated literal, say). Leave it exactly as rendered.
|
|
1257
|
+
return depth === 0 ? text.slice(1, -1) : text;
|
|
1258
|
+
};
|
|
1259
|
+
/**
|
|
1260
|
+
* The `goto`s a `jump` or `branch` renders, given the label of the block
|
|
1261
|
+
* rendered immediately after it (`null` when nothing follows or the caller
|
|
1262
|
+
* does not know).
|
|
1263
|
+
*
|
|
1264
|
+
* Two `goto`s rather than an `if`/`else` pair: the blocks this names are
|
|
1265
|
+
* siblings in one flat sequence, and wrapping either in a C++ block would put
|
|
1266
|
+
* every declaration inside it out of the other blocks' reach. Within that
|
|
1267
|
+
* shape, three things spell less than the IR says:
|
|
1268
|
+
*
|
|
1269
|
+
* - a jump to the very next block is the fall-through it already is and
|
|
1270
|
+
* spells nothing (`goto block1; block1:` was every loop's entry);
|
|
1271
|
+
* - a branch on a literal -- `while (true)`, `for (;;)` -- and a branch whose
|
|
1272
|
+
* two arms thread to one block are each a jump, and render as one: `if
|
|
1273
|
+
* (true) goto block3;` leaves the untaken arm behind as a block nothing
|
|
1274
|
+
* reaches, which `threadedFlowOf` has already left out;
|
|
1275
|
+
* - a branch one of whose arms is the next block spells only the other arm,
|
|
1276
|
+
* negating the condition when the TAKEN arm is the one that falls through.
|
|
1277
|
+
*/
|
|
1278
|
+
const spelledExitsOf = (ctx, labels, terminator, nextLabel) => {
|
|
1279
|
+
if (terminator.kind === 'jump') {
|
|
1280
|
+
const label = requireBlockLabel(labels, terminator.target);
|
|
1281
|
+
return label === nextLabel ? [] : [{ label, condition: 'always' }];
|
|
1282
|
+
}
|
|
1283
|
+
if (terminator.kind !== 'branch')
|
|
1284
|
+
return [];
|
|
1285
|
+
const whenTrue = requireBlockLabel(labels, terminator.whenTrue);
|
|
1286
|
+
const whenFalse = requireBlockLabel(labels, terminator.whenFalse);
|
|
1287
|
+
const known = ctx.booleanConstants.get(terminator.condition.value);
|
|
1288
|
+
if (known !== undefined || whenTrue === whenFalse) {
|
|
1289
|
+
const only = known === false ? whenFalse : whenTrue;
|
|
1290
|
+
return only === nextLabel ? [] : [{ label: only, condition: 'always' }];
|
|
1291
|
+
}
|
|
1292
|
+
if (whenFalse === nextLabel)
|
|
1293
|
+
return [{ label: whenTrue, condition: 'when-true' }];
|
|
1294
|
+
if (whenTrue === nextLabel)
|
|
1295
|
+
return [{ label: whenFalse, condition: 'when-false' }];
|
|
1296
|
+
return [
|
|
1297
|
+
{ label: whenTrue, condition: 'when-true' },
|
|
1298
|
+
{ label: whenFalse, condition: 'always' }
|
|
1299
|
+
];
|
|
1300
|
+
};
|
|
1301
|
+
const emitTerminator = (ctx, lines, labels, isSingleBlock, terminator, nextLabel = null) => {
|
|
1302
|
+
switch (terminator.kind) {
|
|
1303
|
+
case 'jump':
|
|
1304
|
+
case 'branch': {
|
|
1305
|
+
// A single-block body has no other block to name: falling off the end
|
|
1306
|
+
// of this text is exactly what the jump means, so it renders as nothing
|
|
1307
|
+
// rather than a `goto` to a label this file never assigns.
|
|
1308
|
+
if (isSingleBlock)
|
|
1309
|
+
return;
|
|
1310
|
+
const condition = terminator.kind === 'branch' ? terminator.condition : null;
|
|
1311
|
+
for (const exit of spelledExitsOf(ctx, labels, terminator, nextLabel)) {
|
|
1312
|
+
if (exit.condition === 'always' || condition === null) {
|
|
1313
|
+
lines.push(`goto ${exit.label};`);
|
|
1314
|
+
continue;
|
|
1315
|
+
}
|
|
1316
|
+
const text = unwrapConditionParentheses(operandText(ctx, condition));
|
|
1317
|
+
lines.push(exit.condition === 'when-true' ? `if (${text}) goto ${exit.label};` : `if (!(${text})) goto ${exit.label};`);
|
|
1318
|
+
}
|
|
1319
|
+
return;
|
|
1320
|
+
}
|
|
1321
|
+
case 'return':
|
|
1322
|
+
emitReturn(ctx, lines, terminator);
|
|
1323
|
+
return;
|
|
1324
|
+
case 'throw':
|
|
1325
|
+
// A C++ `throw` of the value's own carrier, not of a boxed wrapper: what
|
|
1326
|
+
// the program throws is whatever it constructed, and re-typing it on the
|
|
1327
|
+
// way out would make the thing a handler catches different from the thing
|
|
1328
|
+
// the `throw` statement named.
|
|
1329
|
+
//
|
|
1330
|
+
// No handler is claimed alongside this. That is not an oversight: an
|
|
1331
|
+
// uncaught `throw` terminating the process is exactly what an uncaught
|
|
1332
|
+
// ECMAScript exception does, so this is complete on its own, while `try`
|
|
1333
|
+
// needs a catch-side carrier decision this does not make and does not
|
|
1334
|
+
// pre-empt.
|
|
1335
|
+
lines.push(`throw ${operandText(ctx, terminator.value)};`);
|
|
1336
|
+
// A generator whose body throws before it ever yields or returns
|
|
1337
|
+
// (`function* () { throw new Test262Error() }`) has no other keyword to
|
|
1338
|
+
// make it a coroutine, and C++ would compile it as a plain function
|
|
1339
|
+
// that throws AT THE CALL rather than on the first `next()`. The
|
|
1340
|
+
// unreachable `co_return` after the throw is what keeps the frame a
|
|
1341
|
+
// coroutine, so the throw surfaces where the language says it does.
|
|
1342
|
+
if (ctx.generatorBody)
|
|
1343
|
+
lines.push('co_return;');
|
|
1344
|
+
return;
|
|
1345
|
+
case 'switch':
|
|
1346
|
+
throw createCppEmitBlockedError(`runtime-helper:control:${terminator.kind}`, `no C++ rendering is implemented for the "${terminator.kind}" terminator yet`);
|
|
1347
|
+
}
|
|
1348
|
+
};
|
|
1349
|
+
/**
|
|
1350
|
+
* One operation's statements, withheld where the census proved they may
|
|
1351
|
+
* render at the use instead.
|
|
1352
|
+
*
|
|
1353
|
+
* The withholding is recognized from what the emitter did, not predicted: a
|
|
1354
|
+
* kind that renders exactly one assignment to the name it just minted is the
|
|
1355
|
+
* only shape that can move, and any emitter that rendered anything else --
|
|
1356
|
+
* several statements, a name some other construct declares, nothing at all --
|
|
1357
|
+
* falls through unchanged. That is what keeps this a rendering choice rather
|
|
1358
|
+
* than a second authority over what an operation means: it can only ever
|
|
1359
|
+
* decline.
|
|
1360
|
+
*/
|
|
1361
|
+
/**
|
|
1362
|
+
* Where a candidate class table's own lines go: into that table's buffer, so
|
|
1363
|
+
* the `class` prop can drop them if it spells the table away. See
|
|
1364
|
+
* `EmitContext.classTableRoots`.
|
|
1365
|
+
*/
|
|
1366
|
+
const classTableLines = (ctx, lines, value) => {
|
|
1367
|
+
const root = ctx.classTableRoots.get(value);
|
|
1368
|
+
if (root === undefined)
|
|
1369
|
+
return lines;
|
|
1370
|
+
const pending = ctx.pendingClassTableLines.get(root);
|
|
1371
|
+
if (pending !== undefined)
|
|
1372
|
+
return pending;
|
|
1373
|
+
const opened = [];
|
|
1374
|
+
ctx.pendingClassTableLines.set(root, opened);
|
|
1375
|
+
return opened;
|
|
1376
|
+
};
|
|
1377
|
+
const emitOperation = (ctx, lines, operation) => {
|
|
1378
|
+
try {
|
|
1379
|
+
const result = resultOfIrOperation(operation);
|
|
1380
|
+
if (result !== null && ctx.deadValues.has(result.id)) {
|
|
1381
|
+
settleDeadCalleeSideEffects(ctx, operation);
|
|
1382
|
+
return;
|
|
1383
|
+
}
|
|
1384
|
+
if (result === null || !ctx.deferrable.has(result.id)) {
|
|
1385
|
+
emitOperationStatements(ctx, lines, operation);
|
|
1386
|
+
return;
|
|
1387
|
+
}
|
|
1388
|
+
const lineMark = lines.length;
|
|
1389
|
+
const declarationMark = ctx.declarations.length;
|
|
1390
|
+
emitOperationStatements(ctx, lines, operation);
|
|
1391
|
+
// An array literal withheld whole records its own elements instead
|
|
1392
|
+
// (`emit-arrays.ts`) and has neither a name nor a statement to reclaim.
|
|
1393
|
+
if (ctx.pendingPacks.has(result.id))
|
|
1394
|
+
return;
|
|
1395
|
+
const name = ctx.valueNames.get(result.id);
|
|
1396
|
+
if (name === undefined)
|
|
1397
|
+
return;
|
|
1398
|
+
if (lines.length !== lineMark + 1 || ctx.declarations.length !== declarationMark + 1)
|
|
1399
|
+
return;
|
|
1400
|
+
if (ctx.declarations[declarationMark]?.name !== name)
|
|
1401
|
+
return;
|
|
1402
|
+
const statement = lines[lineMark] ?? '';
|
|
1403
|
+
const prefix = `${name} = `;
|
|
1404
|
+
if (!statement.startsWith(prefix) || !statement.endsWith(';'))
|
|
1405
|
+
return;
|
|
1406
|
+
ctx.declarations.pop();
|
|
1407
|
+
lines.pop();
|
|
1408
|
+
ctx.deferredTexts.set(result.id, `(${statement.slice(prefix.length, statement.length - 1)})`);
|
|
1409
|
+
}
|
|
1410
|
+
catch (error) {
|
|
1411
|
+
if (isCppEmitBlockedError(error)) {
|
|
1412
|
+
const result = resultOfIrOperation(operation);
|
|
1413
|
+
error.message += ` while emitting ${operation.kind}${result === null ? '' : ` -> ${result.id}`}${operation.lineage === null ? '' : ` from ${operation.lineage}`}`;
|
|
1414
|
+
}
|
|
1415
|
+
if (error instanceof Error && error.message.startsWith('ir value ')) {
|
|
1416
|
+
const result = resultOfIrOperation(operation);
|
|
1417
|
+
error.message += ` while emitting ${operation.kind}${result === null ? '' : ` -> ${result.id}`}${operation.lineage === null ? '' : ` from ${operation.lineage}`}`;
|
|
1418
|
+
}
|
|
1419
|
+
throw error;
|
|
1420
|
+
}
|
|
1421
|
+
};
|
|
1422
|
+
/**
|
|
1423
|
+
* What a dead callee producer still has to do, now that `ctx.directCallees`
|
|
1424
|
+
* itself is populated up front from `CallOperation.target` (`emitBody`'s own
|
|
1425
|
+
* walk, above) rather than by each producer's live emitter as a side effect
|
|
1426
|
+
* of rendering: nothing about NAMING the callee is left for this function,
|
|
1427
|
+
* only the two side effects that are tied to the producer's OWN skipped
|
|
1428
|
+
* render rather than to the eventual call.
|
|
1429
|
+
*
|
|
1430
|
+
* `ir/dead-values.ts` counts a directly-called callee as unread precisely
|
|
1431
|
+
* because `emit-callable.ts` spells such a call as `body(args)`, and it
|
|
1432
|
+
* decides that with its own `directCallee` predicate -- a copy of the same
|
|
1433
|
+
* three tests (allocate-callable/binding-read/get) this file used to restate
|
|
1434
|
+
* here as writes. That predicate is unrelated to `CallOperation.target` (it
|
|
1435
|
+
* answers for a PRODUCER value, not a call) and stays as its own render-time
|
|
1436
|
+
* decision; only the direct-callee NAME it used to also mint moved to the
|
|
1437
|
+
* `emitBody`-level pass.
|
|
1438
|
+
*/
|
|
1439
|
+
const settleDeadCalleeSideEffects = (ctx, operation) => {
|
|
1440
|
+
if (operation.kind === 'allocate-callable') {
|
|
1441
|
+
if (ctx.captures.of(operation.functionId).kind === 'none') {
|
|
1442
|
+
// What is left here once `thunkValues` is a sealed fact
|
|
1443
|
+
// (`ir/facts.ts`'s `bodyValueOriginsOf`): the thunk's TEXT. A captureless
|
|
1444
|
+
// thunk is exactly the one the dead-value rule elides -- its only call
|
|
1445
|
+
// is by name -- so its producer never renders, and the one use that
|
|
1446
|
+
// does need a value has to be able to spell it. It is spelled as the
|
|
1447
|
+
// prvalue it is, a body pointer and a null environment.
|
|
1448
|
+
//
|
|
1449
|
+
// The registration this used to also do is why the field moved: the
|
|
1450
|
+
// reader is `emit-jsx.ts`'s `reactiveThunkPlan`, which re-runs the thunk
|
|
1451
|
+
// behind a slot's call on every change of a cell that body reads, and a
|
|
1452
|
+
// registration performed as a side effect of rendering was missing for
|
|
1453
|
+
// exactly the producer whose render is skipped. Every such slot fell to
|
|
1454
|
+
// the once-only path -- the piece in button-tetris drew at its spawn
|
|
1455
|
+
// position and never moved, while the plain-text FPS slot beside it kept
|
|
1456
|
+
// updating.
|
|
1457
|
+
const carrier = operation.result.representation;
|
|
1458
|
+
if (carrier.kind === 'function-value-dispatch' && carrier.abi.parameters.length === 0 && carrier.abi.receiver === null) {
|
|
1459
|
+
ctx.deferredTexts.set(operation.result.id, `${cppTypeOf(carrier)}{&${cppThunkName(operation.functionId)}, nullptr}`);
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
return;
|
|
1463
|
+
}
|
|
1464
|
+
};
|
|
1465
|
+
const threadedFlowOf = (ctx, body, labels, hoists, mergeWrites) => {
|
|
1466
|
+
if (body.tryRegions.length > 0 || (body.iteratorCloseRegions?.length ?? 0) > 0 || body.blockOrder.length === 1) {
|
|
1467
|
+
return { labels, skipped: new Set(), labeled: new Set(labels.values()), nextLabel: new Map() };
|
|
1468
|
+
}
|
|
1469
|
+
// A literal is spelled at its uses (`spellConstants`) and a merge is written
|
|
1470
|
+
// by its predecessors (`mergeWritesOf`): neither renders where it sits. A
|
|
1471
|
+
// read hoisted INTO a block renders there -- unless it is one of those, as
|
|
1472
|
+
// the `true` of a `while (true)` hoisted to the loop's preheader is.
|
|
1473
|
+
const rendersNothingAtOrigin = (operation) => {
|
|
1474
|
+
if (operation.kind === 'constant' || operation.kind === 'phi')
|
|
1475
|
+
return true;
|
|
1476
|
+
const result = resultOfIrOperation(operation);
|
|
1477
|
+
return result !== null && (ctx.deadValues.has(result.id) || hoists.relocated.has(result.id));
|
|
1478
|
+
};
|
|
1479
|
+
const rendersNothingWhenHoisted = (operation) => {
|
|
1480
|
+
if (operation.kind === 'constant' || operation.kind === 'phi')
|
|
1481
|
+
return true;
|
|
1482
|
+
const result = resultOfIrOperation(operation);
|
|
1483
|
+
return result !== null && ctx.deadValues.has(result.id);
|
|
1484
|
+
};
|
|
1485
|
+
const rendersNothing = (block) => block.operations.every(rendersNothingAtOrigin) &&
|
|
1486
|
+
(mergeWrites.get(block.id) ?? []).length === 0 &&
|
|
1487
|
+
// A relocated operation renders here, in its destination block. Counting
|
|
1488
|
+
// it as absent a second time lets the flow thread this block away and
|
|
1489
|
+
// leaves later users with no dominating C++ definition.
|
|
1490
|
+
(hoists.into.get(block.id) ?? []).every(rendersNothingWhenHoisted) &&
|
|
1491
|
+
!ctx.fillLoops.has(block.id) &&
|
|
1492
|
+
![...ctx.denseGroups.values()].some((group) => group.preheader === block.id);
|
|
1493
|
+
const forwarding = new Map();
|
|
1494
|
+
for (const block of body.blocks.values()) {
|
|
1495
|
+
if (block.id !== body.entry && block.terminator.kind === 'jump' && rendersNothing(block))
|
|
1496
|
+
forwarding.set(block.id, block.terminator.target);
|
|
1497
|
+
}
|
|
1498
|
+
const resolve = (id) => {
|
|
1499
|
+
const seen = new Set();
|
|
1500
|
+
let at = id;
|
|
1501
|
+
while (forwarding.has(at) && !seen.has(at)) {
|
|
1502
|
+
seen.add(at);
|
|
1503
|
+
at = forwarding.get(at) ?? at;
|
|
1504
|
+
}
|
|
1505
|
+
return at;
|
|
1506
|
+
};
|
|
1507
|
+
for (let settled = false; !settled;) {
|
|
1508
|
+
settled = true;
|
|
1509
|
+
for (const id of [...forwarding.keys()]) {
|
|
1510
|
+
if (!forwarding.has(resolve(id)))
|
|
1511
|
+
continue;
|
|
1512
|
+
forwarding.delete(id);
|
|
1513
|
+
settled = false;
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
const aliased = new Map(labels);
|
|
1517
|
+
for (const id of forwarding.keys())
|
|
1518
|
+
aliased.set(id, requireBlockLabel(labels, resolve(id)));
|
|
1519
|
+
const successorsOf = (block) => {
|
|
1520
|
+
const fill = ctx.fillLoops.get(block.id);
|
|
1521
|
+
if (fill)
|
|
1522
|
+
return [fill.exit];
|
|
1523
|
+
const terminator = block.terminator;
|
|
1524
|
+
switch (terminator.kind) {
|
|
1525
|
+
case 'jump':
|
|
1526
|
+
return [terminator.target];
|
|
1527
|
+
case 'branch': {
|
|
1528
|
+
const known = ctx.booleanConstants.get(terminator.condition.value);
|
|
1529
|
+
return known === undefined ? [terminator.whenTrue, terminator.whenFalse] : [known ? terminator.whenTrue : terminator.whenFalse];
|
|
1530
|
+
}
|
|
1531
|
+
case 'switch':
|
|
1532
|
+
return [...terminator.cases.map((entry) => entry.target), terminator.defaultTarget];
|
|
1533
|
+
default:
|
|
1534
|
+
return [];
|
|
1535
|
+
}
|
|
1536
|
+
};
|
|
1537
|
+
const reachable = new Set();
|
|
1538
|
+
const pending = [body.entry];
|
|
1539
|
+
while (pending.length > 0) {
|
|
1540
|
+
const id = pending.pop();
|
|
1541
|
+
if (id === undefined || reachable.has(id))
|
|
1542
|
+
continue;
|
|
1543
|
+
reachable.add(id);
|
|
1544
|
+
const block = body.blocks.get(id);
|
|
1545
|
+
if (!block)
|
|
1546
|
+
continue;
|
|
1547
|
+
for (const next of successorsOf(block)) {
|
|
1548
|
+
const target = resolve(next);
|
|
1549
|
+
if (!reachable.has(target))
|
|
1550
|
+
pending.push(target);
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
const rendered = body.blockOrder.filter((id) => reachable.has(id));
|
|
1554
|
+
const nextLabel = new Map();
|
|
1555
|
+
rendered.forEach((id, index) => {
|
|
1556
|
+
const next = rendered[index + 1];
|
|
1557
|
+
if (next !== undefined)
|
|
1558
|
+
nextLabel.set(id, requireBlockLabel(aliased, next));
|
|
1559
|
+
});
|
|
1560
|
+
const labeled = new Set();
|
|
1561
|
+
for (const id of rendered) {
|
|
1562
|
+
const block = body.blocks.get(id);
|
|
1563
|
+
if (!block)
|
|
1564
|
+
continue;
|
|
1565
|
+
// A counted fill loop's header always spells the `goto` to its exit (`emitFillLoop`).
|
|
1566
|
+
const fill = ctx.fillLoops.get(id);
|
|
1567
|
+
if (fill) {
|
|
1568
|
+
labeled.add(requireBlockLabel(aliased, fill.exit));
|
|
1569
|
+
continue;
|
|
1570
|
+
}
|
|
1571
|
+
for (const exit of spelledExitsOf(ctx, aliased, block.terminator, nextLabel.get(id) ?? null))
|
|
1572
|
+
labeled.add(exit.label);
|
|
1573
|
+
}
|
|
1574
|
+
return { labels: aliased, skipped: new Set(body.blockOrder.filter((id) => !reachable.has(id))), labeled, nextLabel };
|
|
1575
|
+
};
|
|
1576
|
+
/** One `blockN` label per block, in `blockOrder`, so a forward jump (a loop back-edge included) always resolves before any block renders. */
|
|
1577
|
+
const blockLabelsOf = (order) => {
|
|
1578
|
+
const labels = new Map();
|
|
1579
|
+
order.forEach((block, index) => labels.set(block, `block${index}`));
|
|
1580
|
+
return labels;
|
|
1581
|
+
};
|
|
1582
|
+
/**
|
|
1583
|
+
* The C++ statements one lowered body renders to, as artifacts for the
|
|
1584
|
+
* emission boundary.
|
|
1585
|
+
*
|
|
1586
|
+
* `hostMembers` is every host spelling this compilation installed -- the
|
|
1587
|
+
* backend's own plus every plugin's. It is a parameter rather than an import
|
|
1588
|
+
* because which host is in front of the compiler is a property of the
|
|
1589
|
+
* compilation, not of this file.
|
|
1590
|
+
*
|
|
1591
|
+
* `deriver` is what `targets/cpp/emit-properties.ts` asks for a receiver's
|
|
1592
|
+
* own declared field representation, to widen a concrete value into a
|
|
1593
|
+
* `dynamic` field the same way this function's own `return` handling widens
|
|
1594
|
+
* into a `dynamic` ABI result -- see citations.md finding 1.
|
|
1595
|
+
*
|
|
1596
|
+
* `captures` defaults to `emptyCaptureIndex` (every owner closes over
|
|
1597
|
+
* nothing) so every existing caller keeps compiling and behaving exactly as
|
|
1598
|
+
* before; `translation-unit.ts` is the one caller that has an index to pass,
|
|
1599
|
+
* built once for the whole program by `targets/cpp/captures.ts`.
|
|
1600
|
+
*/
|
|
1601
|
+
/**
|
|
1602
|
+
* The namespace every incoming value of a merge stands for, or `null`.
|
|
1603
|
+
*
|
|
1604
|
+
* `null` unless at least one arm is a namespace and every other arm is either
|
|
1605
|
+
* the same namespace or an absent constant -- see the call site for why an
|
|
1606
|
+
* absent arm is the one thing that may accompany it.
|
|
1607
|
+
*/
|
|
1608
|
+
export const emitBody = (body, placements, classes, hosts, deriver, wellKnownSymbols = new Map(), captures = emptyCaptureIndex,
|
|
1609
|
+
// No default: `symbolKeys`/`templateObjects` are ONE table for the whole
|
|
1610
|
+
// program (`translation-unit.ts`'s own comment on its `templateObjects`
|
|
1611
|
+
// says why -- a tagged-template site's object must be the same one across
|
|
1612
|
+
// every body that reaches it). A default `new Map()` here would be a
|
|
1613
|
+
// second creation site for state that has exactly one legitimate owner;
|
|
1614
|
+
// `translation-unit.ts` is this function's only caller and always passes
|
|
1615
|
+
// its own program-wide map, so the default was dead and only invited a
|
|
1616
|
+
// future caller to silently get an empty, per-call table instead of the
|
|
1617
|
+
// shared one (scripts/architecture.mjs's EmitContext Map/Set check).
|
|
1618
|
+
symbolKeys, templateObjects, directCallableBindings = new Map(),
|
|
1619
|
+
// `virtualDispatch` is every `class key` with a dispatch member (`virtual-methods.ts`);
|
|
1620
|
+
// `narrowedStorage`/`narrowedFormals` are what the integer census settled here.
|
|
1621
|
+
virtualDispatch = new Map(), narrowedStorage = { reads: new Map(), integral: new Set(), magnitudes: new Map() }, narrowedFormals = new Set(), repeatedConstructors = new Map(), dyingArguments = new Set(), instantiation = noInstantiationFacts, abiOfCallable = () => null, hostMethodAliases = new Map(), callableMemberCandidates = new Map(), borrowableMemberBodies = new Set(), stableBorrowEntries = new Map(), printerDrift = [], conversions = null, nativeSelections = undefined, callableIdentityDemand = observesEveryCallableIdentity, nativeIntegrityRestricted = true, fixedFieldStateConstant = false) => {
|
|
1622
|
+
// Every fact this body settles before a single line renders, computed here
|
|
1623
|
+
// -- from `body` and the plain, already-available inputs above -- and
|
|
1624
|
+
// handed to `createEmitContext` as one argument instead of mutated onto the
|
|
1625
|
+
// context afterward. See `EmitBodyFacts`'s own doc
|
|
1626
|
+
// (the no-write-during-render
|
|
1627
|
+
// rule") for why this stopped being twelve `ctx.field.add(...)` calls.
|
|
1628
|
+
const stableBorrowActuals = stableBorrowActualsOf(body, (declaration) => {
|
|
1629
|
+
const placement = placements.get(declaration);
|
|
1630
|
+
return placement?.storage.kind === 'local' && placement.storage.owner === body.sourceOwner && !captures.isBoxed(declaration)
|
|
1631
|
+
? placement.representation
|
|
1632
|
+
: null;
|
|
1633
|
+
});
|
|
1634
|
+
// A read has to know what the REST of the body does to the cell it reads.
|
|
1635
|
+
// `emit-bindings.ts` lets a host object's read name its cell rather than
|
|
1636
|
+
// copy out of it, and that identity only holds while the cell keeps
|
|
1637
|
+
// pointing at the one object -- which a body that rebinds it cannot
|
|
1638
|
+
// promise, and which the linear emit would otherwise discover only after
|
|
1639
|
+
// the read had already rendered.
|
|
1640
|
+
const receiverValues = new Set();
|
|
1641
|
+
const bindingWriteCounts = new Map();
|
|
1642
|
+
let callsSuper = false;
|
|
1643
|
+
for (const block of body.blocks.values()) {
|
|
1644
|
+
for (const operation of allOperationsOf(block)) {
|
|
1645
|
+
// Censused in the same pass, for `EmitBodyFacts.receiverValues`: a
|
|
1646
|
+
// member access has to know whether its receiver came from `super`, and
|
|
1647
|
+
// the operation that defines it is the only place that says so.
|
|
1648
|
+
if (operation.kind === 'receiver')
|
|
1649
|
+
receiverValues.add(operation.result.id);
|
|
1650
|
+
if (operation.kind === 'super-initialize')
|
|
1651
|
+
callsSuper = true;
|
|
1652
|
+
if (operation.kind !== 'binding-write')
|
|
1653
|
+
continue;
|
|
1654
|
+
bindingWriteCounts.set(operation.declaration, (bindingWriteCounts.get(operation.declaration) ?? 0) + 1);
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
const owningClass = [...classes.values()].find((layout) => layout.constructor === body.sourceOwner) ?? null;
|
|
1658
|
+
const constructorOf = owningClass
|
|
1659
|
+
? { layout: owningClass, derived: constructedBaseOf(owningClass) !== null || owningClass.nativeBase !== null, callsSuper }
|
|
1660
|
+
: null;
|
|
1661
|
+
const formalInputs = ownedFormalInputsOf(body);
|
|
1662
|
+
const ownedDyingValues = new Set([...ownedDyingValuesOf(body), ...formalInputs.arguments]);
|
|
1663
|
+
const origins = bodyValueOriginsOf(body);
|
|
1664
|
+
const hostClassReads = hostClassReadsOf(body, placements);
|
|
1665
|
+
const classObjectReads = classObjectReadsOf(body, placements);
|
|
1666
|
+
const hostNamespaces = hostNamespaceReadsOf(body, placements, hosts, origins.staticKeyTexts);
|
|
1667
|
+
// Settled here rather than as a post-construction merge (invariant 5, 2.3):
|
|
1668
|
+
// both are pure walks over `body` plus a plain, already-available input --
|
|
1669
|
+
// `staticKeyTexts` (== `origins.staticKeyTexts`) and `abiOfCallable` -- with
|
|
1670
|
+
// no dependency on `ctx` at all, unlike the seventeen `EmitBodyPrepassFacts`
|
|
1671
|
+
// fields below, whose collectors genuinely do need one.
|
|
1672
|
+
const functionSources = functionSourceReadsOf(origins.staticKeyTexts, body);
|
|
1673
|
+
const directCalleeFacts = directCalleesOf(body, abiOfCallable);
|
|
1674
|
+
const bodyFacts = {
|
|
1675
|
+
generatorBody: body.generator === true,
|
|
1676
|
+
receiverValues,
|
|
1677
|
+
bindingWriteCounts,
|
|
1678
|
+
constructorOf,
|
|
1679
|
+
ownedDyingValues,
|
|
1680
|
+
consumingFormalConversions: formalInputs.conversions,
|
|
1681
|
+
classTableRoots: classTableRootsOf(body),
|
|
1682
|
+
stableBorrowActuals,
|
|
1683
|
+
computeOrigins: origins.computeOrigins,
|
|
1684
|
+
propertyReadOrigins: origins.propertyReadOrigins,
|
|
1685
|
+
bindingReadDeclarations: origins.bindingReadDeclarations,
|
|
1686
|
+
conversionSources: origins.conversionSources,
|
|
1687
|
+
callCallees: origins.callCallees,
|
|
1688
|
+
calleeOnlyValues: origins.calleeOnlyValues,
|
|
1689
|
+
recordFieldSources: origins.recordFieldSources,
|
|
1690
|
+
thunkValues: origins.thunkValues,
|
|
1691
|
+
hostClassReads,
|
|
1692
|
+
classObjectReads,
|
|
1693
|
+
// Every deferred host member read, decided from the IR, the unioned host
|
|
1694
|
+
// tables and `hostClassReads` -- all settled -- before this body renders a
|
|
1695
|
+
// line. The census was measured against the render-time map it replaces on
|
|
1696
|
+
// both emitted sets (154 + 281 programs): no read the printer recorded was
|
|
1697
|
+
// missing from it and none differed, and every extra was the unconditional
|
|
1698
|
+
// `<value>:present` twin nothing reads unless a narrowing mints one.
|
|
1699
|
+
hostMemberReads: hostMemberReadsOf(body, hosts, origins.staticKeyTexts, hostClassReads, hostMethodAliases),
|
|
1700
|
+
valueCellReads: origins.valueCellReads,
|
|
1701
|
+
staticKeyTexts: origins.staticKeyTexts,
|
|
1702
|
+
hostNamespaceReads: hostNamespaces.reads,
|
|
1703
|
+
hostNamespaceValues: hostNamespaces.values,
|
|
1704
|
+
hostFunctionReads: hostNamespaces.functionReads,
|
|
1705
|
+
functionSourceReads: functionSources.reads,
|
|
1706
|
+
functionSourceSnapshotNames: functionSources.names,
|
|
1707
|
+
directCallees: directCalleeFacts.directCallees,
|
|
1708
|
+
directCalleeAbis: directCalleeFacts.directCalleeAbis
|
|
1709
|
+
};
|
|
1710
|
+
const { ctx, prepass } = createEmitContext(body.abi, body.sourceOwner, placements, classes, hosts, deriver, bodyFacts, wellKnownSymbols, captures, symbolKeys, templateObjects, directCallableBindings, virtualDispatch, narrowedFormals, repeatedConstructors, dyingArguments, instantiation, abiOfCallable, hostMethodAliases, callableMemberCandidates, borrowableMemberBodies, stableBorrowEntries, printerDrift, conversions, nativeSelections, callableIdentityDemand, nativeIntegrityRestricted, fixedFieldStateConstant);
|
|
1711
|
+
// `ownedValues` stays a genuine render-time OUTPUT buffer (`EmitContext`'s
|
|
1712
|
+
// own doc: `defineValue` grows it as each operation's result is named) --
|
|
1713
|
+
// unlike `ownedDyingValues` above, a formal argument's membership in it is
|
|
1714
|
+
// not independent of the naming machinery, so it is set on `ctx` itself
|
|
1715
|
+
// rather than folded into `EmitBodyFacts`.
|
|
1716
|
+
for (const value of formalInputs.arguments)
|
|
1717
|
+
ctx.ownedValues.add(value);
|
|
1718
|
+
// Every deferred prototype-method read, composed from the carrier resolvers'
|
|
1719
|
+
// own claims before this body renders a line. It is filled onto `ctx` rather
|
|
1720
|
+
// than folded into `EmitBodyFacts` because two of the claims need the
|
|
1721
|
+
// context itself -- `objectViewFrom` is the authority for a known shape's
|
|
1722
|
+
// fields, and it reads the layouts `createEmitContext` assembles.
|
|
1723
|
+
for (const [value, read] of prototypeMethodReadsOf(ctx, body))
|
|
1724
|
+
prepass.prototypeMethodReads.set(value, read);
|
|
1725
|
+
// `class-layout.ts`'s `lazyCalleeReadsOf` needs `ctx.abiOfCallable` and
|
|
1726
|
+
// `ctx.classes`, the identical reason `prototypeMethodReadsOf` above is
|
|
1727
|
+
// filled here rather than folded into `EmitBodyFacts`.
|
|
1728
|
+
for (const value of lazyCalleeReadsOf(ctx, body))
|
|
1729
|
+
prepass.lazyCalleeReads.add(value);
|
|
1730
|
+
// The receiver every method-value read keeps for the call that consumes it,
|
|
1731
|
+
// asked of the class layout rather than recorded by whichever resolver
|
|
1732
|
+
// happened to spell the value.
|
|
1733
|
+
for (const [value, receiver] of directCallReceiversOf(ctx, body))
|
|
1734
|
+
prepass.directCallReceivers.set(value, receiver);
|
|
1735
|
+
for (const [value, callee] of virtualCalleesOf(ctx, body))
|
|
1736
|
+
prepass.virtualCallees.set(value, callee);
|
|
1737
|
+
for (const [value, read] of unionMethodReadsOf(ctx, body))
|
|
1738
|
+
prepass.unionMethodReads.set(value, read);
|
|
1739
|
+
// A member read nothing but `typeof` consumes, off arms that disagree about
|
|
1740
|
+
// the member; the read renders nothing and the `typeof` renders the dispatch.
|
|
1741
|
+
for (const [value, read] of unionMemberTypeofReadsOf(ctx, body))
|
|
1742
|
+
prepass.unionMemberTypeofReads.set(value, read);
|
|
1743
|
+
const reactive = reactiveOriginsOf(ctx, body);
|
|
1744
|
+
for (const [value, origin] of reactive.origins)
|
|
1745
|
+
prepass.reactiveOrigins.set(value, origin);
|
|
1746
|
+
for (const [declaration, origin] of reactive.bindingOrigins)
|
|
1747
|
+
prepass.reactiveBindingOrigins.set(declaration, origin);
|
|
1748
|
+
for (const [value, read] of reactive.fieldReads)
|
|
1749
|
+
prepass.reactiveFieldReads.set(value, read);
|
|
1750
|
+
// `functionSourceReads`/`functionSourceSnapshotNames` (both writes of no
|
|
1751
|
+
// C++ at all, from `functionSourceReadsOf`) and `directCallees`/
|
|
1752
|
+
// `directCalleeAbis` (the projection of `CallOperation.target` --
|
|
1753
|
+
// `ir/call-dispatch.ts`, filled once for the whole program in
|
|
1754
|
+
// `compiler.ts` after the shake -- replacing the four call sites in
|
|
1755
|
+
// `emit-bindings.ts`, `emit-callable.ts`, this file's own dead-value
|
|
1756
|
+
// handling below, and `class-properties/emit-class-properties.ts` that used
|
|
1757
|
+
// to re-derive the identical "does the callee capture nothing" test at
|
|
1758
|
+
// render time) are now settled in `bodyFacts` above, before `ctx` exists at
|
|
1759
|
+
// all: both are pure walks over `body` with no dependence on anything a
|
|
1760
|
+
// printer emits.
|
|
1761
|
+
const keyTexts = stringConstantsOf(body);
|
|
1762
|
+
const capturedQueryCells = new Set();
|
|
1763
|
+
for (const block of body.blocks.values())
|
|
1764
|
+
for (const operation of block.operations)
|
|
1765
|
+
if (operation.kind === 'allocate-callable') {
|
|
1766
|
+
const capture = ctx.captures.of(operation.functionId);
|
|
1767
|
+
if (capture.kind === 'ok')
|
|
1768
|
+
for (const slot of capture.layout.slots)
|
|
1769
|
+
capturedQueryCells.add(slot.declaration);
|
|
1770
|
+
}
|
|
1771
|
+
let entryPrologue = [];
|
|
1772
|
+
// Every per-body census, in the one order they depend on each other in --
|
|
1773
|
+
// `ir/facts.ts` owns that order now, and this backend supplies only the
|
|
1774
|
+
// questions about its own carriers plus the two storage steps that have to
|
|
1775
|
+
// run inside it.
|
|
1776
|
+
const census = irBodyCensusOf(body, {
|
|
1777
|
+
integerStorage: narrowedStorage,
|
|
1778
|
+
// A read of a plain field defers like an Array's `length` does -- see
|
|
1779
|
+
// `deferrableValuesOf`. Not a read of a reactive CELL: `gea_this->count`
|
|
1780
|
+
// spells the `Signal<double>` itself, and only the temporary it used to be
|
|
1781
|
+
// copied into converted it to a `double`. Inline, `(gea_this->a) ==
|
|
1782
|
+
// (gea_this->b)` is an ambiguous overload (`examples/apps/weather`).
|
|
1783
|
+
//
|
|
1784
|
+
// A CLASS field's reactivity is `operation.reactive`, filled at lowering
|
|
1785
|
+
// from the plugin's own table (`GetOperation.reactive`) -- precise per
|
|
1786
|
+
// read, including an INHERITED field, because lowering walked the same
|
|
1787
|
+
// inheritance chain `classMemberOf` always does to find which class truly
|
|
1788
|
+
// declares it. This used to be asked by bare key name across every
|
|
1789
|
+
// celled struct in the program (`[...celled.values()].flatMap(...)`),
|
|
1790
|
+
// which refused deferral for a plain field merely SHARING a name with
|
|
1791
|
+
// some unrelated class's reactive field anywhere in the program -- an
|
|
1792
|
+
// over-approximation now replaced by a per-operation fact.
|
|
1793
|
+
//
|
|
1794
|
+
// A RECORD element's reactivity has no plugin declaration to mark at
|
|
1795
|
+
// lowering (`reactiveBoundRecordFields` derives it from what JSX binds,
|
|
1796
|
+
// after lowering has already run), so that half asks the target's own
|
|
1797
|
+
// per-struct table -- exact already, since a record has no base class to
|
|
1798
|
+
// walk.
|
|
1799
|
+
//
|
|
1800
|
+
// But `reactive` alone is the WRONG question here, and answering it that
|
|
1801
|
+
// way materialized a read that had always deferred correctly. Being
|
|
1802
|
+
// declared reactive is not the same as HAVING a cell: a reactive field
|
|
1803
|
+
// whose carrier cannot be one -- an array, `records.ts`'s
|
|
1804
|
+
// `representationCanCell` -- gets a companion revision cell beside it and
|
|
1805
|
+
// is absent from `celled`, and its own read is then an ordinary field
|
|
1806
|
+
// load that must go on deferring. So the marker's job is to say WHICH
|
|
1807
|
+
// struct declares the field (the inheritance walk `celled`'s
|
|
1808
|
+
// declarer-keying needs and a receiver's own name cannot give), and
|
|
1809
|
+
// `celled` still decides whether that field is a cell.
|
|
1810
|
+
deferrableMemberRead: (operation) => {
|
|
1811
|
+
const key = keyTexts.get(operation.key.value) ?? null;
|
|
1812
|
+
if (!isPlainMemberRead(ctx, operation, key))
|
|
1813
|
+
return false;
|
|
1814
|
+
if (key === null)
|
|
1815
|
+
return true;
|
|
1816
|
+
const receiver = operation.receiver.representation;
|
|
1817
|
+
const declaring = operation.reactive === true && receiver.kind === 'class-ref'
|
|
1818
|
+
? (classMemberOf(classes, receiver.declaration, key)?.owner ?? null)
|
|
1819
|
+
: null;
|
|
1820
|
+
const struct = declaring !== null ? cppClassName(declaring) : structNameOfReceiver(receiver);
|
|
1821
|
+
return struct === null || ctx.hosts.reactive.celled.get(struct)?.has(key) !== true;
|
|
1822
|
+
},
|
|
1823
|
+
forwarding: {
|
|
1824
|
+
// Only the renderers that pass every argument through `argumentText`
|
|
1825
|
+
// once: a call by program body or through a `CallableObject`, and a
|
|
1826
|
+
// construct of a program class. A host or prototype fusion spells its
|
|
1827
|
+
// arguments however the host's recipe does, a tagged-union callee
|
|
1828
|
+
// spells them once PER ARM inside one statement, a generic set once per
|
|
1829
|
+
// member, and an `imul` twice.
|
|
1830
|
+
//
|
|
1831
|
+
// And only into a slot the callee's convention HAS. 10.2.1.1 binds the
|
|
1832
|
+
// declared formals; an argument past them is evaluated and then dropped
|
|
1833
|
+
// from the frame (`extra-call-arguments`), and a dropped argument's text
|
|
1834
|
+
// is spelled nowhere -- so a producer forwarded into that position was
|
|
1835
|
+
// never evaluated at all, and `mark('c')` left no mark. A rest position
|
|
1836
|
+
// is packed into an Array by the caller, one more spelling this audit
|
|
1837
|
+
// has not read, and is refused with the extras.
|
|
1838
|
+
forwardsCallInto: (consumer, value) => {
|
|
1839
|
+
const intoDeclaredFormal = (abi) => {
|
|
1840
|
+
if (abi === null)
|
|
1841
|
+
return false;
|
|
1842
|
+
const index = consumer.kind === 'call' || consumer.kind === 'construct'
|
|
1843
|
+
? consumer.arguments.findIndex((argument) => argument.value === value.value)
|
|
1844
|
+
: -1;
|
|
1845
|
+
return index !== -1 && index < (abi.restFrom ?? abi.parameters.length);
|
|
1846
|
+
};
|
|
1847
|
+
switch (consumer.kind) {
|
|
1848
|
+
case 'call': {
|
|
1849
|
+
const callee = consumer.callee;
|
|
1850
|
+
const representation = callee.representation;
|
|
1851
|
+
const carrier = representation.kind;
|
|
1852
|
+
const abi = carrier === 'function' || carrier === 'function-value-dispatch'
|
|
1853
|
+
? representation.abi
|
|
1854
|
+
: carrier === 'function-and-constructor'
|
|
1855
|
+
? representation.call
|
|
1856
|
+
: null;
|
|
1857
|
+
return (intoDeclaredFormal(abi) &&
|
|
1858
|
+
consumer.argumentsAreSpread !== true &&
|
|
1859
|
+
consumer.numericRestHostCall === undefined &&
|
|
1860
|
+
consumer.family === undefined &&
|
|
1861
|
+
!ctx.numericCalls.has(consumer) &&
|
|
1862
|
+
!ctx.hostMemberReads.has(callee.value) &&
|
|
1863
|
+
!ctx.hostFunctionReads.has(callee.value) &&
|
|
1864
|
+
!ctx.hostClassReads.has(callee.value) &&
|
|
1865
|
+
!ctx.prototypeMethodReads.has(callee.value) &&
|
|
1866
|
+
!ctx.unionMethodReads.has(callee.value) &&
|
|
1867
|
+
!ctx.functionSourceReads.has(callee.value));
|
|
1868
|
+
}
|
|
1869
|
+
case 'construct': {
|
|
1870
|
+
const representation = consumer.callee.representation;
|
|
1871
|
+
const callee = representation.kind;
|
|
1872
|
+
const abi = callee === 'constructor-family' || callee === 'constructor-value-dispatch'
|
|
1873
|
+
? representation.abi
|
|
1874
|
+
: callee === 'function-and-constructor'
|
|
1875
|
+
? representation.construct
|
|
1876
|
+
: null;
|
|
1877
|
+
return (intoDeclaredFormal(abi) &&
|
|
1878
|
+
callee !== 'native-handle' &&
|
|
1879
|
+
callee !== 'dynamic' &&
|
|
1880
|
+
callee !== 'tagged-union' &&
|
|
1881
|
+
!ctx.hostClassReads.has(consumer.callee.value));
|
|
1882
|
+
}
|
|
1883
|
+
// The census admits only an Array element store of the element's
|
|
1884
|
+
// own carrier, which `emit-properties.ts` spells once per arm.
|
|
1885
|
+
case 'set':
|
|
1886
|
+
return true;
|
|
1887
|
+
// A cell holding exactly the value's carrier is a plain assignment;
|
|
1888
|
+
// anything else goes through a conversion whose spelling is its own.
|
|
1889
|
+
case 'binding-write': {
|
|
1890
|
+
const placement = ctx.placements.get(consumer.declaration);
|
|
1891
|
+
const held = placement?.representation;
|
|
1892
|
+
if (!placement || !held || (placement.storage.kind !== 'local' && placement.storage.kind !== 'region'))
|
|
1893
|
+
return false;
|
|
1894
|
+
if (ctx.hostMethodAliases.has(consumer.declaration))
|
|
1895
|
+
return false;
|
|
1896
|
+
return representationKey(held) === representationKey(value.representation);
|
|
1897
|
+
}
|
|
1898
|
+
// `emit-return.ts` reconciles the value against the ABI result (or
|
|
1899
|
+
// the promise payload an async body settles); equal carriers make
|
|
1900
|
+
// that reconciliation the identity.
|
|
1901
|
+
case 'return': {
|
|
1902
|
+
if (ctx.abi === null || ctx.generatorBody)
|
|
1903
|
+
return false;
|
|
1904
|
+
const result = ctx.abi.result;
|
|
1905
|
+
const target = result.kind === 'promise' && value.representation.kind !== 'promise' ? result.value : result;
|
|
1906
|
+
return representationKey(target) === representationKey(value.representation);
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
},
|
|
1910
|
+
// A cell of this frame that no closure shares, holding exactly what is
|
|
1911
|
+
// written and read, and whose value renders as C++ of its own (a host
|
|
1912
|
+
// member, class or namespace read has no text until its use).
|
|
1913
|
+
forwardsBinding: (write, read) => {
|
|
1914
|
+
const declaration = write.declaration;
|
|
1915
|
+
const placement = ctx.placements.get(declaration);
|
|
1916
|
+
if (!placement || placement.storage.kind !== 'local' || placement.storage.owner !== ctx.owner)
|
|
1917
|
+
return false;
|
|
1918
|
+
const held = placement.representation;
|
|
1919
|
+
if (!held || held.kind === 'unresolved' || held.kind === 'void')
|
|
1920
|
+
return false;
|
|
1921
|
+
const key = representationKey(held);
|
|
1922
|
+
if (key !== representationKey(write.value.representation) || key !== representationKey(read.result.representation))
|
|
1923
|
+
return false;
|
|
1924
|
+
// A captured cell is read by the closure that copies it at allocation,
|
|
1925
|
+
// which no `binding-read` of this body records.
|
|
1926
|
+
if (ctx.captures.isBoxed(declaration) || ctx.captures.isCaptured(declaration))
|
|
1927
|
+
return false;
|
|
1928
|
+
if (ctx.hostMethodAliases.has(declaration) || ctx.reactiveBindingOrigins.has(declaration))
|
|
1929
|
+
return false;
|
|
1930
|
+
const value = write.value.value;
|
|
1931
|
+
return (!ctx.hostMemberReads.has(value) &&
|
|
1932
|
+
!ctx.hostClassReads.has(value) &&
|
|
1933
|
+
!ctx.hostFunctionReads.has(value) &&
|
|
1934
|
+
!ctx.hostNamespaceReads.has(value) &&
|
|
1935
|
+
!ctx.prototypeMethodReads.has(value));
|
|
1936
|
+
}
|
|
1937
|
+
},
|
|
1938
|
+
deadValues: {
|
|
1939
|
+
pureGet: (operation, key) => isPlainMemberRead(ctx, operation, key),
|
|
1940
|
+
// The exact producers `emit-callable.ts` (`admission.kind === 'none'`) and
|
|
1941
|
+
// `emit-bindings.ts` (`directCallableBindings`) register as direct callees.
|
|
1942
|
+
directCallee: (producer) => producer.kind === 'allocate-callable'
|
|
1943
|
+
? ctx.captures.of(producer.functionId).kind === 'none'
|
|
1944
|
+
: producer.kind === 'binding-read'
|
|
1945
|
+
? ctx.directCallableBindings.has(producer.declaration)
|
|
1946
|
+
: producer.kind === 'get' && directClassMethodBody(ctx, producer, keyTexts.get(producer.key.value) ?? null) !== null,
|
|
1947
|
+
// A named construct entry still reads the constructor's per-evaluation
|
|
1948
|
+
// prototype owner. Devirtualizing the code pointer cannot discard that
|
|
1949
|
+
// carrier, even for a class whose methods capture nothing.
|
|
1950
|
+
directConstructor: () => false,
|
|
1951
|
+
// A construction is deletable when the class it builds is one nothing can
|
|
1952
|
+
// observe being built. Keyed off the RESULT, which is the only operand of a
|
|
1953
|
+
// `construct` that names the class: the callee is the constructor object,
|
|
1954
|
+
// whose carrier is the constructor's convention rather than the instance's.
|
|
1955
|
+
pureConstruct: (operation) => {
|
|
1956
|
+
const result = operation.result.representation;
|
|
1957
|
+
return result.kind === 'class-ref' && ctx.instantiation.unobservableConstructions.has(result.declaration);
|
|
1958
|
+
},
|
|
1959
|
+
// Only for a callee reached BY NAME. An indirect call names no body, so
|
|
1960
|
+
// there is no body to ask -- and a thunk's frame declares the receiver
|
|
1961
|
+
// whatever the target does with it.
|
|
1962
|
+
ignoresReceiver: (callee) => callee !== undefined &&
|
|
1963
|
+
callee.kind === 'allocate-callable' &&
|
|
1964
|
+
ctx.captures.of(callee.functionId).kind === 'none' &&
|
|
1965
|
+
ctx.instantiation.receiverIgnoringFunctions.has(callee.functionId)
|
|
1966
|
+
},
|
|
1967
|
+
stringQueryCell: (declaration) => {
|
|
1968
|
+
const placement = ctx.placements.get(declaration);
|
|
1969
|
+
return (placement?.storage.kind === 'local' &&
|
|
1970
|
+
placement.storage.owner === body.sourceOwner &&
|
|
1971
|
+
placement.representation?.kind === 'string' &&
|
|
1972
|
+
!ctx.captures.isBoxed(declaration) &&
|
|
1973
|
+
!capturedQueryCells.has(declaration));
|
|
1974
|
+
},
|
|
1975
|
+
spellConstants: () => spellConstants(ctx, body),
|
|
1976
|
+
formalStorage: (settled) => {
|
|
1977
|
+
// The collectors below read these off the context, so they are published
|
|
1978
|
+
// first -- and the deferral set they see is deliberately the one the
|
|
1979
|
+
// hoists have not yet claimed from.
|
|
1980
|
+
//
|
|
1981
|
+
// Sixteen of these seventeen write through `prepass`, not `ctx`, because
|
|
1982
|
+
// `ctx`'s own view of the same field is `ReadonlyMap`/`ReadonlySet` --
|
|
1983
|
+
// see `EmitBodyPrepassFacts`'s doc. `ctx.deferrable` is the one
|
|
1984
|
+
// exception still written here directly: it has a genuine render-time
|
|
1985
|
+
// write of its own (`emit-arrays.ts`'s `materializeDenseReference`), so
|
|
1986
|
+
// it stays a plain mutable `Set` rather than joining its siblings.
|
|
1987
|
+
for (const value of settled.localIterators)
|
|
1988
|
+
prepass.localIterators.add(value);
|
|
1989
|
+
for (const value of settled.dead)
|
|
1990
|
+
prepass.deadValues.add(value);
|
|
1991
|
+
for (const value of settled.unread)
|
|
1992
|
+
prepass.unreadValues.add(value);
|
|
1993
|
+
for (const [value, known] of settled.booleanConstants)
|
|
1994
|
+
prepass.booleanConstants.set(value, known);
|
|
1995
|
+
for (const value of settled.deferrable)
|
|
1996
|
+
prepass.deferrable.add(value);
|
|
1997
|
+
for (const value of settled.callArgumentOnly)
|
|
1998
|
+
prepass.callArgumentOnly.add(value);
|
|
1999
|
+
for (const value of settled.typeQueryValues)
|
|
2000
|
+
prepass.typeQueryValues.add(value);
|
|
2001
|
+
for (const binding of settled.typeQueryBindings)
|
|
2002
|
+
prepass.typeQueryBindings.add(binding);
|
|
2003
|
+
for (const [operation, comparison] of settled.typeQueryComparisons)
|
|
2004
|
+
prepass.typeQueryComparisons.set(operation, comparison);
|
|
2005
|
+
for (const value of settled.integerValues)
|
|
2006
|
+
prepass.integerValues.add(value);
|
|
2007
|
+
for (const declaration of settled.integerBindings)
|
|
2008
|
+
prepass.integerBindings.add(declaration);
|
|
2009
|
+
for (const [value, form] of settled.remainderForms)
|
|
2010
|
+
prepass.remainderForms.set(value, form);
|
|
2011
|
+
for (const [call, intrinsic] of settled.numericCalls)
|
|
2012
|
+
prepass.numericCalls.set(call, intrinsic);
|
|
2013
|
+
for (const value of settled.numericCallOnly)
|
|
2014
|
+
prepass.numericCallOnly.add(value);
|
|
2015
|
+
collectFormalCells(ctx, prepass, body);
|
|
2016
|
+
// A captured cell initialized on separate branches has no single first
|
|
2017
|
+
// write that dominates the closure allocation. Allocate its handle at entry
|
|
2018
|
+
// so each branch only assigns the pointee. Kept separate from declarations:
|
|
2019
|
+
// declarations must remain uninitialized so gotos may cross their scope.
|
|
2020
|
+
entryPrologue = earlyCapturedCellPrologue(ctx, body);
|
|
2021
|
+
collectCapacityHints(ctx, prepass, body);
|
|
2022
|
+
return new Set(ctx.formalCells.keys());
|
|
2023
|
+
}
|
|
2024
|
+
});
|
|
2025
|
+
for (const value of census.loopInvariantValues)
|
|
2026
|
+
prepass.loopInvariantValues.add(value);
|
|
2027
|
+
for (const [value, layout] of census.sharedStringLayouts)
|
|
2028
|
+
prepass.sharedStringLayouts.set(value, layout);
|
|
2029
|
+
for (const value of census.hoistedResults)
|
|
2030
|
+
prepass.hoistedResults.add(value);
|
|
2031
|
+
for (const [declaration, forwarded] of census.forwardedBindings)
|
|
2032
|
+
prepass.forwardedBindings.set(declaration, forwarded);
|
|
2033
|
+
for (const [value, prior] of census.reusedStringLengths)
|
|
2034
|
+
prepass.reusedStringLengths.set(value, prior);
|
|
2035
|
+
// The hoists and the length reuse each claim a value out of the deferral set
|
|
2036
|
+
// after the storage step above took its copy, so drop what they claimed.
|
|
2037
|
+
for (const value of prepass.deferrable)
|
|
2038
|
+
if (!census.deferrable.has(value))
|
|
2039
|
+
prepass.deferrable.delete(value);
|
|
2040
|
+
const hoists = census.hoists;
|
|
2041
|
+
collectDirectBindingSinks(prepass, body, hoists.relocated);
|
|
2042
|
+
// After the hoists, because a window's own bound is often the loop-invariant
|
|
2043
|
+
// read they relocate, and a relocated value is one this may name.
|
|
2044
|
+
admitDenseWindows(ctx, prepass, body);
|
|
2045
|
+
const isSingleBlock = body.blockOrder.length === 1;
|
|
2046
|
+
const orderLabels = isSingleBlock ? new Map() : blockLabelsOf(body.blockOrder);
|
|
2047
|
+
const owner = sectionOwnerOf(body);
|
|
2048
|
+
// A merge is one variable written on each incoming path, so its name has to
|
|
2049
|
+
// exist before any predecessor renders -- including a predecessor that comes
|
|
2050
|
+
// earlier in the block order than the merge itself. Minting every merge's
|
|
2051
|
+
// name up front is what makes that hold without depending on block order.
|
|
2052
|
+
const mergeWrites = mergeWritesOf(ctx, body, (result) => defineValue(ctx, result));
|
|
2053
|
+
// After the merges, because a block that writes one is not empty.
|
|
2054
|
+
const flow = threadedFlowOf(ctx, body, orderLabels, hoists, mergeWrites);
|
|
2055
|
+
const labels = flow.labels;
|
|
2056
|
+
// A try region's blocks are rendered together, as one `try { } catch (...)
|
|
2057
|
+
// { }` chunk, by `renderTryRegion` -- never one at a time by the loop below.
|
|
2058
|
+
// `consumedByRegion` is filled in as each region is rendered, so the loop
|
|
2059
|
+
// can skip every block a region already accounted for.
|
|
2060
|
+
const regionByTryEntry = new Map(body.tryRegions.map((region) => [region.tryEntry, region]));
|
|
2061
|
+
const iteratorCloseRegionByEntry = new Map((body.iteratorCloseRegions ?? []).map((region) => [region.entry, region]));
|
|
2062
|
+
const consumedByRegion = new Set();
|
|
2063
|
+
const lineageOf = (blockIds) => {
|
|
2064
|
+
for (const id of blockIds) {
|
|
2065
|
+
const block = body.blocks.get(id);
|
|
2066
|
+
if (!block)
|
|
2067
|
+
continue;
|
|
2068
|
+
const found = allOperationsOf(block).find((operation) => operation.lineage !== null);
|
|
2069
|
+
if (found)
|
|
2070
|
+
return found.lineage;
|
|
2071
|
+
}
|
|
2072
|
+
return null;
|
|
2073
|
+
};
|
|
2074
|
+
const blocks = [];
|
|
2075
|
+
// Invariant 5's seal (`emit-context.ts`'s `sealFactFieldsForRender`): from
|
|
2076
|
+
// here to the end of the loop every operation renders against a context
|
|
2077
|
+
// whose facts are settled. Inert unless `GEA_SEAL_EMIT_CONTEXT` is set.
|
|
2078
|
+
const unseal = sealFactFieldsForRender(ctx);
|
|
2079
|
+
try {
|
|
2080
|
+
for (const blockId of body.blockOrder) {
|
|
2081
|
+
if (consumedByRegion.has(blockId))
|
|
2082
|
+
continue;
|
|
2083
|
+
const block = body.blocks.get(blockId);
|
|
2084
|
+
if (!block)
|
|
2085
|
+
throw new Error(`ir body ${body.owner} lists block ${blockId} in blockOrder but has no matching block`);
|
|
2086
|
+
// Shared by both region renderers below, and by each other's nested
|
|
2087
|
+
// calls: `renderTryRegion`'s `preparePart` recognizes an iterator-close
|
|
2088
|
+
// region nested inside a try part through `iteratorCloseRegionByEntry`/
|
|
2089
|
+
// `renderIteratorCloseRegion`, the reverse of the more common nesting
|
|
2090
|
+
// `renderIteratorCloseRegion` itself already handled through `regionByTryEntry`.
|
|
2091
|
+
const regionRendering = {
|
|
2092
|
+
emitOperation,
|
|
2093
|
+
emitTerminator,
|
|
2094
|
+
labelOf: requireBlockLabel,
|
|
2095
|
+
labels,
|
|
2096
|
+
isSingleBlock,
|
|
2097
|
+
mergeWrites,
|
|
2098
|
+
regionByTryEntry,
|
|
2099
|
+
iteratorCloseRegionByEntry,
|
|
2100
|
+
renderIteratorCloseRegion,
|
|
2101
|
+
// The hoist plan and the block tail, so a region's blocks render the
|
|
2102
|
+
// way every other block does -- see `RegionRendering` for what was
|
|
2103
|
+
// being dropped, and why one of the three was a defect rather than a
|
|
2104
|
+
// missed optimization.
|
|
2105
|
+
relocated: hoists.relocated,
|
|
2106
|
+
emitHoistedInto: (partCtx, partLines, partBlock) => {
|
|
2107
|
+
for (const operation of hoists.into.get(partBlock) ?? [])
|
|
2108
|
+
emitOperation(partCtx, partLines, operation);
|
|
2109
|
+
},
|
|
2110
|
+
emitBlockTail: (partCtx, partLines, partBlock) => {
|
|
2111
|
+
emitDenseSetup(partCtx, partLines, partBlock);
|
|
2112
|
+
const partFill = partCtx.fillLoops.get(partBlock);
|
|
2113
|
+
if (!partFill)
|
|
2114
|
+
return false;
|
|
2115
|
+
emitFillLoop(partCtx, partLines, partFill, requireBlockLabel(labels, partFill.exit));
|
|
2116
|
+
return true;
|
|
2117
|
+
}
|
|
2118
|
+
};
|
|
2119
|
+
const iteratorCloseRegion = iteratorCloseRegionByEntry.get(blockId);
|
|
2120
|
+
if (iteratorCloseRegion) {
|
|
2121
|
+
const lines = [];
|
|
2122
|
+
const consumed = renderIteratorCloseRegion(ctx, lines, body, iteratorCloseRegion, regionRendering);
|
|
2123
|
+
for (const id of consumed)
|
|
2124
|
+
consumedByRegion.add(id);
|
|
2125
|
+
blocks.push({
|
|
2126
|
+
text: lines.join('\n'),
|
|
2127
|
+
facts: { kind: 'materialized', representation: { kind: 'void' }, owner, lineage: iteratorCloseRegion.lineage }
|
|
2128
|
+
});
|
|
2129
|
+
continue;
|
|
2130
|
+
}
|
|
2131
|
+
const region = regionByTryEntry.get(blockId);
|
|
2132
|
+
if (region) {
|
|
2133
|
+
const lines = [];
|
|
2134
|
+
const consumed = renderTryRegion(ctx, lines, body, region, regionRendering);
|
|
2135
|
+
for (const id of consumed)
|
|
2136
|
+
consumedByRegion.add(id);
|
|
2137
|
+
blocks.push({
|
|
2138
|
+
text: lines.join('\n'),
|
|
2139
|
+
facts: { kind: 'materialized', representation: { kind: 'void' }, owner, lineage: lineageOf(consumed) }
|
|
2140
|
+
});
|
|
2141
|
+
continue;
|
|
2142
|
+
}
|
|
2143
|
+
if (flow.skipped.has(blockId))
|
|
2144
|
+
continue;
|
|
2145
|
+
const lines = [];
|
|
2146
|
+
if (!isSingleBlock && flow.labeled.has(requireBlockLabel(labels, blockId)))
|
|
2147
|
+
lines.push(`${requireBlockLabel(labels, blockId)}:`);
|
|
2148
|
+
for (const operation of block.operations) {
|
|
2149
|
+
const result = 'result' in operation ? operation.result : null;
|
|
2150
|
+
if (result && hoists.relocated.has(result.id))
|
|
2151
|
+
continue;
|
|
2152
|
+
emitOperation(ctx, lines, operation);
|
|
2153
|
+
}
|
|
2154
|
+
// AFTER the block's own statements: a preheader assigns the cells a hoisted read names.
|
|
2155
|
+
for (const operation of hoists.into.get(blockId) ?? [])
|
|
2156
|
+
emitOperation(ctx, lines, operation);
|
|
2157
|
+
// The writes belong after everything this block computed and before it
|
|
2158
|
+
// transfers control: that is exactly where the incoming value is live.
|
|
2159
|
+
for (const write of mergeWrites.get(blockId) ?? []) {
|
|
2160
|
+
const raw = operandText(ctx, write.value);
|
|
2161
|
+
const converted = alignedValueText(ctx, 'emit.ts:2111', write.value.representation, write.target, raw);
|
|
2162
|
+
if (converted === null) {
|
|
2163
|
+
throw createCppEmitBlockedError(`conversion:${representationKey(write.value.representation)}->${representationKey(write.target)}`, `cannot convert merge input from "${representationKey(write.value.representation)}" to "${representationKey(write.target)}"`);
|
|
2164
|
+
}
|
|
2165
|
+
lines.push(`${write.name} = ${converted};`);
|
|
2166
|
+
}
|
|
2167
|
+
// Last of all, so every value a window's condition names -- a hoisted read,
|
|
2168
|
+
// a merge -- is already assigned in this block.
|
|
2169
|
+
emitDenseSetup(ctx, lines, blockId);
|
|
2170
|
+
// A counted `push` loop's header does not branch: it appends (`emit-arrays.ts`).
|
|
2171
|
+
const fill = ctx.fillLoops.get(blockId);
|
|
2172
|
+
if (fill)
|
|
2173
|
+
emitFillLoop(ctx, lines, fill, requireBlockLabel(labels, fill.exit));
|
|
2174
|
+
else
|
|
2175
|
+
emitTerminator(ctx, lines, labels, isSingleBlock, block.terminator, flow.nextLabel.get(blockId) ?? null);
|
|
2176
|
+
blocks.push({
|
|
2177
|
+
text: lines.join('\n'),
|
|
2178
|
+
facts: {
|
|
2179
|
+
kind: 'materialized',
|
|
2180
|
+
// A block is a sequence of statements, not a value: `void` is the one
|
|
2181
|
+
// carrier that says exactly that instead of borrowing some operand's
|
|
2182
|
+
// representation to stand in for a whole statement sequence.
|
|
2183
|
+
representation: { kind: 'void' },
|
|
2184
|
+
owner,
|
|
2185
|
+
// The block's authority is the first operation in it that has any: a
|
|
2186
|
+
// terminator is frequently synthesized and carries none, and a section
|
|
2187
|
+
// attributed to nothing when it does contain real work would lose the
|
|
2188
|
+
// trace back to the source that produced it.
|
|
2189
|
+
lineage: allOperationsOf(block).find((operation) => operation.lineage !== null)?.lineage ?? null
|
|
2190
|
+
}
|
|
2191
|
+
});
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
finally {
|
|
2195
|
+
unseal();
|
|
2196
|
+
}
|
|
2197
|
+
// Every dispatched method reference has to have been CALLED. One that was
|
|
2198
|
+
// stored, passed or returned instead is carried as a `CallableObject` over a
|
|
2199
|
+
// single statically named body, which for an overridden method is the base's
|
|
2200
|
+
// -- the exact silent miscompile the dispatch exists to remove. Asked here,
|
|
2201
|
+
// once the whole body has rendered, because only then is it known that no
|
|
2202
|
+
// call consumed it.
|
|
2203
|
+
const carried = [...ctx.virtualCallees.keys()].filter((value) => !ctx.virtualCalleesUsed.has(value));
|
|
2204
|
+
if (carried.length > 0) {
|
|
2205
|
+
throw createCppEmitBlockedError('call-abi:virtual-dispatch', `takes a function object of an overridden method rather than calling it (${carried.map((value) => ctx.virtualCallees.get(value)?.member ?? value).join(', ')}); a carrier names one body, which for an overridden method is the base's`);
|
|
2206
|
+
}
|
|
2207
|
+
const carriedUnionMethods = [...ctx.unionMethodReads.keys()].filter((value) => !ctx.unionMethodReadsUsed.has(value));
|
|
2208
|
+
if (carriedUnionMethods.length > 0) {
|
|
2209
|
+
throw createCppEmitBlockedError('call-abi:tagged-union-method', `takes ${carriedUnionMethods.length} tagged-union method value(s) without calling them; each union arm has its own receiver convention, so no single callable carrier can preserve them`);
|
|
2210
|
+
}
|
|
2211
|
+
// Declarations come last because they are only complete once every block has
|
|
2212
|
+
// rendered, and first in the output because a `goto` may not jump into the
|
|
2213
|
+
// scope of a variable whose declaration initializes it.
|
|
2214
|
+
const declarationFacts = { kind: 'materialized', representation: { kind: 'void' }, owner, lineage: null };
|
|
2215
|
+
const declarations = {
|
|
2216
|
+
text: ctx.declarations.map((entry) => `${entry.type} ${entry.name};`).join('\n'),
|
|
2217
|
+
facts: declarationFacts
|
|
2218
|
+
};
|
|
2219
|
+
const prologue = entryPrologue.length === 0 ? [] : [{ text: entryPrologue.join('\n'), facts: declarationFacts }];
|
|
2220
|
+
return [declarations, ...prologue, ...blocks];
|
|
2221
|
+
};
|
|
2222
|
+
// Re-exported so a consumer that emits bodies has one import for the naming
|
|
2223
|
+
// rules those bodies are rendered under; the definitions live with the context
|
|
2224
|
+
// they name.
|
|
2225
|
+
export { cppConstructedThunkName, cppConstructThunkName, cppFormalName, cppReceiverName, cppThunkName, isCppEmitBlockedError } from './emit-context.js';
|