| OLD | NEW |
| 1 // Copyright 2016 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/builtins/builtins-utils.h" | 5 #include "src/builtins/builtins-utils.h" |
| 6 #include "src/builtins/builtins.h" | 6 #include "src/builtins/builtins.h" |
| 7 #include "src/code-stub-assembler.h" | |
| 8 #include "src/counters.h" | 7 #include "src/counters.h" |
| 9 #include "src/elements.h" | 8 #include "src/elements.h" |
| 10 #include "src/objects-inl.h" | 9 #include "src/objects-inl.h" |
| 11 | 10 |
| 12 namespace v8 { | 11 namespace v8 { |
| 13 namespace internal { | 12 namespace internal { |
| 14 | 13 |
| 15 class TypedArrayBuiltinsAssembler : public CodeStubAssembler { | |
| 16 public: | |
| 17 explicit TypedArrayBuiltinsAssembler(compiler::CodeAssemblerState* state) | |
| 18 : CodeStubAssembler(state) {} | |
| 19 | |
| 20 protected: | |
| 21 void GenerateTypedArrayPrototypeGetter(const char* method_name, | |
| 22 int object_offset); | |
| 23 template <IterationKind kIterationKind> | |
| 24 void GenerateTypedArrayPrototypeIterationMethod(const char* method_name); | |
| 25 | |
| 26 void LoadMapAndElementsSize(Node* const array, Variable* typed_map, | |
| 27 Variable* size); | |
| 28 | |
| 29 void CalculateExternalPointer(Node* const backing_store, | |
| 30 Node* const byte_offset, | |
| 31 Variable* external_pointer); | |
| 32 void DoInitialize(Node* const holder, Node* length, Node* const maybe_buffer, | |
| 33 Node* const byte_offset, Node* byte_length, | |
| 34 Node* const initialize, Node* const context); | |
| 35 }; | |
| 36 | |
| 37 void TypedArrayBuiltinsAssembler::LoadMapAndElementsSize(Node* const array, | |
| 38 Variable* typed_map, | |
| 39 Variable* size) { | |
| 40 Label unreachable(this), done(this); | |
| 41 Label uint8_elements(this), uint8_clamped_elements(this), int8_elements(this), | |
| 42 uint16_elements(this), int16_elements(this), uint32_elements(this), | |
| 43 int32_elements(this), float32_elements(this), float64_elements(this); | |
| 44 Label* elements_kind_labels[] = { | |
| 45 &uint8_elements, &uint8_clamped_elements, &int8_elements, | |
| 46 &uint16_elements, &int16_elements, &uint32_elements, | |
| 47 &int32_elements, &float32_elements, &float64_elements}; | |
| 48 int32_t elements_kinds[] = { | |
| 49 UINT8_ELEMENTS, UINT8_CLAMPED_ELEMENTS, INT8_ELEMENTS, | |
| 50 UINT16_ELEMENTS, INT16_ELEMENTS, UINT32_ELEMENTS, | |
| 51 INT32_ELEMENTS, FLOAT32_ELEMENTS, FLOAT64_ELEMENTS}; | |
| 52 const size_t kTypedElementsKindCount = LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND - | |
| 53 FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND + | |
| 54 1; | |
| 55 DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kinds)); | |
| 56 DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kind_labels)); | |
| 57 | |
| 58 Node* array_map = LoadMap(array); | |
| 59 Node* elements_kind = LoadMapElementsKind(array_map); | |
| 60 Switch(elements_kind, &unreachable, elements_kinds, elements_kind_labels, | |
| 61 kTypedElementsKindCount); | |
| 62 | |
| 63 for (int i = 0; i < static_cast<int>(kTypedElementsKindCount); i++) { | |
| 64 Bind(elements_kind_labels[i]); | |
| 65 { | |
| 66 ElementsKind kind = static_cast<ElementsKind>(elements_kinds[i]); | |
| 67 ExternalArrayType type = | |
| 68 isolate()->factory()->GetArrayTypeFromElementsKind(kind); | |
| 69 Handle<Map> map(isolate()->heap()->MapForFixedTypedArray(type)); | |
| 70 typed_map->Bind(HeapConstant(map)); | |
| 71 size->Bind(SmiConstant(static_cast<int>( | |
| 72 isolate()->factory()->GetExternalArrayElementSize(type)))); | |
| 73 Goto(&done); | |
| 74 } | |
| 75 } | |
| 76 | |
| 77 Bind(&unreachable); | |
| 78 { Unreachable(); } | |
| 79 Bind(&done); | |
| 80 } | |
| 81 | |
| 82 // The byte_offset can be higher than Smi range, in which case to perform the | |
| 83 // pointer arithmetic necessary to calculate external_pointer, converting | |
| 84 // byte_offset to an intptr is more difficult. The max byte_offset is 8 * MaxSmi | |
| 85 // on the particular platform. 32 bit platforms are self-limiting, because we | |
| 86 // can't allocate an array bigger than our 32-bit arithmetic range anyway. 64 | |
| 87 // bit platforms could theoretically have an offset up to 2^35 - 1, so we may | |
| 88 // need to convert the float heap number to an intptr. | |
| 89 void TypedArrayBuiltinsAssembler::CalculateExternalPointer( | |
| 90 Node* const backing_store, Node* const byte_offset, | |
| 91 Variable* external_pointer) { | |
| 92 Label offset_is_smi(this), offset_not_smi(this), done(this); | |
| 93 Branch(TaggedIsSmi(byte_offset), &offset_is_smi, &offset_not_smi); | |
| 94 | |
| 95 Bind(&offset_is_smi); | |
| 96 { | |
| 97 external_pointer->Bind(IntPtrAdd(backing_store, SmiToWord(byte_offset))); | |
| 98 Goto(&done); | |
| 99 } | |
| 100 | |
| 101 Bind(&offset_not_smi); | |
| 102 { | |
| 103 Node* heap_number = LoadHeapNumberValue(byte_offset); | |
| 104 Node* intrptr_value = ChangeFloat64ToUintPtr(heap_number); | |
| 105 external_pointer->Bind(IntPtrAdd(backing_store, intrptr_value)); | |
| 106 Goto(&done); | |
| 107 } | |
| 108 | |
| 109 Bind(&done); | |
| 110 } | |
| 111 | |
| 112 void TypedArrayBuiltinsAssembler::DoInitialize(Node* const holder, Node* length, | |
| 113 Node* const maybe_buffer, | |
| 114 Node* const byte_offset, | |
| 115 Node* byte_length, | |
| 116 Node* const initialize, | |
| 117 Node* const context) { | |
| 118 static const int32_t fta_base_data_offset = | |
| 119 FixedTypedArrayBase::kDataOffset - kHeapObjectTag; | |
| 120 | |
| 121 Label setup_holder(this), alloc_array_buffer(this), aligned(this), | |
| 122 allocate_elements(this), attach_buffer(this), done(this); | |
| 123 Variable fixed_typed_map(this, MachineRepresentation::kTagged); | |
| 124 Variable element_size(this, MachineRepresentation::kTagged); | |
| 125 Variable total_size(this, MachineType::PointerRepresentation()); | |
| 126 | |
| 127 // Make sure length is a Smi. The caller guarantees this is the case. | |
| 128 length = ToInteger(context, length, CodeStubAssembler::kTruncateMinusZero); | |
| 129 CSA_ASSERT(this, TaggedIsSmi(length)); | |
| 130 | |
| 131 // byte_length can be -0, get rid of it. | |
| 132 byte_length = | |
| 133 ToInteger(context, byte_length, CodeStubAssembler::kTruncateMinusZero); | |
| 134 | |
| 135 GotoIfNot(IsNull(maybe_buffer), &setup_holder); | |
| 136 // If the buffer is null, then we need a Smi byte_length. The caller | |
| 137 // guarantees this is the case, because when byte_length > | |
| 138 // TypedArrayMaxSizeInHeap, a buffer is allocated and passed in here. | |
| 139 CSA_ASSERT(this, TaggedIsSmi(byte_length)); | |
| 140 Goto(&setup_holder); | |
| 141 | |
| 142 Bind(&setup_holder); | |
| 143 { | |
| 144 LoadMapAndElementsSize(holder, &fixed_typed_map, &element_size); | |
| 145 // Setup the holder (JSArrayBufferView). | |
| 146 // - Set the length. | |
| 147 // - Set the byte_offset. | |
| 148 // - Set the byte_length. | |
| 149 // - Set InternalFields to 0. | |
| 150 StoreObjectField(holder, JSTypedArray::kLengthOffset, length); | |
| 151 StoreObjectField(holder, JSArrayBufferView::kByteOffsetOffset, byte_offset); | |
| 152 StoreObjectField(holder, JSArrayBufferView::kByteLengthOffset, byte_length); | |
| 153 for (int offset = JSTypedArray::kSize; | |
| 154 offset < JSTypedArray::kSizeWithInternalFields; | |
| 155 offset += kPointerSize) { | |
| 156 StoreObjectField(holder, offset, SmiConstant(Smi::kZero)); | |
| 157 } | |
| 158 | |
| 159 Branch(IsNull(maybe_buffer), &alloc_array_buffer, &attach_buffer); | |
| 160 } | |
| 161 | |
| 162 Bind(&alloc_array_buffer); | |
| 163 { | |
| 164 // Allocate a new ArrayBuffer and initialize it with empty properties and | |
| 165 // elements. | |
| 166 Node* const native_context = LoadNativeContext(context); | |
| 167 Node* const map = | |
| 168 LoadContextElement(native_context, Context::ARRAY_BUFFER_MAP_INDEX); | |
| 169 Node* empty_fixed_array = LoadRoot(Heap::kEmptyFixedArrayRootIndex); | |
| 170 | |
| 171 Node* const buffer = Allocate(JSArrayBuffer::kSizeWithInternalFields); | |
| 172 StoreMapNoWriteBarrier(buffer, map); | |
| 173 StoreObjectFieldNoWriteBarrier(buffer, JSArray::kPropertiesOffset, | |
| 174 empty_fixed_array); | |
| 175 StoreObjectFieldNoWriteBarrier(buffer, JSArray::kElementsOffset, | |
| 176 empty_fixed_array); | |
| 177 // Setup the ArrayBuffer. | |
| 178 // - Set BitField to 0. | |
| 179 // - Set IsExternal and IsNeuterable bits of BitFieldSlot. | |
| 180 // - Set the byte_length field to byte_length. | |
| 181 // - Set backing_store to null/Smi(0). | |
| 182 // - Set all internal fields to Smi(0). | |
| 183 StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBitFieldSlot, | |
| 184 SmiConstant(Smi::kZero)); | |
| 185 int32_t bitfield_value = (1 << JSArrayBuffer::IsExternal::kShift) | | |
| 186 (1 << JSArrayBuffer::IsNeuterable::kShift); | |
| 187 StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBitFieldOffset, | |
| 188 Int32Constant(bitfield_value), | |
| 189 MachineRepresentation::kWord32); | |
| 190 | |
| 191 StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kByteLengthOffset, | |
| 192 byte_length); | |
| 193 StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBackingStoreOffset, | |
| 194 SmiConstant(Smi::kZero)); | |
| 195 for (int i = 0; i < v8::ArrayBuffer::kInternalFieldCount; i++) { | |
| 196 int offset = JSArrayBuffer::kSize + i * kPointerSize; | |
| 197 StoreObjectFieldNoWriteBarrier(buffer, offset, SmiConstant(Smi::kZero)); | |
| 198 } | |
| 199 | |
| 200 StoreObjectField(holder, JSArrayBufferView::kBufferOffset, buffer); | |
| 201 | |
| 202 // Check the alignment. | |
| 203 GotoIf(SmiEqual(SmiMod(element_size.value(), SmiConstant(kObjectAlignment)), | |
| 204 SmiConstant(0)), | |
| 205 &aligned); | |
| 206 | |
| 207 // Fix alignment if needed. | |
| 208 DCHECK_EQ(0, FixedTypedArrayBase::kHeaderSize & kObjectAlignmentMask); | |
| 209 Node* aligned_header_size = | |
| 210 IntPtrConstant(FixedTypedArrayBase::kHeaderSize + kObjectAlignmentMask); | |
| 211 Node* size = IntPtrAdd(SmiToWord(byte_length), aligned_header_size); | |
| 212 total_size.Bind(WordAnd(size, IntPtrConstant(~kObjectAlignmentMask))); | |
| 213 Goto(&allocate_elements); | |
| 214 } | |
| 215 | |
| 216 Bind(&aligned); | |
| 217 { | |
| 218 Node* header_size = IntPtrConstant(FixedTypedArrayBase::kHeaderSize); | |
| 219 total_size.Bind(IntPtrAdd(SmiToWord(byte_length), header_size)); | |
| 220 Goto(&allocate_elements); | |
| 221 } | |
| 222 | |
| 223 Bind(&allocate_elements); | |
| 224 { | |
| 225 // Allocate a FixedTypedArray and set the length, base pointer and external | |
| 226 // pointer. | |
| 227 CSA_ASSERT(this, IsRegularHeapObjectSize(total_size.value())); | |
| 228 Node* elements = Allocate(total_size.value()); | |
| 229 | |
| 230 StoreMapNoWriteBarrier(elements, fixed_typed_map.value()); | |
| 231 StoreObjectFieldNoWriteBarrier(elements, FixedArray::kLengthOffset, length); | |
| 232 StoreObjectFieldNoWriteBarrier( | |
| 233 elements, FixedTypedArrayBase::kBasePointerOffset, elements); | |
| 234 StoreObjectFieldNoWriteBarrier(elements, | |
| 235 FixedTypedArrayBase::kExternalPointerOffset, | |
| 236 IntPtrConstant(fta_base_data_offset), | |
| 237 MachineType::PointerRepresentation()); | |
| 238 | |
| 239 StoreObjectField(holder, JSObject::kElementsOffset, elements); | |
| 240 | |
| 241 GotoIf(IsFalse(initialize), &done); | |
| 242 // Initialize the backing store by filling it with 0s. | |
| 243 Node* backing_store = IntPtrAdd(BitcastTaggedToWord(elements), | |
| 244 IntPtrConstant(fta_base_data_offset)); | |
| 245 // Call out to memset to perform initialization. | |
| 246 Node* memset = | |
| 247 ExternalConstant(ExternalReference::libc_memset_function(isolate())); | |
| 248 CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(), | |
| 249 MachineType::IntPtr(), MachineType::UintPtr(), memset, | |
| 250 backing_store, IntPtrConstant(0), SmiToWord(byte_length)); | |
| 251 Goto(&done); | |
| 252 } | |
| 253 | |
| 254 Bind(&attach_buffer); | |
| 255 { | |
| 256 StoreObjectField(holder, JSArrayBufferView::kBufferOffset, maybe_buffer); | |
| 257 | |
| 258 Node* elements = Allocate(FixedTypedArrayBase::kHeaderSize); | |
| 259 StoreMapNoWriteBarrier(elements, fixed_typed_map.value()); | |
| 260 StoreObjectFieldNoWriteBarrier(elements, FixedArray::kLengthOffset, length); | |
| 261 StoreObjectFieldNoWriteBarrier( | |
| 262 elements, FixedTypedArrayBase::kBasePointerOffset, SmiConstant(0)); | |
| 263 | |
| 264 Variable external_pointer(this, MachineType::PointerRepresentation()); | |
| 265 Node* backing_store = | |
| 266 LoadObjectField(maybe_buffer, JSArrayBuffer::kBackingStoreOffset, | |
| 267 MachineType::Pointer()); | |
| 268 | |
| 269 CalculateExternalPointer(backing_store, byte_offset, &external_pointer); | |
| 270 StoreObjectFieldNoWriteBarrier( | |
| 271 elements, FixedTypedArrayBase::kExternalPointerOffset, | |
| 272 external_pointer.value(), MachineType::PointerRepresentation()); | |
| 273 | |
| 274 StoreObjectField(holder, JSObject::kElementsOffset, elements); | |
| 275 Goto(&done); | |
| 276 } | |
| 277 | |
| 278 Bind(&done); | |
| 279 Return(UndefinedConstant()); | |
| 280 } | |
| 281 | |
| 282 TF_BUILTIN(TypedArrayInitialize, TypedArrayBuiltinsAssembler) { | |
| 283 Node* const holder = Parameter(1); | |
| 284 Node* length = Parameter(2); | |
| 285 Node* const maybe_buffer = Parameter(3); | |
| 286 Node* const byte_offset = Parameter(4); | |
| 287 Node* byte_length = Parameter(5); | |
| 288 Node* const initialize = Parameter(6); | |
| 289 Node* const context = Parameter(9); | |
| 290 | |
| 291 DoInitialize(holder, length, maybe_buffer, byte_offset, byte_length, | |
| 292 initialize, context); | |
| 293 } | |
| 294 | |
| 295 // ----------------------------------------------------------------------------- | 14 // ----------------------------------------------------------------------------- |
| 296 // ES6 section 22.2 TypedArray Objects | 15 // ES6 section 22.2 TypedArray Objects |
| 297 | 16 |
| 298 // ES6 section 22.2.4.2 TypedArray ( length ) | |
| 299 TF_BUILTIN(TypedArrayConstructByLength, TypedArrayBuiltinsAssembler) { | |
| 300 // We know that holder cannot be an object if this builtin was called. | |
| 301 Node* holder = Parameter(1); | |
| 302 Node* length = Parameter(2); | |
| 303 Node* element_size = Parameter(3); | |
| 304 Node* context = Parameter(6); | |
| 305 | |
| 306 Variable maybe_buffer(this, MachineRepresentation::kTagged); | |
| 307 maybe_buffer.Bind(NullConstant()); | |
| 308 Node* byte_offset = SmiConstant(0); | |
| 309 Node* initialize = BooleanConstant(true); | |
| 310 | |
| 311 Label external_buffer(this), call_init(this), invalid_length(this); | |
| 312 | |
| 313 length = ToInteger(context, length, CodeStubAssembler::kTruncateMinusZero); | |
| 314 // The maximum length of a TypedArray is MaxSmi(). | |
| 315 // Note: this is not per spec, but rather a constraint of our current | |
| 316 // representation (which uses smi's). | |
| 317 GotoIf(TaggedIsNotSmi(length), &invalid_length); | |
| 318 GotoIf(SmiLessThan(length, SmiConstant(0)), &invalid_length); | |
| 319 | |
| 320 // For byte_length < typed_array_max_size_in_heap, we allocate the buffer on | |
| 321 // the heap. Otherwise we allocate it externally and attach it. | |
| 322 Node* byte_length = SmiMul(length, element_size); | |
| 323 GotoIf(TaggedIsNotSmi(byte_length), &external_buffer); | |
| 324 Branch(SmiLessThanOrEqual(byte_length, | |
| 325 SmiConstant(FLAG_typed_array_max_size_in_heap)), | |
| 326 &call_init, &external_buffer); | |
| 327 | |
| 328 Bind(&external_buffer); | |
| 329 { | |
| 330 Node* const buffer_constructor = LoadContextElement( | |
| 331 LoadNativeContext(context), Context::ARRAY_BUFFER_FUN_INDEX); | |
| 332 maybe_buffer.Bind(ConstructJS(CodeFactory::Construct(isolate()), context, | |
| 333 buffer_constructor, byte_length)); | |
| 334 Goto(&call_init); | |
| 335 } | |
| 336 | |
| 337 Bind(&call_init); | |
| 338 { | |
| 339 DoInitialize(holder, length, maybe_buffer.value(), byte_offset, byte_length, | |
| 340 initialize, context); | |
| 341 } | |
| 342 | |
| 343 Bind(&invalid_length); | |
| 344 { | |
| 345 CallRuntime(Runtime::kThrowRangeError, context, | |
| 346 SmiConstant(MessageTemplate::kInvalidTypedArrayLength)); | |
| 347 Unreachable(); | |
| 348 } | |
| 349 } | |
| 350 | |
| 351 // ES6 section 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] ) | |
| 352 TF_BUILTIN(TypedArrayConstructByArrayBuffer, TypedArrayBuiltinsAssembler) { | |
| 353 Node* const holder = Parameter(1); | |
| 354 Node* const buffer = Parameter(2); | |
| 355 Node* byte_offset = Parameter(3); | |
| 356 Node* const length = Parameter(4); | |
| 357 Node* const element_size = Parameter(5); | |
| 358 CSA_ASSERT(this, TaggedIsSmi(element_size)); | |
| 359 Node* const context = Parameter(8); | |
| 360 Node* const initialize = BooleanConstant(true); | |
| 361 | |
| 362 Variable new_byte_length(this, MachineRepresentation::kTagged, | |
| 363 SmiConstant(0)); | |
| 364 | |
| 365 Label start_offset_error(this), byte_length_error(this), | |
| 366 invalid_offset_error(this); | |
| 367 Label call_init(this), invalid_length(this), length_undefined(this), | |
| 368 length_defined(this); | |
| 369 | |
| 370 Callable add = CodeFactory::Add(isolate()); | |
| 371 Callable div = CodeFactory::Divide(isolate()); | |
| 372 Callable equal = CodeFactory::Equal(isolate()); | |
| 373 Callable greater_than = CodeFactory::GreaterThan(isolate()); | |
| 374 Callable less_than = CodeFactory::LessThan(isolate()); | |
| 375 Callable mod = CodeFactory::Modulus(isolate()); | |
| 376 Callable sub = CodeFactory::Subtract(isolate()); | |
| 377 | |
| 378 byte_offset = | |
| 379 ToInteger(context, byte_offset, CodeStubAssembler::kTruncateMinusZero); | |
| 380 GotoIf(IsTrue(CallStub(less_than, context, byte_offset, SmiConstant(0))), | |
| 381 &invalid_length); | |
| 382 | |
| 383 Node* remainder = CallStub(mod, context, byte_offset, element_size); | |
| 384 // Remainder can be a heap number. | |
| 385 GotoIf(IsFalse(CallStub(equal, context, remainder, SmiConstant(0))), | |
| 386 &start_offset_error); | |
| 387 | |
| 388 // TODO(petermarshall): Throw on detached typedArray. | |
| 389 Branch(IsUndefined(length), &length_undefined, &length_defined); | |
| 390 | |
| 391 Bind(&length_undefined); | |
| 392 { | |
| 393 Node* buffer_byte_length = | |
| 394 LoadObjectField(buffer, JSArrayBuffer::kByteLengthOffset); | |
| 395 | |
| 396 Node* remainder = CallStub(mod, context, buffer_byte_length, element_size); | |
| 397 // Remainder can be a heap number. | |
| 398 GotoIf(IsFalse(CallStub(equal, context, remainder, SmiConstant(0))), | |
| 399 &byte_length_error); | |
| 400 | |
| 401 new_byte_length.Bind( | |
| 402 CallStub(sub, context, buffer_byte_length, byte_offset)); | |
| 403 | |
| 404 Branch(IsTrue(CallStub(less_than, context, new_byte_length.value(), | |
| 405 SmiConstant(0))), | |
| 406 &invalid_offset_error, &call_init); | |
| 407 } | |
| 408 | |
| 409 Bind(&length_defined); | |
| 410 { | |
| 411 Node* new_length = ToSmiIndex(length, context, &invalid_length); | |
| 412 new_byte_length.Bind(SmiMul(new_length, element_size)); | |
| 413 // Reading the byte length must come after the ToIndex operation, which | |
| 414 // could cause the buffer to become detached. | |
| 415 Node* buffer_byte_length = | |
| 416 LoadObjectField(buffer, JSArrayBuffer::kByteLengthOffset); | |
| 417 | |
| 418 Node* end = CallStub(add, context, byte_offset, new_byte_length.value()); | |
| 419 | |
| 420 Branch(IsTrue(CallStub(greater_than, context, end, buffer_byte_length)), | |
| 421 &invalid_length, &call_init); | |
| 422 } | |
| 423 | |
| 424 Bind(&call_init); | |
| 425 { | |
| 426 Node* new_length = | |
| 427 CallStub(div, context, new_byte_length.value(), element_size); | |
| 428 // Force the result into a Smi, or throw a range error if it doesn't fit. | |
| 429 new_length = ToSmiIndex(new_length, context, &invalid_length); | |
| 430 | |
| 431 DoInitialize(holder, new_length, buffer, byte_offset, | |
| 432 new_byte_length.value(), initialize, context); | |
| 433 } | |
| 434 | |
| 435 Bind(&invalid_offset_error); | |
| 436 { | |
| 437 CallRuntime(Runtime::kThrowRangeError, context, | |
| 438 SmiConstant(MessageTemplate::kInvalidOffset), byte_offset); | |
| 439 Unreachable(); | |
| 440 } | |
| 441 | |
| 442 Bind(&start_offset_error); | |
| 443 { | |
| 444 Node* holder_map = LoadMap(holder); | |
| 445 Node* problem_string = HeapConstant( | |
| 446 factory()->NewStringFromAsciiChecked("start offset", TENURED)); | |
| 447 CallRuntime(Runtime::kThrowInvalidTypedArrayAlignment, context, holder_map, | |
| 448 problem_string); | |
| 449 | |
| 450 Unreachable(); | |
| 451 } | |
| 452 | |
| 453 Bind(&byte_length_error); | |
| 454 { | |
| 455 Node* holder_map = LoadMap(holder); | |
| 456 Node* problem_string = HeapConstant( | |
| 457 factory()->NewStringFromAsciiChecked("byte length", TENURED)); | |
| 458 CallRuntime(Runtime::kThrowInvalidTypedArrayAlignment, context, holder_map, | |
| 459 problem_string); | |
| 460 | |
| 461 Unreachable(); | |
| 462 } | |
| 463 | |
| 464 Bind(&invalid_length); | |
| 465 { | |
| 466 CallRuntime(Runtime::kThrowRangeError, context, | |
| 467 SmiConstant(MessageTemplate::kInvalidTypedArrayLength)); | |
| 468 Unreachable(); | |
| 469 } | |
| 470 } | |
| 471 | |
| 472 // ES6 section 22.2.3.1 get %TypedArray%.prototype.buffer | 17 // ES6 section 22.2.3.1 get %TypedArray%.prototype.buffer |
| 473 BUILTIN(TypedArrayPrototypeBuffer) { | 18 BUILTIN(TypedArrayPrototypeBuffer) { |
| 474 HandleScope scope(isolate); | 19 HandleScope scope(isolate); |
| 475 CHECK_RECEIVER(JSTypedArray, typed_array, "get TypedArray.prototype.buffer"); | 20 CHECK_RECEIVER(JSTypedArray, typed_array, "get TypedArray.prototype.buffer"); |
| 476 return *typed_array->GetBuffer(); | 21 return *typed_array->GetBuffer(); |
| 477 } | 22 } |
| 478 | 23 |
| 479 void TypedArrayBuiltinsAssembler::GenerateTypedArrayPrototypeGetter( | |
| 480 const char* method_name, int object_offset) { | |
| 481 Node* receiver = Parameter(0); | |
| 482 Node* context = Parameter(3); | |
| 483 | |
| 484 // Check if the {receiver} is actually a JSTypedArray. | |
| 485 Label receiver_is_incompatible(this, Label::kDeferred); | |
| 486 GotoIf(TaggedIsSmi(receiver), &receiver_is_incompatible); | |
| 487 GotoIfNot(HasInstanceType(receiver, JS_TYPED_ARRAY_TYPE), | |
| 488 &receiver_is_incompatible); | |
| 489 | |
| 490 // Check if the {receiver}'s JSArrayBuffer was neutered. | |
| 491 Node* receiver_buffer = | |
| 492 LoadObjectField(receiver, JSTypedArray::kBufferOffset); | |
| 493 Label if_receiverisneutered(this, Label::kDeferred); | |
| 494 GotoIf(IsDetachedBuffer(receiver_buffer), &if_receiverisneutered); | |
| 495 Return(LoadObjectField(receiver, object_offset)); | |
| 496 | |
| 497 Bind(&if_receiverisneutered); | |
| 498 { | |
| 499 // The {receiver}s buffer was neutered, default to zero. | |
| 500 Return(SmiConstant(0)); | |
| 501 } | |
| 502 | |
| 503 Bind(&receiver_is_incompatible); | |
| 504 { | |
| 505 // The {receiver} is not a valid JSTypedArray. | |
| 506 CallRuntime(Runtime::kThrowIncompatibleMethodReceiver, context, | |
| 507 HeapConstant( | |
| 508 factory()->NewStringFromAsciiChecked(method_name, TENURED)), | |
| 509 receiver); | |
| 510 Unreachable(); | |
| 511 } | |
| 512 } | |
| 513 | |
| 514 // ES6 section 22.2.3.2 get %TypedArray%.prototype.byteLength | |
| 515 TF_BUILTIN(TypedArrayPrototypeByteLength, TypedArrayBuiltinsAssembler) { | |
| 516 GenerateTypedArrayPrototypeGetter("get TypedArray.prototype.byteLength", | |
| 517 JSTypedArray::kByteLengthOffset); | |
| 518 } | |
| 519 | |
| 520 // ES6 section 22.2.3.3 get %TypedArray%.prototype.byteOffset | |
| 521 TF_BUILTIN(TypedArrayPrototypeByteOffset, TypedArrayBuiltinsAssembler) { | |
| 522 GenerateTypedArrayPrototypeGetter("get TypedArray.prototype.byteOffset", | |
| 523 JSTypedArray::kByteOffsetOffset); | |
| 524 } | |
| 525 | |
| 526 // ES6 section 22.2.3.18 get %TypedArray%.prototype.length | |
| 527 TF_BUILTIN(TypedArrayPrototypeLength, TypedArrayBuiltinsAssembler) { | |
| 528 GenerateTypedArrayPrototypeGetter("get TypedArray.prototype.length", | |
| 529 JSTypedArray::kLengthOffset); | |
| 530 } | |
| 531 | |
| 532 template <IterationKind kIterationKind> | |
| 533 void TypedArrayBuiltinsAssembler::GenerateTypedArrayPrototypeIterationMethod( | |
| 534 const char* method_name) { | |
| 535 Node* receiver = Parameter(0); | |
| 536 Node* context = Parameter(3); | |
| 537 | |
| 538 Label throw_bad_receiver(this, Label::kDeferred); | |
| 539 Label throw_typeerror(this, Label::kDeferred); | |
| 540 | |
| 541 GotoIf(TaggedIsSmi(receiver), &throw_bad_receiver); | |
| 542 | |
| 543 Node* map = LoadMap(receiver); | |
| 544 Node* instance_type = LoadMapInstanceType(map); | |
| 545 GotoIf(Word32NotEqual(instance_type, Int32Constant(JS_TYPED_ARRAY_TYPE)), | |
| 546 &throw_bad_receiver); | |
| 547 | |
| 548 // Check if the {receiver}'s JSArrayBuffer was neutered. | |
| 549 Node* receiver_buffer = | |
| 550 LoadObjectField(receiver, JSTypedArray::kBufferOffset); | |
| 551 Label if_receiverisneutered(this, Label::kDeferred); | |
| 552 GotoIf(IsDetachedBuffer(receiver_buffer), &if_receiverisneutered); | |
| 553 | |
| 554 Return(CreateArrayIterator(receiver, map, instance_type, context, | |
| 555 kIterationKind)); | |
| 556 | |
| 557 Variable var_message(this, MachineRepresentation::kTagged); | |
| 558 Bind(&throw_bad_receiver); | |
| 559 var_message.Bind(SmiConstant(MessageTemplate::kNotTypedArray)); | |
| 560 Goto(&throw_typeerror); | |
| 561 | |
| 562 Bind(&if_receiverisneutered); | |
| 563 var_message.Bind( | |
| 564 SmiConstant(Smi::FromInt(MessageTemplate::kDetachedOperation))); | |
| 565 Goto(&throw_typeerror); | |
| 566 | |
| 567 Bind(&throw_typeerror); | |
| 568 { | |
| 569 Node* method_arg = HeapConstant( | |
| 570 isolate()->factory()->NewStringFromAsciiChecked(method_name, TENURED)); | |
| 571 Node* result = CallRuntime(Runtime::kThrowTypeError, context, | |
| 572 var_message.value(), method_arg); | |
| 573 Return(result); | |
| 574 } | |
| 575 } | |
| 576 | |
| 577 TF_BUILTIN(TypedArrayPrototypeValues, TypedArrayBuiltinsAssembler) { | |
| 578 GenerateTypedArrayPrototypeIterationMethod<IterationKind::kValues>( | |
| 579 "%TypedArray%.prototype.values()"); | |
| 580 } | |
| 581 | |
| 582 TF_BUILTIN(TypedArrayPrototypeEntries, TypedArrayBuiltinsAssembler) { | |
| 583 GenerateTypedArrayPrototypeIterationMethod<IterationKind::kEntries>( | |
| 584 "%TypedArray%.prototype.entries()"); | |
| 585 } | |
| 586 | |
| 587 TF_BUILTIN(TypedArrayPrototypeKeys, TypedArrayBuiltinsAssembler) { | |
| 588 GenerateTypedArrayPrototypeIterationMethod<IterationKind::kKeys>( | |
| 589 "%TypedArray%.prototype.keys()"); | |
| 590 } | |
| 591 | |
| 592 namespace { | 24 namespace { |
| 593 | 25 |
| 594 int64_t CapRelativeIndex(Handle<Object> num, int64_t minimum, int64_t maximum) { | 26 int64_t CapRelativeIndex(Handle<Object> num, int64_t minimum, int64_t maximum) { |
| 595 int64_t relative; | 27 int64_t relative; |
| 596 if (V8_LIKELY(num->IsSmi())) { | 28 if (V8_LIKELY(num->IsSmi())) { |
| 597 relative = Smi::cast(*num)->value(); | 29 relative = Smi::cast(*num)->value(); |
| 598 } else { | 30 } else { |
| 599 DCHECK(num->IsHeapNumber()); | 31 DCHECK(num->IsHeapNumber()); |
| 600 double fp = HeapNumber::cast(*num)->value(); | 32 double fp = HeapNumber::cast(*num)->value(); |
| 601 if (V8_UNLIKELY(!std::isfinite(fp))) { | 33 if (V8_UNLIKELY(!std::isfinite(fp))) { |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 735 ElementsAccessor* elements = array->GetElementsAccessor(); | 167 ElementsAccessor* elements = array->GetElementsAccessor(); |
| 736 Maybe<int64_t> result = elements->IndexOfValue(isolate, array, search_element, | 168 Maybe<int64_t> result = elements->IndexOfValue(isolate, array, search_element, |
| 737 static_cast<uint32_t>(index), | 169 static_cast<uint32_t>(index), |
| 738 static_cast<uint32_t>(len)); | 170 static_cast<uint32_t>(len)); |
| 739 MAYBE_RETURN(result, isolate->heap()->exception()); | 171 MAYBE_RETURN(result, isolate->heap()->exception()); |
| 740 return *isolate->factory()->NewNumberFromInt64(result.FromJust()); | 172 return *isolate->factory()->NewNumberFromInt64(result.FromJust()); |
| 741 } | 173 } |
| 742 | 174 |
| 743 } // namespace internal | 175 } // namespace internal |
| 744 } // namespace v8 | 176 } // namespace v8 |
| OLD | NEW |