82224bc9b3e8021d6b780e7af2343f75b9bb65c0
[platform/upstream/nodejs.git] / deps / v8 / src / runtime / runtime-typedarray.cc
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/v8.h"
6
7 #include "src/arguments.h"
8 #include "src/runtime/runtime.h"
9 #include "src/runtime/runtime-utils.h"
10
11
12 namespace v8 {
13 namespace internal {
14
15 void Runtime::FreeArrayBuffer(Isolate* isolate,
16                               JSArrayBuffer* phantom_array_buffer) {
17   if (phantom_array_buffer->should_be_freed()) {
18     DCHECK(phantom_array_buffer->is_external());
19     free(phantom_array_buffer->backing_store());
20   }
21   if (phantom_array_buffer->is_external()) return;
22
23   size_t allocated_length =
24       NumberToSize(isolate, phantom_array_buffer->byte_length());
25
26   reinterpret_cast<v8::Isolate*>(isolate)
27       ->AdjustAmountOfExternalAllocatedMemory(
28           -static_cast<int64_t>(allocated_length));
29   CHECK(V8::ArrayBufferAllocator() != NULL);
30   V8::ArrayBufferAllocator()->Free(phantom_array_buffer->backing_store(),
31                                    allocated_length);
32 }
33
34
35 void Runtime::SetupArrayBuffer(Isolate* isolate,
36                                Handle<JSArrayBuffer> array_buffer,
37                                bool is_external, void* data,
38                                size_t allocated_length) {
39   DCHECK(array_buffer->GetInternalFieldCount() ==
40          v8::ArrayBuffer::kInternalFieldCount);
41   for (int i = 0; i < v8::ArrayBuffer::kInternalFieldCount; i++) {
42     array_buffer->SetInternalField(i, Smi::FromInt(0));
43   }
44   array_buffer->set_backing_store(data);
45   array_buffer->set_flag(Smi::FromInt(0));
46   array_buffer->set_is_external(is_external);
47   array_buffer->set_is_neuterable(true);
48
49   Handle<Object> byte_length =
50       isolate->factory()->NewNumberFromSize(allocated_length);
51   CHECK(byte_length->IsSmi() || byte_length->IsHeapNumber());
52   array_buffer->set_byte_length(*byte_length);
53
54   array_buffer->set_weak_next(isolate->heap()->array_buffers_list());
55   isolate->heap()->set_array_buffers_list(*array_buffer);
56   array_buffer->set_weak_first_view(isolate->heap()->undefined_value());
57 }
58
59
60 bool Runtime::SetupArrayBufferAllocatingData(Isolate* isolate,
61                                              Handle<JSArrayBuffer> array_buffer,
62                                              size_t allocated_length,
63                                              bool initialize) {
64   void* data;
65   CHECK(V8::ArrayBufferAllocator() != NULL);
66   // Prevent creating array buffers when serializing.
67   DCHECK(!isolate->serializer_enabled());
68   if (allocated_length != 0) {
69     if (initialize) {
70       data = V8::ArrayBufferAllocator()->Allocate(allocated_length);
71     } else {
72       data =
73           V8::ArrayBufferAllocator()->AllocateUninitialized(allocated_length);
74     }
75     if (data == NULL) return false;
76   } else {
77     data = NULL;
78   }
79
80   SetupArrayBuffer(isolate, array_buffer, false, data, allocated_length);
81
82   reinterpret_cast<v8::Isolate*>(isolate)
83       ->AdjustAmountOfExternalAllocatedMemory(allocated_length);
84
85   return true;
86 }
87
88
89 void Runtime::NeuterArrayBuffer(Handle<JSArrayBuffer> array_buffer) {
90   Isolate* isolate = array_buffer->GetIsolate();
91   for (Handle<Object> view_obj(array_buffer->weak_first_view(), isolate);
92        !view_obj->IsUndefined();) {
93     Handle<JSArrayBufferView> view(JSArrayBufferView::cast(*view_obj));
94     if (view->IsJSTypedArray()) {
95       JSTypedArray::cast(*view)->Neuter();
96     } else if (view->IsJSDataView()) {
97       JSDataView::cast(*view)->Neuter();
98     } else {
99       UNREACHABLE();
100     }
101     view_obj = handle(view->weak_next(), isolate);
102   }
103   array_buffer->Neuter();
104 }
105
106
107 RUNTIME_FUNCTION(Runtime_ArrayBufferInitialize) {
108   HandleScope scope(isolate);
109   DCHECK(args.length() == 2);
110   CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, holder, 0);
111   CONVERT_NUMBER_ARG_HANDLE_CHECKED(byteLength, 1);
112   if (!holder->byte_length()->IsUndefined()) {
113     // ArrayBuffer is already initialized; probably a fuzz test.
114     return *holder;
115   }
116   size_t allocated_length = 0;
117   if (!TryNumberToSize(isolate, *byteLength, &allocated_length)) {
118     THROW_NEW_ERROR_RETURN_FAILURE(
119         isolate, NewRangeError("invalid_array_buffer_length",
120                                HandleVector<Object>(NULL, 0)));
121   }
122   if (!Runtime::SetupArrayBufferAllocatingData(isolate, holder,
123                                                allocated_length)) {
124     THROW_NEW_ERROR_RETURN_FAILURE(
125         isolate, NewRangeError("invalid_array_buffer_length",
126                                HandleVector<Object>(NULL, 0)));
127   }
128   return *holder;
129 }
130
131
132 RUNTIME_FUNCTION(Runtime_ArrayBufferGetByteLength) {
133   SealHandleScope shs(isolate);
134   DCHECK(args.length() == 1);
135   CONVERT_ARG_CHECKED(JSArrayBuffer, holder, 0);
136   return holder->byte_length();
137 }
138
139
140 RUNTIME_FUNCTION(Runtime_ArrayBufferSliceImpl) {
141   HandleScope scope(isolate);
142   DCHECK(args.length() == 3);
143   CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, source, 0);
144   CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, target, 1);
145   CONVERT_NUMBER_ARG_HANDLE_CHECKED(first, 2);
146   RUNTIME_ASSERT(!source.is_identical_to(target));
147   size_t start = 0;
148   RUNTIME_ASSERT(TryNumberToSize(isolate, *first, &start));
149   size_t target_length = NumberToSize(isolate, target->byte_length());
150
151   if (target_length == 0) return isolate->heap()->undefined_value();
152
153   size_t source_byte_length = NumberToSize(isolate, source->byte_length());
154   RUNTIME_ASSERT(start <= source_byte_length);
155   RUNTIME_ASSERT(source_byte_length - start >= target_length);
156   uint8_t* source_data = reinterpret_cast<uint8_t*>(source->backing_store());
157   uint8_t* target_data = reinterpret_cast<uint8_t*>(target->backing_store());
158   CopyBytes(target_data, source_data + start, target_length);
159   return isolate->heap()->undefined_value();
160 }
161
162
163 RUNTIME_FUNCTION(Runtime_ArrayBufferIsView) {
164   HandleScope scope(isolate);
165   DCHECK(args.length() == 1);
166   CONVERT_ARG_CHECKED(Object, object, 0);
167   return isolate->heap()->ToBoolean(object->IsJSArrayBufferView());
168 }
169
170
171 RUNTIME_FUNCTION(Runtime_ArrayBufferNeuter) {
172   HandleScope scope(isolate);
173   DCHECK(args.length() == 1);
174   CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, array_buffer, 0);
175   if (array_buffer->backing_store() == NULL) {
176     CHECK(Smi::FromInt(0) == array_buffer->byte_length());
177     return isolate->heap()->undefined_value();
178   }
179   DCHECK(!array_buffer->is_external());
180   void* backing_store = array_buffer->backing_store();
181   size_t byte_length = NumberToSize(isolate, array_buffer->byte_length());
182   array_buffer->set_is_external(true);
183   Runtime::NeuterArrayBuffer(array_buffer);
184   V8::ArrayBufferAllocator()->Free(backing_store, byte_length);
185   return isolate->heap()->undefined_value();
186 }
187
188
189 void Runtime::ArrayIdToTypeAndSize(int arrayId, ExternalArrayType* array_type,
190                                    ElementsKind* external_elements_kind,
191                                    ElementsKind* fixed_elements_kind,
192                                    size_t* element_size) {
193   switch (arrayId) {
194 #define ARRAY_ID_CASE(Type, type, TYPE, ctype, size)      \
195   case ARRAY_ID_##TYPE:                                   \
196     *array_type = kExternal##Type##Array;                 \
197     *external_elements_kind = EXTERNAL_##TYPE##_ELEMENTS; \
198     *fixed_elements_kind = TYPE##_ELEMENTS;               \
199     *element_size = size;                                 \
200     break;
201
202     TYPED_ARRAYS(ARRAY_ID_CASE)
203 #undef ARRAY_ID_CASE
204
205     default:
206       UNREACHABLE();
207   }
208 }
209
210
211 RUNTIME_FUNCTION(Runtime_TypedArrayInitialize) {
212   HandleScope scope(isolate);
213   DCHECK(args.length() == 5);
214   CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
215   CONVERT_SMI_ARG_CHECKED(arrayId, 1);
216   CONVERT_ARG_HANDLE_CHECKED(Object, maybe_buffer, 2);
217   CONVERT_NUMBER_ARG_HANDLE_CHECKED(byte_offset_object, 3);
218   CONVERT_NUMBER_ARG_HANDLE_CHECKED(byte_length_object, 4);
219
220   RUNTIME_ASSERT(arrayId >= Runtime::ARRAY_ID_FIRST &&
221                  arrayId <= Runtime::ARRAY_ID_LAST);
222
223   ExternalArrayType array_type = kExternalInt8Array;  // Bogus initialization.
224   size_t element_size = 1;                            // Bogus initialization.
225   ElementsKind external_elements_kind =
226       EXTERNAL_INT8_ELEMENTS;                        // Bogus initialization.
227   ElementsKind fixed_elements_kind = INT8_ELEMENTS;  // Bogus initialization.
228   Runtime::ArrayIdToTypeAndSize(arrayId, &array_type, &external_elements_kind,
229                                 &fixed_elements_kind, &element_size);
230   RUNTIME_ASSERT(holder->map()->elements_kind() == fixed_elements_kind);
231
232   size_t byte_offset = 0;
233   size_t byte_length = 0;
234   RUNTIME_ASSERT(TryNumberToSize(isolate, *byte_offset_object, &byte_offset));
235   RUNTIME_ASSERT(TryNumberToSize(isolate, *byte_length_object, &byte_length));
236
237   if (maybe_buffer->IsJSArrayBuffer()) {
238     Handle<JSArrayBuffer> buffer = Handle<JSArrayBuffer>::cast(maybe_buffer);
239     size_t array_buffer_byte_length =
240         NumberToSize(isolate, buffer->byte_length());
241     RUNTIME_ASSERT(byte_offset <= array_buffer_byte_length);
242     RUNTIME_ASSERT(array_buffer_byte_length - byte_offset >= byte_length);
243   } else {
244     RUNTIME_ASSERT(maybe_buffer->IsNull());
245   }
246
247   RUNTIME_ASSERT(byte_length % element_size == 0);
248   size_t length = byte_length / element_size;
249
250   if (length > static_cast<unsigned>(Smi::kMaxValue)) {
251     THROW_NEW_ERROR_RETURN_FAILURE(
252         isolate, NewRangeError("invalid_typed_array_length",
253                                HandleVector<Object>(NULL, 0)));
254   }
255
256   // All checks are done, now we can modify objects.
257
258   DCHECK(holder->GetInternalFieldCount() ==
259          v8::ArrayBufferView::kInternalFieldCount);
260   for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) {
261     holder->SetInternalField(i, Smi::FromInt(0));
262   }
263   Handle<Object> length_obj = isolate->factory()->NewNumberFromSize(length);
264   holder->set_length(*length_obj);
265   holder->set_byte_offset(*byte_offset_object);
266   holder->set_byte_length(*byte_length_object);
267
268   if (!maybe_buffer->IsNull()) {
269     Handle<JSArrayBuffer> buffer = Handle<JSArrayBuffer>::cast(maybe_buffer);
270     holder->set_buffer(*buffer);
271     holder->set_weak_next(buffer->weak_first_view());
272     buffer->set_weak_first_view(*holder);
273
274     Handle<ExternalArray> elements = isolate->factory()->NewExternalArray(
275         static_cast<int>(length), array_type,
276         static_cast<uint8_t*>(buffer->backing_store()) + byte_offset);
277     Handle<Map> map =
278         JSObject::GetElementsTransitionMap(holder, external_elements_kind);
279     JSObject::SetMapAndElements(holder, map, elements);
280     DCHECK(IsExternalArrayElementsKind(holder->map()->elements_kind()));
281   } else {
282     holder->set_buffer(Smi::FromInt(0));
283     holder->set_weak_next(isolate->heap()->undefined_value());
284     Handle<FixedTypedArrayBase> elements =
285         isolate->factory()->NewFixedTypedArray(static_cast<int>(length),
286                                                array_type);
287     holder->set_elements(*elements);
288   }
289   return isolate->heap()->undefined_value();
290 }
291
292
293 // Initializes a typed array from an array-like object.
294 // If an array-like object happens to be a typed array of the same type,
295 // initializes backing store using memove.
296 //
297 // Returns true if backing store was initialized or false otherwise.
298 RUNTIME_FUNCTION(Runtime_TypedArrayInitializeFromArrayLike) {
299   HandleScope scope(isolate);
300   DCHECK(args.length() == 4);
301   CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
302   CONVERT_SMI_ARG_CHECKED(arrayId, 1);
303   CONVERT_ARG_HANDLE_CHECKED(Object, source, 2);
304   CONVERT_NUMBER_ARG_HANDLE_CHECKED(length_obj, 3);
305
306   RUNTIME_ASSERT(arrayId >= Runtime::ARRAY_ID_FIRST &&
307                  arrayId <= Runtime::ARRAY_ID_LAST);
308
309   ExternalArrayType array_type = kExternalInt8Array;  // Bogus initialization.
310   size_t element_size = 1;                            // Bogus initialization.
311   ElementsKind external_elements_kind =
312       EXTERNAL_INT8_ELEMENTS;                        // Bogus intialization.
313   ElementsKind fixed_elements_kind = INT8_ELEMENTS;  // Bogus initialization.
314   Runtime::ArrayIdToTypeAndSize(arrayId, &array_type, &external_elements_kind,
315                                 &fixed_elements_kind, &element_size);
316
317   RUNTIME_ASSERT(holder->map()->elements_kind() == fixed_elements_kind);
318
319   Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer();
320   if (source->IsJSTypedArray() &&
321       JSTypedArray::cast(*source)->type() == array_type) {
322     length_obj = Handle<Object>(JSTypedArray::cast(*source)->length(), isolate);
323   }
324   size_t length = 0;
325   RUNTIME_ASSERT(TryNumberToSize(isolate, *length_obj, &length));
326
327   if ((length > static_cast<unsigned>(Smi::kMaxValue)) ||
328       (length > (kMaxInt / element_size))) {
329     THROW_NEW_ERROR_RETURN_FAILURE(
330         isolate, NewRangeError("invalid_typed_array_length",
331                                HandleVector<Object>(NULL, 0)));
332   }
333   size_t byte_length = length * element_size;
334
335   DCHECK(holder->GetInternalFieldCount() ==
336          v8::ArrayBufferView::kInternalFieldCount);
337   for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) {
338     holder->SetInternalField(i, Smi::FromInt(0));
339   }
340
341   // NOTE: not initializing backing store.
342   // We assume that the caller of this function will initialize holder
343   // with the loop
344   //      for(i = 0; i < length; i++) { holder[i] = source[i]; }
345   // We assume that the caller of this function is always a typed array
346   // constructor.
347   // If source is a typed array, this loop will always run to completion,
348   // so we are sure that the backing store will be initialized.
349   // Otherwise, the indexing operation might throw, so the loop will not
350   // run to completion and the typed array might remain partly initialized.
351   // However we further assume that the caller of this function is a typed array
352   // constructor, and the exception will propagate out of the constructor,
353   // therefore uninitialized memory will not be accessible by a user program.
354   //
355   // TODO(dslomov): revise this once we support subclassing.
356
357   if (!Runtime::SetupArrayBufferAllocatingData(isolate, buffer, byte_length,
358                                                false)) {
359     THROW_NEW_ERROR_RETURN_FAILURE(
360         isolate, NewRangeError("invalid_array_buffer_length",
361                                HandleVector<Object>(NULL, 0)));
362   }
363
364   holder->set_buffer(*buffer);
365   holder->set_byte_offset(Smi::FromInt(0));
366   Handle<Object> byte_length_obj(
367       isolate->factory()->NewNumberFromSize(byte_length));
368   holder->set_byte_length(*byte_length_obj);
369   holder->set_length(*length_obj);
370   holder->set_weak_next(buffer->weak_first_view());
371   buffer->set_weak_first_view(*holder);
372
373   Handle<ExternalArray> elements = isolate->factory()->NewExternalArray(
374       static_cast<int>(length), array_type,
375       static_cast<uint8_t*>(buffer->backing_store()));
376   Handle<Map> map =
377       JSObject::GetElementsTransitionMap(holder, external_elements_kind);
378   JSObject::SetMapAndElements(holder, map, elements);
379
380   if (source->IsJSTypedArray()) {
381     Handle<JSTypedArray> typed_array(JSTypedArray::cast(*source));
382
383     if (typed_array->type() == holder->type()) {
384       uint8_t* backing_store =
385           static_cast<uint8_t*>(typed_array->GetBuffer()->backing_store());
386       size_t source_byte_offset =
387           NumberToSize(isolate, typed_array->byte_offset());
388       memcpy(buffer->backing_store(), backing_store + source_byte_offset,
389              byte_length);
390       return isolate->heap()->true_value();
391     }
392   }
393
394   return isolate->heap()->false_value();
395 }
396
397
398 #define BUFFER_VIEW_GETTER(Type, getter, accessor)   \
399   RUNTIME_FUNCTION(Runtime_##Type##Get##getter) {    \
400     HandleScope scope(isolate);                      \
401     DCHECK(args.length() == 1);                      \
402     CONVERT_ARG_HANDLE_CHECKED(JS##Type, holder, 0); \
403     return holder->accessor();                       \
404   }
405
406 BUFFER_VIEW_GETTER(ArrayBufferView, ByteLength, byte_length)
407 BUFFER_VIEW_GETTER(ArrayBufferView, ByteOffset, byte_offset)
408 BUFFER_VIEW_GETTER(TypedArray, Length, length)
409 BUFFER_VIEW_GETTER(DataView, Buffer, buffer)
410
411 #undef BUFFER_VIEW_GETTER
412
413 RUNTIME_FUNCTION(Runtime_TypedArrayGetBuffer) {
414   HandleScope scope(isolate);
415   DCHECK(args.length() == 1);
416   CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
417   return *holder->GetBuffer();
418 }
419
420
421 // Return codes for Runtime_TypedArraySetFastCases.
422 // Should be synchronized with typedarray.js natives.
423 enum TypedArraySetResultCodes {
424   // Set from typed array of the same type.
425   // This is processed by TypedArraySetFastCases
426   TYPED_ARRAY_SET_TYPED_ARRAY_SAME_TYPE = 0,
427   // Set from typed array of the different type, overlapping in memory.
428   TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING = 1,
429   // Set from typed array of the different type, non-overlapping.
430   TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING = 2,
431   // Set from non-typed array.
432   TYPED_ARRAY_SET_NON_TYPED_ARRAY = 3
433 };
434
435
436 RUNTIME_FUNCTION(Runtime_TypedArraySetFastCases) {
437   HandleScope scope(isolate);
438   DCHECK(args.length() == 3);
439   if (!args[0]->IsJSTypedArray()) {
440     THROW_NEW_ERROR_RETURN_FAILURE(
441         isolate,
442         NewTypeError("not_typed_array", HandleVector<Object>(NULL, 0)));
443   }
444
445   if (!args[1]->IsJSTypedArray())
446     return Smi::FromInt(TYPED_ARRAY_SET_NON_TYPED_ARRAY);
447
448   CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, target_obj, 0);
449   CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, source_obj, 1);
450   CONVERT_NUMBER_ARG_HANDLE_CHECKED(offset_obj, 2);
451
452   Handle<JSTypedArray> target(JSTypedArray::cast(*target_obj));
453   Handle<JSTypedArray> source(JSTypedArray::cast(*source_obj));
454   size_t offset = 0;
455   RUNTIME_ASSERT(TryNumberToSize(isolate, *offset_obj, &offset));
456   size_t target_length = NumberToSize(isolate, target->length());
457   size_t source_length = NumberToSize(isolate, source->length());
458   size_t target_byte_length = NumberToSize(isolate, target->byte_length());
459   size_t source_byte_length = NumberToSize(isolate, source->byte_length());
460   if (offset > target_length || offset + source_length > target_length ||
461       offset + source_length < offset) {  // overflow
462     THROW_NEW_ERROR_RETURN_FAILURE(
463         isolate, NewRangeError("typed_array_set_source_too_large",
464                                HandleVector<Object>(NULL, 0)));
465   }
466
467   size_t target_offset = NumberToSize(isolate, target->byte_offset());
468   size_t source_offset = NumberToSize(isolate, source->byte_offset());
469   uint8_t* target_base =
470       static_cast<uint8_t*>(target->GetBuffer()->backing_store()) +
471       target_offset;
472   uint8_t* source_base =
473       static_cast<uint8_t*>(source->GetBuffer()->backing_store()) +
474       source_offset;
475
476   // Typed arrays of the same type: use memmove.
477   if (target->type() == source->type()) {
478     memmove(target_base + offset * target->element_size(), source_base,
479             source_byte_length);
480     return Smi::FromInt(TYPED_ARRAY_SET_TYPED_ARRAY_SAME_TYPE);
481   }
482
483   // Typed arrays of different types over the same backing store
484   if ((source_base <= target_base &&
485        source_base + source_byte_length > target_base) ||
486       (target_base <= source_base &&
487        target_base + target_byte_length > source_base)) {
488     // We do not support overlapping ArrayBuffers
489     DCHECK(target->GetBuffer()->backing_store() ==
490            source->GetBuffer()->backing_store());
491     return Smi::FromInt(TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING);
492   } else {  // Non-overlapping typed arrays
493     return Smi::FromInt(TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING);
494   }
495 }
496
497
498 RUNTIME_FUNCTION(Runtime_TypedArrayMaxSizeInHeap) {
499   DCHECK(args.length() == 0);
500   DCHECK_OBJECT_SIZE(FLAG_typed_array_max_size_in_heap +
501                      FixedTypedArrayBase::kDataOffset);
502   return Smi::FromInt(FLAG_typed_array_max_size_in_heap);
503 }
504
505
506 RUNTIME_FUNCTION(Runtime_IsTypedArray) {
507   HandleScope scope(isolate);
508   DCHECK(args.length() == 1);
509   return isolate->heap()->ToBoolean(args[0]->IsJSTypedArray());
510 }
511
512
513 RUNTIME_FUNCTION(Runtime_DataViewInitialize) {
514   HandleScope scope(isolate);
515   DCHECK(args.length() == 4);
516   CONVERT_ARG_HANDLE_CHECKED(JSDataView, holder, 0);
517   CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, buffer, 1);
518   CONVERT_NUMBER_ARG_HANDLE_CHECKED(byte_offset, 2);
519   CONVERT_NUMBER_ARG_HANDLE_CHECKED(byte_length, 3);
520
521   DCHECK(holder->GetInternalFieldCount() ==
522          v8::ArrayBufferView::kInternalFieldCount);
523   for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) {
524     holder->SetInternalField(i, Smi::FromInt(0));
525   }
526   size_t buffer_length = 0;
527   size_t offset = 0;
528   size_t length = 0;
529   RUNTIME_ASSERT(
530       TryNumberToSize(isolate, buffer->byte_length(), &buffer_length));
531   RUNTIME_ASSERT(TryNumberToSize(isolate, *byte_offset, &offset));
532   RUNTIME_ASSERT(TryNumberToSize(isolate, *byte_length, &length));
533
534   // TODO(jkummerow): When we have a "safe numerics" helper class, use it here.
535   // Entire range [offset, offset + length] must be in bounds.
536   RUNTIME_ASSERT(offset <= buffer_length);
537   RUNTIME_ASSERT(offset + length <= buffer_length);
538   // No overflow.
539   RUNTIME_ASSERT(offset + length >= offset);
540
541   holder->set_buffer(*buffer);
542   holder->set_byte_offset(*byte_offset);
543   holder->set_byte_length(*byte_length);
544
545   holder->set_weak_next(buffer->weak_first_view());
546   buffer->set_weak_first_view(*holder);
547
548   return isolate->heap()->undefined_value();
549 }
550
551
552 inline static bool NeedToFlipBytes(bool is_little_endian) {
553 #ifdef V8_TARGET_LITTLE_ENDIAN
554   return !is_little_endian;
555 #else
556   return is_little_endian;
557 #endif
558 }
559
560
561 template <int n>
562 inline void CopyBytes(uint8_t* target, uint8_t* source) {
563   for (int i = 0; i < n; i++) {
564     *(target++) = *(source++);
565   }
566 }
567
568
569 template <int n>
570 inline void FlipBytes(uint8_t* target, uint8_t* source) {
571   source = source + (n - 1);
572   for (int i = 0; i < n; i++) {
573     *(target++) = *(source--);
574   }
575 }
576
577
578 template <typename T>
579 inline static bool DataViewGetValue(Isolate* isolate,
580                                     Handle<JSDataView> data_view,
581                                     Handle<Object> byte_offset_obj,
582                                     bool is_little_endian, T* result) {
583   size_t byte_offset = 0;
584   if (!TryNumberToSize(isolate, *byte_offset_obj, &byte_offset)) {
585     return false;
586   }
587   Handle<JSArrayBuffer> buffer(JSArrayBuffer::cast(data_view->buffer()));
588
589   size_t data_view_byte_offset =
590       NumberToSize(isolate, data_view->byte_offset());
591   size_t data_view_byte_length =
592       NumberToSize(isolate, data_view->byte_length());
593   if (byte_offset + sizeof(T) > data_view_byte_length ||
594       byte_offset + sizeof(T) < byte_offset) {  // overflow
595     return false;
596   }
597
598   union Value {
599     T data;
600     uint8_t bytes[sizeof(T)];
601   };
602
603   Value value;
604   size_t buffer_offset = data_view_byte_offset + byte_offset;
605   DCHECK(NumberToSize(isolate, buffer->byte_length()) >=
606          buffer_offset + sizeof(T));
607   uint8_t* source =
608       static_cast<uint8_t*>(buffer->backing_store()) + buffer_offset;
609   if (NeedToFlipBytes(is_little_endian)) {
610     FlipBytes<sizeof(T)>(value.bytes, source);
611   } else {
612     CopyBytes<sizeof(T)>(value.bytes, source);
613   }
614   *result = value.data;
615   return true;
616 }
617
618
619 template <typename T>
620 static bool DataViewSetValue(Isolate* isolate, Handle<JSDataView> data_view,
621                              Handle<Object> byte_offset_obj,
622                              bool is_little_endian, T data) {
623   size_t byte_offset = 0;
624   if (!TryNumberToSize(isolate, *byte_offset_obj, &byte_offset)) {
625     return false;
626   }
627   Handle<JSArrayBuffer> buffer(JSArrayBuffer::cast(data_view->buffer()));
628
629   size_t data_view_byte_offset =
630       NumberToSize(isolate, data_view->byte_offset());
631   size_t data_view_byte_length =
632       NumberToSize(isolate, data_view->byte_length());
633   if (byte_offset + sizeof(T) > data_view_byte_length ||
634       byte_offset + sizeof(T) < byte_offset) {  // overflow
635     return false;
636   }
637
638   union Value {
639     T data;
640     uint8_t bytes[sizeof(T)];
641   };
642
643   Value value;
644   value.data = data;
645   size_t buffer_offset = data_view_byte_offset + byte_offset;
646   DCHECK(NumberToSize(isolate, buffer->byte_length()) >=
647          buffer_offset + sizeof(T));
648   uint8_t* target =
649       static_cast<uint8_t*>(buffer->backing_store()) + buffer_offset;
650   if (NeedToFlipBytes(is_little_endian)) {
651     FlipBytes<sizeof(T)>(target, value.bytes);
652   } else {
653     CopyBytes<sizeof(T)>(target, value.bytes);
654   }
655   return true;
656 }
657
658
659 #define DATA_VIEW_GETTER(TypeName, Type, Converter)                   \
660   RUNTIME_FUNCTION(Runtime_DataViewGet##TypeName) {                   \
661     HandleScope scope(isolate);                                       \
662     DCHECK(args.length() == 3);                                       \
663     CONVERT_ARG_HANDLE_CHECKED(JSDataView, holder, 0);                \
664     CONVERT_NUMBER_ARG_HANDLE_CHECKED(offset, 1);                     \
665     CONVERT_BOOLEAN_ARG_CHECKED(is_little_endian, 2);                 \
666     Type result;                                                      \
667     if (DataViewGetValue(isolate, holder, offset, is_little_endian,   \
668                          &result)) {                                  \
669       return *isolate->factory()->Converter(result);                  \
670     } else {                                                          \
671       THROW_NEW_ERROR_RETURN_FAILURE(                                 \
672           isolate, NewRangeError("invalid_data_view_accessor_offset", \
673                                  HandleVector<Object>(NULL, 0)));     \
674     }                                                                 \
675   }
676
677 DATA_VIEW_GETTER(Uint8, uint8_t, NewNumberFromUint)
678 DATA_VIEW_GETTER(Int8, int8_t, NewNumberFromInt)
679 DATA_VIEW_GETTER(Uint16, uint16_t, NewNumberFromUint)
680 DATA_VIEW_GETTER(Int16, int16_t, NewNumberFromInt)
681 DATA_VIEW_GETTER(Uint32, uint32_t, NewNumberFromUint)
682 DATA_VIEW_GETTER(Int32, int32_t, NewNumberFromInt)
683 DATA_VIEW_GETTER(Float32, float, NewNumber)
684 DATA_VIEW_GETTER(Float64, double, NewNumber)
685
686 #undef DATA_VIEW_GETTER
687
688
689 template <typename T>
690 static T DataViewConvertValue(double value);
691
692
693 template <>
694 int8_t DataViewConvertValue<int8_t>(double value) {
695   return static_cast<int8_t>(DoubleToInt32(value));
696 }
697
698
699 template <>
700 int16_t DataViewConvertValue<int16_t>(double value) {
701   return static_cast<int16_t>(DoubleToInt32(value));
702 }
703
704
705 template <>
706 int32_t DataViewConvertValue<int32_t>(double value) {
707   return DoubleToInt32(value);
708 }
709
710
711 template <>
712 uint8_t DataViewConvertValue<uint8_t>(double value) {
713   return static_cast<uint8_t>(DoubleToUint32(value));
714 }
715
716
717 template <>
718 uint16_t DataViewConvertValue<uint16_t>(double value) {
719   return static_cast<uint16_t>(DoubleToUint32(value));
720 }
721
722
723 template <>
724 uint32_t DataViewConvertValue<uint32_t>(double value) {
725   return DoubleToUint32(value);
726 }
727
728
729 template <>
730 float DataViewConvertValue<float>(double value) {
731   return static_cast<float>(value);
732 }
733
734
735 template <>
736 double DataViewConvertValue<double>(double value) {
737   return value;
738 }
739
740
741 #define DATA_VIEW_SETTER(TypeName, Type)                                  \
742   RUNTIME_FUNCTION(Runtime_DataViewSet##TypeName) {                       \
743     HandleScope scope(isolate);                                           \
744     DCHECK(args.length() == 4);                                           \
745     CONVERT_ARG_HANDLE_CHECKED(JSDataView, holder, 0);                    \
746     CONVERT_NUMBER_ARG_HANDLE_CHECKED(offset, 1);                         \
747     CONVERT_NUMBER_ARG_HANDLE_CHECKED(value, 2);                          \
748     CONVERT_BOOLEAN_ARG_CHECKED(is_little_endian, 3);                     \
749     Type v = DataViewConvertValue<Type>(value->Number());                 \
750     if (DataViewSetValue(isolate, holder, offset, is_little_endian, v)) { \
751       return isolate->heap()->undefined_value();                          \
752     } else {                                                              \
753       THROW_NEW_ERROR_RETURN_FAILURE(                                     \
754           isolate, NewRangeError("invalid_data_view_accessor_offset",     \
755                                  HandleVector<Object>(NULL, 0)));         \
756     }                                                                     \
757   }
758
759 DATA_VIEW_SETTER(Uint8, uint8_t)
760 DATA_VIEW_SETTER(Int8, int8_t)
761 DATA_VIEW_SETTER(Uint16, uint16_t)
762 DATA_VIEW_SETTER(Int16, int16_t)
763 DATA_VIEW_SETTER(Uint32, uint32_t)
764 DATA_VIEW_SETTER(Int32, int32_t)
765 DATA_VIEW_SETTER(Float32, float)
766 DATA_VIEW_SETTER(Float64, double)
767
768 #undef DATA_VIEW_SETTER
769 }
770 }  // namespace v8::internal