| // 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 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<FixedArray>(args.elements) otherwise Bailout; |
| if (SmiAbove(end, sourceElements.length)) goto Bailout; |
| |
| const arrayMap: Map = |
| LoadJSArrayElementsMap(ElementsKind::HOLEY_ELEMENTS, context); |
| const result: JSArray = |
| AllocateJSArray(ElementsKind::HOLEY_ELEMENTS, arrayMap, count, count); |
| const newElements: FixedArray = |
| Cast<FixedArray>(result.elements) otherwise Bailout; |
| CopyElements( |
| ElementsKind::PACKED_ELEMENTS, newElements, 0, sourceElements, |
| Convert<intptr>(start), Convert<intptr>(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<SloppyArgumentsElements>(args.elements) otherwise Bailout; |
| const parameterMapLength: Smi = sloppyElements.length; |
| |
| // Check to make sure that the extraction will not access outside the |
| // defined arguments |
| const end: Smi = start + count; |
| const unmappedElements: FixedArray = |
| Cast<FixedArray>(sloppyElements.arguments) |
| otherwise Bailout; |
| const unmappedElementsLength: Smi = unmappedElements.length; |
| if (SmiAbove(end, unmappedElementsLength)) goto Bailout; |
| |
| const argumentsContext: Context = sloppyElements.context; |
| |
| const arrayMap: Map = |
| LoadJSArrayElementsMap(ElementsKind::HOLEY_ELEMENTS, context); |
| const result: JSArray = |
| AllocateJSArray(ElementsKind::HOLEY_ELEMENTS, arrayMap, count, count); |
| |
| let indexOut: Smi = 0; |
| const resultElements: FixedArray = UnsafeCast<FixedArray>(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.mapped_entries[current]; |
| const newElement = UnsafeCast<(JSAny | TheHole)>( |
| e != TheHole ? argumentsContext.elements[UnsafeCast<Smi>(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( |
| ElementsKind::PACKED_ELEMENTS, resultElements, Convert<intptr>(indexOut), |
| unmappedElements, Convert<intptr>(unmappedFrom), |
| Convert<intptr>(restCount)); |
| return result; |
| } |
| |
| macro HandleFastSlice( |
| context: NativeContext, o: JSAny, startNumber: Number, |
| countNumber: Number): JSArray |
| labels Bailout { |
| const start: Smi = Cast<Smi>(startNumber) otherwise Bailout; |
| const count: Smi = Cast<Smi>(countNumber) otherwise Bailout; |
| assert(start >= 0); |
| |
| try { |
| 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: JSStrictArgumentsObject): { |
| goto HandleSimpleArgumentsSlice(a); |
| } |
| case (a: JSSloppyArgumentsObject): { |
| const map: Map = a.map; |
| if (IsFastAliasedArgumentsMap(map)) { |
| return HandleFastAliasedSloppyArgumentsSlice(context, a, start, count) |
| otherwise Bailout; |
| } else if (IsSloppyArgumentsMap(map)) { |
| goto HandleSimpleArgumentsSlice(a); |
| } |
| goto Bailout; |
| } |
| case (JSAny): { |
| goto Bailout; |
| } |
| } |
| } label HandleSimpleArgumentsSlice(a: JSArgumentsObjectWithLength) { |
| return HandleSimpleArgumentsSlice(context, a, start, count) |
| otherwise Bailout; |
| } |
| } |
| |
| // https://tc39.github.io/ecma262/#sec-array.prototype.slice |
| transitioning javascript builtin |
| ArrayPrototypeSlice( |
| js-implicit context: NativeContext, 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(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(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(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; |
| } |
| } |