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_alias(): identifier_values = { scope('w'): AliasDefinition(scope('z.y')), scope('x'): AliasDefinition(scope('z')), scope('z.y'): ConstDefinition(1), } 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()]) assert consts.x.y == 1 assert consts.w == 1
def test_struct_collector(): modules = {'module': """ struct S: member x : S* member y : S* end """, '__main__': """ from module import S func foo{z}(a : S, b) -> (c : S): struct T: member x : S* end const X = 5 return (c=a + X) end const Y = 1 + 1 """} scope = ScopedName.from_string struct_defs = _collect_struct_definitions(modules) expected_def = { 'module.S': StructDefinition( full_name=scope('module.S'), members={ 'x': MemberDefinition(offset=0, cairo_type=TypePointer(pointee=TypeStruct( scope=scope('module.S'), is_fully_resolved=True))), 'y': MemberDefinition(offset=1, cairo_type=TypePointer(pointee=TypeStruct( scope=scope('module.S'), is_fully_resolved=True))), }, size=2), '__main__.S': AliasDefinition(destination=scope('module.S')), '__main__.foo.Args': StructDefinition( full_name=scope('__main__.foo.Args'), members={ 'a': MemberDefinition(offset=0, cairo_type=TypeStruct( scope=scope('module.S'), is_fully_resolved=True)), 'b': MemberDefinition(offset=2, cairo_type=TypeFelt()), }, size=3), '__main__.foo.ImplicitArgs': StructDefinition( full_name=scope('__main__.foo.ImplicitArgs'), members={'z': MemberDefinition(offset=0, cairo_type=TypeFelt())}, size=1), '__main__.foo.Return': StructDefinition( full_name=scope('__main__.foo.Return'), members={ 'c': MemberDefinition(offset=0, cairo_type=TypeStruct( scope=scope('module.S'), is_fully_resolved=True)) }, size=2), '__main__.foo.T': StructDefinition( full_name=scope('__main__.foo.T'), members={ 'x': MemberDefinition(offset=0, cairo_type=TypePointer(pointee=TypeStruct( scope=scope('module.S'), is_fully_resolved=True))), }, size=1) } assert struct_defs == expected_def
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 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 visit_CodeElementFunction(self, elm: CodeElementFunction): super().visit_CodeElementFunction(elm) external_decorator = self.get_external_decorator(elm) if external_decorator is None: return if self.file_lang != STARKNET_LANG_DIRECTIVE: raise PreprocessorError( 'External decorators can only be used in source files that contain the ' '"%lang starknet" directive.', location=external_decorator.location) location = elm.identifier.location # Retrieve the canonical name of the function before switching scopes. _, func_canonical_name = self.get_label(elm.name, location=location) assert func_canonical_name is not None scope = WRAPPER_SCOPE if external_decorator.name == L1_HANDLER_DECORATOR: self.validate_l1_handler_signature(elm) self.flow_tracking.revoke() with self.scoped(scope, parent=elm), self.set_reference_states({}): current_wrapper_scope = self.current_scope + elm.name self.add_name_definition( current_wrapper_scope, FunctionDefinition( # type: ignore pc=self.current_pc, decorators=[identifier.name for identifier in elm.decorators], ), location=elm.identifier.location, require_future_definition=False) with self.scoped(current_wrapper_scope, parent=elm): # Generate an alias that will allow us to call the original function. func_alias_name = f'__wrapped_func' alias_canonical_name = current_wrapper_scope + func_alias_name self.add_future_definition( name=alias_canonical_name, future_definition=FutureIdentifierDefinition( identifier_type=AliasDefinition), ) self.add_name_definition( name=alias_canonical_name, identifier_definition=AliasDefinition(destination=func_canonical_name), location=location) self.create_func_wrapper(elm=elm, func_alias_name=func_alias_name)
def test_imports(): collector = IdentifierCollector() collector.identifiers.add_identifier(ScopedName.from_string('foo.bar'), ConstDefinition(value=0)) ast = parse_file(""" from foo import bar as bar0 """) with collector.scoped(ScopedName(), parent=ast): collector.visit(ast.code_block) assert collector.identifiers.get_scope(ScopedName()).identifiers == { 'bar0': AliasDefinition(destination=ScopedName.from_string('foo.bar')), }
def visit_CodeElementImport(self, elm: CodeElementImport): alias_dst = ScopedName.from_string( elm.path.name) + elm.orig_identifier.name local_identifier = elm.identifier # Ensure destination is a valid identifier. if self.identifiers.get_by_full_name(alias_dst) is None: raise PreprocessorError( f"Scope '{elm.path.name}' does not include identifier " f"'{elm.orig_identifier.name}'.", location=elm.orig_identifier.location) # Add alias to identifiers. self.add_identifier( name=self.current_scope + local_identifier.name, identifier_definition=AliasDefinition(destination=alias_dst), location=elm.identifier.location)
def visit_CodeElementImport(self, elm: CodeElementImport): for import_item in elm.import_items: alias_dst = ScopedName.from_string( elm.path.name) + import_item.orig_identifier.name local_identifier = import_item.identifier # Ensure destination is a valid identifier. if self.identifiers.get_by_full_name(alias_dst) is None: try: self.identifiers.get_scope(alias_dst) except IdentifierError: raise PreprocessorError( f"Cannot import '{import_item.orig_identifier.name}' " f"from '{elm.path.name}'.", location=import_item.orig_identifier.location) # Add alias to identifiers. self.add_identifier( name=self.current_scope + local_identifier.name, identifier_definition=AliasDefinition(destination=alias_dst), location=import_item.identifier.location)