@ 261107.7
current
// Copyright 2023 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 {

transitioning macro ArrayIsArray_Inline(
    implicit context: Context)(element: JSAny): Boolean {
  if (Is<JSArray>(element)) {
    return True;
  } else if (Is<JSProxy>(element)) {
    return Cast<Boolean>(runtime::ArrayIsArray(element))
        otherwise unreachable;
  } else {
    return False;
  }
}

struct FlatVector {
  macro CreateJSArray(implicit context: Context)(targetKind: ElementsKind):
      JSArray {
    const map: Map =
        LoadJSArrayElementsMap(targetKind, LoadNativeContext(context));
    const a: JSArray = NewJSArray(map, this.fixedArray);
    this.fixedArray = kEmptyFixedArray;
    return a;
  }

  macro StoreResult(implicit context: Context)(index: Smi, result: JSAny):
      void {
    this.fixedArray.objects[index] = result;
  }

  fixedArray: FixedArray;
}

macro NewFlatVector(implicit context: Context)(length: Smi): FlatVector {
  const fixedArray = length > 0 ?
      AllocateFixedArrayWithHoles(SmiUntag(length)) :
      kEmptyFixedArray;
  return FlatVector{fixedArray};
}

struct FlattenedLengthResult {
  length: Smi;
  targetKind: ElementsKind;
}

// Fast path safety valve: avoid unbounded explicit stack growth on
// cyclic nesting by bailing out to the slow path after a fixed depth.
// 3 entries per depth: array, index, depth (depth limit = 1024).
const kMaxFlatFastStackEntries: intptr = 3072;

transitioning macro CalculateFlattenedLengthFast(
    implicit context: Context)(source: FastJSArray, sourceLength: Smi,
    depth: Smi): FlattenedLengthResult labels Bailout {
  PerformStackCheck();
  let targetLength: Smi = 0;
  const sourceKind: ElementsKind = source.map.elements_kind;

  // Packed numeric source: the result has the same kind and length.
  // Holey variants are excluded because (1) holes are skipped during
  // flattening, so array.length overcounts actual elements, and (2) with
  // V8_ENABLE_UNDEFINED_DOUBLE, FixedDoubleArray can store undefined which
  // is not a Number and would crash the PACKED_DOUBLE second pass.
  if (sourceKind == ElementsKind::PACKED_SMI_ELEMENTS ||
      sourceKind == ElementsKind::PACKED_DOUBLE_ELEMENTS) {
    return FlattenedLengthResult{length: sourceLength, targetKind: sourceKind};
  }

  // Track which leaf element types are present.
  let seenSmi: bool = false;
  let seenDouble: bool = false;
  let seenObject: bool = false;
  let stack = growable_fixed_array::NewGrowableFixedArray();
  let currentArray: FastJSArrayForRead =
      Cast<FastJSArrayForRead>(source) otherwise goto Bailout;
  let currentDepth: Smi = depth;
  let index: Smi = SmiConstant(0);
  let currentLength: Smi = sourceLength;
  let fastOW = NewFastJSArrayForReadWitness(currentArray);
  let didDescend: bool = false;

  while (true) {
    // Rechecking once per array is enough: nothing in the inner loop
    // invokes JS.
    fastOW.Recheck() otherwise goto Bailout;

    while (index < currentLength) {
      if (index >= fastOW.Get().length) goto Bailout;

      let element: JSAny;
      try {
        element = fastOW.LoadElementNoHole(index) otherwise FoundHole;
      } label FoundHole {
        index++;
        continue;
      }

      if (currentDepth > 0 && Is<JSArray>(element)) {
        const elementArray: FastJSArrayForRead =
            Cast<FastJSArrayForRead>(element) otherwise goto Bailout;
        const rawKind: ElementsKind = elementArray.map.elements_kind;
        const nextDepth: Smi =
            math::TrySmiSub(currentDepth, 1) otherwise goto Bailout;

        // Packed numeric arrays cannot contain sub-arrays or proxies, so add
        // their length directly without scanning elements.
        // Holey variants are excluded because holes are skipped during
        // flattening, so array.length would overcount the actual elements.
        if (rawKind == ElementsKind::PACKED_SMI_ELEMENTS) {
          seenSmi = true;
          const subLen: Smi =
              Cast<Smi>(elementArray.length) otherwise goto Bailout;
          targetLength =
              math::TrySmiAdd(targetLength, subLen) otherwise goto Bailout;
          index++;
          continue;
        }
        if (rawKind == ElementsKind::PACKED_DOUBLE_ELEMENTS) {
          seenDouble = true;
          const subLen: Smi =
              Cast<Smi>(elementArray.length) otherwise goto Bailout;
          targetLength =
              math::TrySmiAdd(targetLength, subLen) otherwise goto Bailout;
          index++;
          continue;
        }

        // Non-numeric sub-array: descend to determine leaf types.
        const nextIndex: Smi = math::TrySmiAdd(index, 1) otherwise goto Bailout;
        if (stack.length >= kMaxFlatFastStackEntries) goto Bailout;
        stack.Push(currentArray);
        stack.Push(nextIndex);
        stack.Push(currentDepth);

        currentArray = elementArray;
        currentDepth = nextDepth;
        index = SmiConstant(0);
        currentLength = Cast<Smi>(currentArray.length) otherwise goto Bailout;
        fastOW = NewFastJSArrayForReadWitness(currentArray);
        didDescend = true;
        break;
      }

      if (currentDepth > 0 && Is<JSProxy>(element)) goto Bailout;
      if (!IsNumber(element)) {
        seenObject = true;
      } else if (!TaggedIsSmi(element)) {
        seenDouble = true;
      } else {
        seenSmi = true;
      }
      targetLength = math::TrySmiAdd(targetLength, 1) otherwise goto Bailout;
      index++;
    }

    if (didDescend) {
      didDescend = false;
      continue;
    }
    if (stack.length == 0) break;

    stack.length = stack.length - 1;
    currentDepth = UnsafeCast<Smi>(stack.array.objects[stack.length]);
    stack.length = stack.length - 1;
    index = UnsafeCast<Smi>(stack.array.objects[stack.length]);
    stack.length = stack.length - 1;
    currentArray = Cast<FastJSArrayForRead>(stack.array.objects[stack.length])
        otherwise goto Bailout;
    currentLength = Cast<Smi>(currentArray.length) otherwise goto Bailout;
    fastOW = NewFastJSArrayForReadWitness(currentArray);
  }

  // Determine target kind from observed leaf types.
  let targetKind: ElementsKind;
  if (seenObject) {
    targetKind = ElementsKind::PACKED_ELEMENTS;
  } else if (seenDouble) {
    targetKind = ElementsKind::PACKED_DOUBLE_ELEMENTS;
  } else {
    targetKind = ElementsKind::PACKED_SMI_ELEMENTS;
  }
  return FlattenedLengthResult{length: targetLength, targetKind: targetKind};
}

