1 // Copyright 2016 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/builtins/builtins-utils-inl.h"
6 #include "src/builtins/builtins.h"
7 #include "src/logging/counters.h"
8 #include "src/objects/elements.h"
9 #include "src/objects/heap-number-inl.h"
10 #include "src/objects/js-array-buffer-inl.h"
11 #include "src/objects/objects-inl.h"
12
13 namespace v8 {
14 namespace internal {
15
16 // -----------------------------------------------------------------------------
17 // ES6 section 22.2 TypedArray Objects
18
19 // ES6 section 22.2.3.1 get %TypedArray%.prototype.buffer
BUILTIN(TypedArrayPrototypeBuffer)20 BUILTIN(TypedArrayPrototypeBuffer) {
21 HandleScope scope(isolate);
22 CHECK_RECEIVER(JSTypedArray, typed_array,
23 "get %TypedArray%.prototype.buffer");
24 return *typed_array->GetBuffer();
25 }
26
27 namespace {
28
CapRelativeIndex(Handle<Object> num,int64_t minimum,int64_t maximum)29 int64_t CapRelativeIndex(Handle<Object> num, int64_t minimum, int64_t maximum) {
30 if (V8_LIKELY(num->IsSmi())) {
31 int64_t relative = Smi::ToInt(*num);
32 return relative < 0 ? std::max<int64_t>(relative + maximum, minimum)
33 : std::min<int64_t>(relative, maximum);
34 } else {
35 DCHECK(num->IsHeapNumber());
36 double relative = HeapNumber::cast(*num).value();
37 DCHECK(!std::isnan(relative));
38 return static_cast<int64_t>(
39 relative < 0 ? std::max<double>(relative + maximum, minimum)
40 : std::min<double>(relative, maximum));
41 }
42 }
43
44 } // namespace
45
BUILTIN(TypedArrayPrototypeCopyWithin)46 BUILTIN(TypedArrayPrototypeCopyWithin) {
47 HandleScope scope(isolate);
48
49 Handle<JSTypedArray> array;
50 const char* method_name = "%TypedArray%.prototype.copyWithin";
51 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
52 isolate, array,
53 JSTypedArray::Validate(isolate, args.receiver(), method_name));
54
55 int64_t len = array->GetLength();
56 int64_t to = 0;
57 int64_t from = 0;
58 int64_t final = len;
59
60 if (V8_LIKELY(args.length() > 1)) {
61 Handle<Object> num;
62 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
63 isolate, num, Object::ToInteger(isolate, args.at<Object>(1)));
64 to = CapRelativeIndex(num, 0, len);
65
66 if (args.length() > 2) {
67 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
68 isolate, num, Object::ToInteger(isolate, args.at<Object>(2)));
69 from = CapRelativeIndex(num, 0, len);
70
71 Handle<Object> end = args.atOrUndefined(isolate, 3);
72 if (!end->IsUndefined(isolate)) {
73 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, num,
74 Object::ToInteger(isolate, end));
75 final = CapRelativeIndex(num, 0, len);
76 }
77 }
78 }
79
80 int64_t count = std::min<int64_t>(final - from, len - to);
81 if (count <= 0) return *array;
82
83 // TypedArray buffer may have been transferred/detached during parameter
84 // processing above.
85 if (V8_UNLIKELY(array->WasDetached())) {
86 THROW_NEW_ERROR_RETURN_FAILURE(
87 isolate, NewTypeError(MessageTemplate::kDetachedOperation,
88 isolate->factory()->NewStringFromAsciiChecked(
89 method_name)));
90 }
91
92 if (V8_UNLIKELY(array->is_backed_by_rab())) {
93 bool out_of_bounds = false;
94 int64_t new_len = array->GetLengthOrOutOfBounds(out_of_bounds);
95 if (out_of_bounds) {
96 const MessageTemplate message = MessageTemplate::kDetachedOperation;
97 Handle<String> operation =
98 isolate->factory()->NewStringFromAsciiChecked(method_name);
99 THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewTypeError(message, operation));
100 }
101 if (new_len < len) {
102 // We don't need to account for growing, since we only copy an already
103 // determined number of elements and growing won't change it. If to >
104 // new_len or from > new_len, the count below will be < 0, so we don't
105 // need to check them separately.
106 if (final > new_len) {
107 final = new_len;
108 }
109 count = std::min<int64_t>(final - from, new_len - to);
110 if (count <= 0) {
111 return *array;
112 }
113 }
114 }
115
116 // Ensure processed indexes are within array bounds
117 DCHECK_GE(from, 0);
118 DCHECK_LT(from, len);
119 DCHECK_GE(to, 0);
120 DCHECK_LT(to, len);
121 DCHECK_GE(len - count, 0);
122
123 size_t element_size = array->element_size();
124 to = to * element_size;
125 from = from * element_size;
126 count = count * element_size;
127
128 uint8_t* data = static_cast<uint8_t*>(array->DataPtr());
129 if (array->buffer().is_shared()) {
130 base::Relaxed_Memmove(reinterpret_cast<base::Atomic8*>(data + to),
131 reinterpret_cast<base::Atomic8*>(data + from), count);
132 } else {
133 std::memmove(data + to, data + from, count);
134 }
135
136 return *array;
137 }
138
BUILTIN(TypedArrayPrototypeFill)139 BUILTIN(TypedArrayPrototypeFill) {
140 HandleScope scope(isolate);
141
142 Handle<JSTypedArray> array;
143 const char* method_name = "%TypedArray%.prototype.fill";
144 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
145 isolate, array,
146 JSTypedArray::Validate(isolate, args.receiver(), method_name));
147 ElementsKind kind = array->GetElementsKind();
148
149 Handle<Object> obj_value = args.atOrUndefined(isolate, 1);
150 if (IsBigIntTypedArrayElementsKind(kind)) {
151 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, obj_value,
152 BigInt::FromObject(isolate, obj_value));
153 } else {
154 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, obj_value,
155 Object::ToNumber(isolate, obj_value));
156 }
157
158 int64_t len = array->GetLength();
159 int64_t start = 0;
160 int64_t end = len;
161
162 if (args.length() > 2) {
163 Handle<Object> num = args.atOrUndefined(isolate, 2);
164 if (!num->IsUndefined(isolate)) {
165 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
166 isolate, num, Object::ToInteger(isolate, num));
167 start = CapRelativeIndex(num, 0, len);
168
169 num = args.atOrUndefined(isolate, 3);
170 if (!num->IsUndefined(isolate)) {
171 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
172 isolate, num, Object::ToInteger(isolate, num));
173 end = CapRelativeIndex(num, 0, len);
174 }
175 }
176 }
177
178 if (V8_UNLIKELY(array->WasDetached())) {
179 THROW_NEW_ERROR_RETURN_FAILURE(
180 isolate, NewTypeError(MessageTemplate::kDetachedOperation,
181 isolate->factory()->NewStringFromAsciiChecked(
182 method_name)));
183 }
184
185 if (V8_UNLIKELY(array->IsVariableLength())) {
186 if (array->IsOutOfBounds()) {
187 const MessageTemplate message = MessageTemplate::kDetachedOperation;
188 Handle<String> operation =
189 isolate->factory()->NewStringFromAsciiChecked(method_name);
190 THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewTypeError(message, operation));
191 }
192 }
193
194 int64_t count = end - start;
195 if (count <= 0) return *array;
196
197 // Ensure processed indexes are within array bounds
198 DCHECK_GE(start, 0);
199 DCHECK_LT(start, len);
200 DCHECK_GE(end, 0);
201 DCHECK_LE(end, len);
202 DCHECK_LE(count, len);
203
204 RETURN_RESULT_OR_FAILURE(isolate, ElementsAccessor::ForKind(kind)->Fill(
205 array, obj_value, start, end));
206 }
207
BUILTIN(TypedArrayPrototypeIncludes)208 BUILTIN(TypedArrayPrototypeIncludes) {
209 HandleScope scope(isolate);
210
211 Handle<JSTypedArray> array;
212 const char* method_name = "%TypedArray%.prototype.includes";
213 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
214 isolate, array,
215 JSTypedArray::Validate(isolate, args.receiver(), method_name));
216
217 if (args.length() < 2) return ReadOnlyRoots(isolate).false_value();
218
219 int64_t len = array->GetLength();
220 if (len == 0) return ReadOnlyRoots(isolate).false_value();
221
222 int64_t index = 0;
223 if (args.length() > 2) {
224 Handle<Object> num;
225 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
226 isolate, num, Object::ToInteger(isolate, args.at<Object>(2)));
227 index = CapRelativeIndex(num, 0, len);
228 }
229
230 Handle<Object> search_element = args.atOrUndefined(isolate, 1);
231 ElementsAccessor* elements = array->GetElementsAccessor();
232 Maybe<bool> result =
233 elements->IncludesValue(isolate, array, search_element, index, len);
234 MAYBE_RETURN(result, ReadOnlyRoots(isolate).exception());
235 return *isolate->factory()->ToBoolean(result.FromJust());
236 }
237
BUILTIN(TypedArrayPrototypeIndexOf)238 BUILTIN(TypedArrayPrototypeIndexOf) {
239 HandleScope scope(isolate);
240
241 Handle<JSTypedArray> array;
242 const char* method_name = "%TypedArray%.prototype.indexOf";
243 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
244 isolate, array,
245 JSTypedArray::Validate(isolate, args.receiver(), method_name));
246
247 int64_t len = array->GetLength();
248 if (len == 0) return Smi::FromInt(-1);
249
250 int64_t index = 0;
251 if (args.length() > 2) {
252 Handle<Object> num;
253 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
254 isolate, num, Object::ToInteger(isolate, args.at<Object>(2)));
255 index = CapRelativeIndex(num, 0, len);
256 }
257
258 if (V8_UNLIKELY(array->WasDetached())) return Smi::FromInt(-1);
259
260 if (V8_UNLIKELY(array->IsVariableLength() && array->IsOutOfBounds())) {
261 return Smi::FromInt(-1);
262 }
263
264 Handle<Object> search_element = args.atOrUndefined(isolate, 1);
265 ElementsAccessor* elements = array->GetElementsAccessor();
266 Maybe<int64_t> result =
267 elements->IndexOfValue(isolate, array, search_element, index, len);
268 MAYBE_RETURN(result, ReadOnlyRoots(isolate).exception());
269 return *isolate->factory()->NewNumberFromInt64(result.FromJust());
270 }
271
BUILTIN(TypedArrayPrototypeLastIndexOf)272 BUILTIN(TypedArrayPrototypeLastIndexOf) {
273 HandleScope scope(isolate);
274
275 Handle<JSTypedArray> array;
276 const char* method_name = "%TypedArray%.prototype.lastIndexOf";
277 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
278 isolate, array,
279 JSTypedArray::Validate(isolate, args.receiver(), method_name));
280
281 int64_t len = array->GetLength();
282 if (len == 0) return Smi::FromInt(-1);
283
284 int64_t index = len - 1;
285 if (args.length() > 2) {
286 Handle<Object> num;
287 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
288 isolate, num, Object::ToInteger(isolate, args.at<Object>(2)));
289 // Set a negative value (-1) for returning -1 if num is negative and
290 // len + num is still negative. Upper bound is len - 1.
291 index = std::min<int64_t>(CapRelativeIndex(num, -1, len), len - 1);
292 }
293
294 if (index < 0) return Smi::FromInt(-1);
295
296 if (V8_UNLIKELY(array->WasDetached())) return Smi::FromInt(-1);
297 if (V8_UNLIKELY(array->IsVariableLength() && array->IsOutOfBounds())) {
298 return Smi::FromInt(-1);
299 }
300
301 Handle<Object> search_element = args.atOrUndefined(isolate, 1);
302 ElementsAccessor* elements = array->GetElementsAccessor();
303 Maybe<int64_t> result =
304 elements->LastIndexOfValue(array, search_element, index);
305 MAYBE_RETURN(result, ReadOnlyRoots(isolate).exception());
306 return *isolate->factory()->NewNumberFromInt64(result.FromJust());
307 }
308
BUILTIN(TypedArrayPrototypeReverse)309 BUILTIN(TypedArrayPrototypeReverse) {
310 HandleScope scope(isolate);
311
312 Handle<JSTypedArray> array;
313 const char* method_name = "%TypedArray%.prototype.reverse";
314 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
315 isolate, array,
316 JSTypedArray::Validate(isolate, args.receiver(), method_name));
317
318 ElementsAccessor* elements = array->GetElementsAccessor();
319 elements->Reverse(*array);
320 return *array;
321 }
322
323 } // namespace internal
324 } // namespace v8
325