diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -176,16 +176,17 @@
 @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
diff --git a/client.py b/client.py
--- a/client.py
+++ b/client.py
@@ -1,15 +1,19 @@
 #!/usr/bin/python
 
-NSPR_DIRS = ('nsprpub',)
-NSS_DIRS  = ('dbm',
-             'security/nss',
-             'security/coreconf',
-             'security/dbm')
+NSPR_DIRS = (('nsprpub', 'mozilla/nsprpub'),)
+NSS_DIRS  = (('dbm', 'mozilla/dbm'),
+             ('security/nss', 'mozilla/security/nss'),
+             ('security/coreconf', 'mozilla/security/coreconf'),
+             ('security/dbm', 'mozilla/security/dbm'))
+LIBFFI_DIRS = (('js/ctypes/libffi', 'libffi'),)
+
+CVSROOT_MOZILLA = ':pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot'
+CVSROOT_LIBFFI = ':pserver:anoncvs@sources.redhat.com:/cvs/libffi'
 
 import os
 import sys
 import datetime
 import shutil
 from optparse import OptionParser
 from build.util import check_call
 
@@ -32,54 +36,64 @@ def do_hg_pull(dir, repository, hg):
         if repository is not None:
             cmd.append(repository)
         check_call_noisy(cmd)
     check_call([hg, 'parent', '-R', fulldir,
                 '--template=Updated to revision {node}.\n'])
 
 def do_cvs_export(modules, tag, cvsroot, cvs):
     """Check out a CVS directory without CVS metadata, using "export"
-    modules is a list of directories to check out, e.g. ['nsprpub']
+    modules is a list of directories to check out and the corresponding
+    cvs module, e.g. (('nsprpub', 'mozilla/nsprpub'))
     """
-    for module in modules:
+    for module_tuple in modules:
+        module = module_tuple[0]
+        cvs_module = module_tuple[1]
         fullpath = os.path.join(topsrcdir, module)
         if os.path.exists(fullpath):
             print "Removing '%s'" % fullpath
             shutil.rmtree(fullpath)
 
         (parent, leaf) = os.path.split(module)
         print "CVS export begin: " + datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC")
         check_call_noisy([cvs, '-d', cvsroot,
-                          'export', '-r', tag, '-d', leaf,
-                          'mozilla/%s' % module],
+                          'export', '-r', tag, '-d', leaf, cvs_module],
                          cwd=os.path.join(topsrcdir, parent))
         print "CVS export end: " + datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC")
 
-o = OptionParser(usage="client.py [options] update_nspr tagname | update_nss tagname")
+o = OptionParser(usage="client.py [options] update_nspr tagname | update_nss tagname | update_libffi tagname")
 o.add_option("--skip-mozilla", dest="skip_mozilla",
              action="store_true", default=False,
              help="Obsolete")
 
 o.add_option("--cvs", dest="cvs", default=os.environ.get('CVS', 'cvs'),
              help="The location of the cvs binary")
 o.add_option("--cvsroot", dest="cvsroot",
-             default=os.environ.get('CVSROOT', ':pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot'),
-             help="The CVSROOT (default: :pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot")
+             help="The CVSROOT (default for mozilla checkouts: %s)" % CVSROOT_MOZILLA)
 
 try:
     options, args = o.parse_args()
     action = args[0]
 except IndexError:
     o.print_help()
     sys.exit(2)
 
 if action in ('checkout', 'co'):
     print >>sys.stderr, "Warning: client.py checkout is obsolete."
     pass
 elif action in ('update_nspr'):
     tag, = args[1:]
+    if not options.cvsroot:
+        options.cvsroot = os.environ.get('CVSROOT', CVSROOT_MOZILLA)
     do_cvs_export(NSPR_DIRS, tag, options.cvsroot, options.cvs)
 elif action in ('update_nss'):
     tag, = args[1:]
+    if not options.cvsroot:
+        options.cvsroot = os.environ.get('CVSROOT', CVSROOT_MOZILLA)
     do_cvs_export(NSS_DIRS, tag, options.cvsroot, options.cvs)
+elif action in ('update_libffi'):
+    tag, = args[1:]
+    if not options.cvsroot:
+        options.cvsroot = CVSROOT_LIBFFI
+    do_cvs_export(LIBFFI_DIRS, tag, options.cvsroot, options.cvs)
 else:
     o.print_help()
     sys.exit(2)
diff --git a/configure.in b/configure.in
--- a/configure.in
+++ b/configure.in
@@ -8232,16 +8232,24 @@ if test "$MOZ_MEMORY"; then
    ac_configure_args="$ac_configure_args --enable-jemalloc"
    if test -n "$MOZ_MEMORY_LDFLAGS"; then
      export MOZ_MEMORY_LDFLAGS
    fi
 fi
 AC_OUTPUT_SUBDIRS(js/src)
 ac_configure_args="$_SUBDIR_CONFIG_ARGS"
 
+# Run the libffi 'configure' script.
+ac_configure_args="--disable-shared --enable-static --with-pic --disable-raw-api"
+if test "$MOZ_DEBUG"; then
+  ac_configure_args="$ac_configure_args --enable-debug"
+fi
+AC_OUTPUT_SUBDIRS(js/ctypes/libffi)
+ac_configure_args="$_SUBDIR_CONFIG_ARGS"
+
 fi # COMPILE_ENVIRONMENT && !LIBXUL_SDK_DIR
 
 dnl Prevent the regeneration of autoconf.mk forcing rebuilds of the world
 dnl Needs to be at the end to respect possible changes from NSPR configure
 if cmp -s config/autoconf.mk config/autoconf.mk.orig; then
   echo "config/autoconf.mk is unchanged"
   mv -f config/autoconf.mk.orig config/autoconf.mk 2> /dev/null
 else
