js-array.tq 11.3 KB
Newer Older
Tobias Tebbi's avatar
Tobias Tebbi committed
1 2 3 4
// Copyright 2019 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.

5
extern enum IterationKind extends uint31 { kKeys, kValues, kEntries }
6

Tobias Tebbi's avatar
Tobias Tebbi committed
7 8
extern class JSArrayIterator extends JSObject {
  iterated_object: JSReceiver;
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32

  // [next_index]: The [[ArrayIteratorNextIndex]] inobject property.
  // The next_index is always a positive integer, and it points to
  // the next index that is to be returned by this iterator. It's
  // possible range is fixed depending on the [[iterated_object]]:
  //
  //   1. For JSArray's the next_index is always in Unsigned32
  //      range, and when the iterator reaches the end it's set
  //      to kMaxUInt32 to indicate that this iterator should
  //      never produce values anymore even if the "length"
  //      property of the JSArray changes at some later point.
  //   2. For JSTypedArray's the next_index is always in
  //      UnsignedSmall range, and when the iterator terminates
  //      it's set to Smi::kMaxValue.
  //   3. For all other JSReceiver's it's always between 0 and
  //      kMaxSafeInteger, and the latter value is used to mark
  //      termination.
  //
  // It's important that for 1. and 2. the value fits into the
  // Unsigned32 range (UnsignedSmall is a subset of Unsigned32),
  // since we use this knowledge in the fast-path for the array
  // iterator next calls in TurboFan (in the JSCallReducer) to
  // keep the index in Word32 representation. This invariant is
  // checked in JSArrayIterator::JSArrayIteratorVerify().
Tobias Tebbi's avatar
Tobias Tebbi committed
33
  next_index: Number;
34

35 36 37 38 39 40 41 42
  kind: SmiTagged<IterationKind>;
}

// Perform CreateArrayIterator (ES #sec-createarrayiterator).
@export
macro CreateArrayIterator(implicit context: NativeContext)(
    array: JSReceiver, kind: constexpr IterationKind): JSArrayIterator {
  return new JSArrayIterator{
43
    map: *NativeContextSlot(ContextSlot::INITIAL_ARRAY_ITERATOR_MAP_INDEX),
44 45 46 47 48 49
    properties_or_hash: kEmptyFixedArray,
    elements: kEmptyFixedArray,
    iterated_object: array,
    next_index: 0,
    kind: SmiTag<IterationKind>(kind)
  };
Tobias Tebbi's avatar
Tobias Tebbi committed
50 51 52
}

extern class JSArray extends JSObject {
53
  macro IsEmpty(): bool {
Tobias Tebbi's avatar
Tobias Tebbi committed
54 55 56 57 58
    return this.length == 0;
  }
  length: Number;
}

59 60 61 62
@doNotGenerateCast
extern class JSArrayConstructor extends JSFunction
    generates 'TNode<JSFunction>';

Tobias Tebbi's avatar
Tobias Tebbi committed
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
macro NewJSArray(implicit context: Context)(
    map: Map, elements: FixedArrayBase): JSArray {
  return new JSArray{
    map,
    properties_or_hash: kEmptyFixedArray,
    elements,
    length: elements.length
  };
}

macro NewJSArray(implicit context: Context)(): JSArray {
  return new JSArray{
    map: GetFastPackedSmiElementsJSArrayMap(),
    properties_or_hash: kEmptyFixedArray,
    elements: kEmptyFixedArray,
    length: 0
  };
}

// A HeapObject with a JSArray map, and either fast packed elements, or fast
// holey elements when the global NoElementsProtector is not invalidated.
transient type FastJSArray extends JSArray;

// A HeapObject with a JSArray map, and either fast packed elements, or fast
// holey elements or frozen, sealed elements when the global NoElementsProtector
// is not invalidated.
transient type FastJSArrayForRead extends JSArray;

// A FastJSArray when the global ArraySpeciesProtector is not invalidated.
transient type FastJSArrayForCopy extends FastJSArray;

