Esempio n. 1
0
 def visit_ExprReg(self, expr: ExprReg) -> ExprConst:
     if expr.reg is Register.AP:
         assert self.ap is not None, 'Cannot substitute ap in the expression.'
         return ExprConst(val=self.ap, location=expr.location)
     elif expr.reg is Register.FP:
         return ExprConst(val=self.fp, location=expr.location)
     else:
         raise NotImplementedError(
             f'Register of type {expr.reg} is not supported')
Esempio n. 2
0
def test_hex_int():
    expr = parse_expr(' 0x1234 ')
    assert expr == ExprConst(val=0x1234)
    assert expr.format_str == '0x1234'
    assert expr.format() == '0x1234'

    expr = parse_expr('-0x01234')
    assert expr == ExprNeg(val=ExprConst(val=0x1234))
    assert expr.val.format_str == '0x01234'
    assert expr.format() == '-0x01234'

    assert parse_expr('-0x1234') == parse_expr('- 0x1234')
    def process_retdata(
            self, ret_struct_ptr: Expression, ret_struct_type: CairoType,
            struct_def: StructDefinition,
            location: Optional[Location]) -> Tuple[Expression, Expression]:
        """
        Processes the return values and return retdata_size and retdata_ptr.
        """

        # Verify all of the return types are felts.
        for _, member_def in struct_def.members.items():
            cairo_type = member_def.cairo_type
            if not isinstance(cairo_type, TypeFelt):
                raise PreprocessorError(
                    f'Unsupported argument type {cairo_type.format()}.',
                    location=cairo_type.location)

        self.add_reference(
            name=self.current_scope + 'retdata_ptr',
            value=ExprDeref(
                addr=ExprReg(reg=Register.AP),
                location=location,
            ),
            cairo_type=TypePointer(TypeFelt()),
            require_future_definition=False,
            location=location)

        self.visit(CodeElementHint(
            hint=ExprHint(
                hint_code='memory[ap] = segments.add()',
                n_prefix_newlines=0,
                location=location,
            ),
            location=location,
        ))

        # Skip check of hint whitelist as it fails before the workaround below.
        super().visit_CodeElementInstruction(CodeElementInstruction(InstructionAst(
            body=AddApInstruction(ExprConst(1)),
            inc_ap=False,
            location=location)))

        # Remove the references from the last instruction's flow tracking as they are
        # not needed by the hint and they cause the hint whitelist to fail.
        assert len(self.instructions[-1].hints) == 1
        hint, hint_flow_tracking_data = self.instructions[-1].hints[0]
        self.instructions[-1].hints[0] = hint, dataclasses.replace(
            hint_flow_tracking_data, reference_ids={})
        self.visit(CodeElementCompoundAssertEq(
            ExprDeref(
                ExprCast(ExprIdentifier('retdata_ptr'), TypePointer(ret_struct_type))),
            ret_struct_ptr))

        return (ExprConst(struct_def.size), ExprIdentifier('retdata_ptr'))
Esempio n. 4
0
def test_deref_expr():
    expr = parse_expr('[[fp - 7] + 3]')
    assert expr == \
        ExprDeref(
            addr=ExprOperator(
                a=ExprDeref(
                    addr=ExprOperator(
                        a=ExprReg(reg=Register.FP),
                        op='-',
                        b=ExprConst(val=7))),
                op='+',
                b=ExprConst(val=3)))
    assert expr.format() == '[[fp - 7] + 3]'
Esempio n. 5
0
 def eval_reference(self, identifier_definition, var_name: str):
     pc_offset = self.tracer_data.get_pc_offset(self.pc)
     current_flow_tracking_data = \
         self.tracer_data.program.debug_info.instruction_locations[pc_offset].flow_tracking_data
     try:
         substitute_transformer = SubstituteRegisterTransformer(
             ap=lambda location: ExprConst(val=self.ap, location=location),
             fp=lambda location: ExprConst(val=self.fp, location=location))
         return self.visit(substitute_transformer.visit(
             identifier_definition.eval(
                 reference_manager=self.tracer_data.program.reference_manager,
                 flow_tracking_data=current_flow_tracking_data)))
     except FlowTrackingError:
         raise FlowTrackingError(f"Invalid reference '{var_name}'.")
