• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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