Bug 891107 - Part 5: Show function name in this and callee type error messages in js-ctypes. r=jorendorff
authorTooru Fujisawa <arai_a@mac.com>
Fri, 07 Aug 2015 06:53:29 +0900
changeset 339829 fc5171ce2a4b96c9f8b4a8f3c3a2c7074cd440c9
parent 339828 9f74c36a3e7a7d75e1261bdb4903b9fd44fcec93
child 339830 daa58aad68585df2ae33b2432d17a5296cb150da
push id12803
push userjbeich@FreeBSD.org
push dateSun, 13 Mar 2016 09:48:54 +0000
reviewersjorendorff
bugs891107
milestone48.0a1
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.