async Task RunCallAsync(TableCall originalCall, MirrorTableCall referenceCall) { // TODO: All assertions should show what the call was. // XXX: We currently have no way to detect incorrect interleaving of // backend calls and AnnotateLastOutgoingCall here. Most incorrect // interleavings will cause an error on the TablesMachine, but some // may go undetected. // For now, we're not doing streaming queries at all, so we don't have // to worry about how to handle them. currentReferenceCall = referenceCall; object actualOutcome = await Catching <StorageException> .Task(originalCall(migratingTable)); // Verify that successfulBatchResult was correct if specified. // (Ideally, we'd also catch if it isn't specified when it should // be, but that's less of a risk as it will likely cause ETag // mismatches anyway.) if (successfulBatchResult != null) { var successfulBatchOutcome = new Outcome <object, StorageException>(successfulBatchResult); PSharpRuntime.Assert(BetterComparer.Instance.Equals(successfulBatchOutcome, actualOutcome), "{0} incorrect successfulBatchResult:\n{1}\nExpected:\n{2}\n", machineId, BetterComparer.ToString(successfulBatchOutcome), BetterComparer.ToString(actualOutcome)); } PSharpRuntime.Assert(currentReferenceOutcome != null, "The call completed without reporting a linearization point."); PSharpRuntime.Assert(BetterComparer.Instance.Equals(actualOutcome, currentReferenceOutcome), "{0} call outcome:\n{1}\nExpected:\n{2}\n", machineId, BetterComparer.ToString(actualOutcome), BetterComparer.ToString(currentReferenceOutcome)); // Reset fields currentReferenceCall = null; successfulBatchResult = null; currentReferenceOutcome = null; }
internal async Task RunQueryAtomicAsync(TableQuery <DynamicTableEntity> query) { // async/await pair needed to upcast the return value to object. TableCall originalCall = async table => await table.ExecuteQueryAtomicAsync(query); MirrorTableCall referenceCall = async referenceTable => await referenceTable.ExecuteQueryAtomicAsync(query); Console.WriteLine("{0} starting atomic query: {1}", machineId, query); await RunCallAsync(originalCall, referenceCall); }
internal async Task RunBatchAsync(TableBatchOperation batch) { TableBatchOperation batchCopy = ChainTableUtils.CopyBatch <DynamicTableEntity>(batch); TableCall originalCall = async table => await table.ExecuteBatchAsync(batch); MirrorTableCall referenceCall = async referenceTable => await referenceTable.ExecuteMirrorBatchAsync(batchCopy, successfulBatchResult); Console.WriteLine("{0} starting batch: {1}", machineId, BetterComparer.ToString(batch)); await RunCallAsync(originalCall, referenceCall); }
// This method does not log what the call is, since there's no way to // know what's inside the delegates. Use RunBatchAsync or RunQueryAtomicAsync. async Task RunCallAsync(TableCall originalCall, MirrorTableCall referenceCall) { // TODO: All assertions should show what the call was. // XXX: We currently have no way to detect incorrect interleaving of // backend calls and AnnotateLastOutgoingCall here. Most incorrect // interleavings will cause an error on the TablesMachine, but some // may go undetected. // - FIXME: A missing annotation will cause all machines to become blocked, and // P# considers that a success! To fix that, we need to enable liveness checking. currentReferenceCall = referenceCall; object actualOutcome = await Catching <StorageException> .RunAsync(() => originalCall(migratingTable)); // Verify that successfulBatchResult was correct if specified. // (Ideally, we'd also catch if it isn't specified when it should // be, but that's less of a risk as it will likely cause ETag // mismatches anyway.) if (successfulBatchResult != null) { var successfulBatchOutcome = new Outcome <object, StorageException>(successfulBatchResult); PSharpRuntime.Assert(BetterComparer.Instance.Equals(successfulBatchOutcome, actualOutcome), "{0} incorrect successfulBatchResult:\n{1}\nExpected:\n{2}\n", machineId, BetterComparer.ToString(successfulBatchOutcome), BetterComparer.ToString(actualOutcome)); } PSharpRuntime.Assert(currentReferenceOutcome != null, "{0}: The call completed without reporting a linearization point.", machineId); PSharpRuntime.Assert(BetterComparer.Instance.Equals(actualOutcome, currentReferenceOutcome), "{0} table call outcome is incorrect:\n{1}\nExpected:\n{2}\n", machineId, BetterComparer.ToString(actualOutcome), BetterComparer.ToString(currentReferenceOutcome)); Console.WriteLine("{0} table call outcome is correct:\n{1}", machineId, BetterComparer.ToString(actualOutcome)); // Reset fields currentReferenceCall = null; successfulBatchResult = null; currentReferenceOutcome = null; }
internal static IValue ParseOperand(string code, Program mainProg, IExecutable currentBlock, Parenthesis p, bool constLock) { code = code.Trim(); if (code.Length == 0) { //fct/tab call if (p.FunctionName != null) { if (constLock) { throw new ILAException("Erreur impossible de donner une valeur non constante"); } var call = new FunctionCall(); call.CalledFunction = null; call.Args = new List <IValue>(); foreach (var item in mainProg.Methods) { if (item.Name == p.FunctionName && item is Function f) { call.CalledFunction = f; break; } } if (call.CalledFunction == null) { throw new ILAException("Aucune fonction nommée '" + p.FunctionName + "' trouvée"); } foreach (var item in p.FctIndexes) { call.Args.Add(ParseParenthesis(item, mainProg, currentBlock, constLock)); } return(call); } else if (p.TabName != null) { if (constLock) { throw new ILAException("Erreur impossible de donner une valeur non constante"); } var call = new TableCall(); call.Table = null; call.DimensionsIndex = new List <IValue>(); foreach (var item in currentBlock.Declarations) { if (item is VariableDeclaration vd && vd.CreatedVariable.Name == p.TabName) { call.Table = vd.CreatedVariable; break; } } if (call.Table == null) { throw new ILAException("Aucune variable nommée '" + p.TabName + "' trouvée"); } foreach (var item in p.TabIndexes) { call.DimensionsIndex.Add(ParseParenthesis(item, mainProg, currentBlock, constLock)); } return(call); } else { return(null); } } int index = Max( code.LastIndexOf('='), code.LastIndexOf('☻'), code.LastIndexOf('♥'), code.LastIndexOf('♦'), code.LastIndexOf('<'), code.LastIndexOf('>') ); if (index == -1) { index = code.LastIndexOf('•'); if (index == -1) { index = code.LastIndexOf('◘'); if (index == -1) { index = Max( code.LastIndexOf('+'), code.LastIndexOf('-') ); if (index == -1) { index = Max( code.LastIndexOf('♣'), code.LastIndexOf('♠'), code.LastIndexOf('*'), code.LastIndexOf('/') ); if (index == -1) { //non <val>, ?####, variable, constant, enum, unary minus if (code.First() == '○') //non { var op = new Operator(); op.Left = null; op.OperatorType = Operator.Tag.NOT; op.Right = ParseOperand(code.Substring(1), mainProg, currentBlock, p, constLock); return(op); } else if (code.First() == '◙') //unary minus { var op = new Operator(); op.Left = null; op.OperatorType = Operator.Tag.MINUS; op.Right = ParseOperand(code.Substring(1), mainProg, currentBlock, p, constLock); return(op); } else if (code.First() == '?')//parenthesis group { int pNumber = int.Parse(code.Substring(1, 4)); return(ParseParenthesis(p.RecursiveParenthesis[pNumber], mainProg, currentBlock, constLock)); } else { //variable, constant, enum if (IsLetter(code.First())) { //variable, enum, bool constant string n = ""; int i = 0; while (IsLetterOrDigit(code[i])) { n += code[i++]; if (i == code.Length) { break; } } if (n == "vrai") { return new ConstantBool() { Value = true } } ; if (n == "faux") { return new ConstantBool() { Value = false } } ; //variable, enum foreach (var decl in mainProg.Declarations) { if (decl is TypeDeclaration td && td.CreatedType is EnumType en) { for (int j = 0; j < en.Values.Count; j++) { if (en.Values[j] == n) { //enum call return(new EnumCall() { Enum = en, Index = j }); } } } } //variable index = -1; { int opened = 0; for (int j = 0; j < code.Length; j++) { if ((code[j] == '[' || code[j] == '.') && opened == 0) { index = j; } if (code[j] == '[') { opened++; } if (code[j] == ']') { opened--; } } } if (index == -1) { //simple variable foreach (var decl in mainProg.Declarations) { if (decl is VariableDeclaration vd && vd.CreatedVariable.Name == n) { if (constLock && !vd.CreatedVariable.Constant) { throw new ILAException("Erreur impossible de donner une valeur non constante"); } return(vd.CreatedVariable); } } foreach (var decl in currentBlock.Declarations) { if (decl is VariableDeclaration vd && vd.CreatedVariable.Name == n) { if (constLock && !vd.CreatedVariable.Constant) { throw new ILAException("Erreur impossible de donner une valeur non constante"); } return(vd.CreatedVariable); } } if (currentBlock is Module m) { foreach (var par in m.Parameters) { if (par.ImportedVariable.Name == n) { return(par.ImportedVariable); } } } throw new ILAException("Aucune variable nommée '" + n + "' trouvée"); } else if (code[index] == '.') { //struct call if (constLock) { throw new ILAException("Erreur impossible de donner une valeur non constante"); } var left = code.Substring(0, index); var right = code.Substring(index + 1); var leftVar = ParseValue(left, mainProg, currentBlock, constLock) as Variable; return(new StructCall() { Constant = false, Name = right, Struct = leftVar }); throw new ILAException("Erreur : variable '" + n + "' introuvable dans cette portée"); } else { //table call if (constLock) { throw new ILAException("Erreur impossible de donner une valeur non constante"); } var left = code.Substring(0, index); var right = code.Substring(index + 1); var leftVar = ParseValue(left, mainProg, currentBlock, constLock) as Variable; var opened = 0; var args = new List <string>(); var currentArg = ""; int j = 0; while (right[j] != ']' || opened > 0) { if (right[j] == '[') { opened++; } if (right[j] == ']') { opened--; } if (opened == 0 && right[j] == ',') { args.Add(currentArg); currentArg = ""; } else { currentArg += right[j]; } j++; } args.Add(currentArg); return(new TableCall() { Constant = false, Table = leftVar, DimensionsIndex = args.Select(a => ParseValue(a, mainProg, currentBlock, constLock)).ToList() }); throw new ILAException("Erreur : variable '" + n + "' introuvable dans cette portée"); } } else { //constant if (char.IsDigit(code.First())) { if (code.Contains('.')) { //float try { return(new ConstantFloat() { Value = float.Parse(code, new CultureInfo("en")) }); } catch (Exception) { throw new ILAException("Erreur, format de nombre incorrect"); } } else { //int try { return(new ConstantInt() { Value = int.Parse(code, new CultureInfo("en")) }); } catch (Exception) { throw new ILAException("Erreur, format de nombre incorrect"); } } } else if (code.First() == '\'') { //char if (code[1] == '\\') { if (code[3] != '\'') { throw new ILAException("Erreur, format de caractere incorrect"); } return((code[2]) switch { '\'' => new ConstantChar() { Value = '\'' }, '"' => new ConstantChar() { Value = '"' }, '\\' => new ConstantChar() { Value = '\\' }, 'n' => new ConstantChar() { Value = '\n' }, 'r' => new ConstantChar() { Value = '\r' }, 't' => new ConstantChar() { Value = '\t' }, 'b' => new ConstantChar() { Value = '\b' }, 'f' => new ConstantChar() { Value = '\f' }, _ => throw new ILAException("Erreur : caractère échapé inconnu '\\" + code[2] + "'"), });