Bug 891107 - Part 5: Show function name in this and callee type error messages in js-ctypes. r=jorendorff
☠☠ backed out by 750308ebc10a ☠ ☠
authorTooru Fujisawa <arai_a@mac.com>
Fri, 07 Aug 2015 06:53:29 +0900
changeset 288440 13e045fff28d32c498821dc0d3f094140f1f1a36
parent 288439 b2624519692fb70a1c09d37a7f6a8c1bf1a9f7b0
child 288441 8fe18f2b8aa9240e032a935f9af3d99e180a9f84
push id30079
push userryanvm@gmail.com
push dateSat, 12 Mar 2016 20:24:19 +0000
treeherdermozilla-central@d1d47ba19ce9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs891107
milestone48.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 891107 - Part 5: Show function name in this and callee type error messages in js-ctypes. r=jorendorff
js/src/ctypes/CTypes.cpp
js/src/ctypes/ctypes.msg
js/src/jit-test/tests/ctypes/incompatible-abi.js
js/src/jit-test/tests/ctypes/incompatible-array.js
js/src/jit-test/tests/ctypes/incompatible-cdata.js
js/src/jit-test/tests/ctypes/incompatible-ctype.js
js/src/jit-test/tests/ctypes/incompatible-finalizer.js
js/src/jit-test/tests/ctypes/incompatible-function.js
js/src/jit-test/tests/ctypes/incompatible-int64.js
js/src/jit-test/tests/ctypes/incompatible-pointer.js
js/src/jit-test/tests/ctypes/incompatible-struct.js
toolkit/components/ctypes/tests/unit/test_jsctypes.js
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -429,16 +429,17 @@ namespace CDataFinalizer {
   };
 
   /*
    * Methods of instances of |CDataFinalizer|
    */
   namespace Methods {
     static bool Dispose(JSContext* cx, unsigned argc, Value* vp);
     static bool Forget(JSContext* cx, unsigned argc, Value* vp);
+    static bool ReadString(JSContext* cx, unsigned argc, Value* vp);
     static bool ToSource(JSContext* cx, unsigned argc, Value* vp);
     static bool ToString(JSContext* cx, unsigned argc, Value* vp);
   } // namespace Methods
 
   /*
    * Utility functions
    *
    * @return true if |obj| is a CDataFinalizer, false otherwise.
@@ -478,17 +479,16 @@ namespace CDataFinalizer {
 
   /*
    * Return the Value contained by this finalizer.
    *
    * Note that the Value is actually not recorded, but converted back from C.
    */
   static bool GetValue(JSContext* cx, JSObject* obj, MutableHandleValue result);
 
-  static JSObject* GetCData(JSContext* cx, JSObject* obj);
 } // namespace CDataFinalizer
 
 
 // Int64Base provides functions common to Int64 and UInt64.
 namespace Int64Base {
   JSObject* Construct(JSContext* cx, HandleObject proto, uint64_t data,
     bool isUnsigned);
 
@@ -670,17 +670,17 @@ static const JSFunctionSpec sCDataFuncti
   JS_FN("toSource", CData::ToSource, 0, CDATAFN_FLAGS),
   JS_FN("toString", CData::ToSource, 0, CDATAFN_FLAGS),
   JS_FS_END
 };
 
 static const JSFunctionSpec sCDataFinalizerFunctions[] = {
   JS_FN("dispose",  CDataFinalizer::Methods::Dispose,  0, CDATAFINALIZERFN_FLAGS),
   JS_FN("forget",   CDataFinalizer::Methods::Forget,   0, CDATAFINALIZERFN_FLAGS),
-  JS_FN("readString",CData::ReadString, 0, CDATAFINALIZERFN_FLAGS),
+  JS_FN("readString", CDataFinalizer::Methods::ReadString, 0, CDATAFINALIZERFN_FLAGS),
   JS_FN("toString", CDataFinalizer::Methods::ToString, 0, CDATAFINALIZERFN_FLAGS),
   JS_FN("toSource", CDataFinalizer::Methods::ToSource, 0, CDATAFINALIZERFN_FLAGS),
   JS_FS_END
 };
 
 static const JSFunctionSpec sPointerFunction =
   JS_FN("PointerType", PointerType::Create, 1, CTYPESCTOR_FLAGS);
 
@@ -1333,16 +1333,24 @@ ArgumentTypeMismatch(JSContext* cx, cons
                      const char* type)
 {
   JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
                        CTYPESMSG_ARG_TYPE_MISMATCH, arg, func, type);
   return false;
 }
 
 static bool