Esempio n. 6
0
 def rewrite_ExprNeg(self, expr: ExprNeg, sim: SimplicityLevel):
     # Treat "-val" as "val * (-1)".
     return self.rewrite(
         ExprOperator(a=expr.val,
                      op='*',
                      b=ExprConst(val=-1, location=expr.location),
                      location=expr.location), sim)
Esempio n. 7
0
def test_from_expr():
    assert RegChange.from_expr(5) == RegChangeKnown(5)
    assert RegChange.from_expr(RegChangeKnown(6)) == RegChangeKnown(6)
    assert RegChange.from_expr(ExprConst(7)) == RegChangeKnown(7)
    assert RegChange.from_expr(ExprIdentifier('asd')) == RegChangeUnknown()

    with pytest.raises(TypeError):
        RegChange.from_expr('wrong type')
Esempio n. 8
0
 def visit_ExprDeref(self, expr: ExprDeref) -> Expression:
     addr = self.visit(expr.addr)
     if not isinstance(addr, ExprConst):
         return expr
     assert self.prime is not None
     try:
         return ExprConst(val=self.memory[addr.val % self.prime],
                          location=expr.location)
     except Exception as exc:
         raise ExpressionEvaluatorError(str(exc), location=expr.location)
    def visit_ExprDot(self, expr: ExprDot) -> Tuple[ExprDeref, CairoType]:
        self.verify_identifier_manager_initialized(location=expr.location)

        inner_expr, inner_type = self.visit(expr.expr)
        if isinstance(inner_type, TypePointer):
            if not isinstance(inner_type.pointee, TypeStruct):
                raise CairoTypeError(
                    f'Cannot apply dot-operator to pointer-to-non-struct type '
                    f"'{inner_type.format()}'.",
                    location=expr.location)
            # Allow for . as ->, once.
            inner_type = inner_type.pointee
        elif isinstance(inner_type, TypeStruct):
            if isinstance(inner_expr, ExprTuple):
                raise CairoTypeError(
                    'Accessing struct members for r-value structs is not supported yet.',
                    location=expr.location)
            # Get the address, to evaluate . as ->.
            inner_expr = get_expr_addr(inner_expr)
        else:
            raise CairoTypeError(
                f"Cannot apply dot-operator to non-struct type '{inner_type.format()}'.",
                location=expr.location)

        try:
            struct_def = get_struct_definition(
                struct_name=inner_type.resolved_scope,
                identifier_manager=self.identifiers)
        except Exception as exc:
            raise CairoTypeError(str(exc), location=expr.location)

        if expr.member.name not in struct_def.members:
            raise CairoTypeError(
                f"Member '{expr.member.name}' does not appear in definition of struct "
                f"'{inner_type.format()}'.",
                location=expr.location)
        member_definition = struct_def.members[expr.member.name]
        member_type = member_definition.cairo_type
        member_offset = member_definition.offset

        if member_offset == 0:
            simplified_expr = ExprDeref(addr=inner_expr,
                                        location=expr.location)
        else:
            mem_offset_expr = ExprConst(val=member_offset,
                                        location=expr.location)
            simplified_expr = ExprDeref(addr=ExprOperator(
                a=inner_expr,
                op='+',
                b=mem_offset_expr,
                location=expr.location),
                                        location=expr.location)

        return simplified_expr, member_type
Esempio n. 10
0
def create_simple_ref_expr(reg: Register, offset: int, cairo_type: CairoType,
                           location: Optional[Location]) -> Expression:
    """
    Creates an expression of the form '[cast(reg + offset, cairo_type*)]'.
    """
    return ExprDeref(addr=ExprCast(expr=ExprOperator(
        a=ExprReg(reg=reg, location=location),
        op='+',
        b=ExprConst(val=offset, location=location),
        location=location),
                                   dest_type=TypePointer(pointee=cairo_type,
                                                         location=location),
                                   location=location),
                     location=location)