transitioning macro TryFastFlat(
    implicit context: Context)(receiver: JSReceiver, sourceLength: Number,
    depth: Smi): JSArray labels Bailout {
  const fastLength: Smi = Cast<Smi>(sourceLength) otherwise goto Bailout;
  const fastO: FastJSArrayForCopy =
      Cast<FastJSArrayForCopy>(receiver) otherwise goto Bailout;
  const info: FlattenedLengthResult = CalculateFlattenedLengthFast(
      fastO, fastLength, depth) otherwise goto Bailout;
  const flattenedLength: Smi = info.length;

  if (flattenedLength == 0) {
    const map: Map = LoadJSArrayElementsMap(
        ElementsKind::PACKED_SMI_ELEMENTS, LoadNativeContext(context));
    return NewJSArray(map, kEmptyFixedArray);
  }

  if (info.targetKind == ElementsKind::PACKED_DOUBLE_ELEMENTS) {
    const doubleMap: Map = LoadJSArrayElementsMap(
        ElementsKind::PACKED_DOUBLE_ELEMENTS, LoadNativeContext(context));
    // AllocateFixedDoubleArrayWithHoles memsets every slot to the hole
    // sentinel, but flattenedLength is exact so every slot will be
    // written.  Use AllocateFixedArray to skip the redundant memset.
    const doubleElements: FixedDoubleArray =
        UnsafeCast<FixedDoubleArray>(AllocateFixedArray(
            ElementsKind::PACKED_DOUBLE_ELEMENTS, SmiUntag(flattenedLength)));
    let targetIndex: Smi = 0;
    let stack = growable_fixed_array::NewGrowableFixedArray();
    let currentArray: FastJSArrayForRead =
        Cast<FastJSArrayForRead>(receiver) otherwise goto Bailout;
    let currentDepth: Smi = depth;
    let index: Smi = SmiConstant(0);
    let currentLength: Smi = fastLength;
    let fastOW = NewFastJSArrayForReadWitness(currentArray);
    let didDescend: bool = false;

    while (true) {
      fastOW.Recheck() otherwise goto Bailout;

      while (index < currentLength) {
        if (index >= fastOW.Get().length) goto Bailout;

        let element: JSAny;
        try {
          element = fastOW.LoadElementNoHole(index) otherwise FoundHole;
        } label FoundHole {
          index++;
          continue;
        }

        if (currentDepth > 0 && Is<JSArray>(element)) {
          const nextDepth: Smi =
              math::TrySmiSub(currentDepth, 1) otherwise goto Bailout;
          const subArray: JSArray = UnsafeCast<JSArray>(element);

          // Packed double sub-array: bulk copy via memcpy.
          // FixedDoubleArray contains no tagged pointers, so the copy is
          // unconditionally safe with respect to write barriers.
          if (subArray.map.elements_kind ==
              ElementsKind::PACKED_DOUBLE_ELEMENTS) {
            const srcElements: FixedDoubleArray =
                Cast<FixedDoubleArray>(subArray.elements)
                otherwise goto Bailout;
            const srcLen: Smi =
                Cast<Smi>(subArray.length) otherwise goto Bailout;
            const newIdx: Smi = math::TrySmiAdd(targetIndex, srcLen)
                otherwise goto Bailout;
            if (Convert<intptr>(newIdx) > doubleElements.length_intptr) {
              goto Bailout;
            }
            TorqueCopyElements(
                doubleElements, SmiUntag(targetIndex), srcElements, 0,
                SmiUntag(srcLen));
            targetIndex = newIdx;
            index++;
            continue;
          }

          const elementArray: FastJSArrayForRead =
              Cast<FastJSArrayForRead>(element) otherwise goto Bailout;
          const nextIndex: Smi =
              math::TrySmiAdd(index, 1) otherwise goto Bailout;
          stack.Push(currentArray);
          stack.Push(nextIndex);
          stack.Push(currentDepth);

          currentArray = elementArray;
          currentDepth = nextDepth;
          index = SmiConstant(0);
          currentLength = Cast<Smi>(currentArray.length) otherwise goto Bailout;
          fastOW = NewFastJSArrayForReadWitness(currentArray);
          didDescend = true;
          break;
        }

        if (currentDepth > 0 && Is<JSProxy>(element)) goto Bailout;
        if (Convert<intptr>(targetIndex) >= doubleElements.length_intptr)
          goto Bailout;
        doubleElements.values[targetIndex] =
            Convert<float64_or_undefined_or_hole>(UnsafeCast<Number>(element));
        targetIndex++;
        index++;
      }

      if (didDescend) {
        didDescend = false;
        continue;
      }
      if (stack.length == 0) break;

      stack.length = stack.length - 1;
      currentDepth = UnsafeCast<Smi>(stack.array.objects[stack.length]);
      stack.length = stack.length - 1;
      index = UnsafeCast<Smi>(stack.array.objects[stack.length]);
      stack.length = stack.length - 1;
      currentArray = Cast<FastJSArrayForRead>(stack.array.objects[stack.length])
          otherwise goto Bailout;
      currentLength = Cast<Smi>(currentArray.length) otherwise goto Bailout;
      fastOW = NewFastJSArrayForReadWitness(currentArray);
    }

    if (targetIndex != flattenedLength) goto Bailout;
    return NewJSArray(doubleMap, doubleElements);
  }

  let vector = NewFlatVector(flattenedLength);
  let targetIndex: Smi = 0;
  let stack = growable_fixed_array::NewGrowableFixedArray();
  let currentArray: FastJSArrayForRead =
      Cast<FastJSArrayForRead>(receiver) otherwise goto Bailout;
  let currentDepth: Smi = depth;
  let index: Smi = SmiConstant(0);
  let currentLength: Smi = fastLength;
  let fastOW = NewFastJSArrayForReadWitness(currentArray);

  let didDescend: bool = false;

  while (true) {
    fastOW.Recheck() otherwise goto Bailout;

    while (index < currentLength) {
      if (index >= fastOW.Get().length) goto Bailout;

      let element: JSAny;
      try {
        element = fastOW.LoadElementNoHole(index) otherwise FoundHole;
      } label FoundHole {
        index++;
        continue;
      }

      if (currentDepth > 0 && Is<JSArray>(element)) {
        const nextDepth: Smi =
            math::TrySmiSub(currentDepth, 1) otherwise goto Bailout;
        const subArray: JSArray = UnsafeCast<JSArray>(element);

        // Packed Smi sub-array: bulk copy via memcpy.  Smi
        // values carry no heap pointer, so write barriers are not required.
        if (subArray.map.elements_kind == ElementsKind::PACKED_SMI_ELEMENTS) {
          const srcElements: FixedArray = Cast<FixedArray>(subArray.elements)
              otherwise goto Bailout;
          const srcLen: Smi = Cast<Smi>(subArray.length) otherwise goto Bailout;
          const newIdx: Smi = math::TrySmiAdd(targetIndex, srcLen)
              otherwise goto Bailout;
          if (Convert<intptr>(newIdx) > vector.fixedArray.length_intptr) {
            goto Bailout;
          }
          TorqueCopyElements(
              vector.fixedArray, SmiUntag(targetIndex), srcElements, 0,
              SmiUntag(srcLen));
          targetIndex = newIdx;
          index++;
          continue;
        }

        const elementArray: FastJSArrayForRead =
            Cast<FastJSArrayForRead>(element) otherwise goto Bailout;
        const nextIndex: Smi = math::TrySmiAdd(index, 1) otherwise goto Bailout;
        if (stack.length >= kMaxFlatFastStackEntries) goto Bailout;
        stack.Push(currentArray);
        stack.Push(nextIndex);
        stack.Push(currentDepth);

        currentArray = elementArray;
        currentDepth = nextDepth;
        index = SmiConstant(0);
        currentLength = Cast<Smi>(currentArray.length) otherwise goto Bailout;
        fastOW = NewFastJSArrayForReadWitness(currentArray);
        didDescend = true;
        break;
      }

      if (currentDepth > 0 && Is<JSProxy>(element)) goto Bailout;
      if (Convert<intptr>(targetIndex) >= vector.fixedArray.length_intptr) {
        goto Bailout;
      }
      vector.StoreResult(targetIndex, element);
      targetIndex++;
      index++;
    }

    if (didDescend) {
      didDescend = false;
      continue;
    }
    if (stack.length == 0) break;

    stack.length = stack.length - 1;
    currentDepth = UnsafeCast<Smi>(stack.array.objects[stack.length]);
    stack.length = stack.length - 1;
    index = UnsafeCast<Smi>(stack.array.objects[stack.length]);
    stack.length = stack.length - 1;
    currentArray = Cast<FastJSArrayForRead>(stack.array.objects[stack.length])
        otherwise goto Bailout;
    currentLength = Cast<Smi>(currentArray.length) otherwise goto Bailout;
    fastOW = NewFastJSArrayForReadWitness(currentArray);
  }

  const endIndex: Smi = targetIndex;
  if (endIndex != flattenedLength) goto Bailout;
  return vector.CreateJSArray(info.targetKind);
}

