Bug 720771 - Companion testsuite. r=jorendorff
authorDavid Rajchenbach-Teller <dteller@mozilla.com>
Wed, 11 Apr 2012 17:55:21 -0400
changeset 91462 26eb08593f890f4b04697805dd4c29c6fad2d961
parent 91461 637a5d7228be55ee34dba86a474c9515ac606d36
child 91463 0e03eb171e0869596901d795e95077fb39d16a14
push id22445
push usereakhgari@mozilla.com
push dateThu, 12 Apr 2012 16:19:55 +0000
treeherdermozilla-central@901dfde60183 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs720771
milestone14.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 720771 - Companion testsuite. r=jorendorff
toolkit/components/ctypes/tests/Makefile.in
toolkit/components/ctypes/tests/jsctypes-test-finalizer.cpp
toolkit/components/ctypes/tests/jsctypes-test-finalizer.h
toolkit/components/ctypes/tests/jsctypes-test.cpp
toolkit/components/ctypes/tests/jsctypes-test.h
toolkit/components/ctypes/tests/unit/head.js
toolkit/components/ctypes/tests/unit/test_finalizer.js
toolkit/components/ctypes/tests/unit/test_finalizer_shouldaccept.js
toolkit/components/ctypes/tests/unit/test_finalizer_shouldfail.js
toolkit/components/ctypes/tests/unit/xpcshell.ini
--- a/toolkit/components/ctypes/tests/Makefile.in
+++ b/toolkit/components/ctypes/tests/Makefile.in
@@ -48,16 +48,17 @@ include $(DEPTH)/config/autoconf.mk
 MODULE = jsctypes-test
 LIBRARY_NAME = jsctypes-test
 SHORT_LIBNAME = jscttest
 FORCE_SHARED_LIB = 1
 NO_DIST_INSTALL = 1
 
 CPPSRCS = jsctypes-test.cpp \
           jsctypes-test-errno.cpp \
+          jsctypes-test-finalizer.cpp \
           $(NULL)
 
 LOCAL_INCLUDES = \
     -I$(topsrcdir)/js/src/ctypes \
     $(NULL)
 
 EXTRA_DSO_LDOPTS += $(MOZALLOC_LIB)
 
