|
| 1 | +// Copyright 2018 the V8 project authors. All rights reserved. |
| 2 | +// Use of this source code is governed by a BSD-style license that can be |
| 3 | +// found in the LICENSE file. |
| 4 | + |
| 5 | +module array { |
| 6 | + macro HandleSimpleArgumentsSlice( |
| 7 | + context: Context, args: JSArgumentsObjectWithLength, start: Smi, |
| 8 | + count: Smi): JSArray |
| 9 | + labels Bailout { |
| 10 | + const end: Smi = start + count; |
| 11 | + const sourceElements: FixedArray = |
| 12 | + Cast<FixedArray>(args.elements) otherwise Bailout; |
| 13 | + if (SmiAbove(end, sourceElements.length)) goto Bailout; |
| 14 | + |
| 15 | + const arrayMap: Map = LoadJSArrayElementsMap(HOLEY_ELEMENTS, context); |
| 16 | + const result: JSArray = |
| 17 | + AllocateJSArray(HOLEY_ELEMENTS, arrayMap, count, count); |
| 18 | + const newElements: FixedArray = |
| 19 | + Cast<FixedArray>(result.elements) otherwise Bailout; |
| 20 | + CopyElements( |
| 21 | + PACKED_ELEMENTS, newElements, Convert<intptr>(0), sourceElements, |
| 22 | + Convert<intptr>(start), Convert<intptr>(count)); |
| 23 | + return result; |
| 24 | + } |
| 25 | + |
| 26 | + macro HandleFastAliasedSloppyArgumentsSlice( |
| 27 | + context: Context, args: JSArgumentsObjectWithLength, start: Smi, |
| 28 | + count: Smi): JSArray |
| 29 | + labels Bailout { |
| 30 | + const sloppyElements: SloppyArgumentsElements = |
| 31 | + Cast<SloppyArgumentsElements>(args.elements) otherwise Bailout; |
| 32 | + const sloppyElementsLength: Smi = sloppyElements.length; |
| 33 | + const parameterMapLength: Smi = |
| 34 | + sloppyElementsLength - kSloppyArgumentsParameterMapStart; |
| 35 | + |
| 36 | + // Check to make sure that the extraction will not access outside the |
| 37 | + // defined arguments |
| 38 | + const end: Smi = start + count; |
| 39 | + const unmappedElements: FixedArray = |
| 40 | + Cast<FixedArray>(sloppyElements[kSloppyArgumentsArgumentsIndex]) |
| 41 | + otherwise Bailout; |
| 42 | + const unmappedElementsLength: Smi = unmappedElements.length; |
| 43 | + if (SmiAbove(end, unmappedElementsLength)) goto Bailout; |
| 44 | + |
| 45 | + const argumentsContext: Context = |
| 46 | + UnsafeCast<Context>(sloppyElements[kSloppyArgumentsContextIndex]); |
| 47 | + |
| 48 | + const arrayMap: Map = LoadJSArrayElementsMap(HOLEY_ELEMENTS, context); |
| 49 | + const result: JSArray = |
| 50 | + AllocateJSArray(HOLEY_ELEMENTS, arrayMap, count, count); |
| 51 | + |
| 52 | + let indexOut: Smi = 0; |
| 53 | + const resultElements: FixedArray = UnsafeCast<FixedArray>(result.elements); |
| 54 | + const to: Smi = SmiMin(parameterMapLength, end); |
| 55 | + |
| 56 | + // Fill in the part of the result that map to context-mapped parameters. |
| 57 | + for (let current: Smi = start; current < to; ++current) { |
| 58 | + const e: Object = |
| 59 | + sloppyElements[current + kSloppyArgumentsParameterMapStart]; |
| 60 | + const newElement: Object = e != Hole ? |
| 61 | + argumentsContext[UnsafeCast<Smi>(e)] : |
| 62 | + unmappedElements[current]; |
| 63 | + StoreFixedArrayElementSmi( |
| 64 | + resultElements, indexOut++, newElement, SKIP_WRITE_BARRIER); |
| 65 | + } |
| 66 | + |
| 67 | + // Fill in the rest of the result that contains the unmapped parameters |
| 68 | + // above the formal parameters. |
| 69 | + const unmappedFrom: Smi = SmiMin(SmiMax(parameterMapLength, start), end); |
| 70 | + const restCount: Smi = end - unmappedFrom; |
| 71 | + CopyElements( |
| 72 | + PACKED_ELEMENTS, resultElements, Convert<intptr>(indexOut), |
| 73 | + unmappedElements, Convert<intptr>(unmappedFrom), |
| 74 | + Convert<intptr>(restCount)); |
| 75 | + return result; |
| 76 | + } |
| 77 | + |
| 78 | + macro HandleFastSlice( |
| 79 | + context: Context, o: Object, startNumber: Number, countNumber: Number): |
| 80 | + JSArray |
| 81 | + labels Bailout { |
| 82 | + const start: Smi = Cast<Smi>(startNumber) otherwise Bailout; |
| 83 | + const count: Smi = Cast<Smi>(countNumber) otherwise Bailout; |
| 84 | + assert(start >= 0); |
| 85 | + |
| 86 | + // If the resulting array doesn't fit in new space, use the slow path. |
| 87 | + if (count >= kMaxNewSpaceFixedArrayElements) goto Bailout; |
| 88 | + |
| 89 | + typeswitch (o) { |
| 90 | + case (a: FastJSArrayForCopy): { |
| 91 | + // It's possible to modify the array length from a valueOf |
| 92 | + // callback between the original array length read and this |
| 93 | + // point. That can change the length of the array backing store, |
| 94 | + // in the worst case, making it smaller than the region that needs |
| 95 | + // to be copied out. Therefore, re-check the length before calling |
| 96 | + // the appropriate fast path. See regress-785804.js |
| 97 | + if (SmiAbove(start + count, a.length)) goto Bailout; |
| 98 | + return ExtractFastJSArray(context, a, start, count); |
| 99 | + } |
| 100 | + case (a: JSArgumentsObjectWithLength): { |
| 101 | + const nativeContext: NativeContext = LoadNativeContext(context); |
| 102 | + const map: Map = a.map; |
| 103 | + if (IsFastAliasedArgumentsMap(map)) { |
| 104 | + return HandleFastAliasedSloppyArgumentsSlice(context, a, start, count) |
| 105 | + otherwise Bailout; |
| 106 | + } else if (IsStrictArgumentsMap(map) || IsSloppyArgumentsMap(map)) { |
| 107 | + return HandleSimpleArgumentsSlice(context, a, start, count) |
| 108 | + otherwise Bailout; |
| 109 | + } |
| 110 | + } |
| 111 | + case (Object): { |
| 112 | + } |
| 113 | + } |
| 114 | + goto Bailout; |
| 115 | + } |
| 116 | + |
| 117 | + // https://tc39.github.io/ecma262/#sec-array.prototype.slice |
| 118 | + javascript builtin ArraySlice( |
| 119 | + context: Context, receiver: Object, ...arguments): Object { |
| 120 | + // Handle array cloning case if the receiver is a fast array. |
| 121 | + if (arguments.length == 0) { |
| 122 | + typeswitch (receiver) { |
| 123 | + case (a: FastJSArrayForCopy): { |
| 124 | + return CloneFastJSArray(context, a); |
| 125 | + } |
| 126 | + case (Object): { |
| 127 | + } |
| 128 | + } |
| 129 | + } |
| 130 | + |
| 131 | + // 1. Let O be ? ToObject(this value). |
| 132 | + const o: JSReceiver = ToObject_Inline(context, receiver); |
| 133 | + |
| 134 | + // 2. Let len be ? ToLength(? Get(O, "length")). |
| 135 | + const len: Number = GetLengthProperty(o); |
| 136 | + |
| 137 | + // 3. Let relativeStart be ? ToInteger(start). |
| 138 | + const start: Object = arguments[0]; |
| 139 | + const relativeStart: Number = ToInteger_Inline(context, start); |
| 140 | + |
| 141 | + // 4. If relativeStart < 0, let k be max((len + relativeStart), 0); |
| 142 | + // else let k be min(relativeStart, len). |
| 143 | + let k: Number = relativeStart < 0 ? Max((len + relativeStart), 0) : |
| 144 | + Min(relativeStart, len); |
| 145 | + |
| 146 | + // 5. If end is undefined, let relativeEnd be len; |
| 147 | + // else let relativeEnd be ? ToInteger(end). |
| 148 | + const end: Object = arguments[1]; |
| 149 | + const relativeEnd: Number = |
| 150 | + end == Undefined ? len : ToInteger_Inline(context, end); |
| 151 | + |
| 152 | + // 6. If relativeEnd < 0, let final be max((len + relativeEnd), 0); |
| 153 | + // else let final be min(relativeEnd, len). |
| 154 | + const final: Number = |
| 155 | + relativeEnd < 0 ? Max((len + relativeEnd), 0) : Min(relativeEnd, len); |
| 156 | + |
| 157 | + // 7. Let count be max(final - k, 0). |
| 158 | + const count: Number = Max(final - k, 0); |
| 159 | + |
| 160 | + assert(0 <= k); |
| 161 | + assert(k <= len); |
| 162 | + assert(0 <= final); |
| 163 | + assert(final <= len); |
| 164 | + assert(0 <= count); |
| 165 | + assert(count <= len); |
| 166 | + |
| 167 | + try { |
| 168 | + return HandleFastSlice(context, o, k, count) otherwise Slow; |
| 169 | + } |
| 170 | + label Slow {} |
| 171 | + |
| 172 | + // 8. Let A be ? ArraySpeciesCreate(O, count). |
| 173 | + const a: JSReceiver = ArraySpeciesCreate(context, o, count); |
| 174 | + |
| 175 | + // 9. Let n be 0. |
| 176 | + let n: Number = 0; |
| 177 | + |
| 178 | + // 10. Repeat, while k < final |
| 179 | + while (k < final) { |
| 180 | + // a. Let Pk be ! ToString(k). |
| 181 | + let pK: Number = k; |
| 182 | + |
| 183 | + // b. Let kPresent be ? HasProperty(O, Pk). |
| 184 | + const fromPresent: Boolean = HasProperty(o, pK); |
| 185 | + |
| 186 | + // c. If kPresent is true, then |
| 187 | + if (fromPresent == True) { |
| 188 | + // i. Let kValue be ? Get(O, Pk). |
| 189 | + const kValue: Object = GetProperty(o, pK); |
| 190 | + |
| 191 | + // ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(n), kValue). |
| 192 | + CreateDataProperty(a, n, kValue); |
| 193 | + } |
| 194 | + |
| 195 | + // d. Increase k by 1. |
| 196 | + k++; |
| 197 | + |
| 198 | + // e. Increase n by 1. |
| 199 | + n++; |
| 200 | + } |
| 201 | + |
| 202 | + // 11. Perform ? Set(A, "length", n, true). |
| 203 | + SetProperty(a, kLengthString, n); |
| 204 | + |
| 205 | + // 12. Return A. |
| 206 | + return a; |
| 207 | + } |
| 208 | +} |
0 commit comments