transitioning macro FlattenIntoArrayFast(
    implicit context: Context)(target: JSReceiver, source: JSReceiver,
    sourceLength: Number, start: Number, depth: Smi, hasMapper: constexpr bool,
    mapfn: JSAny, thisArgs: JSAny): Number
    labels Bailout(Number, Number) {
  // 1. Let targetIndex be start.
  let targetIndex: Number = start;

  // 2. Let sourceIndex be 0.
  let smiSourceIndex: Smi = 0;
  const fastSource = Cast<FastJSArray>(source)
      otherwise goto Bailout(targetIndex, smiSourceIndex);
  let fastOW = NewFastJSArrayWitness(fastSource);

  const smiSourceLength = Cast<Smi>(sourceLength)
      otherwise goto Bailout(targetIndex, smiSourceIndex);

  // 3. Repeat, while sourceIndex < sourceLen
  for (; smiSourceIndex < smiSourceLength; smiSourceIndex++) {
    fastOW.Recheck() otherwise goto Bailout(targetIndex, smiSourceIndex);

    // Ensure that we haven't walked beyond a possibly updated length.
    if (smiSourceIndex >= fastOW.Get().length)
      goto Bailout(targetIndex, smiSourceIndex);

    // a. Let P be ! ToString(sourceIndex).
    // b. Let exists be ? HasProperty(source, P).
    //   i. Let element be ? Get(source, P).
    let element = fastOW.LoadElementNoHole(smiSourceIndex)
        otherwise continue;
    //   ii. If mapperFunction is present, then
    if constexpr (hasMapper) {
      //  1. Set element to ? Call(mapperFunction, thisArgs , « element,
      //                          sourceIndex, source »).
      element = Call(context, mapfn, thisArgs, element, smiSourceIndex, source);
    }
    // iii. Let shouldFlatten be false.
    let shouldFlatten: Boolean = False;
    // iv. If depth > 0, then
    let elementLength: Number = 0;
    if (depth > 0) {
      // Set shouldFlatten to ? IsArray(element).
      // 1. Let elementLen be ? ToLength(? Get(element, "length")).
      try {
        const elementJSArray: JSArray =
            Cast<JSArray>(element) otherwise NonJSArray;
        shouldFlatten = True;
        elementLength = elementJSArray.length;
      } label NonJSArray {
        if (Is<JSProxy>(element)) {
          shouldFlatten = Cast<Boolean>(runtime::ArrayIsArray(element))
              otherwise unreachable;
        }
        if (shouldFlatten == True) {
          elementLength = GetLengthProperty(element);
        }
      }
    }
    // v. If shouldFlatten is true, then
    if (shouldFlatten == True) {
      if (elementLength > 0) {
        // 2. Set targetIndex to ? FlattenIntoArray(target, element,
        //    elementLen, targetIndex, depth - 1).
        const element = Cast<JSReceiver>(element) otherwise unreachable;
        targetIndex = FlattenIntoArrayWithoutMapFn(
            target, element, elementLength, targetIndex, depth - 1);
      }
    } else {
      // 1. If targetIndex >= 2^53-1, throw a TypeError exception.
      if (targetIndex >= kMaxSafeInteger) deferred {
          ThrowTypeError(
              MessageTemplate::kFlattenPastSafeLength, sourceLength,
              targetIndex);
        }
      // 2. Perform ? CreateDataPropertyOrThrow(target,
      //                                        ! ToString(targetIndex),
      //                                        element).
      FastCreateDataProperty(target, targetIndex, element);
      targetIndex++;
    }
  }
  return targetIndex;
}