new file mode 100644
--- /dev/null
+++ b/toolkit/components/ctypes/tests/jsctypes-test-finalizer.cpp
@@ -0,0 +1,293 @@
+#include "errno.h"
+
+#include "jsctypes-test.h"
+#include "jsctypes-test-finalizer.h"
+#include "jsapi.h"
+
+#if defined(XP_WIN)
+#define snprintf _snprintf
+#endif // defined(XP_WIN)
+
+/**
+ * Shared infrastructure
+ */
+
+
+/**
+ * An array of integers representing resources.
+ * - 0: unacquired
+ * - 1: acquired
+ * - < 0: error, resource has been released several times.
+ */
+int *gFinalizerTestResources = NULL;
+char **gFinalizerTestNames = NULL;
+size_t gFinalizerTestSize;
+
+void
+test_finalizer_start(size_t size)
+{
+  gFinalizerTestResources = new int[size];
+  gFinalizerTestNames = new char*[size];
+  gFinalizerTestSize = size;
+  for (size_t i = 0; i < size; ++i) {
+    gFinalizerTestResources[i] = 0;
+    gFinalizerTestNames[i] = NULL;
+  }
+}
+
+void
+test_finalizer_stop()
+{
+  delete[] gFinalizerTestResources;
+}
+
+/**
+ * Check if an acquired resource has been released
+ */
+bool
+test_finalizer_resource_is_acquired(size_t i)
+{
+  return gFinalizerTestResources[i] == 1;
+}
+// Resource type: size_t
+
+// Acquire resource i
+size_t
+test_finalizer_acq_size_t(size_t i)
+{
+  gFinalizerTestResources[i] = 1;
+  return i;
+}
+
+// Release resource i
+void
+test_finalizer_rel_size_t(size_t i)
+{
+  if (--gFinalizerTestResources[i] < 0) {
+    MOZ_NOT_REACHED("Assertion failed");
+  }
+}
+
+size_t
+test_finalizer_rel_size_t_return_size_t(size_t i)
+{
+  if (-- gFinalizerTestResources[i] < 0) {
+    MOZ_NOT_REACHED("Assertion failed");
+  }
+  return i;
+}
+
+
+bool
+test_finalizer_cmp_size_t(size_t a, size_t b)
+{
+  return a==b;
+}
+
+// Resource type: int32_t
+
+// Acquire resource i
+int32_t
+test_finalizer_acq_int32_t(size_t i)
+{
+  gFinalizerTestResources[i] = 1;
+  return i;
+}
+
+// Release resource i
+void
+test_finalizer_rel_int32_t(int32_t i)
+{
+  if (--gFinalizerTestResources[i] < 0) {
+    MOZ_NOT_REACHED("Assertion failed");
+  }
+}
+
+bool
+test_finalizer_cmp_int32_t(int32_t a, int32_t b)
+{
+  return a==b;
+}
+
+// Resource type: int64_t
+
+// Acquire resource i
+int64_t
+test_finalizer_acq_int64_t(size_t i)
+{
+  gFinalizerTestResources[i] = 1;
+  return i;
+}
+
+// Release resource i
+void
+test_finalizer_rel_int64_t(int64_t i)
+{
+  if (-- gFinalizerTestResources[i] < 0) {
+    MOZ_NOT_REACHED("Assertion failed");
+  }
+}
+
+bool
+test_finalizer_cmp_int64_t(int64_t a, int64_t b)
+{
+  return a==b;
+}
+
+// Resource type: void*
+
+// Acquire resource i
+void*
+test_finalizer_acq_ptr_t(size_t i)
+{
+  gFinalizerTestResources[i] = 1;
+  return (void*)&gFinalizerTestResources[i];
+}
+
+// Release resource i
+void
+test_finalizer_rel_ptr_t(void *i)
+{
+  int *as_int = (int*)i;
+  -- (*as_int);
+  if (*as_int < 0) {
+    MOZ_NOT_REACHED("Assertion failed");
+  }
+}
+
+bool
+test_finalizer_cmp_ptr_t(void *a, void *b)
+{
+  return a==b;
+}
+
+// Resource type: NULL
+
+// Acquire resource i
+void*
+test_finalizer_acq_null_t(size_t i)
+{
+  gFinalizerTestResources[0] = 1;//Always index 0
+  return NULL;
+}
+
+// Release resource i
+void
+test_finalizer_rel_null_t(void *i)
+{
+  if (i != NULL) {
+    MOZ_NOT_REACHED("Assertion failed");
+  }
+  gFinalizerTestResources[0] --;
+}
+
+bool
+test_finalizer_null_resource_is_acquired(size_t)
+{
+  return gFinalizerTestResources[0] == 1;
+}
+
+bool
+test_finalizer_cmp_null_t(void *a, void *b)
+{
+  return a==b;
+}
+
+// Resource type: char*
+
+// Acquire resource i
+char*
+test_finalizer_acq_string_t(int i)
+{
+  gFinalizerTestResources[i] = 1;
+  if (!gFinalizerTestNames[i]) {
+    char* buf = new char[10];
+    snprintf(buf, 10, "%d", i);
+    gFinalizerTestNames[i] = buf;
+    return buf;
+  }
+  return gFinalizerTestNames[i];
+}
+
+// Release resource i
+void
+test_finalizer_rel_string_t(char *i)
+{
+  int index = atoi(i);
+  if (index < 0 || index >= (int)gFinalizerTestSize) {
+    MOZ_NOT_REACHED("Assertion failed");
+  }
+  gFinalizerTestResources[index] --;
+}
+
+bool
+test_finalizer_string_resource_is_acquired(size_t i)
+{
+  return gFinalizerTestResources[i] == 1;
+}
+
+bool
+test_finalizer_cmp_string_t(char *a, char *b)
+{
+  return !strncmp(a, b, 10);
+}
+
+// Resource type: RECT
+
+// Acquire resource i
+RECT
+test_finalizer_acq_struct_t(int i)
+{
+  gFinalizerTestResources[i] = 1;
+  RECT result = { i, i, i, i };
+  return result;
+}
+
+// Release resource i
+void
+test_finalizer_rel_struct_t(RECT i)
+{
+  int index = i.top;
+  if (index < 0 || index >= (int)gFinalizerTestSize) {
+    MOZ_NOT_REACHED("Assertion failed");
+  }
+  gFinalizerTestResources[index] --;
+}
+
+bool
+test_finalizer_struct_resource_is_acquired(RECT i)
+{
+  int index = i.top;
+  if (index < 0 || index >= (int)gFinalizerTestSize) {
+    MOZ_NOT_REACHED("Assertion failed");
+  }
+  return gFinalizerTestResources[index] == 1;
+}
+
+bool
+test_finalizer_cmp_struct_t(RECT a, RECT b)
+{
+  return a.top == b.top;
+}
+
+// Support for checking that we reject NULL finalizer
+afun* test_finalizer_rel_null_function()
+{
+  return NULL;
+}
+
+void
+test_finalizer_rel_size_t_set_errno(size_t i)
+{
+  if (-- gFinalizerTestResources[i] < 0) {
+    MOZ_NOT_REACHED("Assertion failed");
+  }
+  errno = 10;
+}
+
+void
+reset_errno()
+{
+  errno = 0;
+}
+
new file mode 100644
--- /dev/null
+++ b/toolkit/components/ctypes/tests/jsctypes-test-finalizer.h
@@ -0,0 +1,52 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "jsapi.h"
+
+#define EXPORT_CDECL(type)   NS_EXPORT type
+
+NS_EXTERN_C
+{
+
+  EXPORT_CDECL(void) test_finalizer_start(size_t size);
+  EXPORT_CDECL(void) test_finalizer_stop();
+  EXPORT_CDECL(bool) test_finalizer_resource_is_acquired(size_t i);
+
+  EXPORT_CDECL(size_t) test_finalizer_acq_size_t(size_t i);
+  EXPORT_CDECL(void) test_finalizer_rel_size_t(size_t i);
+  EXPORT_CDECL(size_t) test_finalizer_rel_size_t_return_size_t(size_t i);
+  EXPORT_CDECL(bool) test_finalizer_cmp_size_t(size_t a, size_t b);
+
+  EXPORT_CDECL(int32_t) test_finalizer_acq_int32_t(size_t i);
+  EXPORT_CDECL(void) test_finalizer_rel_int32_t(int32_t i);
+  EXPORT_CDECL(bool) test_finalizer_cmp_int32_t(int32_t a, int32_t b);
+
+  EXPORT_CDECL(int64_t) test_finalizer_acq_int64_t(size_t i);
+  EXPORT_CDECL(void) test_finalizer_rel_int64_t(int64_t i);
+  EXPORT_CDECL(bool) test_finalizer_cmp_int64_t(int64_t a, int64_t b);
+
+  EXPORT_CDECL(void*) test_finalizer_acq_ptr_t(size_t i);
+  EXPORT_CDECL(void) test_finalizer_rel_ptr_t(void *i);
+  EXPORT_CDECL(bool) test_finalizer_cmp_ptr_t(void *a, void *b);
+
+  EXPORT_CDECL(char*) test_finalizer_acq_string_t(int i);
+  EXPORT_CDECL(void) test_finalizer_rel_string_t(char *i);
+  EXPORT_CDECL(bool) test_finalizer_cmp_string_t(char *a, char *b);
+
+  EXPORT_CDECL(void*) test_finalizer_acq_null_t(size_t i);
+  EXPORT_CDECL(void) test_finalizer_rel_null_t(void *i);
+  EXPORT_CDECL(bool) test_finalizer_cmp_null_t(void *a, void *b);
+  EXPORT_CDECL(bool) test_finalizer_null_resource_is_acquired(size_t i);
+
+  EXPORT_CDECL(RECT) test_finalizer_acq_struct_t(int i);
+  EXPORT_CDECL(void) test_finalizer_rel_struct_t(RECT i);
+  EXPORT_CDECL(bool) test_finalizer_cmp_struct_t(RECT a, RECT b);
+
+  typedef void (*afun)(size_t);
+  EXPORT_CDECL(afun*) test_finalizer_rel_null_function();
+
+  EXPORT_CDECL(void) test_finalizer_rel_size_t_set_errno(size_t i);
+  EXPORT_CDECL(void) reset_errno();
+
+}
--- a/toolkit/components/ctypes/tests/jsctypes-test.cpp
+++ b/toolkit/components/ctypes/tests/jsctypes-test.cpp
@@ -18,35 +18,42 @@
  * 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):
  *  Fredrik Larsson <nossralf@gmail.com>
  *  Mark Finkle <mark.finkle@gmail.com>, <mfinkle@mozilla.com>
  *  Dan Witte <dwitte@mozilla.com>
