def create_verify_message_procedure(self, message: Message) -> UnitPart: specification = ProcedureSpecification( "Verify_Message", [InOutParameter(["Ctx"], "Context")]) loop_invariant = And( Call("Has_Buffer", [Variable("Ctx")]), *common.context_invariant(message, loop_entry=True), ) return UnitPart( [ SubprogramDeclaration( specification, [ Precondition( Call( self.prefix * message.identifier * "Has_Buffer", [Variable("Ctx")], )), Postcondition( And( Call("Has_Buffer", [Variable("Ctx")]), *common.context_invariant(message), )), ], ) ], [ SubprogramBody( specification, [], [ ForIn( "F", Variable("Field"), [ PragmaStatement("Loop_Invariant", [loop_invariant]), CallStatement("Verify", [Variable("Ctx"), Variable("F")]), ], ) ], ) ], )
def valid_path_to_next_field_condition( message: model.Message, field: model.Field, prefix: str ) -> Sequence[Expr]: return [ If( [ ( l.condition.substituted(substitution(message, public=True, prefix=prefix)) .simplified() .ada_expr(), 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 != model.FINAL else TRUE, ), ) ] ) for l in message.outgoing(field) if l.target != model.FINAL ]
def public_context_predicate() -> Expr: return And( GreaterEqual(Call(const.TYPES_TO_INDEX, [Variable("First")]), Variable("Buffer_First")), LessEqual(Call(const.TYPES_TO_INDEX, [Variable("Last")]), Variable("Buffer_Last")), Less(Variable("Buffer_Last"), Last(const.TYPES_INDEX)), LessEqual(Variable("First"), Add(Variable("Last"), Number(1))), Less(Variable("Last"), Last(const.TYPES_BIT_INDEX)), Equal(Rem(Variable("First"), Size(const.TYPES_BYTE)), Number(1)), Equal(Rem(Variable("Last"), Size(const.TYPES_BYTE)), Number(0)), )
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")]), ), )])), ], ) ], private=[ 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 _create_init_pred(self, slots: Sequence[NumberedSlotInfo]) -> UnitPart: return UnitPart([ ExpressionFunctionDeclaration( FunctionSpecification("Initialized", "Boolean", [Parameter(["S"], "Slots")]), And(*[ NotEqual( Variable("S" * self._slot_name(slot.slot_id)), Variable("null"), ) for slot in slots ]), ) ])
def cursors_invariant() -> Expr: return ForAllIn( "F", Variable("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("Verified_Last"), ), LessEqual( Selected(Indexed(Variable("Cursors"), Variable("F")), "First"), Add( Selected( Indexed(Variable("Cursors"), Variable("F")), "Last", ), Number(1), ), ), Call( "Valid_Value", [ Variable("F"), Selected( Indexed(Variable("Cursors"), Variable("F")), "Value", ), ], ), ), ) ] ), )
def _create_global_allocated_pred( self, slots: Sequence[NumberedSlotInfo]) -> UnitPart: return UnitPart([ ExpressionFunctionDeclaration( FunctionSpecification("Global_Allocated", "Boolean", [Parameter(["S"], "Slots")]), And( *[ Equal( Variable("S" * self._slot_name(slot.slot_id)), Variable("null"), ) for slot in slots if slot.global_ ], *[ NotEqual( Variable("S" * self._slot_name(slot.slot_id)), Variable("null"), ) for slot in slots if not slot.global_ ], ), ) ])
def create_verify_procedure( self, message: Message, scalar_fields: Mapping[Field, Scalar], composite_fields: Sequence[Field], ) -> UnitPart: specification = ProcedureSpecification( "Verify", [InOutParameter(["Ctx"], "Context"), Parameter(["Fld"], "Field")]) valid_field_condition = AndThen( Call( "Valid_Value", [Variable("Fld"), Variable("Value")], ), Call( "Field_Condition", [ Variable("Ctx"), Variable("Fld"), *([Variable("Value")] if common.has_value_dependent_condition(message) else []), *([ Slice( Variable("Ctx.Buffer.all"), Call( const.TYPES_TO_INDEX, [ Call("Field_First", [Variable("Ctx"), Variable("Fld")]) ], ), Call( const.TYPES_TO_INDEX, [ Call("Field_Last", [Variable("Ctx"), Variable("Fld")]) ], ), ) ] if common.has_aggregate_dependent_condition(message) else []), *([Call("Field_Size", [Variable("Ctx"), Variable("Fld")])] if common.has_size_dependent_condition(message) else []), ], ), ) last = Mul( Div( Add( Call("Field_Last", [Variable("Ctx"), Variable("Fld")]), Size(const.TYPES_BYTE), -Number(1), ), Size(const.TYPES_BYTE), ), Size(const.TYPES_BYTE), ) set_cursors_statements = [ *([ PragmaStatement( "Assert", [ If([( Or(*[ Equal(Variable("Fld"), Variable( f.affixed_name)) for f in message.direct_predecessors(FINAL) ]), Equal( Mod( Call("Field_Last", [Variable("Ctx"), Variable("Fld")]), Size(const.TYPES_BYTE), ), Number(0), ), )]) ], ) ] if len(message.fields) > 1 else []), # Improve provability of context predicate PragmaStatement( "Assert", [Equal(Mod(last, Size(const.TYPES_BYTE)), Number(0))]), Assignment(Variable("Ctx.Verified_Last"), last), PragmaStatement( "Assert", [ LessEqual( Call("Field_Last", [Variable("Ctx"), Variable("Fld")]), Variable("Ctx.Verified_Last"), ) ], ), IfStatement( [( Call("Composite_Field", [Variable("Fld")]), [set_context_cursor_composite_field("Fld")], )], [set_context_cursor_scalar()], ) if scalar_fields and composite_fields else set_context_cursor_scalar() if scalar_fields and not composite_fields else set_context_cursor_composite_field("Fld"), *([ # https://github.com/Componolit/RecordFlux/issues/664 # The provability of the context predicate is increased by splitting the # assignment into multiple statements. Assignment( Indexed( Variable("Ctx.Cursors"), Call( "Successor", [Variable("Ctx"), Variable("Fld")], ), ), NamedAggregate( ("State", Variable("S_Invalid")), ("Predecessor", Variable("Fld")), ), ) ] if len(message.fields) > 1 else []), ] return UnitPart( [ SubprogramDeclaration( specification, [ Precondition( Call( self.prefix * message.identifier * "Has_Buffer", [Variable("Ctx")], )), Postcondition( And( Call("Has_Buffer", [Variable("Ctx")]), *common.context_invariant(message), )), ], ) ], [ SubprogramBody( specification, [ObjectDeclaration(["Value"], const.TYPES_BASE_INT)], [ IfStatement([( AndThen( Call( "Invalid", [ Indexed(Variable("Ctx.Cursors"), Variable("Fld")) ], ), Call("Valid_Predecessor", [Variable("Ctx"), Variable("Fld")]), Call("Path_Condition", [Variable("Ctx"), Variable("Fld")]), ), [ IfStatement( [( Call( "Sufficient_Buffer_Length", [Variable("Ctx"), Variable("Fld")], ), [ Assignment( "Value", If( [( Call( "Composite_Field", [ Variable( "Fld"), ], ), Number(0), )], Call( "Get", [ Variable("Ctx"), Variable("Fld"), ], ), ) if scalar_fields and composite_fields else Call( "Get", [ Variable("Ctx"), Variable("Fld"), ], ) if scalar_fields else Number(0), ), IfStatement( [( valid_field_condition, set_cursors_statements, )], [ Assignment( Indexed( Variable( "Ctx.Cursors"), Variable("Fld"), ), NamedAggregate( ( "State", Variable( "S_Invalid" ), ), ( "Predecessor", Variable( FINAL. affixed_name, ), ), ), ) ], ), ], )], [ Assignment( Indexed(Variable("Ctx.Cursors"), Variable("Fld")), NamedAggregate( ("State", Variable("S_Incomplete")), ( "Predecessor", Variable( FINAL.affixed_name), ), ), ) ], ) ], )], ) ], ) ], )
def create_generic_opaque_getter_procedures( self, message: Message, opaque_fields: Sequence[Field]) -> UnitPart: def specification(field: Field) -> ProcedureSpecification: return ProcedureSpecification(f"Generic_Get_{field.name}", [Parameter(["Ctx"], "Context")]) return UnitPart( [ SubprogramDeclaration( specification(f), [ Precondition( And( Call( self.prefix * message.identifier * "Has_Buffer", [Variable("Ctx")], ), Call( self.prefix * message.identifier * "Present", [ Variable("Ctx"), Variable( self.prefix * message.identifier * f.affixed_name), ], ), )) ], [ FormalSubprogramDeclaration( ProcedureSpecification( f"Process_{f.name}", [Parameter([f.name], const.TYPES_BYTES)])) ], ) for f in opaque_fields ], [ SubprogramBody( specification(f), [ ObjectDeclaration( ["First"], const.TYPES_INDEX, Call( const.TYPES_TO_INDEX, [ Selected( Indexed( Variable("Ctx.Cursors"), Variable(f.affixed_name), ), "First", ) ], ), constant=True, ), ObjectDeclaration( ["Last"], const.TYPES_INDEX, Call( const.TYPES_TO_INDEX, [ Selected( Indexed( Variable("Ctx.Cursors"), Variable(f.affixed_name), ), "Last", ) ], ), constant=True, ), ], [ CallStatement( f"Process_{f.name}", [ Slice(Variable("Ctx.Buffer.all"), Variable("First"), Variable("Last")) ], ) ], ) for f in opaque_fields ], )
def context_predicate( message: model.Message, composite_fields: Sequence[model.Field], prefix: str ) -> Expr: def cursors_invariant() -> Expr: return ForAllIn( "F", Variable("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("Verified_Last"), ), LessEqual( Selected(Indexed(Variable("Cursors"), Variable("F")), "First"), Add( Selected( Indexed(Variable("Cursors"), Variable("F")), "Last", ), Number(1), ), ), Call( "Valid_Value", [ Variable("F"), Selected( Indexed(Variable("Cursors"), Variable("F")), "Value", ), ], ), ), ) ] ), ) def valid_predecessors_invariant() -> Expr: return AndThen( *[ If( [ ( Call( "Structural_Valid", [ Indexed( Variable("Cursors"), Variable(f.affixed_name), ) ], ), Or( *[ expr.AndThen( expr.Call( "Structural_Valid" if l.source in composite_fields else "Valid", [ expr.Indexed( expr.Variable("Cursors"), expr.Variable(l.source.affixed_name), ) ], ), expr.Equal( expr.Selected( expr.Indexed( expr.Variable("Cursors"), expr.Variable(f.affixed_name), ), "Predecessor", ), expr.Variable(l.source.affixed_name), ), l.condition.substituted( substitution(message, embedded=True, prefix=prefix) ), ) .simplified() .ada_expr() for l in message.incoming(f) ] ), ) ] ) for f in message.fields if f not in message.direct_successors(model.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(model.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(), LessEqual(Sub(Variable("First"), Number(1)), Variable("Verified_Last")), LessEqual(Sub(Variable("First"), Number(1)), Variable("Written_Last")), LessEqual(Variable("Verified_Last"), Variable("Written_Last")), LessEqual(Variable("Written_Last"), Variable("Last")), Equal(Rem(Variable("First"), Size(const.TYPES_BYTE)), Number(1)), Equal(Rem(Variable("Last"), Size(const.TYPES_BYTE)), Number(0)), Equal(Rem(Variable("Verified_Last"), Size(const.TYPES_BYTE)), Number(0)), Equal(Rem(Variable("Written_Last"), Size(const.TYPES_BYTE)), Number(0)), cursors_invariant(), valid_predecessors_invariant(), invalid_successors_invariant(), message_structure_invariant(message, prefix, embedded=True), )