94 95 96
// A FastJSArrayForCopy when the global IsConcatSpreadableProtector is not
// invalidated.
transient type FastJSArrayForConcat extends FastJSArrayForCopy;
97

Tobias Tebbi's avatar
Tobias Tebbi committed
98 99 100 101 102 103 104 105 106 107
// A FastJSArray when the global ArrayIteratorProtector is not invalidated.
transient type FastJSArrayWithNoCustomIteration extends FastJSArray;

// A FastJSArrayForRead when the global ArrayIteratorProtector is not
// invalidated.
transient type FastJSArrayForReadWithNoCustomIteration extends
    FastJSArrayForRead;

extern macro AllocateJSArray(
    constexpr ElementsKind, Map, intptr, Smi,
108
    constexpr AllocationFlag): JSArray;
Tobias Tebbi's avatar
Tobias Tebbi committed
109 110 111 112 113 114 115 116 117 118 119
extern macro AllocateJSArray(constexpr ElementsKind, Map, intptr, Smi): JSArray;
extern macro AllocateJSArray(constexpr ElementsKind, Map, Smi, Smi): JSArray;
extern macro AllocateJSArray(Map, FixedArrayBase, Smi): JSArray;

macro LoadElementNoHole<T : type extends FixedArrayBase>(
    a: JSArray, index: Smi): JSAny
    labels IfHole;

LoadElementNoHole<FixedArray>(implicit context: Context)(
    a: JSArray, index: Smi): JSAny
    labels IfHole {
120 121 122 123 124 125 126 127 128
  const elements: FixedArray =
      Cast<FixedArray>(a.elements) otherwise unreachable;
  const e = UnsafeCast<(JSAny | TheHole)>(elements.objects[index]);
  typeswitch (e) {
    case (TheHole): {
      goto IfHole;
    }
    case (e: JSAny): {
      return e;
Tobias Tebbi's avatar
Tobias Tebbi committed
129 130 131 132 133 134 135
    }
  }
}

LoadElementNoHole<FixedDoubleArray>(implicit context: Context)(
    a: JSArray, index: Smi): JSAny
    labels IfHole {
136 137 138 139
  const elements: FixedDoubleArray =
      Cast<FixedDoubleArray>(a.elements) otherwise unreachable;
  const e: float64 = elements.floats[index].Value() otherwise IfHole;
  return AllocateHeapNumberWithValue(e);
Tobias Tebbi's avatar
Tobias Tebbi committed
140 141 142 143 144 145 146 147 148
}

extern builtin ExtractFastJSArray(Context, JSArray, Smi, Smi): JSArray;

extern macro MoveElements(
    constexpr ElementsKind, FixedArrayBase, intptr, intptr, intptr): void;
macro TorqueMoveElementsSmi(
    elements: FixedArray, dstIndex: intptr, srcIndex: intptr,
    count: intptr): void {
149 150
  MoveElements(
      ElementsKind::HOLEY_SMI_ELEMENTS, elements, dstIndex, srcIndex, count);
Tobias Tebbi's avatar
Tobias Tebbi committed
151 152 153 154
}
macro TorqueMoveElements(
    elements: FixedArray, dstIndex: intptr, srcIndex: intptr,
    count: intptr): void {
155 156
  MoveElements(
      ElementsKind::HOLEY_ELEMENTS, elements, dstIndex, srcIndex, count);
Tobias Tebbi's avatar
Tobias Tebbi committed
157 158 159 160
}
macro TorqueMoveElements(
    elements: FixedDoubleArray, dstIndex: intptr, srcIndex: intptr,
    count: intptr): void {
161 162
  MoveElements(
      ElementsKind::HOLEY_DOUBLE_ELEMENTS, elements, dstIndex, srcIndex, count);
Tobias Tebbi's avatar
Tobias Tebbi committed
163 164 165 166 167 168 169 170 171
}

extern macro CopyElements(
    constexpr ElementsKind, FixedArrayBase, intptr, FixedArrayBase, intptr,
    intptr): void;
