339 lines
9.5 KiB
C
339 lines
9.5 KiB
C
|
/* go-ffi.c -- convert Go type description to libffi.
|
||
|
|
||
|
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 and
|
||
|
reflect.ffi. As these functions call libffi functions, which will
|
||
|
be compiled without -fsplit-stack, they 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));
|
||
|
|
||
|
/* 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)
|
||
|
{
|
||
|
|
||
|
#if defined (__i386__) && !defined (__x86_64__)
|
||
|
/* FFI does not support complex types. On 32-bit x86, a
|
||
|
complex64 will be returned in %eax/%edx. We normally tell
|
||
|
FFI that a complex64 is a struct of two floats. On 32-bit
|
||
|
x86 a struct of two floats is returned via a hidden first
|
||
|
pointer parameter. Fortunately we can make everything work
|
||
|
by pretending that complex64 is int64. */
|
||
|
if ((types[0]->__code & GO_CODE_MASK) == GO_COMPLEX64)
|
||
|
return &ffi_type_sint64;
|
||
|
#endif
|
||
|
|
||
|
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. */
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
#endif /* defined(USE_LIBFFI) */
|