Bug 891107 - Part 4: Show information about value in overflow error messages in js-ctypes. r=jorendorff
authorTooru Fujisawa <arai_a@mac.com>
Fri, 07 Aug 2015 06:48:10 +0900
changeset 288486 9f74c36a3e7a7d75e1261bdb4903b9fd44fcec93
parent 288485 4c1a64f8996abfcc7d32d32202c6def4d13ec5bd
child 288487 fc5171ce2a4b96c9f8b4a8f3c3a2c7074cd440c9
push id73426
push userarai_a@mac.com
push dateSun, 13 Mar 2016 01:35:37 +0000
treeherdermozilla-inbound@dec8c7173f68 [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 4: Show information about value in overflow error messages in js-ctypes. r=jorendorff
js/src/ctypes/CTypes.cpp
js/src/ctypes/ctypes.msg
js/src/jit-test/tests/ctypes/conversion-array.js
js/src/jit-test/tests/ctypes/conversion-int64.js
js/src/jit-test/tests/ctypes/conversion-primitive.js
js/src/jit-test/tests/ctypes/size-overflow-array.js
js/src/jit-test/tests/ctypes/size-overflow-struct.js
toolkit/components/ctypes/tests/unit/test_jsctypes.js
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -1471,28 +1471,49 @@ PropNameNonStringError(JSContext* cx, Ha
   }
 
   JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
                        CTYPESMSG_PROP_NONSTRING, propStr, valStr, posStr);
   return false;
 }
 
 static bool
+SizeOverflow(JSContext* cx, const char* name, const char* limit)
+{
+  JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
+                       CTYPESMSG_SIZE_OVERFLOW, name, limit);
+  return false;
+}
+
+static bool
 TypeError(JSContext* cx, const char* expected, HandleValue actual)
 {
   JSAutoByteString bytes;
   const char* src = CTypesToSourceForError(cx, actual, bytes);
   if (!src)
     return false;
 
   JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
                        CTYPESMSG_TYPE_ERROR, expected, src);
   return false;
 }
 
+static bool
+TypeOverflow(JSContext* cx, const char* expected, HandleValue actual)
+{
+  JSAutoByteString valBytes;
+  const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
+  if (!valStr)
+    return false;
+
+  JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
+                       CTYPESMSG_TYPE_OVERFLOW, valStr, expected);
+  return false;
+}
+
 static JSObject*
 InitCTypeClass(JSContext* cx, HandleObject ctypesObj)
 {
   JSFunction* fun = JS_DefineFunction(cx, ctypesObj, "CType", ConstructAbstract, 0,
                                       CTYPESCTOR_FLAGS);
   if (!fun)
     return nullptr;
 
@@ -2384,17 +2405,18 @@ jsvalToFloat(JSContext* cx, Value val, F
   }
   // Don't silently convert true to 1.0 or false to 0.0, even though C/C++
   // does it. It's likely to be a mistake.
   return false;
 }
 
 template <class IntegerType, class CharT>
 static bool
-StringToInteger(JSContext* cx, CharT* cp, size_t length, IntegerType* result)
+StringToInteger(JSContext* cx, CharT* cp, size_t length, IntegerType* result,
+                bool* overflow)
 {
   JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact);
 
   const CharT* end = cp + length;
   if (cp == end)
     return false;
 
   IntegerType sign = 1;
@@ -2424,48 +2446,54 @@ StringToInteger(JSContext* cx, CharT* cp
       c = c - 'a' + 10;
     else if (base == 16 && c >= 'A' && c <= 'F')
       c = c - 'A' + 10;
     else
       return false;
 
     IntegerType ii = i;
     i = ii * base + sign * c;
-    if (i / base != ii) // overflow
+    if (i / base != ii) {
+      *overflow = true;
       return false;
+    }
   }
 
   *result = i;
   return true;
 }
 
 template<class IntegerType>
 static bool
