@scriptc/compiler 0.1.5 → 0.1.7
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 +0 -1
- package/ambient/scriptc.d.ts +13 -5
- package/dist/backend/c/c-emitter.d.ts +5 -0
- package/dist/backend/c/c-emitter.js +15 -0
- package/dist/backend/c/c-emitter.js.map +1 -1
- package/dist/backend/c/exprs.js +81 -9
- package/dist/backend/c/exprs.js.map +1 -1
- package/dist/backend/c/stmts.js +25 -1
- package/dist/backend/c/stmts.js.map +1 -1
- package/dist/backend/c/walkers.js +2 -3
- package/dist/backend/c/walkers.js.map +1 -1
- package/dist/backend/debug-symbols.d.ts +8 -0
- package/dist/backend/debug-symbols.js +52 -0
- package/dist/backend/debug-symbols.js.map +1 -0
- package/dist/backend/link-plan.d.ts +1 -0
- package/dist/backend/link-plan.js +2 -0
- package/dist/backend/link-plan.js.map +1 -1
- package/dist/backend/linker.js +8 -0
- package/dist/backend/linker.js.map +1 -1
- package/dist/backend/llvm/blocks.d.ts +2 -0
- package/dist/backend/llvm/blocks.js +18 -2
- package/dist/backend/llvm/blocks.js.map +1 -1
- package/dist/backend/llvm/debug-info.d.ts +32 -0
- package/dist/backend/llvm/debug-info.js +203 -0
- package/dist/backend/llvm/debug-info.js.map +1 -0
- package/dist/backend/llvm/dyn.js +4 -10
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.d.ts +2 -0
- package/dist/backend/llvm/emitter.js +49 -4
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/expr-containers.js +28 -2
- package/dist/backend/llvm/expr-containers.js.map +1 -1
- package/dist/backend/llvm/expr-dynamic.js +10 -0
- package/dist/backend/llvm/expr-dynamic.js.map +1 -1
- package/dist/backend/llvm/lib-filesystem.js +37 -0
- package/dist/backend/llvm/lib-filesystem.js.map +1 -1
- package/dist/backend/llvm/lib-process.js +4 -3
- package/dist/backend/llvm/lib-process.js.map +1 -1
- package/dist/backend/llvm/lib-shared.js +9 -0
- package/dist/backend/llvm/lib-shared.js.map +1 -1
- package/dist/backend/llvm/split.js +4 -1
- package/dist/backend/llvm/split.js.map +1 -1
- package/dist/backend/llvm/split.test.js +26 -1
- package/dist/backend/llvm/split.test.js.map +1 -1
- package/dist/backend/native-toolchain.js +58 -8
- package/dist/backend/native-toolchain.js.map +1 -1
- package/dist/backend/native-toolchain.test.js +10 -0
- package/dist/backend/native-toolchain.test.js.map +1 -1
- package/dist/backend/source-locations.d.ts +12 -0
- package/dist/backend/source-locations.js +32 -0
- package/dist/backend/source-locations.js.map +1 -0
- package/dist/backend/source-locations.test.d.ts +1 -0
- package/dist/backend/source-locations.test.js +16 -0
- package/dist/backend/source-locations.test.js.map +1 -0
- package/dist/coverage/surface-manifest.js +6 -1
- package/dist/coverage/surface-manifest.js.map +1 -1
- package/dist/diagnostics/diagnostic.d.ts +4 -0
- package/dist/diagnostics/diagnostic.js +12 -0
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/executable/executable-cache.js +28 -4
- package/dist/executable/executable-cache.js.map +1 -1
- package/dist/executable/executable-cache.test.js +23 -0
- package/dist/executable/executable-cache.test.js.map +1 -1
- package/dist/frontend/arguments-usage.d.ts +6 -0
- package/dist/frontend/arguments-usage.js +40 -0
- package/dist/frontend/arguments-usage.js.map +1 -0
- package/dist/frontend/binding-source.d.ts +4 -0
- package/dist/frontend/binding-source.js +18 -0
- package/dist/frontend/binding-source.js.map +1 -0
- package/dist/frontend/lowering/array-indexed-mutation.d.ts +8 -0
- package/dist/frontend/lowering/array-indexed-mutation.js +176 -0
- package/dist/frontend/lowering/array-indexed-mutation.js.map +1 -0
- package/dist/frontend/lowering/containers/string-and-regexp.d.ts +7 -0
- package/dist/frontend/lowering/containers/string-and-regexp.js +190 -13
- package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
- package/dist/frontend/lowering/expressions/object-literals.js +13 -8
- package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
- package/dist/frontend/lowering/expressions/optional-chains.d.ts +5 -6
- package/dist/frontend/lowering/expressions/optional-chains.js +7 -6
- package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -1
- package/dist/frontend/lowering/indexed-comparison.d.ts +6 -0
- package/dist/frontend/lowering/indexed-comparison.js +30 -0
- package/dist/frontend/lowering/indexed-comparison.js.map +1 -0
- package/dist/frontend/lowering/lower-builtins.d.ts +1 -1
- package/dist/frontend/lowering/lower-builtins.js +21 -10
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +2 -9
- package/dist/frontend/lowering/lower-calls.js +406 -40
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.js +3 -27
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.d.ts +8 -2
- package/dist/frontend/lowering/lower-containers.js +454 -104
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.js +134 -18
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-island.d.ts +1 -1
- package/dist/frontend/lowering/lower-island.js +1 -1
- package/dist/frontend/lowering/lower-modules.js +19 -8
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.js +4 -37
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lowerer.js +14 -3
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/surfaces.d.ts +3 -1
- package/dist/frontend/lowering/surfaces.js +50 -7
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/program.d.ts +5 -0
- package/dist/frontend/program.js +216 -32
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/program.test.d.ts +1 -0
- package/dist/frontend/program.test.js +117 -0
- package/dist/frontend/program.test.js.map +1 -0
- package/dist/frontend/type-mapper.d.ts +4 -2
- package/dist/frontend/type-mapper.js +249 -186
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/frontend/type-mapper.test.d.ts +1 -0
- package/dist/frontend/type-mapper.test.js +86 -0
- package/dist/frontend/type-mapper.test.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +7 -2
- package/dist/index.js.map +1 -1
- package/dist/ir/analysis.js +4 -2
- package/dist/ir/analysis.js.map +1 -1
- package/dist/ir/ir.d.ts +33 -14
- package/dist/ir/ir.js +5 -1
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/validate.js +57 -31
- package/dist/ir/validate.js.map +1 -1
- package/dist/ir/validate.test.js +17 -0
- package/dist/ir/validate.test.js.map +1 -1
- package/dist/library/compiler-self-identity.d.ts +1 -1
- package/dist/library/compiler-self-identity.js +31 -15
- package/dist/library/compiler-self-identity.js.map +1 -1
- package/dist/library/compiler-self-identity.test.d.ts +1 -0
- package/dist/library/compiler-self-identity.test.js +93 -0
- package/dist/library/compiler-self-identity.test.js.map +1 -0
- package/package.json +17 -17
- package/surface-manifest.json +155 -4
|
@@ -14,8 +14,36 @@ import { islandPrimitiveExit, lowerDynDispatchMethodCall } from "./lower-calls.j
|
|
|
14
14
|
import { buildArraySortFn } from "./lower-array-sort.js";
|
|
15
15
|
import { arrayIndexPresent, arrayValueRead, arrayValueStore, arrayValueType, currentArrayIndexPresent } from "./array-values.js";
|
|
16
16
|
import { typeKey } from "../type-mapper.js";
|
|
17
|
-
import { boolLit, countedFor, numLit, varRef } from "../../ir/build.js";
|
|
18
|
-
import {
|
|
17
|
+
import { boolLit, countedFor, numLit, strLit, varRef } from "../../ir/build.js";
|
|
18
|
+
import { defaultAfterUndefined, lowerPositionArgument, lowerStaticallyUndefinedArgument, positionNumber } from "./optional-arguments.js";
|
|
19
|
+
import { lowerArrayCopyWithin, lowerArrayFill } from "./array-indexed-mutation.js";
|
|
20
|
+
function primitivePositionType(lowerer, type) {
|
|
21
|
+
if (type.kind === "union") {
|
|
22
|
+
return lowerer.unions.get(type.unionId)?.arms.every((arm) => primitivePositionType(lowerer, arm)) ?? false;
|
|
23
|
+
}
|
|
24
|
+
return type.kind === "f64" || type.kind === "string" || type.kind === "bool" ||
|
|
25
|
+
type.kind === "nullT" || type.kind === "undefinedT";
|
|
26
|
+
}
|
|
27
|
+
function lowerArrayPosition(lowerer, node, defaultValue, subject) {
|
|
28
|
+
if (!node)
|
|
29
|
+
return defaultValue;
|
|
30
|
+
if (ts.isSpreadElement(node))
|
|
31
|
+
lowerer.noLowering(`${subject} with a spread argument`, node);
|
|
32
|
+
const value = lowerPositionArgument(lowerer, node, defaultValue);
|
|
33
|
+
if (!primitivePositionType(lowerer, value.type)) {
|
|
34
|
+
lowerer.noLowering(`${subject} of '${lowerer.fmt(value.type)}' values`, node);
|
|
35
|
+
}
|
|
36
|
+
const loc = locOf(node);
|
|
37
|
+
const local = lowerer.declareHiddenLocal("%arrayPosition", value.type);
|
|
38
|
+
const ref = varRef(local.id, value.type, loc);
|
|
39
|
+
return {
|
|
40
|
+
kind: "seqExpr",
|
|
41
|
+
stmts: [{ kind: "varDecl", localId: local.id, init: value, loc }],
|
|
42
|
+
result: positionNumber(lowerer, ref, defaultValue, node, subject),
|
|
43
|
+
type: F64,
|
|
44
|
+
loc,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
19
47
|
/** Build the argument array for a mutating/copying operation while keeping
|
|
20
48
|
* the receiver's scalar payload ABI. A missing read is represented as the
|
|
21
49
|
* present UNDEFINED state in this temporary, so push/unshift and toSpliced
|
|
@@ -42,6 +70,68 @@ function lowerArrayValueItems(lowerer, values, elem, arrType, loc) {
|
|
|
42
70
|
}
|
|
43
71
|
return { kind: "seqExpr", stmts: body, result: outRef, type: arrType, loc };
|
|
44
72
|
}
|
|
73
|
+
function lowerArraySpreadItems(lowerer, nodes, elem, arrType, loc) {
|
|
74
|
+
const out = lowerer.declareHiddenLocal("%arrayItems", arrType);
|
|
75
|
+
const outRef = varRef(out.id, arrType, loc);
|
|
76
|
+
const length = () => ({ kind: "arrIntrinsic", method: "length", receiver: outRef, args: [], type: F64, loc });
|
|
77
|
+
const body = [
|
|
78
|
+
{ kind: "varDecl", localId: out.id, init: { kind: "arrayLit", elems: [], type: arrType, loc }, loc },
|
|
79
|
+
];
|
|
80
|
+
for (const node of nodes) {
|
|
81
|
+
if (ts.isSpreadElement(node)) {
|
|
82
|
+
const source = lowerer.lowerExpr(node.expression);
|
|
83
|
+
if (!typeEquals(source.type, arrType)) {
|
|
84
|
+
lowerer.noLowering(`Array insertion spread from '${lowerer.fmt(source.type)}'`, node);
|
|
85
|
+
}
|
|
86
|
+
body.push({
|
|
87
|
+
kind: "exprStmt",
|
|
88
|
+
expr: { kind: "arrIntrinsic", method: "pushSpread", receiver: outRef, args: [source], type: F64, loc },
|
|
89
|
+
loc,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
const value = lowerer.lowerExpr(node);
|
|
94
|
+
const stored = lowerer.runtimeOptionalWidening(value.type, elem) !== null
|
|
95
|
+
? value : lowerer.coerceInto(node, value, elem);
|
|
96
|
+
body.push(arrayValueStore(lowerer, outRef, length(), stored, elem, loc));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return { kind: "seqExpr", stmts: body, result: outRef, type: arrType, loc };
|
|
100
|
+
}
|
|
101
|
+
function lowerArrayJoinSeparator(lowerer, node, loc) {
|
|
102
|
+
const comma = strLit(",", loc);
|
|
103
|
+
if (!node)
|
|
104
|
+
return comma;
|
|
105
|
+
const undefinedValue = lowerStaticallyUndefinedArgument(lowerer, node);
|
|
106
|
+
if (undefinedValue)
|
|
107
|
+
return defaultAfterUndefined(undefinedValue, comma);
|
|
108
|
+
const value = lowerer.lowerExpr(node);
|
|
109
|
+
if (value.type.kind === "nullT")
|
|
110
|
+
return defaultAfterUndefined(value, strLit("null", loc));
|
|
111
|
+
if (value.type.kind === "union") {
|
|
112
|
+
const undefinedTag = lowerer.armTag(value.type.unionId, UNDEFINED_T);
|
|
113
|
+
if (undefinedTag >= 0) {
|
|
114
|
+
const local = lowerer.declareHiddenLocal("%joinSeparator", value.type);
|
|
115
|
+
const ref = varRef(local.id, value.type, loc);
|
|
116
|
+
return {
|
|
117
|
+
kind: "seqExpr",
|
|
118
|
+
stmts: [{ kind: "varDecl", localId: local.id, init: value, loc }],
|
|
119
|
+
result: {
|
|
120
|
+
kind: "ternary",
|
|
121
|
+
cond: { kind: "unionIsTag", unionId: value.type.unionId, tag: undefinedTag, value: ref, negated: false, type: BOOL, loc },
|
|
122
|
+
then: comma,
|
|
123
|
+
else_: lowerer.ensureString(ref, node),
|
|
124
|
+
type: STRING, loc,
|
|
125
|
+
},
|
|
126
|
+
type: STRING, loc,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (value.type.kind === "dyn" || value.type.kind === "jsval") {
|
|
131
|
+
lowerer.noLowering(".join separator with a runtime-dependent undefined value", node);
|
|
132
|
+
}
|
|
133
|
+
return lowerer.ensureString(value, node);
|
|
134
|
+
}
|
|
45
135
|
/** Callback-driven array producers bypass mapType's ordinary T[] mapping:
|
|
46
136
|
* the callback is lowered first, then the helper's result array is built
|
|
47
137
|
* directly from its IR return type. Fence element kinds ScrArr cannot hold
|
|
@@ -181,24 +271,51 @@ export function lowerArrayMethodCall(lowerer, call, access) {
|
|
|
181
271
|
loc,
|
|
182
272
|
};
|
|
183
273
|
}
|
|
274
|
+
if (name === "fill") {
|
|
275
|
+
if (call.arguments.length > 3 || call.arguments.some(ts.isSpreadElement)) {
|
|
276
|
+
lowerer.noLowering(`.fill with ${call.arguments.length} arguments`, call);
|
|
277
|
+
}
|
|
278
|
+
const value = call.arguments[0] ? lowerer.lowerExpr(call.arguments[0]) : null;
|
|
279
|
+
if (value && (value.type.kind === "dyn" || value.type.kind === "jsval") &&
|
|
280
|
+
isRefCounted(elem) && elem.kind !== "string" && !typeEquals(value.type, elem)) {
|
|
281
|
+
lowerer.noLowering(".fill of a checked-dynamic reference into a static array", call.arguments[0]);
|
|
282
|
+
}
|
|
283
|
+
const writeUndefined = value === null || value.type.kind === "undefinedT";
|
|
284
|
+
return lowerArrayFill(lowerer, lowerer.lowerExpr(access.expression), value, writeUndefined, lowerArrayPosition(lowerer, call.arguments[1], numLit(0, loc), "array fill start"), lowerArrayPosition(lowerer, call.arguments[2], numLit(Infinity, loc), "array fill end"), receiverIr, loc);
|
|
285
|
+
}
|
|
286
|
+
if (name === "copyWithin") {
|
|
287
|
+
if (call.arguments.length > 3 || call.arguments.some(ts.isSpreadElement)) {
|
|
288
|
+
lowerer.noLowering(`.copyWithin with ${call.arguments.length} arguments`, call);
|
|
289
|
+
}
|
|
290
|
+
return lowerArrayCopyWithin(lowerer, lowerer.lowerExpr(access.expression), lowerArrayPosition(lowerer, call.arguments[0], numLit(0, loc), "array copyWithin target"), lowerArrayPosition(lowerer, call.arguments[1], numLit(0, loc), "array copyWithin start"), lowerArrayPosition(lowerer, call.arguments[2], numLit(Infinity, loc), "array copyWithin end"), receiverIr, loc);
|
|
291
|
+
}
|
|
184
292
|
if (name === "with") {
|
|
185
293
|
if (call.arguments.length !== 2 || call.arguments.some(ts.isSpreadElement)) {
|
|
186
294
|
lowerer.noLowering(`.with with ${call.arguments.length} arguments`, call);
|
|
187
295
|
}
|
|
188
296
|
const receiver = lowerer.lowerExpr(access.expression);
|
|
189
|
-
const index = lowerer
|
|
297
|
+
const index = lowerPositionArgument(lowerer, call.arguments[0], numLit(0, loc));
|
|
298
|
+
if (!primitivePositionType(lowerer, index.type)) {
|
|
299
|
+
lowerer.noLowering(`.with index of '${lowerer.fmt(index.type)}' values`, call.arguments[0]);
|
|
300
|
+
}
|
|
190
301
|
const value = lowerer.lowerExpr(call.arguments[1]);
|
|
302
|
+
const receiverLocal = lowerer.declareHiddenLocal("%withRecv", receiverIr);
|
|
303
|
+
const indexLocal = lowerer.declareHiddenLocal("%withIndex", index.type);
|
|
304
|
+
const valueLocal = lowerer.declareHiddenLocal("%withValue", value.type);
|
|
305
|
+
const receiverRef = varRef(receiverLocal.id, receiverIr, loc);
|
|
306
|
+
const indexRef = varRef(indexLocal.id, index.type, loc);
|
|
307
|
+
const valueRef = varRef(valueLocal.id, value.type, loc);
|
|
308
|
+
const numericIndex = positionNumber(lowerer, indexRef, numLit(0, loc), call.arguments[0], "array with index");
|
|
309
|
+
const stmts = [
|
|
310
|
+
{ kind: "varDecl", localId: receiverLocal.id, init: receiver, loc },
|
|
311
|
+
{ kind: "varDecl", localId: indexLocal.id, init: index, loc },
|
|
312
|
+
{ kind: "varDecl", localId: valueLocal.id, init: value, loc },
|
|
313
|
+
];
|
|
191
314
|
if (value.type.kind === "union" && lowerer.runtimeOptionalWidening(value.type, elem) !== null) {
|
|
192
315
|
// `.with()` validates its index and returns a fresh copy. Stabilize
|
|
193
316
|
// all three operands, then select the scalar replacement or the
|
|
194
317
|
// present-undefined copy without evaluating a read twice.
|
|
195
|
-
const receiverLocal = lowerer.declareHiddenLocal("%withRecv", receiverIr);
|
|
196
|
-
const indexLocal = lowerer.declareHiddenLocal("%withIndex", F64);
|
|
197
|
-
const valueLocal = lowerer.declareHiddenLocal("%withValue", value.type);
|
|
198
318
|
const unionId = value.type.unionId;
|
|
199
|
-
const receiverRef = varRef(receiverLocal.id, receiverIr, loc);
|
|
200
|
-
const indexRef = varRef(indexLocal.id, F64, loc);
|
|
201
|
-
const valueRef = varRef(valueLocal.id, value.type, loc);
|
|
202
319
|
const undefTag = lowerer.armTag(unionId, UNDEFINED_T);
|
|
203
320
|
const condition = {
|
|
204
321
|
kind: "unionIsTag",
|
|
@@ -213,7 +330,7 @@ export function lowerArrayMethodCall(lowerer, call, access) {
|
|
|
213
330
|
kind: "arrIntrinsic",
|
|
214
331
|
method: "withUndefined",
|
|
215
332
|
receiver: receiverRef,
|
|
216
|
-
args: [
|
|
333
|
+
args: [numericIndex],
|
|
217
334
|
type: receiverIr,
|
|
218
335
|
loc,
|
|
219
336
|
};
|
|
@@ -221,62 +338,45 @@ export function lowerArrayMethodCall(lowerer, call, access) {
|
|
|
221
338
|
kind: "arrIntrinsic",
|
|
222
339
|
method: "with",
|
|
223
340
|
receiver: receiverRef,
|
|
224
|
-
args: [
|
|
341
|
+
args: [numericIndex, lowerer.coerceToExpected(valueRef, elem)],
|
|
225
342
|
type: receiverIr,
|
|
226
343
|
loc,
|
|
227
344
|
};
|
|
228
345
|
return {
|
|
229
346
|
kind: "seqExpr",
|
|
230
|
-
stmts
|
|
231
|
-
{ kind: "varDecl", localId: receiverLocal.id, init: receiver, loc },
|
|
232
|
-
{ kind: "varDecl", localId: indexLocal.id, init: index, loc },
|
|
233
|
-
{ kind: "varDecl", localId: valueLocal.id, init: value, loc },
|
|
234
|
-
],
|
|
347
|
+
stmts,
|
|
235
348
|
result: { kind: "ternary", cond: condition, then: missing, else_: present, type: receiverIr, loc },
|
|
236
349
|
type: receiverIr,
|
|
237
350
|
loc,
|
|
238
351
|
};
|
|
239
352
|
}
|
|
240
|
-
const coercedValue = lowerer.coerceInto(call.arguments[1],
|
|
353
|
+
const coercedValue = lowerer.coerceInto(call.arguments[1], valueRef, elem);
|
|
241
354
|
return {
|
|
242
|
-
kind: "
|
|
243
|
-
|
|
244
|
-
receiver,
|
|
245
|
-
args: [index, coercedValue],
|
|
355
|
+
kind: "seqExpr",
|
|
356
|
+
stmts,
|
|
357
|
+
result: { kind: "arrIntrinsic", method: "with", receiver: receiverRef, args: [numericIndex, coercedValue], type: receiverIr, loc },
|
|
246
358
|
type: receiverIr,
|
|
247
359
|
loc,
|
|
248
360
|
};
|
|
249
361
|
}
|
|
250
362
|
if (name === "toSpliced") {
|
|
251
|
-
if (call.arguments.some(ts.isSpreadElement)) {
|
|
252
|
-
lowerer.unsupported("SC1090", call, "spread arguments to Array.toSpliced");
|
|
253
|
-
}
|
|
254
363
|
const receiver = lowerer.lowerExpr(access.expression);
|
|
255
|
-
const start = call.arguments[0]
|
|
256
|
-
|
|
257
|
-
: { kind: "numLit", value: 0, type: F64, loc };
|
|
258
|
-
const deleteCountDefault = {
|
|
259
|
-
kind: "numLit",
|
|
260
|
-
value: NaN,
|
|
261
|
-
type: F64,
|
|
262
|
-
loc,
|
|
263
|
-
};
|
|
264
|
-
const deleteCount = call.arguments.length === 0
|
|
265
|
-
? { kind: "numLit", value: 0, type: F64, loc }
|
|
266
|
-
: call.arguments[1]
|
|
267
|
-
? lowerOptionalArgument(lowerer, call.arguments[1], F64, deleteCountDefault)
|
|
268
|
-
: { kind: "numLit", value: Infinity, type: F64, loc };
|
|
364
|
+
const start = lowerArrayPosition(lowerer, call.arguments[0], numLit(0, loc), "array toSpliced start");
|
|
365
|
+
const deleteCount = lowerArrayPosition(lowerer, call.arguments[1], numLit(call.arguments.length === 1 ? Infinity : 0, loc), "array toSpliced deleteCount");
|
|
269
366
|
const itemNodes = call.arguments.slice(2);
|
|
270
|
-
const
|
|
367
|
+
const hasSpread = itemNodes.some(ts.isSpreadElement);
|
|
368
|
+
const itemProbes = hasSpread ? [] : itemNodes.map((arg) => tryLowerExpression(lowerer, arg));
|
|
271
369
|
const statefulItems = itemProbes.some((probe) => probe !== null && lowerer.runtimeOptionalWidening(probe.type, elem) !== null);
|
|
272
|
-
const items =
|
|
273
|
-
?
|
|
274
|
-
:
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
370
|
+
const items = hasSpread
|
|
371
|
+
? lowerArraySpreadItems(lowerer, itemNodes, elem, receiverIr, loc)
|
|
372
|
+
: statefulItems
|
|
373
|
+
? lowerArrayValueItems(lowerer, itemNodes.map((arg) => lowerer.lowerExpr(arg)), elem, receiverIr, loc)
|
|
374
|
+
: {
|
|
375
|
+
kind: "arrayLit",
|
|
376
|
+
elems: itemNodes.map((arg) => lowerer.coerceInto(arg, lowerer.lowerExpr(arg), elem)),
|
|
377
|
+
type: receiverIr,
|
|
378
|
+
loc,
|
|
379
|
+
};
|
|
280
380
|
return {
|
|
281
381
|
kind: "arrIntrinsic",
|
|
282
382
|
method: "toSpliced",
|
|
@@ -286,30 +386,27 @@ export function lowerArrayMethodCall(lowerer, call, access) {
|
|
|
286
386
|
loc,
|
|
287
387
|
};
|
|
288
388
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
//
|
|
389
|
+
if (name === "flat")
|
|
390
|
+
return lowerArrayFlatCall(lowerer, call, access, receiverIr);
|
|
391
|
+
// The lib declares wider call forms than the lowered surface — the
|
|
392
|
+
// predicate/mapping HOFs take a thisArg. Each unlowered form is fenced
|
|
292
393
|
// per site (SC2020), never silently truncated to the supported
|
|
293
394
|
// arguments. push/unshift lower every declared form (variadic, 0 args
|
|
294
395
|
// included — Node returns the unchanged length); reduce/reduceRight
|
|
295
396
|
// lower both declared forms (with and without an initial value).
|
|
296
397
|
const arity = {
|
|
297
|
-
push: [0, Number.MAX_SAFE_INTEGER], unshift: [0, Number.MAX_SAFE_INTEGER], pop: [0, 0], indexOf: [1, 2], lastIndexOf: [1, 2], includes: [0, 2], join: [
|
|
398
|
+
push: [0, Number.MAX_SAFE_INTEGER], unshift: [0, Number.MAX_SAFE_INTEGER], pop: [0, 0], indexOf: [1, 2], lastIndexOf: [1, 2], includes: [0, 2], join: [0, 1],
|
|
298
399
|
concat: [0, Number.MAX_SAFE_INTEGER],
|
|
299
|
-
slice: [0, 2], shift: [0, 0], splice: [
|
|
400
|
+
slice: [0, 2], shift: [0, 0], splice: [0, Number.MAX_SAFE_INTEGER], at: [0, 1],
|
|
300
401
|
map: [1, 1], filter: [1, 1], forEach: [1, 1], find: [1, 1], findIndex: [1, 1], some: [1, 1],
|
|
301
402
|
findLast: [1, 1], findLastIndex: [1, 1],
|
|
302
403
|
every: [1, 1], flatMap: [1, 1], reduce: [1, 2], reduceRight: [1, 2],
|
|
303
404
|
}[name];
|
|
304
405
|
if (call.arguments.length < arity[0] || call.arguments.length > arity[1]) {
|
|
305
|
-
const hint = name === "
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
: name === "map" || name === "filter" || name === "forEach" ||
|
|
310
|
-
name === "find" || name === "some" || name === "every" || name === "flatMap"
|
|
311
|
-
? "the thisArg parameter has no lowering — use an arrow function"
|
|
312
|
-
: undefined;
|
|
406
|
+
const hint = name === "map" || name === "filter" || name === "forEach" ||
|
|
407
|
+
name === "find" || name === "some" || name === "every" || name === "flatMap"
|
|
408
|
+
? "the thisArg parameter has no lowering — use an arrow function"
|
|
409
|
+
: undefined;
|
|
313
410
|
lowerer.noLowering(`.${name} with ${call.arguments.length} argument${call.arguments.length === 1 ? "" : "s"}`, call, hint);
|
|
314
411
|
}
|
|
315
412
|
if (name === "push" || name === "unshift") {
|
|
@@ -450,33 +547,37 @@ export function lowerArrayMethodCall(lowerer, call, access) {
|
|
|
450
547
|
return { kind: "call", callee: helper, args: [receiver, ...args], type: receiverIr, loc };
|
|
451
548
|
}
|
|
452
549
|
if (name === "slice") {
|
|
453
|
-
// `a.slice(start?, end?)` — a fresh shallow copy of the index range,
|
|
454
|
-
// JS-exact index handling (ToIntegerOrInfinity, negatives from the
|
|
455
|
-
// end, clamping; omitted args are omitted from the IR — the backend
|
|
456
|
-
// fills 0 / +Infinity, the string-slice convention). Ref elements
|
|
457
|
-
// are RETAINED into the copy: the same references, exactly JS's
|
|
458
|
-
// shallow copy. Every element kind slices — the receiver's own type
|
|
459
|
-
// is the result type.
|
|
460
550
|
const receiver = lowerer.lowerExpr(access.expression);
|
|
461
|
-
const args =
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
}
|
|
551
|
+
const args = [
|
|
552
|
+
lowerArrayPosition(lowerer, call.arguments[0], numLit(0, loc), "array slice start"),
|
|
553
|
+
lowerArrayPosition(lowerer, call.arguments[1], numLit(Infinity, loc), "array slice end"),
|
|
554
|
+
];
|
|
466
555
|
return { kind: "arrIntrinsic", method: "slice", receiver, args, type: receiverIr, loc };
|
|
467
556
|
}
|
|
468
557
|
if (name === "splice") {
|
|
469
|
-
// The REMOVAL forms: splice(start) and splice(start, deleteCount) —
|
|
470
|
-
// Node-exact relative/clamped indices, the removed elements back in
|
|
471
|
-
// order (their ownership moves out of the receiver). Insertion
|
|
472
|
-
// (3+ args) fenced by arity above.
|
|
473
558
|
const receiver = lowerer.lowerExpr(access.expression);
|
|
474
|
-
const args =
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
559
|
+
const args = [
|
|
560
|
+
lowerArrayPosition(lowerer, call.arguments[0], numLit(0, loc), "array splice start"),
|
|
561
|
+
lowerArrayPosition(lowerer, call.arguments[1], numLit(call.arguments.length === 1 ? Infinity : 0, loc), "array splice deleteCount"),
|
|
562
|
+
];
|
|
563
|
+
if (call.arguments.length <= 2) {
|
|
564
|
+
return { kind: "arrIntrinsic", method: "splice", receiver, args, type: receiverIr, loc };
|
|
478
565
|
}
|
|
479
|
-
|
|
566
|
+
const itemNodes = call.arguments.slice(2);
|
|
567
|
+
const hasSpread = itemNodes.some(ts.isSpreadElement);
|
|
568
|
+
const itemProbes = hasSpread ? [] : itemNodes.map((arg) => tryLowerExpression(lowerer, arg));
|
|
569
|
+
const statefulItems = itemProbes.some((probe) => probe !== null && lowerer.runtimeOptionalWidening(probe.type, elem) !== null);
|
|
570
|
+
const items = hasSpread
|
|
571
|
+
? lowerArraySpreadItems(lowerer, itemNodes, elem, receiverIr, loc)
|
|
572
|
+
: statefulItems
|
|
573
|
+
? lowerArrayValueItems(lowerer, itemNodes.map((arg) => lowerer.lowerExpr(arg)), elem, receiverIr, loc)
|
|
574
|
+
: {
|
|
575
|
+
kind: "arrayLit",
|
|
576
|
+
elems: itemNodes.map((arg) => lowerer.coerceInto(arg, lowerer.lowerExpr(arg), elem)),
|
|
577
|
+
type: receiverIr,
|
|
578
|
+
loc,
|
|
579
|
+
};
|
|
580
|
+
return { kind: "arrIntrinsic", method: "spliceInsert", receiver, args: [...args, items], type: receiverIr, loc };
|
|
480
581
|
}
|
|
481
582
|
if (name === "shift") {
|
|
482
583
|
// JS shift exactly: undefined on an empty array, else the first
|
|
@@ -509,7 +610,7 @@ export function lowerArrayMethodCall(lowerer, call, access) {
|
|
|
509
610
|
lowerer.unsupported("SC1090", call, "'.join()' on arrays of this element type (number, string, and boolean arrays join — unions of those with undefined/null arms too, the units printing empty like JS)");
|
|
510
611
|
}
|
|
511
612
|
const receiver = lowerer.lowerExpr(access.expression);
|
|
512
|
-
const sep = lowerer
|
|
613
|
+
const sep = lowerArrayJoinSeparator(lowerer, call.arguments[0], loc);
|
|
513
614
|
return { kind: "arrIntrinsic", method: "join", receiver, args: [sep], type: STRING, loc };
|
|
514
615
|
}
|
|
515
616
|
if (name === "map" || name === "filter" || name === "forEach") {
|
|
@@ -526,6 +627,79 @@ export function lowerArrayMethodCall(lowerer, call, access) {
|
|
|
526
627
|
// reduce / reduceRight
|
|
527
628
|
return lowerArrayReduceCall(lowerer, call, access, name, elem);
|
|
528
629
|
}
|
|
630
|
+
function literalFlatDepth(value) {
|
|
631
|
+
if (value.kind === "numLit")
|
|
632
|
+
return value.value;
|
|
633
|
+
if (value.kind === "strLit")
|
|
634
|
+
return Number(value.value);
|
|
635
|
+
if (value.kind === "boolLit")
|
|
636
|
+
return value.value ? 1 : 0;
|
|
637
|
+
if (value.kind === "unitLit")
|
|
638
|
+
return value.unit === "undefined" ? 1 : 0;
|
|
639
|
+
if (value.kind === "unary" && value.op === "-") {
|
|
640
|
+
const operand = literalFlatDepth(value.operand);
|
|
641
|
+
return operand === null ? null : -operand;
|
|
642
|
+
}
|
|
643
|
+
return null;
|
|
644
|
+
}
|
|
645
|
+
function lowerArrayFlatCall(lowerer, call, access, receiverType) {
|
|
646
|
+
const loc = locOf(call);
|
|
647
|
+
if (call.arguments.length > 1 || call.arguments.some(ts.isSpreadElement)) {
|
|
648
|
+
lowerer.noLowering(`.flat with ${call.arguments.length} arguments`, call);
|
|
649
|
+
}
|
|
650
|
+
const elemCouldNest = (elem) => elem.kind === "array" || elem.kind === "jsval" || elem.kind === "dyn" ||
|
|
651
|
+
(elem.kind === "union" && (lowerer.unions.get(elem.unionId)?.arms.some((arm) => arm.kind === "array" || arm.kind === "jsval" || arm.kind === "dyn") ?? true));
|
|
652
|
+
if (!elemCouldNest(receiverType.elem) && call.arguments[0]) {
|
|
653
|
+
const receiver = lowerer.lowerExpr(access.expression);
|
|
654
|
+
const depth = lowerArrayPosition(lowerer, call.arguments[0], numLit(1, loc), "array flat depth");
|
|
655
|
+
const local = lowerer.declareHiddenLocal("%flatReceiver", receiverType);
|
|
656
|
+
const ref = varRef(local.id, receiverType, loc);
|
|
657
|
+
return {
|
|
658
|
+
kind: "seqExpr",
|
|
659
|
+
stmts: [
|
|
660
|
+
{ kind: "varDecl", localId: local.id, init: receiver, loc },
|
|
661
|
+
{ kind: "exprStmt", expr: depth, loc },
|
|
662
|
+
],
|
|
663
|
+
result: {
|
|
664
|
+
kind: "arrIntrinsic", method: "flatCopy", receiver: ref,
|
|
665
|
+
args: [{ kind: "arrayLit", elems: [], type: receiverType, loc }],
|
|
666
|
+
type: receiverType, loc,
|
|
667
|
+
},
|
|
668
|
+
type: receiverType, loc,
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
const depth = call.arguments[0] ? literalFlatDepth(lowerer.lowerExpr(call.arguments[0])) : 1;
|
|
672
|
+
if (depth === null)
|
|
673
|
+
lowerer.noLowering(".flat with a nonconstant depth", call);
|
|
674
|
+
let remaining = Number.isNaN(depth) || depth <= 0 ? 0 : Math.trunc(depth);
|
|
675
|
+
let result = lowerer.lowerExpr(access.expression);
|
|
676
|
+
let currentType = receiverType;
|
|
677
|
+
let flattened = false;
|
|
678
|
+
while (remaining > 0) {
|
|
679
|
+
const elem = currentType.elem;
|
|
680
|
+
if (elem.kind !== "array" && elemCouldNest(elem)) {
|
|
681
|
+
lowerer.noLowering(".flat over elements with runtime-dependent array shape", call);
|
|
682
|
+
}
|
|
683
|
+
if (elem.kind !== "array")
|
|
684
|
+
break;
|
|
685
|
+
result = {
|
|
686
|
+
kind: "arrIntrinsic", method: "flatOne", receiver: result,
|
|
687
|
+
args: [{ kind: "arrayLit", elems: [], type: elem, loc }],
|
|
688
|
+
type: elem, loc,
|
|
689
|
+
};
|
|
690
|
+
currentType = elem;
|
|
691
|
+
remaining--;
|
|
692
|
+
flattened = true;
|
|
693
|
+
}
|
|
694
|
+
if (!flattened) {
|
|
695
|
+
result = {
|
|
696
|
+
kind: "arrIntrinsic", method: "flatCopy", receiver: result,
|
|
697
|
+
args: [{ kind: "arrayLit", elems: [], type: currentType, loc }],
|
|
698
|
+
type: currentType, loc,
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
return result;
|
|
702
|
+
}
|
|
529
703
|
/** `indexOf` and `includes` normally use the runtime's typed search helpers,
|
|
530
704
|
* but an indexed array read is a `T | undefined` value. Keep that union
|
|
531
705
|
* through a small lowered loop so the needle's missing state is observable:
|
|
@@ -2274,22 +2448,26 @@ function reverseCountedForLoop(loc, body) {
|
|
|
2274
2448
|
* through). */
|
|
2275
2449
|
function lowerArrayAtCall(lowerer, call, access, elem) {
|
|
2276
2450
|
const loc = locOf(call);
|
|
2451
|
+
if (call.arguments.some(ts.isSpreadElement))
|
|
2452
|
+
lowerer.noLowering(".at with spread arguments", call);
|
|
2277
2453
|
const receiver = lowerer.lowerExpr(access.expression);
|
|
2278
|
-
const
|
|
2454
|
+
const indexNode = call.arguments[0];
|
|
2455
|
+
const index = lowerPositionArgument(lowerer, indexNode, numLit(0, loc));
|
|
2279
2456
|
const resultT = lowerer.irTypeOf(call);
|
|
2280
2457
|
if (resultT.kind !== "union")
|
|
2281
2458
|
lowerer.badType(call, lowerer.typeOf(call)); // defensive: T | undefined always maps to a union
|
|
2282
2459
|
const undefTag = lowerer.armTag(resultT.unionId, UNDEFINED_T);
|
|
2283
2460
|
if (undefTag < 0)
|
|
2284
2461
|
lowerer.badType(call, lowerer.typeOf(call));
|
|
2285
|
-
const key = `at:${typeKey(elem)}:${typeKey(resultT)}`;
|
|
2462
|
+
const key = `at:${typeKey(elem)}:${typeKey(resultT)}:${typeKey(index.type)}`;
|
|
2286
2463
|
let name = lowerer.arrHofHelpers.get(key);
|
|
2287
2464
|
if (!name) {
|
|
2288
2465
|
name = `%arr.at.${lowerer.arrHofHelpers.size}`;
|
|
2289
2466
|
lowerer.arrHofHelpers.set(key, name);
|
|
2290
2467
|
const arrT = arrayOf(elem);
|
|
2291
2468
|
const t = varRef("t.0", F64, loc);
|
|
2292
|
-
const i = varRef("i.0",
|
|
2469
|
+
const i = varRef("i.0", index.type, loc);
|
|
2470
|
+
const number = varRef("number.0", F64, loc);
|
|
2293
2471
|
const n = varRef("n.0", F64, loc);
|
|
2294
2472
|
const found = lowerer.coerceToExpected(arrayValueRead(lowerer, varRef("a.0", arrT, loc), t, elem, loc), resultT);
|
|
2295
2473
|
const miss = {
|
|
@@ -2304,20 +2482,21 @@ function lowerArrayAtCall(lowerer, call, access, elem) {
|
|
|
2304
2482
|
const lt = (l, r) => ({ kind: "bin", op: "<", left: l, right: r, type: BOOL, loc });
|
|
2305
2483
|
const body = [
|
|
2306
2484
|
readLenStmt(arrT, loc),
|
|
2485
|
+
{ kind: "varDecl", localId: "number.0", init: positionNumber(lowerer, i, numLit(0, loc), indexNode ?? call, "array at position"), loc },
|
|
2307
2486
|
// ToIntegerOrInfinity: floor is trunc for i >= 0; negatives mirror
|
|
2308
2487
|
// (trunc(i) = -floor(-i)); NaN becomes 0. ±Infinity floors to
|
|
2309
2488
|
// itself and falls out of range below, exactly the spec.
|
|
2310
|
-
{ kind: "varDecl", localId: "t.0", init: floorOf(
|
|
2489
|
+
{ kind: "varDecl", localId: "t.0", init: floorOf(number), loc },
|
|
2311
2490
|
{
|
|
2312
2491
|
kind: "if",
|
|
2313
|
-
cond: lt(
|
|
2314
|
-
then: [{ kind: "assign", localId: "t.0", value: { kind: "bin", op: "-", left: numLit(0, loc), right: floorOf({ kind: "bin", op: "-", left: numLit(0, loc), right:
|
|
2492
|
+
cond: lt(number, numLit(0, loc)),
|
|
2493
|
+
then: [{ kind: "assign", localId: "t.0", value: { kind: "bin", op: "-", left: numLit(0, loc), right: floorOf({ kind: "bin", op: "-", left: numLit(0, loc), right: number, type: F64, loc }), type: F64, loc }, loc }],
|
|
2315
2494
|
else_: null,
|
|
2316
2495
|
loc,
|
|
2317
2496
|
},
|
|
2318
2497
|
{
|
|
2319
2498
|
kind: "if",
|
|
2320
|
-
cond: { kind: "libCall", fn: "num.isNaN", args: [
|
|
2499
|
+
cond: { kind: "libCall", fn: "num.isNaN", args: [number], type: BOOL, loc },
|
|
2321
2500
|
then: [{ kind: "assign", localId: "t.0", value: numLit(0, loc), loc }],
|
|
2322
2501
|
else_: null,
|
|
2323
2502
|
loc,
|
|
@@ -2349,13 +2528,14 @@ function lowerArrayAtCall(lowerer, call, access, elem) {
|
|
|
2349
2528
|
name,
|
|
2350
2529
|
params: [
|
|
2351
2530
|
{ localId: "a.0", name: "a", type: arrT },
|
|
2352
|
-
{ localId: "i.0", name: "i", type:
|
|
2531
|
+
{ localId: "i.0", name: "i", type: index.type },
|
|
2353
2532
|
],
|
|
2354
2533
|
returnType: resultT,
|
|
2355
2534
|
locals: [
|
|
2356
2535
|
{ id: "a.0", name: "a", type: arrT, mutable: true },
|
|
2357
|
-
{ id: "i.0", name: "i", type:
|
|
2536
|
+
{ id: "i.0", name: "i", type: index.type, mutable: false },
|
|
2358
2537
|
{ id: "n.0", name: "n", type: F64, mutable: false },
|
|
2538
|
+
{ id: "number.0", name: "number", type: F64, mutable: false },
|
|
2359
2539
|
{ id: "t.0", name: "t", type: F64, mutable: true },
|
|
2360
2540
|
],
|
|
2361
2541
|
body,
|
|
@@ -2364,6 +2544,84 @@ function lowerArrayAtCall(lowerer, call, access, elem) {
|
|
|
2364
2544
|
}
|
|
2365
2545
|
return { kind: "call", callee: name, args: [receiver, index], type: resultT, loc };
|
|
2366
2546
|
}
|
|
2547
|
+
/** The Array constructor's element and count forms, shared by calls and new. */
|
|
2548
|
+
export function lowerArrayConstructor(lowerer, expr, args) {
|
|
2549
|
+
const loc = locOf(expr);
|
|
2550
|
+
if (args.some(ts.isSpreadElement)) {
|
|
2551
|
+
lowerer.noLowering("Array constructor with spread arguments", expr);
|
|
2552
|
+
}
|
|
2553
|
+
let result = lowerer.mapTypeOf(lowerer.typeOf(expr));
|
|
2554
|
+
if (result?.kind !== "array") {
|
|
2555
|
+
const contextual = lowerer.checker.getContextualType(expr);
|
|
2556
|
+
if (contextual)
|
|
2557
|
+
result = lowerer.mapTypeOf(contextual);
|
|
2558
|
+
}
|
|
2559
|
+
if (result?.kind !== "array" || !isSupportedArrayElem(result.elem)) {
|
|
2560
|
+
lowerer.badType(expr, lowerer.typeOf(expr));
|
|
2561
|
+
}
|
|
2562
|
+
const argType = args.length === 1 ? lowerer.mapTypeOf(lowerer.typeOf(args[0])) : null;
|
|
2563
|
+
if (argType?.kind === "dyn" || argType?.kind === "jsval") {
|
|
2564
|
+
lowerer.noLowering("Array constructor with a dynamically typed sole argument", args[0]);
|
|
2565
|
+
}
|
|
2566
|
+
if (args.length === 1 && (argType?.kind === "f64" ||
|
|
2567
|
+
(argType?.kind === "union" && lowerer.armTag(argType.unionId, F64) >= 0))) {
|
|
2568
|
+
const count = lowerer.lowerExpr(args[0]);
|
|
2569
|
+
const numberTag = count.type.kind === "union" ? lowerer.armTag(count.type.unionId, F64) : -1;
|
|
2570
|
+
if (count.type.kind !== "f64" && numberTag < 0) {
|
|
2571
|
+
lowerer.noLowering("Array constructor length with a value that may be missing", args[0]);
|
|
2572
|
+
}
|
|
2573
|
+
const input = count.type.kind === "union" ? lowerer.declareHiddenLocal("%arrayCtorArg", count.type) : null;
|
|
2574
|
+
const inputRef = input ? varRef(input.id, count.type, loc) : null;
|
|
2575
|
+
const local = lowerer.declareHiddenLocal("%arrayLengthCtor", result);
|
|
2576
|
+
const ref = varRef(local.id, result, loc);
|
|
2577
|
+
const length = inputRef && count.type.kind === "union"
|
|
2578
|
+
? { kind: "unionNarrow", unionId: count.type.unionId, tag: numberTag, value: inputRef, type: F64, loc }
|
|
2579
|
+
: count;
|
|
2580
|
+
const resize = { kind: "arraySetLength", arr: ref, length, loc };
|
|
2581
|
+
return {
|
|
2582
|
+
kind: "seqExpr",
|
|
2583
|
+
stmts: [
|
|
2584
|
+
...(input ? [{ kind: "varDecl", localId: input.id, init: count, loc }] : []),
|
|
2585
|
+
{ kind: "varDecl", localId: local.id, init: { kind: "arrayLit", elems: [], type: result, loc }, loc },
|
|
2586
|
+
...(inputRef && count.type.kind === "union"
|
|
2587
|
+
? [{ kind: "if",
|
|
2588
|
+
cond: { kind: "unionIsTag", unionId: count.type.unionId, tag: numberTag, value: inputRef, negated: false, type: BOOL, loc },
|
|
2589
|
+
then: [resize],
|
|
2590
|
+
else_: [arrayValueStore(lowerer, ref, numLit(0, loc), lowerer.coerceInto(args[0], inputRef, result.elem), result.elem, loc)],
|
|
2591
|
+
loc }]
|
|
2592
|
+
: [resize]),
|
|
2593
|
+
],
|
|
2594
|
+
result: ref,
|
|
2595
|
+
type: result,
|
|
2596
|
+
loc,
|
|
2597
|
+
};
|
|
2598
|
+
}
|
|
2599
|
+
return { kind: "arrayLit", elems: args.map((arg) => lowerer.lowerExprExpecting(arg, result.elem)), type: result, loc };
|
|
2600
|
+
}
|
|
2601
|
+
/** `Array.of` creates an array of its arguments, including a lone number. */
|
|
2602
|
+
export function lowerArrayOfCall(lowerer, call, access) {
|
|
2603
|
+
if (call.questionDotToken || access.questionDotToken || access.name.text !== "of" ||
|
|
2604
|
+
!lowerer.isStdlibGlobal(access.expression, "Array"))
|
|
2605
|
+
return null;
|
|
2606
|
+
let result = lowerer.mapTypeOf(lowerer.typeOf(call));
|
|
2607
|
+
if (result?.kind !== "array") {
|
|
2608
|
+
const contextual = lowerer.checker.getContextualType(call);
|
|
2609
|
+
if (contextual)
|
|
2610
|
+
result = lowerer.mapTypeOf(contextual);
|
|
2611
|
+
}
|
|
2612
|
+
if (result?.kind !== "array" || !isSupportedArrayElem(result.elem)) {
|
|
2613
|
+
lowerer.badType(call, lowerer.typeOf(call));
|
|
2614
|
+
}
|
|
2615
|
+
if (call.arguments.some(ts.isSpreadElement)) {
|
|
2616
|
+
return lowerArraySpreadItems(lowerer, call.arguments, result.elem, result, locOf(call));
|
|
2617
|
+
}
|
|
2618
|
+
return {
|
|
2619
|
+
kind: "arrayLit",
|
|
2620
|
+
elems: call.arguments.map((arg) => lowerer.lowerExprExpecting(arg, result.elem)),
|
|
2621
|
+
type: result,
|
|
2622
|
+
loc: locOf(call),
|
|
2623
|
+
};
|
|
2624
|
+
}
|
|
2367
2625
|
/** `Array.from({ length: n }, mapfn)` — the counted-generation idiom — on
|
|
2368
2626
|
* THE stdlib Array global. The source must be an OBJECT LITERAL whose
|
|
2369
2627
|
* single property is `length` (the shape the idiom always spells; the
|
|
@@ -2373,8 +2631,10 @@ function lowerArrayAtCall(lowerer, call, access, elem) {
|
|
|
2373
2631
|
* checker's own `unknown` for it — and pushing each result. The loop
|
|
2374
2632
|
* bound is `i <= n - 1`, which IS ToLength for the finite lengths that
|
|
2375
2633
|
* terminate (fractional lengths truncate, negative/NaN produce an empty
|
|
2376
|
-
* array — Node-exact).
|
|
2377
|
-
*
|
|
2634
|
+
* array — Node-exact). Array inputs copy through the iterator's indexed
|
|
2635
|
+
* values, including holes as present undefined, and may map each value.
|
|
2636
|
+
* Set inputs copy their values; strings iterate by code point and may map.
|
|
2637
|
+
* Other iterable shapes keep the fence. Null when the callee isn't an
|
|
2378
2638
|
* Array-static access. */
|
|
2379
2639
|
export function lowerArrayFromCall(lowerer, call, access) {
|
|
2380
2640
|
if (call.questionDotToken || access.questionDotToken)
|
|
@@ -2409,23 +2669,70 @@ export function lowerArrayFromCall(lowerer, call, access) {
|
|
|
2409
2669
|
return { kind: "arrayNewLen", length: n, type: arrT, loc };
|
|
2410
2670
|
}
|
|
2411
2671
|
}
|
|
2412
|
-
//
|
|
2413
|
-
//
|
|
2414
|
-
//
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
const src =
|
|
2418
|
-
if (src.type.kind === "
|
|
2672
|
+
// Array iteration reads length again after every mapper call. Without
|
|
2673
|
+
// a mapper, a dense reversed copy followed by reversal preserves order
|
|
2674
|
+
// and materializes holes while keeping element reference identity.
|
|
2675
|
+
if ((args.length === 1 || args.length === 2) && !ts.isObjectLiteralExpression(args[0])) {
|
|
2676
|
+
const source = lowerer.lowerExpr(args[0]);
|
|
2677
|
+
const src = source.type.kind === "string" && args.length === 2 ? strCharsCall(lowerer, source, loc) : source;
|
|
2678
|
+
if (src.type.kind === "array") {
|
|
2679
|
+
const arrT = src.type;
|
|
2680
|
+
if (args.length === 1) {
|
|
2681
|
+
const dense = { kind: "arrIntrinsic", method: "toReversed", receiver: src, args: [], type: arrT, loc };
|
|
2682
|
+
return { kind: "arrIntrinsic", method: "reverse", receiver: dense, args: [], type: arrT, loc };
|
|
2683
|
+
}
|
|
2684
|
+
const mapper = args[1];
|
|
2685
|
+
const valueT = arrayValueType(lowerer, arrT.elem);
|
|
2686
|
+
const firstParam = ts.isArrowFunction(mapper) || ts.isFunctionExpression(mapper)
|
|
2687
|
+
? mapper.parameters[0]?.name : undefined;
|
|
2688
|
+
const paramSymbol = firstParam && ts.isIdentifier(firstParam)
|
|
2689
|
+
? lowerer.checker.getSymbolAtLocation(firstParam) : undefined;
|
|
2690
|
+
const previous = paramSymbol ? lowerer.runtimeOptionalBindingTypes.get(paramSymbol) : undefined;
|
|
2691
|
+
if (paramSymbol)
|
|
2692
|
+
lowerer.runtimeOptionalBindingTypes.set(paramSymbol, valueT);
|
|
2693
|
+
let callback;
|
|
2694
|
+
try {
|
|
2695
|
+
callback = hofCallbackArg(lowerer, mapper, [valueT], arrT);
|
|
2696
|
+
}
|
|
2697
|
+
finally {
|
|
2698
|
+
if (paramSymbol) {
|
|
2699
|
+
if (previous === undefined)
|
|
2700
|
+
lowerer.runtimeOptionalBindingTypes.delete(paramSymbol);
|
|
2701
|
+
else
|
|
2702
|
+
lowerer.runtimeOptionalBindingTypes.set(paramSymbol, previous);
|
|
2703
|
+
}
|
|
2704
|
+
}
|
|
2705
|
+
const { fnArg, arity } = callback;
|
|
2706
|
+
if (arity > 2)
|
|
2707
|
+
lowerer.noLowering("Array.from mapper with an array parameter", args[1]);
|
|
2708
|
+
const fnRet = fnArg.type.ret;
|
|
2709
|
+
if (fnRet.kind === "void" || fnRet.kind === "func")
|
|
2710
|
+
lowerer.badType(call, lowerer.typeOf(call));
|
|
2711
|
+
fenceProducedArrayElem(lowerer, call, "'Array.from(array, mapper)'", fnRet);
|
|
2712
|
+
const outElem = callbackArrayElem(lowerer, call, fnRet);
|
|
2713
|
+
const key = `fromArray:${typeKey(arrT.elem)}:${typeKey(outElem)}:${typeKey(fnRet)}:${arity}`;
|
|
2714
|
+
let helper = lowerer.arrHofHelpers.get(key);
|
|
2715
|
+
if (!helper) {
|
|
2716
|
+
helper = `%arr.fromArray.${lowerer.arrHofHelpers.size}`;
|
|
2717
|
+
lowerer.arrHofHelpers.set(key, helper);
|
|
2718
|
+
lowerer.liftedFns.push(buildArrayFromArrayFn(lowerer, helper, arrT.elem, outElem, fnRet, arity, loc));
|
|
2719
|
+
}
|
|
2720
|
+
return { kind: "call", callee: helper, args: [src, fnArg], type: arrayOf(outElem), loc };
|
|
2721
|
+
}
|
|
2722
|
+
if (src.type.kind === "set" && args.length === 1) {
|
|
2723
|
+
return { kind: "setIntrinsic", method: "toArray", receiver: src, args: [], type: arrayOf(src.type.elem), loc };
|
|
2724
|
+
}
|
|
2725
|
+
// String iteration advances by code point; the same helper serves
|
|
2726
|
+
// `[...s]` and both Array.from(s) forms.
|
|
2727
|
+
if (src.type.kind === "string" && args.length === 1)
|
|
2419
2728
|
return strCharsCall(lowerer, src, loc);
|
|
2420
|
-
lowerer.noLowering("Array.from with this argument shape", call, "Array.from(
|
|
2421
|
-
"forms — copy arrays with [...a] and drain Map/Set iterators where they are made");
|
|
2729
|
+
lowerer.noLowering("Array.from with this argument shape", call, "Array.from(array), Array.from(array, mapper), Array.from(aString), Array.from(aString, mapper), and Array.from({ length: n }, mapper) are the lowered forms");
|
|
2422
2730
|
}
|
|
2423
2731
|
const n = args.length === 2 && ts.isObjectLiteralExpression(args[0]) && args[0].properties.length === 1
|
|
2424
2732
|
? lowerLengthProp(lowerer, args[0].properties[0])
|
|
2425
2733
|
: null;
|
|
2426
2734
|
if (!n) {
|
|
2427
|
-
lowerer.noLowering("Array.from with this argument shape", call, "Array.from(
|
|
2428
|
-
"with [...a] and drain Map/Set iterators where they are made");
|
|
2735
|
+
lowerer.noLowering("Array.from with this argument shape", call, "Array.from(array), Array.from(array, mapper), Array.from(aString), Array.from(aString, mapper), and Array.from({ length: n }, mapper) are the lowered forms");
|
|
2429
2736
|
}
|
|
2430
2737
|
if (n.type.kind !== "f64")
|
|
2431
2738
|
lowerer.badType(args[0], lowerer.typeOf(args[0]));
|
|
@@ -2454,6 +2761,49 @@ export function lowerArrayFromCall(lowerer, call, access) {
|
|
|
2454
2761
|
}
|
|
2455
2762
|
return { kind: "call", callee: helper, args: [n, fnArg], type: arrayOf(fnRet), loc };
|
|
2456
2763
|
}
|
|
2764
|
+
/** Map an array iterator to a fresh dense array. The source length remains
|
|
2765
|
+
* live so mapper mutations affect which later indexes are visited. */
|
|
2766
|
+
function buildArrayFromArrayFn(lowerer, name, elem, outElem, fnRet, arity, loc) {
|
|
2767
|
+
const arrT = arrayOf(elem);
|
|
2768
|
+
const outT = arrayOf(outElem);
|
|
2769
|
+
const fnT = funcOf([arrayValueType(lowerer, elem), F64].slice(0, arity), fnRet);
|
|
2770
|
+
const a = varRef("a.0", arrT, loc);
|
|
2771
|
+
const i = varRef("i.0", F64, loc);
|
|
2772
|
+
const out = varRef("out.0", outT, loc);
|
|
2773
|
+
const mapped = {
|
|
2774
|
+
kind: "callValue",
|
|
2775
|
+
callee: varRef("f.0", fnT, loc),
|
|
2776
|
+
args: [arrayValueRead(lowerer, a, i, elem, loc), i].slice(0, arity),
|
|
2777
|
+
type: fnRet,
|
|
2778
|
+
loc,
|
|
2779
|
+
};
|
|
2780
|
+
return {
|
|
2781
|
+
name,
|
|
2782
|
+
params: [{ localId: "a.0", name: "a", type: arrT }, { localId: "f.0", name: "f", type: fnT }],
|
|
2783
|
+
returnType: outT,
|
|
2784
|
+
locals: [
|
|
2785
|
+
{ id: "a.0", name: "a", type: arrT, mutable: true },
|
|
2786
|
+
{ id: "f.0", name: "f", type: fnT, mutable: true },
|
|
2787
|
+
{ id: "out.0", name: "out", type: outT, mutable: false },
|
|
2788
|
+
{ id: "i.0", name: "i", type: F64, mutable: true },
|
|
2789
|
+
],
|
|
2790
|
+
body: [
|
|
2791
|
+
{ kind: "varDecl", localId: "out.0", init: { kind: "arrayLit", elems: [], type: outT, loc }, loc },
|
|
2792
|
+
{ kind: "varDecl", localId: "i.0", init: numLit(0, loc), loc },
|
|
2793
|
+
{
|
|
2794
|
+
kind: "while",
|
|
2795
|
+
cond: { kind: "bin", op: "<", left: i, right: { kind: "arrIntrinsic", method: "length", receiver: a, args: [], type: F64, loc }, type: BOOL, loc },
|
|
2796
|
+
body: [
|
|
2797
|
+
arrayValueStore(lowerer, out, i, mapped, outElem, loc),
|
|
2798
|
+
{ kind: "assign", localId: "i.0", value: { kind: "bin", op: "+", left: i, right: numLit(1, loc), type: F64, loc }, loc },
|
|
2799
|
+
],
|
|
2800
|
+
loc,
|
|
2801
|
+
},
|
|
2802
|
+
{ kind: "return", value: out, loc },
|
|
2803
|
+
],
|
|
2804
|
+
loc,
|
|
2805
|
+
};
|
|
2806
|
+
}
|
|
2457
2807
|
/** `Array.from(s)` / `[...s]` on a STRING: the code-point split into a
|
|
2458
2808
|
* fresh string[], through one interned helper per module. */
|
|
2459
2809
|
export function strCharsCall(lowerer, src, loc) {
|