Esempio n. 11
0
def test_negative_numbers():
    assert ExprConst(-1).format() == '-1'
    assert ExprNeg(val=ExprConst(val=1)).format() == '-1'
    assert ExprOperator(a=ExprConst(val=-1), op='+',
                        b=ExprConst(val=-2)).format() == '(-1) + (-2)'
    assert ExprOperator(
        a=ExprNeg(val=ExprConst(val=1)),
        op='+',
        b=ExprNeg(val=ExprConst(val=2))).format() == '(-1) + (-2)'
Esempio n. 12
0
def test_add_expr():
    expr = parse_expr('[fp + 1] + [ap - x]')
    assert expr == \
        ExprOperator(
            a=ExprDeref(
                addr=ExprOperator(
                    a=ExprReg(reg=Register.FP),
                    op='+',
                    b=ExprConst(val=1))),
            op='+',
            b=ExprDeref(
                addr=ExprOperator(
                    a=ExprReg(reg=Register.AP),
                    op='-',
                    b=ExprIdentifier(name='x'))))
    assert expr.format() == '[fp + 1] + [ap - x]'
    assert parse_expr('[ap-7]+37').format() == '[ap - 7] + 37'
Esempio n. 13
0
    def visit_ExprPow(self, expr: ExprPow):
        a = self.visit(expr.a)
        # The exponent must not be computed modulo prime (as b = c (mod prime) does not imply
        # a**b = a**c (mod prime)).
        no_prime_simplifier = type(self)(prime=None)
        b = no_prime_simplifier.visit(expr.b)

        if isinstance(a, ExprConst) and isinstance(b, ExprConst):
            if b.val < 0:
                raise SimplifierError(
                    'Power is not supported with a negative exponent.', location=expr.location)
            if self.prime is not None:
                val = pow(a.val, b.val, self.prime)
            else:
                val = a.val ** b.val
            return ExprConst(val=val, location=expr.location)

        return ExprPow(a=a, b=b, location=expr.location)
Esempio n. 14
0
    def eval(
            self, reference_manager: ReferenceManager, flow_tracking_data: FlowTrackingData) -> \
            Expression:
        reference = flow_tracking_data.resolve_reference(
            reference_manager=reference_manager,
            name=self.parent.full_name)
        assert isinstance(flow_tracking_data, FlowTrackingDataActual), \
            'Resolved references can only come from FlowTrackingDataActual.'
        expr, expr_type = simplify_type_system(reference.eval(flow_tracking_data.ap_tracking))
        for member_name in self.member_path.path:
            if isinstance(expr_type, TypeStruct):
                expr_type = expr_type.get_pointer_type()
                # In this case, take the address of the reference value.
                to_addr = lambda expr: ExprAddressOf(expr=expr)
            else:
                to_addr = lambda expr: expr

            if not isinstance(expr_type, TypePointer) or \
                    not isinstance(expr_type.pointee, TypeStruct):
                raise DefinitionError('Member access requires a type of the form Struct*.')

            qualified_member = expr_type.pointee.resolved_scope + member_name
            if qualified_member not in self.identifier_values:
                raise DefinitionError(f"Member '{qualified_member}' was not found.")
            member_definition = self.identifier_values[qualified_member]

            if not isinstance(member_definition, MemberDefinition):
                raise DefinitionError(
                    f"Expected reference offset '{qualified_member}' to be a member, "
                    f'found {member_definition.TYPE}.')
            offset_value = member_definition.offset
            expr_type = member_definition.cairo_type

            expr = ExprDeref(addr=ExprOperator(a=to_addr(expr), op='+', b=ExprConst(offset_value)))

        return ExprCast(
            expr=expr,
            dest_type=expr_type,
        )
