470 lines
12 KiB
C
470 lines
12 KiB
C
|
#ifndef HX_CFFI_H
|
||
|
#define HX_CFFI_H
|
||
|
|
||
|
// 410 - adds gc_try_unblocking
|
||
|
#define HX_CFFI_API_VERSION 410
|
||
|
|
||
|
#ifdef HXCPP_JS_PRIME
|
||
|
#include <emscripten/bind.h>
|
||
|
using namespace emscripten;
|
||
|
|
||
|
typedef struct emscripten::val value;
|
||
|
typedef struct _vkind *vkind;
|
||
|
typedef struct _buffer *buffer;
|
||
|
#define HAVE_NEKO_TYPES 1
|
||
|
#endif
|
||
|
|
||
|
#include "OS.h"
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#if defined(BLACKBERRY)
|
||
|
using namespace std;
|
||
|
#endif
|
||
|
// --- Register functions (primitives) ----
|
||
|
|
||
|
#ifdef STATIC_LINK
|
||
|
|
||
|
#define DEFINE_PRIM_MULT(func) \
|
||
|
int __reg_##func = hx_register_prim(#func "__MULT",(void *)(&func)); \
|
||
|
|
||
|
#define DEFINE_PRIM(func,nargs) \
|
||
|
int __reg_##func = hx_register_prim(#func "__" #nargs,(void *)(&func)); \
|
||
|
|
||
|
|
||
|
#define DEFINE_PRIM_MULT_NATIVE(func,ext) \
|
||
|
int __reg_##func = hx_register_prim(#func "__MULT",(void *)(&func)) + \
|
||
|
hx_register_prim(#func "__" #ext,(void *)(&func##_##ext)) ;
|
||
|
|
||
|
#define DEFINE_PRIM_NATIVE(func,nargs,ext) \
|
||
|
int __reg_##func = hx_register_prim(#func "__" #nargs,(void *)(&func)) + \
|
||
|
hx_register_prim(#func "__" #ext,(void *)(&func##_##ext)) ;
|
||
|
|
||
|
|
||
|
#define DEFINE_LIB_PRIM_MULT(lib,func) \
|
||
|
int __reg_##func = hx_register_prim(lib "_" #func "__MULT",(void *)(&func)); \
|
||
|
|
||
|
#define DEFINE_LIB_PRIM(lib,func,nargs) \
|
||
|
int __reg_##func = hx_register_prim(lib "_" #func "__" #nargs,(void *)(&func)); \
|
||
|
|
||
|
|
||
|
#elif defined(HXCPP_JS_PRIME)
|
||
|
|
||
|
//#define DEFINE_PRIM_MULT(func) EMSCRIPTEN_BINDINGS(func) { function(#func, &func); }
|
||
|
//TODO
|
||
|
#define DEFINE_PRIM_MULT(func)
|
||
|
|
||
|
#define DEFINE_PRIM(func,nargs) EMSCRIPTEN_BINDINGS(func) { function(#func, &func); }
|
||
|
|
||
|
|
||
|
#else
|
||
|
|
||
|
|
||
|
#define DEFINE_PRIM_MULT(func) extern "C" { \
|
||
|
EXPORT void *func##__MULT() { return (void*)(&func); } \
|
||
|
}
|
||
|
|
||
|
|
||
|
#define DEFINE_PRIM(func,nargs) extern "C" { \
|
||
|
EXPORT void *func##__##nargs() { return (void*)(&func); } \
|
||
|
}
|
||
|
|
||
|
|
||
|
#define DEFINE_PRIM_MULT_NATIVE(func,ext) extern "C" { \
|
||
|
EXPORT void *func##__MULT() { return (void*)(&func); } \
|
||
|
EXPORT void *func##__##ext() { return (void*)(&func##_##ext); } \
|
||
|
}
|
||
|
|
||
|
|
||
|
#define DEFINE_PRIM_NATIVE(func,nargs,ext) extern "C" { \
|
||
|
EXPORT void *func##__##nargs() { return (void*)(&func); } \
|
||
|
EXPORT void *func##__##ext() { return (void*)(&func##_##ext); } \
|
||
|
}
|
||
|
|
||
|
|
||
|
#define DEFINE_LIB_PRIM_MULT(lib,func) extern "C" { \
|
||
|
EXPORT void *func##__MULT() { return (void*)(&func); } \
|
||
|
}
|
||
|
|
||
|
|
||
|
#define DEFINE_LIB_PRIM(lib,func,nargs) extern "C" { \
|
||
|
EXPORT void *func##__##nargs() { return (void*)(&func); } \
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
#endif // !STATIC_LINK
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
#define DEFFUNC_0(ret,name) DEFFUNC(name,ret, (), ())
|
||
|
#define DEFFUNC_1(ret,name,t1) DEFFUNC(name,ret, (t1 a1), (a1))
|
||
|
#define DEFFUNC_2(ret,name,t1,t2) DEFFUNC(name,ret, (t1 a1, t2 a2), (a1,a2))
|
||
|
#define DEFFUNC_3(ret,name,t1,t2,t3) DEFFUNC(name,ret, (t1 a1, t2 a2, t3 a3), (a1,a2,a3))
|
||
|
#define DEFFUNC_4(ret,name,t1,t2,t3,t4) DEFFUNC(name,ret, (t1 a1, t2 a2, t3 a3, t4 a4), (a1,a2,a3,a4))
|
||
|
#define DEFFUNC_5(ret,name,t1,t2,t3,t4,t5) DEFFUNC(name,ret, (t1 a1, t2 a2, t3 a3, t4 a4,t5 a5), (a1,a2,a3,a4,a5))
|
||
|
|
||
|
|
||
|
enum hxValueType
|
||
|
{
|
||
|
valtUnknown = -1,
|
||
|
valtInt = 0xff,
|
||
|
valtNull = 0,
|
||
|
valtFloat = 1,
|
||
|
valtBool = 2,
|
||
|
valtString = 3,
|
||
|
valtObject = 4,
|
||
|
valtArray = 5,
|
||
|
valtFunction = 6,
|
||
|
valtEnum,
|
||
|
valtClass,
|
||
|
valtRoot = 0xff,
|
||
|
valtAbstractBase = 0x100,
|
||
|
};
|
||
|
|
||
|
namespace hx
|
||
|
{
|
||
|
enum StringEncoding
|
||
|
{
|
||
|
StringAscii,
|
||
|
StringUtf8,
|
||
|
StringUtf16
|
||
|
};
|
||
|
}
|
||
|
|
||
|
// val_fun_nargs may return a special value
|
||
|
enum { faNotFunction = -2, faVarArgs=-1, faArgs0=0 /* ... */ };
|
||
|
|
||
|
typedef int field;
|
||
|
|
||
|
|
||
|
|
||
|
#ifdef IMPLEMENT_API
|
||
|
#include "CFFILoader.h"
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#if !defined(HAVE_NEKO_TYPES)
|
||
|
#ifdef HXCPP_NATIVE_CFFI_VALUE
|
||
|
namespace hx { class Object; }
|
||
|
typedef hx::Object _value;
|
||
|
#else
|
||
|
struct _value;
|
||
|
#endif
|
||
|
typedef _value *value;
|
||
|
typedef struct _vkind *vkind;
|
||
|
typedef struct _buffer *buffer;
|
||
|
#endif
|
||
|
|
||
|
typedef buffer cffiByteBuffer;
|
||
|
|
||
|
typedef struct _gcroot *gcroot;
|
||
|
|
||
|
typedef void (*hxFinalizer)(value v);
|
||
|
typedef void (*hxPtrFinalizer)(void *v);
|
||
|
|
||
|
typedef void (__hx_field_iter)(value v,field f,void *);
|
||
|
|
||
|
#define hx_failure(msg) hx_fail(msg,__FILE__,__LINE__)
|
||
|
|
||
|
#ifndef IGNORE_CFFI_API_H
|
||
|
|
||
|
|
||
|
#ifndef IMPLEMENT_API
|
||
|
|
||
|
#if defined(STATIC_LINK) || defined(HXCPP_JS_PRIME)
|
||
|
|
||
|
#define DEFFUNC(name,ret,def_args,call_args) \
|
||
|
extern "C" ret name def_args;
|
||
|
|
||
|
|
||
|
#else
|
||
|
|
||
|
#define DEFFUNC(name,ret,def_args,call_args) \
|
||
|
typedef ret (*FUNC_##name) def_args; \
|
||
|
extern FUNC_##name name;
|
||
|
|
||
|
#endif
|
||
|
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#include "CFFIAPI.h"
|
||
|
|
||
|
|
||
|
#ifdef WANT_DYNALLOC_ALLOC_BYTES
|
||
|
void *DynAlloc::allocBytes(size_t n)
|
||
|
{
|
||
|
return hx_alloc((int)n);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
|
||
|
#define DEFINE_KIND(name) extern "C" { vkind name = 0; }
|
||
|
|
||
|
#ifdef STATIC_LINK
|
||
|
# define DEFINE_ENTRY_POINT(name)
|
||
|
#else
|
||
|
# define DEFINE_ENTRY_POINT(name) extern "C" { void name(); EXPORT void *__neko_entry_point() { return (void *)&name; } }
|
||
|
#endif
|
||
|
|
||
|
#ifdef HEADER_IMPORTS
|
||
|
# define H_EXTERN IMPORT
|
||
|
#else
|
||
|
# define H_EXTERN EXPORT
|
||
|
#endif
|
||
|
|
||
|
#define DECLARE_PRIM(func,nargs) extern "C" { H_EXTERN void *func##__##nargs(); }
|
||
|
#define DECLARE_KIND(name) extern "C" { H_EXTERN extern vkind name; }
|
||
|
|
||
|
|
||
|
|
||
|
// --- Helpers ----------------------------------------------------------------
|
||
|
|
||
|
// Check type...
|
||
|
inline bool val_is_null(value inVal) { return val_type(inVal)==valtNull; }
|
||
|
inline bool val_is_int(value inVal) { return val_type(inVal)==valtInt; }
|
||
|
inline bool val_is_bool(value inVal) { return val_type(inVal)==valtBool; }
|
||
|
inline bool val_is_float(value inVal) { return val_type(inVal)==valtFloat; }
|
||
|
inline bool val_is_string(value inVal) { return val_type(inVal)==valtString; }
|
||
|
inline bool val_is_function(value inVal) { return val_type(inVal)==valtFunction; }
|
||
|
inline bool val_is_array(value inVal) { return val_type(inVal)==valtArray; }
|
||
|
inline bool val_is_abstract(value inVal) { return val_type(inVal)>=valtAbstractBase; }
|
||
|
inline bool val_is_kind(value inVal,vkind inKind) { return val_to_kind(inVal,inKind)!=0; }
|
||
|
|
||
|
inline bool val_is_number(value inVal)
|
||
|
{
|
||
|
int t = val_type(inVal);
|
||
|
return t==valtInt || t==valtFloat;
|
||
|
}
|
||
|
inline bool val_is_object(value inVal)
|
||
|
{
|
||
|
int t = val_type(inVal);
|
||
|
return t==valtObject || t==valtEnum ||t==valtClass;
|
||
|
}
|
||
|
|
||
|
class AutoGCBlocking
|
||
|
{
|
||
|
public:
|
||
|
inline AutoGCBlocking(bool inSoftUnlock=false) :
|
||
|
mSoftUnlock(inSoftUnlock), mLocked( gc_try_blocking() ) { }
|
||
|
inline ~AutoGCBlocking() { Close(); }
|
||
|
inline void Close()
|
||
|
{
|
||
|
if (mLocked)
|
||
|
{
|
||
|
if (mSoftUnlock)
|
||
|
gc_try_unblocking();
|
||
|
else
|
||
|
gc_exit_blocking();
|
||
|
}
|
||
|
mLocked = false;
|
||
|
}
|
||
|
|
||
|
bool mLocked;
|
||
|
bool mSoftUnlock;
|
||
|
};
|
||
|
|
||
|
class AutoGCUnblocking
|
||
|
{
|
||
|
public:
|
||
|
AutoGCUnblocking() : mUnlocked( gc_try_unblocking() ) { }
|
||
|
~AutoGCUnblocking() { Close(); }
|
||
|
void Close() { if (mUnlocked) gc_enter_blocking(); mUnlocked = false; }
|
||
|
|
||
|
bool mUnlocked;
|
||
|
};
|
||
|
|
||
|
|
||
|
class AutoGCRoot
|
||
|
{
|
||
|
public:
|
||
|
AutoGCRoot(value inValue)
|
||
|
{
|
||
|
mRoot = 0;
|
||
|
mPtr = alloc_root();
|
||
|
if (mPtr)
|
||
|
*mPtr = inValue;
|
||
|
else
|
||
|
mRoot = create_root(inValue);
|
||
|
}
|
||
|
|
||
|
~AutoGCRoot()
|
||
|
{
|
||
|
if (mPtr)
|
||
|
free_root(mPtr);
|
||
|
else if (mRoot)
|
||
|
destroy_root(mRoot);
|
||
|
}
|
||
|
value get()const { return mPtr ? *mPtr : query_root(mRoot); }
|
||
|
void set(value inValue)
|
||
|
{
|
||
|
if (mPtr)
|
||
|
*mPtr = inValue;
|
||
|
else
|
||
|
{
|
||
|
if (mRoot) destroy_root(mRoot);
|
||
|
mRoot = create_root(inValue);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
value *mPtr;
|
||
|
gcroot mRoot;
|
||
|
AutoGCRoot(const AutoGCRoot &);
|
||
|
void operator=(const AutoGCRoot &);
|
||
|
};
|
||
|
|
||
|
struct CffiBytes
|
||
|
{
|
||
|
CffiBytes( unsigned char *inData=0, int inLength=0) : data(inData), length(inLength) {}
|
||
|
|
||
|
unsigned char *data;
|
||
|
int length;
|
||
|
};
|
||
|
|
||
|
inline CffiBytes getByteData(value inValue)
|
||
|
{
|
||
|
static field bField = 0;
|
||
|
static field lengthField = 0;
|
||
|
if (bField==0)
|
||
|
{
|
||
|
bField = val_id("b");
|
||
|
lengthField = val_id("length");
|
||
|
}
|
||
|
|
||
|
if (val_is_object(inValue))
|
||
|
{
|
||
|
value b = val_field(inValue, bField);
|
||
|
value len = val_field(inValue, lengthField);
|
||
|
if (val_is_string(b) && val_is_int(len))
|
||
|
return CffiBytes( (unsigned char *)val_string(b), val_int(len) );
|
||
|
if (val_is_buffer(b) && val_is_int(len))
|
||
|
return CffiBytes( (unsigned char *)buffer_data(val_to_buffer(b)), val_int(len) );
|
||
|
}
|
||
|
else if (val_is_buffer(inValue))
|
||
|
{
|
||
|
value len = val_field(inValue, lengthField);
|
||
|
if (val_is_int(len))
|
||
|
{
|
||
|
buffer b = val_to_buffer(inValue);
|
||
|
return CffiBytes( (unsigned char *)buffer_data(b), val_int(len) );
|
||
|
}
|
||
|
}
|
||
|
return CffiBytes();
|
||
|
}
|
||
|
|
||
|
inline bool resizeByteData(value inValue, int inNewLen)
|
||
|
{
|
||
|
if (!val_is_object(inValue))
|
||
|
return false;
|
||
|
|
||
|
static field bField = 0;
|
||
|
static field lengthField = 0;
|
||
|
if (bField==0)
|
||
|
{
|
||
|
bField = val_id("b");
|
||
|
lengthField = val_id("length");
|
||
|
}
|
||
|
value len = val_field(inValue, lengthField);
|
||
|
if (!val_is_int(len))
|
||
|
return false;
|
||
|
int oldLen = val_int(len);
|
||
|
value b = val_field(inValue, bField);
|
||
|
if (val_is_string(b))
|
||
|
{
|
||
|
if (inNewLen>oldLen)
|
||
|
{
|
||
|
value newString = alloc_raw_string(inNewLen);
|
||
|
memcpy( (char *)val_string(newString), val_string(b), inNewLen);
|
||
|
alloc_field(inValue, bField, newString );
|
||
|
}
|
||
|
alloc_field(inValue, lengthField, alloc_int(inNewLen) );
|
||
|
}
|
||
|
else if (val_is_buffer(b))
|
||
|
{
|
||
|
cffiByteBuffer buf = val_to_buffer(b);
|
||
|
buffer_set_size(buf,inNewLen);
|
||
|
alloc_field(inValue, lengthField, alloc_int(inNewLen) );
|
||
|
}
|
||
|
else
|
||
|
return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
#define val_null alloc_null()
|
||
|
|
||
|
#define bfailure(x) val_throw(buffer_to_string(x))
|
||
|
|
||
|
#define copy_string(str,len) alloc_string_len(str,len)
|
||
|
|
||
|
|
||
|
// The "Check" macros throw an error if assumtion is false
|
||
|
#define val_check_kind(v,t) if( !val_is_kind(v,t) ) hx_failure("invalid kind");
|
||
|
#define val_check_function(f,n) if( !val_is_function(f) || (val_fun_nargs(f) != (n) && val_fun_nargs(f) != faVarArgs) ) hx_failure("Bad function");
|
||
|
#define val_check(v,t) if( !val_is_##t(v) ) hx_failure("type not " #t);
|
||
|
|
||
|
// The "Get" function will return or force an error
|
||
|
inline bool val_get_bool(value inVal) { val_check(inVal,bool); return val_bool(inVal); }
|
||
|
inline int val_get_int(value inVal) { val_check(inVal,int); return val_int(inVal); }
|
||
|
inline double val_get_double(value inVal) { val_check(inVal,number); return val_number(inVal); }
|
||
|
inline const char *val_get_string(value inVal) { val_check(inVal,string); return val_string(inVal); }
|
||
|
inline void *val_get_handle(value inVal,vkind inKind)
|
||
|
{ val_check_kind(inVal,inKind); return val_to_kind(inVal,inKind); }
|
||
|
|
||
|
|
||
|
inline value alloc_string(const char *inStr)
|
||
|
{
|
||
|
const char *end = inStr;
|
||
|
while(*end) end++;
|
||
|
return alloc_string_len(inStr,(int)(end-inStr));
|
||
|
}
|
||
|
|
||
|
inline value alloc_wstring(const wchar_t *inStr)
|
||
|
{
|
||
|
const wchar_t *end = inStr;
|
||
|
while(*end) end++;
|
||
|
return alloc_wstring_len(inStr,(int)(end-inStr));
|
||
|
}
|
||
|
|
||
|
inline void hxcpp_unscramble(const unsigned char *bytes, int len, const char *key, unsigned char *dest)
|
||
|
{
|
||
|
int keyLen = 0;
|
||
|
while(key[keyLen])
|
||
|
keyLen++;
|
||
|
int state = 0;
|
||
|
//state = ((state + key[i]) ^ ch) & 0xff);
|
||
|
for(int i=0;i<len;i++)
|
||
|
{
|
||
|
dest[i] = ( (state + key[i%keyLen]) ^ bytes[i] ) & 0xff;
|
||
|
state = bytes[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//additional glue for easier neko modules compilation
|
||
|
#define val_true alloc_bool(true)
|
||
|
#define val_false alloc_bool(false)
|
||
|
inline void neko_error() { hx_error(); }
|
||
|
|
||
|
|
||
|
// Conservative marking within a buffer is not yet supported.
|
||
|
//inline void * alloc(int i) { return hx_alloc(i); }
|
||
|
|
||
|
// The bytes themselves will be GC'd, but not the pointers contained within.
|
||
|
inline void * alloc_private(int i) { return hx_alloc(i); }
|
||
|
|
||
|
// You should use alloc_buffer_len/buffer_data instead
|
||
|
//value alloc_empty_string(int len) { }
|
||
|
|
||
|
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#endif
|