def public_context_predicate() -> Expr: return And( GreaterEqual(Call(const.TYPES_TO_INDEX, [Variable("First")]), Variable("Buffer_First")), LessEqual(Call(const.TYPES_TO_INDEX, [Variable("Last")]), Variable("Buffer_Last")), Less(Variable("Buffer_Last"), Last(const.TYPES_INDEX)), LessEqual(Variable("First"), Add(Variable("Last"), Number(1))), Less(Variable("Last"), Last(const.TYPES_BIT_INDEX)), Equal(Rem(Variable("First"), Size(const.TYPES_BYTE)), Number(1)), Equal(Rem(Variable("Last"), Size(const.TYPES_BYTE)), Number(0)), )
def _create_ptr_subtypes(self, slots: Sequence[NumberedSlotInfo]) -> UnitPart: unit = UnitPart(specification=[ Subtype( self._ptr_type(size), const.TYPES_BYTES_PTR, aspects=[ DynamicPredicate( OrElse( Equal(Variable(self._ptr_type(size)), Variable("null")), AndThen( Equal(First(self._ptr_type(size)), First(const.TYPES_INDEX)), Equal( Last(self._ptr_type(size)), Add(First(const.TYPES_INDEX), Number(size - 1)), ), ), )) ], ) for size in sorted({slot.size for slot in slots}) ]) self._declaration_context.append( WithClause(self._prefix * const.TYPES_PACKAGE)) self._declaration_context.append( UseTypeClause(self._prefix * const.TYPES_INDEX)) self._declaration_context.append( UseTypeClause(self._prefix * const.TYPES_BYTES_PTR)) return unit
def create_present_function() -> UnitPart: specification = FunctionSpecification( "Present", "Boolean", [Parameter(["Ctx"], "Context"), Parameter(["Fld"], "Field")]) return UnitPart( [SubprogramDeclaration(specification)], private=[ 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 field_condition_call( prefix: str, message: model.Message, field: model.Field, value: Expr = None, aggregate: Expr = None, size: Expr = None, ) -> Expr: package = prefix * message.identifier if value is None: value = Number(0) if aggregate is None: aggregate = EMPTY_ARRAY if size is None: size = Call( package * "Field_Size", [Variable("Ctx"), Variable(package * field.affixed_name)], ) return Call( package * "Field_Condition", [ Variable("Ctx"), Variable(package * field.affixed_name), *([value] if has_value_dependent_condition(message) else []), *([aggregate] if has_aggregate_dependent_condition(message) else []), *([size] if has_size_dependent_condition(message, field) else []), ], )
def _create_memory(slots: Sequence[NumberedSlotInfo]) -> UnitPart: return UnitPart([ RecordType( "Memory", [ Component( f"Slot_{slot.slot_id}", Slice( Variable(const.TYPES_BYTES), First(const.TYPES_INDEX), Add(First(const.TYPES_INDEX), Number(slot.size - 1)), ), NamedAggregate(("others", Number(0))), aliased=True, ) for slot in slots ], ) ])
def byte_aligned_field(prefix: str, message: model.Message, field: model.Field) -> Expr: return Equal( Rem( Call( prefix * message.identifier * "Field_First", [ Variable("Ctx"), Variable(prefix * message.identifier * field.affixed_name), ], ), Size(const.TYPES_BYTE), ), Number(1), )
def cursors_invariant() -> Expr: return ForAllIn( "F", Variable("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("Verified_Last"), ), LessEqual( Selected(Indexed(Variable("Cursors"), Variable("F")), "First"), Add( Selected( Indexed(Variable("Cursors"), Variable("F")), "Last", ), Number(1), ), ), Call( "Valid_Value", [ Variable("F"), Selected( Indexed(Variable("Cursors"), Variable("F")), "Value", ), ], ), ), ) ] ), )
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 initialize_field_statements( field: model.Field, reset_written_last: bool = False ) -> Sequence[Statement]: return [ CallStatement( "Reset_Dependent_Fields", [Variable("Ctx"), Variable(field.affixed_name)], ), # https://github.com/Componolit/RecordFlux/issues/868 PragmaStatement( "Warnings", [ Variable("Off"), String("attribute Update is an obsolescent feature"), ], ), Assignment( "Ctx", Update( "Ctx", ("Verified_Last", Variable("Last")), ( "Written_Last", Variable("Last") if reset_written_last else Max( const.TYPES_BIT_LENGTH, Variable("Ctx.Written_Last"), Variable("Last"), ), ), ), ), PragmaStatement( "Warnings", [ Variable("On"), String("attribute Update is an obsolescent feature"), ], ), Assignment( Indexed(Variable("Ctx.Cursors"), Variable(field.affixed_name)), NamedAggregate( ("State", Variable("S_Structural_Valid")), ("First", Variable("First")), ("Last", Variable("Last")), ("Value", Number(0)), ( "Predecessor", Selected( Indexed( Variable("Ctx.Cursors"), Variable(field.affixed_name), ), "Predecessor", ), ), ), ), Assignment( Indexed( Variable("Ctx.Cursors"), Call( "Successor", [Variable("Ctx"), Variable(field.affixed_name)], ), ), NamedAggregate( ("State", Variable("S_Invalid")), ("Predecessor", Variable(field.affixed_name)), ), ), ]
def context_predicate( message: model.Message, composite_fields: Sequence[model.Field], prefix: str ) -> Expr: def cursors_invariant() -> Expr: return ForAllIn( "F", Variable("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("Verified_Last"), ), LessEqual( Selected(Indexed(Variable("Cursors"), Variable("F")), "First"), Add( Selected( Indexed(Variable("Cursors"), Variable("F")), "Last", ), Number(1), ), ), Call( "Valid_Value", [ Variable("F"), Selected( Indexed(Variable("Cursors"), Variable("F")), "Value", ), ], ), ), ) ] ), ) def valid_predecessors_invariant() -> Expr: return AndThen( *[ If( [ ( Call( "Structural_Valid", [ Indexed( Variable("Cursors"), Variable(f.affixed_name), ) ], ), Or( *[ expr.AndThen( expr.Call( "Structural_Valid" if l.source in composite_fields else "Valid", [ expr.Indexed( expr.Variable("Cursors"), expr.Variable(l.source.affixed_name), ) ], ), expr.Equal( expr.Selected( expr.Indexed( expr.Variable("Cursors"), expr.Variable(f.affixed_name), ), "Predecessor", ), expr.Variable(l.source.affixed_name), ), l.condition.substituted( substitution(message, embedded=True, prefix=prefix) ), ) .simplified() .ada_expr() for l in message.incoming(f) ] ), ) ] ) for f in message.fields if f not in message.direct_successors(model.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(model.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(), LessEqual(Sub(Variable("First"), Number(1)), Variable("Verified_Last")), LessEqual(Sub(Variable("First"), Number(1)), Variable("Written_Last")), LessEqual(Variable("Verified_Last"), Variable("Written_Last")), LessEqual(Variable("Written_Last"), Variable("Last")), Equal(Rem(Variable("First"), Size(const.TYPES_BYTE)), Number(1)), Equal(Rem(Variable("Last"), Size(const.TYPES_BYTE)), Number(0)), Equal(Rem(Variable("Verified_Last"), Size(const.TYPES_BYTE)), Number(0)), Equal(Rem(Variable("Written_Last"), Size(const.TYPES_BYTE)), Number(0)), cursors_invariant(), valid_predecessors_invariant(), invalid_successors_invariant(), message_structure_invariant(message, prefix, embedded=True), )
def message_structure_invariant( message: model.Message, prefix: str, link: model.Link = None, embedded: bool = False ) -> Expr: def prefixed(name: str) -> expr.Expr: return expr.Selected(expr.Variable("Ctx"), name) if not embedded else expr.Variable(name) if not link: return message_structure_invariant( message, prefix, message.outgoing(model.INITIAL)[0], embedded ) source = link.source target = link.target if target == model.FINAL: return TRUE field_type = message.types[target] condition = link.condition.substituted(substitution(message, prefix, embedded)).simplified() size = ( field_type.size if isinstance(field_type, model.Scalar) else link.size.substituted( substitution(message, prefix, embedded, target_type=const.TYPES_BIT_LENGTH) ).simplified() ) first = ( prefixed("First") if source == model.INITIAL else link.first.substituted( substitution(message, prefix, embedded, target_type=const.TYPES_BIT_INDEX) ) .substituted( mapping={ expr.UNDEFINED: expr.Add( expr.Selected( expr.Indexed(prefixed("Cursors"), expr.Variable(source.affixed_name)), "Last", ), expr.Number(1), ) } ) .simplified() ) invariant = [ message_structure_invariant(message, prefix, l, embedded) for l in message.outgoing(target) ] return If( [ ( AndThen( Call( "Structural_Valid", [Indexed(prefixed("Cursors").ada_expr(), Variable(target.affixed_name))], ), *([condition.ada_expr()] if condition != expr.TRUE else []), ), AndThen( Equal( Add( Sub( Selected( Indexed( prefixed("Cursors").ada_expr(), Variable(target.affixed_name), ), "Last", ), Selected( Indexed( prefixed("Cursors").ada_expr(), Variable(target.affixed_name), ), "First", ), ), Number(1), ), size.ada_expr(), ), Equal( Selected( Indexed( prefixed("Cursors").ada_expr(), Variable(target.affixed_name), ), "Predecessor", ), Variable(source.affixed_name), ), Equal( Selected( Indexed( prefixed("Cursors").ada_expr(), Variable(target.affixed_name), ), "First", ), first.ada_expr(), ), *[i for i in invariant if i != TRUE], ), ) ] )
Selected, Size, Statement, String, Sub, Update, ValueRange, Variable, WithClause, ) from rflx.const import BUILTINS_PACKAGE from rflx.identifier import ID from . import const EMPTY_ARRAY = NamedAggregate((ValueRange(Number(1), Number(0)), Number(0))) class Debug(enum.Enum): NONE = enum.auto() BUILTIN = enum.auto() EXTERNAL = enum.auto() def substitution( message: model.Message, prefix: str, embedded: bool = False, public: bool = False, target_type: Optional[ID] = const.TYPES_BASE_INT, ) -> Callable[[expr.Expr], expr.Expr]: