def create_verify_message_procedure( message: Message, context_invariant: Sequence[Expr]) -> UnitPart: specification = ProcedureSpecification( "Verify_Message", [InOutParameter(["Ctx"], "Context")]) return UnitPart( [ SubprogramDeclaration( specification, [ Postcondition( And( Equal( Call("Has_Buffer", [Variable("Ctx")]), Old(Call("Has_Buffer", [Variable("Ctx")])), ), *context_invariant, )), ], ) ], [ SubprogramBody( specification, [], [ CallStatement( "Verify", [Variable("Ctx"), Variable(f.affixed_name)]) for f in message.fields ], ) ], )
def specification_bounded(field: Field) -> ProcedureSpecification: return ProcedureSpecification( f"Initialize_Bounded_{field.name}", [ InOutParameter(["Ctx"], "Context"), Parameter(["Length"], const.TYPES_BIT_LENGTH) ], )
def insert_function(self, type_name: str) -> Subprogram: return GenericProcedureInstantiation( "Insert", ProcedureSpecification( f"{self.types.types}.Insert", [ Parameter(["Val"], type_name), InOutParameter(["Buffer"], self.types.bytes), Parameter(["Offset"], self.types.offset), ], ), [self.types.index, self.types.byte, self.types.bytes, self.types.offset, type_name], )
def specification(field: Field, field_type: Type) -> ProcedureSpecification: type_name = ( field_type.enum_name if isinstance(field_type, Enumeration) and field_type.always_valid else field_type.name ) return ProcedureSpecification( f"Set_{field.name}", [ InOutParameter(["Ctx"], "Context"), Parameter(["Val"], f"{message.package}.{type_name}"), ], )
def insert_function(self, type_name: ID) -> Subprogram: return GenericProcedureInstantiation( "Insert", ProcedureSpecification( const.TYPES * "Insert", [ Parameter(["Val"], type_name), InOutParameter(["Buffer"], const.TYPES_BYTES), Parameter(["Offset"], const.TYPES_OFFSET), ], ), [common.prefixed_type_name(type_name, self.prefix)], )
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 specification(field: Field, field_type: Type) -> ProcedureSpecification: if field_type.package == BUILTINS_PACKAGE: type_name = ID(field_type.name) elif isinstance(field_type, Enumeration) and field_type.always_valid: type_name = common.prefixed_type_name( common.full_enum_name(field_type), self.prefix) else: type_name = common.prefixed_type_name(field_type.identifier, self.prefix) return ProcedureSpecification( f"Set_{field.name}", [ InOutParameter(["Ctx"], "Context"), Parameter(["Val"], type_name) ], )
def _create_finalize_proc(self, slots: Sequence[NumberedSlotInfo]) -> UnitPart: proc = ProcedureSpecification("Finalize", [InOutParameter(["S"], "Slots")]) return UnitPart( [ SubprogramDeclaration( proc, [Postcondition(Call("Uninitialized", [Variable("S")]))]), ], [ SubprogramBody( proc, declarations=[], statements=([ Assignment( "S" * self._slot_name(slot.slot_id), Variable("null"), ) for slot in slots ] if slots else [NullStatement()]), aspects=[SparkMode(off=True)], ) ], )
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"), ] ], ) ), ], ) ], )
def specification_bounded(field: Field) -> ProcedureSpecification: return ProcedureSpecification( f"Initialize_Bounded_{field.name}", [InOutParameter(["Ctx"], "Context"), Parameter(["Length"], self.types.bit_length)], )
def specification(field: Field) -> ProcedureSpecification: return ProcedureSpecification( f"Initialize_{field.name}", [InOutParameter(["Ctx"], "Context")] )
def create_verify_procedure(self, message: Message, context_invariant: Sequence[Expr]) -> UnitPart: specification = ProcedureSpecification( "Verify", [InOutParameter(["Ctx"], "Context"), Parameter(["Fld"], "Field")]) valid_field_condition = And( Call( "Valid_Value", [Variable("Value")], ), Call( "Field_Condition", [ Variable("Ctx"), Variable("Value"), *([ Call( "Field_Length", [Variable("Ctx"), Variable("Fld")]) ] if common.length_dependent_condition(message) else []), ], ), ) set_cursors_statements = [ IfStatement( [( Call("Composite_Field", [Variable("Fld")]), [ Assignment( Indexed(Variable("Ctx.Cursors"), Variable("Fld")), NamedAggregate( ("State", Variable("S_Structural_Valid")), ( "First", Call("Field_First", [Variable("Ctx"), Variable("Fld")]), ), ( "Last", Call("Field_Last", [Variable("Ctx"), Variable("Fld")]), ), ("Value", Variable("Value")), ( "Predecessor", Selected( Indexed(Variable("Ctx.Cursors"), Variable("Fld")), "Predecessor", ), ), ), ) ], )], [ Assignment( Indexed(Variable("Ctx.Cursors"), Variable("Fld")), NamedAggregate( ("State", Variable("S_Valid")), ("First", Call("Field_First", [Variable("Ctx"), Variable("Fld")])), ("Last", Call("Field_Last", [Variable("Ctx"), Variable("Fld")])), ("Value", Variable("Value")), ( "Predecessor", Selected( Indexed(Variable("Ctx.Cursors"), Variable("Fld")), "Predecessor"), ), ), ) ], ), # WORKAROUND: # Limitation of GNAT Community 2019 / SPARK Pro 20.0 # Provability of predicate is increased by adding part of # predicate as assert PragmaStatement("Assert", [ str(common.message_structure_invariant(message, self.prefix)) ]), # WORKAROUND: # Limitation of GNAT Community 2019 / SPARK Pro 20.0 # Provability of predicate is increased by splitting # assignment in multiple statements IfStatement([( Equal(Variable("Fld"), Variable(f.affixed_name)), [ Assignment( Indexed( Variable("Ctx.Cursors"), Call("Successor", [Variable("Ctx"), Variable("Fld")]), ), NamedAggregate( ("State", Variable("S_Invalid")), ("Predecessor", Variable("Fld")), ), ) ], ) for f in message.fields]), ] return UnitPart( [ SubprogramDeclaration( specification, [ Postcondition( And( Equal( Call("Has_Buffer", [Variable("Ctx")]), Old(Call("Has_Buffer", [Variable("Ctx")])), ), *context_invariant, )), ], ) ], [ SubprogramBody( specification, [ObjectDeclaration(["Value"], "Field_Dependent_Value")], [ IfStatement([( AndThen( Call("Has_Buffer", [Variable("Ctx")]), 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", Call( "Get_Field_Value", [ Variable("Ctx"), Variable("Fld") ], ), ), 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_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 specification(field: Field) -> ProcedureSpecification: return ProcedureSpecification(f"Set_{field.name}_Empty", [InOutParameter(["Ctx"], "Context")])
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), ), ), ) ], ) ], )], ) ], ) ], )