| /* go-reflect-call.c -- call reflection support for Go. |
| |
| Copyright 2009 The Go Authors. All rights reserved. |
| Use of this source code is governed by a BSD-style |
| license that can be found in the LICENSE file. */ |
| |
| #include <stdio.h> |
| #include <stdint.h> |
| #include <stdlib.h> |
| |
| #include "runtime.h" |
| #include "go-alloc.h" |
| #include "go-assert.h" |
| #include "go-type.h" |
| |
| #ifdef USE_LIBFFI |
| |
| #include "ffi.h" |
| |
| /* The functions in this file are only called from reflect_call. As |
| reflect_call calls a libffi function, which will be compiled |
| without -fsplit-stack, it will always run with a large stack. */ |
| |
| static ffi_type *go_array_to_ffi (const struct __go_array_type *) |
| __attribute__ ((no_split_stack)); |
| static ffi_type *go_slice_to_ffi (const struct __go_slice_type *) |
| __attribute__ ((no_split_stack)); |
| static ffi_type *go_struct_to_ffi (const struct __go_struct_type *) |
| __attribute__ ((no_split_stack)); |
| static ffi_type *go_string_to_ffi (void) __attribute__ ((no_split_stack)); |
| static ffi_type *go_interface_to_ffi (void) __attribute__ ((no_split_stack)); |
| static ffi_type *go_complex_to_ffi (ffi_type *) |
| __attribute__ ((no_split_stack, unused)); |
| static ffi_type *go_type_to_ffi (const struct __go_type_descriptor *) |
| __attribute__ ((no_split_stack)); |
| static ffi_type *go_func_return_ffi (const struct __go_func_type *) |
| __attribute__ ((no_split_stack)); |
| static void go_func_to_cif (const struct __go_func_type *, _Bool, _Bool, |
| ffi_cif *) |
| __attribute__ ((no_split_stack)); |
| static size_t go_results_size (const struct __go_func_type *) |
| __attribute__ ((no_split_stack)); |
| static void go_set_results (const struct __go_func_type *, unsigned char *, |
| void **) |
| __attribute__ ((no_split_stack)); |
| |
| /* Return an ffi_type for a Go array type. The libffi library does |
| not have any builtin support for passing arrays as values. We work |
| around this by pretending that the array is a struct. */ |
| |
| static ffi_type * |
| go_array_to_ffi (const struct __go_array_type *descriptor) |
| { |
| ffi_type *ret; |
| uintptr_t len; |
| ffi_type *element; |
| uintptr_t i; |
| |
| ret = (ffi_type *) __go_alloc (sizeof (ffi_type)); |
| ret->type = FFI_TYPE_STRUCT; |
| len = descriptor->__len; |
| ret->elements = (ffi_type **) __go_alloc ((len + 1) * sizeof (ffi_type *)); |
| element = go_type_to_ffi (descriptor->__element_type); |
| for (i = 0; i < len; ++i) |
| ret->elements[i] = element; |
| ret->elements[len] = NULL; |
| return ret; |
| } |
| |
| /* Return an ffi_type for a Go slice type. This describes the |
| __go_open_array type defines in array.h. */ |
| |
| static ffi_type * |
| go_slice_to_ffi ( |
| const struct __go_slice_type *descriptor __attribute__ ((unused))) |
| { |
| ffi_type *ret; |
| ffi_type *ffi_intgo; |
| |
| ret = (ffi_type *) __go_alloc (sizeof (ffi_type)); |
| ret->type = FFI_TYPE_STRUCT; |
| ret->elements = (ffi_type **) __go_alloc (4 * sizeof (ffi_type *)); |
| ret->elements[0] = &ffi_type_pointer; |
| ffi_intgo = sizeof (intgo) == 4 ? &ffi_type_sint32 : &ffi_type_sint64; |
| ret->elements[1] = ffi_intgo; |
| ret->elements[2] = ffi_intgo; |
| ret->elements[3] = NULL; |
| return ret; |
| } |
| |
| /* Return an ffi_type for a Go struct type. */ |
| |
| static ffi_type * |
| go_struct_to_ffi (const struct __go_struct_type *descriptor) |
| { |
| ffi_type *ret; |
| int field_count; |
| const struct __go_struct_field *fields; |
| int i; |
| |
| field_count = descriptor->__fields.__count; |
| if (field_count == 0) { |
| return &ffi_type_void; |
| } |
| ret = (ffi_type *) __go_alloc (sizeof (ffi_type)); |
| ret->type = FFI_TYPE_STRUCT; |
| fields = (const struct __go_struct_field *) descriptor->__fields.__values; |
| ret->elements = (ffi_type **) __go_alloc ((field_count + 1) |
| * sizeof (ffi_type *)); |
| for (i = 0; i < field_count; ++i) |
| ret->elements[i] = go_type_to_ffi (fields[i].__type); |
| ret->elements[field_count] = NULL; |
| return ret; |
| } |
| |
| /* Return an ffi_type for a Go string type. This describes the String |
| struct. */ |
| |
| static ffi_type * |
| go_string_to_ffi (void) |
| { |
| ffi_type *ret; |
| ffi_type *ffi_intgo; |
| |
| ret = (ffi_type *) __go_alloc (sizeof (ffi_type)); |
| ret->type = FFI_TYPE_STRUCT; |
| ret->elements = (ffi_type **) __go_alloc (3 * sizeof (ffi_type *)); |
| ret->elements[0] = &ffi_type_pointer; |
| ffi_intgo = sizeof (intgo) == 4 ? &ffi_type_sint32 : &ffi_type_sint64; |
| ret->elements[1] = ffi_intgo; |
| ret->elements[2] = NULL; |
| return ret; |
| } |
| |
| /* Return an ffi_type for a Go interface type. This describes the |
| __go_interface and __go_empty_interface structs. */ |
| |
| static ffi_type * |
| go_interface_to_ffi (void) |
| { |
| ffi_type *ret; |
| |
| ret = (ffi_type *) __go_alloc (sizeof (ffi_type)); |
| ret->type = FFI_TYPE_STRUCT; |
| ret->elements = (ffi_type **) __go_alloc (3 * sizeof (ffi_type *)); |
| ret->elements[0] = &ffi_type_pointer; |
| ret->elements[1] = &ffi_type_pointer; |
| ret->elements[2] = NULL; |
| return ret; |
| } |
| |
| /* Return an ffi_type for a Go complex type. */ |
| |
| static ffi_type * |
| go_complex_to_ffi (ffi_type *float_type) |
| { |
| ffi_type *ret; |
| |
| ret = (ffi_type *) __go_alloc (sizeof (ffi_type)); |
| ret->type = FFI_TYPE_STRUCT; |
| ret->elements = (ffi_type **) __go_alloc (3 * sizeof (ffi_type *)); |
| ret->elements[0] = float_type; |
| ret->elements[1] = float_type; |
| ret->elements[2] = NULL; |
| return ret; |
| } |
| |
| /* Return an ffi_type for a type described by a |
| __go_type_descriptor. */ |
| |
| static ffi_type * |
| go_type_to_ffi (const struct __go_type_descriptor *descriptor) |
| { |
| switch (descriptor->__code & GO_CODE_MASK) |
| { |
| case GO_BOOL: |
| if (sizeof (_Bool) == 1) |
| return &ffi_type_uint8; |
| else if (sizeof (_Bool) == sizeof (int)) |
| return &ffi_type_uint; |
| abort (); |
| case GO_FLOAT32: |
| if (sizeof (float) == 4) |
| return &ffi_type_float; |
| abort (); |
| case GO_FLOAT64: |
| if (sizeof (double) == 8) |
| return &ffi_type_double; |
| abort (); |
| case GO_COMPLEX64: |
| #ifdef __alpha__ |
| runtime_throw("the libffi library does not support Complex64 type with " |
| "reflect.Call or runtime.SetFinalizer"); |
| #else |
| if (sizeof (float) == 4) |
| return go_complex_to_ffi (&ffi_type_float); |
| abort (); |
| #endif |
| case GO_COMPLEX128: |
| #ifdef __alpha__ |
| runtime_throw("the libffi library does not support Complex128 type with " |
| "reflect.Call or runtime.SetFinalizer"); |
| #else |
| if (sizeof (double) == 8) |
| return go_complex_to_ffi (&ffi_type_double); |
| abort (); |
| #endif |
| case GO_INT16: |
| return &ffi_type_sint16; |
| case GO_INT32: |
| return &ffi_type_sint32; |
| case GO_INT64: |
| return &ffi_type_sint64; |
| case GO_INT8: |
| return &ffi_type_sint8; |
| case GO_INT: |
| return sizeof (intgo) == 4 ? &ffi_type_sint32 : &ffi_type_sint64; |
| case GO_UINT16: |
| return &ffi_type_uint16; |
| case GO_UINT32: |
| return &ffi_type_uint32; |
| case GO_UINT64: |
| return &ffi_type_uint64; |
| case GO_UINT8: |
| return &ffi_type_uint8; |
| case GO_UINT: |
| return sizeof (uintgo) == 4 ? &ffi_type_uint32 : &ffi_type_uint64; |
| case GO_UINTPTR: |
| if (sizeof (void *) == 2) |
| return &ffi_type_uint16; |
| else if (sizeof (void *) == 4) |
| return &ffi_type_uint32; |
| else if (sizeof (void *) == 8) |
| return &ffi_type_uint64; |
| abort (); |
| case GO_ARRAY: |
| return go_array_to_ffi ((const struct __go_array_type *) descriptor); |
| case GO_SLICE: |
| return go_slice_to_ffi ((const struct __go_slice_type *) descriptor); |
| case GO_STRUCT: |
| return go_struct_to_ffi ((const struct __go_struct_type *) descriptor); |
| case GO_STRING: |
| return go_string_to_ffi (); |
| case GO_INTERFACE: |
| return go_interface_to_ffi (); |
| case GO_CHAN: |
| case GO_FUNC: |
| case GO_MAP: |
| case GO_PTR: |
| case GO_UNSAFE_POINTER: |
| /* These types are always pointers, and for FFI purposes nothing |
| else matters. */ |
| return &ffi_type_pointer; |
| default: |
| abort (); |
| } |
| } |
| |
| /* Return the return type for a function, given the number of out |
| parameters and their types. */ |
| |
| static ffi_type * |
| go_func_return_ffi (const struct __go_func_type *func) |
| { |
| int count; |
| const struct __go_type_descriptor **types; |
| ffi_type *ret; |
| int i; |
| |
| count = func->__out.__count; |
| if (count == 0) |
| return &ffi_type_void; |
| |
| types = (const struct __go_type_descriptor **) func->__out.__values; |
| |
| if (count == 1) |
| return go_type_to_ffi (types[0]); |
| |
| ret = (ffi_type *) __go_alloc (sizeof (ffi_type)); |
| ret->type = FFI_TYPE_STRUCT; |
| ret->elements = (ffi_type **) __go_alloc ((count + 1) * sizeof (ffi_type *)); |
| for (i = 0; i < count; ++i) |
| ret->elements[i] = go_type_to_ffi (types[i]); |
| ret->elements[count] = NULL; |
| return ret; |
| } |
| |
| /* Build an ffi_cif structure for a function described by a |
| __go_func_type structure. */ |
| |
| static void |
| go_func_to_cif (const struct __go_func_type *func, _Bool is_interface, |
| _Bool is_method, ffi_cif *cif) |
| { |
| int num_params; |
| const struct __go_type_descriptor **in_types; |
| size_t num_args; |
| ffi_type **args; |
| int off; |
| int i; |
| ffi_type *rettype; |
| ffi_status status; |
| |
| num_params = func->__in.__count; |
| in_types = ((const struct __go_type_descriptor **) |
| func->__in.__values); |
| |
| num_args = num_params + (is_interface ? 1 : 0); |
| args = (ffi_type **) __go_alloc (num_args * sizeof (ffi_type *)); |
| i = 0; |
| off = 0; |
| if (is_interface) |
| { |
| args[0] = &ffi_type_pointer; |
| off = 1; |
| } |
| else if (is_method) |
| { |
| args[0] = &ffi_type_pointer; |
| i = 1; |
| } |
| for (; i < num_params; ++i) |
| args[i + off] = go_type_to_ffi (in_types[i]); |
| |
| rettype = go_func_return_ffi (func); |
| |
| status = ffi_prep_cif (cif, FFI_DEFAULT_ABI, num_args, rettype, args); |
| __go_assert (status == FFI_OK); |
| } |
| |
| /* Get the total size required for the result parameters of a |
| function. */ |
| |
| static size_t |
| go_results_size (const struct __go_func_type *func) |
| { |
| int count; |
| const struct __go_type_descriptor **types; |
| size_t off; |
| size_t maxalign; |
| int i; |
| |
| count = func->__out.__count; |
| if (count == 0) |
| return 0; |
| |
| types = (const struct __go_type_descriptor **) func->__out.__values; |
| |
| /* A single integer return value is always promoted to a full |
| word. */ |
| if (count == 1) |
| { |
| switch (types[0]->__code & GO_CODE_MASK) |
| { |
| case GO_BOOL: |
| case GO_INT8: |
| case GO_INT16: |
| case GO_INT32: |
| case GO_UINT8: |
| case GO_UINT16: |
| case GO_UINT32: |
| case GO_INT: |
| case GO_UINT: |
| return sizeof (ffi_arg); |
| |
| default: |
| break; |
| } |
| } |
| |
| off = 0; |
| maxalign = 0; |
| for (i = 0; i < count; ++i) |
| { |
| size_t align; |
| |
| align = types[i]->__field_align; |
| if (align > maxalign) |
| maxalign = align; |
| off = (off + align - 1) & ~ (align - 1); |
| off += types[i]->__size; |
| } |
| |
| off = (off + maxalign - 1) & ~ (maxalign - 1); |
| |
| return off; |
| } |
| |
| /* Copy the results of calling a function via FFI from CALL_RESULT |
| into the addresses in RESULTS. */ |
| |
| static void |
| go_set_results (const struct __go_func_type *func, unsigned char *call_result, |
| void **results) |
| { |
| int count; |
| const struct __go_type_descriptor **types; |
| size_t off; |
| int i; |
| |
| count = func->__out.__count; |
| if (count == 0) |
| return; |
| |
| types = (const struct __go_type_descriptor **) func->__out.__values; |
| |
| /* A single integer return value is always promoted to a full |
| word. */ |
| if (count == 1) |
| { |
| switch (types[0]->__code & GO_CODE_MASK) |
| { |
| case GO_BOOL: |
| case GO_INT8: |
| case GO_INT16: |
| case GO_INT32: |
| case GO_UINT8: |
| case GO_UINT16: |
| case GO_UINT32: |
| case GO_INT: |
| case GO_UINT: |
| { |
| union |
| { |
| unsigned char buf[sizeof (ffi_arg)]; |
| ffi_arg v; |
| } u; |
| ffi_arg v; |
| |
| __builtin_memcpy (&u.buf, call_result, sizeof (ffi_arg)); |
| v = u.v; |
| |
| switch (types[0]->__size) |
| { |
| case 1: |
| { |
| uint8_t b; |
| |
| b = (uint8_t) v; |
| __builtin_memcpy (results[0], &b, 1); |
| } |
| break; |
| |
| case 2: |
| { |
| uint16_t s; |
| |
| s = (uint16_t) v; |
| __builtin_memcpy (results[0], &s, 2); |
| } |
| break; |
| |
| case 4: |
| { |
| uint32_t w; |
| |
| w = (uint32_t) v; |
| __builtin_memcpy (results[0], &w, 4); |
| } |
| break; |
| |
| case 8: |
| { |
| uint64_t d; |
| |
| d = (uint64_t) v; |
| __builtin_memcpy (results[0], &d, 8); |
| } |
| break; |
| |
| default: |
| abort (); |
| } |
| } |
| return; |
| |
| default: |
| break; |
| } |
| } |
| |
| off = 0; |
| for (i = 0; i < count; ++i) |
| { |
| size_t align; |
| size_t size; |
| |
| align = types[i]->__field_align; |
| size = types[i]->__size; |
| off = (off + align - 1) & ~ (align - 1); |
| __builtin_memcpy (results[i], call_result + off, size); |
| off += size; |
| } |
| } |
| |
| /* Call a function. The type of the function is FUNC_TYPE, and the |
| closure is FUNC_VAL. PARAMS is an array of parameter addresses. |
| RESULTS is an array of result addresses. |
| |
| If IS_INTERFACE is true this is a call to an interface method and |
| the first argument is the receiver, which is always a pointer. |
| This argument, the receiver, is not described in FUNC_TYPE. |
| |
| If IS_METHOD is true this is a call to a method expression. The |
| first argument is the receiver. It is described in FUNC_TYPE, but |
| regardless of FUNC_TYPE, it is passed as a pointer. |
| |
| If neither IS_INTERFACE nor IS_METHOD is true then we are calling a |
| function indirectly, and we must pass a closure pointer via |
| __go_set_closure. The pointer to pass is simply FUNC_VAL. */ |
| |
| void |
| reflect_call (const struct __go_func_type *func_type, FuncVal *func_val, |
| _Bool is_interface, _Bool is_method, void **params, |
| void **results) |
| { |
| ffi_cif cif; |
| unsigned char *call_result; |
| |
| __go_assert ((func_type->__common.__code & GO_CODE_MASK) == GO_FUNC); |
| go_func_to_cif (func_type, is_interface, is_method, &cif); |
| |
| call_result = (unsigned char *) malloc (go_results_size (func_type)); |
| |
| if (!is_interface && !is_method) |
| __go_set_closure (func_val); |
| ffi_call (&cif, func_val->fn, call_result, params); |
| |
| /* Some day we may need to free result values if RESULTS is |
| NULL. */ |
| if (results != NULL) |
| go_set_results (func_type, call_result, results); |
| |
| free (call_result); |
| } |
| |
| #else /* !defined(USE_LIBFFI) */ |
| |
| void |
| reflect_call (const struct __go_func_type *func_type __attribute__ ((unused)), |
| FuncVal *func_val __attribute__ ((unused)), |
| _Bool is_interface __attribute__ ((unused)), |
| _Bool is_method __attribute__ ((unused)), |
| void **params __attribute__ ((unused)), |
| void **results __attribute__ ((unused))) |
| { |
| /* Without FFI there is nothing we can do. */ |
| runtime_throw("libgo built without FFI does not support " |
| "reflect.Call or runtime.SetFinalizer"); |
| } |
| |
| #endif /* !defined(USE_LIBFFI) */ |