def create_scalar_accessor_functions(scalar_fields: Mapping[Field, Scalar]) -> UnitPart: def specification(field: Field, field_type: Type) -> FunctionSpecification: return FunctionSpecification( f"Get_{field.name}", field_type.full_name, [Parameter(["Ctx"], "Context")] ) def result(field: Field, field_type: Type) -> Expr: value = Selected( Indexed("Ctx.Cursors", Name(field.affixed_name)), f"Value.{field.name}_Value" ) if isinstance(field_type, Enumeration): return Call("Convert", [value]) return value return UnitPart( [ SubprogramDeclaration( specification(f, t), [ Precondition( And(VALID_CONTEXT, Call("Valid", [Name("Ctx"), Name(f.affixed_name)])) ) ], ) for f, t in scalar_fields.items() ], [ ExpressionFunctionDeclaration(specification(f, t), result(f, t)) for f, t in scalar_fields.items() ], )
def composite_setter_postconditions( self, message: Message, field: Field, field_type: Type ) -> Sequence[Expr]: return [ VALID_CONTEXT, Call("Has_Buffer", [Name("Ctx")]), *self.setter_postconditions(message, field, field_type), Call("Structural_Valid", [Name("Ctx"), Name(field.affixed_name)]), ]
def setter_preconditions(self, field: Field) -> Sequence[Expr]: return [ VALID_CONTEXT, Not(Constrained("Ctx")), Call("Has_Buffer", [Name("Ctx")]), Call("Valid_Next", [Name("Ctx"), Name(field.affixed_name)]), LessEqual( Call("Field_Last", [Name("Ctx"), Name(field.affixed_name)]), Div(Last(self.types.bit_index), Number(2)), ), ]
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 test_ass_expr_converted(self) -> None: self.assertEqual( And(Variable("X"), Number(1)).converted( lambda x: Name(x.name) if isinstance(x, Variable) else x ), And(Name("X"), Number(1)), ) self.assertEqual( Mul(Variable("X"), Number(1)).converted( lambda x: Name("Y") if x == Mul(Variable("X"), Number(1)) else x ), Name("Y"), )
def test_bin_expr_converted(self) -> None: self.assertEqual( Less(Variable("X"), Number(1)).converted( lambda x: Name(x.name) if isinstance(x, Variable) else x ), Less(Name("X"), Number(1)), ) self.assertEqual( Sub(Variable("X"), Number(1)).converted( lambda x: Name("Y") if x == Sub(Variable("X"), Number(1)) else x ), Name("Y"), )
def unbounded_composite_setter_preconditions( self, message: Message, field: Field ) -> Sequence[Expr]: return [ Call( "Field_Condition", [Name("Ctx"), NamedAggregate(("Fld", Name(field.affixed_name)))] + ( [Call("Field_Length", [Name("Ctx"), Name(field.affixed_name)],)] if length_dependent_condition(message) else [] ), ), self.common.sufficient_space_for_field_condition(Name(field.affixed_name)), ]
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)
def result(field: Field, field_type: Type) -> Expr: value = Selected( Indexed("Ctx.Cursors", Name(field.affixed_name)), f"Value.{field.name}_Value" ) if isinstance(field_type, Enumeration): return Call("Convert", [value]) return value
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_structural_valid_function() -> UnitPart: specification = FunctionSpecification( "Structural_Valid", "Boolean", [Parameter(["Ctx"], "Context"), Parameter(["Fld"], "Field")], ) return UnitPart( [SubprogramDeclaration(specification, [Precondition(VALID_CONTEXT)])], [ ExpressionFunctionDeclaration( specification, And( Or( *[ Equal( Selected(Indexed("Ctx.Cursors", Name("Fld")), "State"), Name(s) ) for s in ("S_Valid", "S_Structural_Valid") ] ) ), ) ], )
def test_quantified_expression_variables(self) -> None: self.assertEqual( ForAllOf( "A", Name("List"), Add(Variable("X"), Add(Variable("Y"), Variable("Z"))) ).variables(), [Variable("X"), Variable("Y"), Variable("Z")], )
def valid_path_to_next_field_condition( self, message: Message, field: Field, field_type: Type ) -> Sequence[Expr]: return [ If( [ ( l.condition, And( Equal( Call("Predecessor", [Name("Ctx"), Name(l.target.affixed_name)],), Name(field.affixed_name), ), Call("Valid_Next", [Name("Ctx"), Name(l.target.affixed_name)]) if l.target != FINAL else TRUE, ), ) ] ).simplified( { **{ Variable(field.name): Call("Convert", [Name("Value")]) if isinstance(field_type, Enumeration) and field_type.always_valid else Name("Value") }, **self.public_substitution(message), } ) for l in message.outgoing(field) if l.target != FINAL ]
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 setter_postconditions( self, message: Message, field: Field, field_type: Type ) -> Sequence[Expr]: return [ *[ Call("Invalid", [Name("Ctx"), Name(p.affixed_name)]) for p in message.successors(field) if p != FINAL ], *self.common.valid_path_to_next_field_condition(message, field, field_type), *[ Equal(e, Old(e)) for e in [ Selected("Ctx", "Buffer_First"), Selected("Ctx", "Buffer_Last"), Selected("Ctx", "First"), Call("Predecessor", [Name("Ctx"), Name(field.affixed_name)]), Call("Valid_Next", [Name("Ctx"), Name(field.affixed_name)]), ] + [ Call(f"Get_{p.name}", [Name("Ctx")]) for p in message.definite_predecessors(field) if isinstance(message.types[p], Scalar) ] ], ]
def field_byte_location_declarations(self) -> Sequence[Declaration]: return [ 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")]), ), ExpressionFunctionDeclaration( FunctionSpecification("Offset", self.types.offset), Call( self.types.offset, [Mod(Sub(Number(8), Mod(Name("Last"), Number(8))), Number(8))], ), ), ]
def create_incomplete_message_function(message: Message) -> UnitPart: specification = FunctionSpecification( "Incomplete_Message", "Boolean", [Parameter(["Ctx"], "Context")] ) return UnitPart( [SubprogramDeclaration(specification, [Precondition(VALID_CONTEXT)])], [ ExpressionFunctionDeclaration( specification, Or( *[ Call("Incomplete", [Name("Ctx"), Name(f.affixed_name)]) for f in message.fields ] ), ) ], )
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) ] )
def create_present_function() -> UnitPart: specification = FunctionSpecification( "Present", "Boolean", [Parameter(["Ctx"], "Context"), Parameter(["Fld"], "Field")] ) return UnitPart( [SubprogramDeclaration(specification, [Precondition(VALID_CONTEXT)])], [ ExpressionFunctionDeclaration( specification, AndThen( Call("Structural_Valid", [Indexed("Ctx.Cursors", Name("Fld"))]), Less( Selected(Indexed("Ctx.Cursors", Name("Fld")), "First"), Add(Selected(Indexed("Ctx.Cursors", Name("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", [Name("Ctx"), Name(l.target.affixed_name)], ), l.condition, valid_message_condition(message, l.target, structural), ) for l in message.outgoing(field) ] )
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 create_verify_message_procedure( message: Message, context_invariant: Sequence[Expr] ) -> UnitPart: specification = ProcedureSpecification( "Verify_Message", [InOutParameter(["Ctx"], "Context")] ) return UnitPart( [ SubprogramDeclaration( specification, [ Precondition(VALID_CONTEXT), Postcondition( And( VALID_CONTEXT, Equal( Call("Has_Buffer", [Name("Ctx")]), Old(Call("Has_Buffer", [Name("Ctx")])), ), *context_invariant, ) ), ], ) ], [ SubprogramBody( specification, [], [ CallStatement("Verify", [Name("Ctx"), Name(f.affixed_name)]) for f in message.fields ], ) ], )
def substitution(self, message: Message, prefix: bool = True) -> Mapping[Name, Expr]: def prefixed(name: str) -> Expr: return Selected(Name("Ctx"), name) if prefix else Name(name) first = prefixed("First") last = prefixed("Last") cursors = prefixed("Cursors") return { **{First("Message"): first}, **{Last("Message"): last}, **{ First(f.name): Selected(Indexed(cursors, Name(f.affixed_name)), "First") for f in message.fields }, **{ Last(f.name): Selected(Indexed(cursors, Name(f.affixed_name)), "Last") for f in message.fields }, **{ Length(f.name): Add( Sub( Selected(Indexed(cursors, Name(f.affixed_name)), "Last"), Selected(Indexed(cursors, Name(f.affixed_name)), "First"), ), Number(1), ) for f in message.fields }, **{ Variable(f.name): Call( self.types.bit_length, [Selected(Indexed(cursors, Name(f.affixed_name)), f"Value.{f.name}_Value")], ) for f, t in message.types.items() if not isinstance(t, Enumeration) }, **{ Variable(f.name): Call( self.types.bit_length, [Selected(Indexed(cursors, Name(f.affixed_name)), f"Value.{f.name}_Value")], ) for f, t in message.types.items() if isinstance(t, Enumeration) }, **{ Variable(l): Call(self.types.bit_length, [Call("Convert", [Name(l)])]) for l in itertools.chain.from_iterable( t.literals.keys() for t in message.types.values() if isinstance(t, Enumeration) ) }, }
def prefixed(name: str) -> Expr: return Selected(Name("Ctx"), name) if prefix else Name(name)
from rflx.model import ( FINAL, INITIAL, Enumeration, Field, Link, Message, ModularInteger, RangeInteger, Scalar, Type, ) from .types import Types NULL = Name("null") VALID_CONTEXT = Call("Valid_Context", [Name("Ctx")]) # WORKAROUND: Componolit/Workarounds#1 class GeneratorCommon: def __init__(self, prefix: str = "") -> None: self.types = Types(prefix) def substitution(self, message: Message, prefix: bool = True) -> Mapping[Name, Expr]: def prefixed(name: str) -> Expr: return Selected(Name("Ctx"), name) if prefix else Name(name) first = prefixed("First") last = prefixed("Last") cursors = prefixed("Cursors")
def public_substitution(self, message: Message) -> Mapping[Name, Expr]: return { **{First("Message"): Selected(Name("Ctx"), "First")}, **{Last("Message"): Selected(Name("Ctx"), "Last")}, **{ First(f.name): Call("Field_First", [Name("Ctx"), Name(f.affixed_name)]) for f in message.fields }, **{ Last(f.name): Call("Field_Last", [Name("Ctx"), Name(f.affixed_name)]) for f in message.fields }, **{ Length(f.name): Call("Field_Length", [Name("Ctx"), Name(f.affixed_name)]) for f in message.fields }, **{ Variable(f.name): Call( self.types.bit_length, [Call(f"Get_{f.name}", [Name("Ctx")])] ) for f, t in message.types.items() if not isinstance(t, Enumeration) }, **{ Variable(f.name): Call( self.types.bit_length, [Call("Convert", [Call(f"Get_{f.name}", [Name("Ctx")])])] ) for f, t in message.types.items() if isinstance(t, Enumeration) }, **{ Variable(l): Call(self.types.bit_length, [Call("Convert", [Name(l)])]) for l in itertools.chain.from_iterable( t.literals.keys() for t in message.types.values() if isinstance(t, Enumeration) ) }, }
def initialize_field_statements(self, message: Message, field: Field) -> Sequence[Statement]: return [ CallStatement("Reset_Dependent_Fields", [Name("Ctx"), Name(field.affixed_name)],), Assignment( "Ctx", Aggregate( Selected("Ctx", "Buffer_First"), Selected("Ctx", "Buffer_Last"), Selected("Ctx", "First"), Name("Last"), Selected("Ctx", "Buffer"), Selected("Ctx", "Cursors"), ), ), # 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(self.message_structure_invariant(message, prefix=True))], ), Assignment( Indexed(Selected("Ctx", "Cursors"), Name(field.affixed_name)), NamedAggregate( ("State", Name("S_Structural_Valid")), ("First", Name("First")), ("Last", Name("Last")), ("Value", NamedAggregate(("Fld", Name(field.affixed_name))),), ( "Predecessor", Selected( Indexed(Selected("Ctx", "Cursors"), Name(field.affixed_name),), "Predecessor", ), ), ), ), Assignment( Indexed( Selected("Ctx", "Cursors"), Call("Successor", [Name("Ctx"), Name(field.affixed_name)]), ), NamedAggregate( ("State", Name("S_Invalid")), ("Predecessor", Name(field.affixed_name)), ), ), ]
def sufficient_space_for_field_condition(field_name: Name) -> Expr: return GreaterEqual( Call("Available_Space", [Name("Ctx"), field_name]), Call("Field_Length", [Name("Ctx"), field_name]), )
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 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), )