@scriptc/compiler 0.2.8 → 0.2.10
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/ambient/scriptc-node-fallback.d.ts +1 -0
- package/dist/backend/cycle-analysis.d.ts +56 -0
- package/dist/backend/cycle-analysis.js +222 -28
- package/dist/backend/cycle-analysis.js.map +1 -1
- package/dist/backend/cycle-analysis.test.js +232 -15
- package/dist/backend/cycle-analysis.test.js.map +1 -1
- package/dist/backend/cycle-mutability.d.ts +27 -6
- package/dist/backend/cycle-mutability.js +110 -17
- package/dist/backend/cycle-mutability.js.map +1 -1
- package/dist/backend/immutability.d.ts +57 -0
- package/dist/backend/immutability.js +178 -0
- package/dist/backend/immutability.js.map +1 -0
- package/dist/backend/immutability.test.d.ts +1 -0
- package/dist/backend/immutability.test.js +131 -0
- package/dist/backend/immutability.test.js.map +1 -0
- package/dist/backend/llvm/alloc.d.ts +13 -3
- package/dist/backend/llvm/alloc.js +24 -10
- package/dist/backend/llvm/alloc.js.map +1 -1
- package/dist/backend/llvm/alloc.test.js +5 -5
- package/dist/backend/llvm/alloc.test.js.map +1 -1
- package/dist/backend/llvm/borrowed-inputs.d.ts +9 -2
- package/dist/backend/llvm/borrowed-inputs.js +31 -3
- package/dist/backend/llvm/borrowed-inputs.js.map +1 -1
- package/dist/backend/llvm/byte-numbers.js +2 -3
- package/dist/backend/llvm/byte-numbers.js.map +1 -1
- package/dist/backend/llvm/classes.d.ts +28 -0
- package/dist/backend/llvm/classes.js +51 -10
- package/dist/backend/llvm/classes.js.map +1 -1
- package/dist/backend/llvm/classes.test.js +129 -1
- package/dist/backend/llvm/classes.test.js.map +1 -1
- package/dist/backend/llvm/common.d.ts +30 -0
- package/dist/backend/llvm/common.js +41 -0
- package/dist/backend/llvm/common.js.map +1 -1
- package/dist/backend/llvm/dense-array-access.js +6 -5
- package/dist/backend/llvm/dense-array-access.js.map +1 -1
- package/dist/backend/llvm/dense-array-access.test.js +10 -2
- package/dist/backend/llvm/dense-array-access.test.js.map +1 -1
- package/dist/backend/llvm/dyn.d.ts +6 -4
- package/dist/backend/llvm/dyn.js +30 -14
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.d.ts +109 -5
- package/dist/backend/llvm/emitter.js +716 -112
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/emitter.test.js +54 -3
- package/dist/backend/llvm/emitter.test.js.map +1 -1
- package/dist/backend/llvm/expr-bytes.js +35 -28
- package/dist/backend/llvm/expr-bytes.js.map +1 -1
- package/dist/backend/llvm/expr-calls.js +14 -6
- package/dist/backend/llvm/expr-calls.js.map +1 -1
- package/dist/backend/llvm/expr-containers.js +31 -8
- package/dist/backend/llvm/expr-containers.js.map +1 -1
- package/dist/backend/llvm/expr-context.d.ts +2 -0
- package/dist/backend/llvm/expr-control.js +46 -5
- package/dist/backend/llvm/expr-control.js.map +1 -1
- package/dist/backend/llvm/expr-dynamic.js +3 -0
- package/dist/backend/llvm/expr-dynamic.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.js +30 -3
- package/dist/backend/llvm/expr-primitives.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.test.js +53 -0
- package/dist/backend/llvm/expr-primitives.test.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.d.ts +3 -0
- package/dist/backend/llvm/expr-stream-bridges.js +76 -14
- package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
- package/dist/backend/llvm/lib-filesystem.js +18 -2
- package/dist/backend/llvm/lib-filesystem.js.map +1 -1
- package/dist/backend/llvm/lib-shared.js +1 -0
- package/dist/backend/llvm/lib-shared.js.map +1 -1
- package/dist/backend/llvm/local-array-reads.d.ts +17 -6
- package/dist/backend/llvm/local-array-reads.js +157 -45
- package/dist/backend/llvm/local-array-reads.js.map +1 -1
- package/dist/backend/llvm/loop-array-borrows.d.ts +5 -2
- package/dist/backend/llvm/loop-array-borrows.js +27 -8
- package/dist/backend/llvm/loop-array-borrows.js.map +1 -1
- package/dist/backend/llvm/map-number-lookup.d.ts +7 -0
- package/dist/backend/llvm/map-number-lookup.js +18 -1
- package/dist/backend/llvm/map-number-lookup.js.map +1 -1
- package/dist/backend/llvm/map-read-lifetimes.d.ts +5 -0
- package/dist/backend/llvm/map-read-lifetimes.js +14 -4
- package/dist/backend/llvm/map-read-lifetimes.js.map +1 -1
- package/dist/backend/llvm/map-read-lifetimes.test.js +5 -0
- package/dist/backend/llvm/map-read-lifetimes.test.js.map +1 -1
- package/dist/backend/llvm/reference-effects.js +34 -1
- package/dist/backend/llvm/reference-effects.js.map +1 -1
- package/dist/backend/llvm/reference-writes.d.ts +124 -0
- package/dist/backend/llvm/reference-writes.js +1090 -0
- package/dist/backend/llvm/reference-writes.js.map +1 -0
- package/dist/backend/llvm/shapes.d.ts +22 -8
- package/dist/backend/llvm/shapes.js +79 -23
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/backend/llvm/shapes.test.js +5 -5
- package/dist/backend/llvm/shapes.test.js.map +1 -1
- package/dist/backend/llvm/string-lifetimes.test.js +39 -10
- package/dist/backend/llvm/string-lifetimes.test.js.map +1 -1
- package/dist/backend/llvm/string-reads.d.ts +3 -0
- package/dist/backend/llvm/string-reads.js +45 -11
- package/dist/backend/llvm/string-reads.js.map +1 -1
- package/dist/backend/llvm/string-slices.js +1 -1
- package/dist/backend/llvm/switch-dispatch.d.ts +2 -2
- package/dist/backend/llvm/switch-dispatch.js +35 -22
- package/dist/backend/llvm/switch-dispatch.js.map +1 -1
- package/dist/backend/llvm/switch-dispatch.test.js +28 -10
- package/dist/backend/llvm/switch-dispatch.test.js.map +1 -1
- package/dist/backend/llvm/trunc.d.ts +8 -0
- package/dist/backend/llvm/trunc.js +28 -0
- package/dist/backend/llvm/trunc.js.map +1 -0
- package/dist/backend/llvm/walk-borrows.d.ts +8 -1
- package/dist/backend/llvm/walk-borrows.js +15 -4
- package/dist/backend/llvm/walk-borrows.js.map +1 -1
- package/dist/backend/loop-polls.d.ts +14 -0
- package/dist/backend/loop-polls.js +100 -0
- package/dist/backend/loop-polls.js.map +1 -0
- package/dist/backend/loop-polls.test.d.ts +1 -0
- package/dist/backend/loop-polls.test.js +86 -0
- package/dist/backend/loop-polls.test.js.map +1 -0
- package/dist/backend/may-throw.d.ts +36 -2
- package/dist/backend/may-throw.js +328 -13
- package/dist/backend/may-throw.js.map +1 -1
- package/dist/backend/may-throw.test.js +169 -1
- package/dist/backend/may-throw.test.js.map +1 -1
- package/dist/backend/native-codegen.d.ts +1 -3
- package/dist/backend/native-codegen.js +4 -32
- package/dist/backend/native-codegen.js.map +1 -1
- package/dist/backend/publication.d.ts +5 -0
- package/dist/backend/publication.js +10 -0
- package/dist/backend/publication.js.map +1 -1
- package/dist/backend/runtime-abi.d.ts +2 -2
- package/dist/backend/runtime-abi.js +2 -2
- package/dist/backend/runtime-abi.js.map +1 -1
- package/dist/backend/runtime-pack-targets.test.js +1 -1
- package/dist/backend/runtime-pack-targets.test.js.map +1 -1
- package/dist/backend/runtime-pack.test.js +1 -1
- package/dist/backend/runtime-pack.test.js.map +1 -1
- package/dist/cli/command.js +8 -1
- package/dist/cli/command.js.map +1 -1
- package/dist/cli/output-options.d.ts +1 -0
- package/dist/cli/output-options.js +3 -0
- package/dist/cli/output-options.js.map +1 -1
- package/dist/cli/usage.d.ts +5 -1
- package/dist/cli/usage.js +6 -0
- package/dist/cli/usage.js.map +1 -1
- package/dist/compile-types.d.ts +5 -0
- package/dist/contracts/check.d.ts +8 -0
- package/dist/contracts/check.js +1577 -0
- package/dist/contracts/check.js.map +1 -0
- package/dist/executable/executable-cache.d.ts +2 -0
- package/dist/executable/executable-cache.js +3 -0
- package/dist/executable/executable-cache.js.map +1 -1
- package/dist/executable/executable-cache.test.js +1 -0
- package/dist/executable/executable-cache.test.js.map +1 -1
- package/dist/executable/prepare.js +9 -0
- package/dist/executable/prepare.js.map +1 -1
- package/dist/frontend/contract-types.d.ts +50 -0
- package/dist/frontend/contract-types.js +232 -0
- package/dist/frontend/contract-types.js.map +1 -0
- package/dist/frontend/lowering/array-values.js +37 -1
- package/dist/frontend/lowering/array-values.js.map +1 -1
- package/dist/frontend/lowering/builtins/strings.js +4 -1
- package/dist/frontend/lowering/builtins/strings.js.map +1 -1
- package/dist/frontend/lowering/call-arguments.js +96 -4
- package/dist/frontend/lowering/call-arguments.js.map +1 -1
- package/dist/frontend/lowering/class-method-mutations.js +88 -0
- package/dist/frontend/lowering/class-method-mutations.js.map +1 -1
- package/dist/frontend/lowering/coercions/unions.d.ts +12 -0
- package/dist/frontend/lowering/coercions/unions.js +63 -0
- package/dist/frontend/lowering/coercions/unions.js.map +1 -1
- package/dist/frontend/lowering/containers/bytes.js +15 -4
- package/dist/frontend/lowering/containers/bytes.js.map +1 -1
- package/dist/frontend/lowering/expressions/optional-chains.js +4 -1
- package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -1
- package/dist/frontend/lowering/expressions/union-fields.d.ts +19 -0
- package/dist/frontend/lowering/expressions/union-fields.js +236 -0
- package/dist/frontend/lowering/expressions/union-fields.js.map +1 -0
- package/dist/frontend/lowering/lower-calls.js +21 -4
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +6 -0
- package/dist/frontend/lowering/lower-exprs.js +245 -4
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-island.js +5 -19
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.js +11 -1
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +14 -0
- package/dist/frontend/lowering/lowerer.js +221 -8
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/nan-coded-reads.d.ts +161 -0
- package/dist/frontend/lowering/nan-coded-reads.js +1342 -0
- package/dist/frontend/lowering/nan-coded-reads.js.map +1 -0
- package/dist/frontend/lowering/narrowing-stability.d.ts +100 -0
- package/dist/frontend/lowering/narrowing-stability.js +1174 -0
- package/dist/frontend/lowering/narrowing-stability.js.map +1 -0
- package/dist/frontend/lowering/narrowing-stability.test.d.ts +1 -0
- package/dist/frontend/lowering/narrowing-stability.test.js +150 -0
- package/dist/frontend/lowering/narrowing-stability.test.js.map +1 -0
- package/dist/frontend/lowering/runtime-optional-bounds.d.ts +14 -2
- package/dist/frontend/lowering/runtime-optional-bounds.js +17 -11
- package/dist/frontend/lowering/runtime-optional-bounds.js.map +1 -1
- package/dist/frontend/lowering/runtime-optional-bounds.test.js +28 -0
- package/dist/frontend/lowering/runtime-optional-bounds.test.js.map +1 -1
- package/dist/frontend/lowering/surfaces.js +8 -0
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/program.js +1 -1
- package/dist/frontend/type-mapper.js +43 -0
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +20 -8
- package/dist/index.js.map +1 -1
- package/dist/ir/builtin-signatures.d.ts +1 -1
- package/dist/ir/builtin-signatures.js +2 -0
- package/dist/ir/builtin-signatures.js.map +1 -1
- package/dist/ir/constant-globals.d.ts +14 -0
- package/dist/ir/constant-globals.js +81 -0
- package/dist/ir/constant-globals.js.map +1 -0
- package/dist/ir/constant-globals.test.d.ts +1 -0
- package/dist/ir/constant-globals.test.js +97 -0
- package/dist/ir/constant-globals.test.js.map +1 -0
- package/dist/ir/final-fields.d.ts +35 -0
- package/dist/ir/final-fields.js +187 -0
- package/dist/ir/final-fields.js.map +1 -0
- package/dist/ir/final-fields.test.d.ts +1 -0
- package/dist/ir/final-fields.test.js +106 -0
- package/dist/ir/final-fields.test.js.map +1 -0
- package/dist/ir/int32-slots.d.ts +35 -0
- package/dist/ir/int32-slots.js +217 -104
- package/dist/ir/int32-slots.js.map +1 -1
- package/dist/ir/int32-slots.test.js +27 -0
- package/dist/ir/int32-slots.test.js.map +1 -1
- package/dist/ir/integer-loops.d.ts +5 -0
- package/dist/ir/integer-loops.js +23 -4
- package/dist/ir/integer-loops.js.map +1 -1
- package/dist/ir/integer-loops.test.js +43 -1
- package/dist/ir/integer-loops.test.js.map +1 -1
- package/dist/ir/integer-ranges.d.ts +20 -0
- package/dist/ir/integer-ranges.js +178 -21
- package/dist/ir/integer-ranges.js.map +1 -1
- package/dist/ir/integer-ranges.test.js +148 -3
- package/dist/ir/integer-ranges.test.js.map +1 -1
- package/dist/ir/ir.d.ts +38 -1
- package/dist/ir/ir.js +4 -0
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/serialize.d.ts +17 -3
- package/dist/ir/serialize.js +198 -39
- package/dist/ir/serialize.js.map +1 -1
- package/dist/ir/serialize.test.js +65 -1
- package/dist/ir/serialize.test.js.map +1 -1
- package/dist/ir/unshare.d.ts +14 -0
- package/dist/ir/unshare.js +60 -0
- package/dist/ir/unshare.js.map +1 -0
- package/dist/ir/unshare.test.d.ts +1 -0
- package/dist/ir/unshare.test.js +115 -0
- package/dist/ir/unshare.test.js.map +1 -0
- package/dist/library/prepare.js +9 -0
- package/dist/library/prepare.js.map +1 -1
- package/dist/native/cache.d.ts +8 -1
- package/dist/native/cache.js +38 -1
- package/dist/native/cache.js.map +1 -1
- package/dist/native/cache.test.js +11 -0
- package/dist/native/cache.test.js.map +1 -1
- package/dist/native/compiler.js +4 -3
- package/dist/native/compiler.js.map +1 -1
- package/dist/native/prepare.d.ts +3 -1
- package/dist/native/prepare.js +63 -28
- package/dist/native/prepare.js.map +1 -1
- package/dist/native/prepare.test.js +23 -1
- package/dist/native/prepare.test.js.map +1 -1
- package/dist/native/text-file.d.ts +10 -0
- package/dist/native/text-file.js +41 -0
- package/dist/native/text-file.js.map +1 -0
- package/dist/text-parts.d.ts +5 -0
- package/dist/text-parts.js +36 -0
- package/dist/text-parts.js.map +1 -0
- package/package.json +20 -20
- package/surface-manifest.json +7 -1
- package/dist/frontend/lowering/expressions/union-field-write.d.ts +0 -8
- package/dist/frontend/lowering/expressions/union-field-write.js +0 -108
- package/dist/frontend/lowering/expressions/union-field-write.js.map +0 -1
|
@@ -164,6 +164,12 @@ export declare function lowerArrayLiteral(lowerer: Lowerer, expr: ts.ArrayLitera
|
|
|
164
164
|
* reads preserve their tagged value; JavaScript's ToNumber(undefined) is NaN,
|
|
165
165
|
* so compounds and binary arithmetic use this checked one-time extraction. */
|
|
166
166
|
export declare function lowerOptionalNumber(lowerer: Lowerer, operand: IrExpr, loc: SrcLoc, narrowedNode?: ts.Expression): IrExpr;
|
|
167
|
+
/** A number argument of a builtin that converts it with ToNumber (Math
|
|
168
|
+
* functions, String.fromCharCode, the global isNaN/isFinite, DataView
|
|
169
|
+
* offsets and values). ToNumber(undefined) is NaN, so a missing value — an
|
|
170
|
+
* out-of-range read, a local or field holding one, or a hole — converts to
|
|
171
|
+
* NaN instead of trusting the checker's number type. */
|
|
172
|
+
export declare function lowerToNumberArgument(lowerer: Lowerer, node: ts.Expression): IrExpr;
|
|
167
173
|
/** `a[i]` reads. Only f64 indices into array receivers are modeled; JS
|
|
168
174
|
* string-key element access (`a["length"]`) and string indexing (`s[0]`
|
|
169
175
|
* typechecks against the lib's index signature; use .charAt) stay out. */
|
|
@@ -19,8 +19,9 @@ import { constituentTypes } from "../ts7/checker.js";
|
|
|
19
19
|
import { dirname } from "node:path";
|
|
20
20
|
import * as posix from "node:path/posix";
|
|
21
21
|
import { checkedClassAssertion, checkedClassUnionAssertion } from "./class-assertions.js";
|
|
22
|
+
import { checkedArmExtraction } from "./coercions/unions.js";
|
|
22
23
|
import { narrowClassUnion, narrowStoredClassValue } from "./class-unions.js";
|
|
23
|
-
import { lowerUnionFieldWrite } from "./expressions/union-
|
|
24
|
+
import { lowerUnionFieldRead, lowerUnionFieldWrite } from "./expressions/union-fields.js";
|
|
24
25
|
import { captureContextArguments } from "./function-context.js";
|
|
25
26
|
import { OBJECT_CALLABLE_VALUES } from "./surfaces.js";
|
|
26
27
|
import { wasiGuestPath } from "../../wasi-paths.js";
|
|
@@ -167,7 +168,14 @@ export function lowerExpr(lowerer, expr) {
|
|
|
167
168
|
}
|
|
168
169
|
lowerExprDepth++;
|
|
169
170
|
try {
|
|
170
|
-
|
|
171
|
+
let lowered = lowerExprInner(lowerer, expr);
|
|
172
|
+
const nanCoded = lowerer.nanCodedReads;
|
|
173
|
+
if (nanCoded) {
|
|
174
|
+
if (lowered.type.kind === "f64" && nanCoded.reboxes(expr))
|
|
175
|
+
lowered = reboxNanCoded(lowerer, lowered, locOf(expr));
|
|
176
|
+
else if (lowered.type.kind === "union" && nanCoded.convertsSource(expr))
|
|
177
|
+
lowered = lowerOptionalNumber(lowerer, lowered, locOf(expr));
|
|
178
|
+
}
|
|
171
179
|
if (lowered.type.kind === "void" && (lowerer.typeOf(expr).flags & ts.TypeFlags.Never) !== 0)
|
|
172
180
|
lowerer.neverValued.add(lowered);
|
|
173
181
|
return lowered;
|
|
@@ -176,6 +184,27 @@ export function lowerExpr(lowerer, expr) {
|
|
|
176
184
|
lowerExprDepth--;
|
|
177
185
|
}
|
|
178
186
|
}
|
|
187
|
+
/** `number | undefined` from a NaN-coded double whose slot never holds a
|
|
188
|
+
* genuine NaN (nan-coded-reads.ts): NaN is exactly undefined. */
|
|
189
|
+
function reboxNanCoded(lowerer, value, loc) {
|
|
190
|
+
const optional = lowerer.withUndefinedArm(F64);
|
|
191
|
+
const stmts = [];
|
|
192
|
+
let stable = value;
|
|
193
|
+
if (value.kind !== "varRef") {
|
|
194
|
+
const tmp = lowerer.declareHiddenLocal("%nanCoded", F64);
|
|
195
|
+
stmts.push({ kind: "varDecl", localId: tmp.id, init: value, loc });
|
|
196
|
+
stable = varRef(tmp.id, F64, loc);
|
|
197
|
+
}
|
|
198
|
+
const result = {
|
|
199
|
+
kind: "ternary",
|
|
200
|
+
cond: { kind: "libCall", fn: "num.isNaN", args: [stable], type: BOOL, loc },
|
|
201
|
+
then: lowerer.wrappedUndefined(optional, loc),
|
|
202
|
+
else_: lowerer.coerceToExpected(stable, optional),
|
|
203
|
+
type: optional,
|
|
204
|
+
loc,
|
|
205
|
+
};
|
|
206
|
+
return stmts.length === 0 ? result : { kind: "seqExpr", stmts, result, type: optional, loc };
|
|
207
|
+
}
|
|
179
208
|
function lowerExprInner(lowerer, expr) {
|
|
180
209
|
const loc = locOf(expr);
|
|
181
210
|
// An optional chain's guarded receiver: already evaluated by the
|
|
@@ -797,6 +826,13 @@ function lowerExprInner(lowerer, expr) {
|
|
|
797
826
|
// tests, so only side-effect-free reads compose — and when every arm
|
|
798
827
|
// agrees the whole expression folds to that one string (dropping only
|
|
799
828
|
// the pure read, the trust-the-checker bet lowerUnitComparison makes).
|
|
829
|
+
// An effectful producer (a getter) evaluates once into a hidden local.
|
|
830
|
+
const prelude = [];
|
|
831
|
+
if (operand.type.kind === "union" && !isSafeToRepeat(operand)) {
|
|
832
|
+
const local = lowerer.declareHiddenLocal("%typeofOperand", operand.type);
|
|
833
|
+
prelude.push({ kind: "varDecl", localId: local.id, init: operand, loc });
|
|
834
|
+
operand = varRef(local.id, operand.type, loc);
|
|
835
|
+
}
|
|
800
836
|
if (operand.type.kind === "union" && isSafeToRepeat(operand)) {
|
|
801
837
|
const def = lowerer.unions.get(operand.type.unionId);
|
|
802
838
|
const answers = def?.arms.map(typeofAnswer);
|
|
@@ -847,7 +883,9 @@ function lowerExprInner(lowerer, expr) {
|
|
|
847
883
|
loc,
|
|
848
884
|
};
|
|
849
885
|
}
|
|
850
|
-
return
|
|
886
|
+
return prelude.length > 0
|
|
887
|
+
? { kind: "seqExpr", stmts: prelude, result, type: STRING, loc }
|
|
888
|
+
: result;
|
|
851
889
|
}
|
|
852
890
|
}
|
|
853
891
|
if (operand.type.kind === "dyn") {
|
|
@@ -2209,6 +2247,11 @@ function lowerExprInner(lowerer, expr) {
|
|
|
2209
2247
|
handled.kind === "recordGet" &&
|
|
2210
2248
|
lowerer.isRuntimeOptionalField(handled.shapeId, handled.field))
|
|
2211
2249
|
return handled;
|
|
2250
|
+
// A class field assigned a missing read: the checker narrows this
|
|
2251
|
+
// read to the assigned value's bare type, but the stored undefined
|
|
2252
|
+
// arm is real (`obj.n = bytes[i]; obj.n` is undefined in Node).
|
|
2253
|
+
if (runtimeOptionalClassField(lowerer, handled, expr))
|
|
2254
|
+
return handled;
|
|
2212
2255
|
return lowerer.maybeNarrow(handled, expr);
|
|
2213
2256
|
}
|
|
2214
2257
|
// `globalThis.<name>` that no lowering above claimed
|
|
@@ -3116,6 +3159,11 @@ export function maybeNarrow(lowerer, expr, node) {
|
|
|
3116
3159
|
if (narrowed?.kind === "object" &&
|
|
3117
3160
|
narrowed.className !== expr.type.className &&
|
|
3118
3161
|
lowerer.isSubclassOf(narrowed.className, expr.type.className)) {
|
|
3162
|
+
if (lowerer.narrowing.untrusted(node, narrowed)) {
|
|
3163
|
+
const checked = checkedClassAssertion(lowerer, expr, narrowed, expr.loc);
|
|
3164
|
+
if (checked)
|
|
3165
|
+
return checked;
|
|
3166
|
+
}
|
|
3119
3167
|
return { kind: "downcast", value: expr, type: narrowed, loc: expr.loc };
|
|
3120
3168
|
}
|
|
3121
3169
|
return expr;
|
|
@@ -3158,6 +3206,9 @@ export function maybeNarrow(lowerer, expr, node) {
|
|
|
3158
3206
|
const arrayTags = lowerer.arrayValueTags(expr.type.unionId);
|
|
3159
3207
|
if (arrayTags.length === 1) {
|
|
3160
3208
|
const arm = def.arms[arrayTags[0]];
|
|
3209
|
+
const checked = checkedStaleArm(lowerer, node, expr, arrayTags[0], arm);
|
|
3210
|
+
if (checked)
|
|
3211
|
+
return checked;
|
|
3161
3212
|
return {
|
|
3162
3213
|
kind: "unionNarrow",
|
|
3163
3214
|
unionId: expr.type.unionId,
|
|
@@ -3191,6 +3242,9 @@ export function maybeNarrow(lowerer, expr, node) {
|
|
|
3191
3242
|
const values = literalValues(lowerer.checker.getTypeOfSymbol(property));
|
|
3192
3243
|
const arm = values && literalUnionArm(def, values, (id) => lowerer.shapes.get(id));
|
|
3193
3244
|
if (arm) {
|
|
3245
|
+
const checked = checkedStaleArm(lowerer, node, expr, lowerer.armTag(expr.type.unionId, arm), arm);
|
|
3246
|
+
if (checked)
|
|
3247
|
+
return checked;
|
|
3194
3248
|
return {
|
|
3195
3249
|
kind: "unionNarrow",
|
|
3196
3250
|
unionId: expr.type.unionId,
|
|
@@ -3216,6 +3270,10 @@ export function maybeNarrow(lowerer, expr, node) {
|
|
|
3216
3270
|
arm.kind !== "object" ||
|
|
3217
3271
|
!lowerer.isSubclassOf(narrowed.className, arm.className)) &&
|
|
3218
3272
|
lowerer.isSubclassOf(narrowed.className, valueType.className)) {
|
|
3273
|
+
const checked = checkedStaleArm(lowerer, node, expr, valueTag, valueType, narrowed);
|
|
3274
|
+
if (checked) {
|
|
3275
|
+
return checkedClassAssertion(lowerer, checked, narrowed, expr.loc) ?? checked;
|
|
3276
|
+
}
|
|
3219
3277
|
return {
|
|
3220
3278
|
kind: "downcast",
|
|
3221
3279
|
value: {
|
|
@@ -3232,6 +3290,9 @@ export function maybeNarrow(lowerer, expr, node) {
|
|
|
3232
3290
|
}
|
|
3233
3291
|
return expr;
|
|
3234
3292
|
}
|
|
3293
|
+
const checked = checkedStaleArm(lowerer, node, expr, tag, narrowed);
|
|
3294
|
+
if (checked)
|
|
3295
|
+
return checked;
|
|
3235
3296
|
return {
|
|
3236
3297
|
kind: "unionNarrow",
|
|
3237
3298
|
unionId: expr.type.unionId,
|
|
@@ -3241,6 +3302,60 @@ export function maybeNarrow(lowerer, expr, node) {
|
|
|
3241
3302
|
loc: expr.loc,
|
|
3242
3303
|
};
|
|
3243
3304
|
}
|
|
3305
|
+
/** The checked form of maybeNarrow's arm extraction for a narrowing the
|
|
3306
|
+
* checker may hold past code that replaced the value (a call clearing the
|
|
3307
|
+
* property; see NarrowingStability). Every other arm throws the TypeError
|
|
3308
|
+
* Node raises for the operation consuming the read (`Cannot read
|
|
3309
|
+
* properties of undefined (reading 'x')`, `o.f is not a function`), or the
|
|
3310
|
+
* stranded-value TypeError where Node would carry the value on. Null when
|
|
3311
|
+
* the narrowing is provably current. `narrowed` is the checker's type when
|
|
3312
|
+
* it refines the arm (a subclass view). */
|
|
3313
|
+
function checkedStaleArm(lowerer, node, value, tag, arm, narrowed = arm) {
|
|
3314
|
+
if (value.type.kind !== "union" || tag < 0)
|
|
3315
|
+
return null;
|
|
3316
|
+
if (!lowerer.narrowing.untrusted(node, narrowed))
|
|
3317
|
+
return null;
|
|
3318
|
+
const inPlace = checkedArmExtraction(lowerer, value, tag, arm, (found) => staleReadMessage(node, found));
|
|
3319
|
+
if (inPlace)
|
|
3320
|
+
return inPlace;
|
|
3321
|
+
const helper = lowerer.narrowedArmHelper(value.type.unionId, arm, value.loc);
|
|
3322
|
+
return helper ? lowerer.checkedNarrowCall(helper, value, arm, value.loc) : null;
|
|
3323
|
+
}
|
|
3324
|
+
/** Node's TypeError for the operation consuming `node` when it holds a
|
|
3325
|
+
* value of type `found`, or null where Node would not throw. */
|
|
3326
|
+
function staleReadMessage(node, found) {
|
|
3327
|
+
let child = node;
|
|
3328
|
+
let parent = node.parent;
|
|
3329
|
+
while (ts.isParenthesizedExpression(parent)) {
|
|
3330
|
+
child = parent;
|
|
3331
|
+
parent = parent.parent;
|
|
3332
|
+
}
|
|
3333
|
+
if (ts.isCallExpression(parent) && parent.expression === child && !parent.questionDotToken) {
|
|
3334
|
+
return found.kind === "func" ? null : `${node.getText()} is not a function`;
|
|
3335
|
+
}
|
|
3336
|
+
const unit = found.kind === "undefinedT" ? "undefined" : found.kind === "nullT" ? "null" : null;
|
|
3337
|
+
if (unit === null)
|
|
3338
|
+
return null;
|
|
3339
|
+
if (!(ts.isPropertyAccessExpression(parent) || ts.isElementAccessExpression(parent)) ||
|
|
3340
|
+
parent.expression !== child ||
|
|
3341
|
+
parent.questionDotToken)
|
|
3342
|
+
return null;
|
|
3343
|
+
let key = null;
|
|
3344
|
+
if (ts.isPropertyAccessExpression(parent))
|
|
3345
|
+
key = parent.name.text;
|
|
3346
|
+
else if (ts.isStringLiteral(parent.argumentExpression))
|
|
3347
|
+
key = parent.argumentExpression.text;
|
|
3348
|
+
else if (ts.isNumericLiteral(parent.argumentExpression))
|
|
3349
|
+
key = String(Number(parent.argumentExpression.text));
|
|
3350
|
+
if (key === null)
|
|
3351
|
+
return null;
|
|
3352
|
+
const store = parent.parent;
|
|
3353
|
+
return ts.isBinaryExpression(store) &&
|
|
3354
|
+
store.left === parent &&
|
|
3355
|
+
store.operatorToken.kind === ts.SyntaxKind.EqualsToken
|
|
3356
|
+
? `Cannot set properties of ${unit} (setting '${key}')`
|
|
3357
|
+
: `Cannot read properties of ${unit} (reading '${key}')`;
|
|
3358
|
+
}
|
|
3244
3359
|
/** A runtime-optional slot can be assigned while a truthy guard is in
|
|
3245
3360
|
* force. TypeScript then types a direct member receiver as the plain arm,
|
|
3246
3361
|
* even though an OOB-safe assignment may have restored undefined. Check
|
|
@@ -5582,6 +5697,34 @@ export function lowerOptionalNumber(lowerer, operand, loc, narrowedNode) {
|
|
|
5582
5697
|
? value
|
|
5583
5698
|
: { kind: "seqExpr", stmts: prefix, result: value, type: F64, loc };
|
|
5584
5699
|
}
|
|
5700
|
+
/** A number argument of a builtin that converts it with ToNumber (Math
|
|
5701
|
+
* functions, String.fromCharCode, the global isNaN/isFinite, DataView
|
|
5702
|
+
* offsets and values). ToNumber(undefined) is NaN, so a missing value — an
|
|
5703
|
+
* out-of-range read, a local or field holding one, or a hole — converts to
|
|
5704
|
+
* NaN instead of trusting the checker's number type. */
|
|
5705
|
+
export function lowerToNumberArgument(lowerer, node) {
|
|
5706
|
+
let inner = node;
|
|
5707
|
+
while (ts.isParenthesizedExpression(inner))
|
|
5708
|
+
inner = inner.expression;
|
|
5709
|
+
const loc = locOf(inner);
|
|
5710
|
+
const isOptionalNumber = (value) => value.type.kind === "union" &&
|
|
5711
|
+
lowerer.armTag(value.type.unionId, UNDEFINED_T) >= 0 &&
|
|
5712
|
+
lowerer.stripUndefinedArm(value.type).kind === "f64";
|
|
5713
|
+
const optional = ts.isIdentifier(inner)
|
|
5714
|
+
? runtimeOptionalStorageOperand(lowerer, inner)
|
|
5715
|
+
: ts.isElementAccessExpression(inner)
|
|
5716
|
+
? lowerAbsenceProbe(lowerer, inner)
|
|
5717
|
+
: null;
|
|
5718
|
+
if (optional && isOptionalNumber(optional))
|
|
5719
|
+
return lowerOptionalNumber(lowerer, optional, loc, inner);
|
|
5720
|
+
// A conditional builds each branch for the destination.
|
|
5721
|
+
if (ts.isConditionalExpression(inner))
|
|
5722
|
+
return lowerer.lowerExprExpecting(node, F64);
|
|
5723
|
+
const value = lowerer.lowerExpr(node);
|
|
5724
|
+
if (isOptionalNumber(value))
|
|
5725
|
+
return lowerOptionalNumber(lowerer, value, loc, inner);
|
|
5726
|
+
return lowerer.coerceInto(node, value, F64);
|
|
5727
|
+
}
|
|
5585
5728
|
/** `a[i]` reads. Only f64 indices into array receivers are modeled; JS
|
|
5586
5729
|
* string-key element access (`a["length"]`) and string indexing (`s[0]`
|
|
5587
5730
|
* typechecks against the lib's index signature; use .charAt) stay out. */
|
|
@@ -5866,7 +6009,10 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
5866
6009
|
// `t[i]!` asserts presence: the numeric element read (NaN when
|
|
5867
6010
|
// invalid). Every other read answers undefined for an invalid index,
|
|
5868
6011
|
// like Node.
|
|
5869
|
-
|
|
6012
|
+
// A read whose every consumer treats undefined like NaN (see
|
|
6013
|
+
// nan-coded-reads.ts) is the same plain numeric read.
|
|
6014
|
+
if ((ts.isNonNullExpression(expr.parent) && expr.parent.expression === expr) ||
|
|
6015
|
+
lowerer.nanCodedReads?.isNumericRead(expr) === true) {
|
|
5870
6016
|
return {
|
|
5871
6017
|
kind: "bytesIntrinsic",
|
|
5872
6018
|
method: "get",
|
|
@@ -6529,6 +6675,23 @@ export function runtimeOptionalStorageOperand(lowerer, node) {
|
|
|
6529
6675
|
return null;
|
|
6530
6676
|
return optional.value;
|
|
6531
6677
|
}
|
|
6678
|
+
/** A `T | undefined` class field read that the checker narrowed to `T`
|
|
6679
|
+
* where the field was assigned a value that may be missing (an unchecked
|
|
6680
|
+
* indexed read). TypeScript's assignment narrowing is not a runtime fact
|
|
6681
|
+
* there, so the read keeps the stored union instead of extracting `T`. */
|
|
6682
|
+
function runtimeOptionalClassField(lowerer, value, node) {
|
|
6683
|
+
if (value.kind !== "fieldGet" || value.type.kind !== "union")
|
|
6684
|
+
return false;
|
|
6685
|
+
if (lowerer.armTag(value.type.unionId, UNDEFINED_T) < 0)
|
|
6686
|
+
return false;
|
|
6687
|
+
if ((lowerer.unions.get(value.type.unionId)?.arms.length ?? 0) !== 2)
|
|
6688
|
+
return false;
|
|
6689
|
+
const symbol = lowerer.checker.getSymbolAtLocation(node.name);
|
|
6690
|
+
if (!symbol || !lowerer.runtimeOptionalAssignedSymbols.has(symbol))
|
|
6691
|
+
return false;
|
|
6692
|
+
const narrowed = lowerer.mapTypeOf(lowerer.typeOf(node));
|
|
6693
|
+
return narrowed !== null && typeEquals(narrowed, lowerer.stripUndefinedArm(value.type));
|
|
6694
|
+
}
|
|
6532
6695
|
/** A presence-test operand: an absence-aware element or field read, an
|
|
6533
6696
|
* identifier's runtime-optional storage, or the ordinary lowering. */
|
|
6534
6697
|
export function lowerPresenceOperand(lowerer, node) {
|
|
@@ -8298,12 +8461,75 @@ export function lowerCompoundValueToTarget(lowerer, expr, compound, target) {
|
|
|
8298
8461
|
}
|
|
8299
8462
|
lowerer.unsupported("SC1043", expr);
|
|
8300
8463
|
}
|
|
8464
|
+
/** Equality of two NaN-coded values (nan-coded-reads.ts), each of which may
|
|
8465
|
+
* stand for undefined and never holds a genuine NaN: Node's undefined equals
|
|
8466
|
+
* undefined, so two NaNs compare equal here. Operands evaluate once, in
|
|
8467
|
+
* order. */
|
|
8468
|
+
function lowerNanCodedEquality(lowerer, expr) {
|
|
8469
|
+
const loc = locOf(expr);
|
|
8470
|
+
const left = lowerer.lowerExpr(expr.left);
|
|
8471
|
+
const right = lowerer.lowerExpr(expr.right);
|
|
8472
|
+
if (left.type.kind !== "f64" || right.type.kind !== "f64") {
|
|
8473
|
+
throw new InternalCompilerError("NaN-coded equality operand is not a plain number");
|
|
8474
|
+
}
|
|
8475
|
+
const stmts = [];
|
|
8476
|
+
const stable = (value, name) => {
|
|
8477
|
+
if (value.kind === "varRef" || value.kind === "numLit")
|
|
8478
|
+
return value;
|
|
8479
|
+
const tmp = lowerer.declareHiddenLocal(name, F64);
|
|
8480
|
+
stmts.push({ kind: "varDecl", localId: tmp.id, init: value, loc });
|
|
8481
|
+
return varRef(tmp.id, F64, loc);
|
|
8482
|
+
};
|
|
8483
|
+
const a = stable(left, "%eqLeft");
|
|
8484
|
+
const b = stable(right, "%eqRight");
|
|
8485
|
+
const isNaN = (value) => ({
|
|
8486
|
+
kind: "libCall",
|
|
8487
|
+
fn: "num.isNaN",
|
|
8488
|
+
args: [value],
|
|
8489
|
+
type: BOOL,
|
|
8490
|
+
loc,
|
|
8491
|
+
});
|
|
8492
|
+
const bothUndefined = {
|
|
8493
|
+
kind: "logical",
|
|
8494
|
+
op: "&&",
|
|
8495
|
+
left: isNaN(a),
|
|
8496
|
+
right: isNaN(b),
|
|
8497
|
+
type: BOOL,
|
|
8498
|
+
loc,
|
|
8499
|
+
};
|
|
8500
|
+
const op = expr.operatorToken.kind;
|
|
8501
|
+
const negated = op === ts.SyntaxKind.ExclamationEqualsEqualsToken ||
|
|
8502
|
+
op === ts.SyntaxKind.ExclamationEqualsToken;
|
|
8503
|
+
const result = negated
|
|
8504
|
+
? {
|
|
8505
|
+
kind: "logical",
|
|
8506
|
+
op: "&&",
|
|
8507
|
+
left: { kind: "bin", op: "!==", left: a, right: b, type: BOOL, loc },
|
|
8508
|
+
right: { kind: "unary", op: "!", operand: bothUndefined, type: BOOL, loc },
|
|
8509
|
+
type: BOOL,
|
|
8510
|
+
loc,
|
|
8511
|
+
}
|
|
8512
|
+
: {
|
|
8513
|
+
kind: "logical",
|
|
8514
|
+
op: "||",
|
|
8515
|
+
left: { kind: "bin", op: "===", left: a, right: b, type: BOOL, loc },
|
|
8516
|
+
right: bothUndefined,
|
|
8517
|
+
type: BOOL,
|
|
8518
|
+
loc,
|
|
8519
|
+
};
|
|
8520
|
+
return stmts.length === 0 ? result : { kind: "seqExpr", stmts, result, type: BOOL, loc };
|
|
8521
|
+
}
|
|
8301
8522
|
export function lowerBinary(lowerer, expr) {
|
|
8302
8523
|
const loc = locOf(expr);
|
|
8303
8524
|
const op = expr.operatorToken.kind;
|
|
8304
8525
|
const cacheHas = lowerRequireCacheHas(lowerer, expr);
|
|
8305
8526
|
if (cacheHas)
|
|
8306
8527
|
return cacheHas;
|
|
8528
|
+
if (lowerer.nanCodedReads?.needsUndefinedEquality(expr)) {
|
|
8529
|
+
const undefinedEquality = lowerNanCodedEquality(lowerer, expr);
|
|
8530
|
+
if (undefinedEquality)
|
|
8531
|
+
return undefinedEquality;
|
|
8532
|
+
}
|
|
8307
8533
|
if (op === ts.SyntaxKind.EqualsToken ||
|
|
8308
8534
|
(op >= ts.SyntaxKind.FirstCompoundAssignment && op <= ts.SyntaxKind.LastCompoundAssignment)) {
|
|
8309
8535
|
const urlWrite = lowerUrlAssignment(lowerer, expr);
|
|
@@ -12246,6 +12472,11 @@ export function lowerUnionProperty(lowerer, expr) {
|
|
|
12246
12472
|
const helper = lowerer.narrowedRetagHelper(expr.expression, value.type.unionId, receiverIr.unionId, locOf(expr));
|
|
12247
12473
|
if (helper)
|
|
12248
12474
|
value = { kind: "call", callee: helper, args: [value], type: receiverIr, loc: locOf(expr) };
|
|
12475
|
+
// Optional storage of a base class (a runtime-optional binding holding
|
|
12476
|
+
// `Base | undefined`) that `instanceof` narrowed to several subclasses:
|
|
12477
|
+
// extract the instance and test its class, as typed slots do.
|
|
12478
|
+
else
|
|
12479
|
+
value = narrowStoredClassValue(lowerer, value, receiverIr) ?? value;
|
|
12249
12480
|
}
|
|
12250
12481
|
// A checker-union receiver whose VALUE lowered to a plain RECORD (the
|
|
12251
12482
|
// merged-signature fiction — `runner(cmd, args)` where runner joined
|
|
@@ -12368,6 +12599,11 @@ export function lowerUnionProperty(lowerer, expr) {
|
|
|
12368
12599
|
const keyed = lowerUnionKeyedRead(lowerer, expr, value.type.unionId, value, key, field);
|
|
12369
12600
|
if (keyed)
|
|
12370
12601
|
return keyed;
|
|
12602
|
+
// Class arms declaring the field with compatible but different types
|
|
12603
|
+
// (`T | undefined` beside `T`): per-arm slot reads widened to one type.
|
|
12604
|
+
const widened = lowerUnionFieldRead(lowerer, expr, value);
|
|
12605
|
+
if (widened)
|
|
12606
|
+
return widened;
|
|
12371
12607
|
// JavaScript overloads commonly inspect a class brand on a scalar-or-
|
|
12372
12608
|
// instance argument (e.g. Matrix4.makeTranslation). Preserve primitive
|
|
12373
12609
|
// missing-property semantics and dispatch class data through its native
|
|
@@ -12532,6 +12768,11 @@ function classFieldTarget(lowerer, receiverNode, receiverType, fieldName, saved)
|
|
|
12532
12768
|
const lowerObjectReceiver = () => {
|
|
12533
12769
|
let obj = represented;
|
|
12534
12770
|
if (obj.type.kind === "union") {
|
|
12771
|
+
// A receiver that may hold a missing value (a field assigned an
|
|
12772
|
+
// unchecked read): Node's member-read TypeError for the unit arm.
|
|
12773
|
+
const present = lowerer.runtimeOptionalPropertyReceiver(receiverNode, obj, receiverType, fieldName);
|
|
12774
|
+
if (present)
|
|
12775
|
+
return present;
|
|
12535
12776
|
const helper = lowerer.narrowedArmHelper(obj.type.unionId, receiverType, locOf(receiverNode));
|
|
12536
12777
|
obj = helper
|
|
12537
12778
|
? lowerer.checkedNarrowCall(helper, obj, receiverType, locOf(receiverNode))
|