def create_present_function() -> UnitPart: specification = FunctionSpecification( "Present", "Boolean", [Parameter(["Ctx"], "Context"), Parameter(["Fld"], "Field")]) return UnitPart( [SubprogramDeclaration(specification)], [ ExpressionFunctionDeclaration( specification, AndThen( Call("Structural_Valid", [ Indexed(Variable("Ctx.Cursors"), Variable("Fld")) ]), Less( Selected( Indexed(Variable("Ctx.Cursors"), Variable("Fld")), "First"), Add( Selected( Indexed(Variable("Ctx.Cursors"), Variable("Fld")), "Last"), Number(1), ), ), ), ) ], )
def create_structural_valid_function() -> UnitPart: specification = FunctionSpecification( "Structural_Valid", "Boolean", [Parameter(["Ctx"], "Context"), Parameter(["Fld"], "Field")], ) return UnitPart( [SubprogramDeclaration(specification)], [ ExpressionFunctionDeclaration( specification, And( Or(*[ Equal( Selected( Indexed(Variable("Ctx.Cursors"), Variable("Fld")), "State"), Variable(s), ) for s in ("S_Valid", "S_Structural_Valid") ])), ) ], )
def create_invalid_function() -> UnitPart: specification = FunctionSpecification( "Invalid", "Boolean", [Parameter(["Ctx"], "Context"), Parameter(["Fld"], "Field")]) return UnitPart( [SubprogramDeclaration(specification)], [ ExpressionFunctionDeclaration( specification, Or( Equal( Selected( Indexed(Variable("Ctx.Cursors"), Variable("Fld")), "State"), Variable("S_Invalid"), ), Equal( Selected( Indexed(Variable("Ctx.Cursors"), Variable("Fld")), "State"), Variable("S_Incomplete"), ), ), ) ], )
def extract_function(self, type_name: str) -> Subprogram: return GenericFunctionInstantiation( "Extract", FunctionSpecification( f"{self.types.types}.Extract", type_name, [Parameter(["Buffer"], self.types.bytes), Parameter(["Offset"], self.types.offset)], ), [self.types.index, self.types.byte, self.types.bytes, self.types.offset, type_name], )
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 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 extract_function(self, type_name: ID) -> Subprogram: return GenericFunctionInstantiation( "Extract", FunctionSpecification( const.TYPES * "Extract", type_name, [ Parameter(["Buffer"], const.TYPES_BYTES), Parameter(["Offset"], const.TYPES_OFFSET), ], ), [common.prefixed_type_name(type_name, self.prefix)], )
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 create_valid_message_function(self, message: Message) -> UnitPart: specification = FunctionSpecification("Valid_Message", "Boolean", [Parameter(["Ctx"], "Context")]) return UnitPart( [ SubprogramDeclaration( specification, [ Precondition( Call( self.prefix * message.identifier * "Has_Buffer", [Variable("Ctx")], )) ], ) ], private=[ ExpressionFunctionDeclaration( specification, self.valid_message_condition(message), ) ], )
def create_composite_accessor_procedures(self, composite_fields: Sequence[Field]) -> UnitPart: def specification(field: Field) -> ProcedureSpecification: return ProcedureSpecification(f"Get_{field.name}", [Parameter(["Ctx"], "Context")]) return UnitPart( [ SubprogramDeclaration( specification(f), [ Precondition( And( VALID_CONTEXT, Call("Has_Buffer", [Name("Ctx")]), Call("Present", [Name("Ctx"), Name(f.affixed_name)]), ) ) ], [ FormalSubprogramDeclaration( ProcedureSpecification( f"Process_{f.name}", [Parameter([f.name], self.types.bytes)] ) ) ], ) for f in composite_fields ], [ SubprogramBody( specification(f), [ ObjectDeclaration( ["First"], self.types.index, Call( self.types.byte_index, [Selected(Indexed("Ctx.Cursors", Name(f.affixed_name)), "First")], ), True, ), ObjectDeclaration( ["Last"], self.types.index, Call( self.types.byte_index, [Selected(Indexed("Ctx.Cursors", Name(f.affixed_name)), "Last")], ), True, ), ], [ CallStatement( f"Process_{f.name}", [Slice("Ctx.Buffer.all", Name("First"), Name("Last"))], ) ], ) for f in composite_fields ], )
def create_incomplete_function() -> UnitPart: specification = FunctionSpecification( "Incomplete", "Boolean", [Parameter(["Ctx"], "Context"), Parameter(["Fld"], "Field")] ) return UnitPart( [SubprogramDeclaration(specification, [Precondition(VALID_CONTEXT)])], [ ExpressionFunctionDeclaration( specification, Equal( Selected(Indexed("Ctx.Cursors", Name("Fld")), "State"), Name("S_Incomplete") ), ) ], )
def _create_init_proc(self, slots: Sequence[NumberedSlotInfo]) -> UnitPart: proc = ProcedureSpecification( "Initialize", [OutParameter(["S"], "Slots"), Parameter(["M"], "Memory")]) return UnitPart( [ SubprogramDeclaration( proc, [Postcondition(Call("Initialized", [Variable("S")]))]), ], [ SubprogramBody( proc, declarations=[], statements=([ Assignment( "S" * self._slot_name(slot.slot_id), UnrestrictedAccess( Variable(ID(f"M.Slot_{slot.slot_id}"))), ) for slot in slots ] if slots else [NullStatement()]), aspects=[SparkMode(off=True)], ) ], )
def specification_bounded(field: Field) -> ProcedureSpecification: return ProcedureSpecification( f"Initialize_Bounded_{field.name}", [ InOutParameter(["Ctx"], "Context"), Parameter(["Length"], const.TYPES_BIT_LENGTH) ], )
def specification(field: Field) -> ProcedureSpecification: return ProcedureSpecification( f"Get_{field.name}", [ Parameter(["Ctx"], "Context"), OutParameter(["Data"], const.TYPES_BYTES) ], )
def specification(field: Field, field_type: Type) -> FunctionSpecification: if field_type.package == BUILTINS_PACKAGE: type_name = ID(field_type.name) else: type_name = self.prefix * field_type.identifier return FunctionSpecification(f"Get_{field.name}", type_name, [Parameter(["Ctx"], "Context")])
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 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 create_valid_message_function(self, message: Message) -> UnitPart: specification = FunctionSpecification( "Valid_Message", "Boolean", [Parameter(["Ctx"], "Context")] ) return UnitPart( [SubprogramDeclaration(specification, [Precondition(VALID_CONTEXT)])], [ ExpressionFunctionDeclaration( specification, valid_message_condition(message).simplified(self.common.substitution(message)), ) ], )
def formal_parameters( field: Field) -> Sequence[FormalSubprogramDeclaration]: return [ FormalSubprogramDeclaration( ProcedureSpecification( f"Process_{field.name}", [OutParameter([field.name], const.TYPES_BYTES)], )), FormalSubprogramDeclaration( FunctionSpecification( "Valid_Length", "Boolean", [Parameter(["Length"], const.TYPES_LENGTH)], )), ]
def create_incomplete_message_function(message: Message) -> UnitPart: specification = FunctionSpecification("Incomplete_Message", "Boolean", [Parameter(["Ctx"], "Context")]) return UnitPart( [SubprogramDeclaration(specification)], [ ExpressionFunctionDeclaration( specification, Or(*[ Call("Incomplete", [Variable("Ctx"), Variable(f.affixed_name)]) for f in message.fields ]), ) ], )
def create_valid_message_function(message: Message) -> UnitPart: specification = FunctionSpecification("Valid_Message", "Boolean", [Parameter(["Ctx"], "Context")]) return UnitPart( [ SubprogramDeclaration( specification, [Precondition(Call("Has_Buffer", [Variable("Ctx")]))], ) ], [ ExpressionFunctionDeclaration( specification, valid_message_condition(message).substituted( common.substitution(message)).simplified(), ) ], )
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_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_incomplete_message_function() -> UnitPart: specification = FunctionSpecification("Incomplete_Message", "Boolean", [Parameter(["Ctx"], "Context")]) return UnitPart( [ # https://github.com/Componolit/Workarounds/issues/47 Pragma( "Warnings", [ Variable("Off"), String( "postcondition does not mention function result") ], ), SubprogramDeclaration(specification, [Postcondition(TRUE)]), Pragma( "Warnings", [ Variable("On"), String( "postcondition does not mention function result") ], ), ], private=[ ExpressionFunctionDeclaration( specification, ForSomeIn( "F", Variable("Field"), Call( "Incomplete", [Variable("Ctx"), Variable("F")], ), ), ) ], )
def create_internal_functions( self, message: Message, scalar_fields: Mapping[Field, Type], composite_fields: Sequence[Field], ) -> UnitPart: def result(field: Field, message: Message) -> NamedAggregate: aggregate: List[Tuple[str, Expr]] = [ ("Fld", Variable(field.affixed_name)) ] if field in message.fields and isinstance(message.types[field], Scalar): aggregate.append(( f"{field.name}_Value", Call( "Extract", [ Slice( Variable("Ctx.Buffer.all"), Variable("Buffer_First"), Variable("Buffer_Last"), ), Variable("Offset"), ], ), )) return NamedAggregate(*aggregate) return UnitPart( [], [ ExpressionFunctionDeclaration( FunctionSpecification("Composite_Field", "Boolean", [Parameter(["Fld"], "Field")]), Case( Variable("Fld"), [(Variable(f.affixed_name), TRUE if f in composite_fields else FALSE) for f in message.fields], ), ), SubprogramBody( FunctionSpecification( "Get_Field_Value", "Field_Dependent_Value", [ Parameter(["Ctx"], "Context"), Parameter(["Fld"], "Field") ], ), [ *common.field_bit_location_declarations( Variable("Fld")), *common.field_byte_location_declarations(), *unique( self.extract_function(common.full_base_type_name( t)) for t in message.types.values() if isinstance(t, Scalar)), ] if scalar_fields else [], [ ReturnStatement( Case( Variable("Fld"), [(Variable(f.affixed_name), result(f, message)) for f in message.fields], )) ], [ Precondition( AndThen( Call("Has_Buffer", [Variable("Ctx")]), Call("Valid_Next", [Variable("Ctx"), Variable("Fld")]), Call("Sufficient_Buffer_Length", [Variable("Ctx"), Variable("Fld")]), )), Postcondition( Equal( Selected(Result("Get_Field_Value"), "Fld"), Variable("Fld"), )), ], ), ], )
def specification(field: Field) -> ProcedureSpecification: return ProcedureSpecification(f"Get_{field.name}", [Parameter(["Ctx"], "Context")])
def specification_bounded(field: Field) -> ProcedureSpecification: return ProcedureSpecification( f"Initialize_Bounded_{field.name}", [InOutParameter(["Ctx"], "Context"), Parameter(["Length"], self.types.bit_length)], )
def create_composite_accessor_procedures( composite_fields: Sequence[Field]) -> UnitPart: def specification(field: Field) -> ProcedureSpecification: return ProcedureSpecification(f"Get_{field.name}", [Parameter(["Ctx"], "Context")]) return UnitPart( [ SubprogramDeclaration( specification(f), [ Precondition( And( Call("Has_Buffer", [Variable("Ctx")]), Call("Present", [ Variable("Ctx"), Variable(f.affixed_name) ]), )) ], [ FormalSubprogramDeclaration( ProcedureSpecification( f"Process_{f.name}", [Parameter([f.name], const.TYPES_BYTES)])) ], ) for f in composite_fields ], [ SubprogramBody( specification(f), [ ObjectDeclaration( ["First"], const.TYPES_INDEX, Call( const.TYPES_BYTE_INDEX, [ Selected( Indexed(Variable("Ctx.Cursors"), Variable(f.affixed_name)), "First", ) ], ), True, ), ObjectDeclaration( ["Last"], const.TYPES_INDEX, Call( const.TYPES_BYTE_INDEX, [ Selected( Indexed(Variable("Ctx.Cursors"), Variable(f.affixed_name)), "Last", ) ], ), True, ), ], [ CallStatement( f"Process_{f.name}", [ Slice(Variable("Ctx.Buffer.all"), Variable("First"), Variable("Last")) ], ) ], ) for f in composite_fields ], )
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 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), ), ), ) ], ) ], )], ) ], ) ], )