@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.maxLength !== null) {
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.max !== null) {
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.maxSize !== null) {
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
- console.log(`[INFO] [TLAGenerator] Symmetry reduction: 1 symmetry group with ${validSymmetryGroups[0].length} message types`);
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
- if (!fields.has(m[1]))
1228
- fields.set(m[1], TLAGenerator.inferFieldType(m[1]));
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
- const usedUnchanged = this.emitStateUpdates(validAssignments, allPreconditions);
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
- condition: "",
2087
- confidence: "high",
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
- const name = prop.getName();
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=5561EDE053F2874C64756E2164756E21
7719
+ //# debugId=616D5E99FD251E9364756E2164756E21