typed-array-set.tq 12.5 KB
Newer Older
1 2 3 4 5 6 7
// 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.

#include 'src/builtins/builtins-typed-array-gen.h'

namespace typed_array {
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
const kBuiltinNameSet: constexpr string = '%TypedArray%.prototype.set';

extern runtime TypedArraySet(
    Context, JSTypedArray, Object, Number, Number): void;

extern macro
TypedArrayBuiltinsAssembler::CallCCopyFastNumberJSArrayElementsToTypedArray(
    Context,
    FastJSArray,           // source
    AttachedJSTypedArray,  // dest
    uintptr,               // sourceLength
    uintptr                // destOffset
    ): void;

extern macro
TypedArrayBuiltinsAssembler::CallCCopyTypedArrayElementsToTypedArray(
    AttachedJSTypedArray,  // source
    AttachedJSTypedArray,  // dest
    uintptr,               // sourceLength
    uintptr                // destOffset
    ): void;

// %TypedArray%.prototype.set ( overloaded [ , offset ] )
// https://tc39.es/ecma262/#sec-%typedarray%.prototype.set-overloaded-offset
transitioning javascript builtin
TypedArrayPrototypeSet(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
  // Steps 2-8 are the same for
  // %TypedArray%.prototype.set ( array [ , offset ] ) and
  // %TypedArray%.prototype.set ( typedArray [ , offset ] ) overloads.

  let target: JSTypedArray;
  try {
    // 2. Let target be the this value.
    // 3. Perform ? RequireInternalSlot(target, [[TypedArrayName]]).
    // 4. Assert: target has a [[ViewedArrayBuffer]] internal slot.
    target = Cast<JSTypedArray>(receiver) otherwise NotTypedArray;
  } label NotTypedArray deferred {
    ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameSet);
  }
48

49 50 51 52 53 54 55
  try {
    // 5. Let targetOffset be ? ToInteger(offset).
    // 6. If targetOffset < 0, throw a RangeError exception.
    let targetOffsetOverflowed: bool = false;
    let targetOffset: uintptr = 0;
    if (arguments.length > 1) {
      const offsetArg = arguments[1];
56
      try {
57 58 59 60 61 62 63 64
        targetOffset = ToUintPtr(offsetArg)
        // On values less than zero throw RangeError immediately.
            otherwise OffsetOutOfBounds,
            // On UintPtr or SafeInteger range overflow throw RangeError after
            // performing observable steps to follow the spec.
            OffsetOverflow, OffsetOverflow;
      } label OffsetOverflow {
        targetOffsetOverflowed = true;
65
      }
66 67
    } else {
      // If the offset argument is not provided then the targetOffset is 0.
68 69
    }

70 71 72 73
    // 7. Let targetBuffer be target.[[ViewedArrayBuffer]].
    // 8. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError
    //   exception.
    const utarget = typed_array::EnsureAttached(target) otherwise IsDetached;
74

75 76 77 78 79 80 81 82
    const overloadedArg = arguments[0];
    try {
      // 1. Choose 22.2.3.23.2 or 22.2.3.23.1 depending on whether the
      //   overloadedArg has a [[TypedArrayName]] internal slot.
      //   If it does, the definition in 22.2.3.23.2 applies.
      //   If it does not, the definition in 22.2.3.23.1 applies.
      const typedArray =
          Cast<JSTypedArray>(overloadedArg) otherwise NotTypedArray;
83

84
      // Step 9 is not observable, do it later.
85

86 87 88 89 90
      // 10. Let srcBuffer be typedArray.[[ViewedArrayBuffer]].
      // 11. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError
      //   exception.
      const utypedArray =
          typed_array::EnsureAttached(typedArray) otherwise IsDetached;
91

92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
      TypedArrayPrototypeSetTypedArray(
          utarget, utypedArray, targetOffset, targetOffsetOverflowed)
          otherwise OffsetOutOfBounds;
      return Undefined;
    } label NotTypedArray deferred {
      TypedArrayPrototypeSetArray(
          utarget, overloadedArg, targetOffset, targetOffsetOverflowed)
          otherwise OffsetOutOfBounds, IsDetached;
      return Undefined;
    }
  } label OffsetOutOfBounds deferred {
    ThrowRangeError(MessageTemplate::kTypedArraySetOffsetOutOfBounds);
  } label IsDetached deferred {
    ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameSet);
  }
}
108

