@scriptc/compiler 0.4.3 → 0.4.4
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 +73 -29
- package/dist/backend/llvm/bytes-loop-versions.js +6 -0
- package/dist/backend/llvm/bytes-loop-versions.js.map +1 -1
- package/dist/backend/llvm/bytes-loop-versions.test.js +7 -2
- package/dist/backend/llvm/bytes-loop-versions.test.js.map +1 -1
- package/dist/backend/llvm/debug-info.js +2 -5
- package/dist/backend/llvm/debug-info.js.map +1 -1
- package/dist/backend/llvm/dyn.d.ts +4 -0
- package/dist/backend/llvm/dyn.js +21 -9
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emit-function.js +2 -8
- package/dist/backend/llvm/emit-function.js.map +1 -1
- package/dist/backend/llvm/emit-module.js +16 -6
- package/dist/backend/llvm/emit-module.js.map +1 -1
- package/dist/backend/llvm/emitter.d.ts +10 -0
- package/dist/backend/llvm/emitter.js +9 -5
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/emitter.test.js +11 -2
- package/dist/backend/llvm/emitter.test.js.map +1 -1
- package/dist/backend/llvm/expr-async.js +11 -13
- package/dist/backend/llvm/expr-async.js.map +1 -1
- package/dist/backend/llvm/expr-bytes.js +14 -8
- package/dist/backend/llvm/expr-bytes.js.map +1 -1
- package/dist/backend/llvm/expr-containers.js +14 -0
- package/dist/backend/llvm/expr-containers.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.js +20 -11
- package/dist/backend/llvm/expr-primitives.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.js +16 -4
- package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
- package/dist/backend/llvm/integer-values.d.ts +5 -0
- package/dist/backend/llvm/integer-values.js +27 -3
- package/dist/backend/llvm/integer-values.js.map +1 -1
- package/dist/backend/llvm/lib-dispatch.js +133 -3
- package/dist/backend/llvm/lib-dispatch.js.map +1 -1
- package/dist/backend/llvm/lib-process.js +37 -1
- package/dist/backend/llvm/lib-process.js.map +1 -1
- package/dist/backend/llvm/lib-shared.js +21 -0
- package/dist/backend/llvm/lib-shared.js.map +1 -1
- package/dist/backend/llvm/publish.js +3 -3
- package/dist/backend/llvm/publish.js.map +1 -1
- package/dist/backend/llvm/reference-effects.js +3 -0
- package/dist/backend/llvm/reference-effects.js.map +1 -1
- package/dist/backend/llvm/shapes.js +2 -7
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/backend/llvm/stack-captures.js +1 -5
- package/dist/backend/llvm/stack-captures.js.map +1 -1
- package/dist/backend/llvm/union-repr.d.ts +4 -3
- package/dist/backend/llvm/union-repr.js +6 -5
- package/dist/backend/llvm/union-repr.js.map +1 -1
- package/dist/backend/llvm/walkers.js +13 -0
- package/dist/backend/llvm/walkers.js.map +1 -1
- package/dist/backend/native-codegen-core.d.ts +5 -3
- package/dist/backend/native-codegen-core.js +13 -6
- package/dist/backend/native-codegen-core.js.map +1 -1
- package/dist/backend/native-codegen.test.js +6 -4
- package/dist/backend/native-codegen.test.js.map +1 -1
- package/dist/backend/target-cpu.d.ts +26 -0
- package/dist/backend/target-cpu.js +98 -0
- package/dist/backend/target-cpu.js.map +1 -0
- package/dist/backend/target-cpu.test.d.ts +1 -0
- package/dist/backend/target-cpu.test.js +38 -0
- package/dist/backend/target-cpu.test.js.map +1 -0
- package/dist/cli/command.js +20 -1
- package/dist/cli/command.js.map +1 -1
- package/dist/cli/usage.d.ts +4 -1
- package/dist/cli/usage.js +7 -0
- package/dist/cli/usage.js.map +1 -1
- package/dist/compile-types.d.ts +7 -0
- package/dist/diagnostics/diagnostic.js +1 -1
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/executable/executable-cache.d.ts +3 -0
- package/dist/executable/executable-cache.js +4 -0
- package/dist/executable/executable-cache.js.map +1 -1
- package/dist/executable/executable-cache.test.js +2 -0
- package/dist/executable/executable-cache.test.js.map +1 -1
- package/dist/frontend/decl-projection.js +105 -10
- package/dist/frontend/decl-projection.js.map +1 -1
- package/dist/frontend/lowering/builtins/async-context.d.ts +8 -2
- package/dist/frontend/lowering/builtins/async-context.js +62 -3
- package/dist/frontend/lowering/builtins/async-context.js.map +1 -1
- package/dist/frontend/lowering/builtins/crypto.js +3 -1
- package/dist/frontend/lowering/builtins/crypto.js.map +1 -1
- package/dist/frontend/lowering/builtins/dates.js +4 -1
- package/dist/frontend/lowering/builtins/dates.js.map +1 -1
- package/dist/frontend/lowering/builtins/filesystem-calls.js +3 -3
- package/dist/frontend/lowering/builtins/filesystem-calls.js.map +1 -1
- package/dist/frontend/lowering/builtins/fs-glob.js +6 -3
- package/dist/frontend/lowering/builtins/fs-glob.js.map +1 -1
- package/dist/frontend/lowering/builtins/fs-paths.d.ts +43 -5
- package/dist/frontend/lowering/builtins/fs-paths.js +245 -93
- package/dist/frontend/lowering/builtins/fs-paths.js.map +1 -1
- package/dist/frontend/lowering/builtins/module-calls.js +6 -3
- package/dist/frontend/lowering/builtins/module-calls.js.map +1 -1
- package/dist/frontend/lowering/builtins/promises.d.ts +1 -1
- package/dist/frontend/lowering/builtins/promises.js +149 -138
- package/dist/frontend/lowering/builtins/promises.js.map +1 -1
- package/dist/frontend/lowering/call-signatures.js +0 -1
- package/dist/frontend/lowering/call-signatures.js.map +1 -1
- package/dist/frontend/lowering/cjs-exports-object.js +1 -1
- package/dist/frontend/lowering/cjs-exports-object.js.map +1 -1
- package/dist/frontend/lowering/cjs-inline-require.d.ts +32 -0
- package/dist/frontend/lowering/cjs-inline-require.js +307 -4
- package/dist/frontend/lowering/cjs-inline-require.js.map +1 -1
- package/dist/frontend/lowering/class-dynamic-dispatch.d.ts +10 -0
- package/dist/frontend/lowering/class-dynamic-dispatch.js +114 -0
- package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -1
- package/dist/frontend/lowering/class-method-mutations.js +54 -18
- package/dist/frontend/lowering/class-method-mutations.js.map +1 -1
- package/dist/frontend/lowering/class-protocols.d.ts +25 -0
- package/dist/frontend/lowering/class-protocols.js +183 -1
- package/dist/frontend/lowering/class-protocols.js.map +1 -1
- package/dist/frontend/lowering/completion-callbacks.d.ts +4 -0
- package/dist/frontend/lowering/completion-callbacks.js +146 -0
- package/dist/frontend/lowering/completion-callbacks.js.map +1 -0
- package/dist/frontend/lowering/containers/array-callbacks.d.ts +5 -4
- package/dist/frontend/lowering/containers/array-callbacks.js +7 -6
- package/dist/frontend/lowering/containers/array-callbacks.js.map +1 -1
- package/dist/frontend/lowering/containers/array-construction.js +5 -0
- package/dist/frontend/lowering/containers/array-construction.js.map +1 -1
- package/dist/frontend/lowering/containers/array-methods.js +11 -3
- package/dist/frontend/lowering/containers/array-methods.js.map +1 -1
- package/dist/frontend/lowering/containers/bytes.d.ts +25 -17
- package/dist/frontend/lowering/containers/bytes.js +453 -45
- package/dist/frontend/lowering/containers/bytes.js.map +1 -1
- package/dist/frontend/lowering/date-values.d.ts +11 -0
- package/dist/frontend/lowering/date-values.js +207 -0
- package/dist/frontend/lowering/date-values.js.map +1 -0
- package/dist/frontend/lowering/expressions/array-literals.js +6 -0
- package/dist/frontend/lowering/expressions/array-literals.js.map +1 -1
- package/dist/frontend/lowering/expressions/binary.js +64 -26
- package/dist/frontend/lowering/expressions/binary.js.map +1 -1
- package/dist/frontend/lowering/expressions/element-access.js +79 -0
- package/dist/frontend/lowering/expressions/element-access.js.map +1 -1
- package/dist/frontend/lowering/expressions/identifiers.js +31 -1
- package/dist/frontend/lowering/expressions/identifiers.js.map +1 -1
- package/dist/frontend/lowering/expressions/property-access.js +56 -0
- package/dist/frontend/lowering/expressions/property-access.js.map +1 -1
- package/dist/frontend/lowering/expressions/typeof.js +6 -0
- package/dist/frontend/lowering/expressions/typeof.js.map +1 -1
- package/dist/frontend/lowering/expressions/updates.js +17 -0
- package/dist/frontend/lowering/expressions/updates.js.map +1 -1
- package/dist/frontend/lowering/function-returns.js +15 -0
- package/dist/frontend/lowering/function-returns.js.map +1 -1
- package/dist/frontend/lowering/indexed-unions.d.ts +54 -0
- package/dist/frontend/lowering/indexed-unions.js +401 -0
- package/dist/frontend/lowering/indexed-unions.js.map +1 -0
- package/dist/frontend/lowering/key-order-witness.js +210 -32
- package/dist/frontend/lowering/key-order-witness.js.map +1 -1
- package/dist/frontend/lowering/key-order-witness.test.js +104 -4
- package/dist/frontend/lowering/key-order-witness.test.js.map +1 -1
- package/dist/frontend/lowering/lower-assert.js +15 -3
- package/dist/frontend/lowering/lower-assert.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.js +19 -3
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.js +22 -47
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.js +205 -1
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-generators.js +1 -1
- package/dist/frontend/lowering/lower-generators.js.map +1 -1
- package/dist/frontend/lowering/lower-inspect.d.ts +1 -1
- package/dist/frontend/lowering/lower-inspect.js +35 -11
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.js +21 -5
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.js +100 -34
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +30 -2
- package/dist/frontend/lowering/lowerer.js +68 -7
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/object-factory-new.d.ts +5 -0
- package/dist/frontend/lowering/object-factory-new.js +20 -6
- package/dist/frontend/lowering/object-factory-new.js.map +1 -1
- package/dist/frontend/lowering/overload-choice.d.ts +33 -0
- package/dist/frontend/lowering/overload-choice.js +190 -0
- package/dist/frontend/lowering/overload-choice.js.map +1 -0
- package/dist/frontend/lowering/surfaces.js +55 -2
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/pipeline.js +1 -1
- package/dist/frontend/pipeline.js.map +1 -1
- package/dist/frontend/program-analysis.d.ts +5 -1
- package/dist/frontend/program-analysis.js +5 -0
- package/dist/frontend/program-analysis.js.map +1 -1
- package/dist/frontend/program.d.ts +13 -5
- package/dist/frontend/program.js +197 -14
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/ts7/semantic-model.d.ts +3 -0
- package/dist/frontend/ts7/semantic-model.js +5 -0
- package/dist/frontend/ts7/semantic-model.js.map +1 -1
- package/dist/frontend/ts7/semantic-model.test.js +6 -1
- package/dist/frontend/ts7/semantic-model.test.js.map +1 -1
- package/dist/frontend/ts7/semantic-types.d.ts +1 -1
- package/dist/frontend/ts7/semantic-types.js +1 -1
- package/dist/frontend/ts7/semantic-types.js.map +1 -1
- package/dist/frontend/type-mapper.js +45 -16
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -1
- package/dist/ir/builtin-effects.js +8 -0
- package/dist/ir/builtin-effects.js.map +1 -1
- package/dist/ir/builtin-signatures.d.ts +1 -1
- package/dist/ir/builtin-signatures.js +56 -0
- package/dist/ir/builtin-signatures.js.map +1 -1
- package/dist/ir/int32-slots.js +7 -0
- package/dist/ir/int32-slots.js.map +1 -1
- package/dist/ir/int32-slots.test.js +33 -0
- package/dist/ir/int32-slots.test.js.map +1 -1
- package/dist/ir/integer-loops.js +14 -4
- package/dist/ir/integer-loops.js.map +1 -1
- package/dist/ir/ir.d.ts +13 -3
- package/dist/ir/ir.js +19 -5
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/ir.test.js +2 -1
- package/dist/ir/ir.test.js.map +1 -1
- package/dist/ir/js-ic.d.ts +2 -15
- package/dist/ir/js-ic.js +11 -79
- package/dist/ir/js-ic.js.map +1 -1
- package/dist/ir/js-ic.test.js +0 -142
- package/dist/ir/js-ic.test.js.map +1 -1
- package/dist/ir/runtime-features.js +25 -1
- package/dist/ir/runtime-features.js.map +1 -1
- package/dist/ir/validate.js +46 -5
- package/dist/ir/validate.js.map +1 -1
- package/dist/library/ir-policy.js +1 -0
- package/dist/library/ir-policy.js.map +1 -1
- package/dist/native/prepare.d.ts +1 -1
- package/dist/native/prepare.js +11 -1
- package/dist/native/prepare.js.map +1 -1
- package/dist/native/prepare.test.js +18 -2
- package/dist/native/prepare.test.js.map +1 -1
- package/package.json +20 -20
- package/surface-manifest.json +2 -4
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { BOOL, F64, KEY_ORDER_FIELD, shapeHasAccessorSlots, typeEquals, UNDEFINED_T, } from "../../ir/ir.js";
|
|
1
|
+
import { BOOL, F64, KEY_ORDER_FIELD, shapeHasAccessorSlots, typeEquals, UNDEFINED_T, VOID, } from "../../ir/ir.js";
|
|
2
2
|
import { everyExprChild, everyStmtChild, everyTypeChild, transformStmtList, } from "../../ir/traverse.js";
|
|
3
3
|
import * as ts from "../ts7/adapter.js";
|
|
4
4
|
import { ALWAYS_THROW_LIB_FNS } from "../../ir/builtin-effects.js";
|
|
5
|
+
import { recordMayKeepSource } from "./record-sources.js";
|
|
5
6
|
/** The statements a key-order helper runs for record `ref`: `walks[0]`,
|
|
6
7
|
* its walk over the shape's table, or, for a shape with orderVariants, a
|
|
7
8
|
* branch per order (walks[i] for order i) on the value's KEY_ORDER_FIELD.
|
|
@@ -138,6 +139,14 @@ function replacesWithoutRecords(lowerer, site) {
|
|
|
138
139
|
return (signatures.length > 0 &&
|
|
139
140
|
signatures.every((signature) => primitiveOnly(lowerer.checker.getReturnTypeOfSignature(signature))));
|
|
140
141
|
}
|
|
142
|
+
/** A compiler helper that boxes a class's own state for JavaScript's reads:
|
|
143
|
+
* its prototype data and the property and method dispatch over boxed
|
|
144
|
+
* instances. */
|
|
145
|
+
function boxesOwnState(fnName) {
|
|
146
|
+
return (fnName.startsWith("%prototype.data.") ||
|
|
147
|
+
fnName.startsWith("%dyn.class.") ||
|
|
148
|
+
fnName.startsWith("%class."));
|
|
149
|
+
}
|
|
141
150
|
/** A type whose values are primitives (or void): it holds no record. */
|
|
142
151
|
function primitiveOnly(type) {
|
|
143
152
|
if (type.isUnionType())
|
|
@@ -198,8 +207,11 @@ export function applyKeyOrderWitnesses(lowerer, fns) {
|
|
|
198
207
|
* its checked field setters, each a witness of its own. A checked
|
|
199
208
|
* function's parameters receive native values: a record parameter is
|
|
200
209
|
* passed out, never built, though a callback among them receives values
|
|
201
|
-
* built from JavaScript again.
|
|
202
|
-
|
|
210
|
+
* built from JavaScript again. `built` false starts the walk at a value
|
|
211
|
+
* passed out (a native value boxed for JavaScript); `writes` false leaves
|
|
212
|
+
* out what JavaScript could write into the containers passed out, so the
|
|
213
|
+
* walk counts only what the functions among them receive. */
|
|
214
|
+
const shapesIn = (type, out, seen, produced = false, built = true, writes = true) => {
|
|
203
215
|
const visit = (t, built) => {
|
|
204
216
|
const mark = built ? "" : "-";
|
|
205
217
|
// A container JavaScript builds converts into fresh native storage, so
|
|
@@ -208,7 +220,8 @@ export function applyKeyOrderWitnesses(lowerer, fns) {
|
|
|
208
220
|
// such as callbacks whose parameters it then builds (children passed
|
|
209
221
|
// out). Those children count in both directions.
|
|
210
222
|
const child = (type) => {
|
|
211
|
-
|
|
223
|
+
if (built || writes)
|
|
224
|
+
visit(type, true);
|
|
212
225
|
if (!built)
|
|
213
226
|
visit(type, false);
|
|
214
227
|
return true;
|
|
@@ -253,7 +266,7 @@ export function applyKeyOrderWitnesses(lowerer, fns) {
|
|
|
253
266
|
}
|
|
254
267
|
return everyTypeChild(t, child);
|
|
255
268
|
};
|
|
256
|
-
visit(type,
|
|
269
|
+
visit(type, built);
|
|
257
270
|
};
|
|
258
271
|
const opaqueShapes = new Set();
|
|
259
272
|
const opaqueSeen = new Set();
|
|
@@ -264,6 +277,7 @@ export function applyKeyOrderWitnesses(lowerer, fns) {
|
|
|
264
277
|
* declaration's order, the order their table holds. */
|
|
265
278
|
const declaredShapes = new Set();
|
|
266
279
|
const declaredSeen = new Set();
|
|
280
|
+
const boxedSeen = new Set();
|
|
267
281
|
/** The checked result of a replaceable method call while every site that
|
|
268
282
|
* can replace the method installs functions that return no record. */
|
|
269
283
|
const unchangedMethodResult = (node) => {
|
|
@@ -282,6 +296,12 @@ export function applyKeyOrderWitnesses(lowerer, fns) {
|
|
|
282
296
|
// ── collection ──────────────────────────────────────────────────────
|
|
283
297
|
let pos = 0;
|
|
284
298
|
let fnName = "";
|
|
299
|
+
let currentFn = null;
|
|
300
|
+
/** Literals that start by spreading a binding, by their witness: where
|
|
301
|
+
* each one is built. A literal's notes are shared by its rebuilt copies,
|
|
302
|
+
* so `literalSites` counts every literal of each witness. */
|
|
303
|
+
const leadingSpreads = new Map();
|
|
304
|
+
const literalSites = new Map();
|
|
285
305
|
const loops = [];
|
|
286
306
|
let loopIds = 0;
|
|
287
307
|
const loopKey = () => loops.join("/");
|
|
@@ -339,8 +359,10 @@ export function applyKeyOrderWitnesses(lowerer, fns) {
|
|
|
339
359
|
}
|
|
340
360
|
const segments = notes.literalOf(e);
|
|
341
361
|
const copy = notes.copyOf(e);
|
|
342
|
-
if (segments !== undefined)
|
|
362
|
+
if (segments !== undefined) {
|
|
343
363
|
f.literals.push(segments);
|
|
364
|
+
literalSites.set(segments, (literalSites.get(segments) ?? 0) + 1);
|
|
365
|
+
}
|
|
344
366
|
else if (copy !== undefined)
|
|
345
367
|
f.copies.push({ source: copy, keys: presentKeys });
|
|
346
368
|
else
|
|
@@ -359,9 +381,33 @@ export function applyKeyOrderWitnesses(lowerer, fns) {
|
|
|
359
381
|
case "assignExpr":
|
|
360
382
|
noteInit(e.localId, e.value);
|
|
361
383
|
return;
|
|
384
|
+
case "seqExpr": {
|
|
385
|
+
// A spread literal evaluates its sources into bindings first.
|
|
386
|
+
const literal = e.result;
|
|
387
|
+
if (literal.kind !== "recordLit" || currentFn === null)
|
|
388
|
+
return;
|
|
389
|
+
const segments = notes.literalOf(literal);
|
|
390
|
+
const first = segments?.[0];
|
|
391
|
+
if (segments === undefined || first?.kind !== "spread" || first.value === undefined)
|
|
392
|
+
return;
|
|
393
|
+
const list = leadingSpreads.get(segments) ?? [];
|
|
394
|
+
list.push({ wrapper: e, literal, fn: currentFn });
|
|
395
|
+
leadingSpreads.set(segments, list);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
362
398
|
case "jsonStringify":
|
|
363
399
|
case "dynFrom":
|
|
364
400
|
observedRoots.push(e.value.type);
|
|
401
|
+
// A native value the program hands to JavaScript: the callbacks it
|
|
402
|
+
// holds (itself, a field, an element) receive records JavaScript
|
|
403
|
+
// builds. The helpers that box the program's own state for
|
|
404
|
+
// JavaScript's reads (a class's prototype slots and property
|
|
405
|
+
// dispatch) hand out the class's methods, whose arguments the
|
|
406
|
+
// program's own calls pass as native values, as a replaceable
|
|
407
|
+
// method's result above. Writes through the boxed value are left
|
|
408
|
+
// out: reading a boxed value creates no record.
|
|
409
|
+
if (!boxesOwnState(fnName))
|
|
410
|
+
shapesIn(e.value.type, runtimeShapes, boxedSeen, true, false, false);
|
|
365
411
|
return;
|
|
366
412
|
case "recordKeyGet":
|
|
367
413
|
observedRoots.push(e.obj.type);
|
|
@@ -470,6 +516,7 @@ export function applyKeyOrderWitnesses(lowerer, fns) {
|
|
|
470
516
|
};
|
|
471
517
|
for (const fn of fns) {
|
|
472
518
|
fnName = fn.name;
|
|
519
|
+
currentFn = fn;
|
|
473
520
|
for (const st of fn.body)
|
|
474
521
|
stmt(st);
|
|
475
522
|
}
|
|
@@ -598,13 +645,63 @@ export function applyKeyOrderWitnesses(lowerer, fns) {
|
|
|
598
645
|
}
|
|
599
646
|
return edges;
|
|
600
647
|
};
|
|
648
|
+
// ── ordered spreads ─────────────────────────────────────────────────
|
|
649
|
+
// A value a checked conversion builds keeps its source's key order in
|
|
650
|
+
// its record source when that order is not the table's (record-
|
|
651
|
+
// sources.ts), so every value of a shape enumerates in the table's order
|
|
652
|
+
// or in the order its record source holds. A literal that starts with
|
|
653
|
+
// `...value` creates the value's keys first, in the value's order. When
|
|
654
|
+
// the value's shape is one checked conversions build, the literal
|
|
655
|
+
// carries that order over: after it builds the record it gives the
|
|
656
|
+
// record a source holding the value's order (record.sourceSpread), and
|
|
657
|
+
// the keys the value lacks follow in the table's order, which every
|
|
658
|
+
// literal sequence of the shape agrees with. Such a literal resolves as
|
|
659
|
+
// if its source were in the source's table order, and its own shape
|
|
660
|
+
// counts as built at run time too, since its values may now carry a
|
|
661
|
+
// source. It qualifies when every value of the source has all of the
|
|
662
|
+
// source's keys, both shapes can keep a source, and every later spread
|
|
663
|
+
// whose values may carry a source only overwrites the source's keys
|
|
664
|
+
// (its own order cannot move them).
|
|
665
|
+
const orderedSpreads = new Set();
|
|
666
|
+
const keepsSource = (shapeId) => recordMayKeepSource(lowerer, { kind: "record", shapeId });
|
|
667
|
+
const userKeys = (shapeId) => (shapes.get(shapeId)?.fields ?? []).map((x) => x.name).filter((k) => !k.startsWith("%"));
|
|
668
|
+
const ordersSpread = (segments, sites) => {
|
|
669
|
+
const first = segments[0];
|
|
670
|
+
if (first?.kind !== "spread" || first.maybeAbsent || first.value === undefined)
|
|
671
|
+
return false;
|
|
672
|
+
const sourceId = first.shapeId;
|
|
673
|
+
if (facts.get(sourceId)?.runtime !== true || sites.length !== literalSites.get(segments))
|
|
674
|
+
return false;
|
|
675
|
+
const target = sites[0].literal.type;
|
|
676
|
+
if (target.kind !== "record" || !keepsSource(sourceId) || !keepsSource(target.shapeId))
|
|
677
|
+
return false;
|
|
678
|
+
const localId = first.value.localId;
|
|
679
|
+
const bound = ({ wrapper, literal }) => typeEquals(literal.type, target) &&
|
|
680
|
+
wrapper.stmts.some((st) => st.kind === "varDecl" &&
|
|
681
|
+
st.localId === localId &&
|
|
682
|
+
st.init !== null &&
|
|
683
|
+
st.init.type.kind === "record" &&
|
|
684
|
+
st.init.type.shapeId === sourceId);
|
|
685
|
+
if (!sites.every(bound))
|
|
686
|
+
return false;
|
|
687
|
+
const sourceKeys = new Set(userKeys(sourceId));
|
|
688
|
+
if ([...sourceKeys].some((k) => maybeAbsent(sourceId, k)))
|
|
689
|
+
return false;
|
|
690
|
+
return segments
|
|
691
|
+
.slice(1)
|
|
692
|
+
.every((s) => s.kind === "key" ||
|
|
693
|
+
(s.kind === "spread" &&
|
|
694
|
+
(facts.get(s.shapeId)?.runtime !== true ||
|
|
695
|
+
userKeys(s.shapeId).every((k) => sourceKeys.has(k)))));
|
|
696
|
+
};
|
|
601
697
|
// ── witness sequences ───────────────────────────────────────────────
|
|
602
698
|
/** The key sequences one literal can create, or null when a spread
|
|
603
699
|
* source's order is unknown. A key sits at its first occurrence that
|
|
604
700
|
* certainly defines it; each earlier occurrence that may define it is
|
|
605
701
|
* another possible position. */
|
|
606
|
-
const literalSequences = (segments, keySet, resolved) => {
|
|
702
|
+
const literalSequences = (segments, keySet, resolved, raw) => {
|
|
607
703
|
const occurrences = [];
|
|
704
|
+
const ordered = orderedSpreads.has(segments);
|
|
608
705
|
for (const s of segments) {
|
|
609
706
|
if (s.kind === "opaque")
|
|
610
707
|
return null;
|
|
@@ -613,7 +710,9 @@ export function applyKeyOrderWitnesses(lowerer, fns) {
|
|
|
613
710
|
occurrences.push({ key: s.name, definite: s.definite });
|
|
614
711
|
continue;
|
|
615
712
|
}
|
|
616
|
-
|
|
713
|
+
// An ordered spread's values without a source enumerate in the
|
|
714
|
+
// source's table; the others carry their order over.
|
|
715
|
+
const source = ordered && s === segments[0] ? raw(s.shapeId) : resolved(s.shapeId);
|
|
617
716
|
if (source === "unknown")
|
|
618
717
|
return null;
|
|
619
718
|
for (const k of source.order) {
|
|
@@ -709,12 +808,12 @@ export function applyKeyOrderWitnesses(lowerer, fns) {
|
|
|
709
808
|
first.kind === "spread" &&
|
|
710
809
|
first.shapeId === id &&
|
|
711
810
|
!first.maybeAbsent &&
|
|
712
|
-
!f.runtime &&
|
|
811
|
+
(!f.runtime || orderedSpreads.has(segments)) &&
|
|
713
812
|
segments.every((s) => s.kind !== "opaque") &&
|
|
714
813
|
table.every((k) => !maybeAbsent(id, k)));
|
|
715
814
|
};
|
|
716
815
|
/** One shape's order given its sources' current orders. */
|
|
717
|
-
const resolveOne = (id, current) => {
|
|
816
|
+
const resolveOne = (id, current, raw) => {
|
|
718
817
|
const shape = shapes.get(id);
|
|
719
818
|
if (!inScope(shape))
|
|
720
819
|
return "unknown";
|
|
@@ -735,7 +834,7 @@ export function applyKeyOrderWitnesses(lowerer, fns) {
|
|
|
735
834
|
for (const segments of f.literals) {
|
|
736
835
|
if (copiesItself(id, f, table, segments))
|
|
737
836
|
continue;
|
|
738
|
-
const sequences = literalSequences(segments, keySet, current);
|
|
837
|
+
const sequences = literalSequences(segments, keySet, current, raw);
|
|
739
838
|
if (sequences === null)
|
|
740
839
|
return "unknown";
|
|
741
840
|
for (const seq of sequences)
|
|
@@ -799,31 +898,59 @@ export function applyKeyOrderWitnesses(lowerer, fns) {
|
|
|
799
898
|
const known = result.get(id);
|
|
800
899
|
return known === undefined ? "unknown" : asSource(id, known);
|
|
801
900
|
};
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
for (const
|
|
806
|
-
const
|
|
807
|
-
guess
|
|
808
|
-
}
|
|
809
|
-
const lookup = (id) => members.has(id) ? asSource(id, guess.get(id)) : external(id);
|
|
810
|
-
let stable = false;
|
|
811
|
-
for (let round = 0; round < scc.length + 3 && !stable; round++) {
|
|
812
|
-
stable = true;
|
|
901
|
+
const resolveAll = () => {
|
|
902
|
+
result.clear();
|
|
903
|
+
late.clear();
|
|
904
|
+
for (const scc of sccs) {
|
|
905
|
+
const members = new Set(scc);
|
|
906
|
+
const guess = new Map();
|
|
813
907
|
for (const id of scc) {
|
|
814
|
-
const
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
908
|
+
const shape = shapes.get(id);
|
|
909
|
+
guess.set(id, inScope(shape) ? { order: tableOf(shape) } : "unknown");
|
|
910
|
+
}
|
|
911
|
+
const lookup = (id) => members.has(id) ? asSource(id, guess.get(id)) : external(id);
|
|
912
|
+
const rawLookup = (id) => members.has(id) ? guess.get(id) : (result.get(id) ?? "unknown");
|
|
913
|
+
let stable = false;
|
|
914
|
+
for (let round = 0; round < scc.length + 3 && !stable; round++) {
|
|
915
|
+
stable = true;
|
|
916
|
+
for (const id of scc) {
|
|
917
|
+
const next = resolveOne(id, lookup, rawLookup);
|
|
918
|
+
const prev = guess.get(id);
|
|
919
|
+
const same = next === "unknown"
|
|
920
|
+
? prev === "unknown"
|
|
921
|
+
: prev !== "unknown" && prev.order.join("\0") === next.order.join("\0");
|
|
922
|
+
if (!same) {
|
|
923
|
+
stable = false;
|
|
924
|
+
guess.set(id, next);
|
|
925
|
+
}
|
|
822
926
|
}
|
|
823
927
|
}
|
|
928
|
+
for (const id of scc)
|
|
929
|
+
result.set(id, stable ? guess.get(id) : "unknown");
|
|
824
930
|
}
|
|
825
|
-
|
|
826
|
-
|
|
931
|
+
};
|
|
932
|
+
// Ordered spreads join once their source resolves; each one can let its
|
|
933
|
+
// own shape resolve, which a later spread of it needs.
|
|
934
|
+
resolveAll();
|
|
935
|
+
for (let grew = true; grew;) {
|
|
936
|
+
grew = false;
|
|
937
|
+
for (const [segments, sites] of leadingSpreads) {
|
|
938
|
+
const first = segments[0];
|
|
939
|
+
if (first?.kind !== "spread" || orderedSpreads.has(segments))
|
|
940
|
+
continue;
|
|
941
|
+
// A self copy resolves with its own shape.
|
|
942
|
+
const target = sites[0].literal.type;
|
|
943
|
+
const source = result.get(first.shapeId) ?? "unknown";
|
|
944
|
+
const self = target.kind === "record" && target.shapeId === first.shapeId;
|
|
945
|
+
if ((source === "unknown" && !self) || !ordersSpread(segments, sites))
|
|
946
|
+
continue;
|
|
947
|
+
orderedSpreads.add(segments);
|
|
948
|
+
if (target.kind === "record")
|
|
949
|
+
factsOf(target.shapeId).runtime = true;
|
|
950
|
+
grew = true;
|
|
951
|
+
}
|
|
952
|
+
if (grew)
|
|
953
|
+
resolveAll();
|
|
827
954
|
}
|
|
828
955
|
// ── observation ─────────────────────────────────────────────────────
|
|
829
956
|
const observed = new Set();
|
|
@@ -832,6 +959,19 @@ export function applyKeyOrderWitnesses(lowerer, fns) {
|
|
|
832
959
|
shapesIn({ kind: "record", shapeId: id }, observed, observedSeen);
|
|
833
960
|
for (const root of observedRoots)
|
|
834
961
|
shapesIn(root, observed, observedSeen);
|
|
962
|
+
// An ordered spread's copies of values without a source take the source's
|
|
963
|
+
// order from its table, which must then hold the resolved order.
|
|
964
|
+
const orderedTarget = (sites) => {
|
|
965
|
+
const target = sites[0].literal.type;
|
|
966
|
+
return target.kind === "record" && (result.get(target.shapeId) ?? "unknown") !== "unknown"
|
|
967
|
+
? target.shapeId
|
|
968
|
+
: null;
|
|
969
|
+
};
|
|
970
|
+
for (const [segments, sites] of leadingSpreads) {
|
|
971
|
+
const first = segments[0];
|
|
972
|
+
if (orderedSpreads.has(segments) && first?.kind === "spread" && orderedTarget(sites) !== null)
|
|
973
|
+
observed.add(first.shapeId);
|
|
974
|
+
}
|
|
835
975
|
// ── apply ───────────────────────────────────────────────────────────
|
|
836
976
|
for (const [id, resolved] of result) {
|
|
837
977
|
if (resolved === "unknown" || !observed.has(id))
|
|
@@ -845,6 +985,44 @@ export function applyKeyOrderWitnesses(lowerer, fns) {
|
|
|
845
985
|
shape.declaredOrder = [...shape.declaredOrder.filter(isArrayIndex), ...resolved.order];
|
|
846
986
|
notes.changed.add(id);
|
|
847
987
|
}
|
|
988
|
+
// Ordered spreads carry their source's order over (see above).
|
|
989
|
+
let orderedResults = 0;
|
|
990
|
+
const rewritten = new Set();
|
|
991
|
+
for (const [segments, sites] of leadingSpreads) {
|
|
992
|
+
const first = segments[0];
|
|
993
|
+
if (!orderedSpreads.has(segments) || first?.kind !== "spread" || first.value === undefined)
|
|
994
|
+
continue;
|
|
995
|
+
// A shape that did not resolve keeps today's table for every value.
|
|
996
|
+
if (orderedTarget(sites) === null)
|
|
997
|
+
continue;
|
|
998
|
+
for (const { wrapper, literal, fn } of sites) {
|
|
999
|
+
if (rewritten.has(wrapper))
|
|
1000
|
+
continue;
|
|
1001
|
+
rewritten.add(wrapper);
|
|
1002
|
+
const loc = literal.loc;
|
|
1003
|
+
const localId = `%orderedSpread.${orderedResults++}`;
|
|
1004
|
+
fn.locals.push({ id: localId, name: "%orderedSpread", type: literal.type, mutable: false });
|
|
1005
|
+
const built = { kind: "varRef", localId, type: literal.type, loc };
|
|
1006
|
+
const source = {
|
|
1007
|
+
kind: "varRef",
|
|
1008
|
+
localId: first.value.localId,
|
|
1009
|
+
type: { kind: "record", shapeId: first.shapeId },
|
|
1010
|
+
loc,
|
|
1011
|
+
};
|
|
1012
|
+
wrapper.stmts.push({ kind: "varDecl", localId, init: literal, loc }, {
|
|
1013
|
+
kind: "exprStmt",
|
|
1014
|
+
expr: {
|
|
1015
|
+
kind: "libCall",
|
|
1016
|
+
fn: "record.sourceSpread",
|
|
1017
|
+
args: [built, source],
|
|
1018
|
+
type: VOID,
|
|
1019
|
+
loc,
|
|
1020
|
+
},
|
|
1021
|
+
loc,
|
|
1022
|
+
});
|
|
1023
|
+
wrapper.result = { kind: "varRef", localId, type: literal.type, loc };
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
848
1026
|
// ── per-instance orders ─────────────────────────────────────────────
|
|
849
1027
|
// A shape whose literals write its keys in more than one order has no
|
|
850
1028
|
// one table. Each value then records which of the orders it was written
|