@fairfox/polly 0.33.0 → 0.35.0
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.
|
@@ -339,6 +339,9 @@ class TemporalTLAGenerator {
|
|
|
339
339
|
case "always":
|
|
340
340
|
lines.push(`${prop.name} == [](${prop.target})`);
|
|
341
341
|
break;
|
|
342
|
+
case "step-always":
|
|
343
|
+
lines.push(`${prop.name} == [][${prop.target}]_allVars`);
|
|
344
|
+
break;
|
|
342
345
|
case "until":
|
|
343
346
|
if (prop.trigger) {
|
|
344
347
|
lines.push(`${prop.name} == (${prop.trigger}) U (${prop.target})`);
|
|
@@ -651,6 +654,7 @@ var init_tla = __esm(() => {
|
|
|
651
654
|
if (this.temporalProperties.length > 0) {
|
|
652
655
|
this.addTemporalProperties();
|
|
653
656
|
}
|
|
657
|
+
this.line("=============================================================================");
|
|
654
658
|
return this.lines.join(`
|
|
655
659
|
`);
|
|
656
660
|
}
|
|
@@ -708,7 +712,7 @@ var init_tla = __esm(() => {
|
|
|
708
712
|
}
|
|
709
713
|
const tabValues = Array.from({ length: this.tabCount }, (_, i) => `Tab${i}`).join(", ");
|
|
710
714
|
lines.push(` Tabs = {${tabValues}}`);
|
|
711
|
-
} else if ("maxTabs" in messages && messages.maxTabs !== undefined) {
|
|
715
|
+
} else if ("maxTabs" in messages && messages.maxTabs !== undefined && messages.maxTabs !== null) {
|
|
712
716
|
const tabValues = Array.from({ length: messages.maxTabs + 1 }, (_, i) => i).join(", ");
|
|
713
717
|
lines.push(` Tabs = {${tabValues}}`);
|
|
714
718
|
} else if (hasProjectConstant) {
|
|
@@ -804,7 +808,7 @@ var init_tla = __esm(() => {
|
|
|
804
808
|
if (hasStateConstants) {
|
|
805
809
|
this.generateConstantDeclarations(config.state, false);
|
|
806
810
|
}
|
|
807
|
-
if (hasPerMessageBounds) {
|
|
811
|
+
if (hasPerMessageBounds && config.messages.perMessageBounds) {
|
|
808
812
|
for (const [msgType, _bound] of Object.entries(config.messages.perMessageBounds)) {
|
|
809
813
|
const constName = `MaxMessages_${msgType}`;
|
|
810
814
|
this.line(`,${constName}`);
|
|
@@ -831,15 +835,15 @@ var init_tla = __esm(() => {
|
|
|
831
835
|
}
|
|
832
836
|
addFieldConstants(field, fieldConfig, constName, first) {
|
|
833
837
|
let isFirst = first;
|
|
834
|
-
if ("maxLength" in fieldConfig && fieldConfig
|
|
838
|
+
if ("maxLength" in fieldConfig && fieldConfig["maxLength"] !== null) {
|
|
835
839
|
this.line(`${isFirst ? "" : ","}${constName}_MaxLength \\* Max length for ${field}`);
|
|
836
840
|
isFirst = false;
|
|
837
841
|
}
|
|
838
|
-
if ("max" in fieldConfig && fieldConfig
|
|
842
|
+
if ("max" in fieldConfig && fieldConfig["max"] !== null) {
|
|
839
843
|
this.line(`${isFirst ? "" : ","}${constName}_Max \\* Max value for ${field}`);
|
|
840
844
|
isFirst = false;
|
|
841
845
|
}
|
|
842
|
-
if ("maxSize" in fieldConfig && fieldConfig
|
|
846
|
+
if ("maxSize" in fieldConfig && fieldConfig["maxSize"] !== null) {
|
|
843
847
|
this.line(`${isFirst ? "" : ","}${constName}_MaxSize \\* Max size for ${field}`);
|
|
844
848
|
isFirst = false;
|
|
845
849
|
}
|
|
@@ -998,7 +1002,6 @@ var init_tla = __esm(() => {
|
|
|
998
1002
|
const messageTypeSet = validMessageTypes.map((t) => `"${t}"`).join(", ");
|
|
999
1003
|
this.line(`UserMessageTypes == {${messageTypeSet}}`);
|
|
1000
1004
|
this.line("");
|
|
1001
|
-
this.filteredMessageTypes = validMessageTypes;
|
|
1002
1005
|
if (config.messages.symmetry && config.messages.symmetry.length > 0) {
|
|
1003
1006
|
this.addSymmetrySets(config.messages.symmetry, validMessageTypes);
|
|
1004
1007
|
}
|
|
@@ -1025,6 +1028,8 @@ var init_tla = __esm(() => {
|
|
|
1025
1028
|
}
|
|
1026
1029
|
for (let i = 0;i < validSymmetryGroups.length; i++) {
|
|
1027
1030
|
const group = validSymmetryGroups[i];
|
|
1031
|
+
if (!group)
|
|
1032
|
+
continue;
|
|
1028
1033
|
const setName = `SymmetrySet${i + 1}`;
|
|
1029
1034
|
const setValues = group.map((t) => `"${t}"`).join(", ");
|
|
1030
1035
|
this.line(`${setName} == {${setValues}}`);
|
|
@@ -1038,7 +1043,8 @@ var init_tla = __esm(() => {
|
|
|
1038
1043
|
console.log(`[INFO] [TLAGenerator] Symmetry reduction: ${validSymmetryGroups.length} independent symmetry groups ` + `(${validSymmetryGroups.map((g) => g.length).join(", ")} message types)`);
|
|
1039
1044
|
} else {
|
|
1040
1045
|
this.line(`Symmetry == Permutations(SymmetrySet1)`);
|
|
1041
|
-
|
|
1046
|
+
const onlyGroup = validSymmetryGroups[0];
|
|
1047
|
+
console.log(`[INFO] [TLAGenerator] Symmetry reduction: 1 symmetry group with ${onlyGroup?.length ?? 0} message types`);
|
|
1042
1048
|
}
|
|
1043
1049
|
this.symmetrySets = ["Symmetry"];
|
|
1044
1050
|
this.line("");
|
|
@@ -1221,11 +1227,12 @@ var init_tla = __esm(() => {
|
|
|
1221
1227
|
addPayloadRefsFromHandler(handler, fields) {
|
|
1222
1228
|
for (const text of this.collectHandlerTexts(handler)) {
|
|
1223
1229
|
const numMatch = TLAGenerator.NUMERIC_PAYLOAD_PATTERN.exec(text);
|
|
1224
|
-
if (numMatch)
|
|
1230
|
+
if (numMatch?.[1])
|
|
1225
1231
|
fields.set(numMatch[1], "0..2");
|
|
1226
1232
|
for (const m of text.matchAll(TLAGenerator.PAYLOAD_REF_PATTERN)) {
|
|
1227
|
-
|
|
1228
|
-
|
|
1233
|
+
const name = m[1];
|
|
1234
|
+
if (name && !fields.has(name))
|
|
1235
|
+
fields.set(name, TLAGenerator.inferFieldType(name));
|
|
1229
1236
|
}
|
|
1230
1237
|
}
|
|
1231
1238
|
}
|
|
@@ -1404,12 +1411,36 @@ var init_tla = __esm(() => {
|
|
|
1404
1411
|
}
|
|
1405
1412
|
this.emitPreconditions(allPreconditions);
|
|
1406
1413
|
const validAssignments = this.processAssignments(allAssignments, config.state);
|
|
1407
|
-
|
|
1408
|
-
if (!usedUnchanged) {
|
|
1409
|
-
this.emitPostconditions(allPostconditions);
|
|
1410
|
-
}
|
|
1414
|
+
this.emitStateUpdates(validAssignments, allPreconditions);
|
|
1411
1415
|
this.indent--;
|
|
1412
1416
|
this.line("");
|
|
1417
|
+
this.recordPostconditionProperty(messageType, actionName, allPostconditions);
|
|
1418
|
+
}
|
|
1419
|
+
recordPostconditionProperty(messageType, actionName, postconditions) {
|
|
1420
|
+
if (postconditions.length === 0)
|
|
1421
|
+
return;
|
|
1422
|
+
const predicateClauses = postconditions.map((pc) => this.tsExpressionToTLA(pc.expression, true)).filter((p) => p && p.length > 0).map((p) => p.replace(/\[ctx\]/g, "[target]"));
|
|
1423
|
+
if (predicateClauses.length === 0)
|
|
1424
|
+
return;
|
|
1425
|
+
const conjunction = predicateClauses.length === 1 ? predicateClauses[0] : predicateClauses.map((c) => ` /\\ ${c}`).join(`
|
|
1426
|
+
`);
|
|
1427
|
+
const propertyName = `EnsuresAfter_${actionName}`;
|
|
1428
|
+
const messages = postconditions.map((pc) => pc.message).filter((m) => Boolean(m)).join("; ");
|
|
1429
|
+
const target = `
|
|
1430
|
+
\\A m \\in 1..Len(messages) :
|
|
1431
|
+
` + ` (messages[m].status = "pending"
|
|
1432
|
+
` + ` /\\ messages'[m].status = "delivered"
|
|
1433
|
+
` + ` /\\ messages[m].msgType = "${messageType}")
|
|
1434
|
+
` + ` => \\A target \\in messages[m].targets :
|
|
1435
|
+
` + ` (target \\in Contexts /\\ ports[target] = "connected")
|
|
1436
|
+
` + ` =>
|
|
1437
|
+
` + (predicateClauses.length === 1 ? ` ${predicateClauses[0]}` : conjunction);
|
|
1438
|
+
this.temporalProperties.push({
|
|
1439
|
+
name: propertyName,
|
|
1440
|
+
description: messages.length > 0 ? `ensures(...) for ${messageType}: ${messages}` : `ensures(...) for ${messageType}`,
|
|
1441
|
+
type: "step-always",
|
|
1442
|
+
target
|
|
1443
|
+
});
|
|
1413
1444
|
}
|
|
1414
1445
|
emitPreconditions(preconditions) {
|
|
1415
1446
|
for (const precondition of preconditions) {
|
|
@@ -1418,16 +1449,18 @@ var init_tla = __esm(() => {
|
|
|
1418
1449
|
this.line(`/\\ ${tlaExpr}${comment}`);
|
|
1419
1450
|
}
|
|
1420
1451
|
}
|
|
1421
|
-
emitPostconditions(postconditions) {
|
|
1422
|
-
for (const postcondition of postconditions) {
|
|
1423
|
-
const tlaExpr = this.tsExpressionToTLA(postcondition.expression, true);
|
|
1424
|
-
const message = postcondition.message ?? "ensures failed";
|
|
1425
|
-
const escapedMessage = message.replace(/"/g, "\\\"");
|
|
1426
|
-
this.line(`/\\ Assert(${tlaExpr}, "${escapedMessage}")`);
|
|
1427
|
-
}
|
|
1428
|
-
}
|
|
1429
1452
|
processAssignments(assignments, state) {
|
|
1430
|
-
return assignments.filter((a) => this.shouldIncludeAssignment(a, state)).map((a) => this.mapNullAssignment(a, state));
|
|
1453
|
+
return assignments.filter((a) => this.isFieldModeled(a.field, state)).filter((a) => this.shouldIncludeAssignment(a, state)).map((a) => this.mapNullAssignment(a, state));
|
|
1454
|
+
}
|
|
1455
|
+
isFieldModeled(field, state) {
|
|
1456
|
+
const sanitized = this.sanitizeFieldName(field);
|
|
1457
|
+
const fakeConfig = {
|
|
1458
|
+
state,
|
|
1459
|
+
messages: { maxInFlight: null },
|
|
1460
|
+
onBuild: "warn",
|
|
1461
|
+
onRelease: "warn"
|
|
1462
|
+
};
|
|
1463
|
+
return this.flattenStateConfig(fakeConfig).has(sanitized);
|
|
1431
1464
|
}
|
|
1432
1465
|
shouldIncludeAssignment(assignment, state) {
|
|
1433
1466
|
if (assignment.value !== null)
|
|
@@ -1452,7 +1485,7 @@ var init_tla = __esm(() => {
|
|
|
1452
1485
|
if ("nullable" in fieldConfig && fieldConfig.nullable)
|
|
1453
1486
|
return assignment;
|
|
1454
1487
|
if ("values" in fieldConfig && fieldConfig.values) {
|
|
1455
|
-
const nullValue = fieldConfig.values[fieldConfig.values.length - 1];
|
|
1488
|
+
const nullValue = fieldConfig.values[fieldConfig.values.length - 1] ?? null;
|
|
1456
1489
|
return { ...assignment, value: nullValue };
|
|
1457
1490
|
}
|
|
1458
1491
|
return assignment;
|
|
@@ -1663,7 +1696,6 @@ var init_tla = __esm(() => {
|
|
|
1663
1696
|
result = result.replace(/(\w+(?:\.\w+)*)\.includes\(([^)]+)\)/g, (_match, arrayRef, item) => {
|
|
1664
1697
|
return `${item.trim()} \\in ${arrayRef}`;
|
|
1665
1698
|
});
|
|
1666
|
-
const _indexMap = new Map;
|
|
1667
1699
|
result = result.replace(/(\w+(?:\.\w+)*)\[(\d+)\]|\]\[(\d+)\]/g, (_match, identPart, index1, index2) => {
|
|
1668
1700
|
if (identPart) {
|
|
1669
1701
|
const newIndex2 = Number.parseInt(index1, 10) + 1;
|
|
@@ -2060,7 +2092,6 @@ var init_tla = __esm(() => {
|
|
|
2060
2092
|
}
|
|
2061
2093
|
this.line("\\* State constraint to bound state space");
|
|
2062
2094
|
this.addStateConstraint(config, _analysis);
|
|
2063
|
-
this.line("=============================================================================");
|
|
2064
2095
|
}
|
|
2065
2096
|
addTemporalConstraints(constraints) {
|
|
2066
2097
|
this.line("\\* Tier 2: Temporal constraint invariants");
|
|
@@ -2068,6 +2099,8 @@ var init_tla = __esm(() => {
|
|
|
2068
2099
|
this.line("");
|
|
2069
2100
|
for (let i = 0;i < constraints.length; i++) {
|
|
2070
2101
|
const constraint = constraints[i];
|
|
2102
|
+
if (!constraint)
|
|
2103
|
+
continue;
|
|
2071
2104
|
const invName = `TemporalConstraint${i + 1}`;
|
|
2072
2105
|
if (constraint.description) {
|
|
2073
2106
|
this.line(`\\* ${constraint.description}`);
|
|
@@ -2083,9 +2116,8 @@ var init_tla = __esm(() => {
|
|
|
2083
2116
|
this.extractedInvariants.push({
|
|
2084
2117
|
name: invName,
|
|
2085
2118
|
description: constraint.description || `${constraint.before} must happen before ${constraint.after}`,
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
source: { file: "", line: 0, column: 0 }
|
|
2119
|
+
expression: `${constraint.before} happens-before ${constraint.after}`,
|
|
2120
|
+
location: { file: "", line: 0 }
|
|
2089
2121
|
});
|
|
2090
2122
|
}
|
|
2091
2123
|
}
|
|
@@ -2098,7 +2130,7 @@ var init_tla = __esm(() => {
|
|
|
2098
2130
|
this.indent++;
|
|
2099
2131
|
if (needsConjunction) {
|
|
2100
2132
|
this.line("/\\ Len(messages) <= MaxMessages");
|
|
2101
|
-
if (hasPerMessageBounds) {
|
|
2133
|
+
if (hasPerMessageBounds && config.messages.perMessageBounds) {
|
|
2102
2134
|
for (const [msgType, _bound] of Object.entries(config.messages.perMessageBounds)) {
|
|
2103
2135
|
const constName = `MaxMessages_${msgType}`;
|
|
2104
2136
|
this.line(`/\\ Cardinality({m \\in DOMAIN messages : messages[m].msgType = "${msgType}"}) <= ${constName}`);
|
|
@@ -2144,7 +2176,6 @@ var init_tla = __esm(() => {
|
|
|
2144
2176
|
this.line(propDef);
|
|
2145
2177
|
this.line("");
|
|
2146
2178
|
}
|
|
2147
|
-
this.line("=============================================================================");
|
|
2148
2179
|
}
|
|
2149
2180
|
fieldConfigToTLAType(_fieldPath, fieldConfig, _config) {
|
|
2150
2181
|
const typeResult = this.tryAbstractType(fieldConfig) || this.tryBooleanType(fieldConfig) || this.tryEnumType(fieldConfig) || this.tryArrayType(fieldConfig) || this.tryExplicitNumberType(fieldConfig) || this.tryNumberType(fieldConfig) || this.tryStringType(fieldConfig) || this.tryMapType(fieldConfig);
|
|
@@ -5231,15 +5262,7 @@ class HandlerExtractor {
|
|
|
5231
5262
|
}
|
|
5232
5263
|
extractPropertyAssignment(prop, assignments, signalName) {
|
|
5233
5264
|
if (Node2.isPropertyAssignment(prop)) {
|
|
5234
|
-
|
|
5235
|
-
const initializer = prop.getInitializer();
|
|
5236
|
-
if (!name || !initializer)
|
|
5237
|
-
return;
|
|
5238
|
-
const value = this.extractValue(initializer);
|
|
5239
|
-
if (value === undefined)
|
|
5240
|
-
return;
|
|
5241
|
-
const field = signalName ? `${signalName}_${name}` : name;
|
|
5242
|
-
assignments.push({ field, value });
|
|
5265
|
+
this.extractRegularPropertyAssignment(prop, assignments, signalName);
|
|
5243
5266
|
return;
|
|
5244
5267
|
}
|
|
5245
5268
|
if (Node2.isShorthandPropertyAssignment(prop)) {
|
|
@@ -5252,6 +5275,37 @@ class HandlerExtractor {
|
|
|
5252
5275
|
}
|
|
5253
5276
|
}
|
|
5254
5277
|
}
|
|
5278
|
+
extractRegularPropertyAssignment(prop, assignments, signalName) {
|
|
5279
|
+
if (!Node2.isPropertyAssignment(prop))
|
|
5280
|
+
return;
|
|
5281
|
+
const name = prop.getName();
|
|
5282
|
+
const initializer = prop.getInitializer();
|
|
5283
|
+
if (!name || !initializer)
|
|
5284
|
+
return;
|
|
5285
|
+
const field = signalName ? `${signalName}_${name}` : name;
|
|
5286
|
+
const value = this.extractValue(initializer);
|
|
5287
|
+
if (value !== undefined) {
|
|
5288
|
+
assignments.push({ field, value });
|
|
5289
|
+
return;
|
|
5290
|
+
}
|
|
5291
|
+
const paramName = this.extractPayloadPropertyParam(initializer);
|
|
5292
|
+
if (paramName !== null) {
|
|
5293
|
+
assignments.push({ field, value: `param:${paramName}` });
|
|
5294
|
+
}
|
|
5295
|
+
}
|
|
5296
|
+
extractPayloadPropertyParam(initializer) {
|
|
5297
|
+
if (!Node2.isPropertyAccessExpression(initializer))
|
|
5298
|
+
return null;
|
|
5299
|
+
const parts = this.getPropertyPath(initializer).split(".");
|
|
5300
|
+
if (parts.length !== 2)
|
|
5301
|
+
return null;
|
|
5302
|
+
const [paramName, fieldName] = parts;
|
|
5303
|
+
if (paramName === undefined || fieldName === undefined)
|
|
5304
|
+
return null;
|
|
5305
|
+
if (!this.currentFunctionParams.includes(paramName))
|
|
5306
|
+
return null;
|
|
5307
|
+
return fieldName;
|
|
5308
|
+
}
|
|
5255
5309
|
extractElementAccessAssignment(left, right, assignments) {
|
|
5256
5310
|
if (!Node2.isElementAccessExpression(left))
|
|
5257
5311
|
return;
|
|
@@ -6468,6 +6522,8 @@ class HandlerExtractor {
|
|
|
6468
6522
|
if (match) {
|
|
6469
6523
|
const signalName = match[1];
|
|
6470
6524
|
const fieldName = match[2];
|
|
6525
|
+
if (!signalName)
|
|
6526
|
+
return;
|
|
6471
6527
|
if (fieldName) {
|
|
6472
6528
|
signals.push(`${signalName}_${fieldName}`);
|
|
6473
6529
|
} else {
|
|
@@ -7660,4 +7716,4 @@ main().catch((error) => {
|
|
|
7660
7716
|
process.exit(1);
|
|
7661
7717
|
});
|
|
7662
7718
|
|
|
7663
|
-
//# debugId=
|
|
7719
|
+
//# debugId=616D5E99FD251E9364756E2164756E21
|