109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
// %TypedArray%.prototype.set ( array [ , offset ] )
// https://tc39.es/ecma262/#sec-%typedarray%.prototype.set-array-offset
transitioning macro
TypedArrayPrototypeSetArray(implicit context: Context, receiver: JSAny)(
    target: JSTypedArray, arrayArg: JSAny, targetOffset: uintptr,
    targetOffsetOverflowed: bool): void labels IfOffsetOutOfBounds,
    IfDetached {
  // Steps 9-13 are not observable, do them later.

  // TODO(v8:8906): This ported behaviour is an observable spec violation and
  // the comment below seems to be outdated. Consider removing this code.
  try {
    const _arrayArgNum = Cast<Number>(arrayArg) otherwise NotNumber;
    // For number as a first argument, throw TypeError instead of silently
    // ignoring the call, so that users know they did something wrong.
    // (Consistent with Firefox and Blink/WebKit)
    ThrowTypeError(MessageTemplate::kInvalidArgument);
  } label NotNumber {
    // Proceed to step 14.
  }
129

130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
  // 14. Let src be ? ToObject(array).
  const src: JSReceiver = ToObject_Inline(context, arrayArg);

  // 15. Let srcLength be ? LengthOfArrayLike(src).
  const srcLengthNum: Number = GetLengthProperty(src);

  if (targetOffsetOverflowed) goto IfOffsetOutOfBounds;

  // 9. Let targetLength be target.[[ArrayLength]].
  const targetLength = target.length;

  // 16. If srcLength + targetOffset > targetLength, throw a RangeError
  //   exception.
  const srcLength = ChangeSafeIntegerNumberToUintPtr(srcLengthNum)
      otherwise IfOffsetOutOfBounds;
  CheckIntegerIndexAdditionOverflow(srcLength, targetOffset, targetLength)
      otherwise IfOffsetOutOfBounds;

  // All the obvervable side effects are executed, so there's nothing else
  // to do with the empty source array.
  if (srcLength == 0) return;

  // 10. Let targetName be the String value of target.[[TypedArrayName]].
  // 11. Let targetElementSize be the Element Size value specified in
  //   Table 62 for targetName.
  // 12. Let targetType be the Element Type value in Table 62 for
  //   targetName.

  try {
    // BigInt typed arrays are not handled by
    // CopyFastNumberJSArrayElementsToTypedArray.
    if (IsBigInt64ElementsKind(target.elements_kind)) goto IfSlow;

    const fastSrc: FastJSArray = Cast<FastJSArray>(src) otherwise goto IfSlow;
    const srcKind: ElementsKind = fastSrc.map.elements_kind;

    // CopyFastNumberJSArrayElementsToTypedArray() can be used only with the
    // following elements kinds:
    // PACKED_SMI_ELEMENTS, HOLEY_SMI_ELEMENTS, PACKED_DOUBLE_ELEMENTS,
    // HOLEY_DOUBLE_ELEMENTS.
    if (IsElementsKindInRange(
            srcKind, ElementsKind::PACKED_SMI_ELEMENTS,
            ElementsKind::HOLEY_SMI_ELEMENTS) ||
        IsElementsKindInRange(
            srcKind, ElementsKind::PACKED_DOUBLE_ELEMENTS,
            ElementsKind::HOLEY_DOUBLE_ELEMENTS)) {
      const utarget = typed_array::EnsureAttached(target) otherwise IfDetached;
      CallCCopyFastNumberJSArrayElementsToTypedArray(
          context, fastSrc, utarget, srcLength, targetOffset);

    } else {
      goto IfSlow;
    }
  } label IfSlow deferred {
    TypedArraySet(
        context, target, src, srcLengthNum, Convert<Number>(targetOffset));
  }
}
188

