def test_if_simplified() -> None: assert_equal( If( [ (Variable("X"), Number(21)), (Variable("Y"), Add(Number(21), Number(21))), (Add(Number(21), Number(21)), Variable("Z")), ] ).simplified(), If([(Variable("X"), Number(21)), (Variable("Y"), Number(42)), (Number(42), Variable("Z"))]), ) assert If([(TRUE, Variable("X"))]).simplified() == Variable("X")
def test_expr_str() -> None: assert_equal( str( And( If([(Variable("X"), Number(1)), (Variable("Y"), Number(2))], Number(3)), Variable("A"), Or(Variable("B"), Variable("C")), Variable("D"), ) ), multilinestr( """(if X then 1 elsif Y then 2 else 3) and A and (B or C) and D""" ), ) assert_equal( str( ForAllOf( "X", Variable("Z"), If([(Variable("X"), Number(1)), (Variable("Y"), Number(2))], Number(3)), ) ), multilinestr( """(for all X of Z => (if X then 1 elsif Y then 2 else 3))""" ), )
def __prove_overlays(self) -> None: for f in (INITIAL, *self.__fields): for p, l in [(p, p[-1]) for p in self.__paths[f] if p]: if l.first != UNDEFINED and isinstance(l.first, First): path_expressions = And(*[self.__link_expression(l) for l in p]) overlaid = If( [(path_expressions, Equal(self.__target_last(l), Last(l.first.name)))], TRUE ) result = overlaid.forall() if result != ProofResult.sat: message = str(overlaid).replace("\n", "") raise ModelError( f'field "{f.name}" not congruent with overlaid field ' f'"{l.first.name}" in "{self.full_name}"' f" ({result}: {message})" )
def valid_path_to_next_field_condition( self, message: Message, field: Field, field_type: Type ) -> Sequence[Expr]: return [ If( [ ( l.condition, And( Equal( Call("Predecessor", [Name("Ctx"), Name(l.target.affixed_name)],), Name(field.affixed_name), ), Call("Valid_Next", [Name("Ctx"), Name(l.target.affixed_name)]) if l.target != FINAL else TRUE, ), ) ] ).simplified( { **{ Variable(field.name): Call("Convert", [Name("Value")]) if isinstance(field_type, Enumeration) and field_type.always_valid else Name("Value") }, **self.public_substitution(message), } ) for l in message.outgoing(field) if l.target != FINAL ]
def invalid_successors_invariant() -> Expr: return AndThen( *[ If( [ ( AndThen( *[ Call( "Invalid", [Indexed(Variable("Cursors"), Variable(p.affixed_name))], ) for p in message.direct_predecessors(f) ] ), Call( "Invalid", [Indexed(Variable("Cursors"), Variable(f.affixed_name))], ), ) ] ) for f in message.fields if f not in message.direct_successors(INITIAL) ] )
def test_if_simplified(self) -> None: self.assertEqual( If( [ (Variable("X"), Number(21)), (Variable("Y"), Add(Number(21), Number(21))), (Add(Number(21), Number(21)), Variable("Z")), ] ).simplified(), If( [ (Variable("X"), Number(21)), (Variable("Y"), Number(42)), (Number(42), Variable("Z")), ] ), ) self.assertEqual(If([(TRUE, Variable("X"))]).simplified(), Variable("X"))
def test_if_expr_substituted() -> None: assert_equal( If( [ (Equal(Variable("X"), Number(42)), Number(21)), (Variable("Y"), Number(42)), (Number(42), Variable("Z")), ] ).substituted(lambda x: Variable(f"P_{x}") if isinstance(x, Variable) else x), If( [ (Equal(Variable("P_X"), Number(42)), Number(21)), (Variable("P_Y"), Number(42)), (Number(42), Variable("P_Z")), ] ), ) assert_equal( If( [ (Equal(Variable("X"), Number(42)), Number(21)), (Variable("Y"), Number(42)), (Number(42), Variable("Z")), ] ).substituted( lambda x: Variable(f"P_{x}") if isinstance(x, Variable) else ( If([*x.condition_expressions, (Variable("Z"), Number(1))], x.else_expression) if isinstance(x, If) else x ) ), If( [ (Equal(Variable("P_X"), Number(42)), Number(21)), (Variable("P_Y"), Number(42)), (Number(42), Variable("P_Z")), (Variable("P_Z"), Number(1)), ] ), )
def test_if_variables(self) -> None: self.assertEqual( If( [ (Variable("X"), Number(21)), (Variable("Y"), Add(Number(21), Number(21))), (Add(Number(21), Number(21)), Variable("Z")), ] ).variables(), [Variable("X"), Variable("Y"), Variable("Z")], )
def test_if_expr_findall() -> None: assert_equal( If( [ (Equal(Variable("X"), Number(42)), Number(21)), (Variable("Y"), Number(42)), (Number(42), Variable("Z")), ] ).findall(lambda x: isinstance(x, Number)), [Number(42), Number(21), Number(42), Number(42)], )
def create_valid_function() -> UnitPart: specification = FunctionSpecification( "Valid", "Boolean", [Parameter(["Ctx"], "Context"), Parameter(["Fld"], "Field")]) return UnitPart( [ SubprogramDeclaration( specification, [ Postcondition( If([( Result("Valid"), And( Call( "Structural_Valid", [Variable("Ctx"), Variable("Fld")], ), Call("Present", [Variable("Ctx"), Variable("Fld")]), ), )])), ], ) ], [ ExpressionFunctionDeclaration( specification, AndThen( Equal( Selected( Indexed(Variable("Ctx.Cursors"), Variable("Fld")), "State"), Variable("S_Valid"), ), Less( Selected( Indexed(Variable("Ctx.Cursors"), Variable("Fld")), "First"), Add( Selected( Indexed(Variable("Ctx.Cursors"), Variable("Fld")), "Last"), Number(1), ), ), ), ) ], )
def __prove_field_positions(self) -> None: for f in self.__fields: for p, l in [(p, p[-1]) for p in self.__paths[f] if p]: path_expressions = And(*[self.__link_expression(l) for l in p]) length = self.__target_length(l) positive = If( [ ( And( self.__type_constraints(And(path_expressions, length)), path_expressions, ), GreaterEqual(length, Number(0)), ) ], TRUE, ) result = positive.forall() if result != ProofResult.sat: path_message = " -> ".join([l.target.name for l in p]) message = str(positive.simplified()).replace("\n\t", "") raise ModelError( f'negative length for field "{f.name}" on path {path_message}' f' in "{self.full_name}" ({result}: {message})' ) first = self.__target_first(l) start = If( [ ( And( self.__type_constraints(And(path_expressions, first)), path_expressions, ), GreaterEqual(first, First("Message")), ) ], TRUE, ) result = start.forall() if result != ProofResult.sat: path_message = " -> ".join([l.target.name for l in p]) message = str(start.simplified()).replace("\n\t", "") raise ModelError( f'start of field "{f.name}" on path {path_message} before' f' message start in "{self.full_name} ({result}: {message})' )
def test_if() -> None: assert_equal( If( [ (Greater(Variable("a"), Number(5)), Number(1)), (Greater(Variable("b"), Number(100)), Number(10)), ], Number(100), ).z3expr(), z3.If( z3.Int("a") > z3.IntVal(5), z3.IntVal(1), z3.If(z3.Int("b") > z3.IntVal(100), z3.IntVal(10), z3.IntVal(100)), ), )
def test_if_str() -> None: assert_equal( str(If([(Variable("X"), Number(1)), (Variable("Y"), Number(2))], Number(3))), multilinestr( """(if X then 1 elsif Y then 2 else 3)""" ), )
def valid_predecessors_invariant() -> Expr: return AndThen( *[ If( [ ( Call( "Structural_Valid", [Indexed(Variable("Cursors"), Variable(f.affixed_name))], ), Or( *[ AndThen( Call( "Structural_Valid" if l.source in composite_fields else "Valid", [ Indexed( Variable("Cursors"), Variable(l.source.affixed_name), ) ], ), Equal( Selected( Indexed( Variable("Cursors"), Variable(f.affixed_name), ), "Predecessor", ), Variable(l.source.affixed_name), ), l.condition.substituted( substitution(message, embedded=True) ), ).simplified() for l in message.incoming(f) ] ), ) ] ) for f in message.fields if f not in message.direct_successors(INITIAL) ] )
def __prove_conflicting_conditions(self) -> None: for f in (INITIAL, *self.__fields): conflict = LessEqual( Add( *[ If([(self.__with_constraints(c.condition), Number(1))], Number(0)) for c in self.outgoing(f) ] ), Number(1), ) result = conflict.forall() if result != ProofResult.sat: message = str(conflict).replace("\n", "") raise ModelError( f'conflicting conditions for field "{f.name}"' f' in "{self.full_name}" ({result}: {message})' )
def valid_path_to_next_field_condition(message: Message, field: Field) -> Sequence[Expr]: return [ If( [ ( l.condition.substituted(substitution(message, public=True)), And( Equal( Call( "Predecessor", [Variable("Ctx"), Variable(l.target.affixed_name)], ), Variable(field.affixed_name), ), Call("Valid_Next", [Variable("Ctx"), Variable(l.target.affixed_name)]) if l.target != FINAL else TRUE, ), ) ] ).simplified() for l in message.outgoing(field) if l.target != FINAL ]
def context_predicate(message: Message, composite_fields: Sequence[Field], prefix: str) -> Expr: def valid_predecessors_invariant() -> Expr: return AndThen( *[ If( [ ( Call( "Structural_Valid", [Indexed(Variable("Cursors"), Variable(f.affixed_name))], ), Or( *[ AndThen( Call( "Structural_Valid" if l.source in composite_fields else "Valid", [ Indexed( Variable("Cursors"), Variable(l.source.affixed_name), ) ], ), Equal( Selected( Indexed( Variable("Cursors"), Variable(f.affixed_name), ), "Predecessor", ), Variable(l.source.affixed_name), ), l.condition.substituted( substitution(message, embedded=True) ), ).simplified() for l in message.incoming(f) ] ), ) ] ) for f in message.fields if f not in message.direct_successors(INITIAL) ] ) def invalid_successors_invariant() -> Expr: return AndThen( *[ If( [ ( AndThen( *[ Call( "Invalid", [Indexed(Variable("Cursors"), Variable(p.affixed_name))], ) for p in message.direct_predecessors(f) ] ), Call( "Invalid", [Indexed(Variable("Cursors"), Variable(f.affixed_name))], ), ) ] ) for f in message.fields if f not in message.direct_successors(INITIAL) ] ) return AndThen( If( [ ( NotEqual(Variable("Buffer"), Variable("null")), And( Equal(First("Buffer"), Variable("Buffer_First")), Equal(Last("Buffer"), Variable("Buffer_Last")), ), ) ] ), public_context_predicate(), ForAllIn( "F", ValueRange(First("Field"), Last("Field")), If( [ ( Call("Structural_Valid", [Indexed(Variable("Cursors"), Variable("F"))]), And( GreaterEqual( Selected(Indexed(Variable("Cursors"), Variable("F")), "First"), Variable("First"), ), LessEqual( Selected(Indexed(Variable("Cursors"), Variable("F")), "Last"), Variable("Last"), ), LessEqual( Selected(Indexed(Variable("Cursors"), Variable("F")), "First"), Add( Selected(Indexed(Variable("Cursors"), Variable("F")), "Last"), Number(1), ), ), Equal( Selected( Selected(Indexed(Variable("Cursors"), Variable("F")), "Value"), "Fld", ), Variable("F"), ), ), ) ] ), ), valid_predecessors_invariant(), invalid_successors_invariant(), message_structure_invariant(message, prefix, embedded=True), )
def message_structure_invariant( message: Message, prefix: str, link: Link = None, embedded: bool = False ) -> Expr: def prefixed(name: str) -> Expr: return Selected(Variable("Ctx"), name) if not embedded else Variable(name) if not link: return message_structure_invariant(message, prefix, message.outgoing(INITIAL)[0], embedded) source = link.source target = link.target if target is FINAL: return TRUE field_type = message.types[target] condition = link.condition.substituted(substitution(message, embedded)).simplified() length = ( Size(prefix * full_base_type_name(field_type)) if isinstance(field_type, Scalar) else link.length.substituted( substitution(message, embedded, target_type=const.TYPES_BIT_LENGTH) ).simplified() ) first = ( prefixed("First") if source == INITIAL else link.first.substituted(substitution(message, embedded)) .substituted( mapping={ UNDEFINED: Add( Selected(Indexed(prefixed("Cursors"), Variable(source.affixed_name)), "Last"), Number(1), ) } ) .simplified() ) return If( [ ( AndThen( Call( "Structural_Valid", [Indexed(prefixed("Cursors"), Variable(target.affixed_name))], ), condition, ), AndThen( Equal( Add( Sub( Selected( Indexed(prefixed("Cursors"), Variable(target.affixed_name)), "Last", ), Selected( Indexed(prefixed("Cursors"), Variable(target.affixed_name)), "First", ), ), Number(1), ), length, ), Equal( Selected( Indexed(prefixed("Cursors"), Variable(target.affixed_name)), "Predecessor", ), Variable(source.affixed_name), ), Equal( Selected( Indexed(prefixed("Cursors"), Variable(target.affixed_name)), "First" ), first, ), *[ message_structure_invariant(message, prefix, l, embedded) for l in message.outgoing(target) ], ), ) ] ).simplified()
def create_internal_functions( self, message: Message, scalar_fields: Mapping[Field, Scalar]) -> UnitPart: return UnitPart( [], [ SubprogramBody( ProcedureSpecification( "Set_Field_Value", [ InOutParameter(["Ctx"], "Context"), Parameter(["Val"], "Field_Dependent_Value"), OutParameter(["Fst", "Lst"], const.TYPES_BIT_INDEX), ], ), [ *common.field_bit_location_declarations( Variable("Val.Fld")), *common.field_byte_location_declarations(), *unique( self.insert_function(common.full_base_type_name(t)) for t in message.types.values() if isinstance(t, Scalar)), ], [ Assignment("Fst", Variable("First")), Assignment("Lst", Variable("Last")), CaseStatement( Variable("Val.Fld"), [( Variable(f.affixed_name), [ CallStatement( "Insert", [ Variable(f"Val.{f.name}_Value"), Slice( Variable("Ctx.Buffer.all"), Variable("Buffer_First"), Variable("Buffer_Last"), ), Variable("Offset"), ], ) if f in scalar_fields else NullStatement() ], ) for f in message.all_fields], ), ], [ Precondition( AndThen( Not(Constrained("Ctx")), Call("Has_Buffer", [Variable("Ctx")]), In(Variable("Val.Fld"), Range("Field")), Call("Valid_Next", [Variable("Ctx"), Variable("Val.Fld")]), common.sufficient_space_for_field_condition( Variable("Val.Fld")), ForAllIn( "F", Range("Field"), If([( Call( "Structural_Valid", [ Indexed( Variable("Ctx.Cursors"), Variable("F"), ) ], ), LessEqual( Selected( Indexed( Variable("Ctx.Cursors"), Variable("F"), ), "Last", ), Call( "Field_Last", [ Variable("Ctx"), Variable("Val.Fld") ], ), ), )]), ), )), Postcondition( And( Call("Has_Buffer", [Variable("Ctx")]), Equal( Variable("Fst"), Call( "Field_First", [Variable("Ctx"), Variable("Val.Fld")]), ), Equal( Variable("Lst"), Call( "Field_Last", [Variable("Ctx"), Variable("Val.Fld")]), ), GreaterEqual(Variable("Fst"), Variable("Ctx.First")), LessEqual(Variable("Fst"), Add(Variable("Lst"), Number(1))), LessEqual( Call(const.TYPES_BYTE_INDEX, [Variable("Lst")]), Variable("Ctx.Buffer_Last"), ), ForAllIn( "F", Range("Field"), If([( Call( "Structural_Valid", [ Indexed( Variable("Ctx.Cursors"), Variable("F"), ) ], ), LessEqual( Selected( Indexed( Variable("Ctx.Cursors"), Variable("F"), ), "Last", ), Variable("Lst"), ), )]), ), *[ Equal(e, Old(e)) for e in [ Variable("Ctx.Buffer_First"), Variable("Ctx.Buffer_Last"), Variable("Ctx.First"), Variable("Ctx.Cursors"), ] ], )), ], ) ] if scalar_fields else [], )
def context_predicate(self, message: Message, composite_fields: Sequence[Field]) -> Expr: def valid_predecessors_invariant() -> Expr: return AndThen( *[ If( [ ( Call( "Structural_Valid", [Indexed("Cursors", Name(f.affixed_name))] ), Or( *[ AndThen( Call( "Structural_Valid" if l.source in composite_fields else "Valid", [Indexed("Cursors", Name(l.source.affixed_name))], ), Equal( Selected( Indexed("Cursors", Name(f.affixed_name)), "Predecessor", ), Name(l.source.affixed_name), ), l.condition, ).simplified(self.substitution(message, False)) for l in message.incoming(f) ] ), ) ] ) for f in message.fields if f not in message.direct_successors(INITIAL) ] ) def invalid_successors_invariant() -> Expr: return AndThen( *[ If( [ ( AndThen( *[ Call("Invalid", [Indexed("Cursors", Name(p.affixed_name))]) for p in message.direct_predecessors(f) ] ), Call("Invalid", [Indexed("Cursors", Name(f.affixed_name))]), ) ] ) for f in message.fields if f not in message.direct_successors(INITIAL) ] ) return AndThen( If( [ ( NotEqual(Name(Name("Buffer")), NULL), And( Equal(First(Name("Buffer")), Name(Name("Buffer_First"))), Equal(Last(Name("Buffer")), Name(Name("Buffer_Last"))), ), ) ] ), GreaterEqual( Call(self.types.byte_index, [Name(Name("First"))]), Name(Name("Buffer_First")) ), LessEqual(Call(self.types.byte_index, [Name(Name("Last"))]), Name(Name("Buffer_Last"))), LessEqual(Name(Name("First")), Name(Name("Last"))), LessEqual(Name(Name("Last")), Div(Last(self.types.bit_index), Number(2))), ForAllIn( "F", ValueRange(First("Field"), Last("Field")), If( [ ( Call("Structural_Valid", [Indexed(Name("Cursors"), Name("F"))]), And( GreaterEqual( Selected(Indexed(Name("Cursors"), Name("F")), "First"), Name(Name("First")), ), LessEqual( Selected(Indexed(Name("Cursors"), Name("F")), "Last"), Name(Name("Last")), ), LessEqual( Selected(Indexed(Name("Cursors"), Name("F")), "First"), Add( Selected(Indexed(Name("Cursors"), Name("F")), "Last"), Number(1), ), ), Equal( Selected( Selected(Indexed(Name("Cursors"), Name("F")), "Value"), "Fld", ), Name("F"), ), ), ) ] ), ), valid_predecessors_invariant(), invalid_successors_invariant(), self.message_structure_invariant(message, prefix=False), )
def message_structure_invariant( self, message: Message, link: Link = None, prefix: bool = True ) -> Expr: def prefixed(name: str) -> Expr: return Selected(Name("Ctx"), name) if prefix else Name(name) if not link: return self.message_structure_invariant(message, message.outgoing(INITIAL)[0], prefix) source = link.source target = link.target if target is FINAL: return TRUE field_type = message.types[target] condition = link.condition.simplified(self.substitution(message, prefix)) length = ( Size(base_type_name(field_type)) if isinstance(field_type, Scalar) else link.length.simplified(self.substitution(message, prefix)) ) first = ( Name(prefixed("First")) if source == INITIAL else link.first.simplified( { **self.substitution(message, prefix), **{ UNDEFINED: Add( Selected( Indexed(prefixed("Cursors"), Name(source.affixed_name)), "Last" ), Number(1), ) }, } ) ) return If( [ ( AndThen( Call( "Structural_Valid", [Indexed(prefixed("Cursors"), Name(target.affixed_name))], ), condition, ), AndThen( Equal( Add( Sub( Selected( Indexed(prefixed("Cursors"), Name(target.affixed_name)), "Last", ), Selected( Indexed(prefixed("Cursors"), Name(target.affixed_name)), "First", ), ), Number(1), ), length, ), Equal( Selected( Indexed(prefixed("Cursors"), Name(target.affixed_name)), "Predecessor", ), Name(source.affixed_name), ), Equal( Selected( Indexed(prefixed("Cursors"), Name(target.affixed_name)), "First" ), first, ), *[ self.message_structure_invariant(message, l, prefix) for l in message.outgoing(target) ], ), ) ] ).simplified()
def create_internal_functions( self, message: Message, scalar_fields: Mapping[Field, Scalar] ) -> UnitPart: return UnitPart( [], [ SubprogramBody( ProcedureSpecification( "Set_Field_Value", [ InOutParameter(["Ctx"], "Context"), Parameter(["Val"], "Field_Dependent_Value"), OutParameter(["Fst", "Lst"], self.types.bit_index), ], ), [ *self.common.field_bit_location_declarations(Selected("Val", "Fld")), *self.common.field_byte_location_declarations(), ], [ Assignment("Fst", Name("First")), Assignment("Lst", Name("Last")), CaseStatement( Selected("Val", "Fld"), [ ( Name(f.affixed_name), [ CallStatement( "Insert", [ Selected("Val", f"{f.name}_Value"), Slice( Selected(Selected("Ctx", "Buffer"), "all"), Name("Buffer_First"), Name("Buffer_Last"), ), Name("Offset"), ], ) if f in scalar_fields else NullStatement() ], ) for f in message.all_fields ], ), ], [ Precondition( AndThen( Not(Constrained("Ctx")), Call("Has_Buffer", [Name("Ctx")]), In(Selected("Val", "Fld"), Range("Field")), Call("Valid_Next", [Name("Ctx"), Selected("Val", "Fld")]), self.common.sufficient_space_for_field_condition( Selected("Val", "Fld") ), ForAllIn( "F", Range("Field"), If( [ ( Call( "Structural_Valid", [ Indexed( Selected("Ctx", "Cursors"), Name("F"), ) ], ), LessEqual( Selected( Indexed( Selected("Ctx", "Cursors"), Name("F"), ), "Last", ), Call( "Field_Last", [Name("Ctx"), Selected("Val", "Fld")], ), ), ) ] ), ), ) ), Postcondition( And( Call("Has_Buffer", [Name("Ctx")]), Equal( Name("Fst"), Call("Field_First", [Name("Ctx"), Selected("Val", "Fld")]), ), Equal( Name("Lst"), Call("Field_Last", [Name("Ctx"), Selected("Val", "Fld")]), ), GreaterEqual(Name("Fst"), Selected("Ctx", "First")), LessEqual(Name("Fst"), Add(Name("Lst"), Number(1))), LessEqual( Call(self.types.byte_index, [Name("Lst")]), Selected("Ctx", "Buffer_Last"), ), ForAllIn( "F", Range("Field"), If( [ ( Call( "Structural_Valid", [ Indexed( Selected("Ctx", "Cursors"), Name("F"), ) ], ), LessEqual( Selected( Indexed( Selected("Ctx", "Cursors"), Name("F"), ), "Last", ), Name("Lst"), ), ) ] ), ), *[ Equal(e, Old(e)) for e in [ Selected("Ctx", "Buffer_First"), Selected("Ctx", "Buffer_Last"), Selected("Ctx", "First"), Selected("Ctx", "Cursors"), ] ], ) ), ], ) ], )