macro TorqueCopyElements(
    dstElements: FixedArray, dstIndex: intptr, srcElements: FixedArray,
    srcIndex: intptr, count: intptr): void {
  CopyElements(
172 173
      ElementsKind::HOLEY_ELEMENTS, dstElements, dstIndex, srcElements,
      srcIndex, count);
Tobias Tebbi's avatar
Tobias Tebbi committed
174 175 176 177 178
}
macro TorqueCopyElements(
    dstElements: FixedDoubleArray, dstIndex: intptr,
    srcElements: FixedDoubleArray, srcIndex: intptr, count: intptr): void {
  CopyElements(
179 180
      ElementsKind::HOLEY_DOUBLE_ELEMENTS, dstElements, dstIndex, srcElements,
      srcIndex, count);
Tobias Tebbi's avatar
Tobias Tebbi committed
181 182 183 184 185
}

extern builtin CloneFastJSArray(Context, FastJSArrayForCopy): JSArray;

struct FastJSArrayWitness {
186
  macro Get(): FastJSArray {
Tobias Tebbi's avatar
Tobias Tebbi committed
187 188 189
    return this.unstable;
  }

190
  macro Recheck(): void labels CastError {
Tobias Tebbi's avatar
Tobias Tebbi committed
191 192 193 194 195 196 197 198 199 200 201
    if (this.stable.map != this.map) goto CastError;
    // We don't need to check elements kind or whether the prototype
    // has changed away from the default JSArray prototype, because
    // if the map remains the same then those properties hold.
    //
    // However, we have to make sure there are no elements in the
    // prototype chain.
    if (IsNoElementsProtectorCellInvalid()) goto CastError;
    this.unstable = %RawDownCast<FastJSArray>(this.stable);
  }

202
  macro LoadElementNoHole(implicit context: Context)(k: Smi): JSAny
Tobias Tebbi's avatar
Tobias Tebbi committed
203 204 205 206 207 208 209 210 211 212
      labels FoundHole {
    if (this.hasDoubles) {
      return LoadElementNoHole<FixedDoubleArray>(this.unstable, k)
          otherwise FoundHole;
    } else {
      return LoadElementNoHole<FixedArray>(this.unstable, k)
          otherwise FoundHole;
    }
  }

213
  macro StoreHole(k: Smi): void {
Tobias Tebbi's avatar
Tobias Tebbi committed
214 215 216
    if (this.hasDoubles) {
      const elements = Cast<FixedDoubleArray>(this.unstable.elements)
          otherwise unreachable;
217
      elements.floats[k] = kDoubleHole;
Tobias Tebbi's avatar
Tobias Tebbi committed
218 219 220
    } else {
      const elements = Cast<FixedArray>(this.unstable.elements)
          otherwise unreachable;
221
      elements.objects[k] = TheHole;
Tobias Tebbi's avatar
Tobias Tebbi committed
222 223 224
    }
  }

225
  macro LoadElementOrUndefined(implicit context: Context)(k: Smi): JSAny {
Tobias Tebbi's avatar
Tobias Tebbi committed
226 227
    try {
      return this.LoadElementNoHole(k) otherwise FoundHole;
228
    } label FoundHole {
Tobias Tebbi's avatar
Tobias Tebbi committed
229 230 231 232
      return Undefined;
    }
  }

233
  macro EnsureArrayPushable(implicit context: Context)(): void labels Failed {
Tobias Tebbi's avatar
Tobias Tebbi committed
234 235 236 237 238
    EnsureArrayPushable(this.map) otherwise Failed;
    array::EnsureWriteableFastElements(this.unstable);
    this.arrayIsPushable = true;
  }

239
  macro ChangeLength(newLength: Smi): void {
240
    dcheck(this.arrayIsPushable);
Tobias Tebbi's avatar
Tobias Tebbi committed
241 242 243
    this.unstable.length = newLength;
  }

244
  macro Push(value: JSAny): void labels Failed {
245
    dcheck(this.arrayIsPushable);
Tobias Tebbi's avatar
Tobias Tebbi committed
246
    if (this.hasDoubles) {
247 248
      BuildAppendJSArray(
          ElementsKind::HOLEY_DOUBLE_ELEMENTS, this.unstable, value)
Tobias Tebbi's avatar
Tobias Tebbi committed
249 250
          otherwise Failed;
    } else if (this.hasSmis) {
251
      BuildAppendJSArray(ElementsKind::HOLEY_SMI_ELEMENTS, this.unstable, value)
Tobias Tebbi's avatar
Tobias Tebbi committed
252 253
          otherwise Failed;
    } else {
254
      dcheck(
255 256 257
          this.map.elements_kind == ElementsKind::HOLEY_ELEMENTS ||
          this.map.elements_kind == ElementsKind::PACKED_ELEMENTS);
      BuildAppendJSArray(ElementsKind::HOLEY_ELEMENTS, this.unstable, value)
Tobias Tebbi's avatar
Tobias Tebbi committed
258 259 260 261
          otherwise Failed;
    }
  }

262
  macro MoveElements(dst: intptr, src: intptr, length: intptr): void {
263
    dcheck(this.arrayIsPushable);
Tobias Tebbi's avatar
Tobias Tebbi committed
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
    if (this.hasDoubles) {
      const elements: FixedDoubleArray =
          Cast<FixedDoubleArray>(this.unstable.elements)
          otherwise unreachable;
      TorqueMoveElements(elements, dst, src, length);
    } else {
      const elements: FixedArray = Cast<FixedArray>(this.unstable.elements)
          otherwise unreachable;
      if (this.hasSmis) {
        TorqueMoveElementsSmi(elements, dst, src, length);
      } else {
        TorqueMoveElements(elements, dst, src, length);
      }
    }
  }