// https://tc39.es/proposal-flatMap/#sec-FlattenIntoArray
transitioning macro FlattenIntoArraySlow(
    implicit context: Context)(target: JSReceiver, source: JSReceiver,
    sourceIndex: Number, sourceLength: Number, start: Number, depth: Smi,
    hasMapper: constexpr bool, mapfn: JSAny, thisArgs: JSAny): Number {
  // 1. Let targetIndex be start.
  let targetIndex: Number = start;

  // 2. Let sourceIndex be 0.
  let sourceIndex: Number = sourceIndex;

  // 3. Repeat, while sourceIndex < sourceLen
  while (sourceIndex < sourceLength) {
    // a. Let P be ! ToString(sourceIndex).
    // b. Let exists be ? HasProperty(source, P).
    const exists: Boolean = HasProperty(source, sourceIndex);
    if (exists == True) {
      let element: JSAny;
      // i. Let element be ? Get(source, P).
      element = GetProperty(source, sourceIndex);

      // ii. If mapperFunction is present, then
      if constexpr (hasMapper) {
        // 1. Set element to ? Call(mapperFunction, thisArgs , « element,
        //                          sourceIndex, source »).
        element = Call(context, mapfn, thisArgs, element, sourceIndex, source);
      }
      // iii. Let shouldFlatten be false.
      let shouldFlatten: Boolean = False;
      // iv. If depth > 0, then
      if (depth > 0) {
        // Set shouldFlatten to ? IsArray(element).
        shouldFlatten = ArrayIsArray_Inline(element);
      }
      // v. If shouldFlatten is true, then
      if (shouldFlatten == True) {
        // 1. Let elementLen be ? ToLength(? Get(element, "length")).
        const elementLength: Number = GetLengthProperty(element);
        // 2. Set targetIndex to ? FlattenIntoArray(target, element,
        //    elementLen, targetIndex, depth - 1).
        const element = Cast<JSReceiver>(element) otherwise unreachable;
        targetIndex = FlattenIntoArrayWithoutMapFn(
            target, element, elementLength, targetIndex, depth - 1);
      } else {
        // 1. If targetIndex >= 2^53-1, throw a TypeError exception.
        if (targetIndex >= kMaxSafeInteger) deferred {
            ThrowTypeError(
                MessageTemplate::kFlattenPastSafeLength, sourceLength,
                targetIndex);
          }
        // 2. Perform ? CreateDataPropertyOrThrow(target,
        //                                        ! ToString(targetIndex),
        //                                        element).
        FastCreateDataProperty(target, targetIndex, element);
        targetIndex++;
      }
    }
    // d. Increase sourceIndex by 1.
    sourceIndex++;
  }
  return targetIndex;
}

