Beispiel #1
0
def return_command_implementation(f: Function):
    with location(f"at {term.yellow(str(f.name))}", f.location):
        # pthread_mutex_lock(&nw_handler_lock);
        # took_lock = 1;
        generate_requires(not f.return_value.type.buffer or f.return_value.type.lifetime != Expr("AVA_CALL"), "Returned buffers must have a lifetime other than `call' (i.e., must be annotated with `ava_lifetime_static', `ava_lifetime_coupled', or `ava_lifetime_manual').")
        return f"""
        case {f.ret_id_spelling}: {{\
            {timing_code_guest("before_unmarshal", str(f.name), f.generate_timing_code)}
            ava_is_in = 0; ava_is_out = 1;
            struct {f.ret_spelling}* __ret = (struct {f.ret_spelling}*)__cmd;
            assert(__ret->base.api_id == {f.api.number_spelling});
            assert(__ret->base.command_size == sizeof(struct {f.ret_spelling}) && "Command size does not match ID. (Can be caused by incorrectly computed buffer sizes, especially using `strlen(s)` instead of `strlen(s)+1`)");
            struct {f.call_record_spelling}* __local = (struct {f.call_record_spelling}*)ava_remove_call(&__ava_endpoint, __ret->__call_id);
        
            {{
                {unpack_struct("__local", f.arguments, "->")} \
                {unpack_struct("__local", f.logue_declarations, "->")} \
                {unpack_struct("__ret", [f.return_value], "->", convert=get_buffer_expr) 
                    if not f.return_value.type.is_void else ""} \
                {lines(copy_result_for_argument(a, "__local", "__ret")
                       for a in f.arguments if a.type.contains_buffer)}
                {copy_result_for_argument(f.return_value, "__local", "__ret") if not f.return_value.type.is_void else ""}\
                {lines(f.epilogue)}
                {lines(deallocate_managed_for_argument(a, "__local")
                       for a in f.arguments)}
            }}

            {timing_code_guest("after_unmarshal", str(f.name), f.generate_timing_code)}
            __local->__call_complete = 1;
            if(__local->__handler_deallocate) {{
                free(__local);
            }}
            break;
        }}
        """.strip()
Beispiel #2
0
def function_wrapper(f: Function) -> str:
    """
    Generate a wrapper function for f which takes the arguments of the function, executes the "logues", and
    calls the function.
    :param f: A function.
    :return: A C static function definition.
    """
    with location(f"at {term.yellow(str(f.name))}", f.location):
        if f.return_value.type.is_void:
            declare_ret = ""
            capture_ret = ""
            return_statement = "return;"
        else:
            declare_ret = f"{f.return_value.type.nonconst.attach_to(f.return_value.name)};"
            capture_ret = f"{f.return_value.name} = "
            return_statement = f"return {f.return_value.name};"
        if f.disable_native:
            # This works for both normal functions and callbacks because the
            # difference between the two is in the call, which is not emitted in
            # this case anyway.
            capture_ret = ""
            call_code = ""
            callback_unpack = ""
        elif not f.callback_decl:
            # Normal call
            call_code = f"""{f.name}({", ".join(a.name for a in f.real_arguments)})"""
            callback_unpack = ""
        else:
            # Indirect call (callback)
            try:
                userdata_arg, = [a for a in f.arguments if a.userdata]
            except ValueError:
                generate_requires(
                    False,
                    "ava_callback_decl function must have exactly one argument annotated with "
                    "ava_userdata.")
            call_code = f"""__target_function({", ".join(a.name for a in f.real_arguments)})"""
            callback_unpack = f"""
                {f.type.attach_to("__target_function")};
                __target_function = {f.type.cast_type(f"((struct ava_callback_user_data*){userdata_arg.name})->function_pointer")};
                {userdata_arg.name} = ((struct ava_callback_user_data*){userdata_arg.name})->userdata;
            """

        return f"""
        static {f.return_value.type.spelling} __wrapper_{f.name}({", ".join(a.declaration for a in f.arguments)}) {{
            {callback_unpack}\
            {lines(a.declaration + ";" for a in f.logue_declarations)}\
            {lines(f.prologue)}\
            {{
            {declare_ret}
            {capture_ret}{call_code};
            {lines(f.epilogue)}
    
            /* Report resources */
            {lines(report_alloc_resources(arg) for arg in f.arguments)}
            {report_alloc_resources(f.return_value)}
            {report_consume_resources(f)}
    
            {return_statement}
            }}
        }}
        """.strip()