-StringToInteger(JSContext* cx, JSString* string, IntegerType* result)
+StringToInteger(JSContext* cx, JSString* string, IntegerType* result,
+                bool* overflow)
 {
   JSLinearString* linear = string->ensureLinear(cx);
   if (!linear)
     return false;
 
   AutoCheckCannotGC nogc;
   size_t length = linear->length();
   return string->hasLatin1Chars()
-         ? StringToInteger<IntegerType>(cx, linear->latin1Chars(nogc), length, result)
-         : StringToInteger<IntegerType>(cx, linear->twoByteChars(nogc), length, result);
+         ? StringToInteger<IntegerType>(cx, linear->latin1Chars(nogc), length,
+                                        result, overflow)
+         : StringToInteger<IntegerType>(cx, linear->twoByteChars(nogc), length,
+                                        result, overflow);
 }
 
 // Implicitly convert val to IntegerType, allowing int, double,
 // Int64, UInt64, and optionally a decimal or hexadecimal string argument.
 // (This is common code shared by jsvalToSize and the Int64/UInt64 constructors.)
 template<class IntegerType>
 static bool
 jsvalToBigInteger(JSContext* cx,
                   Value val,
                   bool allowString,
-                  IntegerType* result)
+                  IntegerType* result,
+                  bool* overflow)
 {
   JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact);
 
   if (val.isInt32()) {
     // Make sure the integer fits in the alotted precision, and has the right
     // sign.
     int32_t i = val.toInt32();
     return ConvertExact(i, result);
@@ -2476,17 +2504,17 @@ jsvalToBigInteger(JSContext* cx,
     double d = val.toDouble();
     return ConvertExact(d, result);
   }
   if (allowString && val.isString()) {
     // Allow conversion from base-10 or base-16 strings, provided the result
     // fits in IntegerType. (This allows an Int64 or UInt64 object to be passed
     // to the JS array element operator, which will automatically call
     // toString() on the object for us.)
-    return StringToInteger(cx, val.toString(), result);
+    return StringToInteger(cx, val.toString(), result, overflow);
   }
   if (val.isObject()) {
     // Allow conversion from an Int64 or UInt64 object directly.
     JSObject* obj = &val.toObject();
 
     if (UInt64::IsUInt64(obj)) {
       // Make sure the integer fits in IntegerType.
       uint64_t i = Int64Base::GetInt(obj);
@@ -2499,29 +2527,30 @@ jsvalToBigInteger(JSContext* cx,
       return ConvertExact(i, result);
     }
 
     if (CDataFinalizer::IsCDataFinalizer(obj)) {
       RootedValue innerData(cx);
       if (!CDataFinalizer::GetValue(cx, obj, &innerData)) {
         return false; // Nothing to convert
       }
-      return jsvalToBigInteger(cx, innerData, allowString, result);
+      return jsvalToBigInteger(cx, innerData, allowString, result, overflow);
     }
 
   }
   return false;
 }
 
 // Implicitly convert val to a size value, where the size value is represented
 // by size_t but must also fit in a double.
 static bool
 jsvalToSize(JSContext* cx, Value val, bool allowString, size_t* result)
 {
-  if (!jsvalToBigInteger(cx, val, allowString, result))
+  bool dummy;
+  if (!jsvalToBigInteger(cx, val, allowString, result, &dummy))
     return false;
 
   // Also check that the result fits in a double.
   return Convert<size_t>(double(*result)) == *result;
 }
 
 // Implicitly convert val to IntegerType, allowing int, double,
 // Int64, UInt64, and optionally a decimal or hexadecimal string argument.
@@ -2541,17 +2570,18 @@ jsidToBigInteger(JSContext* cx,
     int32_t i = JSID_TO_INT(val);
     return ConvertExact(i, result);
   }
   if (allowString && JSID_IS_STRING(val)) {
     // Allow conversion from base-10 or base-16 strings, provided the result
     // fits in IntegerType. (This allows an Int64 or UInt64 object to be passed
     // to the JS array element operator, which will automatically call
     // toString() on the object for us.)
-    return StringToInteger(cx, JSID_TO_STRING(val), result);
+    bool dummy;
+    return StringToInteger(cx, JSID_TO_STRING(val), result, &dummy);
   }
   return false;
 }
 
 // Implicitly convert val to a size value, where the size value is represented
 // by size_t but must also fit in a double.
 static bool
 jsidToSize(JSContext* cx, jsid val, bool allowString, size_t* result)
@@ -2564,17 +2594,16 @@ jsidToSize(JSContext* cx, jsid val, bool
 }
 
 // Implicitly convert a size value to a Value, ensuring that the size_t value
 // fits in a double.
 static bool
 SizeTojsval(JSContext* cx, size_t size, MutableHandleValue result)
 {
   if (Convert<size_t>(double(size)) != size) {
-    JS_ReportError(cx, "size overflow");
     return false;
   }
 
   result.setNumber(double(size));
   return true;
 }
 
 // Forcefully convert val to IntegerType when explicitly requested.
