Viewing file: go-reflect-call.c (5.78 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
/* 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) */
|