1 // Copyright 2014 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 #include "src/common/message-template.h"
6 #include "src/execution/arguments-inl.h"
7 #include "src/heap/factory.h"
8 #include "src/heap/heap-inl.h"
9 #include "src/logging/counters.h"
10 #include "src/objects/elements.h"
11 #include "src/objects/js-array-buffer-inl.h"
12 #include "src/objects/objects-inl.h"
13 #include "src/runtime/runtime-utils.h"
14 #include "src/runtime/runtime.h"
15 
16 namespace v8 {
17 namespace internal {
18 
RUNTIME_FUNCTION(Runtime_ArrayBufferDetach)19 RUNTIME_FUNCTION(Runtime_ArrayBufferDetach) {
20   HandleScope scope(isolate);
21   DCHECK_EQ(1, args.length());
22   Handle<Object> argument = args.at(0);
23   // This runtime function is exposed in ClusterFuzz and as such has to
24   // support arbitrary arguments.
25   if (!argument->IsJSArrayBuffer()) {
26     THROW_NEW_ERROR_RETURN_FAILURE(
27         isolate, NewTypeError(MessageTemplate::kNotTypedArray));
28   }
29   Handle<JSArrayBuffer> array_buffer = Handle<JSArrayBuffer>::cast(argument);
30   array_buffer->Detach();
31   return ReadOnlyRoots(isolate).undefined_value();
32 }
33 
RUNTIME_FUNCTION(Runtime_TypedArrayCopyElements)34 RUNTIME_FUNCTION(Runtime_TypedArrayCopyElements) {
35   HandleScope scope(isolate);
36   DCHECK_EQ(3, args.length());
37   CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, target, 0);
38   CONVERT_ARG_HANDLE_CHECKED(Object, source, 1);
39   CONVERT_NUMBER_ARG_HANDLE_CHECKED(length_obj, 2);
40 
41   size_t length;
42   CHECK(TryNumberToSize(*length_obj, &length));
43 
44   ElementsAccessor* accessor = target->GetElementsAccessor();
45   return accessor->CopyElements(source, target, length, 0);
46 }
47 
RUNTIME_FUNCTION(Runtime_TypedArrayGetBuffer)48 RUNTIME_FUNCTION(Runtime_TypedArrayGetBuffer) {
49   HandleScope scope(isolate);
50   DCHECK_EQ(1, args.length());
51   CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
52   return *holder->GetBuffer();
53 }
54 
55 
56 namespace {
57 
58 template <typename T>
CompareNum(T x,T y)59 bool CompareNum(T x, T y) {
60   if (x < y) {
61     return true;
62   } else if (x > y) {
63     return false;
64   } else if (!std::is_integral<T>::value) {
65     double _x = x, _y = y;
66     if (x == 0 && x == y) {
67       /* -0.0 is less than +0.0 */
68       return std::signbit(_x) && !std::signbit(_y);
69     } else if (!std::isnan(_x) && std::isnan(_y)) {
70       /* number is less than NaN */
71       return true;
72     }
73   }
74   return false;
75 }
76 
77 }  // namespace
78 
RUNTIME_FUNCTION(Runtime_TypedArraySortFast)79 RUNTIME_FUNCTION(Runtime_TypedArraySortFast) {
80   HandleScope scope(isolate);
81   DCHECK_EQ(1, args.length());
82 
83   // Validation is handled in the Torque builtin.
84   CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, array, 0);
85   DCHECK(!array->WasDetached());
86 
87   size_t length = array->length();
88   DCHECK_LT(1, length);
89 
90   // In case of a SAB, the data is copied into temporary memory, as
91   // std::sort might crash in case the underlying data is concurrently
92   // modified while sorting.
93   CHECK(array->buffer().IsJSArrayBuffer());
94   Handle<JSArrayBuffer> buffer(JSArrayBuffer::cast(array->buffer()), isolate);
95   const bool copy_data = buffer->is_shared();
96 
97   Handle<ByteArray> array_copy;
98   std::vector<uint8_t> offheap_copy;
99   void* data_copy_ptr = nullptr;
100   if (copy_data) {
101     const size_t bytes = array->byte_length();
102     if (bytes <= static_cast<unsigned>(
103                      ByteArray::LengthFor(kMaxRegularHeapObjectSize))) {
104       array_copy = isolate->factory()->NewByteArray(static_cast<int>(bytes));
105       data_copy_ptr = array_copy->GetDataStartAddress();
106     } else {
107       // Allocate copy in C++ heap.
108       offheap_copy.resize(bytes);
109       data_copy_ptr = &offheap_copy[0];
110     }
111     std::memcpy(data_copy_ptr, static_cast<void*>(array->DataPtr()), bytes);
112   }
113 
114   DisallowHeapAllocation no_gc;
115 
116   switch (array->type()) {
117 #define TYPED_ARRAY_SORT(Type, type, TYPE, ctype)                          \
118   case kExternal##Type##Array: {                                           \
119     ctype* data = copy_data ? reinterpret_cast<ctype*>(data_copy_ptr)      \
120                             : static_cast<ctype*>(array->DataPtr());       \
121     if (kExternal##Type##Array == kExternalFloat64Array ||                 \
122         kExternal##Type##Array == kExternalFloat32Array) {                 \
123       if (COMPRESS_POINTERS_BOOL && alignof(ctype) > kTaggedSize) {        \
124         /* TODO(ishell, v8:8875): See UnalignedSlot<T> for details. */     \
125         std::sort(UnalignedSlot<ctype>(data),                              \
126                   UnalignedSlot<ctype>(data + length), CompareNum<ctype>); \
127       } else {                                                             \
128         std::sort(data, data + length, CompareNum<ctype>);                 \
129       }                                                                    \
130     } else {                                                               \
131       if (COMPRESS_POINTERS_BOOL && alignof(ctype) > kTaggedSize) {        \
132         /* TODO(ishell, v8:8875): See UnalignedSlot<T> for details. */     \
133         std::sort(UnalignedSlot<ctype>(data),                              \
134                   UnalignedSlot<ctype>(data + length));                    \
135       } else {                                                             \
136         std::sort(data, data + length);                                    \
137       }                                                                    \
138     }                                                                      \
139     break;                                                                 \
140   }
141 
142     TYPED_ARRAYS(TYPED_ARRAY_SORT)
143 #undef TYPED_ARRAY_SORT
144   }
145 
146   if (copy_data) {
147     DCHECK_NOT_NULL(data_copy_ptr);
148     DCHECK_NE(array_copy.is_null(), offheap_copy.empty());
149     const size_t bytes = array->byte_length();
150     std::memcpy(static_cast<void*>(array->DataPtr()), data_copy_ptr, bytes);
151   }
152 
153   return *array;
154 }
155 
RUNTIME_FUNCTION(Runtime_TypedArraySet)156 RUNTIME_FUNCTION(Runtime_TypedArraySet) {
157   HandleScope scope(isolate);
158   DCHECK_EQ(4, args.length());
159   CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, target, 0);
160   CONVERT_ARG_HANDLE_CHECKED(Object, source, 1);
161   CONVERT_NUMBER_ARG_HANDLE_CHECKED(length_obj, 2);
162   CONVERT_NUMBER_ARG_HANDLE_CHECKED(offset_obj, 3);
163 
164   size_t length;
165   CHECK(TryNumberToSize(*length_obj, &length));
166 
167   size_t offset;
168   CHECK(TryNumberToSize(*offset_obj, &offset));
169 
170   ElementsAccessor* accessor = target->GetElementsAccessor();
171   return accessor->CopyElements(source, target, length, offset);
172 }
173 
174 }  // namespace internal
175 }  // namespace v8
176