Implement jsctypes with raw JSAPI. b=518721, r=jorendorff, sr=bsmedberg
authorDan Witte <dwitte@mozilla.com>
Tue, 06 Oct 2009 12:16:38 -0700
changeset 33512 a6e59476b690cae7b87f9dbadb756d107ecbef12
parent 33511 ecf9d61ca3de93433d10654911461b6dc1beac08
child 33513 54e0f634d9616c0b6b6be62aa776bd66da1b8eb5
push idunknown
push userunknown
push dateunknown
reviewersjorendorff, bsmedberg
bugs518721
milestone1.9.3a1pre
Implement jsctypes with raw JSAPI. b=518721, r=jorendorff, sr=bsmedberg
browser/installer/package-manifest.in
js/ctypes/Function.cpp
js/ctypes/Function.h
js/ctypes/Library.cpp
js/ctypes/Library.h
js/ctypes/Makefile.in
js/ctypes/Module.cpp
js/ctypes/Module.h
js/ctypes/ctypes.jsm
js/ctypes/nsIForeignLibrary.idl
js/ctypes/tests/unit/test_jsctypes.js.in
js/ctypes/types.h
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -168,17 +168,16 @@
 @BINPATH@/components/fuel.xpt
 @BINPATH@/components/gfx.xpt
 @BINPATH@/components/htmlparser.xpt
 @BINPATH@/components/imglib2.xpt
 @BINPATH@/components/imgicon.xpt
 @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
 @BINPATH@/components/layout_printing.xpt
 #endif
 @BINPATH@/components/layout_xul_tree.xpt
 @BINPATH@/components/layout_xul.xpt
 #ifdef XP_UNIX
--- a/js/ctypes/Function.cpp
+++ b/js/ctypes/Function.cpp
@@ -34,20 +34,19 @@
  * 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 "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 {
 
 /*******************************************************************************
 ** Static helpers
 *******************************************************************************/
 
@@ -149,199 +148,193 @@ TypeError(JSContext* cx, const char* exp
 {
   const char* src = ToSource(cx, actual);
   JS_ReportErrorNumber(cx, GetErrorMessage, NULL,
                        CTYPESMSG_TYPE_ERROR, expected, src);
   return false;
 }
 
 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
   default:
     return false;
   }
 }
 
 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:
     JS_ReportError(aContext, "Invalid type specification");
     return false;
   }
 
-  aResult.mType = type;
-
   return true;
 }
 
 static bool
 PrepareValue(JSContext* aContext, const Type& aType, jsval aValue, Value& aResult)
 {
   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) ||
         aResult.mValue.mUint8 > 1)
       return TypeError(aContext, "boolean", aValue);
 
     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);
 
     // The following cast silently throws away some bits, but there's
     // no good way around it. Sternly requiring that the 64-bit double
     // argument be exactly representable as a 32-bit float is
     // unrealistic: it would allow 1/2 to pass but not 1/3.
     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;
     } else if (JSVAL_IS_STRING(aValue)) {
       aResult.mValue.mPointer = JS_GetStringBytes(JSVAL_TO_STRING(aValue));
     } else {
       // Don't implicitly convert to string. Users can implicitly convert
       // with `String(x)` or `""+x`.
       return TypeError(aContext, "string", aValue);
     }
 
     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;
     } else if (JSVAL_IS_STRING(aValue)) {
       aResult.mValue.mPointer = JS_GetStringChars(JSVAL_TO_STRING(aValue));
     } else {
       // Don't implicitly convert to string. Users can implicitly convert
       // with `String(x)` or `""+x`.
@@ -357,126 +350,126 @@ PrepareValue(JSContext* aContext, const 
 
   return true;
 }
 
 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:
     NS_NOTREACHED("invalid type");
     break;
   }
 }
 
 static bool
 ConvertReturnValue(JSContext* aContext,
                    const Type& aResultType,
                    const Value& aResultValue,
                    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;
     } else {
       JSString *jsstring = JS_NewStringCopyZ(aContext,
                              reinterpret_cast<const char*>(aResultValue.mValue.mPointer));
       if (!jsstring)
         return false;
 
       *aValue = STRING_TO_JSVAL(jsstring);
     }
     break;
   }