+EmptyFinalizerCallError(JSContext* cx, const char* funName)
+{
+  JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
+                       CTYPESMSG_EMPTY_FIN_CALL, funName);
+  return false;
+}
+
+static bool
 EmptyFinalizerError(JSContext* cx, ConversionType convType,
                     HandleObject funObj = nullptr, unsigned argIndex = 0)
 {
   JSAutoByteString posBytes;
   const char* posStr;
   if (funObj) {
     AutoString posSource;
     BuildConversionPosition(cx, convType, funObj, argIndex, posSource);
@@ -1421,16 +1429,77 @@ FinalizerSizeError(JSContext* cx, Handle
     return false;
 
   JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
                        CTYPESMSG_FIN_SIZE_ERROR, funStr, valStr);
   return false;
 }
 
 static bool
+IncompatibleCallee(JSContext* cx, const char* funName, HandleObject actualObj)
+{
+  JSAutoByteString valBytes;
+  RootedValue val(cx, ObjectValue(*actualObj));
+  const char* valStr = CTypesToSourceForError(cx, val, valBytes);
+  if (!valStr)
+    return false;
+
+  JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
+                       CTYPESMSG_INCOMPATIBLE_CALLEE, funName, valStr);
+  return false;
+}
+
+static bool
+IncompatibleThisProto(JSContext* cx, const char* funName,
+                      const char* actualType)
+{
+  JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
+                       CTYPESMSG_INCOMPATIBLE_THIS,
+                       funName, actualType);
+  return false;
+}
+
+static bool
+IncompatibleThisProto(JSContext* cx, const char* funName, HandleValue actualVal)
+{
+  JSAutoByteString valBytes;
+  const char* valStr = CTypesToSourceForError(cx, actualVal, valBytes);
+  if (!valStr)
+    return false;
+
+  JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
+                       CTYPESMSG_INCOMPATIBLE_THIS_VAL,
+                       funName, "incompatible object", valStr);
+  return false;
+}
+
+static bool
+IncompatibleThisType(JSContext* cx, const char* funName, const char* actualType)
+{
+  JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
+                       CTYPESMSG_INCOMPATIBLE_THIS_TYPE, funName, actualType);
+  return false;
+}
+
+static bool
+IncompatibleThisType(JSContext* cx, const char* funName, const char* actualType,
+                     HandleValue actualVal)
+{
+  JSAutoByteString valBytes;
+  const char* valStr = CTypesToSourceForError(cx, actualVal, valBytes);
+  if (!valStr)
+    return false;
+
+  JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
+                       CTYPESMSG_INCOMPATIBLE_THIS_VAL,
+                       funName, actualType, valStr);
+  return false;
+}
+
+static bool
 NonPrimitiveError(JSContext* cx, HandleObject typeObj)
 {
   MOZ_ASSERT(CType::IsCType(typeObj));
 
   AutoString typeSource;
   JSAutoByteString typeBytes;
   BuildTypeSource(cx, typeObj, true, typeSource);
   const char* typeStr = EncodeLatin1(cx, typeSource, typeBytes);
@@ -3907,18 +3976,17 @@ bool
 CType::ConstructData(JSContext* cx,
                      unsigned argc,
                      Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   // get the callee object...
   RootedObject obj(cx, &args.callee());
   if (!CType::IsCType(obj)) {
-    JS_ReportError(cx, "not a CType");
-    return false;
+    return IncompatibleCallee(cx, "CType constructor", obj);
   }
 
   // How we construct the CData object depends on what type we represent.
   // An instance 'd' of a CData object of type 't' has:
   //   * [[Class]] "CData"
   //   * __proto__ === t.prototype
   switch (GetTypeCode(obj)) {
   case TYPE_void_t:
@@ -4450,18 +4518,17 @@ CType::PtrGetter(JSContext* cx, const JS
 bool
 CType::CreateArray(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   RootedObject baseType(cx, JS_THIS_OBJECT(cx, vp));
   if (!baseType)
     return false;
   if (!CType::IsCType(baseType)) {
-    JS_ReportError(cx, "not a CType");
-    return false;
+    return IncompatibleThisProto(cx, "CType.prototype.array", args.thisv());
   }
 
   // Construct and return a new ArrayType object.
   if (args.length() > 1) {
     return ArgumentLengthError(cx, "CType.prototype.array", "at most one", "");
   }
 
   // Convert the length argument to a size_t.
@@ -4482,18 +4549,18 @@ CType::CreateArray(JSContext* cx, unsign
 bool
 CType::ToString(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
   if (!obj)
     return false;
   if (!CType::IsCType(obj) && !CType::IsCTypeProto(obj)) {
-    JS_ReportError(cx, "not a CType");
-    return false;
+    return IncompatibleThisProto(cx, "CType.prototype.toString",
+                                 InformalValueTypeName(args.thisv()));
   }
 
   // Create the appropriate string depending on whether we're sCTypeClass or
   // sCTypeProtoClass.
   JSString* result;
   if (CType::IsCType(obj)) {
     AutoString type;
     AppendString(type, "type ");
@@ -4512,20 +4579,19 @@ CType::ToString(JSContext* cx, unsigned 
 
 bool
 CType::ToSource(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   JSObject* obj = JS_THIS_OBJECT(cx, vp);
   if (!obj)
     return false;
-  if (!CType::IsCType(obj) && !CType::IsCTypeProto(obj))
-  {
-    JS_ReportError(cx, "not a CType");
-    return false;
+  if (!CType::IsCType(obj) && !CType::IsCTypeProto(obj)) {
+    return IncompatibleThisProto(cx, "CType.prototype.toSource",
+                                 InformalValueTypeName(args.thisv()));
   }
 
   // Create the appropriate string depending on whether we're sCTypeClass or
   // sCTypeProtoClass.
   JSString* result;
   if (CType::IsCType(obj)) {
     AutoString source;
     BuildTypeSource(cx, obj, false, source);
@@ -4602,18 +4668,18 @@ ABI::ToSource(JSContext* cx, unsigned ar
   if (args.length() != 0) {
     return ArgumentLengthError(cx, "ABI.prototype.toSource", "no", "s");
   }
 
   JSObject* obj = JS_THIS_OBJECT(cx, vp);
   if (!obj)
     return false;
   if (!ABI::IsABI(obj)) {
-    JS_ReportError(cx, "not an ABI");
-    return false;
+    return IncompatibleThisProto(cx, "ABI.prototype.toSource",
+                                 InformalValueTypeName(args.thisv()));
   }
 
   JSString* result;
   switch (GetABICode(obj)) {
     case ABI_DEFAULT:
       result = JS_NewStringCopyZ(cx, "ctypes.default_abi");
       break;
     case ABI_STDCALL:
@@ -4698,18 +4764,17 @@ PointerType::CreateInternal(JSContext* c
 }
 
 bool
 PointerType::ConstructData(JSContext* cx,
                            HandleObject obj,
                            const CallArgs& args)
 {
   if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_pointer) {
-    JS_ReportError(cx, "not a PointerType");
-    return false;
+    return IncompatibleCallee(cx, "PointerType constructor", obj);
   }
 
   if (args.length() > 3) {
     return ArgumentLengthError(cx, "PointerType constructor", "0, 1, 2, or 3",
                                "s");
   }
 
   RootedObject result(cx, CData::Create(cx, obj, nullptr, nullptr, true));
@@ -4818,47 +4883,55 @@ PointerType::TargetTypeGetter(JSContext*
 bool
 PointerType::IsNull(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   JSObject* obj = JS_THIS_OBJECT(cx, vp);
   if (!obj)
     return false;
   if (!CData::IsCData(obj)) {
-    JS_ReportError(cx, "not a CData");
-    return false;
+    return IncompatibleThisProto(cx, "PointerType.prototype.isNull",
+                                 args.thisv());
   }
 
   // Get pointer type and base type.
   JSObject* typeObj = CData::GetCType(obj);
   if (CType::GetTypeCode(typeObj) != TYPE_pointer) {
-    JS_ReportError(cx, "not a PointerType");
-    return false;
+    return IncompatibleThisType(cx, "PointerType.prototype.isNull",
+                                "non-PointerType CData", args.thisv());
   }
 
   void* data = *static_cast<void**>(CData::GetData(obj));
   args.rval().setBoolean(data == nullptr);
   return true;
 }
 
 bool
 PointerType::OffsetBy(JSContext* cx, const CallArgs& args, int offset)
 {
   JSObject* obj = JS_THIS_OBJECT(cx, args.base());
   if (!obj)
     return false;
   if (!CData::IsCData(obj)) {
-    JS_ReportError(cx, "not a CData");
-    return false;
+    if (offset == 1) {
+      return IncompatibleThisProto(cx, "PointerType.prototype.increment",
+                                   args.thisv());
+    }
+    return IncompatibleThisProto(cx, "PointerType.prototype.decrement",
+                                 args.thisv());
   }
 
   RootedObject typeObj(cx, CData::GetCType(obj));
   if (CType::GetTypeCode(typeObj) != TYPE_pointer) {
-    JS_ReportError(cx, "not a PointerType");
-    return false;
+    if (offset == 1) {
+      return IncompatibleThisType(cx, "PointerType.prototype.increment",
+                                  "non-PointerType CData", args.thisv());
+    }
+    return IncompatibleThisType(cx, "PointerType.prototype.decrement",
+                                "non-PointerType CData", args.thisv());
   }
 
   RootedObject baseType(cx, PointerType::GetBaseType(typeObj));
   if (!CType::IsSizeDefined(baseType)) {
     JS_ReportError(cx, "cannot modify pointer of undefined size");
     return false;
   }
 
@@ -5030,18 +5103,17 @@ ArrayType::CreateInternal(JSContext* cx,
 bool
 ArrayType::ConstructData(JSContext* cx,
                          HandleObject obj_,
                          const CallArgs& args)
 {
   RootedObject obj(cx, obj_); // Make a mutable version
 
   if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_array) {
-    JS_ReportError(cx, "not an ArrayType");
-    return false;
+    return IncompatibleCallee(cx, "ArrayType constructor", obj);
   }
 
   // Decide whether we have an object to initialize from. We'll override this
   // if we get a length argument instead.
   bool convertObject = args.length() == 1;
 
   // Check if we're an array of undefined length. If we are, allow construction
   // with a length argument, or with an actual JS array.
@@ -5275,18 +5347,18 @@ ArrayType::LengthGetter(JSContext* cx, c
   return true;
 }
 
 bool
 ArrayType::Getter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp)
 {
   // This should never happen, but we'll check to be safe.
   if (!CData::IsCData(obj)) {
-    JS_ReportError(cx, "not a CData");
-    return false;
+    RootedValue objVal(cx, ObjectValue(*obj));
+    return IncompatibleThisProto(cx, "ArrayType property getter", objVal);
   }
 
   // Bail early if we're not an ArrayType. (This setter is present for all
   // CData, regardless of CType.)
   JSObject* typeObj = CData::GetCType(obj);
   if (CType::GetTypeCode(typeObj) != TYPE_array)
     return true;
 
@@ -5316,18 +5388,18 @@ ArrayType::Getter(JSContext* cx, HandleO
 }
 
 bool
 ArrayType::Setter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp,
                   ObjectOpResult& result)
 {
   // This should never happen, but we'll check to be safe.
   if (!CData::IsCData(obj)) {
-    JS_ReportError(cx, "not a CData");
-    return false;
+    RootedValue objVal(cx, ObjectValue(*obj));
+    return IncompatibleThisProto(cx, "ArrayType property setter", objVal);
   }
 
   // Bail early if we're not an ArrayType. (This setter is present for all
   // CData, regardless of CType.)
   RootedObject typeObj(cx, CData::GetCType(obj));
   if (CType::GetTypeCode(typeObj) != TYPE_array)
     return result.succeed();
 
@@ -5362,24 +5434,24 @@ ArrayType::Setter(JSContext* cx, HandleO
 bool
 ArrayType::AddressOfElement(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
   if (!obj)
     return false;
   if (!CData::IsCData(obj)) {
-    JS_ReportError(cx, "not a CData");
-    return false;
+    return IncompatibleThisProto(cx, "ArrayType.prototype.addressOfElement",
+                                 args.thisv());
   }
 
   RootedObject typeObj(cx, CData::GetCType(obj));
   if (CType::GetTypeCode(typeObj) != TYPE_array) {
-    JS_ReportError(cx, "not an ArrayType");
-    return false;
+    return IncompatibleThisType(cx, "ArrayType.prototype.addressOfElement",
+                                "non-ArrayType CData", args.thisv());
   }
 
   if (args.length() != 1) {
     return ArgumentLengthError(cx, "ArrayType.prototype.addressOfElement",
                                "one", "");
   }
 
   RootedObject baseType(cx, GetBaseType(typeObj));
@@ -5763,20 +5835,23 @@ StructType::BuildFFIType(JSContext* cx, 
 
 bool
 StructType::Define(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
   if (!obj)
     return false;
-  if (!CType::IsCType(obj) ||
-      CType::GetTypeCode(obj) != TYPE_struct) {
-    JS_ReportError(cx, "not a StructType");
-    return false;
+  if (!CType::IsCType(obj)) {
+    return IncompatibleThisProto(cx, "StructType.prototype.define",
+                                 args.thisv());
+  }
+  if (CType::GetTypeCode(obj) != TYPE_struct) {
+    return IncompatibleThisType(cx, "StructType.prototype.define",
+                                "non-StructType", args.thisv());
   }
 
   if (CType::IsSizeDefined(obj)) {
     JS_ReportError(cx, "StructType has already been defined");
     return false;
   }
 
   if (args.length() != 1) {
@@ -5807,18 +5882,17 @@ StructType::Define(JSContext* cx, unsign
 }
 
 bool
 StructType::ConstructData(JSContext* cx,
                           HandleObject obj,
                           const CallArgs& args)
 {
   if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_struct) {
-    JS_ReportError(cx, "not a StructType");
-    return false;
+    return IncompatibleCallee(cx, "StructType constructor", obj);
   }
 
   if (!CType::IsSizeDefined(obj)) {
     JS_ReportError(cx, "cannot construct an opaque StructType");
     return false;
   }
 
   JSObject* result = CData::Create(cx, obj, nullptr, nullptr, true);
@@ -5986,30 +6060,28 @@ StructType::FieldsArrayGetter(JSContext*
 }
 
 bool
 StructType::FieldGetter(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   if (!args.thisv().isObject()) {
-    JS_ReportError(cx, "not a CData");
-    return false;
+    return IncompatibleThisProto(cx, "StructType property getter", args.thisv());
   }
 
   RootedObject obj(cx, &args.thisv().toObject());
   if (!CData::IsCData(obj)) {
-    JS_ReportError(cx, "not a CData");
-    return false;
+    return IncompatibleThisProto(cx, "StructType property getter", args.thisv());
   }
 
   JSObject* typeObj = CData::GetCType(obj);
   if (CType::GetTypeCode(typeObj) != TYPE_struct) {
-    JS_ReportError(cx, "not a StructType");
-    return false;
+    return IncompatibleThisType(cx, "StructType property getter",
+                                "non-StructType CData", args.thisv());
   }
 
   RootedValue nameVal(cx, GetFunctionNativeReserved(&args.callee(), SLOT_FIELDNAME));
   Rooted<JSFlatString*> name(cx, JS_FlattenString(cx, nameVal.toString()));
   if (!name)
     return false;
 
   const FieldInfo* field = LookupField(cx, typeObj, name);
@@ -6022,30 +6094,28 @@ StructType::FieldGetter(JSContext* cx, u
 }
 
 bool
 StructType::FieldSetter(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   if (!args.thisv().isObject()) {
-    JS_ReportError(cx, "not a CData");
-    return false;
+    return IncompatibleThisProto(cx, "StructType property setter", args.thisv());
   }
 
   RootedObject obj(cx, &args.thisv().toObject());
   if (!CData::IsCData(obj)) {
-    JS_ReportError(cx, "not a CData");
-    return false;
+    return IncompatibleThisProto(cx, "StructType property setter", args.thisv());
   }
 
   RootedObject typeObj(cx, CData::GetCType(obj));
   if (CType::GetTypeCode(typeObj) != TYPE_struct) {
-    JS_ReportError(cx, "not a StructType");
-    return false;
+    return IncompatibleThisType(cx, "StructType property setter",
+                                "non-StructType CData", args.thisv());
   }
 
   RootedValue nameVal(cx, GetFunctionNativeReserved(&args.callee(), SLOT_FIELDNAME));
   Rooted<JSFlatString*> name(cx, JS_FlattenString(cx, nameVal.toString()));
   if (!name)
     return false;
 
   const FieldInfo* field = LookupField(cx, typeObj, name);
@@ -6061,25 +6131,25 @@ StructType::FieldSetter(JSContext* cx, u
 
 bool
 StructType::AddressOfField(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
   if (!obj)
     return false;
-  if (!CData::IsCData(obj)) {
-    JS_ReportError(cx, "not a CData");
-    return false;
+ if (!CData::IsCData(obj)) {
+    return IncompatibleThisProto(cx, "StructType.prototype.addressOfField",
+                                 args.thisv());
   }
 
   JSObject* typeObj = CData::GetCType(obj);
   if (CType::GetTypeCode(typeObj) != TYPE_struct) {
-    JS_ReportError(cx, "not a StructType");
-    return false;
+    return IncompatibleThisType(cx, "StructType.prototype.addressOfField",
+                                "non-StructType CData", args.thisv());
   }
 
   if (args.length() != 1) {
     return ArgumentLengthError(cx, "StructType.prototype.addressOfField",
                                "one", "");
   }
 
   if (!args[0].isString()) {
@@ -6583,30 +6653,30 @@ bool
 FunctionType::Call(JSContext* cx,
                    unsigned argc,
                    Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   // get the callee object...
   RootedObject obj(cx, &args.callee());
   if (!CData::IsCData(obj)) {
-    JS_ReportError(cx, "not a CData");
-    return false;
+    return IncompatibleThisProto(cx, "FunctionType.prototype.call",
+                                 args.calleev());
   }
 
   RootedObject typeObj(cx, CData::GetCType(obj));
   if (CType::GetTypeCode(typeObj) != TYPE_pointer) {
-    JS_ReportError(cx, "not a FunctionType.ptr");
-    return false;
+    return IncompatibleThisType(cx, "FunctionType.prototype.call",
+                                "non-PointerType CData", args.calleev());
   }
 
   typeObj = PointerType::GetBaseType(typeObj);
   if (CType::GetTypeCode(typeObj) != TYPE_function) {
-    JS_ReportError(cx, "not a FunctionType.ptr");
-    return false;
+    return IncompatibleThisType(cx, "FunctionType.prototype.call",
+                                "non-FunctionType pointer", args.calleev());
   }
 
   FunctionInfo* fninfo = GetFunctionInfo(typeObj);
   uint32_t argcFixed = fninfo->mArgTypes.length();
 
   if ((!fninfo->mIsVariadic && args.length() != argcFixed) ||
       (fninfo->mIsVariadic && args.length() < argcFixed)) {
     JS_ReportError(cx, "Number of arguments does not match declaration");
@@ -7278,18 +7348,17 @@ CData::Address(JSContext* cx, unsigned a
   if (args.length() != 0) {
     return ArgumentLengthError(cx, "CData.prototype.address", "no", "s");
   }
 
   RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
   if (!obj)
     return false;
   if (!IsCData(obj)) {
-    JS_ReportError(cx, "not a CData");
-    return false;
+    return IncompatibleThisProto(cx, "CData.prototype.address", args.thisv());
   }
 
   RootedObject typeObj(cx, CData::GetCType(obj));
   RootedObject pointerType(cx, PointerType::CreateInternal(cx, typeObj));
   if (!pointerType)
     return false;
 
   // Create a PointerType CData object containing null.
@@ -7370,31 +7439,52 @@ CData::GetRuntime(JSContext* cx, unsigne
 
   args.rval().setObject(*result);
   return true;
 }
 
 typedef JS::TwoByteCharsZ (*InflateUTF8Method)(JSContext*, const JS::UTF8Chars, size_t*);
 
 static bool
-ReadStringCommon(JSContext* cx, InflateUTF8Method inflateUTF8, unsigned argc, Value* vp)
+ReadStringCommon(JSContext* cx, InflateUTF8Method inflateUTF8, unsigned argc,
+                 Value* vp, const char* funName)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() != 0) {
-    if (inflateUTF8 == JS::UTF8CharsToNewTwoByteCharsZ) {
-      return ArgumentLengthError(cx, "CData.prototype.readString", "no", "s");
-    }
-    return ArgumentLengthError(cx, "CData.prototype.readStringReplaceMalformed",
-                               "no", "s");
-  }
-
-  JSObject* obj = CDataFinalizer::GetCData(cx, JS_THIS_OBJECT(cx, vp));
-  if (!obj || !CData::IsCData(obj)) {
-    JS_ReportError(cx, "not a CData");
-    return false;
+    return ArgumentLengthError(cx, funName, "no", "s");
+  }
+
+  RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
+  if (!obj) {
+    return IncompatibleThisProto(cx, funName, args.thisv());
+  }
+  if (!CData::IsCData(obj)) {
+      if (!CDataFinalizer::IsCDataFinalizer(obj)) {
+          return IncompatibleThisProto(cx, funName, args.thisv());
+      }
+
+      CDataFinalizer::Private* p = (CDataFinalizer::Private*)
+                                   JS_GetPrivate(obj);
+      if (!p) {
+          return EmptyFinalizerCallError(cx, funName);
+      }
+
+      RootedValue dataVal(cx);
+      if (!CDataFinalizer::GetValue(cx, obj, &dataVal)) {
+          return IncompatibleThisProto(cx, funName, args.thisv());
+      }
+
+      if (dataVal.isPrimitive()) {
+          return IncompatibleThisProto(cx, funName, args.thisv());
+      }
+
+      obj = dataVal.toObjectOrNull();
+      if (!obj || !CData::IsCData(obj)) {
+          return IncompatibleThisProto(cx, funName, args.thisv());
+      }
   }
 
   // Make sure we are a pointer to, or an array of, an 8-bit or 16-bit
   // character or integer type.
   JSObject* baseType;
   JSObject* typeObj = CData::GetCType(obj);
   TypeCode typeCode = CType::GetTypeCode(typeObj);
   void* data;
@@ -7459,23 +7549,32 @@ ReadStringCommon(JSContext* cx, InflateU
 
   args.rval().setString(result);
   return true;
 }
 
 bool
 CData::ReadString(JSContext* cx, unsigned argc, Value* vp)
 {
-  return ReadStringCommon(cx, JS::UTF8CharsToNewTwoByteCharsZ, argc, vp);
+  return ReadStringCommon(cx, JS::UTF8CharsToNewTwoByteCharsZ, argc, vp,
+                          "CData.prototype.readString");
+}
+
+bool
+CDataFinalizer::Methods::ReadString(JSContext* cx, unsigned argc, Value* vp)
+{
+  return ReadStringCommon(cx, JS::UTF8CharsToNewTwoByteCharsZ, argc, vp,
+                          "CDataFinalizer.prototype.readString");
 }
 
 bool
 CData::ReadStringReplaceMalformed(JSContext* cx, unsigned argc, Value* vp)
 {
-  return ReadStringCommon(cx, JS::LossyUTF8CharsToNewTwoByteCharsZ, argc, vp);
+  return ReadStringCommon(cx, JS::LossyUTF8CharsToNewTwoByteCharsZ, argc, vp,
+                          "CData.prototype.readStringReplaceMalformed");
 }
 
 JSString*
 CData::GetSourceString(JSContext* cx, HandleObject typeObj, void* data)
 {
   // Walk the types, building up the toSource() string.
   // First, we build up the type expression:
   // 't.ptr' for pointers;
@@ -7500,18 +7599,18 @@ CData::ToSource(JSContext* cx, unsigned 
   if (args.length() != 0) {
     return ArgumentLengthError(cx, "CData.prototype.toSource", "no", "s");
   }
 
   JSObject* obj = JS_THIS_OBJECT(cx, vp);
   if (!obj)
     return false;
   if (!CData::IsCData(obj) && !CData::IsCDataProto(obj)) {
-    JS_ReportError(cx, "not a CData");
-    return false;
+    return IncompatibleThisProto(cx, "CData.prototype.toSource",
+                                 InformalValueTypeName(args.thisv()));
   }
 
   JSString* result;
   if (CData::IsCData(obj)) {
     RootedObject typeObj(cx, CData::GetCType(obj));
     void* data = CData::GetData(obj);
 
     result = CData::GetSourceString(cx, typeObj, data);
@@ -7545,18 +7644,18 @@ CData::LastErrorGetter(JSContext* cx, co
 bool
 CDataFinalizer::Methods::ToSource(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   RootedObject objThis(cx, JS_THIS_OBJECT(cx, vp));
   if (!objThis)
     return false;
   if (!CDataFinalizer::IsCDataFinalizer(objThis)) {
-    JS_ReportError(cx, "not a CDataFinalizer");
-    return false;
+    return IncompatibleThisProto(cx, "CDataFinalizer.prototype.toSource",
+                                 InformalValueTypeName(args.thisv()));
   }
 
   CDataFinalizer::Private* p = (CDataFinalizer::Private*)
     JS_GetPrivate(objThis);
 
   JSString* strMessage;
   if (!p) {
     strMessage = JS_NewStringCopyZ(cx, "ctypes.CDataFinalizer()");
@@ -7604,18 +7703,18 @@ CDataFinalizer::Methods::ToSource(JSCont
 bool
 CDataFinalizer::Methods::ToString(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   JSObject* objThis = JS_THIS_OBJECT(cx, vp);
   if (!objThis)
     return false;
   if (!CDataFinalizer::IsCDataFinalizer(objThis)) {
-    JS_ReportError(cx, "not a CDataFinalizer");
-    return false;
+    return IncompatibleThisProto(cx, "CDataFinalizer.prototype.toString",
+                                 InformalValueTypeName(args.thisv()));
   }
 
   JSString* strMessage;
   RootedValue value(cx);
   if (!JS_GetPrivate(objThis)) {
     // Pre-check whether CDataFinalizer::GetValue can fail
     // to avoid reporting an error when not appropriate.
     strMessage = JS_NewStringCopyZ(cx, "[CDataFinalizer - empty]");
@@ -7650,49 +7749,29 @@ CDataFinalizer::GetCType(JSContext* cx, 
                                      SLOT_DATAFINALIZER_VALTYPE);
   if (valData.isUndefined()) {
     return nullptr;
   }
 
   return valData.toObjectOrNull();
 }
 
-JSObject*
-CDataFinalizer::GetCData(JSContext* cx, JSObject* obj)
-{
-  if (!obj) {
-    JS_ReportError(cx, "No C data");
-    return nullptr;
-  }
-  if (CData::IsCData(obj)) {
-    return obj;
-  }
-  if (!CDataFinalizer::IsCDataFinalizer(obj)) {
-    JS_ReportError(cx, "Not C data");
-    return nullptr;
-  }
-  RootedValue val(cx);
-  if (!CDataFinalizer::GetValue(cx, obj, &val) || val.isPrimitive()) {
-    JS_ReportError(cx, "Empty CDataFinalizer");
-    return nullptr;
-  }
-  return val.toObjectOrNull();
-}
-
 bool
-CDataFinalizer::GetValue(JSContext* cx, JSObject* obj, MutableHandleValue aResult)
+CDataFinalizer::GetValue(JSContext* cx, JSObject* obj,
+                         MutableHandleValue aResult)
 {
   MOZ_ASSERT(IsCDataFinalizer(obj));
 
   CDataFinalizer::Private* p = (CDataFinalizer::Private*)
     JS_GetPrivate(obj);
 
   if (!p) {
+    // We have called |dispose| or |forget| already.
     JS_ReportError(cx, "Attempting to get the value of an empty CDataFinalizer");
-    return false;  // We have called |dispose| or |forget| already.
+    return false;
   }
 
   RootedObject ctype(cx, GetCType(cx, obj));
   return ConvertToJS(cx, ctype, /*parent*/nullptr, p->cargs, false, true, aResult);
 }
 
 /*
  * Attach a C function as a finalizer to a JS object.
@@ -7939,30 +8018,29 @@ bool
 CDataFinalizer::Methods::Forget(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() != 0) {
     return ArgumentLengthError(cx, "CDataFinalizer.prototype.forget", "no",
                                "s");
   }
 
-  JS::Rooted<JSObject*> obj(cx, args.thisv().toObjectOrNull());
+  RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
   if (!obj)
     return false;
   if (!CDataFinalizer::IsCDataFinalizer(obj)) {
-    JS_ReportError(cx, "not a CDataFinalizer");
-    return false;
+    return IncompatibleThisProto(cx, "CDataFinalizer.prototype.forget",
+                                 args.thisv());
   }
 
   CDataFinalizer::Private* p = (CDataFinalizer::Private*)
     JS_GetPrivate(obj);
 
   if (!p) {
-    JS_ReportError(cx, "forget called on an empty CDataFinalizer");
-    return false;
+    return EmptyFinalizerCallError(cx, "CDataFinalizer.prototype.forget");
   }
 
   RootedValue valJSData(cx);
   RootedObject ctype(cx, GetCType(cx, obj));
   if (!ConvertToJS(cx, ctype, nullptr, p->cargs, false, true, &valJSData)) {
     JS_ReportError(cx, "CDataFinalizer value cannot be represented");
     return false;
   }
@@ -7991,26 +8069,25 @@ CDataFinalizer::Methods::Dispose(JSConte
     return ArgumentLengthError(cx, "CDataFinalizer.prototype.dispose", "no",
                                "s");
   }
 
   RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
   if (!obj)
     return false;
   if (!CDataFinalizer::IsCDataFinalizer(obj)) {
-    JS_ReportError(cx, "not a CDataFinalizer");
-    return false;
+    return IncompatibleThisProto(cx, "CDataFinalizer.prototype.dispose",
+                                 args.thisv());
   }
 
   CDataFinalizer::Private* p = (CDataFinalizer::Private*)
     JS_GetPrivate(obj);
 
   if (!p) {
-    JS_ReportError(cx, "dispose called on an empty CDataFinalizer.");
-    return false;
+    return EmptyFinalizerCallError(cx, "CDataFinalizer.prototype.dispose");
   }
 
   Value valType = JS_GetReservedSlot(obj, SLOT_DATAFINALIZER_VALTYPE);
   MOZ_ASSERT(valType.isObject());
 
   JSObject* objCTypes = CType::GetGlobalCTypes(cx, &valType.toObject());
   if (!objCTypes)
     return false;
@@ -8275,33 +8352,41 @@ Int64::IsInt64(JSObject* obj)
 bool
 Int64::ToString(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   JSObject* obj = JS_THIS_OBJECT(cx, vp);
   if (!obj)
     return false;
   if (!Int64::IsInt64(obj)) {
-    JS_ReportError(cx, "not an Int64");
-    return false;
+    if (!CData::IsCData(obj)) {
+      return IncompatibleThisProto(cx, "Int64.prototype.toString",
+                                   InformalValueTypeName(args.thisv()));
+    }
+    return IncompatibleThisType(cx, "Int64.prototype.toString",
+                                "non-Int64 CData");
   }
 
   return Int64Base::ToString(cx, obj, args, false);
 }
 
 bool
 Int64::ToSource(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   JSObject* obj = JS_THIS_OBJECT(cx, vp);
   if (!obj)
     return false;
   if (!Int64::IsInt64(obj)) {
-    JS_ReportError(cx, "not an Int64");
-    return false;
+    if (!CData::IsCData(obj)) {
+      return IncompatibleThisProto(cx, "Int64.prototype.toSource",
+                                   InformalValueTypeName(args.thisv()));
+    }
+    return IncompatibleThisType(cx, "Int64.prototype.toSource",
+                                "non-Int64 CData");
   }
 
   return Int64Base::ToSource(cx, obj, args, false);
 }
 
 bool
 Int64::Compare(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -8451,33 +8536,41 @@ UInt64::IsUInt64(JSObject* obj)
 bool
 UInt64::ToString(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   JSObject* obj = JS_THIS_OBJECT(cx, vp);
   if (!obj)
     return false;
   if (!UInt64::IsUInt64(obj)) {
-    JS_ReportError(cx, "not a UInt64");
-    return false;
+    if (!CData::IsCData(obj)) {
+      return IncompatibleThisProto(cx, "UInt64.prototype.toString",
+                                   InformalValueTypeName(args.thisv()));
+    }
+    return IncompatibleThisType(cx, "UInt64.prototype.toString",
+                                "non-UInt64 CData");
   }
 
   return Int64Base::ToString(cx, obj, args, true);
 }
 
 bool
 UInt64::ToSource(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   JSObject* obj = JS_THIS_OBJECT(cx, vp);
   if (!obj)
     return false;
   if (!UInt64::IsUInt64(obj)) {
-    JS_ReportError(cx, "not a UInt64");
-    return false;
+    if (!CData::IsCData(obj)) {
+      return IncompatibleThisProto(cx, "UInt64.prototype.toSource",
+                                   InformalValueTypeName(args.thisv()));
+    }
+    return IncompatibleThisType(cx, "UInt64.prototype.toSource",
+                                "non-UInt64 CData");
   }
 
   return Int64Base::ToSource(cx, obj, args, true);
 }
 
 bool
 UInt64::Compare(JSContext* cx, unsigned argc, Value* vp)
 {
--- a/js/src/ctypes/ctypes.msg
+++ b/js/src/ctypes/ctypes.msg
@@ -26,18 +26,23 @@ MSG_DEF(CTYPESMSG_ARRAY_MISMATCH,4, JSEX
 MSG_DEF(CTYPESMSG_ARRAY_OVERFLOW,4, JSEXN_TYPEERR, "length of {0} does not fit in the length of the type {1} (expected {2} or lower, got {3})")
 
 /* struct */
 MSG_DEF(CTYPESMSG_FIELD_MISMATCH,5, JSEXN_TYPEERR, "property count of {0} does not match to field count of the type {1} (expected {2}, got {3}){4}")
 MSG_DEF(CTYPESMSG_PROP_NONSTRING,3, JSEXN_TYPEERR, "property name {0} of {1} is not a string{2}")
 
 /* data finalizer */
 MSG_DEF(CTYPESMSG_EMPTY_FIN,     1, JSEXN_TYPEERR, "attempting to convert an empty CDataFinalizer{0}")
+MSG_DEF(CTYPESMSG_EMPTY_FIN_CALL,1, JSEXN_TYPEERR, "{0} called on empty CDataFinalizer")
 MSG_DEF(CTYPESMSG_FIN_SIZE_ERROR,2, JSEXN_TYPEERR, "expected an object with the same size as argument 1 of {0}, got {1}")
 
 /* native function */
 MSG_DEF(CTYPESMSG_ARG_RANGE_MISMATCH,3, JSEXN_RANGEERR, "{0}argument of {1} must be {2}")
 MSG_DEF(CTYPESMSG_ARG_TYPE_MISMATCH,3, JSEXN_TYPEERR, "{0}argument of {1} must be {2}")
+MSG_DEF(CTYPESMSG_INCOMPATIBLE_CALLEE,2, JSEXN_TYPEERR, "callee is not correct in {0}, got {1}")
+MSG_DEF(CTYPESMSG_INCOMPATIBLE_THIS,2, JSEXN_TYPEERR, "{0} called on incompatible {1}")
+MSG_DEF(CTYPESMSG_INCOMPATIBLE_THIS_TYPE,2, JSEXN_TYPEERR, "{0} called on {1}")
+MSG_DEF(CTYPESMSG_INCOMPATIBLE_THIS_VAL,3, JSEXN_TYPEERR, "{0} called on {1}, got {2}")
 MSG_DEF(CTYPESMSG_WRONG_ARG_LENGTH,3, JSEXN_TYPEERR, "{0} takes {1} argument{2}")
 
 /* overflow */
 MSG_DEF(CTYPESMSG_SIZE_OVERFLOW, 2, JSEXN_RANGEERR, "{0} does not fit in {1}")
 MSG_DEF(CTYPESMSG_TYPE_OVERFLOW, 2, JSEXN_RANGEERR, "{0} does not fit in the type {1}")
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ctypes/incompatible-abi.js
@@ -0,0 +1,9 @@
+load(libdir + 'asserts.js');
+
+function test() {
+  assertTypeErrorMessage(() => { ctypes.default_abi.toSource.call(1); },
+                         "ABI.prototype.toSource called on incompatible Number");
+}
+
+if (typeof ctypes === "object")
+  test();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ctypes/incompatible-array.js
@@ -0,0 +1,13 @@
+load(libdir + 'asserts.js');
+
+function test() {
+  assertTypeErrorMessage(() => { ctypes.int32_t.array.call(1); },
+                         "CType.prototype.array called on incompatible object, got the object (new Number(1))");
+  assertTypeErrorMessage(() => { ctypes.int32_t.array(10)().addressOfElement.call(1); },
+                         "ArrayType.prototype.addressOfElement called on incompatible object, got the object (new Number(1))");
+  assertTypeErrorMessage(() => { ctypes.int32_t.array(10)().addressOfElement.call(ctypes.int32_t(0)); },
+                         "ArrayType.prototype.addressOfElement called on non-ArrayType CData, got ctypes.int32_t(0)");
+}
+
+if (typeof ctypes === "object")
+  test();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ctypes/incompatible-cdata.js
@@ -0,0 +1,24 @@
+load(libdir + 'asserts.js');
+
+function test() {
+  assertTypeErrorMessage(() => { ctypes.int32_t(0).address.call(1); },
+                         "CData.prototype.address called on incompatible object, got the object (new Number(1))");
+  assertTypeErrorMessage(() => { ctypes.char.array(10)("abc").readString.call(1); },
+                         "CData.prototype.readString called on incompatible object, got the object (new Number(1))");
+
+  assertTypeErrorMessage(() => { ctypes.char.array(10)("abc").readStringReplaceMalformed.call(1); },
+                         "CData.prototype.readStringReplaceMalformed called on incompatible object, got the object (new Number(1))");
+  assertTypeErrorMessage(() => { ctypes.int32_t(0).toSource.call(1); },
+                         "CData.prototype.toSource called on incompatible Number");
+
+  let p = Object.getPrototypeOf(ctypes.int32_t());
+  let o = {};
+  Object.setPrototypeOf(o, p);
+  assertTypeErrorMessage(() => { o.readString(); },
+                         "CData.prototype.readString called on incompatible object, got <<error converting value to string>>");
+  assertTypeErrorMessage(() => { o.readStringReplaceMalformed(); },
+                         "CData.prototype.readStringReplaceMalformed called on incompatible object, got <<error converting value to string>>");
+}
+
+if (typeof ctypes === "object")
+  test();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ctypes/incompatible-ctype.js
@@ -0,0 +1,11 @@
+load(libdir + 'asserts.js');
+
+function test() {
+  assertTypeErrorMessage(() => { ctypes.int32_t.toString.call(1); },
+                         "CType.prototype.toString called on incompatible Number");
+  assertTypeErrorMessage(() => { ctypes.int32_t.toSource.call(1); },
+                         "CType.prototype.toSource called on incompatible Number");
+}
+
+if (typeof ctypes === "object")
+  test();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ctypes/incompatible-finalizer.js
@@ -0,0 +1,24 @@
+load(libdir + 'asserts.js');
+
+function test() {
+  let fin = ctypes.CDataFinalizer(ctypes.int32_t(0), ctypes.FunctionType(ctypes.default_abi, ctypes.int32_t, [ctypes.int32_t]).ptr(x => x));
+  assertTypeErrorMessage(() => { fin.toSource.call(1); },
+                         "CDataFinalizer.prototype.toSource called on incompatible Number");
+  assertTypeErrorMessage(() => { fin.toString.call(1); },
+                         "CDataFinalizer.prototype.toString called on incompatible Number");
+  assertTypeErrorMessage(() => { fin.forget.call(1); },
+                         "CDataFinalizer.prototype.forget called on incompatible object, got the object (new Number(1))");
+  assertTypeErrorMessage(() => { fin.dispose.call(1); },
+                         "CDataFinalizer.prototype.dispose called on incompatible object, got the object (new Number(1))");
+  fin.forget();
+
+  assertTypeErrorMessage(() => { fin.readString(); },
+                         "CDataFinalizer.prototype.readString called on empty CDataFinalizer");
+  assertTypeErrorMessage(() => { fin.dispose(); },
+                         "CDataFinalizer.prototype.dispose called on empty CDataFinalizer");
+  assertTypeErrorMessage(() => { fin.forget(); },
+                         "CDataFinalizer.prototype.forget called on empty CDataFinalizer");
+}
+
+if (typeof ctypes === "object")
+  test();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ctypes/incompatible-function.js
@@ -0,0 +1,13 @@
+load(libdir + 'asserts.js');
+
+function test() {
+  assertTypeErrorMessage(() => { ctypes.FunctionType(ctypes.default_abi, ctypes.void_t).call.call(1); },
+                         "Function.prototype.call called on incompatible number");
+  assertTypeErrorMessage(() => { ctypes.FunctionType(ctypes.default_abi, ctypes.void_t).call.call(ctypes.int32_t(0)); },
+                         "FunctionType.prototype.call called on non-PointerType CData, got ctypes.int32_t(0)");
+  assertTypeErrorMessage(() => { ctypes.FunctionType(ctypes.default_abi, ctypes.void_t).call.call(ctypes.int32_t.ptr(0)); },
+                         "FunctionType.prototype.call called on non-FunctionType pointer, got ctypes.int32_t.ptr(ctypes.UInt64(\"0x0\"))");
+}
+
+if (typeof ctypes === "object")
+  test();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ctypes/incompatible-int64.js
@@ -0,0 +1,24 @@
+load(libdir + 'asserts.js');
+
+function test() {
+  assertTypeErrorMessage(() => { ctypes.Int64(0).toString.call(1); },
+                         "Int64.prototype.toString called on incompatible Number");
+  assertTypeErrorMessage(() => { ctypes.Int64(0).toString.call(ctypes.int32_t(0)); },
+                         "Int64.prototype.toString called on non-Int64 CData");
+  assertTypeErrorMessage(() => { ctypes.Int64(0).toSource.call(1); },
+                         "Int64.prototype.toSource called on incompatible Number");
+  assertTypeErrorMessage(() => { ctypes.Int64(0).toSource.call(ctypes.int32_t(0)); },
+                         "Int64.prototype.toSource called on non-Int64 CData");
+
+  assertTypeErrorMessage(() => { ctypes.UInt64(0).toString.call(1); },
+                         "UInt64.prototype.toString called on incompatible Number");
+  assertTypeErrorMessage(() => { ctypes.UInt64(0).toString.call(ctypes.int32_t(0)); },
+                         "UInt64.prototype.toString called on non-UInt64 CData");
+  assertTypeErrorMessage(() => { ctypes.UInt64(0).toSource.call(1); },
+                         "UInt64.prototype.toSource called on incompatible Number");
+  assertTypeErrorMessage(() => { ctypes.UInt64(0).toSource.call(ctypes.int32_t(0)); },
+                         "UInt64.prototype.toSource called on non-UInt64 CData");
+}
+
+if (typeof ctypes === "object")
+  test();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ctypes/incompatible-pointer.js
@@ -0,0 +1,19 @@
+load(libdir + 'asserts.js');
+
+function test() {
+  assertTypeErrorMessage(() => { ctypes.int32_t.ptr(0).isNull.call(1); },
+                         "PointerType.prototype.isNull called on incompatible object, got the object (new Number(1))");
+  assertTypeErrorMessage(() => { ctypes.int32_t.ptr(0).isNull.call({}); },
+                         "PointerType.prototype.isNull called on incompatible object, got the object ({})");
+  assertTypeErrorMessage(() => { ctypes.int32_t.ptr(0).increment.call(1); },
+                         "PointerType.prototype.increment called on incompatible object, got the object (new Number(1))");
+  assertTypeErrorMessage(() => { ctypes.int32_t.ptr(0).increment.call(ctypes.int32_t(0)); },
+                         "PointerType.prototype.increment called on non-PointerType CData, got ctypes.int32_t(0)");
+  assertTypeErrorMessage(() => { ctypes.int32_t.ptr(0).decrement.call(1); },
+                         "PointerType.prototype.decrement called on incompatible object, got the object (new Number(1))");
+  assertTypeErrorMessage(() => { ctypes.int32_t.ptr(0).decrement.call(ctypes.int32_t(0)); },
+                         "PointerType.prototype.decrement called on non-PointerType CData, got ctypes.int32_t(0)");
+}
+
+if (typeof ctypes === "object")
+  test();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ctypes/incompatible-struct.js
@@ -0,0 +1,31 @@
+load(libdir + 'asserts.js');
+
+function test() {
+  assertTypeErrorMessage(() => { ctypes.StructType("a").define.call(1); },
+                         "StructType.prototype.define called on incompatible object, got the object (new Number(1))");
+  assertTypeErrorMessage(() => { ctypes.StructType("a").define.call(ctypes.int32_t); },
+                         "StructType.prototype.define called on non-StructType, got ctypes.int32_t");
+
+  let p = Object.getPrototypeOf(ctypes.StructType("a", [ { "x": ctypes.int32_t, } ])());
+  let o = {};
+  Object.setPrototypeOf(o, p);
+  assertTypeErrorMessage(() => { let a = o.x; },
+                         "StructType property getter called on incompatible object, got <<error converting value to string>>");
+  assertTypeErrorMessage(() => { o.x = 1; },
+                         "StructType property setter called on incompatible object, got <<error converting value to string>>");
+
+  o = ctypes.int32_t(0);
+  Object.setPrototypeOf(o, p);
+  assertTypeErrorMessage(() => { let a = o.x; },
+                         "StructType property getter called on non-StructType CData, got ctypes.int32_t(0)");
+  assertTypeErrorMessage(() => { o.x = 1; },
+                         "StructType property setter called on non-StructType CData, got ctypes.int32_t(0)");
+
+  assertTypeErrorMessage(() => { ctypes.StructType("a", [])().addressOfField.call(1); },
+                         "StructType.prototype.addressOfField called on incompatible object, got the object (new Number(1))");
+  assertTypeErrorMessage(() => { ctypes.StructType("a", [])().addressOfField.call(ctypes.int32_t(0)); },
+                         "StructType.prototype.addressOfField called on non-StructType CData, got ctypes.int32_t(0)");
+}
+
+if (typeof ctypes === "object")
+  test();
--- a/toolkit/components/ctypes/tests/unit/test_jsctypes.js
+++ b/toolkit/components/ctypes/tests/unit/test_jsctypes.js
@@ -252,17 +252,17 @@ function run_abstract_class_tests()
 
   // Make sure we can access 'prototype' on a CTypeProto.
   do_check_true(ctypes.CType.prototype.prototype === ctypes.CData.prototype);
 
   // Check that the shared properties and functions on ctypes.CType.prototype throw.
   do_check_throws(function() { ctypes.CType.prototype.name; }, TypeError);
   do_check_throws(function() { ctypes.CType.prototype.size; }, TypeError);
   do_check_throws(function() { ctypes.CType.prototype.ptr; }, TypeError);
-  do_check_throws(function() { ctypes.CType.prototype.array(); }, Error);
+  do_check_throws(function() { ctypes.CType.prototype.array(); }, TypeError);
 
 
   // toString and toSource are called by the web console during inspection,
   // so we don't want them to throw.
   do_check_eq(typeof ctypes.CType.prototype.toString(), 'string');
   do_check_eq(typeof ctypes.CType.prototype.toSource(), 'string');
 
   // Test that ctypes.CData is an abstract constructor that throws.
@@ -285,18 +285,18 @@ function run_abstract_class_tests()
   do_check_true(ctypes.CData.prototype.hasOwnProperty("address"));
   do_check_true(ctypes.CData.prototype.hasOwnProperty("readString"));
   do_check_true(ctypes.CData.prototype.hasOwnProperty("toString"));
   do_check_true(ctypes.CData.prototype.hasOwnProperty("toSource"));
 
   // Check that the shared properties and functions on ctypes.CData.prototype throw.
   do_check_throws(function() { ctypes.CData.prototype.value; }, TypeError);
   do_check_throws(function() { ctypes.CData.prototype.value = null; }, TypeError);
-  do_check_throws(function() { ctypes.CData.prototype.address(); }, Error);
-  do_check_throws(function() { ctypes.CData.prototype.readString(); }, Error);
+  do_check_throws(function() { ctypes.CData.prototype.address(); }, TypeError);
+  do_check_throws(function() { ctypes.CData.prototype.readString(); }, TypeError);
 
   // toString and toSource are called by the web console during inspection,
   // so we don't want them to throw.
   do_check_eq(ctypes.CData.prototype.toString(), '[CData proto object]');
   do_check_eq(ctypes.CData.prototype.toSource(), '[CData proto object]');
 }
 
 function run_Int64_tests() {
@@ -315,18 +315,18 @@ function run_Int64_tests() {
   do_check_true(ctypes.Int64.hasOwnProperty("compare"));
   do_check_true(ctypes.Int64.hasOwnProperty("lo"));
   do_check_true(ctypes.Int64.hasOwnProperty("hi"));
   do_check_true(ctypes.Int64.hasOwnProperty("join"));
   do_check_true(ctypes.Int64.prototype.hasOwnProperty("toString"));
   do_check_true(ctypes.Int64.prototype.hasOwnProperty("toSource"));
 
   // Check that the shared functions on ctypes.Int64.prototype throw.
-  do_check_throws(function() { ctypes.Int64.prototype.toString(); }, Error);
-  do_check_throws(function() { ctypes.Int64.prototype.toSource(); }, Error);
+  do_check_throws(function() { ctypes.Int64.prototype.toString(); }, TypeError);
+  do_check_throws(function() { ctypes.Int64.prototype.toSource(); }, TypeError);
 
   let i = ctypes.Int64(0);
   do_check_true(i.__proto__ === ctypes.Int64.prototype);
   do_check_true(i instanceof ctypes.Int64);
 
   // Test Int64.toString([radix]).
   do_check_eq(i.toString(), "0");
   for (let radix = 2; radix <= 36; ++radix)
@@ -489,18 +489,18 @@ function run_UInt64_tests() {
   do_check_true(ctypes.UInt64.hasOwnProperty("compare"));
   do_check_true(ctypes.UInt64.hasOwnProperty("lo"));
   do_check_true(ctypes.UInt64.hasOwnProperty("hi"));
   do_check_true(ctypes.UInt64.hasOwnProperty("join"));
   do_check_true(ctypes.UInt64.prototype.hasOwnProperty("toString"));
   do_check_true(ctypes.UInt64.prototype.hasOwnProperty("toSource"));
 
   // Check that the shared functions on ctypes.UInt64.prototype throw.
-  do_check_throws(function() { ctypes.UInt64.prototype.toString(); }, Error);
-  do_check_throws(function() { ctypes.UInt64.prototype.toSource(); }, Error);
+  do_check_throws(function() { ctypes.UInt64.prototype.toString(); }, TypeError);
+  do_check_throws(function() { ctypes.UInt64.prototype.toSource(); }, TypeError);
 
   let i = ctypes.UInt64(0);
   do_check_true(i.__proto__ === ctypes.UInt64.prototype);
   do_check_true(i instanceof ctypes.UInt64);
 
   // Test UInt64.toString([radix]).
   do_check_eq(i.toString(), "0");
   for (let radix = 2; radix <= 36; ++radix)
@@ -754,18 +754,18 @@ function run_basic_class_tests(t)
 
   do_check_true(t.prototype.__proto__ === ctypes.CData.prototype);
   do_check_true(t.prototype instanceof ctypes.CData);
   do_check_true(t.prototype.constructor === t);
 
   // Check that the shared properties and functions on 't.prototype' throw.
   do_check_throws(function() { t.prototype.value; }, TypeError);
   do_check_throws(function() { t.prototype.value = null; }, TypeError);
-  do_check_throws(function() { t.prototype.address(); }, Error);
-  do_check_throws(function() { t.prototype.readString(); }, Error);
+  do_check_throws(function() { t.prototype.address(); }, TypeError);
+  do_check_throws(function() { t.prototype.readString(); }, TypeError);
 
   // toString and toSource are called by the web console during inspection,
   // so we don't want them to throw.
   do_check_eq(t.prototype.toString(), '[CData proto object]');
   do_check_eq(t.prototype.toSource(), '[CData proto object]');
 
   // Test that an instance 'd' of 't' is a CData.
   let d = t();
@@ -1303,17 +1303,17 @@ function run_type_ctor_class_tests(c, t,
     do_check_true(c.prototype.hasOwnProperty(p));
   for (let f of fns)
     do_check_true(c.prototype.hasOwnProperty(f));
 
   // Check that the shared properties and functions on 'c.prototype' throw.
   for (let p of props)
     do_check_throws(function() { c.prototype[p]; }, TypeError);
   for (let f of fns)
-    do_check_throws(function() { c.prototype[f](); }, Error);
+    do_check_throws(function() { c.prototype[f](); }, TypeError);
 
   // Test that classes and prototypes are set up correctly on a constructed
   // type 't'.
   do_check_class(t, "CType");
   do_check_class(t.prototype, "CData");
 
   do_check_true(t.__proto__ === c.prototype);
   do_check_true(t instanceof c);
@@ -1340,27 +1340,27 @@ function run_type_ctor_class_tests(c, t,
 
   // Check that the shared properties and functions on 't.prototype.__proto__'
   // (and thus also 't.prototype') throw.
   for (let p of instanceProps) {
     do_check_throws(function() { t.prototype.__proto__[p]; }, TypeError);
     do_check_throws(function() { t.prototype[p]; }, TypeError);
   }
   for (let f of instanceFns) {
-    do_check_throws(function() { t.prototype.__proto__[f]() }, Error);
-    do_check_throws(function() { t.prototype[f]() }, Error);
+    do_check_throws(function() { t.prototype.__proto__[f]() }, TypeError);
+    do_check_throws(function() { t.prototype[f]() }, TypeError);
   }
 
   // Check that 't.prototype' has the correct special properties.
   for (let p of specialProps)
     do_check_true(t.prototype.hasOwnProperty(p));
 
   // Check that the shared special properties on 't.prototype' throw.
   for (let p of specialProps)
-    do_check_throws(function() { t.prototype[p]; }, Error);
+    do_check_throws(function() { t.prototype[p]; }, TypeError);
 
   // Make sure we can access 'prototype' on a CTypeProto.
   if (t instanceof ctypes.FunctionType)
     do_check_true(Object.getPrototypeOf(c.prototype.prototype) === ctypes.PointerType.prototype.prototype);
   else
     do_check_true(Object.getPrototypeOf(c.prototype.prototype) === ctypes.CType.prototype.prototype);
 
   // Test that an instance 'd' of 't' is a CData.