transitioning macro FlattenIntoArray(
    implicit context: Context)(target: JSReceiver, source: JSReceiver,
    sourceLength: Number, start: Number, depth: Smi, hasMapper: constexpr bool,
    mapfn: JSAny, thisArgs: JSAny): Number {
  try {
    return FlattenIntoArrayFast(
        target, source, sourceLength, start, depth, hasMapper, mapfn, thisArgs)
        otherwise Bailout;
  } label Bailout(kTargetIndex: Number, kSourceIndex: Number) {
    return FlattenIntoArraySlow(
        target, source, kSourceIndex, sourceLength, kTargetIndex, depth,
        hasMapper, mapfn, thisArgs);
  }
}

transitioning builtin FlattenIntoArrayWithoutMapFn(
    implicit context: Context)(target: JSReceiver, source: JSReceiver,
    sourceLength: Number, start: Number, depth: Smi): Number {
  // This builtin might get called recursively, check stack for overflow
  // manually as it has stub linkage.
  PerformStackCheck();
  return FlattenIntoArray(
      target, source, sourceLength, start, depth, false, Undefined, Undefined);
}

transitioning builtin FlattenIntoArrayWithMapFn(
    implicit context: Context)(target: JSReceiver, source: JSReceiver,
    sourceLength: Number, start: Number, depth: Smi, mapfn: JSAny,
    thisArgs: JSAny): Number {
  return FlattenIntoArray(
      target, source, sourceLength, start, depth, true, mapfn, thisArgs);
}

