Line data Source code
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.h"
6 : #include "src/builtins/builtins.h"
7 : #include "src/counters.h"
8 : #include "src/elements.h"
9 : #include "src/objects-inl.h"
10 :
11 : namespace v8 {
12 : namespace internal {
13 :
14 : // -----------------------------------------------------------------------------
15 : // ES6 section 22.2 TypedArray Objects
16 :
17 : // ES6 section 22.2.3.1 get %TypedArray%.prototype.buffer
18 61800 : BUILTIN(TypedArrayPrototypeBuffer) {
19 : HandleScope scope(isolate);
20 23288 : CHECK_RECEIVER(JSTypedArray, typed_array,
21 : "get %TypedArray%.prototype.buffer");
22 39408 : return *typed_array->GetBuffer();
23 : }
24 :
25 : namespace {
26 :
27 28694 : int64_t CapRelativeIndex(Handle<Object> num, int64_t minimum, int64_t maximum) {
28 : int64_t relative;
29 28694 : if (V8_LIKELY(num->IsSmi())) {
30 27182 : relative = Smi::cast(*num)->value();
31 : } else {
32 : DCHECK(num->IsHeapNumber());
33 : double fp = HeapNumber::cast(*num)->value();
34 1512 : if (V8_UNLIKELY(!std::isfinite(fp))) {
35 : // +Infinity / -Infinity
36 : DCHECK(!std::isnan(fp));
37 1386 : return fp < 0 ? minimum : maximum;
38 : }
39 126 : relative = static_cast<int64_t>(fp);
40 : }
41 64948 : return relative < 0 ? std::max<int64_t>(relative + maximum, minimum)
42 76758 : : std::min<int64_t>(relative, maximum);
43 : }
44 :
45 : // ES7 section 22.2.4.6 TypedArrayCreate ( constructor, argumentList )
46 13536 : MaybeHandle<JSTypedArray> TypedArrayCreate(Isolate* isolate,
47 : Handle<JSFunction> default_ctor,
48 : int argc, Handle<Object>* argv,
49 : const char* method_name) {
50 : // 1. Let newTypedArray be ? Construct(constructor, argumentList).
51 : Handle<Object> new_obj;
52 27072 : ASSIGN_RETURN_ON_EXCEPTION(
53 : isolate, new_obj, Execution::New(default_ctor, argc, argv), JSTypedArray);
54 :
55 : // 2. Perform ? ValidateTypedArray(newTypedArray).
56 : Handle<JSTypedArray> new_array;
57 27072 : ASSIGN_RETURN_ON_EXCEPTION(
58 : isolate, new_array, JSTypedArray::Validate(isolate, new_obj, method_name),
59 : JSTypedArray);
60 :
61 : // 3. If argumentList is a List of a single Number, then
62 : // If newTypedArray.[[ArrayLength]] < size, throw a TypeError exception.
63 : DCHECK_IMPLIES(argc == 1, argv[0]->IsSmi());
64 39852 : if (argc == 1 && new_array->length_value() < argv[0]->Number()) {
65 : const MessageTemplate::Template message =
66 : MessageTemplate::kTypedArrayTooShort;
67 252 : THROW_NEW_ERROR(isolate, NewTypeError(message), JSTypedArray);
68 : }
69 :
70 : // 4. Return newTypedArray.
71 : return new_array;
72 : }
73 :
74 : // ES7 section 22.2.4.7 TypedArraySpeciesCreate ( exemplar, argumentList )
75 13536 : MaybeHandle<JSTypedArray> TypedArraySpeciesCreate(Isolate* isolate,
76 : Handle<JSTypedArray> exemplar,
77 : int argc,
78 : Handle<Object>* argv,
79 : const char* method_name) {
80 : // 1. Assert: exemplar is an Object that has a [[TypedArrayName]] internal
81 : // slot.
82 : DCHECK(exemplar->IsJSTypedArray());
83 :
84 : // 2. Let defaultConstructor be the intrinsic object listed in column one of
85 : // Table 51 for exemplar.[[TypedArrayName]].
86 13536 : Handle<JSFunction> default_ctor = isolate->uint8_array_fun();
87 13536 : switch (exemplar->type()) {
88 : #define TYPED_ARRAY_CTOR(Type, type, TYPE, ctype, size) \
89 : case kExternal##Type##Array: { \
90 : default_ctor = isolate->type##_array_fun(); \
91 : break; \
92 : }
93 :
94 2209 : TYPED_ARRAYS(TYPED_ARRAY_CTOR)
95 : #undef TYPED_ARRAY_CTOR
96 : default:
97 0 : UNREACHABLE();
98 : }
99 :
100 : // 3. Let constructor be ? SpeciesConstructor(exemplar, defaultConstructor).
101 : Handle<Object> ctor;
102 27072 : ASSIGN_RETURN_ON_EXCEPTION(
103 : isolate, ctor,
104 : Object::SpeciesConstructor(isolate, exemplar, default_ctor),
105 : JSTypedArray);
106 :
107 : // 4. Return ? TypedArrayCreate(constructor, argumentList).
108 : return TypedArrayCreate(isolate, Handle<JSFunction>::cast(ctor), argc, argv,
109 13536 : method_name);
110 : }
111 :
112 13536 : MaybeHandle<JSTypedArray> TypedArraySpeciesCreateByLength(
113 : Isolate* isolate, Handle<JSTypedArray> exemplar, const char* method_name,
114 : int64_t length) {
115 : const int argc = 1;
116 : ScopedVector<Handle<Object>> argv(argc);
117 13536 : argv[0] = isolate->factory()->NewNumberFromInt64(length);
118 : return TypedArraySpeciesCreate(isolate, exemplar, argc, argv.start(),
119 27072 : method_name);
120 : }
121 :
122 : } // namespace
123 :
124 13272 : BUILTIN(TypedArrayPrototypeCopyWithin) {
125 : HandleScope scope(isolate);
126 :
127 : Handle<JSTypedArray> array;
128 : const char* method = "%TypedArray%.prototype.copyWithin";
129 8848 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
130 : isolate, array, JSTypedArray::Validate(isolate, args.receiver(), method));
131 :
132 3794 : int64_t len = array->length_value();
133 : int64_t to = 0;
134 : int64_t from = 0;
135 : int64_t final = len;
136 :
137 3794 : if (V8_LIKELY(args.length() > 1)) {
138 : Handle<Object> num;
139 3794 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
140 : isolate, num, Object::ToInteger(isolate, args.at<Object>(1)));
141 3794 : to = CapRelativeIndex(num, 0, len);
142 :
143 3794 : if (args.length() > 2) {
144 3780 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
145 : isolate, num, Object::ToInteger(isolate, args.at<Object>(2)));
146 3780 : from = CapRelativeIndex(num, 0, len);
147 :
148 : Handle<Object> end = args.atOrUndefined(isolate, 3);
149 3780 : if (!end->IsUndefined(isolate)) {
150 1260 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, num,
151 : Object::ToInteger(isolate, end));
152 1260 : final = CapRelativeIndex(num, 0, len);
153 : }
154 : }
155 : }
156 :
157 7588 : int64_t count = std::min<int64_t>(final - from, len - to);
158 4550 : if (count <= 0) return *array;
159 :
160 : // TypedArray buffer may have been transferred/detached during parameter
161 : // processing above. Return early in this case, to prevent potential UAF error
162 : // TODO(caitp): throw here, as though the full algorithm were performed (the
163 : // throw would have come from ecma262/#sec-integerindexedelementget)
164 : // (see )
165 3038 : if (V8_UNLIKELY(array->WasNeutered())) return *array;
166 :
167 : // Ensure processed indexes are within array bounds
168 : DCHECK_GE(from, 0);
169 : DCHECK_LT(from, len);
170 : DCHECK_GE(to, 0);
171 : DCHECK_LT(to, len);
172 : DCHECK_GE(len - count, 0);
173 :
174 : Handle<FixedTypedArrayBase> elements(
175 : FixedTypedArrayBase::cast(array->elements()));
176 3024 : size_t element_size = array->element_size();
177 3024 : to = to * element_size;
178 3024 : from = from * element_size;
179 3024 : count = count * element_size;
180 :
181 : uint8_t* data = static_cast<uint8_t*>(elements->DataPtr());
182 3024 : std::memmove(data + to, data + from, count);
183 :
184 3024 : return *array;
185 : }
186 :
187 12360 : BUILTIN(TypedArrayPrototypeFill) {
188 : HandleScope scope(isolate);
189 :
190 : Handle<JSTypedArray> array;
191 : const char* method = "%TypedArray%.prototype.fill";
192 8240 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
193 : isolate, array, JSTypedArray::Validate(isolate, args.receiver(), method));
194 :
195 3616 : Handle<Object> obj_value = args.atOrUndefined(isolate, 1);
196 7232 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
197 : isolate, obj_value, Object::ToNumber(obj_value));
198 :
199 3616 : int64_t len = array->length_value();
200 : int64_t start = 0;
201 : int64_t end = len;
202 :
203 3616 : if (args.length() > 2) {
204 : Handle<Object> num = args.atOrUndefined(isolate, 2);
205 1890 : if (!num->IsUndefined(isolate)) {
206 1890 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
207 : isolate, num, Object::ToInteger(isolate, num));
208 1890 : start = CapRelativeIndex(num, 0, len);
209 :
210 : num = args.atOrUndefined(isolate, 3);
211 1890 : if (!num->IsUndefined(isolate)) {
212 1260 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
213 : isolate, num, Object::ToInteger(isolate, num));
214 1260 : end = CapRelativeIndex(num, 0, len);
215 : }
216 : }
217 : }
218 :
219 3616 : int64_t count = end - start;
220 4246 : if (count <= 0) return *array;
221 :
222 2986 : if (V8_UNLIKELY(array->WasNeutered())) return *array;
223 :
224 : // Ensure processed indexes are within array bounds
225 : DCHECK_GE(start, 0);
226 : DCHECK_LT(start, len);
227 : DCHECK_GE(end, 0);
228 : DCHECK_LE(end, len);
229 : DCHECK_LE(count, len);
230 :
231 2986 : return array->GetElementsAccessor()->Fill(isolate, array, obj_value,
232 : static_cast<uint32_t>(start),
233 5972 : static_cast<uint32_t>(end));
234 : }
235 :
236 9996 : BUILTIN(TypedArrayPrototypeIncludes) {
237 : HandleScope scope(isolate);
238 :
239 : Handle<JSTypedArray> array;
240 : const char* method = "%TypedArray%.prototype.includes";
241 6664 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
242 : isolate, array, JSTypedArray::Validate(isolate, args.receiver(), method));
243 :
244 3276 : if (args.length() < 2) return isolate->heap()->false_value();
245 :
246 3150 : int64_t len = array->length_value();
247 3150 : if (len == 0) return isolate->heap()->false_value();
248 :
249 : int64_t index = 0;
250 2772 : if (args.length() > 2) {
251 : Handle<Object> num;
252 2142 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
253 : isolate, num, Object::ToInteger(isolate, args.at<Object>(2)));
254 1890 : index = CapRelativeIndex(num, 0, len);
255 : }
256 :
257 : // TODO(cwhan.tunz): throw. See the above comment in CopyWithin.
258 2520 : if (V8_UNLIKELY(array->WasNeutered())) return isolate->heap()->false_value();
259 :
260 2520 : Handle<Object> search_element = args.atOrUndefined(isolate, 1);
261 2520 : ElementsAccessor* elements = array->GetElementsAccessor();
262 : Maybe<bool> result = elements->IncludesValue(isolate, array, search_element,
263 : static_cast<uint32_t>(index),
264 5040 : static_cast<uint32_t>(len));
265 2520 : MAYBE_RETURN(result, isolate->heap()->exception());
266 5040 : return *isolate->factory()->ToBoolean(result.FromJust());
267 : }
268 :
269 5670 : BUILTIN(TypedArrayPrototypeIndexOf) {
270 : HandleScope scope(isolate);
271 :
272 : Handle<JSTypedArray> array;
273 : const char* method = "%TypedArray%.prototype.indexOf";
274 3780 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
275 : isolate, array, JSTypedArray::Validate(isolate, args.receiver(), method));
276 :
277 1638 : int64_t len = array->length_value();
278 1638 : if (len == 0) return Smi::FromInt(-1);
279 :
280 : int64_t index = 0;
281 1512 : if (args.length() > 2) {
282 : Handle<Object> num;
283 756 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
284 : isolate, num, Object::ToInteger(isolate, args.at<Object>(2)));
285 756 : index = CapRelativeIndex(num, 0, len);
286 : }
287 :
288 : // TODO(cwhan.tunz): throw. See the above comment in CopyWithin.
289 1512 : if (V8_UNLIKELY(array->WasNeutered())) return Smi::FromInt(-1);
290 :
291 1512 : Handle<Object> search_element = args.atOrUndefined(isolate, 1);
292 1512 : ElementsAccessor* elements = array->GetElementsAccessor();
293 : Maybe<int64_t> result = elements->IndexOfValue(isolate, array, search_element,
294 : static_cast<uint32_t>(index),
295 3024 : static_cast<uint32_t>(len));
296 1512 : MAYBE_RETURN(result, isolate->heap()->exception());
297 3024 : return *isolate->factory()->NewNumberFromInt64(result.FromJust());
298 : }
299 :
300 5292 : BUILTIN(TypedArrayPrototypeLastIndexOf) {
301 : HandleScope scope(isolate);
302 :
303 : Handle<JSTypedArray> array;
304 : const char* method = "%TypedArray%.prototype.lastIndexOf";
305 3528 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
306 : isolate, array, JSTypedArray::Validate(isolate, args.receiver(), method));
307 :
308 1512 : int64_t len = array->length_value();
309 1512 : if (len == 0) return Smi::FromInt(-1);
310 :
311 1386 : int64_t index = len - 1;
312 1386 : if (args.length() > 2) {
313 : Handle<Object> num;
314 756 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
315 : isolate, num, Object::ToInteger(isolate, args.at<Object>(2)));
316 : // Set a negative value (-1) for returning -1 if num is negative and
317 : // len + num is still negative. Upper bound is len - 1.
318 1512 : index = std::min<int64_t>(CapRelativeIndex(num, -1, len), len - 1);
319 : }
320 :
321 1386 : if (index < 0) return Smi::FromInt(-1);
322 :
323 : // TODO(cwhan.tunz): throw. See the above comment in CopyWithin.
324 1260 : if (V8_UNLIKELY(array->WasNeutered())) return Smi::FromInt(-1);
325 :
326 1260 : Handle<Object> search_element = args.atOrUndefined(isolate, 1);
327 1260 : ElementsAccessor* elements = array->GetElementsAccessor();
328 : Maybe<int64_t> result = elements->LastIndexOfValue(
329 2520 : isolate, array, search_element, static_cast<uint32_t>(index));
330 1260 : MAYBE_RETURN(result, isolate->heap()->exception());
331 2520 : return *isolate->factory()->NewNumberFromInt64(result.FromJust());
332 : }
333 :
334 1512 : BUILTIN(TypedArrayPrototypeReverse) {
335 : HandleScope scope(isolate);
336 :
337 : Handle<JSTypedArray> array;
338 : const char* method = "%TypedArray%.prototype.reverse";
339 1008 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
340 : isolate, array, JSTypedArray::Validate(isolate, args.receiver(), method));
341 :
342 252 : ElementsAccessor* elements = array->GetElementsAccessor();
343 504 : elements->Reverse(*array);
344 252 : return *array;
345 : }
346 :
347 41364 : BUILTIN(TypedArrayPrototypeSlice) {
348 : HandleScope scope(isolate);
349 :
350 : Handle<JSTypedArray> array;
351 : const char* method = "%TypedArray%.prototype.slice";
352 27576 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
353 : isolate, array, JSTypedArray::Validate(isolate, args.receiver(), method));
354 :
355 13536 : int64_t len = array->length_value();
356 : int64_t start = 0;
357 : int64_t end = len;
358 : {
359 : Handle<Object> num = args.atOrUndefined(isolate, 1);
360 13536 : if (!num->IsUndefined(isolate)) {
361 7977 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, num,
362 : Object::ToInteger(isolate, num));
363 7977 : start = CapRelativeIndex(num, 0, len);
364 :
365 : num = args.atOrUndefined(isolate, 2);
366 7977 : if (!num->IsUndefined(isolate)) {
367 5331 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, num,
368 : Object::ToInteger(isolate, num));
369 5331 : end = CapRelativeIndex(num, 0, len);
370 : }
371 : }
372 : }
373 :
374 27072 : int64_t count = std::max<int64_t>(end - start, 0);
375 :
376 : Handle<JSTypedArray> result_array;
377 27072 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
378 : isolate, result_array,
379 : TypedArraySpeciesCreateByLength(isolate, array, method, count));
380 :
381 : // TODO(cwhan.tunz): neutering check of the result_array should be done in
382 : // TypedArraySpeciesCreate, but currently ValidateTypedArray does not throw
383 : // for neutered buffer, so this is a temporary neutering check for the result
384 : // array
385 13158 : if (V8_UNLIKELY(result_array->WasNeutered())) return *result_array;
386 :
387 : // TODO(cwhan.tunz): should throw.
388 13158 : if (V8_UNLIKELY(array->WasNeutered())) return *result_array;
389 :
390 13158 : if (count == 0) return *result_array;
391 :
392 8874 : ElementsAccessor* accessor = array->GetElementsAccessor();
393 : return *accessor->Slice(array, static_cast<uint32_t>(start),
394 35496 : static_cast<uint32_t>(end), result_array);
395 : }
396 :
397 : } // namespace internal
398 : } // namespace v8
|