def test_eval_reference(): x = Reference(pc=0, value=parse_expr('2 * ap + 3 * fp - 5'), ap_tracking_data=RegTrackingData(group=1, offset=5)) with pytest.raises(FlowTrackingError): x.eval(RegTrackingData(group=2, offset=5)) assert x.eval(RegTrackingData( group=1, offset=8)).format() == '2 * (ap - 3) + 3 * fp - 5'
def handle_ReferenceDefinition(self, name: str, identifier: ReferenceDefinition, scope: ScopedName, set_value: Optional[MaybeRelocatable]): # In set mode, take the address of the given reference instead. reference = self._context.flow_tracking_data.resolve_reference( reference_manager=self._context.reference_manager, name=identifier.full_name) if set_value is None: expr = reference.eval(self._context.flow_tracking_data.ap_tracking) expr, expr_type = simplify_type_system(expr) if isinstance(expr_type, TypeStruct): # If the reference is of type T, take its address and treat it as T*. assert isinstance(expr, ExprDeref), \ f"Expected expression of type '{expr_type.format()}' to have an address." expr = expr.addr expr_type = TypePointer(pointee=expr_type) val = self._context.evaluator(expr) # Check if the type is felt* or any_type**. is_pointer_to_felt_or_pointer = ( isinstance(expr_type, TypePointer) and isinstance(expr_type.pointee, (TypePointer, TypeFelt))) if isinstance(expr_type, TypeFelt) or is_pointer_to_felt_or_pointer: return val else: # Typed reference, return VmConstsReference which allows accessing members. assert isinstance(expr_type, TypePointer) and \ isinstance(expr_type.pointee, TypeStruct), \ 'Type must be of the form T*.' return VmConstsReference( context=self._context, accessible_scopes=[expr_type.pointee.scope], reference_value=val, add_addr_var=True) else: assert str(scope[-1:]) == name, 'Expecting scope to end with name.' value, value_type = simplify_type_system(reference.value) assert isinstance(value, ExprDeref), f"""\ {scope} (= {value.format()}) does not reference memory and cannot be assigned.""" value_ref = Reference( pc=reference.pc, value=ExprCast(expr=value.addr, dest_type=value_type), ap_tracking_data=reference.ap_tracking_data, ) addr = self._context.evaluator( value_ref.eval(self._context.flow_tracking_data.ap_tracking)) self._context.memory[addr] = set_value
def test_revoked_reference(): reference_manager = ReferenceManager() ref_id = reference_manager.alloc_id(reference=Reference( pc=0, value=parse_expr('[ap + 1]'), ap_tracking_data=RegTrackingData(group=0, offset=2), )) identifier_values = { scope('x'): ReferenceDefinition(full_name=scope('x'), cairo_type=TypeFelt(), references=[]), } prime = 2**64 + 13 ap = 100 fp = 200 memory = {} flow_tracking_data = FlowTrackingDataActual( ap_tracking=RegTrackingData(group=1, offset=4), reference_ids={scope('x'): ref_id}, ) context = VmConstsContext( identifiers=IdentifierManager.from_dict(identifier_values), evaluator=ExpressionEvaluator(prime, ap, fp, memory).eval, reference_manager=reference_manager, flow_tracking_data=flow_tracking_data, memory=memory, pc=0) consts = VmConsts(context=context, accessible_scopes=[ScopedName()]) with pytest.raises(FlowTrackingError, match='Failed to deduce ap.'): assert consts.x
def test_reference_rebinding(): identifier_values = { scope('ref'): ReferenceDefinition( full_name=scope('ref'), cairo_type=TypeFelt(), references=[], ) } reference_manager = ReferenceManager() flow_tracking_data = FlowTrackingDataActual(ap_tracking=RegTrackingData()) consts = get_vm_consts(identifier_values, reference_manager, flow_tracking_data) with pytest.raises(FlowTrackingError, match='Reference ref revoked'): consts.ref flow_tracking_data = flow_tracking_data.add_reference( reference_manager=reference_manager, name=scope('ref'), ref=Reference( pc=10, value=parse_expr('10'), ap_tracking_data=RegTrackingData(group=0, offset=2), ), ) consts = get_vm_consts(identifier_values, reference_manager, flow_tracking_data) assert consts.ref == 10
def test_reference_to_structs(): t = TypeStruct(scope=scope('T'), is_fully_resolved=True) t_star = TypePointer(pointee=t) identifier_values = { scope('ref'): ReferenceDefinition(full_name=scope('ref'), references=[]), scope('T.x'): MemberDefinition(offset=3, cairo_type=t_star), } reference_manager = ReferenceManager() flow_tracking_data = FlowTrackingDataActual(ap_tracking=RegTrackingData()) flow_tracking_data = flow_tracking_data.add_reference( reference_manager=reference_manager, name=scope('ref'), ref=Reference( pc=0, value=mark_types_in_expr_resolved(parse_expr('cast([100], T)')), ap_tracking_data=RegTrackingData(group=0, offset=2), )) consts = get_vm_consts(identifier_values, reference_manager, flow_tracking_data, memory={103: 200}) assert consts.ref.address_ == 100 assert consts.ref.x == 200
def test_offset_reference_definition_typed_members(): t = TypeStruct(scope=scope('T'), is_fully_resolved=True) t_star = TypePointer(pointee=t) reference_manager = ReferenceManager() main_reference = ReferenceDefinition(full_name=scope('a'), cairo_type=t_star, references=[]) references = { scope('a'): reference_manager.alloc_id( Reference( pc=0, value=mark_types_in_expr_resolved(parse_expr('cast(ap, T*)')), ap_tracking_data=RegTrackingData(group=0, offset=0), )), } flow_tracking_data = FlowTrackingDataActual( ap_tracking=RegTrackingData(group=0, offset=1), reference_ids=references, ) # Create OffsetReferenceDefinition instance for an expression of the form "a.<member_path>", # in this case a.x.y.z, and check the result of evaluation of this expression. definition = OffsetReferenceDefinition(parent=main_reference, member_path=scope('x.y.z')) assert definition.eval(reference_manager=reference_manager, flow_tracking_data=flow_tracking_data).format( ) == 'cast(ap - 1, T*).x.y.z'
def test_flow_tracking_converge_references(refs): flow_tracking = FlowTracking() flow_tracking.add_flow_to_label(ScopedName.from_string('a'), RegChangeUnknown()) flow_tracking.add_flow_to_label(ScopedName.from_string('b'), RegChangeUnknown()) # Label a. flow_tracking.revoke() flow_tracking.converge_with_label(ScopedName.from_string('a')) flow_tracking.add_reference( name=ScopedName.from_string('x'), ref=Reference( pc=0, value=refs.expr_a, ap_tracking_data=flow_tracking.get_ap_tracking(), )) flow_tracking.add_ap(13) flow_tracking.add_flow_to_label(ScopedName.from_string('c'), 0) # Label b. flow_tracking.revoke() flow_tracking.converge_with_label(ScopedName.from_string('b')) flow_tracking.add_reference( name=ScopedName.from_string('x'), ref=Reference( pc=0, value=refs.expr_b, ap_tracking_data=flow_tracking.get_ap_tracking(), )) flow_tracking.add_ap(15) flow_tracking.add_flow_to_label(ScopedName.from_string('c'), 0) # Label c - convergence. flow_tracking.revoke() flow_tracking.converge_with_label(ScopedName.from_string('c')) if refs.valid: flow_tracking.resolve_reference(ScopedName.from_string('x')) else: with pytest.raises(FlowTrackingError): flow_tracking.resolve_reference(ScopedName.from_string('x'))
def test_offset_reference_definition_typed_members(): t = TypeStruct(scope=scope('T'), is_fully_resolved=True) s_star = TypePointer( pointee=TypeStruct(scope=scope('S'), is_fully_resolved=True)) reference_manager = ReferenceManager() identifiers = { scope('T'): ScopeDefinition(), scope('T.x'): MemberDefinition(offset=3, cairo_type=s_star), scope('T.flt'): MemberDefinition(offset=4, cairo_type=TypeFelt()), scope('S'): ScopeDefinition(), scope('S.x'): MemberDefinition(offset=10, cairo_type=t), } main_reference = ReferenceDefinition(full_name=scope('a'), references=[]) references = { scope('a'): reference_manager.get_id( Reference( pc=0, value=mark_types_in_expr_resolved(parse_expr('cast(ap, T*)')), ap_tracking_data=RegTrackingData(group=0, offset=0), )), } flow_tracking_data = FlowTrackingDataActual( ap_tracking=RegTrackingData(group=0, offset=1), reference_ids=references, ) # Create OffsetReferenceDefinition instances for expressions of the form "a.<member_path>", # such as a.x and a.x.x, and check the result of evaluation those expressions. for member_path, expected_result in [ ('x', 'cast([ap - 1 + 3], S*)'), ('x.x', 'cast([[ap - 1 + 3] + 10], T)'), ('x.x.x', 'cast([&[[ap - 1 + 3] + 10] + 3], S*)'), ('x.x.flt', 'cast([&[[ap - 1 + 3] + 10] + 4], felt)') ]: definition = OffsetReferenceDefinition(parent=main_reference, identifier_values=identifiers, member_path=scope(member_path)) assert definition.eval( reference_manager=reference_manager, flow_tracking_data=flow_tracking_data).format() == expected_result definition = OffsetReferenceDefinition(parent=main_reference, identifier_values=identifiers, member_path=scope('x.x.flt.x')) with pytest.raises( DefinitionError, match='Member access requires a type of the form Struct*.'): assert definition.eval( reference_manager=reference_manager, flow_tracking_data=flow_tracking_data).format() == expected_result
def converge( self, reference_manager: ReferenceManager, other: 'FlowTrackingData', group_alloc: Callable): if not isinstance(other, FlowTrackingDataActual): return other.converge(reference_manager, self, group_alloc) new_ap_tracking = self.ap_tracking.converge(other.ap_tracking, group_alloc) simplifier = ExpressionSimplifier() # Allow different references from different branches to unite if they have the same name # and the same expression at the converged ap_tracking. reference_ids = {} for name, ref_id in self.reference_ids.items(): other_ref_id = other.reference_ids.get(name) if other_ref_id is None: continue reference = reference_manager.get_ref(ref_id) other_ref = reference_manager.get_ref(other_ref_id) try: ref_expr = reference.eval(self.ap_tracking) if simplifier.visit(ref_expr) == \ simplifier.visit(other_ref.eval(other.ap_tracking)): # Same expression. if self.ap_tracking != new_ap_tracking: # Create a new reference on the new ap tracking. new_reference = Reference( pc=reference.pc, value=ref_expr, ap_tracking_data=new_ap_tracking, ) ref_id = reference_manager.get_id(new_reference) reference_ids[name] = ref_id except FlowTrackingError: pass return FlowTrackingDataActual( ap_tracking=new_ap_tracking, reference_ids=reference_ids, )
def test_reference_to_structs(): t = TypeStruct(scope=scope('T'), is_fully_resolved=True) t_star = TypePointer(pointee=t) identifier_values = { scope('ref'): ReferenceDefinition( full_name=scope('ref'), cairo_type=t, references=[] ), scope('T'): StructDefinition( full_name=scope('T'), members={ 'x': MemberDefinition(offset=3, cairo_type=t_star), }, size=4, ), } reference_manager = ReferenceManager() flow_tracking_data = FlowTrackingDataActual(ap_tracking=RegTrackingData()) flow_tracking_data = flow_tracking_data.add_reference( reference_manager=reference_manager, name=scope('ref'), ref=Reference( pc=0, value=mark_types_in_expr_resolved(parse_expr('[cast(100, T*)]')), ap_tracking_data=RegTrackingData(group=0, offset=2), ), ) memory = {103: 200} consts = get_vm_consts( identifier_values, reference_manager, flow_tracking_data, memory=memory) assert consts.ref.address_ == 100 assert consts.ref.x.address_ == 200 # Set the pointer ref.x.x to 300. consts.ref.x.x = 300 assert memory[203] == 300 assert consts.ref.x.x.address_ == 300 assert consts.ref.type_ == consts.T
def test_references(): reference_manager = ReferenceManager() references = { scope('x.ref'): reference_manager.alloc_id( Reference( pc=0, value=parse_expr('[ap + 1]'), ap_tracking_data=RegTrackingData(group=0, offset=2), )), scope('x.ref2'): reference_manager.alloc_id( Reference( pc=0, value=parse_expr('[ap + 1] + 0'), ap_tracking_data=RegTrackingData(group=0, offset=2), )), scope('x.ref3'): reference_manager.alloc_id( Reference( pc=0, value=parse_expr('ap + 1'), ap_tracking_data=RegTrackingData(group=0, offset=2), )), scope('x.typeref'): reference_manager.alloc_id( Reference( pc=0, value=mark_types_in_expr_resolved( parse_expr('cast(ap + 1, MyStruct*)')), ap_tracking_data=RegTrackingData(group=0, offset=3), )), scope('x.typeref2'): reference_manager.alloc_id( Reference( pc=0, value=mark_types_in_expr_resolved( parse_expr('cast([ap + 1], MyStruct*)')), ap_tracking_data=RegTrackingData(group=0, offset=3), )), } my_struct_star = TypePointer( pointee=TypeStruct(scope=scope('MyStruct'), is_fully_resolved=True)) identifier_values = { scope('x.ref'): ReferenceDefinition(full_name=scope('x.ref'), cairo_type=TypeFelt(), references=[]), scope('x.ref2'): ReferenceDefinition(full_name=scope('x.ref2'), cairo_type=TypeFelt(), references=[]), scope('x.ref3'): ReferenceDefinition(full_name=scope('x.ref3'), cairo_type=TypeFelt(), references=[]), scope('x.typeref'): ReferenceDefinition(full_name=scope('x.typeref'), cairo_type=my_struct_star, references=[]), scope('x.typeref2'): ReferenceDefinition(full_name=scope('x.typeref2'), cairo_type=my_struct_star, references=[]), scope('MyStruct'): StructDefinition( full_name=scope('MyStruct'), members={ 'member': MemberDefinition(offset=10, cairo_type=TypeFelt()), }, size=11, ), } prime = 2**64 + 13 ap = 100 fp = 200 memory = { (ap - 2) + 1: 1234, (ap - 1) + 1: 1000, (ap - 1) + 1 + 2: 13, (ap - 1) + 1 + 10: 17, } flow_tracking_data = FlowTrackingDataActual( ap_tracking=RegTrackingData(group=0, offset=4), reference_ids=references, ) context = VmConstsContext( identifiers=IdentifierManager.from_dict(identifier_values), evaluator=ExpressionEvaluator(prime, ap, fp, memory).eval, reference_manager=reference_manager, flow_tracking_data=flow_tracking_data, memory=memory, pc=0) consts = VmConsts(context=context, accessible_scopes=[ScopedName()]) assert consts.x.ref == memory[(ap - 2) + 1] assert consts.x.typeref.address_ == (ap - 1) + 1 assert consts.x.typeref.member == memory[(ap - 1) + 1 + 10] with pytest.raises(IdentifierError, match="'abc' is not a member of 'MyStruct'."): consts.x.typeref.abc with pytest.raises(IdentifierError, match="'SIZE' is not a member of 'MyStruct'."): consts.x.typeref.SIZE with pytest.raises( AssertionError, match='Cannot change the value of a struct definition.'): consts.MyStruct = 13 assert consts.MyStruct.member == 10 with pytest.raises(AssertionError, match='Cannot change the value of a constant.'): consts.MyStruct.member = 13 assert consts.MyStruct.SIZE == 11 with pytest.raises(AssertionError, match='Cannot change the value of a constant.'): consts.MyStruct.SIZE = 13 with pytest.raises(IdentifierError, match="'abc' is not a member of 'MyStruct'."): consts.MyStruct.abc # Test that VmConsts can be used to assign values to references of the form '[...]'. memory.clear() consts.x.ref = 1234 assert memory == {(ap - 2) + 1: 1234} memory.clear() consts.x.typeref.member = 1001 assert memory == {(ap - 1) + 1 + 10: 1001} memory.clear() consts.x.typeref2 = 4321 assert memory == {(ap - 1) + 1: 4321} consts.x.typeref2.member = 1 assert memory == { (ap - 1) + 1: 4321, 4321 + 10: 1, } with pytest.raises(AssertionError, match='Cannot change the value of a scope definition'): consts.x = 1000 with pytest.raises( AssertionError, match= r'x.ref2 \(= \[ap \+ 1\] \+ 0\) does not reference memory and cannot be assigned.', ): consts.x.ref2 = 1000 with pytest.raises( AssertionError, match= r'x.typeref \(= ap \+ 1\) does not reference memory and cannot be assigned.', ): consts.x.typeref = 1000
def test_references(): reference_manager = ReferenceManager() references = { scope('x.ref'): reference_manager.get_id( Reference( pc=0, value=parse_expr('[ap + 1]'), ap_tracking_data=RegTrackingData(group=0, offset=2), )), scope('x.ref2'): reference_manager.get_id( Reference( pc=0, value=parse_expr('[ap + 1] + 0'), ap_tracking_data=RegTrackingData(group=0, offset=2), )), scope('x.ref3'): reference_manager.get_id( Reference( pc=0, value=parse_expr('ap + 1'), ap_tracking_data=RegTrackingData(group=0, offset=2), )), scope('x.typeref'): reference_manager.get_id( Reference( pc=0, value=mark_types_in_expr_resolved( parse_expr('cast(ap + 1, a*)')), ap_tracking_data=RegTrackingData(group=0, offset=3), )), scope('x.typeref2'): reference_manager.get_id( Reference( pc=0, value=mark_types_in_expr_resolved( parse_expr('cast([ap + 1], a*)')), ap_tracking_data=RegTrackingData(group=0, offset=3), )), } identifier_values = { scope('x.ref'): ReferenceDefinition(full_name=scope('x.ref'), references=[]), scope('x.ref2'): ReferenceDefinition(full_name=scope('x.ref2'), references=[]), scope('x.ref3'): ReferenceDefinition(full_name=scope('x.ref3'), references=[]), scope('x.typeref'): ReferenceDefinition(full_name=scope('x.typeref'), references=[]), scope('x.typeref2'): ReferenceDefinition(full_name=scope('x.typeref2'), references=[]), scope('a.member'): MemberDefinition(offset=10, cairo_type=TypeFelt()), scope('a.scope0.member'): MemberDefinition(offset=2, cairo_type=TypeFelt()), } prime = 2**64 + 13 ap = 100 fp = 200 memory = { (ap - 2) + 1: 1234, (ap - 1) + 1: 1000, (ap - 1) + 1 + 2: 13, (ap - 1) + 1 + 10: 17, } flow_tracking_data = FlowTrackingDataActual( ap_tracking=RegTrackingData(group=0, offset=4), reference_ids=references, ) context = VmConstsContext( identifiers=IdentifierManager.from_dict(identifier_values), evaluator=ExpressionEvaluator(prime, ap, fp, memory).eval, reference_manager=reference_manager, flow_tracking_data=flow_tracking_data, memory=memory, pc=0) consts = VmConsts(context=context, accessible_scopes=[ScopedName()]) assert consts.x.ref == memory[(ap - 2) + 1] assert consts.x.typeref.address_ == (ap - 1) + 1 assert consts.x.typeref.member == memory[(ap - 1) + 1 + 10] with pytest.raises( NotImplementedError, match="Expected a member, found 'scope0' which is 'scope'"): consts.x.typeref.scope0 # Test that VmConsts can be used to assign values to references of the form '[...]'. memory.clear() consts.x.ref = 1234 assert memory == {(ap - 2) + 1: 1234} memory.clear() consts.x.typeref.member = 1001 assert memory == {(ap - 1) + 1 + 10: 1001} memory.clear() consts.x.typeref2 = 4321 assert memory == {(ap - 1) + 1: 4321} consts.x.typeref2.member = 1 assert memory == { (ap - 1) + 1: 4321, 4321 + 10: 1, } with pytest.raises(AssertionError, match='Cannot change the value of a scope definition'): consts.x = 1000 with pytest.raises( AssertionError, match= r'x.ref2 \(= \[ap \+ 1\] \+ 0\) does not reference memory and cannot be assigned.', ): consts.x.ref2 = 1000 with pytest.raises( AssertionError, match= r'x.typeref \(= ap \+ 1\) does not reference memory and cannot be assigned.', ): consts.x.typeref = 1000
def test_eval_reference_fp_only(): x = Reference(pc=0, value=parse_expr('3 * fp - 5 + fp * fp'), ap_tracking_data=RegTrackingData(group=1, offset=5)) assert x.eval(RegTrackingData( group=2, offset=7)) == parse_expr('3 * fp - 5 + fp * fp')