@scriptc/compiler 0.0.17 → 0.0.18
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 +4 -4
- package/dist/backend/cc.d.ts +5 -0
- package/dist/backend/cc.js +2 -0
- package/dist/backend/cc.js.map +1 -1
- package/dist/backend/emission/emit-async.js +35 -2
- package/dist/backend/emission/emit-async.js.map +1 -1
- package/dist/backend/emission/emit-exprs.js +40 -4
- package/dist/backend/emission/emit-exprs.js.map +1 -1
- package/dist/backend/emission/emitter.js +69 -11
- package/dist/backend/emission/emitter.js.map +1 -1
- package/dist/backend/emission/may-throw.d.ts +3 -3
- package/dist/backend/emission/may-throw.js +15 -4
- package/dist/backend/emission/may-throw.js.map +1 -1
- package/dist/backend/llvm/emitter.js +199 -21
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/frontend/lowering/lower-builtins.js +4 -7
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.js +17 -9
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.d.ts +22 -17
- package/dist/frontend/lowering/lower-containers.js +628 -51
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.js +19 -1
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-island.js +55 -7
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.js +144 -10
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.d.ts +8 -0
- package/dist/frontend/lowering/lower-stmts.js +408 -21
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +38 -0
- package/dist/frontend/lowering/lowerer.js +149 -2
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/surfaces.js +4 -0
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/ir/nodes.d.ts +46 -12
- package/dist/ir/nodes.js +41 -0
- package/dist/ir/nodes.js.map +1 -1
- package/dist/ir/serialize.d.ts +1 -1
- package/dist/ir/serialize.js +1 -1
- package/dist/ir/validate.js +98 -39
- package/dist/ir/validate.js.map +1 -1
- package/dist/library/int-infer.js +191 -25
- package/dist/library/int-infer.js.map +1 -1
- package/dist/library/int-infer.test.js +206 -1
- package/dist/library/int-infer.test.js.map +1 -1
- package/package.json +2 -2
- package/surface-manifest.json +25 -1
|
@@ -930,12 +930,13 @@ export function lowerVarStatement(L, stmt) {
|
|
|
930
930
|
return [];
|
|
931
931
|
return L.lowerDestructuringDecl(decl, isLet);
|
|
932
932
|
}
|
|
933
|
-
//
|
|
934
|
-
//
|
|
935
|
-
//
|
|
936
|
-
//
|
|
937
|
-
|
|
938
|
-
|
|
933
|
+
// STORED protocol values with statically closed representations:
|
|
934
|
+
// numeric value iterators retain source/cursor/done hidden locals; a
|
|
935
|
+
// matchAll drain retains its companion index array so a later
|
|
936
|
+
// for-of can serve `m.index`. Both are const-only interceptions.
|
|
937
|
+
const numericIterator = lowerNumericIteratorDecl(L, decl, isConst);
|
|
938
|
+
if (numericIterator)
|
|
939
|
+
return numericIterator;
|
|
939
940
|
const drained = lowerMatchAllDrainDecl(L, decl, isConst);
|
|
940
941
|
if (drained)
|
|
941
942
|
return drained;
|
|
@@ -943,6 +944,88 @@ export function lowerVarStatement(L, stmt) {
|
|
|
943
944
|
return lowered ? [lowered] : [];
|
|
944
945
|
});
|
|
945
946
|
}
|
|
947
|
+
/** The numeric indexed source of a built-in value-iterator expression:
|
|
948
|
+
* `numbers.values()` or the equivalent
|
|
949
|
+
* `numbers[Symbol.iterator]()`, where numbers is number[] or one of the
|
|
950
|
+
* represented typed arrays (including Uint8Array). This is deliberately
|
|
951
|
+
* a syntax + provenance test rather than a mapping for the iterator
|
|
952
|
+
* object: it has observable mutable protocol state, represented by
|
|
953
|
+
* hidden locals only while a stored binding stays in one function. */
|
|
954
|
+
export function numericIteratorSourceOf(L, expr) {
|
|
955
|
+
if (expr === undefined)
|
|
956
|
+
return null;
|
|
957
|
+
let init = expr;
|
|
958
|
+
while (ts.isParenthesizedExpression(init))
|
|
959
|
+
init = init.expression;
|
|
960
|
+
if (!ts.isCallExpression(init) || init.questionDotToken || init.arguments.length !== 0)
|
|
961
|
+
return null;
|
|
962
|
+
let receiver = null;
|
|
963
|
+
if (ts.isPropertyAccessExpression(init.expression) &&
|
|
964
|
+
!init.expression.questionDotToken &&
|
|
965
|
+
init.expression.name.text === "values" &&
|
|
966
|
+
L.isStdlibMember(init.expression)) {
|
|
967
|
+
receiver = init.expression.expression;
|
|
968
|
+
}
|
|
969
|
+
else if (ts.isElementAccessExpression(init.expression) &&
|
|
970
|
+
!init.expression.questionDotToken &&
|
|
971
|
+
init.expression.argumentExpression !== undefined) {
|
|
972
|
+
let key = init.expression.argumentExpression;
|
|
973
|
+
while (ts.isParenthesizedExpression(key))
|
|
974
|
+
key = key.expression;
|
|
975
|
+
if (ts.isPropertyAccessExpression(key) &&
|
|
976
|
+
L.stdlibGlobalMember(key, "Symbol") === "iterator") {
|
|
977
|
+
receiver = init.expression.expression;
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
if (receiver === null)
|
|
981
|
+
return null;
|
|
982
|
+
const sourceT = L.mapTypeOf(L.typeOf(receiver));
|
|
983
|
+
return ((sourceT?.kind === "array" && sourceT.elem.kind === "f64") ||
|
|
984
|
+
sourceT?.kind === "bytes") ? receiver : null;
|
|
985
|
+
}
|
|
986
|
+
/** A stored numeric value iterator does not become a first-class IR
|
|
987
|
+
* value. Its standard-library provenance proves the built-in iterator
|
|
988
|
+
* shape, so the declaration snapshots the indexed source and initializes
|
|
989
|
+
* the exact mutable protocol state:
|
|
990
|
+
*
|
|
991
|
+
* const %source = numbers; let %index = 0; let %done = false;
|
|
992
|
+
*
|
|
993
|
+
* A same-function for-of resolves the binding symbol through
|
|
994
|
+
* numericIterators and drives these locals. Other value uses retain the
|
|
995
|
+
* iterator-object fence. */
|
|
996
|
+
function lowerNumericIteratorDecl(L, decl, isConst) {
|
|
997
|
+
if (!isConst || !ts.isIdentifier(decl.name))
|
|
998
|
+
return null;
|
|
999
|
+
const sourceNode = numericIteratorSourceOf(L, decl.initializer);
|
|
1000
|
+
if (sourceNode === null)
|
|
1001
|
+
return null;
|
|
1002
|
+
const sym = L.checker.getSymbolAtLocation(decl.name);
|
|
1003
|
+
if (!sym || L.tdzPredeclared.has(sym))
|
|
1004
|
+
return null;
|
|
1005
|
+
const source = L.lowerExpr(sourceNode);
|
|
1006
|
+
if (!((source.type.kind === "array" && source.type.elem.kind === "f64") ||
|
|
1007
|
+
source.type.kind === "bytes")) {
|
|
1008
|
+
L.badType(sourceNode, L.typeOf(sourceNode));
|
|
1009
|
+
}
|
|
1010
|
+
const loc = locOf(decl);
|
|
1011
|
+
const indexedSource = L.declareHiddenLocal("%numiterSource", source.type);
|
|
1012
|
+
const index = L.declareHiddenLocal("%numiterIndex", F64);
|
|
1013
|
+
const done = L.declareHiddenLocal("%numiterDone", BOOL);
|
|
1014
|
+
index.mutable = true;
|
|
1015
|
+
done.mutable = true;
|
|
1016
|
+
L.numericIterators.set(sym, {
|
|
1017
|
+
sourceLocalId: indexedSource.id,
|
|
1018
|
+
sourceType: source.type,
|
|
1019
|
+
indexLocalId: index.id,
|
|
1020
|
+
doneLocalId: done.id,
|
|
1021
|
+
ctx: L.ctx,
|
|
1022
|
+
});
|
|
1023
|
+
return [
|
|
1024
|
+
{ kind: "varDecl", localId: indexedSource.id, init: source, loc },
|
|
1025
|
+
{ kind: "varDecl", localId: index.id, init: { kind: "numLit", value: 0, type: F64, loc }, loc },
|
|
1026
|
+
{ kind: "varDecl", localId: done.id, init: { kind: "boolLit", value: false, type: BOOL, loc }, loc },
|
|
1027
|
+
];
|
|
1028
|
+
}
|
|
946
1029
|
/** The stored-drain interception behind lowerVarStatement: lowers
|
|
947
1030
|
* `const rows = s.matchAll(re)` (a direct stdlib matchAll initializer on
|
|
948
1031
|
* a plain const identifier local) as the companion-index drain and
|
|
@@ -1338,6 +1421,53 @@ export function lowerBindingPattern(L, pattern, srcRef, srcType, isLet, out, dyn
|
|
|
1338
1421
|
});
|
|
1339
1422
|
return;
|
|
1340
1423
|
}
|
|
1424
|
+
// Represented typed arrays are dense numeric iterables. Destructuring
|
|
1425
|
+
// reads their elements in index order; a rest binding drains to a
|
|
1426
|
+
// fresh number[] (never another typed array), exactly the iterator
|
|
1427
|
+
// protocol's Array accumulation.
|
|
1428
|
+
if (srcType.kind === "bytes") {
|
|
1429
|
+
pattern.elements.forEach((el, i) => {
|
|
1430
|
+
if (ts.isOmittedExpression(el) || el.name === undefined)
|
|
1431
|
+
return;
|
|
1432
|
+
const loc = locOf(el);
|
|
1433
|
+
if (el.dotDotDotToken) {
|
|
1434
|
+
if (el.initializer) {
|
|
1435
|
+
L.unsupported("SC1031", el, "defaults on rest elements");
|
|
1436
|
+
}
|
|
1437
|
+
const all = {
|
|
1438
|
+
kind: "bytesIntrinsic",
|
|
1439
|
+
method: "toArray",
|
|
1440
|
+
receiver: srcRef(),
|
|
1441
|
+
args: [],
|
|
1442
|
+
type: arrayOf(F64),
|
|
1443
|
+
loc,
|
|
1444
|
+
};
|
|
1445
|
+
const value = {
|
|
1446
|
+
kind: "arrIntrinsic",
|
|
1447
|
+
method: "slice",
|
|
1448
|
+
receiver: all,
|
|
1449
|
+
args: [{ kind: "numLit", value: i, type: F64, loc }],
|
|
1450
|
+
type: arrayOf(F64),
|
|
1451
|
+
loc,
|
|
1452
|
+
};
|
|
1453
|
+
L.bindPatternTarget(el.name, value, isLet, out);
|
|
1454
|
+
return;
|
|
1455
|
+
}
|
|
1456
|
+
let value = {
|
|
1457
|
+
kind: "bytesIntrinsic",
|
|
1458
|
+
method: "get",
|
|
1459
|
+
receiver: srcRef(),
|
|
1460
|
+
args: [{ kind: "numLit", value: i, type: F64, loc }],
|
|
1461
|
+
type: F64,
|
|
1462
|
+
loc,
|
|
1463
|
+
};
|
|
1464
|
+
if (el.initializer) {
|
|
1465
|
+
value = arrayPositionDefault(L, el, value, srcRef, i, F64, out);
|
|
1466
|
+
}
|
|
1467
|
+
L.bindPatternTarget(el.name, value, isLet, out);
|
|
1468
|
+
});
|
|
1469
|
+
return;
|
|
1470
|
+
}
|
|
1341
1471
|
if (srcType.kind !== "array") {
|
|
1342
1472
|
L.unsupported("SC1031", pattern, `array destructuring of non-array values (the source is ${srcType.kind}-typed)`);
|
|
1343
1473
|
}
|
|
@@ -1979,11 +2109,29 @@ function arrayPositionDefaultValue(L, blame, init, read, srcRef, index, elemT, b
|
|
|
1979
2109
|
if (isUnitType(elemT)) {
|
|
1980
2110
|
return L.lowerExprExpecting(init, bodyT);
|
|
1981
2111
|
}
|
|
2112
|
+
const source = srcRef();
|
|
2113
|
+
const length = source.type.kind === "bytes"
|
|
2114
|
+
? {
|
|
2115
|
+
kind: "bytesIntrinsic",
|
|
2116
|
+
method: "length",
|
|
2117
|
+
receiver: source,
|
|
2118
|
+
args: [],
|
|
2119
|
+
type: F64,
|
|
2120
|
+
loc,
|
|
2121
|
+
}
|
|
2122
|
+
: {
|
|
2123
|
+
kind: "arrIntrinsic",
|
|
2124
|
+
method: "length",
|
|
2125
|
+
receiver: source,
|
|
2126
|
+
args: [],
|
|
2127
|
+
type: F64,
|
|
2128
|
+
loc,
|
|
2129
|
+
};
|
|
1982
2130
|
const inRange = {
|
|
1983
2131
|
kind: "bin",
|
|
1984
2132
|
op: "<",
|
|
1985
2133
|
left: { kind: "numLit", value: index, type: F64, loc },
|
|
1986
|
-
right:
|
|
2134
|
+
right: length,
|
|
1987
2135
|
type: BOOL,
|
|
1988
2136
|
loc,
|
|
1989
2137
|
};
|
|
@@ -5195,8 +5343,8 @@ function lowerArrayAssignInto(L, out, target, tmpRef, blame, loc) {
|
|
|
5195
5343
|
});
|
|
5196
5344
|
elemsRef = () => ({ kind: "varRef", localId: iterTmp.id, type: srcType, loc });
|
|
5197
5345
|
}
|
|
5198
|
-
else if (srcType.kind !== "array" && !isTuple) {
|
|
5199
|
-
if (elements.length === 0 && (srcType.kind === "string" || srcType.kind === "
|
|
5346
|
+
else if (srcType.kind !== "array" && srcType.kind !== "bytes" && !isTuple) {
|
|
5347
|
+
if (elements.length === 0 && (srcType.kind === "string" || srcType.kind === "map" || srcType.kind === "set")) {
|
|
5200
5348
|
// `[] = e` over a statically-iterable source: GetIterator +
|
|
5201
5349
|
// immediate close — no user code can observe it, so evaluating
|
|
5202
5350
|
// the RHS is the whole statement.
|
|
@@ -5220,6 +5368,16 @@ function lowerArrayAssignInto(L, out, target, tmpRef, blame, loc) {
|
|
|
5220
5368
|
}
|
|
5221
5369
|
return { kind: "recordGet", obj: tmpRef(), shapeId: srcType.shapeId, field: String(i), type: fieldType, loc: locOf(at) };
|
|
5222
5370
|
}
|
|
5371
|
+
if (srcType.kind === "bytes") {
|
|
5372
|
+
return {
|
|
5373
|
+
kind: "bytesIntrinsic",
|
|
5374
|
+
method: "get",
|
|
5375
|
+
receiver: tmpRef(),
|
|
5376
|
+
args: [{ kind: "numLit", value: i, type: F64, loc: locOf(at) }],
|
|
5377
|
+
type: F64,
|
|
5378
|
+
loc: locOf(at),
|
|
5379
|
+
};
|
|
5380
|
+
}
|
|
5223
5381
|
// Plain arrays read by index — a pattern wider than the runtime
|
|
5224
5382
|
// array traps (the arrayGet discipline, divergence 4's policy; JS
|
|
5225
5383
|
// would answer undefined).
|
|
@@ -5257,6 +5415,24 @@ function lowerArrayAssignInto(L, out, target, tmpRef, blame, loc) {
|
|
|
5257
5415
|
// defaults on nested targets fence in the shared machinery).
|
|
5258
5416
|
const readOf = (targetT) => {
|
|
5259
5417
|
if (isRest) {
|
|
5418
|
+
if (srcType.kind === "bytes") {
|
|
5419
|
+
const all = {
|
|
5420
|
+
kind: "bytesIntrinsic",
|
|
5421
|
+
method: "toArray",
|
|
5422
|
+
receiver: tmpRef(),
|
|
5423
|
+
args: [],
|
|
5424
|
+
type: arrayOf(F64),
|
|
5425
|
+
loc: locOf(el),
|
|
5426
|
+
};
|
|
5427
|
+
return {
|
|
5428
|
+
kind: "arrIntrinsic",
|
|
5429
|
+
method: "slice",
|
|
5430
|
+
receiver: all,
|
|
5431
|
+
args: [{ kind: "numLit", value: i, type: F64, loc: locOf(el) }],
|
|
5432
|
+
type: arrayOf(F64),
|
|
5433
|
+
loc: locOf(el),
|
|
5434
|
+
};
|
|
5435
|
+
}
|
|
5260
5436
|
return isTuple
|
|
5261
5437
|
? tupleTailValue(L, el, tmpRef, srcType, shape, i, targetT)
|
|
5262
5438
|
: { kind: "arrIntrinsic", method: "slice", receiver: tmpRef(), args: [{ kind: "numLit", value: i, type: F64, loc: locOf(el) }], type: srcType, loc: locOf(el) };
|
|
@@ -5295,7 +5471,9 @@ function lowerArrayAssignInto(L, out, target, tmpRef, blame, loc) {
|
|
|
5295
5471
|
else if (targetT !== null) {
|
|
5296
5472
|
read = isTuple
|
|
5297
5473
|
? undefArmDefault(L, el, defaultNode, read, targetT)
|
|
5298
|
-
: arrayPositionDefaultValue(L, el, defaultNode, read, tmpRef, i, srcType.
|
|
5474
|
+
: arrayPositionDefaultValue(L, el, defaultNode, read, tmpRef, i, srcType.kind === "bytes"
|
|
5475
|
+
? F64
|
|
5476
|
+
: srcType.elem, targetT, out);
|
|
5299
5477
|
}
|
|
5300
5478
|
}
|
|
5301
5479
|
return read;
|
|
@@ -5351,6 +5529,22 @@ export function lowerForOf(L, stmt) {
|
|
|
5351
5529
|
}
|
|
5352
5530
|
L.unsupported("SC1070", stmt, "'for await' (async iteration over anything but process.stdin and readable streams)");
|
|
5353
5531
|
}
|
|
5532
|
+
// A stored numeric value iterator declared in this function keeps its
|
|
5533
|
+
// built-in protocol state in hidden locals (lowerVarStatement). The
|
|
5534
|
+
// iterator's own [Symbol.iterator]() returns itself, so for-of resumes
|
|
5535
|
+
// that cursor rather than starting another indexed-source walk.
|
|
5536
|
+
{
|
|
5537
|
+
let src = stmt.expression;
|
|
5538
|
+
while (ts.isParenthesizedExpression(src))
|
|
5539
|
+
src = src.expression;
|
|
5540
|
+
if (ts.isIdentifier(src)) {
|
|
5541
|
+
const sym = L.resolveValueSymbol(src);
|
|
5542
|
+
const state = sym ? L.numericIterators.get(sym) : undefined;
|
|
5543
|
+
if (state?.ctx === L.ctx) {
|
|
5544
|
+
return lowerForOfStoredNumericIterator(L, stmt, state, labels);
|
|
5545
|
+
}
|
|
5546
|
+
}
|
|
5547
|
+
}
|
|
5354
5548
|
// `for (const k of m.keys())` / `.values()` / `.entries()`: the
|
|
5355
5549
|
// iterator methods consumed DIRECTLY by a for-of head ride the
|
|
5356
5550
|
// container's live walk — exactly `for..of m` with the projection
|
|
@@ -5396,6 +5590,15 @@ export function lowerForOf(L, stmt) {
|
|
|
5396
5590
|
return lowerForOfArrayIter(L, stmt, container, proj);
|
|
5397
5591
|
}
|
|
5398
5592
|
}
|
|
5593
|
+
// Typed arrays expose the same live indexed iterator projections.
|
|
5594
|
+
// Their fixed length makes the walk simpler, but the yielded keys
|
|
5595
|
+
// and [key, numeric value] pairs are identical to Array's.
|
|
5596
|
+
if (recv?.kind === "bytes" && (proj === "keys" || proj === "entries")) {
|
|
5597
|
+
const container = L.lowerExpr(src.expression.expression);
|
|
5598
|
+
if (container.type.kind === "bytes") {
|
|
5599
|
+
return lowerForOfArrayIter(L, stmt, container, proj);
|
|
5600
|
+
}
|
|
5601
|
+
}
|
|
5399
5602
|
}
|
|
5400
5603
|
}
|
|
5401
5604
|
// `for (const m of s.matchAll(re))` — direct call or a stored-const
|
|
@@ -5429,25 +5632,32 @@ export function lowerForOf(L, stmt) {
|
|
|
5429
5632
|
}
|
|
5430
5633
|
}
|
|
5431
5634
|
}
|
|
5432
|
-
//
|
|
5433
|
-
// .
|
|
5434
|
-
//
|
|
5435
|
-
//
|
|
5436
|
-
//
|
|
5437
|
-
let iterSrc = stmt.expression;
|
|
5438
|
-
{
|
|
5635
|
+
// A DIRECT built-in value-iterator expression needs no first-class
|
|
5636
|
+
// iterator object. The numeric recognizer covers number[] / typed
|
|
5637
|
+
// arrays and both values/default spellings; retain the established
|
|
5638
|
+
// generic Array.prototype.values() unwrap for every other T[] too.
|
|
5639
|
+
// Stored numeric values took the retained-state path above.
|
|
5640
|
+
let iterSrc = numericIteratorSourceOf(L, stmt.expression);
|
|
5641
|
+
if (iterSrc === null) {
|
|
5439
5642
|
let e = stmt.expression;
|
|
5440
5643
|
while (ts.isParenthesizedExpression(e))
|
|
5441
5644
|
e = e.expression;
|
|
5442
|
-
if (ts.isCallExpression(e) &&
|
|
5443
|
-
|
|
5645
|
+
if (ts.isCallExpression(e) &&
|
|
5646
|
+
!e.questionDotToken &&
|
|
5647
|
+
e.arguments.length === 0 &&
|
|
5648
|
+
ts.isPropertyAccessExpression(e.expression) &&
|
|
5649
|
+
!e.expression.questionDotToken &&
|
|
5444
5650
|
e.expression.name.text === "values" &&
|
|
5445
5651
|
L.isStdlibMember(e.expression) &&
|
|
5446
5652
|
L.mapTypeOf(L.typeOf(e.expression.expression))?.kind === "array") {
|
|
5447
5653
|
iterSrc = e.expression.expression;
|
|
5448
5654
|
}
|
|
5449
5655
|
}
|
|
5656
|
+
iterSrc ??= stmt.expression;
|
|
5450
5657
|
let iterable = L.lowerExpr(iterSrc);
|
|
5658
|
+
if (iterable.type.kind === "bytes") {
|
|
5659
|
+
return lowerForOfBytes(L, stmt, iterable, labels);
|
|
5660
|
+
}
|
|
5451
5661
|
if (iterable.type.kind !== "array") {
|
|
5452
5662
|
// The lib types strings as iterable too, so those for-ofs typecheck;
|
|
5453
5663
|
// only arrays, Maps, and Sets have a lowering. Named specifically —
|
|
@@ -5665,6 +5875,182 @@ export function lowerForOf(L, stmt) {
|
|
|
5665
5875
|
L.scopes.pop();
|
|
5666
5876
|
}
|
|
5667
5877
|
}
|
|
5878
|
+
/** Direct for-of over a represented typed array. The implicit iterator is
|
|
5879
|
+
* unobservable, so instantiate its source/cursor/done state as ordinary
|
|
5880
|
+
* hidden locals and share the retained numeric-iterator driver. */
|
|
5881
|
+
function lowerForOfBytes(L, stmt, iterable, labels) {
|
|
5882
|
+
const loc = locOf(stmt);
|
|
5883
|
+
const source = L.declareHiddenLocal("%numiterSource", iterable.type);
|
|
5884
|
+
const index = L.declareHiddenLocal("%numiterIndex", F64);
|
|
5885
|
+
const done = L.declareHiddenLocal("%numiterDone", BOOL);
|
|
5886
|
+
index.mutable = true;
|
|
5887
|
+
done.mutable = true;
|
|
5888
|
+
const loop = lowerForOfStoredNumericIterator(L, stmt, {
|
|
5889
|
+
sourceLocalId: source.id,
|
|
5890
|
+
sourceType: iterable.type,
|
|
5891
|
+
indexLocalId: index.id,
|
|
5892
|
+
doneLocalId: done.id,
|
|
5893
|
+
}, labels);
|
|
5894
|
+
return {
|
|
5895
|
+
kind: "block",
|
|
5896
|
+
body: [
|
|
5897
|
+
{ kind: "varDecl", localId: source.id, init: iterable, loc },
|
|
5898
|
+
{ kind: "varDecl", localId: index.id, init: { kind: "numLit", value: 0, type: F64, loc }, loc },
|
|
5899
|
+
{ kind: "varDecl", localId: done.id, init: { kind: "boolLit", value: false, type: BOOL, loc }, loc },
|
|
5900
|
+
loop,
|
|
5901
|
+
],
|
|
5902
|
+
loc,
|
|
5903
|
+
};
|
|
5904
|
+
}
|
|
5905
|
+
/** Drive a stored built-in numeric value iterator through its retained
|
|
5906
|
+
* protocol state. Unlike the ordinary array for-of IR, the cursor
|
|
5907
|
+
* advances BEFORE the source body: breaking or returning from the body
|
|
5908
|
+
* leaves the iterator positioned at the next element. Natural
|
|
5909
|
+
* exhaustion sets the sticky done bit; breaking early does not.
|
|
5910
|
+
*
|
|
5911
|
+
* while (true) {
|
|
5912
|
+
* if (%done) break;
|
|
5913
|
+
* if (%index >= %source.length) { %done = true; break; }
|
|
5914
|
+
* const value = %source[%index];
|
|
5915
|
+
* %index += 1;
|
|
5916
|
+
* <assign/bind head>; <body>
|
|
5917
|
+
* }
|
|
5918
|
+
*/
|
|
5919
|
+
function lowerForOfStoredNumericIterator(L, stmt, state, labels) {
|
|
5920
|
+
let exprTarget = null;
|
|
5921
|
+
let exprTargetNode = null;
|
|
5922
|
+
if (!ts.isVariableDeclarationList(stmt.initializer)) {
|
|
5923
|
+
let target = stmt.initializer;
|
|
5924
|
+
while (ts.isParenthesizedExpression(target))
|
|
5925
|
+
target = target.expression;
|
|
5926
|
+
if (ts.isIdentifier(target)) {
|
|
5927
|
+
exprTarget = L.resolveWritable(target);
|
|
5928
|
+
exprTargetNode = target;
|
|
5929
|
+
}
|
|
5930
|
+
if (!exprTarget) {
|
|
5931
|
+
L.unsupported("SC1090", stmt.initializer, "for-of over a stored numeric iterator assigning anything but a pre-declared identifier");
|
|
5932
|
+
}
|
|
5933
|
+
}
|
|
5934
|
+
const list = ts.isVariableDeclarationList(stmt.initializer) ? stmt.initializer : null;
|
|
5935
|
+
if (list && (list.flags & ts.NodeFlags.Using) !== 0) {
|
|
5936
|
+
L.unsupported("SC1090", list, "'using' declarations (dispose-at-scope-exit semantics)");
|
|
5937
|
+
}
|
|
5938
|
+
const isLet = list !== null && (list.flags & ts.NodeFlags.Let) !== 0;
|
|
5939
|
+
const decl = list ? list.declarations[0] : null;
|
|
5940
|
+
if (decl && !ts.isIdentifier(decl.name))
|
|
5941
|
+
L.unsupported("SC1031", decl.name);
|
|
5942
|
+
const loc = locOf(stmt);
|
|
5943
|
+
const sourceT = state.sourceType;
|
|
5944
|
+
if (!((sourceT.kind === "array" && sourceT.elem.kind === "f64") ||
|
|
5945
|
+
sourceT.kind === "bytes")) {
|
|
5946
|
+
throw new Error("internal: retained numeric iterator has a non-numeric source");
|
|
5947
|
+
}
|
|
5948
|
+
const ref = (localId, type) => ({ kind: "varRef", localId, type, loc });
|
|
5949
|
+
const indexRef = () => ref(state.indexLocalId, F64);
|
|
5950
|
+
const doneRef = () => ref(state.doneLocalId, BOOL);
|
|
5951
|
+
const sourceRef = () => ref(state.sourceLocalId, sourceT);
|
|
5952
|
+
const sourceLength = () => sourceT.kind === "array"
|
|
5953
|
+
? {
|
|
5954
|
+
kind: "arrIntrinsic",
|
|
5955
|
+
method: "length",
|
|
5956
|
+
receiver: sourceRef(),
|
|
5957
|
+
args: [],
|
|
5958
|
+
type: F64,
|
|
5959
|
+
loc,
|
|
5960
|
+
}
|
|
5961
|
+
: {
|
|
5962
|
+
kind: "bytesIntrinsic",
|
|
5963
|
+
method: "length",
|
|
5964
|
+
receiver: sourceRef(),
|
|
5965
|
+
args: [],
|
|
5966
|
+
type: F64,
|
|
5967
|
+
loc,
|
|
5968
|
+
};
|
|
5969
|
+
const sourceValue = () => sourceT.kind === "array"
|
|
5970
|
+
? { kind: "arrayGet", arr: sourceRef(), index: indexRef(), type: F64, loc }
|
|
5971
|
+
: {
|
|
5972
|
+
kind: "bytesIntrinsic",
|
|
5973
|
+
method: "get",
|
|
5974
|
+
receiver: sourceRef(),
|
|
5975
|
+
args: [indexRef()],
|
|
5976
|
+
type: F64,
|
|
5977
|
+
loc,
|
|
5978
|
+
};
|
|
5979
|
+
const one = { kind: "numLit", value: 1, type: F64, loc };
|
|
5980
|
+
L.scopes.push(new Map());
|
|
5981
|
+
try {
|
|
5982
|
+
const varTarget = exprTarget ?? (decl ? forOfVarTarget(L, decl) : null);
|
|
5983
|
+
const value = varTarget
|
|
5984
|
+
? L.declareHiddenLocal("%numiterValue", F64)
|
|
5985
|
+
: L.declareLocal(decl.name, decl.name.text, F64, isLet);
|
|
5986
|
+
const valueRef = ref(value.id, F64);
|
|
5987
|
+
const blame = decl?.name ?? exprTargetNode ?? stmt.initializer;
|
|
5988
|
+
const head = [
|
|
5989
|
+
{
|
|
5990
|
+
kind: "if",
|
|
5991
|
+
cond: doneRef(),
|
|
5992
|
+
then: [{ kind: "break", loc }],
|
|
5993
|
+
else_: null,
|
|
5994
|
+
loc,
|
|
5995
|
+
},
|
|
5996
|
+
{
|
|
5997
|
+
kind: "if",
|
|
5998
|
+
cond: {
|
|
5999
|
+
kind: "bin",
|
|
6000
|
+
op: ">=",
|
|
6001
|
+
left: indexRef(),
|
|
6002
|
+
right: sourceLength(),
|
|
6003
|
+
type: BOOL,
|
|
6004
|
+
loc,
|
|
6005
|
+
},
|
|
6006
|
+
then: [
|
|
6007
|
+
{
|
|
6008
|
+
kind: "assign",
|
|
6009
|
+
localId: state.doneLocalId,
|
|
6010
|
+
value: { kind: "boolLit", value: true, type: BOOL, loc },
|
|
6011
|
+
loc,
|
|
6012
|
+
},
|
|
6013
|
+
{ kind: "break", loc },
|
|
6014
|
+
],
|
|
6015
|
+
else_: null,
|
|
6016
|
+
loc,
|
|
6017
|
+
},
|
|
6018
|
+
{
|
|
6019
|
+
kind: "varDecl",
|
|
6020
|
+
localId: value.id,
|
|
6021
|
+
init: sourceValue(),
|
|
6022
|
+
loc,
|
|
6023
|
+
},
|
|
6024
|
+
{
|
|
6025
|
+
kind: "assign",
|
|
6026
|
+
localId: state.indexLocalId,
|
|
6027
|
+
value: { kind: "bin", op: "+", left: indexRef(), right: one, type: F64, loc },
|
|
6028
|
+
loc,
|
|
6029
|
+
},
|
|
6030
|
+
...(varTarget
|
|
6031
|
+
? [
|
|
6032
|
+
{
|
|
6033
|
+
kind: "assign",
|
|
6034
|
+
localId: varTarget.id,
|
|
6035
|
+
value: L.coerceInto(blame, valueRef, varTarget.type),
|
|
6036
|
+
loc,
|
|
6037
|
+
},
|
|
6038
|
+
]
|
|
6039
|
+
: []),
|
|
6040
|
+
];
|
|
6041
|
+
const body = L.inCtl("loop", () => L.lowerScopedBlock(stmt.statement), labels);
|
|
6042
|
+
return {
|
|
6043
|
+
kind: "while",
|
|
6044
|
+
cond: { kind: "boolLit", value: true, type: BOOL, loc },
|
|
6045
|
+
body: [...head, ...body],
|
|
6046
|
+
...(labels && { labels }),
|
|
6047
|
+
loc,
|
|
6048
|
+
};
|
|
6049
|
+
}
|
|
6050
|
+
finally {
|
|
6051
|
+
L.scopes.pop();
|
|
6052
|
+
}
|
|
6053
|
+
}
|
|
5668
6054
|
/** A DIRECT `s.matchAll(re)` call: a stdlib matchAll property call on a
|
|
5669
6055
|
* string-typed receiver with one regex-typed argument (parens stripped).
|
|
5670
6056
|
* The shape the companion-index machinery serves. */
|
|
@@ -6561,8 +6947,9 @@ function lowerForInArray(L, stmt, labels) {
|
|
|
6561
6947
|
* POSIX read never yields an empty data chunk, so EOF is the only way
|
|
6562
6948
|
* to see one. Awaiting parks the fiber like any await, so timers and
|
|
6563
6949
|
* other fibers interleave with the reads exactly as in Node; the loop
|
|
6564
|
-
* variable is a fresh const Uint8Array binding per iteration.
|
|
6565
|
-
*
|
|
6950
|
+
* variable is a fresh const Uint8Array binding per iteration. Async
|
|
6951
|
+
* function bodies and async module initializers can host it (the await
|
|
6952
|
+
* parks their fiber). */
|
|
6566
6953
|
function lowerForAwaitStdin(L, stmt) {
|
|
6567
6954
|
if (!L.ctx.isAsync) {
|
|
6568
6955
|
L.unsupported("SC1090", stmt, "top-level 'for await' (await outside async functions)");
|