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 simplify_type_system_test(orig_expr: str, simplified_expr: str, simplified_type: CairoType, identifiers: Optional[IdentifierManager] = None): parsed_expr = mark_types_in_expr_resolved(parse_expr(orig_expr)) assert simplify_type_system( parsed_expr, identifiers=identifiers) == (parse_expr(simplified_expr), simplified_type)
def verify_exception(expr_str: str, error: str, identifiers: Optional[IdentifierManager] = None, resolve_types=True): """ Verifies that calling simplify_type_system() on the code results in the given error. """ with pytest.raises(CairoTypeError) as e: parsed_expr = parse_expr(expr_str) if resolve_types: parsed_expr = mark_types_in_expr_resolved(parsed_expr) simplify_type_system(parsed_expr, identifiers) # Remove line and column information from the error using a regular expression. assert re.sub(':[0-9]+:[0-9]+: ', 'file:?:?: ', str(e.value)) == error.strip()
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 _deserialize(self, value, attr, data, **kwargs): return mark_types_in_expr_resolved(parse_expr(value))
def _serialize(self, value, attr, obj, **kwargs): if value is None: return None assert mark_types_in_expr_resolved(value) == value, \ f"Expected types in '{value}' to be resolved." return value.format()
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 = TypeStruct( scope=scope('MyStruct'), is_fully_resolved=True) my_struct_star = TypePointer(pointee=my_struct) 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()), 'struct': MemberDefinition(offset=11, cairo_type=my_struct), }, size=11, ), } identifiers = IdentifierManager.from_dict(identifier_values) 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=identifiers, evaluator=ExpressionEvaluator(prime, ap, fp, memory, identifiers).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, } consts.x.typeref2.struct.member = 2 assert memory == { (ap - 1) + 1: 4321, 4321 + 10: 1, 4321 + 11 + 10: 2, } 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_type_subscript_op(): felt_star_star = TypePointer(pointee=TypePointer(pointee=TypeFelt())) t = TypeStruct(scope=scope('T'), is_fully_resolved=True) t_star = TypePointer(pointee=t) identifier_dict = { scope('T'): StructDefinition(full_name=scope('T'), members={}, size=7) } identifiers = IdentifierManager.from_dict(identifier_dict) simplify_type_system_test('cast(fp, felt*)[3]', '[fp + 3 * 1]', TypeFelt()) simplify_type_system_test('cast(fp, felt***)[0]', '[fp + 0 * 1]', felt_star_star) simplify_type_system_test('[cast(fp, T****)][ap][ap]', '[[[fp] + ap * 1] + ap * 1]', t_star) simplify_type_system_test('cast(fp, T**)[1][2]', '[[fp + 1 * 1] + 2 * 7]', t, identifiers=identifiers) # Test that 'cast(fp, T*)[2 * ap + 3]' simplifies into '[fp + (2 * ap + 3) * 7]', but without # the parentheses. assert simplify_type_system( mark_types_in_expr_resolved(parse_expr('cast(fp, T*)[2 * ap + 3]')), identifiers=identifiers) == (remove_parentheses( parse_expr('[fp + (2 * ap + 3) * 7]')), t) # Test subscript operator for tuples. simplify_type_system_test('(cast(fp, felt**), fp, cast(fp, T*))[2]', 'fp', t_star) simplify_type_system_test('(cast(fp, felt**), fp, cast(fp, T*))[0]', 'fp', felt_star_star) simplify_type_system_test('(cast(fp, felt**), ap, cast(fp, T*))[3*4 - 11]', 'ap', TypeFelt()) simplify_type_system_test('[cast(ap, (felt, felt)*)][0]', '[ap + 0]', TypeFelt()) simplify_type_system_test('[cast(ap, (T*, T, felt, T*, felt*)*)][3]', '[ap + 9]', t_star, identifiers=identifiers) # Test failures. verify_exception( '(fp, fp, fp)[cast(ap, felt*)]', """ file:?:?: Cannot apply subscript-operator with offset of non-felt type 'felt*'. (fp, fp, fp)[cast(ap, felt*)] ^*************^ """) verify_exception( '(fp, fp, fp)[[ap]]', """ file:?:?: Subscript-operator for tuples supports only constant offsets, found 'ExprDeref'. (fp, fp, fp)[[ap]] ^**^ """) # The simplifier in TypeSystemVisitor cannot access PRIME, so PyConsts are unsimplified. verify_exception( '(fp, fp, fp)[%[1%]]', """ file:?:?: Subscript-operator for tuples supports only constant offsets, found 'ExprPyConst'. (fp, fp, fp)[%[1%]] ^***^ """) verify_exception( '(fp, fp, fp)[3]', """ file:?:?: Tuple index 3 is out of range [0, 3). (fp, fp, fp)[3] ^*************^ """) verify_exception( '[cast(fp, (T*, T, felt)*)][-1]', """ file:?:?: Tuple index -1 is out of range [0, 3). [cast(fp, (T*, T, felt)*)][-1] ^****************************^ """) verify_exception( 'cast(fp, felt)[0]', """ file:?:?: Cannot apply subscript-operator to non-pointer, non-tuple type 'felt'. cast(fp, felt)[0] ^***************^ """) verify_exception( '[cast(fp, T*)][0]', """ file:?:?: Cannot apply subscript-operator to non-pointer, non-tuple type 'T'. [cast(fp, T*)][0] ^***************^ """) verify_exception( 'cast(fp, felt*)[[cast(ap, T*)]]', """ file:?:?: Cannot apply subscript-operator with offset of non-felt type 'T'. cast(fp, felt*)[[cast(ap, T*)]] ^************^ """) verify_exception('cast(fp, Z*)[0]', """ file:?:?: Unknown identifier 'Z'. cast(fp, Z*)[0] ^*************^ """, identifiers=identifiers) verify_exception('cast(fp, T*)[0]', """ file:?:?: Unknown identifier 'T'. cast(fp, T*)[0] ^*************^ """, identifiers=None)