Esempio n. 15
0
def test_instruction():
    # AssertEq.
    expr = parse_instruction('[ap] = [fp]; ap++')
    assert expr == \
        InstructionAst(
            body=AssertEqInstruction(
                a=ExprDeref(
                    addr=ExprReg(reg=Register.AP)),
                b=ExprDeref(
                    addr=ExprReg(reg=Register.FP))),
            inc_ap=True)
    assert expr.format() == '[ap] = [fp]; ap++'
    assert parse_instruction(
        '[ap+5] = [fp]+[ap] - 5').format() == '[ap + 5] = [fp] + [ap] - 5'
    assert parse_instruction('[ap+5]+3= [fp]*7;ap  ++ ').format() == \
        '[ap + 5] + 3 = [fp] * 7; ap++'

    # Jump.
    expr = parse_instruction('jmp rel [ap] + x; ap++')
    assert expr == \
        InstructionAst(
            body=JumpInstruction(
                val=ExprOperator(
                    a=ExprDeref(addr=ExprReg(reg=Register.AP)),
                    op='+',
                    b=ExprIdentifier(name='x')),
                relative=True),
            inc_ap=True)
    assert expr.format() == 'jmp rel [ap] + x; ap++'
    assert parse_instruction(' jmp   abs[ap]+x').format() == 'jmp abs [ap] + x'
    # Make sure the following are not OK.
    with pytest.raises(ParserError):
        parse_instruction('jmp abs')
    with pytest.raises(ParserError):
        parse_instruction('jmpabs[ap]')

    # JumpToLabel.
    expr = parse_instruction('jmp label')
    assert expr == \
        InstructionAst(
            body=JumpToLabelInstruction(
                label=ExprIdentifier(name='label'),
                condition=None),
            inc_ap=False)
    assert expr.format() == 'jmp label'
    # Make sure the following are not OK.
    with pytest.raises(ParserError):
        parse_instruction('jmp [fp]')
    with pytest.raises(ParserError):
        parse_instruction('jmp 7')

    # Jnz.
    expr = parse_instruction('jmp rel [ap] + x if [fp + 3] != 0')
    assert expr == \
        InstructionAst(
            body=JnzInstruction(
                jump_offset=ExprOperator(
                    a=ExprDeref(addr=ExprReg(reg=Register.AP)),
                    op='+',
                    b=ExprIdentifier(name='x')),
                condition=ExprDeref(
                    addr=ExprOperator(
                        a=ExprReg(reg=Register.FP),
                        op='+',
                        b=ExprConst(val=3)))),
            inc_ap=False)
    assert expr.format() == 'jmp rel [ap] + x if [fp + 3] != 0'
    assert parse_instruction(' jmp   rel 17  if[fp]!=0;ap++').format() == \
        'jmp rel 17 if [fp] != 0; ap++'
    # Make sure the following are not OK.
    with pytest.raises(ParserError):
        parse_instruction('jmprel 17 if x != 0')
    with pytest.raises(ParserError):
        parse_instruction('jmp 17 if x')
    with pytest.raises(ParserError, match='!= 0'):
        parse_instruction('jmp rel 17 if x != 2')
    with pytest.raises(ParserError):
        parse_instruction('jmp rel [fp] ifx != 0')

    # Jnz to label.
    expr = parse_instruction('jmp label if [fp] != 0')
    assert expr == \
        InstructionAst(
            body=JumpToLabelInstruction(
                label=ExprIdentifier('label'),
                condition=ExprDeref(addr=ExprReg(reg=Register.FP))),
            inc_ap=False)
    assert expr.format() == 'jmp label if [fp] != 0'
    # Make sure the following are not OK.
    with pytest.raises(ParserError):
        parse_instruction('jmp [fp] if [fp] != 0')
    with pytest.raises(ParserError):
        parse_instruction('jmp 7 if [fp] != 0')

    # Call abs.
    expr = parse_instruction('call abs [fp] + x')
    assert expr == \
        InstructionAst(
            body=CallInstruction(
                val=ExprOperator(
                    a=ExprDeref(addr=ExprReg(reg=Register.FP)),
                    op='+',
                    b=ExprIdentifier(name='x')),
                relative=False),
            inc_ap=False)
    assert expr.format() == 'call abs [fp] + x'
    assert parse_instruction(
        'call   abs   17;ap++').format() == 'call abs 17; ap++'
    # Make sure the following are not OK.
    with pytest.raises(ParserError):
        parse_instruction('call abs')
    with pytest.raises(ParserError):
        parse_instruction('callabs 7')

    # Call rel.
    expr = parse_instruction('call rel [ap] + x')
    assert expr == \
        InstructionAst(
            body=CallInstruction(
                val=ExprOperator(
                    a=ExprDeref(addr=ExprReg(reg=Register.AP)),
                    op='+',
                    b=ExprIdentifier(name='x')),
                relative=True),
            inc_ap=False)
    assert expr.format() == 'call rel [ap] + x'
    assert parse_instruction(
        'call   rel   17;ap++').format() == 'call rel 17; ap++'
    # Make sure the following are not OK.
    with pytest.raises(ParserError):
        parse_instruction('call rel')
    with pytest.raises(ParserError):
        parse_instruction('callrel 7')

    # Call label.
    expr = parse_instruction('call label')
    assert expr == \
        InstructionAst(
            body=CallLabelInstruction(
                label=ExprIdentifier(name='label')),
            inc_ap=False)
    assert expr.format() == 'call label'
    assert parse_instruction(
        'call   label ;ap++').format() == 'call label; ap++'
    # Make sure the following are not OK.
    with pytest.raises(ParserError):
        parse_instruction('call [fp]')
    with pytest.raises(ParserError):
        parse_instruction('call 7')

    # Ret.
    expr = parse_instruction('ret')
    assert expr == \
        InstructionAst(
            body=RetInstruction(),
            inc_ap=False)
    assert expr.format() == 'ret'

    # AddAp.
    expr = parse_instruction('ap += [fp] + 2')
    assert expr == \
        InstructionAst(
            body=AddApInstruction(
                expr=ExprOperator(
                    a=ExprDeref(
                        addr=ExprReg(reg=Register.FP)),
                    op='+',
                    b=ExprConst(val=2))),
            inc_ap=False)
    assert expr.format() == 'ap += [fp] + 2'
    assert parse_instruction('ap  +=[ fp]+   2').format() == 'ap += [fp] + 2'
    assert parse_instruction(
        'ap  +=[ fp]+   2;ap ++').format() == 'ap += [fp] + 2; ap++'
    def visit_ExprSubscript(
            self, expr: ExprSubscript) -> Tuple[Expression, CairoType]:
        inner_expr, inner_type = self.visit(expr.expr)
        offset_expr, offset_type = self.visit(expr.offset)

        if isinstance(inner_type, TypeTuple):
            self.verify_offset_is_felt(offset_type, offset_expr.location)
            offset_expr = ExpressionSimplifier().visit(offset_expr)
            if not isinstance(offset_expr, ExprConst):
                raise CairoTypeError(
                    'Subscript-operator for tuples supports only constant offsets, found '
                    f"'{type(offset_expr).__name__}'.",
                    location=offset_expr.location)
            offset_value = offset_expr.val

            tuple_len = len(inner_type.members)
            if not 0 <= offset_value < tuple_len:
                raise CairoTypeError(
                    f'Tuple index {offset_value} is out of range [0, {tuple_len}).',
                    location=expr.location)

            item_type = inner_type.members[offset_value]

            if isinstance(inner_expr, ExprTuple):
                assert len(inner_expr.members.args) == tuple_len
                return (
                    # Take the inner item, but keep the original expression's location.
                    dataclasses.replace(
                        inner_expr.members.args[offset_value].expr,
                        location=expr.location),
                    item_type)
            elif isinstance(inner_expr, ExprDeref):
                # Handles pointers cast as tuples*, e.g. `[cast(ap, (felt, felt)*][0]`.
                addr = inner_expr.addr
                offset_in_felts = ExprConst(val=sum(
                    map(self.get_size, inner_type.members[:offset_value])),
                                            location=offset_expr.location)
                addr_with_offset = ExprOperator(a=addr,
                                                op='+',
                                                b=offset_in_felts,
                                                location=expr.location)
                return ExprDeref(addr=addr_with_offset,
                                 location=expr.location), item_type
            else:
                raise CairoTypeError(
                    'Unexpected expression typed as TypeTuple. Expected ExprTuple or ExprDeref, '
                    f"found '{type(inner_expr).__name__}'.",
                    location=expr.location)
        elif isinstance(inner_type, TypePointer):
            self.verify_offset_is_felt(offset_type, offset_expr.location)
            try:
                # If pointed type is struct, get_size could throw IdentifierErrors. We catch and
                # convert them to CairoTypeErrors.
                element_size = self.get_size(inner_type.pointee)
            except Exception as exc:
                raise CairoTypeError(str(exc), location=expr.location)

            element_size_expr = ExprConst(val=element_size,
                                          location=expr.location)
            modified_offset_expr = ExprOperator(a=offset_expr,
                                                op='*',
                                                b=element_size_expr,
                                                location=expr.location)
            simplified_expr = ExprDeref(addr=ExprOperator(
                a=inner_expr,
                op='+',
                b=modified_offset_expr,
                location=expr.location),
                                        location=expr.location)

            return simplified_expr, inner_type.pointee
        else:
            raise CairoTypeError(
                'Cannot apply subscript-operator to non-pointer, non-tuple type '
                f"'{inner_type.format()}'.",
                location=expr.location)