// https://tc39.es/proposal-flatMap/#sec-Array.prototype.flat
transitioning javascript builtin ArrayPrototypeFlat(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
  // 1. Let O be ? ToObject(this value).
  const o: JSReceiver = ToObject_Inline(context, receiver);

  // 2. Let sourceLen be ? ToLength(? Get(O, "length")).
  const len: Number = GetLengthProperty(o);

  // 3. Let depthNum be 1.
  let depthNum: Number = 1;

  // 4. If depth is not Undefined, then
  if (arguments[0] != Undefined) {
    // a. Set depthNum to ? ToInteger(depth).
    depthNum = ToInteger_Inline(arguments[0]);
  }

  // We will hit stack overflow before the stack depth reaches kSmiMax, so we
  // can truncate depthNum(Number) to Smi to improve performance.
  let depthSmi: Smi = 0;
  try {
    depthSmi = Cast<PositiveSmi>(depthNum) otherwise NotPositiveSmi;
  } label NotPositiveSmi {
    if (depthNum <= 0) {
      depthSmi = 0;
    } else {
      depthSmi = Convert<Smi>(Convert<intptr>(kSmiMax));
    }
  }

  // Try fast path in Torque/CSA.
  try {
    return TryFastFlat(o, len, depthSmi) otherwise SlowFastPath;
  } label SlowFastPath {}

  // 5. Let A be ? ArraySpeciesCreate(O, 0).
  const a: JSReceiver = ArraySpeciesCreate(context, o, 0);

  // 6. Perform ? FlattenIntoArray(A, O, sourceLen, 0, depthNum).
  FlattenIntoArrayWithoutMapFn(a, o, len, 0, depthSmi);

  // 7. Return A.
  return a;
}

// https://tc39.es/proposal-flatMap/#sec-Array.prototype.flatMap
transitioning javascript builtin ArrayPrototypeFlatMap(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
  // 1. Let O be ? ToObject(this value).
  const o: JSReceiver = ToObject_Inline(context, receiver);

  // 2. Let sourceLen be ? ToLength(? Get(O, "length")).
  const len: Number = GetLengthProperty(o);

  // 3. If IsCallable(mapperFunction) is false, throw a TypeError exception.
  let mapfn: Callable;
  try {
    mapfn = Cast<Callable>(arguments[0])
        otherwise NonCallableError;
  } label NonCallableError deferred {
    ThrowCalledNonCallable(arguments[0]);
  }


  // 4. If thisArgs is present, let T be thisArgs; else let T be Undefined.
  const t: JSAny = arguments[1];

  // 5. Let A be ? ArraySpeciesCreate(O, 0).
  const a: JSReceiver = ArraySpeciesCreate(context, o, 0);

  // 6. Perform ? FlattenIntoArray(A, O, sourceLen, 0, depthNum).
  FlattenIntoArrayWithMapFn(a, o, len, 0, 1, mapfn, t);

  // 7. Return A.
  return a;
}
}