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