@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,2154 @@
|
|
|
1
|
+
import { receivableArguments } from '../../ir/call-entry.js';
|
|
2
|
+
import { stableBorrowEntryAccepts } from './borrowed-call-entry.js';
|
|
3
|
+
import { callableMemberSlot } from '../../ir/callable-member-candidates.js';
|
|
4
|
+
import { transferOf } from '../../ir/transfer.js';
|
|
5
|
+
import { boxedValueText } from './emit-dynamic-properties.js';
|
|
6
|
+
import { functionConstructorArgumentKindOf } from '../../representation/function-constructor.js';
|
|
7
|
+
import { nativeSumWidenable } from './emit-sum-widening.js';
|
|
8
|
+
import { abiKey, representationKey } from '../../representation/model.js';
|
|
9
|
+
import { alignedValueText, bindsReceiver, callableObjectAbi, movedValueText, unboxedLoadText } from './emit-narrowing.js';
|
|
10
|
+
import { memberAccessOperator, reactiveRevisionText } from './emit-carrier-members.js';
|
|
11
|
+
import { structuralRecordViewText } from './emit-record-view.js';
|
|
12
|
+
import { emitDateConstruct, isDateCarrier } from './prototype/emit-prototype-date.js';
|
|
13
|
+
import { unwrapPresentValue, bindingReference, cppConstructedThunkName, cppConstructThunkName, cppEnvironmentStructName, cppReceiverName, cppThunkName, createCppEmitBlockedError, declareCell, defineValue, defineValueAlias, isCppEmitBlockedError, isIntegerStorageValue, operandText, paddedArguments, captureFieldText } from './emit-context.js';
|
|
14
|
+
import { classMemberOf, classStaticMemberOf, cppFieldInitializerStatements, lazyArrowFieldPlanOf } from './class-layout.js';
|
|
15
|
+
import { declaredRecordFieldOf } from './records.js';
|
|
16
|
+
import { hostArityCallLines, hostResultText } from './host/emit-host-arity.js';
|
|
17
|
+
import { hostMemberOf } from './host/host-members.js';
|
|
18
|
+
import { hostCallText, nativeHandleInvocationText } from './host/emit-host-invoke.js';
|
|
19
|
+
import { nativeReflectCallText } from './host/emit-host-reflect.js';
|
|
20
|
+
import { jsonStringifyFillLines } from './emit-json.js';
|
|
21
|
+
import { withheldCellOf } from './deferral-safety.js';
|
|
22
|
+
import { prototypeMethodCallText } from './prototype/emit-prototype-invoke.js';
|
|
23
|
+
import { mutatingArrayMembers } from './prototype/emit-prototype-array.js';
|
|
24
|
+
import { regexpRoleOf, regExpConstructionFromObject } from './prototype/emit-prototype-regexp.js';
|
|
25
|
+
import { cppConstructPatternEntry, cppRegExpNativeTypes } from './regexp-types.js';
|
|
26
|
+
import { cppErrorNativeType } from './error-types.js';
|
|
27
|
+
import { isNativeErrorBaseRefusal, nativeErrorBaseInitializeStatements } from './native-error-base.js';
|
|
28
|
+
import { emitArrayBufferConstruct, emitSharedArrayBufferConstruct, emitTextCodecConstruct, emitDataViewConstruct, emitTypedArrayBufferConstruct, typedArrayTargetSpelling } from './emit-buffers.js';
|
|
29
|
+
import { armAt, armIs } from './emit-union-properties.js';
|
|
30
|
+
import { cppAbiType, cppBodyName, cppBoxedType, cppCallableDeclarationTagName, cppClassName, cppConstructName, cppInitializeName, cppRecordFieldName, cppRefcountedReceiver, cppTypeOf, cppUndefinedValue } from './types.js';
|
|
31
|
+
/** `Function.prototype.bind` preserves the native call ABI and captures only evaluated prefix values. */
|
|
32
|
+
export const emitBindCallable = (ctx, lines, operation) => {
|
|
33
|
+
if (operation.result.representation.kind !== 'function-value-dispatch') {
|
|
34
|
+
throw createCppEmitBlockedError('call-abi:bind-callable', 'a bound Function result is not carried as an evaluated function-value dispatch');
|
|
35
|
+
}
|
|
36
|
+
if (operation.detached) {
|
|
37
|
+
// See `BindCallableOperation.detached`: the language calls a detached
|
|
38
|
+
// method with no receiver, so a body that reads `this` would be answered
|
|
39
|
+
// where the program would have thrown.
|
|
40
|
+
if (operation.sourceFunctionId !== null && ctx.captures.readsReceiver(operation.sourceFunctionId)) {
|
|
41
|
+
throw createCppEmitBlockedError('call-abi:bind-callable', `passes method ${operation.sourceFunctionId} on as a value, and its body reads \`this\`: the language calls a detached method ` +
|
|
42
|
+
'with no receiver, so binding the object it was read from would answer where the program would have thrown');
|
|
43
|
+
}
|
|
44
|
+
if (operation.receiver === null)
|
|
45
|
+
throw createCppEmitBlockedError('call-abi:bind-callable', 'a detached method bind names no receiver to bind');
|
|
46
|
+
const bound = alignedText(ctx, operation.sourceAbi.receiver ?? undefined, operation.receiver, 'detached method receiver');
|
|
47
|
+
const type = cppTypeOf(operation.result.representation);
|
|
48
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${type}::bindReceiver(${operandText(ctx, operation.source)}, ${bound});`);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const receiver = operation.receiver
|
|
52
|
+
? [alignedText(ctx, operation.sourceAbi.receiver ?? undefined, operation.receiver, 'Function.prototype.bind receiver')]
|
|
53
|
+
: [];
|
|
54
|
+
const bound = operation.bound.map((value, index) => alignedText(ctx, operation.sourceAbi.parameters[index]?.value, value, 'Function.prototype.bind argument'));
|
|
55
|
+
// A dynamic-fallback callable is boxed only for its mutable Function-object
|
|
56
|
+
// properties. Its declaration-owned ABI survives on the operation, so
|
|
57
|
+
// recover that callable through the authenticated Function adapter before
|
|
58
|
+
// capturing the prefix; passing a Value to bindCallable would erase both
|
|
59
|
+
// the call frame and the target's own-property identity.
|
|
60
|
+
let source = operandText(ctx, operation.source);
|
|
61
|
+
if (operation.source.representation.kind === 'dynamic') {
|
|
62
|
+
if (operation.sourceFunctionId === null) {
|
|
63
|
+
throw createCppEmitBlockedError('call-abi:bind-callable', 'a dynamic Function source carries no checker-authenticated declaration identity');
|
|
64
|
+
}
|
|
65
|
+
const authenticated = ctx.abiOfCallable(operation.sourceFunctionId);
|
|
66
|
+
if (authenticated === null || abiKey(authenticated) !== abiKey(operation.sourceAbi)) {
|
|
67
|
+
throw createCppEmitBlockedError('call-abi:bind-callable', `dynamic Function source ${operation.sourceFunctionId} does not publish the exact ABI carried by its bind operation`);
|
|
68
|
+
}
|
|
69
|
+
source = unboxedLoadText({ kind: 'function', functionId: operation.sourceFunctionId, abi: authenticated }, source);
|
|
70
|
+
}
|
|
71
|
+
if (source === null) {
|
|
72
|
+
throw createCppEmitBlockedError('call-abi:bind-callable', 'a dynamic Function source has no authenticated native ABI adapter');
|
|
73
|
+
}
|
|
74
|
+
const prefix = [...receiver, ...bound];
|
|
75
|
+
lines.push(`${defineValue(ctx, operation.result)} = gea::bindCallable<${cppAbiType(operation.result.representation.abi)}, ${operation.bound.length}>(${[
|
|
76
|
+
source,
|
|
77
|
+
...prefix
|
|
78
|
+
].join(', ')});`);
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Rendering the callable family: calling, constructing, super-initializing, and
|
|
82
|
+
* allocating the two carriers that make a function or a class a first-class
|
|
83
|
+
* value.
|
|
84
|
+
*
|
|
85
|
+
* They belong together because they are four views of one convention. A call
|
|
86
|
+
* and a construction both go through a carrier's own pointer; an allocation is
|
|
87
|
+
* what fills that carrier in; and `super(...)` is the one member that is
|
|
88
|
+
* neither -- initialization against a receiver that already exists -- which is
|
|
89
|
+
* exactly why it is easiest to get wrong when it sits among unrelated code.
|
|
90
|
+
*/
|
|
91
|
+
/**
|
|
92
|
+
* A host object written where one of its own base types is declared.
|
|
93
|
+
*
|
|
94
|
+
* The same rule as the class case below, for a hierarchy this compiler did not
|
|
95
|
+
* lay out: an `NSStackView` IS an `NSView`, the generated bridge gives the
|
|
96
|
+
* wrapper structs that same inheritance, and the upcast is the language's own
|
|
97
|
+
* derived-to-base rule with no text of its own. What this cannot be is
|
|
98
|
+
* inferred -- two opaque handles are unrelated to the checker whatever the
|
|
99
|
+
* host's class hierarchy says -- so it is read from what the host stated
|
|
100
|
+
* (`PluginCapabilities.nativeBases`), off the carrier, which is where that
|
|
101
|
+
* statement now travels (`Representation`'s `native-handle` arm).
|
|
102
|
+
*
|
|
103
|
+
* Read from the carrier rather than from this emitter's own host tables so
|
|
104
|
+
* that the conversion census -- which is pure carrier-to-carrier and has no
|
|
105
|
+
* emit context to consult -- answers this pair the same way the printer does.
|
|
106
|
+
* While the fact reached only here, the census minted a `never` node for every
|
|
107
|
+
* host upcast and the certificate refused programs this function was perfectly
|
|
108
|
+
* able to render.
|
|
109
|
+
*
|
|
110
|
+
* A handle with no stated carrier is compared by nothing: a host that named no
|
|
111
|
+
* C++ type for a protocol has said nothing about that protocol's place in a
|
|
112
|
+
* hierarchy either, and treating the protocol name as a carrier would compare
|
|
113
|
+
* two different key spaces. `bases` is empty for such a handle, so the
|
|
114
|
+
* membership test below already says so.
|
|
115
|
+
*/
|
|
116
|
+
const hostHandleDerivesFrom = (source, target) => target.native !== null && source.bases.includes(target.native);
|
|
117
|
+
/**
|
|
118
|
+
* An instance of a class written where one of its base classes is declared.
|
|
119
|
+
*
|
|
120
|
+
* `records.ts` emits a derived class as `struct D : B`, so `shared_ptr<D>`
|
|
121
|
+
* converts to `shared_ptr<B>` by the language's own derived-to-base rule and
|
|
122
|
+
* the value needs no text of its own. `convertedValueText` cannot answer this:
|
|
123
|
+
* two `class-ref`s differ in a `DeclarationId`, and which classes extend which
|
|
124
|
+
* is a projection fact, not something a carrier carries.
|
|
125
|
+
*
|
|
126
|
+
* The chain is walked rather than the immediate base checked, because
|
|
127
|
+
* `class C extends B extends A` really does make a `C` an `A`. Both sides must
|
|
128
|
+
* agree on ownership: the conversion is on the pointer, and a by-value `B`
|
|
129
|
+
* built from a `D` would slice off exactly the fields that made it a `D`.
|
|
130
|
+
*/
|
|
131
|
+
const derivesFrom = (ctx, source, target) => {
|
|
132
|
+
if (source.kind === 'native-handle' && target.kind === 'native-handle')
|
|
133
|
+
return hostHandleDerivesFrom(source, target);
|
|
134
|
+
if (source.kind !== 'class-ref' || target.kind !== 'class-ref')
|
|
135
|
+
return false;
|
|
136
|
+
if (source.ownership !== target.ownership || source.ownership !== 'shared-refcount')
|
|
137
|
+
return false;
|
|
138
|
+
const seen = new Set();
|
|
139
|
+
let current = ctx.classes.get(source.declaration)?.base ?? null;
|
|
140
|
+
while (current !== null && !seen.has(current)) {
|
|
141
|
+
if (current === target.declaration)
|
|
142
|
+
return true;
|
|
143
|
+
seen.add(current);
|
|
144
|
+
current = ctx.classes.get(current)?.base ?? null;
|
|
145
|
+
}
|
|
146
|
+
return false;
|
|
147
|
+
};
|
|
148
|
+
/**
|
|
149
|
+
* A class instance written where a structurally-compatible DECLARED SHAPE is
|
|
150
|
+
* the slot -- the interface view TypeScript's own assignability already
|
|
151
|
+
* granted, materialized.
|
|
152
|
+
*
|
|
153
|
+
* hono's `createResponseInstance(_res.body, _res)` (context.ts) hands a
|
|
154
|
+
* `Response` INSTANCE to a parameter declared `ResponseInit`. The checker
|
|
155
|
+
* accepts it structurally: `ResponseInit`'s three optional members --
|
|
156
|
+
* `status`, `statusText`, `headers` -- are all present on the class. C++ has
|
|
157
|
+
* no such rule. `gea::Ref<gea_class_...>` and `gea::Ref<gea_record_...>` are
|
|
158
|
+
* unrelated types, `derivesFrom` above answers only the nominal hierarchy the
|
|
159
|
+
* program actually wrote, and `convertedValueText` has no way to reach a
|
|
160
|
+
* class's members: a `class-ref` carries a declaration and a shape id, never a
|
|
161
|
+
* field list. This is the one place both layouts are in hand.
|
|
162
|
+
*
|
|
163
|
+
* The rebuild is `recastedRecordText`'s (emit-narrowing.ts), one carrier kind
|
|
164
|
+
* further out: read each of the TARGET's fields off the source and construct
|
|
165
|
+
* the target's struct. Both field lists come from `recordFieldsOfShape`, the
|
|
166
|
+
* same authority `records.ts` builds every struct body from, so the members
|
|
167
|
+
* read here are exactly the members that exist.
|
|
168
|
+
*
|
|
169
|
+
* What this is NOT is an alias. The result is a fresh record holding a
|
|
170
|
+
* snapshot of the instance's fields, so a callee that ASSIGNS one of them
|
|
171
|
+
* writes to the copy and the instance never sees it -- the same limit the
|
|
172
|
+
* record-to-record recast already carries, one carrier kind wider. That makes
|
|
173
|
+
* this sound for a slot the declaration shapes as a read-only bag (an options
|
|
174
|
+
* record, which is what every such interface in practice is) and wrong for one
|
|
175
|
+
* declared to be written through. A field whose own carrier is refcounted --
|
|
176
|
+
* `Response.headers`, a `Headers` instance -- still aliases, because the copy
|
|
177
|
+
* copies the reference.
|
|
178
|
+
*
|
|
179
|
+
* Fail-closed on everything it cannot account for: a target with accessors or
|
|
180
|
+
* a dynamic-property sidecar has members this aggregate cannot fill, a
|
|
181
|
+
* host-named struct is the engine's own type with a layout this compiler did
|
|
182
|
+
* not choose, and a target field with no source member of its own is a real
|
|
183
|
+
* hole -- unless the field is optional, which is the declaration's own word
|
|
184
|
+
* that absence is a value it accepts.
|
|
185
|
+
*/
|
|
186
|
+
/** The lambda's own names, kept out of every body-local namespace a view can be spliced into. */
|
|
187
|
+
/**
|
|
188
|
+
* One argument in the carrier the frame it is being written into declares.
|
|
189
|
+
*
|
|
190
|
+
* An argument arrives in the carrier of the expression that produced it, which
|
|
191
|
+
* is not always the slot's: `setSupportedOrientations('landscape')` hands a
|
|
192
|
+
* `std::string` to a parameter declared `OrientationLock | OrientationLock[]`,
|
|
193
|
+
* a real `gea::TaggedUnion`. Writing it through unconverted is a frame
|
|
194
|
+
* mismatch, and C++ rejects it -- correctly -- at the call.
|
|
195
|
+
*
|
|
196
|
+
* This is deliberately in the emitter and not in the lowering. Only three of
|
|
197
|
+
* this file's renderings build a frame at all: a host member and a
|
|
198
|
+
* String/Array.prototype method are both spelled through a text template that
|
|
199
|
+
* never sees the declared convention (`emit-host-invoke.ts`,
|
|
200
|
+
* `emit-prototype-invoke.ts`), so an argument reaching one of those must stay
|
|
201
|
+
* exactly as its own carrier spells it -- `substring`'s optional `end` is a C++
|
|
202
|
+
* OVERLOAD there, not a `gea::Optional<double>` parameter. Which rendering a
|
|
203
|
+
* call gets is decided here, once, a few lines above; a lowering that decided
|
|
204
|
+
* it a second time would be a second authority over the same question.
|
|
205
|
+
*
|
|
206
|
+
* A slot the convention does not declare is left alone: ECMAScript lets a call
|
|
207
|
+
* pass more arguments than a signature names, and an argument past the last
|
|
208
|
+
* slot has no declared carrier to be reconciled against. So is the rest slot,
|
|
209
|
+
* which `ir/lower-operands.ts`'s `packRestArguments` has already built in
|
|
210
|
+
* exactly the carrier the convention states.
|
|
211
|
+
*/
|
|
212
|
+
export const alignedText = (ctx, slot, argument, what = 'call') => {
|
|
213
|
+
const text = operandText(ctx, argument);
|
|
214
|
+
if (!slot)
|
|
215
|
+
return text;
|
|
216
|
+
if (derivesFrom(ctx, argument.representation, slot))
|
|
217
|
+
return text;
|
|
218
|
+
// Native sum widening owns its source. Preserve a proven dying input's
|
|
219
|
+
// value category through that conversion, rather than copying before the
|
|
220
|
+
// outer argument move gets a chance to see the different carrier.
|
|
221
|
+
const sourceText = nativeSumWidenable(argument.representation, slot) ? movedValueText(ctx, argument, argument.representation, text) : text;
|
|
222
|
+
const converted = alignedValueText(ctx, `emit-callable.ts:503:${what}`, argument.representation, slot, sourceText);
|
|
223
|
+
if (converted !== null)
|
|
224
|
+
return converted;
|
|
225
|
+
// Last, and only here: the structural interface view above needs both
|
|
226
|
+
// layouts, which is a fact of this emitter's context rather than of the two
|
|
227
|
+
// carriers, so `convertedValueText` cannot be asked it. Tried after that
|
|
228
|
+
// function has refused so a pair with a real conversion never reaches a
|
|
229
|
+
// rebuild it does not need.
|
|
230
|
+
const view = structuralRecordViewText(ctx, argument.representation, slot, text);
|
|
231
|
+
if (view !== null)
|
|
232
|
+
return view;
|
|
233
|
+
// A storage-free argument in a slot that declares a real carrier is not a
|
|
234
|
+
// missing conversion: `undefined`/`void` is also `never`'s carrier
|
|
235
|
+
// (`representation/primitives.ts`), so this call sits on a branch flow
|
|
236
|
+
// analysis proved unreachable and there is nothing to convert. Rendered as
|
|
237
|
+
// the throw rather than refused -- mongodb's `execute_operation.ts:198`
|
|
238
|
+
// narrows a generic by `instanceof` to a class its own monomorphized copy
|
|
239
|
+
// cannot be, 28 copies over, and every one of them blocked emission for code
|
|
240
|
+
// that cannot run.
|
|
241
|
+
if (argument.representation.kind === 'undefined' || argument.representation.kind === 'void') {
|
|
242
|
+
return `gea::host::unreachableValue<${cppTypeOf(slot)}>()`;
|
|
243
|
+
}
|
|
244
|
+
const bound = receiverBoundCallableText(ctx, argument, slot, text);
|
|
245
|
+
if (bound !== null)
|
|
246
|
+
return bound;
|
|
247
|
+
throw createCppEmitBlockedError(`conversion:${representationKey(argument.representation)}->${representationKey(slot)}`, `passes a ${representationKey(argument.representation)} argument into a parameter slot carrying ` +
|
|
248
|
+
`${representationKey(slot)}, and no installed load performs that conversion`);
|
|
249
|
+
};
|
|
250
|
+
/**
|
|
251
|
+
* A method read as a value, handed on into a slot that declares no receiver
|
|
252
|
+
* -- tsc's `formatGeneratedName(..., IdentifierNameMap.toKey)`, or
|
|
253
|
+
* `apply(fmt.upper, s)`.
|
|
254
|
+
*
|
|
255
|
+
* `classMethodValueText` materializes a method value in the method's own
|
|
256
|
+
* physical convention, receiver first, and leaves the receiver to be filled
|
|
257
|
+
* by the call (`emitCall`'s `directCallReceivers`), which is right for the
|
|
258
|
+
* one call a property read feeds directly and wrong for a value that
|
|
259
|
+
* escapes: the slot it lands in states the language's own view of the
|
|
260
|
+
* value, `(name: string) => string`, and nothing at THAT site knows which
|
|
261
|
+
* object the read went through. The read does: `propertyReadOrigins` keeps
|
|
262
|
+
* every property read by its result, so the receiver is recovered here from
|
|
263
|
+
* the read itself and packed in with the value (`gea_runtime.h`'s
|
|
264
|
+
* `CallableObject::bindReceiver`).
|
|
265
|
+
*
|
|
266
|
+
* ECMAScript calls a detached method with `this` undefined. Packing the
|
|
267
|
+
* receiver in is the same call exactly when the body never reads it, and
|
|
268
|
+
* that is checked -- directly, and through every closure the body allocates
|
|
269
|
+
* (`captures.ts`'s `readsReceiver`) -- before anything is rendered; a body
|
|
270
|
+
* that does read `this` is refused by name, since the language would have
|
|
271
|
+
* thrown (or, in sloppy code, read the global) where this would have handed
|
|
272
|
+
* it the object. Measured on tsc's `transformers/utilities.ts`: every method
|
|
273
|
+
* passed as a value there is receiver-free.
|
|
274
|
+
*
|
|
275
|
+
* `null` for a pair this is not about, or a read this cannot see: the caller
|
|
276
|
+
* refuses in its own words, exactly as before.
|
|
277
|
+
*/
|
|
278
|
+
/**
|
|
279
|
+
* The body a `receiver.key` read names, for a class instance or for the class
|
|
280
|
+
* object itself -- the same two member sets `emit-class-properties.ts`
|
|
281
|
+
* materializes a method value out of, asked through the same two functions so
|
|
282
|
+
* a value this binds a receiver into is the one that read published.
|
|
283
|
+
*/
|
|
284
|
+
const methodBodyRead = (ctx, receiver, key) => {
|
|
285
|
+
const isStatic = receiver.kind === 'constructor-family';
|
|
286
|
+
const declarations = receiver.kind === 'class-ref' ? [receiver.declaration] : isStatic ? receiver.members : [];
|
|
287
|
+
for (const declaration of declarations) {
|
|
288
|
+
const site = isStatic ? classStaticMemberOf(ctx.classes, declaration, key) : classMemberOf(ctx.classes, declaration, key);
|
|
289
|
+
if (site?.kind === 'method' && site.method.callable)
|
|
290
|
+
return site.method.callable;
|
|
291
|
+
}
|
|
292
|
+
return null;
|
|
293
|
+
};
|
|
294
|
+
export const receiverBoundCallableText = (ctx, operand, target, text) => {
|
|
295
|
+
const source = operand.representation;
|
|
296
|
+
if (!bindsReceiver(source, target))
|
|
297
|
+
return null;
|
|
298
|
+
const origin = ctx.propertyReadOrigins.get(operand.value);
|
|
299
|
+
if (origin === undefined)
|
|
300
|
+
return null;
|
|
301
|
+
const abi = callableObjectAbi(source);
|
|
302
|
+
if (abi === null || abi.receiver === null)
|
|
303
|
+
return null;
|
|
304
|
+
const key = ctx.staticKeyTexts.get(origin.key.value);
|
|
305
|
+
const method = key === undefined ? null : methodBodyRead(ctx, origin.receiver.representation, key);
|
|
306
|
+
if (method === null)
|
|
307
|
+
return null;
|
|
308
|
+
if (ctx.captures.readsReceiver(method)) {
|
|
309
|
+
throw createCppEmitBlockedError('call-abi:bind-callable', `passes method "${key}" on as a value, and its body reads \`this\`: the language calls a detached method with no receiver, ` +
|
|
310
|
+
'so binding the object it was read from would answer where the program would have thrown');
|
|
311
|
+
}
|
|
312
|
+
const receiver = alignedValueText(ctx, 'emit-callable.ts:593', origin.receiver.representation, abi.receiver, operandText(ctx, origin.receiver));
|
|
313
|
+
if (receiver === null)
|
|
314
|
+
return null;
|
|
315
|
+
return `${cppTypeOf(target)}::bindReceiver(${text}, ${receiver})`;
|
|
316
|
+
};
|
|
317
|
+
const argumentText = (ctx, abi, position, argument, what = 'call') => {
|
|
318
|
+
const slot = abi.restFrom !== null && position >= abi.restFrom ? undefined : abi.parameters[position]?.value;
|
|
319
|
+
const text = alignedText(ctx, slot, argument, what);
|
|
320
|
+
// A dying refcounted argument MOVES (`buildDyingArgumentIndex`, captures.ts):
|
|
321
|
+
// the cell it reads is never looked at again, so the increment on the way in
|
|
322
|
+
// and the release on the way out are both pure cost. Only a carrier that
|
|
323
|
+
// OWNS a count can move -- and only into a slot carrying exactly what the
|
|
324
|
+
// argument already holds, since any conversion (`derivesFrom`'s upcast
|
|
325
|
+
// included: `Ref` has no converting MOVE constructor) has already produced a
|
|
326
|
+
// temporary of its own, and moving that is no cheaper than leaving it.
|
|
327
|
+
return movedValueText(ctx, argument, slot ?? null, text);
|
|
328
|
+
};
|
|
329
|
+
/**
|
|
330
|
+
* The receiver occupies the frame's first formal, and reconciles against the
|
|
331
|
+
* slot the convention declares for it exactly as any other argument does.
|
|
332
|
+
*
|
|
333
|
+
* One extra obligation, and only for a refcounted receiver a body takes BY
|
|
334
|
+
* REFERENCE (`cppRefcountedReceiver`, translation-unit.ts): the argument must
|
|
335
|
+
* be something that survives the call. A local, a formal and a temporary all
|
|
336
|
+
* do -- a `const&` parameter extends a temporary's lifetime past the call that
|
|
337
|
+
* made it -- but a WITHHELD cell read renders as the cell's own storage, and a
|
|
338
|
+
* callee that assigns that cell drops the last count and destroys the object
|
|
339
|
+
* its own receiver names. So a withheld read is given its temporary back: the
|
|
340
|
+
* same copy passing by value would have made, in the one shape where the
|
|
341
|
+
* reference has nothing behind it.
|
|
342
|
+
*/
|
|
343
|
+
export const receiverArgumentText = (ctx, abi, receiver) => {
|
|
344
|
+
const slot = abi.receiver ?? receiver.representation;
|
|
345
|
+
// A receiver the callee never names, whose construction was therefore deleted
|
|
346
|
+
// (`ir/instantiation.ts`). The frame still declares the formal, so something
|
|
347
|
+
// has to be passed -- and the one thing that is certainly safe is a
|
|
348
|
+
// default-constructed value of the slot's own type, since no body reads it.
|
|
349
|
+
// Spelling the deleted value's name instead would name a variable that was
|
|
350
|
+
// never declared.
|
|
351
|
+
if (ctx.deadValues.has(receiver.value))
|
|
352
|
+
return `${cppTypeOf(slot)}{}`;
|
|
353
|
+
const text = alignedText(ctx, abi.receiver ?? undefined, receiver, 'receiver');
|
|
354
|
+
// `withheldCellOf` rather than a private pair of lookups: this is one of the
|
|
355
|
+
// three sites where an emitter observes a withheld read somewhere the
|
|
356
|
+
// deferral census never placed it, and they share that question even though
|
|
357
|
+
// the cure here -- give the read its temporary back -- is this site's alone.
|
|
358
|
+
// See `deferral-safety.ts`.
|
|
359
|
+
const withheldCellRead = withheldCellOf(ctx, receiver.value) !== null;
|
|
360
|
+
return withheldCellRead && cppRefcountedReceiver(slot) ? `${cppTypeOf(slot)}{${text}}` : text;
|
|
361
|
+
};
|
|
362
|
+
/**
|
|
363
|
+
* A call through the callable carrier's own invoke pointer.
|
|
364
|
+
*
|
|
365
|
+
* This is the generic path, and it is the only one emitted: `open` is a
|
|
366
|
+
* complete answer, and the plan already narrowed the *convention* to one exact
|
|
367
|
+
* ABI, so an indirect call here costs one pointer load and loses no proof. A
|
|
368
|
+
* direct call to a known symbol is a devirtualization to attach later, on top
|
|
369
|
+
* of this, not instead of it.
|
|
370
|
+
*/
|
|
371
|
+
const emitNullishInvocation = (ctx, lines, operation) => {
|
|
372
|
+
const kind = operation.callee.representation.kind;
|
|
373
|
+
if (kind !== 'undefined' && kind !== 'null' && kind !== 'void')
|
|
374
|
+
return false;
|
|
375
|
+
// Argument expressions have already evaluated in the IR. There is no call
|
|
376
|
+
// frame or parameter conversion for a value with no [[Call]]/[[Construct]].
|
|
377
|
+
const result = operation.result;
|
|
378
|
+
const target = result && result.representation.kind !== 'void' ? cppTypeOf(result.representation) : 'void';
|
|
379
|
+
const call = `gea::host::throwNullishInvocation<${target}>("${kind === 'null' ? 'null' : 'undefined'}", "${operation.kind === 'construct' ? 'constructor' : 'function'}")`;
|
|
380
|
+
lines.push(result && target !== 'void' ? `${defineValue(ctx, result)} = ${call};` : `${call};`);
|
|
381
|
+
if (result?.representation.kind === 'void')
|
|
382
|
+
defineValueAlias(ctx, result, '(void)0');
|
|
383
|
+
return true;
|
|
384
|
+
};
|
|
385
|
+
const emitUnionMethodCall = (ctx, lines, operation) => {
|
|
386
|
+
const read = ctx.unionMethodReads.get(operation.callee.value);
|
|
387
|
+
if (read === undefined)
|
|
388
|
+
return false;
|
|
389
|
+
const arms = read.arms;
|
|
390
|
+
if (arms.length === 0)
|
|
391
|
+
throw createCppEmitBlockedError('call-abi:tagged-union-method', 'a deferred tagged-union method read has no class arms');
|
|
392
|
+
ctx.unionMethodReadsUsed.add(operation.callee.value);
|
|
393
|
+
// The union's own spelling, taken HERE rather than at the read: the arm
|
|
394
|
+
// paths the claim recorded are indices into the carrier, and `armAt`/`armIs`
|
|
395
|
+
// turn them into text against a receiver that has a name by now.
|
|
396
|
+
const receiverText = operandText(ctx, read.receiver);
|
|
397
|
+
const branches = arms.map((arm, index) => {
|
|
398
|
+
const tests = [];
|
|
399
|
+
let armText = receiverText;
|
|
400
|
+
for (const step of arm.path) {
|
|
401
|
+
tests.push(armIs(armText, step));
|
|
402
|
+
armText = armAt(armText, step);
|
|
403
|
+
}
|
|
404
|
+
const armTest = tests.length === 0 ? 'true' : tests.join(' && ');
|
|
405
|
+
const abi = ctx.abiOfCallable(arm.callable);
|
|
406
|
+
if (abi === null || abi.receiver === null) {
|
|
407
|
+
throw createCppEmitBlockedError('call-abi:tagged-union-method', 'a deferred tagged-union method body has no receiver-bearing convention');
|
|
408
|
+
}
|
|
409
|
+
const receiver = alignedValueText(ctx, 'emit-callable.ts:705', arm.receiverRepresentation, abi.receiver, armText);
|
|
410
|
+
if (receiver === null) {
|
|
411
|
+
throw createCppEmitBlockedError(`conversion:${representationKey(arm.receiverRepresentation)}->${representationKey(abi.receiver)}`, `a deferred tagged-union method receiver cannot convert from ${representationKey(arm.receiverRepresentation)} to ${representationKey(abi.receiver)}`);
|
|
412
|
+
}
|
|
413
|
+
const supplied = receivableArguments(abi, operation.arguments).map((argument, position) => argumentText(ctx, abi, position, argument));
|
|
414
|
+
const invocation = `${cppBodyName(arm.callable)}(${paddedArguments(abi, [receiver, ...supplied], 'union method call').join(', ')})`;
|
|
415
|
+
let body;
|
|
416
|
+
if (operation.result === null || operation.result.representation.kind === 'void') {
|
|
417
|
+
body = `${invocation}; return;`;
|
|
418
|
+
}
|
|
419
|
+
else if (abi.result.kind === 'void') {
|
|
420
|
+
const converted = alignedValueText(ctx, 'emit-callable.ts:718', { kind: 'undefined' }, operation.result.representation, cppUndefinedValue);
|
|
421
|
+
if (converted === null) {
|
|
422
|
+
throw createCppEmitBlockedError(`conversion:undefined->${representationKey(operation.result.representation)}`, 'a void tagged-union method result has no installed conversion to its published result');
|
|
423
|
+
}
|
|
424
|
+
body = `${invocation}; return ${converted};`;
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
const converted = alignedValueText(ctx, 'emit-callable.ts:724', abi.result, operation.result.representation, invocation);
|
|
428
|
+
if (converted === null) {
|
|
429
|
+
throw createCppEmitBlockedError(`conversion:${representationKey(abi.result)}->${representationKey(operation.result.representation)}`, `a tagged-union method result cannot convert from ${representationKey(abi.result)} to ${representationKey(operation.result.representation)}`);
|
|
430
|
+
}
|
|
431
|
+
body = `return ${converted};`;
|
|
432
|
+
}
|
|
433
|
+
return index === arms.length - 1 ? body : `if (${armTest}) { ${body} }`;
|
|
434
|
+
});
|
|
435
|
+
const resultType = operation.result === null || operation.result.representation.kind === 'void' ? 'void' : cppTypeOf(operation.result.representation);
|
|
436
|
+
const invocation = `[&]() -> ${resultType} { ${branches.join(' ')} }()`;
|
|
437
|
+
if (operation.result === null)
|
|
438
|
+
lines.push(`${invocation};`);
|
|
439
|
+
else if (operation.result.representation.kind === 'void') {
|
|
440
|
+
lines.push(`${invocation};`);
|
|
441
|
+
defineValueAlias(ctx, operation.result, '(void)0');
|
|
442
|
+
}
|
|
443
|
+
else
|
|
444
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${invocation};`);
|
|
445
|
+
return true;
|
|
446
|
+
};
|
|
447
|
+
/**
|
|
448
|
+
* A DIRECT call on a lazy arrow class field (`class-layout.ts`'s
|
|
449
|
+
* `censusLazyArrowFields`/`LazyArrowFieldPlan`): `c.json(...)` invokes the
|
|
450
|
+
* arrow's own body directly, with the receiver as its own leading argument,
|
|
451
|
+
* while the field's storage is still the zero-initialized `CallableObject`
|
|
452
|
+
* (`invoke == nullptr`) -- exactly the read this field's whole design exists
|
|
453
|
+
* to avoid materializing.
|
|
454
|
+
*
|
|
455
|
+
* A REASSIGNED field (`c.json = other; c.json(...)`) must still call
|
|
456
|
+
* `other`, so this cannot simply always call the body: the field's guard is
|
|
457
|
+
* read at the call site, same as `class-layout.ts`'s `lazyMaterializedFieldText`,
|
|
458
|
+
* and the branch not taken is exactly the ordinary `.call()` this whole
|
|
459
|
+
* feature otherwise renders. Both arms share ONE evaluation of the receiver
|
|
460
|
+
* and ONE evaluation of each argument -- the branches are mutually exclusive
|
|
461
|
+
* at runtime (an `if`/`else` inside one IIFE, the same shape
|
|
462
|
+
* `emitUnionMethodCall`'s arms already rely on above), so duplicating an
|
|
463
|
+
* argument's TEXT across them never duplicates its evaluation.
|
|
464
|
+
*
|
|
465
|
+
* Keyed off `ctx.propertyReadOrigins`, exactly as the existing
|
|
466
|
+
* "callable member candidate" lookup just above is (`memberRead`/`memberKey`)
|
|
467
|
+
* -- both ask "where did this call's callee value come from", just of two
|
|
468
|
+
* different answers (a method slot there, a class field here). Fails closed
|
|
469
|
+
* to `false` -- today's materializing text -- for anything this cannot prove:
|
|
470
|
+
* a receiver that is not statically a class (or subclass) instance, a key
|
|
471
|
+
* this compile never gave lazy-field storage, an unresolved body ABI, or a
|
|
472
|
+
* callee reached through `unwrapPresentValue` (`c.json?.()`) rather than the
|
|
473
|
+
* read itself, none of which this fusion has been asked to handle.
|
|
474
|
+
*/
|
|
475
|
+
const emitLazyArrowFieldCall = (ctx, lines, operation) => {
|
|
476
|
+
const memberRead = ctx.propertyReadOrigins.get(operation.callee.value);
|
|
477
|
+
if (memberRead === undefined)
|
|
478
|
+
return false;
|
|
479
|
+
const receiverRepresentation = memberRead.receiver.representation;
|
|
480
|
+
if (receiverRepresentation.kind !== 'class-ref')
|
|
481
|
+
return false;
|
|
482
|
+
const fieldName = ctx.staticKeyTexts.get(memberRead.key.value);
|
|
483
|
+
if (fieldName === undefined)
|
|
484
|
+
return false;
|
|
485
|
+
// `classMemberOf` walks the receiver's OWN class up through its bases, so a
|
|
486
|
+
// subclass instance resolves to the same declaring owner a base instance
|
|
487
|
+
// would -- the identical walk `emit-properties.ts`'s `emitGet` already
|
|
488
|
+
// trusts for this same field, and the reason an inherited qualifying field
|
|
489
|
+
// is published under the DECLARING class rather than every receiver that
|
|
490
|
+
// might read it (`class-layout.ts`'s own comment on `lazyArrowFieldPlanOf`).
|
|
491
|
+
const site = classMemberOf(ctx.classes, receiverRepresentation.declaration, fieldName);
|
|
492
|
+
if (site === null || site.kind !== 'field')
|
|
493
|
+
return false;
|
|
494
|
+
const plan = lazyArrowFieldPlanOf(ctx.classes, site.owner, fieldName);
|
|
495
|
+
if (plan === null)
|
|
496
|
+
return false;
|
|
497
|
+
// The callee's own representation is the field's STORED CallableObject
|
|
498
|
+
// convention -- what `.call()` below already invokes through, unchanged.
|
|
499
|
+
const callee = operation.callee.representation;
|
|
500
|
+
if (callee.kind !== 'function' && callee.kind !== 'function-value-dispatch' && callee.kind !== 'function-family')
|
|
501
|
+
return false;
|
|
502
|
+
// A `function`-kind read names a SPECIFIC function identity, which is not
|
|
503
|
+
// always the census's own arrow: a generic field instantiates a fresh,
|
|
504
|
+
// per-call-site copy of its body (hono's `LightRequest#cached` --
|
|
505
|
+
// `test/runtime/pending-promise-payload-narrowed-at-call.ts` -- reads back
|
|
506
|
+
// `promise(string)` at one call and `promise(scalar(number))` at another,
|
|
507
|
+
// from the SAME field), and a flow fact narrowing the field to a specific
|
|
508
|
+
// reassigned value is the same shape. Either way, `plan.body`'s own ABI has
|
|
509
|
+
// no reason to agree with THIS read's, so direct-call fusion only fires when
|
|
510
|
+
// the read confirms it is looking at the very arrow the census found --
|
|
511
|
+
// anything else keeps today's materializing text, which is always correct.
|
|
512
|
+
if (callee.kind === 'function' && callee.functionId !== plan.body)
|
|
513
|
+
return false;
|
|
514
|
+
// The stored branch calls whatever the FIELD physically holds, so its result
|
|
515
|
+
// is the field's own declared result, not this read's: a generic field
|
|
516
|
+
// (`cached = <K extends keyof R>(key: K): Promise<R[K]> => ...`) is stored
|
|
517
|
+
// as ONE `CallableObject` whose result is the union of every payload, while
|
|
518
|
+
// the read at `this.cached('text')` is instantiated to `promise(string)`.
|
|
519
|
+
// Taking the read's ABI here declared the `.call()` result already narrowed
|
|
520
|
+
// and emitted `return field.call("text")` into a `Promise<std::string>`
|
|
521
|
+
// lambda -- a C++ type error, not a refusal
|
|
522
|
+
// (`test/runtime/pending-promise-payload-narrowed-at-call.ts`). The
|
|
523
|
+
// conversion below then adapts the stored result to the read's, or refuses
|
|
524
|
+
// and declines the fusion.
|
|
525
|
+
const storedField = ctx.classes.get(site.owner)?.fields.find((field) => field.key === fieldName)?.representation ?? null;
|
|
526
|
+
const storedAbi = storedField !== null && (storedField.kind === 'function' || storedField.kind === 'function-family' || storedField.kind === 'function-value-dispatch')
|
|
527
|
+
? storedField.abi
|
|
528
|
+
: callee.abi;
|
|
529
|
+
const bodyAbi = ctx.abiOfCallable(plan.body);
|
|
530
|
+
if (bodyAbi === null)
|
|
531
|
+
return false;
|
|
532
|
+
// Every check above is exactly what `class-layout.ts`'s `lazyCalleeReadsOf`
|
|
533
|
+
// proves BEFORE admitting a GET to `ctx.lazyCalleeReads` -- so a callee this
|
|
534
|
+
// set names is guaranteed to have reached this point, and `emit-properties.ts`'s
|
|
535
|
+
// `emitGet` has already rendered it as a RAW, unmaterialized copy rather than
|
|
536
|
+
// through `lazyMaterializedFieldText`'s guard-and-store text. That copy's own
|
|
537
|
+
// name -- not a fresh read of the field -- is this call's one evaluation of
|
|
538
|
+
// the callee reference, taken (by `emitGet`, at ITS point in program order)
|
|
539
|
+
// BEFORE any of this call's own arguments could reassign the field: exactly
|
|
540
|
+
// the ECMA-262 order `c.json(c.json = other)` needs and the field-time guard
|
|
541
|
+
// read below, on its own, does not give.
|
|
542
|
+
const snapshot = ctx.lazyCalleeReads.has(operation.callee.value) ? operandText(ctx, operation.callee) : null;
|
|
543
|
+
// A GENERIC field whose `function-value-dispatch` read carries a per-call
|
|
544
|
+
// instantiated result (`test/runtime/pending-promise-payload-narrowed-at-call.ts`'s
|
|
545
|
+
// `cached`: `promise(string)` at one call site, `promise(scalar(number))`
|
|
546
|
+
// at another, from the identical field) has no `functionId` for the guard
|
|
547
|
+
// above to catch -- the census's canonical `plan.body` names the GENERIC
|
|
548
|
+
// declaration, whose own result need not agree with THIS call's. Rather
|
|
549
|
+
// than special-case every shape that can disagree, this tries the direct
|
|
550
|
+
// branch's conversions and DECLINES the fusion (falls back to today's
|
|
551
|
+
// always-correct materializing text) the moment one refuses, the same
|
|
552
|
+
// "prove it before committing" fail-closed answer `receiver whose
|
|
553
|
+
// representation is not the declaring class/subclass` above already gives.
|
|
554
|
+
// `isCppEmitBlockedError` is this file's own distinguishing test for exactly
|
|
555
|
+
// that kind of refusal; anything else escapes as the genuine defect it is.
|
|
556
|
+
try {
|
|
557
|
+
const receiverText = operandText(ctx, memberRead.receiver);
|
|
558
|
+
const fieldMember = cppRecordFieldName(fieldName);
|
|
559
|
+
const accessor = memberAccessOperator(receiverRepresentation.ownership);
|
|
560
|
+
// An arrow's OWN abi never states a receiver (13.2.4: no `this` binding of
|
|
561
|
+
// its own) -- `plan.bodyReceiverRepresentation` is the ONE place that
|
|
562
|
+
// captured receiver is recorded, so a body with neither is genuinely
|
|
563
|
+
// receiver-free (`censusLazyArrowFields`'s `{kind: 'none'}` admission).
|
|
564
|
+
const bodyReceiver = bodyAbi.receiver ?? plan.bodyReceiverRepresentation;
|
|
565
|
+
// `physicalFrame` states whether the receiver occupies the frame's first
|
|
566
|
+
// formal for `paddedArguments`'/`receivableArguments`' own arithmetic --
|
|
567
|
+
// the identical adjustment the ordinary direct-callee path makes for a
|
|
568
|
+
// method value whose semantic ABI declares no receiver of its own (this
|
|
569
|
+
// file's own `physicalFrame`, further below, and `EmitContext.
|
|
570
|
+
// directCallReceivers`'s doc).
|
|
571
|
+
const physicalFrame = { ...bodyAbi, receiver: bodyReceiver };
|
|
572
|
+
const result = operation.result;
|
|
573
|
+
const voidResult = result === null || result.representation.kind === 'void';
|
|
574
|
+
const resultType = voidResult ? 'void' : cppTypeOf(result.representation);
|
|
575
|
+
// The receiver is bound ONCE, by `gea_lazy_receiver` below -- passed here
|
|
576
|
+
// as that plain local name, not re-derived through `receiverArgumentText`,
|
|
577
|
+
// exactly as `class-layout.ts`'s `lazyMaterializedFieldText` already
|
|
578
|
+
// passes it to `initializer` unconverted: a subclass instance's `Ref`
|
|
579
|
+
// converts to the declaring class's `Ref` implicitly, the same conversion
|
|
580
|
+
// struct member access on that same local already relies on for
|
|
581
|
+
// `fieldMember` below.
|
|
582
|
+
const directArgs = [
|
|
583
|
+
...(bodyReceiver !== null ? ['gea_lazy_receiver'] : []),
|
|
584
|
+
...receivableArguments(physicalFrame, operation.arguments).map((argument, position) => argumentText(ctx, physicalFrame, position, argument))
|
|
585
|
+
];
|
|
586
|
+
const directPadded = paddedArguments(physicalFrame, directArgs, 'lazy field direct call');
|
|
587
|
+
const directInvocation = `${cppBodyName(plan.body)}(${[...(plan.bodyHasEnvironment ? ['nullptr'] : []), ...directPadded].join(', ')})`;
|
|
588
|
+
const storedArgs = receivableArguments(storedAbi, operation.arguments).map((argument, position) => argumentText(ctx, storedAbi, position, argument));
|
|
589
|
+
const storedPadded = paddedArguments(storedAbi, storedArgs, 'lazy field stored call');
|
|
590
|
+
// `snapshot` names the copy `emitGet` already rendered, read once at the
|
|
591
|
+
// callee's own evaluation point; the field-time `auto&` alias is the copy's
|
|
592
|
+
// stand-in ONLY where no snapshot exists.
|
|
593
|
+
const storedInvocation = `${snapshot ?? 'gea_lazy_field'}.call(${storedPadded.join(', ')})`;
|
|
594
|
+
const branchText = (abi, invocation) => {
|
|
595
|
+
if (voidResult)
|
|
596
|
+
return `${invocation}; return;`;
|
|
597
|
+
if (abi.result.kind === 'void') {
|
|
598
|
+
const converted = alignedValueText(ctx, 'emit-callable.ts:lazy-field-call', { kind: 'undefined' }, result.representation, cppUndefinedValue);
|
|
599
|
+
if (converted === null) {
|
|
600
|
+
throw createCppEmitBlockedError(`conversion:undefined->${representationKey(result.representation)}`, 'a void lazy arrow field call result has no installed conversion to its published result');
|
|
601
|
+
}
|
|
602
|
+
return `${invocation}; return ${converted};`;
|
|
603
|
+
}
|
|
604
|
+
const converted = alignedValueText(ctx, 'emit-callable.ts:lazy-field-call', abi.result, result.representation, invocation);
|
|
605
|
+
if (converted === null) {
|
|
606
|
+
throw createCppEmitBlockedError(`conversion:${representationKey(abi.result)}->${representationKey(result.representation)}`, `a lazy arrow field call result cannot convert from ${representationKey(abi.result)} to ${representationKey(result.representation)}`);
|
|
607
|
+
}
|
|
608
|
+
return `return ${converted};`;
|
|
609
|
+
};
|
|
610
|
+
// Both branches' text is built -- including the RESULT conversion, the one
|
|
611
|
+
// most likely to refuse for a generic field -- before anything is pushed
|
|
612
|
+
// to `lines`, so a refusal caught below has emitted nothing for this
|
|
613
|
+
// function to undo.
|
|
614
|
+
const storedBranch = branchText(storedAbi, storedInvocation);
|
|
615
|
+
// The direct branch may refuse where the stored one does not: a generic
|
|
616
|
+
// field's body is emitted ONCE, for one instantiation's result
|
|
617
|
+
// (`Promise<std::string>` for `cached`), so a call instantiated to another
|
|
618
|
+
// payload (`this.cached('count')`) has no conversion from the body's result
|
|
619
|
+
// while the stored callable -- whose result is the union of every payload
|
|
620
|
+
// -- converts through its arm. That call is not lost: the field is
|
|
621
|
+
// materialized first (the same initializer `lazyMaterializedFieldText`
|
|
622
|
+
// calls) and the stored branch alone is taken, which is exactly the
|
|
623
|
+
// pre-fusion text with the result conversion the ordinary `.call()` path
|
|
624
|
+
// below cannot add (it declares the result in the READ's type and would
|
|
625
|
+
// hand clang a `Promise<union>` for a `Promise<double>`).
|
|
626
|
+
const directBranch = (() => {
|
|
627
|
+
try {
|
|
628
|
+
return branchText(bodyAbi, directInvocation);
|
|
629
|
+
}
|
|
630
|
+
catch (error) {
|
|
631
|
+
if (!isCppEmitBlockedError(error))
|
|
632
|
+
throw error;
|
|
633
|
+
return null;
|
|
634
|
+
}
|
|
635
|
+
})();
|
|
636
|
+
// A snapshot needs no `auto& gea_lazy_field` alias into the field at all --
|
|
637
|
+
// that alias is precisely the re-read the snapshot exists to avoid, since
|
|
638
|
+
// an argument between the original read and this call may have reassigned
|
|
639
|
+
// the field to something the snapshot never saw.
|
|
640
|
+
const fieldText = `gea_lazy_receiver${accessor}${fieldMember}`;
|
|
641
|
+
const materialize = snapshot !== null
|
|
642
|
+
? `if (${snapshot}.invoke == nullptr) { ${snapshot} = ${cppBodyName(plan.initializer)}(gea_lazy_receiver); ` +
|
|
643
|
+
`if (${fieldText}.invoke == nullptr) { ${fieldText} = ${snapshot}; } } `
|
|
644
|
+
: `auto& gea_lazy_field = ${fieldText}; if (gea_lazy_field.invoke == nullptr) { gea_lazy_field = ${cppBodyName(plan.initializer)}(gea_lazy_receiver); } `;
|
|
645
|
+
const invocation = directBranch === null
|
|
646
|
+
? `[&]() -> ${resultType} { const auto& gea_lazy_receiver = ${receiverText}; ${materialize}${storedBranch} }()`
|
|
647
|
+
: snapshot !== null
|
|
648
|
+
? `[&]() -> ${resultType} { const auto& gea_lazy_receiver = ${receiverText}; ` +
|
|
649
|
+
`if (${snapshot}.invoke == nullptr) { ${directBranch} } else { ${storedBranch} } }()`
|
|
650
|
+
: `[&]() -> ${resultType} { const auto& gea_lazy_receiver = ${receiverText}; auto& gea_lazy_field = ${fieldText}; ` +
|
|
651
|
+
`if (gea_lazy_field.invoke == nullptr) { ${directBranch} } else { ${storedBranch} } }()`;
|
|
652
|
+
if (result === null)
|
|
653
|
+
lines.push(`${invocation};`);
|
|
654
|
+
else if (result.representation.kind === 'void') {
|
|
655
|
+
lines.push(`${invocation};`);
|
|
656
|
+
defineValueAlias(ctx, result, '(void)0');
|
|
657
|
+
}
|
|
658
|
+
else
|
|
659
|
+
lines.push(`${defineValue(ctx, result)} = ${invocation};`);
|
|
660
|
+
return true;
|
|
661
|
+
}
|
|
662
|
+
catch (error) {
|
|
663
|
+
if (!isCppEmitBlockedError(error))
|
|
664
|
+
throw error;
|
|
665
|
+
// The ordinary `.call()` path declares its result in the READ's type. That
|
|
666
|
+
// is only the stored callable's own result when the two agree physically;
|
|
667
|
+
// a stored branch that refused to convert between them has just proved
|
|
668
|
+
// they do not, and declining would emit C++ clang rejects (or, with a
|
|
669
|
+
// reinterpreting carrier, silently misreads). Refuse by name instead.
|
|
670
|
+
if (representationKey(storedAbi.result) !== representationKey(callee.abi.result))
|
|
671
|
+
throw error;
|
|
672
|
+
if (snapshot === null)
|
|
673
|
+
return false;
|
|
674
|
+
// A snapshot the census admitted must NEVER reach the ordinary `.call()`
|
|
675
|
+
// path below unmaterialized: `emitGet` rendered it as the field's RAW
|
|
676
|
+
// storage, with no guarantee `invoke` is non-null. This is the one refusal
|
|
677
|
+
// `lazyCalleeReadsOf` could not predict structurally (a generic field's
|
|
678
|
+
// per-call-site result, `test/runtime/pending-promise-payload-narrowed-at-call.ts`'s
|
|
679
|
+
// `cached`) -- so, rather than decline to a `.call()` this copy cannot
|
|
680
|
+
// safely make, materialize the copy itself (the SAME initializer thunk
|
|
681
|
+
// `class-layout.ts`'s `lazyMaterializedFieldText` calls) and publish it
|
|
682
|
+
// into the field ONLY if the field is still unmaterialized -- an argument
|
|
683
|
+
// between the read and this call may already have written a real value
|
|
684
|
+
// there, which this must not clobber. Never re-reads the field into the
|
|
685
|
+
// local: that would pick up exactly the reassignment the snapshot exists
|
|
686
|
+
// to see past. The ordinary call path below then reads `snapshot` back
|
|
687
|
+
// through `operandText`, now guaranteed materialized either way.
|
|
688
|
+
const receiverText = operandText(ctx, memberRead.receiver);
|
|
689
|
+
const accessor = memberAccessOperator(receiverRepresentation.ownership);
|
|
690
|
+
const fieldText = `${receiverText}${accessor}${cppRecordFieldName(fieldName)}`;
|
|
691
|
+
lines.push(`if (${snapshot}.invoke == nullptr) { ${snapshot} = ${cppBodyName(plan.initializer)}(${receiverText}); ` +
|
|
692
|
+
`if (${fieldText}.invoke == nullptr) { ${fieldText} = ${snapshot}; } }`);
|
|
693
|
+
return false;
|
|
694
|
+
}
|
|
695
|
+
};
|
|
696
|
+
const functionConstructorArgumentTexts = (ctx, arguments_, internalMethod) => arguments_.map((argument) => {
|
|
697
|
+
if (functionConstructorArgumentKindOf(argument.representation) === null) {
|
|
698
|
+
throw createCppEmitBlockedError(`call-abi:function-constructor:${internalMethod}`, 'Function construction requires string or dynamic source arguments');
|
|
699
|
+
}
|
|
700
|
+
return `gea::Eval::functionArgument(${operandText(ctx, argument)})`;
|
|
701
|
+
});
|
|
702
|
+
export const emitCall = (ctx, lines, operation) => {
|
|
703
|
+
if (ctx.numericCalls.get(operation) === 'imul') {
|
|
704
|
+
const args = operation.arguments.map((argument) => {
|
|
705
|
+
const text = operandText(ctx, argument);
|
|
706
|
+
// A proven integer can still live in a double global/captured cell.
|
|
707
|
+
// Convert through the exact signed carrier before taking its low bits:
|
|
708
|
+
// casting a negative double directly to uint32_t is undefined in C++.
|
|
709
|
+
return isIntegerStorageValue(ctx, argument.value)
|
|
710
|
+
? `static_cast<std::uint32_t>(static_cast<long long>(${text}))`
|
|
711
|
+
: `gea::toUint32(${text})`;
|
|
712
|
+
});
|
|
713
|
+
const expression = `gea::integerImul(${args.join(', ')})`;
|
|
714
|
+
if (operation.result && !ctx.unreadValues.has(operation.result.id))
|
|
715
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${expression};`);
|
|
716
|
+
else
|
|
717
|
+
lines.push(`${expression};`);
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
if (operation.result !== null && ctx.unreadValues.has(operation.result.id)) {
|
|
721
|
+
emitCall(ctx, lines, { ...operation, result: null });
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
if (emitNullishInvocation(ctx, lines, operation))
|
|
725
|
+
return;
|
|
726
|
+
if (operation.callee.representation.kind === 'native-handle' && operation.callee.representation.protocol === 'FunctionConstructor') {
|
|
727
|
+
if (!ctx.deriver.dynamicFallback)
|
|
728
|
+
throw createCppEmitBlockedError('call-abi:function-constructor', 'Function construction requires --dynamic-fallback');
|
|
729
|
+
const args = functionConstructorArgumentTexts(ctx, operation.arguments, 'call');
|
|
730
|
+
const expression = `gea::Eval::constructFunction({${args.join(', ')}})`;
|
|
731
|
+
if (operation.result)
|
|
732
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${expression};`);
|
|
733
|
+
else
|
|
734
|
+
lines.push(`${expression};`);
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
const functionSourceText = ctx.functionSourceReads.get(operation.callee.value);
|
|
738
|
+
if (functionSourceText) {
|
|
739
|
+
const text = functionSourceText;
|
|
740
|
+
if (operation.result?.representation.kind === 'void') {
|
|
741
|
+
lines.push(`${text};`);
|
|
742
|
+
defineValueAlias(ctx, operation.result, '(void)0');
|
|
743
|
+
}
|
|
744
|
+
else if (operation.result) {
|
|
745
|
+
const converted = alignedValueText(ctx, 'emit-callable.ts:794', { kind: 'string' }, operation.result.representation, text);
|
|
746
|
+
if (converted === null)
|
|
747
|
+
throw createCppEmitBlockedError(`conversion:string->${representationKey(operation.result.representation)}`, 'Function.toString result has no string conversion');
|
|
748
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${converted};`);
|
|
749
|
+
}
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
// Before every dispatch path below, because this one renders STATEMENTS
|
|
753
|
+
// rather than an expression a single assignment consumes: `JSON.stringify`
|
|
754
|
+
// serialized straight into the cell that receives it. See
|
|
755
|
+
// `emit-json.ts`'s `jsonStringifyFillLines`.
|
|
756
|
+
const jsonFill = jsonStringifyFillLines(ctx, operation);
|
|
757
|
+
if (jsonFill !== null) {
|
|
758
|
+
lines.push(...jsonFill);
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
// Both host paths -- a host method reached through a receiver, and a host
|
|
762
|
+
// handle used as the callee itself -- render a qualified C++ expression
|
|
763
|
+
// rather than a call through a callable carrier (see `emit-host-invoke.ts`).
|
|
764
|
+
const hostCall = hostCallText(ctx, operation);
|
|
765
|
+
if (hostCall !== null) {
|
|
766
|
+
// A member the host states is REALLY `void` -- `OscillatorNode.connect`'s
|
|
767
|
+
// ambient declaration says `AudioDestinationNode` for the Web Audio
|
|
768
|
+
// chaining convention, `gea::host::OscillatorNode::connect` is `void` --
|
|
769
|
+
// is rendered as a bare statement regardless of `operation.result`: the
|
|
770
|
+
// checker's ABI minted a result carrier from the declared type, and
|
|
771
|
+
// assigning that carrier from a void C++ expression is what "no viable
|
|
772
|
+
// overloaded '='" was, at the one place upstream that knows the real
|
|
773
|
+
// signature (`HostSpellings.voidResults`, host-members.ts). Discarding
|
|
774
|
+
// here is sound exactly when the discarded value is never read: if some
|
|
775
|
+
// later operation still names `operation.result.id` as an operand, that
|
|
776
|
+
// read throws `nameOfValue`'s own "read before it is defined" error
|
|
777
|
+
// rather than silently compiling a wrong value -- fail closed, not fail
|
|
778
|
+
// quiet.
|
|
779
|
+
const read = ctx.hostMemberReads.get(operation.callee.value);
|
|
780
|
+
const forcedVoid = read !== undefined && ctx.hosts.voidResults.has(`${read.protocol}.${read.member}`);
|
|
781
|
+
if (!operation.result) {
|
|
782
|
+
lines.push(`${hostCall};`);
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
if (operation.result.representation.kind === 'void') {
|
|
786
|
+
lines.push(`${hostCall};`);
|
|
787
|
+
defineValueAlias(ctx, operation.result, '(void)0');
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
if (forcedVoid) {
|
|
791
|
+
lines.push(`${hostCall};`);
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
// As with a host data member: what the host's C++ returns and what the
|
|
795
|
+
// program holds are not always one carrier -- see `hostResultText`.
|
|
796
|
+
const hostMember = read === undefined ? undefined : hostMemberOf(ctx.hosts.members, read.protocol, read.member);
|
|
797
|
+
const dynamicHostResult = hostMember?.kind === 'method' && hostMember.result === 'dynamic';
|
|
798
|
+
const spelling = dynamicHostResult ? { kind: 'path', text: hostCall, result: 'dynamic' } : undefined;
|
|
799
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${hostResultText(operation.result.representation, hostCall, spelling)};`);
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
// A String/Array.prototype method read defers the same way a host member's
|
|
803
|
+
// does (see `emit-prototype-invoke.ts`'s header) -- checked here, before the
|
|
804
|
+
// generic callable-carrier dispatch below, for the identical reason: this
|
|
805
|
+
// callee was never materialized as a real `CallableObject`.
|
|
806
|
+
const prototypeCall = prototypeMethodCallText(ctx, operation);
|
|
807
|
+
if (prototypeCall !== null) {
|
|
808
|
+
const expression = typeof prototypeCall === 'string' ? prototypeCall : prototypeCall.expression;
|
|
809
|
+
const resultStorage = typeof prototypeCall === 'string' ? undefined : prototypeCall.resultStorage;
|
|
810
|
+
if (!operation.result)
|
|
811
|
+
lines.push(`${expression};`);
|
|
812
|
+
else if (operation.result.representation.kind === 'void') {
|
|
813
|
+
lines.push(`${expression};`);
|
|
814
|
+
defineValueAlias(ctx, operation.result, '(void)0');
|
|
815
|
+
}
|
|
816
|
+
else
|
|
817
|
+
lines.push(`${defineValue(ctx, operation.result, resultStorage)} = ${expression};`);
|
|
818
|
+
// A call that CHANGED a reactive array's contents ticks its revision cell,
|
|
819
|
+
// so a list rendered from it rebuilds. `push`/`splice`/`sort` mutate;
|
|
820
|
+
// `slice`/`concat`/`map` build a new array and owe nothing.
|
|
821
|
+
const mutated = ctx.reactiveOrigins.get(operation.callee.value);
|
|
822
|
+
const member = ctx.prototypeMethodReads.get(operation.callee.value)?.member;
|
|
823
|
+
if (mutated !== undefined && member !== undefined && mutatingArrayMembers.has(member))
|
|
824
|
+
lines.push(`${reactiveRevisionText(ctx, mutated)}.notify();`);
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
if (emitUnionMethodCall(ctx, lines, operation))
|
|
828
|
+
return;
|
|
829
|
+
if (emitLazyArrowFieldCall(ctx, lines, operation))
|
|
830
|
+
return;
|
|
831
|
+
// `f?.(x)` proves its callee present and then calls it, so the operand's
|
|
832
|
+
// declared type is the callable while the value flowing in is still the
|
|
833
|
+
// `Optional<callable>` the plan gave the read -- the same carrier gap a
|
|
834
|
+
// present receiver has, answered by the same helper. It is unwrapped here
|
|
835
|
+
// rather than at the top of this function so a host or prototype call, which
|
|
836
|
+
// returns above without ever loading a callable, does not emit a dereference
|
|
837
|
+
// nothing reads.
|
|
838
|
+
const calleeOperand = unwrapPresentValue(ctx, lines, operation.callee);
|
|
839
|
+
const callee = calleeOperand.representation;
|
|
840
|
+
// A CHOICE of generic functions: the callee is the index of the member the
|
|
841
|
+
// cell holds, and `operation.family` names the copy each member runs at
|
|
842
|
+
// this call. Every arm is a direct call by name, padded to its own
|
|
843
|
+
// convention -- the arguments were packed against none
|
|
844
|
+
// (`ir/lower-invocation.ts`), exactly as a callable union's arms are.
|
|
845
|
+
if (callee.kind === 'generic-function-set') {
|
|
846
|
+
if (!operation.family)
|
|
847
|
+
throw createCppEmitBlockedError('call-abi:generic-function-set', 'a call through a generic function set names no instantiated family');
|
|
848
|
+
if (operation.receiver !== null)
|
|
849
|
+
throw createCppEmitBlockedError('call-abi:generic-function-set', 'a generic function set call carries a receiver its members declare none of');
|
|
850
|
+
const family = operation.family;
|
|
851
|
+
const voidResult = operation.result?.representation.kind === 'void';
|
|
852
|
+
const branches = callee.members.map((member, index) => {
|
|
853
|
+
const target = family.find((entry) => entry.member === member);
|
|
854
|
+
if (!target)
|
|
855
|
+
throw createCppEmitBlockedError('call-abi:generic-function-set', `generic function set member ${member} has no instantiated copy at this call`);
|
|
856
|
+
const abi = ctx.abiOfCallable(target.functionId);
|
|
857
|
+
if (!abi || abi.receiver !== null || abi.restFrom !== null) {
|
|
858
|
+
throw createCppEmitBlockedError('call-abi:generic-function-set', `generic function set member ${member} needs a fixed, receiver-free calling convention`);
|
|
859
|
+
}
|
|
860
|
+
const args = receivableArguments(abi, operation.arguments).map((argument, position) => argumentText(ctx, abi, position, argument));
|
|
861
|
+
const invocation = `${cppBodyName(target.functionId)}(${paddedArguments(abi, args, 'generic set call').join(', ')})`;
|
|
862
|
+
let body = `${invocation}; return;`;
|
|
863
|
+
if (operation.result && !voidResult) {
|
|
864
|
+
const source = abi.result.kind === 'void' ? { kind: 'undefined' } : abi.result;
|
|
865
|
+
const text = abi.result.kind === 'void' ? cppUndefinedValue : invocation;
|
|
866
|
+
const converted = alignedValueText(ctx, 'emit-callable.ts:909', source, operation.result.representation, text);
|
|
867
|
+
if (converted === null) {
|
|
868
|
+
throw createCppEmitBlockedError(`conversion:${representationKey(abi.result)}->${representationKey(operation.result.representation)}`, `generic function set member ${member} returns ${representationKey(abi.result)}, which cannot fill ${representationKey(operation.result.representation)}`);
|
|
869
|
+
}
|
|
870
|
+
body = `${abi.result.kind === 'void' ? `${invocation}; ` : ''}return ${converted};`;
|
|
871
|
+
}
|
|
872
|
+
return `case ${index}: { ${body} }`;
|
|
873
|
+
});
|
|
874
|
+
const resultType = !operation.result || voidResult ? 'void' : cppTypeOf(operation.result.representation);
|
|
875
|
+
const invocation = `[&]() -> ${resultType} { switch (${operandText(ctx, calleeOperand)}) { ${branches.join(' ')} ` +
|
|
876
|
+
`default: gea::detail::refuseTaggedUnionArmMismatch("a generic function set tag naming no member"); } }()`;
|
|
877
|
+
if (!operation.result)
|
|
878
|
+
lines.push(`${invocation};`);
|
|
879
|
+
else if (voidResult) {
|
|
880
|
+
lines.push(`${invocation};`);
|
|
881
|
+
defineValueAlias(ctx, operation.result, '(void)0');
|
|
882
|
+
}
|
|
883
|
+
else
|
|
884
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${invocation};`);
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
// A sum of native callables keeps each frame intact. Dispatching on its
|
|
888
|
+
// tag invokes that frame directly; boxing the sum would erase information
|
|
889
|
+
// already present in every arm. Rest/receiver frames need their own packing
|
|
890
|
+
// and are deliberately refused until that per-arm operation is installed.
|
|
891
|
+
if (callee.kind === 'tagged-union') {
|
|
892
|
+
if (operation.receiver !== null || callee.arms.length === 0) {
|
|
893
|
+
throw createCppEmitBlockedError('call-abi:tagged-union-call', 'a native callable union needs nonempty, receiver-free alternatives');
|
|
894
|
+
}
|
|
895
|
+
const voidResult = operation.result?.representation.kind === 'void';
|
|
896
|
+
const branches = callee.arms.map((arm, index) => {
|
|
897
|
+
const selected = `gea_union_callee.get<${index}>()`;
|
|
898
|
+
// A bare `Function` arm genuinely has no static frame. Keep that arm's
|
|
899
|
+
// dynamic invocation local to the union branch while fixed native arms
|
|
900
|
+
// retain their direct convention. Hono's middleware selection is the
|
|
901
|
+
// representative shape: `Function | Next`, narrowed present before the
|
|
902
|
+
// call. Boxing the whole sum would erase Next's known ABI; refusing the
|
|
903
|
+
// dynamic arm would reject a call the runtime already knows how to make.
|
|
904
|
+
if (arm.value.kind === 'dynamic') {
|
|
905
|
+
const boxed = operation.arguments.map((argument) => alignedText(ctx, arm.value, argument, 'boxed-arm'));
|
|
906
|
+
const invocation = `${selected}.callAsFunction({${boxed.join(', ')}})`;
|
|
907
|
+
let body = `${invocation}; return;`;
|
|
908
|
+
if (operation.result && !voidResult) {
|
|
909
|
+
const held = operation.result.representation;
|
|
910
|
+
const converted = held.kind === 'dynamic' ? invocation : alignedValueText(ctx, 'emit-callable.ts:954', arm.value, held, invocation);
|
|
911
|
+
if (converted === null) {
|
|
912
|
+
throw createCppEmitBlockedError(`conversion:${representationKey(arm.value)}->${representationKey(held)}`, `a dynamic callable-union arm's result is read as ${representationKey(held)}, and no installed load performs that conversion`);
|
|
913
|
+
}
|
|
914
|
+
body = `return ${converted};`;
|
|
915
|
+
}
|
|
916
|
+
return index === callee.arms.length - 1 ? body : `if (gea_union_callee.is<${index}>()) { ${body} }`;
|
|
917
|
+
}
|
|
918
|
+
const abi = callableObjectAbi(arm.value);
|
|
919
|
+
if (!abi || abi.receiver !== null || abi.restFrom !== null) {
|
|
920
|
+
throw createCppEmitBlockedError('call-abi:tagged-union-call', 'a native callable union arm needs a fixed, receiver-free calling convention');
|
|
921
|
+
}
|
|
922
|
+
const args = receivableArguments(abi, operation.arguments).map((argument, position) => argumentText(ctx, abi, position, argument));
|
|
923
|
+
const invocation = `${selected}.call(${paddedArguments(abi, args, 'union call').join(', ')})`;
|
|
924
|
+
let body = `${invocation}; return;`;
|
|
925
|
+
if (operation.result && !voidResult) {
|
|
926
|
+
const source = abi.result.kind === 'void' ? { kind: 'undefined' } : abi.result;
|
|
927
|
+
const text = abi.result.kind === 'void' ? cppUndefinedValue : invocation;
|
|
928
|
+
const converted = alignedValueText(ctx, 'emit-callable.ts:975', source, operation.result.representation, text);
|
|
929
|
+
if (converted === null)
|
|
930
|
+
throw createCppEmitBlockedError(`conversion:${representationKey(source)}->${representationKey(operation.result.representation)}`, 'a native callable union result has no installed conversion');
|
|
931
|
+
body = `${abi.result.kind === 'void' ? `${invocation}; ` : ''}return ${converted};`;
|
|
932
|
+
}
|
|
933
|
+
return index === callee.arms.length - 1 ? body : `if (gea_union_callee.is<${index}>()) { ${body} }`;
|
|
934
|
+
});
|
|
935
|
+
const resultType = !operation.result || voidResult ? 'void' : cppTypeOf(operation.result.representation);
|
|
936
|
+
const invocation = `[&]() -> ${resultType} { const auto& gea_union_callee = ${operandText(ctx, calleeOperand)}; ${branches.join(' ')} }()`;
|
|
937
|
+
if (!operation.result)
|
|
938
|
+
lines.push(`${invocation};`);
|
|
939
|
+
else if (voidResult) {
|
|
940
|
+
lines.push(`${invocation};`);
|
|
941
|
+
defineValueAlias(ctx, operation.result, '(void)0');
|
|
942
|
+
}
|
|
943
|
+
else
|
|
944
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${invocation};`);
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
// A callee the program never gave a type: `const fn: any = handlers[i];
|
|
948
|
+
// fn(event)`. There is no convention to build a frame against, so the
|
|
949
|
+
// arguments cross as boxes and the callable's own declared parameters are
|
|
950
|
+
// recovered by the thunk `gea::Value::box` recorded
|
|
951
|
+
// (`gea::Value::callAsFunction`, gea_runtime.h). Rendered before the
|
|
952
|
+
// callable-carrier dispatch below for the same reason the host paths are:
|
|
953
|
+
// this callee was never materialised as a `CallableObject` the emitter can
|
|
954
|
+
// see the ABI of.
|
|
955
|
+
if (callee.kind === 'dynamic') {
|
|
956
|
+
// The dynamic call adapter transports this separately from positional
|
|
957
|
+
// arguments; the actual callable decides whether its ABI consumes it.
|
|
958
|
+
const boxed = operation.arguments.map((argument) => alignedText(ctx, callee, argument, 'boxed-callee'));
|
|
959
|
+
const receiver = operation.receiver ? boxedValueText(ctx, operation.receiver, 'dynamic call receiver') : 'gea::Value()';
|
|
960
|
+
const text = `${operandText(ctx, calleeOperand)}.callWithReceiver(${receiver}, {${boxed.join(', ')}})`;
|
|
961
|
+
if (!operation.result) {
|
|
962
|
+
lines.push(`${text};`);
|
|
963
|
+
return;
|
|
964
|
+
}
|
|
965
|
+
if (operation.result.representation.kind === 'void') {
|
|
966
|
+
lines.push(`${text};`);
|
|
967
|
+
defineValueAlias(ctx, operation.result, '(void)0');
|
|
968
|
+
return;
|
|
969
|
+
}
|
|
970
|
+
// The call answers a box; what the program holds may be narrower -- `const
|
|
971
|
+
// ok: boolean = fn()` is the checker's answer to a call whose callee is
|
|
972
|
+
// `any`, and the box has to be read back into it.
|
|
973
|
+
const held = operation.result.representation;
|
|
974
|
+
const converted = held.kind === 'dynamic' ? text : alignedValueText(ctx, 'emit-callable.ts:1017', callee, held, text);
|
|
975
|
+
if (converted === null) {
|
|
976
|
+
throw createCppEmitBlockedError(`conversion:${representationKey(callee)}->${representationKey(held)}`, `a dynamic call's result is read as ${representationKey(held)}, and no installed load performs that conversion`);
|
|
977
|
+
}
|
|
978
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${converted};`);
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
981
|
+
if (callee.kind !== 'function' &&
|
|
982
|
+
callee.kind !== 'function-value-dispatch' &&
|
|
983
|
+
callee.kind !== 'function-family' &&
|
|
984
|
+
callee.kind !== 'function-and-constructor') {
|
|
985
|
+
throw createCppEmitBlockedError(`call-abi:no-invoke-path:${callee.kind}`, `a callee carried as "${callee.kind}" has no invoke path; this emitter calls only through a callable carrier`);
|
|
986
|
+
}
|
|
987
|
+
// The convention a *call* site selects, which for a value that is also a
|
|
988
|
+
// constructor is its `[[Call]]` half and never its `[[Construct]]` one.
|
|
989
|
+
const abi = callee.kind === 'function-and-constructor' ? callee.call : callee.abi;
|
|
990
|
+
// A directly-bound class method records the receiver at the property read.
|
|
991
|
+
// Usually IR carries that same receiver on the call. A materialized method
|
|
992
|
+
// value can instead publish the receiver-bearing physical convention while
|
|
993
|
+
// the semantic call still has the receiver-less method-value convention;
|
|
994
|
+
// in that case the authenticated property-read receiver fills the ABI slot.
|
|
995
|
+
// Keep this tied to the exact callee value and direct body: a detached
|
|
996
|
+
// function value has no receiver to recover, and must still refuse below.
|
|
997
|
+
const direct = calleeOperand.value === operation.callee.value ? ctx.directCallees.get(operation.callee.value) : undefined;
|
|
998
|
+
// A direct call's C++ target IS the concrete body, whose declared parameter
|
|
999
|
+
// carriers can be narrower than the semantic ABI at the read that reached
|
|
1000
|
+
// it -- the same erasure the RESULT conversion below already reconciles
|
|
1001
|
+
// through `ctx.directCalleeAbis` (see the comment at its use, further down).
|
|
1002
|
+
// Argument conversion has to go through that identical physical ABI: a
|
|
1003
|
+
// generic method's one C++ body declares a concrete carrier (e.g.
|
|
1004
|
+
// `Optional<Ref<TypedArray<uint8_t>>>` for a `ReadableStream<Uint8Array>`
|
|
1005
|
+
// method) while the semantic read that named it can still carry the wider
|
|
1006
|
+
// erased carrier (`gea::Value`) of the unspecialized declaration. Building
|
|
1007
|
+
// arguments from the semantic `abi` in that case hands the physical body a
|
|
1008
|
+
// `gea::Value` for a slot it never declared, with no conversion rendered.
|
|
1009
|
+
//
|
|
1010
|
+
// WHETHER A RECEIVER TRAVELS is the one thing that stays on the semantic
|
|
1011
|
+
// `abi`, because that is a fact about the CALL -- a method's convention
|
|
1012
|
+
// states a receiver its receiver-less view does not, and reading that off the
|
|
1013
|
+
// physical ABI dropped the receiver of an interface-typed call whose direct
|
|
1014
|
+
// callee was a class method, sliding the first real argument into the
|
|
1015
|
+
// receiver's place (`test/runtime/direct-interface-method-receiver.ts`).
|
|
1016
|
+
//
|
|
1017
|
+
// ARITY AND THE RECEIVER'S CARRIER do NOT. They used to, and that was only
|
|
1018
|
+
// ever true while every body a direct bind could name repeated its
|
|
1019
|
+
// declaration's parameter list. An OVERRIDE need not: method parameters are
|
|
1020
|
+
// bivariant, so `ServerResponse.write(chunk: string | Uint8Array)` overrides
|
|
1021
|
+
// `Writable.write(chunk: unknown, encoding?: unknown, callback?: unknown)`,
|
|
1022
|
+
// and a call reaching that body through `(outgoing as Writable).write(value)`
|
|
1023
|
+
// built the base's three-argument list -- padded with `undefined`, with the
|
|
1024
|
+
// argument boxed into the base's `unknown` slot -- around the override's
|
|
1025
|
+
// one-parameter body, and passed it a `Ref<Writable>` for a
|
|
1026
|
+
// `const Ref<ServerResponse>&`. `@hono/node-server`'s `listener.ts` writes
|
|
1027
|
+
// exactly that (`test/runtime/virtual-call-through-a-base-whose-override-narrows-unknown.ts`).
|
|
1028
|
+
// The surplus arguments are not lost: 10.2.1 binds only the parameters the
|
|
1029
|
+
// body declares and discards the rest, which is what dropping them here
|
|
1030
|
+
// spells.
|
|
1031
|
+
const directAbi = direct === undefined ? abi : (ctx.directCalleeAbis.get(operation.callee.value) ?? abi);
|
|
1032
|
+
// The frame the PHYSICAL call fills: the body's parameter list, under the
|
|
1033
|
+
// call's own answer to whether a receiver occupies the first formal.
|
|
1034
|
+
const physicalFrame = directAbi === abi ? abi : { ...directAbi, receiver: abi.receiver === null ? null : (directAbi.receiver ?? abi.receiver) };
|
|
1035
|
+
const boundReceiverOperand = operation.receiver === null ? ctx.directCallReceivers.get(operation.callee.value) : undefined;
|
|
1036
|
+
const declaredReceiverOperand = operation.receiver ?? (abi.receiver !== null ? (boundReceiverOperand ?? null) : null);
|
|
1037
|
+
// The receiver is the first physical argument when the convention declares
|
|
1038
|
+
// one, and it must be present exactly when it does: a call that passes a
|
|
1039
|
+
// receiver to a convention with none, or omits one where the convention
|
|
1040
|
+
// expects it, is a frame mismatch and not something to paper over. A direct
|
|
1041
|
+
// class method's recorded receiver above is part of that convention, not a
|
|
1042
|
+
// manufactured value.
|
|
1043
|
+
if ((abi.receiver === null) !== (declaredReceiverOperand === null)) {
|
|
1044
|
+
const propertyOrigin = ctx.propertyReadOrigins.get(operation.callee.value);
|
|
1045
|
+
const originText = propertyOrigin === undefined
|
|
1046
|
+
? 'no property-read origin'
|
|
1047
|
+
: `property-read origin ${representationKey(propertyOrigin.receiver.representation)}.${ctx.staticKeyTexts.get(propertyOrigin.key.value) ?? '<computed>'}`;
|
|
1048
|
+
throw createCppEmitBlockedError('call-abi:receiver-mismatch', declaredReceiverOperand
|
|
1049
|
+
? 'passes a receiver to a callee whose convention declares none'
|
|
1050
|
+
: `omits the receiver its callee's convention declares (callee ${operation.callee.value} is ${representationKey(callee)}; ${originText}; ` +
|
|
1051
|
+
`${boundReceiverOperand === undefined ? 'no recorded method receiver' : 'recorded method receiver present'})`);
|
|
1052
|
+
}
|
|
1053
|
+
// A host's own free function is called as a function, not through a
|
|
1054
|
+
// callable carrier: the read that reached it rendered nothing and recorded
|
|
1055
|
+
// the host's spelling instead (`emit-bindings.ts`), because there is no
|
|
1056
|
+
// object of that name to load. Asked for as an operand it refuses, which is
|
|
1057
|
+
// why the callee text is only taken on the carrier path.
|
|
1058
|
+
const hostFunction = ctx.hostFunctionReads.get(operation.callee.value);
|
|
1059
|
+
// A host's parameter that the program may omit is declared with a C++
|
|
1060
|
+
// default, which is filled in at the call site and not by passing an
|
|
1061
|
+
// absence. So a host call with a trailing run of optional arguments is
|
|
1062
|
+
// emitted once per arity rather than handed a `gea::Optional` the host's
|
|
1063
|
+
// `double` cannot accept -- see `emit-host-arity.ts`.
|
|
1064
|
+
if (hostFunction !== undefined) {
|
|
1065
|
+
// A host that answers in a `gea::Value` hands the crossing back a carrier
|
|
1066
|
+
// to REBUILD, and a `native-record-ref` names its layout by shape instead
|
|
1067
|
+
// of carrying it (`representation/model.ts`) -- so the materializer, which
|
|
1068
|
+
// reads fields off the representation it is given, saw no fields and fell
|
|
1069
|
+
// back to asking the box to already hold that exact struct. It never does:
|
|
1070
|
+
// `Reflect.getOwnPropertyDescriptor` returns an object the RUNTIME built.
|
|
1071
|
+
// The two are physically one C++ type (`obligations-graph.ts` says so
|
|
1072
|
+
// where it skips the conversion entirely), so this resolves the shape
|
|
1073
|
+
// through the deriver at the one boundary that needs the fields, rather
|
|
1074
|
+
// than threading a deriver through every unbox in the backend.
|
|
1075
|
+
const resolvedRecordRefs = (representation) => {
|
|
1076
|
+
if (representation.kind === 'optional') {
|
|
1077
|
+
const payload = resolvedRecordRefs(representation.payload);
|
|
1078
|
+
return payload === representation.payload ? representation : { ...representation, payload };
|
|
1079
|
+
}
|
|
1080
|
+
if (representation.kind !== 'native-record-ref' || representation.native !== null)
|
|
1081
|
+
return representation;
|
|
1082
|
+
const derived = ctx.deriver.layoutOf(representation.shapeId);
|
|
1083
|
+
return derived.kind === 'record' ? derived : representation;
|
|
1084
|
+
};
|
|
1085
|
+
const reflection = nativeReflectCallText(ctx, operation, hostFunction.kind === 'path' ? hostFunction.text : hostFunction.emit);
|
|
1086
|
+
if (reflection !== null) {
|
|
1087
|
+
lines.push(operation.result ? `${defineValue(ctx, operation.result)} = ${reflection};` : `${reflection};`);
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
// Host overloads consume the actual native value. Do not mint a larger
|
|
1091
|
+
// union just to dispatch it immediately: that instantiates unreachable
|
|
1092
|
+
// host overloads, and an optional wrapper can disagree with the actual
|
|
1093
|
+
// arity. Retain real conversions (e.g. callback ABI adaptation); only a
|
|
1094
|
+
// widening into an identical payload is unnecessary at this boundary.
|
|
1095
|
+
const slotOf = (position, fallback) => (abi.restFrom !== null && position >= abi.restFrom ? undefined : abi.parameters[position]?.value) ?? fallback;
|
|
1096
|
+
const passed = [
|
|
1097
|
+
...(operation.receiver
|
|
1098
|
+
? [{ text: receiverArgumentText(ctx, abi, operation.receiver), representation: abi.receiver ?? operation.receiver.representation }]
|
|
1099
|
+
: []),
|
|
1100
|
+
...receivableArguments(abi, operation.arguments).map((argument, position) => {
|
|
1101
|
+
if (hostFunction.arguments === 'dynamic')
|
|
1102
|
+
return {
|
|
1103
|
+
text: boxedValueText(ctx, argument, 'dynamic host argument'),
|
|
1104
|
+
representation: { kind: 'dynamic', reason: 'declared-any-never-narrowed' }
|
|
1105
|
+
};
|
|
1106
|
+
const slot = slotOf(position, argument.representation);
|
|
1107
|
+
const preserve = hostPayloadIdentity(argument.representation, slot);
|
|
1108
|
+
return {
|
|
1109
|
+
text: preserve ? operandText(ctx, argument) : argumentText(ctx, abi, position, argument),
|
|
1110
|
+
representation: preserve ? argument.representation : slot
|
|
1111
|
+
};
|
|
1112
|
+
})
|
|
1113
|
+
];
|
|
1114
|
+
const optionals = trailingOptionalCount(passed);
|
|
1115
|
+
const voidResult = operation.result?.representation.kind === 'void';
|
|
1116
|
+
lines.push(...hostArityCallLines({
|
|
1117
|
+
spelling: hostFunction.kind === 'path' && ctx.hosts.nativeArrayFunctions?.has(hostFunction.text)
|
|
1118
|
+
? { ...hostFunction, arrayArguments: 'native' }
|
|
1119
|
+
: hostFunction.kind === 'path' && ctx.hosts.arraySnapshotFunctions?.has(hostFunction.text)
|
|
1120
|
+
? { ...hostFunction, arrayArguments: 'snapshot' }
|
|
1121
|
+
: hostFunction,
|
|
1122
|
+
fixed: passed.slice(0, passed.length - optionals),
|
|
1123
|
+
optionals: passed.slice(passed.length - optionals),
|
|
1124
|
+
result: operation.result && !voidResult
|
|
1125
|
+
? {
|
|
1126
|
+
name: defineValue(ctx, operation.result),
|
|
1127
|
+
representation: hostFunction.result === 'dynamic' ? resolvedRecordRefs(operation.result.representation) : operation.result.representation
|
|
1128
|
+
}
|
|
1129
|
+
: null
|
|
1130
|
+
}));
|
|
1131
|
+
if (operation.result && voidResult)
|
|
1132
|
+
defineValueAlias(ctx, operation.result, '(void)0');
|
|
1133
|
+
return;
|
|
1134
|
+
}
|
|
1135
|
+
const args = [
|
|
1136
|
+
...(declaredReceiverOperand ? [receiverArgumentText(ctx, physicalFrame, declaredReceiverOperand)] : []),
|
|
1137
|
+
...receivableArguments(physicalFrame, operation.arguments).map((argument, position) => argumentText(ctx, directAbi, position, argument))
|
|
1138
|
+
];
|
|
1139
|
+
// A callee whose environment is `nullptr` is called by name. The carrier is
|
|
1140
|
+
// still built wherever the program stores one -- this only decides how THIS
|
|
1141
|
+
// call reaches the body, and an unread `CallableObject` the C++ compiler then
|
|
1142
|
+
// drops costs nothing. Guarded on the operand being the callee itself: `f?.()`
|
|
1143
|
+
// unwraps an `Optional` first, and what was proved present is a different
|
|
1144
|
+
// value from the one the record was keyed by.
|
|
1145
|
+
// A method the program overrides is reached through the OBJECT, not by name:
|
|
1146
|
+
// `virtual-methods.ts` put a dispatch member on the family's root struct, and
|
|
1147
|
+
// the receiver -- always the first padded argument, since a method's
|
|
1148
|
+
// convention states one -- selects the implementation. Binding the body the
|
|
1149
|
+
// member lookup found instead would run the base's for every instance of a
|
|
1150
|
+
// subclass, which compiles and links and is silently wrong.
|
|
1151
|
+
const dispatched = calleeOperand.value === operation.callee.value ? ctx.virtualCallees.get(operation.callee.value) : undefined;
|
|
1152
|
+
if (dispatched !== undefined)
|
|
1153
|
+
ctx.virtualCalleesUsed.add(operation.callee.value);
|
|
1154
|
+
// The receiver a direct bind needs and this call's own convention does not
|
|
1155
|
+
// declare -- see `EmitContext.directCallReceivers`. Prepended to the
|
|
1156
|
+
// PHYSICAL argument list only, so the convention, the plan and every arity
|
|
1157
|
+
// check above stay exactly as they are: this is the one place the emitted
|
|
1158
|
+
// frame and the declared one legitimately differ, because the body being
|
|
1159
|
+
// named is a method and the value being called is its receiver-less view.
|
|
1160
|
+
const boundReceiver = abi.receiver === null && boundReceiverOperand !== undefined ? operandText(ctx, boundReceiverOperand) : undefined;
|
|
1161
|
+
const padded = [...(boundReceiver === undefined ? [] : [boundReceiver]), ...paddedArguments(physicalFrame, args, 'call')];
|
|
1162
|
+
// The callable value is typed at the access site, but the C++ virtual member
|
|
1163
|
+
// is declared with the family root's reconciled ABI. Convert the receiver
|
|
1164
|
+
// and arguments into that ABI before invoking the member; using `args`
|
|
1165
|
+
// directly is only valid when every override repeats the root signature.
|
|
1166
|
+
const virtualArgs = dispatched === undefined
|
|
1167
|
+
? []
|
|
1168
|
+
: [
|
|
1169
|
+
...(operation.receiver ? [receiverArgumentText(ctx, dispatched.abi, operation.receiver)] : []),
|
|
1170
|
+
...receivableArguments(dispatched.abi, operation.arguments).map((argument, position) => argumentText(ctx, dispatched.abi, position, argument))
|
|
1171
|
+
];
|
|
1172
|
+
const virtualPadded = dispatched === undefined ? [] : paddedArguments(dispatched.abi, virtualArgs, 'virtual call');
|
|
1173
|
+
const memberRead = ctx.propertyReadOrigins.get(operation.callee.value);
|
|
1174
|
+
const memberKey = memberRead === undefined ? undefined : ctx.staticKeyTexts.get(memberRead.key.value);
|
|
1175
|
+
const candidate = memberRead === undefined || memberKey === undefined || calleeOperand.value !== operation.callee.value
|
|
1176
|
+
? undefined
|
|
1177
|
+
: ctx.callableMemberCandidates.get(callableMemberSlot(memberRead.receiver.representation, memberKey));
|
|
1178
|
+
const stableCandidate = ctx.stableBorrowEntries.get(direct ?? (candidate === undefined ? '' : cppBodyName(candidate)));
|
|
1179
|
+
const potentiallyMovingArguments = new Set(operation.arguments
|
|
1180
|
+
.filter((argument) => transferOf(ctx.dyingArguments, ctx.ownedValues, ctx.ownedDyingValues, argument.value) === 'move')
|
|
1181
|
+
.map((argument) => argument.value));
|
|
1182
|
+
const stableEntry = stableCandidate !== undefined &&
|
|
1183
|
+
stableBorrowEntryAccepts(stableCandidate, operation.arguments, ctx.stableBorrowActuals, potentiallyMovingArguments)
|
|
1184
|
+
? stableCandidate
|
|
1185
|
+
: undefined;
|
|
1186
|
+
const callMember = candidate === undefined
|
|
1187
|
+
? 'call'
|
|
1188
|
+
: stableEntry !== undefined
|
|
1189
|
+
? `callKnownBorrowed<&${cppThunkName(candidate)}, &${stableEntry.name}>`
|
|
1190
|
+
: ctx.borrowableMemberBodies.has(candidate)
|
|
1191
|
+
? `callKnownBorrowed<&${cppThunkName(candidate)}, &${cppBodyName(candidate)}>`
|
|
1192
|
+
: `callKnown<&${cppThunkName(candidate)}>`;
|
|
1193
|
+
const invocation = dispatched !== undefined
|
|
1194
|
+
? `${virtualPadded[0]}->${dispatched.member}(${virtualPadded.slice(1).join(', ')})`
|
|
1195
|
+
: direct !== undefined
|
|
1196
|
+
? `${stableEntry?.name ?? direct}(${padded.join(', ')})`
|
|
1197
|
+
: `${operandText(ctx, calleeOperand)}.${callMember}(${padded.join(', ')})`;
|
|
1198
|
+
if (!operation.result) {
|
|
1199
|
+
lines.push(`${invocation};`);
|
|
1200
|
+
return;
|
|
1201
|
+
}
|
|
1202
|
+
// A call whose expression is explicitly converted to `void` still runs the
|
|
1203
|
+
// callee, but there is no C++ result object to declare. Publish a real void
|
|
1204
|
+
// expression for the SSA result so a following discard can name it without
|
|
1205
|
+
// fabricating storage.
|
|
1206
|
+
if (operation.result.representation.kind === 'void') {
|
|
1207
|
+
lines.push(`${invocation};`);
|
|
1208
|
+
defineValueAlias(ctx, operation.result, '(void)0');
|
|
1209
|
+
return;
|
|
1210
|
+
}
|
|
1211
|
+
const name = defineValue(ctx, operation.result);
|
|
1212
|
+
// A convention whose result is `void` returns nothing at all in C++, so its
|
|
1213
|
+
// call is a statement and never the right-hand side of an assignment. The
|
|
1214
|
+
// language still gives the *expression* a value -- `undefined` -- and an
|
|
1215
|
+
// optional call is where that value becomes real: `d?.toggle()` has to merge
|
|
1216
|
+
// the call's answer with the absent branch's, so the result is materialized
|
|
1217
|
+
// even though a bare `d.toggle()` would discard it. Written as the literal it
|
|
1218
|
+
// is, rather than assigned from a void call, which is exactly the "no viable
|
|
1219
|
+
// overloaded '='" the host path's own `forcedVoid` above already avoids.
|
|
1220
|
+
if (abi.result.kind === 'void') {
|
|
1221
|
+
// Fail closed on any other carrier: a `void` convention can only ever have
|
|
1222
|
+
// produced `undefined`, so a plan that asked for something else is a
|
|
1223
|
+
// disagreement to fix rather than a value to invent here.
|
|
1224
|
+
if (operation.result.representation.kind !== 'undefined') {
|
|
1225
|
+
throw createCppEmitBlockedError(`conversion:void->${representationKey(operation.result.representation)}`, `a callee whose convention returns void was asked for a "${operation.result.representation.kind}" result`);
|
|
1226
|
+
}
|
|
1227
|
+
lines.push(`${invocation};`);
|
|
1228
|
+
lines.push(`${name} = ${cppUndefinedValue};`);
|
|
1229
|
+
return;
|
|
1230
|
+
}
|
|
1231
|
+
// The CONVENTION states what the call produces; the plan states what the
|
|
1232
|
+
// cell holds, and the two are not always the same carrier. A callable read
|
|
1233
|
+
// out of a union arm carries the arm's own convention -- hono's handler
|
|
1234
|
+
// slot declares `gea::Value(Context, Next)` because that arm's return type
|
|
1235
|
+
// is unstated -- while the call's published result is the type the checker
|
|
1236
|
+
// gave the call expression, `Response | Promise<Response>`. Assigning the
|
|
1237
|
+
// convention's result straight into the cell is the "no viable overloaded
|
|
1238
|
+
// '='" clang reports, and there is no defensible identity to claim between
|
|
1239
|
+
// the two: the box has to be read back out.
|
|
1240
|
+
//
|
|
1241
|
+
// Reconciled through the one conversion authority rather than a local
|
|
1242
|
+
// widening, and refused by name when it has no answer, so a disagreement
|
|
1243
|
+
// between the two is a defect to fix rather than a cast invented here.
|
|
1244
|
+
// A virtual call is physically declared with the family root's ABI, even
|
|
1245
|
+
// when the callable value and the call expression retain a covariant
|
|
1246
|
+
// descendant result. Reconcile from the carrier the invocation actually
|
|
1247
|
+
// returns; using the semantic callable ABI here makes that conversion look
|
|
1248
|
+
// like identity and emits `Ref<Derived> = Ref<Base>` at the assignment.
|
|
1249
|
+
// `convertedValueText` performs the checked class narrowing using the
|
|
1250
|
+
// target's ancestry, while non-virtual calls keep their ordinary ABI.
|
|
1251
|
+
const physicalResult = dispatched?.abi.result ?? ctx.directCalleeAbis.get(operation.callee.value)?.result ?? abi.result;
|
|
1252
|
+
const produced = alignedValueText(ctx, 'emit-callable.ts:1285', physicalResult, operation.result.representation, callResultName);
|
|
1253
|
+
if (produced === null) {
|
|
1254
|
+
throw createCppEmitBlockedError(`conversion:${representationKey(physicalResult)}->${representationKey(operation.result.representation)}`, `a callee whose convention returns "${physicalResult.kind}" cannot fill a "${operation.result.representation.kind}" result`);
|
|
1255
|
+
}
|
|
1256
|
+
// Identity needs no local at all. When a real conversion was rendered it
|
|
1257
|
+
// may read its source more than once (the unbox-into-a-union chain tests
|
|
1258
|
+
// the tag before loading the payload), so the call is evaluated ONCE into a
|
|
1259
|
+
// local and the conversion reads that -- an IIFE rather than two statements
|
|
1260
|
+
// because a declaration between two of this body's `goto` labels would let
|
|
1261
|
+
// a jump bypass its initialization.
|
|
1262
|
+
if (produced === callResultName) {
|
|
1263
|
+
lines.push(`${name} = ${invocation};`);
|
|
1264
|
+
return;
|
|
1265
|
+
}
|
|
1266
|
+
const target = cppTypeOf(operation.result.representation);
|
|
1267
|
+
lines.push(`${name} = ([&]() -> ${target} { auto ${callResultName} = ${invocation}; return ${produced}; }());`);
|
|
1268
|
+
};
|
|
1269
|
+
/** The local a call's own result is read into when its carrier has to be reconciled with the cell's. */
|
|
1270
|
+
const callResultName = 'gea_call_result';
|
|
1271
|
+
/** How many of a call's arguments trail it as optionals -- the run at the end, and only the run at the end. */
|
|
1272
|
+
const hostPayloadIdentity = (source, target) => {
|
|
1273
|
+
if (representationKey(source) === representationKey(target))
|
|
1274
|
+
return true;
|
|
1275
|
+
if (target.kind === 'optional')
|
|
1276
|
+
return hostPayloadIdentity(source, target.payload);
|
|
1277
|
+
if (target.kind === 'tagged-union')
|
|
1278
|
+
return target.arms.some((arm) => hostPayloadIdentity(source, arm.value));
|
|
1279
|
+
return false;
|
|
1280
|
+
};
|
|
1281
|
+
const trailingOptionalCount = (args) => {
|
|
1282
|
+
let count = 0;
|
|
1283
|
+
for (let index = args.length - 1; index >= 0; index -= 1) {
|
|
1284
|
+
if (args[index]?.representation.kind !== 'optional')
|
|
1285
|
+
break;
|
|
1286
|
+
count += 1;
|
|
1287
|
+
}
|
|
1288
|
+
return count;
|
|
1289
|
+
};
|
|
1290
|
+
/**
|
|
1291
|
+
* `super(...)`: the base's initialization against the object under
|
|
1292
|
+
* construction, then this class's own field initializers.
|
|
1293
|
+
*
|
|
1294
|
+
* Both halves are the language's, in the language's order. `super()` runs the
|
|
1295
|
+
* base chain -- its field initializers and its constructor body -- against the
|
|
1296
|
+
* receiver the derived construction already allocated, which is why it calls
|
|
1297
|
+
* the base's *initialize* half rather than its construct: constructing would
|
|
1298
|
+
* allocate a second object of the base's own type and throw the derived one
|
|
1299
|
+
* away. Immediately after it returns, and before the next statement of this
|
|
1300
|
+
* constructor, the derived class's own field initializers run; that is why they
|
|
1301
|
+
* are here and not in `constructDefinitionsOf`, which cannot see where in the
|
|
1302
|
+
* body they belong.
|
|
1303
|
+
*
|
|
1304
|
+
* Which class this is, is read from the projection rather than from the frame:
|
|
1305
|
+
* the body's own receiver carrier names the class the *receiver* has, which for
|
|
1306
|
+
* a derived constructor is the derived class either way, but only
|
|
1307
|
+
* `ClassLayout.constructor` states that this body is that class's constructor
|
|
1308
|
+
* at all -- a `super()` reached from a nested arrow is a different frame and is
|
|
1309
|
+
* refused here rather than silently initializing the enclosing class.
|
|
1310
|
+
*/
|
|
1311
|
+
export const emitSuperInitialize = (ctx, lines, operation) => {
|
|
1312
|
+
const owning = [...ctx.classes.values()].find((layout) => layout.constructor === ctx.owner);
|
|
1313
|
+
if (!owning) {
|
|
1314
|
+
throw createCppEmitBlockedError('call-abi:super-initialize', `runs in ${ctx.owner}, which no class publishes as its constructor body, so there is no base to initialize`);
|
|
1315
|
+
}
|
|
1316
|
+
if (owning.base === null && owning.nativeBase === null) {
|
|
1317
|
+
throw createCppEmitBlockedError('call-abi:super-initialize', `class ${owning.declaration} states no base class to initialize`);
|
|
1318
|
+
}
|
|
1319
|
+
if (ctx.abi?.receiver?.kind !== 'class-ref') {
|
|
1320
|
+
throw createCppEmitBlockedError('call-abi:super-initialize', `the constructor of class ${owning.declaration} declares no class receiver to initialize the base against`);
|
|
1321
|
+
}
|
|
1322
|
+
if (owning.nativeBase !== null) {
|
|
1323
|
+
if (owning.nativeBase.instance.native !== cppErrorNativeType) {
|
|
1324
|
+
throw createCppEmitBlockedError('call-abi:super-initialize', `native base ${owning.nativeBase.protocol} uses "${owning.nativeBase.instance.native ?? 'no native layout'}"; only the intrinsic Error layout has an existing-receiver initializer`);
|
|
1325
|
+
}
|
|
1326
|
+
const baseStatements = nativeErrorBaseInitializeStatements(ctx.deriver, cppReceiverName, operation.arguments.slice(0, 2).map((argument) => ({ representation: argument.representation, text: operandText(ctx, argument) })));
|
|
1327
|
+
if (isNativeErrorBaseRefusal(baseStatements)) {
|
|
1328
|
+
throw baseStatements.refusal === 'conversion'
|
|
1329
|
+
? createCppEmitBlockedError(`conversion:${baseStatements.conversion}`, baseStatements.reason)
|
|
1330
|
+
: createCppEmitBlockedError('call-abi:super-initialize', baseStatements.reason);
|
|
1331
|
+
}
|
|
1332
|
+
for (const statement of baseStatements)
|
|
1333
|
+
lines.push(statement);
|
|
1334
|
+
const fieldInitializers = cppFieldInitializerStatements(ctx, owning.declaration, owning.fields, cppReceiverName, (field) => owning.instance === null ? null : declaredRecordFieldOf(ctx.deriver, owning.instance, field.key, ctx.classes));
|
|
1335
|
+
if (typeof fieldInitializers === 'string')
|
|
1336
|
+
throw createCppEmitBlockedError('conversion:class-property-initializer', fieldInitializers);
|
|
1337
|
+
for (const statement of fieldInitializers)
|
|
1338
|
+
lines.push(statement);
|
|
1339
|
+
return;
|
|
1340
|
+
}
|
|
1341
|
+
const baseDeclaration = owning.base;
|
|
1342
|
+
if (baseDeclaration === null) {
|
|
1343
|
+
throw createCppEmitBlockedError('call-abi:super-initialize', `class ${owning.declaration} has neither a native nor a program class base`);
|
|
1344
|
+
}
|
|
1345
|
+
// A `super()` that omits a trailing argument the base gives a default is the
|
|
1346
|
+
// same omission as any other call's, and is padded the same way -- against
|
|
1347
|
+
// the BASE's construct convention, which is the one `constructDefinitionsOf`
|
|
1348
|
+
// spells the initializer's formals from. Joining the arguments raw instead
|
|
1349
|
+
// emitted `gea_initialize_Base(this)` against a two-parameter initializer:
|
|
1350
|
+
// `class IncomingMessage extends EventEmitter` calls `super()` while
|
|
1351
|
+
// `EventEmitter(options: EventEmitterOptions = {})` declares one parameter,
|
|
1352
|
+
// and the C++ compiler is where that landed rather than here.
|
|
1353
|
+
const baseConstruct = ctx.classes.get(baseDeclaration)?.construct ?? null;
|
|
1354
|
+
if (baseConstruct === null) {
|
|
1355
|
+
throw createCppEmitBlockedError('call-abi:super-initialize', `base class ${baseDeclaration} published no construct convention, so \`super(...)\` has no frame to fill`);
|
|
1356
|
+
}
|
|
1357
|
+
const received = receivableArguments(baseConstruct, operation.arguments);
|
|
1358
|
+
const args = paddedArguments(baseConstruct, received.map((argument, position) => argumentText(ctx, baseConstruct, position, argument, 'super-initialize')), 'super-initialize');
|
|
1359
|
+
lines.push(`${cppInitializeName(baseDeclaration)}(${[cppReceiverName, ...args].join(', ')});`);
|
|
1360
|
+
const fieldInitializers = cppFieldInitializerStatements(ctx, owning.declaration, owning.fields, cppReceiverName, (field) => owning.instance === null ? null : declaredRecordFieldOf(ctx.deriver, owning.instance, field.key, ctx.classes));
|
|
1361
|
+
if (typeof fieldInitializers === 'string')
|
|
1362
|
+
throw createCppEmitBlockedError('conversion:class-property-initializer', fieldInitializers);
|
|
1363
|
+
for (const statement of fieldInitializers)
|
|
1364
|
+
lines.push(statement);
|
|
1365
|
+
};
|
|
1366
|
+
/**
|
|
1367
|
+
* Declares a capture's box before the environment that captures it is built
|
|
1368
|
+
* when no earlier binding write allocated that box.
|
|
1369
|
+
*
|
|
1370
|
+
* Most boxed slots already exist because the owning frame's first
|
|
1371
|
+
* `binding-write` allocates them before a closure is created. Two ordinary
|
|
1372
|
+
* language shapes do not: a closure can precede the first write to a
|
|
1373
|
+
* declaration (`let cached; const get = () => cached; cached = value`), and a
|
|
1374
|
+
* closure can hold ITSELF (`CaptureSlot.boxed` set because
|
|
1375
|
+
* `closureInitialized.get(declaration) === owner`, `captures.ts`):
|
|
1376
|
+
* `const loop = (ts) => { ...; requestAnimationFrame(loop) }` allocates the
|
|
1377
|
+
* closure before the initializing write. In both cases the environment needs
|
|
1378
|
+
* a handle to the cell immediately. So this declares an empty box exactly
|
|
1379
|
+
* once, ahead of the environment build, and marks it declared; a later
|
|
1380
|
+
* initializing write that follows (`emitBindingWrite`) then sees it already
|
|
1381
|
+
* declared and only fills it in, exactly as any later reassignment would.
|
|
1382
|
+
*/
|
|
1383
|
+
/**
|
|
1384
|
+
* The packed environment one capturing body is entered with, built in the
|
|
1385
|
+
* frame that supplies its captures.
|
|
1386
|
+
*
|
|
1387
|
+
* `gea::packEnvironment` (gea_runtime.h) decides where the captured state
|
|
1388
|
+
* physically goes, and the body's own `unpackEnvironment` reads that same
|
|
1389
|
+
* decision from the same predicate, so the two cannot disagree. Where it
|
|
1390
|
+
* fits, the state IS the pointer the carrier holds and nothing is allocated;
|
|
1391
|
+
* where it does not, the struct is a `gea::Ref` block every holder counts on
|
|
1392
|
+
* -- which is why there is no "who deletes it" question here: the last holder
|
|
1393
|
+
* does.
|
|
1394
|
+
*
|
|
1395
|
+
* Two callers, and they are the two ways a body can be reached with state it
|
|
1396
|
+
* did not declare: a `CallableObject` carries it for an allocated closure,
|
|
1397
|
+
* and a record member carries it for a capturing ACCESSOR
|
|
1398
|
+
* (`emit-allocation.ts`), which has no carrier of its own.
|
|
1399
|
+
*/
|
|
1400
|
+
export const packedEnvironmentText = (ctx, lines, functionId, admission) => {
|
|
1401
|
+
if (admission.kind !== 'ok')
|
|
1402
|
+
throw new Error(`packedEnvironmentText called for ${functionId} with no admitted environment`);
|
|
1403
|
+
for (const slot of admission.layout.slots)
|
|
1404
|
+
ensureBoxedSlotDeclared(ctx, lines, slot);
|
|
1405
|
+
const fields = [
|
|
1406
|
+
...admission.layout.slots.map((slot) => slot.boxed
|
|
1407
|
+
? bindingReference(ctx, slot.declaration, `a capture of ${functionId}`).name
|
|
1408
|
+
: captureFieldText(ctx, slot.declaration, slot.representation, bindingReference(ctx, slot.declaration, `a capture of ${functionId}`).name)),
|
|
1409
|
+
...(admission.layout.receiver !== null ? [cppReceiverName] : [])
|
|
1410
|
+
];
|
|
1411
|
+
return `gea::packEnvironment(${cppEnvironmentStructName(functionId)}{${fields.join(', ')}})`;
|
|
1412
|
+
};
|
|
1413
|
+
const ensureBoxedSlotDeclared = (ctx, lines, slot) => {
|
|
1414
|
+
if (!slot.boxed)
|
|
1415
|
+
return;
|
|
1416
|
+
const placement = ctx.placements.get(slot.declaration);
|
|
1417
|
+
if (placement?.storage.kind !== 'local' || placement.storage.owner !== ctx.owner)
|
|
1418
|
+
return;
|
|
1419
|
+
if (ctx.declaredBindings.has(slot.declaration))
|
|
1420
|
+
return;
|
|
1421
|
+
const cell = bindingReference(ctx, slot.declaration, 'a self-referencing capture');
|
|
1422
|
+
declareCell(ctx, cell.name, cppBoxedType(slot.representation));
|
|
1423
|
+
lines.push(`${cell.name} = gea::makeRef<${cppTypeOf(slot.representation)}>();`);
|
|
1424
|
+
ctx.declaredBindings.add(slot.declaration);
|
|
1425
|
+
};
|
|
1426
|
+
/** A statically numeric scalar, including storage-narrowed Number carriers. */
|
|
1427
|
+
const isNumberScalarCarrier = (representation) => representation.kind === 'scalar' &&
|
|
1428
|
+
(representation.domain === 'number' ||
|
|
1429
|
+
representation.domain === 'int32' ||
|
|
1430
|
+
representation.domain === 'uint32' ||
|
|
1431
|
+
representation.domain === 'float64');
|
|
1432
|
+
/** An ordinary numeric Array whose elements `TypedArray.fromValues` can apply ToNumber to without a dynamic boundary. */
|
|
1433
|
+
const isNumericArrayCarrier = (representation) => representation.kind === 'array-object' && representation.ownership === 'shared-refcount' && isNumberScalarCarrier(representation.element);
|
|
1434
|
+
/** A typed-array source with the reference ownership needed to preserve and read its view identity. */
|
|
1435
|
+
const isSharedTypedArrayCarrier = (representation) => representation.kind === 'typed-array' && representation.ownership === 'shared-refcount';
|
|
1436
|
+
/**
|
|
1437
|
+
* `CallableObject{invoke, environment}` for one function-object allocation.
|
|
1438
|
+
*
|
|
1439
|
+
* `operation.captures` is never consulted: nothing publishes it, so it is not
|
|
1440
|
+
* a source of truth for what this closure reads from an enclosing frame.
|
|
1441
|
+
* `targets/cpp/captures.ts`'s whole-program `CaptureIndex` is. Each captured
|
|
1442
|
+
* cell is read through `bindingReference` in *this* (allocating) context,
|
|
1443
|
+
* which owns every declaration the closure captures by construction, so the
|
|
1444
|
+
* ordinary branch of `bindingReference` names it correctly with no special
|
|
1445
|
+
* casing. A captured receiver reads `cppReceiverName` the same way: this
|
|
1446
|
+
* body's own `gea_this` formal exists whenever it has a receiver at all,
|
|
1447
|
+
* whether real or itself captured (`emit-context.ts`'s `effectiveAbiOf`).
|
|
1448
|
+
*
|
|
1449
|
+
* A boxed slot's field copy needs no special casing here either, despite
|
|
1450
|
+
* carrying a `std::shared_ptr` rather than the declaration's own value type:
|
|
1451
|
+
* `bindingReference` already names the box's OWN storage for a boxed
|
|
1452
|
+
* declaration (never its dereferenced pointee -- that is
|
|
1453
|
+
* `emit-bindings.ts`'s `cellValueText`, used only where a value, not a
|
|
1454
|
+
* handle, is wanted), so copying `.name` verbatim into the new environment
|
|
1455
|
+
* struct copies the pointer, which is exactly the aliasing this closure's
|
|
1456
|
+
* capture is supposed to establish.
|
|
1457
|
+
*/
|
|
1458
|
+
export const emitAllocateCallable = (ctx, lines, operation) => {
|
|
1459
|
+
const admission = ctx.captures.of(operation.functionId);
|
|
1460
|
+
if (admission.kind === 'refused') {
|
|
1461
|
+
throw createCppEmitBlockedError('capture:refused', `allocates a closure whose environment cannot be built: ${admission.reason}`);
|
|
1462
|
+
}
|
|
1463
|
+
const name = defineValue(ctx, operation.result);
|
|
1464
|
+
const carrier = operation.result.representation;
|
|
1465
|
+
// An OPTIONAL class method (`bodyString?(): string`) allocates through this
|
|
1466
|
+
// exact same operation -- the checker widened the MEMBER's type to admit
|
|
1467
|
+
// absence (a structural-compatibility escape hatch, `structural-parts.ts`'s
|
|
1468
|
+
// `memberOf`), never the allocation itself: `admission.kind === 'refused'`
|
|
1469
|
+
// just above is the only "cannot build a callable" outcome this operation
|
|
1470
|
+
// has, and it throws rather than leaving the result unassigned. So the
|
|
1471
|
+
// payload built below is unconditionally present; only the C++ TYPE gains a
|
|
1472
|
+
// `gea::Optional<...>` wrapper. `gea::Optional<T>`'s own constructors
|
|
1473
|
+
// (gea_runtime.h) take exactly one `T`, not `CallableObject`'s pointer pair,
|
|
1474
|
+
// so the payload has to be built at its own (unwrapped) type first and
|
|
1475
|
+
// handed to `Optional` as that single value -- `payloadCarrier`/`wrap` below
|
|
1476
|
+
// do exactly that, and reduce to the un-wrapped case unchanged when there is
|
|
1477
|
+
// no `optional` to begin with.
|
|
1478
|
+
const payloadCarrier = carrier.kind === 'optional' ? carrier.payload : carrier;
|
|
1479
|
+
const wrap = (payloadText) => (carrier.kind === 'optional' ? `${cppTypeOf(carrier)}{${payloadText}}` : payloadText);
|
|
1480
|
+
// Which body this callable runs, kept so a consumer that must RE-RUN it --
|
|
1481
|
+
// a reactive JSX slot -- can ask what that body reads. See `thunkValues`.
|
|
1482
|
+
//
|
|
1483
|
+
// A THUNK only: no arguments and no receiver. `emit-jsx.ts`'s
|
|
1484
|
+
// `reactiveThunkPlan` re-runs what it finds here as `thunk.call()`, and that
|
|
1485
|
+
// is the one call it can write -- it has the callee's value and nothing
|
|
1486
|
+
// else, no frame to rebuild and no receiver to pass. A component's render
|
|
1487
|
+
// member is allocated through this same operation (`plugins/gea/lower.ts`
|
|
1488
|
+
// binds it, then calls it with the instance and a props record), and
|
|
1489
|
+
// recording it would offer a re-run that cannot be spelled; it would also
|
|
1490
|
+
// read that body's `this.x` dependencies and subscribe them through the
|
|
1491
|
+
// CALLING frame's receiver, which is a different object. Both are silent
|
|
1492
|
+
// until the slot is one the emitter binds, which is why the filter belongs
|
|
1493
|
+
// here rather than at the one call site that noticed.
|
|
1494
|
+
// A two-entry function object holds THREE fields, not two: an invoke pointer,
|
|
1495
|
+
// a construct pointer and the environment (`gea::CallableConstructorObject`,
|
|
1496
|
+
// gea_runtime.h). The construct pointer is a second thunk over the same body,
|
|
1497
|
+
// rendered by `translation-unit.ts`'s `constructThunkOf` -- ECMA-262 10.2.2's
|
|
1498
|
+
// "create the object, then enter the body with it", which no `[[Call]]`
|
|
1499
|
+
// pointer can serve because it neither allocates nor returns the instance.
|
|
1500
|
+
// Brace-initializing this carrier with two values is what used to leave every
|
|
1501
|
+
// pre-`class` constructor function in three.js's renderer unclaimed.
|
|
1502
|
+
const pointers = payloadCarrier.kind === 'function-and-constructor'
|
|
1503
|
+
? `&${cppThunkName(operation.functionId)}, &${cppConstructedThunkName(operation.functionId)}`
|
|
1504
|
+
: `&${cppThunkName(operation.functionId)}`;
|
|
1505
|
+
// Tagged by the SOURCE DECLARATION, not by this copy's thunk. Two
|
|
1506
|
+
// instantiations of one generic function are two thunks and one JavaScript
|
|
1507
|
+
// function object; the runtime's own `CallableDeclarationTag` comment says
|
|
1508
|
+
// "one program-wide address per emitted source declaration", and passing the
|
|
1509
|
+
// thunk made it one per copy. `translation-unit.ts` defines the tag object.
|
|
1510
|
+
//
|
|
1511
|
+
// And only when the identity can be SEEN. `identifyCallable` forces the
|
|
1512
|
+
// `FunctionObjectIdentity` allocation (and its property table) that the
|
|
1513
|
+
// runtime otherwise mints lazily; `ir/callable-identity-demand.ts` says
|
|
1514
|
+
// whether anything in the program ever asks this convention's callables for
|
|
1515
|
+
// their identity. When nothing does, the allocation stays the bare
|
|
1516
|
+
// thunk/environment pair and a closure in a hot loop costs its environment
|
|
1517
|
+
// and nothing else. A callable that is also a constructor keeps its identity
|
|
1518
|
+
// regardless: its `prototype` lives on that owner and `new F()` reads it.
|
|
1519
|
+
const identified = (payloadText) => payloadCarrier.kind === 'function-and-constructor' ||
|
|
1520
|
+
payloadCarrier.kind === 'dynamic' ||
|
|
1521
|
+
ctx.callableIdentityDemand.observes(payloadCarrier)
|
|
1522
|
+
? `gea::identifyCallable<&${cppCallableDeclarationTagName(operation.functionId)}>(${payloadText})`
|
|
1523
|
+
: payloadText;
|
|
1524
|
+
// A function whose OWN structural type the `--dynamic-fallback` prototype
|
|
1525
|
+
// census marked (`prototypeMutatedConstructorTypes`, dynamic-fallback.ts)
|
|
1526
|
+
// publishes `dynamic` here in place of a native callable carrier -- because
|
|
1527
|
+
// `F.prototype = ...`/`F.prototype.n = ...` needs a real, mutable property
|
|
1528
|
+
// table (ECMA-262 10.2.5 MakeConstructor) that no native callable carrier
|
|
1529
|
+
// has. The box's OWN call ABI is gone once boxed, so it is recovered from
|
|
1530
|
+
// `ctx.abiOfCallable` (the same recovery `emit-union-properties.ts` already
|
|
1531
|
+
// relies on for the identical problem) and used to pick the matching
|
|
1532
|
+
// `Value::box`/`boxMethod`/`boxCallable` overload. `new F()` never uses the
|
|
1533
|
+
// native construct-thunk pointer for this carrier (`translation-unit.ts`'s
|
|
1534
|
+
// `constructThunkOf` refuses to build one for a `dynamic` instance type
|
|
1535
|
+
// anyway) -- `emitConstruct`'s own `dynamic` branch calls `Value::construct`
|
|
1536
|
+
// instead, which re-enters through the ordinary `[[Call]]` thunk -- so only
|
|
1537
|
+
// ONE pointer is ever needed here, never the constructor-family pair.
|
|
1538
|
+
if (payloadCarrier.kind === 'dynamic') {
|
|
1539
|
+
const abi = ctx.abiOfCallable(operation.functionId);
|
|
1540
|
+
if (!abi) {
|
|
1541
|
+
throw createCppEmitBlockedError('call-abi:dynamic', 'a callable boxed as "dynamic" under --dynamic-fallback needs its own call ABI, and this function publishes none');
|
|
1542
|
+
}
|
|
1543
|
+
const callableType = `gea::CallableObject<${cppAbiType(abi)}>`;
|
|
1544
|
+
const boxText = (payloadText) => {
|
|
1545
|
+
const callable = identified(payloadText);
|
|
1546
|
+
if (abi.receiver !== null)
|
|
1547
|
+
return `gea::Value::boxMethod<${abi.restFrom === null ? -1 : abi.restFrom + 1}>(${callable})`;
|
|
1548
|
+
if (abi.restFrom === null)
|
|
1549
|
+
return `gea::Value::box(gea::Value::Tag::Function, ${callable})`;
|
|
1550
|
+
return `gea::Value::boxCallable<${abi.restFrom}>(${callable})`;
|
|
1551
|
+
};
|
|
1552
|
+
if (admission.kind === 'none') {
|
|
1553
|
+
lines.push(`${name} = gea::host::installOrdinaryConstructorPrototype(${boxText(`${callableType}{&${cppThunkName(operation.functionId)}, nullptr}`)});`);
|
|
1554
|
+
return;
|
|
1555
|
+
}
|
|
1556
|
+
const dynamicEnvironment = packedEnvironmentText(ctx, lines, operation.functionId, admission);
|
|
1557
|
+
lines.push(`${name} = gea::host::installOrdinaryConstructorPrototype(${boxText(`${callableType}{&${cppThunkName(operation.functionId)}, ${dynamicEnvironment}}`)});`);
|
|
1558
|
+
return;
|
|
1559
|
+
}
|
|
1560
|
+
if (admission.kind === 'none') {
|
|
1561
|
+
// Same reasoning as a module-level function cell: a callable with no
|
|
1562
|
+
// environment is reached by name, and the carrier stays for whoever stores
|
|
1563
|
+
// one. The name itself is registered up front by `emitBody`'s walk of
|
|
1564
|
+
// `CallOperation.target` (`ir/call-dispatch.ts`), not by this allocation.
|
|
1565
|
+
lines.push(`${name} = ${wrap(identified(`${cppTypeOf(payloadCarrier)}{${pointers}, nullptr}`))};`);
|
|
1566
|
+
return;
|
|
1567
|
+
}
|
|
1568
|
+
const environment = packedEnvironmentText(ctx, lines, operation.functionId, admission);
|
|
1569
|
+
lines.push(`${name} = ${wrap(identified(`${cppTypeOf(payloadCarrier)}{${pointers}, ${environment}}`))};`);
|
|
1570
|
+
};
|
|
1571
|
+
/**
|
|
1572
|
+
* `new Uint8Array(...)`'s checker overload set (ECMA-262 23.2.5.1) has no
|
|
1573
|
+
* single physical frame: a `length: number` overload zero-fills, an
|
|
1574
|
+
* `ArrayLike<number>` overload copies-and-converts each element, and a
|
|
1575
|
+
* `buffer, byteOffset?, length?` overload aliases existing storage -- three
|
|
1576
|
+
* different parameter-0 shapes, not three spellings of one convention. That is
|
|
1577
|
+
* why `callee.construct` is `null` here, unconditionally, for every
|
|
1578
|
+
* typed-array constructor: `representation/host-abi.ts`'s `widestSubsumingAbi`
|
|
1579
|
+
* correctly refuses to join an unrelated shape into "the" ABI.
|
|
1580
|
+
*
|
|
1581
|
+
* This does not re-run overload resolution to work around that: it reads the
|
|
1582
|
+
* argument's own, already-resolved physical representation, which is the
|
|
1583
|
+
* checker's resolved-signature decision made visible through the plan
|
|
1584
|
+
* (`representation/plan.ts`) rather than re-derived from syntax. A `scalar
|
|
1585
|
+
* (number)` argument can only be the `length: number` overload's parameter --
|
|
1586
|
+
* no other construct signature has a bare-number parameter 0 -- and an
|
|
1587
|
+
* `array-object` argument can only be the `ArrayLike<number>` overload's.
|
|
1588
|
+
* Optional numeric lengths preserve absence until this intrinsic applies
|
|
1589
|
+
* ToIndex: both undefined and null allocate an empty array. Native buffer
|
|
1590
|
+
* views and numeric array copies use their own branches below; unsupported
|
|
1591
|
+
* carrier families are refused rather than treated as a numeric length.
|
|
1592
|
+
*/
|
|
1593
|
+
const emitTypedArrayConstruct = (ctx, lines, operation, result) => {
|
|
1594
|
+
if (result.ownership !== 'shared-refcount') {
|
|
1595
|
+
throw createCppEmitBlockedError(`physical-cpp-type:${representationKey(result)}`, `a typed array allocation carries ownership "${result.ownership}", but this emitter only spells std::make_shared for "shared-refcount"`);
|
|
1596
|
+
}
|
|
1597
|
+
const target = typedArrayTargetSpelling(result);
|
|
1598
|
+
// ECMA-262 23.2.5.1's THIRD overload, `(buffer, byteOffset?, length?)`,
|
|
1599
|
+
// which ALIASES the block rather than copying it. Recognized by the
|
|
1600
|
+
// argument's own already-resolved carrier, exactly as the two forms below
|
|
1601
|
+
// are, and rendered in `emit-buffers.ts` with the rest of the block-shaped
|
|
1602
|
+
// family. Checked before the arity guard below because it is the one
|
|
1603
|
+
// overload that takes more than one argument.
|
|
1604
|
+
const first = operation.arguments[0];
|
|
1605
|
+
if (first?.representation.kind === 'array-buffer' || first?.representation.kind === 'shared-array-buffer') {
|
|
1606
|
+
emitTypedArrayBufferConstruct(ctx, lines, operation, target);
|
|
1607
|
+
return;
|
|
1608
|
+
}
|
|
1609
|
+
const name = defineValue(ctx, operation.result);
|
|
1610
|
+
// The zero-argument form IS "no first operand", so reading the operand first
|
|
1611
|
+
// and branching on its absence states the arity check once, in the terms the
|
|
1612
|
+
// rest of this function already uses, instead of asserting a length and then
|
|
1613
|
+
// indexing on the strength of that assertion.
|
|
1614
|
+
const argument = operation.arguments[0];
|
|
1615
|
+
if (!argument) {
|
|
1616
|
+
lines.push(`${name} = gea::makeRef<${target}>();`);
|
|
1617
|
+
return;
|
|
1618
|
+
}
|
|
1619
|
+
if (operation.arguments.length > 1) {
|
|
1620
|
+
throw createCppEmitBlockedError('call-abi:construct:typed-array', `constructs a typed array from ${operation.arguments.length} arguments; only the zero-, length-, and array-like-argument ` +
|
|
1621
|
+
'forms of ECMA-262 23.2.5.1 are implemented (see emitTypedArrayConstruct)');
|
|
1622
|
+
}
|
|
1623
|
+
const carrier = argument.representation;
|
|
1624
|
+
if (isNumberScalarCarrier(carrier) ||
|
|
1625
|
+
(carrier.kind === 'optional' && isNumberScalarCarrier(carrier.payload)) ||
|
|
1626
|
+
carrier.kind === 'undefined' ||
|
|
1627
|
+
carrier.kind === 'null') {
|
|
1628
|
+
const source = operandText(ctx, argument);
|
|
1629
|
+
const length = carrier.kind === 'optional'
|
|
1630
|
+
? `(${source}.has_value() ? *${source} : 0.0)`
|
|
1631
|
+
: carrier.kind === 'undefined' || carrier.kind === 'null'
|
|
1632
|
+
? '0.0'
|
|
1633
|
+
: source;
|
|
1634
|
+
// Normalize the numeric argument before the runtime's integer/range guard.
|
|
1635
|
+
// ToIndex truncates fractions and maps NaN/absence to zero.
|
|
1636
|
+
lines.push(`${name} = gea::makeRef<${target}>(gea::detail::typedArrayLengthIndex(gea::detail::toIntegerOrInfinity(${length})));`);
|
|
1637
|
+
return;
|
|
1638
|
+
}
|
|
1639
|
+
// A BOXED argument. The overload this dispatch reads off a carrier is read
|
|
1640
|
+
// off the BOX'S TAG instead, and the tag can pick exactly one: `Tag::Number`
|
|
1641
|
+
// is the `length` overload. `unboxedLoadText` renders that as the
|
|
1642
|
+
// tag-and-payload-checked read, so a box holding anything else aborts by
|
|
1643
|
+
// name at the construction rather than being reinterpreted as a length --
|
|
1644
|
+
// the same discipline every other boxed read in this backend keeps, and the
|
|
1645
|
+
// reason `preflight/invocation-arguments.ts` can state `scalar(number)` as
|
|
1646
|
+
// this argument's target without over-claiming.
|
|
1647
|
+
//
|
|
1648
|
+
// three's `new Int32Array( n )` (`WebGLUniforms.allocTexUnits`) and
|
|
1649
|
+
// `new Float32Array( flatSize )` (`WebGLClipping.projectPlanes`) are both a
|
|
1650
|
+
// length through a JS parameter the source never typed.
|
|
1651
|
+
if (carrier.kind === 'dynamic') {
|
|
1652
|
+
const length = unboxedLoadText({ kind: 'scalar', domain: 'number' }, operandText(ctx, argument));
|
|
1653
|
+
if (length !== null) {
|
|
1654
|
+
lines.push(`${name} = gea::makeRef<${target}>(gea::detail::typedArrayLengthIndex(gea::detail::toIntegerOrInfinity(${length})));`);
|
|
1655
|
+
return;
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
if (isNumericArrayCarrier(carrier)) {
|
|
1659
|
+
lines.push(`${name} = gea::makeRef<${target}>(${target}::fromValues(${operandText(ctx, argument)}));`);
|
|
1660
|
+
return;
|
|
1661
|
+
}
|
|
1662
|
+
// ECMA-262 23.2.5.1's copy-from-another-typed-array overload
|
|
1663
|
+
// (`InitializeTypedArrayFromTypedArray`): a fresh block the same LENGTH as
|
|
1664
|
+
// the source, each element converted through the target's own write rule --
|
|
1665
|
+
// which is what makes `new Uint8ClampedArray(new Float32Array([1.5]))`
|
|
1666
|
+
// clamp-and-round rather than truncate. `setFrom` performs exactly that
|
|
1667
|
+
// conversion, so the copy is `fromLength` plus one `set` at offset zero.
|
|
1668
|
+
if (isSharedTypedArrayCarrier(carrier)) {
|
|
1669
|
+
const source = operandText(ctx, argument);
|
|
1670
|
+
lines.push(`${name} = gea::makeRef<${target}>(static_cast<std::size_t>(${source}->length()));`);
|
|
1671
|
+
lines.push(`${name}->setFrom(*${source}, 0.0);`);
|
|
1672
|
+
return;
|
|
1673
|
+
}
|
|
1674
|
+
// A parameter declared `number[] | TypedArray` retains both overloads as a
|
|
1675
|
+
// native sum. Each alternative is still one of the two copying forms above;
|
|
1676
|
+
// dispatching on the sum's existing tag preserves that decision without
|
|
1677
|
+
// boxing the source or collapsing typed-array element domains.
|
|
1678
|
+
if (carrier.kind === 'tagged-union' &&
|
|
1679
|
+
carrier.arms.length > 0 &&
|
|
1680
|
+
carrier.arms.every((arm) => isNumericArrayCarrier(arm.value) || isSharedTypedArrayCarrier(arm.value))) {
|
|
1681
|
+
const branches = carrier.arms.map((arm, index) => {
|
|
1682
|
+
const selected = `gea_typed_source.get<${index}>()`;
|
|
1683
|
+
const body = isNumericArrayCarrier(arm.value)
|
|
1684
|
+
? `return gea::makeRef<${target}>(${target}::fromValues(${selected}));`
|
|
1685
|
+
: `{ const auto& gea_typed_input = ${selected}; auto gea_typed_result = gea::makeRef<${target}>(gea_typed_input->size()); gea_typed_result->setFrom(*gea_typed_input, 0.0); return gea_typed_result; }`;
|
|
1686
|
+
return index === carrier.arms.length - 1 ? body : `if (gea_typed_source.is<${index}>()) { ${body} }`;
|
|
1687
|
+
});
|
|
1688
|
+
lines.push(`${name} = ([&]() -> ${cppTypeOf(result)} { const auto& gea_typed_source = ${operandText(ctx, argument)}; ${branches.join(' ')} }());`);
|
|
1689
|
+
return;
|
|
1690
|
+
}
|
|
1691
|
+
throw createCppEmitBlockedError('call-abi:construct:typed-array', `constructs a typed array from a "${carrier.kind}" argument; supported carriers are numeric lengths (including absence), ` +
|
|
1692
|
+
'native buffer views, numeric arrays, typed arrays, and native unions of the two array-copy forms');
|
|
1693
|
+
};
|
|
1694
|
+
/**
|
|
1695
|
+
* `new Array(...)` -- the one native constructor whose result is an ordinary
|
|
1696
|
+
* `array-object` rather than a handle to the host's own object.
|
|
1697
|
+
*
|
|
1698
|
+
* It is here, beside `emitTypedArrayConstruct`, and for the same structural
|
|
1699
|
+
* reason: `ArrayConstructor`'s checker overload set is `new (arrayLength:
|
|
1700
|
+
* number)` and `new <T>(...items: T[])`, two genuinely different parameter-0
|
|
1701
|
+
* shapes with no single physical frame to join, so `derive.ts` leaves
|
|
1702
|
+
* `.construct` null and the recipe has to read the construct operation's own,
|
|
1703
|
+
* already-resolved argument representation instead.
|
|
1704
|
+
*
|
|
1705
|
+
* The length form is exactly `ArrayObject::setLength`, which is ECMA-262
|
|
1706
|
+
* 10.4.2.1's own rule -- `resize` grows with value-initialized `Slot`s, and a
|
|
1707
|
+
* `Slot` value-initializes `present` to false, i.e. a HOLE. `new Array(64)` is
|
|
1708
|
+
* a length-64 array of holes in the specification and a length-64 array of
|
|
1709
|
+
* holes here; nothing is materialized into `undefined` behind the program's
|
|
1710
|
+
* back, and a read of one still aborts by name in `elementAt` rather than
|
|
1711
|
+
* handing back a zero.
|
|
1712
|
+
*
|
|
1713
|
+
* The `...items` form is refused: each item owes a conversion to the array's
|
|
1714
|
+
* element carrier, which is lowering's to state (it is what an array literal
|
|
1715
|
+
* already gets), not something to improvise from the argument list here. So is
|
|
1716
|
+
* a single non-numeric argument -- `new Array("a")` is a one-element array,
|
|
1717
|
+
* `new Array(3)` is three holes, and picking between them is the argument's
|
|
1718
|
+
* type talking, so an argument whose carrier is neither is a refusal rather
|
|
1719
|
+
* than a guess.
|
|
1720
|
+
*/
|
|
1721
|
+
const emitArrayConstruct = (ctx, lines, operation, result) => {
|
|
1722
|
+
if (result.ownership !== 'shared-refcount') {
|
|
1723
|
+
throw createCppEmitBlockedError(`physical-cpp-type:${representationKey(result)}`, `an Array allocation carries ownership "${result.ownership}", but this emitter only spells std::make_shared for "shared-refcount"`);
|
|
1724
|
+
}
|
|
1725
|
+
const target = `gea::ArrayObject<${cppTypeOf(result.element)}>`;
|
|
1726
|
+
const name = defineValue(ctx, operation.result);
|
|
1727
|
+
const argument = operation.arguments[0];
|
|
1728
|
+
if (!argument) {
|
|
1729
|
+
lines.push(`${name} = gea::makeRef<${target}>();`);
|
|
1730
|
+
return;
|
|
1731
|
+
}
|
|
1732
|
+
if (operation.arguments.length > 1) {
|
|
1733
|
+
throw createCppEmitBlockedError('call-abi:construct:array', `constructs an Array from ${operation.arguments.length} arguments; only the zero- and length-argument forms of ` +
|
|
1734
|
+
'ECMA-262 23.1.1.1 are implemented, because the element form owes each item a conversion to the array element ' +
|
|
1735
|
+
'carrier that only lowering states (see emitArrayConstruct)');
|
|
1736
|
+
}
|
|
1737
|
+
const carrier = argument.representation;
|
|
1738
|
+
if (carrier.kind !== 'scalar' || carrier.domain !== 'number') {
|
|
1739
|
+
throw createCppEmitBlockedError('call-abi:construct:array', `constructs an Array from a "${carrier.kind}" argument; ECMA-262 23.1.1.1 reads a single argument as a LENGTH only ` +
|
|
1740
|
+
'when it is a number and as a sole ELEMENT otherwise, and the element form is not implemented');
|
|
1741
|
+
}
|
|
1742
|
+
lines.push(`${name} = gea::makeRef<${target}>();`);
|
|
1743
|
+
lines.push(`${name}->setLength(${operandText(ctx, argument)});`);
|
|
1744
|
+
};
|
|
1745
|
+
/**
|
|
1746
|
+
* `new Map()` / `new Set()` / `new WeakMap()` / `new WeakSet()`, and
|
|
1747
|
+
* `new Set(array)`.
|
|
1748
|
+
*
|
|
1749
|
+
* The third member of the `emitTypedArrayConstruct`/`emitArrayConstruct`
|
|
1750
|
+
* family above, here for exactly the same structural reason: these four
|
|
1751
|
+
* constructors' checker overload sets have no single physical frame. `new
|
|
1752
|
+
* Map()`, `new Map(entries: readonly (readonly [K, V])[] | null)` and `new
|
|
1753
|
+
* Map(iterable: Iterable<readonly [K, V]>)` are three different parameter-0
|
|
1754
|
+
* shapes, so `representation/host-abi.ts`'s `widestSubsumingAbi` correctly
|
|
1755
|
+
* refuses to join them and `derive.ts` leaves `.construct` null. This reads
|
|
1756
|
+
* the construction's own already-resolved result carrier and argument
|
|
1757
|
+
* representation instead.
|
|
1758
|
+
*
|
|
1759
|
+
* Only the zero-argument form, plus `new Set(T[])`, is rendered. Every other
|
|
1760
|
+
* seeding form refuses by name:
|
|
1761
|
+
*
|
|
1762
|
+
* - a Map/WeakMap seeded from entries needs each element to be a `[K, V]`
|
|
1763
|
+
* PAIR, and this compiler carries a tuple as an ordinary record of "0"/"1"
|
|
1764
|
+
* fields (`derive.ts`'s `deriveTuple`). Reading two struct members back out
|
|
1765
|
+
* as a key and a value is a real lowering, not a spelling, and guessing at
|
|
1766
|
+
* it would be the kind of improvisation `emitArrayConstruct` already
|
|
1767
|
+
* declines for `new Array(...items)`;
|
|
1768
|
+
* - a Set seeded from any iterable that is not an Array needs the dynamic
|
|
1769
|
+
* `@@iterator` protocol this backend does not lower at all
|
|
1770
|
+
* (`emit-iterator.ts`);
|
|
1771
|
+
* - `new WeakSet(array)` is refused with the others rather than folded in with
|
|
1772
|
+
* `new Set(array)`: it is genuinely rare, and admitting it would mean
|
|
1773
|
+
* claiming the weak-key precondition holds for every element of an array
|
|
1774
|
+
* whose own element carrier this function would have to re-check.
|
|
1775
|
+
*/
|
|
1776
|
+
/**
|
|
1777
|
+
* The four ambient constructor PROTOCOL NAMES this renderer is written for --
|
|
1778
|
+
* named here, once, rather than being re-typed as a fifth copy by anything
|
|
1779
|
+
* that needs to know which protocols `emitKeyedCollectionConstruct` answers
|
|
1780
|
+
* for. The dispatch above is structural (`result.kind === 'keyed-collection'`),
|
|
1781
|
+
* never by this name, so this table is not read by the renderer itself: it is
|
|
1782
|
+
* the renderer's own restatement of the four names its comment already
|
|
1783
|
+
* documents, exported for `host/native-protocols.ts` to derive a
|
|
1784
|
+
* native-boundary claim from, since no host-member-table row names any of
|
|
1785
|
+
* them the way most other protocols are named.
|
|
1786
|
+
*/
|
|
1787
|
+
export const cppKeyedCollectionConstructorProtocols = [
|
|
1788
|
+
'MapConstructor',
|
|
1789
|
+
'SetConstructor',
|
|
1790
|
+
'WeakMapConstructor',
|
|
1791
|
+
'WeakSetConstructor'
|
|
1792
|
+
];
|
|
1793
|
+
const emitKeyedCollectionConstruct = (ctx, lines, operation, result) => {
|
|
1794
|
+
if (result.ownership !== 'shared-refcount') {
|
|
1795
|
+
throw createCppEmitBlockedError(`physical-cpp-type:${representationKey(result)}`, `a ${result.family} allocation carries ownership "${result.ownership}", but this emitter only spells std::make_shared for "shared-refcount"`);
|
|
1796
|
+
}
|
|
1797
|
+
// The pointee, not the carrier: `cppTypeOf` would wrap it in the
|
|
1798
|
+
// `shared_ptr` this line is itself constructing.
|
|
1799
|
+
const target = cppTypeOf({ ...result, ownership: 'owned' });
|
|
1800
|
+
const name = defineValue(ctx, operation.result);
|
|
1801
|
+
const argument = operation.arguments[0];
|
|
1802
|
+
if (!argument) {
|
|
1803
|
+
lines.push(`${name} = gea::makeRef<${target}>();`);
|
|
1804
|
+
return;
|
|
1805
|
+
}
|
|
1806
|
+
if (operation.arguments.length > 1) {
|
|
1807
|
+
throw createCppEmitBlockedError('call-abi:construct:keyed-collection', `constructs a ${result.family} from ${operation.arguments.length} arguments; ECMA-262 gives these constructors one ` +
|
|
1808
|
+
'optional iterable parameter and nothing else');
|
|
1809
|
+
}
|
|
1810
|
+
const carrier = argument.representation;
|
|
1811
|
+
if (result.family === 'set' && carrier.kind === 'array-object' && representationKey(carrier.element) === representationKey(result.key)) {
|
|
1812
|
+
return void lines.push(`${name} = gea::setFromArray<${cppTypeOf(result.key)}>(${operandText(ctx, argument)});`);
|
|
1813
|
+
}
|
|
1814
|
+
// A STRING is the second iterable this backend proves statically, and it is
|
|
1815
|
+
// not a special case of the Array one: ECMA-262 22.1.5.1 iterates a string
|
|
1816
|
+
// by CODE POINT, so `new Set('ab')` holds two one-character strings and
|
|
1817
|
+
// `new Set('\u{1F600}')` holds one two-unit string, never two halves of a
|
|
1818
|
+
// surrogate pair. The runtime walks it with the same `forEachCodePoint`
|
|
1819
|
+
// `[...str]` uses, so the seeded Set and the spread can never disagree.
|
|
1820
|
+
// Only a `set<string>` -- a `Set<T>` for any other T seeded from a string is
|
|
1821
|
+
// not a program this checker admits, and inventing an element conversion
|
|
1822
|
+
// here would be a conversion nothing proved.
|
|
1823
|
+
if (result.family === 'set' && carrier.kind === 'string' && result.key.kind === 'string') {
|
|
1824
|
+
return void lines.push(`${name} = gea::setFromString(${operandText(ctx, argument)});`);
|
|
1825
|
+
}
|
|
1826
|
+
throw createCppEmitBlockedError('call-abi:construct:keyed-collection', `constructs a ${result.family}<${representationKey(result.key)}> from a "${representationKey(carrier)}" argument; only the zero-argument form is implemented for every ` +
|
|
1827
|
+
"family, plus `new Set(T[])` where the array's element carrier is exactly the set's own -- a Map seeded from " +
|
|
1828
|
+
'entries needs a [K, V] pair lowering (a tuple is carried here as a record of "0"/"1" fields), and any other ' +
|
|
1829
|
+
'iterable needs the dynamic @@iterator protocol this backend does not lower');
|
|
1830
|
+
};
|
|
1831
|
+
/**
|
|
1832
|
+
* `new RegExp(pattern)`, `new RegExp(pattern, flags)` and the two
|
|
1833
|
+
* pattern-copying forms (ECMA-262 22.2.4.1).
|
|
1834
|
+
*
|
|
1835
|
+
* The fourth case of the same `native-handle` exception `emitTypedArrayConstruct`,
|
|
1836
|
+
* `emitArrayConstruct` and `emitKeyedCollectionConstruct` are, for the same
|
|
1837
|
+
* reason: `RegExpConstructor`'s overload set has three different parameter-0
|
|
1838
|
+
* shapes (`string | RegExp`, `string`, `string | RegExp` again with a second
|
|
1839
|
+
* parameter), so `derive.ts` correctly leaves `.construct` null and there is
|
|
1840
|
+
* no joined convention to invoke through. The construction renders off its own
|
|
1841
|
+
* ARGUMENT carriers instead, which is exactly what the specification switches
|
|
1842
|
+
* on: 22.2.4.1 step 3 asks whether the pattern is already a RegExp before it
|
|
1843
|
+
* asks anything else.
|
|
1844
|
+
*
|
|
1845
|
+
* `constructPatternOrThrow` (v1: the validation half of `regex.cpp`'s
|
|
1846
|
+
* construction path) is the entry point rather than `gea::makeRef<Pattern>`
|
|
1847
|
+
* so that 22.2.3.2 `RegExpInitialize`'s flag and source validation runs and an
|
|
1848
|
+
* unknown or repeated flag throws where the language says it throws.
|
|
1849
|
+
*/
|
|
1850
|
+
const emitRegExpConstruct = (ctx, lines, operation) => {
|
|
1851
|
+
const args = operation.arguments;
|
|
1852
|
+
if (args.length > 2) {
|
|
1853
|
+
throw createCppEmitBlockedError('call-abi:construct:regexp', `constructs a RegExp from ${args.length} argument(s); ECMA-262 22.2.4.1 takes a pattern and an optional flags string`);
|
|
1854
|
+
}
|
|
1855
|
+
if (args.length === 0) {
|
|
1856
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${cppConstructPatternEntry}(std::string());`);
|
|
1857
|
+
return;
|
|
1858
|
+
}
|
|
1859
|
+
const spellingOf = (argument, position) => {
|
|
1860
|
+
const carrier = argument.representation;
|
|
1861
|
+
if (carrier.kind === 'dynamic')
|
|
1862
|
+
return operandText(ctx, argument);
|
|
1863
|
+
if (carrier.kind === 'string')
|
|
1864
|
+
return operandText(ctx, argument);
|
|
1865
|
+
if (position === 0 && regexpRoleOf(carrier) === 'pattern')
|
|
1866
|
+
return operandText(ctx, argument);
|
|
1867
|
+
throw createCppEmitBlockedError(`conversion:${representationKey(carrier)}->string`, `"new RegExp" argument ${position} carries "${representationKey(carrier)}"; this backend spells it for a string` +
|
|
1868
|
+
(position === 0 ? ` or an existing ${cppRegExpNativeTypes.pattern}` : '') +
|
|
1869
|
+
', and ECMA-262 22.2.4.1 ToStrings anything else -- ToString of an arbitrary value is what this backend has no box for');
|
|
1870
|
+
};
|
|
1871
|
+
const patternArgument = args[0];
|
|
1872
|
+
if (!patternArgument)
|
|
1873
|
+
throw createCppEmitBlockedError('call-abi:construct:regexp', 'a RegExp construction with an argument has no pattern operand');
|
|
1874
|
+
const flag = args[1];
|
|
1875
|
+
// Steps 5-7 over an ordinary OBJECT pattern. Step 5 is already `false` here
|
|
1876
|
+
// -- a value carrying `[[RegExpMatcher]]` is the `Pattern` carrier
|
|
1877
|
+
// `spellingOf` hands straight through -- so what remains is `patternIsRegExp`
|
|
1878
|
+
// choosing between the object's own `source`/`flags` and a ToString of it.
|
|
1879
|
+
const fromObject = regExpConstructionFromObject(ctx, patternArgument, flag ?? null);
|
|
1880
|
+
if (fromObject !== null) {
|
|
1881
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${fromObject};`);
|
|
1882
|
+
return;
|
|
1883
|
+
}
|
|
1884
|
+
const pattern = spellingOf(patternArgument, 0);
|
|
1885
|
+
const rendered = flag ? spellingOf(flag, 1) : null;
|
|
1886
|
+
const name = defineValue(ctx, operation.result);
|
|
1887
|
+
if (rendered === null) {
|
|
1888
|
+
lines.push(`${name} = ${cppConstructPatternEntry}(${pattern});`);
|
|
1889
|
+
return;
|
|
1890
|
+
}
|
|
1891
|
+
lines.push(`${name} = ${cppConstructPatternEntry}(${pattern}, ${rendered});`);
|
|
1892
|
+
};
|
|
1893
|
+
/** Reconcile one [[Construct]] branch's declared result with the carrier selected for the `new` expression. */
|
|
1894
|
+
const constructResultText = (ctx, source, target, text) => {
|
|
1895
|
+
if (derivesFrom(ctx, source, target))
|
|
1896
|
+
return text;
|
|
1897
|
+
return alignedValueText(ctx, 'emit-callable.ts:2038', source, target, text);
|
|
1898
|
+
};
|
|
1899
|
+
/** Evaluate a construction exactly once before applying any result conversion that may inspect its source more than once. */
|
|
1900
|
+
const convertedConstructInvocationText = (ctx, source, target, invocation) => {
|
|
1901
|
+
const converted = constructResultText(ctx, source, target, callResultName);
|
|
1902
|
+
if (converted === null)
|
|
1903
|
+
return null;
|
|
1904
|
+
if (converted === callResultName)
|
|
1905
|
+
return invocation;
|
|
1906
|
+
return `[&]() -> ${cppTypeOf(target)} { auto ${callResultName} = ${invocation}; return ${converted}; }()`;
|
|
1907
|
+
};
|
|
1908
|
+
/** The actual [[Construct]] convention held by one non-sum carrier. */
|
|
1909
|
+
const constructConventionOf = (representation) => {
|
|
1910
|
+
if (representation.kind === 'function-and-constructor')
|
|
1911
|
+
return representation.construct;
|
|
1912
|
+
if (representation.kind === 'constructor-family' || representation.kind === 'constructor-value-dispatch')
|
|
1913
|
+
return representation.abi;
|
|
1914
|
+
return null;
|
|
1915
|
+
};
|
|
1916
|
+
/**
|
|
1917
|
+
* `new (condition ? A : B)(...)` where every live alternative is a real
|
|
1918
|
+
* constructor carrier. The sum keeps each alternative's frame intact, so the
|
|
1919
|
+
* discriminant selects both the construct pointer and the result conversion.
|
|
1920
|
+
*/
|
|
1921
|
+
const emitTaggedUnionConstruct = (ctx, lines, operation) => {
|
|
1922
|
+
const callee = operation.callee.representation;
|
|
1923
|
+
if (callee.kind !== 'tagged-union')
|
|
1924
|
+
return false;
|
|
1925
|
+
if (callee.arms.length === 0)
|
|
1926
|
+
throw createCppEmitBlockedError('call-abi:tagged-union-construct', 'a constructor union has no live alternatives');
|
|
1927
|
+
const branches = callee.arms.map((arm, index) => {
|
|
1928
|
+
const abi = constructConventionOf(arm.value);
|
|
1929
|
+
if (abi === null || abi.receiver !== null || abi.restFrom !== null) {
|
|
1930
|
+
throw createCppEmitBlockedError('call-abi:tagged-union-construct', `constructor-union arm ${index} carries "${representationKey(arm.value)}", not a fixed receiver-free [[Construct]] convention`);
|
|
1931
|
+
}
|
|
1932
|
+
const args = receivableArguments(abi, operation.arguments).map((argument, position) => argumentText(ctx, abi, position, argument, 'union construct'));
|
|
1933
|
+
const selected = `gea_union_constructor.get<${index}>()`;
|
|
1934
|
+
const invocation = `${selected}.construct(${paddedArguments(abi, args, 'union construct').join(', ')})`;
|
|
1935
|
+
const converted = convertedConstructInvocationText(ctx, abi.result, operation.result.representation, invocation);
|
|
1936
|
+
if (converted === null) {
|
|
1937
|
+
throw createCppEmitBlockedError(`conversion:${representationKey(abi.result)}->${representationKey(operation.result.representation)}`, `constructor-union arm ${index} returns "${representationKey(abi.result)}", which cannot fill "${representationKey(operation.result.representation)}"`);
|
|
1938
|
+
}
|
|
1939
|
+
const body = `return ${converted};`;
|
|
1940
|
+
return index === callee.arms.length - 1 ? body : `if (gea_union_constructor.is<${index}>()) { ${body} }`;
|
|
1941
|
+
});
|
|
1942
|
+
const target = cppTypeOf(operation.result.representation);
|
|
1943
|
+
const invocation = `[&]() -> ${target} { const auto& gea_union_constructor = ${operandText(ctx, operation.callee)}; ${branches.join(' ')} }()`;
|
|
1944
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${invocation};`);
|
|
1945
|
+
return true;
|
|
1946
|
+
};
|
|
1947
|
+
/** Invoke the call entry already selected by the IR's constructor-entry census. */
|
|
1948
|
+
const emitExplicitObjectReturnConstruct = (ctx, lines, operation) => {
|
|
1949
|
+
const entry = operation.entry;
|
|
1950
|
+
if (entry?.kind !== 'explicit-object-return')
|
|
1951
|
+
return false;
|
|
1952
|
+
const abi = entry.abi;
|
|
1953
|
+
const args = receivableArguments(abi, operation.arguments).map((argument, position) => argumentText(ctx, abi, position, argument, 'construct'));
|
|
1954
|
+
const invocation = `${operandText(ctx, operation.callee)}.call(${paddedArguments(abi, args, 'construct').join(', ')})`;
|
|
1955
|
+
const converted = convertedConstructInvocationText(ctx, abi.result, operation.result.representation, invocation);
|
|
1956
|
+
if (converted === null) {
|
|
1957
|
+
throw createCppEmitBlockedError(`conversion:${representationKey(abi.result)}->${representationKey(operation.result.representation)}`, `an ordinary function's explicit object result "${representationKey(abi.result)}" cannot fill "${representationKey(operation.result.representation)}"`);
|
|
1958
|
+
}
|
|
1959
|
+
lines.push(`${defineValue(ctx, operation.result)} = ${converted};`);
|
|
1960
|
+
return true;
|
|
1961
|
+
};
|
|
1962
|
+
/**
|
|
1963
|
+
* `new C(...)`: the construction goes through the constructor carrier's own
|
|
1964
|
+
* construct pointer, exactly as a call goes through a callable's invoke
|
|
1965
|
+
* pointer. `newTarget` is not consulted -- a derived construction needs the
|
|
1966
|
+
* new-target to reach the field initializers of the class that was actually
|
|
1967
|
+
* newed, and nothing here transports it -- so a construction whose new-target
|
|
1968
|
+
* is not its own callee is refused rather than silently treated as ordinary.
|
|
1969
|
+
*/
|
|
1970
|
+
export const emitConstruct = (ctx, lines, operation) => {
|
|
1971
|
+
const callee = operation.callee.representation;
|
|
1972
|
+
if (operation.newTarget.value !== operation.callee.value) {
|
|
1973
|
+
throw createCppEmitBlockedError('call-abi:construct', 'a construction whose new-target differs from its callee needs new-target transport, which is not installed');
|
|
1974
|
+
}
|
|
1975
|
+
if (emitNullishInvocation(ctx, lines, operation))
|
|
1976
|
+
return;
|
|
1977
|
+
if (emitTaggedUnionConstruct(ctx, lines, operation))
|
|
1978
|
+
return;
|
|
1979
|
+
if (emitExplicitObjectReturnConstruct(ctx, lines, operation))
|
|
1980
|
+
return;
|
|
1981
|
+
// `new TextEncoder()` / `new TextDecoder(...)`, dispatched on the RESULT and
|
|
1982
|
+
// therefore ahead of every callee-shaped branch below: their class objects
|
|
1983
|
+
// are declared in `lib.dom.d.ts` as anonymous type literals, so the callee
|
|
1984
|
+
// never derives a `native-handle` the way `Uint8Array`'s named
|
|
1985
|
+
// `Uint8ArrayConstructor` does -- it derives a constructor carrier over a
|
|
1986
|
+
// structural body, and `projection/bindings.ts` gives the NAME `host-class`
|
|
1987
|
+
// storage so no cell is emitted for it. See `emitTextCodecConstruct`.
|
|
1988
|
+
if (emitTextCodecConstruct(ctx, lines, operation))
|
|
1989
|
+
return;
|
|
1990
|
+
if (callee.kind === 'native-handle') {
|
|
1991
|
+
// `Uint8Array`/`Int32Array`/... are `native-handle`s whose checker
|
|
1992
|
+
// overload set genuinely has no single physical frame (see
|
|
1993
|
+
// `emitTypedArrayConstruct`'s own comment), so `callee.construct` is
|
|
1994
|
+
// correctly `null` for them -- this has to come before the general
|
|
1995
|
+
// `!callee.construct` refusal below, which is right for every OTHER
|
|
1996
|
+
// native-handle constructor (one unjoinable overload set there really
|
|
1997
|
+
// does mean "no convention"), just not this one.
|
|
1998
|
+
const result = operation.result.representation;
|
|
1999
|
+
if (result.kind === 'typed-array') {
|
|
2000
|
+
emitTypedArrayConstruct(ctx, lines, operation, result);
|
|
2001
|
+
return;
|
|
2002
|
+
}
|
|
2003
|
+
// `ArrayBuffer` and `DataView` -- the same exception for the same
|
|
2004
|
+
// unjoinable-overload-set reason, rendered in `emit-buffers.ts` with the
|
|
2005
|
+
// rest of the binary family.
|
|
2006
|
+
if (result.kind === 'array-buffer') {
|
|
2007
|
+
emitArrayBufferConstruct(ctx, lines, operation, result);
|
|
2008
|
+
return;
|
|
2009
|
+
}
|
|
2010
|
+
if (result.kind === 'shared-array-buffer') {
|
|
2011
|
+
emitSharedArrayBufferConstruct(ctx, lines, operation, result);
|
|
2012
|
+
return;
|
|
2013
|
+
}
|
|
2014
|
+
if (result.kind === 'data-view') {
|
|
2015
|
+
emitDataViewConstruct(ctx, lines, operation, result);
|
|
2016
|
+
return;
|
|
2017
|
+
}
|
|
2018
|
+
// `Array` is the same shape of exception, and for the same reason -- see
|
|
2019
|
+
// `emitArrayConstruct`. Its result is an ordinary array-object, not a
|
|
2020
|
+
// handle to the host's own object, which is what distinguishes it here.
|
|
2021
|
+
if (callee.protocol === 'FunctionConstructor') {
|
|
2022
|
+
if (!ctx.deriver.dynamicFallback || result.kind !== 'dynamic')
|
|
2023
|
+
throw createCppEmitBlockedError('call-abi:function-constructor', 'Function requires --dynamic-fallback and a dynamic callable result');
|
|
2024
|
+
const args = functionConstructorArgumentTexts(ctx, operation.arguments, 'construct');
|
|
2025
|
+
lines.push(`${defineValue(ctx, operation.result)} = gea::Eval::constructFunction({${args.join(', ')}});`);
|
|
2026
|
+
return;
|
|
2027
|
+
}
|
|
2028
|
+
if (callee.protocol === 'ProxyConstructor') {
|
|
2029
|
+
if (!ctx.deriver.dynamicFallback || result.kind !== 'dynamic')
|
|
2030
|
+
throw createCppEmitBlockedError('call-abi:proxy-construct', 'Proxy requires --dynamic-fallback and a dynamic result carrier');
|
|
2031
|
+
if (operation.arguments.length !== 2)
|
|
2032
|
+
throw createCppEmitBlockedError('call-abi:proxy-construct', 'Proxy requires target and handler');
|
|
2033
|
+
const args = operation.arguments.map((argument) => boxedValueText(ctx, argument, 'Proxy construction'));
|
|
2034
|
+
lines.push(`${defineValue(ctx, operation.result)} = gea::Value::proxy(${args.join(', ')});`);
|
|
2035
|
+
return;
|
|
2036
|
+
}
|
|
2037
|
+
if (result.kind === 'array-object' && callee.protocol === 'ArrayConstructor') {
|
|
2038
|
+
emitArrayConstruct(ctx, lines, operation, result);
|
|
2039
|
+
return;
|
|
2040
|
+
}
|
|
2041
|
+
// `Map`/`Set`/`WeakMap`/`WeakSet` -- the third case of the same exception,
|
|
2042
|
+
// for the same unjoinable-overload-set reason. See
|
|
2043
|
+
// `emitKeyedCollectionConstruct`.
|
|
2044
|
+
if (result.kind === 'keyed-collection') {
|
|
2045
|
+
emitKeyedCollectionConstruct(ctx, lines, operation, result);
|
|
2046
|
+
return;
|
|
2047
|
+
}
|
|
2048
|
+
// `Date` -- the fourth case, and the same unjoinable overload set again:
|
|
2049
|
+
// `new Date()`, `new Date(value)` and `new Date(year, monthIndex, ...)`
|
|
2050
|
+
// are three different parameter-0 shapes. See `emitDateConstruct`
|
|
2051
|
+
// (emit-prototype-date.ts), which owns every Date spelling this backend has.
|
|
2052
|
+
if (isDateCarrier(result) && result.kind === 'native-record-ref') {
|
|
2053
|
+
emitDateConstruct(ctx, lines, operation, result);
|
|
2054
|
+
return;
|
|
2055
|
+
}
|
|
2056
|
+
// `RegExp` -- the fourth case of the same exception, for the same
|
|
2057
|
+
// unjoinable-overload-set reason. See `emitRegExpConstruct`.
|
|
2058
|
+
if (regexpRoleOf(result) === 'pattern') {
|
|
2059
|
+
emitRegExpConstruct(ctx, lines, operation);
|
|
2060
|
+
return;
|
|
2061
|
+
}
|
|
2062
|
+
if (!callee.construct) {
|
|
2063
|
+
throw createCppEmitBlockedError(`host-invocation:${callee.native ?? callee.protocol}`, `a "${callee.protocol}" host handle carries no [[Construct]] convention this program's checker derived for it`);
|
|
2064
|
+
}
|
|
2065
|
+
const invocation = nativeHandleInvocationText(ctx, callee, callee.construct, operation.arguments, 'construct', result);
|
|
2066
|
+
const name = defineValue(ctx, operation.result);
|
|
2067
|
+
lines.push(`${name} = ${invocation};`);
|
|
2068
|
+
return;
|
|
2069
|
+
}
|
|
2070
|
+
// A plain JS constructor function whose `.prototype` the program itself
|
|
2071
|
+
// mutates (`prototypeMutatedConstructorTypes`, dynamic-fallback.ts) is boxed
|
|
2072
|
+
// to `dynamic`, and `translation-unit.ts`'s `constructThunkOf` refuses to
|
|
2073
|
+
// build a native construct thunk for it (its instance carrier is `dynamic`,
|
|
2074
|
+
// not `record`/`record-with-index`/`native-record-ref`) -- so `new F()`
|
|
2075
|
+
// cannot go through the constructor-family path below at all. `Value::construct`
|
|
2076
|
+
// (gea_runtime.h) is the ECMA-262 10.2.2 OrdinaryCreateFromConstructor
|
|
2077
|
+
// equivalent instead: it makes a fresh object whose [[Prototype]] is
|
|
2078
|
+
// whatever `F.prototype` CURRENTLY holds, re-enters through the ordinary
|
|
2079
|
+
// `[[Call]]` thunk with that object as `this`, and returns the object
|
|
2080
|
+
// unless the body itself returned one.
|
|
2081
|
+
if (callee.kind === 'dynamic') {
|
|
2082
|
+
const args = operation.arguments.map((argument) => boxedValueText(ctx, argument, 'dynamic construction argument'));
|
|
2083
|
+
const name = defineValue(ctx, operation.result);
|
|
2084
|
+
lines.push(`${name} = ${operandText(ctx, operation.callee)}.construct({${args.join(', ')}});`);
|
|
2085
|
+
return;
|
|
2086
|
+
}
|
|
2087
|
+
if (callee.kind !== 'constructor-family' && callee.kind !== 'constructor-value-dispatch' && callee.kind !== 'function-and-constructor') {
|
|
2088
|
+
throw createCppEmitBlockedError(`call-abi:no-construct-path:${callee.kind}`, `a callee carried as "${callee.kind}" has no construct path; this emitter constructs only through a constructor carrier`);
|
|
2089
|
+
}
|
|
2090
|
+
// The construct convention, which is the callee's own and not its call one:
|
|
2091
|
+
// `function-and-constructor` states both, and padding a construction against
|
|
2092
|
+
// the call frame would count the wrong parameters.
|
|
2093
|
+
const abi = callee.kind === 'function-and-constructor' ? callee.construct : callee.abi;
|
|
2094
|
+
// Reconciled against the declared parameter slot exactly as `emitCall`'s
|
|
2095
|
+
// generic path reconciles a call's arguments (`argumentText`, above) --
|
|
2096
|
+
// this used raw `operandText` before, which left a dynamic-sourced
|
|
2097
|
+
// argument (e.g. a caught `error` passed to `new SomeClass(error)`) never
|
|
2098
|
+
// converted at all, an unconditional over-claim the moment anything
|
|
2099
|
+
// upstream started asking whether that conversion was even installed. See
|
|
2100
|
+
// `preflight/invocation-arguments.ts`'s `conversion-role:invocation:construct:argument`.
|
|
2101
|
+
const args = receivableArguments(abi, operation.arguments).map((argument, position) => argumentText(ctx, abi, position, argument, 'construct'));
|
|
2102
|
+
// Devirtualized only where the program constructs REPEATEDLY, which is the
|
|
2103
|
+
// distinction an earlier attempt at this lacked: naming the construct
|
|
2104
|
+
// function lets clang inline the whole construction -- `gea::makeRef`, its
|
|
2105
|
+
// pool refill, its `try`/`catch` -- into whatever body constructs, and that
|
|
2106
|
+
// is worth 46.1ms to 41.9ms in `binary_trees`, which constructs a million
|
|
2107
|
+
// times inside a recursive body, and 38.1ms to 49.0ms the wrong way in
|
|
2108
|
+
// `method_calls`, which constructs once before a hot loop whose own callee
|
|
2109
|
+
// then stops being inlined. `buildRepeatedConstructorIndex` answers which,
|
|
2110
|
+
// and its doc carries the rest of the measurements -- including `noinline`
|
|
2111
|
+
// on the construct function, the obvious way to have the direct call without
|
|
2112
|
+
// the inlining, which is worse than either (41.9ms to 44.5ms).
|
|
2113
|
+
const cell = ctx.bindingReadDeclarations.get(operation.callee.value);
|
|
2114
|
+
const repeated = cell === undefined ? undefined : ctx.repeatedConstructors.get(cell);
|
|
2115
|
+
const invocation = repeated === undefined
|
|
2116
|
+
? `${operandText(ctx, operation.callee)}.construct(${paddedArguments(abi, args, 'construct').join(', ')})`
|
|
2117
|
+
: `${cppConstructName(repeated)}(${[
|
|
2118
|
+
`static_cast<gea::NativeClassMethodState*>(${operandText(ctx, operation.callee)}.environment)`,
|
|
2119
|
+
...paddedArguments(abi, args, 'construct')
|
|
2120
|
+
].join(', ')})`;
|
|
2121
|
+
const name = defineValue(ctx, operation.result);
|
|
2122
|
+
lines.push(`${name} = ${invocation};`);
|
|
2123
|
+
};
|
|
2124
|
+
/**
|
|
2125
|
+
* Class evaluation allocates a native prototype owner, even when its methods
|
|
2126
|
+
* capture nothing. Constructor copies share it, while another evaluation of
|
|
2127
|
+
* the same declaration gets a new owner. The evaluated superclass supplies
|
|
2128
|
+
* the parent owner; looking it up by declaration would conflate factories.
|
|
2129
|
+
*
|
|
2130
|
+
* This state carries method identities only. Constructor body/field capture
|
|
2131
|
+
* transport remains subject to the existing fail-closed admission below.
|
|
2132
|
+
*/
|
|
2133
|
+
export const emitAllocateConstructor = (ctx, lines, operation) => {
|
|
2134
|
+
if (operation.captures.length > 0) {
|
|
2135
|
+
throw createCppEmitBlockedError('capture:class-constructor', `carries ${operation.captures.length} capture(s); a class's methods and field initializers are called directly by ` +
|
|
2136
|
+
"translation-unit.ts's constructDefinitionsOf, with no environment parameter to receive one, so no capture can be transported here");
|
|
2137
|
+
}
|
|
2138
|
+
const name = defineValue(ctx, operation.result);
|
|
2139
|
+
const heritageOperand = operation.heritage;
|
|
2140
|
+
const heritage = heritageOperand?.representation;
|
|
2141
|
+
// A family of several classes is a base's constructor slot that the program
|
|
2142
|
+
// also stores subclasses into; the struct extends the family's declared
|
|
2143
|
+
// class alone, so the evaluated value is checked to be exactly that class.
|
|
2144
|
+
const exactBase = heritage?.kind === 'constructor-family' && heritage.members.length > 1 && heritage.abi.result.kind === 'class-ref'
|
|
2145
|
+
? heritage.abi.result.declaration
|
|
2146
|
+
: null;
|
|
2147
|
+
const parent = heritage?.kind !== 'constructor-family' || !heritageOperand
|
|
2148
|
+
? ''
|
|
2149
|
+
: exactBase !== null
|
|
2150
|
+
? `gea::exactNativeClassHeritage<${cppClassName(exactBase)}>(${operandText(ctx, heritageOperand)}.environment)`
|
|
2151
|
+
: `gea::nativeClassMethodStateFromEnvironment(${operandText(ctx, heritageOperand)}.environment)`;
|
|
2152
|
+
const environment = `gea::allocateNativeClassMethodEnvironment<${cppClassName(operation.declaration)}>(${parent})`;
|
|
2153
|
+
lines.push(`${name} = ${cppTypeOf(operation.result.representation)}{&${cppConstructThunkName(operation.declaration)}, ${environment}};`);
|
|
2154
|
+
};
|