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 field_bit_location_declarations(self, field_name: Name) -> Sequence[Declaration]: return [ ObjectDeclaration( ["First"], self.types.bit_index, Call("Field_First", [Name("Ctx"), field_name]), True, ), ObjectDeclaration( ["Last"], self.types.bit_index, Call("Field_Last", [Name("Ctx"), field_name]), True, ), ]
def field_bit_location_declarations(field_name: Name) -> Sequence[Declaration]: return [ ObjectDeclaration( ["First"], const.TYPES_BIT_INDEX, Call("Field_First", [Variable("Ctx"), field_name]), True, ), ObjectDeclaration( ["Last"], const.TYPES_BIT_INDEX, Call("Field_Last", [Variable("Ctx"), field_name]), True, ), ]
def create_composite_initialize_procedures(self, message: Message) -> UnitPart: def specification(field: Field) -> ProcedureSpecification: return ProcedureSpecification( f"Initialize_{field.name}", [InOutParameter(["Ctx"], "Context")] ) def specification_bounded(field: Field) -> ProcedureSpecification: return ProcedureSpecification( f"Initialize_Bounded_{field.name}", [InOutParameter(["Ctx"], "Context"), Parameter(["Length"], self.types.bit_length)], ) return UnitPart( [ SubprogramDeclaration( specification(f), [ Precondition( AndThen( *self.setter_preconditions(f), *self.unbounded_composite_setter_preconditions(message, f), ) ), Postcondition( And( *self.composite_setter_postconditions(message, f, message.types[f]), ) ), ], ) for f, t in message.types.items() if isinstance(t, Payload) and unbounded_setter_required(message, f) ] + [ SubprogramDeclaration( specification_bounded(f), [ Precondition( AndThen( *self.setter_preconditions(f), *self.bounded_composite_setter_preconditions(message, f), ) ), Postcondition( And( *self.composite_setter_postconditions(message, f, message.types[f]), ) ), ], ) for f, t in message.types.items() if isinstance(t, Payload) and bounded_setter_required(message, f) ], [ SubprogramBody( specification(f), self.common.field_bit_location_declarations(Name(f.affixed_name)), self.common.initialize_field_statements(message, f), ) for f, t in message.types.items() if isinstance(t, Payload) and unbounded_setter_required(message, f) ] + [ SubprogramBody( specification_bounded(f), [ ObjectDeclaration( ["First"], self.types.bit_index, Call("Field_First", [Name("Ctx"), Name(f.affixed_name)]), True, ), ObjectDeclaration( ["Last"], self.types.bit_index, Add(Name("First"), Name("Length"), -Number(1)), True, ), ], self.common.initialize_field_statements(message, f), ) for f, t in message.types.items() if isinstance(t, Payload) and bounded_setter_required(message, f) ], )
def create_composite_setter_procedures(self, message: Message) -> UnitPart: def specification(field: Field) -> ProcedureSpecification: return ProcedureSpecification(f"Set_{field.name}", [InOutParameter(["Ctx"], "Context")]) def specification_bounded(field: Field) -> ProcedureSpecification: return ProcedureSpecification( f"Set_Bounded_{field.name}", [InOutParameter(["Ctx"], "Context"), Parameter(["Length"], self.types.bit_length)], ) formal_parameters = [ FormalSubprogramDeclaration( ProcedureSpecification( "Process_Payload", [OutParameter(["Payload"], self.types.bytes)], ) ) ] return UnitPart( [ SubprogramDeclaration( specification(f), [ Precondition( AndThen( *self.setter_preconditions(f), *self.unbounded_composite_setter_preconditions(message, f), ) ), Postcondition( And( *self.composite_setter_postconditions(message, f, message.types[f]), ) ), ], formal_parameters, ) for f, t in message.types.items() if isinstance(t, Payload) and unbounded_setter_required(message, f) ] + [ SubprogramDeclaration( specification_bounded(f), [ Precondition( AndThen( *self.setter_preconditions(f), *self.bounded_composite_setter_preconditions(message, f), ) ), Postcondition( And( *self.composite_setter_postconditions(message, f, message.types[f]), ) ), ], formal_parameters, ) for f, t in message.types.items() if isinstance(t, Payload) and bounded_setter_required(message, f) ], [ SubprogramBody( specification(f), [ *self.common.field_bit_location_declarations(Name(f.affixed_name)), ExpressionFunctionDeclaration( FunctionSpecification("Buffer_First", self.types.index), Call(self.types.byte_index, [Name("First")]), ), ExpressionFunctionDeclaration( FunctionSpecification("Buffer_Last", self.types.index), Call(self.types.byte_index, [Name("Last")]), ), ], [ CallStatement(f"Initialize_{f.name}", [Name("Ctx")]), CallStatement( "Process_Payload", [ Slice( Selected(Selected("Ctx", "Buffer"), "all"), Name("Buffer_First"), Name("Buffer_Last"), ), ], ), ], ) for f, t in message.types.items() if isinstance(t, Payload) and unbounded_setter_required(message, f) ] + [ SubprogramBody( specification_bounded(f), [ ObjectDeclaration( ["First"], self.types.bit_index, Call("Field_First", [Name("Ctx"), Name(f.affixed_name)]), True, ), ObjectDeclaration( ["Last"], self.types.bit_index, Add(Name("First"), Name("Length"), -Number(1)), True, ), ExpressionFunctionDeclaration( FunctionSpecification("Buffer_First", self.types.index), Call(self.types.byte_index, [Name("First")]), ), ExpressionFunctionDeclaration( FunctionSpecification("Buffer_Last", self.types.index), Call(self.types.byte_index, [Name("Last")]), ), ], [ CallStatement( f"Initialize_Bounded_{f.name}", [Name("Ctx"), Name("Length")] ), CallStatement( "Process_Payload", [ Slice( Selected(Selected("Ctx", "Buffer"), "all"), Name("Buffer_First"), Name("Buffer_Last"), ), ], ), ], ) for f, t in message.types.items() if isinstance(t, Payload) and bounded_setter_required(message, f) ], )
def create_scalar_setter_procedures( self, message: Message, scalar_fields: Mapping[Field, Scalar] ) -> UnitPart: 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}"), ], ) return UnitPart( [ SubprogramDeclaration( specification(f, t), [ Precondition( AndThen( *self.setter_preconditions(f), Call( "Field_Condition", [ Name("Ctx"), Aggregate( Name(f.affixed_name), Name("Val") if not isinstance(t, Enumeration) else Call("Convert", [Name("Val")]), ), ], ), Call("Valid", [Name("Val")]) if not isinstance(t, Enumeration) else TRUE, self.common.sufficient_space_for_field_condition( Name(f.affixed_name) ), ) ), Postcondition( And( VALID_CONTEXT, Call("Has_Buffer", [Name("Ctx")]), Call("Valid", [Name("Ctx"), Name(f.affixed_name)]), Equal( Call(f"Get_{f.name}", [Name("Ctx")]), Aggregate(TRUE, Name("Val")) if isinstance(t, Enumeration) and t.always_valid else Name("Val"), ), *self.setter_postconditions(message, f, t), *[ Equal( Call("Cursor", [Name("Ctx"), Name(p.affixed_name)]), Old(Call("Cursor", [Name("Ctx"), Name(p.affixed_name)])), ) for p in message.predecessors(f) ], ) ), ], ) for f, t in scalar_fields.items() ], [ SubprogramBody( specification(f, t), [ ObjectDeclaration( ["Field_Value"], "Field_Dependent_Value", Aggregate( Name(f.affixed_name), Name("Val") if not isinstance(t, Enumeration) else Call("Convert", [Name("Val")]), ), True, ), ObjectDeclaration(["First", "Last"], self.types.bit_index), ], [ CallStatement( "Reset_Dependent_Fields", [Name("Ctx"), Name(f.affixed_name)], ), CallStatement( "Set_Field_Value", [Name("Ctx"), Name("Field_Value"), Name("First"), Name("Last")], ), Assignment( "Ctx", Aggregate( Selected("Ctx", "Buffer_First"), Selected("Ctx", "Buffer_Last"), Selected("Ctx", "First"), Name("Last"), Selected("Ctx", "Buffer"), Selected("Ctx", "Cursors"), ), ), Assignment( Indexed(Selected("Ctx", "Cursors"), Name(f.affixed_name)), NamedAggregate( ("State", Name("S_Valid")), ("First", Name("First")), ("Last", Name("Last")), ("Value", Name("Field_Value")), ( "Predecessor", Selected( Indexed(Selected("Ctx", "Cursors"), Name(f.affixed_name)), "Predecessor", ), ), ), ), Assignment( Indexed( Selected("Ctx", "Cursors"), Call("Successor", [Name("Ctx"), Name(f.affixed_name)]), ), NamedAggregate( ("State", Name("S_Invalid")), ("Predecessor", Name(f.affixed_name)), ), ), ], ) for f, t in scalar_fields.items() ], )
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_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_composite_setter_procedures(self, message: Message) -> UnitPart: def specification(field: Field) -> ProcedureSpecification: return ProcedureSpecification(f"Set_{field.name}", [InOutParameter(["Ctx"], "Context")]) def specification_bounded(field: Field) -> ProcedureSpecification: return ProcedureSpecification( f"Set_Bounded_{field.name}", [ InOutParameter(["Ctx"], "Context"), Parameter(["Length"], const.TYPES_BIT_LENGTH) ], ) 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)], )), ] return UnitPart( [ SubprogramDeclaration( specification(f), [ Precondition( AndThen( *self.setter_preconditions(f), *self.unbounded_composite_setter_preconditions( message, f), Call( "Valid_Length", [ Call( const.TYPES_LENGTH, [ Div( Call( "Field_Length", [ Variable("Ctx"), Variable( f.affixed_name) ], ), Size(const.TYPES_BYTE), ), ], ), ], ), )), Postcondition( And( *self.composite_setter_postconditions( message, f), )), ], formal_parameters(f), ) for f, t in message.types.items() if isinstance(t, Opaque) and unbounded_setter_required(message, f) ] + [ SubprogramDeclaration( specification_bounded(f), [ Precondition( AndThen( *self.setter_preconditions(f), *self.bounded_composite_setter_preconditions( message, f), Call( "Valid_Length", [ Call( const.TYPES_LENGTH, [ Div(Variable("Length"), Size(const.TYPES_BYTE)) ], ) ], ), )), Postcondition( And( *self.composite_setter_postconditions( message, f), )), ], formal_parameters(f), ) for f, t in message.types.items() if isinstance(t, Opaque) and bounded_setter_required(message, f) ], [ SubprogramBody( specification(f), [ *common.field_bit_location_declarations( Variable(f.affixed_name)), ExpressionFunctionDeclaration( FunctionSpecification("Buffer_First", const.TYPES_INDEX), Call(const.TYPES_BYTE_INDEX, [Variable("First")]), ), ExpressionFunctionDeclaration( FunctionSpecification("Buffer_Last", const.TYPES_INDEX), Call(const.TYPES_BYTE_INDEX, [Variable("Last")]), ), ], [ CallStatement(f"Initialize_{f.name}", [Variable("Ctx")]), CallStatement( f"Process_{f.name}", [ Slice( Selected(Variable("Ctx.Buffer"), "all"), Variable("Buffer_First"), Variable("Buffer_Last"), ), ], ), ], ) for f, t in message.types.items() if isinstance(t, Opaque) and unbounded_setter_required(message, f) ] + [ SubprogramBody( specification_bounded(f), [ ObjectDeclaration( ["First"], const.TYPES_BIT_INDEX, Call("Field_First", [Variable("Ctx"), Variable(f.affixed_name)]), True, ), ObjectDeclaration( ["Last"], const.TYPES_BIT_INDEX, Add(Variable("First"), Variable("Length"), -Number(1)), True, ), ExpressionFunctionDeclaration( FunctionSpecification("Buffer_First", const.TYPES_INDEX), Call(const.TYPES_BYTE_INDEX, [Variable("First")]), ), ExpressionFunctionDeclaration( FunctionSpecification("Buffer_Last", const.TYPES_INDEX), Call(const.TYPES_BYTE_INDEX, [Variable("Last")]), ), ], [ CallStatement(f"Initialize_Bounded_{f.name}", [Variable("Ctx"), Variable("Length")]), CallStatement( f"Process_{f.name}", [ Slice( Selected(Variable("Ctx.Buffer"), "all"), Variable("Buffer_First"), Variable("Buffer_Last"), ), ], ), ], ) for f, t in message.types.items() if isinstance(t, Opaque) and bounded_setter_required(message, f) ], )
def create_composite_setter_empty_procedures(self, message: Message) -> UnitPart: def specification(field: Field) -> ProcedureSpecification: return ProcedureSpecification(f"Set_{field.name}_Empty", [InOutParameter(["Ctx"], "Context")]) return UnitPart( [ *[ SubprogramDeclaration( specification(f), [ Precondition( AndThen( *self.setter_preconditions(f), *self. unbounded_composite_setter_preconditions( message, f), Equal( Call( "Field_Length", [ Variable("Ctx"), Variable(f.affixed_name) ], ), Number(0), ), )), Postcondition( And(*self.composite_setter_postconditions( message, f))), ], ) for f, t in message.types.items() if message.is_possibly_empty(f) ], ], [ SubprogramBody( specification(f), [ ObjectDeclaration( ["First"], const.TYPES_BIT_INDEX, Call("Field_First", [Variable("Ctx"), Variable(f.affixed_name)]), True, ), ObjectDeclaration( ["Last"], const.TYPES_BIT_INDEX, Call("Field_Last", [Variable("Ctx"), Variable(f.affixed_name)]), True, ), ], [ CallStatement( "Reset_Dependent_Fields", [Variable("Ctx"), Variable(f.affixed_name)], ), Assignment( "Ctx", Aggregate( Variable("Ctx.Buffer_First"), Variable("Ctx.Buffer_Last"), Variable("Ctx.First"), Variable("Last"), Variable("Ctx.Buffer"), Variable("Ctx.Cursors"), ), ), Assignment( Indexed(Variable("Ctx.Cursors"), Variable(f.affixed_name)), NamedAggregate( ("State", Variable("S_Valid")), ("First", Variable("First")), ("Last", Variable("Last")), ("Value", NamedAggregate( ("Fld", Variable(f.affixed_name)))), ( "Predecessor", Selected( Indexed(Variable("Ctx.Cursors"), Variable(f.affixed_name)), "Predecessor", ), ), ), ), Assignment( Indexed( Variable("Ctx.Cursors"), Call("Successor", [ Variable("Ctx"), Variable(f.affixed_name) ]), ), NamedAggregate( ("State", Variable("S_Invalid")), ("Predecessor", Variable(f.affixed_name)), ), ), ], ) for f, t in message.types.items() if message.is_possibly_empty(f) ], )
def create_scalar_setter_procedures( self, message: Message, scalar_fields: Mapping[Field, Scalar]) -> UnitPart: 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) ], ) return UnitPart( [ SubprogramDeclaration( specification(f, t), [ Precondition( AndThen( *self.setter_preconditions(f), Call( "Field_Condition", [ Variable("Ctx"), Aggregate( Variable(f.affixed_name), Call("To_Base", [Variable("Val")]), ), ], ), Call("Valid", [Call("To_Base", [Variable("Val")])]) if not isinstance(t, Enumeration) else TRUE, common.sufficient_space_for_field_condition( Variable(f.affixed_name)), )), Postcondition( And( Call("Has_Buffer", [Variable("Ctx")]), Call("Valid", [ Variable("Ctx"), Variable(f.affixed_name) ]), Equal( Call(f"Get_{f.name}", [Variable("Ctx")]), Aggregate(TRUE, Variable("Val")) if isinstance(t, Enumeration) and t.always_valid else Variable("Val"), ), *self.setter_postconditions(message, f), *[ Equal( Call( "Context_Cursor", [ Variable("Ctx"), Variable(p.affixed_name) ], ), Old( Call( "Context_Cursor", [ Variable("Ctx"), Variable(p.affixed_name) ], )), ) for p in message.predecessors(f) ], )), ], ) for f, t in scalar_fields.items() ], [ SubprogramBody( specification(f, t), [ ObjectDeclaration( ["Field_Value"], "Field_Dependent_Value", Aggregate( Variable(f.affixed_name), Call("To_Base", [Variable("Val")]), ), True, ), ObjectDeclaration(["First", "Last"], const.TYPES_BIT_INDEX), ], [ CallStatement( "Reset_Dependent_Fields", [Variable("Ctx"), Variable(f.affixed_name)], ), CallStatement( "Set_Field_Value", [ Variable("Ctx"), Variable("Field_Value"), Variable("First"), Variable("Last"), ], ), Assignment( "Ctx", Aggregate( Variable("Ctx.Buffer_First"), Variable("Ctx.Buffer_Last"), Variable("Ctx.First"), Variable("Last"), Variable("Ctx.Buffer"), Variable("Ctx.Cursors"), ), ), Assignment( Indexed(Variable("Ctx.Cursors"), Variable(f.affixed_name)), NamedAggregate( ("State", Variable("S_Valid")), ("First", Variable("First")), ("Last", Variable("Last")), ("Value", Variable("Field_Value")), ( "Predecessor", Selected( Indexed(Variable("Ctx.Cursors"), Variable(f.affixed_name)), "Predecessor", ), ), ), ), Assignment( Indexed( Variable("Ctx.Cursors"), Call("Successor", [ Variable("Ctx"), Variable(f.affixed_name) ]), ), NamedAggregate( ("State", Variable("S_Invalid")), ("Predecessor", Variable(f.affixed_name)), ), ), ], ) for f, t in scalar_fields.items() ], )
def create_opaque_getter_procedures( self, message: Message, opaque_fields: Sequence[Field]) -> UnitPart: def specification(field: Field) -> ProcedureSpecification: return ProcedureSpecification( f"Get_{field.name}", [ Parameter(["Ctx"], "Context"), OutParameter(["Data"], const.TYPES_BYTES) ], ) return UnitPart( [ SubprogramDeclaration( specification(f), [ Precondition( AndThen( Call( self.prefix * message.identifier * "Has_Buffer", [Variable("Ctx")], ), Call( self.prefix * message.identifier * "Structural_Valid", [ Variable("Ctx"), Variable( self.prefix * message.identifier * f.affixed_name), ], ), Call( self.prefix * message.identifier * "Valid_Next", [ Variable("Ctx"), Variable( self.prefix * message.identifier * f.affixed_name), ], ), Equal( Length("Data"), Call( const.TYPES_TO_LENGTH, [ Call( self.prefix * message.identifier * "Field_Size", [ Variable("Ctx"), Variable(self.prefix * message.identifier * f.affixed_name), ], ) ], ), ), )), Postcondition( Call( "Equal", [ Variable("Ctx"), Variable(f.affixed_name), Variable("Data"), ], )), ], ) 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, ), ], [ Assignment( "Data", NamedAggregate( ("others", First(const.TYPES_BYTE))), ), Assignment( Slice( Variable("Data"), First("Data"), Add(First("Data"), Sub(Variable("Last"), Variable("First"))), ), Slice( Variable("Ctx.Buffer.all"), Variable("First"), Variable("Last"), ), ), ], ) for f in opaque_fields ], )
def create_opaque_getter_functions( self, message: Message, opaque_fields: Sequence[Field]) -> UnitPart: def name(field: Field) -> str: return f"Get_{field.name}" def specification(field: Field) -> FunctionSpecification: return FunctionSpecification( name(field), const.TYPES_BYTES, [Parameter(["Ctx"], "Context")], ) return UnitPart( [ SubprogramDeclaration( specification(f), [ Ghost(), Precondition( AndThen( Call( self.prefix * message.identifier * "Has_Buffer", [Variable("Ctx")], ), Call( self.prefix * message.identifier * "Structural_Valid", [ Variable("Ctx"), Variable( self.prefix * message.identifier * f.affixed_name), ], ), Call( self.prefix * message.identifier * "Valid_Next", [ Variable("Ctx"), Variable( self.prefix * message.identifier * f.affixed_name), ], ), )), Postcondition( Equal( Length(Result(name(f))), Call( const.TYPES_TO_LENGTH, [ Call( "Field_Size", [ Variable("Ctx"), Variable(f.affixed_name), ], ) ], ), )), ], ) 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, ), ], [ ReturnStatement( Slice(Variable("Ctx.Buffer.all"), Variable("First"), Variable("Last")), ) ], ) for f in opaque_fields ], )
def create_get_function( self, message: Message, scalar_fields: Mapping[Field, Scalar], composite_fields: Sequence[Field], ) -> UnitPart: if not scalar_fields: return UnitPart() comparison_to_aggregate = any( (isinstance(t, Composite) and common.has_aggregate_dependent_condition(message, f)) for f, t in message.field_types.items()) big_endian_fields = [ f for f in scalar_fields if message.byte_order[f] == ByteOrder.HIGH_ORDER_FIRST ] little_endian_fields = [ f for f in scalar_fields if message.byte_order[f] == ByteOrder.LOW_ORDER_FIRST ] return UnitPart( [], [ SubprogramBody( FunctionSpecification( "Get", const.TYPES_BASE_INT, [ Parameter(["Ctx"], "Context"), Parameter(["Fld"], "Field") ], ), [ *common.field_bit_location_declarations( Variable("Fld")), ObjectDeclaration( ["Buffer_First"], const.TYPES_INDEX, Call(const.TYPES_TO_INDEX, [Variable("First")]), constant=True, ), ObjectDeclaration( ["Buffer_Last"], const.TYPES_INDEX, Call(const.TYPES_TO_INDEX, [Variable("Last")]), constant=True, ), ObjectDeclaration( ["Offset"], const.TYPES_OFFSET, Call( const.TYPES_OFFSET, [ Mod( Sub( Size(const.TYPES_BYTE), Mod(Variable("Last"), Size(const.TYPES_BYTE)), ), Size(const.TYPES_BYTE), ) ], ), constant=True, ), ObjectDeclaration( ["Size"], "Positive", Case( Variable("Fld"), [ *[(Variable(f.affixed_name), t.size.ada_expr()) for f, t in scalar_fields.items()], *([(Variable("others"), Last("Positive"))] if composite_fields else []), ], ), constant=True, ), ObjectDeclaration( ["Byte_Order"], const.TYPES_BYTE_ORDER, If( [( In( Variable("Fld"), ChoiceList(*[ Variable(f.affixed_name) for f in big_endian_fields ]), ), Variable(const.TYPES_HIGH_ORDER_FIRST), )], Variable(const.TYPES_LOW_ORDER_FIRST), ) if big_endian_fields and little_endian_fields else Variable(const.TYPES_HIGH_ORDER_FIRST) if big_endian_fields else Variable( const.TYPES_LOW_ORDER_FIRST), constant=True, ), ] if scalar_fields or comparison_to_aggregate else [], [ ReturnStatement( Call( const.TYPES * "Extract", [ Variable("Ctx.Buffer"), Variable("Buffer_First"), Variable("Buffer_Last"), Variable("Offset"), Variable("Size"), Variable("Byte_Order"), ], ), ), ], [ Precondition( AndThen( Call( self.prefix * message.identifier * "Has_Buffer", [Variable("Ctx")], ), Call( self.prefix * message.identifier * "Valid_Next", [Variable("Ctx"), Variable("Fld")], ), Call( self.prefix * message.identifier * "Sufficient_Buffer_Length", [Variable("Ctx"), Variable("Fld")], ), *([ Not( Call( self.prefix * message.identifier * "Composite_Field", [Variable("Fld")], )) ] if composite_fields else []), )), ], ), ], )
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 ], )