Esempio n. 1
0
def search_identifier_or_scope(
        identifiers: IdentifierManager, accessible_scopes: List[ScopedName],
        name: ScopedName) -> Union[IdentifierSearchResult, 'IdentifierScope']:
    """
    If there is an identifier with the given name, returns an IdentifierSearchResult.
    Otherwise, if there is a scope with that name, returns the IdentifierScope instance.
    If name does not refer to an identifier or a scope, raises an exception.
    """
    try:
        return identifiers.search(accessible_scopes=accessible_scopes,
                                  name=name)
    except IdentifierError as exc:
        first_exception = exc

    try:
        return identifiers.search_scope(accessible_scopes=accessible_scopes,
                                        name=name)
    except IdentifierError:
        raise first_exception from None
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
Esempio n. 7
0
def test_get_dunder_something():
    context = VmConstsContext(identifiers=IdentifierManager(),
                              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(
            AttributeError,
            match=re.escape(
                "'VmConsts' object has no attribute '__something'")):
        consts.__something
Esempio n. 8
0
def get_struct_members(
        struct_name: ScopedName,
        identifier_manager: IdentifierManager) -> Dict[str, MemberDefinition]:
    """
    Returns the member definitions of a struct sorted by offset.
    """

    scope_items = identifier_manager.get_scope(struct_name).identifiers
    members = (
        (name, indentifier_def)
        for (name, indentifier_def) in scope_items.items()
        if isinstance(indentifier_def, MemberDefinition))

    return {
        name: indentifier_def
        for name, indentifier_def in sorted(members, key=lambda key_value: key_value[1].offset)
    }
def get_struct_definition(
        struct_name: ScopedName,
        identifier_manager: IdentifierManager) -> StructDefinition:
    """
    Returns the struct definition of a struct given its full name (no alias resolution).
    """

    struct_def = identifier_manager.get_by_full_name(struct_name)
    if struct_def is None:
        raise MissingIdentifierError(struct_name)

    if not isinstance(struct_def, StructDefinition):
        raise DefinitionError(f"""\
Expected '{struct_name}' to be a {StructDefinition.TYPE}. Found: '{struct_def.TYPE}'."""
                              )

    return struct_def
Esempio n. 10
0
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())),
    ]
Esempio n. 11
0
def test_type_casts(src: str, dest: str, explicit_cast: bool,
                    unpacking_cast: bool, assign_cast: bool):
    identifier_manager = IdentifierManager()
    src_type = parse_type(src)
    dest_type = parse_type(dest)
    expr = parse_expr('[ap]')

    actual_results = [
        check_cast(src_type=src_type,
                   dest_type=dest_type,
                   identifier_manager=identifier_manager,
                   expr=expr,
                   cast_type=cast_type) for cast_type in
        [CastType.EXPLICIT, CastType.UNPACKING, CastType.ASSIGN]
    ]
    expected_results = [explicit_cast, unpacking_cast, assign_cast]
    assert actual_results == expected_results
def get_main_functions_to_compile(
        identifiers: IdentifierManager, main_scope: ScopedName) -> Set[ScopedName]:
    """
    Retrieves the root functions to compile from a main scope.
    The definition of which functions we need to compile is somewhat arbitrary:
    All functions explicitly defined, or aliased in the main scope.
    """
    main_functions: Set[ScopedName] = set()
    try:
        scope = identifiers.get_scope(main_scope)
        main_functions = {main_scope + name for name in scope.subscopes}
        main_functions |= {
            identifier_definition.destination
            for identifier_definition in scope.identifiers.values()
            if isinstance(identifier_definition, AliasDefinition)}
    except MissingIdentifierError:
        return set()
    return main_functions
Esempio n. 13
0
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
Esempio n. 14
0
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
Esempio n. 15
0
def preprocess_codes(
    codes: Sequence[Tuple[str, str]],
    pass_manager: PassManager,
    main_scope: ScopedName = ScopedName()
) -> PreprocessedProgram:
    """
    Preprocesses a list of Cairo files and returns a PreprocessedProgram instance.
    codes is a list of pairs (code_string, file_name).
    """
    context = PassManagerContext(
        codes=list(codes),
        main_scope=main_scope,
        identifiers=IdentifierManager(),
    )

    pass_manager.run(context)

    assert context.preprocessed_program is not None
    return context.preprocessed_program
