// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
namespace array {
macro ConvertAndClampRelativeIndex(index: Number, length: Number): Number {
try {
return ConvertRelativeIndex(index, length) otherwise OutOfBoundsLow,
OutOfBoundsHigh;
} label OutOfBoundsLow {
return 0;
} label OutOfBoundsHigh {
return length;
}
}
// copyWithin moves a range onto another range in the same backing store, which
// MoveElements (a memmove) does directly, so the generic per-element loop and
// its overlap/direction handling are unneeded for a fast JSArray.
macro TryFastArrayCopyWithin(
implicit context: Context)(receiver: JSAny, toNumber: Number,
fromNumber: Number, countNumber: Number): void labels Slow {
// Nothing to copy.
if (countNumber <= 0) return;
const array: FastJSArray = Cast<FastJSArray>(receiver) otherwise Slow;
// The ToInteger coercions in the caller may have run user code that shrank
// the array, so re-validate the clamped indices against the current length.
const to: Smi = Cast<Smi>(toNumber) otherwise Slow;
const from: Smi = Cast<Smi>(fromNumber) otherwise Slow;
const count: Smi = Cast<Smi>(countNumber) otherwise Slow;
const length: Smi = array.length;
if (SmiAbove(to + count, length)) goto Slow;
if (SmiAbove(from + count, length)) goto Slow;
const kind: ElementsKind = array.map.elements_kind;
if (kind == ElementsKind::PACKED_SMI_ELEMENTS ||
kind == ElementsKind::PACKED_ELEMENTS) {
array::EnsureWriteableFastElements(array);
DoMoveElements(UnsafeCast<FixedArray>(array.elements), to, from, count);
} else if (kind == ElementsKind::PACKED_DOUBLE_ELEMENTS) {
DoMoveElements(
UnsafeCast<FixedDoubleArray>(array.elements), to, from, count);
} else if (IsFastElementsKind(kind)) {
// For holey kinds a hole must mean "absent" (spec deletes on absent), which
// only holds with the initial prototype and an intact NoElementsProtector.
if (!IsPrototypeInitialArrayPrototype(array.map)) goto Slow;
if (IsNoElementsProtectorCellInvalid()) goto Slow;
if (kind == ElementsKind::HOLEY_DOUBLE_ELEMENTS) {
DoMoveElements(
UnsafeCast<FixedDoubleArray>(array.elements), to, from, count);
} else {
dcheck(
kind == ElementsKind::HOLEY_SMI_ELEMENTS ||
kind == ElementsKind::HOLEY_ELEMENTS);
array::EnsureWriteableFastElements(array);
DoMoveElements(UnsafeCast<FixedArray>(array.elements), to, from, count);
}
} else {
goto Slow;
}
}
// https://tc39.es/ecma262/#sec-array.prototype.copywithin
transitioning javascript builtin ArrayPrototypeCopyWithin(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
// 1. Let O be ? ToObject(this value).
const object: JSReceiver = ToObject_Inline(context, receiver);
// 2. Let len be ? ToLength(? Get(O, "length")).
const length: Number = GetLengthProperty(object);
// 3. Let relativeTarget be ? ToInteger(target).
const relativeTarget: Number = ToInteger_Inline(arguments[0]);
// 4. If relativeTarget < 0, let to be max((len + relativeTarget), 0);
// else let to be min(relativeTarget, len).
let to: Number = ConvertAndClampRelativeIndex(relativeTarget, length);
// 5. Let relativeStart be ? ToInteger(start).
const relativeStart: Number = ToInteger_Inline(arguments[1]);
// 6. If relativeStart < 0, let from be max((len + relativeStart), 0);
// else let from be min(relativeStart, len).
let from: Number = ConvertAndClampRelativeIndex(relativeStart, length);
// 7. If end is undefined, let relativeEnd be len;
// else let relativeEnd be ? ToInteger(end).
let relativeEnd: Number = length;
if (arguments[2] != Undefined) {
relativeEnd = ToInteger_Inline(arguments[2]);
}
// 8. If relativeEnd < 0, let final be max((len + relativeEnd), 0);
// else let final be min(relativeEnd, len).
const final: Number = ConvertAndClampRelativeIndex(relativeEnd, length);
// 9. Let count be min(final-from, len-to).
let count: Number = Min(final - from, length - to);
// Fast JSArrays: one in-place memmove. Bail to the generic loop below.
try {
TryFastArrayCopyWithin(receiver, to, from, count) otherwise Slow;
return object;
} label Slow {}
// 10. If from<to and to<from+count, then.
let direction: Number = 1;
if (from < to && to < (from + count)) {
// a. Let direction be -1.
direction = -1;
// b. Let from be from + count - 1.
from = from + count - 1;
// c. Let to be to + count - 1.
to = to + count - 1;
}
// 12. Repeat, while count > 0.
while (count > 0) {
// a. Let fromKey be ! ToString(from).
// b. Let toKey be ! ToString(to).
// c. Let fromPresent be ? HasProperty(O, fromKey).
const fromPresent: Boolean = HasProperty(object, from);
// d. If fromPresent is true, then.
if (fromPresent == True) {
// i. Let fromVal be ? Get(O, fromKey).
const fromVal: JSAny = GetProperty(object, from);
// ii. Perform ? Set(O, toKey, fromVal, true).
SetProperty(object, to, fromVal);
} else {
// i. Perform ? DeletePropertyOrThrow(O, toKey).
DeleteProperty(object, to, LanguageMode::kStrict);
}
// f. Let from be from + direction.
from = from + direction;
// g. Let to be to + direction.
to = to + direction;
// h. Let count be count - 1.
--count;
}
// 13. Return O.
return object;
}
}