import { surroundingAgent } from '../host-defined/engine.mts'; import { BooleanValue, Descriptor, JSStringValue, ObjectValue, UndefinedValue, Value, wellKnownSymbols, type Arguments, type FunctionCallContext, } from '../value.mts'; import { Q, X, type ValueCompletion, type ValueEvaluator, } from '../completion.mts'; import { __ts_cast__ } from '../utils/language.mts'; import type { PlainEvaluator } from '../evaluator.mts'; import { assignProps } from './bootstrap.mts'; import { bootstrapArrayPrototypeShared, SortIndexedProperties } from './ArrayPrototypeShared.mts'; import { ArrayCreate, ArraySpeciesCreate, Assert, Call, CreateArrayIterator, CreateDataProperty, CreateDataPropertyOrThrow, DeletePropertyOrThrow, Get, HasProperty, IsArray, IsCallable, IsConcatSpreadable, Set, CompareArrayElements, LengthOfArrayLike, OrdinaryObjectCreate, ToBoolean, ToIntegerOrInfinity, ToObject, ToString, Throw, F, type FunctionObject, Realm, } from '#self'; /** https://tc39.es/ecma262/#sec-array.prototype.concat */ function* ArrayProto_concat(args: Arguments, { thisValue }: FunctionCallContext): ValueEvaluator { const O = Q(ToObject(thisValue)); const A = Q(yield* ArraySpeciesCreate(O, 0)); let n = 0; const items = [O, ...args]; while (items.length > 0) { const E = items.shift()!; const spreadable = Q(yield* IsConcatSpreadable(E)); __ts_cast__(E); if (spreadable === Value.true) { let k = 0; const len = Q(yield* LengthOfArrayLike(E)); if (n + len > (2 ** 53) - 1) { return Throw.TypeError('Cannot make length of array-like object surpass the bounds of an integer index'); } while (k < len) { const P = X(ToString(F(k))); const exists = Q(yield* HasProperty(E, P)); if (exists === Value.true) { const subElement = Q(yield* Get(E, P)); const nStr = X(ToString(F(n))); Q(yield* CreateDataPropertyOrThrow(A, nStr, subElement)); } n += 1; k += 1; } } else { if (n >= (2 ** 53) - 1) { return Throw.TypeError('Cannot make length of array-like object surpass the bounds of an integer index'); } const nStr = X(ToString(F(n))); Q(yield* CreateDataPropertyOrThrow(A, nStr, E)); n += 1; } } Q(yield* Set(A, Value('length'), F(n), Value.true)); return A; } /** https://tc39.es/ecma262/#sec-array.prototype.copywithin */ function* ArrayProto_copyWithin([target = Value.undefined, start = Value.undefined, end = Value.undefined]: Arguments, { thisValue }: FunctionCallContext): ValueEvaluator { const O = Q(ToObject(thisValue)); const len = Q(yield* LengthOfArrayLike(O)); const relativeTarget = Q(yield* ToIntegerOrInfinity(target)); let to; if (relativeTarget < 0) { to = Math.max(len + relativeTarget, 0); } else { to = Math.min(relativeTarget, len); } const relativeStart = Q(yield* ToIntegerOrInfinity(start)); let from; if (relativeStart < 0) { from = Math.max(len + relativeStart, 0); } else { from = Math.min(relativeStart, len); } let relativeEnd; if (end === Value.undefined) { relativeEnd = len; } else { relativeEnd = Q(yield* ToIntegerOrInfinity(end)); } let final; if (relativeEnd < 0) { final = Math.max(len + relativeEnd, 0); } else { final = Math.min(relativeEnd, len); } let count = Math.min(final - from, len - to); let direction; if (from < to && to < from + count) { direction = -1; from += count - 1; to += count - 1; } else { direction = 1; } while (count > 0) { const fromKey: JSStringValue = X(ToString(F(from))); const toKey: JSStringValue = X(ToString(F(to))); const fromPresent = Q(yield* HasProperty(O, fromKey)); if (fromPresent === Value.true) { const fromVal = Q(yield* Get(O, fromKey)); Q(yield* Set(O, toKey, fromVal, Value.true)); } else { Q(yield* DeletePropertyOrThrow(O, toKey)); } from += direction; to += direction; count -= 1; } return O; } /** https://tc39.es/ecma262/#sec-array.prototype.entries */ function ArrayProto_entries(_args: Arguments, { thisValue }: FunctionCallContext): ValueCompletion { const O = Q(ToObject(thisValue)); return CreateArrayIterator(O, 'key+value'); } /** https://tc39.es/ecma262/#sec-array.prototype.fill */ function* ArrayProto_fill([value = Value.undefined, start = Value.undefined, end = Value.undefined]: Arguments, { thisValue }: FunctionCallContext): ValueEvaluator { const O = Q(ToObject(thisValue)); const len = Q(yield* LengthOfArrayLike(O)); const relativeStart = Q(yield* ToIntegerOrInfinity(start)); let k; if (relativeStart < 0) { k = Math.max(len + relativeStart, 0); } else { k = Math.min(relativeStart, len); } let relativeEnd; if (end instanceof UndefinedValue) { relativeEnd = len; } else { relativeEnd = Q(yield* ToIntegerOrInfinity(end)); } let final; if (relativeEnd < 0) { final = Math.max(len + relativeEnd, 0); } else { final = Math.min(relativeEnd, len); } while (k < final) { const Pk: JSStringValue = X(ToString(F(k))); Q(yield* Set(O, Pk, value, Value.true)); k += 1; } return O; } /** https://tc39.es/ecma262/#sec-array.prototype.filter */ function* ArrayProto_filter([callbackfn = Value.undefined, thisArg = Value.undefined]: Arguments, { thisValue }: FunctionCallContext): ValueEvaluator { const O = Q(ToObject(thisValue)); const len = Q(yield* LengthOfArrayLike(O)); if (!IsCallable(callbackfn)) { return Throw.TypeError('$1 is not a function', callbackfn); } const A = Q(yield* ArraySpeciesCreate(O, 0)); let k = 0; let to = 0; while (k < len) { const Pk = X(ToString(F(k))); const kPresent = Q(yield* HasProperty(O, Pk)); if (kPresent === Value.true) { const kValue = Q(yield* Get(O, Pk)); const selected = ToBoolean(Q(yield* Call(callbackfn, thisArg, [kValue, F(k), O]))); if (selected === Value.true) { Q(yield* CreateDataPropertyOrThrow(A, X(ToString(F(to))), kValue)); to += 1; } } k += 1; } return A; } /** https://tc39.es/ecma262/#sec-flattenintoarray */ function* FlattenIntoArray(target: ObjectValue, source: ObjectValue, sourceLen: number, start: number, depth: number, mapperFunction?: FunctionObject, thisArg?: Value): PlainEvaluator { Assert(target instanceof ObjectValue); Assert(source instanceof ObjectValue); Assert(sourceLen >= 0); Assert(start >= 0); // Assert: _depth_ is an integer Number, *+∞*, or *-∞*. // Assert(mapperFunction === undefined || (X(IsCallable(mapperFunction)) === Value.true && thisArg !== undefined && depth === 1)); let targetIndex = start; let sourceIndex = 0; while (sourceIndex < sourceLen) { const P = X(ToString(F(sourceIndex))); const exists = Q(yield* HasProperty(source, P)); if (exists === Value.true) { let element = Q(yield* Get(source, P)); if (mapperFunction) { Assert(!!thisArg); element = Q(yield* Call(mapperFunction, thisArg, [element, F(sourceIndex), source])); } let shouldFlatten: BooleanValue = Value.false; if (depth > 0) { shouldFlatten = Q(IsArray(element)); } if (shouldFlatten === Value.true) { const elementLen = Q(yield* LengthOfArrayLike(element as ObjectValue)); targetIndex = Q(yield* FlattenIntoArray(target, element as ObjectValue, elementLen, targetIndex, depth - 1)); } else { if (targetIndex >= (2 ** 53) - 1) { return Throw.TypeError('$1 is out of range', targetIndex); } Q(yield* CreateDataPropertyOrThrow(target, X(ToString(F(targetIndex))), element)); targetIndex += 1; } } sourceIndex += 1; } return targetIndex; } /** https://tc39.es/ecma262/#sec-array.prototype.flat */ function* ArrayProto_flat([depth = Value.undefined]: Arguments, { thisValue }: FunctionCallContext): ValueEvaluator { const O = Q(ToObject(thisValue)); const sourceLen = Q(yield* LengthOfArrayLike(O)); let depthNum = 1; if (depth !== Value.undefined) { depthNum = Q(yield* ToIntegerOrInfinity(depth)); } const A = Q(yield* ArraySpeciesCreate(O, 0)); Q(yield* FlattenIntoArray(A, O, sourceLen, 0, depthNum)); return A; } /** https://tc39.es/ecma262/#sec-array.prototype.flatmap */ function* ArrayProto_flatMap([mapperFunction = Value.undefined, thisArg = Value.undefined]: Arguments, { thisValue }: FunctionCallContext): ValueEvaluator { const O = Q(ToObject(thisValue)); const sourceLen = Q(yield* LengthOfArrayLike(O)); if (!IsCallable(mapperFunction)) { return Throw.TypeError('$1 is not a function', mapperFunction); } const A = Q(yield* ArraySpeciesCreate(O, 0)); Q(yield* FlattenIntoArray(A, O, sourceLen, 0, 1, mapperFunction, thisArg)); return A; } /** https://tc39.es/ecma262/#sec-array.prototype.keys */ function ArrayProto_keys(_args: Arguments, { thisValue }: FunctionCallContext): ValueCompletion { const O = Q(ToObject(thisValue)); return CreateArrayIterator(O, 'key'); } /** https://tc39.es/ecma262/#sec-array.prototype.map */ function* ArrayProto_map([callbackfn = Value.undefined, thisArg = Value.undefined]: Arguments, { thisValue }: FunctionCallContext): ValueEvaluator { const O = Q(ToObject(thisValue)); const len = Q(yield* LengthOfArrayLike(O)); if (!IsCallable(callbackfn)) { return Throw.TypeError('$1 is not a function', callbackfn); } const A = Q(yield* ArraySpeciesCreate(O, len)); let k = 0; while (k < len) { const Pk = X(ToString(F(k))); const kPresent = Q(yield* HasProperty(O, Pk)); if (kPresent === Value.true) { const kValue = Q(yield* Get(O, Pk)); const mappedValue = Q(yield* Call(callbackfn, thisArg, [kValue, F(k), O])); Q(yield* CreateDataPropertyOrThrow(A, Pk, mappedValue)); } k += 1; } return A; } /** https://tc39.es/ecma262/#sec-array.prototype.pop */ function* ArrayProto_pop(_args: Arguments, { thisValue }: FunctionCallContext): ValueEvaluator { const O = Q(ToObject(thisValue)); const len = Q(yield* LengthOfArrayLike(O)); if (len === 0) { Q(yield* Set(O, Value('length'), F(+0), Value.true)); return Value.undefined; } else { const newLen = len - 1; const index = Q(yield* ToString(F(newLen))); const element = Q(yield* Get(O, index)); Q(yield* DeletePropertyOrThrow(O, index)); Q(yield* Set(O, Value('length'), F(newLen), Value.true)); return element; } } /** https://tc39.es/ecma262/#sec-array.prototype.push */ function* ArrayProto_push(_items: Arguments, { thisValue }: FunctionCallContext): ValueEvaluator { const items = [..._items]; const O = Q(ToObject(thisValue)); let len = Q(yield* LengthOfArrayLike(O)); const argCount = items.length; if (len + argCount > (2 ** 53) - 1) { return Throw.TypeError('Cannot make length of array-like object surpass the bounds of an integer index'); } while (items.length > 0) { const E = items.shift()!; Q(yield* Set(O, X(ToString(F(len))), E, Value.true)); len += 1; } Q(yield* Set(O, Value('length'), F(len), Value.true)); return F(len); } /** https://tc39.es/ecma262/#sec-array.prototype.shift */ function* ArrayProto_shift(_args: Arguments, { thisValue }: FunctionCallContext): ValueEvaluator { const O = Q(ToObject(thisValue)); const len = Q(yield* LengthOfArrayLike(O)); if (len === 0) { Q(yield* Set(O, Value('length'), F(+0), Value.true)); return Value.undefined; } const first = Q(yield* Get(O, Value('0'))); let k = 1; while (k < len) { const from = X(ToString(F(k))); const to = X(ToString(F(k - 1))); const fromPresent = Q(yield* HasProperty(O, from)); if (fromPresent === Value.true) { const fromVal = Q(yield* Get(O, from)); Q(yield* Set(O, to, fromVal, Value.true)); } else { Q(yield* DeletePropertyOrThrow(O, to)); } k += 1; } Q(yield* DeletePropertyOrThrow(O, X(ToString(F(len - 1))))); Q(yield* Set(O, Value('length'), F(len - 1), Value.true)); return first; } /** https://tc39.es/ecma262/#sec-array.prototype.slice */ function* ArrayProto_slice([start = Value.undefined, end = Value.undefined]: Arguments, { thisValue }: FunctionCallContext): ValueEvaluator { const O = Q(ToObject(thisValue)); const len = Q(yield* LengthOfArrayLike(O)); const relativeStart = Q(yield* ToIntegerOrInfinity(start)); let k; if (relativeStart < 0) { k = Math.max(len + relativeStart, 0); } else { k = Math.min(relativeStart, len); } let relativeEnd; if (end instanceof UndefinedValue) { relativeEnd = len; } else { relativeEnd = Q(yield* ToIntegerOrInfinity(end)); } let final; if (relativeEnd < 0) { final = Math.max(len + relativeEnd, 0); } else { final = Math.min(relativeEnd, len); } const count = Math.max(final - k, 0); const A = Q(yield* ArraySpeciesCreate(O, count)); let n = 0; while (k < final) { const Pk: JSStringValue = X(ToString(F(k))); const kPresent = Q(yield* HasProperty(O, Pk)); if (kPresent === Value.true) { const kValue = Q(yield* Get(O, Pk)); const nStr = X(ToString(F(n))); Q(yield* CreateDataPropertyOrThrow(A, nStr, kValue)); } k += 1; n += 1; } Q(yield* Set(A, Value('length'), F(n), Value.true)); return A; } /** https://tc39.es/ecma262/#sec-array.prototype.sort */ function* ArrayProto_sort([comparator = Value.undefined]: Arguments, { thisValue }: FunctionCallContext): ValueEvaluator { if (comparator !== Value.undefined && !IsCallable(comparator)) { return Throw.TypeError('comparator ($1) is not a function', comparator); } const obj = Q(ToObject(thisValue)); const len = Q(yield* LengthOfArrayLike(obj)); const SortCompare = function* SortCompare(x: Value, y: Value) { return yield* CompareArrayElements(x, y, comparator); }; const sortedList = Q(yield* SortIndexedProperties(obj, len, SortCompare, 'skip-holes')); const itemCount = sortedList.length; let j = 0; while (j < itemCount) { Q(yield* Set(obj, X(ToString(F(j))), sortedList[j], Value.true)); j += 1; } while (j < len) { Q(yield* DeletePropertyOrThrow(obj, X(ToString(F(j))))); j += 1; } return obj; } /** https://tc39.es/ecma262/#sec-array.prototype.tosorted */ function* ArrayProto_toSorted([comparator = Value.undefined]: Arguments, { thisValue }: FunctionCallContext): ValueEvaluator { if (comparator !== Value.undefined && !IsCallable(comparator)) { return Throw.TypeError('$1 is not a function', comparator); } const O = Q(ToObject(thisValue)); const len = Q(yield* LengthOfArrayLike(O)); const A = Q(ArrayCreate(len)); const SortCompare = function* SortCompare(x: Value, y: Value) { return yield* CompareArrayElements(x, y, comparator); }; const sortedList = Q(yield* SortIndexedProperties(O, len, SortCompare, 'read-through-holes')); let j = 0; while (j < len) { X(CreateDataPropertyOrThrow(A, X(ToString(F(j))), sortedList[j])); j += 1; } return A; } /** https://tc39.es/ecma262/#sec-array.prototype.splice */ function* ArrayProto_splice(args: Arguments, { thisValue }: FunctionCallContext): ValueEvaluator { const [start = Value.undefined, deleteCount = Value.undefined, ...items] = args; const O = Q(ToObject(thisValue)); const len = Q(yield* LengthOfArrayLike(O)); const relativeStart = Q(yield* ToIntegerOrInfinity(start)); let actualStart; if (relativeStart < 0) { actualStart = Math.max(len + relativeStart, 0); } else { actualStart = Math.min(relativeStart, len); } let insertCount; let actualDeleteCount; if (args.length === 0) { insertCount = 0; actualDeleteCount = 0; } else if (args.length === 1) { insertCount = 0; actualDeleteCount = len - actualStart; } else { insertCount = args.length - 2; const dc = Q(yield* ToIntegerOrInfinity(deleteCount)); actualDeleteCount = Math.min(Math.max(dc, 0), len - actualStart); } if (len + insertCount - actualDeleteCount > (2 ** 53) - 1) { return Throw.TypeError('Cannot make length of array-like object surpass the bounds of an integer index'); } const A = Q(yield* ArraySpeciesCreate(O, actualDeleteCount)); let k = 0; while (k < actualDeleteCount) { const from = X(ToString(F(actualStart + k))); const fromPresent = Q(yield* HasProperty(O, from)); if (fromPresent === Value.true) { const fromValue = Q(yield* Get(O, from)); Q(yield* CreateDataPropertyOrThrow(A, X(ToString(F(k))), fromValue)); } k += 1; } Q(yield* Set(A, Value('length'), F(actualDeleteCount), Value.true)); const itemCount = items.length; if (itemCount < actualDeleteCount) { k = actualStart; while (k < len - actualDeleteCount) { const from: JSStringValue = X(ToString(F(k + actualDeleteCount))); const to = X(ToString(F(k + itemCount))); const fromPresent = Q(yield* HasProperty(O, from)); if (fromPresent === Value.true) { const fromValue = Q(yield* Get(O, from)); Q(yield* Set(O, to, fromValue, Value.true)); } else { Q(yield* DeletePropertyOrThrow(O, to)); } k += 1; } k = len; while (k > len - actualDeleteCount + itemCount) { Q(yield* DeletePropertyOrThrow(O, X(ToString(F(k - 1))))); k -= 1; } } else if (itemCount > actualDeleteCount) { k = len - actualDeleteCount; while (k > actualStart) { const from: JSStringValue = X(ToString(F(k + actualDeleteCount - 1))); const to = X(ToString(F(k + itemCount - 1))); const fromPresent = Q(yield* HasProperty(O, from)); if (fromPresent === Value.true) { const fromValue = Q(yield* Get(O, from)); Q(yield* Set(O, to, fromValue, Value.true)); } else { Q(yield* DeletePropertyOrThrow(O, to)); } k -= 1; } } k = actualStart; while (items.length > 0) { const E = items.shift()!; Q(yield* Set(O, X(ToString(F(k))), E, Value.true)); k += 1; } Q(yield* Set(O, Value('length'), F(len - actualDeleteCount + itemCount), Value.true)); return A; } /** https://tc39.es/ecma262/#sec-array.prototype.tospliced */ function* ArrayProto_toSpliced(args: Arguments, { thisValue }: FunctionCallContext): ValueEvaluator { const [start = Value.undefined, skipCount = Value.undefined, ...items] = args as Value[]; const O = Q(ToObject(thisValue)); const len = Q(yield* LengthOfArrayLike(O)); const relativeStart = Q(yield* ToIntegerOrInfinity(start)); let actualStart; if (relativeStart === -Infinity) { actualStart = 0; } else if (relativeStart < 0) { actualStart = Math.max(len + relativeStart, 0); } else { actualStart = Math.min(relativeStart, len); } const insertCount = items.length; let actualSkipCount; if (args[0] === undefined) { actualSkipCount = 0; } else if (args[1] === undefined) { actualSkipCount = len - actualStart; } else { const sc = Q(yield* ToIntegerOrInfinity(skipCount)); actualSkipCount = Math.min(Math.max(sc, 0), len - actualStart); } const newLen = len - actualSkipCount + insertCount; if (newLen > (2 ** 53) - 1) { return Throw.TypeError('Cannot make length of array-like object surpass the bounds of an integer index'); } const A = Q(ArrayCreate(newLen)); let i = 0; let r = actualStart + actualSkipCount; while (i < actualStart) { const Pi = X(ToString(F(i))); const iValue = Q(yield* Get(O, Pi)); X(CreateDataPropertyOrThrow(A, Pi, iValue)); i += 1; } for (const E of items) { const Pi = X(ToString(F(i))); X(CreateDataPropertyOrThrow(A, Pi, E)); i += 1; } while (i < newLen) { const Pi = X(ToString(F(i))); const from = X(ToString(F(r))); const fromValue = Q(yield* Get(O, from)); X(CreateDataPropertyOrThrow(A, Pi, fromValue)); i += 1; r += 1; } return A; } /** https://tc39.es/ecma262/#sec-array.prototype.with */ function* ArrayProto_with([index = Value.undefined, value = Value.undefined]: Arguments, { thisValue }: FunctionCallContext): ValueEvaluator { const O = Q(ToObject(thisValue)); const len = Q(yield* LengthOfArrayLike(O)); const relativeIndex = Q(yield* ToIntegerOrInfinity(index)); let actualIndex; if (relativeIndex >= 0) { actualIndex = relativeIndex; } else { actualIndex = len + relativeIndex; } if (actualIndex >= len || actualIndex < 0) { return Throw.RangeError('$1 is out of range', index); } const A = Q(ArrayCreate(len)); let k = 0; while (k < len) { const Pk = X(ToString(F(k))); let fromValue; if (k === actualIndex) { fromValue = value; } else { fromValue = Q(yield* Get(O, Pk)); } X(CreateDataPropertyOrThrow(A, Pk, fromValue)); k += 1; } return A; } /** https://tc39.es/ecma262/#sec-array.prototype.tostring */ function* ArrayProto_toString(_a: Arguments, { thisValue }: FunctionCallContext): ValueEvaluator { const array = Q(ToObject(thisValue)); let func = Q(yield* Get(array, Value('join'))); if (!IsCallable(func)) { func = surroundingAgent.intrinsic('%Object.prototype.toString%'); } return Q(yield* Call(func, array)); } /** https://tc39.es/ecma262/#sec-array.prototype.unshift */ function* ArrayProto_unshift(args: Arguments, { thisValue }: FunctionCallContext): ValueEvaluator { const O = Q(ToObject(thisValue)); const len = Q(yield* LengthOfArrayLike(O)); const argCount = args.length; if (argCount > 0) { if (len + argCount > (2 ** 53) - 1) { return Throw.TypeError('Cannot make length of array-like object surpass the bounds of an integer index'); } let k = len; while (k > 0) { const from = X(ToString(F(k - 1))); const to = X(ToString(F(k + argCount - 1))); const fromPresent = Q(yield* HasProperty(O, from)); if (fromPresent === Value.true) { const fromValue = Q(yield* Get(O, from)); Q(yield* Set(O, to, fromValue, Value.true)); } else { Q(yield* DeletePropertyOrThrow(O, to)); } k -= 1; } let j = 0; const items = [...args]; while (items.length !== 0) { const E = items.shift()!; const jStr = X(ToString(F(j))); Q(yield* Set(O, jStr, E, Value.true)); j += 1; } } Q(yield* Set(O, Value('length'), F(len + argCount), Value.true)); return F(len + argCount); } /** https://tc39.es/ecma262/#sec-array.prototype.values */ function ArrayProto_values(_args: Arguments, { thisValue }: FunctionCallContext): ValueCompletion { const O = Q(ToObject(thisValue)); return CreateArrayIterator(O, 'value'); } /** https://tc39.es/ecma262/#sec-array.prototype.at */ function* ArrayProto_at([index = Value.undefined]: Arguments, { thisValue }: FunctionCallContext): ValueEvaluator { // 1. Let O be ? ToObject(this value). const O = Q(ToObject(thisValue)); // 2. Let len be ? LengthOfArrayLike(O). const len = Q(yield* LengthOfArrayLike(O)); // 3. Let relativeIndex be ? ToIntegerOrInfinity(index). const relativeIndex = Q(yield* ToIntegerOrInfinity(index)); let k; // 4. If relativeIndex ≥ 0, then if (relativeIndex >= 0) { // a. Let k be relativeIndex. k = relativeIndex; } else { // 5. Else, // a. Let k be len + relativeIndex. k = len + relativeIndex; } // 6. If k < 0 or k ≥ len, then return undefined. if (k < 0 || k >= len) { return Value.undefined; } // 7. Return ? Get(O, ! ToString(k)). return Q(yield* Get(O, X(ToString(F(k))))); } /** https://tc39.es/ecma262/#sec-array.prototype.toreversed */ function* ArrayProto_toReversed(_args: Arguments, { thisValue }: FunctionCallContext): ValueEvaluator { const O = Q(ToObject(thisValue)); const len = Q(yield* LengthOfArrayLike(O)); const A = Q(ArrayCreate(len)); let k = 0; while (k < len) { const from = X(ToString(F(len - 1 - k))); const Pk = X(ToString(F(k))); const fromValue = Q(yield* Get(O, from)); X(CreateDataPropertyOrThrow(A, Pk, fromValue)); k += 1; } return A; } export function bootstrapArrayPrototype(realmRec: Realm) { const proto = X(ArrayCreate(0, realmRec.Intrinsics['%Object.prototype%'])); assignProps(realmRec, proto, [ ['concat', ArrayProto_concat, 1], ['copyWithin', ArrayProto_copyWithin, 2], ['entries', ArrayProto_entries, 0], ['fill', ArrayProto_fill, 1], ['filter', ArrayProto_filter, 1], ['flat', ArrayProto_flat, 0], ['flatMap', ArrayProto_flatMap, 1], ['at', ArrayProto_at, 1], ['keys', ArrayProto_keys, 0], ['map', ArrayProto_map, 1], ['pop', ArrayProto_pop, 0], ['push', ArrayProto_push, 1], ['shift', ArrayProto_shift, 0], ['slice', ArrayProto_slice, 2], ['sort', ArrayProto_sort, 1], ['toSorted', ArrayProto_toSorted, 1], ['splice', ArrayProto_splice, 2], ['toSpliced', ArrayProto_toSpliced, 2], ['toString', ArrayProto_toString, 0], ['unshift', ArrayProto_unshift, 1], ['values', ArrayProto_values, 0], ['with', ArrayProto_with, 2], ['toReversed', ArrayProto_toReversed, 0], ]); bootstrapArrayPrototypeShared(realmRec, proto, 'Array'); X(proto.DefineOwnProperty(wellKnownSymbols.iterator, X(proto.GetOwnProperty(Value('values'))) as Descriptor)); { const unscopableList = OrdinaryObjectCreate(Value.null); Assert(X(CreateDataProperty(unscopableList, Value('at'), Value.true)) === Value.true); Assert(X(CreateDataProperty(unscopableList, Value('copyWithin'), Value.true)) === Value.true); Assert(X(CreateDataProperty(unscopableList, Value('entries'), Value.true)) === Value.true); Assert(X(CreateDataProperty(unscopableList, Value('fill'), Value.true)) === Value.true); Assert(X(CreateDataProperty(unscopableList, Value('find'), Value.true)) === Value.true); Assert(X(CreateDataProperty(unscopableList, Value('findIndex'), Value.true)) === Value.true); Assert(X(CreateDataProperty(unscopableList, Value('findLast'), Value.true)) === Value.true); Assert(X(CreateDataProperty(unscopableList, Value('findLastIndex'), Value.true)) === Value.true); Assert(X(CreateDataProperty(unscopableList, Value('flat'), Value.true)) === Value.true); Assert(X(CreateDataProperty(unscopableList, Value('flatMap'), Value.true)) === Value.true); Assert(X(CreateDataProperty(unscopableList, Value('includes'), Value.true)) === Value.true); Assert(X(CreateDataProperty(unscopableList, Value('keys'), Value.true)) === Value.true); Assert(X(CreateDataProperty(unscopableList, Value('toReversed'), Value.true)) === Value.true); Assert(X(CreateDataProperty(unscopableList, Value('toSorted'), Value.true)) === Value.true); Assert(X(CreateDataProperty(unscopableList, Value('toSpliced'), Value.true)) === Value.true); Assert(X(CreateDataProperty(unscopableList, Value('values'), Value.true)) === Value.true); X(proto.DefineOwnProperty(wellKnownSymbols.unscopables, Descriptor({ Value: unscopableList, Writable: Value.false, Enumerable: Value.false, Configurable: Value.true, }))); } // Used in `arguments` objects. realmRec.Intrinsics['%Array.prototype.values%'] = X(Get(proto, Value('values'))) as FunctionObject; realmRec.Intrinsics['%Array.prototype%'] = proto; }