-  case nsIForeignLibrary::USTRING: {
+  case TYPE_ustring: {
     if (!aResultValue.mValue.mPointer) {
       // Allow returning a null pointer.
       *aValue = JSVAL_NULL;
     } else {
       JSString *jsstring = JS_NewUCStringCopyZ(aContext,
                              reinterpret_cast<const jschar*>(aResultValue.mValue.mPointer));
       if (!jsstring)
         return false;
@@ -489,58 +482,56 @@ ConvertReturnValue(JSContext* aContext,
     NS_NOTREACHED("invalid type");
     return false;
   }
 
   return true;
 }
 
 /*******************************************************************************
-** Function
+** Function implementation
 *******************************************************************************/
 
-NS_IMPL_ISUPPORTS1(Function, nsIXPCScriptable)
-
 Function::Function()
-  : mFunc(nsnull)
+  : mNext(NULL)
 {
 }
 
 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;
   }
 
   // prepare the result type
   if (!PrepareType(aContext, aResultType, mResultType))
     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;
     }
 
     // ffi_prep_cif requires an array of ffi_types; prepare it separately.
     mFFITypes.AppendElement(&mArgTypes[i].mFFIType);
   }
 
@@ -557,78 +548,126 @@ Function::Init(JSContext* aContext,
     return false;
   default:
     JS_ReportError(aContext, "Unknown libffi error");
     return false;
   }
 }
 
 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;
   }
 
   // create an array of pointers to each value, for passing to ffi_call
   nsAutoTArray<void*, 16> ffiValues;
   for (PRUint32 i = 0; i < mArgTypes.Length(); ++i) {
     ffiValues.AppendElement(values[i].mData);
   }
 
   // initialize a pointer to an appropriate location, for storing the result
   Value resultValue;
   PrepareReturnValue(mResultType, resultValue);
 
   // 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
+JSObject*
+Function::Create(JSContext* aContext,
+                 JSObject* aLibrary,
+                 PRFuncPtr aFunc,
+                 const char* aName,
+                 jsval aCallType,
+                 jsval aResultType,
+                 jsval* aArgTypes,
+                 uintN aArgLength)
+{
+  // create new Function instance
+  nsAutoPtr<Function> self(new Function());
+  if (!self)
+    return NULL;
 
-#include "xpc_map_end.h"
+  // 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;
 
-NS_IMETHODIMP
-Function::Call(nsIXPConnectWrappedNative* wrapper,
-               JSContext* cx,
-               JSObject* obj, 
-               PRUint32 argc, 
-               jsval* argv, 
-               jsval* vp, 
-               PRBool* _retval)
+  // 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)
 {
-  if (!mLibrary->IsOpen()) {
-    JS_ReportError(cx, "Library is not open");
-    *_retval = PR_FALSE;
-    return NS_OK;
+  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);
 }
 
 }
 }
 
--- a/js/ctypes/Function.h
+++ b/js/ctypes/Function.h
@@ -35,22 +35,19 @@
  * 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 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 {
 namespace ctypes {
 
 // for JS error reporting
 enum ErrorNum {
 #define MSG_DEF(name, number, count, exception, format) \
@@ -61,17 +58,17 @@ enum ErrorNum {
 };
 
 const JSErrorFormatString*
 GetErrorMessage(void* userRef, const char* locale, const uintN errorNumber);
 
 struct Type
 {
   ffi_type mFFIType;
-  PRUint16 mType;
+  TypeCode mType;
 };
 
 struct Value
 {
   void* mData;
   union {
     PRInt8   mInt8;
     PRInt16  mInt16;
@@ -82,41 +79,41 @@ struct Value
     PRUint32 mUint32;
     PRUint64 mUint64;
     float    mFloat;
     double   mDouble;
     void*    mPointer;
   } 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; }
 
-private:
+  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);
+
   ~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;
   Type mResultType;
   nsAutoTArray<Type, 16> mArgTypes;
   nsAutoTArray<ffi_type*, 16> mFFITypes;
 
   ffi_cif mCIF;
+
+  Function* mNext;
 };
 
 }
 }
 
 #endif
