mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-21 09:49:14 +00:00
Implement jsctypes with raw JSAPI. b=518721, r=jorendorff, sr=bsmedberg
This commit is contained in:
parent
c4f71f1e57
commit
a75255221b
@ -173,7 +173,6 @@
|
||||
@BINPATH@/components/inspector.xpt
|
||||
@BINPATH@/components/intl.xpt
|
||||
@BINPATH@/components/jar.xpt
|
||||
@BINPATH@/components/jsctypes.xpt
|
||||
@BINPATH@/components/jsdservice.xpt
|
||||
@BINPATH@/components/layout_base.xpt
|
||||
#ifdef NS_PRINTING
|
||||
|
@ -39,10 +39,9 @@
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "Function.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsIXPConnect.h"
|
||||
#include "nsCRT.h"
|
||||
#include "Library.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "jscntxt.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace ctypes {
|
||||
@ -154,17 +153,19 @@ TypeError(JSContext* cx, const char* expected, jsval actual)
|
||||
}
|
||||
|
||||
static bool
|
||||
GetABI(PRUint16 aCallType, ffi_abi& aResult)
|
||||
GetABI(JSContext* cx, jsval aCallType, ffi_abi& aResult)
|
||||
{
|
||||
ABICode abi = Module::GetABICode(cx, aCallType);
|
||||
|
||||
// determine the ABI from the subset of those available on the
|
||||
// given platform. nsIForeignLibrary::DEFAULT specifies the default
|
||||
// given platform. TYPE_DEFAULT specifies the default
|
||||
// C calling convention (cdecl) on each platform.
|
||||
switch (aCallType) {
|
||||
case nsIForeignLibrary::DEFAULT:
|
||||
switch (abi) {
|
||||
case ABI_default_abi:
|
||||
aResult = FFI_DEFAULT_ABI;
|
||||
return true;
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
case nsIForeignLibrary::STDCALL:
|
||||
case ABI_stdcall_abi:
|
||||
aResult = FFI_STDCALL;
|
||||
return true;
|
||||
#endif
|
||||
@ -176,51 +177,45 @@ GetABI(PRUint16 aCallType, ffi_abi& aResult)
|
||||
static bool
|
||||
PrepareType(JSContext* aContext, jsval aType, Type& aResult)
|
||||
{
|
||||
// for now, the only types we accept are integer values.
|
||||
if (!JSVAL_IS_INT(aType)) {
|
||||
JS_ReportError(aContext, "Invalid type specification");
|
||||
return false;
|
||||
}
|
||||
aResult.mType = Module::GetTypeCode(aContext, aType);
|
||||
|
||||
PRInt32 type = JSVAL_TO_INT(aType);
|
||||
|
||||
switch (type) {
|
||||
case nsIForeignLibrary::VOID:
|
||||
switch (aResult.mType) {
|
||||
case TYPE_void_t:
|
||||
aResult.mFFIType = ffi_type_void;
|
||||
break;
|
||||
case nsIForeignLibrary::INT8:
|
||||
case TYPE_int8_t:
|
||||
aResult.mFFIType = ffi_type_sint8;
|
||||
break;
|
||||
case nsIForeignLibrary::INT16:
|
||||
case TYPE_int16_t:
|
||||
aResult.mFFIType = ffi_type_sint16;
|
||||
break;
|
||||
case nsIForeignLibrary::INT32:
|
||||
case TYPE_int32_t:
|
||||
aResult.mFFIType = ffi_type_sint32;
|
||||
break;
|
||||
case nsIForeignLibrary::INT64:
|
||||
case TYPE_int64_t:
|
||||
aResult.mFFIType = ffi_type_sint64;
|
||||
break;
|
||||
case nsIForeignLibrary::BOOL:
|
||||
case nsIForeignLibrary::UINT8:
|
||||
case TYPE_bool:
|
||||
case TYPE_uint8_t:
|
||||
aResult.mFFIType = ffi_type_uint8;
|
||||
break;
|
||||
case nsIForeignLibrary::UINT16:
|
||||
case TYPE_uint16_t:
|
||||
aResult.mFFIType = ffi_type_uint16;
|
||||
break;
|
||||
case nsIForeignLibrary::UINT32:
|
||||
case TYPE_uint32_t:
|
||||
aResult.mFFIType = ffi_type_uint32;
|
||||
break;
|
||||
case nsIForeignLibrary::UINT64:
|
||||
case TYPE_uint64_t:
|
||||
aResult.mFFIType = ffi_type_uint64;
|
||||
break;
|
||||
case nsIForeignLibrary::FLOAT:
|
||||
case TYPE_float:
|
||||
aResult.mFFIType = ffi_type_float;
|
||||
break;
|
||||
case nsIForeignLibrary::DOUBLE:
|
||||
case TYPE_double:
|
||||
aResult.mFFIType = ffi_type_double;
|
||||
break;
|
||||
case nsIForeignLibrary::STRING:
|
||||
case nsIForeignLibrary::USTRING:
|
||||
case TYPE_string:
|
||||
case TYPE_ustring:
|
||||
aResult.mFFIType = ffi_type_pointer;
|
||||
break;
|
||||
default:
|
||||
@ -228,8 +223,6 @@ PrepareType(JSContext* aContext, jsval aType, Type& aResult)
|
||||
return false;
|
||||
}
|
||||
|
||||
aResult.mType = type;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -239,7 +232,7 @@ PrepareValue(JSContext* aContext, const Type& aType, jsval aValue, Value& aResul
|
||||
jsdouble d;
|
||||
|
||||
switch (aType.mType) {
|
||||
case nsIForeignLibrary::BOOL:
|
||||
case TYPE_bool:
|
||||
// Do not implicitly lose bits, but allow the values 0, 1, and -0.
|
||||
// Programs can convert explicitly, if needed, using `Boolean(v)` or `!!v`.
|
||||
if (!jsvalToIntStrict(aValue, &aResult.mValue.mUint8) ||
|
||||
@ -248,63 +241,63 @@ PrepareValue(JSContext* aContext, const Type& aType, jsval aValue, Value& aResul
|
||||
|
||||
aResult.mData = &aResult.mValue.mUint8;
|
||||
break;
|
||||
case nsIForeignLibrary::INT8:
|
||||
case TYPE_int8_t:
|
||||
// Do not implicitly lose bits.
|
||||
if (!jsvalToIntStrict(aValue, &aResult.mValue.mInt8))
|
||||
return TypeError(aContext, "int8", aValue);
|
||||
|
||||
aResult.mData = &aResult.mValue.mInt8;
|
||||
break;
|
||||
case nsIForeignLibrary::INT16:
|
||||
case TYPE_int16_t:
|
||||
// Do not implicitly lose bits.
|
||||
if (!jsvalToIntStrict(aValue, &aResult.mValue.mInt16))
|
||||
return TypeError(aContext, "int16", aValue);
|
||||
|
||||
aResult.mData = &aResult.mValue.mInt16;
|
||||
break;
|
||||
case nsIForeignLibrary::INT32:
|
||||
case TYPE_int32_t:
|
||||
// Do not implicitly lose bits.
|
||||
if (!jsvalToIntStrict(aValue, &aResult.mValue.mInt32))
|
||||
return TypeError(aContext, "int32", aValue);
|
||||
|
||||
aResult.mData = &aResult.mValue.mInt32;
|
||||
break;
|
||||
case nsIForeignLibrary::INT64:
|
||||
case TYPE_int64_t:
|
||||
// Do not implicitly lose bits.
|
||||
if (!jsvalToIntStrict(aValue, &aResult.mValue.mInt64))
|
||||
return TypeError(aContext, "int64", aValue);
|
||||
|
||||
aResult.mData = &aResult.mValue.mInt64;
|
||||
break;
|
||||
case nsIForeignLibrary::UINT8:
|
||||
case TYPE_uint8_t:
|
||||
// Do not implicitly lose bits.
|
||||
if (!jsvalToIntStrict(aValue, &aResult.mValue.mUint8))
|
||||
return TypeError(aContext, "uint8", aValue);
|
||||
|
||||
aResult.mData = &aResult.mValue.mUint8;
|
||||
break;
|
||||
case nsIForeignLibrary::UINT16:
|
||||
case TYPE_uint16_t:
|
||||
// Do not implicitly lose bits.
|
||||
if (!jsvalToIntStrict(aValue, &aResult.mValue.mUint16))
|
||||
return TypeError(aContext, "uint16", aValue);
|
||||
|
||||
aResult.mData = &aResult.mValue.mUint16;
|
||||
break;
|
||||
case nsIForeignLibrary::UINT32:
|
||||
case TYPE_uint32_t:
|
||||
// Do not implicitly lose bits.
|
||||
if (!jsvalToIntStrict(aValue, &aResult.mValue.mUint32))
|
||||
return TypeError(aContext, "uint32", aValue);
|
||||
|
||||
aResult.mData = &aResult.mValue.mUint32;
|
||||
break;
|
||||
case nsIForeignLibrary::UINT64:
|
||||
case TYPE_uint64_t:
|
||||
// Do not implicitly lose bits.
|
||||
if (!jsvalToIntStrict(aValue, &aResult.mValue.mUint64))
|
||||
return TypeError(aContext, "uint64", aValue);
|
||||
|
||||
aResult.mData = &aResult.mValue.mUint64;
|
||||
break;
|
||||
case nsIForeignLibrary::FLOAT:
|
||||
case TYPE_float:
|
||||
if (!jsvalToDoubleStrict(aValue, &d))
|
||||
return TypeError(aContext, "float", aValue);
|
||||
|
||||
@ -315,14 +308,14 @@ PrepareValue(JSContext* aContext, const Type& aType, jsval aValue, Value& aResul
|
||||
aResult.mValue.mFloat = float(d);
|
||||
aResult.mData = &aResult.mValue.mFloat;
|
||||
break;
|
||||
case nsIForeignLibrary::DOUBLE:
|
||||
case TYPE_double:
|
||||
if (!jsvalToDoubleStrict(aValue, &d))
|
||||
return TypeError(aContext, "double", aValue);
|
||||
|
||||
aResult.mValue.mDouble = d;
|
||||
aResult.mData = &aResult.mValue.mDouble;
|
||||
break;
|
||||
case nsIForeignLibrary::STRING:
|
||||
case TYPE_string:
|
||||
if (JSVAL_IS_NULL(aValue)) {
|
||||
// Allow passing a null pointer.
|
||||
aResult.mValue.mPointer = nsnull;
|
||||
@ -336,7 +329,7 @@ PrepareValue(JSContext* aContext, const Type& aType, jsval aValue, Value& aResul
|
||||
|
||||
aResult.mData = &aResult.mValue.mPointer;
|
||||
break;
|
||||
case nsIForeignLibrary::USTRING:
|
||||
case TYPE_ustring:
|
||||
if (JSVAL_IS_NULL(aValue)) {
|
||||
// Allow passing a null pointer.
|
||||
aResult.mValue.mPointer = nsnull;
|
||||
@ -362,42 +355,42 @@ static void
|
||||
PrepareReturnValue(const Type& aType, Value& aResult)
|
||||
{
|
||||
switch (aType.mType) {
|
||||
case nsIForeignLibrary::VOID:
|
||||
case TYPE_void_t:
|
||||
aResult.mData = nsnull;
|
||||
break;
|
||||
case nsIForeignLibrary::INT8:
|
||||
case TYPE_int8_t:
|
||||
aResult.mData = &aResult.mValue.mInt8;
|
||||
break;
|
||||
case nsIForeignLibrary::INT16:
|
||||
case TYPE_int16_t:
|
||||
aResult.mData = &aResult.mValue.mInt16;
|
||||
break;
|
||||
case nsIForeignLibrary::INT32:
|
||||
case TYPE_int32_t:
|
||||
aResult.mData = &aResult.mValue.mInt32;
|
||||
break;
|
||||
case nsIForeignLibrary::INT64:
|
||||
case TYPE_int64_t:
|
||||
aResult.mData = &aResult.mValue.mInt64;
|
||||
break;
|
||||
case nsIForeignLibrary::BOOL:
|
||||
case nsIForeignLibrary::UINT8:
|
||||
case TYPE_bool:
|
||||
case TYPE_uint8_t:
|
||||
aResult.mData = &aResult.mValue.mUint8;
|
||||
break;
|
||||
case nsIForeignLibrary::UINT16:
|
||||
case TYPE_uint16_t:
|
||||
aResult.mData = &aResult.mValue.mUint16;
|
||||
break;
|
||||
case nsIForeignLibrary::UINT32:
|
||||
case TYPE_uint32_t:
|
||||
aResult.mData = &aResult.mValue.mUint32;
|
||||
break;
|
||||
case nsIForeignLibrary::UINT64:
|
||||
case TYPE_uint64_t:
|
||||
aResult.mData = &aResult.mValue.mUint64;
|
||||
break;
|
||||
case nsIForeignLibrary::FLOAT:
|
||||
case TYPE_float:
|
||||
aResult.mData = &aResult.mValue.mFloat;
|
||||
break;
|
||||
case nsIForeignLibrary::DOUBLE:
|
||||
case TYPE_double:
|
||||
aResult.mData = &aResult.mValue.mDouble;
|
||||
break;
|
||||
case nsIForeignLibrary::STRING:
|
||||
case nsIForeignLibrary::USTRING:
|
||||
case TYPE_string:
|
||||
case TYPE_ustring:
|
||||
aResult.mData = &aResult.mValue.mPointer;
|
||||
break;
|
||||
default:
|
||||
@ -413,51 +406,51 @@ ConvertReturnValue(JSContext* aContext,
|
||||
jsval* aValue)
|
||||
{
|
||||
switch (aResultType.mType) {
|
||||
case nsIForeignLibrary::VOID:
|
||||
case TYPE_void_t:
|
||||
*aValue = JSVAL_VOID;
|
||||
break;
|
||||
case nsIForeignLibrary::BOOL:
|
||||
case TYPE_bool:
|
||||
*aValue = aResultValue.mValue.mUint8 ? JSVAL_TRUE : JSVAL_FALSE;
|
||||
break;
|
||||
case nsIForeignLibrary::INT8:
|
||||
case TYPE_int8_t:
|
||||
*aValue = INT_TO_JSVAL(aResultValue.mValue.mInt8);
|
||||
break;
|
||||
case nsIForeignLibrary::INT16:
|
||||
case TYPE_int16_t:
|
||||
*aValue = INT_TO_JSVAL(aResultValue.mValue.mInt16);
|
||||
break;
|
||||
case nsIForeignLibrary::INT32:
|
||||
case TYPE_int32_t:
|
||||
if (!JS_NewNumberValue(aContext, jsdouble(aResultValue.mValue.mInt32), aValue))
|
||||
return false;
|
||||
break;
|
||||
case nsIForeignLibrary::INT64:
|
||||
case TYPE_int64_t:
|
||||
// Implicit conversion with loss of bits. :-[
|
||||
if (!JS_NewNumberValue(aContext, jsdouble(aResultValue.mValue.mInt64), aValue))
|
||||
return false;
|
||||
break;
|
||||
case nsIForeignLibrary::UINT8:
|
||||
case TYPE_uint8_t:
|
||||
*aValue = INT_TO_JSVAL(aResultValue.mValue.mUint8);
|
||||
break;
|
||||
case nsIForeignLibrary::UINT16:
|
||||
case TYPE_uint16_t:
|
||||
*aValue = INT_TO_JSVAL(aResultValue.mValue.mUint16);
|
||||
break;
|
||||
case nsIForeignLibrary::UINT32:
|
||||
case TYPE_uint32_t:
|
||||
if (!JS_NewNumberValue(aContext, jsdouble(aResultValue.mValue.mUint32), aValue))
|
||||
return false;
|
||||
break;
|
||||
case nsIForeignLibrary::UINT64:
|
||||
case TYPE_uint64_t:
|
||||
// Implicit conversion with loss of bits. :-[
|
||||
if (!JS_NewNumberValue(aContext, jsdouble(aResultValue.mValue.mUint64), aValue))
|
||||
return false;
|
||||
break;
|
||||
case nsIForeignLibrary::FLOAT:
|
||||
case TYPE_float:
|
||||
if (!JS_NewNumberValue(aContext, jsdouble(aResultValue.mValue.mFloat), aValue))
|
||||
return false;
|
||||
break;
|
||||
case nsIForeignLibrary::DOUBLE:
|
||||
case TYPE_double:
|
||||
if (!JS_NewNumberValue(aContext, jsdouble(aResultValue.mValue.mDouble), aValue))
|
||||
return false;
|
||||
break;
|
||||
case nsIForeignLibrary::STRING: {
|
||||
case TYPE_string: {
|
||||
if (!aResultValue.mValue.mPointer) {
|
||||
// Allow returning a null pointer.
|
||||
*aValue = JSVAL_NULL;
|
||||
@ -471,7 +464,7 @@ ConvertReturnValue(JSContext* aContext,
|
||||
}
|
||||
break;
|
||||
}
|
||||
case nsIForeignLibrary::USTRING: {
|
||||
case TYPE_ustring: {
|
||||
if (!aResultValue.mValue.mPointer) {
|
||||
// Allow returning a null pointer.
|
||||
*aValue = JSVAL_NULL;
|
||||
@ -494,13 +487,11 @@ ConvertReturnValue(JSContext* aContext,
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** Function
|
||||
** Function implementation
|
||||
*******************************************************************************/
|
||||
|
||||
NS_IMPL_ISUPPORTS1(Function, nsIXPCScriptable)
|
||||
|
||||
Function::Function()
|
||||
: mFunc(nsnull)
|
||||
: mNext(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
@ -510,17 +501,16 @@ Function::~Function()
|
||||
|
||||
bool
|
||||
Function::Init(JSContext* aContext,
|
||||
Library* aLibrary,
|
||||
PRFuncPtr aFunc,
|
||||
PRUint16 aCallType,
|
||||
jsval aCallType,
|
||||
jsval aResultType,
|
||||
const nsTArray<jsval>& aArgTypes)
|
||||
jsval* aArgTypes,
|
||||
uintN aArgLength)
|
||||
{
|
||||
mLibrary = aLibrary;
|
||||
mFunc = aFunc;
|
||||
|
||||
// determine the ABI
|
||||
if (!GetABI(aCallType, mCallType)) {
|
||||
if (!GetABI(aContext, aCallType, mCallType)) {
|
||||
JS_ReportError(aContext, "Invalid ABI specification");
|
||||
return false;
|
||||
}
|
||||
@ -530,12 +520,13 @@ Function::Init(JSContext* aContext,
|
||||
return false;
|
||||
|
||||
// prepare the argument types
|
||||
for (PRUint32 i = 0; i < aArgTypes.Length(); ++i) {
|
||||
mArgTypes.SetCapacity(aArgLength);
|
||||
for (PRUint32 i = 0; i < aArgLength; ++i) {
|
||||
if (!PrepareType(aContext, aArgTypes[i], *mArgTypes.AppendElement()))
|
||||
return false;
|
||||
|
||||
// disallow void argument types
|
||||
if (mArgTypes[i].mType == nsIForeignLibrary::VOID) {
|
||||
if (mArgTypes[i].mType == TYPE_void_t) {
|
||||
JS_ReportError(aContext, "Cannot have void argument type");
|
||||
return false;
|
||||
}
|
||||
@ -562,12 +553,17 @@ Function::Init(JSContext* aContext,
|
||||
}
|
||||
|
||||
bool
|
||||
Function::Execute(JSContext* aContext, PRUint32 aArgc, jsval* aArgv, jsval* aValue)
|
||||
Function::Execute(JSContext* cx, PRUint32 argc, jsval* vp)
|
||||
{
|
||||
if (argc != mArgTypes.Length()) {
|
||||
JS_ReportError(cx, "Number of arguments does not match declaration");
|
||||
return false;
|
||||
}
|
||||
|
||||
// prepare the values for each argument
|
||||
nsAutoTArray<Value, 16> values;
|
||||
for (PRUint32 i = 0; i < mArgTypes.Length(); ++i) {
|
||||
if (!PrepareValue(aContext, mArgTypes[i], aArgv[i], *values.AppendElement()))
|
||||
if (!PrepareValue(cx, mArgTypes[i], JS_ARGV(cx, vp)[i], *values.AppendElement()))
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -583,50 +579,93 @@ Function::Execute(JSContext* aContext, PRUint32 aArgc, jsval* aArgv, jsval* aVal
|
||||
|
||||
// suspend the request before we call into the function, since the call
|
||||
// may block or otherwise take a long time to return.
|
||||
jsrefcount rc = JS_SuspendRequest(aContext);
|
||||
jsrefcount rc = JS_SuspendRequest(cx);
|
||||
|
||||
ffi_call(&mCIF, FFI_FN(mFunc), resultValue.mData, ffiValues.Elements());
|
||||
|
||||
JS_ResumeRequest(aContext, rc);
|
||||
JS_ResumeRequest(cx, rc);
|
||||
|
||||
// prepare a JS object from the result
|
||||
return ConvertReturnValue(aContext, mResultType, resultValue, aValue);
|
||||
jsval rval;
|
||||
if (!ConvertReturnValue(cx, mResultType, resultValue, &rval))
|
||||
return false;
|
||||
|
||||
JS_SET_RVAL(cx, vp, rval);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** nsIXPCScriptable implementation
|
||||
** JSObject implementation
|
||||
*******************************************************************************/
|
||||
|
||||
#define XPC_MAP_CLASSNAME Function
|
||||
#define XPC_MAP_QUOTED_CLASSNAME "Function"
|
||||
#define XPC_MAP_WANT_CALL
|
||||
#define XPC_MAP_FLAGS nsIXPCScriptable::WANT_CALL
|
||||
|
||||
#include "xpc_map_end.h"
|
||||
|
||||
NS_IMETHODIMP
|
||||
Function::Call(nsIXPConnectWrappedNative* wrapper,
|
||||
JSContext* cx,
|
||||
JSObject* obj,
|
||||
PRUint32 argc,
|
||||
jsval* argv,
|
||||
jsval* vp,
|
||||
PRBool* _retval)
|
||||
JSObject*
|
||||
Function::Create(JSContext* aContext,
|
||||
JSObject* aLibrary,
|
||||
PRFuncPtr aFunc,
|
||||
const char* aName,
|
||||
jsval aCallType,
|
||||
jsval aResultType,
|
||||
jsval* aArgTypes,
|
||||
uintN aArgLength)
|
||||
{
|
||||
if (!mLibrary->IsOpen()) {
|
||||
JS_ReportError(cx, "Library is not open");
|
||||
*_retval = PR_FALSE;
|
||||
return NS_OK;
|
||||
// create new Function instance
|
||||
nsAutoPtr<Function> self(new Function());
|
||||
if (!self)
|
||||
return NULL;
|
||||
|
||||
// deduce and check the ABI and parameter types
|
||||
if (!self->Init(aContext, aFunc, aCallType, aResultType, aArgTypes, aArgLength))
|
||||
return NULL;
|
||||
|
||||
// create and root the new JS function object
|
||||
JSFunction* fn = JS_NewFunction(aContext, JSNative(Function::Call),
|
||||
aArgLength, JSFUN_FAST_NATIVE, NULL, aName);
|
||||
if (!fn)
|
||||
return NULL;
|
||||
|
||||
JSObject* fnObj = JS_GetFunctionObject(fn);
|
||||
JSAutoTempValueRooter fnRoot(aContext, fnObj);
|
||||
|
||||
// stash a pointer to self, which Function::Call will need at call time
|
||||
if (!JS_SetReservedSlot(aContext, fnObj, 0, PRIVATE_TO_JSVAL(self.get())))
|
||||
return NULL;
|
||||
|
||||
// make a strong reference to the library for GC-safety
|
||||
if (!JS_SetReservedSlot(aContext, fnObj, 1, OBJECT_TO_JSVAL(aLibrary)))
|
||||
return NULL;
|
||||
|
||||
// tell the library we exist, so it can delete our Function instance
|
||||
// when it comes time to finalize. (JS functions don't have finalizers.)
|
||||
if (!Library::AddFunction(aContext, aLibrary, self))
|
||||
return NULL;
|
||||
|
||||
self.forget();
|
||||
return fnObj;
|
||||
}
|
||||
|
||||
static Function*
|
||||
GetFunction(JSContext* cx, JSObject* obj)
|
||||
{
|
||||
jsval slot;
|
||||
JS_GetReservedSlot(cx, obj, 0, &slot);
|
||||
return static_cast<Function*>(JSVAL_TO_PRIVATE(slot));
|
||||
}
|
||||
|
||||
JSBool
|
||||
Function::Call(JSContext* cx, uintN argc, jsval* vp)
|
||||
{
|
||||
JSObject* callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
|
||||
|
||||
jsval slot;
|
||||
JS_GetReservedSlot(cx, callee, 1, &slot);
|
||||
|
||||
PRLibrary* library = Library::GetLibrary(cx, JSVAL_TO_OBJECT(slot));
|
||||
if (!library) {
|
||||
JS_ReportError(cx, "library is not open");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
if (argc != mArgTypes.Length()) {
|
||||
JS_ReportError(cx, "Number of arguments does not match declaration");
|
||||
*_retval = PR_FALSE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
*_retval = Execute(cx, argc, argv, vp);
|
||||
return NS_OK;
|
||||
return GetFunction(cx, callee)->Execute(cx, argc, vp);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -40,12 +40,9 @@
|
||||
#ifndef FUNCTION_H
|
||||
#define FUNCTION_H
|
||||
|
||||
#include "Library.h"
|
||||
#include "nsIXPCScriptable.h"
|
||||
#include "Module.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "prlink.h"
|
||||
#include "jsapi.h"
|
||||
#include "ffi.h"
|
||||
|
||||
namespace mozilla {
|
||||
@ -66,7 +63,7 @@ GetErrorMessage(void* userRef, const char* locale, const uintN errorNumber);
|
||||
struct Type
|
||||
{
|
||||
ffi_type mFFIType;
|
||||
PRUint16 mType;
|
||||
TypeCode mType;
|
||||
};
|
||||
|
||||
struct Value
|
||||
@ -87,25 +84,23 @@ struct Value
|
||||
} mValue;
|
||||
};
|
||||
|
||||
class Function : public nsIXPCScriptable
|
||||
class Function
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIXPCSCRIPTABLE
|
||||
|
||||
Function();
|
||||
|
||||
bool Init(JSContext* aContext, Library* aLibrary, PRFuncPtr aFunc, PRUint16 aCallType, jsval aResultType, const nsTArray<jsval>& aArgTypes);
|
||||
Function*& Next() { return mNext; }
|
||||
|
||||
static JSObject* Create(JSContext* aContext, JSObject* aLibrary, PRFuncPtr aFunc, const char* aName, jsval aCallType, jsval aResultType, jsval* aArgTypes, uintN aArgLength);
|
||||
static JSBool Call(JSContext* cx, uintN argc, jsval* vp);
|
||||
|
||||
private:
|
||||
~Function();
|
||||
|
||||
bool Execute(JSContext* aContext, PRUint32 aArgc, jsval* aArgv, jsval* aValue);
|
||||
private:
|
||||
bool Init(JSContext* aContext, PRFuncPtr aFunc, jsval aCallType, jsval aResultType, jsval* aArgTypes, uintN aArgLength);
|
||||
bool Execute(JSContext* cx, PRUint32 argc, jsval* vp);
|
||||
|
||||
protected:
|
||||
// reference to the library our function is in
|
||||
nsRefPtr<Library> mLibrary;
|
||||
|
||||
PRFuncPtr mFunc;
|
||||
|
||||
ffi_abi mCallType;
|
||||
@ -114,6 +109,8 @@ protected:
|
||||
nsAutoTArray<ffi_type*, 16> mFFITypes;
|
||||
|
||||
ffi_cif mCIF;
|
||||
|
||||
Function* mNext;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -41,129 +41,215 @@
|
||||
#include "Library.h"
|
||||
#include "Function.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsString.h"
|
||||
#include "nsIXPConnect.h"
|
||||
#include "nsILocalFile.h"
|
||||
#include "prlink.h"
|
||||
#include "jsapi.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace ctypes {
|
||||
|
||||
static inline bool
|
||||
jsvalToUint16(JSContext* aContext, jsval aVal, PRUint16& aResult)
|
||||
/*******************************************************************************
|
||||
** JSObject implementation
|
||||
*******************************************************************************/
|
||||
|
||||
static JSClass sLibraryClass = {
|
||||
"Library",
|
||||
JSCLASS_HAS_RESERVED_SLOTS(2),
|
||||
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
|
||||
JS_EnumerateStub,JS_ResolveStub, JS_ConvertStub, Library::Finalize,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
static JSFunctionSpec sLibraryFunctions[] = {
|
||||
JS_FN("close", Library::Close, 0, JSFUN_FAST_NATIVE | JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT),
|
||||
JS_FN("declare", Library::Declare, 0, JSFUN_FAST_NATIVE | JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
JSObject*
|
||||
Library::Create(JSContext* cx, jsval aPath)
|
||||
{
|
||||
if (JSVAL_IS_INT(aVal)) {
|
||||
PRUint32 i = JSVAL_TO_INT(aVal);
|
||||
if (i <= PR_UINT16_MAX) {
|
||||
aResult = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
JSObject* libraryObj = JS_NewObject(cx, &sLibraryClass, NULL, NULL);
|
||||
if (!libraryObj)
|
||||
return NULL;
|
||||
|
||||
JS_ReportError(aContext, "Parameter must be a valid ABI constant");
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
jsvalToCString(JSContext* aContext, jsval aVal, const char*& aResult)
|
||||
{
|
||||
if (JSVAL_IS_STRING(aVal)) {
|
||||
aResult = JS_GetStringBytes(JSVAL_TO_STRING(aVal));
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_ReportError(aContext, "Parameter must be a string");
|
||||
return false;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(Library, nsIForeignLibrary)
|
||||
|
||||
Library::Library()
|
||||
: mLibrary(nsnull)
|
||||
{
|
||||
}
|
||||
|
||||
Library::~Library()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Library::Open(nsILocalFile* aFile)
|
||||
{
|
||||
NS_ENSURE_ARG(aFile);
|
||||
NS_ENSURE_TRUE(!mLibrary, NS_ERROR_ALREADY_INITIALIZED);
|
||||
|
||||
return aFile->Load(&mLibrary);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Library::Close()
|
||||
{
|
||||
if (mLibrary) {
|
||||
PR_UnloadLibrary(mLibrary);
|
||||
mLibrary = nsnull;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Library::Declare(nsISupports** aResult)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aResult);
|
||||
NS_ENSURE_TRUE(mLibrary, NS_ERROR_NOT_INITIALIZED);
|
||||
// attach API functions
|
||||
if (!JS_DefineFunctions(cx, libraryObj, sLibraryFunctions))
|
||||
return NULL;
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsILocalFile> localFile;
|
||||
|
||||
nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
|
||||
// get the path argument. we accept either an nsILocalFile or a string path.
|
||||
// determine which we have...
|
||||
if (JSVAL_IS_STRING(aPath)) {
|
||||
const jschar* path = JS_GetStringChars(JSVAL_TO_STRING(aPath));
|
||||
if (!path)
|
||||
return NULL;
|
||||
|
||||
nsAXPCNativeCallContext* ncc;
|
||||
rv = xpc->GetCurrentNativeCallContext(&ncc);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
|
||||
if (!localFile)
|
||||
return NULL;
|
||||
|
||||
JSContext *ctx;
|
||||
rv = ncc->GetJSContext(&ctx);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = localFile->InitWithPath(nsDependentString(path));
|
||||
if (NS_FAILED(rv))
|
||||
return NULL;
|
||||
|
||||
JSAutoRequest ar(ctx);
|
||||
} else if (JSVAL_IS_OBJECT(aPath)) {
|
||||
nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
|
||||
|
||||
PRUint32 argc;
|
||||
jsval *argv;
|
||||
ncc->GetArgc(&argc);
|
||||
ncc->GetArgvPtr(&argv);
|
||||
nsISupports* file = xpc->GetNativeOfWrapper(cx, JSVAL_TO_OBJECT(aPath));
|
||||
localFile = do_QueryInterface(file);
|
||||
if (!localFile)
|
||||
return NULL;
|
||||
|
||||
} else {
|
||||
// don't convert the argument
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PRLibrary* library;
|
||||
rv = localFile->Load(&library);
|
||||
if (NS_FAILED(rv))
|
||||
return NULL;
|
||||
|
||||
// stash the library
|
||||
if (!JS_SetReservedSlot(cx, libraryObj, 0, PRIVATE_TO_JSVAL(library)))
|
||||
return NULL;
|
||||
|
||||
// initialize our Function list to empty
|
||||
if (!JS_SetReservedSlot(cx, libraryObj, 1, PRIVATE_TO_JSVAL(NULL)))
|
||||
return NULL;
|
||||
|
||||
return libraryObj;
|
||||
}
|
||||
|
||||
PRLibrary*
|
||||
Library::GetLibrary(JSContext* cx, JSObject* obj)
|
||||
{
|
||||
JS_ASSERT(JS_GET_CLASS(cx, obj) == &sLibraryClass);
|
||||
|
||||
jsval slot;
|
||||
JS_GetReservedSlot(cx, obj, 0, &slot);
|
||||
return static_cast<PRLibrary*>(JSVAL_TO_PRIVATE(slot));
|
||||
}
|
||||
|
||||
static Function*
|
||||
GetFunctionList(JSContext* cx, JSObject* obj)
|
||||
{
|
||||
JS_ASSERT(JS_GET_CLASS(cx, obj) == &sLibraryClass);
|
||||
|
||||
jsval slot;
|
||||
JS_GetReservedSlot(cx, obj, 1, &slot);
|
||||
return static_cast<Function*>(JSVAL_TO_PRIVATE(slot));
|
||||
}
|
||||
|
||||
bool
|
||||
Library::AddFunction(JSContext* cx, JSObject* aLibrary, Function* aFunction)
|
||||
{
|
||||
// add the new Function instance to the head of the list
|
||||
aFunction->Next() = GetFunctionList(cx, aLibrary);
|
||||
return JS_SetReservedSlot(cx, aLibrary, 1, PRIVATE_TO_JSVAL(aFunction));
|
||||
}
|
||||
|
||||
void
|
||||
Library::Finalize(JSContext* cx, JSObject* obj)
|
||||
{
|
||||
// unload the library
|
||||
PRLibrary* library = GetLibrary(cx, obj);
|
||||
if (library)
|
||||
PR_UnloadLibrary(library);
|
||||
|
||||
// delete each Function instance
|
||||
Function* current = GetFunctionList(cx, obj);
|
||||
while (current) {
|
||||
Function* next = current->Next();
|
||||
delete current;
|
||||
current = next;
|
||||
}
|
||||
}
|
||||
|
||||
JSBool
|
||||
Library::Open(JSContext* cx, uintN argc, jsval *vp)
|
||||
{
|
||||
if (argc != 1) {
|
||||
JS_ReportError(cx, "open requires a single argument");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
JSObject* library = Create(cx, JS_ARGV(cx, vp)[0]);
|
||||
if (!library) {
|
||||
JS_ReportError(cx, "couldn't open library");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(library));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSBool
|
||||
Library::Close(JSContext* cx, uintN argc, jsval* vp)
|
||||
{
|
||||
JSObject* obj = JS_THIS_OBJECT(cx, vp);
|
||||
if (JS_GET_CLASS(cx, obj) != &sLibraryClass) {
|
||||
JS_ReportError(cx, "not a library");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
if (argc != 0) {
|
||||
JS_ReportError(cx, "close doesn't take any arguments");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
// delete our internal objects
|
||||
Finalize(cx, obj);
|
||||
JS_SetReservedSlot(cx, obj, 0, PRIVATE_TO_JSVAL(NULL));
|
||||
JS_SetReservedSlot(cx, obj, 1, PRIVATE_TO_JSVAL(NULL));
|
||||
|
||||
JS_SET_RVAL(cx, vp, JSVAL_VOID);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSBool
|
||||
Library::Declare(JSContext* cx, uintN argc, jsval* vp)
|
||||
{
|
||||
JSObject* obj = JS_THIS_OBJECT(cx, vp);
|
||||
if (JS_GET_CLASS(cx, obj) != &sLibraryClass) {
|
||||
JS_ReportError(cx, "not a library");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
PRLibrary* library = GetLibrary(cx, obj);
|
||||
if (!library) {
|
||||
JS_ReportError(cx, "library not open");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
// we always need at least a method name, a call type and a return type
|
||||
if (argc < 3) {
|
||||
JS_ReportError(ctx, "Insufficient number of arguments");
|
||||
return NS_OK;
|
||||
JS_ReportError(cx, "declare requires at least three arguments");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
const char* name;
|
||||
if (!jsvalToCString(ctx, argv[0], name))
|
||||
return NS_OK;
|
||||
|
||||
PRUint16 callType;
|
||||
if (!jsvalToUint16(ctx, argv[1], callType))
|
||||
return NS_OK;
|
||||
|
||||
nsAutoTArray<jsval, 16> argTypes;
|
||||
for (PRUint32 i = 3; i < argc; ++i) {
|
||||
argTypes.AppendElement(argv[i]);
|
||||
jsval* argv = JS_ARGV(cx, vp);
|
||||
if (!JSVAL_IS_STRING(argv[0])) {
|
||||
JS_ReportError(cx, "first argument must be a string");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
PRFuncPtr func = PR_FindFunctionSymbol(mLibrary, name);
|
||||
const char* name = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
|
||||
PRFuncPtr func = PR_FindFunctionSymbol(library, name);
|
||||
if (!func) {
|
||||
JS_ReportError(ctx, "Couldn't find function symbol in library");
|
||||
return NS_OK;
|
||||
JS_ReportError(cx, "couldn't find function symbol in library");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
nsRefPtr<Function> call = new Function;
|
||||
if (!call->Init(ctx, this, func, callType, argv[2], argTypes))
|
||||
return NS_OK;
|
||||
JSObject* fn = Function::Create(cx, obj, func, name, argv[1], argv[2], &argv[3], argc - 3);
|
||||
if (!fn)
|
||||
return JS_FALSE;
|
||||
|
||||
call.forget(aResult);
|
||||
return NS_OK;
|
||||
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(fn));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -40,33 +40,32 @@
|
||||
#ifndef LIBRARY_H
|
||||
#define LIBRARY_H
|
||||
|
||||
#include "nsIForeignLibrary.h"
|
||||
|
||||
#define FOREIGNLIBRARY_CONTRACTID \
|
||||
"@mozilla.org/jsctypes;1"
|
||||
|
||||
#define FOREIGNLIBRARY_CID \
|
||||
{ 0xc797702, 0x1c60, 0x4051, { 0x9d, 0xd7, 0x4d, 0x74, 0x5, 0x60, 0x56, 0x42 } }
|
||||
#include "Function.h"
|
||||
#include "jsapi.h"
|
||||
|
||||
struct PRLibrary;
|
||||
class Function;
|
||||
|
||||
namespace mozilla {
|
||||
namespace ctypes {
|
||||
|
||||
class Library : public nsIForeignLibrary
|
||||
class Library
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIFOREIGNLIBRARY
|
||||
static JSObject* Create(JSContext* cx, jsval aPath);
|
||||
static void Finalize(JSContext* cx, JSObject* obj);
|
||||
|
||||
Library();
|
||||
static PRLibrary* GetLibrary(JSContext* cx, JSObject* obj);
|
||||
static bool AddFunction(JSContext* cx, JSObject* aLibrary, Function* aFunction);
|
||||
|
||||
bool IsOpen() { return mLibrary != nsnull; }
|
||||
// JSFastNative functions
|
||||
static JSBool Open(JSContext* cx, uintN argc, jsval* vp);
|
||||
static JSBool Close(JSContext* cx, uintN argc, jsval* vp);
|
||||
static JSBool Declare(JSContext* cx, uintN argc, jsval* vp);
|
||||
|
||||
private:
|
||||
~Library();
|
||||
|
||||
PRLibrary* mLibrary;
|
||||
// nothing to instantiate here!
|
||||
Library();
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -44,19 +44,14 @@ VPATH = @srcdir@
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
MODULE = jsctypes
|
||||
XPIDL_MODULE = jsctypes
|
||||
MODULE_NAME = jsctypes
|
||||
GRE_MODULE = 1
|
||||
|
||||
# package the interface whether ctypes is enabled or not.
|
||||
# package the js module whether ctypes is enabled or not.
|
||||
EXTRA_JS_MODULES = \
|
||||
ctypes.jsm \
|
||||
$(NULL)
|
||||
|
||||
XPIDLSRCS = \
|
||||
nsIForeignLibrary.idl \
|
||||
$(NULL)
|
||||
|
||||
ifdef BUILD_CTYPES
|
||||
|
||||
LIBRARY_NAME = jsctypes
|
||||
|
@ -37,13 +37,149 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsIGenericFactory.h"
|
||||
#include "Module.h"
|
||||
#include "Library.h"
|
||||
#include "nsIGenericFactory.h"
|
||||
#include "nsMemory.h"
|
||||
|
||||
#define JSCTYPES_CONTRACTID \
|
||||
"@mozilla.org/jsctypes;1"
|
||||
|
||||
#define JSCTYPES_CID \
|
||||
{ 0xc797702, 0x1c60, 0x4051, { 0x9d, 0xd7, 0x4d, 0x74, 0x5, 0x60, 0x56, 0x42 } }
|
||||
|
||||
namespace mozilla {
|
||||
namespace ctypes {
|
||||
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(Library)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(Module)
|
||||
|
||||
NS_IMPL_ISUPPORTS1(Module, nsIXPCScriptable)
|
||||
|
||||
Module::Module()
|
||||
{
|
||||
}
|
||||
|
||||
Module::~Module()
|
||||
{
|
||||
}
|
||||
|
||||
#define XPC_MAP_CLASSNAME Module
|
||||
#define XPC_MAP_QUOTED_CLASSNAME "Module"
|
||||
#define XPC_MAP_WANT_CALL
|
||||
#define XPC_MAP_FLAGS nsIXPCScriptable::WANT_CALL
|
||||
#include "xpc_map_end.h"
|
||||
|
||||
NS_IMETHODIMP
|
||||
Module::Call(nsIXPConnectWrappedNative* wrapper,
|
||||
JSContext* cx,
|
||||
JSObject* obj,
|
||||
PRUint32 argc,
|
||||
jsval* argv,
|
||||
jsval* vp,
|
||||
PRBool* _retval)
|
||||
{
|
||||
JSObject* global = JS_GetGlobalObject(cx);
|
||||
*_retval = Init(cx, global);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static JSClass sCABIClass = {
|
||||
"CABI",
|
||||
JSCLASS_HAS_RESERVED_SLOTS(1),
|
||||
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
|
||||
JS_EnumerateStub,JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
static JSClass sCTypeClass = {
|
||||
"CType",
|
||||
JSCLASS_HAS_RESERVED_SLOTS(1),
|
||||
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
|
||||
JS_EnumerateStub,JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
static JSFunctionSpec sModuleFunctions[] = {
|
||||
JS_FN("open", Library::Open, 0, JSFUN_FAST_NATIVE | JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
ABICode
|
||||
Module::GetABICode(JSContext* cx, jsval val)
|
||||
{
|
||||
// make sure we have an object representing a CABI class,
|
||||
// and extract the enumerated class type from the reserved slot.
|
||||
if (!JSVAL_IS_OBJECT(val))
|
||||
return INVALID_ABI;
|
||||
|
||||
JSObject* obj = JSVAL_TO_OBJECT(val);
|
||||
if (JS_GET_CLASS(cx, obj) != &sCABIClass)
|
||||
return INVALID_ABI;
|
||||
|
||||
jsval result;
|
||||
JS_GetReservedSlot(cx, obj, 0, &result);
|
||||
|
||||
return ABICode(JSVAL_TO_INT(result));
|
||||
}
|
||||
|
||||
TypeCode
|
||||
Module::GetTypeCode(JSContext* cx, jsval val)
|
||||
{
|
||||
// make sure we have an object representing a CType class,
|
||||
// and extract the enumerated class type from the reserved slot.
|
||||
if (!JSVAL_IS_OBJECT(val))
|
||||
return INVALID_TYPE;
|
||||
|
||||
JSObject* obj = JSVAL_TO_OBJECT(val);
|
||||
if (JS_GET_CLASS(cx, obj) != &sCTypeClass)
|
||||
return INVALID_TYPE;
|
||||
|
||||
jsval result;
|
||||
JS_GetReservedSlot(cx, obj, 0, &result);
|
||||
|
||||
return TypeCode(JSVAL_TO_INT(result));
|
||||
}
|
||||
|
||||
static bool
|
||||
DefineConstant(JSContext* cx, JSObject* parent, JSClass* clasp, const char* name, jsint code)
|
||||
{
|
||||
JSObject* obj = JS_DefineObject(cx, parent, name, clasp, NULL,
|
||||
JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
return JS_SetReservedSlot(cx, obj, 0, INT_TO_JSVAL(code));
|
||||
}
|
||||
|
||||
bool
|
||||
Module::Init(JSContext* cx, JSObject* aGlobal)
|
||||
{
|
||||
// attach ctypes property to global object
|
||||
JSObject* ctypes = JS_NewObject(cx, NULL, NULL, NULL);
|
||||
if (!ctypes)
|
||||
return false;
|
||||
|
||||
if (!JS_DefineProperty(cx, aGlobal, "ctypes", OBJECT_TO_JSVAL(ctypes),
|
||||
NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
|
||||
return false;
|
||||
|
||||
// attach classes representing ABI and type constants
|
||||
#define DEFINE_ABI(name) \
|
||||
if (!DefineConstant(cx, ctypes, &sCABIClass, #name, ABI_##name)) \
|
||||
return false;
|
||||
#define DEFINE_TYPE(name) \
|
||||
if (!DefineConstant(cx, ctypes, &sCTypeClass, #name, TYPE_##name)) \
|
||||
return false;
|
||||
#include "types.h"
|
||||
#undef DEFINE_ABI
|
||||
#undef DEFINE_TYPE
|
||||
|
||||
// attach API functions
|
||||
if (!JS_DefineFunctions(cx, ctypes, sModuleFunctions))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -52,9 +188,9 @@ static nsModuleComponentInfo components[] =
|
||||
{
|
||||
{
|
||||
"jsctypes",
|
||||
FOREIGNLIBRARY_CID,
|
||||
FOREIGNLIBRARY_CONTRACTID,
|
||||
mozilla::ctypes::LibraryConstructor,
|
||||
JSCTYPES_CID,
|
||||
JSCTYPES_CONTRACTID,
|
||||
mozilla::ctypes::ModuleConstructor,
|
||||
}
|
||||
};
|
||||
|
||||
|
89
js/ctypes/Module.h
Normal file
89
js/ctypes/Module.h
Normal file
@ -0,0 +1,89 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is js-ctypes.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* The Mozilla Foundation <http://www.mozilla.org/>.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Dan Witte <dwitte@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef MODULE_H
|
||||
#define MODULE_H
|
||||
|
||||
#include "nsIXPCScriptable.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace ctypes {
|
||||
|
||||
// Each internal CABI and CType class (representing ABI and type constants,
|
||||
// respectively) has a unique identifier, stored in a reserved slot on the
|
||||
// JSObject.
|
||||
enum ABICode {
|
||||
#define DEFINE_ABI(name) ABI_##name,
|
||||
#define DEFINE_TYPE(name)
|
||||
#include "types.h"
|
||||
#undef DEFINE_ABI
|
||||
#undef DEFINE_TYPE
|
||||
INVALID_ABI
|
||||
};
|
||||
|
||||
enum TypeCode {
|
||||
#define DEFINE_ABI(name)
|
||||
#define DEFINE_TYPE(name) TYPE_##name,
|
||||
#include "types.h"
|
||||
#undef DEFINE_ABI
|
||||
#undef DEFINE_TYPE
|
||||
INVALID_TYPE
|
||||
};
|
||||
|
||||
class Module : public nsIXPCScriptable
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIXPCSCRIPTABLE
|
||||
|
||||
Module();
|
||||
|
||||
// Creates the ctypes object and attaches it to the global object.
|
||||
bool Init(JSContext* aContext, JSObject* aGlobal);
|
||||
|
||||
static ABICode GetABICode(JSContext* cx, jsval val);
|
||||
static TypeCode GetTypeCode(JSContext* cx, jsval val);
|
||||
|
||||
private:
|
||||
~Module();
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -38,26 +38,80 @@
|
||||
|
||||
let EXPORTED_SYMBOLS = [ "ctypes" ];
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
/**
|
||||
* This is the js module for ctypes. Import it like so:
|
||||
* Components.utils.import("resource://gre/modules/ctypes.jsm");
|
||||
*
|
||||
* This will create a 'ctypes' object, which provides an interface to describe
|
||||
* C types and call C functions from a dynamic library. It has the following
|
||||
* properties and functions:
|
||||
*
|
||||
* ABI constants that specify the calling convention to use.
|
||||
* ctypes.default_abi corresponds to the cdecl convention, and in almost all
|
||||
* cases is the correct choice. ctypes.stdcall is provided for calling
|
||||
* functions in the Microsoft Win32 API.
|
||||
*
|
||||
* ctypes.default_abi // corresponds to cdecl
|
||||
* ctypes.stdcall_abi // for calling Win32 API functions
|
||||
*
|
||||
* Types available for arguments and return values, representing
|
||||
* their C counterparts.
|
||||
*
|
||||
* ctypes.void_t // Only allowed for return types.
|
||||
* ctypes.bool // _Bool type (assumed 8 bits wide).
|
||||
* ctypes.int8_t // int8_t (signed char) type.
|
||||
* ctypes.int16_t // int16_t (short) type.
|
||||
* ctypes.int32_t // int32_t (int) type.
|
||||
* ctypes.int64_t // int64_t (long long) type.
|
||||
* ctypes.uint8_t // uint8_t (unsigned char) type.
|
||||
* ctypes.uint16_t // uint16_t (unsigned short) type.
|
||||
* ctypes.uint32_t // uint32_t (unsigned int) type.
|
||||
* ctypes.uint64_t // uint64_t (unsigned long long) type.
|
||||
* ctypes.float // float type.
|
||||
* ctypes.double // double type.
|
||||
* ctypes.string // C string (char *).
|
||||
* ctypes.ustring // 16-bit string (char16_t *).
|
||||
*
|
||||
* Library ctypes.open(name)
|
||||
*
|
||||
* Attempts to dynamically load the specified library. Returns a Library
|
||||
* object on success.
|
||||
* @name A string or nsILocalFile representing the name and path of
|
||||
* the library to open.
|
||||
* @returns A Library object.
|
||||
*
|
||||
* Library.close()
|
||||
*
|
||||
* Unloads the currently loaded library. Any subsequent attempts to call
|
||||
* functions on this interface will fail.
|
||||
*
|
||||
* function Library.declare(name, abi, returnType, argType1, argType2, ...)
|
||||
*
|
||||
* Declares a C function in a library.
|
||||
* @name Function name. This must be a valid symbol in the library.
|
||||
* @abi The calling convention to use. Must be an ABI constant
|
||||
* from ctypes.
|
||||
* @returnType The return type of the function. Must be a type constant
|
||||
* from ctypes.
|
||||
* @argTypes Argument types. Must be a type constant (other than void_t)
|
||||
* from ctypes.
|
||||
* @returns A function object.
|
||||
*
|
||||
* A function object can then be used to call the C function it represents
|
||||
* like so:
|
||||
*
|
||||
* const myFunction = myLibrary.declare("myFunction", ctypes.default_abi,
|
||||
* ctypes.double, ctypes.int32_t, ctypes.int32_t, ...);
|
||||
*
|
||||
* var result = myFunction(5, 10, ...);
|
||||
*
|
||||
* Arguments will be checked against the types supplied at declaration, and
|
||||
* some attempt to convert values (e.g. boolean true/false to integer 0/1)
|
||||
* will be made. Otherwise, if types do not match, or conversion fails,
|
||||
* an exception will be thrown.
|
||||
*/
|
||||
|
||||
let ctypes = {
|
||||
types: Ci.nsIForeignLibrary,
|
||||
|
||||
open: function(name) {
|
||||
let library = Cc["@mozilla.org/jsctypes;1"]
|
||||
.createInstance(Ci.nsIForeignLibrary);
|
||||
|
||||
let file;
|
||||
if (name instanceof Ci.nsILocalFile) {
|
||||
file = name;
|
||||
} else {
|
||||
file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
|
||||
file.initWithPath(name);
|
||||
}
|
||||
|
||||
library.open(file);
|
||||
return library;
|
||||
}
|
||||
};
|
||||
// Initialize the ctypes object. You do not need to do this yourself.
|
||||
const init = Components.classes["@mozilla.org/jsctypes;1"].createInstance();
|
||||
init();
|
||||
|
||||
|
@ -1,108 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is js-ctypes.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* The Mozilla Foundation <http://www.mozilla.org/>.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Mark Finkle <mark.finkle@gmail.com>, <mfinkle@mozilla.com>
|
||||
* Dan Witte <dwitte@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsILocalFile;
|
||||
|
||||
/**
|
||||
* Interface that wraps a dynamic library and can create
|
||||
* callable 'method' objects from exportable functions in the library
|
||||
*/
|
||||
[scriptable, uuid(352a72c1-5b99-451e-a330-c45079d8f087)]
|
||||
interface nsIForeignLibrary : nsISupports
|
||||
{
|
||||
/**
|
||||
* ABI constants that specify the calling convention to use.
|
||||
* DEFAULT corresponds to the cdecl convention, and in almost all
|
||||
* cases is the correct choice. STDCALL is provided for calling
|
||||
* functions in the Microsoft Win32 API.
|
||||
*/
|
||||
const PRUint16 DEFAULT = 0; // corresponds to cdecl
|
||||
const PRUint16 STDCALL = 1; // for calling Win32 API functions
|
||||
|
||||
/**
|
||||
* Types available for arguments and return values, representing
|
||||
* their C counterparts.
|
||||
*/
|
||||
const PRUint16 VOID = 0; // Only allowed for return types.
|
||||
const PRUint16 BOOL = 1; // _Bool type (assumed 8 bits wide).
|
||||
const PRUint16 INT8 = 2; // int8_t (signed char) type.
|
||||
const PRUint16 INT16 = 3; // int16_t (short) type.
|
||||
const PRUint16 INT32 = 4; // int32_t (int) type.
|
||||
const PRUint16 INT64 = 5; // int64_t (long long) type.
|
||||
const PRUint16 UINT8 = 6; // uint8_t (unsigned char) type.
|
||||
const PRUint16 UINT16 = 7; // uint16_t (unsigned short) type.
|
||||
const PRUint16 UINT32 = 8; // uint32_t (unsigned int) type.
|
||||
const PRUint16 UINT64 = 9; // uint64_t (unsigned long long) type.
|
||||
const PRUint16 FLOAT = 10; // float type.
|
||||
const PRUint16 DOUBLE = 11; // double type.
|
||||
const PRUint16 STRING = 12; // C string (char *).
|
||||
const PRUint16 USTRING = 13; // 16-bit string (char16_t *).
|
||||
|
||||
/**
|
||||
* Attempts to dynamically load the specified library
|
||||
*/
|
||||
void open(in nsILocalFile aFile);
|
||||
|
||||
/**
|
||||
* Unloads the currently loaded library. Any subsequent attempts to call
|
||||
* functions on this interface will fail.
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Used to specify an exported method from the currently loaded library. Even
|
||||
* though the method appears to take no parameters, it does in fact require
|
||||
* several. It uses some XPCOM/JSAPI magic to extract the parameters.
|
||||
*
|
||||
* declare(in AString aName, in int aABI, in int aReturnType, in int aArgType1, ...)
|
||||
*
|
||||
* The resulting object can then be used to call the underlying
|
||||
* C function like so:
|
||||
*
|
||||
* var retval = nativeMethod(arg1, arg2, ...);
|
||||
*
|
||||
* Arguments will be checked against the types supplied at declaration, and
|
||||
* some attempt to convert values (e.g. boolean true/false to integer 0/1)
|
||||
* will be made. Otherwise, if types do not match, or conversion fails,
|
||||
* an exception will be thrown.
|
||||
*/
|
||||
nsISupports declare();
|
||||
};
|
@ -43,8 +43,6 @@ Components.utils.import("resource://gre/modules/ctypes.jsm");
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
const Types = ctypes.types;
|
||||
|
||||
function do_check_throws(f, type, stack)
|
||||
{
|
||||
if (!stack)
|
||||
@ -90,18 +88,39 @@ function run_test()
|
||||
var libpath = libfile.path;
|
||||
library = ctypes.open(libpath);
|
||||
run_void_tests(library);
|
||||
|
||||
// test library.close
|
||||
var test_v = library.declare("test_v", ctypes.default_abi, ctypes.void_t);
|
||||
library.close();
|
||||
do_check_throws(function () { test_v(); }, Error);
|
||||
do_check_throws(function () { library.declare("test_v", ctypes.default_abi, ctypes.void_t); }, Error);
|
||||
|
||||
// test that library functions throw when bound to other objects
|
||||
library = ctypes.open(libpath);
|
||||
let obj = {};
|
||||
obj.declare = library.declare;
|
||||
do_check_throws(function () { obj.declare("test_v", ctypes.default_abi, ctypes.void_t); }, Error);
|
||||
obj.close = library.close;
|
||||
do_check_throws(function () { obj.close(); }, Error);
|
||||
|
||||
// test that functions work as properties of other objects
|
||||
var test_i8 = library.declare("test_i8", ctypes.default_abi, ctypes.int8_t);
|
||||
do_check_eq(test_i8(), 123);
|
||||
obj.t = test_i8;
|
||||
do_check_eq(test_i8(), 123);
|
||||
do_check_eq(obj.t(), 123);
|
||||
}
|
||||
|
||||
function run_void_tests(library) {
|
||||
var test_v = library.declare("test_v", Types.DEFAULT, Types.VOID);
|
||||
var test_v = library.declare("test_v", ctypes.default_abi, ctypes.void_t);
|
||||
do_check_eq(test_v(), undefined);
|
||||
}
|
||||
|
||||
function run_int8_tests(library) {
|
||||
var test_i8 = library.declare("test_i8", Types.DEFAULT, Types.INT8);
|
||||
var test_i8 = library.declare("test_i8", ctypes.default_abi, ctypes.int8_t);
|
||||
do_check_eq(test_i8(), 123);
|
||||
|
||||
var test_i8_i8 = library.declare("test_i8_i8", Types.DEFAULT, Types.INT8, Types.INT8);
|
||||
var test_i8_i8 = library.declare("test_i8_i8", ctypes.default_abi, ctypes.int8_t, ctypes.int8_t);
|
||||
do_check_eq(test_i8_i8(5), 5);
|
||||
do_check_eq(test_i8_i8(0), 0);
|
||||
do_check_eq(test_i8_i8(0x7f), 0x7f);
|
||||
@ -118,22 +137,22 @@ function run_int8_tests(library) {
|
||||
for (var i = 0; i < vals.length; i++)
|
||||
do_check_throws(function () { test_i8_i8(vals[i]); }, TypeError);
|
||||
|
||||
var test_i8_i8_sum = library.declare("test_i8_i8_sum", Types.DEFAULT, Types.INT8, Types.INT8, Types.INT8);
|
||||
var test_i8_i8_sum = library.declare("test_i8_i8_sum", ctypes.default_abi, ctypes.int8_t, ctypes.int8_t, ctypes.int8_t);
|
||||
do_check_eq(test_i8_i8_sum(5, 5), 10);
|
||||
|
||||
// test the range of unsigned. (we can reuse the signed C function
|
||||
// here, since it's binary-compatible.)
|
||||
var test_ui8_ui8 = library.declare("test_i8_i8", Types.DEFAULT, Types.UINT8, Types.UINT8);
|
||||
var test_ui8_ui8 = library.declare("test_i8_i8", ctypes.default_abi, ctypes.uint8_t, ctypes.uint8_t);
|
||||
do_check_eq(test_ui8_ui8(0xff), 0xff);
|
||||
do_check_throws(function () { test_ui8_ui8(0x100); }, TypeError);
|
||||
do_check_throws(function () { test_ui8_ui8(-1); }, TypeError);
|
||||
}
|
||||
|
||||
function run_int16_tests(library) {
|
||||
var test_i16 = library.declare("test_i16", Types.DEFAULT, Types.INT16);
|
||||
var test_i16 = library.declare("test_i16", ctypes.default_abi, ctypes.int16_t);
|
||||
do_check_eq(test_i16(), 12345);
|
||||
|
||||
var test_i16_i16 = library.declare("test_i16_i16", Types.DEFAULT, Types.INT16, Types.INT16);
|
||||
var test_i16_i16 = library.declare("test_i16_i16", ctypes.default_abi, ctypes.int16_t, ctypes.int16_t);
|
||||
do_check_eq(test_i16_i16(5), 5);
|
||||
do_check_eq(test_i16_i16(0), 0);
|
||||
do_check_eq(test_i16_i16(0x7fff), 0x7fff);
|
||||
@ -150,22 +169,22 @@ function run_int16_tests(library) {
|
||||
for (var i = 0; i < vals.length; i++)
|
||||
do_check_throws(function () { test_i16_i16(vals[i]); }, TypeError);
|
||||
|
||||
var test_i16_i16_sum = library.declare("test_i16_i16_sum", Types.DEFAULT, Types.INT16, Types.INT16, Types.INT16);
|
||||
var test_i16_i16_sum = library.declare("test_i16_i16_sum", ctypes.default_abi, ctypes.int16_t, ctypes.int16_t, ctypes.int16_t);
|
||||
do_check_eq(test_i16_i16_sum(5, 5), 10);
|
||||
|
||||
// test the range of unsigned. (we can reuse the signed C function
|
||||
// here, since it's binary-compatible.)
|
||||
var test_ui16_ui16 = library.declare("test_i16_i16", Types.DEFAULT, Types.UINT16, Types.UINT16);
|
||||
var test_ui16_ui16 = library.declare("test_i16_i16", ctypes.default_abi, ctypes.uint16_t, ctypes.uint16_t);
|
||||
do_check_eq(test_ui16_ui16(0xffff), 0xffff);
|
||||
do_check_throws(function () { test_ui16_ui16(0x10000); }, TypeError);
|
||||
do_check_throws(function () { test_ui16_ui16(-1); }, TypeError);
|
||||
}
|
||||
|
||||
function run_int32_tests(library) {
|
||||
var test_i32 = library.declare("test_i32", Types.DEFAULT, Types.INT32);
|
||||
var test_i32 = library.declare("test_i32", ctypes.default_abi, ctypes.int32_t);
|
||||
do_check_eq(test_i32(), 123456789);
|
||||
|
||||
var test_i32_i32 = library.declare("test_i32_i32", Types.DEFAULT, Types.INT32, Types.INT32);
|
||||
var test_i32_i32 = library.declare("test_i32_i32", ctypes.default_abi, ctypes.int32_t, ctypes.int32_t);
|
||||
do_check_eq(test_i32_i32(5), 5);
|
||||
do_check_eq(test_i32_i32(0), 0);
|
||||
do_check_eq(test_i32_i32(0x7fffffff), 0x7fffffff);
|
||||
@ -182,24 +201,24 @@ function run_int32_tests(library) {
|
||||
for (var i = 0; i < vals.length; i++)
|
||||
do_check_throws(function () { test_i32_i32(vals[i]); }, TypeError);
|
||||
|
||||
var test_i32_i32_sum = library.declare("test_i32_i32_sum", Types.DEFAULT, Types.INT32, Types.INT32, Types.INT32);
|
||||
var test_i32_i32_sum = library.declare("test_i32_i32_sum", ctypes.default_abi, ctypes.int32_t, ctypes.int32_t, ctypes.int32_t);
|
||||
do_check_eq(test_i32_i32_sum(5, 5), 10);
|
||||
|
||||
// test the range of unsigned. (we can reuse the signed C function
|
||||
// here, since it's binary-compatible.)
|
||||
var test_ui32_ui32 = library.declare("test_i32_i32", Types.DEFAULT, Types.UINT32, Types.UINT32);
|
||||
var test_ui32_ui32 = library.declare("test_i32_i32", ctypes.default_abi, ctypes.uint32_t, ctypes.uint32_t);
|
||||
do_check_eq(test_ui32_ui32(0xffffffff), 0xffffffff);
|
||||
do_check_throws(function () { test_ui32_ui32(0x100000000); }, TypeError);
|
||||
do_check_throws(function () { test_ui32_ui32(-1); }, TypeError);
|
||||
}
|
||||
|
||||
function run_int64_tests(library) {
|
||||
var test_i64 = library.declare("test_i64", Types.DEFAULT, Types.INT64);
|
||||
var test_i64 = library.declare("test_i64", ctypes.default_abi, ctypes.int64_t);
|
||||
// JS represents 64 bit ints as doubles, so we have to be careful how many
|
||||
// significant digits we use
|
||||
do_check_eq(test_i64(), 0x28590a1c921de000);
|
||||
|
||||
var test_i64_i64 = library.declare("test_i64_i64", Types.DEFAULT, Types.INT64, Types.INT64);
|
||||
var test_i64_i64 = library.declare("test_i64_i64", ctypes.default_abi, ctypes.int64_t, ctypes.int64_t);
|
||||
do_check_eq(test_i64_i64(5), 5);
|
||||
do_check_eq(test_i64_i64(0), 0);
|
||||
do_check_eq(test_i64_i64(0x7ffffffffffff000), 0x7ffffffffffff000);
|
||||
@ -216,22 +235,22 @@ function run_int64_tests(library) {
|
||||
for (var i = 0; i < vals.length; i++)
|
||||
do_check_throws(function () { test_i64_i64(vals[i]); }, TypeError);
|
||||
|
||||
var test_i64_i64_sum = library.declare("test_i64_i64_sum", Types.DEFAULT, Types.INT64, Types.INT64, Types.INT64);
|
||||
var test_i64_i64_sum = library.declare("test_i64_i64_sum", ctypes.default_abi, ctypes.int64_t, ctypes.int64_t, ctypes.int64_t);
|
||||
do_check_eq(test_i64_i64_sum(5, 5), 10);
|
||||
|
||||
// test the range of unsigned. (we can reuse the signed C function
|
||||
// here, since it's binary-compatible.)
|
||||
var test_ui64_ui64 = library.declare("test_i64_i64", Types.DEFAULT, Types.UINT64, Types.UINT64);
|
||||
var test_ui64_ui64 = library.declare("test_i64_i64", ctypes.default_abi, ctypes.uint64_t, ctypes.uint64_t);
|
||||
do_check_eq(test_ui64_ui64(0xfffffffffffff000), 0xfffffffffffff000);
|
||||
do_check_throws(function () { test_ui64_ui64(0x10000000000000000); }, TypeError);
|
||||
do_check_throws(function () { test_ui64_ui64(-1); }, TypeError);
|
||||
}
|
||||
|
||||
function run_float_tests(library) {
|
||||
var test_f = library.declare("test_f", Types.DEFAULT, Types.FLOAT);
|
||||
var test_f = library.declare("test_f", ctypes.default_abi, ctypes.float);
|
||||
do_check_eq(test_f(), 123456.5);
|
||||
|
||||
var test_f_f = library.declare("test_f_f", Types.DEFAULT, Types.FLOAT, Types.FLOAT);
|
||||
var test_f_f = library.declare("test_f_f", ctypes.default_abi, ctypes.float, ctypes.float);
|
||||
do_check_eq(test_f_f(5), 5);
|
||||
do_check_eq(test_f_f(5.25), 5.25);
|
||||
do_check_eq(test_f_f(Infinity), Infinity);
|
||||
@ -249,16 +268,16 @@ function run_float_tests(library) {
|
||||
for (var i = 0; i < vals.length; i++)
|
||||
do_check_throws(function () { test_f_f(vals[i]); }, TypeError);
|
||||
|
||||
var test_f_f_sum = library.declare("test_f_f_sum", Types.DEFAULT, Types.FLOAT, Types.FLOAT, Types.FLOAT);
|
||||
var test_f_f_sum = library.declare("test_f_f_sum", ctypes.default_abi, ctypes.float, ctypes.float, ctypes.float);
|
||||
do_check_eq(test_f_f_sum(5, 5), 10);
|
||||
do_check_eq(test_f_f_sum(5.5, 5.5), 11);
|
||||
}
|
||||
|
||||
function run_double_tests(library) {
|
||||
var test_d = library.declare("test_d", Types.DEFAULT, Types.DOUBLE);
|
||||
var test_d = library.declare("test_d", ctypes.default_abi, ctypes.double);
|
||||
do_check_eq(test_d(), 1234567890123456789.5);
|
||||
|
||||
var test_d_d = library.declare("test_d_d", Types.DEFAULT, Types.DOUBLE, Types.DOUBLE);
|
||||
var test_d_d = library.declare("test_d_d", ctypes.default_abi, ctypes.double, ctypes.double);
|
||||
do_check_eq(test_d_d(5), 5);
|
||||
do_check_eq(test_d_d(5.25), 5.25);
|
||||
do_check_eq(test_d_d(Infinity), Infinity);
|
||||
@ -273,13 +292,13 @@ function run_double_tests(library) {
|
||||
for (var i = 0; i < vals.length; i++)
|
||||
do_check_throws(function () { test_d_d(vals[i]); }, TypeError);
|
||||
|
||||
var test_d_d_sum = library.declare("test_d_d_sum", Types.DEFAULT, Types.DOUBLE, Types.DOUBLE, Types.DOUBLE);
|
||||
var test_d_d_sum = library.declare("test_d_d_sum", ctypes.default_abi, ctypes.double, ctypes.double, ctypes.double);
|
||||
do_check_eq(test_d_d_sum(5, 5), 10);
|
||||
do_check_eq(test_d_d_sum(5.5, 5.5), 11);
|
||||
}
|
||||
|
||||
function run_string_tests(library) {
|
||||
var test_ansi_len = library.declare("test_ansi_len", Types.DEFAULT, Types.INT32, Types.STRING);
|
||||
var test_ansi_len = library.declare("test_ansi_len", ctypes.default_abi, ctypes.int32_t, ctypes.string);
|
||||
do_check_eq(test_ansi_len(""), 0);
|
||||
do_check_eq(test_ansi_len("hello world"), 11);
|
||||
|
||||
@ -288,22 +307,22 @@ function run_string_tests(library) {
|
||||
for (var i = 0; i < vals.length; i++)
|
||||
do_check_throws(function() { test_ansi_len(vals[i]); }, TypeError);
|
||||
|
||||
var test_wide_len = library.declare("test_wide_len", Types.DEFAULT, Types.INT32, Types.USTRING);
|
||||
var test_wide_len = library.declare("test_wide_len", ctypes.default_abi, ctypes.int32_t, ctypes.ustring);
|
||||
do_check_eq(test_wide_len("hello world"), 11);
|
||||
|
||||
var test_ansi_ret = library.declare("test_ansi_ret", Types.DEFAULT, Types.STRING);
|
||||
var test_ansi_ret = library.declare("test_ansi_ret", ctypes.default_abi, ctypes.string);
|
||||
do_check_eq(test_ansi_ret(), "success");
|
||||
|
||||
var test_wide_ret = library.declare("test_wide_ret", Types.DEFAULT, Types.USTRING);
|
||||
var test_wide_ret = library.declare("test_wide_ret", ctypes.default_abi, ctypes.ustring);
|
||||
do_check_eq(test_wide_ret(), "success");
|
||||
|
||||
var test_ansi_echo = library.declare("test_ansi_echo", Types.DEFAULT, Types.STRING, Types.STRING);
|
||||
var test_ansi_echo = library.declare("test_ansi_echo", ctypes.default_abi, ctypes.string, ctypes.string);
|
||||
do_check_eq(test_ansi_echo("anybody in there?"), "anybody in there?");
|
||||
do_check_eq(test_ansi_echo(null), null);
|
||||
}
|
||||
|
||||
function run_mixed_tests(library) {
|
||||
var test_floor = library.declare("test_floor", Types.DEFAULT, Types.INT32, Types.INT32, Types.FLOAT);
|
||||
var test_floor = library.declare("test_floor", ctypes.default_abi, ctypes.int32_t, ctypes.int32_t, ctypes.float);
|
||||
do_check_eq(test_floor(5, 5.5), 10);
|
||||
do_check_throws(function() { test_floor(5.5, 5); }, TypeError);
|
||||
}
|
||||
|
72
js/ctypes/types.h
Normal file
72
js/ctypes/types.h
Normal file
@ -0,0 +1,72 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is js-ctypes.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* The Mozilla Foundation <http://www.mozilla.org/>.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Dan Witte <dwitte@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/**
|
||||
* This file defines the constants available on the ctypes.types object (e.g.
|
||||
* ctypes.types.VOID). They do not have any interesting properties; they simply
|
||||
* exist as unique identifiers for the type they represent.
|
||||
*/
|
||||
|
||||
/**
|
||||
* ABI constants that specify the calling convention to use.
|
||||
* DEFAULT corresponds to the cdecl convention, and in almost all
|
||||
* cases is the correct choice. STDCALL is provided for calling
|
||||
* functions in the Microsoft Win32 API.
|
||||
*/
|
||||
DEFINE_ABI(default_abi) // corresponds to cdecl
|
||||
DEFINE_ABI(stdcall_abi) // for calling Win32 API functions
|
||||
|
||||
/**
|
||||
* Types available for arguments and return values, representing
|
||||
* their C counterparts.
|
||||
*/
|
||||
DEFINE_TYPE(void_t) // Only allowed for return types.
|
||||
DEFINE_TYPE(bool) // _Bool type (assumed 8 bits wide).
|
||||
DEFINE_TYPE(int8_t) // int8_t (signed char) type.
|
||||
DEFINE_TYPE(int16_t) // int16_t (short) type.
|
||||
DEFINE_TYPE(int32_t) // int32_t (int) type.
|
||||
DEFINE_TYPE(int64_t) // int64_t (long long) type.
|
||||
DEFINE_TYPE(uint8_t) // uint8_t (unsigned char) type.
|
||||
DEFINE_TYPE(uint16_t) // uint16_t (unsigned short) type.
|
||||
DEFINE_TYPE(uint32_t) // uint32_t (unsigned int) type.
|
||||
DEFINE_TYPE(uint64_t) // uint64_t (unsigned long long) type.
|
||||
DEFINE_TYPE(float) // float type.
|
||||
DEFINE_TYPE(double) // double type.
|
||||
DEFINE_TYPE(string) // C string (char *).
|
||||
DEFINE_TYPE(ustring) // 16-bit string (char16_t *).
|
||||
|
Loading…
x
Reference in New Issue
Block a user