diff --git a/js/ctypes/Makefile.in b/js/ctypes/Makefile.in
new file mode 100644
--- /dev/null
+++ b/js/ctypes/Makefile.in
@@ -0,0 +1,86 @@
+# ***** 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 *****
+
+DEPTH = ../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = jsctypes
+XPIDL_MODULE = jsctypes
+MODULE_NAME = jsctypes
+LIBRARY_NAME = jsctypes
+GRE_MODULE = 1
+LIBXUL_LIBRARY = 1
+EXPORT_LIBRARY = 1
+IS_COMPONENT = 1
+
+XPIDLSRCS	= \
+		nsINativeTypes.idl \
+		$(NULL)
+
+LOCAL_INCLUDES = \
+    -Ilibffi/include \
+    $(NULL)
+
+CPPSRCS = \
+    nsNativeMethod.cpp \
+    nsNativeTypes.cpp \
+    nsNativeModule.cpp \
+    $(NULL)
+
+EXTRA_JS_MODULES = \
+    ctypes.jsm \
+    $(NULL)
+
+SHARED_LIBRARY_LIBS = \
+    libffi/.libs/$(LIB_PREFIX)ffi.$(LIB_SUFFIX) \
+    $(NULL)
+
+EXTRA_DSO_LDOPTS += \
+    $(MOZ_COMPONENT_LIBS) \
+    $(MOZ_JS_LIBS) \
+    $(NULL)
+
+ifdef ENABLE_TESTS
+DIRS += tests
+endif
+
+include $(topsrcdir)/config/rules.mk
diff --git a/js/ctypes/ctypes.jsm b/js/ctypes/ctypes.jsm
new file mode 100644
--- /dev/null
+++ b/js/ctypes/ctypes.jsm
@@ -0,0 +1,63 @@
+/* -*-  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 ***** */
+
+let EXPORTED_SYMBOLS = [ "ctypes" ];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+let ctypes = {
+  types: Ci.nsINativeTypes,
+
+  open: function(name) {
+    let library = Cc["@mozilla.org/jsctypes;1"]
+                  .createInstance(Ci.nsINativeTypes);
+
+    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;
+  }
+};
+
diff --git a/js/ctypes/nsINativeTypes.idl b/js/ctypes/nsINativeTypes.idl
new file mode 100644
--- /dev/null
+++ b/js/ctypes/nsINativeTypes.idl
@@ -0,0 +1,108 @@
+/* -*-  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 nsINativeTypes : 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
+   * nsINativeMethod functions 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();
+};
diff --git a/js/ctypes/nsNativeMethod.cpp b/js/ctypes/nsNativeMethod.cpp
new file mode 100644
--- /dev/null
+++ b/js/ctypes/nsNativeMethod.cpp
@@ -0,0 +1,586 @@
+/* -*-  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 "nsNativeMethod.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIXPConnect.h"
+#include "nsCRT.h"
+
+/*******************************************************************************
+** Static helpers
+*******************************************************************************/
+
+template<class IntegerType>
+static bool
+jsvalToIntStrict(jsval aValue, IntegerType *aResult)
+{
+  if (JSVAL_IS_INT(aValue)) {
+    jsint i = JSVAL_TO_INT(aValue);
+    *aResult = i;
+
+    // Make sure the integer fits in the alotted precision.
+    return jsint(*aResult) == i;
+  }
+  if (JSVAL_IS_DOUBLE(aValue)) {
+    jsdouble d = *JSVAL_TO_DOUBLE(aValue);
+    *aResult = d;
+
+    // Don't silently lose bits here -- check that aValue really is an
+    // integer value.
+    return jsdouble(*aResult) == d;
+  }
+  if (JSVAL_IS_BOOLEAN(aValue)) {
+    // Implicitly promote boolean values to 0 or 1, like C.
+    *aResult = JSVAL_TO_BOOLEAN(aValue);
+    NS_ASSERTION(*aResult == 0 || *aResult == 1, "invalid boolean");
+    return true;
+  }
+  // Don't silently convert null to an integer. It's probably a mistake.
+  return false;
+}
+
+static bool
+jsvalToDoubleStrict(jsval aValue, jsdouble *dp)
+{
+  // Don't silently convert true to 1.0 or false to 0.0, even though C/C++
+  // does it. It's likely to be a mistake.
+  if (JSVAL_IS_INT(aValue)) {
+    *dp = JSVAL_TO_INT(aValue);
+    return true;
+  }
+  if (JSVAL_IS_DOUBLE(aValue)) {
+    *dp = *JSVAL_TO_DOUBLE(aValue);
+    return true;
+  }
+  return false;
+}
+
+static nsresult
+TypeError(JSContext *cx, const char *message)
+{
+  JS_ReportError(cx, message);
+  return NS_ERROR_FAILURE;
+}
+
+static nsresult
+GetABI(PRUint16 aCallType, ffi_abi& aResult)
+{
+  // determine the ABI from the subset of those available on the
+  // given platform. nsINativeTypes::DEFAULT specifies the default
+  // C calling convention (cdecl) on each platform.
+  switch (aCallType) {
+  case nsINativeTypes::DEFAULT:
+    aResult = FFI_DEFAULT_ABI;
+    return NS_OK;
+#if defined(XP_WIN32)
+  case nsINativeTypes::STDCALL:
+    aResult = FFI_STDCALL;
+    return NS_OK;
+#endif
+  default:
+    return NS_ERROR_INVALID_ARG;
+  }
+}
+
+static nsresult
+PrepareType(JSContext* aContext, jsval aType, nsNativeType& aResult)
+{
+  // for now, the only types we accept are integer values.
+  if (!JSVAL_IS_INT(aType)) {
+    JS_ReportError(aContext, "Invalid type specification");
+    return NS_ERROR_FAILURE;
+  }
+
+  PRInt32 type = JSVAL_TO_INT(aType);
+
+  switch (type) {
+  case nsINativeTypes::VOID:
+    aResult.mType = ffi_type_void;
+    break;
+  case nsINativeTypes::INT8:
+    aResult.mType = ffi_type_sint8;
+    break;
+  case nsINativeTypes::INT16:
+    aResult.mType = ffi_type_sint16;
+    break;
+  case nsINativeTypes::INT32:
+    aResult.mType = ffi_type_sint32;
+    break;
+  case nsINativeTypes::INT64:
+    aResult.mType = ffi_type_sint64;
+    break;
+  case nsINativeTypes::BOOL:
+  case nsINativeTypes::UINT8:
+    aResult.mType = ffi_type_uint8;
+    break;
+  case nsINativeTypes::UINT16:
+    aResult.mType = ffi_type_uint16;
+    break;
+  case nsINativeTypes::UINT32:
+    aResult.mType = ffi_type_uint32;
+    break;
+  case nsINativeTypes::UINT64:
+    aResult.mType = ffi_type_uint64;
+    break;
+  case nsINativeTypes::FLOAT:
+    aResult.mType = ffi_type_float;
+    break;
+  case nsINativeTypes::DOUBLE:
+    aResult.mType = ffi_type_double;
+    break;
+  case nsINativeTypes::STRING:
+  case nsINativeTypes::USTRING:
+    aResult.mType = ffi_type_pointer;
+    break;
+  default:
+    JS_ReportError(aContext, "Invalid type specification");
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  aResult.mNativeType = type;
+
+  return NS_OK;
+}
+
+static nsresult
+PrepareValue(JSContext* aContext, const nsNativeType& aType, jsval aValue, nsNativeValue& aResult)
+{
+  jsdouble d;
+
+  switch (aType.mNativeType) {
+  case nsINativeTypes::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, "Expected boolean value");
+
+    aResult.mData = &aResult.mValue.mUint8;
+    break;
+  case nsINativeTypes::INT8:
+    // Do not implicitly lose bits.
+    if (!jsvalToIntStrict(aValue, &aResult.mValue.mInt8))
+      return TypeError(aContext, "Expected int8 value");
+
+    aResult.mData = &aResult.mValue.mInt8;
+    break;
+  case nsINativeTypes::INT16:
+    // Do not implicitly lose bits.
+    if (!jsvalToIntStrict(aValue, &aResult.mValue.mInt16))
+      return TypeError(aContext, "Expected int16 value");
+
+    aResult.mData = &aResult.mValue.mInt16;
+    break;
+  case nsINativeTypes::INT32:
+    // Do not implicitly lose bits.
+    if (!jsvalToIntStrict(aValue, &aResult.mValue.mInt32))
+      return TypeError(aContext, "Expected int32 value");
+
+    aResult.mData = &aResult.mValue.mInt32;
+  case nsINativeTypes::INT64:
+    // Do not implicitly lose bits.
+    if (!jsvalToIntStrict(aValue, &aResult.mValue.mInt64))
+      return TypeError(aContext, "Expected int64 value");
+
+    aResult.mData = &aResult.mValue.mInt64;
+    break;
+  case nsINativeTypes::UINT8:
+    // Do not implicitly lose bits.
+    if (!jsvalToIntStrict(aValue, &aResult.mValue.mUint8))
+      return TypeError(aContext, "Expected uint8 value");
+
+    aResult.mData = &aResult.mValue.mUint8;
+    break;
+  case nsINativeTypes::UINT16:
+    // Do not implicitly lose bits.
+    if (!jsvalToIntStrict(aValue, &aResult.mValue.mUint16))
+      return TypeError(aContext, "Expected uint16 value");
+
+    aResult.mData = &aResult.mValue.mUint16;
+    break;
+  case nsINativeTypes::UINT32:
+    // Do not implicitly lose bits.
+    if (!jsvalToIntStrict(aValue, &aResult.mValue.mUint32))
+      return TypeError(aContext, "Expected uint32 value");
+
+    aResult.mData = &aResult.mValue.mUint32;
+  case nsINativeTypes::UINT64:
+    // Do not implicitly lose bits.
+    if (!jsvalToIntStrict(aValue, &aResult.mValue.mUint64))
+      return TypeError(aContext, "Expected uint64 value");
+
+    aResult.mData = &aResult.mValue.mUint64;
+    break;
+  case nsINativeTypes::FLOAT:
+    if (!jsvalToDoubleStrict(aValue, &d))
+      return TypeError(aContext, "Expected number");
+
+    // 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 = d;
+    aResult.mData = &aResult.mValue.mFloat;
+    break;
+  case nsINativeTypes::DOUBLE:
+    if (!jsvalToDoubleStrict(aValue, &d))
+      return TypeError(aContext, "Expected number");
+
+    aResult.mValue.mDouble = d;
+    aResult.mData = &aResult.mValue.mDouble;
+    break;
+  case nsINativeTypes::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, "Expected string");
+    }
+
+    aResult.mData = &aResult.mValue.mPointer;
+    break;
+  case nsINativeTypes::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`.
+      return TypeError(aContext, "Expected string");
+    }
+
+    aResult.mData = &aResult.mValue.mPointer;
+    break;
+  default:
+    NS_NOTREACHED("invalid type");
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+static void
+PrepareReturnValue(const nsNativeType& aType, nsNativeValue& aResult)
+{
+  switch (aType.mNativeType) {
+  case nsINativeTypes::VOID:
+    aResult.mData = nsnull;
+    break;
+  case nsINativeTypes::INT8:
+    aResult.mData = &aResult.mValue.mInt8;
+    break;
+  case nsINativeTypes::INT16:
+    aResult.mData = &aResult.mValue.mInt16;
+    break;
+  case nsINativeTypes::INT32:
+    aResult.mData = &aResult.mValue.mInt32;
+    break;
+  case nsINativeTypes::INT64:
+    aResult.mData = &aResult.mValue.mInt64;
+    break;
+  case nsINativeTypes::BOOL:
+  case nsINativeTypes::UINT8:
+    aResult.mData = &aResult.mValue.mUint8;
+    break;
+  case nsINativeTypes::UINT16:
+    aResult.mData = &aResult.mValue.mUint16;
+    break;
+  case nsINativeTypes::UINT32:
+    aResult.mData = &aResult.mValue.mUint32;
+    break;
+  case nsINativeTypes::UINT64:
+    aResult.mData = &aResult.mValue.mUint64;
+    break;
+  case nsINativeTypes::FLOAT:
+    aResult.mData = &aResult.mValue.mFloat;
+    break;
+  case nsINativeTypes::DOUBLE:
+    aResult.mData = &aResult.mValue.mDouble;
+    break;
+  case nsINativeTypes::STRING:
+  case nsINativeTypes::USTRING:
+    aResult.mData = &aResult.mValue.mPointer;
+    break;
+  default:
+    NS_NOTREACHED("invalid type");
+    break;
+  }
+}
+
+static nsresult
+ConvertReturnValue(JSContext* aContext,
+                   const nsNativeType& aResultType,
+                   const nsNativeValue& aResultValue,
+                   jsval* aValue)
+{
+  switch (aResultType.mNativeType) {
+  case nsINativeTypes::VOID:
+    *aValue = JSVAL_VOID;
+    break;
+  case nsINativeTypes::BOOL:
+    *aValue = aResultValue.mValue.mUint8 ? JSVAL_TRUE : JSVAL_FALSE;
+    break;
+  case nsINativeTypes::INT8:
+    *aValue = INT_TO_JSVAL(aResultValue.mValue.mInt8);
+    break;
+  case nsINativeTypes::INT16:
+    *aValue = INT_TO_JSVAL(aResultValue.mValue.mInt16);
+    break;
+  case nsINativeTypes::INT32:
+    if (!JS_NewNumberValue(aContext, aResultValue.mValue.mInt32, aValue))
+      return NS_ERROR_OUT_OF_MEMORY;
+    break;
+  case nsINativeTypes::INT64:
+    // Implicit conversion with loss of bits.  :-[
+    if (!JS_NewNumberValue(aContext, aResultValue.mValue.mInt64, aValue))
+      return NS_ERROR_OUT_OF_MEMORY;
+    break;
+  case nsINativeTypes::UINT8:
+    *aValue = INT_TO_JSVAL(aResultValue.mValue.mUint8);
+    break;
+  case nsINativeTypes::UINT16:
+    *aValue = INT_TO_JSVAL(aResultValue.mValue.mUint16);
+    break;
+  case nsINativeTypes::UINT32:
+    if (!JS_NewNumberValue(aContext, aResultValue.mValue.mUint32, aValue))
+      return NS_ERROR_OUT_OF_MEMORY;
+    break;
+  case nsINativeTypes::UINT64:
+    // Implicit conversion with loss of bits.  :-[
+    if (!JS_NewNumberValue(aContext, aResultValue.mValue.mUint64, aValue))
+      return NS_ERROR_OUT_OF_MEMORY;
+    break;
+  case nsINativeTypes::FLOAT:
+    if (!JS_NewNumberValue(aContext, aResultValue.mValue.mFloat, aValue))
+      return NS_ERROR_OUT_OF_MEMORY;
+    break;
+  case nsINativeTypes::DOUBLE:
+    if (!JS_NewNumberValue(aContext, aResultValue.mValue.mDouble, aValue))
+      return NS_ERROR_OUT_OF_MEMORY;
+    break;
+  case nsINativeTypes::STRING: {
+    if (!aResultValue.mValue.mPointer) {
+      // Allow returning a null pointer.
+      *aValue = JSVAL_VOID;
+    } else {
+      JSString *jsstring = JS_NewStringCopyZ(aContext,
+                             reinterpret_cast<const char*>(aResultValue.mValue.mPointer));
+      if (!jsstring)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+      *aValue = STRING_TO_JSVAL(jsstring);
+    }
+    break;
+  }
+  case nsINativeTypes::USTRING: {
+    if (!aResultValue.mValue.mPointer) {
+      // Allow returning a null pointer.
+      *aValue = JSVAL_VOID;
+    } else {
+      JSString *jsstring = JS_NewUCStringCopyZ(aContext,
+                             reinterpret_cast<const jschar*>(aResultValue.mValue.mPointer));
+      if (!jsstring)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+      *aValue = STRING_TO_JSVAL(jsstring);
+    }
+    break;
+  }
+  default:
+    NS_NOTREACHED("invalid type");
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+/*******************************************************************************
+** nsNativeMethod
+*******************************************************************************/
+
+NS_IMPL_ISUPPORTS1(nsNativeMethod, nsIXPCScriptable)
+
+nsNativeMethod::nsNativeMethod()
+  : mFunc(nsnull)
+{
+}
+
+nsNativeMethod::~nsNativeMethod()
+{
+}
+
+nsresult
+nsNativeMethod::Init(JSContext* aContext,
+                     nsNativeTypes* aLibrary,
+                     PRFuncPtr aFunc,
+                     PRUint16 aCallType,
+                     jsval aResultType,
+                     const nsTArray<jsval>& aArgTypes)
+{
+  nsresult rv;
+
+  mLibrary = aLibrary;
+  mFunc = aFunc;
+
+  // determine the ABI
+  rv = GetABI(aCallType, mCallType);
+  if (NS_FAILED(rv)) {
+    JS_ReportError(aContext, "Invalid ABI specification");
+    return rv;
+  }
+
+  // prepare the result type
+  rv = PrepareType(aContext, aResultType, mResultType);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // prepare the argument types
+  for (PRUint32 i = 0; i < aArgTypes.Length(); ++i) {
+    rv = PrepareType(aContext, aArgTypes[i], *mArgTypes.AppendElement());
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // disallow void argument types
+    if (mArgTypes[i].mNativeType == nsINativeTypes::VOID)
+      return TypeError(aContext, "Cannot have void argument type");
+
+    // ffi_prep_cif requires an array of ffi_types; prepare it separately.
+    mFFITypes.AppendElement(&mArgTypes[i].mType);
+  }
+
+  ffi_status status = ffi_prep_cif(&mCIF, mCallType, mFFITypes.Length(),
+                                   &mResultType.mType, mFFITypes.Elements());
+  switch (status) {
+  case FFI_OK:
+    return NS_OK;
+  case FFI_BAD_ABI:
+    JS_ReportError(aContext, "Invalid ABI specification");
+    return NS_ERROR_INVALID_ARG;
+  case FFI_BAD_TYPEDEF:
+    JS_ReportError(aContext, "Invalid type specification");
+    return NS_ERROR_INVALID_ARG;
+  default:
+    JS_ReportError(aContext, "Unknown libffi error");
+    return NS_ERROR_FAILURE;
+  }
+}
+
+PRBool
+nsNativeMethod::Execute(JSContext* aContext, PRUint32 aArgc, jsval* aArgv, jsval* aValue)
+{
+  nsresult rv;
+
+  // prepare the values for each argument
+  nsAutoTArray<nsNativeValue, 16> nativeValues;
+  for (PRUint32 i = 0; i < mArgTypes.Length(); ++i) {
+    rv = PrepareValue(aContext, mArgTypes[i], aArgv[i], *nativeValues.AppendElement());
+    if (NS_FAILED(rv)) return PR_FALSE;
+  }
+
+  // create an array of pointers to each value, for passing to ffi_call
+  nsAutoTArray<void*, 16> values;
+  for (PRUint32 i = 0; i < mArgTypes.Length(); ++i) {
+    values.AppendElement(nativeValues[i].mData);
+  }
+
+  // initialize a pointer to an appropriate location, for storing the result
+  nsNativeValue 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);
+
+  ffi_call(&mCIF, mFunc, resultValue.mData, values.Elements());
+
+  JS_ResumeRequest(aContext, rc);
+
+  // prepare a JS object from the result
+  rv = ConvertReturnValue(aContext, mResultType, resultValue, aValue);
+  if (NS_FAILED(rv)) return PR_FALSE;
+
+  return PR_TRUE;
+}
+
+/*******************************************************************************
+** nsIXPCScriptable implementation
+*******************************************************************************/
+
+#define XPC_MAP_CLASSNAME nsNativeMethod
+#define XPC_MAP_QUOTED_CLASSNAME "ctypes"
+#define XPC_MAP_WANT_CALL
+#define XPC_MAP_FLAGS nsIXPCScriptable::WANT_CALL
+
+#include "xpc_map_end.h"
+
+NS_IMETHODIMP
+nsNativeMethod::Call(nsIXPConnectWrappedNative* wrapper,
+                     JSContext* cx,
+                     JSObject* obj, 
+                     PRUint32 argc, 
+                     jsval* argv, 
+                     jsval* vp, 
+                     PRBool* _retval)
+{
+  JSAutoRequest ar(cx);
+
+  if (!mLibrary->IsOpen()) {
+    JS_ReportError(cx, "Library is not open");
+    *_retval = PR_FALSE;
+    return NS_ERROR_FAILURE;
+  }
+
+  if (argc != mArgTypes.Length()) {
+    JS_ReportError(cx, "Number of arguments does not match declaration");
+    *_retval = PR_FALSE;
+    return NS_ERROR_FAILURE;
+  }
+
+  *_retval = Execute(cx, argc, argv, vp);
+  if (!*_retval)
+    return NS_ERROR_FAILURE;
+
+  return NS_OK;
+}
+
diff --git a/js/ctypes/nsNativeMethod.h b/js/ctypes/nsNativeMethod.h
new file mode 100644
--- /dev/null
+++ b/js/ctypes/nsNativeMethod.h
@@ -0,0 +1,105 @@
+/* -*-  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 ***** */
+
+#ifndef NSNATIVEMETHOD_H
+#define NSNATIVEMETHOD_H
+
+#include "nsNativeTypes.h"
+#include "nsIXPCScriptable.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsAutoPtr.h"
+#include "prlink.h"
+#include "jsapi.h"
+#include "ffi.h"
+
+struct nsNativeType
+{
+  ffi_type mType;
+  PRUint16 mNativeType;
+};
+
+struct nsNativeValue
+{
+  void* mData;
+  union {
+    PRInt8   mInt8;
+    PRInt16  mInt16;
+    PRInt32  mInt32;
+    PRInt64  mInt64;
+    PRUint8  mUint8;
+    PRUint16 mUint16;
+    PRUint32 mUint32;
+    PRUint64 mUint64;
+    float    mFloat;
+    double   mDouble;
+    void*    mPointer;
+  } mValue;
+};
+
+class nsNativeMethod : public nsIXPCScriptable
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIXPCSCRIPTABLE
+
+  nsNativeMethod();
+
+  nsresult Init(JSContext* aContext, nsNativeTypes* aLibrary, PRFuncPtr aFunc, PRUint16 aCallType, jsval aResultType, const nsTArray<jsval>& aArgTypes);
+
+private:
+  ~nsNativeMethod();
+
+  PRBool Execute(JSContext* aContext, PRUint32 aArgc, jsval* aArgv, jsval* aValue);
+
+protected:
+  // reference to the library our function is in
+  nsRefPtr<nsNativeTypes> mLibrary;
+
+  PRFuncPtr mFunc;
+
+  ffi_abi mCallType;
+  nsNativeType mResultType;
+  nsAutoTArray<nsNativeType, 16> mArgTypes;
+  nsAutoTArray<ffi_type*, 16> mFFITypes;
+
+  ffi_cif mCIF;
+};
+
+#endif
diff --git a/js/ctypes/nsNativeModule.cpp b/js/ctypes/nsNativeModule.cpp
new file mode 100644
--- /dev/null
+++ b/js/ctypes/nsNativeModule.cpp
@@ -0,0 +1,55 @@
+/* -*-  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 "nsIGenericFactory.h"
+#include "nsNativeTypes.h"
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsNativeTypes)
+
+static nsModuleComponentInfo components[] =
+{
+  {
+    "jsctypes",
+    NATIVETYPES_CID,
+    NATIVETYPES_CONTRACTID,
+    nsNativeTypesConstructor,
+  }
+};
+
+NS_IMPL_NSGETMODULE("jsctypes", components)
diff --git a/js/ctypes/nsNativeTypes.cpp b/js/ctypes/nsNativeTypes.cpp
new file mode 100644
--- /dev/null
+++ b/js/ctypes/nsNativeTypes.cpp
@@ -0,0 +1,164 @@
+/* -*-  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 "nsNativeTypes.h"
+#include "nsNativeMethod.h"
+#include "nsServiceManagerUtils.h"
+#include "nsAutoPtr.h"
+#include "nsILocalFile.h"
+#include "prlink.h"
+#include "jsapi.h"
+
+static inline nsresult
+jsvalToUint16(JSContext* aContext, jsval aVal, PRUint16& aResult)
+{
+  if (JSVAL_IS_INT(aVal)) {
+    PRUint32 i = JSVAL_TO_INT(aVal);
+    if (i <= PR_UINT16_MAX) {
+      aResult = i;
+      return NS_OK;
+    }
+  }
+
+  JS_ReportError(aContext, "Parameter must be an integer");
+  return NS_ERROR_INVALID_ARG;
+}
+
+static inline nsresult
+jsvalToCString(JSContext* aContext, jsval aVal, const char*& aResult)
+{
+  if (JSVAL_IS_STRING(aVal)) {
+    aResult = JS_GetStringBytes(JSVAL_TO_STRING(aVal));
+    return NS_OK;
+  }
+
+  JS_ReportError(aContext, "Parameter must be a string");
+  return NS_ERROR_INVALID_ARG;
+}
+
+NS_IMPL_ISUPPORTS1(nsNativeTypes, nsINativeTypes)
+
+nsNativeTypes::nsNativeTypes()
+  : mLibrary(nsnull)
+{
+}
+
+nsNativeTypes::~nsNativeTypes()
+{
+  Close();
+}
+
+NS_IMETHODIMP
+nsNativeTypes::Open(nsILocalFile* aFile)
+{
+  NS_ENSURE_ARG(aFile);
+  NS_ENSURE_TRUE(!mLibrary, NS_ERROR_ALREADY_INITIALIZED);
+
+  return aFile->Load(&mLibrary);
+}
+
+NS_IMETHODIMP
+nsNativeTypes::Close()
+{
+  if (mLibrary) {
+    PR_UnloadLibrary(mLibrary);
+    mLibrary = nsnull;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNativeTypes::Declare(nsISupports** aResult)
+{
+  NS_ENSURE_ARG_POINTER(aResult);
+  NS_ENSURE_TRUE(mLibrary, NS_ERROR_NOT_INITIALIZED);
+
+  nsresult rv;
+
+  nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
+
+  nsAXPCNativeCallContext* ncc;
+  rv = xpc->GetCurrentNativeCallContext(&ncc);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  JSContext *ctx;
+  rv = ncc->GetJSContext(&ctx);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  JSAutoRequest ar(ctx);
+
+  PRUint32 argc;
+  jsval *argv;
+  ncc->GetArgc(&argc);
+  ncc->GetArgvPtr(&argv);
+
+  // 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_ERROR_INVALID_ARG;
+  }
+
+  const char* name;
+  rv = jsvalToCString(ctx, argv[0], name);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRUint16 callType;
+  rv = jsvalToUint16(ctx, argv[1], callType);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoTArray<jsval, 16> argTypes;
+  for (PRUint32 i = 3; i < argc; ++i) {
+    argTypes.AppendElement(argv[i]);
+  }
+
+  PRFuncPtr func = PR_FindFunctionSymbol(mLibrary, name);
+  if (!func) {
+    JS_ReportError(ctx, "Couldn't find function symbol in library");
+    return NS_ERROR_FAILURE;
+  }
+
+  nsRefPtr<nsNativeMethod> call = new nsNativeMethod;
+  rv = call->Init(ctx, this, func, callType, argv[2], argTypes);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  call.forget(aResult);
+  return rv;
+}
+
diff --git a/js/ctypes/nsNativeTypes.h b/js/ctypes/nsNativeTypes.h
new file mode 100644
--- /dev/null
+++ b/js/ctypes/nsNativeTypes.h
@@ -0,0 +1,69 @@
+/* -*-  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 ***** */
+
+#ifndef NSNATIVETYPES_H
+#define NSNATIVETYPES_H
+
+#include "nsINativeTypes.h"
+
+#define NATIVETYPES_CONTRACTID \
+  "@mozilla.org/jsctypes;1"
+
+#define NATIVETYPES_CID \
+{ 0xc797702, 0x1c60, 0x4051, { 0x9d, 0xd7, 0x4d, 0x74, 0x5, 0x60, 0x56, 0x42 } }
+
+struct PRLibrary;
+
+class nsNativeTypes : public nsINativeTypes
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSINATIVETYPES
+
+  nsNativeTypes();
+
+  PRBool IsOpen() { return mLibrary != nsnull; }
+
+private:
+  ~nsNativeTypes();
+
+  PRLibrary* mLibrary;
+};
+
+#endif
diff --git a/js/ctypes/tests/Makefile.in b/js/ctypes/tests/Makefile.in
new file mode 100644
--- /dev/null
+++ b/js/ctypes/tests/Makefile.in
@@ -0,0 +1,76 @@
+# ***** 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 *****
+
+DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = jsctypes-test
+LIBRARY_NAME = jsctypes-test
+FORCE_SHARED_LIB = 1
+NO_DIST_INSTALL = 1
+
+CPPSRCS = jsctypes-test.cpp
+
+EXTRA_DSO_LDOPTS += \
+    $(XPCOM_GLUE_LDOPTS) \
+    $(NSPR_LIBS) \
+    $(NULL)
+
+XPCSHELL_TESTS = unit
+
+include $(topsrcdir)/config/rules.mk
+
+xpctestdir = $(testxpcobjdir)/$(MODULE)/unit
+
+# preprocess and install our unit test into the appropriate directory,
+# and install the test library as well. the xpcshell test rules will
+# install the .js.in from the tests srcdir, so remove it when we're done.
+libs:: unit/test_jsctypes.js.in
+	$(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) \
+	  $^ > $(xpctestdir)/test_jsctypes.js
+	$(TEST_INSTALLER) $(SHARED_LIBRARY) $(xpctestdir)
+	$(RM) $(xpctestdir)/test_jsctypes.js.in
+
+GARBAGE += \
+    $(xpctestdir)/test_jsctypes.js \
+    $(xpctestdir)/$(SHARED_LIBRARY) \
+    $(NULL)
diff --git a/js/ctypes/tests/jsctypes-test.cpp b/js/ctypes/tests/jsctypes-test.cpp
new file mode 100644
--- /dev/null
+++ b/js/ctypes/tests/jsctypes-test.cpp
@@ -0,0 +1,180 @@
+/* -*-  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 "jsctypes-test.h"
+#include "nsStringAPI.h"
+#include "nsCRT.h"
+#include <math.h>
+
+void
+test_v()
+{
+  // do nothing
+  return;
+}
+
+short
+test_s()
+{
+  return 12345;
+}
+
+short
+test_s_s(short number)
+{
+  return number;
+}
+
+short
+test_s_ss(short number1, short number2)
+{
+  return number1 + number2;
+}
+
+int
+test_i()
+{
+  return 123456789;
+}
+
+int
+test_i_i(int number)
+{
+  return number;
+}
+
+int
+test_i_ii(int number1, int number2)
+{
+  return number1 + number2;
+}
+
+float
+test_f()
+{
+  return 123456.5f;
+}
+
+float
+test_f_f(float number)
+{
+  return number;
+}
+
+float
+test_f_ff(float number1, float number2)
+{
+  return (number1 + number2);
+}
+
+double
+test_d()
+{
+  return 123456789.5;
+}
+
+double
+test_d_d(double number)
+{
+  return number;
+}
+
+double
+test_d_dd(double number1, double number2)
+{
+  return (number1 + number2);
+}
+
+int
+test_ansi_len(const char* string)
+{
+  return strlen(string);
+}
+
+int
+test_wide_len(const PRUnichar* string)
+{
+  return nsCRT::strlen(string);
+}
+
+const char *
+test_ansi_ret()
+{
+  return "success";
+}
+
+const PRUnichar *
+test_wide_ret()
+{
+  static const PRUnichar kSuccess[] = {'s', 'u', 'c', 'c', 'e', 's', 's', '\0'};
+  return kSuccess;
+}
+
+char *
+test_ansi_echo(const char* string)
+{
+  return (char*)string;
+}
+
+int
+test_i_if_floor(int number1, float number2)
+{
+  return floor(float(number1) + number2);
+}
+
+int
+test_pt_in_rect(RECT rc, POINT pt)
+{
+  if (pt.x < rc.left || pt.x > rc.right)
+    return 0;
+  if (pt.y < rc.bottom || pt.y > rc.top)
+    return 0;
+  return 1;
+}
+
+int test_nested_struct(NESTED n) {
+  return n.n1 + n.n2 + n.inner.i1 + n.inner.i2 + n.inner.i3 + n.n3 + n.n4;
+}
+
+POINT test_struct_return(RECT r) {
+  POINT p;
+  p.x = r.left; p.y = r.top;
+  return p;
+}
+
diff --git a/js/ctypes/tests/jsctypes-test.h b/js/ctypes/tests/jsctypes-test.h
new file mode 100644
--- /dev/null
+++ b/js/ctypes/tests/jsctypes-test.h
@@ -0,0 +1,99 @@
+/* -*-  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 "nscore.h"
+
+NS_EXTERN_C
+{
+  NS_EXPORT void test_v();
+
+  NS_EXPORT short test_s();
+  NS_EXPORT short test_s_s(short);
+  NS_EXPORT short test_s_ss(short, short);
+
+  NS_EXPORT int test_i();
+  NS_EXPORT int test_i_i(int);
+  NS_EXPORT int test_i_ii(int, int);
+
+  NS_EXPORT float test_f();
+  NS_EXPORT float test_f_f(float);
+  NS_EXPORT float test_f_ff(float, float);
+
+  NS_EXPORT double test_d();
+  NS_EXPORT double test_d_d(double);
+  NS_EXPORT double test_d_dd(double, double);
+
+  NS_EXPORT int test_ansi_len(const char*);
+  NS_EXPORT int test_wide_len(const PRUnichar*);
+  NS_EXPORT const char* test_ansi_ret();
+  NS_EXPORT const PRUnichar* test_wide_ret();
+  NS_EXPORT char* test_ansi_echo(const char*);
+
+  NS_EXPORT int test_i_if_floor(int, float);
+
+  struct POINT {
+    int x;
+    int y;
+  };
+
+  struct RECT {
+    int top;
+    int left;
+    int bottom;
+    int right;
+  };
+
+  struct INNER {
+    unsigned char i1;
+    long long int i2;
+    char i3;
+  };
+
+  struct NESTED {
+    int n1;
+    short n2;
+    INNER inner;
+    long long int n3;
+    int n4;
+  };
+
+  NS_EXPORT int test_pt_in_rect(RECT, POINT);
+  NS_EXPORT int test_nested_struct(NESTED);
+  NS_EXPORT POINT test_struct_return(RECT);
+}
diff --git a/js/ctypes/tests/unit/test_jsctypes.js.in b/js/ctypes/tests/unit/test_jsctypes.js.in
new file mode 100644
--- /dev/null
+++ b/js/ctypes/tests/unit/test_jsctypes.js.in
@@ -0,0 +1,304 @@
+/* -*-  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 ***** */
+
+Components.utils.import("resource://gre/modules/ctypes.jsm");
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+const Types = ctypes.types;
+
+function POINT(x, y) {
+  this.x = x; this.y = y;
+}
+
+POINT.prototype = {
+  _fields_ : [{"x" : Types.INT32}, {"y" : Types.INT32}]
+}
+
+function RECT(top, left, bottom, right) {
+  this.top = top; this.left = left; this.bottom = bottom; this.right = right;
+}
+
+RECT.prototype = {
+  _fields_ : [{"top" : Types.INT32}, {"left" : Types.INT32}, {"bottom" : Types.INT32}, {"right" : Types.INT32}]
+}
+
+function INNER(i1, i2, i3) {
+  this.i1 = i1; this.i2 = i2; this.i3 = i3;
+}
+
+INNER.prototype = {
+  _fields_ : [{"i1" : Types.INT8}, {"i2" : Types.INT64}, {"i3" : Types.INT8}]
+}
+
+function NESTED(n1, n2, inner, n3, n4) {
+  this.n1 = n1; this.n2 = n2; this.inner = inner; this.n3 = n3; this.n4 = n4;
+}
+
+NESTED.prototype = {
+  _fields_ : [{"n1" : Types.INT32}, {"n2" : Types.INT16}, {"inner" : INNER}, {"n3" : Types.INT64}, {"n4" : Types.INT32}]
+}
+
+function do_check_throws(f, type, stack)
+{
+  if (!stack)
+    stack = Components.stack.caller;
+
+  try {
+    f();
+  } catch (exc) {
+    if (exc instanceof type)
+      return;
+    do_throw("expected " + type.name + " exception, caught " + exc, stack);
+  }
+  do_throw("expected " + type.name + " exception, none thrown", stack);
+}
+
+function run_test()
+{
+#ifdef XP_WIN
+  var libfile = do_get_file("jsctypes-test.dll");
+#elifdef XP_MACOSX
+  var libfile = do_get_file("libjsctypes-test.dylib");
+#elifdef XP_UNIX
+  var libfile = do_get_file("libjsctypes-test.so");
+#else
+  // unsupported OS - don't run the test
+  return;
+#endif
+
+  // open the library with an nsILocalFile
+  var library = ctypes.open(libfile);
+
+  run_void_tests(library);
+  run_short_tests(library);
+  run_int_tests(library);
+  run_float_tests(library);
+  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);
+
+  // structs not supported yet
+  //run_struct_tests(library);
+}
+
+function run_void_tests(library) {
+  var test_v = library.declare("test_v", Types.DEFAULT, Types.VOID);
+  do_check_eq(test_v(), undefined);
+}
+
+function run_short_tests(library) {
+  var test_s = library.declare("test_s", Types.DEFAULT, Types.INT16);
+  do_check_eq(test_s(), 12345);
+
+  var test_s_s = library.declare("test_s_s", Types.DEFAULT, Types.INT16, Types.INT16);
+  do_check_eq(test_s_s(5), 5);
+  do_check_eq(test_s_s(0), 0);
+  do_check_eq(test_s_s(0x7fff), 0x7fff);
+  do_check_eq(test_s_s(-0x8000), -0x8000);
+  do_check_eq(1/test_s_s(-0), 1/0);  // that is, test_s_s(-0) is +0
+  do_check_eq(test_s_s(true), 1);
+  do_check_eq(test_s_s(false), 0);
+  do_check_eq(test_s_s(Number(16)), 16);
+
+  // 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_s_s(vals[i]); }, Error);
+
+  var test_s_ss = library.declare("test_s_ss", Types.DEFAULT, Types.INT16, Types.INT16, Types.INT16);
+  do_check_eq(test_s_ss(5, 5), 10);
+
+  // test the range of unsigned. (we can reuse the signed C function
+  // here, since it's binary-compatible.)
+  var test_us_us = library.declare("test_s_s", Types.DEFAULT, Types.UINT16, Types.UINT16);
+  do_check_eq(test_us_us(0xffff), 0xffff);
+  do_check_throws(function () { test_us_us(0x10000); }, Error);
+
+}
+
+function run_int_tests(library) {
+  var test_i = library.declare("test_i", Types.DEFAULT, Types.INT32);
+  do_check_eq(test_i(), 123456789);
+
+  var test_i_i = library.declare("test_i_i", Types.DEFAULT, Types.INT32, Types.INT32);
+  do_check_eq(test_i_i(5), 5);
+  do_check_eq(test_i_i(0), 0);
+  do_check_eq(test_i_i(0x7fffffff), 0x7fffffff);
+  do_check_eq(test_i_i(-0x80000000), -0x80000000);
+  do_check_eq(1/test_i_i(-0), 1/0);  // that is, test_i_i(-0) is +0
+  do_check_eq(test_i_i(true), 1);
+  do_check_eq(test_i_i(false), 0);
+  do_check_eq(test_i_i(Number(16)), 16);
+
+  // don't convert anything else to an int
+  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_i_i(vals[i]); }, Error);
+
+  var test_i_ii = library.declare("test_i_ii", Types.DEFAULT, Types.INT32, Types.INT32, Types.INT32);
+  do_check_eq(test_i_ii(5, 5), 10);
+
+  // test the range of unsigned. (we can reuse the signed C function
+  // here, since it's binary-compatible.)
+  var test_ui_ui = library.declare("test_i_i", Types.DEFAULT, Types.UINT32, Types.UINT32);
+  do_check_eq(test_ui_ui(0xffffffff), 0xffffffff);
+  do_check_throws(function () { test_ui_ui(0x100000000); }, Error);
+}
+
+function run_float_tests(library) {
+  var test_f = library.declare("test_f", Types.DEFAULT, Types.FLOAT);
+  do_check_eq(test_f(), 123456.5);
+
+  var test_f_f = library.declare("test_f_f", Types.DEFAULT, Types.FLOAT, Types.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
+  do_check_eq(test_f_f(Number(16.5)), 16.5);
+
+  // allow values that can't be represented precisely as a float
+  do_check_eq(test_f_f(1 + 1/0x80000000), 1);
+
+  // 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_d_d(vals[i]); }, Error);
+
+  var test_f_ff = library.declare("test_f_ff", Types.DEFAULT, Types.FLOAT, Types.FLOAT, Types.FLOAT);
+  do_check_eq(test_f_ff(5, 5), 10);
+  do_check_eq(test_f_ff(5.5, 5.5), 11);
+}
+
+function run_double_tests(library) {
+  var test_d = library.declare("test_d", Types.DEFAULT, Types.DOUBLE);
+  do_check_eq(test_d(), 123456789.5);
+
+  var test_d_d = library.declare("test_d_d", Types.DEFAULT, Types.DOUBLE, Types.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
+  do_check_eq(test_d_d(Number(16.5)), 16.5);
+
+  // 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]); }, Error);
+
+  var test_d_dd = library.declare("test_d_dd", Types.DEFAULT, Types.DOUBLE, Types.DOUBLE, Types.DOUBLE);
+  do_check_eq(test_d_dd(5, 5), 10);
+  do_check_eq(test_d_dd(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);
+  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]); }, Error);
+
+  var test_wide_len = library.declare("test_wide_len", Types.DEFAULT, Types.INT32, Types.USTRING);
+  do_check_eq(test_wide_len("hello world"), 11);
+
+  var test_ansi_ret = library.declare("test_ansi_ret", Types.DEFAULT, Types.STRING);
+  do_check_eq(test_ansi_ret(), "success");
+
+  var test_wide_ret = library.declare("test_wide_ret", Types.DEFAULT, Types.USTRING);
+  do_check_eq(test_wide_ret(), "success");
+
+  var test_ansi_echo = library.declare("test_ansi_echo", Types.DEFAULT, Types.STRING, Types.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_i_if_floor = library.declare("test_i_if_floor", Types.DEFAULT, Types.INT32, Types.INT32, Types.FLOAT);
+  do_check_eq(test_i_if_floor(5, 5.5), 10);
+  do_check_throws(function() { test_i_if_floor(5.5, 5); }, Error);
+}
+
+function run_struct_tests(library) {
+  var test_pt_in_rect = library.declare("test_pt_in_rect", Types.DEFAULT, Types.INT32, RECT, POINT);
+  var rect = new RECT(10, 5, 5, 10);
+  var pt1 = new POINT(6, 6);
+  do_check_eq(test_pt_in_rect(rect, pt1), 1);
+  var pt2 = new POINT(2, 2);
+  do_check_eq(test_pt_in_rect(rect, pt2), 0);
+
+  // don't allow 0 or null when passing by value
+  do_check_throws(function () { test_pt_in_rect(rect, 0); }, Error);
+  do_check_throws(function () { test_pt_in_rect(rect, null); }, Error);
+  do_check_throws(function () { test_pt_in_rect(rect, undefined); }, Error);
+
+  var test_nested_struct = library.declare("test_nested_struct", Types.DEFAULT, Types.INT32, NESTED);
+  var inner = new INNER(161, 523412, 43);
+  var nested = new NESTED(13155, 1241, inner, 24512115, 1234111);
+  // add up all the numbers and make sure the C function agrees
+  do_check_eq(test_nested_struct(nested), 26284238);
+
+  var test_struct_return = library.declare("test_struct_return", Types.DEFAULT, POINT, RECT);
+  var ret = test_struct_return(rect);
+  do_check_eq(ret.x, rect.left);
+  do_check_eq(ret.y, rect.top);
+}
+
diff --git a/toolkit/library/libxul-config.mk b/toolkit/library/libxul-config.mk
--- a/toolkit/library/libxul-config.mk
+++ b/toolkit/library/libxul-config.mk
@@ -123,16 +123,17 @@ COMPONENT_LIBS += \
 	webbrwsr \
 	nsappshell \
 	txmgr \
 	chrome \
 	commandlines \
 	toolkitcomps \
 	pipboot \
 	pipnss \
+	jsctypes \
 	$(NULL)
 
 ifdef MOZ_XMLEXTRAS
 COMPONENT_LIBS += \
 	xmlextras \
 	$(NULL)
 endif
   
diff --git a/toolkit/library/nsStaticXULComponents.cpp b/toolkit/library/nsStaticXULComponents.cpp
--- a/toolkit/library/nsStaticXULComponents.cpp
+++ b/toolkit/library/nsStaticXULComponents.cpp
@@ -307,16 +307,17 @@
     MODULE(NSS)                              \
     SYSTEMPREF_MODULES                       \
     SPELLCHECK_MODULE                        \
     XMLEXTRAS_MODULE                         \
     LAYOUT_DEBUG_MODULE                      \
     UNIXPROXY_MODULE                         \
     OSXPROXY_MODULE                          \
     WINDOWSPROXY_MODULE                      \
+    MODULE(jsctypes)                         \
     /* end of list */
 
 #define MODULE(_name) \
 NSGETMODULE_ENTRY_POINT(_name) (nsIComponentManager*, nsIFile*, nsIModule**);
 
 XUL_MODULES
 
 #undef MODULE
diff --git a/toolkit/toolkit-makefiles.sh b/toolkit/toolkit-makefiles.sh
--- a/toolkit/toolkit-makefiles.sh
+++ b/toolkit/toolkit-makefiles.sh
@@ -201,16 +201,20 @@ MAKEFILES_xpconnect="
   js/src/xpconnect/tools/idl/Makefile
 "
 
 MAKEFILES_jsdebugger="
   js/jsd/Makefile
   js/jsd/idl/Makefile
 "
 
+MAKEFILES_jsctypes="
+  js/ctypes/Makefile
+"
+
 MAKEFILES_content="
   content/Makefile
   content/base/Makefile
   content/base/public/Makefile
   content/base/src/Makefile
   content/base/test/Makefile
   content/base/test/chrome/Makefile
   content/canvas/Makefile
@@ -841,16 +845,17 @@ add_makefiles "
   $MAKEFILES_dom
   $MAKEFILES_editor
   $MAKEFILES_xmlparser
   $MAKEFILES_gfx
   $MAKEFILES_htmlparser
   $MAKEFILES_intl
   $MAKEFILES_xpconnect
   $MAKEFILES_jsdebugger
+  $MAKEFILES_jsctypes
   $MAKEFILES_content
   $MAKEFILES_layout
   $MAKEFILES_libimg
   $MAKEFILES_libjar
   $MAKEFILES_libreg
   $MAKEFILES_libpref
   $MAKEFILES_plugin
   $MAKEFILES_netwerk
@@ -953,16 +958,17 @@ if [ "$ENABLE_TESTS" ]; then
     dom/tests/mochitest/whatwg/Makefile
     editor/libeditor/html/tests/Makefile
     editor/libeditor/text/tests/Makefile
     embedding/test/Makefile
     extensions/cookie/test/Makefile
     extensions/pref/Makefile
     intl/locale/tests_multilocale/Makefile
     js/src/xpconnect/tests/mochitest/Makefile
+    js/ctypes/tests/Makefile
     layout/forms/test/Makefile
     layout/generic/test/Makefile
     layout/inspector/tests/Makefile
     layout/reftests/fonts/Makefile
     layout/reftests/fonts/mplus/Makefile
     layout/style/test/Makefile
     layout/tables/test/Makefile
     layout/tools/pageloader/Makefile
diff --git a/toolkit/toolkit-tiers.mk b/toolkit/toolkit-tiers.mk
--- a/toolkit/toolkit-tiers.mk
+++ b/toolkit/toolkit-tiers.mk
@@ -73,19 +73,24 @@ endif
 tier_external_dirs	+= gfx/qcms
 
 #
 # tier "gecko" - core components
 #
 
 tier_gecko_dirs += \
 		js/src/xpconnect \
+		js/ctypes \
 		intl/chardet \
 		$(NULL)
 
+tier_gecko_staticdirs += \
+		js/ctypes/libffi \
+		$(NULL)
+
 ifdef MOZ_ENABLE_GTK2
 ifdef MOZ_X11
 tier_gecko_dirs     += widget/src/gtkxtbin
 endif
 endif
 
 tier_gecko_dirs	+= \
 		modules/libjar \