--- a/js/ctypes/Library.cpp
+++ b/js/ctypes/Library.cpp
@@ -36,136 +36,222 @@
  * 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 "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;
+
+  // attach API functions
+  if (!JS_DefineFunctions(cx, libraryObj, sLibraryFunctions))
+    return NULL;
+
+  nsresult rv;
+  nsCOMPtr<nsILocalFile> localFile;
+
+  // 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;
+
+    localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
+    if (!localFile)
+      return NULL;
+
+    rv = localFile->InitWithPath(nsDependentString(path));
+    if (NS_FAILED(rv))
+      return NULL;
+
+  } else if (JSVAL_IS_OBJECT(aPath)) {
+    nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
+
+    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;
   }
 
-  JS_ReportError(aContext, "Parameter must be a valid ABI constant");
-  return false;
+  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;
 }
 
-static inline bool
-jsvalToCString(JSContext* aContext, jsval aVal, const char*& aResult)
+PRLibrary*
+Library::GetLibrary(JSContext* cx, JSObject* obj)
 {
-  if (JSVAL_IS_STRING(aVal)) {
-    aResult = JS_GetStringBytes(JSVAL_TO_STRING(aVal));
-    return true;
-  }
+  JS_ASSERT(JS_GET_CLASS(cx, obj) == &sLibraryClass);
 
-  JS_ReportError(aContext, "Parameter must be a string");
-  return false;
-}
-
-NS_IMPL_ISUPPORTS1(Library, nsIForeignLibrary)
-
-Library::Library()
-  : mLibrary(nsnull)
-{
-}
-
-Library::~Library()
-{
-  Close();
+  jsval slot;
+  JS_GetReservedSlot(cx, obj, 0, &slot);
+  return static_cast<PRLibrary*>(JSVAL_TO_PRIVATE(slot));
 }
 
-NS_IMETHODIMP
-Library::Open(nsILocalFile* aFile)
+static Function*
+GetFunctionList(JSContext* cx, JSObject* obj)
 {
-  NS_ENSURE_ARG(aFile);
-  NS_ENSURE_TRUE(!mLibrary, NS_ERROR_ALREADY_INITIALIZED);
+  JS_ASSERT(JS_GET_CLASS(cx, obj) == &sLibraryClass);
 
-  return aFile->Load(&mLibrary);
+  jsval slot;
+  JS_GetReservedSlot(cx, obj, 1, &slot);
+  return static_cast<Function*>(JSVAL_TO_PRIVATE(slot));
 }
 
-NS_IMETHODIMP
-Library::Close()
+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)
 {
-  if (mLibrary) {
-    PR_UnloadLibrary(mLibrary);
-    mLibrary = nsnull;
+  // 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;
   }
-
-  return NS_OK;
 }
 
-NS_IMETHODIMP
-Library::Declare(nsISupports** aResult)
+JSBool
+Library::Open(JSContext* cx, uintN argc, jsval *vp)
 {
-  NS_ENSURE_ARG_POINTER(aResult);
-  NS_ENSURE_TRUE(mLibrary, NS_ERROR_NOT_INITIALIZED);
+  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;
+  }
 
-  nsresult rv;
+  JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(library));
+  return JS_TRUE;
+}
 
-  nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
+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;
+  }
 
-  nsAXPCNativeCallContext* ncc;
-  rv = xpc->GetCurrentNativeCallContext(&ncc);
-  NS_ENSURE_SUCCESS(rv, rv);
+  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;
+}
 
-  JSContext *ctx;
-  rv = ncc->GetJSContext(&ctx);
-  NS_ENSURE_SUCCESS(rv, rv);
+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;
+  }
 
