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 create_invalid_function() -> UnitPart: specification = FunctionSpecification( "Invalid", "Boolean", [Parameter(["Ctx"], "Context"), Parameter(["Fld"], "Field")]) return UnitPart( [SubprogramDeclaration(specification)], private=[ ExpressionFunctionDeclaration( specification, Or( Equal( Selected( Indexed(Variable("Ctx.Cursors"), Variable("Fld")), "State"), Variable("S_Invalid"), ), Equal( Selected( Indexed(Variable("Ctx.Cursors"), Variable("Fld")), "State"), Variable("S_Incomplete"), ), ), ) ], )
def set_context_cursor_composite_field(field_name: str) -> Assignment: return Assignment( Indexed( Variable("Ctx.Cursors"), Variable(field_name), ), NamedAggregate( ("State", Variable("S_Structural_Valid")), ( "First", Call( "Field_First", [Variable("Ctx"), Variable(field_name)], ), ), ( "Last", Call( "Field_Last", [Variable("Ctx"), Variable(field_name)], ), ), ("Value", Variable("Value")), ( "Predecessor", Selected( Indexed( Variable("Ctx.Cursors"), Variable(field_name), ), "Predecessor", ), ), ), )
def set_context_cursor_scalar() -> Assignment: return 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"), ), ), )
def result(field: Field) -> Expr: return Call( "To_Actual", [ Selected( Indexed(Variable("Ctx.Cursors"), Variable(field.affixed_name)), "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 unchanged_cursor_before_or_invalid( limit: Expr, loop_entry: bool, or_invalid: bool = True ) -> Expr: return ForAllIn( "F", Variable("Field"), IfExpr( [ ( Less(Variable("F"), limit), Equal( Indexed( Variable("Ctx.Cursors"), Variable("F"), ), Indexed( LoopEntry(Variable("Ctx.Cursors")) if loop_entry else Old(Variable("Ctx.Cursors")), Variable("F"), ), ), ) ], *( [ Call( "Invalid", [ Variable("Ctx"), Variable("F"), ], ) ] if or_invalid else [] ), ), )
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) ] )
def conditional_field_size(field: model.Field, message: model.Message, prefix: str) -> Expr: def substituted(expression: expr.Expr) -> Expr: return ( expression.substituted( substitution(message, prefix, target_type=const.TYPES_BIT_LENGTH) ) .simplified() .ada_expr() ) field_type = message.field_types[field] if isinstance(field_type, model.Scalar): return field_type.size.ada_expr() links = message.incoming(field) if len(links) == 1: return substituted(links[0].size) return If( [ ( AndThen( Equal( Selected( Indexed(Variable("Ctx.Cursors"), Variable("Fld")), "Predecessor", ), Variable(l.source.affixed_name), ), *([substituted(l.condition)] if l.condition != expr.TRUE else []), ), substituted(l.size), ) for l in links ], const.UNREACHABLE, )
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_structural_valid_function() -> UnitPart: specification = FunctionSpecification( "Structural_Valid", "Boolean", [Parameter(["Ctx"], "Context"), Parameter(["Fld"], "Field")], ) return UnitPart( [SubprogramDeclaration(specification)], private=[ ExpressionFunctionDeclaration( specification, Or(*[ Equal( Selected( Indexed(Variable("Ctx.Cursors"), Variable( "Fld")), "State"), Variable(s), ) for s in ("S_Valid", "S_Structural_Valid") ]), ) ], )
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 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 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 ], )
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 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], ), ) ] )