OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 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/v8.h" |
| 6 |
| 7 #include "src/arguments.h" |
| 8 #include "src/runtime/runtime.h" |
| 9 #include "src/runtime/runtime-utils.h" |
| 10 |
| 11 |
| 12 namespace v8 { |
| 13 namespace internal { |
| 14 |
| 15 void Runtime::FreeArrayBuffer(Isolate* isolate, |
| 16 JSArrayBuffer* phantom_array_buffer) { |
| 17 if (phantom_array_buffer->should_be_freed()) { |
| 18 DCHECK(phantom_array_buffer->is_external()); |
| 19 free(phantom_array_buffer->backing_store()); |
| 20 } |
| 21 if (phantom_array_buffer->is_external()) return; |
| 22 |
| 23 size_t allocated_length = |
| 24 NumberToSize(isolate, phantom_array_buffer->byte_length()); |
| 25 |
| 26 reinterpret_cast<v8::Isolate*>(isolate) |
| 27 ->AdjustAmountOfExternalAllocatedMemory( |
| 28 -static_cast<int64_t>(allocated_length)); |
| 29 CHECK(V8::ArrayBufferAllocator() != NULL); |
| 30 V8::ArrayBufferAllocator()->Free(phantom_array_buffer->backing_store(), |
| 31 allocated_length); |
| 32 } |
| 33 |
| 34 |
| 35 void Runtime::SetupArrayBuffer(Isolate* isolate, |
| 36 Handle<JSArrayBuffer> array_buffer, |
| 37 bool is_external, void* data, |
| 38 size_t allocated_length) { |
| 39 DCHECK(array_buffer->GetInternalFieldCount() == |
| 40 v8::ArrayBuffer::kInternalFieldCount); |
| 41 for (int i = 0; i < v8::ArrayBuffer::kInternalFieldCount; i++) { |
| 42 array_buffer->SetInternalField(i, Smi::FromInt(0)); |
| 43 } |
| 44 array_buffer->set_backing_store(data); |
| 45 array_buffer->set_flag(Smi::FromInt(0)); |
| 46 array_buffer->set_is_external(is_external); |
| 47 |
| 48 Handle<Object> byte_length = |
| 49 isolate->factory()->NewNumberFromSize(allocated_length); |
| 50 CHECK(byte_length->IsSmi() || byte_length->IsHeapNumber()); |
| 51 array_buffer->set_byte_length(*byte_length); |
| 52 |
| 53 array_buffer->set_weak_next(isolate->heap()->array_buffers_list()); |
| 54 isolate->heap()->set_array_buffers_list(*array_buffer); |
| 55 array_buffer->set_weak_first_view(isolate->heap()->undefined_value()); |
| 56 } |
| 57 |
| 58 |
| 59 bool Runtime::SetupArrayBufferAllocatingData(Isolate* isolate, |
| 60 Handle<JSArrayBuffer> array_buffer, |
| 61 size_t allocated_length, |
| 62 bool initialize) { |
| 63 void* data; |
| 64 CHECK(V8::ArrayBufferAllocator() != NULL); |
| 65 if (allocated_length != 0) { |
| 66 if (initialize) { |
| 67 data = V8::ArrayBufferAllocator()->Allocate(allocated_length); |
| 68 } else { |
| 69 data = |
| 70 V8::ArrayBufferAllocator()->AllocateUninitialized(allocated_length); |
| 71 } |
| 72 if (data == NULL) return false; |
| 73 } else { |
| 74 data = NULL; |
| 75 } |
| 76 |
| 77 SetupArrayBuffer(isolate, array_buffer, false, data, allocated_length); |
| 78 |
| 79 reinterpret_cast<v8::Isolate*>(isolate) |
| 80 ->AdjustAmountOfExternalAllocatedMemory(allocated_length); |
| 81 |
| 82 return true; |
| 83 } |
| 84 |
| 85 |
| 86 void Runtime::NeuterArrayBuffer(Handle<JSArrayBuffer> array_buffer) { |
| 87 Isolate* isolate = array_buffer->GetIsolate(); |
| 88 for (Handle<Object> view_obj(array_buffer->weak_first_view(), isolate); |
| 89 !view_obj->IsUndefined();) { |
| 90 Handle<JSArrayBufferView> view(JSArrayBufferView::cast(*view_obj)); |
| 91 if (view->IsJSTypedArray()) { |
| 92 JSTypedArray::cast(*view)->Neuter(); |
| 93 } else if (view->IsJSDataView()) { |
| 94 JSDataView::cast(*view)->Neuter(); |
| 95 } else { |
| 96 UNREACHABLE(); |
| 97 } |
| 98 view_obj = handle(view->weak_next(), isolate); |
| 99 } |
| 100 array_buffer->Neuter(); |
| 101 } |
| 102 |
| 103 |
| 104 RUNTIME_FUNCTION(Runtime_ArrayBufferInitialize) { |
| 105 HandleScope scope(isolate); |
| 106 DCHECK(args.length() == 2); |
| 107 CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, holder, 0); |
| 108 CONVERT_NUMBER_ARG_HANDLE_CHECKED(byteLength, 1); |
| 109 if (!holder->byte_length()->IsUndefined()) { |
| 110 // ArrayBuffer is already initialized; probably a fuzz test. |
| 111 return *holder; |
| 112 } |
| 113 size_t allocated_length = 0; |
| 114 if (!TryNumberToSize(isolate, *byteLength, &allocated_length)) { |
| 115 THROW_NEW_ERROR_RETURN_FAILURE( |
| 116 isolate, NewRangeError("invalid_array_buffer_length", |
| 117 HandleVector<Object>(NULL, 0))); |
| 118 } |
| 119 if (!Runtime::SetupArrayBufferAllocatingData(isolate, holder, |
| 120 allocated_length)) { |
| 121 THROW_NEW_ERROR_RETURN_FAILURE( |
| 122 isolate, NewRangeError("invalid_array_buffer_length", |
| 123 HandleVector<Object>(NULL, 0))); |
| 124 } |
| 125 return *holder; |
| 126 } |
| 127 |
| 128 |
| 129 RUNTIME_FUNCTION(Runtime_ArrayBufferGetByteLength) { |
| 130 SealHandleScope shs(isolate); |
| 131 DCHECK(args.length() == 1); |
| 132 CONVERT_ARG_CHECKED(JSArrayBuffer, holder, 0); |
| 133 return holder->byte_length(); |
| 134 } |
| 135 |
| 136 |
| 137 RUNTIME_FUNCTION(Runtime_ArrayBufferSliceImpl) { |
| 138 HandleScope scope(isolate); |
| 139 DCHECK(args.length() == 3); |
| 140 CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, source, 0); |
| 141 CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, target, 1); |
| 142 CONVERT_NUMBER_ARG_HANDLE_CHECKED(first, 2); |
| 143 RUNTIME_ASSERT(!source.is_identical_to(target)); |
| 144 size_t start = 0; |
| 145 RUNTIME_ASSERT(TryNumberToSize(isolate, *first, &start)); |
| 146 size_t target_length = NumberToSize(isolate, target->byte_length()); |
| 147 |
| 148 if (target_length == 0) return isolate->heap()->undefined_value(); |
| 149 |
| 150 size_t source_byte_length = NumberToSize(isolate, source->byte_length()); |
| 151 RUNTIME_ASSERT(start <= source_byte_length); |
| 152 RUNTIME_ASSERT(source_byte_length - start >= target_length); |
| 153 uint8_t* source_data = reinterpret_cast<uint8_t*>(source->backing_store()); |
| 154 uint8_t* target_data = reinterpret_cast<uint8_t*>(target->backing_store()); |
| 155 CopyBytes(target_data, source_data + start, target_length); |
| 156 return isolate->heap()->undefined_value(); |
| 157 } |
| 158 |
| 159 |
| 160 RUNTIME_FUNCTION(Runtime_ArrayBufferIsView) { |
| 161 HandleScope scope(isolate); |
| 162 DCHECK(args.length() == 1); |
| 163 CONVERT_ARG_CHECKED(Object, object, 0); |
| 164 return isolate->heap()->ToBoolean(object->IsJSArrayBufferView()); |
| 165 } |
| 166 |
| 167 |
| 168 RUNTIME_FUNCTION(Runtime_ArrayBufferNeuter) { |
| 169 HandleScope scope(isolate); |
| 170 DCHECK(args.length() == 1); |
| 171 CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, array_buffer, 0); |
| 172 if (array_buffer->backing_store() == NULL) { |
| 173 CHECK(Smi::FromInt(0) == array_buffer->byte_length()); |
| 174 return isolate->heap()->undefined_value(); |
| 175 } |
| 176 DCHECK(!array_buffer->is_external()); |
| 177 void* backing_store = array_buffer->backing_store(); |
| 178 size_t byte_length = NumberToSize(isolate, array_buffer->byte_length()); |
| 179 array_buffer->set_is_external(true); |
| 180 Runtime::NeuterArrayBuffer(array_buffer); |
| 181 V8::ArrayBufferAllocator()->Free(backing_store, byte_length); |
| 182 return isolate->heap()->undefined_value(); |
| 183 } |
| 184 |
| 185 |
| 186 void Runtime::ArrayIdToTypeAndSize(int arrayId, ExternalArrayType* array_type, |
| 187 ElementsKind* external_elements_kind, |
| 188 ElementsKind* fixed_elements_kind, |
| 189 size_t* element_size) { |
| 190 switch (arrayId) { |
| 191 #define ARRAY_ID_CASE(Type, type, TYPE, ctype, size) \ |
| 192 case ARRAY_ID_##TYPE: \ |
| 193 *array_type = kExternal##Type##Array; \ |
| 194 *external_elements_kind = EXTERNAL_##TYPE##_ELEMENTS; \ |
| 195 *fixed_elements_kind = TYPE##_ELEMENTS; \ |
| 196 *element_size = size; \ |
| 197 break; |
| 198 |
| 199 TYPED_ARRAYS(ARRAY_ID_CASE) |
| 200 #undef ARRAY_ID_CASE |
| 201 |
| 202 default: |
| 203 UNREACHABLE(); |
| 204 } |
| 205 } |
| 206 |
| 207 |
| 208 RUNTIME_FUNCTION(Runtime_TypedArrayInitialize) { |
| 209 HandleScope scope(isolate); |
| 210 DCHECK(args.length() == 5); |
| 211 CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0); |
| 212 CONVERT_SMI_ARG_CHECKED(arrayId, 1); |
| 213 CONVERT_ARG_HANDLE_CHECKED(Object, maybe_buffer, 2); |
| 214 CONVERT_NUMBER_ARG_HANDLE_CHECKED(byte_offset_object, 3); |
| 215 CONVERT_NUMBER_ARG_HANDLE_CHECKED(byte_length_object, 4); |
| 216 |
| 217 RUNTIME_ASSERT(arrayId >= Runtime::ARRAY_ID_FIRST && |
| 218 arrayId <= Runtime::ARRAY_ID_LAST); |
| 219 |
| 220 ExternalArrayType array_type = kExternalInt8Array; // Bogus initialization. |
| 221 size_t element_size = 1; // Bogus initialization. |
| 222 ElementsKind external_elements_kind = |
| 223 EXTERNAL_INT8_ELEMENTS; // Bogus initialization. |
| 224 ElementsKind fixed_elements_kind = INT8_ELEMENTS; // Bogus initialization. |
| 225 Runtime::ArrayIdToTypeAndSize(arrayId, &array_type, &external_elements_kind, |
| 226 &fixed_elements_kind, &element_size); |
| 227 RUNTIME_ASSERT(holder->map()->elements_kind() == fixed_elements_kind); |
| 228 |
| 229 size_t byte_offset = 0; |
| 230 size_t byte_length = 0; |
| 231 RUNTIME_ASSERT(TryNumberToSize(isolate, *byte_offset_object, &byte_offset)); |
| 232 RUNTIME_ASSERT(TryNumberToSize(isolate, *byte_length_object, &byte_length)); |
| 233 |
| 234 if (maybe_buffer->IsJSArrayBuffer()) { |
| 235 Handle<JSArrayBuffer> buffer = Handle<JSArrayBuffer>::cast(maybe_buffer); |
| 236 size_t array_buffer_byte_length = |
| 237 NumberToSize(isolate, buffer->byte_length()); |
| 238 RUNTIME_ASSERT(byte_offset <= array_buffer_byte_length); |
| 239 RUNTIME_ASSERT(array_buffer_byte_length - byte_offset >= byte_length); |
| 240 } else { |
| 241 RUNTIME_ASSERT(maybe_buffer->IsNull()); |
| 242 } |
| 243 |
| 244 RUNTIME_ASSERT(byte_length % element_size == 0); |
| 245 size_t length = byte_length / element_size; |
| 246 |
| 247 if (length > static_cast<unsigned>(Smi::kMaxValue)) { |
| 248 THROW_NEW_ERROR_RETURN_FAILURE( |
| 249 isolate, NewRangeError("invalid_typed_array_length", |
| 250 HandleVector<Object>(NULL, 0))); |
| 251 } |
| 252 |
| 253 // All checks are done, now we can modify objects. |
| 254 |
| 255 DCHECK(holder->GetInternalFieldCount() == |
| 256 v8::ArrayBufferView::kInternalFieldCount); |
| 257 for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) { |
| 258 holder->SetInternalField(i, Smi::FromInt(0)); |
| 259 } |
| 260 Handle<Object> length_obj = isolate->factory()->NewNumberFromSize(length); |
| 261 holder->set_length(*length_obj); |
| 262 holder->set_byte_offset(*byte_offset_object); |
| 263 holder->set_byte_length(*byte_length_object); |
| 264 |
| 265 if (!maybe_buffer->IsNull()) { |
| 266 Handle<JSArrayBuffer> buffer = Handle<JSArrayBuffer>::cast(maybe_buffer); |
| 267 holder->set_buffer(*buffer); |
| 268 holder->set_weak_next(buffer->weak_first_view()); |
| 269 buffer->set_weak_first_view(*holder); |
| 270 |
| 271 Handle<ExternalArray> elements = isolate->factory()->NewExternalArray( |
| 272 static_cast<int>(length), array_type, |
| 273 static_cast<uint8_t*>(buffer->backing_store()) + byte_offset); |
| 274 Handle<Map> map = |
| 275 JSObject::GetElementsTransitionMap(holder, external_elements_kind); |
| 276 JSObject::SetMapAndElements(holder, map, elements); |
| 277 DCHECK(IsExternalArrayElementsKind(holder->map()->elements_kind())); |
| 278 } else { |
| 279 holder->set_buffer(Smi::FromInt(0)); |
| 280 holder->set_weak_next(isolate->heap()->undefined_value()); |
| 281 Handle<FixedTypedArrayBase> elements = |
| 282 isolate->factory()->NewFixedTypedArray(static_cast<int>(length), |
| 283 array_type); |
| 284 holder->set_elements(*elements); |
| 285 } |
| 286 return isolate->heap()->undefined_value(); |
| 287 } |
| 288 |
| 289 |
| 290 // Initializes a typed array from an array-like object. |
| 291 // If an array-like object happens to be a typed array of the same type, |
| 292 // initializes backing store using memove. |
| 293 // |
| 294 // Returns true if backing store was initialized or false otherwise. |
| 295 RUNTIME_FUNCTION(Runtime_TypedArrayInitializeFromArrayLike) { |
| 296 HandleScope scope(isolate); |
| 297 DCHECK(args.length() == 4); |
| 298 CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0); |
| 299 CONVERT_SMI_ARG_CHECKED(arrayId, 1); |
| 300 CONVERT_ARG_HANDLE_CHECKED(Object, source, 2); |
| 301 CONVERT_NUMBER_ARG_HANDLE_CHECKED(length_obj, 3); |
| 302 |
| 303 RUNTIME_ASSERT(arrayId >= Runtime::ARRAY_ID_FIRST && |
| 304 arrayId <= Runtime::ARRAY_ID_LAST); |
| 305 |
| 306 ExternalArrayType array_type = kExternalInt8Array; // Bogus initialization. |
| 307 size_t element_size = 1; // Bogus initialization. |
| 308 ElementsKind external_elements_kind = |
| 309 EXTERNAL_INT8_ELEMENTS; // Bogus intialization. |
| 310 ElementsKind fixed_elements_kind = INT8_ELEMENTS; // Bogus initialization. |
| 311 Runtime::ArrayIdToTypeAndSize(arrayId, &array_type, &external_elements_kind, |
| 312 &fixed_elements_kind, &element_size); |
| 313 |
| 314 RUNTIME_ASSERT(holder->map()->elements_kind() == fixed_elements_kind); |
| 315 |
| 316 Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer(); |
| 317 if (source->IsJSTypedArray() && |
| 318 JSTypedArray::cast(*source)->type() == array_type) { |
| 319 length_obj = Handle<Object>(JSTypedArray::cast(*source)->length(), isolate); |
| 320 } |
| 321 size_t length = 0; |
| 322 RUNTIME_ASSERT(TryNumberToSize(isolate, *length_obj, &length)); |
| 323 |
| 324 if ((length > static_cast<unsigned>(Smi::kMaxValue)) || |
| 325 (length > (kMaxInt / element_size))) { |
| 326 THROW_NEW_ERROR_RETURN_FAILURE( |
| 327 isolate, NewRangeError("invalid_typed_array_length", |
| 328 HandleVector<Object>(NULL, 0))); |
| 329 } |
| 330 size_t byte_length = length * element_size; |
| 331 |
| 332 DCHECK(holder->GetInternalFieldCount() == |
| 333 v8::ArrayBufferView::kInternalFieldCount); |
| 334 for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) { |
| 335 holder->SetInternalField(i, Smi::FromInt(0)); |
| 336 } |
| 337 |
| 338 // NOTE: not initializing backing store. |
| 339 // We assume that the caller of this function will initialize holder |
| 340 // with the loop |
| 341 // for(i = 0; i < length; i++) { holder[i] = source[i]; } |
| 342 // We assume that the caller of this function is always a typed array |
| 343 // constructor. |
| 344 // If source is a typed array, this loop will always run to completion, |
| 345 // so we are sure that the backing store will be initialized. |
| 346 // Otherwise, the indexing operation might throw, so the loop will not |
| 347 // run to completion and the typed array might remain partly initialized. |
| 348 // However we further assume that the caller of this function is a typed array |
| 349 // constructor, and the exception will propagate out of the constructor, |
| 350 // therefore uninitialized memory will not be accessible by a user program. |
| 351 // |
| 352 // TODO(dslomov): revise this once we support subclassing. |
| 353 |
| 354 if (!Runtime::SetupArrayBufferAllocatingData(isolate, buffer, byte_length, |
| 355 false)) { |
| 356 THROW_NEW_ERROR_RETURN_FAILURE( |
| 357 isolate, NewRangeError("invalid_array_buffer_length", |
| 358 HandleVector<Object>(NULL, 0))); |
| 359 } |
| 360 |
| 361 holder->set_buffer(*buffer); |
| 362 holder->set_byte_offset(Smi::FromInt(0)); |
| 363 Handle<Object> byte_length_obj( |
| 364 isolate->factory()->NewNumberFromSize(byte_length)); |
| 365 holder->set_byte_length(*byte_length_obj); |
| 366 holder->set_length(*length_obj); |
| 367 holder->set_weak_next(buffer->weak_first_view()); |
| 368 buffer->set_weak_first_view(*holder); |
| 369 |
| 370 Handle<ExternalArray> elements = isolate->factory()->NewExternalArray( |
| 371 static_cast<int>(length), array_type, |
| 372 static_cast<uint8_t*>(buffer->backing_store())); |
| 373 Handle<Map> map = |
| 374 JSObject::GetElementsTransitionMap(holder, external_elements_kind); |
| 375 JSObject::SetMapAndElements(holder, map, elements); |
| 376 |
| 377 if (source->IsJSTypedArray()) { |
| 378 Handle<JSTypedArray> typed_array(JSTypedArray::cast(*source)); |
| 379 |
| 380 if (typed_array->type() == holder->type()) { |
| 381 uint8_t* backing_store = |
| 382 static_cast<uint8_t*>(typed_array->GetBuffer()->backing_store()); |
| 383 size_t source_byte_offset = |
| 384 NumberToSize(isolate, typed_array->byte_offset()); |
| 385 memcpy(buffer->backing_store(), backing_store + source_byte_offset, |
| 386 byte_length); |
| 387 return isolate->heap()->true_value(); |
| 388 } |
| 389 } |
| 390 |
| 391 return isolate->heap()->false_value(); |
| 392 } |
| 393 |
| 394 |
| 395 #define BUFFER_VIEW_GETTER(Type, getter, accessor) \ |
| 396 RUNTIME_FUNCTION(Runtime_##Type##Get##getter) { \ |
| 397 HandleScope scope(isolate); \ |
| 398 DCHECK(args.length() == 1); \ |
| 399 CONVERT_ARG_HANDLE_CHECKED(JS##Type, holder, 0); \ |
| 400 return holder->accessor(); \ |
| 401 } |
| 402 |
| 403 BUFFER_VIEW_GETTER(ArrayBufferView, ByteLength, byte_length) |
| 404 BUFFER_VIEW_GETTER(ArrayBufferView, ByteOffset, byte_offset) |
| 405 BUFFER_VIEW_GETTER(TypedArray, Length, length) |
| 406 BUFFER_VIEW_GETTER(DataView, Buffer, buffer) |
| 407 |
| 408 #undef BUFFER_VIEW_GETTER |
| 409 |
| 410 RUNTIME_FUNCTION(Runtime_TypedArrayGetBuffer) { |
| 411 HandleScope scope(isolate); |
| 412 DCHECK(args.length() == 1); |
| 413 CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0); |
| 414 return *holder->GetBuffer(); |
| 415 } |
| 416 |
| 417 |
| 418 // Return codes for Runtime_TypedArraySetFastCases. |
| 419 // Should be synchronized with typedarray.js natives. |
| 420 enum TypedArraySetResultCodes { |
| 421 // Set from typed array of the same type. |
| 422 // This is processed by TypedArraySetFastCases |
| 423 TYPED_ARRAY_SET_TYPED_ARRAY_SAME_TYPE = 0, |
| 424 // Set from typed array of the different type, overlapping in memory. |
| 425 TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING = 1, |
| 426 // Set from typed array of the different type, non-overlapping. |
| 427 TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING = 2, |
| 428 // Set from non-typed array. |
| 429 TYPED_ARRAY_SET_NON_TYPED_ARRAY = 3 |
| 430 }; |
| 431 |
| 432 |
| 433 RUNTIME_FUNCTION(Runtime_TypedArraySetFastCases) { |
| 434 HandleScope scope(isolate); |
| 435 DCHECK(args.length() == 3); |
| 436 if (!args[0]->IsJSTypedArray()) { |
| 437 THROW_NEW_ERROR_RETURN_FAILURE( |
| 438 isolate, |
| 439 NewTypeError("not_typed_array", HandleVector<Object>(NULL, 0))); |
| 440 } |
| 441 |
| 442 if (!args[1]->IsJSTypedArray()) |
| 443 return Smi::FromInt(TYPED_ARRAY_SET_NON_TYPED_ARRAY); |
| 444 |
| 445 CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, target_obj, 0); |
| 446 CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, source_obj, 1); |
| 447 CONVERT_NUMBER_ARG_HANDLE_CHECKED(offset_obj, 2); |
| 448 |
| 449 Handle<JSTypedArray> target(JSTypedArray::cast(*target_obj)); |
| 450 Handle<JSTypedArray> source(JSTypedArray::cast(*source_obj)); |
| 451 size_t offset = 0; |
| 452 RUNTIME_ASSERT(TryNumberToSize(isolate, *offset_obj, &offset)); |
| 453 size_t target_length = NumberToSize(isolate, target->length()); |
| 454 size_t source_length = NumberToSize(isolate, source->length()); |
| 455 size_t target_byte_length = NumberToSize(isolate, target->byte_length()); |
| 456 size_t source_byte_length = NumberToSize(isolate, source->byte_length()); |
| 457 if (offset > target_length || offset + source_length > target_length || |
| 458 offset + source_length < offset) { // overflow |
| 459 THROW_NEW_ERROR_RETURN_FAILURE( |
| 460 isolate, NewRangeError("typed_array_set_source_too_large", |
| 461 HandleVector<Object>(NULL, 0))); |
| 462 } |
| 463 |
| 464 size_t target_offset = NumberToSize(isolate, target->byte_offset()); |
| 465 size_t source_offset = NumberToSize(isolate, source->byte_offset()); |
| 466 uint8_t* target_base = |
| 467 static_cast<uint8_t*>(target->GetBuffer()->backing_store()) + |
| 468 target_offset; |
| 469 uint8_t* source_base = |
| 470 static_cast<uint8_t*>(source->GetBuffer()->backing_store()) + |
| 471 source_offset; |
| 472 |
| 473 // Typed arrays of the same type: use memmove. |
| 474 if (target->type() == source->type()) { |
| 475 memmove(target_base + offset * target->element_size(), source_base, |
| 476 source_byte_length); |
| 477 return Smi::FromInt(TYPED_ARRAY_SET_TYPED_ARRAY_SAME_TYPE); |
| 478 } |
| 479 |
| 480 // Typed arrays of different types over the same backing store |
| 481 if ((source_base <= target_base && |
| 482 source_base + source_byte_length > target_base) || |
| 483 (target_base <= source_base && |
| 484 target_base + target_byte_length > source_base)) { |
| 485 // We do not support overlapping ArrayBuffers |
| 486 DCHECK(target->GetBuffer()->backing_store() == |
| 487 source->GetBuffer()->backing_store()); |
| 488 return Smi::FromInt(TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING); |
| 489 } else { // Non-overlapping typed arrays |
| 490 return Smi::FromInt(TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING); |
| 491 } |
| 492 } |
| 493 |
| 494 |
| 495 RUNTIME_FUNCTION(Runtime_TypedArrayMaxSizeInHeap) { |
| 496 DCHECK(args.length() == 0); |
| 497 DCHECK_OBJECT_SIZE(FLAG_typed_array_max_size_in_heap + |
| 498 FixedTypedArrayBase::kDataOffset); |
| 499 return Smi::FromInt(FLAG_typed_array_max_size_in_heap); |
| 500 } |
| 501 |
| 502 |
| 503 RUNTIME_FUNCTION(Runtime_DataViewInitialize) { |
| 504 HandleScope scope(isolate); |
| 505 DCHECK(args.length() == 4); |
| 506 CONVERT_ARG_HANDLE_CHECKED(JSDataView, holder, 0); |
| 507 CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, buffer, 1); |
| 508 CONVERT_NUMBER_ARG_HANDLE_CHECKED(byte_offset, 2); |
| 509 CONVERT_NUMBER_ARG_HANDLE_CHECKED(byte_length, 3); |
| 510 |
| 511 DCHECK(holder->GetInternalFieldCount() == |
| 512 v8::ArrayBufferView::kInternalFieldCount); |
| 513 for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) { |
| 514 holder->SetInternalField(i, Smi::FromInt(0)); |
| 515 } |
| 516 size_t buffer_length = 0; |
| 517 size_t offset = 0; |
| 518 size_t length = 0; |
| 519 RUNTIME_ASSERT( |
| 520 TryNumberToSize(isolate, buffer->byte_length(), &buffer_length)); |
| 521 RUNTIME_ASSERT(TryNumberToSize(isolate, *byte_offset, &offset)); |
| 522 RUNTIME_ASSERT(TryNumberToSize(isolate, *byte_length, &length)); |
| 523 |
| 524 // TODO(jkummerow): When we have a "safe numerics" helper class, use it here. |
| 525 // Entire range [offset, offset + length] must be in bounds. |
| 526 RUNTIME_ASSERT(offset <= buffer_length); |
| 527 RUNTIME_ASSERT(offset + length <= buffer_length); |
| 528 // No overflow. |
| 529 RUNTIME_ASSERT(offset + length >= offset); |
| 530 |
| 531 holder->set_buffer(*buffer); |
| 532 holder->set_byte_offset(*byte_offset); |
| 533 holder->set_byte_length(*byte_length); |
| 534 |
| 535 holder->set_weak_next(buffer->weak_first_view()); |
| 536 buffer->set_weak_first_view(*holder); |
| 537 |
| 538 return isolate->heap()->undefined_value(); |
| 539 } |
| 540 |
| 541 |
| 542 inline static bool NeedToFlipBytes(bool is_little_endian) { |
| 543 #ifdef V8_TARGET_LITTLE_ENDIAN |
| 544 return !is_little_endian; |
| 545 #else |
| 546 return is_little_endian; |
| 547 #endif |
| 548 } |
| 549 |
| 550 |
| 551 template <int n> |
| 552 inline void CopyBytes(uint8_t* target, uint8_t* source) { |
| 553 for (int i = 0; i < n; i++) { |
| 554 *(target++) = *(source++); |
| 555 } |
| 556 } |
| 557 |
| 558 |
| 559 template <int n> |
| 560 inline void FlipBytes(uint8_t* target, uint8_t* source) { |
| 561 source = source + (n - 1); |
| 562 for (int i = 0; i < n; i++) { |
| 563 *(target++) = *(source--); |
| 564 } |
| 565 } |
| 566 |
| 567 |
| 568 template <typename T> |
| 569 inline static bool DataViewGetValue(Isolate* isolate, |
| 570 Handle<JSDataView> data_view, |
| 571 Handle<Object> byte_offset_obj, |
| 572 bool is_little_endian, T* result) { |
| 573 size_t byte_offset = 0; |
| 574 if (!TryNumberToSize(isolate, *byte_offset_obj, &byte_offset)) { |
| 575 return false; |
| 576 } |
| 577 Handle<JSArrayBuffer> buffer(JSArrayBuffer::cast(data_view->buffer())); |
| 578 |
| 579 size_t data_view_byte_offset = |
| 580 NumberToSize(isolate, data_view->byte_offset()); |
| 581 size_t data_view_byte_length = |
| 582 NumberToSize(isolate, data_view->byte_length()); |
| 583 if (byte_offset + sizeof(T) > data_view_byte_length || |
| 584 byte_offset + sizeof(T) < byte_offset) { // overflow |
| 585 return false; |
| 586 } |
| 587 |
| 588 union Value { |
| 589 T data; |
| 590 uint8_t bytes[sizeof(T)]; |
| 591 }; |
| 592 |
| 593 Value value; |
| 594 size_t buffer_offset = data_view_byte_offset + byte_offset; |
| 595 DCHECK(NumberToSize(isolate, buffer->byte_length()) >= |
| 596 buffer_offset + sizeof(T)); |
| 597 uint8_t* source = |
| 598 static_cast<uint8_t*>(buffer->backing_store()) + buffer_offset; |
| 599 if (NeedToFlipBytes(is_little_endian)) { |
| 600 FlipBytes<sizeof(T)>(value.bytes, source); |
| 601 } else { |
| 602 CopyBytes<sizeof(T)>(value.bytes, source); |
| 603 } |
| 604 *result = value.data; |
| 605 return true; |
| 606 } |
| 607 |
| 608 |
| 609 template <typename T> |
| 610 static bool DataViewSetValue(Isolate* isolate, Handle<JSDataView> data_view, |
| 611 Handle<Object> byte_offset_obj, |
| 612 bool is_little_endian, T data) { |
| 613 size_t byte_offset = 0; |
| 614 if (!TryNumberToSize(isolate, *byte_offset_obj, &byte_offset)) { |
| 615 return false; |
| 616 } |
| 617 Handle<JSArrayBuffer> buffer(JSArrayBuffer::cast(data_view->buffer())); |
| 618 |
| 619 size_t data_view_byte_offset = |
| 620 NumberToSize(isolate, data_view->byte_offset()); |
| 621 size_t data_view_byte_length = |
| 622 NumberToSize(isolate, data_view->byte_length()); |
| 623 if (byte_offset + sizeof(T) > data_view_byte_length || |
| 624 byte_offset + sizeof(T) < byte_offset) { // overflow |
| 625 return false; |
| 626 } |
| 627 |
| 628 union Value { |
| 629 T data; |
| 630 uint8_t bytes[sizeof(T)]; |
| 631 }; |
| 632 |
| 633 Value value; |
| 634 value.data = data; |
| 635 size_t buffer_offset = data_view_byte_offset + byte_offset; |
| 636 DCHECK(NumberToSize(isolate, buffer->byte_length()) >= |
| 637 buffer_offset + sizeof(T)); |
| 638 uint8_t* target = |
| 639 static_cast<uint8_t*>(buffer->backing_store()) + buffer_offset; |
| 640 if (NeedToFlipBytes(is_little_endian)) { |
| 641 FlipBytes<sizeof(T)>(target, value.bytes); |
| 642 } else { |
| 643 CopyBytes<sizeof(T)>(target, value.bytes); |
| 644 } |
| 645 return true; |
| 646 } |
| 647 |
| 648 |
| 649 #define DATA_VIEW_GETTER(TypeName, Type, Converter) \ |
| 650 RUNTIME_FUNCTION(Runtime_DataViewGet##TypeName) { \ |
| 651 HandleScope scope(isolate); \ |
| 652 DCHECK(args.length() == 3); \ |
| 653 CONVERT_ARG_HANDLE_CHECKED(JSDataView, holder, 0); \ |
| 654 CONVERT_NUMBER_ARG_HANDLE_CHECKED(offset, 1); \ |
| 655 CONVERT_BOOLEAN_ARG_CHECKED(is_little_endian, 2); \ |
| 656 Type result; \ |
| 657 if (DataViewGetValue(isolate, holder, offset, is_little_endian, \ |
| 658 &result)) { \ |
| 659 return *isolate->factory()->Converter(result); \ |
| 660 } else { \ |
| 661 THROW_NEW_ERROR_RETURN_FAILURE( \ |
| 662 isolate, NewRangeError("invalid_data_view_accessor_offset", \ |
| 663 HandleVector<Object>(NULL, 0))); \ |
| 664 } \ |
| 665 } |
| 666 |
| 667 DATA_VIEW_GETTER(Uint8, uint8_t, NewNumberFromUint) |
| 668 DATA_VIEW_GETTER(Int8, int8_t, NewNumberFromInt) |
| 669 DATA_VIEW_GETTER(Uint16, uint16_t, NewNumberFromUint) |
| 670 DATA_VIEW_GETTER(Int16, int16_t, NewNumberFromInt) |
| 671 DATA_VIEW_GETTER(Uint32, uint32_t, NewNumberFromUint) |
| 672 DATA_VIEW_GETTER(Int32, int32_t, NewNumberFromInt) |
| 673 DATA_VIEW_GETTER(Float32, float, NewNumber) |
| 674 DATA_VIEW_GETTER(Float64, double, NewNumber) |
| 675 |
| 676 #undef DATA_VIEW_GETTER |
| 677 |
| 678 |
| 679 template <typename T> |
| 680 static T DataViewConvertValue(double value); |
| 681 |
| 682 |
| 683 template <> |
| 684 int8_t DataViewConvertValue<int8_t>(double value) { |
| 685 return static_cast<int8_t>(DoubleToInt32(value)); |
| 686 } |
| 687 |
| 688 |
| 689 template <> |
| 690 int16_t DataViewConvertValue<int16_t>(double value) { |
| 691 return static_cast<int16_t>(DoubleToInt32(value)); |
| 692 } |
| 693 |
| 694 |
| 695 template <> |
| 696 int32_t DataViewConvertValue<int32_t>(double value) { |
| 697 return DoubleToInt32(value); |
| 698 } |
| 699 |
| 700 |
| 701 template <> |
| 702 uint8_t DataViewConvertValue<uint8_t>(double value) { |
| 703 return static_cast<uint8_t>(DoubleToUint32(value)); |
| 704 } |
| 705 |
| 706 |
| 707 template <> |
| 708 uint16_t DataViewConvertValue<uint16_t>(double value) { |
| 709 return static_cast<uint16_t>(DoubleToUint32(value)); |
| 710 } |
| 711 |
| 712 |
| 713 template <> |
| 714 uint32_t DataViewConvertValue<uint32_t>(double value) { |
| 715 return DoubleToUint32(value); |
| 716 } |
| 717 |
| 718 |
| 719 template <> |
| 720 float DataViewConvertValue<float>(double value) { |
| 721 return static_cast<float>(value); |
| 722 } |
| 723 |
| 724 |
| 725 template <> |
| 726 double DataViewConvertValue<double>(double value) { |
| 727 return value; |
| 728 } |
| 729 |
| 730 |
| 731 #define DATA_VIEW_SETTER(TypeName, Type) \ |
| 732 RUNTIME_FUNCTION(Runtime_DataViewSet##TypeName) { \ |
| 733 HandleScope scope(isolate); \ |
| 734 DCHECK(args.length() == 4); \ |
| 735 CONVERT_ARG_HANDLE_CHECKED(JSDataView, holder, 0); \ |
| 736 CONVERT_NUMBER_ARG_HANDLE_CHECKED(offset, 1); \ |
| 737 CONVERT_NUMBER_ARG_HANDLE_CHECKED(value, 2); \ |
| 738 CONVERT_BOOLEAN_ARG_CHECKED(is_little_endian, 3); \ |
| 739 Type v = DataViewConvertValue<Type>(value->Number()); \ |
| 740 if (DataViewSetValue(isolate, holder, offset, is_little_endian, v)) { \ |
| 741 return isolate->heap()->undefined_value(); \ |
| 742 } else { \ |
| 743 THROW_NEW_ERROR_RETURN_FAILURE( \ |
| 744 isolate, NewRangeError("invalid_data_view_accessor_offset", \ |
| 745 HandleVector<Object>(NULL, 0))); \ |
| 746 } \ |
| 747 } |
| 748 |
| 749 DATA_VIEW_SETTER(Uint8, uint8_t) |
| 750 DATA_VIEW_SETTER(Int8, int8_t) |
| 751 DATA_VIEW_SETTER(Uint16, uint16_t) |
| 752 DATA_VIEW_SETTER(Int16, int16_t) |
| 753 DATA_VIEW_SETTER(Uint32, uint32_t) |
| 754 DATA_VIEW_SETTER(Int32, int32_t) |
| 755 DATA_VIEW_SETTER(Float32, float) |
| 756 DATA_VIEW_SETTER(Float64, double) |
| 757 |
| 758 #undef DATA_VIEW_SETTER |
| 759 } |
| 760 } // namespace v8::internal |
OLD | NEW |