@chenglou/freerange 0.0.2 → 0.0.3

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/dist/fr.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // src/project.ts
4
4
  import { existsSync, realpathSync } from "node:fs";
5
5
  import { resolve as resolve3 } from "node:path";
6
- import * as ts12 from "typescript";
6
+ import * as ts13 from "typescript";
7
7
 
8
8
  // src/domain/number.ts
9
9
  var float64Scratch = new Float64Array(1);
@@ -663,6 +663,7 @@ function declaredKindOf(category) {
663
663
  case "kind":
664
664
  return category.declaredKind;
665
665
  case "importedConstant":
666
+ case "function":
666
667
  case "import":
667
668
  case "opaque":
668
669
  return null;
@@ -775,20 +776,61 @@ function siteLocation(program, site) {
775
776
  return { line: low + 1, column: span.start - lineStarts[low] + 1 };
776
777
  }
777
778
 
778
- // src/requirements/infer.ts
779
- function createExpressionContext(fn, parameterExpressions, parameterIdentityKeys, identityNamespace = `${fn.name}/`) {
780
- const identityKeys = parameterIdentityKeys ?? fn.parameters.map((_, index) => `p${index}`);
781
- if (identityKeys.length !== fn.parameters.length) {
782
- throw new Error(`Expected ${fn.parameters.length} parameter identity keys for ${fn.name}`);
779
+ // src/domain/value-identity.ts
780
+ function createValueIdentityOwner(parent = null) {
781
+ return { kind: "valueIdentityOwner", parent };
782
+ }
783
+ function sameValueIdentity(left, right) {
784
+ if (left === right)
785
+ return true;
786
+ if (left.kind !== right.kind)
787
+ return false;
788
+ switch (left.kind) {
789
+ case "local":
790
+ return right.kind === "local" && left.owner === right.owner && left.value === right.value;
791
+ case "property":
792
+ return right.kind === "property" && left.property === right.property && sameValueIdentity(left.object, right.object);
793
+ case "arrayIndex":
794
+ return right.kind === "arrayIndex" && sameValueIdentity(left.array, right.array) && sameValueIdentity(left.index, right.index);
783
795
  }
796
+ }
797
+ function valueIdentityUsesOwner(identity, owner) {
798
+ switch (identity.kind) {
799
+ case "local": {
800
+ let current = identity.owner;
801
+ while (current != null) {
802
+ if (current === owner)
803
+ return true;
804
+ current = current.parent;
805
+ }
806
+ return false;
807
+ }
808
+ case "property":
809
+ return valueIdentityUsesOwner(identity.object, owner);
810
+ case "arrayIndex":
811
+ return valueIdentityUsesOwner(identity.array, owner) || valueIdentityUsesOwner(identity.index, owner);
812
+ }
813
+ }
814
+
815
+ // src/requirements/infer.ts
816
+ function createExpressionContext(fn, parameterExpressions, parameterIdentities, identityOwner = createValueIdentityOwner()) {
784
817
  const context = {
785
818
  parameterExpressions,
786
- parameterIdentityKeys: identityKeys,
787
- identityNamespace,
819
+ parameterIdentities: [],
820
+ identityOwner,
821
+ identityByValue: [],
788
822
  parameterIndexByValue: [],
789
823
  instructionByValue: [],
790
824
  instructionCount: 0
791
825
  };
826
+ context.parameterIdentities = parameterIdentities ?? fn.parameters.map((parameter) => ({
827
+ kind: "local",
828
+ owner: identityOwner,
829
+ value: parameter.value
830
+ }));
831
+ if (context.parameterIdentities.length !== fn.parameters.length) {
832
+ throw new Error(`Expected ${fn.parameters.length} parameter identities for ${fn.name}`);
833
+ }
792
834
  for (let index = 0;index < fn.parameters.length; index++) {
793
835
  context.parameterIndexByValue[fn.parameters[index].value] = index;
794
836
  }
@@ -891,28 +933,58 @@ function staticRequirement(instruction, site, context, purpose) {
891
933
  }
892
934
  return null;
893
935
  }
894
- function canonicalValueKey(value, context) {
936
+ function canonicalValueIdentity(value, context) {
937
+ const cached = context.identityByValue[value];
938
+ if (cached != null)
939
+ return cached;
895
940
  const stored = resolveStoredValue(value, context);
896
- if (stored !== value)
897
- return canonicalValueKey(stored, context);
941
+ if (stored !== value) {
942
+ const identity2 = canonicalValueIdentity(stored, context);
943
+ context.identityByValue[value] = identity2;
944
+ return identity2;
945
+ }
898
946
  const parameterIndex = context.parameterIndexByValue[value];
899
- if (parameterIndex != null)
900
- return context.parameterIdentityKeys[parameterIndex] ?? `p${parameterIndex}`;
947
+ if (parameterIndex != null) {
948
+ const identity2 = context.parameterIdentities[parameterIndex];
949
+ if (identity2 == null)
950
+ throw new Error(`Missing identity for parameter ${parameterIndex}`);
951
+ context.identityByValue[value] = identity2;
952
+ return identity2;
953
+ }
901
954
  const producer = context.instructionByValue[value];
955
+ let identity;
902
956
  if (producer?.kind === "property") {
903
- return `${canonicalValueKey(producer.object, context)}.${JSON.stringify(producer.property)}`;
904
- }
905
- if (producer?.kind === "arrayLength")
906
- return `${canonicalValueKey(producer.array, context)}.length`;
907
- if (producer?.kind === "stringLength")
908
- return `${canonicalValueKey(producer.value, context)}.length`;
909
- if (producer?.kind === "arrayIndex") {
910
- return `${canonicalValueKey(producer.array, context)}[${canonicalValueKey(producer.index, context)}]`;
957
+ identity = {
958
+ kind: "property",
959
+ object: canonicalValueIdentity(producer.object, context),
960
+ property: producer.property
961
+ };
962
+ } else if (producer?.kind === "arrayLength") {
963
+ identity = {
964
+ kind: "property",
965
+ object: canonicalValueIdentity(producer.array, context),
966
+ property: "length"
967
+ };
968
+ } else if (producer?.kind === "stringLength") {
969
+ identity = {
970
+ kind: "property",
971
+ object: canonicalValueIdentity(producer.value, context),
972
+ property: "length"
973
+ };
974
+ } else if (producer?.kind === "arrayIndex") {
975
+ identity = {
976
+ kind: "arrayIndex",
977
+ array: canonicalValueIdentity(producer.array, context),
978
+ index: canonicalValueIdentity(producer.index, context)
979
+ };
980
+ } else {
981
+ identity = { kind: "local", owner: context.identityOwner, value };
911
982
  }
912
- return `v:${context.identityNamespace}${value}`;
983
+ context.identityByValue[value] = identity;
984
+ return identity;
913
985
  }
914
986
  function sameRuntimeValue(left, right, context) {
915
- return left === right || canonicalValueKey(left, context) === canonicalValueKey(right, context);
987
+ return left === right || sameValueIdentity(canonicalValueIdentity(left, context), canonicalValueIdentity(right, context));
916
988
  }
917
989
  function addPrecondition(preconditions, candidate) {
918
990
  if (candidate.kind === "declaredNumberCheck") {
@@ -1078,10 +1150,10 @@ function completedEvaluation(evaluation) {
1078
1150
 
1079
1151
  // src/engine/state.ts
1080
1152
  function hasNonzeroFact(facts, value) {
1081
- return facts.some((fact) => fact.kind === "nonzero" && fact.value === value);
1153
+ return facts.some((fact) => fact.kind === "nonzero" && sameValueIdentity(fact.value, value));
1082
1154
  }
1083
1155
  function hasIndexFact(facts, kind, index, array) {
1084
- return facts.some((fact) => fact.kind === kind && fact.index === index && fact.array === array);
1156
+ return facts.some((fact) => fact.kind === kind && sameValueIdentity(fact.index, index) && sameValueIdentity(fact.array, array));
1085
1157
  }
1086
1158
  function addValueFact(facts, candidate) {
1087
1159
  if (!facts.some((fact) => sameValueFact(fact, candidate)))
@@ -1103,18 +1175,19 @@ function intersectValueFact(left, right) {
1103
1175
  return left;
1104
1176
  if (left.kind === "nonzero" || right.kind === "nonzero")
1105
1177
  return null;
1106
- if (left.index !== right.index || left.array !== right.array)
1178
+ if (!sameValueIdentity(left.index, right.index) || !sameValueIdentity(left.array, right.array))
1107
1179
  return null;
1108
1180
  return { kind: "belowLength", index: left.index, array: left.array };
1109
1181
  }
1110
1182
  function sameValueFact(left, right) {
1111
1183
  if (left.kind !== right.kind)
1112
1184
  return false;
1113
- if (left.kind === "nonzero" && right.kind === "nonzero")
1114
- return left.value === right.value;
1185
+ if (left.kind === "nonzero" && right.kind === "nonzero") {
1186
+ return sameValueIdentity(left.value, right.value);
1187
+ }
1115
1188
  if (left.kind === "nonzero" || right.kind === "nonzero")
1116
1189
  return false;
1117
- return left.index === right.index && left.array === right.array;
1190
+ return sameValueIdentity(left.index, right.index) && sameValueIdentity(left.array, right.array);
1118
1191
  }
1119
1192
  function emptySharedState(moduleCount) {
1120
1193
  return Array.from({ length: moduleCount }, () => null);
@@ -1268,10 +1341,10 @@ function evaluateInstructionKinded(instruction, state, context) {
1268
1341
  const index = requiredNumberWithFacts(state, instruction.index, context.expressionContext);
1269
1342
  const element = sequence.kind === "tuple" ? tupleElement(sequence, index) : sequence.element;
1270
1343
  const length = sequence.kind === "tuple" ? constantNumber(sequence.elements.length) : sequence.length;
1271
- const indexKey = canonicalValueKey(instruction.index, context.expressionContext);
1272
- const arrayKey = canonicalValueKey(instruction.array, context.expressionContext);
1273
- const assumedValid = hasIndexFact(state.valueFacts, "validIndex", indexKey, arrayKey);
1274
- const inBounds = assumedValid || index.integer && !index.mayBeNaN && index.lower >= 0 && index.upper < length.lower || index.integer && !index.mayBeNaN && index.lower >= 0 && hasIndexFact(state.valueFacts, "belowLength", indexKey, arrayKey);
1344
+ const indexIdentity = canonicalValueIdentity(instruction.index, context.expressionContext);
1345
+ const arrayIdentity = canonicalValueIdentity(instruction.array, context.expressionContext);
1346
+ const assumedValid = hasIndexFact(state.valueFacts, "validIndex", indexIdentity, arrayIdentity);
1347
+ const inBounds = assumedValid || index.integer && !index.mayBeNaN && index.lower >= 0 && index.upper < length.lower || index.integer && !index.mayBeNaN && index.lower >= 0 && hasIndexFact(state.valueFacts, "belowLength", indexIdentity, arrayIdentity);
1275
1348
  const firstPossibleIndex = Math.ceil(Math.max(index.lower, 0));
1276
1349
  const lastPossibleIndex = Math.floor(Math.min(index.upper, length.upper - 1));
1277
1350
  const provablyOut = element == null || firstPossibleIndex > lastPossibleIndex;
@@ -1298,7 +1371,11 @@ function evaluateInstructionKinded(instruction, state, context) {
1298
1371
  addBoundsAssumption(context.boundsAssumptions, { site: instruction.site, kind: "elementInBounds" });
1299
1372
  }
1300
1373
  writeThroughProducers(state, instruction.index, validIndexNumber(index), context.expressionContext.instructionByValue);
1301
- addValueFact(state.valueFacts, { kind: "validIndex", index: indexKey, array: arrayKey });
1374
+ addValueFact(state.valueFacts, {
1375
+ kind: "validIndex",
1376
+ index: indexIdentity,
1377
+ array: arrayIdentity
1378
+ });
1302
1379
  }
1303
1380
  return passthroughValue(element);
1304
1381
  }
@@ -1507,6 +1584,15 @@ function evaluateInstructionKinded(instruction, state, context) {
1507
1584
  return computedNumber(maximumNumbers(operands), operands, instruction.site);
1508
1585
  }
1509
1586
  case "call": {
1587
+ if (instruction.binding != null && state.shared[instruction.binding] == null) {
1588
+ return {
1589
+ kind: "stop",
1590
+ stop: {
1591
+ site: instruction.site,
1592
+ reason: { kind: "moduleRead", binding: instruction.binding }
1593
+ }
1594
+ };
1595
+ }
1510
1596
  const callee = context.program.functions[instruction.function];
1511
1597
  if (callee == null)
1512
1598
  throw new Error(`Unknown function ${instruction.function}`);
@@ -1518,9 +1604,9 @@ function evaluateInstructionKinded(instruction, state, context) {
1518
1604
  }
1519
1605
  const arguments_ = instruction.arguments.map((id) => requiredValue(state, id));
1520
1606
  const argumentExpressions = instruction.arguments.map((id) => numericExpression(id, context.expressionContext));
1521
- const argumentKeys = instruction.arguments.map((id) => canonicalValueKey(id, context.expressionContext));
1522
- const calleeNamespace = `${context.expressionContext.identityNamespace}call:${instruction.function}:${instruction.site}/`;
1523
- const evaluation = context.evaluateFunction(instruction.function, arguments_, argumentExpressions, state.shared, context.callStack, state.valueFacts, argumentKeys, calleeNamespace);
1607
+ const argumentIdentities = instruction.arguments.map((id) => canonicalValueIdentity(id, context.expressionContext));
1608
+ const calleeOwner = createValueIdentityOwner(context.expressionContext.identityOwner);
1609
+ const evaluation = context.evaluateFunction(instruction.function, arguments_, argumentExpressions, state.shared, context.callStack, state.valueFacts, argumentIdentities, calleeOwner);
1524
1610
  const completed = completedEvaluation(evaluation);
1525
1611
  if (completed == null) {
1526
1612
  if (evaluation.stops.length === 0 && evaluation.normal == null) {
@@ -1541,7 +1627,7 @@ function evaluateInstructionKinded(instruction, state, context) {
1541
1627
  return { kind: "stop", stop: { site: instruction.site, reason: { kind: "calleeStopped", callee: instruction.function } } };
1542
1628
  }
1543
1629
  state.shared = completed.sharedState;
1544
- state.valueFacts = completed.valueFacts.filter((fact) => !valueFactUsesNamespace(fact, calleeNamespace));
1630
+ state.valueFacts = completed.valueFacts.filter((fact) => !valueFactUsesOwner(fact, calleeOwner));
1545
1631
  for (let index = 0;index < callee.parameters.length; index++) {
1546
1632
  refineFiniteCallArgument(state, instruction.arguments[index], callee.parameters[index].type, context.expressionContext);
1547
1633
  }
@@ -1583,11 +1669,10 @@ function addBoundsAssumption(assumptions, candidate) {
1583
1669
  assumptions.push(candidate);
1584
1670
  }
1585
1671
  }
1586
- function valueFactUsesNamespace(fact, namespace) {
1587
- const marker = `v:${namespace}`;
1672
+ function valueFactUsesOwner(fact, owner) {
1588
1673
  if (fact.kind === "nonzero")
1589
- return fact.value.includes(marker);
1590
- return fact.index.includes(marker) || fact.array.includes(marker);
1674
+ return valueIdentityUsesOwner(fact.value, owner);
1675
+ return valueIdentityUsesOwner(fact.index, owner) || valueIdentityUsesOwner(fact.array, owner);
1591
1676
  }
1592
1677
  function sentinelsAdmit(sentinels, sentinel) {
1593
1678
  return sentinels === "both" || sentinels === sentinel;
@@ -1947,8 +2032,8 @@ function refineComparison(state, comparison, truth, expressionContext) {
1947
2032
  if (rightProducer?.kind === "arrayLength") {
1948
2033
  addValueFact(result.valueFacts, {
1949
2034
  kind: "belowLength",
1950
- index: canonicalValueKey(comparison.left, expressionContext),
1951
- array: canonicalValueKey(rightProducer.array, expressionContext)
2035
+ index: canonicalValueIdentity(comparison.left, expressionContext),
2036
+ array: canonicalValueIdentity(rightProducer.array, expressionContext)
1952
2037
  });
1953
2038
  }
1954
2039
  }
@@ -1957,8 +2042,8 @@ function refineComparison(state, comparison, truth, expressionContext) {
1957
2042
  if (leftProducer?.kind === "arrayLength") {
1958
2043
  addValueFact(result.valueFacts, {
1959
2044
  kind: "belowLength",
1960
- index: canonicalValueKey(comparison.right, expressionContext),
1961
- array: canonicalValueKey(leftProducer.array, expressionContext)
2045
+ index: canonicalValueIdentity(comparison.right, expressionContext),
2046
+ array: canonicalValueIdentity(leftProducer.array, expressionContext)
1962
2047
  });
1963
2048
  }
1964
2049
  }
@@ -2013,12 +2098,13 @@ function numberWithFacts(state, id, expressionContext) {
2013
2098
  if (held?.kind !== "number")
2014
2099
  return null;
2015
2100
  let result = held;
2016
- const key = canonicalValueKey(id, expressionContext);
2017
- if (state.valueFacts.some((fact) => fact.kind === "validIndex" && fact.index === key)) {
2101
+ const identity = canonicalValueIdentity(id, expressionContext);
2102
+ if (state.valueFacts.some((fact) => fact.kind === "validIndex" && sameValueIdentity(fact.index, identity))) {
2018
2103
  result = validIndexNumber(result);
2019
2104
  }
2020
- if (hasNonzeroFact(state.valueFacts, key))
2105
+ if (hasNonzeroFact(state.valueFacts, identity)) {
2021
2106
  result = excludePointFrom(result, constantNumber(0));
2107
+ }
2022
2108
  return result;
2023
2109
  }
2024
2110
  function validIndexNumber(value2) {
@@ -2040,7 +2126,10 @@ function recordNonzeroComparisonFacts(state, check, expressionContext) {
2040
2126
  }
2041
2127
  }
2042
2128
  function recordNonzeroValueFact(state, value2, expressionContext) {
2043
- addValueFact(state.valueFacts, { kind: "nonzero", value: canonicalValueKey(value2, expressionContext) });
2129
+ addValueFact(state.valueFacts, {
2130
+ kind: "nonzero",
2131
+ value: canonicalValueIdentity(value2, expressionContext)
2132
+ });
2044
2133
  }
2045
2134
  function requiredBoolean(state, id) {
2046
2135
  const value2 = requiredValue(state, id);
@@ -2431,7 +2520,7 @@ function analyzeProgram(program) {
2431
2520
  initializerState[binding] = constantNumber(category.value);
2432
2521
  }
2433
2522
  }
2434
- const initializer = runEvaluation(program.initializer, null, [], [], initializerState, program, [], { identityNamespace: "module/" });
2523
+ const initializer = runEvaluation(program.initializer, null, [], [], initializerState, program, []);
2435
2524
  const moduleValues = publishedModuleValues(program, initializer.run, initializer.evaluation);
2436
2525
  const functionEntrySharedState = seedModuleSlots(program, moduleValues);
2437
2526
  const moduleReads = transitiveModuleBindings(functionUsage(program));
@@ -2452,8 +2541,7 @@ function analyzeProgram(program) {
2452
2541
  argumentExpressions.push({ kind: "parameter", index });
2453
2542
  }
2454
2543
  const { evaluation } = runEvaluation(fn, functionID, arguments_, argumentExpressions, sharedState, program, [], {
2455
- boundsAssumptions: moduleReads[functionID].size > 0 ? initializerBounds : [],
2456
- identityNamespace: `function:${functionID}/`
2544
+ boundsAssumptions: moduleReads[functionID].size > 0 ? initializerBounds : []
2457
2545
  });
2458
2546
  functions.push(publishedAnalysis(fn, evaluation));
2459
2547
  }
@@ -2567,9 +2655,9 @@ function publishedModuleValues(program, run, evaluation) {
2567
2655
  }
2568
2656
  const fullyAnalyzed = evaluation.stops.length === 0 && program.initializerSkips.length === 0 && program.functions.every((lowered) => lowered.kind === "lowered");
2569
2657
  return program.moduleBindings.map((binding, index) => {
2570
- if (binding.category.kind !== "value" || demoted.has(index))
2658
+ if (binding.category.kind !== "value" && binding.category.kind !== "function" || demoted.has(index))
2571
2659
  return null;
2572
- if (holdsMutableStructure(binding.category.declaredKind) && !fullyAnalyzed)
2660
+ if (binding.category.kind === "value" && holdsMutableStructure(binding.category.declaredKind) && !fullyAnalyzed)
2573
2661
  return null;
2574
2662
  const slot = end?.[index];
2575
2663
  return slot ?? null;
@@ -2588,7 +2676,7 @@ function runEvaluation(fn, functionID, arguments_, argumentExpressions, sharedSt
2588
2676
  for (let index = 0;index < fn.parameters.length; index++) {
2589
2677
  initial.values[fn.parameters[index].value] = arguments_[index];
2590
2678
  }
2591
- const expressionContext = createExpressionContext(fn, argumentExpressions, seed.parameterIdentityKeys, seed.identityNamespace ?? fn.name);
2679
+ const expressionContext = createExpressionContext(fn, argumentExpressions, seed.parameterIdentities, seed.identityOwner);
2592
2680
  const preconditions = [];
2593
2681
  const boundsAssumptions = [...seed.boundsAssumptions ?? []];
2594
2682
  const successors = blockSuccessors(fn);
@@ -2607,13 +2695,13 @@ function runEvaluation(fn, functionID, arguments_, argumentExpressions, sharedSt
2607
2695
  expressionContext,
2608
2696
  preconditions,
2609
2697
  boundsAssumptions,
2610
- evaluateFunction: (callee, values, expressions, calleeState, stack, valueFacts, parameterIdentityKeys, identityNamespace) => {
2698
+ evaluateFunction: (callee, values, expressions, calleeState, stack, valueFacts, parameterIdentities, identityOwner) => {
2611
2699
  const calleeFn = program.functions[callee];
2612
2700
  if (calleeFn == null)
2613
2701
  throw new Error(`Unknown function ${callee}`);
2614
2702
  if (calleeFn.kind !== "lowered")
2615
2703
  throw new Error(`Analysis reached unlowered function ${calleeFn.name}`);
2616
- return runEvaluation(calleeFn, callee, values, expressions, calleeState, program, stack, { valueFacts, parameterIdentityKeys, identityNamespace }).evaluation;
2704
+ return runEvaluation(calleeFn, callee, values, expressions, calleeState, program, stack, { valueFacts, parameterIdentities, identityOwner }).evaluation;
2617
2705
  }
2618
2706
  };
2619
2707
  let queueIndex = 0;
@@ -2887,13 +2975,13 @@ function reachableFrom(successors, start) {
2887
2975
  }
2888
2976
 
2889
2977
  // src/lower/program.ts
2890
- import * as ts8 from "typescript";
2978
+ import * as ts9 from "typescript";
2891
2979
 
2892
2980
  // src/lower/accept.ts
2893
2981
  import * as ts from "typescript";
2894
2982
 
2895
2983
  // src/lower/context.ts
2896
- function createFunctionContext(sourceFile, checker, functionsBySymbol, moduleBindingsBySymbol, sites, staticAnnotations = []) {
2984
+ function createFunctionContext(sourceFile, checker, functionsBySymbol, moduleBindingsBySymbol, sites, staticAnnotations = [], returnsVoid = true) {
2897
2985
  const entry = { loopHeader: null, parameters: [], instructions: [], terminator: null };
2898
2986
  return {
2899
2987
  sourceFile,
@@ -2908,6 +2996,7 @@ function createFunctionContext(sourceFile, checker, functionsBySymbol, moduleBin
2908
2996
  bindings: new Map,
2909
2997
  parameters: [],
2910
2998
  assertions: [],
2999
+ returnsVoid,
2911
3000
  loops: []
2912
3001
  };
2913
3002
  }
@@ -3025,8 +3114,10 @@ function unsupported(node, reason) {
3025
3114
  }
3026
3115
 
3027
3116
  // src/lower/accept.ts
3028
- function assertAccepted(root) {
3117
+ function assertAccepted(root, deferFunctionBodies = false) {
3029
3118
  const visit = (node) => {
3119
+ if (deferFunctionBodies && node !== root && ts.isFunctionLike(node))
3120
+ return;
3030
3121
  if (ts.isTypeNode(node))
3031
3122
  return;
3032
3123
  if (ts.isJsxElement(node) || ts.isJsxSelfClosingElement(node) || ts.isJsxFragment(node)) {
@@ -3116,7 +3207,8 @@ var catalog = [
3116
3207
  { path: ["document", "body", "scrollTop"], call: false, fact: { ...anyFinite, integer: false } },
3117
3208
  { path: ["document", "body", "scrollLeft"], call: false, fact: { ...anyFinite, integer: false } },
3118
3209
  { path: ["performance", "now"], call: true, fact: { lower: 0, upper: Number.MAX_VALUE, integer: false } },
3119
- { path: ["Date", "now"], call: true, fact: { lower: 0, upper: Number.MAX_VALUE, integer: true } }
3210
+ { path: ["Date", "now"], call: true, fact: { lower: 0, upper: Number.MAX_VALUE, integer: true } },
3211
+ { path: ["Math", "random"], call: true, fact: { lower: 0, upper: 0.9999999999999999, integer: false } }
3120
3212
  ];
3121
3213
  function platformFact(expression, call, checker) {
3122
3214
  const parts = [];
@@ -3499,7 +3591,8 @@ function lowerExpression(expression, context) {
3499
3591
  arguments_.push(lowerParameterDefault(default_2, parameter.initializer, context));
3500
3592
  continue;
3501
3593
  }
3502
- if (parameter.questionToken != null) {
3594
+ const callableParameter = callee.signature?.declaration?.parameters[index];
3595
+ if (callableParameter != null && ts4.isParameter(callableParameter) && callableParameter.questionToken != null) {
3503
3596
  arguments_.push(addInstruction(context, parameter, { kind: "nullishConstant", sentinel: "undefined" }));
3504
3597
  continue;
3505
3598
  }
@@ -3519,7 +3612,12 @@ function lowerExpression(expression, context) {
3519
3612
  });
3520
3613
  arguments_.push(lowerValueBranch(argument, supplied, () => value2, () => lowerParameterDefault(default_, parameter.initializer, context), context));
3521
3614
  }
3522
- return addInstruction(context, current, { kind: "call", function: callee.id, arguments: arguments_ });
3615
+ return addInstruction(context, current, {
3616
+ kind: "call",
3617
+ function: callee.id,
3618
+ arguments: arguments_,
3619
+ binding: callee.binding
3620
+ });
3523
3621
  }
3524
3622
  if (ts4.isPropertyAccessExpression(current.expression)) {
3525
3623
  const platformCall = current.arguments.length === 0 ? platformFact(current.expression, true, context.checker) : null;
@@ -3871,6 +3969,15 @@ function lowerValueBranch(node, condition, lowerTrueArm, lowerFalseArm, context)
3871
3969
  whenFalse: { block: whenFalse, arguments: [] },
3872
3970
  site: addSite(context, node)
3873
3971
  });
3972
+ return joinValueBranches(node, whenTrue, whenFalse, lowerTrueArm, lowerFalseArm, context);
3973
+ }
3974
+ function lowerBranchingValue(node, condition, lowerTrueArm, lowerFalseArm, context) {
3975
+ const whenTrue = createBlock(context);
3976
+ const whenFalse = createBlock(context);
3977
+ lowerBranchingCondition(condition, whenTrue, whenFalse, context);
3978
+ return joinValueBranches(node, whenTrue, whenFalse, lowerTrueArm, lowerFalseArm, context);
3979
+ }
3980
+ function joinValueBranches(node, whenTrue, whenFalse, lowerTrueArm, lowerFalseArm, context) {
3874
3981
  context.currentBlock = context.blocks[whenTrue];
3875
3982
  const trueValue = lowerTrueArm();
3876
3983
  const trueBlock = context.currentBlock;
@@ -3897,28 +4004,7 @@ function lowerConditionalExpression(expression, context) {
3897
4004
  if (valueKind(resultType, context.checker) == null) {
3898
4005
  throw unsupported(expression, { kind: "valueType", typeText: context.checker.typeToString(resultType) });
3899
4006
  }
3900
- const whenTrue = createBlock(context);
3901
- const whenFalse = createBlock(context);
3902
- lowerBranchingCondition(expression.condition, whenTrue, whenFalse, context);
3903
- context.currentBlock = context.blocks[whenTrue];
3904
- const trueValue = lowerExpression(expression.whenTrue, context);
3905
- const trueBlock = context.currentBlock;
3906
- context.currentBlock = context.blocks[whenFalse];
3907
- const falseValue = lowerExpression(expression.whenFalse, context);
3908
- const falseBlock = context.currentBlock;
3909
- const continuation = createBlock(context, 1);
3910
- terminate(trueBlock, {
3911
- kind: "jump",
3912
- target: { block: continuation, arguments: [trueValue] },
3913
- site: addSite(context, expression)
3914
- });
3915
- terminate(falseBlock, {
3916
- kind: "jump",
3917
- target: { block: continuation, arguments: [falseValue] },
3918
- site: addSite(context, expression)
3919
- });
3920
- context.currentBlock = context.blocks[continuation];
3921
- return context.currentBlock.parameters[0];
4007
+ return lowerBranchingValue(expression, expression.condition, () => lowerExpression(expression.whenTrue, context), () => lowerExpression(expression.whenFalse, context), context);
3922
4008
  }
3923
4009
  function lowerBranchingCondition(expression, whenTrue, whenFalse, context) {
3924
4010
  const current = unwrap(expression, context.checker);
@@ -3953,8 +4039,7 @@ function lowerLogicalExpression(expression, context) {
3953
4039
  requireBooleanCondition(expression.left, context.checker);
3954
4040
  requireBooleanCondition(expression.right, context.checker);
3955
4041
  const isAnd = expression.operatorToken.kind === ts4.SyntaxKind.AmpersandAmpersandToken;
3956
- const condition = lowerExpression(expression.left, context);
3957
- return lowerValueBranch(expression, condition, () => isAnd ? lowerExpression(expression.right, context) : addInstruction(context, expression, { kind: "booleanConstant", value: true }), () => isAnd ? addInstruction(context, expression, { kind: "booleanConstant", value: false }) : lowerExpression(expression.right, context), context);
4042
+ return lowerBranchingValue(expression, expression.left, () => isAnd ? lowerExpression(expression.right, context) : addInstruction(context, expression, { kind: "booleanConstant", value: true }), () => isAnd ? addInstruction(context, expression, { kind: "booleanConstant", value: false }) : lowerExpression(expression.right, context), context);
3958
4043
  }
3959
4044
  function arithmeticOperator(kind) {
3960
4045
  switch (kind) {
@@ -4492,11 +4577,51 @@ function unwrap(expression, checker) {
4492
4577
  }
4493
4578
  }
4494
4579
 
4580
+ // src/lower/function-unit.ts
4581
+ import * as ts5 from "typescript";
4582
+ function callableSignature(unit, checker) {
4583
+ if (unit.initializer == null) {
4584
+ return checker.getSignatureFromDeclaration(unit.declaration) ?? null;
4585
+ }
4586
+ const signatures = checker.getSignaturesOfType(checker.getTypeAtLocation(unit.name), ts5.SignatureKind.Call);
4587
+ if (signatures.length !== 1)
4588
+ return null;
4589
+ const signature = signatures[0];
4590
+ const signatureDeclaration = signature.declaration;
4591
+ if (signatureDeclaration == null || signature.thisParameter != null || signature.parameters.length !== unit.declaration.parameters.length || signatureDeclaration.parameters.some((parameter) => !ts5.isParameter(parameter) || parameter.dotDotDotToken != null)) {
4592
+ return null;
4593
+ }
4594
+ return signature;
4595
+ }
4596
+ function topLevelFunctionUnits(sourceFile) {
4597
+ const units = [];
4598
+ for (const statement of sourceFile.statements) {
4599
+ if (ts5.isFunctionDeclaration(statement) && statement.name != null) {
4600
+ units.push({ name: statement.name, declaration: statement, initializer: null });
4601
+ continue;
4602
+ }
4603
+ if (!ts5.isVariableStatement(statement) || (statement.declarationList.flags & ts5.NodeFlags.Const) === 0)
4604
+ continue;
4605
+ for (const initializer of statement.declarationList.declarations) {
4606
+ if (!ts5.isIdentifier(initializer.name) || initializer.initializer == null)
4607
+ continue;
4608
+ const declaration = directFunctionExpression(initializer.initializer);
4609
+ if (declaration != null) {
4610
+ units.push({ name: initializer.name, declaration, initializer });
4611
+ }
4612
+ }
4613
+ }
4614
+ return units;
4615
+ }
4616
+ function directFunctionExpression(expression) {
4617
+ return ts5.isArrowFunction(expression) || ts5.isFunctionExpression(expression) ? expression : null;
4618
+ }
4619
+
4495
4620
  // src/lower/module.ts
4496
- import * as ts6 from "typescript";
4621
+ import * as ts7 from "typescript";
4497
4622
 
4498
4623
  // src/lower/statements.ts
4499
- import * as ts5 from "typescript";
4624
+ import * as ts6 from "typescript";
4500
4625
  function lowerStatements(statements, context) {
4501
4626
  for (const statement of statements) {
4502
4627
  if (context.currentBlock.terminator != null)
@@ -4505,61 +4630,57 @@ function lowerStatements(statements, context) {
4505
4630
  }
4506
4631
  }
4507
4632
  function lowerStatement(statement, context) {
4508
- if (ts5.isVariableStatement(statement)) {
4633
+ if (ts6.isVariableStatement(statement)) {
4509
4634
  lowerVariableDeclarationList(statement.declarationList, context);
4510
4635
  return;
4511
4636
  }
4512
- if (ts5.isReturnStatement(statement)) {
4637
+ if (ts6.isReturnStatement(statement)) {
4513
4638
  let value2 = statement.expression == null ? null : lowerExpression(statement.expression, context);
4514
- if (value2 == null) {
4515
- const enclosing = ts5.findAncestor(statement, ts5.isFunctionDeclaration);
4516
- const signature = enclosing == null ? undefined : context.checker.getSignatureFromDeclaration(enclosing);
4517
- const returnType = signature == null ? null : context.checker.getReturnTypeOfSignature(signature);
4518
- const returnsVoid = returnType == null || (returnType.flags & (ts5.TypeFlags.Void | ts5.TypeFlags.Undefined)) !== 0;
4519
- if (!returnsVoid) {
4520
- value2 = addInstruction(context, statement, { kind: "nullishConstant", sentinel: "undefined" });
4521
- }
4639
+ if (context.returnsVoid)
4640
+ value2 = null;
4641
+ if (value2 == null && !context.returnsVoid) {
4642
+ value2 = addInstruction(context, statement, { kind: "nullishConstant", sentinel: "undefined" });
4522
4643
  }
4523
4644
  terminate(context.currentBlock, { kind: "return", value: value2, site: addSite(context, statement) });
4524
4645
  return;
4525
4646
  }
4526
- if (ts5.isExpressionStatement(statement)) {
4647
+ if (ts6.isExpressionStatement(statement)) {
4527
4648
  lowerStatementExpression(statement.expression, context);
4528
4649
  return;
4529
4650
  }
4530
- if (ts5.isIfStatement(statement)) {
4651
+ if (ts6.isIfStatement(statement)) {
4531
4652
  lowerIfStatement(statement, context);
4532
4653
  return;
4533
4654
  }
4534
- if (ts5.isForOfStatement(statement)) {
4655
+ if (ts6.isForOfStatement(statement)) {
4535
4656
  lowerForOfStatement(statement, context);
4536
4657
  return;
4537
4658
  }
4538
- if (ts5.isForStatement(statement)) {
4659
+ if (ts6.isForStatement(statement)) {
4539
4660
  lowerForStatement(statement, context);
4540
4661
  return;
4541
4662
  }
4542
- if (ts5.isWhileStatement(statement)) {
4663
+ if (ts6.isWhileStatement(statement)) {
4543
4664
  lowerWhileStatement(statement, context);
4544
4665
  return;
4545
4666
  }
4546
- if (ts5.isContinueStatement(statement)) {
4667
+ if (ts6.isContinueStatement(statement)) {
4547
4668
  lowerContinueStatement(statement, context);
4548
4669
  return;
4549
4670
  }
4550
- if (ts5.isBlock(statement)) {
4671
+ if (ts6.isBlock(statement)) {
4551
4672
  lowerStatements(statement.statements, context);
4552
4673
  return;
4553
4674
  }
4554
- if (ts5.isSwitchStatement(statement)) {
4675
+ if (ts6.isSwitchStatement(statement)) {
4555
4676
  lowerSwitchStatement(statement, context);
4556
4677
  return;
4557
4678
  }
4558
- if (ts5.isThrowStatement(statement)) {
4679
+ if (ts6.isThrowStatement(statement)) {
4559
4680
  terminate(context.currentBlock, { kind: "thrown", site: addSite(context, statement) });
4560
4681
  return;
4561
4682
  }
4562
- throw unsupported(statement, { kind: "statementForm", syntax: ts5.SyntaxKind[statement.kind] });
4683
+ throw unsupported(statement, { kind: "statementForm", syntax: ts6.SyntaxKind[statement.kind] });
4563
4684
  }
4564
4685
  function lowerIfStatement(statement, context) {
4565
4686
  const bindingsBeforeBranch = new Map(context.bindings);
@@ -4586,7 +4707,7 @@ function lowerSwitchStatement(statement, context) {
4586
4707
  const subjectType = context.checker.getTypeAtLocation(statement.expression);
4587
4708
  const subjectKind = valueKind(subjectType, context.checker);
4588
4709
  const tagUnionExpression = taggedUnionTagRead(statement.expression, context);
4589
- const missingFlags = ts5.TypeFlags.Null | ts5.TypeFlags.Undefined;
4710
+ const missingFlags = ts6.TypeFlags.Null | ts6.TypeFlags.Undefined;
4590
4711
  const nullableOpaqueSubject = subjectKind === "nullable" && subjectType.isUnion() && subjectType.types.every((member) => (member.flags & missingFlags) !== 0 || valueKind(member, context.checker) === "opaque");
4591
4712
  if (tagUnionExpression == null && subjectKind !== "number" && subjectKind !== "opaque" && !nullableOpaqueSubject) {
4592
4713
  throw unsupported(statement.expression, { kind: "switchSubject", typeText: context.checker.typeToString(subjectType) });
@@ -4598,7 +4719,7 @@ function lowerSwitchStatement(statement, context) {
4598
4719
  const clauses = statement.caseBlock.clauses;
4599
4720
  for (let index = 0;index < clauses.length; index++) {
4600
4721
  const clause = clauses[index];
4601
- if (ts5.isDefaultClause(clause)) {
4722
+ if (ts6.isDefaultClause(clause)) {
4602
4723
  if (index !== clauses.length - 1 || pendingLabels.length > 0) {
4603
4724
  throw unsupported(clause, { kind: "switchDefaultNotLast" });
4604
4725
  }
@@ -4619,7 +4740,7 @@ function lowerSwitchStatement(statement, context) {
4619
4740
  const lowerBody = (group) => {
4620
4741
  const body = group.statements;
4621
4742
  const last = body[body.length - 1];
4622
- const endsWithBreak = last != null && ts5.isBreakStatement(last);
4743
+ const endsWithBreak = last != null && ts6.isBreakStatement(last);
4623
4744
  lowerStatements(endsWithBreak ? body.slice(0, -1) : body, context);
4624
4745
  if (context.currentBlock.terminator == null) {
4625
4746
  if (!endsWithBreak)
@@ -4638,7 +4759,7 @@ function lowerSwitchStatement(statement, context) {
4638
4759
  let condition;
4639
4760
  if (tagUnionExpression != null) {
4640
4761
  const unwrappedLabel = label;
4641
- if (!ts5.isStringLiteral(unwrappedLabel) && !ts5.isNoSubstitutionTemplateLiteral(unwrappedLabel)) {
4762
+ if (!ts6.isStringLiteral(unwrappedLabel) && !ts6.isNoSubstitutionTemplateLiteral(unwrappedLabel)) {
4642
4763
  throw unsupported(label, { kind: "switchLabel", typeText: context.checker.typeToString(context.checker.getTypeAtLocation(label)) });
4643
4764
  }
4644
4765
  condition = addInstruction(context, label, { kind: "tagCheck", union: subject, tagValue: unwrappedLabel.text, negated: false });
@@ -4680,7 +4801,7 @@ function lowerSwitchStatement(statement, context) {
4680
4801
  mergeAtContinuation(exits, bindingsBefore, statement, context);
4681
4802
  }
4682
4803
  function lowerForOfStatement(statement, context) {
4683
- if (!ts5.isVariableDeclarationList(statement.initializer) || statement.initializer.declarations.length !== 1 || !ts5.isIdentifier(statement.initializer.declarations[0].name)) {
4804
+ if (!ts6.isVariableDeclarationList(statement.initializer) || statement.initializer.declarations.length !== 1 || !ts6.isIdentifier(statement.initializer.declarations[0].name)) {
4684
4805
  throw unsupported(statement.initializer, { kind: "variableDeclarationShape" });
4685
4806
  }
4686
4807
  const elementName = statement.initializer.declarations[0].name;
@@ -4757,7 +4878,7 @@ function lowerForOfStatement(statement, context) {
4757
4878
  }
4758
4879
  function lowerForStatement(statement, context) {
4759
4880
  if (statement.initializer != null) {
4760
- if (ts5.isVariableDeclarationList(statement.initializer)) {
4881
+ if (ts6.isVariableDeclarationList(statement.initializer)) {
4761
4882
  lowerVariableDeclarationList(statement.initializer, context);
4762
4883
  } else {
4763
4884
  lowerStatementExpression(statement.initializer, context);
@@ -4866,13 +4987,13 @@ function lowerBranch(statement, block, bindings, context) {
4866
4987
  }
4867
4988
  function lowerVariableDeclarationList(declarations, context) {
4868
4989
  for (const declaration of declarations.declarations) {
4869
- if (ts5.isObjectBindingPattern(declaration.name) && declaration.initializer != null) {
4990
+ if (ts6.isObjectBindingPattern(declaration.name) && declaration.initializer != null) {
4870
4991
  const source = lowerExpression(declaration.initializer, context);
4871
4992
  for (const element of declaration.name.elements) {
4872
- if (!ts5.isIdentifier(element.name) || element.dotDotDotToken != null || element.initializer != null) {
4993
+ if (!ts6.isIdentifier(element.name) || element.dotDotDotToken != null || element.initializer != null) {
4873
4994
  throw unsupported(element, { kind: "variableDeclarationShape" });
4874
4995
  }
4875
- const property = element.propertyName == null ? element.name.text : ts5.isIdentifier(element.propertyName) ? element.propertyName.text : null;
4996
+ const property = element.propertyName == null ? element.name.text : ts6.isIdentifier(element.propertyName) ? element.propertyName.text : null;
4876
4997
  if (property == null)
4877
4998
  throw unsupported(element, { kind: "variableDeclarationShape" });
4878
4999
  const elementType = context.checker.getTypeAtLocation(element.name);
@@ -4889,7 +5010,7 @@ function lowerVariableDeclarationList(declarations, context) {
4889
5010
  }
4890
5011
  continue;
4891
5012
  }
4892
- if (!ts5.isIdentifier(declaration.name) || declaration.initializer == null) {
5013
+ if (!ts6.isIdentifier(declaration.name) || declaration.initializer == null) {
4893
5014
  throw unsupported(declaration, { kind: "variableDeclarationShape" });
4894
5015
  }
4895
5016
  const value2 = lowerExpression(declaration.initializer, context);
@@ -4908,12 +5029,12 @@ function lowerVariableDeclarationList(declarations, context) {
4908
5029
  function assignedSymbols(nodes, checker) {
4909
5030
  const symbols = new Set;
4910
5031
  const visit = (node) => {
4911
- if (ts5.isFunctionLike(node))
5032
+ if (ts6.isFunctionLike(node))
4912
5033
  return;
4913
5034
  const assignment = identifierAssignment(node);
4914
5035
  if (assignment != null)
4915
5036
  symbols.add(requiredSymbol(assignment.target, checker));
4916
- ts5.forEachChild(node, visit);
5037
+ ts6.forEachChild(node, visit);
4917
5038
  };
4918
5039
  for (const node of nodes)
4919
5040
  visit(node);
@@ -4932,45 +5053,46 @@ function scanModuleBindings(sourceFile, checker) {
4932
5053
  bindings.push({ name: name.text, category });
4933
5054
  };
4934
5055
  for (const statement of sourceFile.statements) {
4935
- if (ts6.isVariableStatement(statement)) {
4936
- if ((statement.declarationList.flags & (ts6.NodeFlags.Let | ts6.NodeFlags.Const)) === 0)
5056
+ if (ts7.isVariableStatement(statement)) {
5057
+ if ((statement.declarationList.flags & (ts7.NodeFlags.Let | ts7.NodeFlags.Const)) === 0)
4937
5058
  continue;
5059
+ const isConst = (statement.declarationList.flags & ts7.NodeFlags.Const) !== 0;
4938
5060
  for (const declarator of statement.declarationList.declarations) {
4939
- if (ts6.isIdentifier(declarator.name)) {
4940
- register(declarator.name, declaredCategory(declarator.name, checker));
5061
+ if (ts7.isIdentifier(declarator.name)) {
5062
+ register(declarator.name, isConst && declarator.initializer != null && directFunctionExpression(declarator.initializer) != null ? { kind: "function" } : declaredCategory(declarator.name, checker));
4941
5063
  continue;
4942
5064
  }
4943
- if (ts6.isObjectBindingPattern(declarator.name)) {
5065
+ if (ts7.isObjectBindingPattern(declarator.name)) {
4944
5066
  for (const element of declarator.name.elements) {
4945
- if (ts6.isIdentifier(element.name))
5067
+ if (ts7.isIdentifier(element.name))
4946
5068
  register(element.name, declaredCategory(element.name, checker));
4947
5069
  }
4948
5070
  }
4949
5071
  }
4950
5072
  continue;
4951
5073
  }
4952
- if (ts6.isImportDeclaration(statement)) {
5074
+ if (ts7.isImportDeclaration(statement)) {
4953
5075
  const clause = statement.importClause;
4954
5076
  if (clause == null || clause.isTypeOnly)
4955
5077
  continue;
4956
5078
  if (clause.name != null)
4957
5079
  register(clause.name, importedCategory(clause.name, checker));
4958
5080
  const named = clause.namedBindings;
4959
- if (named != null && ts6.isNamedImports(named)) {
5081
+ if (named != null && ts7.isNamedImports(named)) {
4960
5082
  for (const element of named.elements) {
4961
5083
  if (!element.isTypeOnly)
4962
5084
  register(element.name, importedCategory(element.name, checker));
4963
5085
  }
4964
5086
  }
4965
- if (named != null && ts6.isNamespaceImport(named))
5087
+ if (named != null && ts7.isNamespaceImport(named))
4966
5088
  register(named.name, { kind: "import" });
4967
5089
  }
4968
5090
  }
4969
5091
  const visit = (node, insideFunction) => {
4970
5092
  if (insideFunction)
4971
5093
  demoteModuleWritesInNode(node, checker, bindingsBySymbol, bindings);
4972
- const enteringFunction = insideFunction || ts6.isFunctionLike(node);
4973
- ts6.forEachChild(node, (child) => {
5094
+ const enteringFunction = insideFunction || ts7.isFunctionLike(node);
5095
+ ts7.forEachChild(node, (child) => {
4974
5096
  visit(child, enteringFunction);
4975
5097
  });
4976
5098
  };
@@ -4979,13 +5101,13 @@ function scanModuleBindings(sourceFile, checker) {
4979
5101
  }
4980
5102
  function importedCategory(name, checker) {
4981
5103
  const symbol = checker.getSymbolAtLocation(name);
4982
- if (symbol == null || (symbol.flags & ts6.SymbolFlags.Alias) === 0)
5104
+ if (symbol == null || (symbol.flags & ts7.SymbolFlags.Alias) === 0)
4983
5105
  return { kind: "import" };
4984
5106
  const target = checker.getAliasedSymbol(symbol);
4985
5107
  const declaration = target.valueDeclaration;
4986
- if (declaration == null || !ts6.isVariableDeclaration(declaration))
5108
+ if (declaration == null || !ts7.isVariableDeclaration(declaration))
4987
5109
  return { kind: "import" };
4988
- if ((ts6.getCombinedNodeFlags(declaration) & ts6.NodeFlags.Const) === 0)
5110
+ if ((ts7.getCombinedNodeFlags(declaration) & ts7.NodeFlags.Const) === 0)
4989
5111
  return { kind: "import" };
4990
5112
  if (declaration.getSourceFile().isDeclarationFile)
4991
5113
  return { kind: "import" };
@@ -5001,7 +5123,7 @@ function demoteModuleWritesInNode(node, checker, bindingsBySymbol, bindings, wri
5001
5123
  written[binding] = true;
5002
5124
  };
5003
5125
  const target = (expression) => {
5004
- if (ts6.isIdentifier(expression)) {
5126
+ if (ts7.isIdentifier(expression)) {
5005
5127
  const symbol = checker.getSymbolAtLocation(expression);
5006
5128
  const binding = symbol == null ? undefined : bindingsBySymbol.get(symbol);
5007
5129
  if (binding != null)
@@ -5009,34 +5131,34 @@ function demoteModuleWritesInNode(node, checker, bindingsBySymbol, bindings, wri
5009
5131
  return;
5010
5132
  }
5011
5133
  const visitPattern = (child) => {
5012
- if (ts6.isShorthandPropertyAssignment(child)) {
5134
+ if (ts7.isShorthandPropertyAssignment(child)) {
5013
5135
  const symbol = checker.getShorthandAssignmentValueSymbol(child);
5014
5136
  const binding = symbol == null ? undefined : bindingsBySymbol.get(symbol);
5015
5137
  if (binding != null)
5016
5138
  record(binding);
5017
- ts6.forEachChild(child, visitPattern);
5139
+ ts7.forEachChild(child, visitPattern);
5018
5140
  return;
5019
5141
  }
5020
- if (ts6.isIdentifier(child)) {
5142
+ if (ts7.isIdentifier(child)) {
5021
5143
  const symbol = checker.getSymbolAtLocation(child);
5022
5144
  const binding = symbol == null ? undefined : bindingsBySymbol.get(symbol);
5023
5145
  if (binding != null)
5024
5146
  record(binding);
5025
5147
  return;
5026
5148
  }
5027
- ts6.forEachChild(child, visitPattern);
5149
+ ts7.forEachChild(child, visitPattern);
5028
5150
  };
5029
- ts6.forEachChild(expression, visitPattern);
5151
+ ts7.forEachChild(expression, visitPattern);
5030
5152
  };
5031
- if (ts6.isBinaryExpression(node)) {
5153
+ if (ts7.isBinaryExpression(node)) {
5032
5154
  const kind = node.operatorToken.kind;
5033
- if (kind >= ts6.SyntaxKind.FirstAssignment && kind <= ts6.SyntaxKind.LastAssignment)
5155
+ if (kind >= ts7.SyntaxKind.FirstAssignment && kind <= ts7.SyntaxKind.LastAssignment)
5034
5156
  target(node.left);
5035
5157
  }
5036
- if ((ts6.isPrefixUnaryExpression(node) || ts6.isPostfixUnaryExpression(node)) && (node.operator === ts6.SyntaxKind.PlusPlusToken || node.operator === ts6.SyntaxKind.MinusMinusToken) && ts6.isExpression(node.operand)) {
5158
+ if ((ts7.isPrefixUnaryExpression(node) || ts7.isPostfixUnaryExpression(node)) && (node.operator === ts7.SyntaxKind.PlusPlusToken || node.operator === ts7.SyntaxKind.MinusMinusToken) && ts7.isExpression(node.operand)) {
5037
5159
  target(node.operand);
5038
5160
  }
5039
- if ((ts6.isForOfStatement(node) || ts6.isForInStatement(node)) && ts6.isExpression(node.initializer)) {
5161
+ if ((ts7.isForOfStatement(node) || ts7.isForInStatement(node)) && ts7.isExpression(node.initializer)) {
5040
5162
  target(node.initializer);
5041
5163
  }
5042
5164
  }
@@ -5049,8 +5171,8 @@ function lowerModuleInitializer(sourceFile, checker, functionsBySymbol, scan, si
5049
5171
  continue;
5050
5172
  const recovery = snapshotLowering(context);
5051
5173
  try {
5052
- assertAccepted(statement);
5053
- if (ts6.isVariableStatement(statement)) {
5174
+ assertAccepted(statement, true);
5175
+ if (ts7.isVariableStatement(statement)) {
5054
5176
  lowerTopLevelDeclarations(statement, context, scan);
5055
5177
  continue;
5056
5178
  }
@@ -5088,31 +5210,31 @@ function lowerModuleInitializer(sourceFile, checker, functionsBySymbol, scan, si
5088
5210
  };
5089
5211
  }
5090
5212
  function containsArrayLiteral(root) {
5091
- if (ts6.isArrayLiteralExpression(root))
5213
+ if (ts7.isArrayLiteralExpression(root))
5092
5214
  return true;
5093
5215
  let found = false;
5094
- ts6.forEachChild(root, (child) => {
5216
+ ts7.forEachChild(root, (child) => {
5095
5217
  if (!found && containsArrayLiteral(child))
5096
5218
  found = true;
5097
5219
  });
5098
5220
  return found;
5099
5221
  }
5100
5222
  function forEachImmediatelyEvaluatedChild(node, visit) {
5101
- if (ts6.isFunctionLike(node)) {
5102
- if (node.name != null && ts6.isComputedPropertyName(node.name)) {
5223
+ if (ts7.isFunctionLike(node)) {
5224
+ if (node.name != null && ts7.isComputedPropertyName(node.name)) {
5103
5225
  visit(node.name.expression);
5104
5226
  }
5105
5227
  return;
5106
5228
  }
5107
- ts6.forEachChild(node, visit);
5229
+ ts7.forEachChild(node, visit);
5108
5230
  }
5109
5231
  function lowerSupportedArgumentsOfSkippedTopLevelCall(statement, stop, context) {
5110
- if (!ts6.isExpressionStatement(statement))
5232
+ if (!ts7.isExpressionStatement(statement))
5111
5233
  return;
5112
5234
  let expression = statement.expression;
5113
- while (ts6.isParenthesizedExpression(expression))
5235
+ while (ts7.isParenthesizedExpression(expression))
5114
5236
  expression = expression.expression;
5115
- if (!ts6.isCallExpression(expression) || expression !== stop.node || expression.questionDotToken != null || !plainCallTarget(expression.expression))
5237
+ if (!ts7.isCallExpression(expression) || expression !== stop.node || expression.questionDotToken != null || !plainCallTarget(expression.expression))
5116
5238
  return;
5117
5239
  for (const argument of expression.arguments) {
5118
5240
  const recovery = snapshotLowering(context);
@@ -5127,23 +5249,23 @@ function lowerSupportedArgumentsOfSkippedTopLevelCall(statement, stop, context)
5127
5249
  }
5128
5250
  }
5129
5251
  function plainCallTarget(expression) {
5130
- if (ts6.isIdentifier(expression))
5252
+ if (ts7.isIdentifier(expression))
5131
5253
  return true;
5132
- if (ts6.isParenthesizedExpression(expression))
5254
+ if (ts7.isParenthesizedExpression(expression))
5133
5255
  return plainCallTarget(expression.expression);
5134
- return ts6.isPropertyAccessExpression(expression) && expression.questionDotToken == null && plainCallTarget(expression.expression);
5256
+ return ts7.isPropertyAccessExpression(expression) && expression.questionDotToken == null && plainCallTarget(expression.expression);
5135
5257
  }
5136
5258
  function lowerTopLevelDeclarations(statement, context, scan) {
5137
5259
  for (const declarator of statement.declarationList.declarations) {
5138
5260
  if (declarator.initializer == null)
5139
5261
  throw new LoweringStop(declarator, { kind: "variableDeclarationShape" });
5140
- if (ts6.isObjectBindingPattern(declarator.name)) {
5262
+ if (ts7.isObjectBindingPattern(declarator.name)) {
5141
5263
  const source = lowerExpression(declarator.initializer, context);
5142
5264
  for (const element of declarator.name.elements) {
5143
- if (!ts6.isIdentifier(element.name) || element.dotDotDotToken != null || element.initializer != null) {
5265
+ if (!ts7.isIdentifier(element.name) || element.dotDotDotToken != null || element.initializer != null) {
5144
5266
  throw new LoweringStop(element, { kind: "variableDeclarationShape" });
5145
5267
  }
5146
- const property = element.propertyName == null ? element.name.text : ts6.isIdentifier(element.propertyName) ? element.propertyName.text : null;
5268
+ const property = element.propertyName == null ? element.name.text : ts7.isIdentifier(element.propertyName) ? element.propertyName.text : null;
5147
5269
  if (property == null)
5148
5270
  throw new LoweringStop(element, { kind: "variableDeclarationShape" });
5149
5271
  const elementType = context.checker.getTypeAtLocation(element.name);
@@ -5159,27 +5281,32 @@ function lowerTopLevelDeclarations(statement, context, scan) {
5159
5281
  }
5160
5282
  continue;
5161
5283
  }
5162
- if (!ts6.isIdentifier(declarator.name)) {
5284
+ if (!ts7.isIdentifier(declarator.name)) {
5163
5285
  throw new LoweringStop(declarator, { kind: "variableDeclarationShape" });
5164
5286
  }
5165
5287
  const symbol = context.checker.getSymbolAtLocation(declarator.name);
5166
5288
  const binding = symbol == null ? undefined : scan.bindingsBySymbol.get(symbol);
5167
5289
  if (binding == null)
5168
5290
  throw new LoweringStop(declarator, { kind: "variableDeclarationShape" });
5291
+ if ((statement.declarationList.flags & ts7.NodeFlags.Const) !== 0 && directFunctionExpression(declarator.initializer) != null) {
5292
+ const marker = addInstruction(context, declarator.initializer, { kind: "opaqueConstant" });
5293
+ addInstruction(context, declarator, { kind: "moduleWrite", binding, value: marker });
5294
+ continue;
5295
+ }
5169
5296
  const value2 = lowerExpression(declarator.initializer, context);
5170
5297
  addInstruction(context, declarator, { kind: "moduleWrite", binding, value: value2 });
5171
5298
  }
5172
5299
  }
5173
5300
  function skippedAtTopLevel(statement) {
5174
- return ts6.isFunctionDeclaration(statement) && statement.name != null || ts6.isImportDeclaration(statement) || ts6.isTypeAliasDeclaration(statement) || ts6.isInterfaceDeclaration(statement) || ts6.isExportDeclaration(statement);
5301
+ return ts7.isFunctionDeclaration(statement) && statement.name != null || ts7.isImportDeclaration(statement) || ts7.isTypeAliasDeclaration(statement) || ts7.isInterfaceDeclaration(statement) || ts7.isExportDeclaration(statement);
5175
5302
  }
5176
5303
  function scanSkippedModuleEffects(root, checker, bindingsBySymbol, bindings) {
5177
5304
  const directWrites = [];
5178
5305
  let invokesUnknownCode = false;
5179
5306
  const visit = (node) => {
5180
5307
  demoteModuleWritesInNode(node, checker, bindingsBySymbol, bindings, directWrites);
5181
- invokesUnknownCode ||= ts6.isCallExpression(node) || ts6.isNewExpression(node) || ts6.isTaggedTemplateExpression(node) || ts6.isAwaitExpression(node) || ts6.isYieldExpression(node) || ts6.isForOfStatement(node) || ts6.isSpreadElement(node) || ts6.isArrayBindingPattern(node) || ts6.isVariableDeclarationList(node) && (node.flags & ts6.NodeFlags.Using) !== 0 || ts6.isBinaryExpression(node) && (node.operatorToken.kind === ts6.SyntaxKind.InstanceOfKeyword || node.operatorToken.kind === ts6.SyntaxKind.EqualsToken && containsArrayLiteral(node.left)) || ts6.isJsxElement(node) || ts6.isJsxSelfClosingElement(node) || ts6.isJsxFragment(node) || ts6.isClassDeclaration(node) || ts6.isClassExpression(node);
5182
- if (ts6.isVariableDeclaration(node) && ts6.isIdentifier(node.name)) {
5308
+ invokesUnknownCode ||= ts7.isCallExpression(node) || ts7.isNewExpression(node) || ts7.isTaggedTemplateExpression(node) || ts7.isAwaitExpression(node) || ts7.isYieldExpression(node) || ts7.isForOfStatement(node) || ts7.isSpreadElement(node) || ts7.isArrayBindingPattern(node) || ts7.isVariableDeclarationList(node) && (node.flags & ts7.NodeFlags.Using) !== 0 || ts7.isBinaryExpression(node) && (node.operatorToken.kind === ts7.SyntaxKind.InstanceOfKeyword || node.operatorToken.kind === ts7.SyntaxKind.EqualsToken && containsArrayLiteral(node.left)) || ts7.isJsxElement(node) || ts7.isJsxSelfClosingElement(node) || ts7.isJsxFragment(node) || ts7.isClassDeclaration(node) || ts7.isClassExpression(node);
5309
+ if (ts7.isVariableDeclaration(node) && ts7.isIdentifier(node.name)) {
5183
5310
  const symbol = checker.getSymbolAtLocation(node.name);
5184
5311
  const binding = symbol == null ? undefined : bindingsBySymbol.get(symbol);
5185
5312
  if (binding != null) {
@@ -5201,7 +5328,7 @@ function declaredRecordProperties(type, checker, seen) {
5201
5328
  return null;
5202
5329
  const properties = [];
5203
5330
  for (const property of checker.getPropertiesOfType(type)) {
5204
- const optional = (property.flags & ts6.SymbolFlags.Optional) !== 0;
5331
+ const optional = (property.flags & ts7.SymbolFlags.Optional) !== 0;
5205
5332
  const walked = declaredKind(checker.getTypeOfSymbol(property), checker, [...seen, type]);
5206
5333
  const opaqueLeaf = { kind: "opaque" };
5207
5334
  const propertyDeclared = declaredOnlyInDeclarationFiles(property) ? opaqueLeaf : walked ?? opaqueLeaf;
@@ -5307,14 +5434,14 @@ function declaredKindUncached(type, checker, seen) {
5307
5434
  }
5308
5435
  if (inner == null)
5309
5436
  return null;
5310
- const admitsNull = type.types.some((member) => (member.flags & ts6.TypeFlags.Null) !== 0);
5311
- const admitsUndefined = type.types.some((member) => (member.flags & ts6.TypeFlags.Undefined) !== 0);
5437
+ const admitsNull = type.types.some((member) => (member.flags & ts7.TypeFlags.Null) !== 0);
5438
+ const admitsUndefined = type.types.some((member) => (member.flags & ts7.TypeFlags.Undefined) !== 0);
5312
5439
  return { kind: "nullish", inner, sentinels: admitsNull && admitsUndefined ? "both" : admitsNull ? "null" : "undefined" };
5313
5440
  }
5314
5441
  case "opaque":
5315
5442
  return { kind: "opaque" };
5316
5443
  case "array": {
5317
- const element = checker.getIndexTypeOfType(type, ts6.IndexKind.Number);
5444
+ const element = checker.getIndexTypeOfType(type, ts7.IndexKind.Number);
5318
5445
  if (element == null)
5319
5446
  return null;
5320
5447
  const elementKind = declaredKind(element, checker, [...seen, type]);
@@ -5366,7 +5493,7 @@ function declaredKindUncached(type, checker, seen) {
5366
5493
  }
5367
5494
  function numericLiteralInterval(type) {
5368
5495
  const members = type.isUnion() ? type.types : [type];
5369
- if (!members.every((member) => (member.flags & ts6.TypeFlags.NumberLiteral) !== 0 && (member.flags & ts6.TypeFlags.EnumLiteral) === 0))
5496
+ if (!members.every((member) => (member.flags & ts7.TypeFlags.NumberLiteral) !== 0 && (member.flags & ts7.TypeFlags.EnumLiteral) === 0))
5370
5497
  return null;
5371
5498
  let lower = Infinity;
5372
5499
  let upper = -Infinity;
@@ -5419,7 +5546,7 @@ function joinScalarDeclaredKinds(members) {
5419
5546
  function tupleHasOptionalOrRestPositions(type, checker) {
5420
5547
  if (!checker.isTupleType(type))
5421
5548
  return false;
5422
- return type.target.elementFlags.some((flags) => (flags & ts6.ElementFlags.Required) === 0);
5549
+ return type.target.elementFlags.some((flags) => (flags & ts7.ElementFlags.Required) === 0);
5423
5550
  }
5424
5551
  function demote(bindings, binding) {
5425
5552
  const category = bindings[binding].category;
@@ -5433,6 +5560,7 @@ function demote(bindings, binding) {
5433
5560
  break;
5434
5561
  }
5435
5562
  case "kind":
5563
+ case "function":
5436
5564
  case "import":
5437
5565
  case "opaque":
5438
5566
  break;
@@ -5440,31 +5568,31 @@ function demote(bindings, binding) {
5440
5568
  }
5441
5569
 
5442
5570
  // src/lower/static-intrinsics.ts
5443
- import * as ts7 from "typescript";
5444
- function scanStaticAnnotations(sourceFile, declarations, checker) {
5445
- const ownerIndex = new Map(declarations.map((declaration, index) => [declaration, index]));
5446
- const callsByFunction = declarations.map(() => []);
5571
+ import * as ts8 from "typescript";
5572
+ function scanStaticAnnotations(sourceFile, functions, checker) {
5573
+ const ownerIndex = new Map(functions.map((unit, index) => [unit.declaration, index]));
5574
+ const callsByFunction = functions.map(() => []);
5447
5575
  const outsideTopLevelFunctions = [];
5448
5576
  const visit = (node) => {
5449
- if (ts7.isCallExpression(node) && isStaticIntent(node, checker)) {
5450
- const owner = ts7.findAncestor(node, ts7.isFunctionLike);
5451
- const index = owner != null && ts7.isFunctionDeclaration(owner) ? ownerIndex.get(owner) : undefined;
5577
+ if (ts8.isCallExpression(node) && isStaticIntent(node, checker)) {
5578
+ const owner = ts8.findAncestor(node, ts8.isFunctionLike);
5579
+ const index = owner == null ? undefined : ownerIndex.get(owner);
5452
5580
  if (index == null)
5453
5581
  outsideTopLevelFunctions.push(node);
5454
5582
  else
5455
5583
  callsByFunction[index].push(node);
5456
5584
  }
5457
- ts7.forEachChild(node, visit);
5585
+ ts8.forEachChild(node, visit);
5458
5586
  };
5459
5587
  visit(sourceFile);
5460
5588
  return {
5461
- functions: declarations.map((declaration, index) => {
5589
+ functions: functions.map((unit, index) => {
5462
5590
  const calls = callsByFunction[index];
5463
5591
  const callSet = new Set(calls);
5464
5592
  const leading = new Set;
5465
- if (declaration.body != null) {
5466
- for (const statement of declaration.body.statements) {
5467
- if (!ts7.isExpressionStatement(statement) || !ts7.isCallExpression(statement.expression) || !callSet.has(statement.expression))
5593
+ if (unit.declaration.body != null && ts8.isBlock(unit.declaration.body)) {
5594
+ for (const statement of unit.declaration.body.statements) {
5595
+ if (!ts8.isExpressionStatement(statement) || !ts8.isCallExpression(statement.expression) || !callSet.has(statement.expression))
5468
5596
  break;
5469
5597
  leading.add(statement.expression);
5470
5598
  }
@@ -5478,34 +5606,30 @@ function annotationForCall(call, role) {
5478
5606
  if (call.arguments.length !== 1) {
5479
5607
  return { kind: "invalid", call, role, node: call, problem: "argumentCount" };
5480
5608
  }
5481
- if (!ts7.isExpressionStatement(call.parent) || call.parent.expression !== call) {
5609
+ if (!ts8.isExpressionStatement(call.parent) || call.parent.expression !== call) {
5482
5610
  return { kind: "invalid", call, role, node: call, problem: "position" };
5483
5611
  }
5484
5612
  const callee = call.expression;
5485
- if (call.questionDotToken != null || ts7.isPropertyAccessExpression(callee) && callee.questionDotToken != null) {
5613
+ if (call.questionDotToken != null || ts8.isPropertyAccessExpression(callee) && callee.questionDotToken != null) {
5486
5614
  return { kind: "invalid", call, role, node: call, problem: "optionalCall" };
5487
5615
  }
5488
5616
  return { kind: "valid", call, role, condition: call.arguments[0] };
5489
5617
  }
5490
5618
  function isStaticIntent(call, checker) {
5491
- if (!ts7.isPropertyAccessExpression(call.expression))
5619
+ if (!ts8.isPropertyAccessExpression(call.expression))
5492
5620
  return false;
5493
5621
  const access = call.expression;
5494
- if (!ts7.isIdentifier(access.expression) || access.expression.text !== "console" || access.name.text !== "assert")
5622
+ if (!ts8.isIdentifier(access.expression) || access.expression.text !== "console" || access.name.text !== "assert")
5495
5623
  return false;
5496
5624
  const resolved = checker.getSymbolAtLocation(access.expression);
5497
- const globalConsole = checker.resolveName("console", undefined, ts7.SymbolFlags.Value, false);
5625
+ const globalConsole = checker.resolveName("console", undefined, ts8.SymbolFlags.Value, false);
5498
5626
  return resolved != null && resolved === globalConsole;
5499
5627
  }
5500
5628
 
5501
5629
  // src/lower/program.ts
5502
5630
  function lowerSource(checked, baseDirectory = process.cwd()) {
5503
5631
  const { sourceFile, checker } = checked;
5504
- const declarations = [];
5505
- for (const statement of sourceFile.statements) {
5506
- if (ts8.isFunctionDeclaration(statement) && statement.name != null)
5507
- declarations.push(statement);
5508
- }
5632
+ const declarations = topLevelFunctionUnits(sourceFile);
5509
5633
  const staticScan = scanStaticAnnotations(sourceFile, declarations, checker);
5510
5634
  const recordStaticAnnotationIssues = (sites2) => staticScan.outsideTopLevelFunctions.map((call) => {
5511
5635
  sites2.push(nodeSpan(sourceFile, call));
@@ -5519,9 +5643,9 @@ function lowerSource(checked, baseDirectory = process.cwd()) {
5519
5643
  baseDirectory,
5520
5644
  lineStarts: [...sourceFile.getLineStarts()],
5521
5645
  sites: sites2,
5522
- functions: declarations.map((declaration, index) => ({
5646
+ functions: declarations.map((unit, index) => ({
5523
5647
  kind: "unsupported",
5524
- name: declaration.name.text,
5648
+ name: unit.name.text,
5525
5649
  hasStaticAnnotations: staticScan.functions[index].length > 0,
5526
5650
  site: 0,
5527
5651
  reason
@@ -5548,19 +5672,31 @@ function lowerSource(checked, baseDirectory = process.cwd()) {
5548
5672
  return rejectFile(nodeSpan(sourceFile, evalNode), { kind: "evalInFile" });
5549
5673
  }
5550
5674
  const functionsBySymbol = new Map;
5675
+ const scan = scanModuleBindings(sourceFile, checker);
5676
+ const topLevelFunctions = [];
5551
5677
  for (let index = 0;index < declarations.length; index++) {
5552
- const declaration = declarations[index];
5553
- const symbol = checker.getSymbolAtLocation(declaration.name);
5678
+ const unit = declarations[index];
5679
+ const symbol = checker.getSymbolAtLocation(unit.name);
5554
5680
  if (symbol == null)
5555
- throw new Error(`Function declaration ${declaration.name.text} has no TypeScript symbol`);
5556
- functionsBySymbol.set(symbol, { id: index, declaration });
5681
+ throw new Error(`Function ${unit.name.text} has no TypeScript symbol`);
5682
+ const binding = scan.bindingsBySymbol.get(symbol);
5683
+ if (unit.initializer != null && binding == null) {
5684
+ throw new Error(`Const-bound function ${unit.name.text} has no module binding`);
5685
+ }
5686
+ const fn = {
5687
+ ...unit,
5688
+ id: index,
5689
+ binding: unit.initializer == null ? null : binding,
5690
+ signature: callableSignature(unit, checker)
5691
+ };
5692
+ topLevelFunctions.push(fn);
5693
+ functionsBySymbol.set(symbol, fn);
5557
5694
  }
5558
- const scan = scanModuleBindings(sourceFile, checker);
5559
5695
  const sites = [];
5560
5696
  const staticAnnotationIssues = recordStaticAnnotationIssues(sites);
5561
5697
  const functions = [];
5562
- for (let index = 0;index < declarations.length; index++) {
5563
- const declaration = declarations[index];
5698
+ for (let index = 0;index < topLevelFunctions.length; index++) {
5699
+ const declaration = topLevelFunctions[index];
5564
5700
  const staticAnnotations = staticScan.functions[index];
5565
5701
  try {
5566
5702
  functions.push(lowerFunction(declaration, staticAnnotations, sourceFile, checker, functionsBySymbol, scan, sites));
@@ -5590,7 +5726,8 @@ function lowerSource(checked, baseDirectory = process.cwd()) {
5590
5726
  initializerSkips: skips
5591
5727
  };
5592
5728
  }
5593
- function lowerFunction(declaration, staticAnnotations, sourceFile, checker, functionsBySymbol, scan, sites) {
5729
+ function lowerFunction(unit, staticAnnotations, sourceFile, checker, functionsBySymbol, scan, sites) {
5730
+ const { declaration } = unit;
5594
5731
  for (const annotation of staticAnnotations) {
5595
5732
  if (annotation.kind === "invalid") {
5596
5733
  throw unsupported(annotation.node, { kind: "staticAssertionForm", problem: annotation.problem });
@@ -5598,24 +5735,31 @@ function lowerFunction(declaration, staticAnnotations, sourceFile, checker, func
5598
5735
  }
5599
5736
  if (declaration.body == null)
5600
5737
  throw unsupported(declaration, { kind: "functionWithoutBody" });
5601
- if (declaration.asteriskToken != null || declaration.modifiers?.some((modifier) => modifier.kind === ts8.SyntaxKind.AsyncKeyword) === true) {
5738
+ if (!ts9.isArrowFunction(declaration) && declaration.asteriskToken != null || declaration.modifiers?.some((modifier) => modifier.kind === ts9.SyntaxKind.AsyncKeyword) === true) {
5602
5739
  throw unsupported(declaration, { kind: "asyncOrGeneratorFunction" });
5603
5740
  }
5604
5741
  assertAccepted(declaration);
5605
- const signature = checker.getSignatureFromDeclaration(declaration);
5606
- if (signature != null && checker.getTypePredicateOfSignature(signature) != null) {
5742
+ const signature = unit.signature;
5743
+ if (signature == null) {
5744
+ throw unsupported(unit.name, {
5745
+ kind: unit.initializer == null ? "functionWithoutSignature" : "constFunctionSignature"
5746
+ });
5747
+ }
5748
+ if (checker.getTypePredicateOfSignature(signature) != null) {
5607
5749
  throw unsupported(declaration, { kind: "typePredicate" });
5608
5750
  }
5609
- const returnType = functionReturnType(declaration, checker);
5610
- const returnsVoid = (returnType.flags & (ts8.TypeFlags.Void | ts8.TypeFlags.Undefined | ts8.TypeFlags.Never)) !== 0;
5751
+ const returnType = checker.getReturnTypeOfSignature(signature);
5752
+ const returnsVoid = (returnType.flags & (ts9.TypeFlags.Void | ts9.TypeFlags.Undefined | ts9.TypeFlags.Never)) !== 0;
5611
5753
  if (!returnsVoid && valueKind(returnType, checker) == null) {
5612
5754
  throw unsupported(declaration.type ?? declaration, { kind: "valueType", typeText: checker.typeToString(returnType) });
5613
5755
  }
5614
- const context = createFunctionContext(sourceFile, checker, functionsBySymbol, scan.bindingsBySymbol, sites, staticAnnotations);
5756
+ const context = createFunctionContext(sourceFile, checker, functionsBySymbol, scan.bindingsBySymbol, sites, staticAnnotations, returnsVoid);
5615
5757
  const entry = context.currentBlock;
5616
- for (const parameter of declaration.parameters) {
5617
- if (ts8.isObjectBindingPattern(parameter.name)) {
5618
- const type2 = lowerParameterType(parameter, checker);
5758
+ for (let parameterIndex = 0;parameterIndex < declaration.parameters.length; parameterIndex++) {
5759
+ const parameter = declaration.parameters[parameterIndex];
5760
+ const parameterType = unit.initializer == null ? checker.getTypeAtLocation(parameter) : checker.getTypeOfSymbolAtLocation(signature.parameters[parameterIndex], signature.declaration);
5761
+ if (ts9.isObjectBindingPattern(parameter.name)) {
5762
+ const type2 = lowerParameterType(parameter, parameterType, checker);
5619
5763
  const patternName = parameter.name.getText(sourceFile).replace(/\s+/g, " ");
5620
5764
  if (parameter.initializer != null) {
5621
5765
  throw unsupported(parameter, { kind: "parameterDefaultValue", name: patternName });
@@ -5624,10 +5768,10 @@ function lowerFunction(declaration, staticAnnotations, sourceFile, checker, func
5624
5768
  const bindings = [];
5625
5769
  context.parameters.push({ value: value3, name: patternName, type: type2, site: addSite(context, parameter), bindings });
5626
5770
  for (const element of parameter.name.elements) {
5627
- if (!ts8.isIdentifier(element.name) || element.dotDotDotToken != null || element.initializer != null) {
5771
+ if (!ts9.isIdentifier(element.name) || element.dotDotDotToken != null || element.initializer != null) {
5628
5772
  throw unsupported(element, { kind: "destructuredParameter" });
5629
5773
  }
5630
- const property = element.propertyName == null ? element.name.text : ts8.isIdentifier(element.propertyName) ? element.propertyName.text : null;
5774
+ const property = element.propertyName == null ? element.name.text : ts9.isIdentifier(element.propertyName) ? element.propertyName.text : null;
5631
5775
  if (property == null)
5632
5776
  throw unsupported(element, { kind: "destructuredParameter" });
5633
5777
  bindings.push({ property, local: element.name.text });
@@ -5643,12 +5787,12 @@ function lowerFunction(declaration, staticAnnotations, sourceFile, checker, func
5643
5787
  }
5644
5788
  continue;
5645
5789
  }
5646
- if (!ts8.isIdentifier(parameter.name))
5790
+ if (!ts9.isIdentifier(parameter.name))
5647
5791
  throw unsupported(parameter.name, { kind: "destructuredParameter" });
5648
5792
  if (parameter.dotDotDotToken != null) {
5649
5793
  throw unsupported(parameter, { kind: "parameterType", typeText: `...${checker.typeToString(checker.getTypeAtLocation(parameter))}`, optionalOrRestTuple: false });
5650
5794
  }
5651
- let type = lowerParameterType(parameter, checker);
5795
+ let type = lowerParameterType(parameter, parameterType, checker);
5652
5796
  if (parameter.initializer != null) {
5653
5797
  const default_ = parameterDefaultLiteral(parameter.initializer, checker);
5654
5798
  if (default_ == null || !parameterDefaultFits(default_, type)) {
@@ -5661,7 +5805,16 @@ function lowerFunction(declaration, staticAnnotations, sourceFile, checker, func
5661
5805
  context.parameters.push({ value: value2, name: parameter.name.text, type, site: addSite(context, parameter), bindings: null });
5662
5806
  }
5663
5807
  lowerFiniteInputRequirements(context);
5664
- lowerStatements(declaration.body.statements, context);
5808
+ if (ts9.isBlock(declaration.body)) {
5809
+ lowerStatements(declaration.body.statements, context);
5810
+ } else {
5811
+ const value2 = lowerExpression(declaration.body, context);
5812
+ terminate(context.currentBlock, {
5813
+ kind: "return",
5814
+ value: returnsVoid ? null : value2,
5815
+ site: addSite(context, declaration.body)
5816
+ });
5817
+ }
5665
5818
  if (context.currentBlock.terminator == null) {
5666
5819
  if (!returnsVoid) {
5667
5820
  terminate(context.currentBlock, { kind: "stop", site: addSite(context, declaration), reason: { kind: "missingReturn" } });
@@ -5671,12 +5824,12 @@ function lowerFunction(declaration, staticAnnotations, sourceFile, checker, func
5671
5824
  }
5672
5825
  return {
5673
5826
  kind: "lowered",
5674
- name: declaration.name.text,
5827
+ name: unit.name.text,
5675
5828
  assertions: context.assertions,
5676
5829
  parameters: context.parameters,
5677
5830
  returnPropertyNames: declaredRecordReturnNames(returnType, checker),
5678
5831
  entry: 0,
5679
- blocks: sealBlocks(context.blocks, declaration.name.text)
5832
+ blocks: sealBlocks(context.blocks, unit.name.text)
5680
5833
  };
5681
5834
  }
5682
5835
  function lowerFiniteInputRequirements(context) {
@@ -5710,7 +5863,7 @@ function declaredRecordReturnNames(returnType, checker) {
5710
5863
  if (kind === "object")
5711
5864
  return checker.getPropertiesOfType(returnType).map((property) => property.name);
5712
5865
  if (kind === "nullable" && returnType.isUnion()) {
5713
- const missing = ts8.TypeFlags.Null | ts8.TypeFlags.Undefined;
5866
+ const missing = ts9.TypeFlags.Null | ts9.TypeFlags.Undefined;
5714
5867
  const members = returnType.types.filter((member) => (member.flags & missing) === 0);
5715
5868
  if (members.length > 0 && members.every((member) => valueKind(member, checker) === "object")) {
5716
5869
  const names = new Set;
@@ -5723,8 +5876,7 @@ function declaredRecordReturnNames(returnType, checker) {
5723
5876
  }
5724
5877
  return null;
5725
5878
  }
5726
- function lowerParameterType(parameter, checker) {
5727
- const type = checker.getTypeAtLocation(parameter);
5879
+ function lowerParameterType(parameter, type, checker) {
5728
5880
  const declared = declaredKind(type, checker, []);
5729
5881
  if (declared == null) {
5730
5882
  throw unsupported(parameter, {
@@ -5735,12 +5887,6 @@ function lowerParameterType(parameter, checker) {
5735
5887
  }
5736
5888
  return declared;
5737
5889
  }
5738
- function functionReturnType(declaration, checker) {
5739
- const signature = checker.getSignatureFromDeclaration(declaration);
5740
- if (signature == null)
5741
- throw unsupported(declaration, { kind: "functionWithoutSignature" });
5742
- return checker.getReturnTypeOfSignature(signature);
5743
- }
5744
5890
 
5745
5891
  // src/analyze.ts
5746
5892
  function analyzeCheckedSource(checked, baseDirectory) {
@@ -6505,6 +6651,7 @@ function formatStop(stop, program, analysis) {
6505
6651
  return `reads ${binding.name}, whose value the analysis does not track (read at ${formatSite(program, stop.site)})`;
6506
6652
  case "value":
6507
6653
  case "kind":
6654
+ case "function":
6508
6655
  return `reads ${binding.name} before it is initialized (read at ${formatSite(program, stop.site)})`;
6509
6656
  }
6510
6657
  }
@@ -6563,6 +6710,8 @@ function formatUnsupportedReason(reason) {
6563
6710
  return "node without a TypeScript symbol";
6564
6711
  case "functionWithoutSignature":
6565
6712
  return "function without a TypeScript signature";
6713
+ case "constFunctionSignature":
6714
+ return "a top-level const function whose declared call shape differs from its implementation";
6566
6715
  case "functionWithoutBody":
6567
6716
  return "function declarations need bodies";
6568
6717
  case "destructuredParameter":
@@ -6788,7 +6937,7 @@ function formatNumber(value2) {
6788
6937
  }
6789
6938
 
6790
6939
  // src/typescript/diagnostics.ts
6791
- import * as ts9 from "typescript";
6940
+ import * as ts10 from "typescript";
6792
6941
 
6793
6942
  class TypeScriptDiagnosticsError extends Error {
6794
6943
  diagnostics;
@@ -6805,10 +6954,10 @@ class TypeScriptDiagnosticsError extends Error {
6805
6954
  function formatTypeScriptDiagnostics(diagnostics, options, currentDirectory) {
6806
6955
  const host = {
6807
6956
  getCurrentDirectory: () => currentDirectory,
6808
- getCanonicalFileName: ts9.sys.useCaseSensitiveFileNames ? (file) => file : (file) => file.toLowerCase(),
6809
- getNewLine: () => ts9.sys.newLine
6957
+ getCanonicalFileName: ts10.sys.useCaseSensitiveFileNames ? (file) => file : (file) => file.toLowerCase(),
6958
+ getNewLine: () => ts10.sys.newLine
6810
6959
  };
6811
- return usePrettyOutput(options["pretty"]) ? ts9.formatDiagnosticsWithColorAndContext(diagnostics, host) : ts9.formatDiagnostics(diagnostics, host);
6960
+ return usePrettyOutput(options["pretty"]) ? ts10.formatDiagnosticsWithColorAndContext(diagnostics, host) : ts10.formatDiagnostics(diagnostics, host);
6812
6961
  }
6813
6962
  function usePrettyOutput(configured) {
6814
6963
  if (typeof configured === "boolean")
@@ -6819,7 +6968,7 @@ function usePrettyOutput(configured) {
6819
6968
  const forceColor = process.env["FORCE_COLOR"];
6820
6969
  if (forceColor != null && forceColor !== "")
6821
6970
  return true;
6822
- return ts9.sys.writeOutputIsTTY?.() === true;
6971
+ return ts10.sys.writeOutputIsTTY?.() === true;
6823
6972
  }
6824
6973
  function color(code, text) {
6825
6974
  return `\x1B[${code}m${text}\x1B[0m`;
@@ -7046,7 +7195,7 @@ function createFileAudit({ program, analysis }) {
7046
7195
  };
7047
7196
  }
7048
7197
  function formatAuditCoverage(coverage) {
7049
- const parts = coverage.functions === 0 ? ["no named function declarations"] : [`${coverage.analyzed}/${coverage.functions} functions fully analyzed`];
7198
+ const parts = coverage.functions === 0 ? ["no named top-level functions"] : [`${coverage.analyzed}/${coverage.functions} functions fully analyzed`];
7050
7199
  if (coverage.partial > 0)
7051
7200
  parts.push(`${coverage.partial} partially supported`);
7052
7201
  if (coverage.unsupported > 0)
@@ -7141,6 +7290,7 @@ function guidesForUnsupportedReason(reason) {
7141
7290
  case "unknownIdentifier":
7142
7291
  case "missingSymbol":
7143
7292
  case "functionWithoutSignature":
7293
+ case "constFunctionSignature":
7144
7294
  case "functionWithoutBody":
7145
7295
  case "destructuredParameter":
7146
7296
  case "parameterType":
@@ -7211,12 +7361,12 @@ function isCallerInput(expression) {
7211
7361
 
7212
7362
  // src/typescript/check.ts
7213
7363
  import { resolve } from "node:path";
7214
- import * as ts10 from "typescript";
7364
+ import * as ts11 from "typescript";
7215
7365
  var fallbackOptions = {
7216
- target: ts10.ScriptTarget.ESNext,
7217
- module: ts10.ModuleKind.ESNext,
7218
- moduleResolution: ts10.ModuleResolutionKind.Bundler,
7219
- moduleDetection: ts10.ModuleDetectionKind.Force,
7366
+ target: ts11.ScriptTarget.ESNext,
7367
+ module: ts11.ModuleKind.ESNext,
7368
+ moduleResolution: ts11.ModuleResolutionKind.Bundler,
7369
+ moduleDetection: ts11.ModuleDetectionKind.Force,
7220
7370
  strict: true,
7221
7371
  noUncheckedIndexedAccess: true,
7222
7372
  exactOptionalPropertyTypes: true,
@@ -7226,11 +7376,11 @@ var fallbackOptions = {
7226
7376
  };
7227
7377
  function checkFile(file) {
7228
7378
  const absoluteFile = resolve(file);
7229
- const program = ts10.createProgram([absoluteFile], fallbackOptions);
7379
+ const program = ts11.createProgram([absoluteFile], fallbackOptions);
7230
7380
  return checkedSource(program, absoluteFile, fallbackOptions);
7231
7381
  }
7232
7382
  function checkedSource(program, file, options) {
7233
- const diagnostics = ts10.getPreEmitDiagnostics(program);
7383
+ const diagnostics = ts11.getPreEmitDiagnostics(program);
7234
7384
  if (diagnostics.length > 0) {
7235
7385
  throw new TypeScriptDiagnosticsError(diagnostics, options, process.cwd());
7236
7386
  }
@@ -7242,9 +7392,9 @@ function checkedSource(program, file, options) {
7242
7392
 
7243
7393
  // src/typescript/project.ts
7244
7394
  import { dirname, isAbsolute, relative as relative2, resolve as resolve2, sep } from "node:path";
7245
- import * as ts11 from "typescript";
7395
+ import * as ts12 from "typescript";
7246
7396
  function findTypeScriptConfig(searchFrom) {
7247
- return ts11.findConfigFile(resolve2(searchFrom), (file) => ts11.sys.fileExists(file), "tsconfig.json") ?? null;
7397
+ return ts12.findConfigFile(resolve2(searchFrom), (file) => ts12.sys.fileExists(file), "tsconfig.json") ?? null;
7248
7398
  }
7249
7399
  function loadTypeScriptProjectGraph(configPath) {
7250
7400
  const loaded = [];
@@ -7261,8 +7411,8 @@ function loadTypeScriptProjectGraph(configPath) {
7261
7411
  const parsed = parseConfig(absoluteConfigPath);
7262
7412
  requireStrictNullChecks(parsed.options, absoluteConfigPath);
7263
7413
  for (const reference of parsed.projectReferences ?? [])
7264
- load(ts11.resolveProjectReferencePath(reference));
7265
- const program = ts11.createProgram({
7414
+ load(ts12.resolveProjectReferencePath(reference));
7415
+ const program = ts12.createProgram({
7266
7416
  rootNames: parsed.fileNames,
7267
7417
  options: parsed.options,
7268
7418
  configFileParsingDiagnostics: parsed.errors,
@@ -7297,8 +7447,8 @@ function projectSources(projects) {
7297
7447
  return [...sources.values()].sort((left, right) => left.sourceFile.fileName.localeCompare(right.sourceFile.fileName));
7298
7448
  }
7299
7449
  function parseConfig(configPath) {
7300
- const parsed = ts11.getParsedCommandLineOfConfigFile(configPath, undefined, {
7301
- ...ts11.sys,
7450
+ const parsed = ts12.getParsedCommandLineOfConfigFile(configPath, undefined, {
7451
+ ...ts12.sys,
7302
7452
  onUnRecoverableConfigFileDiagnostic: (diagnostic) => {
7303
7453
  throw new TypeScriptDiagnosticsError([diagnostic], {}, dirname(configPath));
7304
7454
  }
@@ -7359,7 +7509,7 @@ function analyzeProject(searchFrom) {
7359
7509
  const projects = loadTypeScriptProjectGraph(configPath);
7360
7510
  const rootProject = projects.at(-1);
7361
7511
  const sources = projectSources(projects);
7362
- const diagnostics = uniqueDiagnostics(projects.flatMap((project) => ts12.getPreEmitDiagnostics(project.program)));
7512
+ const diagnostics = uniqueDiagnostics(projects.flatMap((project) => ts13.getPreEmitDiagnostics(project.program)));
7363
7513
  requireNoTypeScriptErrors(diagnostics, rootProject.parsed.options);
7364
7514
  const files = [];
7365
7515
  let analyzed = 0;
@@ -7483,7 +7633,7 @@ function collectLintFindings({ program, analysis }) {
7483
7633
  collectStops(analysis.initializer);
7484
7634
  collectAssertions(analysis.initializer);
7485
7635
  for (const issue of program.staticAnnotationIssues) {
7486
- addError(issue.site, "console-assert", "console.assert is only supported inside a named top-level function declaration");
7636
+ addError(issue.site, "console-assert", "console.assert is only supported inside a named top-level function");
7487
7637
  }
7488
7638
  for (const fn of analysis.functions) {
7489
7639
  collectStops(fn);
@@ -7580,7 +7730,7 @@ function formatLintPrefix(finding, rule, pretty) {
7580
7730
  return formatDiagnosticPrefix(finding, lintLevel(finding), rule, pretty);
7581
7731
  }
7582
7732
  function formatCoverage(coverage) {
7583
- return `coverage: ${coverage.analyzed}/${coverage.functions} named top-level function declarations fully analyzed; ${coverage.partial} partially supported; ${coverage.unsupported} unsupported.`;
7733
+ return `coverage: ${coverage.analyzed}/${coverage.functions} named top-level functions fully analyzed; ${coverage.partial} partially supported; ${coverage.unsupported} unsupported.`;
7584
7734
  }
7585
7735
  function analyzeTargetFile(file) {
7586
7736
  const absoluteFile = resolve3(file);
@@ -7596,7 +7746,7 @@ function analyzeTargetFile(file) {
7596
7746
  if (source == null) {
7597
7747
  throw new Error(`File is not part of the project resolved from ${configPath}: ${absoluteFile}`);
7598
7748
  }
7599
- const diagnostics = ts12.getPreEmitDiagnostics(source.project.program, source.sourceFile);
7749
+ const diagnostics = ts13.getPreEmitDiagnostics(source.project.program, source.sourceFile);
7600
7750
  requireNoTypeScriptErrors(diagnostics, rootProject.parsed.options);
7601
7751
  return {
7602
7752
  detailed: analyzeProjectSource(source, process.cwd()),
@@ -7605,7 +7755,7 @@ function analyzeTargetFile(file) {
7605
7755
  }
7606
7756
  function canonicalFilePath(file) {
7607
7757
  const real = realpathSync.native(file);
7608
- return ts12.sys.useCaseSensitiveFileNames ? real : real.toLowerCase();
7758
+ return ts13.sys.useCaseSensitiveFileNames ? real : real.toLowerCase();
7609
7759
  }
7610
7760
  function analyzeFileAlone(absoluteFile) {
7611
7761
  return {
@@ -7622,7 +7772,7 @@ function analyzeProjectSource(source, reportBaseDirectory) {
7622
7772
  function uniqueDiagnostics(diagnostics) {
7623
7773
  const seen = new Set;
7624
7774
  return diagnostics.filter((diagnostic) => {
7625
- const message = ts12.flattenDiagnosticMessageText(diagnostic.messageText, `
7775
+ const message = ts13.flattenDiagnosticMessageText(diagnostic.messageText, `
7626
7776
  `);
7627
7777
  const key = `${diagnostic.file?.fileName ?? ""}:${diagnostic.start ?? ""}:${diagnostic.length ?? ""}:${diagnostic.code}:${message}`;
7628
7778
  if (seen.has(key))
@@ -7643,7 +7793,7 @@ function requireNoTypeScriptErrors(diagnostics, options) {
7643
7793
  printTypeScriptDiagnostics(diagnostics, options, process.cwd());
7644
7794
  }
7645
7795
  function hasErrorDiagnostics(diagnostics) {
7646
- return diagnostics.some((diagnostic) => diagnostic.category === ts12.DiagnosticCategory.Error);
7796
+ return diagnostics.some((diagnostic) => diagnostic.category === ts13.DiagnosticCategory.Error);
7647
7797
  }
7648
7798
 
7649
7799
  // fr.ts