| // 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; |
| } |
| } |
| |
| 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); |
| |
| // The source is a FastJSArray, thus its length must be a Smi. |
| dcheck(Is<Smi>(sourceLength)); |
| const smiSourceLength = UnsafeCast<Smi>(sourceLength); |
| |
| // 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.github.io/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.github.io/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)); |
| } |
| } |
| |
| // 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.github.io/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; |
| } |
| } |