Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.

Commit 47a87ae

Browse files
committed
Bug 1965844 - Part 1: Use ToIntegerIndex for Array built-ins. r=jandem
Use the new `ToIntegerIndex` helper function. Drive-by change: - Update spec references in the modified methods. Differential Revision: https://phabricator.services.mozilla.com/D248926
1 parent 0e722cf commit 47a87ae

File tree

1 file changed

+49
-106
lines changed

1 file changed

+49
-106
lines changed

js/src/builtin/Array.cpp

Lines changed: 49 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -3082,32 +3082,8 @@ static bool CopyArrayElements(JSContext* cx, HandleObject obj, uint64_t begin,
30823082
// Helpers for array_splice_impl() and array_to_spliced()
30833083
//
30843084
// Initialize variables common to splice() and toSpliced():
3085-
// - GetActualStart() returns the index at which to start deleting elements.
30863085
// - GetItemCount() returns the number of new elements being added.
30873086
// - GetActualDeleteCount() returns the number of elements being deleted.
3088-
static bool GetActualStart(JSContext* cx, HandleValue start, uint64_t len,
3089-
uint64_t* result) {
3090-
MOZ_ASSERT(len < DOUBLE_INTEGRAL_PRECISION_LIMIT);
3091-
3092-
// Steps from proposal: https://github.com/tc39/proposal-change-array-by-copy
3093-
// Array.prototype.toSpliced()
3094-
3095-
// Step 3. Let relativeStart be ? ToIntegerOrInfinity(start).
3096-
double relativeStart;
3097-
if (!ToInteger(cx, start, &relativeStart)) {
3098-
return false;
3099-
}
3100-
3101-
// Steps 4-5. If relativeStart is -∞, let actualStart be 0.
3102-
// Else if relativeStart < 0, let actualStart be max(len + relativeStart, 0).
3103-
if (relativeStart < 0) {
3104-
*result = uint64_t(std::max(double(len) + relativeStart, 0.0));
3105-
} else {
3106-
// Step 6. Else, let actualStart be min(relativeStart, len).
3107-
*result = uint64_t(std::min(relativeStart, double(len)));
3108-
}
3109-
return true;
3110-
}
31113087

31123088
static uint32_t GetItemCount(const CallArgs& args) {
31133089
if (args.length() < 2) {
@@ -3124,9 +3100,6 @@ static bool GetActualDeleteCount(JSContext* cx, const CallArgs& args,
31243100
MOZ_ASSERT(actualStart <= len);
31253101
MOZ_ASSERT(insertCount == GetItemCount(args));
31263102

3127-
// Steps from proposal: https://github.com/tc39/proposal-change-array-by-copy
3128-
// Array.prototype.toSpliced()
3129-
31303103
if (args.length() < 1) {
31313104
// Step 8. If start is not present, then let actualDeleteCount be 0.
31323105
*actualDeleteCount = 0;
@@ -3181,10 +3154,13 @@ static bool array_splice_impl(JSContext* cx, unsigned argc, Value* vp,
31813154
/* Steps 3-6. */
31823155
/* actualStart is the index after which elements will be
31833156
deleted and/or new elements will be added */
3184-
uint64_t actualStart;
3185-
if (!GetActualStart(cx, args.get(0), len, &actualStart)) {
3186-
return false;
3157+
uint64_t actualStart = 0;
3158+
if (args.hasDefined(0)) {
3159+
if (!ToIntegerIndex(cx, args[0], len, &actualStart)) {
3160+
return false;
3161+
}
31873162
}
3163+
MOZ_ASSERT(actualStart <= len);
31883164

31893165
/* Steps 7-10.*/
31903166
/* itemCount is the number of elements being added */
@@ -3498,8 +3474,8 @@ static void CopyDenseElementsFillHoles(ArrayObject* arr, NativeObject* nobj,
34983474
MOZ_ASSERT(arr->denseElementsArePacked());
34993475
}
35003476

3501-
// https://github.com/tc39/proposal-change-array-by-copy
3502-
// Array.prototype.toSpliced()
3477+
// ES2026 draft rev a562082b031d89d00ee667181ce8a6158656bd4b
3478+
// 23.1.3.35 Array.prototype.toSpliced ( start, skipCount, ...items )
35033479
static bool array_toSpliced(JSContext* cx, unsigned argc, Value* vp) {
35043480
AutoJSMethodProfilerEntry pseudoFrame(cx, "Array.prototype", "toSpliced");
35053481
CallArgs args = CallArgsFromVp(argc, vp);
@@ -3519,9 +3495,11 @@ static bool array_toSpliced(JSContext* cx, unsigned argc, Value* vp) {
35193495
// Steps 3-6.
35203496
// |actualStart| is the index after which elements will be deleted and/or
35213497
// new elements will be added
3522-
uint64_t actualStart;
3523-
if (!GetActualStart(cx, args.get(0), len, &actualStart)) {
3524-
return false;
3498+
uint64_t actualStart = 0;
3499+
if (args.hasDefined(0)) {
3500+
if (!ToIntegerIndex(cx, args[0], len, &actualStart)) {
3501+
return false;
3502+
}
35253503
}
35263504
MOZ_ASSERT(actualStart <= len);
35273505

@@ -4038,19 +4016,6 @@ static JSObject* SliceArguments(JSContext* cx, Handle<ArgumentsObject*> argsobj,
40384016
return result;
40394017
}
40404018

4041-
template <typename T, typename ArrayLength>
4042-
static inline ArrayLength NormalizeSliceTerm(T value, ArrayLength length) {
4043-
if (value < 0) {
4044-
value += length;
4045-
if (value < 0) {
4046-
return 0;
4047-
}
4048-
} else if (double(value) > double(length)) {
4049-
return length;
4050-
}
4051-
return ArrayLength(value);
4052-
}
4053-
40544019
static bool ArraySliceOrdinary(JSContext* cx, HandleObject obj, uint64_t begin,
40554020
uint64_t end, MutableHandleValue rval) {
40564021
if (begin > end) {
@@ -4141,26 +4106,19 @@ static bool array_slice(JSContext* cx, unsigned argc, Value* vp) {
41414106
return false;
41424107
}
41434108

4109+
/* Steps 3-4. */
41444110
uint64_t k = 0;
4145-
uint64_t final = length;
4146-
if (args.length() > 0) {
4147-
double d;
4148-
/* Step 3. */
4149-
if (!ToInteger(cx, args[0], &d)) {
4111+
if (args.hasDefined(0)) {
4112+
if (!ToIntegerIndex(cx, args[0], length, &k)) {
41504113
return false;
41514114
}
4115+
}
41524116

4153-
/* Step 4. */
4154-
k = NormalizeSliceTerm(d, length);
4155-
4156-
if (args.hasDefined(1)) {
4157-
/* Step 5. */
4158-
if (!ToInteger(cx, args[1], &d)) {
4159-
return false;
4160-
}
4161-
4162-
/* Step 6. */
4163-
final = NormalizeSliceTerm(d, length);
4117+
/* Steps 5-6. */
4118+
uint64_t final = length;
4119+
if (args.hasDefined(1)) {
4120+
if (!ToIntegerIndex(cx, args[1], length, &final)) {
4121+
return false;
41644122
}
41654123
}
41664124

@@ -4218,6 +4176,13 @@ static bool array_slice(JSContext* cx, unsigned argc, Value* vp) {
42184176
return true;
42194177
}
42204178

4179+
static inline uint32_t NormalizeSliceTerm(int32_t value, uint32_t length) {
4180+
if (value >= 0) {
4181+
return std::min(uint32_t(value), length);
4182+
}
4183+
return uint32_t(std::max(int32_t(uint32_t(value) + length), 0));
4184+
}
4185+
42214186
static bool ArraySliceDenseKernel(JSContext* cx, ArrayObject* arr,
42224187
int32_t beginArg, int32_t endArg,
42234188
ArrayObject* result) {
@@ -4494,8 +4459,8 @@ static bool SearchElementDense(JSContext* cx, HandleValue val, Iter iterator,
44944459
return iterator(cx, cmp, rval);
44954460
}
44964461

4497-
// ES2020 draft rev dc1e21c454bd316810be1c0e7af0131a2d7f38e9
4498-
// 22.1.3.14 Array.prototype.indexOf ( searchElement [ , fromIndex ] )
4462+
// ES2026 draft rev a562082b031d89d00ee667181ce8a6158656bd4b
4463+
// 23.1.3.17 Array.prototype.indexOf ( searchElement [ , fromIndex ] )
44994464
bool js::array_indexOf(JSContext* cx, unsigned argc, Value* vp) {
45004465
AutoJSMethodProfilerEntry pseudoFrame(cx, "Array.prototype", "indexOf");
45014466
CallArgs args = CallArgsFromVp(argc, vp);
@@ -4518,36 +4483,25 @@ bool js::array_indexOf(JSContext* cx, unsigned argc, Value* vp) {
45184483
return true;
45194484
}
45204485

4521-
// Steps 4-8.
4486+
// Steps 4-9.
45224487
uint64_t k = 0;
4523-
if (args.length() > 1) {
4524-
double n;
4525-
if (!ToInteger(cx, args[1], &n)) {
4488+
if (args.hasDefined(1)) {
4489+
if (!ToIntegerIndex(cx, args[1], len, &k)) {
45264490
return false;
45274491
}
45284492

4529-
// Step 6.
4530-
if (n >= double(len)) {
4493+
// Return early if |k| exceeds the current length.
4494+
if (k >= len) {
45314495
args.rval().setInt32(-1);
45324496
return true;
45334497
}
4534-
4535-
// Steps 7-8.
4536-
if (n >= 0) {
4537-
k = uint64_t(n);
4538-
} else {
4539-
double d = double(len) + n;
4540-
if (d >= 0) {
4541-
k = uint64_t(d);
4542-
}
4543-
}
45444498
}
45454499

45464500
MOZ_ASSERT(k < len);
45474501

45484502
HandleValue searchElement = args.get(0);
45494503

4550-
// Steps 9 and 10 optimized for dense elements.
4504+
// Step 10 optimized for dense elements.
45514505
if (CanOptimizeForDenseStorage<ArrayAccess::Read>(obj, len)) {
45524506
MOZ_ASSERT(len <= UINT32_MAX);
45534507

@@ -4591,7 +4545,7 @@ bool js::array_indexOf(JSContext* cx, unsigned argc, Value* vp) {
45914545
args.rval());
45924546
}
45934547

4594-
// Step 9.
4548+
// Step 10.
45954549
RootedValue v(cx);
45964550
for (; k < len; k++) {
45974551
if (!CheckForInterrupt(cx)) {
@@ -4616,7 +4570,7 @@ bool js::array_indexOf(JSContext* cx, unsigned argc, Value* vp) {
46164570
}
46174571
}
46184572

4619-
// Step 10.
4573+
// Step 11.
46204574
args.rval().setInt32(-1);
46214575
return true;
46224576
}
@@ -4735,8 +4689,8 @@ bool js::array_lastIndexOf(JSContext* cx, unsigned argc, Value* vp) {
47354689
return true;
47364690
}
47374691

4738-
// ES2020 draft rev dc1e21c454bd316810be1c0e7af0131a2d7f38e9
4739-
// 22.1.3.13 Array.prototype.includes ( searchElement [ , fromIndex ] )
4692+
// ES2026 draft rev a562082b031d89d00ee667181ce8a6158656bd4b
4693+
// 23.1.3.16 Array.prototype.includes ( searchElement [ , fromIndex ] )
47404694
bool js::array_includes(JSContext* cx, unsigned argc, Value* vp) {
47414695
AutoJSMethodProfilerEntry pseudoFrame(cx, "Array.prototype", "includes");
47424696
CallArgs args = CallArgsFromVp(argc, vp);
@@ -4759,35 +4713,25 @@ bool js::array_includes(JSContext* cx, unsigned argc, Value* vp) {
47594713
return true;
47604714
}
47614715

4762-
// Steps 4-7.
4716+
// Steps 4-9.
47634717
uint64_t k = 0;
4764-
if (args.length() > 1) {
4765-
double n;
4766-
if (!ToInteger(cx, args[1], &n)) {
4718+
if (args.hasDefined(1)) {
4719+
if (!ToIntegerIndex(cx, args[1], len, &k)) {
47674720
return false;
47684721
}
47694722

4770-
if (n >= double(len)) {
4723+
// Return early if |k| exceeds the current length.
4724+
if (k >= len) {
47714725
args.rval().setBoolean(false);
47724726
return true;
47734727
}
4774-
4775-
// Steps 6-7.
4776-
if (n >= 0) {
4777-
k = uint64_t(n);
4778-
} else {
4779-
double d = double(len) + n;
4780-
if (d >= 0) {
4781-
k = uint64_t(d);
4782-
}
4783-
}
47844728
}
47854729

47864730
MOZ_ASSERT(k < len);
47874731

47884732
HandleValue searchElement = args.get(0);
47894733

4790-
// Steps 8 and 9 optimized for dense elements.
4734+
// Step 10 optimized for dense elements.
47914735
if (CanOptimizeForDenseStorage<ArrayAccess::Read>(obj, len)) {
47924736
MOZ_ASSERT(len <= UINT32_MAX);
47934737

@@ -4836,7 +4780,7 @@ bool js::array_includes(JSContext* cx, unsigned argc, Value* vp) {
48364780
args.rval());
48374781
}
48384782

4839-
// Step 8.
4783+
// Step 10.
48404784
RootedValue v(cx);
48414785
for (; k < len; k++) {
48424786
if (!CheckForInterrupt(cx)) {
@@ -4857,7 +4801,7 @@ bool js::array_includes(JSContext* cx, unsigned argc, Value* vp) {
48574801
}
48584802
}
48594803

4860-
// Step 9.
4804+
// Step 11.
48614805
args.rval().setBoolean(false);
48624806
return true;
48634807
}
@@ -5211,7 +5155,6 @@ static const JSFunctionSpec array_methods[] = {
52115155
JS_SELF_HOSTED_FN("flatMap", "ArrayFlatMap", 1, 0),
52125156
JS_SELF_HOSTED_FN("flat", "ArrayFlat", 0, 0),
52135157

5214-
/* Proposal */
52155158
JS_SELF_HOSTED_FN("at", "ArrayAt", 1, 0),
52165159
JS_SELF_HOSTED_FN("findLast", "ArrayFindLast", 1, 0),
52175160
JS_SELF_HOSTED_FN("findLastIndex", "ArrayFindLastIndex", 1, 0),

0 commit comments

Comments
 (0)