  const stable: JSArray;
  unstable: FastJSArray;
  const map: Map;
  const hasDoubles: bool;
  const hasSmis: bool;
  arrayIsPushable: bool;
}

macro NewFastJSArrayWitness(array: FastJSArray): FastJSArrayWitness {
  const kind = array.map.elements_kind;
  return FastJSArrayWitness{
    stable: array,
    unstable: array,
    map: array.map,
    hasDoubles: IsDoubleElementsKind(kind),
295 296
    hasSmis:
        IsElementsKindLessThanOrEqual(kind, ElementsKind::HOLEY_SMI_ELEMENTS),
Tobias Tebbi's avatar
Tobias Tebbi committed
297 298 299 300 301
    arrayIsPushable: false
  };
}

struct FastJSArrayForReadWitness {
302
  macro Get(): FastJSArrayForRead {
Tobias Tebbi's avatar
Tobias Tebbi committed
303 304 305
    return this.unstable;
  }

306
  macro Recheck(): void labels CastError {
Tobias Tebbi's avatar
Tobias Tebbi committed
307 308 309 310 311 312 313 314 315 316 317
    if (this.stable.map != this.map) goto CastError;
    // We don't need to check elements kind or whether the prototype
    // has changed away from the default JSArray prototype, because
    // if the map remains the same then those properties hold.
    //
    // However, we have to make sure there are no elements in the
    // prototype chain.
    if (IsNoElementsProtectorCellInvalid()) goto CastError;
    this.unstable = %RawDownCast<FastJSArrayForRead>(this.stable);
  }

318
  macro LoadElementNoHole(implicit context: Context)(k: Smi): JSAny
Tobias Tebbi's avatar
Tobias Tebbi committed
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
      labels FoundHole {
    if (this.hasDoubles) {
      return LoadElementNoHole<FixedDoubleArray>(this.unstable, k)
          otherwise FoundHole;
    } else {
      return LoadElementNoHole<FixedArray>(this.unstable, k)
          otherwise FoundHole;
    }
  }

  const stable: JSArray;
  unstable: FastJSArrayForRead;
  const map: Map;
  const hasDoubles: bool;
}

macro NewFastJSArrayForReadWitness(array: FastJSArrayForRead):
    FastJSArrayForReadWitness {
  const kind = array.map.elements_kind;
  return FastJSArrayForReadWitness{
    stable: array,
    unstable: array,
    map: array.map,
    hasDoubles: IsDoubleElementsKind(kind)
  };
}