+ *  David Rajchenbach-Teller <dteller@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 "jsapi.h"
 #include "nsCRTGlue.h"
 #include <math.h>
 #include <stdarg.h>
+#include <stdio.h>
+
+#if defined(XP_WIN)
+#define snprintf _snprintf
+#endif // defined(XP_WIN)
 
 template <typename T> struct ValueTraits {
   static T literal() { return static_cast<T>(109.25); }
   static T sum(T a, T b) { return a + b; }
   static T sum_many(
     T a, T b, T c, T d, T e, T f, T g, T h, T i,
     T j, T k, T l, T m, T n, T o, T p, T q, T r)
   {
@@ -398,9 +405,8 @@ test_vector_add_va_cdecl(PRUint8 num_vec
     for (PRUint8 j = 0; j < vec_len; ++j)
       result[j] += vec[j];
   }
   va_end(list);
   return result;
 }
 
 RECT data_rect = { -1, -2, 3, 4 };
-
--- a/toolkit/components/ctypes/tests/jsctypes-test.h
+++ b/toolkit/components/ctypes/tests/jsctypes-test.h
@@ -18,16 +18,17 @@
  * 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):
  *  Fredrik Larsson <nossralf@gmail.com>
  *  Mark Finkle <mark.finkle@gmail.com>, <mfinkle@mozilla.com>
  *  Dan Witte <dwitte@mozilla.com>
