current commit
261004.7 · deps: update V8 to 15.2.124.38
// 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;
}
}