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 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 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 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 valid_message_condition(message: Message, field: Field = INITIAL, structural: bool = False) -> Expr: if not message.outgoing(field): return TRUE return Or(*[ l.condition if l.target == FINAL else AndThen( Call( "Structural_Valid" if structural and isinstance( message.types[l.target], Composite) else "Valid", [Variable("Ctx" ), Variable(l.target.affixed_name)], ), l.condition, valid_message_condition(message, l.target, structural), ) for l in message.outgoing(field) ])
def test_log_expr_str() -> None: assert_equal( str(And(Variable("A"), Or(Variable("B"), Variable("C")), Variable("D"))), multilinestr( """A and (B or C) and D""" ), ) assert_equal( str(AndThen(Variable("A"), OrElse(Variable("B"), Variable("C")), Variable("D"))), multilinestr( """A and then (B or else C) and then D""" ), )
def create_internal_functions( self, message: Message, composite_fields: Sequence[Field] ) -> UnitPart: def result(field: Field, message: Message) -> NamedAggregate: aggregate: List[Tuple[str, Expr]] = [("Fld", Name(field.affixed_name))] if field in message.fields and isinstance(message.types[field], Scalar): aggregate.append( ( f"{field.name}_Value", Call( "Extract", [ Slice("Ctx.Buffer.all", Name("Buffer_First"), Name("Buffer_Last")), Name("Offset"), ], ), ) ) return NamedAggregate(*aggregate) return UnitPart( [], [ ExpressionFunctionDeclaration( FunctionSpecification( "Sufficient_Buffer_Length", "Boolean", [Parameter(["Ctx"], "Context"), Parameter(["Fld"], "Field")], ), And( NotEqual(Name("Ctx.Buffer"), NULL), LessEqual(Name("Ctx.First"), Div(Last(self.types.bit_index), Number(2))), LessEqual( Call("Field_First", [Name("Ctx"), Name("Fld")]), Div(Last(self.types.bit_index), Number(2)), ), GreaterEqual(Call("Field_Length", [Name("Ctx"), Name("Fld")]), Number(0)), LessEqual( Call("Field_Length", [Name("Ctx"), Name("Fld")]), Div(Last(self.types.bit_length), Number(2)), ), LessEqual( Add( Call("Field_First", [Name("Ctx"), Name("Fld")]), Call("Field_Length", [Name("Ctx"), Name("Fld")]), ), Div(Last(self.types.bit_length), Number(2)), ), LessEqual( Name("Ctx.First"), Call("Field_First", [Name("Ctx"), Name("Fld")]) ), GreaterEqual( Name("Ctx.Last"), Call("Field_Last", [Name("Ctx"), Name("Fld")]) ), ), [ Precondition( And( Call("Has_Buffer", [Name("Ctx")]), Call("Valid_Next", [Name("Ctx"), Name("Fld")]), ) ) ], ), ExpressionFunctionDeclaration( FunctionSpecification( "Composite_Field", "Boolean", [Parameter(["Fld"], "Field")] ), Case( Name("Fld"), [ (Name(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")], ), [ *self.common.field_bit_location_declarations(Name("Fld")), *self.common.field_byte_location_declarations(), ], [ ReturnStatement( Case( Name("Fld"), [ (Name(f.affixed_name), result(f, message)) for f in message.fields ], ) ) ], [ Precondition( AndThen( Call("Has_Buffer", [Name("Ctx")]), Call("Valid_Next", [Name("Ctx"), Name("Fld")]), Call("Sufficient_Buffer_Length", [Name("Ctx"), Name("Fld")]), ) ), Postcondition( Equal(Selected(Result("Get_Field_Value"), "Fld"), Name("Fld")) ), ], ), ], )
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 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 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"), ] ], ) ), ], ) ], )
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_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 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_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_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 [], )