runtime-typedarray.cc 8.52 KB
Newer Older
1 2 3 4
// Copyright 2014 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
#include "src/arguments-inl.h"
6
#include "src/counters.h"
7
#include "src/elements.h"
8
#include "src/heap/factory.h"
9
#include "src/heap/heap-inl.h"
10
#include "src/message-template.h"
11
#include "src/objects-inl.h"
12
#include "src/objects/js-array-buffer-inl.h"
13
#include "src/runtime/runtime-utils.h"
14 15 16 17 18
#include "src/runtime/runtime.h"

namespace v8 {
namespace internal {

19
RUNTIME_FUNCTION(Runtime_ArrayBufferDetach) {
20
  HandleScope scope(isolate);
21
  DCHECK_EQ(1, args.length());
22 23 24 25 26 27 28 29
  Handle<Object> argument = args.at(0);
  // This runtime function is exposed in ClusterFuzz and as such has to
  // support arbitrary arguments.
  if (!argument->IsJSArrayBuffer()) {
    THROW_NEW_ERROR_RETURN_FAILURE(
        isolate, NewTypeError(MessageTemplate::kNotTypedArray));
  }
  Handle<JSArrayBuffer> array_buffer = Handle<JSArrayBuffer>::cast(argument);
30
  if (!array_buffer->is_detachable()) {
31
    return ReadOnlyRoots(isolate).undefined_value();
32
  }
33
  if (array_buffer->backing_store() == nullptr) {
34
    CHECK_EQ(0, array_buffer->byte_length());
35
    return ReadOnlyRoots(isolate).undefined_value();
36
  }
37
  // Shared array buffers should never be detached.
38
  CHECK(!array_buffer->is_shared());
39 40
  DCHECK(!array_buffer->is_external());
  void* backing_store = array_buffer->backing_store();
41
  size_t byte_length = array_buffer->byte_length();
42
  array_buffer->set_is_external(true);
43
  isolate->heap()->UnregisterArrayBuffer(*array_buffer);
44
  array_buffer->Detach();
45
  isolate->array_buffer_allocator()->Free(backing_store, byte_length);
46
  return ReadOnlyRoots(isolate).undefined_value();
47 48
}

49 50 51
RUNTIME_FUNCTION(Runtime_TypedArrayCopyElements) {
  HandleScope scope(isolate);
  DCHECK_EQ(3, args.length());
52
  CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, target, 0);
53
  CONVERT_ARG_HANDLE_CHECKED(Object, source, 1);
54 55
  CONVERT_NUMBER_ARG_HANDLE_CHECKED(length_obj, 2);

56 57 58 59 60
  size_t length;
  CHECK(TryNumberToSize(*length_obj, &length));

  ElementsAccessor* accessor = target->GetElementsAccessor();
  return accessor->CopyElements(source, target, length);
61 62
}

63 64 65 66 67 68 69
RUNTIME_FUNCTION(Runtime_TypedArrayGetLength) {
  HandleScope scope(isolate);
  DCHECK_EQ(1, args.length());
  CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
  return holder->length();
}

70
RUNTIME_FUNCTION(Runtime_ArrayBufferViewWasDetached) {
71 72
  HandleScope scope(isolate);
  DCHECK_EQ(1, args.length());
73
  return isolate->heap()->ToBoolean(JSTypedArray::cast(args[0])->WasDetached());
74 75
}

76 77
RUNTIME_FUNCTION(Runtime_TypedArrayGetBuffer) {
  HandleScope scope(isolate);
78
  DCHECK_EQ(1, args.length());
79 80 81 82
  CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
  return *holder->GetBuffer();
}

83

84 85
namespace {

86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
template <typename T>
bool CompareNum(T x, T y) {
  if (x < y) {
    return true;
  } else if (x > y) {
    return false;
  } else if (!std::is_integral<T>::value) {
    double _x = x, _y = y;
    if (x == 0 && x == y) {
      /* -0.0 is less than +0.0 */
      return std::signbit(_x) && !std::signbit(_y);
    } else if (!std::isnan(_x) && std::isnan(_y)) {
      /* number is less than NaN */
      return true;
    }
101
  }
102 103
  return false;
}
104 105 106 107 108 109 110

}  // namespace

RUNTIME_FUNCTION(Runtime_TypedArraySortFast) {
  HandleScope scope(isolate);
  DCHECK_EQ(1, args.length());

111 112 113
  // Validation is handled in the Torque builtin.
  CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, array, 0);
  DCHECK(!array->WasDetached());
114

115
  size_t length = array->length_value();
116
  if (length <= 1) return *array;
117

118
  Handle<FixedTypedArrayBase> elements(
119
      FixedTypedArrayBase::cast(array->elements()), isolate);
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140

  // In case of a SAB, the data is copied into temporary memory, as
  // std::sort might crash in case the underlying data is concurrently
  // modified while sorting.
  CHECK(array->buffer()->IsJSArrayBuffer());
  Handle<JSArrayBuffer> buffer(JSArrayBuffer::cast(array->buffer()), isolate);
  const bool copy_data = buffer->is_shared();