Esempio n. 16
0
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_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)
Esempio n. 18
0
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
Esempio n. 19
0
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)
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
Esempio n. 22
0
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=[]
        ),
    }
    identifiers = IdentifierManager.from_dict(identifier_values)
    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=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()])

    with pytest.raises(FlowTrackingError, match="Reference 'x' is revoked."):
        consts.x

    with pytest.raises(FlowTrackingError, match="Reference 'x' is revoked."):
        consts.x = 85
Esempio n. 23
0
 def __init__(self):
     super().__init__()
     self.anon_label_gen = AnonymousLabelGenerator()
     self.identifiers = IdentifierManager()
Esempio n. 24
0
 def __init__(self):
     super().__init__()
     self.identifiers = IdentifierManager()
Esempio n. 25
0
class IdentifierCollector(Visitor):
    """
    Collects all the identifiers in a code element.
    Uses a partial visitor.
    """
    # A dict from code element types to the identifier type they define.
    IDENTIFIER_DEFINERS = {
        CodeElementConst: ConstDefinition,
        CodeElementLabel: LabelDefinition,
        CodeElementReference: ReferenceDefinition,
        CodeElementLocalVariable: ReferenceDefinition,
        CodeElementTemporaryVariable: ReferenceDefinition,
        CodeElementReturnValueReference: ReferenceDefinition,
    }

    def __init__(self):
        super().__init__()
        self.identifiers = IdentifierManager()

    def add_identifier(self, name: ScopedName,
                       identifier_definition: IdentifierDefinition,
                       location: Optional[Location]):
        """
        Adds an identifier with name 'name' and the given identifier definition at location
        'location'.
        """
        existing_definition = self.identifiers.get_by_full_name(name)
        if existing_definition is not None:
            if not isinstance(existing_definition, FutureIdentifierDefinition) or \
                    not isinstance(identifier_definition, FutureIdentifierDefinition):
                raise PreprocessorError(f"Redefinition of '{name}'.",
                                        location=location)
            if (existing_definition.identifier_type,
                    identifier_definition.identifier_type) != (
                        ReferenceDefinition, ReferenceDefinition):
                # Redefinition is only allowed in reference rebinding.
                raise PreprocessorError(f"Redefinition of '{name}'.",
                                        location=location)

        self.identifiers.add_identifier(name, identifier_definition)

    def add_future_identifier(self, name: ScopedName, identifier_type: type,
                              location: Optional[Location]):
        """
        Adds a future identifier with name 'name' of type 'identifier_type' at location 'location'.
        """

        self.add_identifier(name=name,
                            identifier_definition=FutureIdentifierDefinition(
                                identifier_type=identifier_type),
                            location=location)

    def visit(self, obj):
        if type(obj) in self.IDENTIFIER_DEFINERS:
            definition_type = self.IDENTIFIER_DEFINERS[type(obj)]
            identifier = _get_identifier(obj)
            self.add_future_identifier(self.current_scope + identifier.name,
                                       definition_type, identifier.location)
        return super().visit(obj)

    def _visit_default(self, obj):
        assert isinstance(obj, (CodeBlock, CodeElement)), \
            f'Received unexpected object of type {type(obj).__name__}.'

    def visit_CodeElementFunction(self, elm: CodeElementFunction):
        """
        Registers the function's identifier, arguments and return values, and then recursively
        visits the code block contained in the function.
        """
        function_scope = self.current_scope + elm.name
        if elm.element_type == 'struct':
            self.add_future_identifier(function_scope, StructDefinition,
                                       elm.identifier.location)
            return

        args_scope = function_scope + CodeElementFunction.ARGUMENT_SCOPE
        implicit_args_scope = function_scope + CodeElementFunction.IMPLICIT_ARGUMENT_SCOPE
        rets_scope = function_scope + CodeElementFunction.RETURN_SCOPE

        def handle_struct_def(identifier_list: Optional[IdentifierList],
                              struct_name: ScopedName):
            location = elm.identifier.location
            if identifier_list is not None:
                location = identifier_list.location

            self.add_future_identifier(name=struct_name,
                                       identifier_type=StructDefinition,
                                       location=location)

        def handle_function_arguments(
                identifier_list: Optional[IdentifierList],
                struct_name: ScopedName):
            handle_struct_def(identifier_list=identifier_list,
                              struct_name=struct_name)
            if identifier_list is None:
                return

            for arg_id in identifier_list.identifiers:
                if arg_id.name == N_LOCALS_CONSTANT:
                    raise PreprocessorError(
                        f"The name '{N_LOCALS_CONSTANT}' is reserved and cannot be used as an "
                        'argument name.',
                        location=arg_id.location)
                # Within a function, arguments are also accessible directly.
                self.add_future_identifier(function_scope + arg_id.name,
                                           ReferenceDefinition,
                                           arg_id.location)

        handle_function_arguments(identifier_list=elm.arguments,
                                  struct_name=args_scope)
        handle_function_arguments(identifier_list=elm.implicit_arguments,
                                  struct_name=implicit_args_scope)

        handle_struct_def(identifier_list=elm.returns, struct_name=rets_scope)

        # Make sure there is no name collision.
        if elm.implicit_arguments is not None:
            implicit_arg_names = {
                arg_id.name
                for arg_id in elm.implicit_arguments.identifiers
            }
            arg_and_return_identifiers = list(elm.arguments.identifiers)
            if elm.returns is not None:
                arg_and_return_identifiers += elm.returns.identifiers

            for arg_id in arg_and_return_identifiers:
                if arg_id.name in implicit_arg_names:
                    raise PreprocessorError(
                        'Arguments and return values cannot have the same name of an implicit '
                        'argument.',
                        location=arg_id.location)

        self.add_future_identifier(function_scope, LabelDefinition,
                                   elm.identifier.location)

        # Add SIZEOF_LOCALS for current block at identifier definition location if available.
        self.add_future_identifier(function_scope + N_LOCALS_CONSTANT,
                                   ConstDefinition, elm.identifier.location)
        super().visit_CodeElementFunction(elm)

    def visit_CodeElementUnpackBinding(self, elm: CodeElementUnpackBinding):
        """
        Registers all the unpacked identifiers.
        """
        for identifier in elm.unpacking_list.identifiers:
            if identifier.name == '_':
                continue
            self.add_future_identifier(self.current_scope + identifier.name,
                                       ReferenceDefinition,
                                       identifier.location)

    def visit_CodeElementIf(self, obj: CodeElementIf):
        assert obj.label_neq is not None
        assert obj.label_end is not None
        self.add_future_identifier(name=self.current_scope + obj.label_neq,
                                   identifier_type=LabelDefinition,
                                   location=obj.location)
        self.add_future_identifier(name=self.current_scope + obj.label_end,
                                   identifier_type=LabelDefinition,
                                   location=obj.location)
        self.visit(obj.main_code_block)
        if obj.else_code_block is not None:
            self.visit(obj.else_code_block)

    def visit_CodeBlock(self, code_block: CodeBlock):
        """
        Collects all identifiers in a code block.
        """
        for elm in code_block.code_elements:
            self.visit(elm.code_elm)

    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:
                raise PreprocessorError(
                    f"Scope '{elm.path.name}' does not include identifier "
                    f"'{import_item.orig_identifier.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)

    def visit_CodeElementWith(self, elm: CodeElementWith):
        for aliased_identifier in elm.identifiers:
            if aliased_identifier.local_name is not None:
                self.add_future_identifier(
                    name=self.current_scope +
                    aliased_identifier.local_name.name,
                    identifier_type=ReferenceDefinition,
                    location=aliased_identifier.local_name.location)
        self.visit(elm.code_block)
Esempio n. 26
0
 def __init__(self, identifiers: Optional[IdentifierManager] = None):
     super().__init__()
     if identifiers is None:
         identifiers = IdentifierManager()
     self.identifiers = identifiers
     self.identifier_locations: Dict[ScopedName, Location] = {}
Esempio n. 27
0
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)
Esempio n. 28
0
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
Esempio n. 29
0
 def __init__(self, identifiers: Optional[IdentifierManager] = None):
     super().__init__()
     self.identifiers = IdentifierManager(
     ) if identifiers is None else identifiers
Esempio n. 30
0
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