@@ -3387,20 +3416,25 @@ ExplicitConvert(JSContext* cx, HandleVal
     *static_cast<bool*>(buffer) = ToBoolean(val);
     break;
   }
 #define INTEGRAL_CASE(name, type, ffiType)                                     \
   case TYPE_##name: {                                                          \
     /* Convert numeric values with a C-style cast, and */                      \
     /* allow conversion from a base-10 or base-16 string. */                   \
     type result;                                                               \
+    bool overflow = false;                                                     \
     if (!jsvalToIntegerExplicit(val, &result) &&                               \
         (!val.isString() ||                                                    \
-         !StringToInteger(cx, val.toString(), &result)))                       \
+         !StringToInteger(cx, val.toString(), &result, &overflow))) {          \
+      if (overflow) {                                                          \
+        return TypeOverflow(cx, #name, val);                                   \
+      }                                                                        \
       return ConvError(cx, #name, val, convType);                              \
+    }                                                                          \
     *static_cast<type*>(buffer) = result;                                      \
     break;                                                                     \
   }
   CTYPES_FOR_EACH_INT_TYPE(INTEGRAL_CASE)
   CTYPES_FOR_EACH_WRAPPED_INT_TYPE(INTEGRAL_CASE)
   CTYPES_FOR_EACH_CHAR_TYPE(INTEGRAL_CASE)
   CTYPES_FOR_EACH_CHAR16_TYPE(INTEGRAL_CASE)
 #undef INTEGRAL_CASE
@@ -4958,22 +4992,27 @@ ArrayType::CreateInternal(JSContext* cx,
   }
 
   RootedValue sizeVal(cx, JS::UndefinedValue());
   RootedValue lengthVal(cx, JS::UndefinedValue());
   if (lengthDefined) {
     // Check for overflow, and convert to an int or double as required.
     size_t size = length * baseSize;
     if (length > 0 && size / length != baseSize) {
-      JS_ReportError(cx, "size overflow");
+      SizeOverflow(cx, "array size", "size_t");
       return nullptr;
     }
-    if (!SizeTojsval(cx, size, &sizeVal) ||
-        !SizeTojsval(cx, length, &lengthVal))
+    if (!SizeTojsval(cx, size, &sizeVal)) {
+      SizeOverflow(cx, "array size", "JavaScript number");
       return nullptr;
+    }
+    if (!SizeTojsval(cx, length, &lengthVal)) {
+      SizeOverflow(cx, "array length", "JavaScript number");
+      return nullptr;
+    }
   }
 
   size_t align = CType::GetAlignment(baseType);
 
   // Create a new CType object with the common properties and slots.
   JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_array, nullptr,
                         sizeVal, Int32Value(align), nullptr);
   if (!typeObj)
