@chenglou/freerange 0.0.2 → 0.0.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/CHANGELOG.md +14 -0
- package/README.md +232 -66
- package/dist/fr.js +627 -480
- package/package.json +1 -1
- package/src/audit.ts +2 -3
- package/src/domain/value-identity.ts +56 -0
- package/src/engine/analyze.ts +19 -12
- package/src/engine/state.ts +23 -11
- package/src/engine/transfer.ts +78 -29
- package/src/ir/instructions.ts +7 -1
- package/src/ir/program.ts +7 -3
- package/src/lower/accept.ts +6 -2
- package/src/lower/context.ts +14 -5
- package/src/lower/expression.ts +106 -88
- package/src/lower/function-unit.ts +64 -0
- package/src/lower/module.ts +28 -9
- package/src/lower/numeric-intersection.ts +15 -0
- package/src/lower/platform.ts +28 -12
- package/src/lower/program.ts +66 -41
- package/src/lower/statements.ts +5 -18
- package/src/lower/static-intrinsics.ts +9 -8
- package/src/project.ts +3 -3
- package/src/report/index.ts +2 -2
- package/src/requirements/infer.ts +76 -25
- package/src/typescript/check.ts +2 -2
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
|
|
6
|
+
import * as ts14 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/
|
|
779
|
-
function
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
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
|
-
|
|
787
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
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
|
-
|
|
983
|
+
context.identityByValue[value] = identity;
|
|
984
|
+
return identity;
|
|
913
985
|
}
|
|
914
986
|
function sameRuntimeValue(left, right, context) {
|
|
915
|
-
return left === right ||
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1272
|
-
const
|
|
1273
|
-
const assumedValid = hasIndexFact(state.valueFacts, "validIndex",
|
|
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",
|
|
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, {
|
|
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
|
|
1522
|
-
const
|
|
1523
|
-
const evaluation = context.evaluateFunction(instruction.function, arguments_, argumentExpressions, state.shared, context.callStack, state.valueFacts,
|
|
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) => !
|
|
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
|
|
1587
|
-
const marker = `v:${namespace}`;
|
|
1672
|
+
function valueFactUsesOwner(fact, owner) {
|
|
1588
1673
|
if (fact.kind === "nonzero")
|
|
1589
|
-
return fact.value
|
|
1590
|
-
return fact.index
|
|
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:
|
|
1951
|
-
array:
|
|
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:
|
|
1961
|
-
array:
|
|
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
|
|
2017
|
-
if (state.valueFacts.some((fact) => fact.kind === "validIndex" && fact.index
|
|
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,
|
|
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, {
|
|
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, []
|
|
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.
|
|
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,
|
|
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,
|
|
2704
|
+
return runEvaluation(calleeFn, callee, values, expressions, calleeState, program, stack, { valueFacts, parameterIdentities, identityOwner }).evaluation;
|
|
2617
2705
|
}
|
|
2618
2706
|
};
|
|
2619
2707
|
let queueIndex = 0;
|
|
@@ -2887,17 +2975,18 @@ function reachableFrom(successors, start) {
|
|
|
2887
2975
|
}
|
|
2888
2976
|
|
|
2889
2977
|
// src/lower/program.ts
|
|
2890
|
-
import * as
|
|
2978
|
+
import * as ts10 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, program, functionsBySymbol, moduleBindingsBySymbol, sites, staticAnnotations = [], returnsVoid = true) {
|
|
2897
2985
|
const entry = { loopHeader: null, parameters: [], instructions: [], terminator: null };
|
|
2898
2986
|
return {
|
|
2899
2987
|
sourceFile,
|
|
2900
2988
|
checker,
|
|
2989
|
+
program,
|
|
2901
2990
|
functionsBySymbol,
|
|
2902
2991
|
moduleBindingsBySymbol,
|
|
2903
2992
|
staticAnnotations: new Map(staticAnnotations.map((annotation) => [annotation.call, annotation])),
|
|
@@ -2908,6 +2997,7 @@ function createFunctionContext(sourceFile, checker, functionsBySymbol, moduleBin
|
|
|
2908
2997
|
bindings: new Map,
|
|
2909
2998
|
parameters: [],
|
|
2910
2999
|
assertions: [],
|
|
3000
|
+
returnsVoid,
|
|
2911
3001
|
loops: []
|
|
2912
3002
|
};
|
|
2913
3003
|
}
|
|
@@ -3025,8 +3115,10 @@ function unsupported(node, reason) {
|
|
|
3025
3115
|
}
|
|
3026
3116
|
|
|
3027
3117
|
// src/lower/accept.ts
|
|
3028
|
-
function assertAccepted(root) {
|
|
3118
|
+
function assertAccepted(root, deferFunctionBodies = false) {
|
|
3029
3119
|
const visit = (node) => {
|
|
3120
|
+
if (deferFunctionBodies && node !== root && ts.isFunctionLike(node))
|
|
3121
|
+
return;
|
|
3030
3122
|
if (ts.isTypeNode(node))
|
|
3031
3123
|
return;
|
|
3032
3124
|
if (ts.isJsxElement(node) || ts.isJsxSelfClosingElement(node) || ts.isJsxFragment(node)) {
|
|
@@ -3101,11 +3193,29 @@ function evalMention(sourceFile) {
|
|
|
3101
3193
|
}
|
|
3102
3194
|
|
|
3103
3195
|
// src/lower/expression.ts
|
|
3104
|
-
import * as
|
|
3196
|
+
import * as ts5 from "typescript";
|
|
3105
3197
|
|
|
3106
|
-
// src/lower/
|
|
3198
|
+
// src/lower/numeric-intersection.ts
|
|
3107
3199
|
import * as ts2 from "typescript";
|
|
3200
|
+
function numberConstituent(type) {
|
|
3201
|
+
if ((type.flags & ts2.TypeFlags.NumberLike) !== 0)
|
|
3202
|
+
return type;
|
|
3203
|
+
if (!type.isIntersection())
|
|
3204
|
+
return null;
|
|
3205
|
+
const numberTypes = type.types.filter((member) => (member.flags & ts2.TypeFlags.NumberLike) !== 0);
|
|
3206
|
+
if (numberTypes.length !== 1)
|
|
3207
|
+
return null;
|
|
3208
|
+
for (const member of type.types) {
|
|
3209
|
+
if (member !== numberTypes[0] && (member.flags & ts2.TypeFlags.Object) === 0)
|
|
3210
|
+
return null;
|
|
3211
|
+
}
|
|
3212
|
+
return numberTypes[0];
|
|
3213
|
+
}
|
|
3214
|
+
|
|
3215
|
+
// src/lower/platform.ts
|
|
3216
|
+
import * as ts3 from "typescript";
|
|
3108
3217
|
var anyFinite = { lower: -Number.MAX_VALUE, upper: Number.MAX_VALUE };
|
|
3218
|
+
var maximumDateTime = 8640000000000000;
|
|
3109
3219
|
var catalog = [
|
|
3110
3220
|
{ path: ["document", "documentElement", "clientWidth"], call: false, fact: { lower: 0, upper: Number.MAX_VALUE, integer: true } },
|
|
3111
3221
|
{ path: ["document", "documentElement", "clientHeight"], call: false, fact: { lower: 0, upper: Number.MAX_VALUE, integer: true } },
|
|
@@ -3116,25 +3226,32 @@ var catalog = [
|
|
|
3116
3226
|
{ path: ["document", "body", "scrollTop"], call: false, fact: { ...anyFinite, integer: false } },
|
|
3117
3227
|
{ path: ["document", "body", "scrollLeft"], call: false, fact: { ...anyFinite, integer: false } },
|
|
3118
3228
|
{ path: ["performance", "now"], call: true, fact: { lower: 0, upper: Number.MAX_VALUE, integer: false } },
|
|
3119
|
-
{ path: ["Date", "now"], call: true, fact: { lower:
|
|
3229
|
+
{ path: ["Date", "now"], call: true, fact: { lower: -maximumDateTime, upper: maximumDateTime, integer: true } },
|
|
3230
|
+
{ path: ["Math", "random"], call: true, fact: { lower: 0, upper: 0.9999999999999999, integer: false } }
|
|
3120
3231
|
];
|
|
3121
|
-
function platformFact(expression, call, checker) {
|
|
3232
|
+
function platformFact(expression, call, checker, program) {
|
|
3122
3233
|
const parts = [];
|
|
3123
3234
|
let current = expression;
|
|
3124
|
-
while (
|
|
3235
|
+
while (ts3.isPropertyAccessExpression(current)) {
|
|
3125
3236
|
parts.unshift(current.name.text);
|
|
3126
3237
|
current = current.expression;
|
|
3127
3238
|
}
|
|
3128
|
-
if (!
|
|
3239
|
+
if (!ts3.isIdentifier(current))
|
|
3129
3240
|
return null;
|
|
3130
3241
|
parts.unshift(current.text);
|
|
3131
3242
|
const entry = catalog.find((candidate) => candidate.call === call && candidate.path.length === parts.length && candidate.path.every((segment, index) => segment === parts[index]));
|
|
3132
3243
|
if (entry == null)
|
|
3133
3244
|
return null;
|
|
3134
|
-
if (!
|
|
3245
|
+
if (!hasDefaultLibraryDeclaration(checker.getSymbolAtLocation(current), program))
|
|
3135
3246
|
return null;
|
|
3136
3247
|
return entry.fact;
|
|
3137
3248
|
}
|
|
3249
|
+
function hasDefaultLibraryDeclaration(symbol, program) {
|
|
3250
|
+
const declarations = symbol?.declarations;
|
|
3251
|
+
if (declarations == null || declarations.length === 0)
|
|
3252
|
+
return false;
|
|
3253
|
+
return declarations.some((declaration) => program.isSourceFileDefaultLibrary(declaration.getSourceFile()));
|
|
3254
|
+
}
|
|
3138
3255
|
function declaredOnlyInDeclarationFiles(symbol) {
|
|
3139
3256
|
const declarations = symbol?.declarations;
|
|
3140
3257
|
if (declarations == null || declarations.length === 0)
|
|
@@ -3143,16 +3260,16 @@ function declaredOnlyInDeclarationFiles(symbol) {
|
|
|
3143
3260
|
}
|
|
3144
3261
|
|
|
3145
3262
|
// src/lower/literals.ts
|
|
3146
|
-
import * as
|
|
3263
|
+
import * as ts4 from "typescript";
|
|
3147
3264
|
function numericLiteralValue(expression) {
|
|
3148
3265
|
const current = unwrapLiteral(expression);
|
|
3149
|
-
if (
|
|
3266
|
+
if (ts4.isNumericLiteral(current))
|
|
3150
3267
|
return Number(current.text);
|
|
3151
|
-
if (
|
|
3268
|
+
if (ts4.isPrefixUnaryExpression(current) && (current.operator === ts4.SyntaxKind.PlusToken || current.operator === ts4.SyntaxKind.MinusToken)) {
|
|
3152
3269
|
const operand = unwrapLiteral(current.operand);
|
|
3153
|
-
if (!
|
|
3270
|
+
if (!ts4.isNumericLiteral(operand))
|
|
3154
3271
|
return null;
|
|
3155
|
-
return Number(operand.text) * (current.operator ===
|
|
3272
|
+
return Number(operand.text) * (current.operator === ts4.SyntaxKind.MinusToken ? -1 : 1);
|
|
3156
3273
|
}
|
|
3157
3274
|
return null;
|
|
3158
3275
|
}
|
|
@@ -3161,13 +3278,13 @@ function parameterDefaultLiteral(initializer, checker) {
|
|
|
3161
3278
|
const number = numericLiteralValue(current);
|
|
3162
3279
|
if (number != null)
|
|
3163
3280
|
return Number.isFinite(number) ? { kind: "number", value: number } : null;
|
|
3164
|
-
if (current.kind ===
|
|
3165
|
-
return { kind: "boolean", value: current.kind ===
|
|
3281
|
+
if (current.kind === ts4.SyntaxKind.TrueKeyword || current.kind === ts4.SyntaxKind.FalseKeyword) {
|
|
3282
|
+
return { kind: "boolean", value: current.kind === ts4.SyntaxKind.TrueKeyword };
|
|
3166
3283
|
}
|
|
3167
|
-
if (
|
|
3284
|
+
if (ts4.isStringLiteral(current) || ts4.isNoSubstitutionTemplateLiteral(current)) {
|
|
3168
3285
|
return { kind: "opaque", content: current.text };
|
|
3169
3286
|
}
|
|
3170
|
-
if (current.kind ===
|
|
3287
|
+
if (current.kind === ts4.SyntaxKind.NullKeyword)
|
|
3171
3288
|
return { kind: "nullish", sentinel: "null" };
|
|
3172
3289
|
if (isUndefinedGlobal(current, checker)) {
|
|
3173
3290
|
return { kind: "nullish", sentinel: "undefined" };
|
|
@@ -3175,10 +3292,10 @@ function parameterDefaultLiteral(initializer, checker) {
|
|
|
3175
3292
|
return null;
|
|
3176
3293
|
}
|
|
3177
3294
|
function isUndefinedGlobal(expression, checker) {
|
|
3178
|
-
if (!
|
|
3295
|
+
if (!ts4.isIdentifier(expression) || expression.text !== "undefined")
|
|
3179
3296
|
return false;
|
|
3180
3297
|
const symbol = checker.getSymbolAtLocation(expression);
|
|
3181
|
-
const global = checker.resolveName("undefined", undefined,
|
|
3298
|
+
const global = checker.resolveName("undefined", undefined, ts4.SymbolFlags.Value, false);
|
|
3182
3299
|
return symbol != null && symbol === global;
|
|
3183
3300
|
}
|
|
3184
3301
|
function parameterDefaultFits(default_, declared) {
|
|
@@ -3207,7 +3324,7 @@ function parameterDefaultFits(default_, declared) {
|
|
|
3207
3324
|
}
|
|
3208
3325
|
function unwrapLiteral(expression) {
|
|
3209
3326
|
let current = expression;
|
|
3210
|
-
while (
|
|
3327
|
+
while (ts4.isParenthesizedExpression(current) || ts4.isAsExpression(current) || ts4.isTypeAssertionExpression(current))
|
|
3211
3328
|
current = current.expression;
|
|
3212
3329
|
return current;
|
|
3213
3330
|
}
|
|
@@ -3257,7 +3374,7 @@ function lowerStatementExpression(expression, context) {
|
|
|
3257
3374
|
return;
|
|
3258
3375
|
}
|
|
3259
3376
|
case "compound": {
|
|
3260
|
-
if (assignment.operator === "add" && (context.checker.getTypeAtLocation(current).flags &
|
|
3377
|
+
if (assignment.operator === "add" && (context.checker.getTypeAtLocation(current).flags & ts5.TypeFlags.StringLike) !== 0) {
|
|
3261
3378
|
lowerExpression(assignment.node.right, context);
|
|
3262
3379
|
const concatenated = addInstruction(context, current, { kind: "opaqueConstant" });
|
|
3263
3380
|
assignIdentifier(symbol, assignment.target, concatenated, current, context);
|
|
@@ -3276,7 +3393,7 @@ function lowerStatementExpression(expression, context) {
|
|
|
3276
3393
|
const one = addInstruction(context, current, { kind: "constant", value: 1 });
|
|
3277
3394
|
const value2 = addInstruction(context, current, {
|
|
3278
3395
|
kind: "binary",
|
|
3279
|
-
operator: assignment.node.operator ===
|
|
3396
|
+
operator: assignment.node.operator === ts5.SyntaxKind.PlusPlusToken ? "add" : "subtract",
|
|
3280
3397
|
left: previous,
|
|
3281
3398
|
right: one
|
|
3282
3399
|
});
|
|
@@ -3289,43 +3406,43 @@ function lowerStatementExpression(expression, context) {
|
|
|
3289
3406
|
}
|
|
3290
3407
|
function lowerExpression(expression, context) {
|
|
3291
3408
|
const current = unwrap(expression, context.checker);
|
|
3292
|
-
if (
|
|
3409
|
+
if (ts5.isNumericLiteral(current)) {
|
|
3293
3410
|
return addInstruction(context, current, { kind: "constant", value: Number(current.text) });
|
|
3294
3411
|
}
|
|
3295
|
-
if (current.kind ===
|
|
3296
|
-
return addInstruction(context, current, { kind: "booleanConstant", value: current.kind ===
|
|
3412
|
+
if (current.kind === ts5.SyntaxKind.TrueKeyword || current.kind === ts5.SyntaxKind.FalseKeyword) {
|
|
3413
|
+
return addInstruction(context, current, { kind: "booleanConstant", value: current.kind === ts5.SyntaxKind.TrueKeyword });
|
|
3297
3414
|
}
|
|
3298
|
-
if (
|
|
3415
|
+
if (ts5.isPrefixUnaryExpression(current) && current.operator === ts5.SyntaxKind.PlusToken) {
|
|
3299
3416
|
const positive = unwrap(current.operand, context.checker);
|
|
3300
|
-
if (
|
|
3417
|
+
if (ts5.isNumericLiteral(positive)) {
|
|
3301
3418
|
return addInstruction(context, current, { kind: "constant", value: Number(positive.text) });
|
|
3302
3419
|
}
|
|
3303
|
-
throw unsupported(current, { kind: "expressionForm", syntax:
|
|
3420
|
+
throw unsupported(current, { kind: "expressionForm", syntax: ts5.SyntaxKind[current.kind] });
|
|
3304
3421
|
}
|
|
3305
|
-
if (
|
|
3422
|
+
if (ts5.isPrefixUnaryExpression(current) && current.operator === ts5.SyntaxKind.MinusToken) {
|
|
3306
3423
|
const negated = unwrap(current.operand, context.checker);
|
|
3307
|
-
if (
|
|
3424
|
+
if (ts5.isNumericLiteral(negated)) {
|
|
3308
3425
|
return addInstruction(context, current, { kind: "constant", value: -Number(negated.text) });
|
|
3309
3426
|
}
|
|
3310
|
-
if (
|
|
3427
|
+
if (isStandardGlobal(negated, "Infinity", context)) {
|
|
3311
3428
|
return addInstruction(context, current, { kind: "constant", value: Number.NEGATIVE_INFINITY });
|
|
3312
3429
|
}
|
|
3313
3430
|
const zero = addInstruction(context, current, { kind: "constant", value: 0 });
|
|
3314
3431
|
const value2 = lowerExpression(current.operand, context);
|
|
3315
3432
|
return addInstruction(context, current, { kind: "binary", operator: "subtract", left: zero, right: value2 });
|
|
3316
3433
|
}
|
|
3317
|
-
if (
|
|
3434
|
+
if (ts5.isPrefixUnaryExpression(current) && current.operator === ts5.SyntaxKind.ExclamationToken) {
|
|
3318
3435
|
requireBooleanCondition(current.operand, context.checker);
|
|
3319
3436
|
const value2 = lowerExpression(current.operand, context);
|
|
3320
3437
|
return addInstruction(context, current, { kind: "not", value: value2 });
|
|
3321
3438
|
}
|
|
3322
|
-
if (
|
|
3439
|
+
if (ts5.isConditionalExpression(current)) {
|
|
3323
3440
|
return lowerConditionalExpression(current, context);
|
|
3324
3441
|
}
|
|
3325
|
-
if (
|
|
3442
|
+
if (ts5.isIdentifier(current)) {
|
|
3326
3443
|
return identifierValue(requiredSymbol(current, context.checker), current, context);
|
|
3327
3444
|
}
|
|
3328
|
-
if (
|
|
3445
|
+
if (ts5.isArrayLiteralExpression(current)) {
|
|
3329
3446
|
const literalType = context.checker.getTypeAtLocation(current);
|
|
3330
3447
|
const literalKind = valueKind(literalType, context.checker);
|
|
3331
3448
|
if (literalKind !== "array" && literalKind !== "tuple" && current.elements.length > 0) {
|
|
@@ -3333,23 +3450,23 @@ function lowerExpression(expression, context) {
|
|
|
3333
3450
|
}
|
|
3334
3451
|
const elements = [];
|
|
3335
3452
|
for (const element of current.elements) {
|
|
3336
|
-
if (
|
|
3337
|
-
throw unsupported(element, { kind: "expressionForm", syntax:
|
|
3453
|
+
if (ts5.isSpreadElement(element) || ts5.isOmittedExpression(element)) {
|
|
3454
|
+
throw unsupported(element, { kind: "expressionForm", syntax: ts5.SyntaxKind[element.kind] });
|
|
3338
3455
|
}
|
|
3339
3456
|
elements.push(lowerExpression(element, context));
|
|
3340
3457
|
}
|
|
3341
3458
|
return addInstruction(context, current, { kind: "arrayLiteral", elements, form: literalKind === "tuple" ? "tuple" : "array" });
|
|
3342
3459
|
}
|
|
3343
|
-
if (
|
|
3460
|
+
if (ts5.isNonNullExpression(current) && ts5.isElementAccessExpression(current.expression)) {
|
|
3344
3461
|
return lowerElementAccess(current.expression, true, context);
|
|
3345
3462
|
}
|
|
3346
|
-
if (
|
|
3463
|
+
if (ts5.isElementAccessExpression(current)) {
|
|
3347
3464
|
return lowerElementAccess(current, false, context);
|
|
3348
3465
|
}
|
|
3349
|
-
if (
|
|
3466
|
+
if (ts5.isObjectLiteralExpression(current)) {
|
|
3350
3467
|
const properties = new Map;
|
|
3351
3468
|
for (const property of current.properties) {
|
|
3352
|
-
if (
|
|
3469
|
+
if (ts5.isShorthandPropertyAssignment(property)) {
|
|
3353
3470
|
const symbol = context.checker.getShorthandAssignmentValueSymbol(property);
|
|
3354
3471
|
if (symbol == null)
|
|
3355
3472
|
throw unsupported(property, { kind: "missingSymbol" });
|
|
@@ -3359,21 +3476,21 @@ function lowerExpression(expression, context) {
|
|
|
3359
3476
|
});
|
|
3360
3477
|
continue;
|
|
3361
3478
|
}
|
|
3362
|
-
if (
|
|
3479
|
+
if (ts5.isPropertyAssignment(property)) {
|
|
3363
3480
|
const name = propertyName(property.name);
|
|
3364
3481
|
if (name === "__proto__")
|
|
3365
3482
|
throw unsupported(property, { kind: "protoProperty" });
|
|
3366
3483
|
properties.set(name, { name, value: lowerExpression(property.initializer, context) });
|
|
3367
3484
|
continue;
|
|
3368
3485
|
}
|
|
3369
|
-
if (
|
|
3486
|
+
if (ts5.isSpreadAssignment(property))
|
|
3370
3487
|
throw unsupported(property, { kind: "objectSpread" });
|
|
3371
3488
|
throw unsupported(property, { kind: "objectPropertyForm" });
|
|
3372
3489
|
}
|
|
3373
3490
|
const contextual = context.checker.getContextualType(current);
|
|
3374
3491
|
const fillOptionalsFrom = (recordType) => {
|
|
3375
3492
|
for (const member of context.checker.getPropertiesOfType(recordType)) {
|
|
3376
|
-
if ((member.flags &
|
|
3493
|
+
if ((member.flags & ts5.SymbolFlags.Optional) === 0 || properties.has(member.name))
|
|
3377
3494
|
continue;
|
|
3378
3495
|
const absent = addInstruction(context, current, { kind: "nullishConstant", sentinel: "undefined" });
|
|
3379
3496
|
properties.set(member.name, { name: member.name, value: absent });
|
|
@@ -3406,25 +3523,25 @@ function lowerExpression(expression, context) {
|
|
|
3406
3523
|
if (identifierAssignment(current) != null) {
|
|
3407
3524
|
throw unsupported(current, { kind: "assignmentInValuePosition" });
|
|
3408
3525
|
}
|
|
3409
|
-
if (
|
|
3526
|
+
if (ts5.isBinaryExpression(current) && (current.operatorToken.kind === ts5.SyntaxKind.AmpersandAmpersandToken || current.operatorToken.kind === ts5.SyntaxKind.BarBarToken)) {
|
|
3410
3527
|
return lowerLogicalExpression(current, context);
|
|
3411
3528
|
}
|
|
3412
|
-
if (current.kind ===
|
|
3529
|
+
if (current.kind === ts5.SyntaxKind.NullKeyword) {
|
|
3413
3530
|
return addInstruction(context, current, { kind: "nullishConstant", sentinel: "null" });
|
|
3414
3531
|
}
|
|
3415
|
-
if (
|
|
3532
|
+
if (ts5.isStringLiteral(current) || ts5.isNoSubstitutionTemplateLiteral(current)) {
|
|
3416
3533
|
return addInstruction(context, current, { kind: "opaqueConstant", content: current.text });
|
|
3417
3534
|
}
|
|
3418
|
-
if (
|
|
3535
|
+
if (ts5.isAsExpression(current) || ts5.isTypeAssertionExpression(current)) {
|
|
3419
3536
|
lowerExpression(current.expression, context);
|
|
3420
3537
|
return addInstruction(context, current, { kind: "opaqueConstant" });
|
|
3421
3538
|
}
|
|
3422
|
-
if (
|
|
3539
|
+
if (ts5.isTemplateExpression(current)) {
|
|
3423
3540
|
for (const span of current.templateSpans)
|
|
3424
3541
|
lowerExpression(span.expression, context);
|
|
3425
3542
|
return addInstruction(context, current, { kind: "opaqueConstant" });
|
|
3426
3543
|
}
|
|
3427
|
-
if (
|
|
3544
|
+
if (ts5.isBinaryExpression(current) && current.operatorToken.kind === ts5.SyntaxKind.QuestionQuestionToken) {
|
|
3428
3545
|
const resultType = context.checker.getTypeAtLocation(current);
|
|
3429
3546
|
if (valueKind(resultType, context.checker) == null) {
|
|
3430
3547
|
throw unsupported(current, { kind: "valueType", typeText: context.checker.typeToString(resultType) });
|
|
@@ -3433,11 +3550,11 @@ function lowerExpression(expression, context) {
|
|
|
3433
3550
|
const notMissing = addInstruction(context, current, { kind: "nullishCheck", value: left, sentinel: "nullish", negated: true });
|
|
3434
3551
|
return lowerValueBranch(current, notMissing, () => left, () => lowerExpression(current.right, context), context);
|
|
3435
3552
|
}
|
|
3436
|
-
if (
|
|
3553
|
+
if (ts5.isBinaryExpression(current)) {
|
|
3437
3554
|
const missingCheck = missingSentinelCheck(current, context);
|
|
3438
3555
|
if (missingCheck != null)
|
|
3439
3556
|
return missingCheck;
|
|
3440
|
-
if (current.operatorToken.kind ===
|
|
3557
|
+
if (current.operatorToken.kind === ts5.SyntaxKind.InstanceOfKeyword && ts5.isIdentifier(current.right) && declaredOnlyInDeclarationFiles(context.checker.getSymbolAtLocation(current.right))) {
|
|
3441
3558
|
lowerExpression(current.left, context);
|
|
3442
3559
|
return addInstruction(context, current, { kind: "unknownBoolean" });
|
|
3443
3560
|
}
|
|
@@ -3447,7 +3564,7 @@ function lowerExpression(expression, context) {
|
|
|
3447
3564
|
const opaqueComparison = opaqueEqualityCheck(current, context);
|
|
3448
3565
|
if (opaqueComparison != null)
|
|
3449
3566
|
return opaqueComparison;
|
|
3450
|
-
if (current.operatorToken.kind ===
|
|
3567
|
+
if (current.operatorToken.kind === ts5.SyntaxKind.PlusToken && (context.checker.getTypeAtLocation(current).flags & ts5.TypeFlags.StringLike) !== 0) {
|
|
3451
3568
|
lowerExpression(current.left, context);
|
|
3452
3569
|
lowerExpression(current.right, context);
|
|
3453
3570
|
return addInstruction(context, current, { kind: "opaqueConstant" });
|
|
@@ -3468,13 +3585,13 @@ function lowerExpression(expression, context) {
|
|
|
3468
3585
|
const right = lowerExpression(current.right, context);
|
|
3469
3586
|
return arithmetic != null ? addInstruction(context, current, { kind: "binary", operator: arithmetic, left, right }) : addInstruction(context, current, { kind: "compare", operator: comparison, left, right });
|
|
3470
3587
|
}
|
|
3471
|
-
if (
|
|
3588
|
+
if (ts5.isCallExpression(current)) {
|
|
3472
3589
|
const staticAnnotation = context.staticAnnotations.get(current);
|
|
3473
3590
|
if (staticAnnotation != null)
|
|
3474
3591
|
return lowerStaticAnnotation(staticAnnotation, context);
|
|
3475
|
-
if (
|
|
3592
|
+
if (ts5.isIdentifier(current.expression)) {
|
|
3476
3593
|
const globalName = current.expression.text;
|
|
3477
|
-
if ((globalName === "parseFloat" || globalName === "parseInt" || globalName === "Number") &&
|
|
3594
|
+
if ((globalName === "parseFloat" || globalName === "parseInt" || globalName === "Number") && isStandardGlobal(current.expression, globalName, context)) {
|
|
3478
3595
|
for (const argument of current.arguments)
|
|
3479
3596
|
lowerExpression(argument, context);
|
|
3480
3597
|
return addInstruction(context, current, { kind: "parsedNumber", integer: globalName === "parseInt" });
|
|
@@ -3499,7 +3616,8 @@ function lowerExpression(expression, context) {
|
|
|
3499
3616
|
arguments_.push(lowerParameterDefault(default_2, parameter.initializer, context));
|
|
3500
3617
|
continue;
|
|
3501
3618
|
}
|
|
3502
|
-
|
|
3619
|
+
const callableParameter = callee.signature?.declaration?.parameters[index];
|
|
3620
|
+
if (callableParameter != null && ts5.isParameter(callableParameter) && callableParameter.questionToken != null) {
|
|
3503
3621
|
arguments_.push(addInstruction(context, parameter, { kind: "nullishConstant", sentinel: "undefined" }));
|
|
3504
3622
|
continue;
|
|
3505
3623
|
}
|
|
@@ -3519,15 +3637,20 @@ function lowerExpression(expression, context) {
|
|
|
3519
3637
|
});
|
|
3520
3638
|
arguments_.push(lowerValueBranch(argument, supplied, () => value2, () => lowerParameterDefault(default_, parameter.initializer, context), context));
|
|
3521
3639
|
}
|
|
3522
|
-
return addInstruction(context, current, {
|
|
3640
|
+
return addInstruction(context, current, {
|
|
3641
|
+
kind: "call",
|
|
3642
|
+
function: callee.id,
|
|
3643
|
+
arguments: arguments_,
|
|
3644
|
+
binding: callee.binding
|
|
3645
|
+
});
|
|
3523
3646
|
}
|
|
3524
|
-
if (
|
|
3525
|
-
const platformCall = current.arguments.length === 0 ? platformFact(current.expression, true, context.checker) : null;
|
|
3647
|
+
if (ts5.isPropertyAccessExpression(current.expression)) {
|
|
3648
|
+
const platformCall = current.arguments.length === 0 ? platformFact(current.expression, true, context.checker, context.program) : null;
|
|
3526
3649
|
if (platformCall != null) {
|
|
3527
3650
|
return addInstruction(context, current, { kind: "platformValue", ...platformCall });
|
|
3528
3651
|
}
|
|
3529
3652
|
const method = current.expression.name.text;
|
|
3530
|
-
const standardMath =
|
|
3653
|
+
const standardMath = isStandardGlobal(current.expression.expression, "Math", context);
|
|
3531
3654
|
if (standardMath && method === "floor" && current.arguments.length === 1) {
|
|
3532
3655
|
requireNumberType(current.arguments[0], context.checker);
|
|
3533
3656
|
const value2 = lowerExpression(current.arguments[0], context);
|
|
@@ -3549,7 +3672,7 @@ function lowerExpression(expression, context) {
|
|
|
3549
3672
|
const values = current.arguments.map((argument) => lowerExpression(argument, context));
|
|
3550
3673
|
return addInstruction(context, current, { kind: method === "min" ? "minimum" : "maximum", values });
|
|
3551
3674
|
}
|
|
3552
|
-
const standardNumber =
|
|
3675
|
+
const standardNumber = isStandardGlobal(current.expression.expression, "Number", context);
|
|
3553
3676
|
if (standardNumber && (method === "parseFloat" || method === "parseInt") && current.arguments.length >= 1) {
|
|
3554
3677
|
for (const argument of current.arguments)
|
|
3555
3678
|
lowerExpression(argument, context);
|
|
@@ -3564,7 +3687,7 @@ function lowerExpression(expression, context) {
|
|
|
3564
3687
|
value: value2
|
|
3565
3688
|
});
|
|
3566
3689
|
}
|
|
3567
|
-
const arrayMethod =
|
|
3690
|
+
const arrayMethod = ts5.isPropertyAccessExpression(current.expression) && context.checker.isArrayType(context.checker.getTypeAtLocation(current.expression.expression)) ? current.expression.name.text === "reduce" ? "reduce" : "other" : null;
|
|
3568
3691
|
throw unsupported(current, {
|
|
3569
3692
|
kind: "call",
|
|
3570
3693
|
callee: calleeDisplayName(current.expression, context.sourceFile),
|
|
@@ -3572,8 +3695,8 @@ function lowerExpression(expression, context) {
|
|
|
3572
3695
|
});
|
|
3573
3696
|
}
|
|
3574
3697
|
}
|
|
3575
|
-
if (
|
|
3576
|
-
const platform = platformFact(current, false, context.checker);
|
|
3698
|
+
if (ts5.isPropertyAccessExpression(current)) {
|
|
3699
|
+
const platform = platformFact(current, false, context.checker, context.program);
|
|
3577
3700
|
if (platform != null) {
|
|
3578
3701
|
return addInstruction(context, current, { kind: "platformValue", ...platform });
|
|
3579
3702
|
}
|
|
@@ -3591,12 +3714,12 @@ function lowerExpression(expression, context) {
|
|
|
3591
3714
|
const array = lowerExpression(current.expression, context);
|
|
3592
3715
|
return addInstruction(context, current, { kind: "arrayLength", array });
|
|
3593
3716
|
}
|
|
3594
|
-
if (receiverKind === "opaque" && current.name.text === "length" && (objectType.flags &
|
|
3717
|
+
if (receiverKind === "opaque" && current.name.text === "length" && (objectType.flags & ts5.TypeFlags.StringLike) !== 0) {
|
|
3595
3718
|
const value2 = lowerExpression(current.expression, context);
|
|
3596
3719
|
return addInstruction(context, current, { kind: "stringLength", value: value2 });
|
|
3597
3720
|
}
|
|
3598
|
-
const receiverSymbol =
|
|
3599
|
-
if (receiverSymbol != null && (receiverSymbol.flags & (
|
|
3721
|
+
const receiverSymbol = ts5.isIdentifier(current.expression) ? context.checker.getSymbolAtLocation(current.expression) : undefined;
|
|
3722
|
+
if (receiverSymbol != null && (receiverSymbol.flags & (ts5.SymbolFlags.RegularEnum | ts5.SymbolFlags.ConstEnum)) !== 0) {
|
|
3600
3723
|
throw unsupported(current, { kind: "enumMemberRead" });
|
|
3601
3724
|
}
|
|
3602
3725
|
if (receiverKind !== "object" && receiverKind !== "taggedUnion") {
|
|
@@ -3606,7 +3729,7 @@ function lowerExpression(expression, context) {
|
|
|
3606
3729
|
const object = lowerExpression(current.expression, context);
|
|
3607
3730
|
return addInstruction(context, current, { kind: "property", object, property: current.name.text });
|
|
3608
3731
|
}
|
|
3609
|
-
throw unsupported(current, { kind: "expressionForm", syntax:
|
|
3732
|
+
throw unsupported(current, { kind: "expressionForm", syntax: ts5.SyntaxKind[current.kind] });
|
|
3610
3733
|
}
|
|
3611
3734
|
function lowerStaticAnnotation(annotation, context) {
|
|
3612
3735
|
if (annotation.kind === "invalid") {
|
|
@@ -3623,15 +3746,15 @@ function lowerStaticAnnotation(annotation, context) {
|
|
|
3623
3746
|
if (requirement == null) {
|
|
3624
3747
|
throw unsupported(condition, {
|
|
3625
3748
|
kind: "staticAssertionForm",
|
|
3626
|
-
problem: staticAssertionProblem(condition, context
|
|
3749
|
+
problem: staticAssertionProblem(condition, context, "callerRequirement")
|
|
3627
3750
|
});
|
|
3628
3751
|
}
|
|
3629
3752
|
value2 = lowerWrittenRequirement(requirement, condition, context);
|
|
3630
3753
|
} else {
|
|
3631
|
-
if (!supportedWrittenAssertion(condition, context
|
|
3754
|
+
if (!supportedWrittenAssertion(condition, context)) {
|
|
3632
3755
|
throw unsupported(condition, {
|
|
3633
3756
|
kind: "staticAssertionForm",
|
|
3634
|
-
problem: staticAssertionProblem(condition, context
|
|
3757
|
+
problem: staticAssertionProblem(condition, context, "directCheck")
|
|
3635
3758
|
});
|
|
3636
3759
|
}
|
|
3637
3760
|
value2 = lowerExpression(condition, context);
|
|
@@ -3653,7 +3776,7 @@ function lowerStaticAnnotation(annotation, context) {
|
|
|
3653
3776
|
}
|
|
3654
3777
|
function writtenRequirement(condition, context) {
|
|
3655
3778
|
const current = unwrapParentheses(condition);
|
|
3656
|
-
if (
|
|
3779
|
+
if (ts5.isCallExpression(current) && current.questionDotToken == null && current.arguments.length === 1 && ts5.isPropertyAccessExpression(current.expression) && current.expression.questionDotToken == null && isStandardGlobal(current.expression.expression, "Number", context) && (current.expression.name.text === "isInteger" || current.expression.name.text === "isFinite")) {
|
|
3657
3780
|
const argument = current.arguments[0];
|
|
3658
3781
|
const value2 = staticRequirementParameterPathValue(argument, context);
|
|
3659
3782
|
return value2 == null ? null : {
|
|
@@ -3662,7 +3785,7 @@ function writtenRequirement(condition, context) {
|
|
|
3662
3785
|
value: value2
|
|
3663
3786
|
};
|
|
3664
3787
|
}
|
|
3665
|
-
if (!
|
|
3788
|
+
if (!ts5.isBinaryExpression(current) || !staticAssertionComparison(current.operatorToken.kind))
|
|
3666
3789
|
return null;
|
|
3667
3790
|
const leftParameter = staticRequirementParameterPathValue(current.left, context);
|
|
3668
3791
|
const rightParameter = staticRequirementParameterPathValue(current.right, context);
|
|
@@ -3678,10 +3801,10 @@ function writtenRequirement(condition, context) {
|
|
|
3678
3801
|
function staticRequirementParameterPathValue(expression, context) {
|
|
3679
3802
|
requireNumberType(expression, context.checker);
|
|
3680
3803
|
let root = unwrapParentheses(expression);
|
|
3681
|
-
while (
|
|
3804
|
+
while (ts5.isPropertyAccessExpression(root) && root.questionDotToken == null) {
|
|
3682
3805
|
root = unwrapParentheses(root.expression);
|
|
3683
3806
|
}
|
|
3684
|
-
if (!
|
|
3807
|
+
if (!ts5.isIdentifier(root))
|
|
3685
3808
|
return null;
|
|
3686
3809
|
const symbol = context.checker.getSymbolAtLocation(root);
|
|
3687
3810
|
const rootValue = symbol == null ? null : context.bindings.get(symbol);
|
|
@@ -3708,14 +3831,14 @@ function staticFiniteValue(expression, context) {
|
|
|
3708
3831
|
if (literal != null)
|
|
3709
3832
|
return Number.isFinite(literal) ? literal : null;
|
|
3710
3833
|
const unwrapped = unwrapParentheses(current);
|
|
3711
|
-
if (!
|
|
3834
|
+
if (!ts5.isIdentifier(unwrapped))
|
|
3712
3835
|
return null;
|
|
3713
3836
|
const symbol = resolvedSymbol(context.checker.getSymbolAtLocation(unwrapped), context.checker);
|
|
3714
3837
|
if (symbol == null || seen.has(symbol))
|
|
3715
3838
|
return null;
|
|
3716
3839
|
seen.add(symbol);
|
|
3717
3840
|
const declaration = symbol.valueDeclaration;
|
|
3718
|
-
if (declaration == null || !
|
|
3841
|
+
if (declaration == null || !ts5.isVariableDeclaration(declaration) || (ts5.getCombinedNodeFlags(declaration) & ts5.NodeFlags.Const) === 0 || declaration.getSourceFile().isDeclarationFile || declaration.initializer == null)
|
|
3719
3842
|
return null;
|
|
3720
3843
|
current = declaration.initializer;
|
|
3721
3844
|
}
|
|
@@ -3740,25 +3863,25 @@ function lowerWrittenRequirement(requirement, condition, context) {
|
|
|
3740
3863
|
right: lowerOperand(requirement.right)
|
|
3741
3864
|
});
|
|
3742
3865
|
}
|
|
3743
|
-
function supportedWrittenAssertion(condition,
|
|
3866
|
+
function supportedWrittenAssertion(condition, context) {
|
|
3744
3867
|
const current = unwrapParentheses(condition);
|
|
3745
|
-
if (
|
|
3746
|
-
return staticAssertionNumericAtom(current.left, checker) && staticAssertionNumericAtom(current.right, checker);
|
|
3868
|
+
if (ts5.isBinaryExpression(current) && staticAssertionComparison(current.operatorToken.kind)) {
|
|
3869
|
+
return staticAssertionNumericAtom(current.left, context.checker) && staticAssertionNumericAtom(current.right, context.checker);
|
|
3747
3870
|
}
|
|
3748
|
-
const operand = staticNumberCheckOperand(current,
|
|
3749
|
-
return operand != null && staticAssertionNumericAtom(operand, checker);
|
|
3871
|
+
const operand = staticNumberCheckOperand(current, context);
|
|
3872
|
+
return operand != null && staticAssertionNumericAtom(operand, context.checker);
|
|
3750
3873
|
}
|
|
3751
3874
|
function staticAssertionNumericAtom(expression, checker) {
|
|
3752
3875
|
return staticAssertionAtom(expression) && valueKind(checker.getTypeAtLocation(expression), checker) === "number";
|
|
3753
3876
|
}
|
|
3754
3877
|
function staticAssertionComparison(kind) {
|
|
3755
3878
|
switch (kind) {
|
|
3756
|
-
case
|
|
3757
|
-
case
|
|
3758
|
-
case
|
|
3759
|
-
case
|
|
3760
|
-
case
|
|
3761
|
-
case
|
|
3879
|
+
case ts5.SyntaxKind.LessThanToken:
|
|
3880
|
+
case ts5.SyntaxKind.LessThanEqualsToken:
|
|
3881
|
+
case ts5.SyntaxKind.GreaterThanToken:
|
|
3882
|
+
case ts5.SyntaxKind.GreaterThanEqualsToken:
|
|
3883
|
+
case ts5.SyntaxKind.EqualsEqualsEqualsToken:
|
|
3884
|
+
case ts5.SyntaxKind.ExclamationEqualsEqualsToken:
|
|
3762
3885
|
return true;
|
|
3763
3886
|
default:
|
|
3764
3887
|
return false;
|
|
@@ -3766,54 +3889,54 @@ function staticAssertionComparison(kind) {
|
|
|
3766
3889
|
}
|
|
3767
3890
|
function staticAssertionAtomProblem(expression) {
|
|
3768
3891
|
const current = unwrapParentheses(expression);
|
|
3769
|
-
if (
|
|
3892
|
+
if (ts5.isIdentifier(current) || ts5.isNumericLiteral(current))
|
|
3770
3893
|
return null;
|
|
3771
|
-
if (
|
|
3772
|
-
return
|
|
3894
|
+
if (ts5.isPrefixUnaryExpression(current) && (current.operator === ts5.SyntaxKind.PlusToken || current.operator === ts5.SyntaxKind.MinusToken)) {
|
|
3895
|
+
return ts5.isNumericLiteral(unwrapParentheses(current.operand)) ? null : "bindValueFirst";
|
|
3773
3896
|
}
|
|
3774
|
-
if (
|
|
3897
|
+
if (ts5.isElementAccessExpression(current))
|
|
3775
3898
|
return "bindValueFirst";
|
|
3776
|
-
if (
|
|
3899
|
+
if (ts5.isNonNullExpression(current))
|
|
3777
3900
|
return staticAssertionAtomProblem(current.expression);
|
|
3778
|
-
if (
|
|
3901
|
+
if (ts5.isCallExpression(current))
|
|
3779
3902
|
return "functionCall";
|
|
3780
|
-
if (
|
|
3903
|
+
if (ts5.isBinaryExpression(current))
|
|
3781
3904
|
return "bindValueFirst";
|
|
3782
|
-
if (
|
|
3905
|
+
if (ts5.isPropertyAccessExpression(current)) {
|
|
3783
3906
|
return current.questionDotToken == null ? staticAssertionAtomProblem(current.expression) : "directCheck";
|
|
3784
3907
|
}
|
|
3785
3908
|
return "directCheck";
|
|
3786
3909
|
}
|
|
3787
3910
|
function staticAssertionAtom(expression) {
|
|
3788
3911
|
const current = unwrapParentheses(expression);
|
|
3789
|
-
if (
|
|
3912
|
+
if (ts5.isIdentifier(current) || ts5.isNumericLiteral(current))
|
|
3790
3913
|
return true;
|
|
3791
|
-
if (
|
|
3792
|
-
return
|
|
3914
|
+
if (ts5.isPrefixUnaryExpression(current) && (current.operator === ts5.SyntaxKind.PlusToken || current.operator === ts5.SyntaxKind.MinusToken)) {
|
|
3915
|
+
return ts5.isNumericLiteral(unwrapParentheses(current.operand));
|
|
3793
3916
|
}
|
|
3794
|
-
return
|
|
3917
|
+
return ts5.isPropertyAccessExpression(current) && current.questionDotToken == null && staticAssertionAtom(current.expression);
|
|
3795
3918
|
}
|
|
3796
|
-
function staticAssertionProblem(condition,
|
|
3919
|
+
function staticAssertionProblem(condition, context, fallback) {
|
|
3797
3920
|
const current = unwrapParentheses(condition);
|
|
3798
|
-
if (
|
|
3921
|
+
if (ts5.isBinaryExpression(current) && staticAssertionComparison(current.operatorToken.kind)) {
|
|
3799
3922
|
return staticAssertionAtomProblem(current.left) ?? staticAssertionAtomProblem(current.right) ?? fallback;
|
|
3800
3923
|
}
|
|
3801
|
-
const operand = staticNumberCheckOperand(current,
|
|
3924
|
+
const operand = staticNumberCheckOperand(current, context);
|
|
3802
3925
|
if (operand != null)
|
|
3803
3926
|
return staticAssertionAtomProblem(operand) ?? fallback;
|
|
3804
|
-
if (
|
|
3927
|
+
if (ts5.isCallExpression(current))
|
|
3805
3928
|
return "functionCall";
|
|
3806
3929
|
return "directCheck";
|
|
3807
3930
|
}
|
|
3808
|
-
function staticNumberCheckOperand(expression,
|
|
3809
|
-
if (!
|
|
3931
|
+
function staticNumberCheckOperand(expression, context) {
|
|
3932
|
+
if (!ts5.isCallExpression(expression) || expression.questionDotToken != null || expression.arguments.length !== 1 || !ts5.isPropertyAccessExpression(expression.expression) || expression.expression.questionDotToken != null)
|
|
3810
3933
|
return null;
|
|
3811
3934
|
const callee = expression.expression;
|
|
3812
|
-
return
|
|
3935
|
+
return isStandardGlobal(callee.expression, "Number", context) && (callee.name.text === "isInteger" || callee.name.text === "isFinite" || callee.name.text === "isNaN") ? expression.arguments[0] : null;
|
|
3813
3936
|
}
|
|
3814
3937
|
function unwrapParentheses(expression) {
|
|
3815
3938
|
let current = expression;
|
|
3816
|
-
while (
|
|
3939
|
+
while (ts5.isParenthesizedExpression(current))
|
|
3817
3940
|
current = current.expression;
|
|
3818
3941
|
return current;
|
|
3819
3942
|
}
|
|
@@ -3832,30 +3955,30 @@ function removableStaticConditionInstruction(instruction) {
|
|
|
3832
3955
|
}
|
|
3833
3956
|
}
|
|
3834
3957
|
function identifierAssignment(node) {
|
|
3835
|
-
if (
|
|
3836
|
-
if (node.operatorToken.kind ===
|
|
3958
|
+
if (ts5.isBinaryExpression(node) && ts5.isIdentifier(node.left)) {
|
|
3959
|
+
if (node.operatorToken.kind === ts5.SyntaxKind.EqualsToken)
|
|
3837
3960
|
return { form: "assign", target: node.left, node };
|
|
3838
3961
|
const operator = compoundAssignmentOperator(node.operatorToken.kind);
|
|
3839
3962
|
if (operator != null)
|
|
3840
3963
|
return { form: "compound", target: node.left, node, operator };
|
|
3841
|
-
const logical = node.operatorToken.kind ===
|
|
3964
|
+
const logical = node.operatorToken.kind === ts5.SyntaxKind.QuestionQuestionEqualsToken ? "nullish" : node.operatorToken.kind === ts5.SyntaxKind.BarBarEqualsToken ? "or" : node.operatorToken.kind === ts5.SyntaxKind.AmpersandAmpersandEqualsToken ? "and" : null;
|
|
3842
3965
|
if (logical != null)
|
|
3843
3966
|
return { form: "logical", target: node.left, node, logical };
|
|
3844
3967
|
}
|
|
3845
|
-
if ((
|
|
3968
|
+
if ((ts5.isPrefixUnaryExpression(node) || ts5.isPostfixUnaryExpression(node)) && (node.operator === ts5.SyntaxKind.PlusPlusToken || node.operator === ts5.SyntaxKind.MinusMinusToken) && ts5.isIdentifier(node.operand)) {
|
|
3846
3969
|
return { form: "update", target: node.operand, node };
|
|
3847
3970
|
}
|
|
3848
3971
|
return null;
|
|
3849
3972
|
}
|
|
3850
3973
|
function compoundAssignmentOperator(kind) {
|
|
3851
3974
|
switch (kind) {
|
|
3852
|
-
case
|
|
3975
|
+
case ts5.SyntaxKind.PlusEqualsToken:
|
|
3853
3976
|
return "add";
|
|
3854
|
-
case
|
|
3977
|
+
case ts5.SyntaxKind.MinusEqualsToken:
|
|
3855
3978
|
return "subtract";
|
|
3856
|
-
case
|
|
3979
|
+
case ts5.SyntaxKind.AsteriskEqualsToken:
|
|
3857
3980
|
return "multiply";
|
|
3858
|
-
case
|
|
3981
|
+
case ts5.SyntaxKind.SlashEqualsToken:
|
|
3859
3982
|
return "divide";
|
|
3860
3983
|
default:
|
|
3861
3984
|
return null;
|
|
@@ -3871,6 +3994,15 @@ function lowerValueBranch(node, condition, lowerTrueArm, lowerFalseArm, context)
|
|
|
3871
3994
|
whenFalse: { block: whenFalse, arguments: [] },
|
|
3872
3995
|
site: addSite(context, node)
|
|
3873
3996
|
});
|
|
3997
|
+
return joinValueBranches(node, whenTrue, whenFalse, lowerTrueArm, lowerFalseArm, context);
|
|
3998
|
+
}
|
|
3999
|
+
function lowerBranchingValue(node, condition, lowerTrueArm, lowerFalseArm, context) {
|
|
4000
|
+
const whenTrue = createBlock(context);
|
|
4001
|
+
const whenFalse = createBlock(context);
|
|
4002
|
+
lowerBranchingCondition(condition, whenTrue, whenFalse, context);
|
|
4003
|
+
return joinValueBranches(node, whenTrue, whenFalse, lowerTrueArm, lowerFalseArm, context);
|
|
4004
|
+
}
|
|
4005
|
+
function joinValueBranches(node, whenTrue, whenFalse, lowerTrueArm, lowerFalseArm, context) {
|
|
3874
4006
|
context.currentBlock = context.blocks[whenTrue];
|
|
3875
4007
|
const trueValue = lowerTrueArm();
|
|
3876
4008
|
const trueBlock = context.currentBlock;
|
|
@@ -3897,33 +4029,12 @@ function lowerConditionalExpression(expression, context) {
|
|
|
3897
4029
|
if (valueKind(resultType, context.checker) == null) {
|
|
3898
4030
|
throw unsupported(expression, { kind: "valueType", typeText: context.checker.typeToString(resultType) });
|
|
3899
4031
|
}
|
|
3900
|
-
|
|
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];
|
|
4032
|
+
return lowerBranchingValue(expression, expression.condition, () => lowerExpression(expression.whenTrue, context), () => lowerExpression(expression.whenFalse, context), context);
|
|
3922
4033
|
}
|
|
3923
4034
|
function lowerBranchingCondition(expression, whenTrue, whenFalse, context) {
|
|
3924
4035
|
const current = unwrap(expression, context.checker);
|
|
3925
|
-
if (
|
|
3926
|
-
const isAnd = current.operatorToken.kind ===
|
|
4036
|
+
if (ts5.isBinaryExpression(current) && (current.operatorToken.kind === ts5.SyntaxKind.AmpersandAmpersandToken || current.operatorToken.kind === ts5.SyntaxKind.BarBarToken)) {
|
|
4037
|
+
const isAnd = current.operatorToken.kind === ts5.SyntaxKind.AmpersandAmpersandToken;
|
|
3927
4038
|
const middle = createBlock(context);
|
|
3928
4039
|
if (isAnd) {
|
|
3929
4040
|
lowerBranchingCondition(current.left, middle, whenFalse, context);
|
|
@@ -3934,7 +4045,7 @@ function lowerBranchingCondition(expression, whenTrue, whenFalse, context) {
|
|
|
3934
4045
|
lowerBranchingCondition(current.right, whenTrue, whenFalse, context);
|
|
3935
4046
|
return;
|
|
3936
4047
|
}
|
|
3937
|
-
if (
|
|
4048
|
+
if (ts5.isPrefixUnaryExpression(current) && current.operator === ts5.SyntaxKind.ExclamationToken) {
|
|
3938
4049
|
lowerBranchingCondition(current.operand, whenFalse, whenTrue, context);
|
|
3939
4050
|
return;
|
|
3940
4051
|
}
|
|
@@ -3952,21 +4063,20 @@ function lowerBranchingCondition(expression, whenTrue, whenFalse, context) {
|
|
|
3952
4063
|
function lowerLogicalExpression(expression, context) {
|
|
3953
4064
|
requireBooleanCondition(expression.left, context.checker);
|
|
3954
4065
|
requireBooleanCondition(expression.right, context.checker);
|
|
3955
|
-
const isAnd = expression.operatorToken.kind ===
|
|
3956
|
-
|
|
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);
|
|
4066
|
+
const isAnd = expression.operatorToken.kind === ts5.SyntaxKind.AmpersandAmpersandToken;
|
|
4067
|
+
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
4068
|
}
|
|
3959
4069
|
function arithmeticOperator(kind) {
|
|
3960
4070
|
switch (kind) {
|
|
3961
|
-
case
|
|
4071
|
+
case ts5.SyntaxKind.PlusToken:
|
|
3962
4072
|
return "add";
|
|
3963
|
-
case
|
|
4073
|
+
case ts5.SyntaxKind.MinusToken:
|
|
3964
4074
|
return "subtract";
|
|
3965
|
-
case
|
|
4075
|
+
case ts5.SyntaxKind.AsteriskToken:
|
|
3966
4076
|
return "multiply";
|
|
3967
|
-
case
|
|
4077
|
+
case ts5.SyntaxKind.SlashToken:
|
|
3968
4078
|
return "divide";
|
|
3969
|
-
case
|
|
4079
|
+
case ts5.SyntaxKind.PercentToken:
|
|
3970
4080
|
return "remainder";
|
|
3971
4081
|
default:
|
|
3972
4082
|
return null;
|
|
@@ -3974,19 +4084,19 @@ function arithmeticOperator(kind) {
|
|
|
3974
4084
|
}
|
|
3975
4085
|
function comparisonOperator(kind) {
|
|
3976
4086
|
switch (kind) {
|
|
3977
|
-
case
|
|
4087
|
+
case ts5.SyntaxKind.LessThanToken:
|
|
3978
4088
|
return "lessThan";
|
|
3979
|
-
case
|
|
4089
|
+
case ts5.SyntaxKind.LessThanEqualsToken:
|
|
3980
4090
|
return "lessThanOrEqual";
|
|
3981
|
-
case
|
|
4091
|
+
case ts5.SyntaxKind.GreaterThanToken:
|
|
3982
4092
|
return "greaterThan";
|
|
3983
|
-
case
|
|
4093
|
+
case ts5.SyntaxKind.GreaterThanEqualsToken:
|
|
3984
4094
|
return "greaterThanOrEqual";
|
|
3985
|
-
case
|
|
3986
|
-
case
|
|
4095
|
+
case ts5.SyntaxKind.EqualsEqualsToken:
|
|
4096
|
+
case ts5.SyntaxKind.EqualsEqualsEqualsToken:
|
|
3987
4097
|
return "equal";
|
|
3988
|
-
case
|
|
3989
|
-
case
|
|
4098
|
+
case ts5.SyntaxKind.ExclamationEqualsToken:
|
|
4099
|
+
case ts5.SyntaxKind.ExclamationEqualsEqualsToken:
|
|
3990
4100
|
return "notEqual";
|
|
3991
4101
|
default:
|
|
3992
4102
|
return null;
|
|
@@ -3994,12 +4104,12 @@ function comparisonOperator(kind) {
|
|
|
3994
4104
|
}
|
|
3995
4105
|
function requireNumberType(node, checker) {
|
|
3996
4106
|
const type = checker.getTypeAtLocation(node);
|
|
3997
|
-
if (valueKind(type, checker) !== "number" && (type.flags &
|
|
4107
|
+
if (valueKind(type, checker) !== "number" && (type.flags & ts5.TypeFlags.Any) === 0) {
|
|
3998
4108
|
throw unsupported(node, { kind: "nonNumberOperand", typeText: checker.typeToString(type) });
|
|
3999
4109
|
}
|
|
4000
4110
|
}
|
|
4001
4111
|
function typeCanIncludeUndefined(type) {
|
|
4002
|
-
if ((type.flags & (
|
|
4112
|
+
if ((type.flags & (ts5.TypeFlags.Undefined | ts5.TypeFlags.Any | ts5.TypeFlags.Unknown)) !== 0)
|
|
4003
4113
|
return true;
|
|
4004
4114
|
return type.isUnion() && type.types.some(typeCanIncludeUndefined);
|
|
4005
4115
|
}
|
|
@@ -4032,19 +4142,19 @@ function valueKind(type, checker, depth = 0) {
|
|
|
4032
4142
|
return result;
|
|
4033
4143
|
}
|
|
4034
4144
|
function valueKindUncached(type, checker, depth) {
|
|
4035
|
-
if ((type
|
|
4145
|
+
if (numberConstituent(type) != null)
|
|
4036
4146
|
return "number";
|
|
4037
|
-
if ((type.flags &
|
|
4147
|
+
if ((type.flags & ts5.TypeFlags.BooleanLike) !== 0)
|
|
4038
4148
|
return "boolean";
|
|
4039
|
-
if ((type.flags &
|
|
4149
|
+
if ((type.flags & ts5.TypeFlags.StringLike) !== 0)
|
|
4040
4150
|
return "opaque";
|
|
4041
4151
|
if (checker.isTupleType(type))
|
|
4042
4152
|
return "tuple";
|
|
4043
4153
|
if (checker.isArrayType(type)) {
|
|
4044
|
-
const element = checker.getIndexTypeOfType(type,
|
|
4154
|
+
const element = checker.getIndexTypeOfType(type, ts5.IndexKind.Number);
|
|
4045
4155
|
return element != null && valueKind(element, checker, depth + 1) != null ? "array" : null;
|
|
4046
4156
|
}
|
|
4047
|
-
const objectLike = (type.flags &
|
|
4157
|
+
const objectLike = (type.flags & ts5.TypeFlags.Object) !== 0 || type.isIntersection() && type.types.every((member) => valueKind(member, checker, depth + 1) === "object");
|
|
4048
4158
|
if (objectLike) {
|
|
4049
4159
|
if (checker.getIndexInfosOfType(type).length > 0)
|
|
4050
4160
|
return null;
|
|
@@ -4055,10 +4165,10 @@ function valueKindUncached(type, checker, depth) {
|
|
|
4055
4165
|
const anchored = checker.getPropertiesOfType(type).some((property) => checker.getTypeOfSymbol(property).getCallSignatures().length === 0);
|
|
4056
4166
|
return anchored ? "object" : null;
|
|
4057
4167
|
}
|
|
4058
|
-
if ((type.flags & (
|
|
4168
|
+
if ((type.flags & (ts5.TypeFlags.Unknown | ts5.TypeFlags.Any)) !== 0)
|
|
4059
4169
|
return "opaque";
|
|
4060
4170
|
if (type.isUnion()) {
|
|
4061
|
-
const missingFlags =
|
|
4171
|
+
const missingFlags = ts5.TypeFlags.Null | ts5.TypeFlags.Undefined;
|
|
4062
4172
|
if (type.types.some((member) => (member.flags & missingFlags) !== 0)) {
|
|
4063
4173
|
const rest = nonMissingUnionMembers(type);
|
|
4064
4174
|
const restKind = classifyUnionMembers(rest, checker, depth + 1);
|
|
@@ -4077,7 +4187,7 @@ function nonMissingUnionMembers(type) {
|
|
|
4077
4187
|
const cached = nonMissingUnionMembersCache.get(type);
|
|
4078
4188
|
if (cached != null)
|
|
4079
4189
|
return cached;
|
|
4080
|
-
const missingFlags =
|
|
4190
|
+
const missingFlags = ts5.TypeFlags.Null | ts5.TypeFlags.Undefined;
|
|
4081
4191
|
const members = type.types.filter((member) => (member.flags & missingFlags) === 0);
|
|
4082
4192
|
nonMissingUnionMembersCache.set(type, members);
|
|
4083
4193
|
return members;
|
|
@@ -4107,7 +4217,7 @@ function taggedUnionPropertyUncached(members, checker, depth) {
|
|
|
4107
4217
|
const qualifies = (candidateName, singleLiteralOnly) => {
|
|
4108
4218
|
for (const member of members) {
|
|
4109
4219
|
const property = checker.getPropertyOfType(member, candidateName);
|
|
4110
|
-
if (property == null || (property.flags &
|
|
4220
|
+
if (property == null || (property.flags & ts5.SymbolFlags.Optional) !== 0)
|
|
4111
4221
|
return false;
|
|
4112
4222
|
const literals = tagLiteralValues(checker.getTypeOfSymbol(property));
|
|
4113
4223
|
if (literals == null || singleLiteralOnly && literals.length !== 1)
|
|
@@ -4117,7 +4227,7 @@ function taggedUnionPropertyUncached(members, checker, depth) {
|
|
|
4117
4227
|
};
|
|
4118
4228
|
for (const singleLiteralOnly of [true, false]) {
|
|
4119
4229
|
for (const candidate of checker.getPropertiesOfType(first)) {
|
|
4120
|
-
if ((candidate.flags &
|
|
4230
|
+
if ((candidate.flags & ts5.SymbolFlags.Optional) !== 0)
|
|
4121
4231
|
continue;
|
|
4122
4232
|
if (qualifies(candidate.name, singleLiteralOnly))
|
|
4123
4233
|
return candidate.name;
|
|
@@ -4126,28 +4236,28 @@ function taggedUnionPropertyUncached(members, checker, depth) {
|
|
|
4126
4236
|
return null;
|
|
4127
4237
|
}
|
|
4128
4238
|
function calleeDisplayName(expression, sourceFile) {
|
|
4129
|
-
if (
|
|
4239
|
+
if (ts5.isPropertyAccessExpression(expression)) {
|
|
4130
4240
|
const receiver = expression.expression;
|
|
4131
|
-
const receiverName =
|
|
4241
|
+
const receiverName = ts5.isIdentifier(receiver) ? receiver.text : ts5.isPropertyAccessExpression(receiver) && ts5.isIdentifier(receiver.expression) ? `${receiver.expression.text}.${receiver.name.text}` : "(…)";
|
|
4132
4242
|
return `${receiverName}.${expression.name.text}`;
|
|
4133
4243
|
}
|
|
4134
4244
|
return expression.getText(sourceFile).replace(/\s+/g, " ").slice(0, 60);
|
|
4135
4245
|
}
|
|
4136
4246
|
function writtenTagLiteral(literal, tagProperty, context) {
|
|
4137
|
-
if (!
|
|
4247
|
+
if (!ts5.isObjectLiteralExpression(literal))
|
|
4138
4248
|
return null;
|
|
4139
4249
|
for (const property of literal.properties) {
|
|
4140
|
-
if (!
|
|
4250
|
+
if (!ts5.isPropertyAssignment(property))
|
|
4141
4251
|
continue;
|
|
4142
|
-
const name =
|
|
4252
|
+
const name = ts5.isIdentifier(property.name) || ts5.isStringLiteral(property.name) ? property.name.text : null;
|
|
4143
4253
|
if (name !== tagProperty)
|
|
4144
4254
|
continue;
|
|
4145
4255
|
const initializer = unwrap(property.initializer, context.checker);
|
|
4146
|
-
if (
|
|
4256
|
+
if (ts5.isStringLiteral(initializer) || ts5.isNoSubstitutionTemplateLiteral(initializer))
|
|
4147
4257
|
return initializer.text;
|
|
4148
|
-
if (initializer.kind ===
|
|
4258
|
+
if (initializer.kind === ts5.SyntaxKind.TrueKeyword)
|
|
4149
4259
|
return true;
|
|
4150
|
-
if (initializer.kind ===
|
|
4260
|
+
if (initializer.kind === ts5.SyntaxKind.FalseKeyword)
|
|
4151
4261
|
return false;
|
|
4152
4262
|
return null;
|
|
4153
4263
|
}
|
|
@@ -4157,7 +4267,7 @@ function tagLiteralValues(type) {
|
|
|
4157
4267
|
const single = (member) => {
|
|
4158
4268
|
if (member.isStringLiteral())
|
|
4159
4269
|
return member.value;
|
|
4160
|
-
if ((member.flags &
|
|
4270
|
+
if ((member.flags & ts5.TypeFlags.BooleanLiteral) !== 0) {
|
|
4161
4271
|
return member.intrinsicName === "true";
|
|
4162
4272
|
}
|
|
4163
4273
|
return null;
|
|
@@ -4204,10 +4314,10 @@ function structuralTypeWalkCompletes(type, checker, seen) {
|
|
|
4204
4314
|
if (checker.isArrayType(type)) {
|
|
4205
4315
|
if (seen.length >= 8 || seen.includes(type))
|
|
4206
4316
|
return false;
|
|
4207
|
-
const element = checker.getIndexTypeOfType(type,
|
|
4317
|
+
const element = checker.getIndexTypeOfType(type, ts5.IndexKind.Number);
|
|
4208
4318
|
return element == null || structuralTypeWalkCompletes(element, checker, [...seen, type]);
|
|
4209
4319
|
}
|
|
4210
|
-
if ((type.flags &
|
|
4320
|
+
if ((type.flags & ts5.TypeFlags.Object) === 0)
|
|
4211
4321
|
return true;
|
|
4212
4322
|
if (seen.length >= 8 || seen.includes(type))
|
|
4213
4323
|
return false;
|
|
@@ -4222,7 +4332,7 @@ function structuralTypeWalkCompletes(type, checker, seen) {
|
|
|
4222
4332
|
function structuralPropertiesComplete(type, checker, seen) {
|
|
4223
4333
|
const nextSeen = [...seen, type];
|
|
4224
4334
|
for (const property of checker.getPropertiesOfType(type)) {
|
|
4225
|
-
if ((property.flags &
|
|
4335
|
+
if ((property.flags & ts5.SymbolFlags.Optional) !== 0)
|
|
4226
4336
|
continue;
|
|
4227
4337
|
if (!structuralTypeWalkCompletes(checker.getTypeOfSymbol(property), checker, nextSeen))
|
|
4228
4338
|
return false;
|
|
@@ -4241,12 +4351,6 @@ function requireBooleanCondition(node, checker) {
|
|
|
4241
4351
|
});
|
|
4242
4352
|
}
|
|
4243
4353
|
function requireAccessedPropertyKind(access, checker) {
|
|
4244
|
-
const receiverType = checker.getTypeAtLocation(access.expression);
|
|
4245
|
-
const presentType = access.questionDotToken != null ? checker.getNonNullableType(receiverType) : receiverType;
|
|
4246
|
-
const property = checker.getPropertyOfType(presentType, access.name.text);
|
|
4247
|
-
if (valueKind(presentType, checker) === "object" && property != null && declaredOnlyInDeclarationFiles(property)) {
|
|
4248
|
-
throw unsupported(access, { kind: "prototypeMemberRead", property: access.name.text });
|
|
4249
|
-
}
|
|
4250
4354
|
const type = checker.getTypeAtLocation(access);
|
|
4251
4355
|
if (valueKind(type, checker) != null)
|
|
4252
4356
|
return;
|
|
@@ -4255,17 +4359,12 @@ function requireAccessedPropertyKind(access, checker) {
|
|
|
4255
4359
|
function resolvedSymbol(symbol, checker) {
|
|
4256
4360
|
if (symbol == null)
|
|
4257
4361
|
return null;
|
|
4258
|
-
return (symbol.flags &
|
|
4259
|
-
}
|
|
4260
|
-
function isStandardMathObject(expression, checker) {
|
|
4261
|
-
if (!ts4.isIdentifier(expression) || expression.text !== "Math")
|
|
4262
|
-
return false;
|
|
4263
|
-
return declaredOnlyInDeclarationFiles(checker.getSymbolAtLocation(expression));
|
|
4362
|
+
return (symbol.flags & ts5.SymbolFlags.Alias) === 0 ? symbol : checker.getAliasedSymbol(symbol);
|
|
4264
4363
|
}
|
|
4265
|
-
function
|
|
4266
|
-
if (!
|
|
4364
|
+
function isStandardGlobal(expression, name, context) {
|
|
4365
|
+
if (!ts5.isIdentifier(expression) || expression.text !== name)
|
|
4267
4366
|
return false;
|
|
4268
|
-
return
|
|
4367
|
+
return hasDefaultLibraryDeclaration(context.checker.getSymbolAtLocation(expression), context.program);
|
|
4269
4368
|
}
|
|
4270
4369
|
function lowerElementAccess(access, asserted, context) {
|
|
4271
4370
|
const receiverType = context.checker.getTypeAtLocation(access.expression);
|
|
@@ -4280,7 +4379,7 @@ function lowerElementAccess(access, asserted, context) {
|
|
|
4280
4379
|
requireNumberType(access.argumentExpression, context.checker);
|
|
4281
4380
|
const array = lowerExpression(access.expression, context);
|
|
4282
4381
|
const index = lowerExpression(access.argumentExpression, context);
|
|
4283
|
-
const missingFlag =
|
|
4382
|
+
const missingFlag = ts5.TypeFlags.Undefined;
|
|
4284
4383
|
const staticTypeAllowsUndefined = (resultType.flags & missingFlag) !== 0 || resultType.isUnion() && resultType.types.some((member) => (member.flags & missingFlag) !== 0);
|
|
4285
4384
|
return addInstruction(context, access, {
|
|
4286
4385
|
kind: "arrayIndex",
|
|
@@ -4291,7 +4390,7 @@ function lowerElementAccess(access, asserted, context) {
|
|
|
4291
4390
|
}
|
|
4292
4391
|
function taggedUnionTagRead(expression, context) {
|
|
4293
4392
|
const unwrapped = unwrap(expression, context.checker);
|
|
4294
|
-
if (!
|
|
4393
|
+
if (!ts5.isPropertyAccessExpression(unwrapped))
|
|
4295
4394
|
return null;
|
|
4296
4395
|
const objectType = context.checker.getTypeAtLocation(unwrapped.expression);
|
|
4297
4396
|
if (valueKind(objectType, context.checker) !== "taggedUnion" || !objectType.isUnion())
|
|
@@ -4301,17 +4400,17 @@ function taggedUnionTagRead(expression, context) {
|
|
|
4301
4400
|
}
|
|
4302
4401
|
function tagCheckComparison(expression, context) {
|
|
4303
4402
|
const operator = expression.operatorToken.kind;
|
|
4304
|
-
const equals = operator ===
|
|
4305
|
-
const notEquals = operator ===
|
|
4403
|
+
const equals = operator === ts5.SyntaxKind.EqualsEqualsEqualsToken || operator === ts5.SyntaxKind.EqualsEqualsToken;
|
|
4404
|
+
const notEquals = operator === ts5.SyntaxKind.ExclamationEqualsEqualsToken || operator === ts5.SyntaxKind.ExclamationEqualsToken;
|
|
4306
4405
|
if (!equals && !notEquals)
|
|
4307
4406
|
return null;
|
|
4308
4407
|
const literalOf = (side) => {
|
|
4309
4408
|
const unwrapped = unwrap(side, context.checker);
|
|
4310
|
-
if (
|
|
4409
|
+
if (ts5.isStringLiteral(unwrapped) || ts5.isNoSubstitutionTemplateLiteral(unwrapped))
|
|
4311
4410
|
return unwrapped.text;
|
|
4312
|
-
if (unwrapped.kind ===
|
|
4411
|
+
if (unwrapped.kind === ts5.SyntaxKind.TrueKeyword)
|
|
4313
4412
|
return true;
|
|
4314
|
-
if (unwrapped.kind ===
|
|
4413
|
+
if (unwrapped.kind === ts5.SyntaxKind.FalseKeyword)
|
|
4315
4414
|
return false;
|
|
4316
4415
|
return null;
|
|
4317
4416
|
};
|
|
@@ -4329,7 +4428,7 @@ function tagCheckComparison(expression, context) {
|
|
|
4329
4428
|
}
|
|
4330
4429
|
function opaqueEqualityCheck(expression, context) {
|
|
4331
4430
|
const operator = expression.operatorToken.kind;
|
|
4332
|
-
const isEquality = operator ===
|
|
4431
|
+
const isEquality = operator === ts5.SyntaxKind.EqualsEqualsEqualsToken || operator === ts5.SyntaxKind.ExclamationEqualsEqualsToken || operator === ts5.SyntaxKind.EqualsEqualsToken || operator === ts5.SyntaxKind.ExclamationEqualsToken;
|
|
4333
4432
|
if (!isEquality)
|
|
4334
4433
|
return null;
|
|
4335
4434
|
const opaqueOrMissingOpaque = (side) => {
|
|
@@ -4338,7 +4437,7 @@ function opaqueEqualityCheck(expression, context) {
|
|
|
4338
4437
|
if (kind === "opaque")
|
|
4339
4438
|
return true;
|
|
4340
4439
|
if (kind === "nullable" && type.isUnion()) {
|
|
4341
|
-
const missing =
|
|
4440
|
+
const missing = ts5.TypeFlags.Null | ts5.TypeFlags.Undefined;
|
|
4342
4441
|
const rest = type.types.filter((member) => (member.flags & missing) === 0);
|
|
4343
4442
|
return rest.length >= 1 && rest.every((member) => valueKind(member, context.checker) === "opaque");
|
|
4344
4443
|
}
|
|
@@ -4352,14 +4451,14 @@ function opaqueEqualityCheck(expression, context) {
|
|
|
4352
4451
|
}
|
|
4353
4452
|
function missingSentinelCheck(expression, context) {
|
|
4354
4453
|
const operator = expression.operatorToken.kind;
|
|
4355
|
-
const strict = operator ===
|
|
4356
|
-
const loose = operator ===
|
|
4454
|
+
const strict = operator === ts5.SyntaxKind.EqualsEqualsEqualsToken || operator === ts5.SyntaxKind.ExclamationEqualsEqualsToken;
|
|
4455
|
+
const loose = operator === ts5.SyntaxKind.EqualsEqualsToken || operator === ts5.SyntaxKind.ExclamationEqualsToken;
|
|
4357
4456
|
if (!strict && !loose)
|
|
4358
4457
|
return null;
|
|
4359
|
-
const negated = operator ===
|
|
4458
|
+
const negated = operator === ts5.SyntaxKind.ExclamationEqualsEqualsToken || operator === ts5.SyntaxKind.ExclamationEqualsToken;
|
|
4360
4459
|
const sentinelOf = (side) => {
|
|
4361
4460
|
const unwrapped = unwrap(side, context.checker);
|
|
4362
|
-
if (unwrapped.kind ===
|
|
4461
|
+
if (unwrapped.kind === ts5.SyntaxKind.NullKeyword)
|
|
4363
4462
|
return "null";
|
|
4364
4463
|
if (isUndefinedGlobal(unwrapped, context.checker))
|
|
4365
4464
|
return "undefined";
|
|
@@ -4367,39 +4466,39 @@ function missingSentinelCheck(expression, context) {
|
|
|
4367
4466
|
};
|
|
4368
4467
|
const isUndefinedString = (side) => {
|
|
4369
4468
|
const unwrapped = unwrap(side, context.checker);
|
|
4370
|
-
return
|
|
4469
|
+
return ts5.isStringLiteral(unwrapped) && unwrapped.text === "undefined";
|
|
4371
4470
|
};
|
|
4372
|
-
if (
|
|
4471
|
+
if (ts5.isTypeOfExpression(expression.left) && isUndefinedString(expression.right)) {
|
|
4373
4472
|
const value3 = lowerExpression(expression.left.expression, context);
|
|
4374
4473
|
return addInstruction(context, expression, { kind: "nullishCheck", value: value3, sentinel: "undefined", negated });
|
|
4375
4474
|
}
|
|
4376
|
-
if (
|
|
4475
|
+
if (ts5.isTypeOfExpression(expression.right) && isUndefinedString(expression.left)) {
|
|
4377
4476
|
const value3 = lowerExpression(expression.right.expression, context);
|
|
4378
4477
|
return addInstruction(context, expression, { kind: "nullishCheck", value: value3, sentinel: "undefined", negated });
|
|
4379
4478
|
}
|
|
4380
4479
|
const primitiveTypeofFlags = (side) => {
|
|
4381
4480
|
const unwrapped = unwrap(side, context.checker);
|
|
4382
|
-
if (!
|
|
4481
|
+
if (!ts5.isStringLiteral(unwrapped))
|
|
4383
4482
|
return null;
|
|
4384
4483
|
switch (unwrapped.text) {
|
|
4385
4484
|
case "number":
|
|
4386
|
-
return
|
|
4485
|
+
return ts5.TypeFlags.NumberLike;
|
|
4387
4486
|
case "string":
|
|
4388
|
-
return
|
|
4487
|
+
return ts5.TypeFlags.StringLike;
|
|
4389
4488
|
case "boolean":
|
|
4390
|
-
return
|
|
4489
|
+
return ts5.TypeFlags.BooleanLike;
|
|
4391
4490
|
default:
|
|
4392
4491
|
return null;
|
|
4393
4492
|
}
|
|
4394
4493
|
};
|
|
4395
4494
|
const rightFlags = primitiveTypeofFlags(expression.right);
|
|
4396
4495
|
const leftFlags = primitiveTypeofFlags(expression.left);
|
|
4397
|
-
const typeofSide =
|
|
4496
|
+
const typeofSide = ts5.isTypeOfExpression(expression.left) && rightFlags != null ? { operand: expression.left, flags: rightFlags } : ts5.isTypeOfExpression(expression.right) && leftFlags != null ? { operand: expression.right, flags: leftFlags } : null;
|
|
4398
4497
|
if (typeofSide != null) {
|
|
4399
4498
|
const operandType = context.checker.getTypeAtLocation(typeofSide.operand.expression);
|
|
4400
|
-
const missing =
|
|
4499
|
+
const missing = ts5.TypeFlags.Null | ts5.TypeFlags.Undefined;
|
|
4401
4500
|
const members = operandType.isUnion() ? operandType.types : [operandType];
|
|
4402
|
-
const restMatches = members.every((member) => (member.flags & missing) !== 0 || (member.flags & typeofSide.flags) !== 0) && members.some((member) => (member.flags & missing) === 0);
|
|
4501
|
+
const restMatches = members.every((member) => (member.flags & missing) !== 0 || (typeofSide.flags === ts5.TypeFlags.NumberLike ? valueKind(member, context.checker) === "number" : (member.flags & typeofSide.flags) !== 0)) && members.some((member) => (member.flags & missing) === 0);
|
|
4403
4502
|
const value3 = lowerExpression(typeofSide.operand.expression, context);
|
|
4404
4503
|
if (restMatches) {
|
|
4405
4504
|
return addInstruction(context, expression, { kind: "nullishCheck", value: value3, sentinel: "nullish", negated: !negated });
|
|
@@ -4413,7 +4512,7 @@ function missingSentinelCheck(expression, context) {
|
|
|
4413
4512
|
return null;
|
|
4414
4513
|
const checked = leftSentinel == null ? expression.left : expression.right;
|
|
4415
4514
|
const checkedType = context.checker.getTypeAtLocation(checked);
|
|
4416
|
-
const missingFlags =
|
|
4515
|
+
const missingFlags = ts5.TypeFlags.Null | ts5.TypeFlags.Undefined;
|
|
4417
4516
|
const pureSentinel = (checkedType.flags & missingFlags) !== 0 || checkedType.isUnion() && checkedType.types.every((member) => (member.flags & missingFlags) !== 0);
|
|
4418
4517
|
if (!pureSentinel && valueKind(checkedType, context.checker) == null) {
|
|
4419
4518
|
throw unsupported(checked, { kind: "valueType", typeText: context.checker.typeToString(checkedType) });
|
|
@@ -4426,11 +4525,6 @@ function missingSentinelCheck(expression, context) {
|
|
|
4426
4525
|
negated
|
|
4427
4526
|
});
|
|
4428
4527
|
}
|
|
4429
|
-
function isGlobalInfinity(expression, checker) {
|
|
4430
|
-
if (!ts4.isIdentifier(expression) || expression.text !== "Infinity")
|
|
4431
|
-
return false;
|
|
4432
|
-
return declaredOnlyInDeclarationFiles(checker.getSymbolAtLocation(expression));
|
|
4433
|
-
}
|
|
4434
4528
|
function identifierValue(symbol, node, context) {
|
|
4435
4529
|
const local = context.bindings.get(symbol);
|
|
4436
4530
|
if (local != null)
|
|
@@ -4438,7 +4532,7 @@ function identifierValue(symbol, node, context) {
|
|
|
4438
4532
|
const binding = context.moduleBindingsBySymbol.get(symbol);
|
|
4439
4533
|
if (binding != null)
|
|
4440
4534
|
return addInstruction(context, node, { kind: "moduleRead", binding });
|
|
4441
|
-
if (
|
|
4535
|
+
if (isStandardGlobal(node, "Infinity", context)) {
|
|
4442
4536
|
return addInstruction(context, node, { kind: "constant", value: Number.POSITIVE_INFINITY });
|
|
4443
4537
|
}
|
|
4444
4538
|
if (isUndefinedGlobal(node, context.checker)) {
|
|
@@ -4457,25 +4551,25 @@ function assignIdentifier(symbol, node, value2, wholeExpression, context) {
|
|
|
4457
4551
|
throw unsupported(node, { kind: "unknownIdentifier", name: node.text });
|
|
4458
4552
|
}
|
|
4459
4553
|
function propertyName(name) {
|
|
4460
|
-
if (
|
|
4554
|
+
if (ts5.isIdentifier(name) || ts5.isStringLiteral(name) || ts5.isNumericLiteral(name))
|
|
4461
4555
|
return name.text;
|
|
4462
4556
|
throw unsupported(name, { kind: "computedPropertyName" });
|
|
4463
4557
|
}
|
|
4464
4558
|
function unwrap(expression, checker) {
|
|
4465
4559
|
let current = expression;
|
|
4466
4560
|
while (true) {
|
|
4467
|
-
if (
|
|
4561
|
+
if (ts5.isParenthesizedExpression(current) || ts5.isSatisfiesExpression(current)) {
|
|
4468
4562
|
current = current.expression;
|
|
4469
4563
|
continue;
|
|
4470
4564
|
}
|
|
4471
|
-
if ((
|
|
4565
|
+
if ((ts5.isAsExpression(current) || ts5.isTypeAssertionExpression(current)) && ts5.isConstTypeReference(current.type)) {
|
|
4472
4566
|
current = current.expression;
|
|
4473
4567
|
continue;
|
|
4474
4568
|
}
|
|
4475
|
-
if (
|
|
4569
|
+
if (ts5.isNonNullExpression(current)) {
|
|
4476
4570
|
const assertedType = checker.getTypeAtLocation(current);
|
|
4477
4571
|
const operandType = checker.getTypeAtLocation(current.expression);
|
|
4478
|
-
if (
|
|
4572
|
+
if (ts5.isElementAccessExpression(current.expression) && valueKind(assertedType, checker) != null) {
|
|
4479
4573
|
return current;
|
|
4480
4574
|
}
|
|
4481
4575
|
if (valueKind(assertedType, checker) !== valueKind(operandType, checker)) {
|
|
@@ -4492,11 +4586,51 @@ function unwrap(expression, checker) {
|
|
|
4492
4586
|
}
|
|
4493
4587
|
}
|
|
4494
4588
|
|
|
4495
|
-
// src/lower/
|
|
4589
|
+
// src/lower/function-unit.ts
|
|
4496
4590
|
import * as ts6 from "typescript";
|
|
4591
|
+
function callableSignature(unit, checker) {
|
|
4592
|
+
if (unit.initializer == null) {
|
|
4593
|
+
return checker.getSignatureFromDeclaration(unit.declaration) ?? null;
|
|
4594
|
+
}
|
|
4595
|
+
const signatures = checker.getSignaturesOfType(checker.getTypeAtLocation(unit.name), ts6.SignatureKind.Call);
|
|
4596
|
+
if (signatures.length !== 1)
|
|
4597
|
+
return null;
|
|
4598
|
+
const signature = signatures[0];
|
|
4599
|
+
const signatureDeclaration = signature.declaration;
|
|
4600
|
+
if (signatureDeclaration == null || signature.thisParameter != null || signature.parameters.length !== unit.declaration.parameters.length || signatureDeclaration.parameters.some((parameter) => !ts6.isParameter(parameter) || parameter.dotDotDotToken != null)) {
|
|
4601
|
+
return null;
|
|
4602
|
+
}
|
|
4603
|
+
return signature;
|
|
4604
|
+
}
|
|
4605
|
+
function topLevelFunctionUnits(sourceFile) {
|
|
4606
|
+
const units = [];
|
|
4607
|
+
for (const statement of sourceFile.statements) {
|
|
4608
|
+
if (ts6.isFunctionDeclaration(statement) && statement.name != null) {
|
|
4609
|
+
units.push({ name: statement.name, declaration: statement, initializer: null });
|
|
4610
|
+
continue;
|
|
4611
|
+
}
|
|
4612
|
+
if (!ts6.isVariableStatement(statement) || (statement.declarationList.flags & ts6.NodeFlags.Const) === 0)
|
|
4613
|
+
continue;
|
|
4614
|
+
for (const initializer of statement.declarationList.declarations) {
|
|
4615
|
+
if (!ts6.isIdentifier(initializer.name) || initializer.initializer == null)
|
|
4616
|
+
continue;
|
|
4617
|
+
const declaration = directFunctionExpression(initializer.initializer);
|
|
4618
|
+
if (declaration != null) {
|
|
4619
|
+
units.push({ name: initializer.name, declaration, initializer });
|
|
4620
|
+
}
|
|
4621
|
+
}
|
|
4622
|
+
}
|
|
4623
|
+
return units;
|
|
4624
|
+
}
|
|
4625
|
+
function directFunctionExpression(expression) {
|
|
4626
|
+
return ts6.isArrowFunction(expression) || ts6.isFunctionExpression(expression) ? expression : null;
|
|
4627
|
+
}
|
|
4628
|
+
|
|
4629
|
+
// src/lower/module.ts
|
|
4630
|
+
import * as ts8 from "typescript";
|
|
4497
4631
|
|
|
4498
4632
|
// src/lower/statements.ts
|
|
4499
|
-
import * as
|
|
4633
|
+
import * as ts7 from "typescript";
|
|
4500
4634
|
function lowerStatements(statements, context) {
|
|
4501
4635
|
for (const statement of statements) {
|
|
4502
4636
|
if (context.currentBlock.terminator != null)
|
|
@@ -4505,61 +4639,57 @@ function lowerStatements(statements, context) {
|
|
|
4505
4639
|
}
|
|
4506
4640
|
}
|
|
4507
4641
|
function lowerStatement(statement, context) {
|
|
4508
|
-
if (
|
|
4642
|
+
if (ts7.isVariableStatement(statement)) {
|
|
4509
4643
|
lowerVariableDeclarationList(statement.declarationList, context);
|
|
4510
4644
|
return;
|
|
4511
4645
|
}
|
|
4512
|
-
if (
|
|
4646
|
+
if (ts7.isReturnStatement(statement)) {
|
|
4513
4647
|
let value2 = statement.expression == null ? null : lowerExpression(statement.expression, context);
|
|
4514
|
-
if (
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
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
|
-
}
|
|
4648
|
+
if (context.returnsVoid)
|
|
4649
|
+
value2 = null;
|
|
4650
|
+
if (value2 == null && !context.returnsVoid) {
|
|
4651
|
+
value2 = addInstruction(context, statement, { kind: "nullishConstant", sentinel: "undefined" });
|
|
4522
4652
|
}
|
|
4523
4653
|
terminate(context.currentBlock, { kind: "return", value: value2, site: addSite(context, statement) });
|
|
4524
4654
|
return;
|
|
4525
4655
|
}
|
|
4526
|
-
if (
|
|
4656
|
+
if (ts7.isExpressionStatement(statement)) {
|
|
4527
4657
|
lowerStatementExpression(statement.expression, context);
|
|
4528
4658
|
return;
|
|
4529
4659
|
}
|
|
4530
|
-
if (
|
|
4660
|
+
if (ts7.isIfStatement(statement)) {
|
|
4531
4661
|
lowerIfStatement(statement, context);
|
|
4532
4662
|
return;
|
|
4533
4663
|
}
|
|
4534
|
-
if (
|
|
4664
|
+
if (ts7.isForOfStatement(statement)) {
|
|
4535
4665
|
lowerForOfStatement(statement, context);
|
|
4536
4666
|
return;
|
|
4537
4667
|
}
|
|
4538
|
-
if (
|
|
4668
|
+
if (ts7.isForStatement(statement)) {
|
|
4539
4669
|
lowerForStatement(statement, context);
|
|
4540
4670
|
return;
|
|
4541
4671
|
}
|
|
4542
|
-
if (
|
|
4672
|
+
if (ts7.isWhileStatement(statement)) {
|
|
4543
4673
|
lowerWhileStatement(statement, context);
|
|
4544
4674
|
return;
|
|
4545
4675
|
}
|
|
4546
|
-
if (
|
|
4676
|
+
if (ts7.isContinueStatement(statement)) {
|
|
4547
4677
|
lowerContinueStatement(statement, context);
|
|
4548
4678
|
return;
|
|
4549
4679
|
}
|
|
4550
|
-
if (
|
|
4680
|
+
if (ts7.isBlock(statement)) {
|
|
4551
4681
|
lowerStatements(statement.statements, context);
|
|
4552
4682
|
return;
|
|
4553
4683
|
}
|
|
4554
|
-
if (
|
|
4684
|
+
if (ts7.isSwitchStatement(statement)) {
|
|
4555
4685
|
lowerSwitchStatement(statement, context);
|
|
4556
4686
|
return;
|
|
4557
4687
|
}
|
|
4558
|
-
if (
|
|
4688
|
+
if (ts7.isThrowStatement(statement)) {
|
|
4559
4689
|
terminate(context.currentBlock, { kind: "thrown", site: addSite(context, statement) });
|
|
4560
4690
|
return;
|
|
4561
4691
|
}
|
|
4562
|
-
throw unsupported(statement, { kind: "statementForm", syntax:
|
|
4692
|
+
throw unsupported(statement, { kind: "statementForm", syntax: ts7.SyntaxKind[statement.kind] });
|
|
4563
4693
|
}
|
|
4564
4694
|
function lowerIfStatement(statement, context) {
|
|
4565
4695
|
const bindingsBeforeBranch = new Map(context.bindings);
|
|
@@ -4586,7 +4716,7 @@ function lowerSwitchStatement(statement, context) {
|
|
|
4586
4716
|
const subjectType = context.checker.getTypeAtLocation(statement.expression);
|
|
4587
4717
|
const subjectKind = valueKind(subjectType, context.checker);
|
|
4588
4718
|
const tagUnionExpression = taggedUnionTagRead(statement.expression, context);
|
|
4589
|
-
const missingFlags =
|
|
4719
|
+
const missingFlags = ts7.TypeFlags.Null | ts7.TypeFlags.Undefined;
|
|
4590
4720
|
const nullableOpaqueSubject = subjectKind === "nullable" && subjectType.isUnion() && subjectType.types.every((member) => (member.flags & missingFlags) !== 0 || valueKind(member, context.checker) === "opaque");
|
|
4591
4721
|
if (tagUnionExpression == null && subjectKind !== "number" && subjectKind !== "opaque" && !nullableOpaqueSubject) {
|
|
4592
4722
|
throw unsupported(statement.expression, { kind: "switchSubject", typeText: context.checker.typeToString(subjectType) });
|
|
@@ -4598,7 +4728,7 @@ function lowerSwitchStatement(statement, context) {
|
|
|
4598
4728
|
const clauses = statement.caseBlock.clauses;
|
|
4599
4729
|
for (let index = 0;index < clauses.length; index++) {
|
|
4600
4730
|
const clause = clauses[index];
|
|
4601
|
-
if (
|
|
4731
|
+
if (ts7.isDefaultClause(clause)) {
|
|
4602
4732
|
if (index !== clauses.length - 1 || pendingLabels.length > 0) {
|
|
4603
4733
|
throw unsupported(clause, { kind: "switchDefaultNotLast" });
|
|
4604
4734
|
}
|
|
@@ -4619,7 +4749,7 @@ function lowerSwitchStatement(statement, context) {
|
|
|
4619
4749
|
const lowerBody = (group) => {
|
|
4620
4750
|
const body = group.statements;
|
|
4621
4751
|
const last = body[body.length - 1];
|
|
4622
|
-
const endsWithBreak = last != null &&
|
|
4752
|
+
const endsWithBreak = last != null && ts7.isBreakStatement(last);
|
|
4623
4753
|
lowerStatements(endsWithBreak ? body.slice(0, -1) : body, context);
|
|
4624
4754
|
if (context.currentBlock.terminator == null) {
|
|
4625
4755
|
if (!endsWithBreak)
|
|
@@ -4638,7 +4768,7 @@ function lowerSwitchStatement(statement, context) {
|
|
|
4638
4768
|
let condition;
|
|
4639
4769
|
if (tagUnionExpression != null) {
|
|
4640
4770
|
const unwrappedLabel = label;
|
|
4641
|
-
if (!
|
|
4771
|
+
if (!ts7.isStringLiteral(unwrappedLabel) && !ts7.isNoSubstitutionTemplateLiteral(unwrappedLabel)) {
|
|
4642
4772
|
throw unsupported(label, { kind: "switchLabel", typeText: context.checker.typeToString(context.checker.getTypeAtLocation(label)) });
|
|
4643
4773
|
}
|
|
4644
4774
|
condition = addInstruction(context, label, { kind: "tagCheck", union: subject, tagValue: unwrappedLabel.text, negated: false });
|
|
@@ -4680,7 +4810,7 @@ function lowerSwitchStatement(statement, context) {
|
|
|
4680
4810
|
mergeAtContinuation(exits, bindingsBefore, statement, context);
|
|
4681
4811
|
}
|
|
4682
4812
|
function lowerForOfStatement(statement, context) {
|
|
4683
|
-
if (!
|
|
4813
|
+
if (!ts7.isVariableDeclarationList(statement.initializer) || statement.initializer.declarations.length !== 1 || !ts7.isIdentifier(statement.initializer.declarations[0].name)) {
|
|
4684
4814
|
throw unsupported(statement.initializer, { kind: "variableDeclarationShape" });
|
|
4685
4815
|
}
|
|
4686
4816
|
const elementName = statement.initializer.declarations[0].name;
|
|
@@ -4757,7 +4887,7 @@ function lowerForOfStatement(statement, context) {
|
|
|
4757
4887
|
}
|
|
4758
4888
|
function lowerForStatement(statement, context) {
|
|
4759
4889
|
if (statement.initializer != null) {
|
|
4760
|
-
if (
|
|
4890
|
+
if (ts7.isVariableDeclarationList(statement.initializer)) {
|
|
4761
4891
|
lowerVariableDeclarationList(statement.initializer, context);
|
|
4762
4892
|
} else {
|
|
4763
4893
|
lowerStatementExpression(statement.initializer, context);
|
|
@@ -4866,21 +4996,16 @@ function lowerBranch(statement, block, bindings, context) {
|
|
|
4866
4996
|
}
|
|
4867
4997
|
function lowerVariableDeclarationList(declarations, context) {
|
|
4868
4998
|
for (const declaration of declarations.declarations) {
|
|
4869
|
-
if (
|
|
4999
|
+
if (ts7.isObjectBindingPattern(declaration.name) && declaration.initializer != null) {
|
|
4870
5000
|
const source = lowerExpression(declaration.initializer, context);
|
|
4871
5001
|
for (const element of declaration.name.elements) {
|
|
4872
|
-
if (!
|
|
5002
|
+
if (!ts7.isIdentifier(element.name) || element.dotDotDotToken != null || element.initializer != null) {
|
|
4873
5003
|
throw unsupported(element, { kind: "variableDeclarationShape" });
|
|
4874
5004
|
}
|
|
4875
|
-
const property = element.propertyName == null ? element.name.text :
|
|
5005
|
+
const property = element.propertyName == null ? element.name.text : ts7.isIdentifier(element.propertyName) ? element.propertyName.text : null;
|
|
4876
5006
|
if (property == null)
|
|
4877
5007
|
throw unsupported(element, { kind: "variableDeclarationShape" });
|
|
4878
5008
|
const elementType = context.checker.getTypeAtLocation(element.name);
|
|
4879
|
-
const sourceType = context.checker.getTypeAtLocation(declaration.initializer);
|
|
4880
|
-
const propertySymbol = context.checker.getPropertyOfType(sourceType, property);
|
|
4881
|
-
if (propertySymbol != null && declaredOnlyInDeclarationFiles(propertySymbol)) {
|
|
4882
|
-
throw unsupported(element, { kind: "prototypeMemberRead", property });
|
|
4883
|
-
}
|
|
4884
5009
|
if (valueKind(elementType, context.checker) == null) {
|
|
4885
5010
|
throw unsupported(element, { kind: "valueType", typeText: context.checker.typeToString(elementType) });
|
|
4886
5011
|
}
|
|
@@ -4889,7 +5014,7 @@ function lowerVariableDeclarationList(declarations, context) {
|
|
|
4889
5014
|
}
|
|
4890
5015
|
continue;
|
|
4891
5016
|
}
|
|
4892
|
-
if (!
|
|
5017
|
+
if (!ts7.isIdentifier(declaration.name) || declaration.initializer == null) {
|
|
4893
5018
|
throw unsupported(declaration, { kind: "variableDeclarationShape" });
|
|
4894
5019
|
}
|
|
4895
5020
|
const value2 = lowerExpression(declaration.initializer, context);
|
|
@@ -4908,12 +5033,12 @@ function lowerVariableDeclarationList(declarations, context) {
|
|
|
4908
5033
|
function assignedSymbols(nodes, checker) {
|
|
4909
5034
|
const symbols = new Set;
|
|
4910
5035
|
const visit = (node) => {
|
|
4911
|
-
if (
|
|
5036
|
+
if (ts7.isFunctionLike(node))
|
|
4912
5037
|
return;
|
|
4913
5038
|
const assignment = identifierAssignment(node);
|
|
4914
5039
|
if (assignment != null)
|
|
4915
5040
|
symbols.add(requiredSymbol(assignment.target, checker));
|
|
4916
|
-
|
|
5041
|
+
ts7.forEachChild(node, visit);
|
|
4917
5042
|
};
|
|
4918
5043
|
for (const node of nodes)
|
|
4919
5044
|
visit(node);
|
|
@@ -4932,45 +5057,46 @@ function scanModuleBindings(sourceFile, checker) {
|
|
|
4932
5057
|
bindings.push({ name: name.text, category });
|
|
4933
5058
|
};
|
|
4934
5059
|
for (const statement of sourceFile.statements) {
|
|
4935
|
-
if (
|
|
4936
|
-
if ((statement.declarationList.flags & (
|
|
5060
|
+
if (ts8.isVariableStatement(statement)) {
|
|
5061
|
+
if ((statement.declarationList.flags & (ts8.NodeFlags.Let | ts8.NodeFlags.Const)) === 0)
|
|
4937
5062
|
continue;
|
|
5063
|
+
const isConst = (statement.declarationList.flags & ts8.NodeFlags.Const) !== 0;
|
|
4938
5064
|
for (const declarator of statement.declarationList.declarations) {
|
|
4939
|
-
if (
|
|
4940
|
-
register(declarator.name, declaredCategory(declarator.name, checker));
|
|
5065
|
+
if (ts8.isIdentifier(declarator.name)) {
|
|
5066
|
+
register(declarator.name, isConst && declarator.initializer != null && directFunctionExpression(declarator.initializer) != null ? { kind: "function" } : declaredCategory(declarator.name, checker));
|
|
4941
5067
|
continue;
|
|
4942
5068
|
}
|
|
4943
|
-
if (
|
|
5069
|
+
if (ts8.isObjectBindingPattern(declarator.name)) {
|
|
4944
5070
|
for (const element of declarator.name.elements) {
|
|
4945
|
-
if (
|
|
5071
|
+
if (ts8.isIdentifier(element.name))
|
|
4946
5072
|
register(element.name, declaredCategory(element.name, checker));
|
|
4947
5073
|
}
|
|
4948
5074
|
}
|
|
4949
5075
|
}
|
|
4950
5076
|
continue;
|
|
4951
5077
|
}
|
|
4952
|
-
if (
|
|
5078
|
+
if (ts8.isImportDeclaration(statement)) {
|
|
4953
5079
|
const clause = statement.importClause;
|
|
4954
5080
|
if (clause == null || clause.isTypeOnly)
|
|
4955
5081
|
continue;
|
|
4956
5082
|
if (clause.name != null)
|
|
4957
5083
|
register(clause.name, importedCategory(clause.name, checker));
|
|
4958
5084
|
const named = clause.namedBindings;
|
|
4959
|
-
if (named != null &&
|
|
5085
|
+
if (named != null && ts8.isNamedImports(named)) {
|
|
4960
5086
|
for (const element of named.elements) {
|
|
4961
5087
|
if (!element.isTypeOnly)
|
|
4962
5088
|
register(element.name, importedCategory(element.name, checker));
|
|
4963
5089
|
}
|
|
4964
5090
|
}
|
|
4965
|
-
if (named != null &&
|
|
5091
|
+
if (named != null && ts8.isNamespaceImport(named))
|
|
4966
5092
|
register(named.name, { kind: "import" });
|
|
4967
5093
|
}
|
|
4968
5094
|
}
|
|
4969
5095
|
const visit = (node, insideFunction) => {
|
|
4970
5096
|
if (insideFunction)
|
|
4971
5097
|
demoteModuleWritesInNode(node, checker, bindingsBySymbol, bindings);
|
|
4972
|
-
const enteringFunction = insideFunction ||
|
|
4973
|
-
|
|
5098
|
+
const enteringFunction = insideFunction || ts8.isFunctionLike(node);
|
|
5099
|
+
ts8.forEachChild(node, (child) => {
|
|
4974
5100
|
visit(child, enteringFunction);
|
|
4975
5101
|
});
|
|
4976
5102
|
};
|
|
@@ -4979,13 +5105,13 @@ function scanModuleBindings(sourceFile, checker) {
|
|
|
4979
5105
|
}
|
|
4980
5106
|
function importedCategory(name, checker) {
|
|
4981
5107
|
const symbol = checker.getSymbolAtLocation(name);
|
|
4982
|
-
if (symbol == null || (symbol.flags &
|
|
5108
|
+
if (symbol == null || (symbol.flags & ts8.SymbolFlags.Alias) === 0)
|
|
4983
5109
|
return { kind: "import" };
|
|
4984
5110
|
const target = checker.getAliasedSymbol(symbol);
|
|
4985
5111
|
const declaration = target.valueDeclaration;
|
|
4986
|
-
if (declaration == null || !
|
|
5112
|
+
if (declaration == null || !ts8.isVariableDeclaration(declaration))
|
|
4987
5113
|
return { kind: "import" };
|
|
4988
|
-
if ((
|
|
5114
|
+
if ((ts8.getCombinedNodeFlags(declaration) & ts8.NodeFlags.Const) === 0)
|
|
4989
5115
|
return { kind: "import" };
|
|
4990
5116
|
if (declaration.getSourceFile().isDeclarationFile)
|
|
4991
5117
|
return { kind: "import" };
|
|
@@ -5001,7 +5127,7 @@ function demoteModuleWritesInNode(node, checker, bindingsBySymbol, bindings, wri
|
|
|
5001
5127
|
written[binding] = true;
|
|
5002
5128
|
};
|
|
5003
5129
|
const target = (expression) => {
|
|
5004
|
-
if (
|
|
5130
|
+
if (ts8.isIdentifier(expression)) {
|
|
5005
5131
|
const symbol = checker.getSymbolAtLocation(expression);
|
|
5006
5132
|
const binding = symbol == null ? undefined : bindingsBySymbol.get(symbol);
|
|
5007
5133
|
if (binding != null)
|
|
@@ -5009,39 +5135,39 @@ function demoteModuleWritesInNode(node, checker, bindingsBySymbol, bindings, wri
|
|
|
5009
5135
|
return;
|
|
5010
5136
|
}
|
|
5011
5137
|
const visitPattern = (child) => {
|
|
5012
|
-
if (
|
|
5138
|
+
if (ts8.isShorthandPropertyAssignment(child)) {
|
|
5013
5139
|
const symbol = checker.getShorthandAssignmentValueSymbol(child);
|
|
5014
5140
|
const binding = symbol == null ? undefined : bindingsBySymbol.get(symbol);
|
|
5015
5141
|
if (binding != null)
|
|
5016
5142
|
record(binding);
|
|
5017
|
-
|
|
5143
|
+
ts8.forEachChild(child, visitPattern);
|
|
5018
5144
|
return;
|
|
5019
5145
|
}
|
|
5020
|
-
if (
|
|
5146
|
+
if (ts8.isIdentifier(child)) {
|
|
5021
5147
|
const symbol = checker.getSymbolAtLocation(child);
|
|
5022
5148
|
const binding = symbol == null ? undefined : bindingsBySymbol.get(symbol);
|
|
5023
5149
|
if (binding != null)
|
|
5024
5150
|
record(binding);
|
|
5025
5151
|
return;
|
|
5026
5152
|
}
|
|
5027
|
-
|
|
5153
|
+
ts8.forEachChild(child, visitPattern);
|
|
5028
5154
|
};
|
|
5029
|
-
|
|
5155
|
+
ts8.forEachChild(expression, visitPattern);
|
|
5030
5156
|
};
|
|
5031
|
-
if (
|
|
5157
|
+
if (ts8.isBinaryExpression(node)) {
|
|
5032
5158
|
const kind = node.operatorToken.kind;
|
|
5033
|
-
if (kind >=
|
|
5159
|
+
if (kind >= ts8.SyntaxKind.FirstAssignment && kind <= ts8.SyntaxKind.LastAssignment)
|
|
5034
5160
|
target(node.left);
|
|
5035
5161
|
}
|
|
5036
|
-
if ((
|
|
5162
|
+
if ((ts8.isPrefixUnaryExpression(node) || ts8.isPostfixUnaryExpression(node)) && (node.operator === ts8.SyntaxKind.PlusPlusToken || node.operator === ts8.SyntaxKind.MinusMinusToken) && ts8.isExpression(node.operand)) {
|
|
5037
5163
|
target(node.operand);
|
|
5038
5164
|
}
|
|
5039
|
-
if ((
|
|
5165
|
+
if ((ts8.isForOfStatement(node) || ts8.isForInStatement(node)) && ts8.isExpression(node.initializer)) {
|
|
5040
5166
|
target(node.initializer);
|
|
5041
5167
|
}
|
|
5042
5168
|
}
|
|
5043
|
-
function lowerModuleInitializer(sourceFile, checker, functionsBySymbol, scan, sites) {
|
|
5044
|
-
const context = createFunctionContext(sourceFile, checker, functionsBySymbol, scan.bindingsBySymbol, sites);
|
|
5169
|
+
function lowerModuleInitializer(sourceFile, checker, program, functionsBySymbol, scan, sites) {
|
|
5170
|
+
const context = createFunctionContext(sourceFile, checker, program, functionsBySymbol, scan.bindingsBySymbol, sites);
|
|
5045
5171
|
const skips = [];
|
|
5046
5172
|
const statements = sourceFile.statements;
|
|
5047
5173
|
for (const statement of statements) {
|
|
@@ -5049,8 +5175,8 @@ function lowerModuleInitializer(sourceFile, checker, functionsBySymbol, scan, si
|
|
|
5049
5175
|
continue;
|
|
5050
5176
|
const recovery = snapshotLowering(context);
|
|
5051
5177
|
try {
|
|
5052
|
-
assertAccepted(statement);
|
|
5053
|
-
if (
|
|
5178
|
+
assertAccepted(statement, true);
|
|
5179
|
+
if (ts8.isVariableStatement(statement)) {
|
|
5054
5180
|
lowerTopLevelDeclarations(statement, context, scan);
|
|
5055
5181
|
continue;
|
|
5056
5182
|
}
|
|
@@ -5088,31 +5214,31 @@ function lowerModuleInitializer(sourceFile, checker, functionsBySymbol, scan, si
|
|
|
5088
5214
|
};
|
|
5089
5215
|
}
|
|
5090
5216
|
function containsArrayLiteral(root) {
|
|
5091
|
-
if (
|
|
5217
|
+
if (ts8.isArrayLiteralExpression(root))
|
|
5092
5218
|
return true;
|
|
5093
5219
|
let found = false;
|
|
5094
|
-
|
|
5220
|
+
ts8.forEachChild(root, (child) => {
|
|
5095
5221
|
if (!found && containsArrayLiteral(child))
|
|
5096
5222
|
found = true;
|
|
5097
5223
|
});
|
|
5098
5224
|
return found;
|
|
5099
5225
|
}
|
|
5100
5226
|
function forEachImmediatelyEvaluatedChild(node, visit) {
|
|
5101
|
-
if (
|
|
5102
|
-
if (node.name != null &&
|
|
5227
|
+
if (ts8.isFunctionLike(node)) {
|
|
5228
|
+
if (node.name != null && ts8.isComputedPropertyName(node.name)) {
|
|
5103
5229
|
visit(node.name.expression);
|
|
5104
5230
|
}
|
|
5105
5231
|
return;
|
|
5106
5232
|
}
|
|
5107
|
-
|
|
5233
|
+
ts8.forEachChild(node, visit);
|
|
5108
5234
|
}
|
|
5109
5235
|
function lowerSupportedArgumentsOfSkippedTopLevelCall(statement, stop, context) {
|
|
5110
|
-
if (!
|
|
5236
|
+
if (!ts8.isExpressionStatement(statement))
|
|
5111
5237
|
return;
|
|
5112
5238
|
let expression = statement.expression;
|
|
5113
|
-
while (
|
|
5239
|
+
while (ts8.isParenthesizedExpression(expression))
|
|
5114
5240
|
expression = expression.expression;
|
|
5115
|
-
if (!
|
|
5241
|
+
if (!ts8.isCallExpression(expression) || expression !== stop.node || expression.questionDotToken != null || !plainCallTarget(expression.expression))
|
|
5116
5242
|
return;
|
|
5117
5243
|
for (const argument of expression.arguments) {
|
|
5118
5244
|
const recovery = snapshotLowering(context);
|
|
@@ -5127,23 +5253,23 @@ function lowerSupportedArgumentsOfSkippedTopLevelCall(statement, stop, context)
|
|
|
5127
5253
|
}
|
|
5128
5254
|
}
|
|
5129
5255
|
function plainCallTarget(expression) {
|
|
5130
|
-
if (
|
|
5256
|
+
if (ts8.isIdentifier(expression))
|
|
5131
5257
|
return true;
|
|
5132
|
-
if (
|
|
5258
|
+
if (ts8.isParenthesizedExpression(expression))
|
|
5133
5259
|
return plainCallTarget(expression.expression);
|
|
5134
|
-
return
|
|
5260
|
+
return ts8.isPropertyAccessExpression(expression) && expression.questionDotToken == null && plainCallTarget(expression.expression);
|
|
5135
5261
|
}
|
|
5136
5262
|
function lowerTopLevelDeclarations(statement, context, scan) {
|
|
5137
5263
|
for (const declarator of statement.declarationList.declarations) {
|
|
5138
5264
|
if (declarator.initializer == null)
|
|
5139
5265
|
throw new LoweringStop(declarator, { kind: "variableDeclarationShape" });
|
|
5140
|
-
if (
|
|
5266
|
+
if (ts8.isObjectBindingPattern(declarator.name)) {
|
|
5141
5267
|
const source = lowerExpression(declarator.initializer, context);
|
|
5142
5268
|
for (const element of declarator.name.elements) {
|
|
5143
|
-
if (!
|
|
5269
|
+
if (!ts8.isIdentifier(element.name) || element.dotDotDotToken != null || element.initializer != null) {
|
|
5144
5270
|
throw new LoweringStop(element, { kind: "variableDeclarationShape" });
|
|
5145
5271
|
}
|
|
5146
|
-
const property = element.propertyName == null ? element.name.text :
|
|
5272
|
+
const property = element.propertyName == null ? element.name.text : ts8.isIdentifier(element.propertyName) ? element.propertyName.text : null;
|
|
5147
5273
|
if (property == null)
|
|
5148
5274
|
throw new LoweringStop(element, { kind: "variableDeclarationShape" });
|
|
5149
5275
|
const elementType = context.checker.getTypeAtLocation(element.name);
|
|
@@ -5159,27 +5285,32 @@ function lowerTopLevelDeclarations(statement, context, scan) {
|
|
|
5159
5285
|
}
|
|
5160
5286
|
continue;
|
|
5161
5287
|
}
|
|
5162
|
-
if (!
|
|
5288
|
+
if (!ts8.isIdentifier(declarator.name)) {
|
|
5163
5289
|
throw new LoweringStop(declarator, { kind: "variableDeclarationShape" });
|
|
5164
5290
|
}
|
|
5165
5291
|
const symbol = context.checker.getSymbolAtLocation(declarator.name);
|
|
5166
5292
|
const binding = symbol == null ? undefined : scan.bindingsBySymbol.get(symbol);
|
|
5167
5293
|
if (binding == null)
|
|
5168
5294
|
throw new LoweringStop(declarator, { kind: "variableDeclarationShape" });
|
|
5295
|
+
if ((statement.declarationList.flags & ts8.NodeFlags.Const) !== 0 && directFunctionExpression(declarator.initializer) != null) {
|
|
5296
|
+
const marker = addInstruction(context, declarator.initializer, { kind: "opaqueConstant" });
|
|
5297
|
+
addInstruction(context, declarator, { kind: "moduleWrite", binding, value: marker });
|
|
5298
|
+
continue;
|
|
5299
|
+
}
|
|
5169
5300
|
const value2 = lowerExpression(declarator.initializer, context);
|
|
5170
5301
|
addInstruction(context, declarator, { kind: "moduleWrite", binding, value: value2 });
|
|
5171
5302
|
}
|
|
5172
5303
|
}
|
|
5173
5304
|
function skippedAtTopLevel(statement) {
|
|
5174
|
-
return
|
|
5305
|
+
return ts8.isFunctionDeclaration(statement) && statement.name != null || ts8.isImportDeclaration(statement) || ts8.isTypeAliasDeclaration(statement) || ts8.isInterfaceDeclaration(statement) || ts8.isExportDeclaration(statement);
|
|
5175
5306
|
}
|
|
5176
5307
|
function scanSkippedModuleEffects(root, checker, bindingsBySymbol, bindings) {
|
|
5177
5308
|
const directWrites = [];
|
|
5178
5309
|
let invokesUnknownCode = false;
|
|
5179
5310
|
const visit = (node) => {
|
|
5180
5311
|
demoteModuleWritesInNode(node, checker, bindingsBySymbol, bindings, directWrites);
|
|
5181
|
-
invokesUnknownCode ||=
|
|
5182
|
-
if (
|
|
5312
|
+
invokesUnknownCode ||= ts8.isCallExpression(node) || ts8.isNewExpression(node) || ts8.isTaggedTemplateExpression(node) || ts8.isAwaitExpression(node) || ts8.isYieldExpression(node) || ts8.isForOfStatement(node) || ts8.isSpreadElement(node) || ts8.isArrayBindingPattern(node) || ts8.isVariableDeclarationList(node) && (node.flags & ts8.NodeFlags.Using) !== 0 || ts8.isBinaryExpression(node) && (node.operatorToken.kind === ts8.SyntaxKind.InstanceOfKeyword || node.operatorToken.kind === ts8.SyntaxKind.EqualsToken && containsArrayLiteral(node.left)) || ts8.isJsxElement(node) || ts8.isJsxSelfClosingElement(node) || ts8.isJsxFragment(node) || ts8.isClassDeclaration(node) || ts8.isClassExpression(node);
|
|
5313
|
+
if (ts8.isVariableDeclaration(node) && ts8.isIdentifier(node.name)) {
|
|
5183
5314
|
const symbol = checker.getSymbolAtLocation(node.name);
|
|
5184
5315
|
const binding = symbol == null ? undefined : bindingsBySymbol.get(symbol);
|
|
5185
5316
|
if (binding != null) {
|
|
@@ -5201,7 +5332,7 @@ function declaredRecordProperties(type, checker, seen) {
|
|
|
5201
5332
|
return null;
|
|
5202
5333
|
const properties = [];
|
|
5203
5334
|
for (const property of checker.getPropertiesOfType(type)) {
|
|
5204
|
-
const optional = (property.flags &
|
|
5335
|
+
const optional = (property.flags & ts8.SymbolFlags.Optional) !== 0;
|
|
5205
5336
|
const walked = declaredKind(checker.getTypeOfSymbol(property), checker, [...seen, type]);
|
|
5206
5337
|
const opaqueLeaf = { kind: "opaque" };
|
|
5207
5338
|
const propertyDeclared = declaredOnlyInDeclarationFiles(property) ? opaqueLeaf : walked ?? opaqueLeaf;
|
|
@@ -5307,14 +5438,14 @@ function declaredKindUncached(type, checker, seen) {
|
|
|
5307
5438
|
}
|
|
5308
5439
|
if (inner == null)
|
|
5309
5440
|
return null;
|
|
5310
|
-
const admitsNull = type.types.some((member) => (member.flags &
|
|
5311
|
-
const admitsUndefined = type.types.some((member) => (member.flags &
|
|
5441
|
+
const admitsNull = type.types.some((member) => (member.flags & ts8.TypeFlags.Null) !== 0);
|
|
5442
|
+
const admitsUndefined = type.types.some((member) => (member.flags & ts8.TypeFlags.Undefined) !== 0);
|
|
5312
5443
|
return { kind: "nullish", inner, sentinels: admitsNull && admitsUndefined ? "both" : admitsNull ? "null" : "undefined" };
|
|
5313
5444
|
}
|
|
5314
5445
|
case "opaque":
|
|
5315
5446
|
return { kind: "opaque" };
|
|
5316
5447
|
case "array": {
|
|
5317
|
-
const element = checker.getIndexTypeOfType(type,
|
|
5448
|
+
const element = checker.getIndexTypeOfType(type, ts8.IndexKind.Number);
|
|
5318
5449
|
if (element == null)
|
|
5319
5450
|
return null;
|
|
5320
5451
|
const elementKind = declaredKind(element, checker, [...seen, type]);
|
|
@@ -5366,13 +5497,14 @@ function declaredKindUncached(type, checker, seen) {
|
|
|
5366
5497
|
}
|
|
5367
5498
|
function numericLiteralInterval(type) {
|
|
5368
5499
|
const members = type.isUnion() ? type.types : [type];
|
|
5369
|
-
if (!members.every((member) => (member.flags & ts6.TypeFlags.NumberLiteral) !== 0 && (member.flags & ts6.TypeFlags.EnumLiteral) === 0))
|
|
5370
|
-
return null;
|
|
5371
5500
|
let lower = Infinity;
|
|
5372
5501
|
let upper = -Infinity;
|
|
5373
5502
|
let integer = true;
|
|
5374
5503
|
for (const member of members) {
|
|
5375
|
-
const
|
|
5504
|
+
const number = numberConstituent(member);
|
|
5505
|
+
if (number == null || (number.flags & ts8.TypeFlags.NumberLiteral) === 0 || (number.flags & ts8.TypeFlags.EnumLiteral) !== 0)
|
|
5506
|
+
return null;
|
|
5507
|
+
const value2 = number.value;
|
|
5376
5508
|
if (!Number.isFinite(value2))
|
|
5377
5509
|
return "nonFinite";
|
|
5378
5510
|
lower = Math.min(lower, value2);
|
|
@@ -5419,7 +5551,7 @@ function joinScalarDeclaredKinds(members) {
|
|
|
5419
5551
|
function tupleHasOptionalOrRestPositions(type, checker) {
|
|
5420
5552
|
if (!checker.isTupleType(type))
|
|
5421
5553
|
return false;
|
|
5422
|
-
return type.target.elementFlags.some((flags) => (flags &
|
|
5554
|
+
return type.target.elementFlags.some((flags) => (flags & ts8.ElementFlags.Required) === 0);
|
|
5423
5555
|
}
|
|
5424
5556
|
function demote(bindings, binding) {
|
|
5425
5557
|
const category = bindings[binding].category;
|
|
@@ -5433,6 +5565,7 @@ function demote(bindings, binding) {
|
|
|
5433
5565
|
break;
|
|
5434
5566
|
}
|
|
5435
5567
|
case "kind":
|
|
5568
|
+
case "function":
|
|
5436
5569
|
case "import":
|
|
5437
5570
|
case "opaque":
|
|
5438
5571
|
break;
|
|
@@ -5440,31 +5573,31 @@ function demote(bindings, binding) {
|
|
|
5440
5573
|
}
|
|
5441
5574
|
|
|
5442
5575
|
// src/lower/static-intrinsics.ts
|
|
5443
|
-
import * as
|
|
5444
|
-
function scanStaticAnnotations(sourceFile,
|
|
5445
|
-
const ownerIndex = new Map(
|
|
5446
|
-
const callsByFunction =
|
|
5576
|
+
import * as ts9 from "typescript";
|
|
5577
|
+
function scanStaticAnnotations(sourceFile, functions, checker) {
|
|
5578
|
+
const ownerIndex = new Map(functions.map((unit, index) => [unit.declaration, index]));
|
|
5579
|
+
const callsByFunction = functions.map(() => []);
|
|
5447
5580
|
const outsideTopLevelFunctions = [];
|
|
5448
5581
|
const visit = (node) => {
|
|
5449
|
-
if (
|
|
5450
|
-
const owner =
|
|
5451
|
-
const index = owner
|
|
5582
|
+
if (ts9.isCallExpression(node) && isStaticIntent(node, checker)) {
|
|
5583
|
+
const owner = ts9.findAncestor(node, ts9.isFunctionLike);
|
|
5584
|
+
const index = owner == null ? undefined : ownerIndex.get(owner);
|
|
5452
5585
|
if (index == null)
|
|
5453
5586
|
outsideTopLevelFunctions.push(node);
|
|
5454
5587
|
else
|
|
5455
5588
|
callsByFunction[index].push(node);
|
|
5456
5589
|
}
|
|
5457
|
-
|
|
5590
|
+
ts9.forEachChild(node, visit);
|
|
5458
5591
|
};
|
|
5459
5592
|
visit(sourceFile);
|
|
5460
5593
|
return {
|
|
5461
|
-
functions:
|
|
5594
|
+
functions: functions.map((unit, index) => {
|
|
5462
5595
|
const calls = callsByFunction[index];
|
|
5463
5596
|
const callSet = new Set(calls);
|
|
5464
5597
|
const leading = new Set;
|
|
5465
|
-
if (declaration.body != null) {
|
|
5466
|
-
for (const statement of declaration.body.statements) {
|
|
5467
|
-
if (!
|
|
5598
|
+
if (unit.declaration.body != null && ts9.isBlock(unit.declaration.body)) {
|
|
5599
|
+
for (const statement of unit.declaration.body.statements) {
|
|
5600
|
+
if (!ts9.isExpressionStatement(statement) || !ts9.isCallExpression(statement.expression) || !callSet.has(statement.expression))
|
|
5468
5601
|
break;
|
|
5469
5602
|
leading.add(statement.expression);
|
|
5470
5603
|
}
|
|
@@ -5478,34 +5611,31 @@ function annotationForCall(call, role) {
|
|
|
5478
5611
|
if (call.arguments.length !== 1) {
|
|
5479
5612
|
return { kind: "invalid", call, role, node: call, problem: "argumentCount" };
|
|
5480
5613
|
}
|
|
5481
|
-
if (!
|
|
5614
|
+
if (!ts9.isExpressionStatement(call.parent) || call.parent.expression !== call) {
|
|
5482
5615
|
return { kind: "invalid", call, role, node: call, problem: "position" };
|
|
5483
5616
|
}
|
|
5484
5617
|
const callee = call.expression;
|
|
5485
|
-
if (call.questionDotToken != null ||
|
|
5618
|
+
if (call.questionDotToken != null || ts9.isPropertyAccessExpression(callee) && callee.questionDotToken != null) {
|
|
5486
5619
|
return { kind: "invalid", call, role, node: call, problem: "optionalCall" };
|
|
5487
5620
|
}
|
|
5488
5621
|
return { kind: "valid", call, role, condition: call.arguments[0] };
|
|
5489
5622
|
}
|
|
5490
5623
|
function isStaticIntent(call, checker) {
|
|
5491
|
-
if (!
|
|
5624
|
+
if (!ts9.isPropertyAccessExpression(call.expression))
|
|
5492
5625
|
return false;
|
|
5493
5626
|
const access = call.expression;
|
|
5494
|
-
if (!
|
|
5627
|
+
if (!ts9.isIdentifier(access.expression) || access.expression.text !== "console" || access.name.text !== "assert")
|
|
5495
5628
|
return false;
|
|
5496
5629
|
const resolved = checker.getSymbolAtLocation(access.expression);
|
|
5497
|
-
const globalConsole = checker.resolveName("console", undefined,
|
|
5630
|
+
const globalConsole = checker.resolveName("console", undefined, ts9.SymbolFlags.Value, false);
|
|
5498
5631
|
return resolved != null && resolved === globalConsole;
|
|
5499
5632
|
}
|
|
5500
5633
|
|
|
5501
5634
|
// src/lower/program.ts
|
|
5502
5635
|
function lowerSource(checked, baseDirectory = process.cwd()) {
|
|
5503
|
-
const { sourceFile,
|
|
5504
|
-
const
|
|
5505
|
-
|
|
5506
|
-
if (ts8.isFunctionDeclaration(statement) && statement.name != null)
|
|
5507
|
-
declarations.push(statement);
|
|
5508
|
-
}
|
|
5636
|
+
const { sourceFile, program } = checked;
|
|
5637
|
+
const checker = program.getTypeChecker();
|
|
5638
|
+
const declarations = topLevelFunctionUnits(sourceFile);
|
|
5509
5639
|
const staticScan = scanStaticAnnotations(sourceFile, declarations, checker);
|
|
5510
5640
|
const recordStaticAnnotationIssues = (sites2) => staticScan.outsideTopLevelFunctions.map((call) => {
|
|
5511
5641
|
sites2.push(nodeSpan(sourceFile, call));
|
|
@@ -5519,9 +5649,9 @@ function lowerSource(checked, baseDirectory = process.cwd()) {
|
|
|
5519
5649
|
baseDirectory,
|
|
5520
5650
|
lineStarts: [...sourceFile.getLineStarts()],
|
|
5521
5651
|
sites: sites2,
|
|
5522
|
-
functions: declarations.map((
|
|
5652
|
+
functions: declarations.map((unit, index) => ({
|
|
5523
5653
|
kind: "unsupported",
|
|
5524
|
-
name:
|
|
5654
|
+
name: unit.name.text,
|
|
5525
5655
|
hasStaticAnnotations: staticScan.functions[index].length > 0,
|
|
5526
5656
|
site: 0,
|
|
5527
5657
|
reason
|
|
@@ -5548,22 +5678,34 @@ function lowerSource(checked, baseDirectory = process.cwd()) {
|
|
|
5548
5678
|
return rejectFile(nodeSpan(sourceFile, evalNode), { kind: "evalInFile" });
|
|
5549
5679
|
}
|
|
5550
5680
|
const functionsBySymbol = new Map;
|
|
5681
|
+
const scan = scanModuleBindings(sourceFile, checker);
|
|
5682
|
+
const topLevelFunctions = [];
|
|
5551
5683
|
for (let index = 0;index < declarations.length; index++) {
|
|
5552
|
-
const
|
|
5553
|
-
const symbol = checker.getSymbolAtLocation(
|
|
5684
|
+
const unit = declarations[index];
|
|
5685
|
+
const symbol = checker.getSymbolAtLocation(unit.name);
|
|
5554
5686
|
if (symbol == null)
|
|
5555
|
-
throw new Error(`Function
|
|
5556
|
-
|
|
5687
|
+
throw new Error(`Function ${unit.name.text} has no TypeScript symbol`);
|
|
5688
|
+
const binding = scan.bindingsBySymbol.get(symbol);
|
|
5689
|
+
if (unit.initializer != null && binding == null) {
|
|
5690
|
+
throw new Error(`Const-bound function ${unit.name.text} has no module binding`);
|
|
5691
|
+
}
|
|
5692
|
+
const fn = {
|
|
5693
|
+
...unit,
|
|
5694
|
+
id: index,
|
|
5695
|
+
binding: unit.initializer == null ? null : binding,
|
|
5696
|
+
signature: callableSignature(unit, checker)
|
|
5697
|
+
};
|
|
5698
|
+
topLevelFunctions.push(fn);
|
|
5699
|
+
functionsBySymbol.set(symbol, fn);
|
|
5557
5700
|
}
|
|
5558
|
-
const scan = scanModuleBindings(sourceFile, checker);
|
|
5559
5701
|
const sites = [];
|
|
5560
5702
|
const staticAnnotationIssues = recordStaticAnnotationIssues(sites);
|
|
5561
5703
|
const functions = [];
|
|
5562
|
-
for (let index = 0;index <
|
|
5563
|
-
const declaration =
|
|
5704
|
+
for (let index = 0;index < topLevelFunctions.length; index++) {
|
|
5705
|
+
const declaration = topLevelFunctions[index];
|
|
5564
5706
|
const staticAnnotations = staticScan.functions[index];
|
|
5565
5707
|
try {
|
|
5566
|
-
functions.push(lowerFunction(declaration, staticAnnotations, sourceFile, checker, functionsBySymbol, scan, sites));
|
|
5708
|
+
functions.push(lowerFunction(declaration, staticAnnotations, sourceFile, checker, program, functionsBySymbol, scan, sites));
|
|
5567
5709
|
} catch (error) {
|
|
5568
5710
|
if (!(error instanceof LoweringStop))
|
|
5569
5711
|
throw error;
|
|
@@ -5577,7 +5719,7 @@ function lowerSource(checked, baseDirectory = process.cwd()) {
|
|
|
5577
5719
|
});
|
|
5578
5720
|
}
|
|
5579
5721
|
}
|
|
5580
|
-
const { initializer, skips } = lowerModuleInitializer(sourceFile, checker, functionsBySymbol, scan, sites);
|
|
5722
|
+
const { initializer, skips } = lowerModuleInitializer(sourceFile, checker, program, functionsBySymbol, scan, sites);
|
|
5581
5723
|
return {
|
|
5582
5724
|
file: sourceFile.fileName,
|
|
5583
5725
|
baseDirectory,
|
|
@@ -5590,7 +5732,8 @@ function lowerSource(checked, baseDirectory = process.cwd()) {
|
|
|
5590
5732
|
initializerSkips: skips
|
|
5591
5733
|
};
|
|
5592
5734
|
}
|
|
5593
|
-
function lowerFunction(
|
|
5735
|
+
function lowerFunction(unit, staticAnnotations, sourceFile, checker, program, functionsBySymbol, scan, sites) {
|
|
5736
|
+
const { declaration } = unit;
|
|
5594
5737
|
for (const annotation of staticAnnotations) {
|
|
5595
5738
|
if (annotation.kind === "invalid") {
|
|
5596
5739
|
throw unsupported(annotation.node, { kind: "staticAssertionForm", problem: annotation.problem });
|
|
@@ -5598,24 +5741,28 @@ function lowerFunction(declaration, staticAnnotations, sourceFile, checker, func
|
|
|
5598
5741
|
}
|
|
5599
5742
|
if (declaration.body == null)
|
|
5600
5743
|
throw unsupported(declaration, { kind: "functionWithoutBody" });
|
|
5601
|
-
if (declaration.asteriskToken != null || declaration.modifiers?.some((modifier) => modifier.kind ===
|
|
5744
|
+
if (!ts10.isArrowFunction(declaration) && declaration.asteriskToken != null || declaration.modifiers?.some((modifier) => modifier.kind === ts10.SyntaxKind.AsyncKeyword) === true) {
|
|
5602
5745
|
throw unsupported(declaration, { kind: "asyncOrGeneratorFunction" });
|
|
5603
5746
|
}
|
|
5604
5747
|
assertAccepted(declaration);
|
|
5605
|
-
const signature =
|
|
5606
|
-
if (signature
|
|
5607
|
-
throw unsupported(
|
|
5748
|
+
const signature = unit.signature;
|
|
5749
|
+
if (signature == null) {
|
|
5750
|
+
throw unsupported(unit.name, {
|
|
5751
|
+
kind: unit.initializer == null ? "functionWithoutSignature" : "constFunctionSignature"
|
|
5752
|
+
});
|
|
5608
5753
|
}
|
|
5609
|
-
const returnType =
|
|
5610
|
-
const returnsVoid = (returnType.flags & (
|
|
5754
|
+
const returnType = checker.getReturnTypeOfSignature(signature);
|
|
5755
|
+
const returnsVoid = (returnType.flags & (ts10.TypeFlags.Void | ts10.TypeFlags.Undefined | ts10.TypeFlags.Never)) !== 0;
|
|
5611
5756
|
if (!returnsVoid && valueKind(returnType, checker) == null) {
|
|
5612
5757
|
throw unsupported(declaration.type ?? declaration, { kind: "valueType", typeText: checker.typeToString(returnType) });
|
|
5613
5758
|
}
|
|
5614
|
-
const context = createFunctionContext(sourceFile, checker, functionsBySymbol, scan.bindingsBySymbol, sites, staticAnnotations);
|
|
5759
|
+
const context = createFunctionContext(sourceFile, checker, program, functionsBySymbol, scan.bindingsBySymbol, sites, staticAnnotations, returnsVoid);
|
|
5615
5760
|
const entry = context.currentBlock;
|
|
5616
|
-
for (
|
|
5617
|
-
|
|
5618
|
-
|
|
5761
|
+
for (let parameterIndex = 0;parameterIndex < declaration.parameters.length; parameterIndex++) {
|
|
5762
|
+
const parameter = declaration.parameters[parameterIndex];
|
|
5763
|
+
const parameterType = unit.initializer == null ? checker.getTypeAtLocation(parameter) : checker.getTypeOfSymbolAtLocation(signature.parameters[parameterIndex], signature.declaration);
|
|
5764
|
+
if (ts10.isObjectBindingPattern(parameter.name)) {
|
|
5765
|
+
const type2 = lowerParameterType(parameter, parameterType, checker);
|
|
5619
5766
|
const patternName = parameter.name.getText(sourceFile).replace(/\s+/g, " ");
|
|
5620
5767
|
if (parameter.initializer != null) {
|
|
5621
5768
|
throw unsupported(parameter, { kind: "parameterDefaultValue", name: patternName });
|
|
@@ -5624,10 +5771,10 @@ function lowerFunction(declaration, staticAnnotations, sourceFile, checker, func
|
|
|
5624
5771
|
const bindings = [];
|
|
5625
5772
|
context.parameters.push({ value: value3, name: patternName, type: type2, site: addSite(context, parameter), bindings });
|
|
5626
5773
|
for (const element of parameter.name.elements) {
|
|
5627
|
-
if (!
|
|
5774
|
+
if (!ts10.isIdentifier(element.name) || element.dotDotDotToken != null || element.initializer != null) {
|
|
5628
5775
|
throw unsupported(element, { kind: "destructuredParameter" });
|
|
5629
5776
|
}
|
|
5630
|
-
const property = element.propertyName == null ? element.name.text :
|
|
5777
|
+
const property = element.propertyName == null ? element.name.text : ts10.isIdentifier(element.propertyName) ? element.propertyName.text : null;
|
|
5631
5778
|
if (property == null)
|
|
5632
5779
|
throw unsupported(element, { kind: "destructuredParameter" });
|
|
5633
5780
|
bindings.push({ property, local: element.name.text });
|
|
@@ -5643,12 +5790,12 @@ function lowerFunction(declaration, staticAnnotations, sourceFile, checker, func
|
|
|
5643
5790
|
}
|
|
5644
5791
|
continue;
|
|
5645
5792
|
}
|
|
5646
|
-
if (!
|
|
5793
|
+
if (!ts10.isIdentifier(parameter.name))
|
|
5647
5794
|
throw unsupported(parameter.name, { kind: "destructuredParameter" });
|
|
5648
5795
|
if (parameter.dotDotDotToken != null) {
|
|
5649
5796
|
throw unsupported(parameter, { kind: "parameterType", typeText: `...${checker.typeToString(checker.getTypeAtLocation(parameter))}`, optionalOrRestTuple: false });
|
|
5650
5797
|
}
|
|
5651
|
-
let type = lowerParameterType(parameter, checker);
|
|
5798
|
+
let type = lowerParameterType(parameter, parameterType, checker);
|
|
5652
5799
|
if (parameter.initializer != null) {
|
|
5653
5800
|
const default_ = parameterDefaultLiteral(parameter.initializer, checker);
|
|
5654
5801
|
if (default_ == null || !parameterDefaultFits(default_, type)) {
|
|
@@ -5661,7 +5808,16 @@ function lowerFunction(declaration, staticAnnotations, sourceFile, checker, func
|
|
|
5661
5808
|
context.parameters.push({ value: value2, name: parameter.name.text, type, site: addSite(context, parameter), bindings: null });
|
|
5662
5809
|
}
|
|
5663
5810
|
lowerFiniteInputRequirements(context);
|
|
5664
|
-
|
|
5811
|
+
if (ts10.isBlock(declaration.body)) {
|
|
5812
|
+
lowerStatements(declaration.body.statements, context);
|
|
5813
|
+
} else {
|
|
5814
|
+
const value2 = lowerExpression(declaration.body, context);
|
|
5815
|
+
terminate(context.currentBlock, {
|
|
5816
|
+
kind: "return",
|
|
5817
|
+
value: returnsVoid ? null : value2,
|
|
5818
|
+
site: addSite(context, declaration.body)
|
|
5819
|
+
});
|
|
5820
|
+
}
|
|
5665
5821
|
if (context.currentBlock.terminator == null) {
|
|
5666
5822
|
if (!returnsVoid) {
|
|
5667
5823
|
terminate(context.currentBlock, { kind: "stop", site: addSite(context, declaration), reason: { kind: "missingReturn" } });
|
|
@@ -5671,12 +5827,12 @@ function lowerFunction(declaration, staticAnnotations, sourceFile, checker, func
|
|
|
5671
5827
|
}
|
|
5672
5828
|
return {
|
|
5673
5829
|
kind: "lowered",
|
|
5674
|
-
name:
|
|
5830
|
+
name: unit.name.text,
|
|
5675
5831
|
assertions: context.assertions,
|
|
5676
5832
|
parameters: context.parameters,
|
|
5677
5833
|
returnPropertyNames: declaredRecordReturnNames(returnType, checker),
|
|
5678
5834
|
entry: 0,
|
|
5679
|
-
blocks: sealBlocks(context.blocks,
|
|
5835
|
+
blocks: sealBlocks(context.blocks, unit.name.text)
|
|
5680
5836
|
};
|
|
5681
5837
|
}
|
|
5682
5838
|
function lowerFiniteInputRequirements(context) {
|
|
@@ -5710,7 +5866,7 @@ function declaredRecordReturnNames(returnType, checker) {
|
|
|
5710
5866
|
if (kind === "object")
|
|
5711
5867
|
return checker.getPropertiesOfType(returnType).map((property) => property.name);
|
|
5712
5868
|
if (kind === "nullable" && returnType.isUnion()) {
|
|
5713
|
-
const missing =
|
|
5869
|
+
const missing = ts10.TypeFlags.Null | ts10.TypeFlags.Undefined;
|
|
5714
5870
|
const members = returnType.types.filter((member) => (member.flags & missing) === 0);
|
|
5715
5871
|
if (members.length > 0 && members.every((member) => valueKind(member, checker) === "object")) {
|
|
5716
5872
|
const names = new Set;
|
|
@@ -5723,8 +5879,7 @@ function declaredRecordReturnNames(returnType, checker) {
|
|
|
5723
5879
|
}
|
|
5724
5880
|
return null;
|
|
5725
5881
|
}
|
|
5726
|
-
function lowerParameterType(parameter, checker) {
|
|
5727
|
-
const type = checker.getTypeAtLocation(parameter);
|
|
5882
|
+
function lowerParameterType(parameter, type, checker) {
|
|
5728
5883
|
const declared = declaredKind(type, checker, []);
|
|
5729
5884
|
if (declared == null) {
|
|
5730
5885
|
throw unsupported(parameter, {
|
|
@@ -5735,12 +5890,6 @@ function lowerParameterType(parameter, checker) {
|
|
|
5735
5890
|
}
|
|
5736
5891
|
return declared;
|
|
5737
5892
|
}
|
|
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
5893
|
|
|
5745
5894
|
// src/analyze.ts
|
|
5746
5895
|
function analyzeCheckedSource(checked, baseDirectory) {
|
|
@@ -6505,6 +6654,7 @@ function formatStop(stop, program, analysis) {
|
|
|
6505
6654
|
return `reads ${binding.name}, whose value the analysis does not track (read at ${formatSite(program, stop.site)})`;
|
|
6506
6655
|
case "value":
|
|
6507
6656
|
case "kind":
|
|
6657
|
+
case "function":
|
|
6508
6658
|
return `reads ${binding.name} before it is initialized (read at ${formatSite(program, stop.site)})`;
|
|
6509
6659
|
}
|
|
6510
6660
|
}
|
|
@@ -6563,6 +6713,8 @@ function formatUnsupportedReason(reason) {
|
|
|
6563
6713
|
return "node without a TypeScript symbol";
|
|
6564
6714
|
case "functionWithoutSignature":
|
|
6565
6715
|
return "function without a TypeScript signature";
|
|
6716
|
+
case "constFunctionSignature":
|
|
6717
|
+
return "a top-level const function whose declared call shape differs from its implementation";
|
|
6566
6718
|
case "functionWithoutBody":
|
|
6567
6719
|
return "function declarations need bodies";
|
|
6568
6720
|
case "destructuredParameter":
|
|
@@ -6581,14 +6733,10 @@ function formatUnsupportedReason(reason) {
|
|
|
6581
6733
|
return "object spread (list every field explicitly, e.g. {gain: config.gain})";
|
|
6582
6734
|
case "asyncOrGeneratorFunction":
|
|
6583
6735
|
return "an async or generator function (the runtime result is a Promise or iterator, not the body's return value)";
|
|
6584
|
-
case "typePredicate":
|
|
6585
|
-
return "a type predicate (the checker takes the predicate on faith; return a plain boolean and check properties where they are read)";
|
|
6586
6736
|
case "protoProperty":
|
|
6587
6737
|
return "a property named __proto__ (prototype-setting syntax at runtime, not a data property)";
|
|
6588
6738
|
case "enumMemberRead":
|
|
6589
6739
|
return "an enum member read (replace the enum with plain module consts, e.g. const directionUp = 1)";
|
|
6590
|
-
case "prototypeMemberRead":
|
|
6591
|
-
return `read of the inherited prototype member ${reason.property} (records carry only their own data properties)`;
|
|
6592
6740
|
case "binaryOperator":
|
|
6593
6741
|
return reason.operator === "in" ? "the `in` operator (use a distinct string or boolean tag when property presence distinguishes union variants)" : `binary operator ${reason.operator} (supported: + - * / %, comparisons, and boolean && || !)`;
|
|
6594
6742
|
case "call":
|
|
@@ -6788,7 +6936,7 @@ function formatNumber(value2) {
|
|
|
6788
6936
|
}
|
|
6789
6937
|
|
|
6790
6938
|
// src/typescript/diagnostics.ts
|
|
6791
|
-
import * as
|
|
6939
|
+
import * as ts11 from "typescript";
|
|
6792
6940
|
|
|
6793
6941
|
class TypeScriptDiagnosticsError extends Error {
|
|
6794
6942
|
diagnostics;
|
|
@@ -6805,10 +6953,10 @@ class TypeScriptDiagnosticsError extends Error {
|
|
|
6805
6953
|
function formatTypeScriptDiagnostics(diagnostics, options, currentDirectory) {
|
|
6806
6954
|
const host = {
|
|
6807
6955
|
getCurrentDirectory: () => currentDirectory,
|
|
6808
|
-
getCanonicalFileName:
|
|
6809
|
-
getNewLine: () =>
|
|
6956
|
+
getCanonicalFileName: ts11.sys.useCaseSensitiveFileNames ? (file) => file : (file) => file.toLowerCase(),
|
|
6957
|
+
getNewLine: () => ts11.sys.newLine
|
|
6810
6958
|
};
|
|
6811
|
-
return usePrettyOutput(options["pretty"]) ?
|
|
6959
|
+
return usePrettyOutput(options["pretty"]) ? ts11.formatDiagnosticsWithColorAndContext(diagnostics, host) : ts11.formatDiagnostics(diagnostics, host);
|
|
6812
6960
|
}
|
|
6813
6961
|
function usePrettyOutput(configured) {
|
|
6814
6962
|
if (typeof configured === "boolean")
|
|
@@ -6819,7 +6967,7 @@ function usePrettyOutput(configured) {
|
|
|
6819
6967
|
const forceColor = process.env["FORCE_COLOR"];
|
|
6820
6968
|
if (forceColor != null && forceColor !== "")
|
|
6821
6969
|
return true;
|
|
6822
|
-
return
|
|
6970
|
+
return ts11.sys.writeOutputIsTTY?.() === true;
|
|
6823
6971
|
}
|
|
6824
6972
|
function color(code, text) {
|
|
6825
6973
|
return `\x1B[${code}m${text}\x1B[0m`;
|
|
@@ -7046,7 +7194,7 @@ function createFileAudit({ program, analysis }) {
|
|
|
7046
7194
|
};
|
|
7047
7195
|
}
|
|
7048
7196
|
function formatAuditCoverage(coverage) {
|
|
7049
|
-
const parts = coverage.functions === 0 ? ["no named
|
|
7197
|
+
const parts = coverage.functions === 0 ? ["no named top-level functions"] : [`${coverage.analyzed}/${coverage.functions} functions fully analyzed`];
|
|
7050
7198
|
if (coverage.partial > 0)
|
|
7051
7199
|
parts.push(`${coverage.partial} partially supported`);
|
|
7052
7200
|
if (coverage.unsupported > 0)
|
|
@@ -7141,6 +7289,7 @@ function guidesForUnsupportedReason(reason) {
|
|
|
7141
7289
|
case "unknownIdentifier":
|
|
7142
7290
|
case "missingSymbol":
|
|
7143
7291
|
case "functionWithoutSignature":
|
|
7292
|
+
case "constFunctionSignature":
|
|
7144
7293
|
case "functionWithoutBody":
|
|
7145
7294
|
case "destructuredParameter":
|
|
7146
7295
|
case "parameterType":
|
|
@@ -7150,10 +7299,8 @@ function guidesForUnsupportedReason(reason) {
|
|
|
7150
7299
|
case "computedPropertyName":
|
|
7151
7300
|
case "objectSpread":
|
|
7152
7301
|
case "asyncOrGeneratorFunction":
|
|
7153
|
-
case "typePredicate":
|
|
7154
7302
|
case "protoProperty":
|
|
7155
7303
|
case "enumMemberRead":
|
|
7156
|
-
case "prototypeMemberRead":
|
|
7157
7304
|
case "binaryOperator":
|
|
7158
7305
|
case "callWithFewerArguments":
|
|
7159
7306
|
case "callWithMoreArguments":
|
|
@@ -7211,12 +7358,12 @@ function isCallerInput(expression) {
|
|
|
7211
7358
|
|
|
7212
7359
|
// src/typescript/check.ts
|
|
7213
7360
|
import { resolve } from "node:path";
|
|
7214
|
-
import * as
|
|
7361
|
+
import * as ts12 from "typescript";
|
|
7215
7362
|
var fallbackOptions = {
|
|
7216
|
-
target:
|
|
7217
|
-
module:
|
|
7218
|
-
moduleResolution:
|
|
7219
|
-
moduleDetection:
|
|
7363
|
+
target: ts12.ScriptTarget.ESNext,
|
|
7364
|
+
module: ts12.ModuleKind.ESNext,
|
|
7365
|
+
moduleResolution: ts12.ModuleResolutionKind.Bundler,
|
|
7366
|
+
moduleDetection: ts12.ModuleDetectionKind.Force,
|
|
7220
7367
|
strict: true,
|
|
7221
7368
|
noUncheckedIndexedAccess: true,
|
|
7222
7369
|
exactOptionalPropertyTypes: true,
|
|
@@ -7226,25 +7373,25 @@ var fallbackOptions = {
|
|
|
7226
7373
|
};
|
|
7227
7374
|
function checkFile(file) {
|
|
7228
7375
|
const absoluteFile = resolve(file);
|
|
7229
|
-
const program =
|
|
7376
|
+
const program = ts12.createProgram([absoluteFile], fallbackOptions);
|
|
7230
7377
|
return checkedSource(program, absoluteFile, fallbackOptions);
|
|
7231
7378
|
}
|
|
7232
7379
|
function checkedSource(program, file, options) {
|
|
7233
|
-
const diagnostics =
|
|
7380
|
+
const diagnostics = ts12.getPreEmitDiagnostics(program);
|
|
7234
7381
|
if (diagnostics.length > 0) {
|
|
7235
7382
|
throw new TypeScriptDiagnosticsError(diagnostics, options, process.cwd());
|
|
7236
7383
|
}
|
|
7237
7384
|
const sourceFile = program.getSourceFile(file);
|
|
7238
7385
|
if (sourceFile == null)
|
|
7239
7386
|
throw new Error(`TypeScript did not load ${file}`);
|
|
7240
|
-
return { sourceFile,
|
|
7387
|
+
return { sourceFile, program };
|
|
7241
7388
|
}
|
|
7242
7389
|
|
|
7243
7390
|
// src/typescript/project.ts
|
|
7244
7391
|
import { dirname, isAbsolute, relative as relative2, resolve as resolve2, sep } from "node:path";
|
|
7245
|
-
import * as
|
|
7392
|
+
import * as ts13 from "typescript";
|
|
7246
7393
|
function findTypeScriptConfig(searchFrom) {
|
|
7247
|
-
return
|
|
7394
|
+
return ts13.findConfigFile(resolve2(searchFrom), (file) => ts13.sys.fileExists(file), "tsconfig.json") ?? null;
|
|
7248
7395
|
}
|
|
7249
7396
|
function loadTypeScriptProjectGraph(configPath) {
|
|
7250
7397
|
const loaded = [];
|
|
@@ -7261,8 +7408,8 @@ function loadTypeScriptProjectGraph(configPath) {
|
|
|
7261
7408
|
const parsed = parseConfig(absoluteConfigPath);
|
|
7262
7409
|
requireStrictNullChecks(parsed.options, absoluteConfigPath);
|
|
7263
7410
|
for (const reference of parsed.projectReferences ?? [])
|
|
7264
|
-
load(
|
|
7265
|
-
const program =
|
|
7411
|
+
load(ts13.resolveProjectReferencePath(reference));
|
|
7412
|
+
const program = ts13.createProgram({
|
|
7266
7413
|
rootNames: parsed.fileNames,
|
|
7267
7414
|
options: parsed.options,
|
|
7268
7415
|
configFileParsingDiagnostics: parsed.errors,
|
|
@@ -7297,8 +7444,8 @@ function projectSources(projects) {
|
|
|
7297
7444
|
return [...sources.values()].sort((left, right) => left.sourceFile.fileName.localeCompare(right.sourceFile.fileName));
|
|
7298
7445
|
}
|
|
7299
7446
|
function parseConfig(configPath) {
|
|
7300
|
-
const parsed =
|
|
7301
|
-
...
|
|
7447
|
+
const parsed = ts13.getParsedCommandLineOfConfigFile(configPath, undefined, {
|
|
7448
|
+
...ts13.sys,
|
|
7302
7449
|
onUnRecoverableConfigFileDiagnostic: (diagnostic) => {
|
|
7303
7450
|
throw new TypeScriptDiagnosticsError([diagnostic], {}, dirname(configPath));
|
|
7304
7451
|
}
|
|
@@ -7359,7 +7506,7 @@ function analyzeProject(searchFrom) {
|
|
|
7359
7506
|
const projects = loadTypeScriptProjectGraph(configPath);
|
|
7360
7507
|
const rootProject = projects.at(-1);
|
|
7361
7508
|
const sources = projectSources(projects);
|
|
7362
|
-
const diagnostics = uniqueDiagnostics(projects.flatMap((project) =>
|
|
7509
|
+
const diagnostics = uniqueDiagnostics(projects.flatMap((project) => ts14.getPreEmitDiagnostics(project.program)));
|
|
7363
7510
|
requireNoTypeScriptErrors(diagnostics, rootProject.parsed.options);
|
|
7364
7511
|
const files = [];
|
|
7365
7512
|
let analyzed = 0;
|
|
@@ -7483,7 +7630,7 @@ function collectLintFindings({ program, analysis }) {
|
|
|
7483
7630
|
collectStops(analysis.initializer);
|
|
7484
7631
|
collectAssertions(analysis.initializer);
|
|
7485
7632
|
for (const issue of program.staticAnnotationIssues) {
|
|
7486
|
-
addError(issue.site, "console-assert", "console.assert is only supported inside a named top-level function
|
|
7633
|
+
addError(issue.site, "console-assert", "console.assert is only supported inside a named top-level function");
|
|
7487
7634
|
}
|
|
7488
7635
|
for (const fn of analysis.functions) {
|
|
7489
7636
|
collectStops(fn);
|
|
@@ -7580,7 +7727,7 @@ function formatLintPrefix(finding, rule, pretty) {
|
|
|
7580
7727
|
return formatDiagnosticPrefix(finding, lintLevel(finding), rule, pretty);
|
|
7581
7728
|
}
|
|
7582
7729
|
function formatCoverage(coverage) {
|
|
7583
|
-
return `coverage: ${coverage.analyzed}/${coverage.functions} named top-level
|
|
7730
|
+
return `coverage: ${coverage.analyzed}/${coverage.functions} named top-level functions fully analyzed; ${coverage.partial} partially supported; ${coverage.unsupported} unsupported.`;
|
|
7584
7731
|
}
|
|
7585
7732
|
function analyzeTargetFile(file) {
|
|
7586
7733
|
const absoluteFile = resolve3(file);
|
|
@@ -7596,7 +7743,7 @@ function analyzeTargetFile(file) {
|
|
|
7596
7743
|
if (source == null) {
|
|
7597
7744
|
throw new Error(`File is not part of the project resolved from ${configPath}: ${absoluteFile}`);
|
|
7598
7745
|
}
|
|
7599
|
-
const diagnostics =
|
|
7746
|
+
const diagnostics = ts14.getPreEmitDiagnostics(source.project.program, source.sourceFile);
|
|
7600
7747
|
requireNoTypeScriptErrors(diagnostics, rootProject.parsed.options);
|
|
7601
7748
|
return {
|
|
7602
7749
|
detailed: analyzeProjectSource(source, process.cwd()),
|
|
@@ -7605,7 +7752,7 @@ function analyzeTargetFile(file) {
|
|
|
7605
7752
|
}
|
|
7606
7753
|
function canonicalFilePath(file) {
|
|
7607
7754
|
const real = realpathSync.native(file);
|
|
7608
|
-
return
|
|
7755
|
+
return ts14.sys.useCaseSensitiveFileNames ? real : real.toLowerCase();
|
|
7609
7756
|
}
|
|
7610
7757
|
function analyzeFileAlone(absoluteFile) {
|
|
7611
7758
|
return {
|
|
@@ -7616,13 +7763,13 @@ function analyzeFileAlone(absoluteFile) {
|
|
|
7616
7763
|
function analyzeProjectSource(source, reportBaseDirectory) {
|
|
7617
7764
|
return analyzeCheckedSource({
|
|
7618
7765
|
sourceFile: source.sourceFile,
|
|
7619
|
-
|
|
7766
|
+
program: source.project.program
|
|
7620
7767
|
}, reportBaseDirectory);
|
|
7621
7768
|
}
|
|
7622
7769
|
function uniqueDiagnostics(diagnostics) {
|
|
7623
7770
|
const seen = new Set;
|
|
7624
7771
|
return diagnostics.filter((diagnostic) => {
|
|
7625
|
-
const message =
|
|
7772
|
+
const message = ts14.flattenDiagnosticMessageText(diagnostic.messageText, `
|
|
7626
7773
|
`);
|
|
7627
7774
|
const key = `${diagnostic.file?.fileName ?? ""}:${diagnostic.start ?? ""}:${diagnostic.length ?? ""}:${diagnostic.code}:${message}`;
|
|
7628
7775
|
if (seen.has(key))
|
|
@@ -7643,7 +7790,7 @@ function requireNoTypeScriptErrors(diagnostics, options) {
|
|
|
7643
7790
|
printTypeScriptDiagnostics(diagnostics, options, process.cwd());
|
|
7644
7791
|
}
|
|
7645
7792
|
function hasErrorDiagnostics(diagnostics) {
|
|
7646
|
-
return diagnostics.some((diagnostic) => diagnostic.category ===
|
|
7793
|
+
return diagnostics.some((diagnostic) => diagnostic.category === ts14.DiagnosticCategory.Error);
|
|
7647
7794
|
}
|
|
7648
7795
|
|
|
7649
7796
|
// fr.ts
|