diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index 552602aa1eb8..a376f9963945 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -1494,6 +1494,40 @@ IncompatibleThisType(JSContext* cx, const char* funName, const char* actualType, return false; } +static bool +InvalidIndexError(JSContext* cx, HandleValue val) +{ + JSAutoByteString idBytes; + const char* indexStr = CTypesToSourceForError(cx, val, idBytes); + if (!indexStr) + return false; + + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, + CTYPESMSG_INVALID_INDEX, indexStr); + return false; +} + +static bool +InvalidIndexError(JSContext* cx, HandleId id) +{ + RootedValue idVal(cx, IdToValue(id)); + return InvalidIndexError(cx, idVal); +} + +static bool +InvalidIndexRangeError(JSContext* cx, size_t index, size_t length) +{ + char indexStr[16]; + JS_snprintf(indexStr, 16, "%u", index); + + char lengthStr[16]; + JS_snprintf(lengthStr, 16, "%u", length); + + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, + CTYPESMSG_INVALID_RANGE, indexStr, lengthStr); + return false; +} + static bool NonPrimitiveError(JSContext* cx, HandleObject typeObj) { @@ -5376,9 +5410,11 @@ ArrayType::Getter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandle // Chances are it's a regular property lookup, so return. return true; } - if (!ok || index >= length) { - JS_ReportError(cx, "invalid index"); - return false; + if (!ok) { + return InvalidIndexError(cx, idval); + } + if (index >= length) { + return InvalidIndexRangeError(cx, index, length); } RootedObject baseType(cx, GetBaseType(typeObj)); @@ -5417,9 +5453,11 @@ ArrayType::Setter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandle // Chances are it's a regular property lookup, so return. return result.succeed(); } - if (!ok || index >= length) { - JS_ReportError(cx, "invalid index"); - return false; + if (!ok) { + return InvalidIndexError(cx, idval); + } + if (index >= length) { + return InvalidIndexRangeError(cx, index, length); } RootedObject baseType(cx, GetBaseType(typeObj)); @@ -5469,10 +5507,11 @@ ArrayType::AddressOfElement(JSContext* cx, unsigned argc, Value* vp) // Convert the index to a size_t and bounds-check it. size_t index; size_t length = GetLength(typeObj); - if (!jsvalToSize(cx, args[0], false, &index) || - index >= length) { - JS_ReportError(cx, "invalid index"); - return false; + if (!jsvalToSize(cx, args[0], false, &index)) { + return InvalidIndexError(cx, args[0]); + } + if (index >= length) { + return InvalidIndexRangeError(cx, index, length); } // Manually set the pointer inside the object, so we skip the conversion step. diff --git a/js/src/ctypes/ctypes.msg b/js/src/ctypes/ctypes.msg index bac1431d367f..86b50893971c 100644 --- a/js/src/ctypes/ctypes.msg +++ b/js/src/ctypes/ctypes.msg @@ -24,6 +24,8 @@ 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 in the length of the type {1} (expected {2} or lower, got {3})") +MSG_DEF(CTYPESMSG_INVALID_INDEX, 1, JSEXN_TYPEERR, "{0} is not a valid array index") +MSG_DEF(CTYPESMSG_INVALID_RANGE, 2, JSEXN_RANGEERR, "array index {0} is out of bounds for array of length {1}") /* 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}") diff --git a/js/src/jit-test/tests/ctypes/array-index.js b/js/src/jit-test/tests/ctypes/array-index.js new file mode 100644 index 000000000000..e70add1a062d --- /dev/null +++ b/js/src/jit-test/tests/ctypes/array-index.js @@ -0,0 +1,29 @@ +load(libdir + 'asserts.js'); + +function test() { + let a = ctypes.int32_t.array(10)(); + assertTypeErrorMessage(() => { let x = a[-1]; }, + "the string \"-1\" is not a valid array index"); + assertTypeErrorMessage(() => { a[-1] = 1; }, + "the string \"-1\" is not a valid array index"); + assertTypeErrorMessage(() => { a.addressOfElement(-1); }, + "the number -1 is not a valid array index"); + + assertRangeErrorMessage(() => { let x = a[10]; }, + "array index 10 is out of bounds for array of length 10"); + assertRangeErrorMessage(() => { a[10] = 1; }, + "array index 10 is out of bounds for array of length 10"); + assertRangeErrorMessage(() => { a.addressOfElement(10); }, + "array index 10 is out of bounds for array of length 10"); + + let obj = { + toSource() { + throw 1; + } + }; + assertTypeErrorMessage(() => { a.addressOfElement(obj); }, + "<> is not a valid array index"); +} + +if (typeof ctypes === "object") + test(); diff --git a/toolkit/components/ctypes/tests/unit/test_jsctypes.js b/toolkit/components/ctypes/tests/unit/test_jsctypes.js index a4dbb97eaf9d..67f28b619046 100644 --- a/toolkit/components/ctypes/tests/unit/test_jsctypes.js +++ b/toolkit/components/ctypes/tests/unit/test_jsctypes.js @@ -2016,9 +2016,9 @@ function run_ArrayType_tests() { a[0] = g; do_check_eq(a[0].a, 1); do_check_eq(a[0].b, 2); - do_check_throws(function() { a[-1]; }, Error); + do_check_throws(function() { a[-1]; }, TypeError); do_check_eq(a[9].a, 0); - do_check_throws(function() { a[10]; }, Error); + do_check_throws(function() { a[10]; }, RangeError); do_check_eq(a[ctypes.Int64(0)].a, 1); do_check_eq(a[ctypes.UInt64(0)].b, 2);