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/arguments-inl.h"
6 #include "src/elements.h"
7 #include "src/heap/factory.h"
8 #include "src/heap/heap-inl.h"
9 #include "src/messages.h"
10 #include "src/objects-inl.h"
11 #include "src/objects/js-array-buffer-inl.h"
12 #include "src/runtime/runtime-utils.h"
13 #include "src/runtime/runtime.h"
14
15 namespace v8 {
16 namespace internal {
17
RUNTIME_FUNCTION(Runtime_ArrayBufferNeuter)18 RUNTIME_FUNCTION(Runtime_ArrayBufferNeuter) {
19 HandleScope scope(isolate);
20 DCHECK_EQ(1, args.length());
21 Handle<Object> argument = args.at(0);
22 // This runtime function is exposed in ClusterFuzz and as such has to
23 // support arbitrary arguments.
24 if (!argument->IsJSArrayBuffer()) {
25 THROW_NEW_ERROR_RETURN_FAILURE(
26 isolate, NewTypeError(MessageTemplate::kNotTypedArray));
27 }
28 Handle<JSArrayBuffer> array_buffer = Handle<JSArrayBuffer>::cast(argument);
29 if (!array_buffer->is_neuterable()) {
30 return ReadOnlyRoots(isolate).undefined_value();
31 }
32 if (array_buffer->backing_store() == nullptr) {
33 CHECK_EQ(Smi::kZero, array_buffer->byte_length());
34 return ReadOnlyRoots(isolate).undefined_value();
35 }
36 // Shared array buffers should never be neutered.
37 CHECK(!array_buffer->is_shared());
38 DCHECK(!array_buffer->is_external());
39 void* backing_store = array_buffer->backing_store();
40 size_t byte_length = NumberToSize(array_buffer->byte_length());
41 array_buffer->set_is_external(true);
42 isolate->heap()->UnregisterArrayBuffer(*array_buffer);
43 array_buffer->Neuter();
44 isolate->array_buffer_allocator()->Free(backing_store, byte_length);
45 return ReadOnlyRoots(isolate).undefined_value();
46 }
47
RUNTIME_FUNCTION(Runtime_TypedArrayCopyElements)48 RUNTIME_FUNCTION(Runtime_TypedArrayCopyElements) {
49 HandleScope scope(isolate);
50 DCHECK_EQ(3, args.length());
51 CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, target, 0);
52 CONVERT_ARG_HANDLE_CHECKED(Object, source, 1);
53 CONVERT_NUMBER_ARG_HANDLE_CHECKED(length_obj, 2);
54
55 size_t length;
56 CHECK(TryNumberToSize(*length_obj, &length));
57
58 ElementsAccessor* accessor = target->GetElementsAccessor();
59 return accessor->CopyElements(source, target, length);
60 }
61
RUNTIME_FUNCTION(Runtime_TypedArrayGetLength)62 RUNTIME_FUNCTION(Runtime_TypedArrayGetLength) {
63 HandleScope scope(isolate);
64 DCHECK_EQ(1, args.length());
65 CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
66 return holder->length();
67 }
68
RUNTIME_FUNCTION(Runtime_ArrayBufferViewWasNeutered)69 RUNTIME_FUNCTION(Runtime_ArrayBufferViewWasNeutered) {
70 HandleScope scope(isolate);
71 DCHECK_EQ(1, args.length());
72 return isolate->heap()->ToBoolean(JSTypedArray::cast(args[0])->WasNeutered());
73 }
74
RUNTIME_FUNCTION(Runtime_TypedArrayGetBuffer)75 RUNTIME_FUNCTION(Runtime_TypedArrayGetBuffer) {
76 HandleScope scope(isolate);
77 DCHECK_EQ(1, args.length());
78 CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
79 return *holder->GetBuffer();
80 }
81
82
83 namespace {
84
85 template <typename T>
CompareNum(T x,T y)86 bool CompareNum(T x, T y) {
87 if (x < y) {
88 return true;
89 } else if (x > y) {
90 return false;
91 } else if (!std::is_integral<T>::value) {
92 double _x = x, _y = y;
93 if (x == 0 && x == y) {
94 /* -0.0 is less than +0.0 */
95 return std::signbit(_x) && !std::signbit(_y);
96 } else if (!std::isnan(_x) && std::isnan(_y)) {
97 /* number is less than NaN */
98 return true;
99 }
100 }
101 return false;
102 }
103
104 } // namespace
105
RUNTIME_FUNCTION(Runtime_TypedArraySortFast)106 RUNTIME_FUNCTION(Runtime_TypedArraySortFast) {
107 HandleScope scope(isolate);
108 DCHECK_EQ(1, args.length());
109
110 CONVERT_ARG_HANDLE_CHECKED(Object, target_obj, 0);
111
112 Handle<JSTypedArray> array;
113 const char* method = "%TypedArray%.prototype.sort";
114 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
115 isolate, array, JSTypedArray::Validate(isolate, target_obj, method));
116
117 // This line can be removed when JSTypedArray::Validate throws
118 // if array.[[ViewedArrayBuffer]] is neutered(v8:4648)
119 if (V8_UNLIKELY(array->WasNeutered())) return *array;
120
121 size_t length = array->length_value();
122 if (length <= 1) return *array;
123
124 Handle<FixedTypedArrayBase> elements(
125 FixedTypedArrayBase::cast(array->elements()), isolate);
126 switch (array->type()) {
127 #define TYPED_ARRAY_SORT(Type, type, TYPE, ctype) \
128 case kExternal##Type##Array: { \
129 ctype* data = static_cast<ctype*>(elements->DataPtr()); \
130 if (kExternal##Type##Array == kExternalFloat64Array || \
131 kExternal##Type##Array == kExternalFloat32Array) \
132 std::sort(data, data + length, CompareNum<ctype>); \
133 else \
134 std::sort(data, data + length); \
135 break; \
136 }
137
138 TYPED_ARRAYS(TYPED_ARRAY_SORT)
139 #undef TYPED_ARRAY_SORT
140 }
141
142 return *array;
143 }
144
RUNTIME_FUNCTION(Runtime_IsTypedArray)145 RUNTIME_FUNCTION(Runtime_IsTypedArray) {
146 HandleScope scope(isolate);
147 DCHECK_EQ(1, args.length());
148 return isolate->heap()->ToBoolean(args[0]->IsJSTypedArray());
149 }
150
151 // 22.2.3.23 %TypedArray%.prototype.set ( overloaded [ , offset ] )
RUNTIME_FUNCTION(Runtime_TypedArraySet)152 RUNTIME_FUNCTION(Runtime_TypedArraySet) {
153 HandleScope scope(isolate);
154 Handle<JSTypedArray> target = args.at<JSTypedArray>(0);
155 Handle<Object> obj = args.at(1);
156 Handle<Smi> offset = args.at<Smi>(2);
157
158 DCHECK(!target->WasNeutered()); // Checked in TypedArrayPrototypeSet.
159 DCHECK(!obj->IsJSTypedArray()); // Should be handled by CSA.
160 DCHECK_LE(0, offset->value());
161
162 const uint32_t uint_offset = static_cast<uint32_t>(offset->value());
163
164 if (obj->IsNumber()) {
165 // For number as a first argument, throw TypeError
166 // instead of silently ignoring the call, so that
167 // users know they did something wrong.
168 // (Consistent with Firefox and Blink/WebKit)
169 THROW_NEW_ERROR_RETURN_FAILURE(
170 isolate, NewTypeError(MessageTemplate::kInvalidArgument));
171 }
172
173 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, obj,
174 Object::ToObject(isolate, obj));
175
176 Handle<Object> len;
177 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
178 isolate, len,
179 Object::GetProperty(isolate, obj, isolate->factory()->length_string()));
180 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, len,
181 Object::ToLength(isolate, len));
182
183 if (uint_offset + len->Number() > target->length_value()) {
184 THROW_NEW_ERROR_RETURN_FAILURE(
185 isolate, NewRangeError(MessageTemplate::kTypedArraySetSourceTooLarge));
186 }
187
188 uint32_t int_l;
189 CHECK(DoubleToUint32IfEqualToSelf(len->Number(), &int_l));
190
191 Handle<JSReceiver> source = Handle<JSReceiver>::cast(obj);
192 ElementsAccessor* accessor = target->GetElementsAccessor();
193 return accessor->CopyElements(source, target, int_l, uint_offset);
194 }
195
196 } // namespace internal
197 } // namespace v8
198