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')),
    }
Esempio n. 8
0
    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)
Esempio n. 9
0
    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)