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 |