Esempio n. 17
0
 def visit_ExprNeg(self, expr: ExprNeg):
     val = self.visit(expr.val)
     if isinstance(val, ExprConst):
         return ExprConst(
             val=self._to_field_element(-val.val), location=expr.location)
     return ExprNeg(val=val, location=expr.location)
Esempio n. 18
0
 def atom_hex_number(self, value, meta):
     return ExprConst(val=int(value[0], 16),
                      format_str=value[0].value,
                      location=self.meta2loc(meta))
 def visit_ExprConst(self, expr: ExprConst):
     return ExprConst(val=expr.val,
                      format_str=expr.format_str,
                      location=self.location_modifier(expr.location))
Esempio n. 20
0
    def visit_ExprOperator(self, expr: ExprOperator):
        a = self.visit(expr.a)
        b = self.visit(expr.b)
        op = expr.op

        if isinstance(b, ExprConst) and op == '/' and b.val == 0:
            raise SimplifierError('Division by zero.', location=b.location)

        if isinstance(a, ExprConst) and isinstance(b, ExprConst):
            val = None
            if op == '/' and self.prime is not None:
                if b.val % self.prime == 0:
                    raise SimplifierError('Division by zero.', location=b.location)
                val = div_mod(a.val, b.val, self.prime)
            if op != '/':
                val = self._to_field_element(OPERATOR_DICT[op](a.val, b.val))
            if val is not None:
                return ExprConst(val, location=expr.location)

        if isinstance(a, ExprConst) and op == '+':
            assert not isinstance(b, ExprConst)
            # Move constant expression to the right. E.g., "5 + fp" -> "fp + 5"
            a, b = b, a

        if isinstance(b, ExprConst) and op == '-':
            # Replace x - y with x + (-y) for constant y.
            op = '+'
            b = ExprConst(val=self._to_field_element(-b.val), location=b.location)

        if isinstance(b, ExprConst) and op == '/' and self.prime is not None:
            # Replace x / y with x * (1/y) for constant y.
            op = '*'
            if b.val % self.prime == 0:
                raise SimplifierError('Division by zero.', location=b.location)
            inv_val = div_mod(1, b.val, self.prime)
            b = ExprConst(val=self._to_field_element(inv_val), location=b.location)

        if isinstance(b, ExprConst) and b.val == 0 and op in ['+', '-']:
            # Replace x + 0 and x - 0 by x.
            return a

        if isinstance(b, ExprConst) and b.val == 1 and op in ['*', '/']:
            # Replace x * 1 and x / 1 by x.
            return a

        if isinstance(a, ExprConst) and a.val == 1 and op == '*':
            # Replace 1 * x by x.
            return b

        if isinstance(b, ExprConst) and isinstance(a, ExprOperator) and \
                ((op == '+' and a.op in ['+', '-']) or (op == '*' and a.op == '*')):
            # If the expression is of the form "(a + b) + c" where c is constant, change it to
            # "a + (b + c)", this allows compiling expressions of the form: "[fp + x + y]".

            # Rotate right.
            return self.visit(ExprOperator(
                a=a.a,
                op=a.op,
                b=ExprOperator(
                    a=a.b,
                    op=a.op,
                    b=b,
                    location=expr.location),
                location=expr.location))

        return ExprOperator(a=a, op=op, b=b, location=expr.location)