+ *  David Rajchenbach-Teller <dteller@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
--- a/toolkit/components/ctypes/tests/unit/head.js
+++ b/toolkit/components/ctypes/tests/unit/head.js
@@ -72,8 +72,23 @@ function structural_check_eq_aux(a, b) {
   ak.forEach(
     function(k) {
       let av = a[k];
       let bv = b[k];
       structural_check_eq_aux(av, bv);
     }
   );
 }
+
+function trigger_gc() {
+  dump("Triggering garbage-collection");
+  Components.utils.forceGC();
+}
+
+function must_throw(f) {
+  let has_thrown = false;
+  try {
+    f();
+  } catch (x) {
+    has_thrown = true;
+  }
+  do_check_true(has_thrown);
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/ctypes/tests/unit/test_finalizer.js
@@ -0,0 +1,385 @@
+let TEST_SIZE = 100;
+
+function run_test()
+{
+  let library = open_ctypes_test_lib();
+
+  let start = library.declare("test_finalizer_start", ctypes.default_abi,
+                          ctypes.void_t,
+                          ctypes.size_t);
+  let stop = library.declare("test_finalizer_stop", ctypes.default_abi,
+                         ctypes.void_t);
+  let status = library.declare("test_finalizer_resource_is_acquired",
+                           ctypes.default_abi,
+                           ctypes.bool,
+                           ctypes.size_t);
+
+  let samples = [];
+  samples.push(
+    {
+      name: "size_t",
+      acquire: library.declare("test_finalizer_acq_size_t",
+                           ctypes.default_abi,
+                           ctypes.size_t,
+                           ctypes.size_t),
+      release: library.declare("test_finalizer_rel_size_t",
+                           ctypes.default_abi,
+                           ctypes.void_t,
+                           ctypes.size_t),
+      compare: library.declare("test_finalizer_cmp_size_t",
+                               ctypes.default_abi,
+                               ctypes.bool,
+                               ctypes.size_t,
+                               ctypes.size_t),
+      status: status
+  });
+  samples.push(
+    {
+      name: "size_t",
+      acquire: library.declare("test_finalizer_acq_size_t",
+                           ctypes.default_abi,
+                           ctypes.size_t,
+                           ctypes.size_t),
+      release: library.declare("test_finalizer_rel_size_t_set_errno",
+                           ctypes.default_abi,
+                           ctypes.void_t,
+                           ctypes.size_t),
+      compare: library.declare("test_finalizer_cmp_size_t",
+                               ctypes.default_abi,
+                               ctypes.bool,
+                               ctypes.size_t,
+                               ctypes.size_t),
+      status: status
+  });
+  samples.push(
+    {
+      name: "int32_t",
+      acquire: library.declare("test_finalizer_acq_int32_t",
+                           ctypes.default_abi,
+                           ctypes.int32_t,
+                           ctypes.size_t),
+      release: library.declare("test_finalizer_rel_int32_t",
+                           ctypes.default_abi,
+                           ctypes.void_t,
+                           ctypes.int32_t),
+      compare: library.declare("test_finalizer_cmp_int32_t",
+                               ctypes.default_abi,
+                               ctypes.bool,
+                               ctypes.int32_t,
+                               ctypes.int32_t),
+      status: status
+    }
+  );
+  samples.push(
+    {
+      name: "int64_t",
+      acquire: library.declare("test_finalizer_acq_int64_t",
+                           ctypes.default_abi,
+                           ctypes.int64_t,
+                           ctypes.size_t),
+      release: library.declare("test_finalizer_rel_int64_t",
+                           ctypes.default_abi,
+                           ctypes.void_t,
+                           ctypes.int64_t),
+      compare: library.declare("test_finalizer_cmp_int64_t",
+                               ctypes.default_abi,
+                               ctypes.bool,
+                               ctypes.int64_t,
+                               ctypes.int64_t),
+      status: status
+    }
+  );
+  samples.push(
+    {
+      name: "ptr",
+      acquire: library.declare("test_finalizer_acq_ptr_t",
+                           ctypes.default_abi,
+                           ctypes.PointerType(ctypes.void_t),
+                           ctypes.size_t),
+      release: library.declare("test_finalizer_rel_ptr_t",
+                           ctypes.default_abi,
+                           ctypes.void_t,
+                           ctypes.PointerType(ctypes.void_t)),
+      compare: library.declare("test_finalizer_cmp_ptr_t",
+                               ctypes.default_abi,
+                               ctypes.bool,
+                               ctypes.void_t.ptr,
+                               ctypes.void_t.ptr),
+      status: status
+    }
+  );
+  samples.push(
+    {
+      name: "string",
+      acquire: library.declare("test_finalizer_acq_string_t",
+                               ctypes.default_abi,
+                               ctypes.char.ptr,
+                               ctypes.int),
+      release: library.declare("test_finalizer_rel_string_t",
+                               ctypes.default_abi,
+                               ctypes.void_t,
+                               ctypes.char.ptr),
+      compare: library.declare("test_finalizer_cmp_string_t",
+                               ctypes.default_abi,
+                               ctypes.bool,
+                               ctypes.char.ptr,
+                               ctypes.char.ptr),
+      status: status
+    }
+  );
+  const rect_t = new ctypes.StructType("RECT",
+                                       [{ top   : ctypes.int32_t },
+                                        { left  : ctypes.int32_t },
+                                        { bottom: ctypes.int32_t },
+                                        { right : ctypes.int32_t }]);
+  samples.push(
+    {
+      name: "struct",
+      acquire: library.declare("test_finalizer_acq_struct_t",
+                               ctypes.default_abi,
+                               rect_t,
+                               ctypes.int),
+      release: library.declare("test_finalizer_rel_struct_t",
+                               ctypes.default_abi,
+                               ctypes.void_t,
+                               rect_t),
+      compare: library.declare("test_finalizer_cmp_struct_t",
+                               ctypes.default_abi,
+                               ctypes.bool,
+                               rect_t,
+                               rect_t),
+      status: status
+    }
+  );
+  samples.push(
+    {
+      name: "size_t, release returns size_t",
+      acquire: library.declare("test_finalizer_acq_size_t",
+                           ctypes.default_abi,
+                           ctypes.size_t,
+                           ctypes.size_t),
+      release: library.declare("test_finalizer_rel_size_t_return_size_t",
+                           ctypes.default_abi,
+                           ctypes.size_t,
+                           ctypes.size_t),
+      compare: library.declare("test_finalizer_cmp_size_t",
+                               ctypes.default_abi,
+                               ctypes.bool,
+                               ctypes.size_t,
+                               ctypes.size_t),
+      status: status
+    }
+  );
+  samples.push(
+    {
+      name: "null",
+      acquire: library.declare("test_finalizer_acq_null_t",
+                           ctypes.default_abi,
+                           ctypes.PointerType(ctypes.void_t),
+                           ctypes.size_t),
+      release: library.declare("test_finalizer_rel_null_t",
+                           ctypes.default_abi,
+                           ctypes.void_t,
+                           ctypes.PointerType(ctypes.void_t)),
+      status: library.declare("test_finalizer_null_resource_is_acquired",
+                             ctypes.default_abi,
+                             ctypes.bool,
+                             ctypes.size_t),
+      compare: library.declare("test_finalizer_cmp_null_t",
+                               ctypes.default_abi,
+                               ctypes.bool,
+                               ctypes.void_t.ptr,
+                               ctypes.void_t.ptr)
+    }
+  );
+
+  let tester = new ResourceTester(start, stop);
+  samples.forEach(
+    function(sample) {
+      dump("Executing finalization test for data "+sample.name+"\n");
+      tester.launch(TEST_SIZE, test_executing_finalizers, sample);
+      tester.launch(TEST_SIZE, test_do_not_execute_finalizers_on_referenced_stuff, sample);
+      tester.launch(TEST_SIZE, test_executing_dispose, sample);
+      tester.launch(TEST_SIZE, test_executing_forget, sample);
+    }
+  );
+
+  /*
+   * Following test deactivated: Cycle collection never takes place
+   * (see bug 727371)
+  tester.launch(TEST_SIZE, test_cycles, samples[0]);
+   */
+  library.close();
+}
+
+// If only I could have Promises to test this :)
+// There is only so much we can do at this stage,
+// if we want to avoid tests overlapping.
+function test_cycles(size, tc) {
+  // Now, restart this with unreferenced cycles
+  for (i = 0; i < size/2; ++i) {
+    let a = {
+      a: ctypes.CDataFinalizer(tc.acquire(i*2), tc.release),
+      b: {
+        b: ctypes.CDataFinalizer(tc.acquire(i*2+1), tc.release)
+      }
+    };
+    a.b.a = a;
+  }
+  do_test_pending();
+
+  Components.utils.schedulePreciseGC(
+    function() {
+      // Check that _something_ has been finalized
+      do_check_true(count_finalized(size, tc) > 0);
+      do_test_finished();
+    }
+  );
+
+  do_timeout(10000, do_throw);
+}
+
+
+function count_finalized(size, tc) {
+  let finalizedItems = 0;
+  for (let i = 0; i < size; ++i) {
+    if (!tc.status(i)) {
+      ++finalizedItems;
+    }
+  }
+  return finalizedItems;
+}
+
+/**
+ * Test:
+ * - that (some) finalizers are executed;
+ * - that no finalizer is executed twice (this is done on the C side).
+ */
+function test_executing_finalizers(size, tc)
+{
+  dump("test_executing_finalizers\n");
+  // Allocate |size| items without references
+  for (let i = 0; i < size; ++i) {
+    ctypes.CDataFinalizer(tc.acquire(i), tc.release);
+  }
+  trigger_gc(); // This should trigger some finalizations, hopefully all
+
+  // Check that _something_ has been finalized
+  do_check_true(count_finalized(size, tc) > 0);
+}
+
+
+/**
+ * Check that
+ * - |dispose| is executed properly
+ * - finalizers are not executed after |dispose|
+ */
+function test_executing_dispose(size, tc)
+{
+  dump("test_executing_dispose\n");
+  let ref = [];
+  // Allocate |size| items with references
+  for (let i = 0; i < size; ++i) {
+    ref.push(ctypes.CDataFinalizer(tc.acquire(i), tc.release));
+  }
+  do_check_eq(count_finalized(size, tc), 0);
+
+  // Dispose of everything and make sure that everything has been cleaned up
+  ref.forEach(
+    function(v) {
+      v.dispose();
+    }
+  );
+  do_check_eq(count_finalized(size, tc), size);
+
+  // Remove references
+  ref = [];
+
+  // Re-acquireialize data and make sure that everything has been reinialized
+  for (i = 0; i < size; ++i) {
+    tc.acquire(i);
+  }
+
+  do_check_eq(count_finalized(size, tc), 0);
+
+
+  // Attempt to trigger finalizations, ensure that they do not take place
+  trigger_gc();
+
+  do_check_eq(count_finalized(size, tc), 0);
+}
+
+
+/**
+ * Check that
+ * - |forget| does not dispose
+ * - |forget| has the right content
+ * - finalizers are not executed after |forget|
+ */
+function test_executing_forget(size, tc)
+{
+  dump("test_executing_forget\n");
+  let ref = [];
+  // Allocate |size| items with references
+  for (let i = 0; i < size; ++i) {
+    let original = tc.acquire(i);
+    let finalizer = ctypes.CDataFinalizer(original, tc.release);
+    ref.push(
+      {
+        original: original,
+        finalizer: finalizer
+      }
+    );
+    do_check_true(tc.compare(original, finalizer));
+  }
+  do_check_eq(count_finalized(size, tc), 0);
+
+  // Forget everything, making sure that we recover the original info
+  ref.forEach(
+    function(v) {
+      let original  = v.original;
+      let recovered = v.finalizer.forget();
+      // Note: Cannot use do_check_eq on Uint64 et al.
+      do_check_true(tc.compare(original, recovered));
+      do_check_eq(original.constructor, recovered.constructor);
+    }
+  );
+
+  // Also make sure that we have not performed any clean up
+  do_check_eq(count_finalized(size, tc), 0);
+
+  // Remove references
+  ref = [];
+
+  // Attempt to trigger finalizations, ensure that they have no effect
+  trigger_gc();
+
+  do_check_eq(count_finalized(size, tc), 0);
+}
+
+
+/**
+ * Check that finalizers are not executed
+ */
+function test_do_not_execute_finalizers_on_referenced_stuff(size, tc)
+{
+  dump("test_do_not_execute_finalizers_on_referenced_stuff\n");
+
+  let ref = [];
+  // Allocate |size| items without references
+  for (let i = 0; i < size; ++i) {
+    ref.push(ctypes.CDataFinalizer(tc.acquire(i), tc.release));
+  }
+  trigger_gc(); // This might trigger some finalizations, but it should not
+
+  // Check that _nothing_ has been finalized
+  do_check_eq(count_finalized(size, tc), 0);
+
+  // Clean up manually, lest leftover data interferes with following tests
+  ref.forEach(function(v) {
+    v.dispose();
+  });
+  ref = [];
+  trigger_gc();
+}
+
new file mode 100644
--- /dev/null
+++ b/toolkit/components/ctypes/tests/unit/test_finalizer_shouldaccept.js
@@ -0,0 +1,122 @@
+try {
+  // We might be running without privileges, in which case it's up to the
+  // harness to give us the 'ctypes' object.
+  Components.utils.import("resource://gre/modules/ctypes.jsm");
+} catch(e) {
+}
+
+let acquire, dispose, reset_errno, dispose_errno;
+
+function run_test()
+{
+  let library = open_ctypes_test_lib();
+
+  let start = library.declare("test_finalizer_start", ctypes.default_abi,
+                          ctypes.void_t,
+                          ctypes.size_t);
+  let stop = library.declare("test_finalizer_stop", ctypes.default_abi,
+                             ctypes.void_t);
+  let tester = new ResourceTester(start, stop);
+  acquire = library.declare("test_finalizer_acq_size_t",
+                            ctypes.default_abi,
+                            ctypes.size_t,
+                            ctypes.size_t);
+  dispose = library.declare("test_finalizer_rel_size_t",
+                            ctypes.default_abi,
+                            ctypes.void_t,
+                            ctypes.size_t);
+  reset_errno = library.declare("reset_errno",
+                                ctypes.default_abi,
+                                ctypes.void_t);
+  dispose_errno = library.declare("test_finalizer_rel_size_t_set_errno",
+                                  ctypes.default_abi,
+                                  ctypes.void_t,
+                                  ctypes.size_t);
+  tester.launch(10, test_to_string);
+  tester.launch(10, test_to_source);
+  tester.launch(10, test_to_int);
+  tester.launch(10, test_errno);
+}
+
+/**
+ * Check that toString succeeds before/after forget/dispose.
+ */
+function test_to_string()
+{
+  do_print("Starting test_to_string");
+  let a = ctypes.CDataFinalizer(acquire(0), dispose);
+  do_check_eq(a.toString(), "0");
+
+  a.forget();
+  do_check_eq(a.toString(), "[CDataFinalizer - empty]");
+
+  a = ctypes.CDataFinalizer(acquire(0), dispose);
+  a.dispose();
+  do_check_eq(a.toString(), "[CDataFinalizer - empty]");
+}
+
+/**
+ * Check that toSource succeeds before/after forget/dispose.
+ */
+function test_to_source()
+{
+  do_print("Starting test_to_source");
+  let value = acquire(0);
+  let a = ctypes.CDataFinalizer(value, dispose);
+  do_check_eq(a.toSource(),
+              "ctypes.CDataFinalizer("
+              + ctypes.size_t(value).toSource()
+              +", "
+              +dispose.toSource()
+              +")");
+  value = null;
+
+  a.forget();
+  do_check_eq(a.toSource(), "ctypes.CDataFinalizer()");
+
+  a = ctypes.CDataFinalizer(acquire(0), dispose);
+  a.dispose();
+  do_check_eq(a.toSource(), "ctypes.CDataFinalizer()");
+}
+
+/**
+ * Test conversion to int32
+ */
+function test_to_int()
+{
+  let value = 2;
+  let wrapped, converted, finalizable;
+  wrapped   = ctypes.int32_t(value);
+  finalizable= ctypes.CDataFinalizer(acquire(value), dispose);
+  converted = ctypes.int32_t(finalizable);
+
+  structural_check_eq(converted, wrapped);
+  structural_check_eq(converted, ctypes.int32_t(finalizable.forget()));
+
+  wrapped   = ctypes.int64_t(value);
+  converted = ctypes.int64_t(ctypes.CDataFinalizer(acquire(value),
+                                                   dispose));
+  structural_check_eq(converted, wrapped);
+}
+
+/**
+ * Test that dispose can change errno but finalization cannot
+ */
+function test_errno(size)
+{
+  reset_errno();
+  do_check_eq(ctypes.errno, 0);
+
+  let finalizable = ctypes.CDataFinalizer(acquire(3), dispose_errno);
+  finalizable.dispose();
+  do_check_eq(ctypes.errno, 10);
+  reset_errno();
+
+  do_check_eq(ctypes.errno, 0);
+  for (let i = 0; i < size; ++i) {
+    finalizable = ctypes.CDataFinalizer(acquire(i), dispose_errno);
+  }
+
+  trigger_gc();
+  do_check_eq(ctypes.errno, 0);
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/toolkit/components/ctypes/tests/unit/test_finalizer_shouldfail.js
@@ -0,0 +1,156 @@
+try {
+  // We might be running without privileges, in which case it's up to the
+  // harness to give us the 'ctypes' object.
+  Components.utils.import("resource://gre/modules/ctypes.jsm");
+} catch(e) {
+}
+
+let acquire, dispose, null_dispose, compare;
+
+function run_test()
+{
+  let library = open_ctypes_test_lib();
+
+  let start = library.declare("test_finalizer_start", ctypes.default_abi,
+                          ctypes.void_t,
+                          ctypes.size_t);
+  let stop = library.declare("test_finalizer_stop", ctypes.default_abi,
+                             ctypes.void_t);
+  let tester = new ResourceTester(start, stop);
+  acquire = library.declare("test_finalizer_acq_size_t",
+                            ctypes.default_abi,
+                            ctypes.size_t,
+                            ctypes.size_t);
+  dispose = library.declare("test_finalizer_rel_size_t",
+                            ctypes.default_abi,
+                            ctypes.void_t,
+                            ctypes.size_t);
+  compare = library.declare("test_finalizer_cmp_size_t",
+                            ctypes.default_abi,
+                            ctypes.bool,
+                            ctypes.size_t,
+                            ctypes.size_t);
+
+  let type_afun = ctypes.FunctionType(ctypes.default_abi,
+                                      ctypes.void_t,
+                                      [ctypes.size_t]).ptr;
+
+  let null_dispose_maker =
+    library.declare("test_finalizer_rel_null_function",
+                    ctypes.default_abi,
+                    type_afun
+                   );
+  null_dispose = null_dispose_maker();
+
+  tester.launch(10, test_double_dispose);
+  tester.launch(10, test_finalize_bad_construction);
+  tester.launch(10, test_null_dispose);
+  tester.launch(10, test_pass_disposed);
+}
+
+
+/**
+ * Testing construction of finalizers with wrong arguments.
+ */
+function test_finalize_bad_construction() {
+  // First argument does not match second
+  must_throw(function() { ctypes.CDataFinalizer({}, dispose); });
+  must_throw(function() { ctypes.CDataFinalizer(dispose, dispose); });
+
+  // Not enough arguments
+  must_throw(function() { ctypes.CDataFinalizer(init(0)); });
+
+  // Too many arguments
+  must_throw(function() { ctypes.CDataFinalizer(init(0), dispose, dispose); });
+
+  // Second argument is null
+  must_throw(function() { ctypes.CDataFinalizer(init(0), null); });
+
+  // Second argument is undefined
+  must_throw(function() {
+    let a;
+    ctypes.CDataFinalizer(init(0), a);
+  });
+
+}
+
+/**
+ * Test that forget/dispose can only take place once.
+ */
+function test_double_dispose() {
+  function test_one_combination(i, a, b) {
+    let v = ctypes.CDataFinalizer(acquire(i), dispose);
+    a(v);
+    must_throw(function() { b(v); } );
+  }
+
+  let call_dispose = function(v) {
+    v.dispose();
+  };
+  let call_forget = function(v) {
+    v.forget();
+  };
+
+  test_one_combination(0, call_dispose, call_dispose);
+  test_one_combination(1, call_dispose, call_forget);
+  test_one_combination(2, call_forget, call_dispose);
+  test_one_combination(3, call_forget, call_forget);
+}
+
+
+/**
+ * Test that nothing (too) bad happens when the finalizer is NULL
+ */
+function test_null_dispose()
+{
+  let exception;
+
+  exception = false;
+  try {
+    let v = ctypes.CDataFinalizer(acquire(0), null_dispose);
+  } catch (x) {
+    exception = true;
+  }
+  do_check_true(exception);
+}
+
+/**
+ * Test that conversion of a disposed/forgotten CDataFinalizer to a C
+ * value fails nicely.
+ */
+function test_pass_disposed()
+{
+  let exception, v;
+
+  exception = false;
+  v = ctypes.CDataFinalizer(acquire(0), dispose);
+  do_check_true(compare(v, 0));
+  v.forget();
+
+  try {
+    compare(v, 0);
+  } catch (x) {
+    exception = true;
+  }
+  do_check_true(exception);
+
+  exception = false;
+  v = ctypes.CDataFinalizer(acquire(0), dispose);
+  do_check_true(compare(v, 0));
+  v.dispose();
+
+  try {
+    compare(v, 0);
+  } catch (x) {
+    exception = true;
+  }
+  do_check_true(exception);
+
+  exception = false;
+  try {
+    ctypes.int32_t(ctypes.CDataFinalizer(v, dispose));
+  } catch (x) {
+    exception = true;
+  }
+  do_check_true(exception);
+}
\ No newline at end of file
--- a/toolkit/components/ctypes/tests/unit/xpcshell.ini
+++ b/toolkit/components/ctypes/tests/unit/xpcshell.ini
@@ -1,9 +1,13 @@
 [DEFAULT]
 head = head.js
 tail = 
 
 [test_errno.js]
 
+[test_finalizer.js]
+[test_finalizer_shouldfail.js]
+[test_finalizer_shouldaccept.js]
+
 [test_jsctypes.js]
 # Bug 676989: test fails consistently on Android
 fail-if = os == "android"