3d628e41b3
[INTERPRETER] * NEW: Add an API for the JIT compiler that retrieves the address of an object or a class for external functions. [GB.JIT] * BUG: Fix JIT compilation of extern function calls.
761 lines
15 KiB
C
761 lines
15 KiB
C
/***************************************************************************
|
|
|
|
gbx_extern.c
|
|
|
|
(c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
MA 02110-1301, USA.
|
|
|
|
***************************************************************************/
|
|
|
|
#define __GBX_EXTERN_C
|
|
|
|
#include "config.h"
|
|
#include "gb_common.h"
|
|
|
|
#ifdef HAVE_FFI_COMPONENT
|
|
|
|
#include <ffi.h>
|
|
|
|
#include "gb_common_buffer.h"
|
|
#include "gb_table.h"
|
|
#include "gb_hash.h"
|
|
#include "gbx_type.h"
|
|
#include "gbx_value.h"
|
|
#include "gbx_class_desc.h"
|
|
#include "gbx_class.h"
|
|
#include "gbx_exec.h"
|
|
#include "gbx_api.h"
|
|
#include "gbx_c_array.h"
|
|
#include "gbx_struct.h"
|
|
#include "gbx_extern.h"
|
|
|
|
typedef
|
|
struct {
|
|
SYMBOL sym;
|
|
lt_dlhandle handle;
|
|
}
|
|
EXTERN_SYMBOL;
|
|
|
|
typedef
|
|
struct {
|
|
ffi_cif cif;
|
|
ffi_type **types;
|
|
ffi_type *rtype;
|
|
}
|
|
EXTERN_CIF;
|
|
|
|
typedef
|
|
struct EXTERN_CALLBACK {
|
|
struct EXTERN_CALLBACK *next;
|
|
EXEC_GLOBAL exec;
|
|
void *closure;
|
|
void *code;
|
|
int nparam;
|
|
TYPE *sign;
|
|
TYPE ret;
|
|
EXTERN_CIF info;
|
|
}
|
|
EXTERN_CALLBACK;
|
|
|
|
typedef
|
|
struct EXTERN_FUNC {
|
|
struct EXTERN_FUNC *next;
|
|
char *alias;
|
|
void *call;
|
|
EXTERN_CIF info;
|
|
}
|
|
EXTERN_FUNC;
|
|
|
|
static TABLE *_table = NULL;
|
|
//static EXTERN_CALLBACK *_callbacks = NULL;
|
|
static HASH_TABLE *_callbacks = NULL;
|
|
static EXTERN_FUNC *_functions = NULL;
|
|
|
|
static ffi_type *_to_ffi_type[17] = {
|
|
&ffi_type_void, &ffi_type_sint32, &ffi_type_sint32, &ffi_type_sint32,
|
|
&ffi_type_sint32, &ffi_type_sint64, &ffi_type_float, &ffi_type_double,
|
|
&ffi_type_void, &ffi_type_pointer, &ffi_type_pointer, &ffi_type_pointer,
|
|
&ffi_type_void, &ffi_type_void, &ffi_type_void, &ffi_type_pointer,
|
|
&ffi_type_pointer
|
|
};
|
|
|
|
|
|
static void prepare_cif(EXTERN_CIF *info, int nsign, TYPE *sign, TYPE ret, int nparam, VALUE *value)
|
|
{
|
|
int i;
|
|
TYPE t;
|
|
|
|
info->types = NULL;
|
|
|
|
if (nparam > 0)
|
|
{
|
|
ALLOC(&info->types, sizeof(ffi_type *) * nparam);
|
|
|
|
for (i = 0; i < nparam; i++)
|
|
{
|
|
if (i < nsign)
|
|
t = sign[i];
|
|
else
|
|
t = value[i].type;
|
|
|
|
if (TYPE_is_object(t))
|
|
t = T_OBJECT;
|
|
|
|
info->types[i] = _to_ffi_type[t];
|
|
}
|
|
}
|
|
|
|
if (TYPE_is_object(ret))
|
|
t = T_OBJECT;
|
|
else
|
|
t = ret;
|
|
|
|
info->rtype = _to_ffi_type[t];
|
|
|
|
if (ffi_prep_cif(&info->cif, FFI_DEFAULT_ABI, nparam, info->rtype, info->types) != FFI_OK)
|
|
THROW(E_EXTCB, "Unable to prepare function description");
|
|
}
|
|
|
|
|
|
static lt_dlhandle get_library(const char *name)
|
|
{
|
|
EXTERN_SYMBOL *esym;
|
|
char *p;
|
|
int index;
|
|
|
|
if (!_table)
|
|
TABLE_create(&_table, sizeof(EXTERN_SYMBOL), TF_NORMAL);
|
|
|
|
index = TABLE_add_symbol(_table, name, strlen(name));
|
|
esym = (EXTERN_SYMBOL *)TABLE_get_symbol(_table, index);
|
|
if (!esym->handle)
|
|
{
|
|
/* !!! Must add the suffix !!! */
|
|
|
|
p = strrchr(name, ':');
|
|
if (!p)
|
|
sprintf(COMMON_buffer, "%s." SHARED_LIBRARY_EXT, name);
|
|
else
|
|
sprintf(COMMON_buffer, "%.*s." SHARED_LIBRARY_EXT ".%s", (int)(p - name), name, p + 1);
|
|
|
|
name = COMMON_buffer;
|
|
|
|
#ifndef DONT_USE_LTDL
|
|
/* no more available in libltld ?
|
|
lt_dlopen_flag = RTLD_LAZY;
|
|
*/
|
|
esym->handle = lt_dlopenext(name);
|
|
#else
|
|
esym->handle = dlopen(name, RTLD_LAZY);
|
|
#endif
|
|
|
|
if (esym->handle == NULL)
|
|
{
|
|
name = GB_RealFileName(name, strlen(name));
|
|
#ifndef DONT_USE_LTDL
|
|
esym->handle = lt_dlopenext(name);
|
|
#else
|
|
esym->handle = dlopen(name, RTLD_LAZY);
|
|
#endif
|
|
}
|
|
|
|
if (esym->handle == NULL)
|
|
THROW(E_EXTLIB, name, lt_dlerror());
|
|
|
|
//fprintf(stderr, "%s loaded.\n", name);
|
|
}
|
|
|
|
return esym->handle;
|
|
}
|
|
|
|
void *EXTERN_get_symbol(const char *library, const char *symbol)
|
|
{
|
|
lt_dlhandle handle = get_library(library);
|
|
return lt_dlsym(handle, symbol);
|
|
}
|
|
|
|
static EXTERN_FUNC *get_function(CLASS_EXTERN *ext)
|
|
{
|
|
EXTERN_FUNC *func;
|
|
void *call;
|
|
lt_dlhandle handle;
|
|
|
|
if (ext->loaded)
|
|
return (EXTERN_FUNC *)ext->alias;
|
|
|
|
handle = get_library(ext->library);
|
|
call = lt_dlsym(handle, ext->alias);
|
|
|
|
if (call == NULL)
|
|
THROW(E_EXTSYM, ext->library, ext->alias);
|
|
|
|
ALLOC_ZERO(&func, sizeof(EXTERN_FUNC));
|
|
func->next = _functions;
|
|
func->alias = ext->alias;
|
|
_functions = func;
|
|
|
|
func->call = call;
|
|
|
|
if (!ext->vararg)
|
|
prepare_cif(&func->info, ext->n_param, (TYPE *)ext->param, ext->type, ext->n_param, NULL);
|
|
|
|
//ext->library = (char *)handle;
|
|
ext->alias = (char *)func;
|
|
ext->loaded = TRUE;
|
|
|
|
return func;
|
|
}
|
|
|
|
void *EXTERN_get_addr(CLASS_EXTERN *ext)
|
|
{
|
|
EXTERN_FUNC *func = get_function(ext);
|
|
return func->call;
|
|
}
|
|
|
|
|
|
/*
|
|
EXEC.class : the class
|
|
EXEC.index : the extern function index
|
|
EXEC.nparam : the number of parameters to the call
|
|
EXEC.drop : if the return value should be dropped.
|
|
*/
|
|
|
|
void EXTERN_call(void)
|
|
{
|
|
static const void *jump[17] = {
|
|
&&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE,
|
|
&&__STRING, &&__STRING, &&__POINTER, &&__VARIANT, &&__FUNCTION, &&__CLASS, &&__NULL, &&__OBJECT
|
|
};
|
|
static const int use_temp[17] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, sizeof(char *), sizeof(char *), 0, 0, 0, 0, 0, sizeof(void *) };
|
|
static char temp[16 * sizeof(void *)];
|
|
static void *null = 0;
|
|
|
|
CLASS_EXTERN *ext = &EXEC.class->load->ext[EXEC.index];
|
|
EXTERN_FUNC *func;
|
|
EXTERN_CIF cif;
|
|
bool vararg;
|
|
int nparam = EXEC.nparam;
|
|
void *args[nparam];
|
|
TYPE *sign;
|
|
VALUE *value;
|
|
char *tmp = NULL;
|
|
char *next_tmp;
|
|
int i, t;
|
|
union {
|
|
int _integer;
|
|
float _single;
|
|
double _float;
|
|
char *_string;
|
|
int64_t _long;
|
|
void *_pointer;
|
|
}
|
|
rvalue;
|
|
|
|
if (!ext->loaded)
|
|
{
|
|
if (nparam < ext->n_param)
|
|
THROW(E_NEPARAM);
|
|
if (!ext->vararg && nparam > ext->n_param)
|
|
THROW(E_TMPARAM);
|
|
}
|
|
|
|
func = get_function(ext);
|
|
sign = (TYPE *)ext->param;
|
|
vararg = ext->vararg;
|
|
value = &SP[-nparam];
|
|
next_tmp = temp;
|
|
|
|
for (i = 0; i < nparam; i++)
|
|
{
|
|
if (i < ext->n_param)
|
|
VALUE_conv(&value[i], sign[i]);
|
|
else
|
|
VARIANT_undo(&value[i]);
|
|
}
|
|
|
|
if (vararg)
|
|
prepare_cif(&cif, ext->n_param, (TYPE *)ext->param, ext->type, nparam, value);
|
|
|
|
for (i = 0; i < nparam; i++, value++, sign++)
|
|
{
|
|
if (TYPE_is_object(value->type))
|
|
t = T_OBJECT;
|
|
else
|
|
t = (int)value->type;
|
|
|
|
if (use_temp[t])
|
|
{
|
|
tmp = next_tmp;
|
|
if ((tmp + use_temp[t]) > &temp[sizeof(temp)])
|
|
THROW(E_TMPARAM);
|
|
args[i] = tmp;
|
|
next_tmp = tmp + use_temp[t];
|
|
}
|
|
goto *jump[t];
|
|
|
|
__BOOLEAN:
|
|
__BYTE:
|
|
__SHORT:
|
|
__INTEGER:
|
|
args[i] = &value->_integer.value;
|
|
continue;
|
|
|
|
__LONG:
|
|
args[i] = &value->_long.value;
|
|
continue;
|
|
|
|
__SINGLE:
|
|
args[i] = &value->_single.value;
|
|
continue;
|
|
|
|
__FLOAT:
|
|
args[i] = &value->_float.value;
|
|
continue;
|
|
|
|
__STRING:
|
|
*((char **)tmp) = (char *)(value->_string.addr + value->_string.start);
|
|
continue;
|
|
|
|
__OBJECT:
|
|
*((void **)tmp) = OBJECT_get_addr(value->_object.object);
|
|
continue;
|
|
|
|
__POINTER:
|
|
args[i] = &value->_pointer.value;
|
|
continue;
|
|
|
|
__NULL:
|
|
args[i] = &null;
|
|
continue;
|
|
|
|
__DATE:
|
|
__VARIANT:
|
|
__VOID:
|
|
__CLASS:
|
|
__FUNCTION:
|
|
|
|
THROW(E_UTYPE);
|
|
}
|
|
|
|
if (vararg)
|
|
{
|
|
ffi_call(&cif.cif, func->call, &rvalue, args);
|
|
FREE(&cif.types);
|
|
}
|
|
else
|
|
ffi_call(&func->info.cif, func->call, &rvalue, args);
|
|
|
|
|
|
switch (ext->type)
|
|
{
|
|
case T_BOOLEAN:
|
|
case T_BYTE:
|
|
case T_SHORT:
|
|
case T_INTEGER:
|
|
GB_ReturnInteger(rvalue._integer);
|
|
break;
|
|
|
|
case T_LONG:
|
|
GB_ReturnLong(rvalue._long);
|
|
break;
|
|
|
|
case T_SINGLE:
|
|
GB_ReturnSingle(rvalue._single);
|
|
break;
|
|
|
|
case T_FLOAT:
|
|
GB_ReturnFloat(rvalue._float);
|
|
break;
|
|
|
|
case T_STRING:
|
|
GB_ReturnConstZeroString(rvalue._string);
|
|
break;
|
|
|
|
case T_POINTER:
|
|
GB_ReturnPointer(rvalue._pointer);
|
|
break;
|
|
|
|
case T_VOID:
|
|
default:
|
|
|
|
if (TYPE_is_pure_object(ext->type))
|
|
{
|
|
CLASS *class = (CLASS *)(ext->type);
|
|
if (CLASS_is_struct(class))
|
|
{
|
|
GB_ReturnObject(CSTRUCT_create_static(STRUCT_CONST, class, rvalue._pointer));
|
|
break;
|
|
}
|
|
}
|
|
|
|
TEMP.type = T_VOID;
|
|
break;
|
|
}
|
|
|
|
while (nparam)
|
|
{
|
|
nparam--;
|
|
POP();
|
|
}
|
|
|
|
POP(); /* extern function */
|
|
|
|
/* from EXEC_native() */
|
|
|
|
BORROW(&TEMP);
|
|
VALUE_conv(&TEMP, ext->type);
|
|
*SP = TEMP;
|
|
SP++;
|
|
}
|
|
|
|
void EXTERN_release(void)
|
|
{
|
|
EXTERN_CALLBACK *cb;
|
|
HASH_ENUM iter;
|
|
|
|
if (!_callbacks)
|
|
return;
|
|
|
|
CLEAR(&iter);
|
|
|
|
for(;;)
|
|
{
|
|
cb = HASH_TABLE_next(_callbacks, &iter, FALSE);
|
|
if (!cb)
|
|
break;
|
|
if (cb->exec.object)
|
|
{
|
|
OBJECT_UNREF(cb->exec.object);
|
|
cb->exec.object = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
void EXTERN_exit(void)
|
|
{
|
|
int i;
|
|
EXTERN_SYMBOL *esym;
|
|
EXTERN_CALLBACK *cb;
|
|
EXTERN_FUNC *func;
|
|
HASH_ENUM iter;
|
|
|
|
if (_table)
|
|
{
|
|
for (i = 0; i < TABLE_count(_table); i++)
|
|
{
|
|
esym = (EXTERN_SYMBOL *)TABLE_get_symbol(_table, i);
|
|
if (esym->handle)
|
|
lt_dlclose(esym->handle);
|
|
}
|
|
|
|
TABLE_delete(&_table);
|
|
}
|
|
|
|
if (_callbacks)
|
|
{
|
|
CLEAR(&iter);
|
|
|
|
for(;;)
|
|
{
|
|
cb = HASH_TABLE_next(_callbacks, &iter, FALSE);
|
|
if (!cb)
|
|
break;
|
|
if (cb->exec.object)
|
|
OBJECT_UNREF(cb->exec.object);
|
|
FREE(&cb->info.types);
|
|
ffi_closure_free(cb->closure);
|
|
}
|
|
|
|
HASH_TABLE_delete(&_callbacks);
|
|
}
|
|
|
|
while (_functions)
|
|
{
|
|
func = _functions;
|
|
_functions = func->next;
|
|
|
|
FREE(&func->info.types);
|
|
FREE(&func);
|
|
}
|
|
}
|
|
|
|
static void callback(ffi_cif *cif, void *result, void **args, void *user_data)
|
|
{
|
|
static const void *jump[17] = {
|
|
&&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE,
|
|
&&__STRING, &&__STRING, &&__POINTER, &&__VARIANT, &&__FUNCTION, &&__CLASS, &&__NULL, &&__OBJECT
|
|
};
|
|
|
|
EXTERN_CALLBACK *cb = (EXTERN_CALLBACK *)user_data;
|
|
//VALUE_FUNCTION *value = &cb->func;
|
|
int i;
|
|
VALUE *arg;
|
|
|
|
STACK_check(cb->nparam);
|
|
|
|
for (i = 0; i < cb->nparam; i++)
|
|
{
|
|
arg = SP++;
|
|
arg->type = cb->sign[i];
|
|
|
|
if (TYPE_is_object(cb->sign[i]))
|
|
goto __OBJECT;
|
|
else
|
|
goto *jump[cb->sign[i]];
|
|
|
|
__BOOLEAN:
|
|
arg->_integer.value = *((char *)args[i]) ? -1 : 0;
|
|
continue;
|
|
|
|
__BYTE:
|
|
arg->_integer.value = *((char *)args[i]);
|
|
continue;
|
|
|
|
__SHORT:
|
|
arg->_integer.value = *((short *)args[i]);
|
|
continue;
|
|
|
|
__INTEGER:
|
|
arg->_integer.value = *((int *)args[i]);
|
|
continue;
|
|
|
|
__LONG:
|
|
arg->_long.value = *((int64_t *)args[i]);
|
|
continue;
|
|
|
|
__SINGLE:
|
|
arg->_single.value = *((float *)args[i]);
|
|
continue;
|
|
|
|
__FLOAT:
|
|
arg->_float.value = *((double *)args[i]);
|
|
continue;
|
|
|
|
__STRING:
|
|
arg->type = T_CSTRING;
|
|
arg->_string.addr = *((char **)args[i]);
|
|
arg->_string.start = 0;
|
|
arg->_string.len = arg->_string.addr ? strlen(arg->_string.addr) : 0;
|
|
continue;
|
|
|
|
__OBJECT:
|
|
arg->_object.object = *((void **)args[i]);
|
|
continue;
|
|
|
|
__POINTER:
|
|
arg->_pointer.value = *((void **)args[i]);
|
|
continue;
|
|
|
|
__NULL:
|
|
__DATE:
|
|
__VARIANT:
|
|
__VOID:
|
|
__CLASS:
|
|
__FUNCTION:
|
|
VALUE_null(arg);
|
|
}
|
|
|
|
EXEC = cb->exec;
|
|
|
|
if (!EXEC.native)
|
|
{
|
|
EXEC_function_keep();
|
|
|
|
// Do that later, within a TRY/CATCH: VALUE_conv(RP, cb->ret);
|
|
|
|
switch (cb->ret)
|
|
{
|
|
case T_BOOLEAN:
|
|
case T_BYTE:
|
|
case T_SHORT:
|
|
case T_INTEGER:
|
|
*((ffi_arg *)result) = RP->_integer.value;
|
|
break;
|
|
|
|
case T_LONG:
|
|
*((int64_t *)result) = RP->_long.value;
|
|
break;
|
|
|
|
case T_SINGLE:
|
|
*((float *)result) = RP->_single.value;
|
|
break;
|
|
|
|
case T_FLOAT:
|
|
*((double *)result) = RP->_float.value;
|
|
break;
|
|
|
|
case T_STRING:
|
|
if (!RP->_string.len)
|
|
*((char **)result) = NULL;
|
|
else
|
|
*((char **)result) = RP->_string.addr + RP->_string.start;
|
|
break;
|
|
|
|
case T_OBJECT:
|
|
*((void **)result) = RP->_object.object;
|
|
break;
|
|
|
|
case T_POINTER:
|
|
*((void **)result) = RP->_pointer.value;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
EXEC_release_return_value();
|
|
}
|
|
}
|
|
|
|
|
|
static void prepare_cif_from_gambas(EXTERN_CALLBACK *cb, FUNCTION *func)
|
|
{
|
|
if (func->npmin != func->n_param || func->vararg)
|
|
THROW(E_EXTCB, "The function must take a fixed number of arguments");
|
|
|
|
cb->nparam = func->npmin;
|
|
cb->sign = (TYPE *)func->param;
|
|
cb->ret = func->type;
|
|
}
|
|
|
|
/*static void prepare_cif_from_native(EXTERN_CALLBACK *cb, CLASS_DESC_METHOD *desc)
|
|
{
|
|
THROW(E_EXTCB, "Not implemented yet");
|
|
}*/
|
|
|
|
void *EXTERN_make_callback(VALUE_FUNCTION *value)
|
|
{
|
|
EXEC_GLOBAL exec = { 0 };
|
|
FUNCTION *func;
|
|
EXTERN_CALLBACK *cb;
|
|
union {
|
|
char key[sizeof(void *)];
|
|
void *addr;
|
|
}
|
|
cb_key;
|
|
|
|
if (value->kind == FUNCTION_EXTERN)
|
|
{
|
|
CLASS_EXTERN *ext = &value->class->load->ext[value->index];
|
|
return get_function(ext)->call;
|
|
}
|
|
|
|
if (!_callbacks)
|
|
HASH_TABLE_create(&_callbacks, sizeof(EXTERN_CALLBACK), HF_NORMAL);
|
|
|
|
//ALLOC(&cb, sizeof(EXTERN_CALLBACK), "EXTERN_make_callback");
|
|
|
|
// See gbx_exec_loop.c, at the _CALL label, to understand the following.
|
|
|
|
if (value->kind == FUNCTION_PRIVATE)
|
|
{
|
|
exec.object = value->object;
|
|
exec.class = value->class;
|
|
exec.native = FALSE;
|
|
exec.index = value->index;
|
|
}
|
|
else if (value->kind == FUNCTION_PUBLIC)
|
|
{
|
|
exec.object = value->object;
|
|
exec.native = FALSE;
|
|
exec.desc = &value->class->table[value->index].desc->method;
|
|
exec.index = (int)(intptr_t)(exec.desc->exec);
|
|
exec.class = exec.desc->class;
|
|
}
|
|
/*else if (value->kind == FUNCTION_NATIVE)
|
|
{
|
|
cb->exec.object = value->object;
|
|
cb->exec.class = value->class;
|
|
cb->exec.native = TRUE;
|
|
cb->exec.index = value->index;
|
|
cb->exec.desc = &value->class->table[value->index].desc->method;
|
|
//cb->desc = &value->class->table[value->index].desc->method;
|
|
|
|
prepare_cif_from_native(cb, cb->exec.desc);
|
|
}*/
|
|
else
|
|
THROW(E_EXTCB, "Not supported");
|
|
|
|
func = &exec.class->load->func[exec.index];
|
|
cb_key.addr = func;
|
|
|
|
cb = (EXTERN_CALLBACK *)HASH_TABLE_insert(_callbacks, cb_key.key, sizeof(void *));
|
|
if (cb->code)
|
|
{
|
|
OBJECT_UNREF(value->object);
|
|
return cb->code;
|
|
}
|
|
|
|
// Do not reference value->_function.object, as it has been already referenced
|
|
// when put on the stack in exec_loop.c
|
|
|
|
/*if (value->object)
|
|
{
|
|
fprintf(stderr, "EXTERN_make_callback: ref: %p\n", value->object);
|
|
OBJECT_REF(value->object);
|
|
}*/
|
|
|
|
cb->exec = exec;
|
|
prepare_cif_from_gambas(cb, func);
|
|
|
|
cb->exec.nparam = cb->nparam;
|
|
|
|
prepare_cif(&cb->info, cb->nparam, cb->sign, cb->ret, cb->nparam, NULL);
|
|
|
|
cb->closure = ffi_closure_alloc(sizeof(ffi_closure), &cb->code);
|
|
|
|
if (ffi_prep_closure_loc(cb->closure, &cb->info.cif, callback, cb, cb->code) != FFI_OK)
|
|
THROW(E_EXTCB, "Unable to create closure");
|
|
|
|
return cb->code;
|
|
}
|
|
|
|
#else /* HAVE_FFI_COMPONENT */
|
|
|
|
#include "gbx_value.h"
|
|
#include "gbx_extern.h"
|
|
|
|
void EXTERN_call(void)
|
|
{
|
|
THROW_ILLEGAL();
|
|
}
|
|
|
|
void EXTERN_release(void)
|
|
{
|
|
}
|
|
|
|
void EXTERN_exit(void)
|
|
{
|
|
}
|
|
|
|
void *EXTERN_make_callback(VALUE_FUNCTION *value)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
void *EXTERN_get_symbol(const char *library, const char *symbol)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
void *EXTERN_get_addr(CLASS_EXTERN *ext)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
#endif
|
|
|