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.h"
6 #include "src/builtins/builtins-utils.h"
7 
8 namespace v8 {
9 namespace internal {
10 
11 // -----------------------------------------------------------------------------
12 // ES6 section 24.2 DataView Objects
13 
14 // ES6 section 24.2.2 The DataView Constructor for the [[Call]] case.
BUILTIN(DataViewConstructor)15 BUILTIN(DataViewConstructor) {
16   HandleScope scope(isolate);
17   THROW_NEW_ERROR_RETURN_FAILURE(
18       isolate,
19       NewTypeError(MessageTemplate::kConstructorNotFunction,
20                    isolate->factory()->NewStringFromAsciiChecked("DataView")));
21 }
22 
23 // ES6 section 24.2.2 The DataView Constructor for the [[Construct]] case.
BUILTIN(DataViewConstructor_ConstructStub)24 BUILTIN(DataViewConstructor_ConstructStub) {
25   HandleScope scope(isolate);
26   Handle<JSFunction> target = args.target();
27   Handle<JSReceiver> new_target = Handle<JSReceiver>::cast(args.new_target());
28   Handle<Object> buffer = args.atOrUndefined(isolate, 1);
29   Handle<Object> byte_offset = args.atOrUndefined(isolate, 2);
30   Handle<Object> byte_length = args.atOrUndefined(isolate, 3);
31 
32   // 2. If Type(buffer) is not Object, throw a TypeError exception.
33   // 3. If buffer does not have an [[ArrayBufferData]] internal slot, throw a
34   //    TypeError exception.
35   if (!buffer->IsJSArrayBuffer()) {
36     THROW_NEW_ERROR_RETURN_FAILURE(
37         isolate, NewTypeError(MessageTemplate::kDataViewNotArrayBuffer));
38   }
39   Handle<JSArrayBuffer> array_buffer = Handle<JSArrayBuffer>::cast(buffer);
40 
41   // 4. Let offset be ToIndex(byteOffset).
42   Handle<Object> offset;
43   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
44       isolate, offset,
45       Object::ToIndex(isolate, byte_offset,
46                       MessageTemplate::kInvalidDataViewOffset));
47 
48   // 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
49   // We currently violate the specification at this point.
50 
51   // 6. Let bufferByteLength be the value of buffer's [[ArrayBufferByteLength]]
52   // internal slot.
53   double const buffer_byte_length = array_buffer->byte_length()->Number();
54 
55   // 7. If offset > bufferByteLength, throw a RangeError exception
56   if (offset->Number() > buffer_byte_length) {
57     THROW_NEW_ERROR_RETURN_FAILURE(
58         isolate,
59         NewRangeError(MessageTemplate::kInvalidDataViewOffset, offset));
60   }
61 
62   Handle<Object> view_byte_length;
63   if (byte_length->IsUndefined(isolate)) {
64     // 8. If byteLength is 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 RangeError
72     //          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   // 11. Set O's [[DataView]] internal slot to true.
87   Handle<JSObject> result;
88   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result,
89                                      JSObject::New(target, new_target));
90   for (int i = 0; i < ArrayBufferView::kInternalFieldCount; ++i) {
91     Handle<JSDataView>::cast(result)->SetInternalField(i, Smi::kZero);
92   }
93 
94   // 12. Set O's [[ViewedArrayBuffer]] internal slot to buffer.
95   Handle<JSDataView>::cast(result)->set_buffer(*array_buffer);
96 
97   // 13. Set O's [[ByteLength]] internal slot to viewByteLength.
98   Handle<JSDataView>::cast(result)->set_byte_length(*view_byte_length);
99 
100   // 14. Set O's [[ByteOffset]] internal slot to offset.
101   Handle<JSDataView>::cast(result)->set_byte_offset(*offset);
102 
103   // 15. Return O.
104   return *result;
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 // ES6 section 24.2.1.1 GetViewValue (view, requestIndex, isLittleEndian, type)
158 template <typename T>
GetViewValue(Isolate * isolate,Handle<JSDataView> data_view,Handle<Object> request_index,bool is_little_endian)159 MaybeHandle<Object> GetViewValue(Isolate* isolate, Handle<JSDataView> data_view,
160                                  Handle<Object> request_index,
161                                  bool is_little_endian) {
162   ASSIGN_RETURN_ON_EXCEPTION(
163       isolate, request_index,
164       Object::ToIndex(isolate, request_index,
165                       MessageTemplate::kInvalidDataViewAccessorOffset),
166       Object);
167   size_t get_index = 0;
168   if (!TryNumberToSize(*request_index, &get_index)) {
169     THROW_NEW_ERROR(
170         isolate, NewRangeError(MessageTemplate::kInvalidDataViewAccessorOffset),
171         Object);
172   }
173   Handle<JSArrayBuffer> buffer(JSArrayBuffer::cast(data_view->buffer()),
174                                isolate);
175   size_t const data_view_byte_offset = NumberToSize(data_view->byte_offset());
176   size_t const data_view_byte_length = NumberToSize(data_view->byte_length());
177   if (get_index + sizeof(T) > data_view_byte_length ||
178       get_index + sizeof(T) < get_index) {  // overflow
179     THROW_NEW_ERROR(
180         isolate, NewRangeError(MessageTemplate::kInvalidDataViewAccessorOffset),
181         Object);
182   }
183   union {
184     T data;
185     uint8_t bytes[sizeof(T)];
186   } v;
187   size_t const buffer_offset = data_view_byte_offset + get_index;
188   DCHECK_GE(NumberToSize(buffer->byte_length()), buffer_offset + sizeof(T));
189   uint8_t const* const source =
190       static_cast<uint8_t*>(buffer->backing_store()) + buffer_offset;
191   if (NeedToFlipBytes(is_little_endian)) {
192     FlipBytes<sizeof(T)>(v.bytes, source);
193   } else {
194     CopyBytes<sizeof(T)>(v.bytes, source);
195   }
196   return isolate->factory()->NewNumber(v.data);
197 }
198 
199 template <typename T>
200 T DataViewConvertValue(double value);
201 
202 template <>
DataViewConvertValue(double value)203 int8_t DataViewConvertValue<int8_t>(double value) {
204   return static_cast<int8_t>(DoubleToInt32(value));
205 }
206 
207 template <>
DataViewConvertValue(double value)208 int16_t DataViewConvertValue<int16_t>(double value) {
209   return static_cast<int16_t>(DoubleToInt32(value));
210 }
211 
212 template <>
DataViewConvertValue(double value)213 int32_t DataViewConvertValue<int32_t>(double value) {
214   return DoubleToInt32(value);
215 }
216 
217 template <>
DataViewConvertValue(double value)218 uint8_t DataViewConvertValue<uint8_t>(double value) {
219   return static_cast<uint8_t>(DoubleToUint32(value));
220 }
221 
222 template <>
DataViewConvertValue(double value)223 uint16_t DataViewConvertValue<uint16_t>(double value) {
224   return static_cast<uint16_t>(DoubleToUint32(value));
225 }
226 
227 template <>
DataViewConvertValue(double value)228 uint32_t DataViewConvertValue<uint32_t>(double value) {
229   return DoubleToUint32(value);
230 }
231 
232 template <>
DataViewConvertValue(double value)233 float DataViewConvertValue<float>(double value) {
234   return static_cast<float>(value);
235 }
236 
237 template <>
DataViewConvertValue(double value)238 double DataViewConvertValue<double>(double value) {
239   return value;
240 }
241 
242 // ES6 section 24.2.1.2 SetViewValue (view, requestIndex, isLittleEndian, type,
243 //                                    value)
244 template <typename T>
SetViewValue(Isolate * isolate,Handle<JSDataView> data_view,Handle<Object> request_index,bool is_little_endian,Handle<Object> value)245 MaybeHandle<Object> SetViewValue(Isolate* isolate, Handle<JSDataView> data_view,
246                                  Handle<Object> request_index,
247                                  bool is_little_endian, Handle<Object> value) {
248   ASSIGN_RETURN_ON_EXCEPTION(
249       isolate, request_index,
250       Object::ToIndex(isolate, request_index,
251                       MessageTemplate::kInvalidDataViewAccessorOffset),
252       Object);
253   ASSIGN_RETURN_ON_EXCEPTION(isolate, value, Object::ToNumber(value), Object);
254   size_t get_index = 0;
255   if (!TryNumberToSize(*request_index, &get_index)) {
256     THROW_NEW_ERROR(
257         isolate, NewRangeError(MessageTemplate::kInvalidDataViewAccessorOffset),
258         Object);
259   }
260   Handle<JSArrayBuffer> buffer(JSArrayBuffer::cast(data_view->buffer()),
261                                isolate);
262   size_t const data_view_byte_offset = NumberToSize(data_view->byte_offset());
263   size_t const data_view_byte_length = NumberToSize(data_view->byte_length());
264   if (get_index + sizeof(T) > data_view_byte_length ||
265       get_index + sizeof(T) < get_index) {  // overflow
266     THROW_NEW_ERROR(
267         isolate, NewRangeError(MessageTemplate::kInvalidDataViewAccessorOffset),
268         Object);
269   }
270   union {
271     T data;
272     uint8_t bytes[sizeof(T)];
273   } v;
274   v.data = DataViewConvertValue<T>(value->Number());
275   size_t const buffer_offset = data_view_byte_offset + get_index;
276   DCHECK(NumberToSize(buffer->byte_length()) >= buffer_offset + sizeof(T));
277   uint8_t* const target =
278       static_cast<uint8_t*>(buffer->backing_store()) + buffer_offset;
279   if (NeedToFlipBytes(is_little_endian)) {
280     FlipBytes<sizeof(T)>(target, v.bytes);
281   } else {
282     CopyBytes<sizeof(T)>(target, v.bytes);
283   }
284   return isolate->factory()->undefined_value();
285 }
286 
287 }  // namespace
288 
289 #define DATA_VIEW_PROTOTYPE_GET(Type, type)                                \
290   BUILTIN(DataViewPrototypeGet##Type) {                                    \
291     HandleScope scope(isolate);                                            \
292     CHECK_RECEIVER(JSDataView, data_view, "DataView.prototype.get" #Type); \
293     Handle<Object> byte_offset = args.atOrUndefined(isolate, 1);           \
294     Handle<Object> is_little_endian = args.atOrUndefined(isolate, 2);      \
295     Handle<Object> result;                                                 \
296     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(                                    \
297         isolate, result,                                                   \
298         GetViewValue<type>(isolate, data_view, byte_offset,                \
299                            is_little_endian->BooleanValue()));             \
300     return *result;                                                        \
301   }
302 DATA_VIEW_PROTOTYPE_GET(Int8, int8_t)
303 DATA_VIEW_PROTOTYPE_GET(Uint8, uint8_t)
304 DATA_VIEW_PROTOTYPE_GET(Int16, int16_t)
305 DATA_VIEW_PROTOTYPE_GET(Uint16, uint16_t)
306 DATA_VIEW_PROTOTYPE_GET(Int32, int32_t)
307 DATA_VIEW_PROTOTYPE_GET(Uint32, uint32_t)
308 DATA_VIEW_PROTOTYPE_GET(Float32, float)
309 DATA_VIEW_PROTOTYPE_GET(Float64, double)
310 #undef DATA_VIEW_PROTOTYPE_GET
311 
312 #define DATA_VIEW_PROTOTYPE_SET(Type, type)                                \
313   BUILTIN(DataViewPrototypeSet##Type) {                                    \
314     HandleScope scope(isolate);                                            \
315     CHECK_RECEIVER(JSDataView, data_view, "DataView.prototype.set" #Type); \
316     Handle<Object> byte_offset = args.atOrUndefined(isolate, 1);           \
317     Handle<Object> value = args.atOrUndefined(isolate, 2);                 \
318     Handle<Object> is_little_endian = args.atOrUndefined(isolate, 3);      \
319     Handle<Object> result;                                                 \
320     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(                                    \
321         isolate, result,                                                   \
322         SetViewValue<type>(isolate, data_view, byte_offset,                \
323                            is_little_endian->BooleanValue(), value));      \
324     return *result;                                                        \
325   }
326 DATA_VIEW_PROTOTYPE_SET(Int8, int8_t)
327 DATA_VIEW_PROTOTYPE_SET(Uint8, uint8_t)
328 DATA_VIEW_PROTOTYPE_SET(Int16, int16_t)
329 DATA_VIEW_PROTOTYPE_SET(Uint16, uint16_t)
330 DATA_VIEW_PROTOTYPE_SET(Int32, int32_t)
331 DATA_VIEW_PROTOTYPE_SET(Uint32, uint32_t)
332 DATA_VIEW_PROTOTYPE_SET(Float32, float)
333 DATA_VIEW_PROTOTYPE_SET(Float64, double)
334 #undef DATA_VIEW_PROTOTYPE_SET
335 
336 }  // namespace internal
337 }  // namespace v8
338