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 resolve_search_result( search_result: IdentifierSearchResult, identifiers: IdentifierManager) -> IdentifierDefinition: """ Returns a fully parsed identifier definition for the given identifier search result. If search_result contains a reference with non_parsed data, returns an instance of OffsetReferenceDefinition. """ identifier_definition = search_result.identifier_definition if isinstance(identifier_definition, ReferenceDefinition) and \ len(search_result.non_parsed) > 0: identifier_definition = OffsetReferenceDefinition( parent=identifier_definition, identifier_values=identifiers.as_dict(), member_path=search_result.non_parsed) else: search_result.assert_fully_parsed() return identifier_definition
def resolve_search_result( search_result: IdentifierSearchResult, identifiers: IdentifierManager) -> IdentifierDefinition: """ Returns a fully parsed identifier definition for the given identifier search result. If search_result contains a reference with non_parsed data, returns an instance of OffsetReferenceDefinition. """ identifier_definition = search_result.identifier_definition if len(search_result.non_parsed) == 0: return identifier_definition if isinstance(identifier_definition, StructDefinition): if search_result.non_parsed == SIZE_CONSTANT: return ConstDefinition(value=identifier_definition.size) member_def = identifier_definition.members.get( search_result.non_parsed.path[0]) struct_name = identifier_definition.full_name if member_def is None: raise DefinitionError( f"'{search_result.non_parsed}' is not a member of '{struct_name}'." ) if len(search_result.non_parsed) > 1: raise IdentifierError( f"Unexpected '.' after '{struct_name + search_result.non_parsed.path[0]}' which is " f'{member_def.TYPE}.') identifier_definition = member_def elif isinstance(identifier_definition, ReferenceDefinition): identifier_definition = OffsetReferenceDefinition( parent=identifier_definition, identifiers=identifiers, member_path=search_result.non_parsed) else: search_result.assert_fully_parsed() return identifier_definition
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_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)