def is_blob(self, allow_handle=False): """ Check if self is a blob type. A blob is a type that can be treated as a sequence of bytes without any semantics. This implies that no elements within the blob need to be translated in any way during command processing. :param allow_handle: If True, allow handles inside the blob. :return: True, iff self is a blob. """ individual_field_preds = [ t.is_blob(allow_handle) for t in self.fields.values() ] fields_pred = reduce(lambda a, b: Expr(a) & b, individual_field_preds, True) allowed = { "NW_OPAQUE", "NW_CALLBACK", "NW_CALLBACK", "NW_CALLBACK_REGISTRATION" } | ({"NW_HANDLE"} if allow_handle else set()) transfer_pred = Expr(self.transfer).one_of(allowed) pointee_pred = True if isinstance(self, StaticArray): pointee_pred = self.pointee.is_blob(allow_handle) & Expr( self.buffer_allocator == "malloc") pred = transfer_pred & fields_pred & pointee_pred if "ava_index" in str( pred ): # TODO: Remove this hack by tracking the usage of variables in expressions. return Expr(False) return pred
def compute_buffer_size(type: Type, original_type: Optional[Type] = None): size_expr = Expr(type.buffer) if original_type and original_type.pointee.spelling != type.pointee.spelling: size_adjustment = f" * sizeof({original_type.pointee.spelling}) / sizeof({type.pointee.spelling})" else: size_adjustment = "" return Expr(f"(size_t){size_expr.group()}{size_adjustment}").group()
def default_case(): return (Expr(type.transfer).equals("NW_HANDLE")).if_then_else( Expr(not type.deallocates).if_then_else( assign_record_replay_functions(param_value, type).then( record_call_metadata(param_value, type)), expunge_calls(param_value, type), ))
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), cmd_value_type, 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(): dealloc_code = (Expr(type.transfer).equals("NW_HANDLE") & type.deallocates).if_then_else( f""" ava_coupled_free(&__ava_endpoint, {local_value}); """.strip() ) return dealloc_code.then((Expr(arg.output) | arg.ret).if_then_else(f"{param_value} = {local_value};"))
def get_transfer_buffer_expr(value: str, type_: Type, *, not_null=False) -> ExprOrStr: """ Generate an expression that gets the transfer buffer of `value` (which has `type`). :param value: The value whose transfer buffer to get. :param type: The type of value. :param not_null: If True, then this function does not generate NULL checks. :return: A C expression. """ return (Expr(value).not_equals("NULL") | not_null).if_then_else_expression( Expr(type_.transfer).equals("NW_BUFFER").if_then_else_expression( f"({type_.spelling})command_channel_get_buffer(__chan, __cmd, {value})", Expr(type_.transfer).equals("NW_ZEROCOPY_BUFFER"). if_then_else_expression( # TODO(yuhc): Add back zero copy region supprot after this feature is refactored. # pylint: disable=line-too-long # f"({type_.spelling})ava_zcopy_region_decode_position_independent(__ava_endpoint.zcopy_region, {value})", f"({type_.spelling})command_channel_get_buffer(__chan, __cmd, {value})", f"({type_.spelling}){value}", ), ), f"({type_.spelling}){value}", )
def get_buffer_code(): nonlocal reported_missing_lifetime if not reported_missing_lifetime: if ((arg.ret or arg.output and depth > 0) and type_.buffer) and type_.lifetime == "AVA_CALL": reported_missing_lifetime = True generate_expects( False, "Returned buffers with call lifetime are almost always incorrect. " "(You may want to set a lifetime.)", ) return Expr( f""" {DECLARE_BUFFER_SIZE_EXPR} {type_.attach_to(src_name)}; {src_name} = ({type_.spelling})({get_transfer_buffer_expr(local_value, type_, not_null=True)}); """ ).then( Expr(type_.lifetime) .not_equals("AVA_CALL") .if_then_else( f"""{get_buffer( param_value, cast_type, local_value, type_, original_type=original_type, not_null=True, declare_buffer_size=False )}""", f"""__buffer_size = {compute_buffer_size(type_, original_type)};""", ) .then(Expr(arg.output).if_then_else(f"AVA_DEBUG_ASSERT({param_value} != NULL);")) )
def convert_result_value(values, type: Type, depth, original_type=None, **other) -> str: if isinstance(type, ConditionalType): return Expr(type.predicate).if_then_else( convert_result_value(values, type.then_type, depth, original_type=type.original_type, **other), convert_result_value(values, type.else_type, depth, original_type=type.original_type, **other)) if type.is_void: return """abort_with_reason("Reached code to handle void value.");""" original_value, local_value = values buffer_pred = (Expr(type.transfer).equals("NW_BUFFER") & Expr(local_value).not_equals("NULL") & (Expr(type.buffer) > 0)) def simple_buffer_case(): return "" def buffer_case(): if not hasattr(type, "pointee"): return """abort_with_reason("Reached code to handle buffer in non-pointer type.");""" tmp_name = f"__tmp_{arg.name}_{depth}" inner_values = (tmp_name, local_value) return buffer_pred.if_then_else(f""" {type.nonconst.attach_to(tmp_name)}; {get_buffer(tmp_name, original_value, type, original_type=original_type)} {for_all_elements(inner_values, type, depth=depth, original_type=original_type, **other)} """) def default_case(): return Expr(type.transfer).equals("NW_HANDLE").if_then_else(( ~Expr(type.deallocates) ).if_then_else( f"nw_handle_pool_assign_handle(handle_pool, (void*){original_value}, (void*){local_value});" ), ((Expr(arg.ret) | Expr(type.transfer).equals("NW_OPAQUE")) & Expr(not isinstance(type, FunctionPointer)) ).if_then_else(f"assert({original_value} == {local_value});")) if type.fields: return for_all_elements(values, type, depth=depth, original_type=original_type, **other) return type.is_simple_buffer().if_then_else( simple_buffer_case, Expr(type.transfer).equals("NW_BUFFER").if_then_else( buffer_case, default_case)).scope()
def default_case(): return Expr(type.transfer).equals("NW_HANDLE").if_then_else(( ~Expr(type.deallocates) ).if_then_else( f"nw_handle_pool_assign_handle(handle_pool, (void*){original_value}, (void*){local_value});" ), ((Expr(arg.ret) | Expr(type.transfer).equals("NW_OPAQUE")) & Expr(not isinstance(type, FunctionPointer)) ).if_then_else(f"assert({original_value} == {local_value});"))
def default_case(): handlepool_function = "nw_handle_pool_lookup_or_insert" return Expr(type.transfer).equals("NW_HANDLE").if_then_else( Expr(type.deallocates).if_then_else( f"{param_value} = NULL;", f"{param_value} = ({type.nonconst.spelling}){handlepool_function}(handle_pool, (void*){local_value});" ), Expr(not type.is_void).if_then_else( f"{param_value} = {local_value};"))
def convert_result_value(values, type: Type, depth, original_type=None, **other): if isinstance(type, ConditionalType): return Expr(type.predicate).if_then_else( convert_result_value(values, type.then_type, depth, original_type=type.original_type, **other), convert_result_value(values, type.else_type, depth, original_type=type.original_type, **other)) local_value, = values buffer_pred = (Expr(type.transfer).equals("NW_BUFFER") & f"{local_value} != NULL") dealloc_shadows = Expr(type.deallocates).if_then_else( f"ava_shadow_buffer_free_coupled(&__ava_endpoint, (void *){local_value});" ) def simple_buffer_case(): return "" def buffer_case(): if not hasattr(type, "pointee"): return """abort_with_reason("Reached code to handle buffer in non-pointer type.");""" return buffer_pred.if_then_else( for_all_elements(values, type, depth=depth, original_type=original_type, **other)) def default_case(): dealloc_code = Expr(type.deallocates).if_then_else( Expr(type.transfer).equals("NW_HANDLE").if_then_else(f""" ava_coupled_free(&__ava_endpoint, {local_value}); """.strip())) return dealloc_code if type.fields: return for_all_elements(values, type, depth=depth, original_type=original_type, **other) return type.is_simple_buffer(allow_handle=False).if_then_else( simple_buffer_case, Expr(type.transfer).equals("NW_BUFFER").if_then_else( buffer_case, (Expr(type.transfer).one_of({ "NW_OPAQUE", "NW_HANDLE" })).if_then_else(default_case))).then(dealloc_shadows).scope()
def deref_code(handlepool_function: str) -> callable: return lambda: (Expr(type.transfer).one_of({ "NW_CALLBACK", "NW_CALLBACK_REGISTRATION" })).if_then_else( f"{local_value} = ({param_value} == NULL) ? NULL : {type.callback_stub_function};", (Expr(type.transfer).equals("NW_HANDLE")).if_then_else( f"{local_value} = ({type.nonconst.spelling}){handlepool_function}(handle_pool, (void*){param_value});", Expr(not type.is_void).if_then_else( f"{local_value} = {param_value};", """abort_with_reason("Reached code to handle void value.");""" )))
def compute_buffer_size(type: Type, original_type: Optional[Type] = None): size_expr = Expr(type.buffer) if original_type and original_type.pointee.spelling != type.pointee.spelling: pointee_size = f"sizeof({type.pointee.spelling})" # for void* buffer, assume that each element is 1 byte if type.pointee.is_void: pointee_size = "1" size_adjustment = f" * sizeof({original_type.pointee.spelling}) / {pointee_size}" else: size_adjustment = "" return Expr(f"(size_t){size_expr.group()}{size_adjustment}").group()
def get_buffer( target: str, target_type: Type, value: str, type_: Type, *, original_type: Optional[Type], precomputed_size=None, not_null=False, declare_buffer_size=True, ) -> Expr: """ Generate code that sets target to the local buffer to use for value. After this code executes `__buffer_size` is equal to the buffer size in elements. :param target: The variable in which to store the buffer pointer. :param value: The original value to translate. :param type_: The type of value. :param original_type: The original type if type is immediately inside a ConditionalType. :param precomputed_size: The expression for a precomputed size of value in *elements*. :param not_null: If True, then this function does not generate NULL checks. :return: A series of C statements. """ size_expr = compute_buffer_size( type_, original_type) if precomputed_size is None else precomputed_size declarations = DECLARE_BUFFER_SIZE_EXPR if declare_buffer_size else Expr( "") pointee_size = f"sizeof({type_.pointee.spelling})" # for void* buffer, assume that each element is 1 byte if type_.pointee.is_void: pointee_size = "1" return declarations.then(( type_.lifetime.not_equals("AVA_CALL") & (Expr(value).not_equals("NULL") | not_null) ).if_then_else( # FIXME: The initial assert is probably incorrect. Should it be checking the ADDRESSES? # Should it be there at all? Commented for now: AVA_DEBUG_ASSERT({target} != {value}); # (was at top of code) # FIXME: Should we check the buffer size? Can it be computed here? # AVA_DEBUG_ASSERT(__buffer_size == {size_expr}); (was at bottom of code) f""" /* Size is in bytes until the division below. */ {target} = ({target_type})({get_buffer_expr(value, type_, not_null=not_null, size_out="__buffer_size")}); AVA_DEBUG_ASSERT(__buffer_size % {pointee_size} == 0); __buffer_size /= {pointee_size}; /* Size is now in elements. */ """.lstrip(), f""" __buffer_size = {size_expr}; {target} = ({target_type})({get_buffer_expr(value, type_, not_null=not_null, size_out="__buffer_size")}); """.strip(), ))
def attach_buffer(target, value, data, type: Type, copy: Union[Expr, Any], *, cmd, original_type: Optional[Type], expect_reply: bool, precomputed_size=None): """ Generate code to attach a buffer to a command. :param target: The value to set to the buffer offset after attaching. :param value: The value to attach. :param type: The type of value. :param copy: An expression which is true if if this value should be copied. :param cmd: The command to attach to. :param original_type: The original type if type is immediately inside a ConditionalType. :param precomputed_size: The expression for a precomputed size of value in *elements*. :return: A series of C statements. """ cmd = f"(struct command_base*){cmd}" size_expr = size_to_bytes( compute_buffer_size(type, original_type) if precomputed_size is None else precomputed_size, type) def simple_attach(func): return lambda: f"""{target} = ({type.nonconst.spelling}){func}(__chan, {cmd}, {data}, {size_expr}); """ def zerocopy_attach(func="ava_zcopy_region_encode_position_independent"): return lambda: f"""{target} = ({type.nonconst.spelling}){func}(__ava_endpoint.zcopy_region, {data}); """ def shadow_attach(func): return lambda: f"""{target} = ({type.nonconst.spelling}){func}(&__ava_endpoint, __chan, {cmd}, {value}, {data}, {size_expr}, {type.lifetime}, {type.buffer_allocator}, {type.buffer_deallocator}, alloca(sizeof(struct ava_buffer_header_t)));""" return type.transfer.equals("NW_ZEROCOPY_BUFFER").if_then_else( zerocopy_attach(), type.lifetime.equals("AVA_CALL").if_then_else( Expr(copy).if_then_else( simple_attach("command_channel_attach_buffer"), f"{target} = HAS_OUT_BUFFER_SENTINEL;" if expect_reply else f"{target} = NULL; /* No output */\n"), Expr(copy).if_then_else( shadow_attach("ava_shadow_buffer_attach_buffer"), shadow_attach("ava_shadow_buffer_attach_buffer_without_data"), )))
def default_case(): handlepool_function = "nw_handle_pool_lookup_or_insert" return ( Expr(type_.transfer) .equals("NW_HANDLE") .if_then_else( Expr(type_.deallocates).if_then_else( f"{param_value} = NULL;", f"{param_value} = ({cast_type})({handlepool_function}(handle_pool, (void*){local_value}));", ), Expr(not type_.is_void).if_then_else(f"{param_value} = {local_value};"), ) )
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()
def simple_buffer_case(): if not hasattr(type, "pointee"): return """abort_with_reason("Reached code to handle buffer in non-pointer type.");""" copy_code = Expr(arg.output).if_then_else( f"""memcpy({param_value}, {src_name}, {size_to_bytes("__buffer_size", type)});""") if copy_code: return Expr(local_value).not_equals("NULL").if_then_else( f""" {get_buffer_code()} {copy_code} """.strip() ) else: return ""
def convert_result_value(values, cast_type: Type, type_: Type, depth, original_type=None, **other) -> str: if isinstance(type_, ConditionalType): return Expr(type_.predicate).if_then_else( convert_result_value( values, type_.then_type, type_.then_type, depth, original_type=type_.original_type, **other ), convert_result_value( values, type_.else_type, type_.else_type, depth, original_type=type_.original_type, **other ), ) if type_.is_void: return """abort_with_reason("Reached code to handle void value.");""" (param_value,) = values buffer_pred = Expr(type_.transfer).equals("NW_BUFFER") & Expr(param_value).not_equals("NULL") def simple_buffer_case(): return "" def buffer_case(): if not hasattr(type_, "pointee"): return """abort_with_reason("Reached code to handle buffer in non-pointer type.");""" tmp_name = f"__tmp_{arg.name}_{depth}" inner_values = (tmp_name,) loop = for_all_elements(inner_values, cast_type, type_, depth=depth, original_type=original_type, **other) if loop: return buffer_pred.if_then_else( f""" {type_.nonconst.attach_to(tmp_name)}; {tmp_name} = {param_value}; {loop} """ ) return "" def default_case(): return (Expr(type_.transfer).equals("NW_HANDLE")).if_then_else( Expr(not type_.deallocates).if_then_else( assign_record_replay_functions(param_value, type_).then(record_call_metadata(param_value, type_)), expunge_calls(param_value), ) ) if type_.fields: return for_all_elements(values, cast_type, type_, depth=depth, original_type=original_type, **other) return type_.is_simple_buffer().if_then_else( simple_buffer_case, Expr(type_.transfer).equals("NW_BUFFER").if_then_else(buffer_case, default_case) )
def get_transfer_buffer_expr(value, type, *, not_null=False) -> ExprOrStr: """ Generate an expression that gets the transfer buffer of `value` (which has `type`). :param value: The value whose transfer buffer to get. :param type: The type of value. :param not_null: If True, then this function does not generate NULL checks. :return: A C expression. """ return (Expr(value).not_equals("NULL") | not_null).if_then_else_expression( Expr(type.transfer).equals("NW_BUFFER").if_then_else_expression( f"({type.spelling})command_channel_get_buffer(__chan, __cmd, {value})", Expr(type.transfer).equals("NW_ZEROCOPY_BUFFER"). if_then_else_expression( f"({type.spelling})ava_zcopy_region_decode_position_independent(__ava_endpoint.zcopy_region, {value})", f"({type.spelling}){value}")), f"({type.spelling}){value}")
def buffer_case(): if not hasattr(type_, "pointee"): return """abort_with_reason("Reached code to handle buffer in non-pointer type.");""" if not arg.output: return simple_buffer_case() tmp_name = f"__tmp_{arg.name}_{depth}" size_name = f"__size_{arg.name}_{depth}" inner_values = (tmp_name, local_value) return ( Expr(local_value) .not_equals("NULL") .if_then_else( f"""{{ {allocate_tmp_buffer(tmp_name, size_name, type_, alloc_list=alloc_list, original_type=original_type)} {for_all_elements( inner_values, cast_type, type_, precomputed_size=size_name, depth=depth, original_type=original_type, **other )} {attach_data(tmp_name)} }}""", f"{param_value} = NULL;", ) )
def record_call_metadata(handle: str, type_: Optional[Type]) -> Expr: log_call_command = """if(__call_log_offset == -1) { __call_log_offset = command_channel_log_transfer_command((struct command_channel_log*)__log, __chan, (const struct command_base *)__cmd); } assert(__call_log_offset != -1);""" log_ret_command = """if(__ret_log_offset == -1) { __ret_log_offset = command_channel_log_transfer_command((struct command_channel_log*)__log, __chan, (const struct command_base *)__ret); } assert(__ret_log_offset != -1);""" def dep(dependent, dependency): return f"ava_add_dependency(&__ava_endpoint, {dependent}, {dependency});" return ( Expr(type_ is None or type_.object_record).if_then_else( f""" {log_call_command}{log_ret_command} ava_add_recorded_call(&__ava_endpoint, {handle}, ava_new_offset_pair(__call_log_offset, __ret_log_offset)); """ ) # pylint: disable=superfluous-parens .then(lines(dep(handle, h) for h in (type_.object_depends_on if type_ else []))) )
def get_buffer_expr(value: str, type: Type, *, not_null=False, size_out=None) -> Expr: """ Generate an expression that finds the local buffer to use for `value`. :param value: The value. :param type: The type of value. :param not_null: If True, then this function does not generate NULL checks. :param size_out: If non-None, fill the existing variable with the buffer size in BYTES. :return: A C expression. """ if size_out: # handle cast of volative size_t* to size_t* explicitly for default # buffer size expr if size_out == "__buffer_size": size_out = "(size_t*)&" + size_out else: size_out = "&" + size_out else: size_out = "NULL" return ( type.lifetime.not_equals("AVA_CALL") & (Expr(value).not_equals("NULL") | not_null)).if_then_else_expression( f""" ({type.spelling})ava_shadow_buffer_get_buffer(&__ava_endpoint, __chan, __cmd, {value}, {type.lifetime}, {type.lifetime_coupled}, {size_out}, {type.buffer_allocator}, {type.buffer_deallocator}) """, (type.lifetime.equals("AVA_CALL") & type.transfer.one_of( {"NW_BUFFER", "NW_ZEROCOPY_BUFFER"})).if_then_else_expression( get_transfer_buffer_expr(value, type, not_null=not_null), f"({type.spelling}){value}"), )
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() inner_values = (local_value, src_name) core = for_all_elements( inner_values, local_value_type, type_, depth=depth, precomputed_size="__buffer_size", original_type=original_type, **other, ) return ( (type_.lifetime.not_equals("AVA_CALL") | arg.input) & Expr(param_value).not_equals("NULL") ).if_then_else( f""" {get_buffer_code()} {core} """.strip(), maybe_alloc_local_temporary_buffer, )
def buffer_case(): if not hasattr(type_, "pointee"): return """abort_with_reason("Reached code to handle buffer in non-pointer type.");""" if not arg.output: return simple_buffer_case() inner_values = (param_value, src_name) loop = for_all_elements( inner_values, cast_type, type_, depth=depth, precomputed_size="__buffer_size", original_type=original_type, **other, ) if loop: return ( Expr(local_value) .not_equals("NULL") .if_then_else( f""" {get_buffer_code()} {loop} """ ) ) return ""
def simple_buffer_case(): if not hasattr(type_, "pointee"): return """abort_with_reason("Reached code to handle buffer in non-pointer type.");""" copy_code = (Expr(arg.input) & Expr(local_value).not_equals(src_name)).if_then_else( f"""memcpy({local_value}, {src_name}, {size_to_bytes("__buffer_size", type_)});""" ) return ( (type_.lifetime.not_equals("AVA_CALL") | arg.input) & Expr(param_value).not_equals("NULL") ).if_then_else( f""" {get_buffer_code()} {copy_code} """.strip(), (Expr(arg.input) | type_.transfer.equals("NW_ZEROCOPY_BUFFER")).if_then_else( preassignment, maybe_alloc_local_temporary_buffer ), )
def maybe_alloc_local_temporary_buffer(): # TODO: Deduplicate with allocate_tmp_buffer allocator = type.buffer_allocator deallocator = type.buffer_deallocator return Expr(param_value).not_equals("NULL").if_then_else(f"""{{ const size_t __size = {compute_buffer_size(type, original_type)}; {local_value} = ({type.nonconst.spelling}){allocator}({size_to_bytes("__size", type)}); {alloc_list.insert(local_value, deallocator)} }}""")
def assign_record_replay_functions(local_value, type: Type) -> Expr: extract: Expr = type.object_explicit_state_extract replace: Expr = type.object_explicit_state_replace return ( extract.not_equals("NULL") | replace.not_equals("NULL") ).if_then_else( Expr( f"ava_assign_record_replay_functions(&__ava_endpoint, (void*){local_value}, {extract}, {replace});" ))
def print_value_deep(values, cast_type: Type, type: Type, depth, no_depends, argument, **other): (value, ) = values if type.is_void: return "" buffer_pred = Expr(type.transfer).equals("NW_BUFFER") & Expr( value).not_equals("NULL") def address(): if not hasattr(type, "pointee"): return """abort_with_reason("Reached code to handle buffer in non-pointer type.");""" tmp_name = f"__tmp_{argument.name}_{depth}" inner_values = (tmp_name, ) data_code = buffer_pred.if_then_else(f""" fprintf(file, " = {{"); {type.nonconst.attach_to(tmp_name)}; {tmp_name} = ({cast_type})({get_transfer_buffer_expr(value, type)}); {for_all_elements(inner_values, cast_type, type, precomputed_size=Expr(1), depth=depth, argument=argument, no_depends=no_depends, **other)} fprintf(file, ",...}}"); """) return f""" {printf("ptr 0x%012lx", f"(long int){value}")} {data_code} """ def handle(): return printf("handle %#lx", f"(long int){value}") def opaque(): st = str(type) if "*" in st: return printf("%#lx", f"(long int){value}") elif "int" in st: return printf("%ld", f"(long int){value}") elif "float" in st or "double" in st: return printf("%Lf", f"(long double){value}") else: # Fall back on pointer representation return printf("%#lx", f"(long int){value}") return Expr(bool( type.fields or argument.depends_on and no_depends)).if_then_else( "", # Using only else branch Expr(type.transfer).equals("NW_BUFFER").if_then_else( address, Expr(type.transfer).equals( "NW_ZEROCOPY_BUFFER").if_then_else( address, Expr(type.transfer).equals( "NW_OPAQUE").if_then_else( opaque, Expr(type.transfer).equals( "NW_HANDLE").if_then_else(handle)), ), ), )
def add_buffer_size(): size_expr = size_to_bytes(compute_buffer_size(type, original_type), type) return Expr(copy_pred(argument)).if_then_else( type.lifetime.equals("AVA_CALL").if_then_else( f"{size} += command_channel_buffer_size(__chan, {size_expr});\n", f"{size} += ava_shadow_buffer_size(&__ava_endpoint, __chan, {size_expr});\n" ), type.lifetime.equals("AVA_CALL").if_then_else( "", f"{size} += ava_shadow_buffer_size_without_data(&__ava_endpoint, __chan, {size_expr});\n" ))