示例#1
0
def generate_inline_function(code, variables, variables_2, memory_allocator):
    ast_code = parse_to_ast(code)
    # Annotate the AST with a temporary old (i.e. typecheck) namespace
    namespace = Namespace()
    namespace.update(variables_2)
    with override_global_namespace(namespace):
        # Initialise a placeholder `FunctionDef` AST node and corresponding
        # `ContractFunction` type to rely on the annotation visitors in semantics
        # module.
        fn_node = vy_ast.FunctionDef()
        fn_node.body = []
        fn_node.args = vy_ast.arguments(defaults=[])
        fn_node._metadata["type"] = ContractFunction(
            "sqrt_builtin", {}, 0, 0, None, FunctionVisibility.INTERNAL,
            StateMutability.NONPAYABLE)
        sv = FunctionNodeVisitor(ast_code, fn_node, namespace)
        for n in ast_code.body:
            sv.visit(n)

    new_context = Context(vars_=variables,
                          global_ctx=GlobalContext(),
                          memory_allocator=memory_allocator)
    generated_ir = parse_body(ast_code.body, new_context)
    # strip source position info from the generated_ir since
    # it doesn't make any sense (e.g. the line numbers will start from 0
    # instead of where we are in the code)
    # NOTE if we ever use this for inlining user-code, it would make
    # sense to fix the offsets of the source positions in the generated
    # code instead of stripping them.
    _strip_source_pos(generated_ir)
    return new_context, generated_ir
示例#2
0
def generate_lll_for_internal_function(code: vy_ast.FunctionDef,
                                       sig: FunctionSignature,
                                       context: Context) -> LLLnode:
    """
    Parse a internal function (FuncDef), and produce full function body.

    :param sig: the FuntionSignature
    :param code: ast of function
    :param context: current calling context
    :return: function body in LLL
    """

    # The calling convention is:
    #   Caller fills in argument buffer
    #   Caller provides return address, return buffer on the stack
    #   Callee runs its code, fills in return buffer provided by caller
    #   Callee jumps back to caller

    # The reason caller fills argument buffer is so there is less
    # complication with passing args on the stack; the caller is better
    # suited to optimize the copy operation. Also it avoids the callee
    # having to handle default args; that is easier left to the caller
    # as well. Meanwhile, the reason the callee fills the return buffer
    # is first, similarly, the callee is more suited to optimize the copy
    # operation. Second, it allows the caller to allocate the return
    # buffer in a way which reduces the number of copies. Third, it
    # reduces the potential for bugs since it forces the caller to have
    # the return data copied into a preallocated location. Otherwise, a
    # situation like the following is easy to bork:
    #   x: T[2] = [self.generate_T(), self.generate_T()]

    func_type = code._metadata["type"]

    # Get nonreentrant lock

    for arg in sig.args:
        # allocate a variable for every arg, setting mutability
        # to False to comply with vyper semantics, function arguments are immutable
        context.new_variable(arg.name, arg.typ, is_mutable=False)

    nonreentrant_pre, nonreentrant_post = get_nonreentrant_lock(func_type)

    function_entry_label = sig.internal_function_label
    cleanup_label = sig.exit_sequence_label

    # jump to the label which was passed in via stack
    stop_func = LLLnode.from_list(["jump", "pass"],
                                  annotation="jump to return address")

    enter = [["label", function_entry_label]] + nonreentrant_pre

    body = [parse_body(c, context) for c in code.body]

    exit = [["label", cleanup_label]] + nonreentrant_post + [stop_func]

    return LLLnode.from_list(
        ["seq"] + enter + body + exit,
        typ=None,
        pos=getpos(code),
    )
示例#3
0
def generate_lll_for_external_function(code, sig, context, check_nonpayable):
    # TODO type hints:
    # def generate_lll_for_external_function(
    #    code: vy_ast.FunctionDef, sig: FunctionSignature, context: Context, check_nonpayable: bool,
    # ) -> LLLnode:
    """Return the LLL for an external function. Includes code to inspect the method_id,
    enter the function (nonpayable and reentrancy checks), handle kwargs and exit
    the function (clean up reentrancy storage variables)
    """
    func_type = code._metadata["type"]
    pos = getpos(code)

    nonreentrant_pre, nonreentrant_post = get_nonreentrant_lock(func_type)

    # generate handlers for base args and register the variable records
    handle_base_args = _register_function_args(context, sig)

    # generate handlers for kwargs and register the variable records
    kwarg_handlers = _generate_kwarg_handlers(context, sig, pos)

    # once optional args have been handled,
    # generate the main body of the function
    entrance = [["label", sig.external_function_base_entry_label]]

    entrance += handle_base_args

    if check_nonpayable and sig.mutability != "payable":
        # if the contract contains payable functions, but this is not one of them
        # add an assertion that the value of the call is zero
        entrance += [["assert", ["iszero", "callvalue"]]]

    entrance += nonreentrant_pre

    body = [parse_body(c, context) for c in code.body]

    exit = [["label", sig.exit_sequence_label]] + nonreentrant_post
    if sig.is_init_func:
        pass  # init func has special exit sequence generated by module.py
    elif context.return_type is None:
        exit += [["stop"]]
    else:
        # ret_ofst and ret_len stack items passed by function body; consume using 'pass'
        exit += [["return", "pass", "pass"]]

    # the lll which comprises the main body of the function,
    # besides any kwarg handling
    func_common_lll = ["seq"] + entrance + body + exit

    if sig.is_default_func or sig.is_init_func:
        # default and init funcs have special entries generated by module.py
        ret = func_common_lll
    else:
        ret = kwarg_handlers
        # sneak the base code into the kwarg handler
        # TODO rethink this / make it clearer
        ret[-1][-1].append(func_common_lll)

    return LLLnode.from_list(ret, pos=getpos(code))
