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