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 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 public_context_predicate() -> Expr: return And( GreaterEqual(Call(const.TYPES_BYTE_INDEX, [Variable("First")]), Variable("Buffer_First")), LessEqual(Call(const.TYPES_BYTE_INDEX, [Variable("Last")]), Variable("Buffer_Last")), LessEqual(Variable("First"), Variable("Last")), LessEqual(Variable("Last"), Div(Last(const.TYPES_BIT_INDEX), Number(2))), )
def create_verify_message_procedure( message: Message, context_invariant: Sequence[Expr]) -> UnitPart: specification = ProcedureSpecification( "Verify_Message", [InOutParameter(["Ctx"], "Context")]) return UnitPart( [ SubprogramDeclaration( specification, [ Postcondition( And( Equal( Call("Has_Buffer", [Variable("Ctx")]), Old(Call("Has_Buffer", [Variable("Ctx")])), ), *context_invariant, )), ], ) ], [ SubprogramBody( specification, [], [ CallStatement( "Verify", [Variable("Ctx"), Variable(f.affixed_name)]) for f in message.fields ], ) ], )
def test_attribute_expression_substituted() -> None: assert_equal( Val("X", Variable("Y")).substituted( lambda x: Number(42) if x == Val("X", Variable("Y")) else x ), Number(42), ) assert_equal( -Val("X", Variable("Y")).substituted( lambda x: Number(42) if x == Val("X", Variable("Y")) else x ), Number(-42), ) assert_equal( Val("X", Variable("Y")).substituted(lambda x: Call("Y") if x == Variable("Y") else x), Val("X", Call("Y")), ) assert_equal( -Val("X", Variable("Y")).substituted(lambda x: Call("Y") if x == Variable("Y") else x), -Val("X", Call("Y")), ) assert_equal( -Val("X", Variable("Y")).substituted( lambda x: Variable(f"P_{x}") if isinstance(x, Variable) else (Pos(x.prefix, x.expression) if isinstance(x, Val) else x) ), -Pos("P_X", Variable("P_Y")), )
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 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 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 composite_setter_postconditions(self, message: Message, field: Field) -> Sequence[Expr]: return [ Call("Has_Buffer", [Variable("Ctx")]), *self.setter_postconditions(message, field), Call("Structural_Valid", [Variable("Ctx"), Variable(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 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 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 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 setter_preconditions(field: Field) -> Sequence[Expr]: return [ Not(Constrained("Ctx")), Call("Has_Buffer", [Variable("Ctx")]), Call("Valid_Next", [Variable("Ctx"), Variable(field.affixed_name)]), LessEqual( Call("Field_Last", [Variable("Ctx"), Variable(field.affixed_name)]), Div(Last(const.TYPES_BIT_INDEX), Number(2)), ), ]
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 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 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 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_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 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 field_value(field: Field, field_type: Type) -> Expr: if isinstance(field_type, Enumeration): if public: return Call( target_type, [Call("To_Base", [Call(f"Get_{field.name}", [Variable("Ctx")])])], ) return Call( target_type, [ Selected( Indexed(cursors, Variable(field.affixed_name)), f"Value.{field.name}_Value" ) ], ) if isinstance(field_type, Scalar): if public: return Call(target_type, [Call(f"Get_{field.name}", [Variable("Ctx")])]) return Call( target_type, [ Selected( Indexed(cursors, Variable(field.affixed_name)), f"Value.{field.name}_Value" ) ], ) if isinstance(field_type, Composite): return Variable(field.name) assert False, f'unexpected type "{type(field_type).__name__}"' return UNDEFINED
def field_byte_location_declarations() -> Sequence[Declaration]: return [ 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")]), ), ExpressionFunctionDeclaration( FunctionSpecification("Offset", const.TYPES_OFFSET), Call( const.TYPES_OFFSET, [Mod(Sub(Number(8), Mod(Variable("Last"), Number(8))), Number(8))], ), ), ]
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 field_length(field: Field) -> Expr: if public: return Call("Field_Length", [Variable("Ctx"), Variable(field.affixed_name)]) return Add( Sub( Selected(Indexed(cursors, Variable(field.affixed_name)), "Last"), Selected(Indexed(cursors, Variable(field.affixed_name)), "First"), ), Number(1), )
def field_value(field: Field) -> Expr: if public: return Call(f"Get_{field.name}", [Variable("Ctx")]) return Selected( Indexed( Variable("Ctx.Cursors" if not embedded else "Cursors"), Variable(field.affixed_name), ), f"Value.{field.name}_Value", )
def test_attribute_substituted() -> None: assert_equal(First("X").substituted(lambda x: Number(42) if x == First("X") else x), Number(42)) assert_equal( -First("X").substituted(lambda x: Number(42) if x == First("X") else x), Number(-42) ) assert_equal( First("X").substituted(lambda x: Call("Y") if x == Variable("X") else x), First(Call("Y")), ) assert_equal( -First("X").substituted(lambda x: Call("Y") if x == Variable("X") else x), -First(Call("Y")), ) assert_equal( -First("X").substituted( lambda x: Variable(f"P_{x}") if isinstance(x, Variable) else (Last(x.prefix) if isinstance(x, First) else x) ), -Last(Variable("P_X")), )
def result(field: Field) -> Expr: return Call( "To_Actual", [ Selected( Indexed(Variable("Ctx.Cursors"), Variable(field.affixed_name)), f"Value.{field.name}_Value", ) ], )
def bounded_composite_setter_preconditions(message: Message, field: Field) -> Sequence[Expr]: return [ Call( "Field_Condition", [ Variable("Ctx"), NamedAggregate(("Fld", Variable(field.affixed_name))) ] + ([Variable("Length")] if common.length_dependent_condition(message) else []), ), GreaterEqual( Call("Available_Space", [Variable("Ctx"), Variable(field.affixed_name)]), Variable("Length"), ), LessEqual( Add( Call("Field_First", [Variable("Ctx"), Variable(field.affixed_name)]), Variable("Length"), ), Div(Last(const.TYPES_BIT_INDEX), Number(2)), ), Or(*[ And( *[ Call("Valid", [Variable("Ctx"), Variable(field.affixed_name)]) for field in message.fields if Variable(field.name) in l.condition.variables() ], l.condition.substituted( mapping={ Variable(field.name): Call(f"Get_{field.name}", [Variable("Ctx")]) for field in message.fields if Variable(field.name) in l.condition.variables() }), ) for l in message.incoming(field) if Last("Message") in l.length ]), Equal( Mod( Call("Field_First", [Variable("Ctx"), Variable(field.affixed_name)]), Size(const.TYPES_BYTE), ), Number(1), ), Equal( Mod(Variable("Length"), Size(const.TYPES_BYTE)), Number(0), ), ]
def valid_path_to_next_field_condition(message: Message, field: Field) -> Sequence[Expr]: return [ If( [ ( l.condition.substituted(substitution(message, public=True)), And( Equal( Call( "Predecessor", [Variable("Ctx"), Variable(l.target.affixed_name)], ), Variable(field.affixed_name), ), Call("Valid_Next", [Variable("Ctx"), Variable(l.target.affixed_name)]) if l.target != FINAL else TRUE, ), ) ] ).simplified() for l in message.outgoing(field) if l.target != FINAL ]
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) ) }, }