189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
// %TypedArray%.prototype.set ( typedArray [ , offset ] )
// https://tc39.es/ecma262/#sec-%typedarray%.prototype.set-typedarray-offset
transitioning macro
TypedArrayPrototypeSetTypedArray(implicit context: Context, receiver: JSAny)(
    target: AttachedJSTypedArray, typedArray: AttachedJSTypedArray,
    targetOffset: uintptr,
    targetOffsetOverflowed: bool): void labels IfOffsetOutOfBounds {
  // Steps 12-20 are not observable, so we can handle offset overflow
  // at step 21 here.
  if (targetOffsetOverflowed) goto IfOffsetOutOfBounds;

  // 9. Let targetLength be target.[[ArrayLength]].
  const targetLength = target.length;

  // 19. Let srcLength be typedArray.[[ArrayLength]].
  const srcLength: uintptr = typedArray.length;

  // Steps 12-20 are not observable, so we can do step 21 here.

  // 21. If srcLength + targetOffset > targetLength, throw a RangeError
  //   exception.
  CheckIntegerIndexAdditionOverflow(srcLength, targetOffset, targetLength)
      otherwise IfOffsetOutOfBounds;

  // 12. Let targetName be the String value of target.[[TypedArrayName]].
  // 13. Let targetType be the Element Type value in Table 62 for
  //   targetName.
  // 14. Let targetElementSize be the Element Size value specified in
  //   Table 62 for targetName.
  const targetElementsInfo = GetTypedArrayElementsInfo(target);

  // 16. Let srcName be the String value of typedArray.[[TypedArrayName]].
  // 17. Let srcType be the Element Type value in Table 62 for srcName.
  // 18. Let srcElementSize be the Element Size value specified in
  //   Table 62 for srcName.
  const srcKind: ElementsKind = typedArray.elements_kind;
  // const srcElementsInfo = GetTypedArrayElementsInfo(typedArray);

  // We skip steps 23-25 because both memmove and
  // CopyTypedArrayElementsToTypedArray() properly handle overlapping
  // regions.

  // 23. If both IsSharedArrayBuffer(srcBuffer) and
  //   IsSharedArrayBuffer(targetBuffer) are true, then
  // 23a. If srcBuffer.[[ArrayBufferData]] and
  //   targetBuffer.[[ArrayBufferData]] are the same Shared Data Block
  //   values, let same be true; else let same be false.
  // 24. Else, let same be SameValue(srcBuffer, targetBuffer).
  // 25. If same is true, then
  //   a. Let srcByteLength be typedArray.[[ByteLength]].
  //   b. Set srcBuffer to ? CloneArrayBuffer(srcBuffer, srcByteOffset,
  //    srcByteLength, %ArrayBuffer%).
  //   c. NOTE: %ArrayBuffer% is used to clone srcBuffer because is it known
  //    to not have any observable side-effects.
  //   d. Let srcByteIndex be 0.

  try {
    // Use memmove if possible.
    if (srcKind != targetElementsInfo.kind) {
      // Uint8/Uint8Clamped elements could still be copied with memmove.
      if (!IsUint8ElementsKind(srcKind) ||
          !IsUint8ElementsKind(targetElementsInfo.kind)) {
251 252 253 254
        goto IfSlow;
      }
    }

255 256 257
    // All the obvervable side effects are executed, so there's nothing else
    // to do with the empty source array.
    if (srcLength == 0) return;
258

259 260 261 262 263 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
    // Source and destination typed arrays have same elements kinds (modulo
    // Uint8-Uint8Clamped difference) so we can use targetElementsInfo for
    // calculations.
    const countBytes: uintptr =
        targetElementsInfo.CalculateByteLength(srcLength)
        otherwise unreachable;
    const startOffset: uintptr =
        targetElementsInfo.CalculateByteLength(targetOffset)
        otherwise unreachable;
    const dstPtr: RawPtr = target.data_ptr + Convert<intptr>(startOffset);

    assert(countBytes <= target.byte_length - startOffset);
    assert(countBytes <= typedArray.byte_length);

    // 29. If srcType is the same as targetType, then
    //   a. NOTE: If srcType and targetType are the same, the transfer must
    //      be performed in a manner that preserves the bit-level encoding of
    //      the source data.
    //   b. Repeat, while targetByteIndex < limit
    //      i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, Uint8,
    //                                         true, Unordered).
    //     ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex, Uint8,
    //                                  value, true, Unordered).
    //    iii. Set srcByteIndex to srcByteIndex + 1.
    //     iv. Set targetByteIndex to targetByteIndex + 1.
    CallCMemmove(dstPtr, typedArray.data_ptr, countBytes);
  } label IfSlow deferred {
    // 22. If target.[[ContentType]] is not equal to
    //   typedArray.[[ContentType]], throw a TypeError exception.
    if (IsBigInt64ElementsKind(srcKind) !=
        IsBigInt64ElementsKind(targetElementsInfo.kind))
      deferred {
        ThrowTypeError(MessageTemplate::kBigIntMixedTypes);
292 293
      }

294 295 296 297 298 299 300 301 302 303 304 305 306 307
    // All the obvervable side effects are executed, so there's nothing else
    // to do with the empty source array.
    if (srcLength == 0) return;

    // 30. Else,
    //   a. Repeat, while targetByteIndex < limit
    //      i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex,
    //                                         srcType, true, Unordered).
    //     ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex,
    //                                  targetType, value, true, Unordered).
    //    iii. Set srcByteIndex to srcByteIndex + srcElementSize.
    //     iv. Set targetByteIndex to targetByteIndex + targetElementSize.
    CallCCopyTypedArrayElementsToTypedArray(
        typedArray, target, srcLength, targetOffset);
308 309
  }
}
310
}