// 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_slice { macro HandleSimpleArgumentsSlice( context: NativeContext, args: JSArgumentsObjectWithLength, start: Smi, count: Smi): JSArray labels Bailout { // If the resulting array doesn't fit in new space, use the slow path. if (count >= kMaxNewSpaceFixedArrayElements) goto Bailout; const end: Smi = start + count; const sourceElements: FixedArray = Cast(args.elements) otherwise Bailout; if (SmiAbove(end, sourceElements.length)) goto Bailout; const arrayMap: Map = LoadJSArrayElementsMap(HOLEY_ELEMENTS, context); const result: JSArray = AllocateJSArray(HOLEY_ELEMENTS, arrayMap, count, count); const newElements: FixedArray = Cast(result.elements) otherwise Bailout; CopyElements( PACKED_ELEMENTS, newElements, 0, sourceElements, Convert(start), Convert(count)); return result; } macro HandleFastAliasedSloppyArgumentsSlice( context: NativeContext, args: JSArgumentsObjectWithLength, start: Smi, count: Smi): JSArray labels Bailout { // If the resulting array doesn't fit in new space, use the slow path. if (count >= kMaxNewSpaceFixedArrayElements) goto Bailout; const sloppyElements: SloppyArgumentsElements = Cast(args.elements) otherwise Bailout; const sloppyElementsLength: Smi = sloppyElements.length; const parameterMapLength: Smi = sloppyElementsLength - kSloppyArgumentsParameterMapStart; // Check to make sure that the extraction will not access outside the // defined arguments const end: Smi = start + count; const unmappedElements: FixedArray = Cast(sloppyElements.objects[kSloppyArgumentsArgumentsIndex]) otherwise Bailout; const unmappedElementsLength: Smi = unmappedElements.length; if (SmiAbove(end, unmappedElementsLength)) goto Bailout; const argumentsContext: Context = UnsafeCast( sloppyElements.objects[kSloppyArgumentsContextIndex]); const arrayMap: Map = LoadJSArrayElementsMap(HOLEY_ELEMENTS, context); const result: JSArray = AllocateJSArray(HOLEY_ELEMENTS, arrayMap, count, count); let indexOut: Smi = 0; const resultElements: FixedArray = UnsafeCast(result.elements); const to: Smi = SmiMin(parameterMapLength, end); // Fill in the part of the result that map to context-mapped parameters. for (let current: Smi = start; current < to; ++current) { const e: Object = sloppyElements.objects[current + kSloppyArgumentsParameterMapStart]; const newElement: JSAny = UnsafeCast( e != TheHole ? argumentsContext[UnsafeCast(e)] : unmappedElements.objects[current]); // It is safe to skip the write barrier here because resultElements was // allocated together with result in a folded allocation. // TODO(tebbi): The verification of this fails at the moment due to // missing load elimination. StoreFixedArrayElement( resultElements, indexOut++, newElement, UNSAFE_SKIP_WRITE_BARRIER); } // Fill in the rest of the result that contains the unmapped parameters // above the formal parameters. const unmappedFrom: Smi = SmiMin(SmiMax(parameterMapLength, start), end); const restCount: Smi = end - unmappedFrom; CopyElements( PACKED_ELEMENTS, resultElements, Convert(indexOut), unmappedElements, Convert(unmappedFrom), Convert(restCount)); return result; } macro HandleFastSlice( context: NativeContext, o: JSAny, startNumber: Number, countNumber: Number): JSArray labels Bailout { const start: Smi = Cast(startNumber) otherwise Bailout; const count: Smi = Cast(countNumber) otherwise Bailout; assert(start >= 0); typeswitch (o) { case (a: FastJSArrayForCopy): { // It's possible to modify the array length from a valueOf // callback between the original array length read and this // point. That can change the length of the array backing store, // in the worst case, making it smaller than the region that needs // to be copied out. Therefore, re-check the length before calling // the appropriate fast path. See regress-785804.js if (SmiAbove(start + count, a.length)) goto Bailout; return ExtractFastJSArray(context, a, start, count); } case (a: JSArgumentsObjectWithLength): { const map: Map = a.map; if (IsFastAliasedArgumentsMap(map)) { return HandleFastAliasedSloppyArgumentsSlice(context, a, start, count) otherwise Bailout; } else if (IsStrictArgumentsMap(map) || IsSloppyArgumentsMap(map)) { return HandleSimpleArgumentsSlice(context, a, start, count) otherwise Bailout; } } case (JSAny): { } } goto Bailout; } // https://tc39.github.io/ecma262/#sec-array.prototype.slice transitioning javascript builtin ArrayPrototypeSlice(js-implicit context: Context, receiver: JSAny)( ...arguments): JSAny { // Handle array cloning case if the receiver is a fast array. if (arguments.length == 0) { typeswitch (receiver) { case (a: FastJSArrayForCopy): { return CloneFastJSArray(context, a); } case (JSAny): { } } } // 1. Let O be ? ToObject(this value). const o: JSReceiver = ToObject_Inline(context, receiver); // 2. Let len be ? ToLength(? Get(O, "length")). const len: Number = GetLengthProperty(o); // 3. Let relativeStart be ? ToInteger(start). const start: JSAny = arguments[0]; const relativeStart: Number = ToInteger_Inline(context, start); // 4. If relativeStart < 0, let k be max((len + relativeStart), 0); // else let k be min(relativeStart, len). let k: Number = relativeStart < 0 ? Max((len + relativeStart), 0) : Min(relativeStart, len); // 5. If end is undefined, let relativeEnd be len; // else let relativeEnd be ? ToInteger(end). const end: JSAny = arguments[1]; const relativeEnd: Number = end == Undefined ? len : ToInteger_Inline(context, end); // 6. If relativeEnd < 0, let final be max((len + relativeEnd), 0); // else let final be min(relativeEnd, len). const final: Number = relativeEnd < 0 ? Max((len + relativeEnd), 0) : Min(relativeEnd, len); // 7. Let count be max(final - k, 0). const count: Number = Max(final - k, 0); assert(0 <= k); assert(k <= len); assert(0 <= final); assert(final <= len); assert(0 <= count); assert(count <= len); try { return HandleFastSlice(UnsafeCast(context), o, k, count) otherwise Slow; } label Slow {} // 8. Let A be ? ArraySpeciesCreate(O, count). const a: JSReceiver = ArraySpeciesCreate(context, o, count); // 9. Let n be 0. let n: Number = 0; // 10. Repeat, while k < final while (k < final) { // a. Let Pk be ! ToString(k). const pK: Number = k; // b. Let kPresent be ? HasProperty(O, Pk). const fromPresent: Boolean = HasProperty(o, pK); // c. If kPresent is true, then if (fromPresent == True) { // i. Let kValue be ? Get(O, Pk). const kValue: JSAny = GetProperty(o, pK); // ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(n), kValue). FastCreateDataProperty(a, n, kValue); } // d. Increase k by 1. k++; // e. Increase n by 1. n++; } // 11. Perform ? Set(A, "length", n, true). SetProperty(a, kLengthString, n); // 12. Return A. return a; } }