Implement jsctypes with raw JSAPI. b=518721, r=jorendorff, sr=bsmedberg

This commit is contained in:
Dan Witte 2009-10-06 12:16:38 -07:00
parent c4f71f1e57
commit a75255221b
12 changed files with 795 additions and 418 deletions

View File

@ -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

View File

@ -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);
}
}

View File

@ -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;
};
}

View File

@ -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;
}
}

View File

@ -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();
};
}

View File

@ -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

View File

@ -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
View 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

View File

@ -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();

View File

@ -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();
};

View File

@ -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
View 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 *).