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/conversions.h"
8 #include "src/counters.h"
9 #include "src/heap/factory.h"
10 #include "src/isolate.h"
11 #include "src/objects-inl.h"
12 
13 namespace v8 {
14 namespace internal {
15 
16 // -----------------------------------------------------------------------------
17 // ES #sec-dataview-objects
18 
19 // ES #sec-dataview-constructor
BUILTIN(DataViewConstructor)20 BUILTIN(DataViewConstructor) {
21   HandleScope scope(isolate);
22   if (args.new_target()->IsUndefined(isolate)) {  // [[Call]]
23     THROW_NEW_ERROR_RETURN_FAILURE(
24         isolate, NewTypeError(MessageTemplate::kConstructorNotFunction,
25                               isolate->factory()->NewStringFromAsciiChecked(
26                                   "DataView")));
27   } else {  // [[Construct]]
28     Handle<JSFunction> target = args.target();
29     Handle<JSReceiver> new_target = Handle<JSReceiver>::cast(args.new_target());
30     Handle<Object> buffer = args.atOrUndefined(isolate, 1);
31     Handle<Object> byte_offset = args.atOrUndefined(isolate, 2);
32     Handle<Object> byte_length = args.atOrUndefined(isolate, 3);
33 
34     // 2. If Type(buffer) is not Object, throw a TypeError exception.
35     // 3. If buffer does not have an [[ArrayBufferData]] internal slot, throw a
36     //    TypeError exception.
37     if (!buffer->IsJSArrayBuffer()) {
38       THROW_NEW_ERROR_RETURN_FAILURE(
39           isolate, NewTypeError(MessageTemplate::kDataViewNotArrayBuffer));
40     }
41     Handle<JSArrayBuffer> array_buffer = Handle<JSArrayBuffer>::cast(buffer);
42 
43     // 4. Let offset be ? ToIndex(byteOffset).
44     Handle<Object> offset;
45     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
46         isolate, offset,
47         Object::ToIndex(isolate, byte_offset, MessageTemplate::kInvalidOffset));
48 
49     // 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
50     //    We currently violate the specification at this point. TODO: Fix that.
51 
52     // 6. Let bufferByteLength be the value of buffer's
53     // [[ArrayBufferByteLength]] internal slot.
54     double const buffer_byte_length = array_buffer->byte_length()->Number();
55 
56     // 7. If offset > bufferByteLength, throw a RangeError exception.
57     if (offset->Number() > buffer_byte_length) {
58       THROW_NEW_ERROR_RETURN_FAILURE(
59           isolate, NewRangeError(MessageTemplate::kInvalidOffset, offset));
60     }
61 
62     Handle<Object> view_byte_length;
63     if (byte_length->IsUndefined(isolate)) {
64       // 8. If byteLength is either not present or undefined, then
65       //       a. Let viewByteLength be bufferByteLength - offset.
66       view_byte_length =
67           isolate->factory()->NewNumber(buffer_byte_length - offset->Number());
68     } else {
69       // 9. Else,
70       //       a. Let viewByteLength be ? ToIndex(byteLength).
71       //       b. If offset+viewByteLength > bufferByteLength, throw a
72       //          RangeError exception.
73       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
74           isolate, view_byte_length,
75           Object::ToIndex(isolate, byte_length,
76                           MessageTemplate::kInvalidDataViewLength));
77       if (offset->Number() + view_byte_length->Number() > buffer_byte_length) {
78         THROW_NEW_ERROR_RETURN_FAILURE(
79             isolate, NewRangeError(MessageTemplate::kInvalidDataViewLength));
80       }
81     }
82 
83     // 10. Let O be ? OrdinaryCreateFromConstructor(NewTarget,
84     //     "%DataViewPrototype%", «[[DataView]], [[ViewedArrayBuffer]],
85     //     [[ByteLength]], [[ByteOffset]]»).
86     Handle<JSObject> result;
87     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result,
88                                        JSObject::New(target, new_target));
89     for (int i = 0; i < ArrayBufferView::kEmbedderFieldCount; ++i) {
90       Handle<JSDataView>::cast(result)->SetEmbedderField(i, Smi::kZero);
91     }
92 
93     // 11. Set O's [[ViewedArrayBuffer]] internal slot to buffer.
94     Handle<JSDataView>::cast(result)->set_buffer(*array_buffer);
95 
96     // 12. Set O's [[ByteLength]] internal slot to viewByteLength.
97     Handle<JSDataView>::cast(result)->set_byte_length(*view_byte_length);
98 
99     // 13. Set O's [[ByteOffset]] internal slot to offset.
100     Handle<JSDataView>::cast(result)->set_byte_offset(*offset);
101 
102     // 14. Return O.
103     return *result;
104   }
105 }
106 
107 // ES6 section 24.2.4.1 get DataView.prototype.buffer
BUILTIN(DataViewPrototypeGetBuffer)108 BUILTIN(DataViewPrototypeGetBuffer) {
109   HandleScope scope(isolate);
110   CHECK_RECEIVER(JSDataView, data_view, "get DataView.prototype.buffer");
111   return data_view->buffer();
112 }
113 
114 // ES6 section 24.2.4.2 get DataView.prototype.byteLength
BUILTIN(DataViewPrototypeGetByteLength)115 BUILTIN(DataViewPrototypeGetByteLength) {
116   HandleScope scope(isolate);
117   CHECK_RECEIVER(JSDataView, data_view, "get DataView.prototype.byteLength");
118   // TODO(bmeurer): According to the ES6 spec, we should throw a TypeError
119   // here if the JSArrayBuffer of the {data_view} was neutered.
120   return data_view->byte_length();
121 }
122 
123 // ES6 section 24.2.4.3 get DataView.prototype.byteOffset
BUILTIN(DataViewPrototypeGetByteOffset)124 BUILTIN(DataViewPrototypeGetByteOffset) {
125   HandleScope scope(isolate);
126   CHECK_RECEIVER(JSDataView, data_view, "get DataView.prototype.byteOffset");
127   // TODO(bmeurer): According to the ES6 spec, we should throw a TypeError
128   // here if the JSArrayBuffer of the {data_view} was neutered.
129   return data_view->byte_offset();
130 }
131 
132 namespace {
133 
NeedToFlipBytes(bool is_little_endian)134 bool NeedToFlipBytes(bool is_little_endian) {
135 #ifdef V8_TARGET_LITTLE_ENDIAN
136   return !is_little_endian;
137 #else
138   return is_little_endian;
139 #endif
140 }
141 
142 template <size_t n>
CopyBytes(uint8_t * target,uint8_t const * source)143 void CopyBytes(uint8_t* target, uint8_t const* source) {
144   for (size_t i = 0; i < n; i++) {
145     *(target++) = *(source++);
146   }
147 }
148 
149 template <size_t n>
FlipBytes(uint8_t * target,uint8_t const * source)150 void FlipBytes(uint8_t* target, uint8_t const* source) {
151   source = source + (n - 1);
152   for (size_t i = 0; i < n; i++) {
153     *(target++) = *(source--);
154   }
155 }
156 
157 template <typename T>
AllocateResult(Isolate * isolate,T value)158 MaybeHandle<Object> AllocateResult(Isolate* isolate, T value) {
159   return isolate->factory()->NewNumber(value);
160 }
161 
162 template <>
AllocateResult(Isolate * isolate,int64_t value)163 MaybeHandle<Object> AllocateResult(Isolate* isolate, int64_t value) {
164   return BigInt::FromInt64(isolate, value);
165 }
166 
167 template <>
AllocateResult(Isolate * isolate,uint64_t value)168 MaybeHandle<Object> AllocateResult(Isolate* isolate, uint64_t value) {
169   return BigInt::FromUint64(isolate, value);
170 }
171 
172 // ES6 section 24.2.1.1 GetViewValue (view, requestIndex, isLittleEndian, type)
173 template <typename T>
GetViewValue(Isolate * isolate,Handle<JSDataView> data_view,Handle<Object> request_index,bool is_little_endian,const char * method)174 MaybeHandle<Object> GetViewValue(Isolate* isolate, Handle<JSDataView> data_view,
175                                  Handle<Object> request_index,
176                                  bool is_little_endian, const char* method) {
177   ASSIGN_RETURN_ON_EXCEPTION(
178       isolate, request_index,
179       Object::ToIndex(isolate, request_index,
180                       MessageTemplate::kInvalidDataViewAccessorOffset),
181       Object);
182   size_t get_index = 0;
183   if (!TryNumberToSize(*request_index, &get_index)) {
184     THROW_NEW_ERROR(
185         isolate, NewRangeError(MessageTemplate::kInvalidDataViewAccessorOffset),
186         Object);
187   }
188   Handle<JSArrayBuffer> buffer(JSArrayBuffer::cast(data_view->buffer()),
189                                isolate);
190   if (buffer->was_neutered()) {
191     Handle<String> operation =
192         isolate->factory()->NewStringFromAsciiChecked(method);
193     THROW_NEW_ERROR(
194         isolate, NewTypeError(MessageTemplate::kDetachedOperation, operation),
195         Object);
196   }
197   size_t const data_view_byte_offset = NumberToSize(data_view->byte_offset());
198   size_t const data_view_byte_length = NumberToSize(data_view->byte_length());
199   if (get_index + sizeof(T) > data_view_byte_length ||
200       get_index + sizeof(T) < get_index) {  // overflow
201     THROW_NEW_ERROR(
202         isolate, NewRangeError(MessageTemplate::kInvalidDataViewAccessorOffset),
203         Object);
204   }
205   union {
206     T data;
207     uint8_t bytes[sizeof(T)];
208   } v;
209   size_t const buffer_offset = data_view_byte_offset + get_index;
210   DCHECK_GE(NumberToSize(buffer->byte_length()), buffer_offset + sizeof(T));
211   uint8_t const* const source =
212       static_cast<uint8_t*>(buffer->backing_store()) + buffer_offset;
213   if (NeedToFlipBytes(is_little_endian)) {
214     FlipBytes<sizeof(T)>(v.bytes, source);
215   } else {
216     CopyBytes<sizeof(T)>(v.bytes, source);
217   }
218   return AllocateResult<T>(isolate, v.data);
219 }
220 
221 template <typename T>
DataViewConvertInput(Isolate * isolate,Handle<Object> input)222 MaybeHandle<Object> DataViewConvertInput(Isolate* isolate,
223                                          Handle<Object> input) {
224   return Object::ToNumber(input);
225 }
226 
227 template <>
DataViewConvertInput(Isolate * isolate,Handle<Object> input)228 MaybeHandle<Object> DataViewConvertInput<int64_t>(Isolate* isolate,
229                                                   Handle<Object> input) {
230   return BigInt::FromObject(isolate, input);
231 }
232 
233 template <>
DataViewConvertInput(Isolate * isolate,Handle<Object> input)234 MaybeHandle<Object> DataViewConvertInput<uint64_t>(Isolate* isolate,
235                                                    Handle<Object> input) {
236   return BigInt::FromObject(isolate, input);
237 }
238 
239 template <typename T>
240 T DataViewConvertValue(Handle<Object> value);
241 
242 template <>
DataViewConvertValue(Handle<Object> value)243 int8_t DataViewConvertValue<int8_t>(Handle<Object> value) {
244   return static_cast<int8_t>(DoubleToInt32(value->Number()));
245 }
246 
247 template <>
DataViewConvertValue(Handle<Object> value)248 int16_t DataViewConvertValue<int16_t>(Handle<Object> value) {
249   return static_cast<int16_t>(DoubleToInt32(value->Number()));
250 }
251 
252 template <>
DataViewConvertValue(Handle<Object> value)253 int32_t DataViewConvertValue<int32_t>(Handle<Object> value) {
254   return DoubleToInt32(value->Number());
255 }
256 
257 template <>
DataViewConvertValue(Handle<Object> value)258 uint8_t DataViewConvertValue<uint8_t>(Handle<Object> value) {
259   return static_cast<uint8_t>(DoubleToUint32(value->Number()));
260 }
261 
262 template <>
DataViewConvertValue(Handle<Object> value)263 uint16_t DataViewConvertValue<uint16_t>(Handle<Object> value) {
264   return static_cast<uint16_t>(DoubleToUint32(value->Number()));
265 }
266 
267 template <>
DataViewConvertValue(Handle<Object> value)268 uint32_t DataViewConvertValue<uint32_t>(Handle<Object> value) {
269   return DoubleToUint32(value->Number());
270 }
271 
272 template <>
DataViewConvertValue(Handle<Object> value)273 float DataViewConvertValue<float>(Handle<Object> value) {
274   return static_cast<float>(value->Number());
275 }
276 
277 template <>
DataViewConvertValue(Handle<Object> value)278 double DataViewConvertValue<double>(Handle<Object> value) {
279   return value->Number();
280 }
281 
282 template <>
DataViewConvertValue(Handle<Object> value)283 int64_t DataViewConvertValue<int64_t>(Handle<Object> value) {
284   return BigInt::cast(*value)->AsInt64();
285 }
286 
287 template <>
DataViewConvertValue(Handle<Object> value)288 uint64_t DataViewConvertValue<uint64_t>(Handle<Object> value) {
289   return BigInt::cast(*value)->AsUint64();
290 }
291 
292 // ES6 section 24.2.1.2 SetViewValue (view, requestIndex, isLittleEndian, type,
293 //                                    value)
294 template <typename T>
SetViewValue(Isolate * isolate,Handle<JSDataView> data_view,Handle<Object> request_index,bool is_little_endian,Handle<Object> value,const char * method)295 MaybeHandle<Object> SetViewValue(Isolate* isolate, Handle<JSDataView> data_view,
296                                  Handle<Object> request_index,
297                                  bool is_little_endian, Handle<Object> value,
298                                  const char* method) {
299   ASSIGN_RETURN_ON_EXCEPTION(
300       isolate, request_index,
301       Object::ToIndex(isolate, request_index,
302                       MessageTemplate::kInvalidDataViewAccessorOffset),
303       Object);
304   ASSIGN_RETURN_ON_EXCEPTION(isolate, value,
305                              DataViewConvertInput<T>(isolate, value), Object);
306   size_t get_index = 0;
307   if (!TryNumberToSize(*request_index, &get_index)) {
308     THROW_NEW_ERROR(
309         isolate, NewRangeError(MessageTemplate::kInvalidDataViewAccessorOffset),
310         Object);
311   }
312   Handle<JSArrayBuffer> buffer(JSArrayBuffer::cast(data_view->buffer()),
313                                isolate);
314   if (buffer->was_neutered()) {
315     Handle<String> operation =
316         isolate->factory()->NewStringFromAsciiChecked(method);
317     THROW_NEW_ERROR(
318         isolate, NewTypeError(MessageTemplate::kDetachedOperation, operation),
319         Object);
320   }
321   size_t const data_view_byte_offset = NumberToSize(data_view->byte_offset());
322   size_t const data_view_byte_length = NumberToSize(data_view->byte_length());
323   if (get_index + sizeof(T) > data_view_byte_length ||
324       get_index + sizeof(T) < get_index) {  // overflow
325     THROW_NEW_ERROR(
326         isolate, NewRangeError(MessageTemplate::kInvalidDataViewAccessorOffset),
327         Object);
328   }
329   union {
330     T data;
331     uint8_t bytes[sizeof(T)];
332   } v;
333   v.data = DataViewConvertValue<T>(value);
334   size_t const buffer_offset = data_view_byte_offset + get_index;
335   DCHECK(NumberToSize(buffer->byte_length()) >= buffer_offset + sizeof(T));
336   uint8_t* const target =
337       static_cast<uint8_t*>(buffer->backing_store()) + buffer_offset;
338   if (NeedToFlipBytes(is_little_endian)) {
339     FlipBytes<sizeof(T)>(target, v.bytes);
340   } else {
341     CopyBytes<sizeof(T)>(target, v.bytes);
342   }
343   return isolate->factory()->undefined_value();
344 }
345 
346 }  // namespace
347 
348 #define DATA_VIEW_PROTOTYPE_GET(Type, type)                                \
349   BUILTIN(DataViewPrototypeGet##Type) {                                    \
350     HandleScope scope(isolate);                                            \
351     CHECK_RECEIVER(JSDataView, data_view, "DataView.prototype.get" #Type); \
352     Handle<Object> byte_offset = args.atOrUndefined(isolate, 1);           \
353     Handle<Object> is_little_endian = args.atOrUndefined(isolate, 2);      \
354     Handle<Object> result;                                                 \
355     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(                                    \
356         isolate, result,                                                   \
357         GetViewValue<type>(isolate, data_view, byte_offset,                \
358                            is_little_endian->BooleanValue(),               \
359                            "DataView.prototype.get" #Type));               \
360     return *result;                                                        \
361   }
362 DATA_VIEW_PROTOTYPE_GET(Int8, int8_t)
363 DATA_VIEW_PROTOTYPE_GET(Uint8, uint8_t)
364 DATA_VIEW_PROTOTYPE_GET(Int16, int16_t)
365 DATA_VIEW_PROTOTYPE_GET(Uint16, uint16_t)
366 DATA_VIEW_PROTOTYPE_GET(Int32, int32_t)
367 DATA_VIEW_PROTOTYPE_GET(Uint32, uint32_t)
368 DATA_VIEW_PROTOTYPE_GET(Float32, float)
369 DATA_VIEW_PROTOTYPE_GET(Float64, double)
370 DATA_VIEW_PROTOTYPE_GET(BigInt64, int64_t)
371 DATA_VIEW_PROTOTYPE_GET(BigUint64, uint64_t)
372 #undef DATA_VIEW_PROTOTYPE_GET
373 
374 #define DATA_VIEW_PROTOTYPE_SET(Type, type)                                \
375   BUILTIN(DataViewPrototypeSet##Type) {                                    \
376     HandleScope scope(isolate);                                            \
377     CHECK_RECEIVER(JSDataView, data_view, "DataView.prototype.set" #Type); \
378     Handle<Object> byte_offset = args.atOrUndefined(isolate, 1);           \
379     Handle<Object> value = args.atOrUndefined(isolate, 2);                 \
380     Handle<Object> is_little_endian = args.atOrUndefined(isolate, 3);      \
381     Handle<Object> result;                                                 \
382     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(                                    \
383         isolate, result,                                                   \
384         SetViewValue<type>(isolate, data_view, byte_offset,                \
385                            is_little_endian->BooleanValue(), value,        \
386                            "DataView.prototype.get" #Type));               \
387     return *result;                                                        \
388   }
389 DATA_VIEW_PROTOTYPE_SET(Int8, int8_t)
390 DATA_VIEW_PROTOTYPE_SET(Uint8, uint8_t)
391 DATA_VIEW_PROTOTYPE_SET(Int16, int16_t)
392 DATA_VIEW_PROTOTYPE_SET(Uint16, uint16_t)
393 DATA_VIEW_PROTOTYPE_SET(Int32, int32_t)
394 DATA_VIEW_PROTOTYPE_SET(Uint32, uint32_t)
395 DATA_VIEW_PROTOTYPE_SET(Float32, float)
396 DATA_VIEW_PROTOTYPE_SET(Float64, double)
397 DATA_VIEW_PROTOTYPE_SET(BigInt64, int64_t)
398 DATA_VIEW_PROTOTYPE_SET(BigUint64, uint64_t)
399 #undef DATA_VIEW_PROTOTYPE_SET
400 
401 }  // namespace internal
402 }  // namespace v8
403