-  JSAutoRequest ar(ctx);
-
-  PRUint32 argc;
-  jsval *argv;
-  ncc->GetArgc(&argc);
-  ncc->GetArgvPtr(&argv);
+  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;
+  }
+
+  jsval* argv = JS_ARGV(cx, vp);
+  if (!JSVAL_IS_STRING(argv[0])) {
+    JS_ReportError(cx, "first argument must be a string");
+    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]);
+  const char* name = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
+  PRFuncPtr func = PR_FindFunctionSymbol(library, name);
+  if (!func) {
+    JS_ReportError(cx, "couldn't find function symbol in library");
+    return JS_FALSE;
   }
 
-  PRFuncPtr func = PR_FindFunctionSymbol(mLibrary, name);
-  if (!func) {
-    JS_ReportError(ctx, "Couldn't find function symbol in library");
-    return NS_OK;
-  }
+  JSObject* fn = Function::Create(cx, obj, func, name, argv[1], argv[2], &argv[3], argc - 3);
+  if (!fn)
+    return JS_FALSE;
 
-  nsRefPtr<Function> call = new Function;
-  if (!call->Init(ctx, this, func, callType, argv[2], argTypes))
-    return NS_OK;
-
-  call.forget(aResult);
-  return NS_OK;
+  JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(fn));
+  return JS_TRUE;
 }
 
 }
 }
 
--- a/js/ctypes/Library.h
+++ b/js/ctypes/Library.h
@@ -35,41 +35,40 @@
  * 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 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();
 };
 
 }
 }
 
 #endif
--- a/js/ctypes/Makefile.in
+++ b/js/ctypes/Makefile.in
@@ -39,29 +39,24 @@
 DEPTH = ../..
 topsrcdir = @top_srcdir@
 srcdir = @srcdir@
 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
 LIBXUL_LIBRARY = 1
 EXPORT_LIBRARY = 1
 IS_COMPONENT = 1
 
 CPPSRCS = \
--- a/js/ctypes/Module.cpp
+++ b/js/ctypes/Module.cpp
@@ -32,31 +32,167 @@
  * 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 "Module.h"
+#include "Library.h"
 #include "nsIGenericFactory.h"
-#include "Library.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;
+}
 
 }
 }
 
 static nsModuleComponentInfo components[] =
 {
   {
     "jsctypes",
-    FOREIGNLIBRARY_CID,
-    FOREIGNLIBRARY_CONTRACTID,
-    mozilla::ctypes::LibraryConstructor,
+    JSCTYPES_CID,
+    JSCTYPES_CONTRACTID,
+    mozilla::ctypes::ModuleConstructor,
   }
 };
 
 NS_IMPL_NSGETMODULE(jsctypes, components)
 
