@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,3089 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import { isClassSpelledSourceClass } from './model.js';
|
|
3
|
+
import { dependencyFactSolver } from './component-solver.js';
|
|
4
|
+
import { classConstructorKeepsInstanceOf, exactSourceConstructionOf, sourceConstructorSelectionsOf } from './member-call-forwarding.js';
|
|
5
|
+
import { sourceClassCallableMemberPlanOf, sourceClassDataMemberPlanOf, sourceClassKeyReadPlanOf } from './source-class-data.js';
|
|
6
|
+
import { sourceClassFamilyOf } from './owned-class-receivers.js';
|
|
7
|
+
import { isGlobalObjectAssign } from './value-flow.js';
|
|
8
|
+
import { intrinsicDataDefinitionTargetOf } from './intrinsic-data-definition.js';
|
|
9
|
+
import { callableCompletionSummaryOf, constructionYieldsCompletionOf } from './callable-completions.js';
|
|
10
|
+
import { sourceInvocationFrameLayoutOf } from './source-invocation-frame-layout.js';
|
|
11
|
+
import { localBindingValuesOf, localBindingWritesAreComplete } from './value-provenance.js';
|
|
12
|
+
import { collectionStoredValuesOf, collectionValueContinuationsOf } from './collection-value-continuation.js';
|
|
13
|
+
import { isTypePositionReference, runtimeParametersOf, unwrapNaming } from './targets.js';
|
|
14
|
+
import { outermostErasureOf, unwrapErasedExpression } from '../producers/erasure.js';
|
|
15
|
+
import { deferredIntrinsicProtocolLedgerOf } from '../deferred-intrinsic-protocols.js';
|
|
16
|
+
import { censusArgumentsObjects } from '../arguments-objects.js';
|
|
17
|
+
import { exportIsUnimported } from './targets.js';
|
|
18
|
+
import { inProgramImportReferencesOf } from './export-importers.js';
|
|
19
|
+
import { sourceConstructionFramesOf } from './source-construction-frames.js';
|
|
20
|
+
import { isSourceInstanceMethod } from './source-prototype-method-identity.js';
|
|
21
|
+
/**
|
|
22
|
+
* Walk two lists as one without building a third.
|
|
23
|
+
*
|
|
24
|
+
* Every caller is inside a transfer, and a transfer runs once per
|
|
25
|
+
* re-evaluation of its node -- so a spread that reads as a convenience is an
|
|
26
|
+
* allocation and a copy priced per transfer, not per program.
|
|
27
|
+
*/
|
|
28
|
+
const both = (first, second) => first === undefined || first.length === 0 ? second : second.length === 0 ? first : [...first, ...second];
|
|
29
|
+
const refusalDebug = process.env['GEA_SESSION_REFUSALS'] !== undefined;
|
|
30
|
+
/** The query kinds whose facts are values and therefore need a grounding witness. */
|
|
31
|
+
const VALUE_CARRYING_KINDS = new Set([
|
|
32
|
+
'value',
|
|
33
|
+
'slot',
|
|
34
|
+
'parameter',
|
|
35
|
+
'binding-element',
|
|
36
|
+
'super-targets',
|
|
37
|
+
'receiver',
|
|
38
|
+
'completion',
|
|
39
|
+
'frame-receiver',
|
|
40
|
+
'frame-argument',
|
|
41
|
+
'frame-parameter',
|
|
42
|
+
'frame-value',
|
|
43
|
+
'frame-completion'
|
|
44
|
+
]);
|
|
45
|
+
const VALUE_ORIGIN = 'source-value-origin';
|
|
46
|
+
// Read once at load, not per value transfer: `process.env` is a native interceptor.
|
|
47
|
+
const noArrayKeys = process.env['GEA_NO_ARRAY_KEYS'] !== undefined;
|
|
48
|
+
/**
|
|
49
|
+
* The element slot of an array root: every position at once. A computed
|
|
50
|
+
* index names no single key (`this.textures[ i ]` in three's RenderTarget,
|
|
51
|
+
* with the loop counter as the index), so a store or read through one is
|
|
52
|
+
* filed under this key, and a literal-index slot and this one alias each
|
|
53
|
+
* other -- `slot(root, '0')` includes what `[ i ]` stored, and `[ i ]` reads
|
|
54
|
+
* what `[ 0 ]` stored. Before this the unbounded index published nothing and
|
|
55
|
+
* read nothing, and every render target's textures were unresolvable.
|
|
56
|
+
*/
|
|
57
|
+
const ELEMENT_KEY = '[]';
|
|
58
|
+
const CANONICAL_INDEX = /^(?:0|[1-9][0-9]*)$/;
|
|
59
|
+
const isCanonicalIndex = (key) => CANONICAL_INDEX.test(key);
|
|
60
|
+
/** Whether a store or read filed under `stored` can reach the slot `key` of `root`. */
|
|
61
|
+
const keyReaches = (root, stored, key) => stored.includes(key) ||
|
|
62
|
+
(ts.isArrayLiteralExpression(root) &&
|
|
63
|
+
((key === ELEMENT_KEY && stored.some(isCanonicalIndex)) || (isCanonicalIndex(key) && stored.includes(ELEMENT_KEY))));
|
|
64
|
+
const isObjectRoot = (node) => ts.isNewExpression(node) || ts.isObjectLiteralExpression(node) || ts.isArrayLiteralExpression(node);
|
|
65
|
+
const isBody = (node) => ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isArrowFunction(node) || ts.isMethodDeclaration(node);
|
|
66
|
+
const accessKey = (access) => ts.isPropertyAccessExpression(access) ? access.name.text : literalKey(access.argumentExpression);
|
|
67
|
+
const literalKey = (node) => {
|
|
68
|
+
const value = unwrapErasedExpression(node);
|
|
69
|
+
return ts.isStringLiteralLike(value) || ts.isNumericLiteral(value) ? value.text : null;
|
|
70
|
+
};
|
|
71
|
+
const declarationKey = (name) => ts.isComputedPropertyName(name) ? literalKey(name.expression) : ts.isPrivateIdentifier(name) ? null : name.text;
|
|
72
|
+
/**
|
|
73
|
+
* The slot a binding element reads out of the object being destructured.
|
|
74
|
+
*
|
|
75
|
+
* `{ app }` and `{ app: local }` both read `app` -- the PROPERTY name is the
|
|
76
|
+
* slot and the bound name is only what the cell is called afterwards, which is
|
|
77
|
+
* the same distinction `declarationKey` makes for a property. An array pattern
|
|
78
|
+
* reads by position, and an omitted element still occupies one, so the index
|
|
79
|
+
* comes from the element list rather than from a running count of the elements
|
|
80
|
+
* that bind something.
|
|
81
|
+
*/
|
|
82
|
+
const bindingElementKey = (element) => {
|
|
83
|
+
const pattern = element.parent;
|
|
84
|
+
if (ts.isArrayBindingPattern(pattern))
|
|
85
|
+
return String(pattern.elements.indexOf(element));
|
|
86
|
+
if (element.propertyName)
|
|
87
|
+
return declarationKey(element.propertyName);
|
|
88
|
+
return ts.isIdentifier(element.name) ? element.name.text : null;
|
|
89
|
+
};
|
|
90
|
+
const sessions = new WeakMap();
|
|
91
|
+
export const sourceValueSessionOf = (checker, flow) => {
|
|
92
|
+
const known = sessions.get(flow);
|
|
93
|
+
if (known)
|
|
94
|
+
return known;
|
|
95
|
+
const session = createSession(checker, flow);
|
|
96
|
+
sessions.set(flow, session);
|
|
97
|
+
return session;
|
|
98
|
+
};
|
|
99
|
+
const createSession = (checker, flow) => {
|
|
100
|
+
// A closed native Map is only closed while `Map.prototype`'s `get`/`set`
|
|
101
|
+
// are the intrinsics: a finite root walk is not that proof
|
|
102
|
+
// (`native-collection-protocol.ts`), so only the deferred obligation -- an
|
|
103
|
+
// intrinsic-protocol requirement the final sealed census discharges -- is
|
|
104
|
+
// accepted here, and a plan with no ledger to file it with is refused.
|
|
105
|
+
const nativeProtocolClosed = (plan) => plan.deferred &&
|
|
106
|
+
deferredIntrinsicProtocolLedgerOf(flow)?.requirePrototypeKeys(plan.intrinsic, plan.prototypeKeys, plan.location) === true;
|
|
107
|
+
const unary = new Map();
|
|
108
|
+
const slots = new Map();
|
|
109
|
+
const uses = new Map();
|
|
110
|
+
const publications = new Map();
|
|
111
|
+
const frameQueries = new Map();
|
|
112
|
+
const requirements = new Map();
|
|
113
|
+
const argumentsByFile = new Map();
|
|
114
|
+
const argumentsUsesAt = (body) => {
|
|
115
|
+
const file = body.getSourceFile();
|
|
116
|
+
let census = argumentsByFile.get(file);
|
|
117
|
+
if (!census)
|
|
118
|
+
argumentsByFile.set(file, (census = censusArgumentsObjects(checker, [file])));
|
|
119
|
+
return census.usesByOwner.get(body);
|
|
120
|
+
};
|
|
121
|
+
const undefinedValue = ts.factory.createVoidZero();
|
|
122
|
+
const arrayLengths = new Map();
|
|
123
|
+
const sites = new Map(flow.calls.map((site) => [site.call, site]));
|
|
124
|
+
const isNullish = (held) => held === undefinedValue || held.kind === ts.SyntaxKind.NullKeyword || ts.isVoidExpression(held);
|
|
125
|
+
const constructionFrames = sourceConstructionFramesOf(checker, flow);
|
|
126
|
+
// Destructuring is the other spelling of a slot read, so the enumeration of
|
|
127
|
+
// "everything that can read this key" has to hold both. A key no element
|
|
128
|
+
// states -- a rest element, or a computed property name -- can read any key
|
|
129
|
+
// at all and joins the unknown side, exactly as an unresolved element access
|
|
130
|
+
// does.
|
|
131
|
+
const patternReadsByKey = new Map();
|
|
132
|
+
const unknownPatternReads = [];
|
|
133
|
+
for (const element of flow.bindingPatternReads) {
|
|
134
|
+
const key = bindingElementKey(element);
|
|
135
|
+
if (element.dotDotDotToken || key === null)
|
|
136
|
+
unknownPatternReads.push(element);
|
|
137
|
+
else {
|
|
138
|
+
let reads = patternReadsByKey.get(key);
|
|
139
|
+
if (!reads)
|
|
140
|
+
patternReadsByKey.set(key, (reads = []));
|
|
141
|
+
reads.push(element);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const nodeQuery = (kind, node) => {
|
|
145
|
+
let nodes = unary.get(kind);
|
|
146
|
+
if (!nodes)
|
|
147
|
+
unary.set(kind, (nodes = new Map()));
|
|
148
|
+
let query = nodes.get(node);
|
|
149
|
+
if (!query)
|
|
150
|
+
nodes.set(node, (query = { kind, node }));
|
|
151
|
+
return query;
|
|
152
|
+
};
|
|
153
|
+
const value = (expression) => nodeQuery('value', unwrapErasedExpression(expression));
|
|
154
|
+
const frameQuery = (kind, call, body, subject = null) => {
|
|
155
|
+
let calls = frameQueries.get(kind);
|
|
156
|
+
if (!calls)
|
|
157
|
+
frameQueries.set(kind, (calls = new Map()));
|
|
158
|
+
let bodies = calls.get(call);
|
|
159
|
+
if (!bodies)
|
|
160
|
+
calls.set(call, (bodies = new Map()));
|
|
161
|
+
let subjects = bodies.get(body);
|
|
162
|
+
if (!subjects)
|
|
163
|
+
bodies.set(body, (subjects = new Map()));
|
|
164
|
+
let query = subjects.get(subject);
|
|
165
|
+
if (!query)
|
|
166
|
+
subjects.set(subject, (query = { kind, node: call, body, subject }));
|
|
167
|
+
return query;
|
|
168
|
+
};
|
|
169
|
+
const closure = (root) => nodeQuery('closure', root);
|
|
170
|
+
/**
|
|
171
|
+
* The plain source function `new F( ... )` runs when `F` is an identifier
|
|
172
|
+
* naming an unwritten, non-generator, non-async function declaration
|
|
173
|
+
* with ordinary formals, or null. Its completions are proven separately
|
|
174
|
+
* (`constructionYieldsCompletionOf`); this only names the body.
|
|
175
|
+
*/
|
|
176
|
+
const thisConstructorReceiverOf = (call) => {
|
|
177
|
+
const callee = unwrapErasedExpression(call.expression);
|
|
178
|
+
if (!ts.isPropertyAccessExpression(callee) || callee.name.text !== 'constructor')
|
|
179
|
+
return null;
|
|
180
|
+
const receiver = unwrapErasedExpression(callee.expression);
|
|
181
|
+
return receiver.kind === ts.SyntaxKind.ThisKeyword ? receiver : null;
|
|
182
|
+
};
|
|
183
|
+
const constructorFunctionCalleeOf = (call) => {
|
|
184
|
+
if (call.arguments?.some(ts.isSpreadElement))
|
|
185
|
+
return null;
|
|
186
|
+
const callee = unwrapErasedExpression(call.expression);
|
|
187
|
+
if (!ts.isIdentifier(callee))
|
|
188
|
+
return null;
|
|
189
|
+
const named = flow.targetOf(callee);
|
|
190
|
+
const target = aliasedDeclarationOf(named?.symbol ?? undefined) ?? named?.declaration;
|
|
191
|
+
if (!target || !ts.isFunctionDeclaration(target) || !target.body || target.getSourceFile().isDeclarationFile)
|
|
192
|
+
return null;
|
|
193
|
+
if (target.asteriskToken || target.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword))
|
|
194
|
+
return null;
|
|
195
|
+
if (target.parameters.some((parameter) => parameter.dotDotDotToken || !ts.isIdentifier(parameter.name)))
|
|
196
|
+
return null;
|
|
197
|
+
if (flow.writesToDeclaration(target).some((write) => write.slot === 'whole' && write.edge !== 'return' && write.edge !== 'yield'))
|
|
198
|
+
return null;
|
|
199
|
+
return target;
|
|
200
|
+
};
|
|
201
|
+
/**
|
|
202
|
+
* The computed-key reads that can be reading THIS container.
|
|
203
|
+
*
|
|
204
|
+
* A read whose key the program does not spell -- `o[ k ]` -- can publish
|
|
205
|
+
* any key of any container its receiver reaches, so every publication had
|
|
206
|
+
* to test all 968 of the three.js app's unknown reads against its own container.
|
|
207
|
+
* That test does not depend on the key, and a container publishes many
|
|
208
|
+
* keys, so the same 968-entry scan was subscribed once PER KEY: this one
|
|
209
|
+
* loop is the largest single source of the 11.3M observe edges the solver
|
|
210
|
+
* carries on the three.js app. It is one node's fact, asked once per container.
|
|
211
|
+
*/
|
|
212
|
+
const unknownReadsOf = (container) => nodeQuery('unknown-reads', container);
|
|
213
|
+
const targets = (call) => nodeQuery('targets', call);
|
|
214
|
+
const superTargets = (call) => nodeQuery('super-targets', call);
|
|
215
|
+
/**
|
|
216
|
+
* The method a lexical `super.<key>` selects, walking `extends` from the
|
|
217
|
+
* home class the way the prototype chain does.
|
|
218
|
+
*
|
|
219
|
+
* `super` is a LOOKUP ORIGIN, not a receiver: the object used as `this` is
|
|
220
|
+
* the instance, but the search for the callable starts one link up the
|
|
221
|
+
* chain, so an override on the instance's own class is exactly what it skips.
|
|
222
|
+
* Without this the graph's use walk read a `super` mention of the instance as
|
|
223
|
+
* an ordinary member call on it, asked for the targets of a dispatch it
|
|
224
|
+
* declines, and refused -- taking the enclosing ORDINARY call down with it,
|
|
225
|
+
* because a method whose body contains `super.copy(v)` is a method whose
|
|
226
|
+
* receiver closure could not be proven.
|
|
227
|
+
*
|
|
228
|
+
* Integrity is not re-derived here. Every class the walk visits must satisfy
|
|
229
|
+
* `classConstructorKeepsInstanceOf`, the same authority the receiver family
|
|
230
|
+
* already rests on, which is what refuses a program that writes
|
|
231
|
+
* `Base.prototype.copy`.
|
|
232
|
+
*/
|
|
233
|
+
const superLookup = (home, key, seen) => {
|
|
234
|
+
if (!isClassSpelledSourceClass(home) || seen.has(home))
|
|
235
|
+
return null;
|
|
236
|
+
seen.add(home);
|
|
237
|
+
const heritage = home.heritageClauses?.find((clause) => clause.token === ts.SyntaxKind.ExtendsKeyword);
|
|
238
|
+
if (!heritage || heritage.types.length !== 1)
|
|
239
|
+
return null;
|
|
240
|
+
const bases = sourceConstructorSelectionsOf(checker, flow, heritage.types[0].expression);
|
|
241
|
+
if (!bases || bases.length === 0)
|
|
242
|
+
return null;
|
|
243
|
+
const found = [];
|
|
244
|
+
for (const base of bases) {
|
|
245
|
+
const own = ownSuperSlot(base, key, seen);
|
|
246
|
+
if (own === null)
|
|
247
|
+
return null;
|
|
248
|
+
found.push(...own);
|
|
249
|
+
}
|
|
250
|
+
return found;
|
|
251
|
+
};
|
|
252
|
+
const ownSuperSlot = (owner, key, seen) => {
|
|
253
|
+
if (!isClassSpelledSourceClass(owner))
|
|
254
|
+
return null;
|
|
255
|
+
const symbol = owner.name ? checker.getSymbolAtLocation(owner.name) : checker.getTypeAtLocation(owner).getSymbol();
|
|
256
|
+
const instance = symbol && checker.getDeclaredTypeOfSymbol(symbol);
|
|
257
|
+
if (!instance?.isClassOrInterface() || !classConstructorKeepsInstanceOf(checker, flow, instance))
|
|
258
|
+
return null;
|
|
259
|
+
const own = [];
|
|
260
|
+
for (const member of owner.members) {
|
|
261
|
+
if (!member.name || (ts.getCombinedModifierFlags(member) & ts.ModifierFlags.Static) !== 0)
|
|
262
|
+
continue;
|
|
263
|
+
if (ts.isPrivateIdentifier(member.name))
|
|
264
|
+
continue;
|
|
265
|
+
// An instance field lives on the RECEIVER, and a constructor is not on
|
|
266
|
+
// the prototype at all: neither is where this lookup starts.
|
|
267
|
+
if (ts.isPropertyDeclaration(member) || ts.isConstructorDeclaration(member))
|
|
268
|
+
continue;
|
|
269
|
+
const memberKey = declarationKey(member.name);
|
|
270
|
+
// One computed member name makes the whole prototype's key set unknown,
|
|
271
|
+
// so the absence of this key on this class is no longer a fact.
|
|
272
|
+
if (memberKey === null)
|
|
273
|
+
return null;
|
|
274
|
+
if (memberKey !== key)
|
|
275
|
+
continue;
|
|
276
|
+
if (!ts.isMethodDeclaration(member) || member.body === undefined)
|
|
277
|
+
return null;
|
|
278
|
+
own.push(member);
|
|
279
|
+
}
|
|
280
|
+
return own.length > 0 ? own : superLookup(owner, key, seen);
|
|
281
|
+
};
|
|
282
|
+
/**
|
|
283
|
+
* The key is absent from this class's whole prototype chain.
|
|
284
|
+
*
|
|
285
|
+
* Absence is a chain-wide fact, so every step must be spelled: a computed
|
|
286
|
+
* member name anywhere makes the key set unknown, a constructor the graph
|
|
287
|
+
* cannot keep hold of its instance can add the key at run time, and a chain
|
|
288
|
+
* that does not end at a class with no `extends` never reaches the point
|
|
289
|
+
* where `Object.prototype` is the only thing left to ask about. Any of
|
|
290
|
+
* those, and the key is not PROVEN absent -- which is a different answer
|
|
291
|
+
* from proven present, and the caller must treat it as neither.
|
|
292
|
+
*/
|
|
293
|
+
const chainAbsences = new Map();
|
|
294
|
+
const absentThroughChain = (owner, key, seen) => {
|
|
295
|
+
// The chain is syntax and a constructor proof, neither of which changes as
|
|
296
|
+
// the graph settles -- but it is asked once per (root, key) reaching an
|
|
297
|
+
// unspelled name, which on three's Object3D chain is a class-keeps-its-
|
|
298
|
+
// instance proof per access. Memoized on the pair it actually depends on.
|
|
299
|
+
let byKey = chainAbsences.get(owner);
|
|
300
|
+
if (!byKey)
|
|
301
|
+
chainAbsences.set(owner, (byKey = new Map()));
|
|
302
|
+
const known = byKey.get(key);
|
|
303
|
+
if (known !== undefined)
|
|
304
|
+
return known;
|
|
305
|
+
// Only a walk that starts here is memoized: one entered mid-chain carries
|
|
306
|
+
// the classes already visited, and its answer is about that path.
|
|
307
|
+
const top = seen.size === 0;
|
|
308
|
+
const answer = absentThroughChainUncached(owner, key, seen);
|
|
309
|
+
if (top)
|
|
310
|
+
byKey.set(key, answer);
|
|
311
|
+
return answer;
|
|
312
|
+
};
|
|
313
|
+
const absentThroughChainUncached = (owner, key, seen) => {
|
|
314
|
+
if (!isClassSpelledSourceClass(owner) || seen.has(owner))
|
|
315
|
+
return false;
|
|
316
|
+
seen.add(owner);
|
|
317
|
+
const symbol = owner.name ? checker.getSymbolAtLocation(owner.name) : checker.getTypeAtLocation(owner).getSymbol();
|
|
318
|
+
const instance = symbol && checker.getDeclaredTypeOfSymbol(symbol);
|
|
319
|
+
if (!instance?.isClassOrInterface() || !classConstructorKeepsInstanceOf(checker, flow, instance))
|
|
320
|
+
return false;
|
|
321
|
+
for (const member of owner.members) {
|
|
322
|
+
if (!member.name || (ts.getCombinedModifierFlags(member) & ts.ModifierFlags.Static) !== 0)
|
|
323
|
+
continue;
|
|
324
|
+
if (ts.isPrivateIdentifier(member.name))
|
|
325
|
+
continue;
|
|
326
|
+
const memberKey = declarationKey(member.name);
|
|
327
|
+
if (memberKey === null || memberKey === key)
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
const heritage = owner.heritageClauses?.find((clause) => clause.token === ts.SyntaxKind.ExtendsKeyword);
|
|
331
|
+
if (!heritage)
|
|
332
|
+
return true;
|
|
333
|
+
if (heritage.types.length !== 1)
|
|
334
|
+
return false;
|
|
335
|
+
const bases = sourceConstructorSelectionsOf(checker, flow, heritage.types[0].expression);
|
|
336
|
+
if (!bases || bases.length === 0)
|
|
337
|
+
return false;
|
|
338
|
+
return bases.every((base) => absentThroughChain(base, key, new Set(seen)));
|
|
339
|
+
};
|
|
340
|
+
/**
|
|
341
|
+
* A key an object provably does not have -- for an instance, anywhere on
|
|
342
|
+
* its chain; for a literal, among the keys it spells.
|
|
343
|
+
*
|
|
344
|
+
* `for ( const key in values ) this[ key ] = values[ key ]` -- three's
|
|
345
|
+
* `setValues` -- stores to keys the class never declares. That is not an
|
|
346
|
+
* unmodelled descriptor: with the key absent from the chain the store
|
|
347
|
+
* creates a FRESH own data property, runs no accessor, and disturbs no slot
|
|
348
|
+
* the graph tracks. What it is NOT is a readable cell: `slot` keeps refusing
|
|
349
|
+
* the key, so a program that reads it back is opaque, and the value stored
|
|
350
|
+
* into it can only be reached through the instance itself.
|
|
351
|
+
*
|
|
352
|
+
* The last link in the chain is `Object.prototype`, which is an obligation
|
|
353
|
+
* rather than an observation -- registered here under exactly the key asked
|
|
354
|
+
* about, and discharged later by the sealed host-mutation census. With no
|
|
355
|
+
* ledger to carry it there is nothing that will ever discharge it, so the
|
|
356
|
+
* absence does not hold.
|
|
357
|
+
*/
|
|
358
|
+
const absentOwnKey = (root, key, at, descriptorOf, hasUnnamedWrite) => {
|
|
359
|
+
if (ts.isObjectLiteralExpression(root)) {
|
|
360
|
+
// A literal that spells every key it has proves the absence of every
|
|
361
|
+
// key it does not. This is asked while walking the literal's OWN uses,
|
|
362
|
+
// so a store that added an unspelled key is a refusal in the same walk.
|
|
363
|
+
if (literalSpelledKeys(root) === null || descriptorOf(root, key) || hasUnnamedWrite(root))
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
else if (ts.isNewExpression(root)) {
|
|
367
|
+
const fact = construction(root);
|
|
368
|
+
if (!fact || fact.alternatives.length === 0)
|
|
369
|
+
return false;
|
|
370
|
+
if (!fact.alternatives.every((owner) => absentThroughChain(owner, key, new Set())))
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
else
|
|
374
|
+
return false;
|
|
375
|
+
return deferredIntrinsicProtocolLedgerOf(flow)?.requirePrototypeKeys('Object', { names: [key] }, at) === true;
|
|
376
|
+
};
|
|
377
|
+
const readsByKey = new Map();
|
|
378
|
+
const unknownReads = [];
|
|
379
|
+
for (const access of flow.propertyAccesses) {
|
|
380
|
+
const key = accessKey(access);
|
|
381
|
+
const entry = { access, receiver: value(access.expression) };
|
|
382
|
+
if (key === null)
|
|
383
|
+
unknownReads.push(entry);
|
|
384
|
+
else {
|
|
385
|
+
let reads = readsByKey.get(key);
|
|
386
|
+
if (!reads)
|
|
387
|
+
readsByKey.set(key, (reads = []));
|
|
388
|
+
reads.push(entry);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* `unknownReads`, indexed by the query that answers what its RECEIVER
|
|
393
|
+
* holds -- the one hop `case 'unknown-reads'` needs from a holder key (a
|
|
394
|
+
* `Query`, handed back by `holdersOf`) to the access it belongs to.
|
|
395
|
+
*
|
|
396
|
+
* Built once, beside the list it indexes, for the same reason `readsByKey`,
|
|
397
|
+
* `writeEdgeByAccess` and `constructionSitesByTarget` are: the mapping is
|
|
398
|
+
* syntax and never changes as the graph solves, so computing it inside the
|
|
399
|
+
* transfer would be paying a whole-list rebuild on every re-evaluation of
|
|
400
|
+
* every container -- exactly the cost this inversion exists to remove.
|
|
401
|
+
*/
|
|
402
|
+
const unknownReadsByReceiver = new Map();
|
|
403
|
+
for (const entry of unknownReads) {
|
|
404
|
+
let entries = unknownReadsByReceiver.get(entry.receiver);
|
|
405
|
+
if (!entries)
|
|
406
|
+
unknownReadsByReceiver.set(entry.receiver, (entries = []));
|
|
407
|
+
entries.push(entry);
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Writes that go THROUGH some object, grouped by the name they write.
|
|
411
|
+
*
|
|
412
|
+
* Which root a write reaches is a solved question and changes as the graph
|
|
413
|
+
* settles; which NAME it writes is syntax and never changes. Splitting the
|
|
414
|
+
* two lets a descriptor ask about one key without walking every write in
|
|
415
|
+
* the program -- the difference between a scan per key and a scan per
|
|
416
|
+
* (key, root) pair, which is what made three's Material unfinishable.
|
|
417
|
+
*/
|
|
418
|
+
const namedWrites = new Map();
|
|
419
|
+
const computedWrites = [];
|
|
420
|
+
const namedWriteReceivers = new Map();
|
|
421
|
+
/** The receiver queries of every computed write, for `hasUnnamedWrite`'s reverse lookup. */
|
|
422
|
+
const computedWriteReceiverQueries = new Set();
|
|
423
|
+
const writeEdgeByAccess = new Map();
|
|
424
|
+
for (const write of flow.allWrites) {
|
|
425
|
+
const access = write.propertyAccess;
|
|
426
|
+
if (!access)
|
|
427
|
+
continue;
|
|
428
|
+
const key = accessKey(access);
|
|
429
|
+
const edge = { access, receiver: value(access.expression), key, write };
|
|
430
|
+
writeEdgeByAccess.set(access, edge);
|
|
431
|
+
if (key === null) {
|
|
432
|
+
computedWrites.push(write);
|
|
433
|
+
computedWriteReceiverQueries.add(edge.receiver);
|
|
434
|
+
}
|
|
435
|
+
else {
|
|
436
|
+
let entries = namedWrites.get(key);
|
|
437
|
+
if (!entries)
|
|
438
|
+
namedWrites.set(key, (entries = []));
|
|
439
|
+
entries.push(write);
|
|
440
|
+
let edges = namedWriteReceivers.get(key);
|
|
441
|
+
if (!edges)
|
|
442
|
+
namedWriteReceivers.set(key, (edges = []));
|
|
443
|
+
edges.push(edge);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* The declaration an import actually names.
|
|
448
|
+
*
|
|
449
|
+
* `import { inspect } from './m'` declares an ALIAS, whose own declaration
|
|
450
|
+
* is the import specifier -- a name for a name. Every consumer here wants
|
|
451
|
+
* the thing named: the function, the class, the binding in the other file.
|
|
452
|
+
* A barrel re-export is that same question asked twice, so the checker's own
|
|
453
|
+
* alias resolution does the walking rather than one hop of it.
|
|
454
|
+
*
|
|
455
|
+
* A namespace import is deliberately NOT resolved: `import * as api` names
|
|
456
|
+
* an object carrying every export as a slot, which is a different value
|
|
457
|
+
* from any one of them, and collapsing it would let a namespace that
|
|
458
|
+
* escaped look like a callable that did not.
|
|
459
|
+
*/
|
|
460
|
+
const aliasedDeclarationOf = (symbol) => {
|
|
461
|
+
if (!symbol || (symbol.flags & ts.SymbolFlags.Alias) === 0)
|
|
462
|
+
return null;
|
|
463
|
+
const named = symbol.declarations ?? [];
|
|
464
|
+
if (named.length === 0)
|
|
465
|
+
return null;
|
|
466
|
+
if (!named.every((declaration) => ts.isImportSpecifier(declaration) || ts.isImportClause(declaration) || ts.isExportSpecifier(declaration)))
|
|
467
|
+
return null;
|
|
468
|
+
const aliased = checker.getAliasedSymbol(symbol).declarations ?? [];
|
|
469
|
+
return aliased.length === 1 ? aliased[0] : null;
|
|
470
|
+
};
|
|
471
|
+
const slot = (root, key) => {
|
|
472
|
+
let keys = slots.get(root);
|
|
473
|
+
if (!keys)
|
|
474
|
+
slots.set(root, (keys = new Map()));
|
|
475
|
+
let query = keys.get(key);
|
|
476
|
+
if (!query)
|
|
477
|
+
keys.set(key, (query = { kind: 'slot', node: root, key }));
|
|
478
|
+
return query;
|
|
479
|
+
};
|
|
480
|
+
const use = (root, reference) => {
|
|
481
|
+
let refs = uses.get(root);
|
|
482
|
+
if (!refs)
|
|
483
|
+
uses.set(root, (refs = new Map()));
|
|
484
|
+
let query = refs.get(reference);
|
|
485
|
+
if (!query)
|
|
486
|
+
refs.set(reference, (query = { kind: 'use', root, node: reference }));
|
|
487
|
+
return query;
|
|
488
|
+
};
|
|
489
|
+
const publication = (root, container, key) => {
|
|
490
|
+
let containers = publications.get(root);
|
|
491
|
+
if (!containers)
|
|
492
|
+
publications.set(root, (containers = new Map()));
|
|
493
|
+
let keys = containers.get(container);
|
|
494
|
+
if (!keys)
|
|
495
|
+
containers.set(container, (keys = new Map()));
|
|
496
|
+
let query = keys.get(key);
|
|
497
|
+
if (!query)
|
|
498
|
+
keys.set(key, (query = { kind: 'publication', root, node: container, key }));
|
|
499
|
+
return query;
|
|
500
|
+
};
|
|
501
|
+
/** The cell holding the object a binding element takes apart, as a query. */
|
|
502
|
+
const bindingContainer = (element) => {
|
|
503
|
+
const holder = element.parent.parent;
|
|
504
|
+
if (ts.isParameter(holder))
|
|
505
|
+
return holder.dotDotDotToken ? null : nodeQuery('parameter', holder);
|
|
506
|
+
if (ts.isBindingElement(holder))
|
|
507
|
+
return nodeQuery('binding-element', holder);
|
|
508
|
+
return ts.isVariableDeclaration(holder) && holder.initializer ? value(holder.initializer) : null;
|
|
509
|
+
};
|
|
510
|
+
const construction = (root) => exactSourceConstructionOf(checker, flow, root);
|
|
511
|
+
/**
|
|
512
|
+
* What a key names on this construction: its data member, or the method the
|
|
513
|
+
* call through it dispatches to.
|
|
514
|
+
*
|
|
515
|
+
* Both, because a slot is a slot. Asking only for data meant
|
|
516
|
+
* `descriptor(new App(), 'go')` was false, so the simplest class call in the
|
|
517
|
+
* language -- `const a = new App(); a.go()` -- resolved its receiver and then
|
|
518
|
+
* had no slot to read the callee out of.
|
|
519
|
+
*/
|
|
520
|
+
const classMembers = (root, key) => {
|
|
521
|
+
const fact = construction(root);
|
|
522
|
+
if (!fact)
|
|
523
|
+
return null;
|
|
524
|
+
const found = new Set();
|
|
525
|
+
for (const owner of fact.alternatives) {
|
|
526
|
+
const symbol = owner.name ? checker.getSymbolAtLocation(owner.name) : checker.getTypeAtLocation(owner).getSymbol();
|
|
527
|
+
if (!symbol)
|
|
528
|
+
return null;
|
|
529
|
+
const query = { kind: 'declared', receiver: checker.getDeclaredTypeOfSymbol(symbol) };
|
|
530
|
+
const plan = sourceClassDataMemberPlanOf(checker, flow, query, key) ?? sourceClassCallableMemberPlanOf(checker, flow, query, key);
|
|
531
|
+
if (!plan)
|
|
532
|
+
return null;
|
|
533
|
+
for (const declaration of plan.declarations)
|
|
534
|
+
found.add(declaration);
|
|
535
|
+
}
|
|
536
|
+
return [...found];
|
|
537
|
+
};
|
|
538
|
+
const classSymbolOf = (owner) => owner.name ? checker.getSymbolAtLocation(owner.name) : checker.getTypeAtLocation(owner).getSymbol();
|
|
539
|
+
/**
|
|
540
|
+
* A read of `key` on this construction by the key-read plan of every class
|
|
541
|
+
* it can be: the source getter bodies it can enter, the data declarations
|
|
542
|
+
* the key selects, and whether some class lacks the key (the read is then
|
|
543
|
+
* `undefined` there). Null when some class's read is opaque -- a method,
|
|
544
|
+
* a bodiless getter, an unseen member kind. `classMembers` answers first
|
|
545
|
+
* for the all-data and all-method keys; this covers the mixed and the
|
|
546
|
+
* accessor families it refuses.
|
|
547
|
+
*
|
|
548
|
+
* `source.depthTexture` in three's `RenderTarget.copy`: the key is a getter
|
|
549
|
+
* over `this._depthTexture`, and the read was an unmodelled descriptor --
|
|
550
|
+
* which made every value reaching a render target's depth texture opaque,
|
|
551
|
+
* `current.renderTarget = this` in its setter included.
|
|
552
|
+
*/
|
|
553
|
+
const familyReadOf = (root, key) => {
|
|
554
|
+
const fact = construction(root);
|
|
555
|
+
if (!fact || fact.alternatives.length === 0)
|
|
556
|
+
return null;
|
|
557
|
+
const getters = new Set();
|
|
558
|
+
const carriers = new Set();
|
|
559
|
+
let absent = false;
|
|
560
|
+
for (const owner of fact.alternatives) {
|
|
561
|
+
const symbol = classSymbolOf(owner);
|
|
562
|
+
if (!symbol)
|
|
563
|
+
return null;
|
|
564
|
+
const plan = sourceClassKeyReadPlanOf(checker, flow, { kind: 'declared', receiver: checker.getDeclaredTypeOfSymbol(symbol) }, key);
|
|
565
|
+
if (!plan || plan.readBodies === null)
|
|
566
|
+
return null;
|
|
567
|
+
for (const body of plan.readBodies)
|
|
568
|
+
getters.add(body);
|
|
569
|
+
for (const carrier of plan.carriers)
|
|
570
|
+
carriers.add(carrier.declaration);
|
|
571
|
+
if (plan.needsDefaultPrototype)
|
|
572
|
+
absent = true;
|
|
573
|
+
}
|
|
574
|
+
return { getters: [...getters], carriers: [...carriers], absent };
|
|
575
|
+
};
|
|
576
|
+
/**
|
|
577
|
+
* Whether a plain store to `key` on this construction writes DATA on every
|
|
578
|
+
* class it can be -- an own data member, or a key the class lacks and so
|
|
579
|
+
* creates (with `Object.prototype` proven to hold no setter for it, which
|
|
580
|
+
* the key-read plan registers). `this.isWebGLRenderTarget = true` in
|
|
581
|
+
* three's `WebGLRenderTarget` constructor, reached through a
|
|
582
|
+
* `new this.constructor()` whose alternatives include the base class that
|
|
583
|
+
* has no such member: the data-member plan refuses a key absent on any
|
|
584
|
+
* owner, and the store was an unmodelled descriptor.
|
|
585
|
+
*/
|
|
586
|
+
const dataStoreOf = (root, key) => {
|
|
587
|
+
const fact = construction(root);
|
|
588
|
+
if (!fact || fact.alternatives.length === 0)
|
|
589
|
+
return false;
|
|
590
|
+
for (const owner of fact.alternatives) {
|
|
591
|
+
const symbol = classSymbolOf(owner);
|
|
592
|
+
if (!symbol)
|
|
593
|
+
return false;
|
|
594
|
+
const plan = sourceClassKeyReadPlanOf(checker, flow, { kind: 'declared', receiver: checker.getDeclaredTypeOfSymbol(symbol) }, key);
|
|
595
|
+
if (!plan || !plan.codeFree)
|
|
596
|
+
return false;
|
|
597
|
+
}
|
|
598
|
+
return true;
|
|
599
|
+
};
|
|
600
|
+
/** The complete set of source setters a plain store to `key` on this construction runs, or null when that is opaque. */
|
|
601
|
+
const settersOf = (root, key) => {
|
|
602
|
+
const fact = construction(root);
|
|
603
|
+
if (!fact || fact.alternatives.length === 0)
|
|
604
|
+
return null;
|
|
605
|
+
const bodies = new Set();
|
|
606
|
+
for (const owner of fact.alternatives) {
|
|
607
|
+
const symbol = classSymbolOf(owner);
|
|
608
|
+
if (!symbol)
|
|
609
|
+
return null;
|
|
610
|
+
const plan = sourceClassKeyReadPlanOf(checker, flow, { kind: 'declared', receiver: checker.getDeclaredTypeOfSymbol(symbol) }, key);
|
|
611
|
+
if (!plan?.writeBodies || plan.writeBodies.length === 0)
|
|
612
|
+
return null;
|
|
613
|
+
for (const setter of plan.writeBodies)
|
|
614
|
+
bodies.add(setter);
|
|
615
|
+
}
|
|
616
|
+
return [...bodies];
|
|
617
|
+
};
|
|
618
|
+
/**
|
|
619
|
+
* `Object.assign( target, ...sources )` on the intact intrinsic: the one
|
|
620
|
+
* bulk write this graph models. The call evaluates to its target, and each
|
|
621
|
+
* source's own slot values are copied into the target's slots -- three's
|
|
622
|
+
* `RenderTarget` builds its `options` exactly this way.
|
|
623
|
+
*/
|
|
624
|
+
const bulkAssignOf = (call) => {
|
|
625
|
+
if (!isGlobalObjectAssign(checker, call))
|
|
626
|
+
return null;
|
|
627
|
+
const [target, ...sources] = call.arguments;
|
|
628
|
+
if (!target || call.arguments.some(ts.isSpreadElement))
|
|
629
|
+
return null;
|
|
630
|
+
if (deferredIntrinsicProtocolLedgerOf(flow)?.requireMember('Object', 'assign', call) !== true)
|
|
631
|
+
return null;
|
|
632
|
+
return { target, sources };
|
|
633
|
+
};
|
|
634
|
+
/**
|
|
635
|
+
* `Object.defineProperty( target, key, { value } )` on the intact intrinsic:
|
|
636
|
+
* a store of the descriptor's `value` into `key` of the target, running no
|
|
637
|
+
* code of the target's. Three's `Texture` and `Material` define `id` this
|
|
638
|
+
* way in their constructors, and the target's use as the call's argument
|
|
639
|
+
* was a call with no source target -- so every texture was opaque.
|
|
640
|
+
* `defineProperties` is lowered to this form by the source transform and is
|
|
641
|
+
* not modelled here.
|
|
642
|
+
*/
|
|
643
|
+
const definitionPlans = new Map();
|
|
644
|
+
const definitionReceivers = new Map();
|
|
645
|
+
for (const site of flow.calls) {
|
|
646
|
+
const call = site.call;
|
|
647
|
+
if (!ts.isCallExpression(call) || call.arguments.length < 2)
|
|
648
|
+
continue;
|
|
649
|
+
const plan = intrinsicDataDefinitionTargetOf(checker, call.arguments[0], (declaration, argument) => flow.referencesToDeclaration(declaration).every((mention) => mention === declaration.name || mention === argument));
|
|
650
|
+
if (plan === null || plan.member !== 'defineProperty')
|
|
651
|
+
continue;
|
|
652
|
+
definitionPlans.set(call, plan);
|
|
653
|
+
for (const key of plan.keys) {
|
|
654
|
+
let entries = definitionReceivers.get(key);
|
|
655
|
+
if (!entries)
|
|
656
|
+
definitionReceivers.set(key, (entries = []));
|
|
657
|
+
entries.push({ receiver: value(call.arguments[0]) });
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
const definitionOf = (call) => {
|
|
661
|
+
const plan = definitionPlans.get(call);
|
|
662
|
+
if (!plan)
|
|
663
|
+
return null;
|
|
664
|
+
const ledger = deferredIntrinsicProtocolLedgerOf(flow);
|
|
665
|
+
if (ledger?.requireMember(plan.owner, plan.member, call) !== true)
|
|
666
|
+
return null;
|
|
667
|
+
if (plan.descriptorPrototypeKeys.length > 0 && !ledger.requirePrototypeKeys('Object', { names: plan.descriptorPrototypeKeys }, call))
|
|
668
|
+
return null;
|
|
669
|
+
return plan;
|
|
670
|
+
};
|
|
671
|
+
const ownEntries = (root, key) => {
|
|
672
|
+
const entries = [];
|
|
673
|
+
for (const entry of root.properties) {
|
|
674
|
+
if (ts.isSpreadAssignment(entry))
|
|
675
|
+
return null;
|
|
676
|
+
const name = declarationKey(entry.name);
|
|
677
|
+
if (name === null || name === '__proto__')
|
|
678
|
+
return null;
|
|
679
|
+
if (name !== key)
|
|
680
|
+
continue;
|
|
681
|
+
if (ts.isGetAccessorDeclaration(entry) || ts.isSetAccessorDeclaration(entry))
|
|
682
|
+
return null;
|
|
683
|
+
entries.push(entry);
|
|
684
|
+
}
|
|
685
|
+
return entries.length > 0 ? entries : null;
|
|
686
|
+
};
|
|
687
|
+
const descriptor = (root, key) => {
|
|
688
|
+
if (ts.isNewExpression(root))
|
|
689
|
+
return classMembers(root, key) !== null;
|
|
690
|
+
if (ts.isObjectLiteralExpression(root))
|
|
691
|
+
return ownEntries(root, key) !== null;
|
|
692
|
+
if (key === 'length')
|
|
693
|
+
return true;
|
|
694
|
+
// An index the literal does not spell is still the array's own: it reads
|
|
695
|
+
// `undefined` until an index store fills it, and `case 'slot'` reads the
|
|
696
|
+
// stores through `keyReaches`. three's `RenderTarget` starts with
|
|
697
|
+
// `this.textures = []`, fills it by index in the constructor, and its
|
|
698
|
+
// `texture` getter reads `this.textures[ 0 ]`.
|
|
699
|
+
return (key === ELEMENT_KEY || isCanonicalIndex(key)) && !root.elements.some(ts.isSpreadElement);
|
|
700
|
+
};
|
|
701
|
+
const ownersOf = (root) => constructionFrames.receiverOwnersOf(root) ?? [];
|
|
702
|
+
/**
|
|
703
|
+
* Every construction of each owning class, built once.
|
|
704
|
+
*
|
|
705
|
+
* `belongsTo` is decided by `receiverOwnersOf`, which reads the index and
|
|
706
|
+
* nothing a solve revises, so the whole relation is known before the first
|
|
707
|
+
* query. Two transfers were rediscovering it by walking every call in the
|
|
708
|
+
* program per query.
|
|
709
|
+
*/
|
|
710
|
+
/**
|
|
711
|
+
* The calls that can dispatch to a body, decided by NAME before any solving.
|
|
712
|
+
*
|
|
713
|
+
* A call's targets are its callee's VALUES, and a body enters a value set
|
|
714
|
+
* only as the name it is declared or stored under: `value` of a property
|
|
715
|
+
* access reads `slot(root, key)`, and a store into that slot is spelled with
|
|
716
|
+
* the same key, so a callee spelling some OTHER name cannot produce this body
|
|
717
|
+
* however the graph solves. Three transfers -- a parameter's frame, a
|
|
718
|
+
* receiver's frame and a completion's use -- asked anyway, by observing
|
|
719
|
+
* `targets` of EVERY call in the program, per query. That forced the entire
|
|
720
|
+
* program's dispatch graph from each of them and was the three.js app's compile time:
|
|
721
|
+
* the fan-out, not any one answer. `callersOf` below is the fix for the
|
|
722
|
+
* fan-out; this function is unchanged and still returns the same
|
|
723
|
+
* whole-program list for an unnamed body -- only how many times that list
|
|
724
|
+
* gets walked has changed.
|
|
725
|
+
*
|
|
726
|
+
* `unspelledCallees` is every call this layer cannot name -- a computed
|
|
727
|
+
* element access, a call result, anything but an identifier or a static key
|
|
728
|
+
* -- and is a candidate for every body, so the restriction only ever removes
|
|
729
|
+
* calls that were already impossible. A body whose own names cannot be
|
|
730
|
+
* enumerated falls back to the whole list rather than to a guess.
|
|
731
|
+
*
|
|
732
|
+
* That fallback fires for the three.js app's ~156 anonymous bodies handed straight to
|
|
733
|
+
* a host/intrinsic callee -- `[].map(x => ...)`, `setTimeout(() => ...)` --
|
|
734
|
+
* because `ValueWrite.naming` only spells "the parameter's name for a call
|
|
735
|
+
* argument" when the callee is a SOURCE function; a host callee's parameter
|
|
736
|
+
* is never spelled, so `namesOf` meets an unnamed write and returns null.
|
|
737
|
+
* It is tempting to narrow this to just the receiving call site, since the
|
|
738
|
+
* body is never stored under a name and so cannot be READ back and called
|
|
739
|
+
* from anywhere this layer's write census would see. That is not the same
|
|
740
|
+
* as proving no OTHER call reaches it: a host function is free to retain the
|
|
741
|
+
* callback it was handed and invoke it again later through a binding this
|
|
742
|
+
* layer never inventories (a memoized comparator, a registered listener), so
|
|
743
|
+
* "receiving call site only" is not provably a superset. Left as the whole
|
|
744
|
+
* list rather than landing an unsound narrowing.
|
|
745
|
+
*/
|
|
746
|
+
let calleeNames = null;
|
|
747
|
+
let unspelledCallees = [];
|
|
748
|
+
const buildCalleeIndex = () => {
|
|
749
|
+
if (calleeNames !== null)
|
|
750
|
+
return calleeNames;
|
|
751
|
+
const index = new Map();
|
|
752
|
+
const unspelled = [];
|
|
753
|
+
for (const site of flow.calls) {
|
|
754
|
+
// `new F( ... )` is indexed under F's spelling too: on a plain function it
|
|
755
|
+
// is a caller frame (`case 'callers'`), which the construction domain
|
|
756
|
+
// never names.
|
|
757
|
+
if (!ts.isCallExpression(site.call) && !ts.isNewExpression(site.call))
|
|
758
|
+
continue;
|
|
759
|
+
const callee = site.operands ? unwrapErasedExpression(site.operands.callee) : null;
|
|
760
|
+
const name = callee
|
|
761
|
+
? ts.isIdentifier(callee)
|
|
762
|
+
? callee.text
|
|
763
|
+
: ts.isPropertyAccessExpression(callee) || ts.isElementAccessExpression(callee)
|
|
764
|
+
? accessKey(callee)
|
|
765
|
+
: null
|
|
766
|
+
: null;
|
|
767
|
+
if (name === null)
|
|
768
|
+
unspelled.push(site);
|
|
769
|
+
else {
|
|
770
|
+
let held = index.get(name);
|
|
771
|
+
if (!held)
|
|
772
|
+
index.set(name, (held = []));
|
|
773
|
+
held.push(site);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
unspelledCallees = unspelled;
|
|
777
|
+
calleeNames = index;
|
|
778
|
+
return index;
|
|
779
|
+
};
|
|
780
|
+
/** Every name a body can be reached by: its own, and every cell it is stored in. */
|
|
781
|
+
const bodyNames = new Map();
|
|
782
|
+
const namesOf = (body) => {
|
|
783
|
+
const known = bodyNames.get(body);
|
|
784
|
+
if (known !== undefined)
|
|
785
|
+
return known;
|
|
786
|
+
const names = new Set();
|
|
787
|
+
const declared = ts.getNameOfDeclaration(body);
|
|
788
|
+
if (declared) {
|
|
789
|
+
const key = ts.isIdentifier(declared) || ts.isStringLiteralLike(declared) ? declared.text : declarationKey(declared);
|
|
790
|
+
if (key === null) {
|
|
791
|
+
bodyNames.set(body, null);
|
|
792
|
+
return null;
|
|
793
|
+
}
|
|
794
|
+
names.add(key);
|
|
795
|
+
}
|
|
796
|
+
// A function expression or arrow is reached by whatever holds it. Every
|
|
797
|
+
// write whose VALUE is this body names one such cell; a write this layer
|
|
798
|
+
// cannot name means the body can be reached by a name it cannot enumerate.
|
|
799
|
+
if (!declared || ts.isFunctionExpression(body) || ts.isArrowFunction(body))
|
|
800
|
+
for (const write of flow.allWrites) {
|
|
801
|
+
if (write.value === undefined || write.value === null || unwrapErasedExpression(write.value) !== body)
|
|
802
|
+
continue;
|
|
803
|
+
const named = write.member ?? (write.naming && ts.isIdentifier(write.naming) ? write.naming.text : null);
|
|
804
|
+
if (named === null) {
|
|
805
|
+
bodyNames.set(body, null);
|
|
806
|
+
return null;
|
|
807
|
+
}
|
|
808
|
+
names.add(named);
|
|
809
|
+
}
|
|
810
|
+
const answer = names.size > 0 ? [...names] : null;
|
|
811
|
+
bodyNames.set(body, answer);
|
|
812
|
+
return answer;
|
|
813
|
+
};
|
|
814
|
+
/** The declared instance type of a class member's holder; null for anything a stated type cannot exclude callers of. */
|
|
815
|
+
const ownerInstanceTypeOf = (owner) => {
|
|
816
|
+
if (!ts.isClassElement(owner) || !ts.isClassLike(owner.parent))
|
|
817
|
+
return null;
|
|
818
|
+
if ((ts.getCombinedModifierFlags(owner) & ts.ModifierFlags.Static) !== 0)
|
|
819
|
+
return null;
|
|
820
|
+
const symbol = classSymbolOf(owner.parent);
|
|
821
|
+
if (!symbol)
|
|
822
|
+
return null;
|
|
823
|
+
const declared = checker.getDeclaredTypeOfSymbol(symbol);
|
|
824
|
+
return declared.isClassOrInterface() ? declared : null;
|
|
825
|
+
};
|
|
826
|
+
const statedTypeAdmits = (stated, instance) => {
|
|
827
|
+
if ((stated.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown | ts.TypeFlags.TypeParameter)) !== 0)
|
|
828
|
+
return true;
|
|
829
|
+
if (stated.isUnion())
|
|
830
|
+
return stated.types.some((member) => statedTypeAdmits(member, instance));
|
|
831
|
+
if ((stated.flags & ts.TypeFlags.Object) === 0)
|
|
832
|
+
return false;
|
|
833
|
+
return checker.isTypeAssignableTo(instance, stated);
|
|
834
|
+
};
|
|
835
|
+
const candidateCallersOf = (body) => {
|
|
836
|
+
const index = buildCalleeIndex();
|
|
837
|
+
const names = namesOf(body);
|
|
838
|
+
if (names === null)
|
|
839
|
+
return flow.calls;
|
|
840
|
+
// `unspelledCallees` is a whole-program list and this runs once per
|
|
841
|
+
// TRANSFER, not once per program -- `[...unspelledCallees]` copied it on
|
|
842
|
+
// every call just to push a handful of named matches on top. `both`
|
|
843
|
+
// hands it back by reference when there is nothing to add.
|
|
844
|
+
const named = [];
|
|
845
|
+
for (const name of names)
|
|
846
|
+
for (const site of index.get(name) ?? [])
|
|
847
|
+
named.push(site);
|
|
848
|
+
return both(unspelledCallees, named);
|
|
849
|
+
};
|
|
850
|
+
/**
|
|
851
|
+
* The calls that actually target this body -- the same question
|
|
852
|
+
* `candidateCallersOf(owner)` plus a `targets` filter answers, asked ONCE
|
|
853
|
+
* per body instead of once per consumer.
|
|
854
|
+
*
|
|
855
|
+
* A parameter's frame, a receiver's frame and a return statement's
|
|
856
|
+
* completion each used to run that filter themselves, inside their own
|
|
857
|
+
* transfer: `for (const site of candidateCallersOf(owner)) if (...
|
|
858
|
+
* observe(targets(site.call)).has(owner)) ...`. For a NAMED body
|
|
859
|
+
* `candidateCallersOf` is already small and the repetition was cheap. For
|
|
860
|
+
* one of the three.js app's ~156 anonymous bodies handed to a host callee it is
|
|
861
|
+
* `flow.calls` -- 5,543 sites -- and every parameter of such a body, plus
|
|
862
|
+
* its receiver, plus every one of its return statements, walked and
|
|
863
|
+
* `observe`d that entire list separately. 844 `parameter` states alone
|
|
864
|
+
* carried 605,547 observe edges this way, 717 per state on average, almost
|
|
865
|
+
* all of it the same whole-program filter re-run for the same owner.
|
|
866
|
+
*
|
|
867
|
+
* This is `unknownReadsOf`'s shape exactly: a whole-program list, tested
|
|
868
|
+
* against one thing, wanted by many consumers of that one thing. Giving the
|
|
869
|
+
* filter its own node means the solver runs it once per owner and caches
|
|
870
|
+
* the (small) result; every consumer then pays one `observe` edge to reach
|
|
871
|
+
* it, discovery-only exactly as the inline `observe(targets(...))` was, so
|
|
872
|
+
* none of them is required to wait for `callers` to settle before they
|
|
873
|
+
* report what they already know.
|
|
874
|
+
*/
|
|
875
|
+
//
|
|
876
|
+
// Consumers `read` it, not `observe` it: the node carries the closure
|
|
877
|
+
// requirements that make its caller set complete, and a frame built from
|
|
878
|
+
// the discovered calls alone would be complete over an open caller set.
|
|
879
|
+
const callersOf = (owner) => nodeQuery('callers', owner);
|
|
880
|
+
let constructionsByOwner = null;
|
|
881
|
+
/**
|
|
882
|
+
* Every site that constructs: a `new`, or a `super(...)` standing in for one.
|
|
883
|
+
*
|
|
884
|
+
* The predicate is syntax and never moves, but it was asked inside the
|
|
885
|
+
* `parameter` transfer, so proving one parameter's frame walked every call in
|
|
886
|
+
* the program -- and did it again on every re-evaluation.
|
|
887
|
+
*
|
|
888
|
+
* Worse than the walk was what the walk SUBSCRIBED to. `construct-targets`
|
|
889
|
+
* neither reads nor observes: it is `constructionFrames.targetsOf` and
|
|
890
|
+
* nothing else, fixed before the first transfer runs. So the 854 edges a
|
|
891
|
+
* parameter registered were edges to constants -- they could never carry a
|
|
892
|
+
* wake-up, and they existed only to force the leaf's state into being. Asked
|
|
893
|
+
* the other way round, as an index from target to the sites that construct
|
|
894
|
+
* it, the answer is the same set and the edges are none.
|
|
895
|
+
*/
|
|
896
|
+
const NO_READS = [];
|
|
897
|
+
const NO_SITES = [];
|
|
898
|
+
let constructionSitesByTarget = null;
|
|
899
|
+
const constructionSitesTargeting = (owner) => {
|
|
900
|
+
if (constructionSitesByTarget === null) {
|
|
901
|
+
constructionSitesByTarget = new Map();
|
|
902
|
+
for (const site of flow.calls) {
|
|
903
|
+
if (!ts.isNewExpression(site.call) && site.operands.dispatch.kind !== 'super-constructor')
|
|
904
|
+
continue;
|
|
905
|
+
for (const target of constructionFrames.targetsOf(site.call) ?? []) {
|
|
906
|
+
let held = constructionSitesByTarget.get(target);
|
|
907
|
+
if (!held)
|
|
908
|
+
constructionSitesByTarget.set(target, (held = []));
|
|
909
|
+
held.push(site);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
return constructionSitesByTarget.get(owner) ?? NO_SITES;
|
|
914
|
+
};
|
|
915
|
+
/**
|
|
916
|
+
* The `new` sites the construction domain cannot place, found once. A
|
|
917
|
+
* class member's instances are `constructionsOf` its family, and that list
|
|
918
|
+
* is complete only while none of these names a family class: `new Owner()`
|
|
919
|
+
* after `Owner.prototype.take = replacement` is an Owner the domain refused,
|
|
920
|
+
* not a non-Owner. Only the stated instance type can say which class such a
|
|
921
|
+
* site names; a site typed `any` names none, and the class value's own
|
|
922
|
+
* closure (the family inventory) already says it flows into no table.
|
|
923
|
+
*/
|
|
924
|
+
let opaqueConstructions = null;
|
|
925
|
+
const opaqueConstructionsOf = () => {
|
|
926
|
+
if (opaqueConstructions === null) {
|
|
927
|
+
const found = [];
|
|
928
|
+
for (const site of flow.calls)
|
|
929
|
+
if (ts.isNewExpression(site.call) && constructionFrames.receiverOwnersOf(site.call) === null)
|
|
930
|
+
found.push(site.call);
|
|
931
|
+
opaqueConstructions = found;
|
|
932
|
+
}
|
|
933
|
+
return opaqueConstructions;
|
|
934
|
+
};
|
|
935
|
+
const constructionsOf = (owner) => {
|
|
936
|
+
if (constructionsByOwner === null) {
|
|
937
|
+
constructionsByOwner = new Map();
|
|
938
|
+
for (const site of flow.calls) {
|
|
939
|
+
if (!ts.isNewExpression(site.call))
|
|
940
|
+
continue;
|
|
941
|
+
for (const candidate of ownersOf(site.call)) {
|
|
942
|
+
let held = constructionsByOwner.get(candidate);
|
|
943
|
+
if (!held)
|
|
944
|
+
constructionsByOwner.set(candidate, (held = []));
|
|
945
|
+
held.push(site.call);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
const own = constructionsByOwner.get(owner) ?? [];
|
|
950
|
+
const inherited = owner.parent ? (constructionsByOwner.get(owner.parent) ?? []) : [];
|
|
951
|
+
return inherited.length === 0 ? own : own.length === 0 ? inherited : [...new Set([...own, ...inherited])];
|
|
952
|
+
};
|
|
953
|
+
const isCallableValue = (node) => isBody(node);
|
|
954
|
+
/**
|
|
955
|
+
* The enumerated expression, when this key expression is a `for-in` head.
|
|
956
|
+
*
|
|
957
|
+
* `for ( const key in values ) this[ key ] = values[ key ]` -- three's
|
|
958
|
+
* `setValues`, and the three.js app's single largest computed-store family. `key`
|
|
959
|
+
* names no VALUE the graph can carry, so `value(key)` correctly answers
|
|
960
|
+
* nothing and the access refuses on an unresolved key. What the graph CAN
|
|
961
|
+
* answer is what `values` holds, and an object literal spells its own keys.
|
|
962
|
+
*
|
|
963
|
+
* The head is a fresh binding each round. A body that assigns to it is
|
|
964
|
+
* naming something other than the enumerated key, so any write disqualifies
|
|
965
|
+
* the whole head rather than being folded into the set.
|
|
966
|
+
*/
|
|
967
|
+
const enumerationHeads = new Map();
|
|
968
|
+
const enumerationHeadOf = (expression) => {
|
|
969
|
+
const known = enumerationHeads.get(expression);
|
|
970
|
+
if (known !== undefined)
|
|
971
|
+
return known;
|
|
972
|
+
const answer = enumerationHeadUncached(expression);
|
|
973
|
+
enumerationHeads.set(expression, answer);
|
|
974
|
+
return answer;
|
|
975
|
+
};
|
|
976
|
+
const enumerationHeadUncached = (expression) => {
|
|
977
|
+
const reference = unwrapErasedExpression(expression);
|
|
978
|
+
if (!ts.isIdentifier(reference))
|
|
979
|
+
return null;
|
|
980
|
+
const declaration = flow.targetOf(reference)?.declaration;
|
|
981
|
+
if (!declaration || !ts.isVariableDeclaration(declaration) || !ts.isIdentifier(declaration.name))
|
|
982
|
+
return null;
|
|
983
|
+
const list = declaration.parent;
|
|
984
|
+
if (!ts.isVariableDeclarationList(list) || list.declarations.length !== 1)
|
|
985
|
+
return null;
|
|
986
|
+
const statement = list.parent;
|
|
987
|
+
if (!ts.isForInStatement(statement) || statement.initializer !== list)
|
|
988
|
+
return null;
|
|
989
|
+
if (flow.writesToDeclaration(declaration).some((write) => write.edge !== 'iteration-binding'))
|
|
990
|
+
return null;
|
|
991
|
+
return statement.expression;
|
|
992
|
+
};
|
|
993
|
+
/** The own keys an object literal spells, or nothing when a spread or a
|
|
994
|
+
* computed name means the literal does not spell them all. Under-reporting a
|
|
995
|
+
* key set is not a smaller answer, it is a wrong one: it would leave a slot
|
|
996
|
+
* the program actually stores into looking untouched. */
|
|
997
|
+
const literalSpelledKeys = (root) => {
|
|
998
|
+
if (!ts.isObjectLiteralExpression(root))
|
|
999
|
+
return null;
|
|
1000
|
+
const keys = [];
|
|
1001
|
+
for (const property of root.properties) {
|
|
1002
|
+
if (ts.isSpreadAssignment(property))
|
|
1003
|
+
return null;
|
|
1004
|
+
const key = declarationKey(property.name);
|
|
1005
|
+
if (key === null || key === '__proto__')
|
|
1006
|
+
return null;
|
|
1007
|
+
keys.push(key);
|
|
1008
|
+
}
|
|
1009
|
+
return keys;
|
|
1010
|
+
};
|
|
1011
|
+
/**
|
|
1012
|
+
* The keys a `for-in` head binds: the own keys of every root the enumerated
|
|
1013
|
+
* expression can hold. The dependency goes into the graph -- `value(...)` on
|
|
1014
|
+
* the enumerated expression -- rather than to a second key-set authority
|
|
1015
|
+
* asking the same question a different way.
|
|
1016
|
+
*
|
|
1017
|
+
* `Object.prototype` must carry no enumerable string key of its own, or the
|
|
1018
|
+
* head walks keys no root spells. That obligation is deferred to the ledger,
|
|
1019
|
+
* exactly as the legacy key-set authority defers it; with no ledger to carry
|
|
1020
|
+
* it there is nothing to discharge it later, so the enumeration refuses.
|
|
1021
|
+
*/
|
|
1022
|
+
const enumeratedKeysOf = (access, enumerated, read, fail, sealing, ownKeysOf) => {
|
|
1023
|
+
if (!deferredIntrinsicProtocolLedgerOf(flow)?.require('Object', access)) {
|
|
1024
|
+
fail('undeferrable-enumeration-protocol', access);
|
|
1025
|
+
return [];
|
|
1026
|
+
}
|
|
1027
|
+
const keys = new Set();
|
|
1028
|
+
const roots = read(value(enumerated));
|
|
1029
|
+
for (const root of roots) {
|
|
1030
|
+
// `for ( key in undefined )` binds nothing: three's `Material` runs
|
|
1031
|
+
// `setValues( parameters )` for a bare `new MeshDepthMaterial()`.
|
|
1032
|
+
if (isNullish(root))
|
|
1033
|
+
continue;
|
|
1034
|
+
const own = ownKeysOf(root);
|
|
1035
|
+
if (own === null) {
|
|
1036
|
+
fail('unspelled-enumeration-root', isObjectRoot(root) ? root : enumerated);
|
|
1037
|
+
continue;
|
|
1038
|
+
}
|
|
1039
|
+
// A literal spells its keys only while nothing else adds one, and the
|
|
1040
|
+
// graph already refuses a store to a key an object literal does not
|
|
1041
|
+
// declare -- so the root's own closure IS that proof. It is required
|
|
1042
|
+
// here rather than assumed: an under-reported key set leaves a slot the
|
|
1043
|
+
// program actually writes looking untouched, which is a wrong answer
|
|
1044
|
+
// wearing a smaller one's clothes.
|
|
1045
|
+
read(closure(root));
|
|
1046
|
+
for (const key of own)
|
|
1047
|
+
keys.add(key);
|
|
1048
|
+
}
|
|
1049
|
+
if (sealing && roots.size === 0)
|
|
1050
|
+
fail('unresolved-enumeration', enumerated);
|
|
1051
|
+
return [...keys];
|
|
1052
|
+
};
|
|
1053
|
+
const define = (query) => {
|
|
1054
|
+
let finalCauses = [];
|
|
1055
|
+
let finalGrounding = [];
|
|
1056
|
+
let seeded = false;
|
|
1057
|
+
const ownKeysAnswers = new Map();
|
|
1058
|
+
const evaluate = (required, observe, holdersOf, sealing) => {
|
|
1059
|
+
const facts = new Set();
|
|
1060
|
+
const causes = [];
|
|
1061
|
+
const grounding = new Set();
|
|
1062
|
+
const fail = (reason, node = query.node) => {
|
|
1063
|
+
causes.push({ reason, node });
|
|
1064
|
+
if (refusalDebug) {
|
|
1065
|
+
const file = node.getSourceFile();
|
|
1066
|
+
const { line } = file.getLineAndCharacterOfPosition(node.getStart(file));
|
|
1067
|
+
const text = node.getText(file).replace(/\s+/g, ' ');
|
|
1068
|
+
console.error(`[SESSION-REFUSAL] ${reason} ${query.kind} ${file.fileName.split('/').pop()}:${line + 1} ${text.slice(0, 90)}`);
|
|
1069
|
+
}
|
|
1070
|
+
};
|
|
1071
|
+
// No edge ledger of our own: the solver records every required edge, and
|
|
1072
|
+
// `obligations` walks its copy. Keeping a second one cost a map lookup
|
|
1073
|
+
// and a set insert on every edge read in the program -- the largest
|
|
1074
|
+
// single self-time entry in the three.js app's profile once the state lookup
|
|
1075
|
+
// beside it was gone -- and a duplicate of the whole proof graph in a
|
|
1076
|
+
// solve that was already running out of heap.
|
|
1077
|
+
const read = required;
|
|
1078
|
+
const carry = (dependency) => {
|
|
1079
|
+
grounding.add(dependency);
|
|
1080
|
+
return read(dependency);
|
|
1081
|
+
};
|
|
1082
|
+
const add = (values) => {
|
|
1083
|
+
for (const held of values)
|
|
1084
|
+
facts.add(held);
|
|
1085
|
+
};
|
|
1086
|
+
/**
|
|
1087
|
+
* A member call whose receiver holds nothing but `null`/`undefined`
|
|
1088
|
+
* throws at the lookup: no target, no frame, no completion -- and no
|
|
1089
|
+
* refusal, since the throw is the program's own answer. three's
|
|
1090
|
+
* `RenderTarget.copy` runs `source.depthTexture.clone()` under a guard
|
|
1091
|
+
* this graph does not read, and every value `depthTexture` holds in
|
|
1092
|
+
* the fixture is `null`.
|
|
1093
|
+
*/
|
|
1094
|
+
const lookupThrows = (call) => {
|
|
1095
|
+
const site = sites.get(call);
|
|
1096
|
+
if (!site?.operands || site.explicitThis || site.operands.dispatch.kind !== 'member' || site.operands.receiver === null)
|
|
1097
|
+
return false;
|
|
1098
|
+
const held = read(value(site.operands.receiver));
|
|
1099
|
+
return held.size > 0 && [...held].every(isNullish);
|
|
1100
|
+
};
|
|
1101
|
+
/**
|
|
1102
|
+
* The keys a named write through a root adds to it.
|
|
1103
|
+
*
|
|
1104
|
+
* `const values = { minFilter: 1, flipY: false }; values.wrapS = w` --
|
|
1105
|
+
* three's RenderTarget. The literal SPELLS two keys and the program
|
|
1106
|
+
* gives it a third, so the spelling is not the key set. `case 'slot'`
|
|
1107
|
+
* already reads those writes; without them in the descriptor the very
|
|
1108
|
+
* slot they write was refused before it could be read -- the descriptor
|
|
1109
|
+
* answering about the LITERAL when it was asked about the OBJECT.
|
|
1110
|
+
*
|
|
1111
|
+
* Only literals are asked. A class instance's absent keys are a chain
|
|
1112
|
+
* proof rather than a spelling, and `this[ key ] = v` inside a method
|
|
1113
|
+
* would otherwise leave every instance's key set unknown.
|
|
1114
|
+
*/
|
|
1115
|
+
/**
|
|
1116
|
+
* The keys a named write through a root adds to it.
|
|
1117
|
+
*
|
|
1118
|
+
* `const values = { minFilter: 1, flipY: false }; values.wrapS = w` --
|
|
1119
|
+
* three's RenderTarget. The literal SPELLS two keys and the program
|
|
1120
|
+
* gives it a third, so the spelling is not the key set. `case 'slot'`
|
|
1121
|
+
* already reads those writes; without them in the descriptor the very
|
|
1122
|
+
* slot they write was refused before it could be read -- the descriptor
|
|
1123
|
+
* answering about the LITERAL when it was asked about the OBJECT.
|
|
1124
|
+
*
|
|
1125
|
+
* Only literals are asked. A class instance's absent keys are a chain
|
|
1126
|
+
* proof rather than a spelling, and `this[ key ] = v` inside a method
|
|
1127
|
+
* would otherwise leave every instance's key set unknown.
|
|
1128
|
+
*
|
|
1129
|
+
* This scan is asked about ONE key's writes, or about the computed ones,
|
|
1130
|
+
* and stays here for that reason. Routing it through `written-keys` was
|
|
1131
|
+
* measured and reverted: that query depends on every named write in the
|
|
1132
|
+
* program, so a per-key descriptor question -- asked about most roots in
|
|
1133
|
+
* the program, not just the enumerated ones -- pulled a whole-program
|
|
1134
|
+
* dependency set into each of them and put the three.js app into GC death at
|
|
1135
|
+
* 8m31s. A whole-program walk becomes a query only where the consumer
|
|
1136
|
+
* genuinely wants the whole program, which here is enumeration alone.
|
|
1137
|
+
*/
|
|
1138
|
+
const writesTo = (root, edges) => edges.some((edge) => observe(edge.receiver).has(root));
|
|
1139
|
+
/**
|
|
1140
|
+
* A write under a key this graph cannot name leaves the key set unknown
|
|
1141
|
+
* -- reported as unknown, never as a smaller set. Asked in reverse:
|
|
1142
|
+
* which states hold this root, and is any of them a computed write's
|
|
1143
|
+
* receiver -- one lookup per root instead of observing all of the three.js app's
|
|
1144
|
+
* computed-write receivers per root per re-evaluation. `holdersOf`
|
|
1145
|
+
* subscribes this node to the root's future holders exactly as the
|
|
1146
|
+
* observe edges did, and the receivers are seeded below so each exists.
|
|
1147
|
+
*/
|
|
1148
|
+
const unknownKeys = new Map();
|
|
1149
|
+
const holderStampLists = [];
|
|
1150
|
+
const hasUnnamedWrite = (root) => {
|
|
1151
|
+
const known = unknownKeys.get(root);
|
|
1152
|
+
if (known !== undefined)
|
|
1153
|
+
return known;
|
|
1154
|
+
let found = false;
|
|
1155
|
+
let count = 0;
|
|
1156
|
+
for (const holder of holdersOf(root)) {
|
|
1157
|
+
count++;
|
|
1158
|
+
if (computedWriteReceiverQueries.has(holder))
|
|
1159
|
+
found = true;
|
|
1160
|
+
}
|
|
1161
|
+
for (const stamps of holderStampLists)
|
|
1162
|
+
stamps.push([root, count]);
|
|
1163
|
+
unknownKeys.set(root, found);
|
|
1164
|
+
return found;
|
|
1165
|
+
};
|
|
1166
|
+
/** Every cell a root has: what it spells, plus what was written into it. */
|
|
1167
|
+
const ownKeys = new Map();
|
|
1168
|
+
const activeOwnKeys = new Set();
|
|
1169
|
+
const stampLists = [];
|
|
1170
|
+
const watched = (dependency) => {
|
|
1171
|
+
const held = observe(dependency);
|
|
1172
|
+
for (const stamps of stampLists)
|
|
1173
|
+
stamps.push([held, held.size]);
|
|
1174
|
+
return held;
|
|
1175
|
+
};
|
|
1176
|
+
const holdersUnchanged = (stamps) => {
|
|
1177
|
+
for (const [root, count] of stamps) {
|
|
1178
|
+
let now = 0;
|
|
1179
|
+
for (const _holder of holdersOf(root))
|
|
1180
|
+
now++;
|
|
1181
|
+
if (now !== count)
|
|
1182
|
+
return false;
|
|
1183
|
+
}
|
|
1184
|
+
return true;
|
|
1185
|
+
};
|
|
1186
|
+
const ownKeysOf = (root) => {
|
|
1187
|
+
const known = ownKeys.get(root);
|
|
1188
|
+
if (known !== undefined)
|
|
1189
|
+
return known;
|
|
1190
|
+
// A bulk assign whose source's keys come back to this root -- two
|
|
1191
|
+
// records merged into each other -- names no finite set on the way
|
|
1192
|
+
// round; the re-entry is unknown, and only the outer answer is kept.
|
|
1193
|
+
if (activeOwnKeys.has(root))
|
|
1194
|
+
return null;
|
|
1195
|
+
const kept = ownKeysAnswers.get(root);
|
|
1196
|
+
if (kept !== undefined && kept.stamps.every(([held, size]) => held.size === size) && holdersUnchanged(kept.holderStamps)) {
|
|
1197
|
+
for (const stamps of stampLists)
|
|
1198
|
+
stamps.push(...kept.stamps);
|
|
1199
|
+
for (const stamps of holderStampLists)
|
|
1200
|
+
stamps.push(...kept.holderStamps);
|
|
1201
|
+
ownKeys.set(root, kept.keys);
|
|
1202
|
+
return kept.keys;
|
|
1203
|
+
}
|
|
1204
|
+
activeOwnKeys.add(root);
|
|
1205
|
+
const stamps = [];
|
|
1206
|
+
const holderStamps = [];
|
|
1207
|
+
stampLists.push(stamps);
|
|
1208
|
+
holderStampLists.push(holderStamps);
|
|
1209
|
+
try {
|
|
1210
|
+
const answer = ownKeysUncached(root);
|
|
1211
|
+
ownKeys.set(root, answer);
|
|
1212
|
+
ownKeysAnswers.set(root, { keys: answer, stamps, holderStamps });
|
|
1213
|
+
return answer;
|
|
1214
|
+
}
|
|
1215
|
+
finally {
|
|
1216
|
+
stampLists.pop();
|
|
1217
|
+
holderStampLists.pop();
|
|
1218
|
+
activeOwnKeys.delete(root);
|
|
1219
|
+
}
|
|
1220
|
+
};
|
|
1221
|
+
const ownKeysUncached = (root) => {
|
|
1222
|
+
const spelled = literalSpelledKeys(root);
|
|
1223
|
+
if (spelled === null || !isObjectRoot(root))
|
|
1224
|
+
return spelled;
|
|
1225
|
+
if (hasUnnamedWrite(root))
|
|
1226
|
+
return null;
|
|
1227
|
+
const keys = new Set(spelled);
|
|
1228
|
+
for (const stored of watched(closure(root))) {
|
|
1229
|
+
if (ts.isCallExpression(stored) && definitionPlans.has(stored)) {
|
|
1230
|
+
if (watched(value(stored.arguments[0])).has(root))
|
|
1231
|
+
for (const key of definitionPlans.get(stored).keys)
|
|
1232
|
+
keys.add(key);
|
|
1233
|
+
continue;
|
|
1234
|
+
}
|
|
1235
|
+
if (ts.isCallExpression(stored)) {
|
|
1236
|
+
// A bulk assign onto this root gives it every key its sources have.
|
|
1237
|
+
const bulk = bulkAssignOf(stored);
|
|
1238
|
+
if (bulk === null || !watched(value(bulk.target)).has(root))
|
|
1239
|
+
continue;
|
|
1240
|
+
for (const source of bulk.sources)
|
|
1241
|
+
for (const held of watched(value(source))) {
|
|
1242
|
+
if (held === undefinedValue || held.kind === ts.SyntaxKind.NullKeyword || ts.isVoidExpression(held))
|
|
1243
|
+
continue;
|
|
1244
|
+
const sourceKeys = ownKeysOf(held);
|
|
1245
|
+
if (sourceKeys === null)
|
|
1246
|
+
return null;
|
|
1247
|
+
for (const written of sourceKeys)
|
|
1248
|
+
keys.add(written);
|
|
1249
|
+
}
|
|
1250
|
+
continue;
|
|
1251
|
+
}
|
|
1252
|
+
if (!ts.isPropertyAccessExpression(stored) && !ts.isElementAccessExpression(stored))
|
|
1253
|
+
continue;
|
|
1254
|
+
const written = accessKey(stored);
|
|
1255
|
+
if (written !== null)
|
|
1256
|
+
keys.add(written);
|
|
1257
|
+
}
|
|
1258
|
+
return [...keys];
|
|
1259
|
+
};
|
|
1260
|
+
const descriptorOf = (root, key) => descriptor(root, key) ||
|
|
1261
|
+
(ts.isObjectLiteralExpression(root) && writesTo(root, namedWriteReceivers.get(key) ?? [])) ||
|
|
1262
|
+
writesTo(root, definitionReceivers.get(key) ?? []);
|
|
1263
|
+
/**
|
|
1264
|
+
* Every element key of the arrays an access could be reaching.
|
|
1265
|
+
*
|
|
1266
|
+
* `this.textures[ i ]` -- three's RenderTarget, and every loop over a
|
|
1267
|
+
* field array. The index is a counter no value bounds, so asking what
|
|
1268
|
+
* `i` HOLDS correctly answers nothing; but the access still names some
|
|
1269
|
+
* element, and an array literal spells all of them. Their union is a
|
|
1270
|
+
* true superset where a single key is not available. `length` is not an
|
|
1271
|
+
* element and is not in it, and nothing is inferred from list order --
|
|
1272
|
+
* the keys are the positions the literal actually has.
|
|
1273
|
+
*
|
|
1274
|
+
* The container is read WITHOUT requiring it: the caller requires it
|
|
1275
|
+
* anyway, and a bounded index must stay exact rather than widening to
|
|
1276
|
+
* the whole array, so this answers only where no key is available.
|
|
1277
|
+
*/
|
|
1278
|
+
const numericIndex = (index) => {
|
|
1279
|
+
const numeric = (type) => type.isUnion() ? type.types.every(numeric) : (type.flags & ts.TypeFlags.NumberLike) !== 0 && !type.isNumberLiteral();
|
|
1280
|
+
return numeric(checker.getTypeAtLocation(index));
|
|
1281
|
+
};
|
|
1282
|
+
const arrayElementKeys = (access) => {
|
|
1283
|
+
const roots = observe(value(access.expression));
|
|
1284
|
+
if (roots.size === 0)
|
|
1285
|
+
return null;
|
|
1286
|
+
for (const root of roots)
|
|
1287
|
+
if (!ts.isArrayLiteralExpression(root) || root.elements.some(ts.isSpreadElement))
|
|
1288
|
+
return null;
|
|
1289
|
+
const bounded = observe(value(access.argumentExpression));
|
|
1290
|
+
if (bounded.size > 0 && [...bounded].every((candidate) => ts.isStringLiteralLike(candidate) || ts.isNumericLiteral(candidate)))
|
|
1291
|
+
return null;
|
|
1292
|
+
// `bounded` empty is ambiguous: on the FIRST transfer of any node it
|
|
1293
|
+
// means the index's own state was only just created and has not been
|
|
1294
|
+
// transferred yet, not that no bound exists -- `enumeratedKeysOf` and
|
|
1295
|
+
// `keysOf`'s own `unresolved-property-key` meet the identical shape
|
|
1296
|
+
// and both defer the empty-vs-opaque call to `sealing` for that
|
|
1297
|
+
// reason. Widening here before the fixpoint published a PERMANENT
|
|
1298
|
+
// fact per element (`case 'value'` -> `carry(slot(root, key))`) that
|
|
1299
|
+
// the index's later, exact resolution to a single literal could never
|
|
1300
|
+
// retract: `arr[k]` read every element forever because pass one saw
|
|
1301
|
+
// `k` before its own value had transferred. Defer instead of guessing
|
|
1302
|
+
// -- returning null falls through to `keysOf`'s own index read, which
|
|
1303
|
+
// already requires the argument and applies the same `sealing` gate.
|
|
1304
|
+
if (!sealing && bounded.size === 0)
|
|
1305
|
+
return null;
|
|
1306
|
+
return [ELEMENT_KEY];
|
|
1307
|
+
};
|
|
1308
|
+
const keysOf = (access) => {
|
|
1309
|
+
const direct = accessKey(access);
|
|
1310
|
+
if (direct !== null)
|
|
1311
|
+
return [direct];
|
|
1312
|
+
if (!ts.isElementAccessExpression(access))
|
|
1313
|
+
return [];
|
|
1314
|
+
const enumerated = enumerationHeadOf(access.argumentExpression);
|
|
1315
|
+
if (enumerated !== null)
|
|
1316
|
+
return enumeratedKeysOf(access, enumerated, read, fail, sealing, ownKeysOf);
|
|
1317
|
+
// A counter names a POSITION, never a key: `this.textures[ i ]` with
|
|
1318
|
+
// `let i` in three's RenderTarget. The checker calls it `number`, and
|
|
1319
|
+
// asking what the counter holds only ever refused (`i++` is no value
|
|
1320
|
+
// this graph states) and made the access opaque; the element slot is
|
|
1321
|
+
// the answer, and it is a superset whatever the counter holds.
|
|
1322
|
+
if (numericIndex(access.argumentExpression))
|
|
1323
|
+
return [ELEMENT_KEY];
|
|
1324
|
+
const elements = noArrayKeys ? null : arrayElementKeys(access);
|
|
1325
|
+
if (elements !== null)
|
|
1326
|
+
return elements;
|
|
1327
|
+
const keys = new Set();
|
|
1328
|
+
const candidates = read(value(access.argumentExpression));
|
|
1329
|
+
for (const candidate of candidates) {
|
|
1330
|
+
if (ts.isStringLiteralLike(candidate) || ts.isNumericLiteral(candidate))
|
|
1331
|
+
keys.add(candidate.text);
|
|
1332
|
+
else
|
|
1333
|
+
fail('coercing-or-unbounded-property-key', access);
|
|
1334
|
+
}
|
|
1335
|
+
if (sealing && candidates.size === 0)
|
|
1336
|
+
fail('unresolved-property-key', access);
|
|
1337
|
+
return [...keys];
|
|
1338
|
+
};
|
|
1339
|
+
/**
|
|
1340
|
+
* Mentions of an exported cell in the modules that import it, or null
|
|
1341
|
+
* when the set of modules that can read it is not enumerable.
|
|
1342
|
+
*
|
|
1343
|
+
* `export const taurus = new TaurusStore()` -- taurus-display's entire
|
|
1344
|
+
* store, and the receiver of every `this.<method>()` in it. An export
|
|
1345
|
+
* was refused outright unless NOTHING imported it, which reads "someone
|
|
1346
|
+
* imports this" as "this escaped". It is not an escape while every
|
|
1347
|
+
* module that can read the name is enumerated: the importers' mentions
|
|
1348
|
+
* are then simply more uses of the same cell. They are also mentions
|
|
1349
|
+
* `referencesToDeclaration` does not carry -- an importing file's
|
|
1350
|
+
* identifier resolves to the import specifier, not to the declaration --
|
|
1351
|
+
* so a caller that widens the predicate must walk these too, or it has
|
|
1352
|
+
* called a cell closed on the strength of uses it never looked at.
|
|
1353
|
+
*/
|
|
1354
|
+
const importerMentions = (declaration) => (ts.getCombinedModifierFlags(declaration) & ts.ModifierFlags.Export) === 0
|
|
1355
|
+
? []
|
|
1356
|
+
: inProgramImportReferencesOf(checker, flow, declaration);
|
|
1357
|
+
/** The refusal a cell earns, or null when it is closed -- named, because
|
|
1358
|
+
* "exposed" covered three different facts and each has its own fix. */
|
|
1359
|
+
const bindingClosed = (declaration) => {
|
|
1360
|
+
if (!localBindingWritesAreComplete(flow, declaration))
|
|
1361
|
+
return 'binding-writes-incomplete';
|
|
1362
|
+
if ((ts.getCombinedModifierFlags(declaration) & ts.ModifierFlags.Export) === 0)
|
|
1363
|
+
return null;
|
|
1364
|
+
if (exportIsUnimported(checker, flow, declaration))
|
|
1365
|
+
return null;
|
|
1366
|
+
return importerMentions(declaration) !== null ? null : 'export-importers-unenumerable';
|
|
1367
|
+
};
|
|
1368
|
+
const refs = (root, declaration) => {
|
|
1369
|
+
for (const reference of flow.referencesToDeclaration(declaration)) {
|
|
1370
|
+
if (reference === ts.getNameOfDeclaration(declaration) || isTypePositionReference(reference))
|
|
1371
|
+
continue;
|
|
1372
|
+
add(read(use(root, reference)));
|
|
1373
|
+
}
|
|
1374
|
+
for (const reference of importerMentions(declaration) ?? [])
|
|
1375
|
+
add(read(use(root, reference)));
|
|
1376
|
+
};
|
|
1377
|
+
/**
|
|
1378
|
+
* A binding pattern taken apart over this value: every element reads one
|
|
1379
|
+
* slot and nothing reads the object itself.
|
|
1380
|
+
*
|
|
1381
|
+
* Destructuring is a property READ, and `visitUse` already states that a
|
|
1382
|
+
* property read of a tracked object is not a publication of it. Refusing
|
|
1383
|
+
* the pattern spelling made the two disagree -- `p.app.go()` proved its
|
|
1384
|
+
* receiver and `const { app } = p; app.go()` did not, though the object
|
|
1385
|
+
* never left the program either way. A rest element stays refused: it
|
|
1386
|
+
* builds a FRESH object out of whatever keys nothing else took, and that
|
|
1387
|
+
* object is not modelled here.
|
|
1388
|
+
*/
|
|
1389
|
+
const patternReadsSlots = (root, pattern) => {
|
|
1390
|
+
if (!isObjectRoot(root)) {
|
|
1391
|
+
fail('non-object-destructured-container', pattern);
|
|
1392
|
+
return;
|
|
1393
|
+
}
|
|
1394
|
+
for (const element of pattern.elements) {
|
|
1395
|
+
if (ts.isOmittedExpression(element))
|
|
1396
|
+
continue;
|
|
1397
|
+
const key = bindingElementKey(element);
|
|
1398
|
+
if (element.dotDotDotToken)
|
|
1399
|
+
fail('rest-binding-element', element);
|
|
1400
|
+
else if (key === null)
|
|
1401
|
+
fail('computed-binding-element', element);
|
|
1402
|
+
else if (!descriptorOf(root, key))
|
|
1403
|
+
fail('unmodelled-descriptor', element);
|
|
1404
|
+
}
|
|
1405
|
+
};
|
|
1406
|
+
/**
|
|
1407
|
+
* The complete set of source setters a plain store to `key` executes.
|
|
1408
|
+
*
|
|
1409
|
+
* `class Base { constructor() { this.type = 'Base' } }` with a subclass
|
|
1410
|
+
* declaring `get type` / `set type`: the base constructor's plain store
|
|
1411
|
+
* runs the SUBCLASS's setter. The plan already names every setter such
|
|
1412
|
+
* a store can reach, or says the effects are opaque -- this only has to
|
|
1413
|
+
* put the receiver into each of those bodies, which is what a member
|
|
1414
|
+
* call does with its own frame.
|
|
1415
|
+
*/
|
|
1416
|
+
const storeRunsSetters = (root, key) => {
|
|
1417
|
+
if (!ts.isNewExpression(root))
|
|
1418
|
+
return false;
|
|
1419
|
+
const bodies = settersOf(root, key);
|
|
1420
|
+
if (bodies === null)
|
|
1421
|
+
return false;
|
|
1422
|
+
for (const setter of bodies)
|
|
1423
|
+
for (const reference of flow.receiverReferencesToDeclaration(setter))
|
|
1424
|
+
add(read(use(root, reference)));
|
|
1425
|
+
return true;
|
|
1426
|
+
};
|
|
1427
|
+
/**
|
|
1428
|
+
* Every value a setter's parameter receives: the right-hand side of each
|
|
1429
|
+
* store that runs the setter -- a store to its key on a construction of
|
|
1430
|
+
* its class's family. A setter has no call sites, so `callersOf` names
|
|
1431
|
+
* nothing for it and the parameter was an unindexed frame: three's
|
|
1432
|
+
* `RenderTarget` `set depthTexture( current )`. The family is by
|
|
1433
|
+
* heritage; a descendant's own override is over-counted, which only
|
|
1434
|
+
* widens the answer. A bulk `Object.assign` onto one of ours names no
|
|
1435
|
+
* key to compare and refuses.
|
|
1436
|
+
*/
|
|
1437
|
+
/**
|
|
1438
|
+
* Every instance a class member can run on: the constructions of its
|
|
1439
|
+
* heritage family, refused when the family is open or when a `new`
|
|
1440
|
+
* naming a family class could not be placed.
|
|
1441
|
+
*/
|
|
1442
|
+
const familyInstancesOf = (holder, reason) => {
|
|
1443
|
+
const family = sourceClassFamilyOf(checker, flow, new Set([holder]));
|
|
1444
|
+
if (family === null) {
|
|
1445
|
+
fail(reason, holder);
|
|
1446
|
+
return null;
|
|
1447
|
+
}
|
|
1448
|
+
const instances = new Set();
|
|
1449
|
+
for (const owner of family.keys())
|
|
1450
|
+
for (const instance of constructionsOf(owner))
|
|
1451
|
+
instances.add(instance);
|
|
1452
|
+
for (const call of opaqueConstructionsOf()) {
|
|
1453
|
+
const declarations = checker.getTypeAtLocation(call).getSymbol()?.declarations ?? [];
|
|
1454
|
+
if (declarations.some((declaration) => family.has(declaration)))
|
|
1455
|
+
fail('opaque-construction', call);
|
|
1456
|
+
}
|
|
1457
|
+
return [...instances];
|
|
1458
|
+
};
|
|
1459
|
+
/**
|
|
1460
|
+
* A value a getter returns is used wherever the getter is READ: every
|
|
1461
|
+
* access spelling its key on one of its class family's constructions,
|
|
1462
|
+
* from the instances outward like `setterStores`. A getter has no call
|
|
1463
|
+
* sites, so `callersOf` names nothing for it; three's `RenderTarget`
|
|
1464
|
+
* `get texture()` returns `this.textures[ 0 ]`, and the element is used
|
|
1465
|
+
* at every `target.texture` in the program. A descendant's override is
|
|
1466
|
+
* over-counted, which only widens.
|
|
1467
|
+
*/
|
|
1468
|
+
const getterReturns = (root, getter) => {
|
|
1469
|
+
const holder = getter.parent;
|
|
1470
|
+
if ((!ts.isClassDeclaration(holder) && !ts.isClassExpression(holder)) ||
|
|
1471
|
+
(ts.getCombinedModifierFlags(getter) & ts.ModifierFlags.Static) !== 0) {
|
|
1472
|
+
fail('unmodelled-getter-owner', getter);
|
|
1473
|
+
return;
|
|
1474
|
+
}
|
|
1475
|
+
const key = declarationKey(getter.name);
|
|
1476
|
+
if (key === null) {
|
|
1477
|
+
fail('computed-getter-key', getter);
|
|
1478
|
+
return;
|
|
1479
|
+
}
|
|
1480
|
+
const instances = familyInstancesOf(holder, 'getter-family-open');
|
|
1481
|
+
if (instances === null)
|
|
1482
|
+
return;
|
|
1483
|
+
for (const instance of instances)
|
|
1484
|
+
for (const fact of read(closure(instance))) {
|
|
1485
|
+
if (!ts.isPropertyAccessExpression(fact) && !ts.isElementAccessExpression(fact))
|
|
1486
|
+
continue;
|
|
1487
|
+
if (writeEdgeByAccess.has(fact) || !keysOf(fact).includes(key))
|
|
1488
|
+
continue;
|
|
1489
|
+
add(read(use(root, fact)));
|
|
1490
|
+
}
|
|
1491
|
+
};
|
|
1492
|
+
const setterStores = (setter) => {
|
|
1493
|
+
const holder = setter.parent;
|
|
1494
|
+
if ((!ts.isClassDeclaration(holder) && !ts.isClassExpression(holder)) ||
|
|
1495
|
+
(ts.getCombinedModifierFlags(setter) & ts.ModifierFlags.Static) !== 0) {
|
|
1496
|
+
fail('unmodelled-setter-owner', setter);
|
|
1497
|
+
return;
|
|
1498
|
+
}
|
|
1499
|
+
const key = declarationKey(setter.name);
|
|
1500
|
+
if (key === null) {
|
|
1501
|
+
fail('computed-setter-key', setter);
|
|
1502
|
+
return;
|
|
1503
|
+
}
|
|
1504
|
+
const instances = familyInstancesOf(holder, 'setter-family-open');
|
|
1505
|
+
if (instances === null)
|
|
1506
|
+
return;
|
|
1507
|
+
// From the instances outward, never from every write inward. The
|
|
1508
|
+
// stores that can run this setter go through one of the family's
|
|
1509
|
+
// constructions, and a construction's closure already lists every
|
|
1510
|
+
// store through it (`case 'slot'` reads the same facts). Placing the
|
|
1511
|
+
// receiver of every write naming the key instead read cells that
|
|
1512
|
+
// had nothing to do with this family -- EventDispatcher's
|
|
1513
|
+
// `listeners[ type ] = []` resolves `this._listeners` on every
|
|
1514
|
+
// dispatcher in the program, Material's included, and Material's
|
|
1515
|
+
// computed stores hang on the very `setValues` parameter this
|
|
1516
|
+
// question is part of establishing. Their opacity became this
|
|
1517
|
+
// parameter's.
|
|
1518
|
+
for (const root of instances)
|
|
1519
|
+
for (const stored of read(closure(root))) {
|
|
1520
|
+
if (ts.isCallExpression(stored)) {
|
|
1521
|
+
if (bulkAssignOf(stored) !== null && read(value(stored.arguments[0])).has(root))
|
|
1522
|
+
fail('bulk-assign-runs-setter', stored);
|
|
1523
|
+
continue;
|
|
1524
|
+
}
|
|
1525
|
+
if (!ts.isPropertyAccessExpression(stored) && !ts.isElementAccessExpression(stored))
|
|
1526
|
+
continue;
|
|
1527
|
+
const edge = writeEdgeByAccess.get(stored);
|
|
1528
|
+
if (edge === undefined || !keysOf(stored).includes(key))
|
|
1529
|
+
continue;
|
|
1530
|
+
const write = edge.write;
|
|
1531
|
+
if (write.value === null || !['property-assignment', 'index-assignment', 'logical-assignment'].includes(write.edge))
|
|
1532
|
+
fail('unmodelled-setter-store', write.site);
|
|
1533
|
+
else
|
|
1534
|
+
add(carry(value(write.value)));
|
|
1535
|
+
}
|
|
1536
|
+
};
|
|
1537
|
+
/**
|
|
1538
|
+
* Every read of an argument through the `arguments` object.
|
|
1539
|
+
*
|
|
1540
|
+
* three's `Object3D.add( object )` walks its own `arguments` to add
|
|
1541
|
+
* several children at once, so a value handed to it arrives at a
|
|
1542
|
+
* PARAMETER and, on some index, at `arguments[ i ]` as well. Refusing
|
|
1543
|
+
* the whole frame for that gave up on the entire Object3D family --
|
|
1544
|
+
* every `onBeforeRender` callback's `renderer` hung on this one
|
|
1545
|
+
* refusal.
|
|
1546
|
+
*
|
|
1547
|
+
* The index is not resolved and does not need to be: one that names any
|
|
1548
|
+
* position means every argument reaches every read, which is the
|
|
1549
|
+
* superset. `arguments.length` is a number and carries nothing. Any
|
|
1550
|
+
* other mention -- a spread, the object handed on whole -- is a use this
|
|
1551
|
+
* graph cannot follow, and stays a refusal.
|
|
1552
|
+
*/
|
|
1553
|
+
const argumentsReads = (body) => {
|
|
1554
|
+
const reads = [];
|
|
1555
|
+
for (const mention of argumentsUsesAt(body) ?? []) {
|
|
1556
|
+
const parent = mention.parent;
|
|
1557
|
+
if (ts.isPropertyAccessExpression(parent) && parent.expression === mention && parent.name.text === 'length')
|
|
1558
|
+
continue;
|
|
1559
|
+
if (ts.isElementAccessExpression(parent) && parent.expression === mention)
|
|
1560
|
+
reads.push(parent);
|
|
1561
|
+
else
|
|
1562
|
+
return null;
|
|
1563
|
+
}
|
|
1564
|
+
return reads;
|
|
1565
|
+
};
|
|
1566
|
+
const forward = (root, site, receiver, position) => {
|
|
1567
|
+
if (ts.isNewExpression(site.call) || site.operands.dispatch.kind === 'super-constructor') {
|
|
1568
|
+
for (const body of read(nodeQuery('construct-targets', site.call))) {
|
|
1569
|
+
if (!ts.isConstructorDeclaration(body) && !isBody(body)) {
|
|
1570
|
+
fail('non-constructor-target', body);
|
|
1571
|
+
continue;
|
|
1572
|
+
}
|
|
1573
|
+
const spread = argumentsReads(body);
|
|
1574
|
+
if (spread === null) {
|
|
1575
|
+
fail('arguments-object-frame', body);
|
|
1576
|
+
continue;
|
|
1577
|
+
}
|
|
1578
|
+
for (const mention of spread)
|
|
1579
|
+
add(read(use(root, mention)));
|
|
1580
|
+
const parameter = runtimeParametersOf(body)[position];
|
|
1581
|
+
if (parameter?.dotDotDotToken)
|
|
1582
|
+
fail('rest-frame', parameter);
|
|
1583
|
+
else if (parameter && ts.isIdentifier(parameter.name))
|
|
1584
|
+
refs(root, parameter);
|
|
1585
|
+
else if (parameter)
|
|
1586
|
+
patternReadsSlots(root, parameter.name);
|
|
1587
|
+
}
|
|
1588
|
+
return;
|
|
1589
|
+
}
|
|
1590
|
+
const selected = read(targets(site.call));
|
|
1591
|
+
if (sealing && selected.size === 0)
|
|
1592
|
+
fail('no-source-call-target', site.call);
|
|
1593
|
+
for (const body of selected) {
|
|
1594
|
+
if (!isCallableValue(body)) {
|
|
1595
|
+
fail('non-callable-target', body);
|
|
1596
|
+
continue;
|
|
1597
|
+
}
|
|
1598
|
+
if (receiver) {
|
|
1599
|
+
// `arguments` never holds the receiver, so an arguments-using body
|
|
1600
|
+
// says nothing about where `this` goes.
|
|
1601
|
+
if (!ts.isArrowFunction(body))
|
|
1602
|
+
for (const reference of flow.receiverReferencesToDeclaration(body))
|
|
1603
|
+
add(read(use(root, reference)));
|
|
1604
|
+
}
|
|
1605
|
+
else {
|
|
1606
|
+
const mentions = argumentsReads(body);
|
|
1607
|
+
if (mentions === null) {
|
|
1608
|
+
fail('arguments-object-frame', body);
|
|
1609
|
+
continue;
|
|
1610
|
+
}
|
|
1611
|
+
for (const mention of mentions)
|
|
1612
|
+
add(read(use(root, mention)));
|
|
1613
|
+
const parameter = runtimeParametersOf(body)[position];
|
|
1614
|
+
if (parameter?.dotDotDotToken)
|
|
1615
|
+
fail('rest-frame', parameter);
|
|
1616
|
+
else if (parameter && ts.isIdentifier(parameter.name))
|
|
1617
|
+
refs(root, parameter);
|
|
1618
|
+
else if (parameter)
|
|
1619
|
+
patternReadsSlots(root, parameter.name);
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
};
|
|
1623
|
+
const visitUse = (root, reference) => {
|
|
1624
|
+
if (isTypePositionReference(reference))
|
|
1625
|
+
return;
|
|
1626
|
+
const parent = reference.parent;
|
|
1627
|
+
if (!parent) {
|
|
1628
|
+
fail('unindexed-use', reference);
|
|
1629
|
+
return;
|
|
1630
|
+
}
|
|
1631
|
+
const erased = unwrapErasedExpression(parent);
|
|
1632
|
+
if (ts.isExpression(parent) && erased === reference && parent !== reference) {
|
|
1633
|
+
add(read(use(root, parent)));
|
|
1634
|
+
return;
|
|
1635
|
+
}
|
|
1636
|
+
if (ts.isVariableDeclaration(parent) || ts.isParameter(parent)) {
|
|
1637
|
+
if (parent.name === reference)
|
|
1638
|
+
return;
|
|
1639
|
+
if (parent.initializer !== reference) {
|
|
1640
|
+
fail('binding-pattern', parent);
|
|
1641
|
+
return;
|
|
1642
|
+
}
|
|
1643
|
+
if (!ts.isIdentifier(parent.name)) {
|
|
1644
|
+
patternReadsSlots(root, parent.name);
|
|
1645
|
+
return;
|
|
1646
|
+
}
|
|
1647
|
+
const exposure = ts.isVariableDeclaration(parent) ? bindingClosed(parent) : null;
|
|
1648
|
+
if (exposure !== null) {
|
|
1649
|
+
fail(`exposed-binding:${exposure}`, parent);
|
|
1650
|
+
return;
|
|
1651
|
+
}
|
|
1652
|
+
refs(root, parent);
|
|
1653
|
+
return;
|
|
1654
|
+
}
|
|
1655
|
+
if (ts.isBinaryExpression(parent)) {
|
|
1656
|
+
if (parent.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
|
|
1657
|
+
if (parent.left === reference)
|
|
1658
|
+
return;
|
|
1659
|
+
const left = unwrapNaming(parent.left);
|
|
1660
|
+
if (ts.isIdentifier(left)) {
|
|
1661
|
+
const declaration = flow.targetOf(left)?.declaration;
|
|
1662
|
+
if (!declaration || (!ts.isVariableDeclaration(declaration) && !ts.isParameter(declaration))) {
|
|
1663
|
+
fail('external-binding-store', left);
|
|
1664
|
+
return;
|
|
1665
|
+
}
|
|
1666
|
+
// `bindingClosed` names the REFUSAL, null when the cell is closed
|
|
1667
|
+
// (its other reader above spells the same test). Read as a boolean
|
|
1668
|
+
// it refused every store into a closed local and admitted every
|
|
1669
|
+
// store into an exposed one -- `entry = makeRecord()` inside a
|
|
1670
|
+
// closed factory was the 'exposed-binding' that kept the cache
|
|
1671
|
+
// `.get()` receiver from ever being proven.
|
|
1672
|
+
const exposure = ts.isVariableDeclaration(declaration) ? bindingClosed(declaration) : null;
|
|
1673
|
+
if (exposure !== null) {
|
|
1674
|
+
fail(`exposed-binding:${exposure}`, declaration);
|
|
1675
|
+
return;
|
|
1676
|
+
}
|
|
1677
|
+
refs(root, declaration);
|
|
1678
|
+
}
|
|
1679
|
+
else if (ts.isPropertyAccessExpression(left) || ts.isElementAccessExpression(left)) {
|
|
1680
|
+
const keys = keysOf(left);
|
|
1681
|
+
const containers = read(value(left.expression));
|
|
1682
|
+
if (sealing && containers.size === 0)
|
|
1683
|
+
fail('unresolved-container', left);
|
|
1684
|
+
for (const container of containers) {
|
|
1685
|
+
// A store on `null`/`undefined` throws before it lands: an
|
|
1686
|
+
// element slot read through a counter carries `undefined` for
|
|
1687
|
+
// the positions never written (`this.textures[ i ]` in three's
|
|
1688
|
+
// RenderTarget), and that is no container.
|
|
1689
|
+
if (container === undefinedValue || container.kind === ts.SyntaxKind.NullKeyword || ts.isVoidExpression(container))
|
|
1690
|
+
continue;
|
|
1691
|
+
if (!isObjectRoot(container)) {
|
|
1692
|
+
fail('opaque-container-store', left);
|
|
1693
|
+
continue;
|
|
1694
|
+
}
|
|
1695
|
+
for (const key of keys) {
|
|
1696
|
+
if (descriptorOf(container, key)) {
|
|
1697
|
+
add(read(publication(root, container, key)));
|
|
1698
|
+
continue;
|
|
1699
|
+
}
|
|
1700
|
+
// A store to an accessor key hands the value to the
|
|
1701
|
+
// SETTER's parameter, never to a slot of the container.
|
|
1702
|
+
if (ts.isNewExpression(container) && dataStoreOf(container, key)) {
|
|
1703
|
+
add(read(publication(root, container, key)));
|
|
1704
|
+
continue;
|
|
1705
|
+
}
|
|
1706
|
+
const setters = ts.isNewExpression(container) ? settersOf(container, key) : null;
|
|
1707
|
+
if (setters === null)
|
|
1708
|
+
fail('opaque-container-store', left);
|
|
1709
|
+
else
|
|
1710
|
+
for (const setter of setters) {
|
|
1711
|
+
const parameter = setter.parameters[0];
|
|
1712
|
+
if (!parameter || !ts.isIdentifier(parameter.name))
|
|
1713
|
+
fail('unmodelled-setter-parameter', setter);
|
|
1714
|
+
else
|
|
1715
|
+
refs(root, parameter);
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
else
|
|
1721
|
+
fail('unmodelled-store', left);
|
|
1722
|
+
add(read(use(root, parent)));
|
|
1723
|
+
return;
|
|
1724
|
+
}
|
|
1725
|
+
if (parent.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken ||
|
|
1726
|
+
parent.operatorToken.kind === ts.SyntaxKind.BarBarToken ||
|
|
1727
|
+
parent.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken ||
|
|
1728
|
+
parent.operatorToken.kind === ts.SyntaxKind.CommaToken) {
|
|
1729
|
+
add(read(use(root, parent)));
|
|
1730
|
+
return;
|
|
1731
|
+
}
|
|
1732
|
+
if (parent.operatorToken.kind === ts.SyntaxKind.EqualsEqualsEqualsToken ||
|
|
1733
|
+
parent.operatorToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken)
|
|
1734
|
+
return;
|
|
1735
|
+
fail('coercing-value-use', parent);
|
|
1736
|
+
return;
|
|
1737
|
+
}
|
|
1738
|
+
if (ts.isConditionalExpression(parent)) {
|
|
1739
|
+
if (parent.condition !== reference)
|
|
1740
|
+
add(read(use(root, parent)));
|
|
1741
|
+
return;
|
|
1742
|
+
}
|
|
1743
|
+
if (ts.isPropertyAssignment(parent) && parent.initializer === reference) {
|
|
1744
|
+
const key = declarationKey(parent.name);
|
|
1745
|
+
if (key === null || key === '__proto__')
|
|
1746
|
+
fail('computed-publication', parent);
|
|
1747
|
+
else
|
|
1748
|
+
add(read(publication(root, parent.parent, key)));
|
|
1749
|
+
return;
|
|
1750
|
+
}
|
|
1751
|
+
if (ts.isShorthandPropertyAssignment(parent)) {
|
|
1752
|
+
add(read(publication(root, parent.parent, parent.name.text)));
|
|
1753
|
+
return;
|
|
1754
|
+
}
|
|
1755
|
+
if (ts.isPropertyDeclaration(parent) && parent.initializer === reference) {
|
|
1756
|
+
const key = declarationKey(parent.name);
|
|
1757
|
+
if (key === null || (ts.getCombinedModifierFlags(parent) & ts.ModifierFlags.Static) !== 0) {
|
|
1758
|
+
fail('static-or-computed-publication', parent);
|
|
1759
|
+
return;
|
|
1760
|
+
}
|
|
1761
|
+
for (const construction of constructionsOf(parent))
|
|
1762
|
+
add(read(publication(root, construction, key)));
|
|
1763
|
+
return;
|
|
1764
|
+
}
|
|
1765
|
+
if (ts.isArrayLiteralExpression(parent)) {
|
|
1766
|
+
if (parent.elements.some(ts.isSpreadElement))
|
|
1767
|
+
fail('spread-publication', parent);
|
|
1768
|
+
else
|
|
1769
|
+
add(read(publication(root, parent, String(parent.elements.indexOf(reference)))));
|
|
1770
|
+
return;
|
|
1771
|
+
}
|
|
1772
|
+
if (ts.isPropertyAccessExpression(parent) || ts.isElementAccessExpression(parent)) {
|
|
1773
|
+
if (parent.expression !== reference) {
|
|
1774
|
+
// The reference is the access's NAME, not its object: `a.go`
|
|
1775
|
+
// MENTIONS the method `go`, and `flow.referencesToDeclaration`
|
|
1776
|
+
// hands that mention back when the tracked value is the method's
|
|
1777
|
+
// own declaration. What is used is the access's result. Without
|
|
1778
|
+
// this branch every class method's closure proof read its own call
|
|
1779
|
+
// sites as a key coercion, so `this.run()` had no callable slot at
|
|
1780
|
+
// all -- the one place a member name is not a key being coerced.
|
|
1781
|
+
if (ts.isPropertyAccessExpression(parent) && parent.name === reference) {
|
|
1782
|
+
add(read(use(root, parent)));
|
|
1783
|
+
return;
|
|
1784
|
+
}
|
|
1785
|
+
fail('coercing-property-key', reference);
|
|
1786
|
+
return;
|
|
1787
|
+
}
|
|
1788
|
+
const keys = keysOf(parent);
|
|
1789
|
+
// `new ( /** @type {new (...args: any[]) => this} */ ( this.constructor ) )()`
|
|
1790
|
+
// -- three's own spelling of the clone idiom wherever it JSDoc-casts
|
|
1791
|
+
// the read (Texture.js, BufferGeometry.js, Object3D.js, Camera.js) --
|
|
1792
|
+
// sits a ParenthesizedExpression between the access and the `new` that
|
|
1793
|
+
// actually invokes it, purely to anchor the cast comment. Plain
|
|
1794
|
+
// `parent.parent` lands on that paren, not the `new`, so the
|
|
1795
|
+
// `constructor-slot-read` exception below never matched and every
|
|
1796
|
+
// family member's `copy` override the census could not otherwise index
|
|
1797
|
+
// came back an unmodelled frame. `outermostErasureOf` is the same
|
|
1798
|
+
// erasure-aware "which node occupies the syntactic position" walk this
|
|
1799
|
+
// graph already uses for `use`/`visitUse`; for the unparenthesized
|
|
1800
|
+
// spelling it is a no-op (`parent.parent` unchanged).
|
|
1801
|
+
const positioned = outermostErasureOf(parent);
|
|
1802
|
+
const invocation = positioned.parent;
|
|
1803
|
+
const callee = ts.isCallExpression(invocation) && invocation.expression === positioned;
|
|
1804
|
+
const stored = ts.isBinaryExpression(invocation) &&
|
|
1805
|
+
invocation.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
1806
|
+
invocation.left === positioned;
|
|
1807
|
+
// `delete holder.field`, `holder.n += 1`, `holder.n++`, `holder.f ??=
|
|
1808
|
+
// g`: the slot is rewritten without a plain `=`. Reading these as
|
|
1809
|
+
// mere mentions of the slot left every one of them invisible to the
|
|
1810
|
+
// slot's own value set -- a deleted field still "held" its
|
|
1811
|
+
// initializer. They are stores; the slot decides what each edge
|
|
1812
|
+
// means, and refuses the ones it cannot model.
|
|
1813
|
+
const mutated = (ts.isDeleteExpression(invocation) && invocation.expression === positioned) ||
|
|
1814
|
+
(ts.isBinaryExpression(invocation) &&
|
|
1815
|
+
invocation.left === positioned &&
|
|
1816
|
+
invocation.operatorToken.kind !== ts.SyntaxKind.EqualsToken &&
|
|
1817
|
+
invocation.operatorToken.kind >= ts.SyntaxKind.FirstAssignment &&
|
|
1818
|
+
invocation.operatorToken.kind <= ts.SyntaxKind.LastAssignment) ||
|
|
1819
|
+
((ts.isPrefixUnaryExpression(invocation) || ts.isPostfixUnaryExpression(invocation)) &&
|
|
1820
|
+
(invocation.operator === ts.SyntaxKind.PlusPlusToken || invocation.operator === ts.SyntaxKind.MinusMinusToken));
|
|
1821
|
+
if (mutated && !writeEdgeByAccess.has(parent)) {
|
|
1822
|
+
fail('unmodelled-property-write', parent);
|
|
1823
|
+
return;
|
|
1824
|
+
}
|
|
1825
|
+
// A store through this root is a key the root gains, and THIS walk is
|
|
1826
|
+
// where the program says so. Asking the write index instead -- "of
|
|
1827
|
+
// every write in the program, which ones reach this root" -- is the
|
|
1828
|
+
// same fact derived a second way, at O(all writes) per re-evaluation
|
|
1829
|
+
// instead of O(uses of the root); on the three.js app that was the compile.
|
|
1830
|
+
// `enumeratedKeysOf` already requires this closure, so the key set
|
|
1831
|
+
// costs it no dependency it was not already holding.
|
|
1832
|
+
if (stored || mutated)
|
|
1833
|
+
facts.add(parent);
|
|
1834
|
+
/**
|
|
1835
|
+
* Whether this graph can say what reaching `key` through this root
|
|
1836
|
+
* does. A declared cell it can. A key proven absent from the chain
|
|
1837
|
+
* it can -- except in a CALLEE position, where absence names no
|
|
1838
|
+
* callable. And a plain store to a key the family declares as an
|
|
1839
|
+
* accessor it can, because the store runs that accessor's SETTER:
|
|
1840
|
+
* a frame, not an unmodelled descriptor. The getter is never
|
|
1841
|
+
* evaluated -- a store does not read -- so a getter that hands its
|
|
1842
|
+
* receiver out says nothing about this store.
|
|
1843
|
+
*/
|
|
1844
|
+
const modelled = (held, key) => {
|
|
1845
|
+
if (descriptorOf(held, key))
|
|
1846
|
+
return true;
|
|
1847
|
+
if (callee)
|
|
1848
|
+
return false;
|
|
1849
|
+
if (stored && storeRunsSetters(held, key))
|
|
1850
|
+
return true;
|
|
1851
|
+
if (ts.isNewExpression(held)) {
|
|
1852
|
+
if (!stored && !mutated && familyReadOf(held, key) !== null)
|
|
1853
|
+
return true;
|
|
1854
|
+
if (stored && dataStoreOf(held, key))
|
|
1855
|
+
return true;
|
|
1856
|
+
}
|
|
1857
|
+
return absentOwnKey(held, key, parent, descriptorOf, hasUnnamedWrite);
|
|
1858
|
+
};
|
|
1859
|
+
// `instance.constructor` READS the class object off the prototype
|
|
1860
|
+
// -- a value this graph does not carry, and one whose escape opens
|
|
1861
|
+
// the family: `unknown( value.constructor )` can construct and
|
|
1862
|
+
// retarget Base from anywhere. Only `new this.constructor()` is a
|
|
1863
|
+
// modelled use of it (`case 'value'` places the construction).
|
|
1864
|
+
if (ts.isNewExpression(root) &&
|
|
1865
|
+
keys.includes('constructor') &&
|
|
1866
|
+
!stored &&
|
|
1867
|
+
!mutated &&
|
|
1868
|
+
!(ts.isNewExpression(invocation) && invocation.expression === positioned)) {
|
|
1869
|
+
fail('constructor-slot-read', parent);
|
|
1870
|
+
return;
|
|
1871
|
+
}
|
|
1872
|
+
if (!isObjectRoot(root) || keys.some((key) => !modelled(root, key))) {
|
|
1873
|
+
fail('unmodelled-descriptor', parent);
|
|
1874
|
+
return;
|
|
1875
|
+
}
|
|
1876
|
+
if (callee) {
|
|
1877
|
+
const site = sites.get(invocation);
|
|
1878
|
+
if (!site)
|
|
1879
|
+
fail('unindexed-call', invocation);
|
|
1880
|
+
else
|
|
1881
|
+
forward(root, site, true, -1);
|
|
1882
|
+
}
|
|
1883
|
+
return;
|
|
1884
|
+
}
|
|
1885
|
+
if (ts.isCallExpression(parent) || ts.isNewExpression(parent)) {
|
|
1886
|
+
if (parent.expression === reference) {
|
|
1887
|
+
if (reference.kind === ts.SyntaxKind.SuperKeyword && ts.isCallExpression(parent) && ts.isNewExpression(root)) {
|
|
1888
|
+
read(nodeQuery('construct-targets', parent));
|
|
1889
|
+
return;
|
|
1890
|
+
}
|
|
1891
|
+
if (!isBody(root))
|
|
1892
|
+
fail('non-callable-use', reference);
|
|
1893
|
+
return;
|
|
1894
|
+
}
|
|
1895
|
+
if (ts.isCallExpression(parent) && definitionPlans.has(parent)) {
|
|
1896
|
+
const plan = definitionOf(parent);
|
|
1897
|
+
if (plan === null)
|
|
1898
|
+
fail('intrinsic-definition-unproven', parent);
|
|
1899
|
+
else if (parent.arguments[0] === reference) {
|
|
1900
|
+
// The target gains the key (`case 'slot'` reads the value
|
|
1901
|
+
// through this fact) and is handed to nobody but, when the
|
|
1902
|
+
// intrinsic returns it, whoever reads the call.
|
|
1903
|
+
facts.add(parent);
|
|
1904
|
+
if (plan.returnsTarget && !ts.isExpressionStatement(parent.parent))
|
|
1905
|
+
add(read(use(root, parent)));
|
|
1906
|
+
}
|
|
1907
|
+
else if (parent.arguments[2] === reference) {
|
|
1908
|
+
// The descriptor literal: its `value` is copied into the target
|
|
1909
|
+
// (`case 'publication'` forwards it); the literal goes nowhere.
|
|
1910
|
+
facts.add(parent);
|
|
1911
|
+
}
|
|
1912
|
+
else if (parent.arguments[1] === reference)
|
|
1913
|
+
fail('coercing-property-key', reference);
|
|
1914
|
+
else
|
|
1915
|
+
fail('unmodelled-invocation-frame', parent);
|
|
1916
|
+
return;
|
|
1917
|
+
}
|
|
1918
|
+
if (ts.isCallExpression(parent)) {
|
|
1919
|
+
const bulk = bulkAssignOf(parent);
|
|
1920
|
+
if (bulk !== null && parent.arguments.includes(reference)) {
|
|
1921
|
+
// The call evaluates to its target, so a target root is used
|
|
1922
|
+
// wherever the call's result is; a source's own slot values are
|
|
1923
|
+
// copied into it (`case 'slot'` reads them through this fact),
|
|
1924
|
+
// and a source literal itself goes nowhere. A source that is
|
|
1925
|
+
// not a literal could run getters, which this does not enter.
|
|
1926
|
+
facts.add(parent);
|
|
1927
|
+
if (bulk.target === reference)
|
|
1928
|
+
add(read(use(root, parent)));
|
|
1929
|
+
else if (!ts.isObjectLiteralExpression(root))
|
|
1930
|
+
fail('bulk-assign-source-unmodelled', reference);
|
|
1931
|
+
return;
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
const site = sites.get(parent);
|
|
1935
|
+
// `f.call( receiver, ... )` writes the receiver in an ARGUMENT
|
|
1936
|
+
// position of the syntax and reads it in the RECEIVER position of
|
|
1937
|
+
// the frame. The operands already say which is which -- reading the
|
|
1938
|
+
// syntax instead made every value handed to `.call` an argument no
|
|
1939
|
+
// parameter had a slot for.
|
|
1940
|
+
if (site?.explicitThis && site.operands.receiver === reference) {
|
|
1941
|
+
forward(root, site, true, -1);
|
|
1942
|
+
return;
|
|
1943
|
+
}
|
|
1944
|
+
// A value stored into a closed native Map (`store.set( key, value )`)
|
|
1945
|
+
// is not handed to unknown code: the map's only way out is its
|
|
1946
|
+
// `.get()` reads, and the value continues at each of them exactly
|
|
1947
|
+
// as it would at a parameter. `case 'completion'` above reads the
|
|
1948
|
+
// same stores back from the `.get()` side.
|
|
1949
|
+
if (ts.isCallExpression(parent)) {
|
|
1950
|
+
const continuations = collectionValueContinuationsOf(checker, flow, parent, reference, nativeProtocolClosed);
|
|
1951
|
+
if (continuations !== null) {
|
|
1952
|
+
for (const continued of continuations)
|
|
1953
|
+
add(read(use(root, continued)));
|
|
1954
|
+
return;
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
const position = site?.operands?.args.indexOf(reference) ?? -1;
|
|
1958
|
+
if (!site || position < 0 || site.operands?.args.some(ts.isSpreadElement))
|
|
1959
|
+
fail('unmodelled-invocation-frame', parent);
|
|
1960
|
+
else
|
|
1961
|
+
forward(root, site, false, position);
|
|
1962
|
+
return;
|
|
1963
|
+
}
|
|
1964
|
+
if (ts.isReturnStatement(parent)) {
|
|
1965
|
+
const body = ts.findAncestor(parent, ts.isFunctionLike);
|
|
1966
|
+
if (body && ts.isGetAccessorDeclaration(body)) {
|
|
1967
|
+
getterReturns(root, body);
|
|
1968
|
+
return;
|
|
1969
|
+
}
|
|
1970
|
+
if (!body || !isBody(body) || callableCompletionSummaryOf(flow, body)?.execution !== 'sync') {
|
|
1971
|
+
fail('deferred-completion', parent);
|
|
1972
|
+
return;
|
|
1973
|
+
}
|
|
1974
|
+
read(closure(body));
|
|
1975
|
+
for (const call of read(callersOf(body)))
|
|
1976
|
+
if (ts.isCallExpression(call))
|
|
1977
|
+
add(read(use(root, call)));
|
|
1978
|
+
return;
|
|
1979
|
+
}
|
|
1980
|
+
// Enumerating an object reads its KEY SET and nothing else: no slot is
|
|
1981
|
+
// read, nothing escapes, and the object is not even coerced. What the
|
|
1982
|
+
// bound key then does is a question asked where the head is USED, not
|
|
1983
|
+
// here -- and without this branch the enumerated object was opaque, so
|
|
1984
|
+
// the key set could never be proven closed.
|
|
1985
|
+
if (ts.isForInStatement(parent) && parent.expression === reference)
|
|
1986
|
+
return;
|
|
1987
|
+
if (ts.isExpressionStatement(parent) ||
|
|
1988
|
+
ts.isTypeOfExpression(parent) ||
|
|
1989
|
+
ts.isVoidExpression(parent) ||
|
|
1990
|
+
(ts.isPrefixUnaryExpression(parent) && parent.operator === ts.SyntaxKind.ExclamationToken) ||
|
|
1991
|
+
(ts.isIfStatement(parent) && parent.expression === reference))
|
|
1992
|
+
return;
|
|
1993
|
+
fail('opaque-value-use', reference);
|
|
1994
|
+
};
|
|
1995
|
+
seeded = false;
|
|
1996
|
+
switch (query.kind) {
|
|
1997
|
+
case 'value': {
|
|
1998
|
+
const expression = query.node;
|
|
1999
|
+
if (isObjectRoot(expression)) {
|
|
2000
|
+
if (ts.isNewExpression(expression) && construction(expression) === null) {
|
|
2001
|
+
// `new WebGLState()` on a plain function that returns an object
|
|
2002
|
+
// from every completion and never mentions `this` yields THAT
|
|
2003
|
+
// completion, never the fresh allocation (`new` discards it):
|
|
2004
|
+
// the value is the call's completion, solved through the same
|
|
2005
|
+
// frame a call would be. Anything else under `new` that is not
|
|
2006
|
+
// a source construction stays opaque.
|
|
2007
|
+
if (constructorFunctionCalleeOf(expression) !== null)
|
|
2008
|
+
add(carry(nodeQuery('completion', expression)));
|
|
2009
|
+
else
|
|
2010
|
+
fail('opaque-construction');
|
|
2011
|
+
}
|
|
2012
|
+
else {
|
|
2013
|
+
facts.add(expression);
|
|
2014
|
+
seeded = true;
|
|
2015
|
+
// `new this.constructor()` is a construction of one of the owned
|
|
2016
|
+
// family `thisConstructorFamilyOf` named (its construction fact's
|
|
2017
|
+
// alternatives) only while `this` itself holds nothing but such
|
|
2018
|
+
// constructions. A method invoked with an explicit foreign
|
|
2019
|
+
// receiver would make the fresh object a foreign instance, and
|
|
2020
|
+
// that is decided here, by what `this` holds, not by the
|
|
2021
|
+
// static fact.
|
|
2022
|
+
if (ts.isNewExpression(expression)) {
|
|
2023
|
+
const receiver = thisConstructorReceiverOf(expression);
|
|
2024
|
+
if (receiver !== null) {
|
|
2025
|
+
const alternatives = construction(expression)?.alternatives ?? [];
|
|
2026
|
+
for (const root of read(value(receiver))) {
|
|
2027
|
+
if (!ts.isNewExpression(root) || !construction(root)?.alternatives.every((owner) => alternatives.includes(owner))) {
|
|
2028
|
+
fail('opaque-construction', root);
|
|
2029
|
+
continue;
|
|
2030
|
+
}
|
|
2031
|
+
// The read is of the INSTANCE's `constructor`, which an
|
|
2032
|
+
// own store or definition on that instance replaces:
|
|
2033
|
+
// `value.constructor = function () {}` makes the fresh
|
|
2034
|
+
// object whatever that function builds.
|
|
2035
|
+
for (const stored of read(closure(root))) {
|
|
2036
|
+
if (ts.isCallExpression(stored) && definitionPlans.has(stored)) {
|
|
2037
|
+
const plan = definitionOf(stored);
|
|
2038
|
+
if (plan === null || (plan.keys.includes('constructor') && read(value(stored.arguments[0])).has(root)))
|
|
2039
|
+
fail('constructor-slot-written', stored);
|
|
2040
|
+
continue;
|
|
2041
|
+
}
|
|
2042
|
+
if (!ts.isPropertyAccessExpression(stored) && !ts.isElementAccessExpression(stored))
|
|
2043
|
+
continue;
|
|
2044
|
+
if (writeEdgeByAccess.has(stored) && keysOf(stored).includes('constructor'))
|
|
2045
|
+
fail('constructor-slot-written', stored);
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
else if (isBody(expression)) {
|
|
2053
|
+
facts.add(expression);
|
|
2054
|
+
seeded = true;
|
|
2055
|
+
}
|
|
2056
|
+
else if (ts.isIdentifier(expression)) {
|
|
2057
|
+
const target = flow.targetOf(expression);
|
|
2058
|
+
const declaration = aliasedDeclarationOf(target?.symbol ?? undefined) ?? target?.declaration;
|
|
2059
|
+
if (declaration && isBody(declaration) && flow.callableBodyIsIndexed(declaration)) {
|
|
2060
|
+
facts.add(declaration);
|
|
2061
|
+
seeded = true;
|
|
2062
|
+
if (ts.isFunctionDeclaration(declaration) && ts.isSourceFile(declaration.parent) && !ts.isExternalModule(declaration.parent))
|
|
2063
|
+
fail('global-object-callable', declaration);
|
|
2064
|
+
for (const write of flow.writesToDeclaration(declaration)) {
|
|
2065
|
+
if (write.slot !== 'whole' || write.edge === 'return' || write.edge === 'yield')
|
|
2066
|
+
continue;
|
|
2067
|
+
if (write.value && ['identifier-assignment', 'logical-assignment'].includes(write.edge))
|
|
2068
|
+
add(carry(value(write.value)));
|
|
2069
|
+
else
|
|
2070
|
+
fail('unmodelled-callable-binding-write', write.site);
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
else if (declaration && ts.isVariableDeclaration(declaration)) {
|
|
2074
|
+
const incoming = localBindingValuesOf(flow, declaration);
|
|
2075
|
+
if (incoming === null)
|
|
2076
|
+
fail('unaccounted-binding-write', declaration);
|
|
2077
|
+
else
|
|
2078
|
+
for (const entry of incoming)
|
|
2079
|
+
add(carry(value(entry)));
|
|
2080
|
+
}
|
|
2081
|
+
else if (declaration && ts.isParameter(declaration))
|
|
2082
|
+
add(carry(nodeQuery('parameter', declaration)));
|
|
2083
|
+
else if (declaration && ts.isBindingElement(declaration))
|
|
2084
|
+
add(carry(nodeQuery('binding-element', declaration)));
|
|
2085
|
+
else if (expression.text === 'undefined' && (!declaration || declaration.getSourceFile().hasNoDefaultLib)) {
|
|
2086
|
+
facts.add(undefinedValue);
|
|
2087
|
+
seeded = true;
|
|
2088
|
+
}
|
|
2089
|
+
else
|
|
2090
|
+
fail('opaque-binding');
|
|
2091
|
+
}
|
|
2092
|
+
else if (expression.kind === ts.SyntaxKind.ThisKeyword || expression.kind === ts.SyntaxKind.SuperKeyword) {
|
|
2093
|
+
const owner = flow.receiverOwnerOf(expression);
|
|
2094
|
+
if (!owner)
|
|
2095
|
+
fail('unindexed-receiver');
|
|
2096
|
+
else
|
|
2097
|
+
add(carry(nodeQuery('receiver', owner)));
|
|
2098
|
+
}
|
|
2099
|
+
else if (ts.isPropertyAccessExpression(expression) || ts.isElementAccessExpression(expression)) {
|
|
2100
|
+
const keys = keysOf(expression);
|
|
2101
|
+
const roots = read(value(expression.expression));
|
|
2102
|
+
for (const root of roots) {
|
|
2103
|
+
// An element read off an array literal is answered by the
|
|
2104
|
+
// literal only while `Array.prototype` is the original: the
|
|
2105
|
+
// obligation goes to the ledger, as the legacy element authority
|
|
2106
|
+
// (`nativeArrayProtocolPlanOf`) registered it, and refuses with
|
|
2107
|
+
// nothing to carry it.
|
|
2108
|
+
if (ts.isArrayLiteralExpression(root) && deferredIntrinsicProtocolLedgerOf(flow)?.require('Array', expression) !== true) {
|
|
2109
|
+
fail('undeferrable-array-protocol', expression);
|
|
2110
|
+
continue;
|
|
2111
|
+
}
|
|
2112
|
+
if (isObjectRoot(root))
|
|
2113
|
+
for (const key of keys)
|
|
2114
|
+
add(carry(slot(root, key)));
|
|
2115
|
+
else if (!isNullish(root))
|
|
2116
|
+
fail('non-object-receiver');
|
|
2117
|
+
}
|
|
2118
|
+
// Every receiver nullish: the read throws, so the cell provably
|
|
2119
|
+
// holds nothing. Empty is the answer, witnessed here, not a
|
|
2120
|
+
// value still waiting on some origin.
|
|
2121
|
+
if (roots.size > 0 && [...roots].every(isNullish))
|
|
2122
|
+
seeded = true;
|
|
2123
|
+
}
|
|
2124
|
+
else if (ts.isCallExpression(expression))
|
|
2125
|
+
add(carry(nodeQuery('completion', expression)));
|
|
2126
|
+
else if (ts.isConditionalExpression(expression)) {
|
|
2127
|
+
add(carry(value(expression.whenTrue)));
|
|
2128
|
+
add(carry(value(expression.whenFalse)));
|
|
2129
|
+
}
|
|
2130
|
+
else if (ts.isBinaryExpression(expression) &&
|
|
2131
|
+
[ts.SyntaxKind.EqualsToken, ts.SyntaxKind.CommaToken].includes(expression.operatorToken.kind))
|
|
2132
|
+
add(carry(value(expression.right)));
|
|
2133
|
+
else if (ts.isBinaryExpression(expression) &&
|
|
2134
|
+
[ts.SyntaxKind.BarBarToken, ts.SyntaxKind.AmpersandAmpersandToken, ts.SyntaxKind.QuestionQuestionToken].includes(expression.operatorToken.kind)) {
|
|
2135
|
+
add(carry(value(expression.left)));
|
|
2136
|
+
add(carry(value(expression.right)));
|
|
2137
|
+
}
|
|
2138
|
+
else if (ts.isLiteralExpression(expression) ||
|
|
2139
|
+
expression.kind === ts.SyntaxKind.NullKeyword ||
|
|
2140
|
+
expression.kind === ts.SyntaxKind.TrueKeyword ||
|
|
2141
|
+
expression.kind === ts.SyntaxKind.FalseKeyword ||
|
|
2142
|
+
ts.isTypeOfExpression(expression) ||
|
|
2143
|
+
ts.isVoidExpression(expression) ||
|
|
2144
|
+
ts.isBinaryExpression(expression)) {
|
|
2145
|
+
facts.add(expression);
|
|
2146
|
+
seeded = true;
|
|
2147
|
+
}
|
|
2148
|
+
else
|
|
2149
|
+
fail('unmodelled-value-origin');
|
|
2150
|
+
break;
|
|
2151
|
+
}
|
|
2152
|
+
case 'slot': {
|
|
2153
|
+
const root = query.node;
|
|
2154
|
+
const key = query.key;
|
|
2155
|
+
const getterRead = ts.isNewExpression(root) && !descriptorOf(root, key) ? familyReadOf(root, key) : null;
|
|
2156
|
+
if (!descriptorOf(root, key) && getterRead === null) {
|
|
2157
|
+
// A literal that spells every key it has and holds no key this
|
|
2158
|
+
// graph cannot name reads `undefined` for the ones it lacks:
|
|
2159
|
+
// `options.depthTexture` on the `{}` a parameter defaults to.
|
|
2160
|
+
if (ts.isObjectLiteralExpression(root) && absentOwnKey(root, key, root, descriptorOf, hasUnnamedWrite)) {
|
|
2161
|
+
read(closure(root));
|
|
2162
|
+
facts.add(undefinedValue);
|
|
2163
|
+
seeded = true;
|
|
2164
|
+
break;
|
|
2165
|
+
}
|
|
2166
|
+
fail('unmodelled-descriptor');
|
|
2167
|
+
break;
|
|
2168
|
+
}
|
|
2169
|
+
read(closure(root));
|
|
2170
|
+
if (ts.isObjectLiteralExpression(root)) {
|
|
2171
|
+
for (const entry of ownEntries(root, key) ?? []) {
|
|
2172
|
+
if (ts.isPropertyAssignment(entry))
|
|
2173
|
+
add(carry(value(entry.initializer)));
|
|
2174
|
+
else if (ts.isShorthandPropertyAssignment(entry))
|
|
2175
|
+
add(carry(value(entry.name)));
|
|
2176
|
+
else if (ts.isMethodDeclaration(entry)) {
|
|
2177
|
+
facts.add(entry);
|
|
2178
|
+
seeded = true;
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
else if (ts.isArrayLiteralExpression(root)) {
|
|
2183
|
+
if (key === ELEMENT_KEY) {
|
|
2184
|
+
// Every position at once; an index never written reads undefined.
|
|
2185
|
+
for (const element of root.elements)
|
|
2186
|
+
if (!ts.isOmittedExpression(element))
|
|
2187
|
+
add(carry(value(element)));
|
|
2188
|
+
facts.add(undefinedValue);
|
|
2189
|
+
seeded = true;
|
|
2190
|
+
}
|
|
2191
|
+
else if (key === 'length') {
|
|
2192
|
+
let length = arrayLengths.get(root);
|
|
2193
|
+
if (!length)
|
|
2194
|
+
arrayLengths.set(root, (length = ts.factory.createNumericLiteral(root.elements.length)));
|
|
2195
|
+
facts.add(length);
|
|
2196
|
+
seeded = true;
|
|
2197
|
+
}
|
|
2198
|
+
else {
|
|
2199
|
+
const element = root.elements[Number(key)];
|
|
2200
|
+
if (element !== undefined && !ts.isOmittedExpression(element))
|
|
2201
|
+
add(carry(value(element)));
|
|
2202
|
+
else {
|
|
2203
|
+
facts.add(undefinedValue);
|
|
2204
|
+
seeded = true;
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
else {
|
|
2209
|
+
for (const declaration of [...(classMembers(root, key) ?? []), ...(getterRead?.carriers ?? [])]) {
|
|
2210
|
+
if (ts.isMethodDeclaration(declaration)) {
|
|
2211
|
+
facts.add(declaration);
|
|
2212
|
+
seeded = true;
|
|
2213
|
+
}
|
|
2214
|
+
if (ts.isPropertyDeclaration(declaration)) {
|
|
2215
|
+
if (declaration.initializer)
|
|
2216
|
+
add(carry(value(declaration.initializer)));
|
|
2217
|
+
else {
|
|
2218
|
+
facts.add(undefinedValue);
|
|
2219
|
+
seeded = true;
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
if (ts.isParameter(declaration))
|
|
2223
|
+
add(carry(nodeQuery('parameter', declaration)));
|
|
2224
|
+
}
|
|
2225
|
+
if (getterRead !== null) {
|
|
2226
|
+
// The getter runs with this construction as its receiver, and
|
|
2227
|
+
// the read yields what the getter's completions yield.
|
|
2228
|
+
for (const getter of getterRead.getters) {
|
|
2229
|
+
for (const reference of flow.receiverReferencesToDeclaration(getter))
|
|
2230
|
+
add(read(use(root, reference)));
|
|
2231
|
+
const summary = callableCompletionSummaryOf(flow, getter);
|
|
2232
|
+
if (summary === null || summary.execution !== 'sync')
|
|
2233
|
+
fail('unmodelled-getter-completion', getter);
|
|
2234
|
+
else {
|
|
2235
|
+
for (const completion of summary.values)
|
|
2236
|
+
add(carry(value(completion)));
|
|
2237
|
+
if (summary.mayCompleteUndefined) {
|
|
2238
|
+
facts.add(undefinedValue);
|
|
2239
|
+
seeded = true;
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
if (getterRead.absent) {
|
|
2244
|
+
facts.add(undefinedValue);
|
|
2245
|
+
seeded = true;
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
// Only the writes that could name THIS key: the ones that spell it,
|
|
2250
|
+
// and the ones that spell no key at all. Walking every write in the
|
|
2251
|
+
// program here cost O(all writes) per (root, key) -- fine while
|
|
2252
|
+
// computed slots were rare, and quadratic the moment a `for-in` head
|
|
2253
|
+
// started naming a key set, which is what stopped three's
|
|
2254
|
+
// WebGLRenderer finishing.
|
|
2255
|
+
// The stores that go through this root are the root's own closure's
|
|
2256
|
+
// facts -- `visitUse` publishes each one as it walks -- and this
|
|
2257
|
+
// transfer already requires that closure above. Asking the write
|
|
2258
|
+
// index instead meant subscribing to every write naming this key
|
|
2259
|
+
// PLUS every computed write in the program, per slot, permanently:
|
|
2260
|
+
// ~320 edges on each of the three.js app's ~20,000 slot states, and by far
|
|
2261
|
+
// the largest contributor to a 14.7M-edge graph. Same answer, no
|
|
2262
|
+
// dependency this node was not already holding.
|
|
2263
|
+
for (const stored of read(closure(root))) {
|
|
2264
|
+
if (ts.isCallExpression(stored) && definitionPlans.has(stored)) {
|
|
2265
|
+
const plan = definitionOf(stored);
|
|
2266
|
+
if (plan === null || !plan.keys.includes(key) || !read(value(stored.arguments[0])).has(root))
|
|
2267
|
+
continue;
|
|
2268
|
+
if (plan.values.length === 0) {
|
|
2269
|
+
facts.add(undefinedValue);
|
|
2270
|
+
seeded = true;
|
|
2271
|
+
}
|
|
2272
|
+
for (const defined of plan.values)
|
|
2273
|
+
add(carry(value(defined)));
|
|
2274
|
+
continue;
|
|
2275
|
+
}
|
|
2276
|
+
if (ts.isCallExpression(stored)) {
|
|
2277
|
+
// `Object.assign( root, ...sources )`: each source's own value
|
|
2278
|
+
// under this key lands in the root. Only a literal whose keys
|
|
2279
|
+
// are all spelled says what it holds; `null`/`undefined`
|
|
2280
|
+
// sources are skipped by the intrinsic itself.
|
|
2281
|
+
const bulk = bulkAssignOf(stored);
|
|
2282
|
+
if (bulk === null || !read(value(bulk.target)).has(root))
|
|
2283
|
+
continue;
|
|
2284
|
+
for (const source of bulk.sources)
|
|
2285
|
+
for (const held of read(value(source))) {
|
|
2286
|
+
if (held === undefinedValue || held.kind === ts.SyntaxKind.NullKeyword || ts.isVoidExpression(held))
|
|
2287
|
+
continue;
|
|
2288
|
+
if (!ts.isObjectLiteralExpression(held))
|
|
2289
|
+
fail('bulk-assign-source-unmodelled', source);
|
|
2290
|
+
else if (literalSpelledKeys(held) === null || hasUnnamedWrite(held))
|
|
2291
|
+
fail('bulk-assign-source-keys-open', source);
|
|
2292
|
+
else if (descriptorOf(held, key))
|
|
2293
|
+
add(carry(slot(held, key)));
|
|
2294
|
+
}
|
|
2295
|
+
continue;
|
|
2296
|
+
}
|
|
2297
|
+
if (!ts.isPropertyAccessExpression(stored) && !ts.isElementAccessExpression(stored))
|
|
2298
|
+
continue;
|
|
2299
|
+
const edge = writeEdgeByAccess.get(stored);
|
|
2300
|
+
if (edge === undefined)
|
|
2301
|
+
continue;
|
|
2302
|
+
if (!keyReaches(root, keysOf(stored), key))
|
|
2303
|
+
continue;
|
|
2304
|
+
const write = edge.write;
|
|
2305
|
+
if (write.value === null || !['property-assignment', 'index-assignment', 'class-field-initializer'].includes(write.edge)) {
|
|
2306
|
+
fail('unmodelled-property-write', stored);
|
|
2307
|
+
continue;
|
|
2308
|
+
}
|
|
2309
|
+
add(carry(value(write.value)));
|
|
2310
|
+
}
|
|
2311
|
+
break;
|
|
2312
|
+
}
|
|
2313
|
+
case 'construct-targets': {
|
|
2314
|
+
const selected = constructionFrames.targetsOf(query.node);
|
|
2315
|
+
if (selected === null)
|
|
2316
|
+
fail('opaque-construction-frame');
|
|
2317
|
+
else {
|
|
2318
|
+
add(selected);
|
|
2319
|
+
seeded = selected.length > 0;
|
|
2320
|
+
}
|
|
2321
|
+
break;
|
|
2322
|
+
}
|
|
2323
|
+
case 'super-targets': {
|
|
2324
|
+
const dispatch = sites.get(query.node)?.operands.dispatch;
|
|
2325
|
+
// An object-literal home has no `extends` chain to walk, and a
|
|
2326
|
+
// static home starts its lookup on the constructor object, not the
|
|
2327
|
+
// prototype: both are other domains, named rather than mishandled.
|
|
2328
|
+
const home = dispatch?.kind === 'lexical-super' && dispatch.home !== null && !ts.isObjectLiteralExpression(dispatch.home)
|
|
2329
|
+
? dispatch.home
|
|
2330
|
+
: null;
|
|
2331
|
+
if (dispatch?.kind !== 'lexical-super' || dispatch.static || dispatch.key === null || home === null) {
|
|
2332
|
+
fail('unmodelled-super-lookup');
|
|
2333
|
+
break;
|
|
2334
|
+
}
|
|
2335
|
+
const selected = superLookup(home, dispatch.key, new Set());
|
|
2336
|
+
if (selected === null)
|
|
2337
|
+
fail('unresolved-super-lookup', home);
|
|
2338
|
+
else {
|
|
2339
|
+
add(selected);
|
|
2340
|
+
seeded = selected.length > 0;
|
|
2341
|
+
}
|
|
2342
|
+
break;
|
|
2343
|
+
}
|
|
2344
|
+
case 'targets': {
|
|
2345
|
+
const site = sites.get(query.node);
|
|
2346
|
+
if (site?.operands && !site.explicitThis && site.operands.dispatch.kind === 'lexical-super') {
|
|
2347
|
+
add(carry(superTargets(query.node)));
|
|
2348
|
+
break;
|
|
2349
|
+
}
|
|
2350
|
+
// `super()` selects a BASE CONSTRUCTOR, which is the construction
|
|
2351
|
+
// domain's question and already answered there -- the same selection
|
|
2352
|
+
// `new Base()` makes, reached from the subclass's heritage instead of
|
|
2353
|
+
// from a spelled callee.
|
|
2354
|
+
if (site?.operands && !site.explicitThis && site.operands.dispatch.kind === 'super-constructor') {
|
|
2355
|
+
add(carry(nodeQuery('construct-targets', query.node)));
|
|
2356
|
+
break;
|
|
2357
|
+
}
|
|
2358
|
+
// `.call`/`.apply` needs no separate resolver here: `operands.callee`
|
|
2359
|
+
// is already the executable callee with the wrapper removed, and the
|
|
2360
|
+
// thisArg is already out of `args`. What made this dispatch special
|
|
2361
|
+
// was carrying the receiver, which is a FRAME question answered where
|
|
2362
|
+
// frames are projected.
|
|
2363
|
+
if (!site?.operands) {
|
|
2364
|
+
fail('unmodelled-dispatch');
|
|
2365
|
+
break;
|
|
2366
|
+
}
|
|
2367
|
+
for (const candidate of read(value(site.operands.callee))) {
|
|
2368
|
+
if (isBody(candidate) && flow.callableBodyIsIndexed(candidate)) {
|
|
2369
|
+
facts.add(candidate);
|
|
2370
|
+
seeded = true;
|
|
2371
|
+
}
|
|
2372
|
+
else if (candidate.kind !== ts.SyntaxKind.NullKeyword && !ts.isVoidExpression(candidate))
|
|
2373
|
+
fail('opaque-call-target', candidate);
|
|
2374
|
+
}
|
|
2375
|
+
if (sealing && facts.size === 0 && !lookupThrows(query.node))
|
|
2376
|
+
fail('no-source-call-target');
|
|
2377
|
+
break;
|
|
2378
|
+
}
|
|
2379
|
+
case 'completion': {
|
|
2380
|
+
if (ts.isCallExpression(query.node)) {
|
|
2381
|
+
const bulk = bulkAssignOf(query.node);
|
|
2382
|
+
if (bulk !== null) {
|
|
2383
|
+
add(carry(value(bulk.target)));
|
|
2384
|
+
break;
|
|
2385
|
+
}
|
|
2386
|
+
// `store.get( key )` on a native Map this program alone fills:
|
|
2387
|
+
// the completion is one of the values `store.set( _, value )`
|
|
2388
|
+
// stored, whichever key -- the same closed-map proof
|
|
2389
|
+
// (`collectionValueFlowOf`) that refuses the moment the map or
|
|
2390
|
+
// its values leave through anything but these reads. Asked before
|
|
2391
|
+
// `targets`, because a native method has no source body to
|
|
2392
|
+
// select, and the receiver walk would otherwise end at `new Map()`
|
|
2393
|
+
// as an opaque construction. The stores themselves continue at
|
|
2394
|
+
// these reads (`visitUse` below), so the two are one graph.
|
|
2395
|
+
const stored = collectionStoredValuesOf(checker, flow, query.node, nativeProtocolClosed);
|
|
2396
|
+
if (stored !== null) {
|
|
2397
|
+
for (const candidate of stored)
|
|
2398
|
+
add(carry(value(candidate)));
|
|
2399
|
+
break;
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
if (ts.isNewExpression(query.node)) {
|
|
2403
|
+
for (const candidate of read(value(query.node.expression))) {
|
|
2404
|
+
if (ts.isFunctionDeclaration(candidate) &&
|
|
2405
|
+
flow.callableBodyIsIndexed(candidate) &&
|
|
2406
|
+
candidate === constructorFunctionCalleeOf(query.node) &&
|
|
2407
|
+
constructionYieldsCompletionOf(flow, candidate))
|
|
2408
|
+
add(carry(frameQuery('frame-completion', query.node, candidate)));
|
|
2409
|
+
else
|
|
2410
|
+
fail('opaque-construction', candidate);
|
|
2411
|
+
}
|
|
2412
|
+
break;
|
|
2413
|
+
}
|
|
2414
|
+
for (const body of read(targets(query.node))) {
|
|
2415
|
+
if (!isBody(body))
|
|
2416
|
+
fail('unindexed-body', body);
|
|
2417
|
+
else
|
|
2418
|
+
add(carry(frameQuery('frame-completion', query.node, body)));
|
|
2419
|
+
}
|
|
2420
|
+
if (ts.isCallExpression(query.node) && lookupThrows(query.node))
|
|
2421
|
+
seeded = true;
|
|
2422
|
+
break;
|
|
2423
|
+
}
|
|
2424
|
+
case 'frame-receiver':
|
|
2425
|
+
case 'frame-argument':
|
|
2426
|
+
case 'frame-parameter':
|
|
2427
|
+
case 'frame-value':
|
|
2428
|
+
case 'frame-completion': {
|
|
2429
|
+
const layout = sourceInvocationFrameLayoutOf(flow, query.node, query.body);
|
|
2430
|
+
if (!layout) {
|
|
2431
|
+
fail('unindexed-source-frame');
|
|
2432
|
+
break;
|
|
2433
|
+
}
|
|
2434
|
+
const contextual = (expression) => frameQuery('frame-value', query.node, query.body, unwrapErasedExpression(expression));
|
|
2435
|
+
if (query.kind === 'frame-receiver') {
|
|
2436
|
+
const receiver = layout.receiver;
|
|
2437
|
+
if (receiver.kind === 'undefined') {
|
|
2438
|
+
facts.add(undefinedValue);
|
|
2439
|
+
seeded = true;
|
|
2440
|
+
}
|
|
2441
|
+
else if (receiver.kind === 'expression') {
|
|
2442
|
+
const memberCall = !sites.get(query.node)?.explicitThis;
|
|
2443
|
+
for (const candidate of carry(value(receiver.expression))) {
|
|
2444
|
+
// `source.depthTexture.clone()` with no depth texture set:
|
|
2445
|
+
// the member lookup on `null` throws and the frame never runs
|
|
2446
|
+
// with that receiver. Only an explicit `f.call( null )` binds
|
|
2447
|
+
// a nullish receiver.
|
|
2448
|
+
if (memberCall && isNullish(candidate))
|
|
2449
|
+
continue;
|
|
2450
|
+
if (receiver.conversion === 'identity' || isObjectRoot(candidate) || isBody(candidate))
|
|
2451
|
+
facts.add(candidate);
|
|
2452
|
+
else
|
|
2453
|
+
fail(ts.isVoidExpression(candidate) || candidate.kind === ts.SyntaxKind.NullKeyword
|
|
2454
|
+
? 'global-object-receiver-origin'
|
|
2455
|
+
: 'primitive-receiver-conversion', receiver.expression);
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2458
|
+
else if (receiver.kind === 'constructed')
|
|
2459
|
+
add(carry(value(receiver.call)));
|
|
2460
|
+
else if (receiver.kind === 'lexical' && receiver.owner) {
|
|
2461
|
+
if (ts.isSourceFile(receiver.owner)) {
|
|
2462
|
+
if (ts.isExternalModule(receiver.owner)) {
|
|
2463
|
+
facts.add(undefinedValue);
|
|
2464
|
+
seeded = true;
|
|
2465
|
+
}
|
|
2466
|
+
else
|
|
2467
|
+
fail('global-object-receiver-origin', receiver.owner);
|
|
2468
|
+
}
|
|
2469
|
+
else
|
|
2470
|
+
add(carry(nodeQuery('receiver', receiver.owner)));
|
|
2471
|
+
}
|
|
2472
|
+
else
|
|
2473
|
+
fail(receiver.kind === 'unsupported' ? receiver.reason : 'global-object-receiver-origin');
|
|
2474
|
+
}
|
|
2475
|
+
else if (query.kind === 'frame-argument' || query.kind === 'frame-parameter') {
|
|
2476
|
+
const parameter = query.subject;
|
|
2477
|
+
if (!parameter || !ts.isParameter(parameter) || parameter.parent !== query.body) {
|
|
2478
|
+
fail('unindexed-parameter-frame');
|
|
2479
|
+
break;
|
|
2480
|
+
}
|
|
2481
|
+
if (query.kind === 'frame-parameter') {
|
|
2482
|
+
add(carry(frameQuery('frame-argument', query.node, query.body, parameter)));
|
|
2483
|
+
for (const write of flow.writesToDeclaration(parameter)) {
|
|
2484
|
+
if (write.edge === 'call-argument' || write.edge === 'super-argument' || write.edge === 'default-parameter')
|
|
2485
|
+
continue;
|
|
2486
|
+
if (write.slot !== 'whole')
|
|
2487
|
+
continue;
|
|
2488
|
+
if (!['identifier-assignment', 'logical-assignment'].includes(write.edge) || !write.value)
|
|
2489
|
+
fail('unmodelled-parameter-write', write.site);
|
|
2490
|
+
else
|
|
2491
|
+
add(carry(contextual(write.value)));
|
|
2492
|
+
}
|
|
2493
|
+
break;
|
|
2494
|
+
}
|
|
2495
|
+
if (layout.arguments.kind === 'spread') {
|
|
2496
|
+
fail('spread-arguments');
|
|
2497
|
+
break;
|
|
2498
|
+
}
|
|
2499
|
+
const slot = layout.arguments.slots.find((candidate) => candidate.parameter === parameter);
|
|
2500
|
+
if (!slot || slot.kind === 'rest') {
|
|
2501
|
+
fail('unmodelled-parameter-frame', parameter);
|
|
2502
|
+
break;
|
|
2503
|
+
}
|
|
2504
|
+
if (slot.actual) {
|
|
2505
|
+
for (const candidate of carry(value(slot.actual))) {
|
|
2506
|
+
if (ts.isVoidExpression(candidate) && slot.defaultValue)
|
|
2507
|
+
add(carry(contextual(slot.defaultValue)));
|
|
2508
|
+
else
|
|
2509
|
+
facts.add(candidate);
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
else if (slot.defaultValue)
|
|
2513
|
+
add(carry(contextual(slot.defaultValue)));
|
|
2514
|
+
else {
|
|
2515
|
+
facts.add(undefinedValue);
|
|
2516
|
+
seeded = true;
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
else if (query.kind === 'frame-completion') {
|
|
2520
|
+
const summary = callableCompletionSummaryOf(flow, query.body);
|
|
2521
|
+
if (!summary || summary.execution !== 'sync')
|
|
2522
|
+
fail('deferred-completion', query.body);
|
|
2523
|
+
else {
|
|
2524
|
+
for (const expression of summary.values)
|
|
2525
|
+
add(carry(contextual(expression)));
|
|
2526
|
+
if (summary.mayCompleteUndefined || (ts.isCallExpression(query.node) && ts.isCallChain(query.node))) {
|
|
2527
|
+
facts.add(undefinedValue);
|
|
2528
|
+
seeded = true;
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2532
|
+
else {
|
|
2533
|
+
const expression = query.subject;
|
|
2534
|
+
if (!expression || ts.isParameter(expression)) {
|
|
2535
|
+
fail('unindexed-frame-value');
|
|
2536
|
+
break;
|
|
2537
|
+
}
|
|
2538
|
+
const declaration = ts.isIdentifier(expression) ? flow.targetOf(expression)?.declaration : null;
|
|
2539
|
+
if (declaration && ts.isParameter(declaration) && declaration.parent === query.body)
|
|
2540
|
+
add(carry(frameQuery('frame-parameter', query.node, query.body, declaration)));
|
|
2541
|
+
else if (declaration &&
|
|
2542
|
+
ts.isVariableDeclaration(declaration) &&
|
|
2543
|
+
ts.findAncestor(declaration, ts.isFunctionLike) === query.body) {
|
|
2544
|
+
const incoming = localBindingValuesOf(flow, declaration);
|
|
2545
|
+
if (incoming === null)
|
|
2546
|
+
fail('unaccounted-binding-write', declaration);
|
|
2547
|
+
else
|
|
2548
|
+
for (const entry of incoming)
|
|
2549
|
+
add(carry(contextual(entry)));
|
|
2550
|
+
}
|
|
2551
|
+
else if ((expression.kind === ts.SyntaxKind.ThisKeyword || expression.kind === ts.SyntaxKind.SuperKeyword) &&
|
|
2552
|
+
(ts.isArrowFunction(query.body) || flow.receiverOwnerOf(expression) === query.body))
|
|
2553
|
+
add(carry(frameQuery('frame-receiver', query.node, query.body)));
|
|
2554
|
+
else if (ts.isConditionalExpression(expression)) {
|
|
2555
|
+
add(carry(contextual(expression.whenTrue)));
|
|
2556
|
+
add(carry(contextual(expression.whenFalse)));
|
|
2557
|
+
}
|
|
2558
|
+
else if (ts.isPropertyAccessExpression(expression) || ts.isElementAccessExpression(expression)) {
|
|
2559
|
+
const keys = keysOf(expression);
|
|
2560
|
+
for (const root of read(contextual(expression.expression))) {
|
|
2561
|
+
if (isObjectRoot(root))
|
|
2562
|
+
for (const key of keys)
|
|
2563
|
+
add(carry(slot(root, key)));
|
|
2564
|
+
else if (root.kind !== ts.SyntaxKind.NullKeyword && !ts.isVoidExpression(root))
|
|
2565
|
+
fail('non-object-receiver');
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
else if (ts.isBinaryExpression(expression) &&
|
|
2569
|
+
[ts.SyntaxKind.EqualsToken, ts.SyntaxKind.CommaToken].includes(expression.operatorToken.kind))
|
|
2570
|
+
add(carry(contextual(expression.right)));
|
|
2571
|
+
else if (ts.isBinaryExpression(expression) &&
|
|
2572
|
+
[ts.SyntaxKind.BarBarToken, ts.SyntaxKind.AmpersandAmpersandToken, ts.SyntaxKind.QuestionQuestionToken].includes(expression.operatorToken.kind)) {
|
|
2573
|
+
add(carry(contextual(expression.left)));
|
|
2574
|
+
add(carry(contextual(expression.right)));
|
|
2575
|
+
}
|
|
2576
|
+
else
|
|
2577
|
+
add(carry(value(expression)));
|
|
2578
|
+
}
|
|
2579
|
+
break;
|
|
2580
|
+
}
|
|
2581
|
+
case 'parameter': {
|
|
2582
|
+
const parameter = query.node;
|
|
2583
|
+
const owner = parameter.parent;
|
|
2584
|
+
if (ts.isSetAccessorDeclaration(owner)) {
|
|
2585
|
+
setterStores(owner);
|
|
2586
|
+
// The enumeration is exhaustive -- every store through every
|
|
2587
|
+
// instance -- so a setter nothing stores through has an EMPTY
|
|
2588
|
+
// parameter, witnessed here, not one pending some origin.
|
|
2589
|
+
seeded = true;
|
|
2590
|
+
break;
|
|
2591
|
+
}
|
|
2592
|
+
if (!isBody(owner) && !ts.isConstructorDeclaration(owner)) {
|
|
2593
|
+
fail('unindexed-parameter-frame');
|
|
2594
|
+
break;
|
|
2595
|
+
}
|
|
2596
|
+
// A binding pattern names no cell of its own, but the parameter DOES
|
|
2597
|
+
// hold the whole argument -- that value is what each binding element
|
|
2598
|
+
// then reads a slot of. Refusing here made the pattern's container
|
|
2599
|
+
// unknowable, so every receiver destructured out of a parameter was
|
|
2600
|
+
// opaque however completely the program built the object.
|
|
2601
|
+
if (parameter.dotDotDotToken) {
|
|
2602
|
+
fail('unmodelled-parameter-frame');
|
|
2603
|
+
break;
|
|
2604
|
+
}
|
|
2605
|
+
if (isBody(owner))
|
|
2606
|
+
read(closure(owner));
|
|
2607
|
+
for (const site of constructionSitesTargeting(owner))
|
|
2608
|
+
add(carry(frameQuery('frame-argument', site.call, owner, parameter)));
|
|
2609
|
+
for (const call of read(callersOf(owner)))
|
|
2610
|
+
if (ts.isCallExpression(call) || ts.isNewExpression(call))
|
|
2611
|
+
add(carry(frameQuery('frame-argument', call, owner, parameter)));
|
|
2612
|
+
for (const write of flow.writesToDeclaration(parameter)) {
|
|
2613
|
+
if (write.edge === 'call-argument' || write.edge === 'super-argument' || write.edge === 'default-parameter')
|
|
2614
|
+
continue;
|
|
2615
|
+
// A member or element write goes THROUGH the cell into the object
|
|
2616
|
+
// it holds; it never rebinds the cell, so it is no evidence about
|
|
2617
|
+
// what this parameter's value set is. Whether the object can still
|
|
2618
|
+
// be described afterwards is that object's own closure to answer,
|
|
2619
|
+
// and refusing here answered it in the wrong place -- turning
|
|
2620
|
+
// `values[ k ] = 1` into "this parameter holds nothing".
|
|
2621
|
+
if (write.slot !== 'whole')
|
|
2622
|
+
continue;
|
|
2623
|
+
if (!['identifier-assignment', 'logical-assignment'].includes(write.edge) || !write.value) {
|
|
2624
|
+
fail('unmodelled-parameter-write', write.site);
|
|
2625
|
+
continue;
|
|
2626
|
+
}
|
|
2627
|
+
add(carry(value(write.value)));
|
|
2628
|
+
}
|
|
2629
|
+
break;
|
|
2630
|
+
}
|
|
2631
|
+
case 'binding-element': {
|
|
2632
|
+
const element = query.node;
|
|
2633
|
+
const pattern = element.parent;
|
|
2634
|
+
const holder = pattern.parent;
|
|
2635
|
+
const key = bindingElementKey(element);
|
|
2636
|
+
// A rest element holds a FRESH object built from the keys nothing
|
|
2637
|
+
// else took -- unmodelled regardless of presence.
|
|
2638
|
+
if (element.dotDotDotToken)
|
|
2639
|
+
fail('rest-binding-element', element);
|
|
2640
|
+
else if (key === null)
|
|
2641
|
+
fail('computed-binding-element', element);
|
|
2642
|
+
else {
|
|
2643
|
+
// The container is whatever cell holds the object being taken
|
|
2644
|
+
// apart, and every form of it is already a query: a parameter, an
|
|
2645
|
+
// outer binding element of a nested pattern, or the initializer of
|
|
2646
|
+
// a destructuring declaration.
|
|
2647
|
+
const container = bindingContainer(element);
|
|
2648
|
+
if (container === null)
|
|
2649
|
+
fail('unmodelled-binding-container', holder);
|
|
2650
|
+
else {
|
|
2651
|
+
const roots = read(container);
|
|
2652
|
+
// A default makes the cell's value depend on whether the slot
|
|
2653
|
+
// was present -- a presence fact this graph does not otherwise
|
|
2654
|
+
// carry, EXCEPT for the one shape `ownEntries` already answers:
|
|
2655
|
+
// an object-literal root that OWNS this key outright (a plain
|
|
2656
|
+
// property, no spread, no accessor). JS only consults a
|
|
2657
|
+
// destructuring default when the read is exactly `undefined`,
|
|
2658
|
+
// and a literal's own stated property is never that by
|
|
2659
|
+
// omission -- so when every root the container resolves to is
|
|
2660
|
+
// such a literal, the default is dead code and the slot reads
|
|
2661
|
+
// below are the whole, sound answer. Every host-options literal
|
|
2662
|
+
// three passes (`new WebGLRenderer({ canvas, context, depth:
|
|
2663
|
+
// true, ... })`) states every field its constructor destructures
|
|
2664
|
+
// with a default, so refusing EVERY defaulted binding element
|
|
2665
|
+
// unconditionally made `_gl` -- and everything downstream of it,
|
|
2666
|
+
// the whole WebGL host surface -- permanently unenumerable no
|
|
2667
|
+
// matter what the program actually wrote at the call site.
|
|
2668
|
+
const defaultIsLive = element.initializer !== undefined &&
|
|
2669
|
+
(roots.size === 0 || [...roots].some((root) => !ts.isObjectLiteralExpression(root) || ownEntries(root, key) === null));
|
|
2670
|
+
if (defaultIsLive)
|
|
2671
|
+
fail('defaulted-binding-element', element);
|
|
2672
|
+
else
|
|
2673
|
+
for (const root of roots) {
|
|
2674
|
+
if (isObjectRoot(root))
|
|
2675
|
+
add(carry(slot(root, key)));
|
|
2676
|
+
else
|
|
2677
|
+
fail('non-object-destructured-container', element);
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
break;
|
|
2682
|
+
}
|
|
2683
|
+
case 'receiver': {
|
|
2684
|
+
const owner = query.node;
|
|
2685
|
+
if (ts.isSourceFile(owner) && ts.isExternalModule(owner)) {
|
|
2686
|
+
facts.add(undefinedValue);
|
|
2687
|
+
seeded = true;
|
|
2688
|
+
}
|
|
2689
|
+
else if (ts.isConstructorDeclaration(owner) ||
|
|
2690
|
+
ts.isPropertyDeclaration(owner) ||
|
|
2691
|
+
ts.isGetAccessorDeclaration(owner) ||
|
|
2692
|
+
ts.isSetAccessorDeclaration(owner)) {
|
|
2693
|
+
for (const construction of constructionsOf(owner))
|
|
2694
|
+
add(carry(value(construction)));
|
|
2695
|
+
}
|
|
2696
|
+
else if (isBody(owner)) {
|
|
2697
|
+
read(closure(owner));
|
|
2698
|
+
for (const construction of constructionsOf(owner))
|
|
2699
|
+
add(carry(value(construction)));
|
|
2700
|
+
for (const call of read(callersOf(owner)))
|
|
2701
|
+
if (ts.isCallExpression(call))
|
|
2702
|
+
add(carry(frameQuery('frame-receiver', call, owner)));
|
|
2703
|
+
}
|
|
2704
|
+
else
|
|
2705
|
+
fail('unmodelled-receiver-owner');
|
|
2706
|
+
break;
|
|
2707
|
+
}
|
|
2708
|
+
case 'callers': {
|
|
2709
|
+
// The exact filter every inline caller used to run for itself --
|
|
2710
|
+
// moved here so the whole-program walk it does for an unnamed body
|
|
2711
|
+
// is paid once per OWNER, not once per (owner, consumer) pair. See
|
|
2712
|
+
// `callersOf`'s comment for the shape and the measured cost this
|
|
2713
|
+
// replaces.
|
|
2714
|
+
// Discovery by name, soundness by CLOSURE. The spelled calls that
|
|
2715
|
+
// resolve to this owner are its callers only while the owner can
|
|
2716
|
+
// be reached from nowhere else: the body's own closure says where
|
|
2717
|
+
// the callable value goes, and for a class member each instance's
|
|
2718
|
+
// closure says where the receivers go -- an instance handed to
|
|
2719
|
+
// unknown code can be called through from there with arguments no
|
|
2720
|
+
// site spells. A spelled call whose targets are opaque is then
|
|
2721
|
+
// either a call through one of those closures, which already
|
|
2722
|
+
// refused, or a call on something that never holds this owner.
|
|
2723
|
+
// Requiring every candidate's targets instead made `Material.copy`
|
|
2724
|
+
// wait on `targetMatrix.copy( ... )` in three's ColorManagement, a
|
|
2725
|
+
// Matrix3 the graph never places.
|
|
2726
|
+
const owner = query.node;
|
|
2727
|
+
// A constructor is never CALLED: its frames are its construction
|
|
2728
|
+
// sites, which `constructionSitesTargeting` names from the
|
|
2729
|
+
// construction domain, so it has no closure to read here.
|
|
2730
|
+
if (ts.isConstructorDeclaration(owner))
|
|
2731
|
+
break;
|
|
2732
|
+
read(closure(owner));
|
|
2733
|
+
if (ts.isClassElement(owner) && ts.isClassLike(owner.parent)) {
|
|
2734
|
+
if ((ts.getCombinedModifierFlags(owner) & ts.ModifierFlags.Static) !== 0)
|
|
2735
|
+
fail('static-member-callers-open', owner);
|
|
2736
|
+
else
|
|
2737
|
+
for (const instance of familyInstancesOf(owner.parent, 'member-family-open') ?? [])
|
|
2738
|
+
read(closure(instance));
|
|
2739
|
+
}
|
|
2740
|
+
const ownerType = ownerInstanceTypeOf(owner);
|
|
2741
|
+
for (const site of candidateCallersOf(owner)) {
|
|
2742
|
+
// `new WebGLTextures( state )` on a plain function enters the
|
|
2743
|
+
// function's own argument frame exactly as a call would; the
|
|
2744
|
+
// construction domain names no class for it, so it is a caller.
|
|
2745
|
+
if (ts.isNewExpression(site.call)) {
|
|
2746
|
+
if (ts.isFunctionDeclaration(owner) && constructorFunctionCalleeOf(site.call) === owner)
|
|
2747
|
+
facts.add(site.call);
|
|
2748
|
+
continue;
|
|
2749
|
+
}
|
|
2750
|
+
if (!ts.isCallExpression(site.call))
|
|
2751
|
+
continue;
|
|
2752
|
+
if (ownerType !== null && !site.explicitThis && site.operands.dispatch.kind === 'member' && site.operands.receiver !== null) {
|
|
2753
|
+
if (!statedTypeAdmits(checker.getTypeAtLocation(site.operands.receiver), ownerType))
|
|
2754
|
+
continue;
|
|
2755
|
+
}
|
|
2756
|
+
if (observe(targets(site.call)).has(owner))
|
|
2757
|
+
facts.add(site.call);
|
|
2758
|
+
}
|
|
2759
|
+
break;
|
|
2760
|
+
}
|
|
2761
|
+
case 'closure': {
|
|
2762
|
+
const root = query.node;
|
|
2763
|
+
if (ts.isFunctionDeclaration(root)) {
|
|
2764
|
+
if ((ts.getCombinedModifierFlags(root) & ts.ModifierFlags.Export) !== 0 && !exportIsUnimported(checker, flow, root))
|
|
2765
|
+
fail('exported-callable');
|
|
2766
|
+
if (ts.isSourceFile(root.parent) && !ts.isExternalModule(root.parent))
|
|
2767
|
+
fail('global-object-callable');
|
|
2768
|
+
refs(root, root);
|
|
2769
|
+
}
|
|
2770
|
+
else if (ts.isMethodDeclaration(root)) {
|
|
2771
|
+
if (ts.isObjectLiteralExpression(root.parent)) {
|
|
2772
|
+
const key = declarationKey(root.name);
|
|
2773
|
+
if (key === null)
|
|
2774
|
+
fail('computed-method');
|
|
2775
|
+
else
|
|
2776
|
+
add(read(publication(root, root.parent, key)));
|
|
2777
|
+
}
|
|
2778
|
+
else
|
|
2779
|
+
refs(root, root);
|
|
2780
|
+
}
|
|
2781
|
+
else if (ts.isExpression(root))
|
|
2782
|
+
add(read(use(root, root)));
|
|
2783
|
+
else
|
|
2784
|
+
fail('unmodelled-root');
|
|
2785
|
+
if (ts.isNewExpression(root)) {
|
|
2786
|
+
const owners = constructionFrames.receiverOwnersOf(root);
|
|
2787
|
+
if (!owners) {
|
|
2788
|
+
fail('opaque-construction');
|
|
2789
|
+
break;
|
|
2790
|
+
}
|
|
2791
|
+
for (const owner of owners) {
|
|
2792
|
+
if (!isClassSpelledSourceClass(owner)) {
|
|
2793
|
+
for (const reference of flow.receiverReferencesToDeclaration(owner))
|
|
2794
|
+
add(read(use(root, reference)));
|
|
2795
|
+
continue;
|
|
2796
|
+
}
|
|
2797
|
+
for (const member of owner.members) {
|
|
2798
|
+
if (ts.isConstructorDeclaration(member) ||
|
|
2799
|
+
(ts.isPropertyDeclaration(member) && (ts.getCombinedModifierFlags(member) & ts.ModifierFlags.Static) === 0))
|
|
2800
|
+
for (const reference of flow.receiverReferencesToDeclaration(member))
|
|
2801
|
+
add(read(use(root, reference)));
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2804
|
+
}
|
|
2805
|
+
break;
|
|
2806
|
+
}
|
|
2807
|
+
case 'use':
|
|
2808
|
+
visitUse(query.root, query.node);
|
|
2809
|
+
break;
|
|
2810
|
+
case 'unknown-reads': {
|
|
2811
|
+
// Inverted from a scan of all 968 receivers to a reverse-index
|
|
2812
|
+
// lookup: `holdersOf` hands back the receiver queries that hold
|
|
2813
|
+
// THIS container, so the container asks once instead of every
|
|
2814
|
+
// container asking the same 968-entry list. A holder that is not
|
|
2815
|
+
// one of the unknown-read receivers -- a `slot`, `parameter` or any
|
|
2816
|
+
// other kind of state that also happens to hold this exact node --
|
|
2817
|
+
// is not in `unknownReadsByReceiver` and is simply skipped.
|
|
2818
|
+
for (const holder of holdersOf(query.node))
|
|
2819
|
+
for (const entry of unknownReadsByReceiver.get(holder) ?? NO_READS)
|
|
2820
|
+
facts.add(entry.access);
|
|
2821
|
+
break;
|
|
2822
|
+
}
|
|
2823
|
+
case 'publication': {
|
|
2824
|
+
read(closure(query.node));
|
|
2825
|
+
// A container merged into another by `Object.assign` hands this
|
|
2826
|
+
// key's value to the target as well.
|
|
2827
|
+
for (const stored of read(closure(query.node))) {
|
|
2828
|
+
if (!ts.isCallExpression(stored))
|
|
2829
|
+
continue;
|
|
2830
|
+
const bulk = bulkAssignOf(stored);
|
|
2831
|
+
if (bulk === null || !bulk.sources.some((source) => read(value(source)).has(query.node)))
|
|
2832
|
+
continue;
|
|
2833
|
+
for (const held of read(value(bulk.target)))
|
|
2834
|
+
if (isObjectRoot(held))
|
|
2835
|
+
add(read(publication(query.root, held, query.key)));
|
|
2836
|
+
}
|
|
2837
|
+
for (const stored of read(closure(query.node))) {
|
|
2838
|
+
if (!ts.isCallExpression(stored) || !definitionPlans.has(stored) || query.key !== 'value')
|
|
2839
|
+
continue;
|
|
2840
|
+
const plan = definitionOf(stored);
|
|
2841
|
+
if (plan === null || !read(value(stored.arguments[2])).has(query.node))
|
|
2842
|
+
continue;
|
|
2843
|
+
for (const held of read(value(stored.arguments[0])))
|
|
2844
|
+
if (isObjectRoot(held))
|
|
2845
|
+
for (const key of plan.keys)
|
|
2846
|
+
add(read(publication(query.root, held, key)));
|
|
2847
|
+
}
|
|
2848
|
+
const aliases = ts.isArrayLiteralExpression(query.node)
|
|
2849
|
+
? query.key === ELEMENT_KEY
|
|
2850
|
+
? [...readsByKey.keys()].filter((key) => key === ELEMENT_KEY || isCanonicalIndex(key))
|
|
2851
|
+
: isCanonicalIndex(query.key)
|
|
2852
|
+
? [query.key, ELEMENT_KEY]
|
|
2853
|
+
: [query.key]
|
|
2854
|
+
: [query.key];
|
|
2855
|
+
for (const alias of aliases)
|
|
2856
|
+
for (const entry of readsByKey.get(alias) ?? NO_READS) {
|
|
2857
|
+
if (!observe(entry.receiver).has(query.node))
|
|
2858
|
+
continue;
|
|
2859
|
+
if (keyReaches(query.node, keysOf(entry.access), query.key))
|
|
2860
|
+
add(read(use(query.root, entry.access)));
|
|
2861
|
+
}
|
|
2862
|
+
// Discovery-only, exactly as the receiver test it replaces was: a
|
|
2863
|
+
// publication never required the unknown reads to have settled, it
|
|
2864
|
+
// only had to see the ones that had.
|
|
2865
|
+
for (const access of observe(unknownReadsOf(query.node))) {
|
|
2866
|
+
if (!ts.isPropertyAccessExpression(access) && !ts.isElementAccessExpression(access))
|
|
2867
|
+
continue;
|
|
2868
|
+
if (keyReaches(query.node, keysOf(access), query.key))
|
|
2869
|
+
add(read(use(query.root, access)));
|
|
2870
|
+
}
|
|
2871
|
+
// The value this key holds also reaches every cell a binding element
|
|
2872
|
+
// of this container declares. Enumerating only `propertyAccesses`
|
|
2873
|
+
// left `const { app } = p; sink(app)` out of the inventory, which is
|
|
2874
|
+
// a hole in a closure proof, not a missing capability.
|
|
2875
|
+
for (const element of both(patternReadsByKey.get(query.key), unknownPatternReads)) {
|
|
2876
|
+
const container = bindingContainer(element);
|
|
2877
|
+
if (container === null || !observe(container).has(query.node))
|
|
2878
|
+
continue;
|
|
2879
|
+
if (element.dotDotDotToken || bindingElementKey(element) === null)
|
|
2880
|
+
fail('unmodelled-container-read', element);
|
|
2881
|
+
else
|
|
2882
|
+
refs(query.root, element);
|
|
2883
|
+
}
|
|
2884
|
+
break;
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
finalCauses = causes;
|
|
2888
|
+
// Only the seal reads the grounding edges, and the seal is the last
|
|
2889
|
+
// call this query receives; materializing the list on every transfer
|
|
2890
|
+
// allocated one object per carried edge per re-evaluation for nothing.
|
|
2891
|
+
if (sealing)
|
|
2892
|
+
finalGrounding = [...grounding].map((dependency) => ({ domain: VALUE_ORIGIN, dependency }));
|
|
2893
|
+
return facts;
|
|
2894
|
+
};
|
|
2895
|
+
const run = (read, observe, holdersOf, sealing) => {
|
|
2896
|
+
const ledger = deferredIntrinsicProtocolLedgerOf(flow);
|
|
2897
|
+
if (!ledger)
|
|
2898
|
+
return evaluate(read, observe, holdersOf, sealing);
|
|
2899
|
+
const captured = ledger.capture(() => evaluate(read, observe, holdersOf, sealing));
|
|
2900
|
+
// Overwrite, not union -- like `finalCauses`/`finalGrounding` above,
|
|
2901
|
+
// this is safe ONLY because `evaluate` is monotone: `facts` published to
|
|
2902
|
+
// the solver (`component-solver.ts`'s `state.facts`) accumulate forever
|
|
2903
|
+
// regardless of what any one call returns, so the LAST call for this
|
|
2904
|
+
// query (the seal that ends the re-seal loop) is the only one whose
|
|
2905
|
+
// `facts` can differ from that accumulated set, and then only by being
|
|
2906
|
+
// equal to it. `arrayElementKeys`' widen-on-empty was the one place that
|
|
2907
|
+
// invariant broke -- an early pass published every element as a
|
|
2908
|
+
// permanent fact that a later, exact pass never re-derived, so the
|
|
2909
|
+
// ledger requirement that early pass owed was needed forever but this
|
|
2910
|
+
// map would have forgotten it the moment a narrower pass overwrote it.
|
|
2911
|
+
// With that widening now gated on `sealing`, no known transfer in this
|
|
2912
|
+
// file can produce fewer facts on a later call than an earlier one
|
|
2913
|
+
// already published, and a requirement that is raised without being
|
|
2914
|
+
// needed is merely a stricter refusal, while a needed one gone missing
|
|
2915
|
+
// is a wrong answer -- so if this invariant is ever broken again by a
|
|
2916
|
+
// NEW transfer, union (not overwrite) is the direction that cannot lie:
|
|
2917
|
+
// `both(requirements.get(query), captured.requirements)`.
|
|
2918
|
+
requirements.set(query, captured.requirements);
|
|
2919
|
+
return captured.value;
|
|
2920
|
+
};
|
|
2921
|
+
return {
|
|
2922
|
+
transfer: (read, observe, holdersOf) => run(read, observe, holdersOf, false),
|
|
2923
|
+
seal: (read, observe, holdersOf) => {
|
|
2924
|
+
run(read, observe, holdersOf, true);
|
|
2925
|
+
const carriesValue = VALUE_CARRYING_KINDS.has(query.kind);
|
|
2926
|
+
return {
|
|
2927
|
+
locallyComplete: finalCauses.length === 0,
|
|
2928
|
+
causes: finalCauses,
|
|
2929
|
+
groundingRequirements: carriesValue ? [VALUE_ORIGIN] : [],
|
|
2930
|
+
groundingSeeds: seeded ? [VALUE_ORIGIN] : [],
|
|
2931
|
+
groundingDependencies: finalGrounding
|
|
2932
|
+
};
|
|
2933
|
+
}
|
|
2934
|
+
};
|
|
2935
|
+
};
|
|
2936
|
+
const { solve, requiredDependenciesOf, seed } = dependencyFactSolver(define, 'grounding-domains');
|
|
2937
|
+
// `case 'unknown-reads'` now asks `holdersOf` for the receivers that hold a
|
|
2938
|
+
// container, instead of `observe`-ing all 968 of them itself -- but a
|
|
2939
|
+
// reverse index only finds states that already EXIST and have already run.
|
|
2940
|
+
// The `observe` calls it replaces used to materialise every receiver as a
|
|
2941
|
+
// SIDE EFFECT of `factsFrom`'s own `stateOf`; dropping them without this
|
|
2942
|
+
// loop would leave a receiver nothing else in the program asks about never
|
|
2943
|
+
// created; `holdersOf(container)` would then silently return nothing for
|
|
2944
|
+
// it, missing a read that program prints. Seeded once here, for the whole
|
|
2945
|
+
// session, not once per container: 968 scheduling operations total, zero
|
|
2946
|
+
// stored edges, against the 455,769 observe edges this replaces.
|
|
2947
|
+
for (const entry of unknownReads)
|
|
2948
|
+
seed(entry.receiver);
|
|
2949
|
+
// `hasUnnamedWrite` asks the same reverse question of every computed write's receiver.
|
|
2950
|
+
for (const receiver of computedWriteReceiverQueries)
|
|
2951
|
+
seed(receiver);
|
|
2952
|
+
const obligations = (root) => {
|
|
2953
|
+
const found = [];
|
|
2954
|
+
const visited = new Set();
|
|
2955
|
+
const queue = [root];
|
|
2956
|
+
for (let index = 0; index < queue.length; index++) {
|
|
2957
|
+
const query = queue[index];
|
|
2958
|
+
if (visited.has(query))
|
|
2959
|
+
continue;
|
|
2960
|
+
visited.add(query);
|
|
2961
|
+
found.push(...(requirements.get(query) ?? []));
|
|
2962
|
+
for (const dependency of requiredDependenciesOf(query))
|
|
2963
|
+
queue.push(dependency);
|
|
2964
|
+
}
|
|
2965
|
+
return found;
|
|
2966
|
+
};
|
|
2967
|
+
// A literal's function-valued entry (`convert: function ( ... ) { ... }`)
|
|
2968
|
+
// is the same slot as its shorthand method: one literal, one write, no
|
|
2969
|
+
// nominal family. Both spellings occur in three's `ColorManagement`.
|
|
2970
|
+
const isObjectLiteralCallableEntry = (declaration) => ts.isObjectLiteralExpression(declaration.parent) &&
|
|
2971
|
+
!declaration.getSourceFile().isDeclarationFile &&
|
|
2972
|
+
(isSourceInstanceMethod(declaration) ||
|
|
2973
|
+
(ts.isPropertyAssignment(declaration) &&
|
|
2974
|
+
(ts.isFunctionExpression(declaration.initializer) ||
|
|
2975
|
+
ts.isArrowFunction(declaration.initializer) ||
|
|
2976
|
+
// `{ texImage3D: texImage3D }`: the entry names a source function;
|
|
2977
|
+
// the slot's value is that function's binding, which the graph
|
|
2978
|
+
// resolves as it resolves any identifier.
|
|
2979
|
+
(ts.isIdentifier(declaration.initializer) && isBody(flow.targetOf(declaration.initializer)?.declaration ?? declaration)))));
|
|
2980
|
+
const admitted = (query) => {
|
|
2981
|
+
const result = solve(query);
|
|
2982
|
+
if (result.status !== 'complete')
|
|
2983
|
+
return null;
|
|
2984
|
+
const required = obligations(query);
|
|
2985
|
+
if (required.length && deferredIntrinsicProtocolLedgerOf(flow)?.include(required) !== true)
|
|
2986
|
+
return null;
|
|
2987
|
+
return [...result.facts];
|
|
2988
|
+
};
|
|
2989
|
+
const invocationTargetsOf = (call) => {
|
|
2990
|
+
const found = admitted(targets(call));
|
|
2991
|
+
return found !== null && found.every(isCallableValue) ? found : null;
|
|
2992
|
+
};
|
|
2993
|
+
return {
|
|
2994
|
+
ownsInvocation: (call) => {
|
|
2995
|
+
const site = sites.get(call);
|
|
2996
|
+
// Wrapper integrity and lexical-super lookup are separate dispatch
|
|
2997
|
+
// domains still owned by the existing invocation authority. Partition
|
|
2998
|
+
// before solving; failure within the ordinary-member domain is final.
|
|
2999
|
+
if (!site || site.explicitThis || site.operands.dispatch.kind !== 'member')
|
|
3000
|
+
return false;
|
|
3001
|
+
const callee = site.operands.callee;
|
|
3002
|
+
if (!callee || (!ts.isPropertyAccessExpression(callee) && !ts.isElementAccessExpression(callee)))
|
|
3003
|
+
return false;
|
|
3004
|
+
const symbol = flow.targetOf(callee)?.nameSymbol ?? flow.targetOf(callee)?.symbol;
|
|
3005
|
+
const declarations = symbol?.declarations;
|
|
3006
|
+
if (!declarations?.length)
|
|
3007
|
+
return false;
|
|
3008
|
+
// Ordinary source interface-member calls only (§5's cut-over row) --
|
|
3009
|
+
// the graph owns these with no fallback.
|
|
3010
|
+
if (declarations.some((declaration) => !declaration.getSourceFile().isDeclarationFile && (ts.isPropertySignature(declaration) || ts.isMethodSignature(declaration))))
|
|
3011
|
+
return true;
|
|
3012
|
+
// An OBJECT-LITERAL method has no nominal family: no `super`, no
|
|
3013
|
+
// override set to enumerate, no base constructor, no class receiver
|
|
3014
|
+
// forwarded through `new`. None of the four facts listed below applies
|
|
3015
|
+
// to it, and the graph already resolves a literal's slots joint with
|
|
3016
|
+
// the writes that replace them (source-value-session.test.ts). So the
|
|
3017
|
+
// graph owns these now; narrowing the whole `isSourceInstanceMethod`
|
|
3018
|
+
// widening back to the legacy resolver took literal-method-receiver
|
|
3019
|
+
// from 2/3 to 1/3 -- the legacy authority never counted
|
|
3020
|
+
// `ColorManagement.define( ... )` on a literal returned by a factory.
|
|
3021
|
+
if (declarations.every(isObjectLiteralCallableEntry))
|
|
3022
|
+
return true;
|
|
3023
|
+
// Ordinary CLASS methods stay with the legacy authority (§5's
|
|
3024
|
+
// "not migrated" row). Admitting them too, via
|
|
3025
|
+
// `declarations.every(isSourceInstanceMethod)`, was tried and measured
|
|
3026
|
+
// (SEMANTIC-AUTHORITY.md §7 item 5, "the partition, not landed"): it
|
|
3027
|
+
// broke 16 tests across 8 suites plus a new `computed-key-set` failure,
|
|
3028
|
+
// clustering on four still-unbuilt facts --
|
|
3029
|
+
// - lexical `super` beside an ordinary override (invocation-dispatch)
|
|
3030
|
+
// - computed member closure / override-family enumeration for an
|
|
3031
|
+
// EXACT allocation rather than a whole nominal family
|
|
3032
|
+
// (exact-class-allocation-origins, computed-key-set: `this[key] = v`)
|
|
3033
|
+
// - base-constructor setter following (closed-callable-authority)
|
|
3034
|
+
// - frame details: safe `arguments` reads, rest-array element
|
|
3035
|
+
// continuations, TypeScript `this` positions, imported-function
|
|
3036
|
+
// receiver evidence, and a receiver forwarded through a `new`
|
|
3037
|
+
// constructor argument (member-call-forwarding,
|
|
3038
|
+
// source-invocation-layout, array-element-continuation,
|
|
3039
|
+
// class-origin-imports, arguments-frame-member-closure,
|
|
3040
|
+
// mutable-method-parameters)
|
|
3041
|
+
//
|
|
3042
|
+
// Re-measured 2026-09-14 against a later tree (after
|
|
3043
|
+
// `sourceClassCallableMemberPlanOf` and the member-name-is-a-slot-read
|
|
3044
|
+
// fix, commit 83ad7008b and later, §7 item 5 "the missing fact,
|
|
3045
|
+
// built"): widening `ownsInvocation` to
|
|
3046
|
+
// `declarations.every(isSourceInstanceMethod)` now leaves SIX of the
|
|
3047
|
+
// nine files above green -- `invocation-dispatch`,
|
|
3048
|
+
// `exact-class-allocation-origins`, `member-call-forwarding`,
|
|
3049
|
+
// `source-invocation-layout`, `array-element-continuation` and
|
|
3050
|
+
// `class-origin-imports` all pass unmodified. THREE facts remain
|
|
3051
|
+
// unbuilt, each reproduced by exactly one still-failing suite:
|
|
3052
|
+
// - `mutable-method-parameters.test.ts` (3/37 fail): once a class
|
|
3053
|
+
// method's own member calls route through the graph, the legacy
|
|
3054
|
+
// escape walk that `parameter-bindings.ts` still asks
|
|
3055
|
+
// (`hasClosedMemberCallableUses`) cannot read the graph's answer
|
|
3056
|
+
// for those calls and reports the receiver open, so a parameter
|
|
3057
|
+
// bound only through sibling class-method calls loses its type.
|
|
3058
|
+
// - `arguments-frame-member-closure.test.ts` (1/2 fail): a receiver
|
|
3059
|
+
// forwarded through `arguments[ i ]` inside a recursive self-call
|
|
3060
|
+
// (three's `Object3D.add` multi-child form) is not a shape the
|
|
3061
|
+
// graph's `targets`/`value` queries resolve yet, so the whole
|
|
3062
|
+
// family closure opens with
|
|
3063
|
+
// `function-escapes:uncounted-member-reference`. This is the
|
|
3064
|
+
// single largest leaf-refusal shape measured on the three.js app
|
|
3065
|
+
// (`measurements/leaf-refusals.log`, 2026-09-14): a receiver
|
|
3066
|
+
// forwarded through a call argument is not followed -- see §7
|
|
3067
|
+
// item 0's `uncounted-member-reference` breakdown.
|
|
3068
|
+
// - `computed-key-set.test.ts` (1/10 fail, "three's setValues,
|
|
3069
|
+
// reduced"): the refusal reason regresses from the precise
|
|
3070
|
+
// `origin-computed-write` to the earlier, coarser
|
|
3071
|
+
// `object-origin-open-parameter` -- the graph trips a less
|
|
3072
|
+
// specific refusal before it reaches the computed-write site.
|
|
3073
|
+
// - `closed-callable-authority.test.ts` (1/20 fail, "a closed
|
|
3074
|
+
// script realm includes writes and callers from every script"):
|
|
3075
|
+
// the graph's caller closure for a class method shared by two
|
|
3076
|
+
// files under one `ClosedScriptScope` does not yet match the
|
|
3077
|
+
// legacy caller walk it is meant to replace.
|
|
3078
|
+
// Complete those, THEN widen this predicate, then delete the legacy
|
|
3079
|
+
// resolver -- in that order, and never by relaxing a test.
|
|
3080
|
+
return false;
|
|
3081
|
+
},
|
|
3082
|
+
invocationTargetsOf,
|
|
3083
|
+
valuesOf: (expression) => admitted(value(expression)),
|
|
3084
|
+
parameterValuesOf: (parameter) => admitted(nodeQuery('parameter', parameter)),
|
|
3085
|
+
explainParameter: (parameter) => solve(nodeQuery('parameter', parameter)).explain(),
|
|
3086
|
+
explainInvocation: (call) => solve(targets(call)).explain(),
|
|
3087
|
+
explainValue: (expression) => solve(value(expression)).explain()
|
|
3088
|
+
};
|
|
3089
|
+
};
|