示例#4
0
def generate_inline_function(code, variables, memory_allocator):
    ast_code = parse_to_ast(code)
    new_context = Context(vars_=variables,
                          global_ctx=GlobalContext(),
                          memory_allocator=memory_allocator)
    generated_ir = parse_body(ast_code.body, new_context)
    # strip source position info from the generated_ir since
    # it doesn't make any sense (e.g. the line numbers will start from 0
    # instead of where we are in the code)
    # NOTE if we ever use this for inlining user-code, it would make
    # sense to fix the offsets of the source positions in the generated
    # code instead of stripping them.
    _strip_source_pos(generated_ir)
    return new_context, generated_ir
示例#5
0
def generate_lll_for_external_function(code, sig, context, check_nonpayable):
    # TODO type hints:
    # def generate_lll_for_external_function(
    #    code: vy_ast.FunctionDef, sig: FunctionSignature, context: Context, check_nonpayable: bool,
    # ) -> LLLnode:
    """Return the LLL for an external function. Includes code to inspect the method_id,
    enter the function (nonpayable and reentrancy checks), handle kwargs and exit
    the function (clean up reentrancy storage variables)
    """
    func_type = code._metadata["type"]
    pos = getpos(code)

    nonreentrant_pre, nonreentrant_post = get_nonreentrant_lock(func_type)

    # generate handlers for base args and register the variable records
    handle_base_args = _register_function_args(context, sig)

    # generate handlers for kwargs and register the variable records
    kwarg_handlers = _generate_kwarg_handlers(context, sig, pos)

    body = ["seq"]
    # once optional args have been handled,
    # generate the main body of the function
    body += handle_base_args

    if check_nonpayable and sig.mutability != "payable":
        # if the contract contains payable functions, but this is not one of them
        # add an assertion that the value of the call is zero
        body += [["assert", ["iszero", "callvalue"]]]

    body += nonreentrant_pre

    body += [parse_body(code.body, context, ensure_terminated=True)]

    # wrap the body in labeled block
    body = ["label", sig.external_function_base_entry_label, ["var_list"], body]

    exit_sequence = ["seq"] + nonreentrant_post
    if sig.is_init_func:
        pass  # init func has special exit sequence generated by module.py
    elif context.return_type is None:
        exit_sequence += [["stop"]]
    else:
        exit_sequence += [["return", "ret_ofst", "ret_len"]]

    exit_sequence_args = ["var_list"]
    if context.return_type is not None:
        exit_sequence_args += ["ret_ofst", "ret_len"]
    # wrap the exit in a labeled block
    exit = ["label", sig.exit_sequence_label, exit_sequence_args, exit_sequence]

    # the lll which comprises the main body of the function,
    # besides any kwarg handling
    func_common_lll = ["seq", body, exit]

    if sig.is_default_func or sig.is_init_func:
        ret = ["seq"]
        # add a goto to make the function entry look like other functions
        # (for zksync interpreter)
        ret.append(["goto", sig.external_function_base_entry_label])
        ret.append(func_common_lll)
    else:
        ret = kwarg_handlers
        # sneak the base code into the kwarg handler
        # TODO rethink this / make it clearer
        ret[-1][-1].append(func_common_lll)

    return LLLnode.from_list(ret, pos=getpos(code))
示例#6
0
def generate_ir_for_internal_function(
    code: vy_ast.FunctionDef, sig: FunctionSignature, context: Context
) -> IRnode:
    """
    Parse a internal function (FuncDef), and produce full function body.

    :param sig: the FuntionSignature
    :param code: ast of function
    :param context: current calling context
    :return: function body in IR
    """

    # The calling convention is:
    #   Caller fills in argument buffer
    #   Caller provides return address, return buffer on the stack
    #   Callee runs its code, fills in return buffer provided by caller
    #   Callee jumps back to caller

    # The reason caller fills argument buffer is so there is less
    # complication with passing args on the stack; the caller is better
    # suited to optimize the copy operation. Also it avoids the callee
    # having to handle default args; that is easier left to the caller
    # as well. Meanwhile, the reason the callee fills the return buffer
    # is first, similarly, the callee is more suited to optimize the copy
    # operation. Second, it allows the caller to allocate the return
    # buffer in a way which reduces the number of copies. Third, it
    # reduces the potential for bugs since it forces the caller to have
    # the return data copied into a preallocated location. Otherwise, a
    # situation like the following is easy to bork:
    #   x: T[2] = [self.generate_T(), self.generate_T()]

    func_type = code._metadata["type"]

    # Get nonreentrant lock

    for arg in sig.args:
        # allocate a variable for every arg, setting mutability
        # to False to comply with vyper semantics, function arguments are immutable
        context.new_variable(arg.name, arg.typ, is_mutable=False)

    nonreentrant_pre, nonreentrant_post = get_nonreentrant_lock(func_type)

    function_entry_label = sig.internal_function_label
    cleanup_label = sig.exit_sequence_label

    stack_args = ["var_list"]
    if func_type.return_type:
        stack_args += ["return_buffer"]
    stack_args += ["return_pc"]

    body = [
        "label",
        function_entry_label,
        stack_args,
        ["seq"] + nonreentrant_pre + [parse_body(code.body, context, ensure_terminated=True)],
    ]

    cleanup_routine = [
        "label",
        cleanup_label,
        ["var_list", "return_pc"],
        ["seq"] + nonreentrant_post + [["exit_to", "return_pc"]],
    ]

    return IRnode.from_list(["seq", body, cleanup_routine])