| /* 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-assert.h" |
| |
| #ifdef USE_LIBFFI |
| #include "ffi.h" |
| #endif |
| |
| #if defined(USE_LIBFFI) && FFI_GO_CLOSURES |
| |
| /* 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 size_t go_results_size (const struct functype *) |
| __attribute__ ((no_split_stack)); |
| static void go_set_results (const struct functype *, unsigned char *, void **) |
| __attribute__ ((no_split_stack)); |
| |
| /* Get the total size required for the result parameters of a |
| function. */ |
| |
| static size_t |
| go_results_size (const struct functype *func) |
| { |
| int count; |
| const struct _type **types; |
| size_t off; |
| size_t maxalign; |
| int i; |
| |
| count = func->out.__count; |
| if (count == 0) |
| return 0; |
| |
| types = (const struct _type **) func->out.__values; |
| |
| /* A single integer return value is always promoted to a full word. |
| There is similar code below and in libgo/go/reflect/makefunc_ffi.go.*/ |
| if (count == 1) |
| { |
| switch (types[0]->kind & kindMask) |
| { |
| case kindBool: |
| case kindInt8: |
| case kindInt16: |
| case kindInt32: |
| case kindUint8: |
| case kindUint16: |
| case kindUint32: |
| return sizeof (ffi_arg); |
| |
| default: |
| break; |
| } |
| } |
| |
| off = 0; |
| maxalign = 0; |
| for (i = 0; i < count; ++i) |
| { |
| size_t align; |
| |
| align = types[i]->fieldAlign; |
| if (align > maxalign) |
| maxalign = align; |
| off = (off + align - 1) & ~ (align - 1); |
| off += types[i]->size; |
| } |
| |
| off = (off + maxalign - 1) & ~ (maxalign - 1); |
| |
| // The libffi library doesn't understand a struct with no fields. |
| // We generate a struct with a single field of type void. When used |
| // as a return value, libffi will think that requires a byte. |
| if (off == 0) |
| off = 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 functype *func, unsigned char *call_result, |
| void **results) |
| { |
| int count; |
| const struct _type **types; |
| size_t off; |
| int i; |
| |
| count = func->out.__count; |
| if (count == 0) |
| return; |
| |
| types = (const struct _type **) func->out.__values; |
| |
| /* A single integer return value is always promoted to a full word. |
| There is similar code above and in libgo/go/reflect/makefunc_ffi.go.*/ |
| if (count == 1) |
| { |
| switch (types[0]->kind & kindMask) |
| { |
| case kindBool: |
| case kindInt8: |
| case kindInt16: |
| case kindInt32: |
| case kindUint8: |
| case kindUint16: |
| case kindUint32: |
| { |
| 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]->fieldAlign; |
| size = types[i]->size; |
| off = (off + align - 1) & ~ (align - 1); |
| __builtin_memcpy (results[i], call_result + off, size); |
| off += size; |
| } |
| } |
| |
| /* The code that converts the Go type to an FFI type is written in Go, |
| so that it can allocate Go heap memory. */ |
| extern void ffiFuncToCIF(const struct functype*, _Bool, _Bool, ffi_cif*) |
| __asm__ ("runtime.ffiFuncToCIF"); |
| |
| /* 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. */ |
| |
| void |
| reflect_call (const struct functype *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->typ.kind & kindMask) == kindFunc); |
| ffiFuncToCIF (func_type, is_interface, is_method, &cif); |
| |
| call_result = (unsigned char *) malloc (go_results_size (func_type)); |
| |
| ffi_call_go (&cif, (void (*)(void)) func_val->fn, call_result, params, |
| func_val); |
| |
| /* 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 functype *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) */ |