def test_program_start_property(): identifiers = IdentifierManager.from_dict({ ScopedName.from_string('some.main.__start__'): LabelDefinition(3), }) reference_manager = ReferenceManager() main_scope = ScopedName.from_string('some.main') # The label __start__ is in identifiers. program = Program(prime=0, data=[], hints={}, builtins=[], main_scope=main_scope, identifiers=identifiers, reference_manager=reference_manager) assert program.start == 3 # The label __start__ is not in identifiers. program = Program(prime=0, data=[], hints={}, builtins=[], main_scope=main_scope, identifiers=IdentifierManager(), reference_manager=reference_manager) assert program.start == 0
def _deserialize(self, value, attr, data, **kwargs) -> IdentifierManager: identifier_definition_schema = IdentifierDefinitionSchema() return IdentifierManager.from_dict({ ScopedName.from_string(name): identifier_definition_schema.load(serialized_identifier_definition) for name, serialized_identifier_definition in value.items() })
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_unsupported_attribute(): class UnsupportedIdentifier(IdentifierDefinition): TYPE: ClassVar[str] = 'tested_t' identifier_values = { scope('x'): UnsupportedIdentifier(), scope('y.z'): UnsupportedIdentifier(), } context = VmConstsContext( identifiers=IdentifierManager.from_dict(identifier_values), evaluator=dummy_evaluator, reference_manager=ReferenceManager(), flow_tracking_data=FlowTrackingDataActual( ap_tracking=RegTrackingData()), memory={}, pc=0) consts = VmConsts(context=context, accessible_scopes=[scope('')]) # Identifier in root namespace. with pytest.raises( NotImplementedError, match="Unsupported identifier type 'tested_t' of identifier 'x'."): consts.x # Identifier in sub namespace. with pytest.raises( NotImplementedError, match="Unsupported identifier type 'tested_t' of identifier 'y.z'." ): consts.y.z
def test_get_struct_definition(): identifier_dict = { scope('T'): StructDefinition( full_name=scope('T'), members={ 'a': MemberDefinition(offset=0, cairo_type=TypeFelt()), 'b': MemberDefinition(offset=1, cairo_type=TypeFelt()), }, size=2, ), scope('MyConst'): ConstDefinition(value=5), } manager = IdentifierManager.from_dict(identifier_dict) struct_def = get_struct_definition(ScopedName.from_string('T'), manager) # Convert to a list, to check the order of the elements in the dict. assert list(struct_def.members.items()) == [ ('a', MemberDefinition(offset=0, cairo_type=TypeFelt())), ('b', MemberDefinition(offset=1, cairo_type=TypeFelt())), ] assert struct_def.size == 2 with pytest.raises( DefinitionError, match="Expected 'MyConst' to be a struct. Found: 'const'."): get_struct_definition(scope('MyConst'), manager) with pytest.raises(MissingIdentifierError, match=re.escape("Unknown identifier 'abc'.")): get_struct_definition(scope('abc'), manager)
def test_identifier_manager_search(): identifier_dict = { scope('a.b.c.y'): ConstDefinition(value=1), scope('a.b.x'): ConstDefinition(value=2), scope('a.b.z'): ConstDefinition(value=3), scope('a.x'): ConstDefinition(value=4), scope('x'): ConstDefinition(value=5), scope('d.b.w'): ConstDefinition(value=6), } manager = IdentifierManager.from_dict(identifier_dict) for accessible_scopes, name, canonical_name in [ (['a', 'a.b', 'a.b.c', 'e'], 'x', 'a.b.x'), (['a', 'a.b'], 'x', 'a.b.x'), (['a.b', 'a'], 'x', 'a.x'), ([''], 'x', 'x'), (['a', 'e', 'a.b.c'], 'b.z', 'a.b.z'), ]: result = manager.search(list(map(scope, accessible_scopes)), scope(name)) assert result.canonical_name == scope(canonical_name) assert result.identifier_definition == identifier_dict[scope(canonical_name)] with pytest.raises(IdentifierError, match="Unknown identifier 'x'"): manager.search([], scope('x')) # Since 'd.b' exists, and it does not contain a sub-identifier 'z' the following raises an # exception (even though a.b.z exists). # Compare with the line (['a', 'e', 'a.b.c'], 'b.z', 'a.b.z') above. with pytest.raises(IdentifierError, match="Unknown identifier 'd.b.z'."): manager.search([scope('a'), scope('d'), scope('e'), scope('a.b.c')], scope('b.z.w'))
def test_missing_attributes(): identifier_values = { scope('x.y'): ConstDefinition(1), scope('z'): AliasDefinition(scope('x')), scope('x.missing'): AliasDefinition(scope('nothing')), } context = VmConstsContext( identifiers=IdentifierManager.from_dict(identifier_values), evaluator=dummy_evaluator, reference_manager=ReferenceManager(), flow_tracking_data=FlowTrackingDataActual(ap_tracking=RegTrackingData()), memory={}, pc=0) consts = VmConsts(context=context, accessible_scopes=[ScopedName()]) # Identifier not exists anywhere. with pytest.raises(MissingIdentifierError, match="Unknown identifier 'xx'."): consts.xx # Identifier not exists in accessible scopes. with pytest.raises(MissingIdentifierError, match="Unknown identifier 'y'."): consts.y # Recursive search. with pytest.raises(MissingIdentifierError, match="Unknown identifier 'x.z'."): consts.x.z # Pass through alias. with pytest.raises(MissingIdentifierError, match="Unknown identifier 'z.x'."): consts.z.x # Pass through bad alias. with pytest.raises( IdentifierError, match="Alias resolution failed: x.missing -> nothing. Unknown identifier 'nothing'."): consts.x.missing.y
def test_identifier_manager_aliases(): identifier_dict = { scope('a.b.c'): AliasDefinition(destination=scope('x.y')), scope('x.y'): AliasDefinition(destination=scope('x.y2')), scope('x.y2.z'): ConstDefinition(value=3), scope('x.y2.s.z'): ConstDefinition(value=4), scope('x.y2.s2'): AliasDefinition(destination=scope('x.y2.s')), scope('z0'): AliasDefinition(destination=scope('z1.z2')), scope('z1.z2'): AliasDefinition(destination=scope('z3')), scope('z3'): AliasDefinition(destination=scope('z0')), scope('to_const'): AliasDefinition(destination=scope('x.y2.z')), scope('unresolved'): AliasDefinition(destination=scope('z1.missing')), } manager = IdentifierManager.from_dict(identifier_dict) # Test manager.get(). assert manager.get(scope('a.b.c.z.w')) == IdentifierSearchResult( identifier_definition=identifier_dict[scope('x.y2.z')], canonical_name=scope('x.y2.z'), non_parsed=scope('w')) assert manager.get(scope('to_const.w')) == IdentifierSearchResult( identifier_definition=identifier_dict[scope('x.y2.z')], canonical_name=scope('x.y2.z'), non_parsed=scope('w')) with pytest.raises(IdentifierError, match='Cyclic aliasing detected: z0 -> z1.z2 -> z3 -> z0'): manager.get(scope('z0')) with pytest.raises(IdentifierError, match=(re.escape( 'Alias resolution failed: unresolved -> z1.missing. ' "Unknown identifier 'z1.missing'."))): manager.get(scope('unresolved')) # Test manager.get_scope(). assert manager.get_scope(scope('a.b')).fullname == scope('a.b') assert manager.get_scope(scope('a.b.c')).fullname == scope('x.y2') assert manager.get_scope(scope('a.b.c.s')).fullname == scope('x.y2.s') assert manager.get_scope(scope('a.b.c.s2')).fullname == scope('x.y2.s') with pytest.raises(IdentifierError, match='Cyclic aliasing detected: z0 -> z1.z2 -> z3 -> z0'): manager.get_scope(scope('z0')) with pytest.raises(IdentifierError, match=( 'Alias resolution failed: unresolved -> z1.missing. ' "Unknown identifier 'z1.missing'.")): manager.get_scope(scope('unresolved')) with pytest.raises(IdentifierError, match=( "^Identifier 'x.y2.z' is const, expected a scope.")): manager.get_scope(scope('x.y2.z')) with pytest.raises(IdentifierError, match=( 'Alias resolution failed: a.b.c.z.w -> x.y.z.w -> x.y2.z.w. ' "Identifier 'x.y2.z' is const, expected a scope.")): manager.get_scope(scope('a.b.c.z.w'))
def get_vm_consts(identifier_values, reference_manager, flow_tracking_data, memory={}): """ Creates a simple VmConsts object. """ identifiers = IdentifierManager.from_dict(identifier_values) context = VmConstsContext( identifiers=identifiers, evaluator=ExpressionEvaluator(2**64 + 13, 0, 0, memory, identifiers).eval, reference_manager=reference_manager, flow_tracking_data=flow_tracking_data, memory=memory, pc=9) return VmConsts(context=context, accessible_scopes=[ScopedName()])
def test_identifier_manager_get_by_full_name(): identifier_dict = { scope('a.b.c'): ConstDefinition(value=7), scope('x'): AliasDefinition(destination=scope('a')), } manager = IdentifierManager.from_dict(identifier_dict) assert manager.get_by_full_name(scope('a.b.c')) == identifier_dict[scope('a.b.c')] assert manager.get_by_full_name(scope('x')) == identifier_dict[scope('x')] assert manager.get_by_full_name(scope('a.b')) is None assert manager.get_by_full_name(scope('a.b.c.d')) is None assert manager.get_by_full_name(scope('x.b.c')) is None
def test_scope_order(): identifier_values = { scope('x.y'): ConstDefinition(1), scope('y'): ConstDefinition(2), } context = VmConstsContext( identifiers=IdentifierManager.from_dict(identifier_values), evaluator=dummy_evaluator, reference_manager=ReferenceManager(), flow_tracking_data=FlowTrackingDataActual(ap_tracking=RegTrackingData()), memory={}, pc=0) consts = VmConsts(context=context, accessible_scopes=[ScopedName(), scope('x')]) assert consts.y == 1 assert consts.x.y == 1
def test_unparsed(): identifier_values = { scope('x'): LabelDefinition(10), } context = VmConstsContext( identifiers=IdentifierManager.from_dict(identifier_values), evaluator=dummy_evaluator, reference_manager=ReferenceManager(), flow_tracking_data=FlowTrackingDataActual(ap_tracking=RegTrackingData()), memory={}, pc=0) consts = VmConsts(context=context, accessible_scopes=[scope('')]) with pytest.raises(IdentifierError, match="Unexpected '.' after 'x' which is label."): consts.x.z
def test_get_struct_members(): identifier_dict = { scope('T.b'): MemberDefinition(offset=1, cairo_type=TypeFelt()), scope('T.a'): MemberDefinition(offset=0, cairo_type=TypeFelt()), scope('T.SIZE'): ConstDefinition(value=2), scope('S.a'): MemberDefinition(offset=0, cairo_type=TypeFelt()), scope('S.c'): MemberDefinition(offset=1, cairo_type=TypeFelt()), scope('S.SIZE'): ConstDefinition(value=2), } manager = IdentifierManager.from_dict(identifier_dict) member = get_struct_members(scope('T'), manager) # Convert to a list, to check the order of the elements in the dict. assert list(member.items()) == [ ('a', MemberDefinition(offset=0, cairo_type=TypeFelt())), ('b', MemberDefinition(offset=1, cairo_type=TypeFelt())), ]
def test_type_tuples_failures(): identifier_dict = { scope('T'): StructDefinition( full_name=scope('T'), members={ 'x': MemberDefinition(offset=0, cairo_type=TypeFelt()), 'y': MemberDefinition(offset=1, cairo_type=TypeFelt()), }, size=2, ), } identifiers = IdentifierManager.from_dict(identifier_dict) verify_exception('1 + cast((1, 2), T).x', """ file:?:?: Accessing struct members for r-value structs is not supported yet. 1 + cast((1, 2), T).x ^***************^ """, identifiers=identifiers)
def process_test_calldata(members: Dict[str, MemberDefinition], has_range_check_builtin=True): identifier_values: Dict[ScopedName, IdentifierDefinition] = { scope('MyStruct'): StructDefinition( full_name=scope('MyStruct'), members=members, size=0, ), } identifiers = IdentifierManager.from_dict(identifier_values) calldata_ptr = ExprIdentifier('calldata_ptr') calldata_size = ExprIdentifier('calldata_size') return process_calldata(calldata_ptr=calldata_ptr, calldata_size=calldata_size, identifiers=identifiers, struct_def=get_struct_definition( struct_name=scope('MyStruct'), identifier_manager=identifiers), has_range_check_builtin=has_range_check_builtin, location=dummy_location())
def test_main_scope(): identifiers = IdentifierManager.from_dict({ ScopedName.from_string('a.b'): ConstDefinition(value=1), ScopedName.from_string('x.y.z'): ConstDefinition(value=2), }) reference_manager = ReferenceManager() program = Program(prime=0, data=[], hints={}, builtins=[], main_scope=ScopedName.from_string('a'), identifiers=identifiers, reference_manager=reference_manager) # Check accessible identifiers. assert program.get_identifier('b', ConstDefinition).value == 1 # Ensure inaccessible identifiers. with pytest.raises(MissingIdentifierError, match="Unknown identifier 'a'."): program.get_identifier('a.b', ConstDefinition) with pytest.raises(MissingIdentifierError, match="Unknown identifier 'x'."): program.get_identifier('x.y', ConstDefinition) with pytest.raises(MissingIdentifierError, match="Unknown identifier 'y'."): program.get_identifier('y', ConstDefinition) # Full name lookup. assert program.get_identifier('a.b', ConstDefinition, full_name_lookup=True).value == 1 assert program.get_identifier('x.y.z', ConstDefinition, full_name_lookup=True).value == 2
def test_identifier_manager_get(): identifier_dict = { scope('a.b.c'): ConstDefinition(value=7), } manager = IdentifierManager.from_dict(identifier_dict) for name in ['a', 'a.b']: with pytest.raises(MissingIdentifierError, match=f"Unknown identifier '{name}'."): manager.get(scope(name)) # Search 'a.b.c.*'. for suffix in ['d', 'd.e']: result = manager.get(scope('a.b.c') + scope(suffix)) assert result == IdentifierSearchResult( identifier_definition=identifier_dict[scope('a.b.c')], canonical_name=scope('a.b.c'), non_parsed=scope(suffix)) error_msg = re.escape("Unexpected '.' after 'a.b.c' which is const") with pytest.raises(IdentifierError, match=error_msg): result.assert_fully_parsed() with pytest.raises(IdentifierError, match=error_msg): result.get_canonical_name() result = manager.get(scope('a.b.c')) assert result == IdentifierSearchResult( identifier_definition=identifier_dict[scope('a.b.c')], canonical_name=scope('a.b.c'), non_parsed=ScopedName()) result.assert_fully_parsed() assert result.get_canonical_name() == scope('a.b.c') for name in ['a.d', 'a.d.e']: # The error should point to the first unknown item, rather then the entire name. with pytest.raises(MissingIdentifierError, match="Unknown identifier 'a.d'."): manager.get(scope(name))
def test_identifier_manager_field_serialization(): @marshmallow_dataclass.dataclass class Foo: identifiers: IdentifierManager = field(metadata=dict( marshmallow_field=IdentifierManagerField())) Schema = marshmallow_dataclass.class_schema(Foo) foo = Foo(identifiers=IdentifierManager.from_dict({ scope('aa.b'): LabelDefinition(pc=1000), })) serialized = Schema().dump(foo) assert serialized == { 'identifiers': { 'aa.b': { 'pc': 1000, 'type': 'label' } } } assert Schema().load(serialized) == foo
def test_resolve_search_result(): struct_def = StructDefinition( full_name=scope('T'), members={ 'a': MemberDefinition(offset=0, cairo_type=TypeFelt()), 'b': MemberDefinition(offset=1, cairo_type=TypeFelt()), }, size=2, ) identifier_dict = { struct_def.full_name: struct_def, } identifier = IdentifierManager.from_dict(identifier_dict) with pytest.raises(IdentifierError, match="Unexpected '.' after 'T.a' which is member"): resolve_search_result(search_result=IdentifierSearchResult( identifier_definition=struct_def, canonical_name=struct_def.full_name, non_parsed=scope('a.z')), identifiers=identifier)
def test_type_dot_op(): """ Tests type_system_visitor for ExprDot-s, in the following struct architecture: struct S: member x : felt member y : felt end struct T: member t : felt member s : S member sp : S* end struct R: member r : R* end """ t = TypeStruct(scope=scope('T'), is_fully_resolved=True) s = TypeStruct(scope=scope('S'), is_fully_resolved=True) s_star = TypePointer(pointee=s) r = TypeStruct(scope=scope('R'), is_fully_resolved=True) r_star = TypePointer(pointee=r) identifier_dict = { scope('T'): StructDefinition( full_name=scope('T'), members={ 't': MemberDefinition(offset=0, cairo_type=TypeFelt()), 's': MemberDefinition(offset=1, cairo_type=s), 'sp': MemberDefinition(offset=3, cairo_type=s_star), }, size=4, ), scope('S'): StructDefinition( full_name=scope('S'), members={ 'x': MemberDefinition(offset=0, cairo_type=TypeFelt()), 'y': MemberDefinition(offset=1, cairo_type=TypeFelt()), }, size=2, ), scope('R'): StructDefinition( full_name=scope('R'), members={ 'r': MemberDefinition(offset=0, cairo_type=r_star), }, size=1, ), } identifiers = IdentifierManager.from_dict(identifier_dict) for (orig_expr, simplified_expr, simplified_type) in [ ('[cast(fp, T*)].t', '[fp]', TypeFelt()), ('[cast(fp, T*)].s', '[fp + 1]', s), ('[cast(fp, T*)].sp', '[fp + 3]', s_star), ('[cast(fp, T*)].s.x', '[fp + 1]', TypeFelt()), ('[cast(fp, T*)].s.y', '[fp + 1 + 1]', TypeFelt()), ('[[cast(fp, T*)].sp].x', '[[fp + 3]]', TypeFelt()), ('[cast(fp, R*)]', '[fp]', r), ('[cast(fp, R*)].r', '[fp]', r_star), ('[[[cast(fp, R*)].r].r].r', '[[[fp]]]', r_star), # Test . as -> ('cast(fp, T*).t', '[fp]', TypeFelt()), ('cast(fp, T*).sp.y', '[[fp + 3] + 1]', TypeFelt()), ('cast(fp, R*).r.r.r', '[[[fp]]]', r_star), # More tests. ('(cast(fp, T*).s)', '[fp + 1]', s), ('(cast(fp, T*).s).x', '[fp + 1]', TypeFelt()), ('(&(cast(fp, T*).s)).x', '[fp + 1]', TypeFelt()) ]: simplify_type_system_test(orig_expr, simplified_expr, simplified_type, identifiers=identifiers) # Test failures. verify_exception('cast(fp, felt).x', """ file:?:?: Cannot apply dot-operator to non-struct type 'felt'. cast(fp, felt).x ^**************^ """, identifiers=identifiers) verify_exception('cast(fp, felt*).x', """ file:?:?: Cannot apply dot-operator to pointer-to-non-struct type 'felt*'. cast(fp, felt*).x ^***************^ """, identifiers=identifiers) verify_exception('cast(fp, T*).x', """ file:?:?: Member 'x' does not appear in definition of struct 'T'. cast(fp, T*).x ^************^ """, identifiers=identifiers) verify_exception('cast(fp, Z*).x', """ file:?:?: Unknown identifier 'Z'. cast(fp, Z*).x ^************^ """, identifiers=identifiers) verify_exception('cast(fp, T*).x', """ file:?:?: Identifiers must be initialized for type-simplification of dot-operator expressions. cast(fp, T*).x ^************^ """, identifiers=None) verify_exception('cast(fp, Z*).x', """ file:?:?: Type is expected to be fully resolved at this point. cast(fp, Z*).x ^************^ """, identifiers=identifiers, resolve_types=False)
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)
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_offset_reference_definition_typed_members(): t = TypeStruct(scope=scope('T'), is_fully_resolved=True) t_star = TypePointer(pointee=t) s_star = TypePointer( pointee=TypeStruct(scope=scope('S'), is_fully_resolved=True)) reference_manager = ReferenceManager() identifiers = IdentifierManager.from_dict({ scope('T'): StructDefinition( full_name='T', members={ 'x': MemberDefinition(offset=3, cairo_type=s_star), 'flt': MemberDefinition(offset=4, cairo_type=TypeFelt()), }, size=5, ), scope('S'): StructDefinition( full_name='S', members={ 'x': MemberDefinition(offset=10, cairo_type=t), }, size=15, ), }) 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 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, identifiers=identifiers, member_path=scope(member_path)) definition.eval( reference_manager=reference_manager, flow_tracking_data=flow_tracking_data).format() == expected_result definition = OffsetReferenceDefinition(parent=main_reference, identifiers=identifiers, member_path=scope('x.x.flt.x')) with pytest.raises( DefinitionError, match='Member access requires a type of the form Struct*.'): definition.eval(reference_manager=reference_manager, flow_tracking_data=flow_tracking_data) definition = OffsetReferenceDefinition(parent=main_reference, identifiers=identifiers, member_path=scope('x.y')) with pytest.raises(DefinitionError, match="'y' is not a member of 'S'."): definition.eval(reference_manager=reference_manager, flow_tracking_data=flow_tracking_data)
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