@@ -5253,18 +5292,19 @@ ArrayType::Getter(JSContext* cx, HandleO
 
   // Convert the index to a size_t and bounds-check it.
   size_t index;
   size_t length = GetLength(typeObj);
   bool ok = jsidToSize(cx, idval, true, &index);
   int32_t dummy;
   if (!ok && JSID_IS_SYMBOL(idval))
     return true;
+  bool dummy2;
   if (!ok && JSID_IS_STRING(idval) &&
-      !StringToInteger(cx, JSID_TO_STRING(idval), &dummy)) {
+      !StringToInteger(cx, JSID_TO_STRING(idval), &dummy, &dummy2)) {
     // String either isn't a number, or doesn't fit in size_t.
     // Chances are it's a regular property lookup, so return.
     return true;
   }
   if (!ok || index >= length) {
     JS_ReportError(cx, "invalid index");
     return false;
   }
@@ -5293,18 +5333,19 @@ ArrayType::Setter(JSContext* cx, HandleO
 
   // Convert the index to a size_t and bounds-check it.
   size_t index;
   size_t length = GetLength(typeObj);
   bool ok = jsidToSize(cx, idval, true, &index);
   int32_t dummy;
   if (!ok && JSID_IS_SYMBOL(idval))
     return true;
+  bool dummy2;
   if (!ok && JSID_IS_STRING(idval) &&
-      !StringToInteger(cx, JSID_TO_STRING(idval), &dummy)) {
+      !StringToInteger(cx, JSID_TO_STRING(idval), &dummy, &dummy2)) {
     // String either isn't a number, or doesn't fit in size_t.
     // Chances are it's a regular property lookup, so return.
     return result.succeed();
   }
   if (!ok || index >= length) {
     JS_ReportError(cx, "invalid index");
     return false;
   }
@@ -5585,17 +5626,17 @@ StructType::DefineInternal(JSContext* cx
 
       size_t fieldSize = CType::GetSize(fieldType);
       size_t fieldAlign = CType::GetAlignment(fieldType);
       size_t fieldOffset = Align(structSize, fieldAlign);
       // Check for overflow. Since we hold invariant that fieldSize % fieldAlign
       // be zero, we can safely check fieldOffset + fieldSize without first
       // checking fieldOffset for overflow.
       if (fieldOffset + fieldSize < structSize) {
-        JS_ReportError(cx, "size overflow");
+        SizeOverflow(cx, "struct size", "size_t");
         return false;
       }
 
       // Add field name to the hash
       FieldInfo info;
       info.mType = fieldType;
       info.mIndex = i;
       info.mOffset = fieldOffset;
@@ -5608,33 +5649,35 @@ StructType::DefineInternal(JSContext* cx
 
       if (fieldAlign > structAlign)
         structAlign = fieldAlign;
     }
 
     // Pad the struct tail according to struct alignment.
     size_t structTail = Align(structSize, structAlign);
     if (structTail < structSize) {
-      JS_ReportError(cx, "size overflow");
+      SizeOverflow(cx, "struct size", "size_t");
       return false;
     }
     structSize = structTail;
 
   } else {
     // Empty structs are illegal in C, but are legal and have a size of
     // 1 byte in C++. We're going to allow them, and trick libffi into
     // believing this by adding a char member. The resulting struct will have
     // no getters or setters, and will be initialized to zero.
     structSize = 1;
     structAlign = 1;
   }
 
   RootedValue sizeVal(cx);
-  if (!SizeTojsval(cx, structSize, &sizeVal))
-    return false;
+  if (!SizeTojsval(cx, structSize, &sizeVal)) {
+    SizeOverflow(cx, "struct size", "double");
+    return false;
+  }
 
   // Move the field hash to the heap and store it in the typeObj.
   FieldInfoHash *heapHash = cx->new_<FieldInfoHash>(mozilla::Move(fields.get()));
   if (!heapHash) {
     JS_ReportOutOfMemory(cx);
     return false;
   }
   MOZ_ASSERT(heapHash->initialized());
@@ -8195,17 +8238,21 @@ Int64::Construct(JSContext* cx,
   CallArgs args = CallArgsFromVp(argc, vp);
 
   // Construct and return a new Int64 object.
   if (args.length() != 1) {
     return ArgumentLengthError(cx, "Int64 constructor", "one", "");
   }
 
   int64_t i = 0;
-  if (!jsvalToBigInteger(cx, args[0], true, &i)) {
+  bool overflow = false;
+  if (!jsvalToBigInteger(cx, args[0], true, &i, &overflow)) {
+    if (overflow) {
+      return TypeOverflow(cx, "int64", args[0]);
+    }
     return ArgumentConvError(cx, args[0], "Int64", 0);
   }
 
   // Get ctypes.Int64.prototype from the 'prototype' property of the ctor.
   RootedValue slot(cx);
   RootedObject callee(cx, &args.callee());
   ASSERT_OK(JS_GetProperty(cx, callee, "prototype", &slot));
   RootedObject proto(cx, slot.toObjectOrNull());
@@ -8367,17 +8414,21 @@ UInt64::Construct(JSContext* cx,
   CallArgs args = CallArgsFromVp(argc, vp);
 
   // Construct and return a new UInt64 object.
   if (args.length() != 1) {
     return ArgumentLengthError(cx, "UInt64 constructor", "one", "");
   }
 
   uint64_t u = 0;
-  if (!jsvalToBigInteger(cx, args[0], true, &u)) {
+  bool overflow = false;
+  if (!jsvalToBigInteger(cx, args[0], true, &u, &overflow)) {
+    if (overflow) {
+      return TypeOverflow(cx, "uint64", args[0]);
+    }
     return ArgumentConvError(cx, args[0], "UInt64", 0);
   }
 
   // Get ctypes.UInt64.prototype from the 'prototype' property of the ctor.
   RootedValue slot(cx);
   RootedObject callee(cx, &args.callee());
   ASSERT_OK(JS_GetProperty(cx, callee, "prototype", &slot));
   RootedObject proto(cx, &slot.toObject());
--- a/js/src/ctypes/ctypes.msg
+++ b/js/src/ctypes/ctypes.msg
@@ -18,22 +18,26 @@ MSG_DEF(CTYPESMSG_CONV_ERROR_FIN,2, JSEX
 MSG_DEF(CTYPESMSG_CONV_ERROR_RET,2, JSEXN_TYPEERR, "can't convert {0} to the return type of {1}")
 MSG_DEF(CTYPESMSG_CONV_ERROR_SET,2, JSEXN_TYPEERR, "can't convert {0} to the type {1}")
 MSG_DEF(CTYPESMSG_CONV_ERROR_STRUCT,5, JSEXN_TYPEERR, "can't convert {0} to the '{1}' field ({2}) of {3}{4}")
 MSG_DEF(CTYPESMSG_NON_PRIMITIVE, 1, JSEXN_TYPEERR, ".value only works on character and numeric types, not `{0}`")
 MSG_DEF(CTYPESMSG_TYPE_ERROR,    2, JSEXN_TYPEERR, "expected {0}, got {1}")
 
 /* array */
 MSG_DEF(CTYPESMSG_ARRAY_MISMATCH,4, JSEXN_TYPEERR, "length of {0} does not match to the length of the type {1} (expected {2}, got {3})")
-MSG_DEF(CTYPESMSG_ARRAY_OVERFLOW,4, JSEXN_TYPEERR, "length of {0} does not fit to the length of the type {1} (expected {2} or lower, got {3})")
+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_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_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}")
--- a/js/src/jit-test/tests/ctypes/conversion-array.js
+++ b/js/src/jit-test/tests/ctypes/conversion-array.js
@@ -4,19 +4,19 @@ load(libdir + 'asserts.js');
 
 function test() {
   // constructor
   assertTypeErrorMessage(() => { ctypes.int32_t.array()("foo"); },
                          "can't convert the string \"foo\" to the type ctypes.int32_t.array()");
   assertTypeErrorMessage(() => { ctypes.int32_t.array(10)("foo"); },
                          "can't convert the string \"foo\" to the type ctypes.int32_t.array(10)");
   assertTypeErrorMessage(() => { ctypes.char.array(2)("foo"); },
-                         "length of the string \"foo\" does not fit to the length of the type ctypes.char.array(2) (expected 2 or lower, got 3)");
+                         "length of the string \"foo\" does not fit in the length of the type ctypes.char.array(2) (expected 2 or lower, got 3)");
   assertTypeErrorMessage(() => { ctypes.char16_t.array(2)("foo"); },
-                         "length of the string \"foo\" does not fit to the length of the type ctypes.char16_t.array(2) (expected 2 or lower, got 3)");
+                         "length of the string \"foo\" does not fit in the length of the type ctypes.char16_t.array(2) (expected 2 or lower, got 3)");
   assertTypeErrorMessage(() => { ctypes.int8_t.array(2)(new ArrayBuffer(8)); },
                          "length of the array buffer ({}) does not match to the length of the type ctypes.int8_t.array(2) (expected 2, got 8)");
   assertTypeErrorMessage(() => { ctypes.int8_t.array(2)(new Int8Array(8)); },
                          "length of the typed array ({0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0}) does not match to the length of the type ctypes.int8_t.array(2) (expected 2, got 8)");
 
   // elem setter
   assertTypeErrorMessage(() => { ctypes.int32_t.array(10)()[0] = "foo"; },
                          "can't convert the string \"foo\" to element 0 of the type ctypes.int32_t.array(10)");
--- a/js/src/jit-test/tests/ctypes/conversion-int64.js
+++ b/js/src/jit-test/tests/ctypes/conversion-int64.js
@@ -1,20 +1,20 @@
 load(libdir + 'asserts.js');
 
 function test() {
-  assertTypeErrorMessage(() => { ctypes.Int64("0xfffffffffffffffffffffff"); },
-                         "can't pass the string \"0xfffffffffffffffffffffff\" to argument 1 of Int64");
+  assertRangeErrorMessage(() => { ctypes.Int64("0xfffffffffffffffffffffff"); },
+                          "the string \"0xfffffffffffffffffffffff\" does not fit in the type int64");
   assertTypeErrorMessage(() => { ctypes.Int64.join("foo", 0); },
                          "can't pass the string \"foo\" to argument 1 of Int64.join");
   assertTypeErrorMessage(() => { ctypes.Int64.join(0, "foo"); },
                          "can't pass the string \"foo\" to argument 2 of Int64.join");
 
-  assertTypeErrorMessage(() => { ctypes.UInt64("0xfffffffffffffffffffffff"); },
-                         "can't pass the string \"0xfffffffffffffffffffffff\" to argument 1 of UInt64");
+  assertRangeErrorMessage(() => { ctypes.UInt64("0xfffffffffffffffffffffff"); },
+                          "the string \"0xfffffffffffffffffffffff\" does not fit in the type uint64");
   assertTypeErrorMessage(() => { ctypes.UInt64.join("foo", 0); },
                          "can't pass the string \"foo\" to argument 1 of UInt64.join");
   assertTypeErrorMessage(() => { ctypes.UInt64.join(0, "foo"); },
                          "can't pass the string \"foo\" to argument 2 of UInt64.join");
 }
 
 if (typeof ctypes === "object")
   test();
--- a/js/src/jit-test/tests/ctypes/conversion-primitive.js
+++ b/js/src/jit-test/tests/ctypes/conversion-primitive.js
@@ -13,18 +13,18 @@ function test() {
   assertTypeErrorMessage(() => { ctypes.int32_t({}); },
                          "can't convert the object ({}) to the type int32_t");
   assertTypeErrorMessage(() => { ctypes.int32_t([]); },
                          "can't convert the array [] to the type int32_t");
   assertTypeErrorMessage(() => { ctypes.int32_t(new Int8Array([])); },
                          "can't convert the typed array ({}) to the type int32_t");
   assertTypeErrorMessage(() => { ctypes.int32_t(ctypes.int32_t); },
                          "can't convert ctypes.int32_t to the type int32_t");
-  assertTypeErrorMessage(() => { ctypes.int32_t("0xfffffffffffffffffffffff"); },
-                         "can't convert the string \"0xfffffffffffffffffffffff\" to the type int32_t");
+  assertRangeErrorMessage(() => { ctypes.int32_t("0xfffffffffffffffffffffff"); },
+                          "the string \"0xfffffffffffffffffffffff\" does not fit in the type int32_t");
   if (typeof Symbol === "function") {
     assertTypeErrorMessage(() => { ctypes.int32_t(Symbol.iterator); },
                            "can't convert Symbol.iterator to the type int32_t");
     assertTypeErrorMessage(() => { ctypes.int32_t(Symbol("foo")); },
                            "can't convert Symbol(\"foo\") to the type int32_t");
   }
 
   // value setter
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ctypes/size-overflow-array.js
@@ -0,0 +1,18 @@
+load(libdir + 'asserts.js');
+
+function test() {
+  if (getBuildConfiguration()["pointer-byte-size"] == 4) {
+    let big_array = ctypes.int32_t.array(0xfffffff);
+    assertRangeErrorMessage(() => { big_array.array(0xfffffff); },
+                            "array size does not fit in size_t");
+  } else if (getBuildConfiguration()["pointer-byte-size"] == 8) {
+    let big_array = ctypes.int32_t.array(0xfffffff);
+    assertRangeErrorMessage(() => { big_array.array(0xfffffff); },
+                            "array size does not fit in JavaScript number");
+    assertRangeErrorMessage(() => { big_array.array(0xfffffffff); },
+                            "array size does not fit in size_t");
+  }
+}
+
+if (typeof ctypes === "object")
+  test();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ctypes/size-overflow-struct.js
@@ -0,0 +1,24 @@
+load(libdir + 'asserts.js');
+
+function test() {
+  if (getBuildConfiguration()["pointer-byte-size"] == 4) {
+    let big_array = ctypes.int32_t.array(0xfffffff);
+    assertRangeErrorMessage(() => { ctypes.StructType("x", [{a: big_array},
+                                                            {b: big_array},
+                                                            {c: big_array},
+                                                            {d: big_array},
+                                                            {e: big_array}]); },
+                            "struct size does not fit in size_t");
+  } else if (getBuildConfiguration()["pointer-byte-size"] == 8) {
+    let big_array = ctypes.int32_t.array(0xfffffffffffffff);
+    assertRangeErrorMessage(() => { ctypes.StructType("x", [{a: big_array},
+                                                            {b: big_array},
+                                                            {c: big_array},
+                                                            {d: big_array},
+                                                            {e: big_array}]); },
+                            "struct size does not fit in size_t");
+  }
+}
+
+if (typeof ctypes === "object")
+  test();
--- a/toolkit/components/ctypes/tests/unit/test_jsctypes.js
+++ b/toolkit/components/ctypes/tests/unit/test_jsctypes.js
@@ -424,25 +424,28 @@ function run_Int64_tests() {
 
   i = ctypes.Int64(ctypes.UInt64("0x7fffffffffffffff"));
   do_check_eq(i.toString(), i.toString(10));
   do_check_eq(i.toString(10), "9223372036854775807");
   do_check_eq(i.toString(16), "7fffffffffffffff");
   do_check_eq(i.toString(2), "111111111111111111111111111111111111111111111111111111111111111");
 
   let vals = [-0x8000000000001000, 0x8000000000000000,
-              "-0x8000000000000001", "0x8000000000000000",
               ctypes.UInt64("0x8000000000000000"),
               Infinity, -Infinity, NaN, 0.1,
               5.68e21, null, undefined, "", {}, [], new Number(16),
               {toString: function () { return 7; }},
               {valueOf: function () { return 7; }}];
   for (let i = 0; i < vals.length; i++)
     do_check_throws(function () { ctypes.Int64(vals[i]); }, TypeError);
 
+  vals = ["-0x8000000000000001", "0x8000000000000000"];
+  for (let i = 0; i < vals.length; i++)
+    do_check_throws(function () { ctypes.Int64(vals[i]); }, RangeError);
+
   // Test ctypes.Int64.compare.
   do_check_eq(ctypes.Int64.compare(ctypes.Int64(5), ctypes.Int64(5)), 0);
   do_check_eq(ctypes.Int64.compare(ctypes.Int64(5), ctypes.Int64(4)), 1);
   do_check_eq(ctypes.Int64.compare(ctypes.Int64(4), ctypes.Int64(5)), -1);
   do_check_eq(ctypes.Int64.compare(ctypes.Int64(-5), ctypes.Int64(-5)), 0);
   do_check_eq(ctypes.Int64.compare(ctypes.Int64(-5), ctypes.Int64(-4)), -1);
   do_check_eq(ctypes.Int64.compare(ctypes.Int64(-4), ctypes.Int64(-5)), 1);
   do_check_throws(function() { ctypes.Int64.compare(ctypes.Int64(4), ctypes.UInt64(4)); }, TypeError);
@@ -575,24 +578,28 @@ function run_UInt64_tests() {
   do_check_eq(i.toString(), "0");
 
   i = ctypes.UInt64(ctypes.Int64("0x7fffffffffffffff"));
   do_check_eq(i.toString(), i.toString(10));
   do_check_eq(i.toString(10), "9223372036854775807");
   do_check_eq(i.toString(16), "7fffffffffffffff");
   do_check_eq(i.toString(2), "111111111111111111111111111111111111111111111111111111111111111");
 
-  let vals = [-1, 0x10000000000000000, "-1", "-0x1", "0x10000000000000000",
+  let vals = [-1, 0x10000000000000000, "-1", "-0x1",
               ctypes.Int64("-1"), Infinity, -Infinity, NaN, 0.1,
               5.68e21, null, undefined, "", {}, [], new Number(16),
               {toString: function () { return 7; }},
               {valueOf: function () { return 7; }}];
   for (let i = 0; i < vals.length; i++)
     do_check_throws(function () { ctypes.UInt64(vals[i]); }, TypeError);
 
+  vals = ["0x10000000000000000"];
+  for (let i = 0; i < vals.length; i++)
+    do_check_throws(function () { ctypes.UInt64(vals[i]); }, RangeError);
+
   // Test ctypes.UInt64.compare.
   do_check_eq(ctypes.UInt64.compare(ctypes.UInt64(5), ctypes.UInt64(5)), 0);
   do_check_eq(ctypes.UInt64.compare(ctypes.UInt64(5), ctypes.UInt64(4)), 1);
   do_check_eq(ctypes.UInt64.compare(ctypes.UInt64(4), ctypes.UInt64(5)), -1);
   do_check_throws(function() { ctypes.UInt64.compare(ctypes.UInt64(4), ctypes.Int64(4)); }, TypeError);
   do_check_throws(function() { ctypes.UInt64.compare(4, 5); }, TypeError);
 
   // Test ctypes.UInt64.{lo,hi}.
@@ -1490,63 +1497,63 @@ function run_StructType_tests() {
   // are OK.
   if (ctypes.size_t.size == 4) {
     // Test 1: overflow struct size + field padding + field size.
     let large_t = ctypes.StructType("large_t",
         [{"a": ctypes.int8_t.array(0xffffffff)}]);
     do_check_eq(large_t.size, 0xffffffff);
     do_check_throws(function() {
       ctypes.StructType("large_t", [{"a": large_t}, {"b": ctypes.int8_t}]);
-    }, Error);
+    }, RangeError);
 
     // Test 2: overflow struct size + struct tail padding.
     // To do this, we use a struct with maximum size and alignment 2.
     large_t = ctypes.StructType("large_t",
       [{"a": ctypes.int16_t.array(0xfffffffe / 2)}]);
     do_check_eq(large_t.size, 0xfffffffe);
     do_check_throws(function() {
       ctypes.StructType("large_t", [{"a": large_t}, {"b": ctypes.int8_t}]);
-    }, Error);
+    }, RangeError);
 
   } else {
     // Test 1: overflow struct size when converting from size_t to jsdouble.
     let large_t = ctypes.StructType("large_t",
         [{"a": ctypes.int8_t.array(0xfffffffffffff800)}]);
     do_check_eq(large_t.size, 0xfffffffffffff800);
     do_check_throws(function() {
       ctypes.StructType("large_t", [{"a": large_t}, {"b": ctypes.int8_t}]);
-    }, Error);
+    }, RangeError);
     let small_t = ctypes.int8_t.array(0x400);
     do_check_throws(function() {
       ctypes.StructType("large_t", [{"a": large_t}, {"b": small_t}]);
-    }, Error);
+    }, RangeError);
 
     large_t = ctypes.StructType("large_t",
       [{"a": ctypes.int8_t.array(0x1fffffffffffff)}]);
     do_check_eq(large_t.size, 0x1fffffffffffff);
     do_check_throws(function() {
       ctypes.StructType("large_t", [{"a": large_t.array(2)}, {"b": ctypes.int8_t}]);
-    }, Error);
+    }, RangeError);
 
     // Test 2: overflow struct size + field padding + field size.
     large_t = ctypes.int8_t.array(0xfffffffffffff800);
     small_t = ctypes.int8_t.array(0x800);
     do_check_throws(function() {
       ctypes.StructType("large_t", [{"a": large_t}, {"b": small_t}]);
-    }, Error);
+    }, RangeError);
 
     // Test 3: overflow struct size + struct tail padding.
     // To do this, we use a struct with maximum size and alignment 2.
     large_t = ctypes.StructType("large_t",
       [{"a": ctypes.int16_t.array(0xfffffffffffff000 / 2)}]);
     do_check_eq(large_t.size, 0xfffffffffffff000);
     small_t = ctypes.int8_t.array(0xfff);
     do_check_throws(function() {
       ctypes.StructType("large_t", [{"a": large_t}, {"b": small_t}]);
-    }, Error);
+    }, RangeError);
   }
 
   let g = g_t();
   do_check_eq(g.a, 0);
   do_check_eq(g.b, 0);
   g = new g_t(1, 2);
   do_check_eq(g.a, 1);
   do_check_eq(g.b, 2);