Beispiel #3
0
def attach_for_argument(arg: Argument, dest):
    """
    Copy arg into dest attaching buffers as needed.
    :param arg: The argument to copy.
    :param dest: The destination CALL struct.
    :return: A series of C statements.
    """
    alloc_list = AllocList(arg.function)

    def copy_for_value(values, type: Type, depth, argument, original_type=None, **other):
        if isinstance(type, ConditionalType):
            return Expr(type.predicate).if_then_else(
                copy_for_value(values, type.then_type, depth, argument, original_type=type.original_type, **other),
                copy_for_value(values, type.else_type, depth, argument, original_type=type.original_type, **other))

        arg_value, cmd_value = values

        def attach_data(data):
            return attach_buffer(cmd_value, arg_value, data, type, arg.input, cmd=dest, original_type=original_type, expect_reply=True)

        def simple_buffer_case():
            if not hasattr(type, "pointee"):
                return """abort_with_reason("Reached code to handle buffer in non-pointer type.");"""
            return (Expr(arg_value).not_equals("NULL") & (Expr(type.buffer) > 0)).if_then_else(
                attach_data(arg_value),
                f"{cmd_value} = NULL;")

        def buffer_case():
            if not hasattr(type, "pointee"):
                return """abort_with_reason("Reached code to handle buffer in non-pointer type.");"""
            if not arg.input:
                return simple_buffer_case()

            tmp_name = f"__tmp_{arg.name}_{depth}"
            size_name = f"__size_{arg.name}_{depth}"
            loop = for_all_elements((arg_value, tmp_name), type, depth=depth, argument=argument,
                                    precomputed_size=size_name, original_type=original_type, **other)
            return (Expr(arg_value).not_equals("NULL") & (Expr(type.buffer) > 0)).if_then_else(
                f"""
                    {allocate_tmp_buffer(tmp_name, size_name, type, alloc_list=alloc_list, original_type=original_type)}
                    {loop}
                    {attach_data(tmp_name)}
                """,
                f"{cmd_value} = NULL;"
            )

        def default_case():
            return Expr(not type.is_void).if_then_else(
                f"{cmd_value} = {arg_value};",
                """abort_with_reason("Reached code to handle void value.");""")

        if type.fields:
            return for_all_elements(values, type, depth=depth, argument=argument, original_type=original_type, **other)
        return type.is_simple_buffer(allow_handle=True).if_then_else(
            simple_buffer_case,
            Expr(type.transfer).equals("NW_BUFFER").if_then_else(
                buffer_case,
                default_case
            )
        ).scope()

    with location(f"at {term.yellow(str(arg.name))}", arg.location):
        userdata_code = ""
        if arg.userdata and not arg.function.callback_decl:
            try:
                callback_arg, = [a for a in arg.function.arguments if a.type.transfer == "NW_CALLBACK"]
            except ValueError:
                generate_requires(False, "If ava_userdata is applied to an argument exactly one other argument "
                                         "must be annotated with ava_callback.")
            generate_requires([arg] == [a for a in arg.function.arguments if a.userdata],
                              "Only one argument on a given function can be annotated with ava_userdata.")
            userdata_code = f"""
            if ({callback_arg.param_spelling} != NULL) {{
                // TODO:MEMORYLEAK: This leaks 2*sizeof(void*) whenever a callback is transported. Should be fixable  
                //  with "coupled buffer" framework.
                struct ava_callback_user_data *__callback_data = malloc(sizeof(struct ava_callback_user_data));
                __callback_data->userdata = {arg.param_spelling};
                __callback_data->function_pointer = (void*){callback_arg.param_spelling};
                {arg.param_spelling} = __callback_data;
            }}
            """
        return comment_block(
            f"Input: {arg}",
            Expr(userdata_code).then(
            copy_for_value((arg.param_spelling, f"{dest}->{arg.param_spelling}"), arg.type, depth=0,
                           argument=arg,
                           name=arg.name,
                           kernel=copy_for_value,
                           only_complex_buffers=False, self_index=0)))