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 _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 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 valid_path_to_next_field_condition( message: model.Message, field: model.Field, prefix: str ) -> Sequence[Expr]: return [ If( [ ( l.condition.substituted(substitution(message, public=True, prefix=prefix)) .simplified() .ada_expr(), 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 != model.FINAL else TRUE, ), ) ] ) for l in message.outgoing(field) if l.target != model.FINAL ]
def _create_init_proc(self, slots: Sequence[NumberedSlotInfo]) -> UnitPart: proc = ProcedureSpecification( "Initialize", [OutParameter(["S"], "Slots"), Parameter(["M"], "Memory")]) return UnitPart( [ SubprogramDeclaration( proc, [Postcondition(Call("Initialized", [Variable("S")]))]), ], [ SubprogramBody( proc, declarations=[], statements=([ Assignment( "S" * self._slot_name(slot.slot_id), UnrestrictedAccess( Variable(ID(f"M.Slot_{slot.slot_id}"))), ) for slot in slots ] if slots else [NullStatement()]), aspects=[SparkMode(off=True)], ) ], )
def create_scalar_getter_functions( self, message: Message, scalar_fields: Mapping[Field, Scalar]) -> UnitPart: def specification(field: Field, field_type: Type) -> FunctionSpecification: if field_type.package == BUILTINS_PACKAGE: type_identifier = ID(field_type.name) else: type_identifier = self.prefix * field_type.identifier return FunctionSpecification(f"Get_{field.name}", type_identifier, [Parameter(["Ctx"], "Context")]) def result(field: Field) -> Expr: return Call( "To_Actual", [ Selected( Indexed(Variable("Ctx.Cursors"), Variable(field.affixed_name)), "Value") ], ) return UnitPart( [ # https://github.com/Componolit/Workarounds/issues/31 Pragma( "Warnings", [Variable("Off"), String("precondition is always False")]), *[ SubprogramDeclaration( specification(f, t), [ Precondition( Call( self.prefix * message.identifier * "Valid", [ Variable("Ctx"), Variable( self.prefix * message.identifier * f.affixed_name), ], ), ) ], ) for f, t in scalar_fields.items() ], Pragma( "Warnings", [Variable("On"), String("precondition is always False")]), ], private=[ ExpressionFunctionDeclaration(specification(f, t), result(f)) for f, t in scalar_fields.items() ], )
def result(field: Field) -> Expr: return Call( "To_Actual", [ Selected( Indexed(Variable("Ctx.Cursors"), Variable(field.affixed_name)), "Value") ], )
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 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 context_invariant(message: model.Message, loop_entry: bool = False) -> Sequence[Expr]: return [ Equal(e, LoopEntry(e) if loop_entry else Old(e)) for e in [ Variable("Ctx.Buffer_First"), Variable("Ctx.Buffer_Last"), Variable("Ctx.First"), Variable("Ctx.Last"), *[Variable("Ctx" * ID(p.name)) for p in message.parameters], ] ]
def initialize_conditions(message: model.Message) -> Sequence[Expr]: return [ *[ Equal( Variable("Ctx" * ID(p.name)), Variable(p.name), ) for p, t in message.parameter_types.items() ], Call("Initialized", [Variable("Ctx")]), ]
def _create_init_pred(self, slots: Sequence[NumberedSlotInfo]) -> UnitPart: return UnitPart([ ExpressionFunctionDeclaration( FunctionSpecification("Initialized", "Boolean", [Parameter(["S"], "Slots")]), And(*[ NotEqual( Variable("S" * self._slot_name(slot.slot_id)), Variable("null"), ) 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 field_bit_location_declarations(field_name: Name) -> Sequence[Declaration]: return [ ObjectDeclaration( ["First"], const.TYPES_BIT_INDEX, Call("Field_First", [Variable("Ctx"), field_name]), constant=True, ), ObjectDeclaration( ["Last"], const.TYPES_BIT_INDEX, Call("Field_Last", [Variable("Ctx"), field_name]), constant=True, ), ]
def create_valid_message_function(self, message: Message) -> UnitPart: specification = FunctionSpecification("Valid_Message", "Boolean", [Parameter(["Ctx"], "Context")]) return UnitPart( [ SubprogramDeclaration( specification, [ Precondition( Call( self.prefix * message.identifier * "Has_Buffer", [Variable("Ctx")], )) ], ) ], private=[ ExpressionFunctionDeclaration( specification, self.valid_message_condition(message), ) ], )
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 create_incomplete_function() -> UnitPart: specification = FunctionSpecification( "Incomplete", "Boolean", [Parameter(["Ctx"], "Context"), Parameter(["Fld"], "Field")]) return UnitPart( [SubprogramDeclaration(specification)], private=[ ExpressionFunctionDeclaration( specification, Equal( Selected( Indexed(Variable("Ctx.Cursors"), Variable("Fld")), "State"), Variable("S_Incomplete"), ), ) ], )
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_global_allocated_pred( self, slots: Sequence[NumberedSlotInfo]) -> UnitPart: return UnitPart([ ExpressionFunctionDeclaration( FunctionSpecification("Global_Allocated", "Boolean", [Parameter(["S"], "Slots")]), And( *[ Equal( Variable("S" * self._slot_name(slot.slot_id)), Variable("null"), ) for slot in slots if slot.global_ ], *[ NotEqual( Variable("S" * self._slot_name(slot.slot_id)), Variable("null"), ) for slot in slots if not slot.global_ ], ), ) ])
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 create_incomplete_message_function() -> UnitPart: specification = FunctionSpecification("Incomplete_Message", "Boolean", [Parameter(["Ctx"], "Context")]) return UnitPart( [ # https://github.com/Componolit/Workarounds/issues/47 Pragma( "Warnings", [ Variable("Off"), String( "postcondition does not mention function result") ], ), SubprogramDeclaration(specification, [Postcondition(TRUE)]), Pragma( "Warnings", [ Variable("On"), String( "postcondition does not mention function result") ], ), ], private=[ ExpressionFunctionDeclaration( specification, ForSomeIn( "F", Variable("Field"), Call( "Incomplete", [Variable("Ctx"), Variable("F")], ), ), ) ], )
def context_cursors_initialization(message: model.Message) -> Expr: return NamedAggregate( ( message.fields[0].affixed_name, NamedAggregate( ("State", Variable("S_Invalid")), ( "Predecessor", Variable(model.INITIAL.affixed_name), ), ), ), ( "others", NamedAggregate( ("State", Variable("S_Invalid")), ( "Predecessor", Variable(model.FINAL.affixed_name), ), ), ), )
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 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_finalize_proc(self, slots: Sequence[NumberedSlotInfo]) -> UnitPart: proc = ProcedureSpecification("Finalize", [InOutParameter(["S"], "Slots")]) return UnitPart( [ SubprogramDeclaration( proc, [Postcondition(Call("Uninitialized", [Variable("S")]))]), ], [ SubprogramBody( proc, declarations=[], statements=([ Assignment( "S" * self._slot_name(slot.slot_id), Variable("null"), ) for slot in slots ] if slots else [NullStatement()]), aspects=[SparkMode(off=True)], ) ], )
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 _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 context_cursor_unchanged( message: model.Message, field: model.Field, predecessors: bool ) -> List[Expr]: lower: model.Field upper: model.Field if predecessors: if len(message.predecessors(field)) == 0: return [] lower = message.fields[0] upper = message.fields[message.fields.index(field) - 1] else: if len(message.successors(field)) == 0: return [] lower = message.fields[message.fields.index(field) + 1] upper = message.fields[-1] return [ ForAllIn( "F", ValueRange( lower=Variable(lower.affixed_name), upper=Variable(upper.affixed_name), type_identifier=ID("Field"), ), Equal( Call( "Context_Cursors_Index", [ Call( "Context_Cursors", [Variable("Ctx")], ), Variable("F"), ], ), Call( "Context_Cursors_Index", [ Old( Call( "Context_Cursors", [Variable("Ctx")], ) ), Variable("F"), ], ), ), ) ]
def create_verify_message_procedure(self, message: Message) -> UnitPart: specification = ProcedureSpecification( "Verify_Message", [InOutParameter(["Ctx"], "Context")]) loop_invariant = And( Call("Has_Buffer", [Variable("Ctx")]), *common.context_invariant(message, loop_entry=True), ) 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, [], [ ForIn( "F", Variable("Field"), [ PragmaStatement("Loop_Invariant", [loop_invariant]), CallStatement("Verify", [Variable("Ctx"), Variable("F")]), ], ) ], ) ], )
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 ], )