@@ -2044,31 +2051,31 @@ function run_ArrayType_tests() {
   // Check that array size bounds work, and that large, but not illegal, sizes
   // are OK.
   if (ctypes.size_t.size == 4) {
     do_check_throws(function() {
       ctypes.ArrayType(ctypes.int8_t, 0x100000000);
     }, TypeError);
     do_check_throws(function() {
       ctypes.ArrayType(ctypes.int16_t, 0x80000000);
-    }, Error);
+    }, RangeError);
 
     let large_t = ctypes.int8_t.array(0x80000000);
-    do_check_throws(function() { large_t.array(2); }, Error);
+    do_check_throws(function() { large_t.array(2); }, RangeError);
 
   } else {
     do_check_throws(function() {
       ctypes.ArrayType(ctypes.int8_t, ctypes.UInt64("0xffffffffffffffff"));
     }, TypeError);
     do_check_throws(function() {
       ctypes.ArrayType(ctypes.int16_t, ctypes.UInt64("0x8000000000000000"));
-    }, Error);
+    }, RangeError);
 
     let large_t = ctypes.int8_t.array(0x8000000000000000);
-    do_check_throws(function() { large_t.array(2); }, Error);
+    do_check_throws(function() { large_t.array(2); }, RangeError);
   }
 
   // Test that arrays ImplicitConvert to pointers.
   let b = ctypes.int32_t.array(10)();
   let p = ctypes.int32_t.ptr();
   p.value = b;
   do_check_eq(ptrValue(b.addressOfElement(0)), ptrValue(p));
   p = ctypes.voidptr_t();