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/handles/maybe-handles-inl.h" 8#include "src/heap/heap-inl.h" // For ToBoolean. TODO(jkummerow): Drop. 9#include "src/logging/counters.h" 10#include "src/numbers/conversions.h" 11#include "src/objects/js-array-buffer-inl.h" 12#include "src/objects/objects-inl.h" 13 14namespace v8 { 15namespace internal { 16 17#define CHECK_SHARED(expected, name, method) \ 18 if (name->is_shared() != expected) { \ 19 THROW_NEW_ERROR_RETURN_FAILURE( \ 20 isolate, \ 21 NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, \ 22 isolate->factory()->NewStringFromAsciiChecked(method), \ 23 name)); \ 24 } 25 26#define CHECK_RESIZABLE(expected, name, method) \ 27 if (name->is_resizable() != expected) { \ 28 THROW_NEW_ERROR_RETURN_FAILURE( \ 29 isolate, \ 30 NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, \ 31 isolate->factory()->NewStringFromAsciiChecked(method), \ 32 name)); \ 33 } 34 35// ----------------------------------------------------------------------------- 36// ES#sec-arraybuffer-objects 37 38namespace { 39 40Object ConstructBuffer(Isolate* isolate, Handle<JSFunction> target, 41 Handle<JSReceiver> new_target, Handle<Object> length, 42 Handle<Object> max_length, InitializedFlag initialized) { 43 SharedFlag shared = *target != target->native_context().array_buffer_fun() 44 ? SharedFlag::kShared 45 : SharedFlag::kNotShared; 46 ResizableFlag resizable = max_length.is_null() ? ResizableFlag::kNotResizable 47 : ResizableFlag::kResizable; 48 Handle<JSObject> result; 49 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 50 isolate, result, 51 JSObject::New(target, new_target, Handle<AllocationSite>::null())); 52 auto array_buffer = Handle<JSArrayBuffer>::cast(result); 53 // Ensure that all fields are initialized because BackingStore::Allocate is 54 // allowed to GC. Note that we cannot move the allocation of the ArrayBuffer 55 // after BackingStore::Allocate because of the spec. 56 array_buffer->Setup(shared, resizable, nullptr); 57 58 size_t byte_length; 59 size_t max_byte_length = 0; 60 if (!TryNumberToSize(*length, &byte_length) || 61 byte_length > JSArrayBuffer::kMaxByteLength) { 62 // ToNumber failed. 63 THROW_NEW_ERROR_RETURN_FAILURE( 64 isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferLength)); 65 } 66 67 std::unique_ptr<BackingStore> backing_store; 68 if (resizable == ResizableFlag::kNotResizable) { 69 backing_store = 70 BackingStore::Allocate(isolate, byte_length, shared, initialized); 71 max_byte_length = byte_length; 72 } else { 73 if (!TryNumberToSize(*max_length, &max_byte_length)) { 74 THROW_NEW_ERROR_RETURN_FAILURE( 75 isolate, 76 NewRangeError(MessageTemplate::kInvalidArrayBufferMaxLength)); 77 } 78 if (byte_length > max_byte_length) { 79 THROW_NEW_ERROR_RETURN_FAILURE( 80 isolate, 81 NewRangeError(MessageTemplate::kInvalidArrayBufferMaxLength)); 82 } 83 84 size_t page_size, initial_pages, max_pages; 85 MAYBE_RETURN(JSArrayBuffer::GetResizableBackingStorePageConfiguration( 86 isolate, byte_length, max_byte_length, kThrowOnError, 87 &page_size, &initial_pages, &max_pages), 88 ReadOnlyRoots(isolate).exception()); 89 90 constexpr bool kIsWasmMemory = false; 91 backing_store = BackingStore::TryAllocateAndPartiallyCommitMemory( 92 isolate, byte_length, max_byte_length, page_size, initial_pages, 93 max_pages, kIsWasmMemory, shared); 94 } 95 if (!backing_store) { 96 // Allocation of backing store failed. 97 THROW_NEW_ERROR_RETURN_FAILURE( 98 isolate, NewRangeError(MessageTemplate::kArrayBufferAllocationFailed)); 99 } 100 101 array_buffer->Attach(std::move(backing_store)); 102 array_buffer->set_max_byte_length(max_byte_length); 103 return *array_buffer; 104} 105 106} // namespace 107 108// ES #sec-arraybuffer-constructor 109BUILTIN(ArrayBufferConstructor) { 110 HandleScope scope(isolate); 111 Handle<JSFunction> target = args.target(); 112 DCHECK(*target == target->native_context().array_buffer_fun() || 113 *target == target->native_context().shared_array_buffer_fun()); 114 if (args.new_target()->IsUndefined(isolate)) { // [[Call]] 115 THROW_NEW_ERROR_RETURN_FAILURE( 116 isolate, NewTypeError(MessageTemplate::kConstructorNotFunction, 117 handle(target->shared().Name(), isolate))); 118 } 119 // [[Construct]] 120 Handle<JSReceiver> new_target = Handle<JSReceiver>::cast(args.new_target()); 121 Handle<Object> length = args.atOrUndefined(isolate, 1); 122 123 Handle<Object> number_length; 124 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, number_length, 125 Object::ToInteger(isolate, length)); 126 if (number_length->Number() < 0.0) { 127 THROW_NEW_ERROR_RETURN_FAILURE( 128 isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferLength)); 129 } 130 131 Handle<Object> number_max_length; 132 if (FLAG_harmony_rab_gsab) { 133 Handle<Object> max_length; 134 Handle<Object> options = args.atOrUndefined(isolate, 2); 135 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 136 isolate, max_length, 137 JSObject::ReadFromOptionsBag( 138 options, isolate->factory()->max_byte_length_string(), isolate)); 139 140 if (!max_length->IsUndefined(isolate)) { 141 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 142 isolate, number_max_length, Object::ToInteger(isolate, max_length)); 143 } 144 } 145 return ConstructBuffer(isolate, target, new_target, number_length, 146 number_max_length, InitializedFlag::kZeroInitialized); 147} 148 149// This is a helper to construct an ArrayBuffer with uinitialized memory. 150// This means the caller must ensure the buffer is totally initialized in 151// all cases, or we will expose uinitialized memory to user code. 152BUILTIN(ArrayBufferConstructor_DoNotInitialize) { 153 HandleScope scope(isolate); 154 Handle<JSFunction> target(isolate->native_context()->array_buffer_fun(), 155 isolate); 156 Handle<Object> length = args.atOrUndefined(isolate, 1); 157 return ConstructBuffer(isolate, target, target, length, Handle<Object>(), 158 InitializedFlag::kUninitialized); 159} 160 161static Object SliceHelper(BuiltinArguments args, Isolate* isolate, 162 const char* kMethodName, bool is_shared) { 163 HandleScope scope(isolate); 164 Handle<Object> start = args.at(1); 165 Handle<Object> end = args.atOrUndefined(isolate, 2); 166 167 // * If Type(O) is not Object, throw a TypeError exception. 168 // * If O does not have an [[ArrayBufferData]] internal slot, throw a 169 // TypeError exception. 170 CHECK_RECEIVER(JSArrayBuffer, array_buffer, kMethodName); 171 // * [AB] If IsSharedArrayBuffer(O) is true, throw a TypeError exception. 172 // * [SAB] If IsSharedArrayBuffer(O) is false, throw a TypeError exception. 173 CHECK_SHARED(is_shared, array_buffer, kMethodName); 174 175 // * [AB] If IsDetachedBuffer(buffer) is true, throw a TypeError exception. 176 if (!is_shared && array_buffer->was_detached()) { 177 THROW_NEW_ERROR_RETURN_FAILURE( 178 isolate, NewTypeError(MessageTemplate::kDetachedOperation, 179 isolate->factory()->NewStringFromAsciiChecked( 180 kMethodName))); 181 } 182 183 // * [AB] Let len be O.[[ArrayBufferByteLength]]. 184 // * [SAB] Let len be O.[[ArrayBufferByteLength]]. 185 double const len = array_buffer->GetByteLength(); 186 187 // * Let relativeStart be ? ToInteger(start). 188 Handle<Object> relative_start; 189 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, relative_start, 190 Object::ToInteger(isolate, start)); 191 192 // * If relativeStart < 0, let first be max((len + relativeStart), 0); else 193 // let first be min(relativeStart, len). 194 double const first = (relative_start->Number() < 0) 195 ? std::max(len + relative_start->Number(), 0.0) 196 : std::min(relative_start->Number(), len); 197 198 // * If end is undefined, let relativeEnd be len; else let relativeEnd be ? 199 // ToInteger(end). 200 double relative_end; 201 if (end->IsUndefined(isolate)) { 202 relative_end = len; 203 } else { 204 Handle<Object> relative_end_obj; 205 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, relative_end_obj, 206 Object::ToInteger(isolate, end)); 207 relative_end = relative_end_obj->Number(); 208 } 209 210 // * If relativeEnd < 0, let final be max((len + relativeEnd), 0); else let 211 // final be min(relativeEnd, len). 212 double const final_ = (relative_end < 0) ? std::max(len + relative_end, 0.0) 213 : std::min(relative_end, len); 214 215 // * Let newLen be max(final-first, 0). 216 double const new_len = std::max(final_ - first, 0.0); 217 Handle<Object> new_len_obj = isolate->factory()->NewNumber(new_len); 218 219 // * [AB] Let ctor be ? SpeciesConstructor(O, %ArrayBuffer%). 220 // * [SAB] Let ctor be ? SpeciesConstructor(O, %SharedArrayBuffer%). 221 Handle<JSFunction> constructor_fun = is_shared 222 ? isolate->shared_array_buffer_fun() 223 : isolate->array_buffer_fun(); 224 Handle<Object> ctor; 225 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 226 isolate, ctor, 227 Object::SpeciesConstructor( 228 isolate, Handle<JSReceiver>::cast(args.receiver()), constructor_fun)); 229 230 // * Let new be ? Construct(ctor, newLen). 231 Handle<JSReceiver> new_; 232 { 233 const int argc = 1; 234 235 base::ScopedVector<Handle<Object>> argv(argc); 236 argv[0] = new_len_obj; 237 238 Handle<Object> new_obj; 239 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 240 isolate, new_obj, Execution::New(isolate, ctor, argc, argv.begin())); 241 242 new_ = Handle<JSReceiver>::cast(new_obj); 243 } 244 245 // * If new does not have an [[ArrayBufferData]] internal slot, throw a 246 // TypeError exception. 247 if (!new_->IsJSArrayBuffer()) { 248 THROW_NEW_ERROR_RETURN_FAILURE( 249 isolate, 250 NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, 251 isolate->factory()->NewStringFromAsciiChecked(kMethodName), 252 new_)); 253 } 254 255 // * [AB] If IsSharedArrayBuffer(new) is true, throw a TypeError exception. 256 // * [SAB] If IsSharedArrayBuffer(new) is false, throw a TypeError exception. 257 Handle<JSArrayBuffer> new_array_buffer = Handle<JSArrayBuffer>::cast(new_); 258 CHECK_SHARED(is_shared, new_array_buffer, kMethodName); 259 260 // The created ArrayBuffer might or might not be resizable, since the species 261 // constructor might return a non-resizable or a resizable buffer. 262 263 // * [AB] If IsDetachedBuffer(new) is true, throw a TypeError exception. 264 if (!is_shared && new_array_buffer->was_detached()) { 265 THROW_NEW_ERROR_RETURN_FAILURE( 266 isolate, NewTypeError(MessageTemplate::kDetachedOperation, 267 isolate->factory()->NewStringFromAsciiChecked( 268 kMethodName))); 269 } 270 271 // * [AB] If SameValue(new, O) is true, throw a TypeError exception. 272 if (!is_shared && new_->SameValue(*args.receiver())) { 273 THROW_NEW_ERROR_RETURN_FAILURE( 274 isolate, NewTypeError(MessageTemplate::kArrayBufferSpeciesThis)); 275 } 276 277 // * [SAB] If new.[[ArrayBufferData]] and O.[[ArrayBufferData]] are the same 278 // Shared Data Block values, throw a TypeError exception. 279 if (is_shared && 280 new_array_buffer->backing_store() == array_buffer->backing_store()) { 281 THROW_NEW_ERROR_RETURN_FAILURE( 282 isolate, NewTypeError(MessageTemplate::kSharedArrayBufferSpeciesThis)); 283 } 284 285 // * If new.[[ArrayBufferByteLength]] < newLen, throw a TypeError exception. 286 size_t new_array_buffer_byte_length = new_array_buffer->GetByteLength(); 287 if (new_array_buffer_byte_length < new_len) { 288 THROW_NEW_ERROR_RETURN_FAILURE( 289 isolate, 290 NewTypeError(is_shared ? MessageTemplate::kSharedArrayBufferTooShort 291 : MessageTemplate::kArrayBufferTooShort)); 292 } 293 294 // * [AB] NOTE: Side-effects of the above steps may have detached O. 295 // * [AB] If IsDetachedBuffer(O) is true, throw a TypeError exception. 296 if (!is_shared && array_buffer->was_detached()) { 297 THROW_NEW_ERROR_RETURN_FAILURE( 298 isolate, NewTypeError(MessageTemplate::kDetachedOperation, 299 isolate->factory()->NewStringFromAsciiChecked( 300 kMethodName))); 301 } 302 303 // * Let fromBuf be O.[[ArrayBufferData]]. 304 // * Let toBuf be new.[[ArrayBufferData]]. 305 // * Perform CopyDataBlockBytes(toBuf, 0, fromBuf, first, newLen). 306 size_t first_size = first; 307 size_t new_len_size = new_len; 308 DCHECK(new_array_buffer_byte_length >= new_len_size); 309 310 if (new_len_size != 0) { 311 size_t from_byte_length = array_buffer->GetByteLength(); 312 if (V8_UNLIKELY(!is_shared && array_buffer->is_resizable())) { 313 // The above steps might have resized the underlying buffer. In that case, 314 // only copy the still-accessible portion of the underlying data. 315 if (first_size > from_byte_length) { 316 return *new_; // Nothing to copy. 317 } 318 if (new_len_size > from_byte_length - first_size) { 319 new_len_size = from_byte_length - first_size; 320 } 321 } 322 DCHECK(first_size <= from_byte_length); 323 DCHECK(from_byte_length - first_size >= new_len_size); 324 uint8_t* from_data = 325 reinterpret_cast<uint8_t*>(array_buffer->backing_store()) + first_size; 326 uint8_t* to_data = 327 reinterpret_cast<uint8_t*>(new_array_buffer->backing_store()); 328 if (is_shared) { 329 base::Relaxed_Memcpy(reinterpret_cast<base::Atomic8*>(to_data), 330 reinterpret_cast<base::Atomic8*>(from_data), 331 new_len_size); 332 } else { 333 CopyBytes(to_data, from_data, new_len_size); 334 } 335 } 336 337 return *new_; 338} 339 340// ES #sec-sharedarraybuffer.prototype.slice 341BUILTIN(SharedArrayBufferPrototypeSlice) { 342 const char* const kMethodName = "SharedArrayBuffer.prototype.slice"; 343 return SliceHelper(args, isolate, kMethodName, true); 344} 345 346// ES #sec-arraybuffer.prototype.slice 347// ArrayBuffer.prototype.slice ( start, end ) 348BUILTIN(ArrayBufferPrototypeSlice) { 349 const char* const kMethodName = "ArrayBuffer.prototype.slice"; 350 return SliceHelper(args, isolate, kMethodName, false); 351} 352 353static Object ResizeHelper(BuiltinArguments args, Isolate* isolate, 354 const char* kMethodName, bool is_shared) { 355 HandleScope scope(isolate); 356 357 // 1 Let O be the this value. 358 // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferMaxByteLength]]). 359 CHECK_RECEIVER(JSArrayBuffer, array_buffer, kMethodName); 360 CHECK_RESIZABLE(true, array_buffer, kMethodName); 361 362 // [RAB] 3. If IsSharedArrayBuffer(O) is true, throw a *TypeError* exception 363 // [GSAB] 3. If IsSharedArrayBuffer(O) is false, throw a *TypeError* exception 364 CHECK_SHARED(is_shared, array_buffer, kMethodName); 365 366 // Let newByteLength to ? ToIntegerOrInfinity(newLength). 367 Handle<Object> new_length = args.at(1); 368 Handle<Object> number_new_byte_length; 369 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, number_new_byte_length, 370 Object::ToInteger(isolate, new_length)); 371 372 // [RAB] If IsDetachedBuffer(O) is true, throw a TypeError exception. 373 if (!is_shared && array_buffer->was_detached()) { 374 THROW_NEW_ERROR_RETURN_FAILURE( 375 isolate, NewTypeError(MessageTemplate::kDetachedOperation, 376 isolate->factory()->NewStringFromAsciiChecked( 377 kMethodName))); 378 } 379 380 // [RAB] If newByteLength < 0 or newByteLength > 381 // O.[[ArrayBufferMaxByteLength]], throw a RangeError exception. 382 383 // [GSAB] If newByteLength < currentByteLength or newByteLength > 384 // O.[[ArrayBufferMaxByteLength]], throw a RangeError exception. 385 size_t new_byte_length; 386 if (!TryNumberToSize(*number_new_byte_length, &new_byte_length)) { 387 THROW_NEW_ERROR_RETURN_FAILURE( 388 isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferResizeLength, 389 isolate->factory()->NewStringFromAsciiChecked( 390 kMethodName))); 391 } 392 393 if (is_shared && new_byte_length < array_buffer->byte_length()) { 394 // GrowableSharedArrayBuffer is only allowed to grow. 395 THROW_NEW_ERROR_RETURN_FAILURE( 396 isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferResizeLength, 397 isolate->factory()->NewStringFromAsciiChecked( 398 kMethodName))); 399 } 400 401 if (new_byte_length > array_buffer->max_byte_length()) { 402 THROW_NEW_ERROR_RETURN_FAILURE( 403 isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferResizeLength, 404 isolate->factory()->NewStringFromAsciiChecked( 405 kMethodName))); 406 } 407 408 size_t page_size = AllocatePageSize(); 409 size_t new_committed_pages; 410 bool round_return_value = 411 RoundUpToPageSize(new_byte_length, page_size, 412 JSArrayBuffer::kMaxByteLength, &new_committed_pages); 413 CHECK(round_return_value); 414 415 // [RAB] Let hostHandled be ? HostResizeArrayBuffer(O, newByteLength). 416 // [GSAB] Let hostHandled be ? HostGrowArrayBuffer(O, newByteLength). 417 // If hostHandled is handled, return undefined. 418 419 // TODO(v8:11111, v8:12746): Wasm integration. 420 421 if (!is_shared) { 422 // [RAB] Let oldBlock be O.[[ArrayBufferData]]. 423 // [RAB] Let newBlock be ? CreateByteDataBlock(newByteLength). 424 // [RAB] Let copyLength be min(newByteLength, O.[[ArrayBufferByteLength]]). 425 // [RAB] Perform CopyDataBlockBytes(newBlock, 0, oldBlock, 0, copyLength). 426 // [RAB] NOTE: Neither creation of the new Data Block nor copying from the 427 // old Data Block are observable. Implementations reserve the right to 428 // implement this method as in-place growth or shrinkage. 429 if (array_buffer->GetBackingStore()->ResizeInPlace( 430 isolate, new_byte_length, new_committed_pages * page_size) != 431 BackingStore::ResizeOrGrowResult::kSuccess) { 432 THROW_NEW_ERROR_RETURN_FAILURE( 433 isolate, NewRangeError(MessageTemplate::kOutOfMemory, 434 isolate->factory()->NewStringFromAsciiChecked( 435 kMethodName))); 436 } 437 // [RAB] Set O.[[ArrayBufferByteLength]] to newLength. 438 array_buffer->set_byte_length(new_byte_length); 439 } else { 440 // [GSAB] (Detailed description of the algorithm omitted.) 441 auto result = array_buffer->GetBackingStore()->GrowInPlace( 442 isolate, new_byte_length, new_committed_pages * page_size); 443 if (result == BackingStore::ResizeOrGrowResult::kFailure) { 444 THROW_NEW_ERROR_RETURN_FAILURE( 445 isolate, NewRangeError(MessageTemplate::kOutOfMemory, 446 isolate->factory()->NewStringFromAsciiChecked( 447 kMethodName))); 448 } 449 if (result == BackingStore::ResizeOrGrowResult::kRace) { 450 THROW_NEW_ERROR_RETURN_FAILURE( 451 isolate, 452 NewRangeError( 453 MessageTemplate::kInvalidArrayBufferResizeLength, 454 isolate->factory()->NewStringFromAsciiChecked(kMethodName))); 455 } 456 // Invariant: byte_length for a GSAB is 0 (it needs to be read from the 457 // BackingStore). 458 CHECK_EQ(0, array_buffer->byte_length()); 459 } 460 return ReadOnlyRoots(isolate).undefined_value(); 461} 462 463// ES #sec-get-sharedarraybuffer.prototype.bytelength 464// get SharedArrayBuffer.prototype.byteLength 465BUILTIN(SharedArrayBufferPrototypeGetByteLength) { 466 const char* const kMethodName = "get SharedArrayBuffer.prototype.byteLength"; 467 HandleScope scope(isolate); 468 // 1. Let O be the this value. 469 // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]). 470 CHECK_RECEIVER(JSArrayBuffer, array_buffer, kMethodName); 471 // 3. If IsSharedArrayBuffer(O) is false, throw a TypeError exception. 472 CHECK_SHARED(true, array_buffer, kMethodName); 473 474 DCHECK_EQ(array_buffer->max_byte_length(), 475 array_buffer->GetBackingStore()->max_byte_length()); 476 477 // 4. Let length be ArrayBufferByteLength(O, SeqCst). 478 size_t byte_length = array_buffer->GetByteLength(); 479 // 5. Return F(length). 480 return *isolate->factory()->NewNumberFromSize(byte_length); 481} 482 483// ES #sec-arraybuffer.prototype.resize 484// ArrayBuffer.prototype.resize(new_size)) 485BUILTIN(ArrayBufferPrototypeResize) { 486 const char* const kMethodName = "ArrayBuffer.prototype.resize"; 487 constexpr bool kIsShared = false; 488 return ResizeHelper(args, isolate, kMethodName, kIsShared); 489} 490 491// ES #sec-sharedarraybuffer.prototype.grow 492// SharedArrayBuffer.prototype.grow(new_size)) 493BUILTIN(SharedArrayBufferPrototypeGrow) { 494 const char* const kMethodName = "SharedArrayBuffer.prototype.grow"; 495 constexpr bool kIsShared = true; 496 return ResizeHelper(args, isolate, kMethodName, kIsShared); 497} 498 499} // namespace internal 500} // namespace v8 501