Esempio n. 21
0
 def visit_ExprConst(self, expr: ExprConst):
     return ExprConst(
         val=self._to_field_element(expr.val), location=expr.location)
Esempio n. 22
0
 def ap(location):
     return ExprOperator(ExprReg(reg=Register.AP, location=location),
                         '-',
                         ExprConst(val=diff, location=location),
                         location=location)
 def visit_ExprIdentifier(self, expr: ExprIdentifier) -> Expression:
     val = self.get_identifier_callback(expr)
     if isinstance(val, int):
         return ExprConst(val, location=expr.location)
     return val
Esempio n. 24
0
 def visit_ExprPyConst(self, expr: ExprPyConst):
     if self.prime is None:
         return expr
     val = eval(expr.code, {'PRIME': self.prime}, {})
     return ExprConst(val=val, location=expr.location)
Esempio n. 25
0
 def atom_number(self, value, meta):
     return ExprConst(val=int(value[0]), location=self.meta2loc(meta))
    def create_func_wrapper(self, elm: CodeElementFunction, func_alias_name: str):
        """
        Generates a wrapper that converts between the StarkNet contract ABI and the
        Cairo calling convention.

        Arguments:
        elm - the CodeElementFunction of the wrapped function.
        func_alias_name - an alias for the FunctionDefention in the current scope.
        """

        os_context = self.get_os_context()

        func_location = elm.identifier.location
        assert func_location is not None

        # We expect the call stack to look as follows:
        # pointer to os_context struct.
        # calldata size.
        # pointer to the call data array.
        # ret_fp.
        # ret_pc.
        os_context_ptr = ExprDeref(
            addr=ExprOperator(
                ExprReg(reg=Register.FP, location=func_location),
                '+',
                ExprConst(-5, location=func_location),
                location=func_location),
            location=func_location)

        calldata_size = ExprDeref(
            addr=ExprOperator(
                ExprReg(reg=Register.FP, location=func_location),
                '+',
                ExprConst(-4, location=func_location),
                location=func_location),
            location=func_location)

        calldata_ptr = ExprDeref(
            addr=ExprOperator(
                ExprReg(reg=Register.FP, location=func_location),
                '+',
                ExprConst(-3, location=func_location),
                location=func_location),
            location=func_location)

        implicit_arguments = None

        implicit_arguments_identifiers: Dict[str, TypedIdentifier] = {}
        if elm.implicit_arguments is not None:
            args = []
            for typed_identifier in elm.implicit_arguments.identifiers:
                ptr_name = typed_identifier.name
                if ptr_name not in os_context:
                    raise PreprocessorError(
                        f"Unexpected implicit argument '{ptr_name}' in an external function.",
                        location=typed_identifier.identifier.location)

                implicit_arguments_identifiers[ptr_name] = typed_identifier

                # Add the assignment expression 'ptr_name = ptr_name' to the implicit arg list.
                args.append(ExprAssignment(
                    identifier=typed_identifier.identifier,
                    expr=typed_identifier.identifier,
                    location=typed_identifier.location,
                ))

            implicit_arguments = ArgList(
                args=args, notes=[], has_trailing_comma=True,
                location=elm.implicit_arguments.location)

        return_args_exprs: List[Expression] = []

        # Create references.
        for ptr_name, index in os_context.items():
            ref_name = self.current_scope + ptr_name

            arg_identifier = implicit_arguments_identifiers.get(ptr_name)
            if arg_identifier is None:
                location: Optional[Location] = func_location
                cairo_type: CairoType = TypeFelt(location=location)
            else:
                location = arg_identifier.location
                cairo_type = self.resolve_type(arg_identifier.get_type())

            # Add a reference of the form
            # 'let ref_name = [cast(os_context_ptr + index, cairo_type*)]'.
            self.add_reference(
                name=ref_name,
                value=ExprDeref(
                    addr=ExprCast(
                        ExprOperator(
                            os_context_ptr, '+', ExprConst(index, location=location),
                            location=location),
                        dest_type=TypePointer(pointee=cairo_type, location=cairo_type.location),
                        location=cairo_type.location),
                    location=location),
                cairo_type=cairo_type,
                location=location,
                require_future_definition=False)

            assert index == len(return_args_exprs), 'Unexpected index.'

            return_args_exprs.append(ExprIdentifier(name=ptr_name, location=func_location))

        arg_struct_def = self.get_struct_definition(
            name=ScopedName.from_string(func_alias_name) + CodeElementFunction.ARGUMENT_SCOPE,
            location=func_location)

        code_elements, call_args = process_calldata(
            calldata_ptr=calldata_ptr,
            calldata_size=calldata_size,
            identifiers=self.identifiers,
            struct_def=arg_struct_def,
            has_range_check_builtin='range_check_ptr' in os_context,
            location=func_location,
        )

        for code_element in code_elements:
            self.visit(code_element)

        self.visit(CodeElementFuncCall(
            func_call=RvalueFuncCall(
                func_ident=ExprIdentifier(name=func_alias_name, location=func_location),
                arguments=call_args,
                implicit_arguments=implicit_arguments,
                location=func_location)))

        ret_struct_name = ScopedName.from_string(func_alias_name) + CodeElementFunction.RETURN_SCOPE
        ret_struct_type = self.resolve_type(TypeStruct(ret_struct_name, False))
        ret_struct_def = self.get_struct_definition(
            name=ret_struct_name,
            location=func_location)
        ret_struct_expr = create_simple_ref_expr(
            reg=Register.AP, offset=-ret_struct_def.size, cairo_type=ret_struct_type,
            location=func_location)
        self.add_reference(
            name=self.current_scope + 'ret_struct',
            value=ret_struct_expr,
            cairo_type=ret_struct_type,
            require_future_definition=False,
            location=func_location)

        # Add function return values.
        retdata_size, retdata_ptr = self.process_retdata(
            ret_struct_ptr=ExprIdentifier(name='ret_struct'),
            ret_struct_type=ret_struct_type, struct_def=ret_struct_def,
            location=func_location,
        )
        return_args_exprs += [retdata_size, retdata_ptr]

        # Push the return values.
        self.push_compound_expressions(
            compound_expressions=[self.simplify_expr_as_felt(expr) for expr in return_args_exprs],
            location=func_location,
        )

        # Add a ret instruction.
        self.visit(CodeElementInstruction(
            instruction=InstructionAst(
                body=RetInstruction(),
                inc_ap=False,
                location=func_location)))

        # Add an entry to the ABI.
        external_decorator = self.get_external_decorator(elm)
        assert external_decorator is not None
        is_view = external_decorator.name == 'view'

        if external_decorator.name == L1_HANDLER_DECORATOR:
            entry_type = 'l1_handler'
        elif external_decorator.name in [EXTERNAL_DECORATOR, VIEW_DECORATOR]:
            entry_type = 'function'
        else:
            raise NotImplementedError(f'Unsupported decorator {external_decorator.name}')

        entry_type = (
            'function' if external_decorator.name != L1_HANDLER_DECORATOR else L1_HANDLER_DECORATOR)
        self.add_abi_entry(
            name=elm.name, arg_struct_def=arg_struct_def, ret_struct_def=ret_struct_def,
            is_view=is_view, entry_type=entry_type)