new file mode 100644
--- /dev/null
+++ b/js/ctypes/Module.h
@@ -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
--- a/js/ctypes/ctypes.jsm
+++ b/js/ctypes/ctypes.jsm
@@ -33,31 +33,85 @@
  * 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 ***** */
 
 let EXPORTED_SYMBOLS = [ "ctypes" ];
 
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-
-let ctypes = {
-  types: Ci.nsIForeignLibrary,
-
-  open: function(name) {
-    let library = Cc["@mozilla.org/jsctypes;1"]
-                  .createInstance(Ci.nsIForeignLibrary);
+/**
+ * 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 file;
-    if (name instanceof Ci.nsILocalFile) {
-      file = name;
-    } else {
-      file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
-      file.initWithPath(name);
-    }
+// Initialize the ctypes object. You do not need to do this yourself.
+const init = Components.classes["@mozilla.org/jsctypes;1"].createInstance();
+init();
 
-    library.open(file);
-    return library;
-  }
-};
-
deleted file mode 100644
--- a/js/ctypes/nsIForeignLibrary.idl
+++ /dev/null
@@ -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();
-};
--- a/js/ctypes/tests/unit/test_jsctypes.js.in
+++ b/js/ctypes/tests/unit/test_jsctypes.js.in
@@ -38,18 +38,16 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 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)
     stack = Components.stack.caller;
 
   try {
     f();
   } catch (exc) {
@@ -85,158 +83,179 @@ function run_test()
   run_double_tests(library);
   run_string_tests(library);
   run_mixed_tests(library);
 
   // test the string version of ctypes.open() as well
   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);
   do_check_eq(test_i8_i8(-0x80), -0x80);
   do_check_eq(1/test_i8_i8(-0), 1/0);  // that is, test_i8_i8(-0) is +0
   do_check_eq(test_i8_i8(true), 1);
   do_check_eq(test_i8_i8(false), 0);
 
   // don't convert anything else to an int8
   var vals = [0x80, -0x81, 0x100000000, Infinity, -Infinity, NaN,
               null, undefined, "", "0", {}, [], new Number(16),
               {toString: function () { return 7; }},
               {valueOf: function () { return 7; }}];
   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);
   do_check_eq(test_i16_i16(-0x8000), -0x8000);
   do_check_eq(1/test_i16_i16(-0), 1/0);  // that is, test_i16_i16(-0) is +0
   do_check_eq(test_i16_i16(true), 1);
   do_check_eq(test_i16_i16(false), 0);
 
   // don't convert anything else to an int16
   var vals = [0x8000, -0x8001, 0x100000000, Infinity, -Infinity, NaN,
               null, undefined, "", "0", {}, [], new Number(16),
               {toString: function () { return 7; }},
               {valueOf: function () { return 7; }}];
   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);
   do_check_eq(test_i32_i32(-0x80000000), -0x80000000);
   do_check_eq(1/test_i32_i32(-0), 1/0);  // that is, test_i32_i32(-0) is +0
   do_check_eq(test_i32_i32(true), 1);
   do_check_eq(test_i32_i32(false), 0);
 
   // don't convert anything else to an int32
   var vals = [0x80000000, -0x80000001, Infinity, -Infinity, NaN,
               null, undefined, "", "0", {}, [], new Number(16),
               {toString: function () { return 7; }},
               {valueOf: function () { return 7; }}];
   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);
   do_check_eq(test_i64_i64(-0x8000000000000000), -0x8000000000000000);
   do_check_eq(1/test_i64_i64(-0), 1/0);  // that is, test_i64_i64(-0) is +0
   do_check_eq(test_i64_i64(true), 1);
   do_check_eq(test_i64_i64(false), 0);
 
   // don't convert anything else to an int64
   var vals = [0x8000000000000000, -0x8000000000001000, Infinity, -Infinity, NaN,
               null, undefined, "", "0", {}, [], new Number(16),
               {toString: function () { return 7; }},
               {valueOf: function () { return 7; }}];
   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);
   do_check_eq(test_f_f(-Infinity), -Infinity);
   do_check_eq(isNaN(test_f_f(NaN)), true);
   do_check_eq(1/test_f_f(-0), 1/-0); // that is, test_f_f(-0) is -0
 
   // allow values that can't be represented precisely as a float
@@ -244,67 +263,67 @@ function run_float_tests(library) {
 
   // don't convert anything else to a float
   var vals = [true, false, null, undefined, "", "0", {}, [], new Number(16),
               {toString: function () { return 7; }},
               {valueOf: function () { return 7; }}];
   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);
   do_check_eq(test_d_d(-Infinity), -Infinity);
   do_check_eq(isNaN(test_d_d(NaN)), true);
   do_check_eq(1/test_d_d(-0), 1/-0); // that is, test_d_d(-0) is -0
 
   // don't convert anything else to a double
   var vals = [true, false, null, undefined, "", "0", {}, [], new Number(16),
               {toString: function () { return 7; }},
               {valueOf: function () { return 7; }}];
   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);
 
   // don't convert anything else to a string
   var vals = [true, 0, 1/3, undefined, {}, {toString: function () { return "bad"; }}, []];
   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);
 }
 
new file mode 100644
--- /dev/null
+++ b/js/ctypes/types.h
@@ -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 *).
+