Issue #2838 - Use ToIndex in TypedArray CTORs

Also adjusts index handling to be more accurate/spec compliant.
This commit is contained in:
Moonchild 2025-08-04 17:16:57 +02:00 • committed by roytam1
commit c07ea665ea
5 changed files with 33 additions and 24 deletions

View file

@ -83,8 +83,7 @@ static bool
ReportOutOfRange(JSContext* cx) ReportOutOfRange(JSContext* cx)
{ {
// Use JSMSG_BAD_INDEX here even if it is generic, since that is // Use JSMSG_BAD_INDEX here even if it is generic, since that is
// the message used by ToIntegerIndex for its initial range // the message used by NonStandardToIndex for its initial range checking.
// checking.
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX); JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
return false; return false;
} }
@ -114,7 +113,7 @@ static bool
GetTypedArrayIndex(JSContext* cx, HandleValue v, Handle<TypedArrayObject*> view, uint32_t* offset) GetTypedArrayIndex(JSContext* cx, HandleValue v, Handle<TypedArrayObject*> view, uint32_t* offset)
{ {
uint64_t index; uint64_t index;
if (!js::ToIntegerIndex(cx, v, &index)) if (!NonStandardToIndex(cx, v, &index))
return false; return false;
if (index >= view->length()) if (index >= view->length())
return ReportOutOfRange(cx); return ReportOutOfRange(cx);

View file

@ -1725,8 +1725,9 @@ js::ToLengthClamped<JSContext>(JSContext*, HandleValue, uint32_t*, bool*);
template bool template bool
js::ToLengthClamped<ExclusiveContext>(ExclusiveContext*, HandleValue, uint32_t*, bool*); js::ToLengthClamped<ExclusiveContext>(ExclusiveContext*, HandleValue, uint32_t*, bool*);
//Non-standard: Used by Atomics.
bool bool
js::ToIntegerIndex(JSContext* cx, JS::HandleValue v, uint64_t* index) js::NonStandardToIndex(JSContext* cx, HandleValue v, uint64_t* index)
{ {
// Fast common case. // Fast common case.
if (v.isInt32()) { if (v.isInt32()) {
@ -1776,7 +1777,7 @@ js::ToIntegerIndex(JSContext* cx, JS::HandleValue v, uint64_t* index)
// ES2017 draft 7.1.17 ToIndex // ES2017 draft 7.1.17 ToIndex
bool bool
js::ToIndex(JSContext* cx, JS::HandleValue v, uint64_t* index) js::ToIndex(JSContext* cx, JS::HandleValue v, const unsigned errorNumber, uint64_t* index)
{ {
// Step 1. // Step 1.
if (v.isUndefined()) { if (v.isUndefined()) {
@ -1794,7 +1795,7 @@ js::ToIndex(JSContext* cx, JS::HandleValue v, uint64_t* index)
// 2. Step eliminates < 0, +0 == -0 with SameValueZero. // 2. Step eliminates < 0, +0 == -0 with SameValueZero.
// 3/4. Limit to <= 2^53-1, so everything above should fail. // 3/4. Limit to <= 2^53-1, so everything above should fail.
if (integerIndex < 0 || integerIndex >= DOUBLE_INTEGRAL_PRECISION_LIMIT) { if (integerIndex < 0 || integerIndex >= DOUBLE_INTEGRAL_PRECISION_LIMIT) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX); JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, errorNumber);
return false; return false;
} }
@ -1803,6 +1804,12 @@ js::ToIndex(JSContext* cx, JS::HandleValue v, uint64_t* index)
return true; return true;
} }
bool
js::ToIndex(JSContext* cx, JS::HandleValue v, uint64_t* index)
{
return ToIndex(cx, v, JSMSG_BAD_INDEX, index);
}
template <typename CharT> template <typename CharT>
bool bool
js_strtod(ExclusiveContext* cx, const CharT* begin, const CharT* end, const CharT** dEnd, js_strtod(ExclusiveContext* cx, const CharT* begin, const CharT* end, const CharT** dEnd,

View file

@ -292,8 +292,7 @@ ToInteger(JSContext* cx, HandleValue v, double* dp)
template<typename T> template<typename T>
MOZ_MUST_USE bool ToLengthClamped(T* cx, HandleValue v, uint32_t* out, bool* overflow); MOZ_MUST_USE bool ToLengthClamped(T* cx, HandleValue v, uint32_t* out, bool* overflow);
/* Convert and range check an index value as for DataView, SIMD, and Atomics /* Convert and range check an index value for Atomics
* operations, eg ES7 24.2.1.1, DataView's GetViewValue():
* *
* 1. numericIndex = ToNumber(argument) (may throw TypeError) * 1. numericIndex = ToNumber(argument) (may throw TypeError)
* 2. intIndex = ToInteger(numericIndex) * 2. intIndex = ToInteger(numericIndex)
@ -310,7 +309,7 @@ MOZ_MUST_USE bool ToLengthClamped(T* cx, HandleValue v, uint32_t* out, bool* ove
* *
* The returned index will always be in the range 0 <= *index <= 2^53. * The returned index will always be in the range 0 <= *index <= 2^53.
*/ */
MOZ_MUST_USE bool ToIntegerIndex(JSContext* cx, JS::HandleValue v, uint64_t* index); MOZ_MUST_USE bool NonStandardToIndex(JSContext* cx, JS::HandleValue v, uint64_t* index);
/* ES2017 draft 7.1.17 ToIndex /* ES2017 draft 7.1.17 ToIndex
* *
@ -319,6 +318,7 @@ MOZ_MUST_USE bool ToIntegerIndex(JSContext* cx, JS::HandleValue v, uint64_t* ind
* *
* The returned index will always be in the range 0 <= *index <= 2^53-1. * The returned index will always be in the range 0 <= *index <= 2^53-1.
*/ */
MOZ_MUST_USE bool ToIndex(JSContext* cx, JS::HandleValue v, const unsigned errorNumber, uint64_t* index);
MOZ_MUST_USE bool ToIndex(JSContext* cx, JS::HandleValue v, uint64_t* index); MOZ_MUST_USE bool ToIndex(JSContext* cx, JS::HandleValue v, uint64_t* index);
MOZ_MUST_USE inline bool MOZ_MUST_USE inline bool

View file

@ -286,6 +286,7 @@ ArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, Value* vp)
return false; return false;
} }
// Step 3.
RootedObject proto(cx); RootedObject proto(cx);
RootedObject newTarget(cx, &args.newTarget().toObject()); RootedObject newTarget(cx, &args.newTarget().toObject());
if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) if (!GetPrototypeFromConstructor(cx, newTarget, &proto))

View file

@ -675,7 +675,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject
makeTypedArrayWithTemplate(JSContext* cx, TypedArrayObject* templateObj, int32_t len) makeTypedArrayWithTemplate(JSContext* cx, TypedArrayObject* templateObj, int32_t len)
{ {
if (len < 0 || uint32_t(len) >= INT32_MAX / sizeof(NativeType)) { if (len < 0 || uint32_t(len) >= INT32_MAX / sizeof(NativeType)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
return nullptr; return nullptr;
} }
@ -745,18 +745,16 @@ class TypedArrayObjectTemplate : public TypedArrayObject
MOZ_ASSERT(args.isConstructing()); MOZ_ASSERT(args.isConstructing());
RootedObject newTarget(cx, &args.newTarget().toObject()); RootedObject newTarget(cx, &args.newTarget().toObject());
/* () or (number) */ /* () or (length) */
uint32_t len = 0; if (args.length() == 0 || !args[0].isObject()) {
if (args.length() == 0 || ValueIsLength(args[0], &len)) uint64_t len;
return fromLength(cx, len, newTarget); if (!ToIndex(cx, args.get(0), JSMSG_BAD_ARRAY_LENGTH, &len))
return nullptr;
/* (not an object) */ return fromLength(cx, len, newTarget);
if (!args[0].isObject()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
return nullptr;
} }
RootedObject dataObj(cx, &args.get(0).toObject()); RootedObject dataObj(cx, &args[0].toObject());
/* /*
* (typedArray) * (typedArray)
@ -933,8 +931,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject
MutableHandle<ArrayBufferObject*> buffer) MutableHandle<ArrayBufferObject*> buffer)
{ {
if (count >= INT32_MAX / unit) { if (count >= INT32_MAX / unit) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NEED_DIET, JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
"size and count");
return false; return false;
} }
uint32_t byteLength = count * unit; uint32_t byteLength = count * unit;
@ -957,17 +954,22 @@ class TypedArrayObjectTemplate : public TypedArrayObject
} }
static JSObject* static JSObject*
fromLength(JSContext* cx, uint32_t nelements, HandleObject newTarget = nullptr) fromLength(JSContext* cx, uint64_t nelements, HandleObject newTarget = nullptr)
{ {
RootedObject proto(cx); RootedObject proto(cx);
if (!GetPrototypeForInstance(cx, newTarget, &proto)) if (!GetPrototypeForInstance(cx, newTarget, &proto))
return nullptr; return nullptr;
if (nelements > UINT32_MAX) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
return nullptr;
}
Rooted<ArrayBufferObject*> buffer(cx); Rooted<ArrayBufferObject*> buffer(cx);
if (!maybeCreateArrayBuffer(cx, nelements, BYTES_PER_ELEMENT, nullptr, &buffer)) if (!maybeCreateArrayBuffer(cx, uint32_t(nelements), BYTES_PER_ELEMENT, nullptr, &buffer))
return nullptr; return nullptr;
return makeInstance(cx, buffer, 0, nelements, proto); return makeInstance(cx, buffer, 0, uint32_t(nelements), proto);
} }
static bool static bool