  Handle<ByteArray> array_copy;
  if (copy_data) {
    const size_t bytes = array->byte_length();
    // TODO(szuend): Re-check this approach once support for larger typed
    //               arrays has landed.
    CHECK_LE(bytes, INT_MAX);
    array_copy = isolate->factory()->NewByteArray(static_cast<int>(bytes));
    std::memcpy(static_cast<void*>(array_copy->GetDataStartAddress()),
                static_cast<void*>(elements->DataPtr()), bytes);
  }

  DisallowHeapAllocation no_gc;

141
  switch (array->type()) {
142 143
#define TYPED_ARRAY_SORT(Type, type, TYPE, ctype)                          \
  case kExternal##Type##Array: {                                           \
144 145 146 147
    ctype* data =                                                          \
        copy_data                                                          \
            ? reinterpret_cast<ctype*>(array_copy->GetDataStartAddress())  \
            : static_cast<ctype*>(elements->DataPtr());                    \
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
    if (kExternal##Type##Array == kExternalFloat64Array ||                 \
        kExternal##Type##Array == kExternalFloat32Array) {                 \
      if (COMPRESS_POINTERS_BOOL && alignof(ctype) > kTaggedSize) {        \
        /* TODO(ishell, v8:8875): See UnalignedSlot<T> for details. */     \
        std::sort(UnalignedSlot<ctype>(data),                              \
                  UnalignedSlot<ctype>(data + length), CompareNum<ctype>); \
      } else {                                                             \
        std::sort(data, data + length, CompareNum<ctype>);                 \
      }                                                                    \
    } else {                                                               \
      if (COMPRESS_POINTERS_BOOL && alignof(ctype) > kTaggedSize) {        \
        /* TODO(ishell, v8:8875): See UnalignedSlot<T> for details. */     \
        std::sort(UnalignedSlot<ctype>(data),                              \
                  UnalignedSlot<ctype>(data + length));                    \
      } else {                                                             \
        std::sort(data, data + length);                                    \
      }                                                                    \
    }                                                                      \
    break;                                                                 \
167 168 169 170 171 172
  }

    TYPED_ARRAYS(TYPED_ARRAY_SORT)
#undef TYPED_ARRAY_SORT
  }

173 174 175 176 177 178 179
  if (copy_data) {
    DCHECK(!array_copy.is_null());
    const size_t bytes = array->byte_length();
    std::memcpy(static_cast<void*>(elements->DataPtr()),
                static_cast<void*>(array_copy->GetDataStartAddress()), bytes);
  }

180 181
  return *array;
}
182

183 184
RUNTIME_FUNCTION(Runtime_IsTypedArray) {
  HandleScope scope(isolate);
185
  DCHECK_EQ(1, args.length());
186 187 188
  return isolate->heap()->ToBoolean(args[0]->IsJSTypedArray());
}

189
// 22.2.3.23 %TypedArray%.prototype.set ( overloaded [ , offset ] )
190 191 192 193 194 195
RUNTIME_FUNCTION(Runtime_TypedArraySet) {
  HandleScope scope(isolate);
  Handle<JSTypedArray> target = args.at<JSTypedArray>(0);
  Handle<Object> obj = args.at(1);
  Handle<Smi> offset = args.at<Smi>(2);

196
  DCHECK(!target->WasDetached());  // Checked in TypedArrayPrototypeSet.
197
  DCHECK(!obj->IsJSTypedArray());  // Should be handled by CSA.
198
  DCHECK_LE(0, offset->value());
199 200 201

  const uint32_t uint_offset = static_cast<uint32_t>(offset->value());

202 203 204 205 206 207 208 209 210 211 212 213 214
  if (obj->IsNumber()) {
    // 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)
    THROW_NEW_ERROR_RETURN_FAILURE(
        isolate, NewTypeError(MessageTemplate::kInvalidArgument));
  }

  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, obj,
                                     Object::ToObject(isolate, obj));

  Handle<Object> len;
215
  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
216
      isolate, len,
217
      Object::GetProperty(isolate, obj, isolate->factory()->length_string()));
218 219 220
  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, len,
                                     Object::ToLength(isolate, len));

221
  if (uint_offset + len->Number() > target->length_value()) {
222 223
    THROW_NEW_ERROR_RETURN_FAILURE(
        isolate, NewRangeError(MessageTemplate::kTypedArraySetSourceTooLarge));
224 225
  }

226 227 228 229 230 231
  uint32_t int_l;
  CHECK(DoubleToUint32IfEqualToSelf(len->Number(), &int_l));

  Handle<JSReceiver> source = Handle<JSReceiver>::cast(obj);
  ElementsAccessor* accessor = target->GetElementsAccessor();
  return accessor->CopyElements(source, target, int_l, uint_offset);
232 233
}

234 235
}  // namespace internal
}  // namespace v8