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-inl.h"
6 #include "src/builtins/builtins.h"
7 #include "src/conversions.h"
8 #include "src/counters.h"
9 #include "src/maybe-handles-inl.h"
10 #include "src/objects-inl.h"
11 #include "src/objects/js-array-buffer-inl.h"
12
13 namespace v8 {
14 namespace internal {
15
16 #define CHECK_SHARED(expected, name, method) \
17 if (name->is_shared() != expected) { \
18 THROW_NEW_ERROR_RETURN_FAILURE( \
19 isolate, \
20 NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, \
21 isolate->factory()->NewStringFromAsciiChecked(method), \
22 name)); \
23 }
24
25 // -----------------------------------------------------------------------------
26 // ES6 section 21.1 ArrayBuffer Objects
27
28 namespace {
29
ConstructBuffer(Isolate * isolate,Handle<JSFunction> target,Handle<JSReceiver> new_target,Handle<Object> length,bool initialize)30 Object* ConstructBuffer(Isolate* isolate, Handle<JSFunction> target,
31 Handle<JSReceiver> new_target, Handle<Object> length,
32 bool initialize) {
33 Handle<JSObject> result;
34 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result,
35 JSObject::New(target, new_target));
36 size_t byte_length;
37 if (!TryNumberToSize(*length, &byte_length)) {
38 THROW_NEW_ERROR_RETURN_FAILURE(
39 isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferLength));
40 }
41 SharedFlag shared_flag =
42 (*target == target->native_context()->array_buffer_fun())
43 ? SharedFlag::kNotShared
44 : SharedFlag::kShared;
45 if (!JSArrayBuffer::SetupAllocatingData(Handle<JSArrayBuffer>::cast(result),
46 isolate, byte_length, initialize,
47 shared_flag)) {
48 THROW_NEW_ERROR_RETURN_FAILURE(
49 isolate, NewRangeError(MessageTemplate::kArrayBufferAllocationFailed));
50 }
51 return *result;
52 }
53
54 } // namespace
55
56 // ES #sec-arraybuffer-constructor
BUILTIN(ArrayBufferConstructor)57 BUILTIN(ArrayBufferConstructor) {
58 HandleScope scope(isolate);
59 Handle<JSFunction> target = args.target();
60 DCHECK(*target == target->native_context()->array_buffer_fun() ||
61 *target == target->native_context()->shared_array_buffer_fun());
62 if (args.new_target()->IsUndefined(isolate)) { // [[Call]]
63 THROW_NEW_ERROR_RETURN_FAILURE(
64 isolate, NewTypeError(MessageTemplate::kConstructorNotFunction,
65 handle(target->shared()->Name(), isolate)));
66 }
67 // [[Construct]]
68 Handle<JSReceiver> new_target = Handle<JSReceiver>::cast(args.new_target());
69 Handle<Object> length = args.atOrUndefined(isolate, 1);
70
71 Handle<Object> number_length;
72 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, number_length,
73 Object::ToInteger(isolate, length));
74 if (number_length->Number() < 0.0) {
75 THROW_NEW_ERROR_RETURN_FAILURE(
76 isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferLength));
77 }
78
79 return ConstructBuffer(isolate, target, new_target, number_length, true);
80 }
81
82 // This is a helper to construct an ArrayBuffer with uinitialized memory.
83 // This means the caller must ensure the buffer is totally initialized in
84 // all cases, or we will expose uinitialized memory to user code.
BUILTIN(ArrayBufferConstructor_DoNotInitialize)85 BUILTIN(ArrayBufferConstructor_DoNotInitialize) {
86 HandleScope scope(isolate);
87 Handle<JSFunction> target(isolate->native_context()->array_buffer_fun(),
88 isolate);
89 Handle<Object> length = args.atOrUndefined(isolate, 1);
90 return ConstructBuffer(isolate, target, target, length, false);
91 }
92
93 // ES6 section 24.1.4.1 get ArrayBuffer.prototype.byteLength
BUILTIN(ArrayBufferPrototypeGetByteLength)94 BUILTIN(ArrayBufferPrototypeGetByteLength) {
95 const char* const kMethodName = "get ArrayBuffer.prototype.byteLength";
96 HandleScope scope(isolate);
97 CHECK_RECEIVER(JSArrayBuffer, array_buffer, kMethodName);
98 CHECK_SHARED(false, array_buffer, kMethodName);
99 // TODO(franzih): According to the ES6 spec, we should throw a TypeError
100 // here if the JSArrayBuffer is detached.
101 return array_buffer->byte_length();
102 }
103
104 // ES7 sharedmem 6.3.4.1 get SharedArrayBuffer.prototype.byteLength
BUILTIN(SharedArrayBufferPrototypeGetByteLength)105 BUILTIN(SharedArrayBufferPrototypeGetByteLength) {
106 const char* const kMethodName = "get SharedArrayBuffer.prototype.byteLength";
107 HandleScope scope(isolate);
108 CHECK_RECEIVER(JSArrayBuffer, array_buffer,
109 "get SharedArrayBuffer.prototype.byteLength");
110 CHECK_SHARED(true, array_buffer, kMethodName);
111 return array_buffer->byte_length();
112 }
113
114 // ES6 section 24.1.3.1 ArrayBuffer.isView ( arg )
BUILTIN(ArrayBufferIsView)115 BUILTIN(ArrayBufferIsView) {
116 SealHandleScope shs(isolate);
117 DCHECK_EQ(2, args.length());
118 Object* arg = args[1];
119 return isolate->heap()->ToBoolean(arg->IsJSArrayBufferView());
120 }
121
SliceHelper(BuiltinArguments args,Isolate * isolate,const char * kMethodName,bool is_shared)122 static Object* SliceHelper(BuiltinArguments args, Isolate* isolate,
123 const char* kMethodName, bool is_shared) {
124 HandleScope scope(isolate);
125 Handle<Object> start = args.at(1);
126 Handle<Object> end = args.atOrUndefined(isolate, 2);
127
128 // * If Type(O) is not Object, throw a TypeError exception.
129 // * If O does not have an [[ArrayBufferData]] internal slot, throw a
130 // TypeError exception.
131 CHECK_RECEIVER(JSArrayBuffer, array_buffer, kMethodName);
132 // * [AB] If IsSharedArrayBuffer(O) is true, throw a TypeError exception.
133 // * [SAB] If IsSharedArrayBuffer(O) is false, throw a TypeError exception.
134 CHECK_SHARED(is_shared, array_buffer, kMethodName);
135
136 // * [AB] If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
137 if (!is_shared && array_buffer->was_neutered()) {
138 THROW_NEW_ERROR_RETURN_FAILURE(
139 isolate, NewTypeError(MessageTemplate::kDetachedOperation,
140 isolate->factory()->NewStringFromAsciiChecked(
141 kMethodName)));
142 }
143
144 // * [AB] Let len be O.[[ArrayBufferByteLength]].
145 // * [SAB] Let len be O.[[ArrayBufferByteLength]].
146 double const len = array_buffer->byte_length()->Number();
147
148 // * Let relativeStart be ? ToInteger(start).
149 Handle<Object> relative_start;
150 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, relative_start,
151 Object::ToInteger(isolate, start));
152
153 // * If relativeStart < 0, let first be max((len + relativeStart), 0); else
154 // let first be min(relativeStart, len).
155 double const first = (relative_start->Number() < 0)
156 ? Max(len + relative_start->Number(), 0.0)
157 : Min(relative_start->Number(), len);
158 Handle<Object> first_obj = isolate->factory()->NewNumber(first);
159
160 // * If end is undefined, let relativeEnd be len; else let relativeEnd be ?
161 // ToInteger(end).
162 double relative_end;
163 if (end->IsUndefined(isolate)) {
164 relative_end = len;
165 } else {
166 Handle<Object> relative_end_obj;
167 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, relative_end_obj,
168 Object::ToInteger(isolate, end));
169 relative_end = relative_end_obj->Number();
170 }
171
172 // * If relativeEnd < 0, let final be max((len + relativeEnd), 0); else let
173 // final be min(relativeEnd, len).
174 double const final_ = (relative_end < 0) ? Max(len + relative_end, 0.0)
175 : Min(relative_end, len);
176
177 // * Let newLen be max(final-first, 0).
178 double const new_len = Max(final_ - first, 0.0);
179 Handle<Object> new_len_obj = isolate->factory()->NewNumber(new_len);
180
181 // * [AB] Let ctor be ? SpeciesConstructor(O, %ArrayBuffer%).
182 // * [SAB] Let ctor be ? SpeciesConstructor(O, %SharedArrayBuffer%).
183 Handle<JSFunction> constructor_fun = is_shared
184 ? isolate->shared_array_buffer_fun()
185 : isolate->array_buffer_fun();
186 Handle<Object> ctor;
187 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
188 isolate, ctor,
189 Object::SpeciesConstructor(
190 isolate, Handle<JSReceiver>::cast(args.receiver()), constructor_fun));
191
192 // * Let new be ? Construct(ctor, newLen).
193 Handle<JSReceiver> new_;
194 {
195 const int argc = 1;
196
197 ScopedVector<Handle<Object>> argv(argc);
198 argv[0] = new_len_obj;
199
200 Handle<Object> new_obj;
201 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
202 isolate, new_obj, Execution::New(isolate, ctor, argc, argv.start()));
203
204 new_ = Handle<JSReceiver>::cast(new_obj);
205 }
206
207 // * If new does not have an [[ArrayBufferData]] internal slot, throw a
208 // TypeError exception.
209 if (!new_->IsJSArrayBuffer()) {
210 THROW_NEW_ERROR_RETURN_FAILURE(
211 isolate,
212 NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
213 isolate->factory()->NewStringFromAsciiChecked(kMethodName),
214 new_));
215 }
216
217 // * [AB] If IsSharedArrayBuffer(new) is true, throw a TypeError exception.
218 // * [SAB] If IsSharedArrayBuffer(new) is false, throw a TypeError exception.
219 Handle<JSArrayBuffer> new_array_buffer = Handle<JSArrayBuffer>::cast(new_);
220 CHECK_SHARED(is_shared, new_array_buffer, kMethodName);
221
222 // * [AB] If IsDetachedBuffer(new) is true, throw a TypeError exception.
223 if (!is_shared && new_array_buffer->was_neutered()) {
224 THROW_NEW_ERROR_RETURN_FAILURE(
225 isolate, NewTypeError(MessageTemplate::kDetachedOperation,
226 isolate->factory()->NewStringFromAsciiChecked(
227 kMethodName)));
228 }
229
230 // * [AB] If SameValue(new, O) is true, throw a TypeError exception.
231 if (!is_shared && new_->SameValue(*args.receiver())) {
232 THROW_NEW_ERROR_RETURN_FAILURE(
233 isolate, NewTypeError(MessageTemplate::kArrayBufferSpeciesThis));
234 }
235
236 // * [SAB] If new.[[ArrayBufferData]] and O.[[ArrayBufferData]] are the same
237 // Shared Data Block values, throw a TypeError exception.
238 if (is_shared &&
239 new_array_buffer->backing_store() == array_buffer->backing_store()) {
240 THROW_NEW_ERROR_RETURN_FAILURE(
241 isolate, NewTypeError(MessageTemplate::kSharedArrayBufferSpeciesThis));
242 }
243
244 // * If new.[[ArrayBufferByteLength]] < newLen, throw a TypeError exception.
245 if (new_array_buffer->byte_length()->Number() < new_len) {
246 THROW_NEW_ERROR_RETURN_FAILURE(
247 isolate,
248 NewTypeError(is_shared ? MessageTemplate::kSharedArrayBufferTooShort
249 : MessageTemplate::kArrayBufferTooShort));
250 }
251
252 // * [AB] NOTE: Side-effects of the above steps may have detached O.
253 // * [AB] If IsDetachedBuffer(O) is true, throw a TypeError exception.
254 if (!is_shared && array_buffer->was_neutered()) {
255 THROW_NEW_ERROR_RETURN_FAILURE(
256 isolate, NewTypeError(MessageTemplate::kDetachedOperation,
257 isolate->factory()->NewStringFromAsciiChecked(
258 kMethodName)));
259 }
260
261 // * Let fromBuf be O.[[ArrayBufferData]].
262 // * Let toBuf be new.[[ArrayBufferData]].
263 // * Perform CopyDataBlockBytes(toBuf, 0, fromBuf, first, newLen).
264 size_t first_size = 0, new_len_size = 0;
265 CHECK(TryNumberToSize(*first_obj, &first_size));
266 CHECK(TryNumberToSize(*new_len_obj, &new_len_size));
267 DCHECK(NumberToSize(new_array_buffer->byte_length()) >= new_len_size);
268
269 if (new_len_size != 0) {
270 size_t from_byte_length = NumberToSize(array_buffer->byte_length());
271 USE(from_byte_length);
272 DCHECK(first_size <= from_byte_length);
273 DCHECK(from_byte_length - first_size >= new_len_size);
274 uint8_t* from_data =
275 reinterpret_cast<uint8_t*>(array_buffer->backing_store());
276 uint8_t* to_data =
277 reinterpret_cast<uint8_t*>(new_array_buffer->backing_store());
278 CopyBytes(to_data, from_data + first_size, new_len_size);
279 }
280
281 return *new_;
282 }
283
284 // ES #sec-sharedarraybuffer.prototype.slice
BUILTIN(SharedArrayBufferPrototypeSlice)285 BUILTIN(SharedArrayBufferPrototypeSlice) {
286 const char* const kMethodName = "SharedArrayBuffer.prototype.slice";
287 return SliceHelper(args, isolate, kMethodName, true);
288 }
289
290 // ES #sec-arraybuffer.prototype.slice
291 // ArrayBuffer.prototype.slice ( start, end )
BUILTIN(ArrayBufferPrototypeSlice)292 BUILTIN(ArrayBufferPrototypeSlice) {
293 const char* const kMethodName = "ArrayBuffer.prototype.slice";
294 return SliceHelper(args, isolate, kMethodName, false);
295 }
296
297 } // namespace internal
298 } // namespace v8
299