Files
completion-viz/engine262/src/abstract-ops/array-objects.mjs
T

251 lines
7.6 KiB
JavaScript

import { surroundingAgent } from '../engine.mjs';
import {
Descriptor,
Type,
Value,
wellKnownSymbols,
} from '../value.mjs';
import { Q, X } from '../completion.mjs';
import {
AbstractRelationalComparison,
Assert,
Call,
Construct,
Get,
GetFunctionRealm,
IsDataDescriptor,
IsArray,
IsConstructor,
OrdinaryDefineOwnProperty,
OrdinaryGetOwnProperty,
OrdinaryObjectCreate,
MakeBasicObject,
SameValue,
ToBoolean,
ToNumber,
ToString,
ToUint32,
IsPropertyKey,
IsNonNegativeInteger,
isArrayIndex,
} from './all.mjs';
// #sec-array-exotic-objects-defineownproperty-p-desc
function ArrayDefineOwnProperty(P, Desc) {
const A = this;
Assert(IsPropertyKey(P));
if (Type(P) === 'String' && P.stringValue() === 'length') {
return Q(ArraySetLength(A, Desc));
} else if (isArrayIndex(P)) {
const oldLenDesc = OrdinaryGetOwnProperty(A, new Value('length'));
Assert(X(IsDataDescriptor(oldLenDesc)));
Assert(oldLenDesc.Configurable === Value.false);
const oldLen = oldLenDesc.Value;
const index = X(ToUint32(P));
if (index.numberValue() >= oldLen.numberValue() && oldLenDesc.Writable === Value.false) {
return Value.false;
}
const succeeded = X(OrdinaryDefineOwnProperty(A, P, Desc));
if (succeeded === Value.false) {
return Value.false;
}
if (index.numberValue() >= oldLen.numberValue()) {
oldLenDesc.Value = new Value(index.numberValue() + 1);
const succeeded = OrdinaryDefineOwnProperty(A, new Value('length'), oldLenDesc); // eslint-disable-line no-shadow
Assert(succeeded === Value.true);
}
return Value.true;
}
return OrdinaryDefineOwnProperty(A, P, Desc);
}
export function isArrayExoticObject(O) {
return O.DefineOwnProperty === ArrayDefineOwnProperty;
}
// 9.4.2.2 #sec-arraycreate
export function ArrayCreate(length, proto) {
Assert(X(IsNonNegativeInteger(length)) === Value.true);
if (Object.is(length.numberValue(), -0)) {
length = new Value(0);
}
if (length.numberValue() > (2 ** 32) - 1) {
return surroundingAgent.Throw('RangeError', 'InvalidArrayLength', length);
}
if (proto === undefined) {
proto = surroundingAgent.intrinsic('%Array.prototype%');
}
const A = X(MakeBasicObject(['Prototype', 'Extensible']));
A.Prototype = proto;
A.DefineOwnProperty = ArrayDefineOwnProperty;
X(OrdinaryDefineOwnProperty(A, new Value('length'), Descriptor({
Value: length,
Writable: Value.true,
Enumerable: Value.false,
Configurable: Value.false,
})));
return A;
}
// 9.4.2.3 #sec-arrayspeciescreate
export function ArraySpeciesCreate(originalArray, length) {
Assert(Type(length) === 'Number' && Number.isInteger(length.numberValue()) && length.numberValue() >= 0);
if (Object.is(length.numberValue(), -0)) {
length = new Value(+0);
}
const isArray = Q(IsArray(originalArray));
if (isArray === Value.false) {
return Q(ArrayCreate(length));
}
let C = Q(Get(originalArray, new Value('constructor')));
if (IsConstructor(C) === Value.true) {
const thisRealm = surroundingAgent.currentRealmRecord;
const realmC = Q(GetFunctionRealm(C));
if (thisRealm !== realmC) {
if (SameValue(C, realmC.Intrinsics['%Array%']) === Value.true) {
C = Value.undefined;
}
}
}
if (Type(C) === 'Object') {
C = Q(Get(C, wellKnownSymbols.species));
if (C === Value.null) {
C = Value.undefined;
}
}
if (C === Value.undefined) {
return Q(ArrayCreate(length));
}
if (IsConstructor(C) === Value.false) {
return surroundingAgent.Throw('TypeError', 'NotAConstructor', C);
}
return Q(Construct(C, [length]));
}
// 9.4.2.4 #sec-arraysetlength
export function ArraySetLength(A, Desc) {
if (Desc.Value === undefined) {
return OrdinaryDefineOwnProperty(A, new Value('length'), Desc);
}
const newLenDesc = Descriptor({ ...Desc });
const newLen = Q(ToUint32(Desc.Value)).numberValue();
const numberLen = Q(ToNumber(Desc.Value)).numberValue();
if (newLen !== numberLen) {
return surroundingAgent.Throw('RangeError', 'InvalidArrayLength', Desc.Value);
}
newLenDesc.Value = new Value(newLen);
const oldLenDesc = OrdinaryGetOwnProperty(A, new Value('length'));
Assert(X(IsDataDescriptor(oldLenDesc)));
Assert(oldLenDesc.Configurable === Value.false);
const oldLen = oldLenDesc.Value.numberValue();
if (newLen >= oldLen) {
return OrdinaryDefineOwnProperty(A, new Value('length'), newLenDesc);
}
if (oldLenDesc.Writable === Value.false) {
return Value.false;
}
let newWritable;
if (newLenDesc.Writable === undefined || newLenDesc.Writable === Value.true) {
newWritable = true;
} else {
newWritable = false;
newLenDesc.Writable = Value.true;
}
const succeeded = X(OrdinaryDefineOwnProperty(A, new Value('length'), newLenDesc));
if (succeeded === Value.false) {
return Value.false;
}
const keys = [];
A.properties.forEach((value, key) => {
if (isArrayIndex(key) && Number(key.stringValue()) >= newLen) {
keys.push(key);
}
});
keys.sort((a, b) => Number(b.stringValue()) - Number(a.stringValue()));
for (const P of keys) {
const deleteSucceeded = X(A.Delete(P));
if (deleteSucceeded === Value.false) {
newLenDesc.Value = new Value(X(ToUint32(P)).numberValue() + 1);
if (newWritable === false) {
newLenDesc.Writable = Value.false;
}
X(OrdinaryDefineOwnProperty(A, new Value('length'), newLenDesc));
return Value.false;
}
}
if (newWritable === false) {
const s = OrdinaryDefineOwnProperty(A, new Value('length'), Descriptor({ Writable: Value.false }));
Assert(s === Value.true);
}
return Value.true;
}
// 22.1.3.1.1 #sec-isconcatspreadable
export function IsConcatSpreadable(O) {
if (Type(O) !== 'Object') {
return Value.false;
}
const spreadable = Q(Get(O, wellKnownSymbols.isConcatSpreadable));
if (spreadable !== Value.undefined) {
return ToBoolean(spreadable);
}
return Q(IsArray(O));
}
// 22.1.3.27.1 #sec-sortcompare
export function SortCompare(x, y, comparefn) {
if (x === Value.undefined && y === Value.undefined) {
return new Value(+0);
}
if (x === Value.undefined) {
return new Value(1);
}
if (y === Value.undefined) {
return new Value(-1);
}
if (comparefn !== Value.undefined) {
const callRes = Q(Call(comparefn, Value.undefined, [x, y]));
const v = Q(ToNumber(callRes));
if (v.isNaN()) {
return new Value(+0);
}
return v;
}
const xString = Q(ToString(x));
const yString = Q(ToString(y));
const xSmaller = AbstractRelationalComparison(xString, yString);
if (xSmaller === Value.true) {
return new Value(-1);
}
const ySmaller = AbstractRelationalComparison(yString, xString);
if (ySmaller === Value.true) {
return new Value(1);
}
return new Value(+0);
}
// 22.1.5.1 #sec-createarrayiterator
export function CreateArrayIterator(array, kind) {
// 1. Assert: Type(array) is Object.
Assert(Type(array) === 'Object');
// 2. Assert: kind is key+value, key, or value.
Assert(kind === 'key+value' || kind === 'key' || kind === 'value');
// 3. Let iterator be ObjectCreate(%ArrayIteratorPrototype%, « [[IteratedArrayLike]], [[ArrayLikeNextIndex]], [[ArrayLikeIterationKind]] »).
const iterator = OrdinaryObjectCreate(surroundingAgent.intrinsic('%ArrayIterator.prototype%'), [
'IteratedArrayLike',
'ArrayLikeNextIndex',
'ArrayLikeIterationKind',
]);
// 4. Set iterator.[[IteratedArrayLike]] to array.
iterator.IteratedArrayLike = array;
// 5. Set iterator.[[ArrayLikeNextIndex]] to 0.
iterator.ArrayLikeNextIndex = 0;
// 6. Set iterator.[[ArrayLikeIterationKind]] to kind.
iterator.ArrayLikeIterationKind = kind;
// 7. Return iterator.
return iterator;
}