Line data Source code
1 : // Copyright 2017 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-gen.h"
6 : #include "src/builtins/builtins.h"
7 : #include "src/code-stub-assembler.h"
8 :
9 : namespace v8 {
10 : namespace internal {
11 :
12 : // -----------------------------------------------------------------------------
13 : // ES6 section 22.2 TypedArray Objects
14 :
15 : class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
16 : public:
17 : explicit TypedArrayBuiltinsAssembler(compiler::CodeAssemblerState* state)
18 473 : : CodeStubAssembler(state) {}
19 :
20 : protected:
21 : void GenerateTypedArrayPrototypeGetter(Node* context, Node* receiver,
22 : const char* method_name,
23 : int object_offset);
24 : void GenerateTypedArrayPrototypeIterationMethod(Node* context, Node* receiver,
25 : const char* method_name,
26 : IterationKind iteration_kind);
27 :
28 : void SetupTypedArray(Node* holder, Node* length, Node* byte_offset,
29 : Node* byte_length);
30 : void AttachBuffer(Node* holder, Node* buffer, Node* map, Node* length,
31 : Node* byte_offset);
32 :
33 : Node* LoadMapForType(Node* array);
34 : Node* CalculateExternalPointer(Node* backing_store, Node* byte_offset);
35 : Node* LoadDataPtr(Node* typed_array);
36 : Node* ByteLengthIsValid(Node* byte_length);
37 : };
38 :
39 86 : compiler::Node* TypedArrayBuiltinsAssembler::LoadMapForType(Node* array) {
40 : CSA_ASSERT(this, IsJSTypedArray(array));
41 :
42 172 : Label unreachable(this), done(this);
43 86 : Label uint8_elements(this), uint8_clamped_elements(this), int8_elements(this),
44 86 : uint16_elements(this), int16_elements(this), uint32_elements(this),
45 86 : int32_elements(this), float32_elements(this), float64_elements(this);
46 : Label* elements_kind_labels[] = {
47 : &uint8_elements, &uint8_clamped_elements, &int8_elements,
48 : &uint16_elements, &int16_elements, &uint32_elements,
49 86 : &int32_elements, &float32_elements, &float64_elements};
50 : int32_t elements_kinds[] = {
51 : UINT8_ELEMENTS, UINT8_CLAMPED_ELEMENTS, INT8_ELEMENTS,
52 : UINT16_ELEMENTS, INT16_ELEMENTS, UINT32_ELEMENTS,
53 86 : INT32_ELEMENTS, FLOAT32_ELEMENTS, FLOAT64_ELEMENTS};
54 : const size_t kTypedElementsKindCount = LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND -
55 : FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND +
56 : 1;
57 : DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kinds));
58 : DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kind_labels));
59 :
60 172 : VARIABLE(var_typed_map, MachineRepresentation::kTagged);
61 :
62 86 : Node* array_map = LoadMap(array);
63 86 : Node* elements_kind = LoadMapElementsKind(array_map);
64 : Switch(elements_kind, &unreachable, elements_kinds, elements_kind_labels,
65 86 : kTypedElementsKindCount);
66 :
67 860 : for (int i = 0; i < static_cast<int>(kTypedElementsKindCount); i++) {
68 774 : BIND(elements_kind_labels[i]);
69 : {
70 774 : ElementsKind kind = static_cast<ElementsKind>(elements_kinds[i]);
71 : ExternalArrayType type =
72 774 : isolate()->factory()->GetArrayTypeFromElementsKind(kind);
73 774 : Handle<Map> map(isolate()->heap()->MapForFixedTypedArray(type));
74 774 : var_typed_map.Bind(HeapConstant(map));
75 774 : Goto(&done);
76 : }
77 : }
78 :
79 86 : BIND(&unreachable);
80 86 : { Unreachable(); }
81 86 : BIND(&done);
82 172 : return var_typed_map.value();
83 : }
84 :
85 : // The byte_offset can be higher than Smi range, in which case to perform the
86 : // pointer arithmetic necessary to calculate external_pointer, converting
87 : // byte_offset to an intptr is more difficult. The max byte_offset is 8 * MaxSmi
88 : // on the particular platform. 32 bit platforms are self-limiting, because we
89 : // can't allocate an array bigger than our 32-bit arithmetic range anyway. 64
90 : // bit platforms could theoretically have an offset up to 2^35 - 1, so we may
91 : // need to convert the float heap number to an intptr.
92 86 : compiler::Node* TypedArrayBuiltinsAssembler::CalculateExternalPointer(
93 : Node* backing_store, Node* byte_offset) {
94 86 : return IntPtrAdd(backing_store, ChangeNumberToIntPtr(byte_offset));
95 : }
96 :
97 : // Setup the TypedArray which is under construction.
98 : // - Set the length.
99 : // - Set the byte_offset.
100 : // - Set the byte_length.
101 : // - Set EmbedderFields to 0.
102 86 : void TypedArrayBuiltinsAssembler::SetupTypedArray(Node* holder, Node* length,
103 : Node* byte_offset,
104 : Node* byte_length) {
105 : CSA_ASSERT(this, IsJSTypedArray(holder));
106 : CSA_ASSERT(this, TaggedIsSmi(length));
107 : CSA_ASSERT(this, IsNumber(byte_offset));
108 : CSA_ASSERT(this, IsNumber(byte_length));
109 :
110 86 : StoreObjectField(holder, JSTypedArray::kLengthOffset, length);
111 86 : StoreObjectField(holder, JSArrayBufferView::kByteOffsetOffset, byte_offset);
112 86 : StoreObjectField(holder, JSArrayBufferView::kByteLengthOffset, byte_length);
113 258 : for (int offset = JSTypedArray::kSize;
114 : offset < JSTypedArray::kSizeWithEmbedderFields; offset += kPointerSize) {
115 172 : StoreObjectField(holder, offset, SmiConstant(Smi::kZero));
116 : }
117 86 : }
118 :
119 : // Attach an off-heap buffer to a TypedArray.
120 86 : void TypedArrayBuiltinsAssembler::AttachBuffer(Node* holder, Node* buffer,
121 : Node* map, Node* length,
122 : Node* byte_offset) {
123 : CSA_ASSERT(this, IsJSTypedArray(holder));
124 : CSA_ASSERT(this, IsJSArrayBuffer(buffer));
125 : CSA_ASSERT(this, IsMap(map));
126 : CSA_ASSERT(this, TaggedIsSmi(length));
127 : CSA_ASSERT(this, IsNumber(byte_offset));
128 :
129 86 : StoreObjectField(holder, JSArrayBufferView::kBufferOffset, buffer);
130 :
131 86 : Node* elements = Allocate(FixedTypedArrayBase::kHeaderSize);
132 86 : StoreMapNoWriteBarrier(elements, map);
133 86 : StoreObjectFieldNoWriteBarrier(elements, FixedArray::kLengthOffset, length);
134 : StoreObjectFieldNoWriteBarrier(
135 86 : elements, FixedTypedArrayBase::kBasePointerOffset, SmiConstant(0));
136 :
137 : Node* backing_store = LoadObjectField(
138 86 : buffer, JSArrayBuffer::kBackingStoreOffset, MachineType::Pointer());
139 :
140 86 : Node* external_pointer = CalculateExternalPointer(backing_store, byte_offset);
141 : StoreObjectFieldNoWriteBarrier(
142 : elements, FixedTypedArrayBase::kExternalPointerOffset, external_pointer,
143 86 : MachineType::PointerRepresentation());
144 :
145 86 : StoreObjectField(holder, JSObject::kElementsOffset, elements);
146 86 : }
147 :
148 172 : TF_BUILTIN(TypedArrayInitializeWithBuffer, TypedArrayBuiltinsAssembler) {
149 : Node* holder = Parameter(Descriptor::kHolder);
150 : Node* length = Parameter(Descriptor::kLength);
151 : Node* buffer = Parameter(Descriptor::kBuffer);
152 : Node* element_size = Parameter(Descriptor::kElementSize);
153 : Node* byte_offset = Parameter(Descriptor::kByteOffset);
154 :
155 : CSA_ASSERT(this, IsJSTypedArray(holder));
156 : CSA_ASSERT(this, TaggedIsSmi(length));
157 : CSA_ASSERT(this, IsJSArrayBuffer(buffer));
158 : CSA_ASSERT(this, TaggedIsSmi(element_size));
159 : CSA_ASSERT(this, IsNumber(byte_offset));
160 :
161 43 : Node* fixed_typed_map = LoadMapForType(holder);
162 :
163 : // SmiMul returns a heap number in case of Smi overflow.
164 43 : Node* byte_length = SmiMul(length, element_size);
165 : CSA_ASSERT(this, IsNumber(byte_length));
166 :
167 43 : SetupTypedArray(holder, length, byte_offset, byte_length);
168 43 : AttachBuffer(holder, buffer, fixed_typed_map, length, byte_offset);
169 43 : Return(UndefinedConstant());
170 43 : }
171 :
172 172 : TF_BUILTIN(TypedArrayInitialize, TypedArrayBuiltinsAssembler) {
173 : Node* holder = Parameter(Descriptor::kHolder);
174 : Node* length = Parameter(Descriptor::kLength);
175 : Node* element_size = Parameter(Descriptor::kElementSize);
176 : Node* initialize = Parameter(Descriptor::kInitialize);
177 : Node* context = Parameter(Descriptor::kContext);
178 :
179 : CSA_ASSERT(this, IsJSTypedArray(holder));
180 : CSA_ASSERT(this, TaggedIsPositiveSmi(length));
181 : CSA_ASSERT(this, TaggedIsPositiveSmi(element_size));
182 : CSA_ASSERT(this, IsBoolean(initialize));
183 :
184 43 : Node* byte_offset = SmiConstant(0);
185 :
186 : static const int32_t fta_base_data_offset =
187 : FixedTypedArrayBase::kDataOffset - kHeapObjectTag;
188 :
189 43 : Label setup_holder(this), allocate_on_heap(this), aligned(this),
190 43 : allocate_elements(this), allocate_off_heap(this),
191 43 : allocate_off_heap_no_init(this), attach_buffer(this), done(this);
192 86 : VARIABLE(var_total_size, MachineType::PointerRepresentation());
193 :
194 : // SmiMul returns a heap number in case of Smi overflow.
195 43 : Node* byte_length = SmiMul(length, element_size);
196 : CSA_ASSERT(this, IsNumber(byte_length));
197 :
198 43 : SetupTypedArray(holder, length, byte_offset, byte_length);
199 :
200 43 : Node* fixed_typed_map = LoadMapForType(holder);
201 43 : GotoIf(TaggedIsNotSmi(byte_length), &allocate_off_heap);
202 : GotoIf(SmiGreaterThan(byte_length,
203 : SmiConstant(FLAG_typed_array_max_size_in_heap)),
204 43 : &allocate_off_heap);
205 43 : Goto(&allocate_on_heap);
206 :
207 43 : BIND(&allocate_on_heap);
208 : {
209 : CSA_ASSERT(this, TaggedIsPositiveSmi(byte_length));
210 : // Allocate a new ArrayBuffer and initialize it with empty properties and
211 : // elements.
212 43 : Node* native_context = LoadNativeContext(context);
213 : Node* map =
214 43 : LoadContextElement(native_context, Context::ARRAY_BUFFER_MAP_INDEX);
215 43 : Node* empty_fixed_array = LoadRoot(Heap::kEmptyFixedArrayRootIndex);
216 :
217 43 : Node* buffer = Allocate(JSArrayBuffer::kSizeWithEmbedderFields);
218 43 : StoreMapNoWriteBarrier(buffer, map);
219 : StoreObjectFieldNoWriteBarrier(buffer, JSArray::kPropertiesOffset,
220 43 : empty_fixed_array);
221 : StoreObjectFieldNoWriteBarrier(buffer, JSArray::kElementsOffset,
222 43 : empty_fixed_array);
223 : // Setup the ArrayBuffer.
224 : // - Set BitField to 0.
225 : // - Set IsExternal and IsNeuterable bits of BitFieldSlot.
226 : // - Set the byte_length field to byte_length.
227 : // - Set backing_store to null/Smi(0).
228 : // - Set all embedder fields to Smi(0).
229 : StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBitFieldSlot,
230 43 : SmiConstant(Smi::kZero));
231 : int32_t bitfield_value = (1 << JSArrayBuffer::IsExternal::kShift) |
232 : (1 << JSArrayBuffer::IsNeuterable::kShift);
233 : StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBitFieldOffset,
234 : Int32Constant(bitfield_value),
235 43 : MachineRepresentation::kWord32);
236 :
237 : StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kByteLengthOffset,
238 43 : byte_length);
239 : StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBackingStoreOffset,
240 43 : SmiConstant(Smi::kZero));
241 129 : for (int i = 0; i < v8::ArrayBuffer::kEmbedderFieldCount; i++) {
242 86 : int offset = JSArrayBuffer::kSize + i * kPointerSize;
243 86 : StoreObjectFieldNoWriteBarrier(buffer, offset, SmiConstant(Smi::kZero));
244 : }
245 :
246 43 : StoreObjectField(holder, JSArrayBufferView::kBufferOffset, buffer);
247 :
248 : // Check the alignment.
249 : GotoIf(SmiEqual(SmiMod(element_size, SmiConstant(kObjectAlignment)),
250 : SmiConstant(0)),
251 43 : &aligned);
252 :
253 : // Fix alignment if needed.
254 : DCHECK_EQ(0, FixedTypedArrayBase::kHeaderSize & kObjectAlignmentMask);
255 : Node* aligned_header_size =
256 43 : IntPtrConstant(FixedTypedArrayBase::kHeaderSize + kObjectAlignmentMask);
257 43 : Node* size = IntPtrAdd(SmiToWord(byte_length), aligned_header_size);
258 43 : var_total_size.Bind(WordAnd(size, IntPtrConstant(~kObjectAlignmentMask)));
259 43 : Goto(&allocate_elements);
260 : }
261 :
262 43 : BIND(&aligned);
263 : {
264 43 : Node* header_size = IntPtrConstant(FixedTypedArrayBase::kHeaderSize);
265 43 : var_total_size.Bind(IntPtrAdd(SmiToWord(byte_length), header_size));
266 43 : Goto(&allocate_elements);
267 : }
268 :
269 43 : BIND(&allocate_elements);
270 : {
271 : // Allocate a FixedTypedArray and set the length, base pointer and external
272 : // pointer.
273 : CSA_ASSERT(this, IsRegularHeapObjectSize(var_total_size.value()));
274 :
275 : Node* elements;
276 : int heap_alignment =
277 43 : ElementSizeLog2Of(MachineType::PointerRepresentation());
278 :
279 129 : if (UnalignedLoadSupported(MachineType::Float64(), heap_alignment) &&
280 86 : UnalignedStoreSupported(MachineType::Float64(), heap_alignment)) {
281 43 : elements = AllocateInNewSpace(var_total_size.value());
282 : } else {
283 0 : elements = AllocateInNewSpace(var_total_size.value(), kDoubleAlignment);
284 : }
285 :
286 43 : StoreMapNoWriteBarrier(elements, fixed_typed_map);
287 43 : StoreObjectFieldNoWriteBarrier(elements, FixedArray::kLengthOffset, length);
288 : StoreObjectFieldNoWriteBarrier(
289 43 : elements, FixedTypedArrayBase::kBasePointerOffset, elements);
290 : StoreObjectFieldNoWriteBarrier(elements,
291 : FixedTypedArrayBase::kExternalPointerOffset,
292 : IntPtrConstant(fta_base_data_offset),
293 43 : MachineType::PointerRepresentation());
294 :
295 43 : StoreObjectField(holder, JSObject::kElementsOffset, elements);
296 :
297 43 : GotoIf(IsFalse(initialize), &done);
298 : // Initialize the backing store by filling it with 0s.
299 : Node* backing_store = IntPtrAdd(BitcastTaggedToWord(elements),
300 43 : IntPtrConstant(fta_base_data_offset));
301 : // Call out to memset to perform initialization.
302 : Node* memset =
303 43 : ExternalConstant(ExternalReference::libc_memset_function(isolate()));
304 : CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(),
305 : MachineType::IntPtr(), MachineType::UintPtr(), memset,
306 43 : backing_store, IntPtrConstant(0), SmiToWord(byte_length));
307 43 : Goto(&done);
308 : }
309 :
310 86 : VARIABLE(var_buffer, MachineRepresentation::kTagged);
311 :
312 43 : BIND(&allocate_off_heap);
313 : {
314 43 : GotoIf(IsFalse(initialize), &allocate_off_heap_no_init);
315 :
316 : Node* buffer_constructor = LoadContextElement(
317 43 : LoadNativeContext(context), Context::ARRAY_BUFFER_FUN_INDEX);
318 : var_buffer.Bind(ConstructJS(CodeFactory::Construct(isolate()), context,
319 86 : buffer_constructor, byte_length));
320 43 : Goto(&attach_buffer);
321 : }
322 :
323 43 : BIND(&allocate_off_heap_no_init);
324 : {
325 : Node* buffer_constructor_noinit = LoadContextElement(
326 43 : LoadNativeContext(context), Context::ARRAY_BUFFER_NOINIT_FUN_INDEX);
327 : var_buffer.Bind(CallJS(CodeFactory::Call(isolate()), context,
328 : buffer_constructor_noinit, UndefinedConstant(),
329 86 : byte_length));
330 43 : Goto(&attach_buffer);
331 : }
332 :
333 43 : BIND(&attach_buffer);
334 : {
335 : AttachBuffer(holder, var_buffer.value(), fixed_typed_map, length,
336 43 : byte_offset);
337 43 : Goto(&done);
338 : }
339 :
340 43 : BIND(&done);
341 86 : Return(UndefinedConstant());
342 43 : }
343 :
344 : // ES6 #sec-typedarray-length
345 172 : TF_BUILTIN(TypedArrayConstructByLength, TypedArrayBuiltinsAssembler) {
346 : Node* holder = Parameter(Descriptor::kHolder);
347 : Node* length = Parameter(Descriptor::kLength);
348 : Node* element_size = Parameter(Descriptor::kElementSize);
349 : Node* context = Parameter(Descriptor::kContext);
350 :
351 : CSA_ASSERT(this, IsJSTypedArray(holder));
352 : CSA_ASSERT(this, TaggedIsPositiveSmi(element_size));
353 :
354 43 : Node* initialize = BooleanConstant(true);
355 :
356 : Label invalid_length(this);
357 :
358 43 : length = ToInteger(context, length, CodeStubAssembler::kTruncateMinusZero);
359 : // The maximum length of a TypedArray is MaxSmi().
360 : // Note: this is not per spec, but rather a constraint of our current
361 : // representation (which uses smi's).
362 43 : GotoIf(TaggedIsNotSmi(length), &invalid_length);
363 43 : GotoIf(SmiLessThan(length, SmiConstant(0)), &invalid_length);
364 :
365 : CallBuiltin(Builtins::kTypedArrayInitialize, context, holder, length,
366 43 : element_size, initialize);
367 43 : Return(UndefinedConstant());
368 :
369 43 : BIND(&invalid_length);
370 : {
371 : CallRuntime(Runtime::kThrowRangeError, context,
372 43 : SmiConstant(MessageTemplate::kInvalidTypedArrayLength), length);
373 43 : Unreachable();
374 43 : }
375 43 : }
376 :
377 : // ES6 #sec-typedarray-buffer-byteoffset-length
378 172 : TF_BUILTIN(TypedArrayConstructByArrayBuffer, TypedArrayBuiltinsAssembler) {
379 : Node* holder = Parameter(Descriptor::kHolder);
380 : Node* buffer = Parameter(Descriptor::kBuffer);
381 : Node* byte_offset = Parameter(Descriptor::kByteOffset);
382 : Node* length = Parameter(Descriptor::kLength);
383 : Node* element_size = Parameter(Descriptor::kElementSize);
384 : Node* context = Parameter(Descriptor::kContext);
385 :
386 : CSA_ASSERT(this, IsJSTypedArray(holder));
387 : CSA_ASSERT(this, IsJSArrayBuffer(buffer));
388 : CSA_ASSERT(this, TaggedIsPositiveSmi(element_size));
389 :
390 43 : VARIABLE(new_byte_length, MachineRepresentation::kTagged, SmiConstant(0));
391 86 : VARIABLE(offset, MachineRepresentation::kTagged, SmiConstant(0));
392 :
393 43 : Label start_offset_error(this, Label::kDeferred),
394 43 : byte_length_error(this, Label::kDeferred),
395 43 : invalid_offset_error(this, Label::kDeferred);
396 43 : Label offset_is_smi(this), offset_not_smi(this, Label::kDeferred),
397 43 : check_length(this), call_init(this), invalid_length(this),
398 43 : length_undefined(this), length_defined(this);
399 :
400 43 : Callable add = CodeFactory::Add(isolate());
401 43 : Callable div = CodeFactory::Divide(isolate());
402 43 : Callable equal = CodeFactory::Equal(isolate());
403 43 : Callable greater_than = CodeFactory::GreaterThan(isolate());
404 43 : Callable less_than = CodeFactory::LessThan(isolate());
405 43 : Callable mod = CodeFactory::Modulus(isolate());
406 43 : Callable sub = CodeFactory::Subtract(isolate());
407 :
408 43 : GotoIf(IsUndefined(byte_offset), &check_length);
409 :
410 : offset.Bind(
411 43 : ToInteger(context, byte_offset, CodeStubAssembler::kTruncateMinusZero));
412 43 : Branch(TaggedIsSmi(offset.value()), &offset_is_smi, &offset_not_smi);
413 :
414 : // Check that the offset is a multiple of the element size.
415 43 : BIND(&offset_is_smi);
416 : {
417 43 : GotoIf(SmiEqual(offset.value(), SmiConstant(0)), &check_length);
418 43 : GotoIf(SmiLessThan(offset.value(), SmiConstant(0)), &invalid_length);
419 43 : Node* remainder = SmiMod(offset.value(), element_size);
420 : Branch(SmiEqual(remainder, SmiConstant(0)), &check_length,
421 43 : &start_offset_error);
422 : }
423 43 : BIND(&offset_not_smi);
424 : {
425 : GotoIf(IsTrue(CallStub(less_than, context, offset.value(), SmiConstant(0))),
426 43 : &invalid_length);
427 43 : Node* remainder = CallStub(mod, context, offset.value(), element_size);
428 : // Remainder can be a heap number.
429 : Branch(IsTrue(CallStub(equal, context, remainder, SmiConstant(0))),
430 43 : &check_length, &start_offset_error);
431 : }
432 :
433 43 : BIND(&check_length);
434 : // TODO(petermarshall): Throw on detached typedArray.
435 43 : Branch(IsUndefined(length), &length_undefined, &length_defined);
436 :
437 43 : BIND(&length_undefined);
438 : {
439 : Node* buffer_byte_length =
440 43 : LoadObjectField(buffer, JSArrayBuffer::kByteLengthOffset);
441 :
442 43 : Node* remainder = CallStub(mod, context, buffer_byte_length, element_size);
443 : // Remainder can be a heap number.
444 : GotoIf(IsFalse(CallStub(equal, context, remainder, SmiConstant(0))),
445 43 : &byte_length_error);
446 :
447 : new_byte_length.Bind(
448 43 : CallStub(sub, context, buffer_byte_length, offset.value()));
449 :
450 : Branch(IsTrue(CallStub(less_than, context, new_byte_length.value(),
451 : SmiConstant(0))),
452 43 : &invalid_offset_error, &call_init);
453 : }
454 :
455 43 : BIND(&length_defined);
456 : {
457 43 : Node* new_length = ToSmiIndex(length, context, &invalid_length);
458 43 : new_byte_length.Bind(SmiMul(new_length, element_size));
459 : // Reading the byte length must come after the ToIndex operation, which
460 : // could cause the buffer to become detached.
461 : Node* buffer_byte_length =
462 43 : LoadObjectField(buffer, JSArrayBuffer::kByteLengthOffset);
463 :
464 43 : Node* end = CallStub(add, context, offset.value(), new_byte_length.value());
465 :
466 : Branch(IsTrue(CallStub(greater_than, context, end, buffer_byte_length)),
467 43 : &invalid_length, &call_init);
468 : }
469 :
470 43 : BIND(&call_init);
471 : {
472 : Node* new_length =
473 43 : CallStub(div, context, new_byte_length.value(), element_size);
474 : // Force the result into a Smi, or throw a range error if it doesn't fit.
475 43 : new_length = ToSmiIndex(new_length, context, &invalid_length);
476 :
477 : CallBuiltin(Builtins::kTypedArrayInitializeWithBuffer, context, holder,
478 43 : new_length, buffer, element_size, offset.value());
479 43 : Return(UndefinedConstant());
480 : }
481 :
482 43 : BIND(&invalid_offset_error);
483 : {
484 : CallRuntime(Runtime::kThrowRangeError, context,
485 43 : SmiConstant(MessageTemplate::kInvalidOffset), byte_offset);
486 43 : Unreachable();
487 : }
488 :
489 43 : BIND(&start_offset_error);
490 : {
491 43 : Node* holder_map = LoadMap(holder);
492 : Node* problem_string = HeapConstant(
493 86 : factory()->NewStringFromAsciiChecked("start offset", TENURED));
494 : CallRuntime(Runtime::kThrowInvalidTypedArrayAlignment, context, holder_map,
495 43 : problem_string);
496 :
497 43 : Unreachable();
498 : }
499 :
500 43 : BIND(&byte_length_error);
501 : {
502 43 : Node* holder_map = LoadMap(holder);
503 : Node* problem_string = HeapConstant(
504 86 : factory()->NewStringFromAsciiChecked("byte length", TENURED));
505 : CallRuntime(Runtime::kThrowInvalidTypedArrayAlignment, context, holder_map,
506 43 : problem_string);
507 :
508 43 : Unreachable();
509 : }
510 :
511 43 : BIND(&invalid_length);
512 : {
513 : CallRuntime(Runtime::kThrowRangeError, context,
514 43 : SmiConstant(MessageTemplate::kInvalidTypedArrayLength), length);
515 43 : Unreachable();
516 43 : }
517 43 : }
518 :
519 86 : compiler::Node* TypedArrayBuiltinsAssembler::LoadDataPtr(Node* typed_array) {
520 : CSA_ASSERT(this, IsJSTypedArray(typed_array));
521 86 : Node* elements = LoadElements(typed_array);
522 : CSA_ASSERT(this, IsFixedTypedArray(elements));
523 : Node* base_pointer = BitcastTaggedToWord(
524 86 : LoadObjectField(elements, FixedTypedArrayBase::kBasePointerOffset));
525 : Node* external_pointer = BitcastTaggedToWord(
526 86 : LoadObjectField(elements, FixedTypedArrayBase::kExternalPointerOffset));
527 86 : return IntPtrAdd(base_pointer, external_pointer);
528 : }
529 :
530 0 : compiler::Node* TypedArrayBuiltinsAssembler::ByteLengthIsValid(
531 : Node* byte_length) {
532 0 : Label smi(this), done(this);
533 0 : VARIABLE(is_valid, MachineRepresentation::kWord32);
534 0 : GotoIf(TaggedIsSmi(byte_length), &smi);
535 :
536 : CSA_ASSERT(this, IsHeapNumber(byte_length));
537 0 : Node* float_value = LoadHeapNumberValue(byte_length);
538 : Node* max_byte_length_double =
539 0 : Float64Constant(FixedTypedArrayBase::kMaxByteLength);
540 0 : is_valid.Bind(Float64LessThanOrEqual(float_value, max_byte_length_double));
541 0 : Goto(&done);
542 :
543 0 : BIND(&smi);
544 0 : Node* max_byte_length = IntPtrConstant(FixedTypedArrayBase::kMaxByteLength);
545 0 : is_valid.Bind(UintPtrLessThanOrEqual(SmiUntag(byte_length), max_byte_length));
546 0 : Goto(&done);
547 :
548 0 : BIND(&done);
549 0 : return is_valid.value();
550 : }
551 :
552 172 : TF_BUILTIN(TypedArrayConstructByArrayLike, TypedArrayBuiltinsAssembler) {
553 : Node* holder = Parameter(Descriptor::kHolder);
554 : Node* array_like = Parameter(Descriptor::kArrayLike);
555 : Node* initial_length = Parameter(Descriptor::kLength);
556 : Node* element_size = Parameter(Descriptor::kElementSize);
557 : CSA_ASSERT(this, TaggedIsSmi(element_size));
558 : Node* context = Parameter(Descriptor::kContext);
559 :
560 43 : Node* initialize = BooleanConstant(false);
561 :
562 43 : Label invalid_length(this), fill(this), fast_copy(this);
563 :
564 : // The caller has looked up length on array_like, which is observable.
565 43 : Node* length = ToSmiLength(initial_length, context, &invalid_length);
566 :
567 : CallBuiltin(Builtins::kTypedArrayInitialize, context, holder, length,
568 43 : element_size, initialize);
569 43 : GotoIf(SmiNotEqual(length, SmiConstant(0)), &fill);
570 43 : Return(UndefinedConstant());
571 :
572 43 : BIND(&fill);
573 43 : Node* holder_kind = LoadMapElementsKind(LoadMap(holder));
574 43 : Node* source_kind = LoadMapElementsKind(LoadMap(array_like));
575 43 : GotoIf(Word32Equal(holder_kind, source_kind), &fast_copy);
576 :
577 : // Copy using the elements accessor.
578 : CallRuntime(Runtime::kTypedArrayCopyElements, context, holder, array_like,
579 43 : length);
580 43 : Return(UndefinedConstant());
581 :
582 43 : BIND(&fast_copy);
583 : {
584 43 : Node* holder_data_ptr = LoadDataPtr(holder);
585 43 : Node* source_data_ptr = LoadDataPtr(array_like);
586 :
587 : // Calculate the byte length. We shouldn't be trying to copy if the typed
588 : // array was neutered.
589 : CSA_ASSERT(this, SmiNotEqual(length, SmiConstant(0)));
590 : CSA_ASSERT(this, Word32Equal(IsDetachedBuffer(LoadObjectField(
591 : array_like, JSTypedArray::kBufferOffset)),
592 : Int32Constant(0)));
593 :
594 43 : Node* byte_length = SmiMul(length, element_size);
595 : CSA_ASSERT(this, ByteLengthIsValid(byte_length));
596 43 : Node* byte_length_intptr = ChangeNumberToIntPtr(byte_length);
597 : CSA_ASSERT(this, UintPtrLessThanOrEqual(
598 : byte_length_intptr,
599 : IntPtrConstant(FixedTypedArrayBase::kMaxByteLength)));
600 :
601 : Node* memcpy =
602 43 : ExternalConstant(ExternalReference::libc_memcpy_function(isolate()));
603 : CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(),
604 : MachineType::Pointer(), MachineType::UintPtr(), memcpy,
605 43 : holder_data_ptr, source_data_ptr, byte_length_intptr);
606 43 : Return(UndefinedConstant());
607 : }
608 :
609 43 : BIND(&invalid_length);
610 : {
611 : CallRuntime(Runtime::kThrowRangeError, context,
612 : SmiConstant(MessageTemplate::kInvalidTypedArrayLength),
613 43 : initial_length);
614 43 : Unreachable();
615 43 : }
616 43 : }
617 :
618 129 : void TypedArrayBuiltinsAssembler::GenerateTypedArrayPrototypeGetter(
619 : Node* context, Node* receiver, const char* method_name, int object_offset) {
620 : // Check if the {receiver} is actually a JSTypedArray.
621 129 : Label receiver_is_incompatible(this, Label::kDeferred);
622 129 : GotoIf(TaggedIsSmi(receiver), &receiver_is_incompatible);
623 : GotoIfNot(HasInstanceType(receiver, JS_TYPED_ARRAY_TYPE),
624 129 : &receiver_is_incompatible);
625 :
626 : // Check if the {receiver}'s JSArrayBuffer was neutered.
627 : Node* receiver_buffer =
628 129 : LoadObjectField(receiver, JSTypedArray::kBufferOffset);
629 129 : Label if_receiverisneutered(this, Label::kDeferred);
630 129 : GotoIf(IsDetachedBuffer(receiver_buffer), &if_receiverisneutered);
631 129 : Return(LoadObjectField(receiver, object_offset));
632 :
633 129 : BIND(&if_receiverisneutered);
634 : {
635 : // The {receiver}s buffer was neutered, default to zero.
636 129 : Return(SmiConstant(0));
637 : }
638 :
639 129 : BIND(&receiver_is_incompatible);
640 : {
641 : // The {receiver} is not a valid JSTypedArray.
642 : CallRuntime(Runtime::kThrowIncompatibleMethodReceiver, context,
643 : HeapConstant(
644 : factory()->NewStringFromAsciiChecked(method_name, TENURED)),
645 258 : receiver);
646 129 : Unreachable();
647 129 : }
648 129 : }
649 :
650 : // ES6 #sec-get-%typedarray%.prototype.bytelength
651 172 : TF_BUILTIN(TypedArrayPrototypeByteLength, TypedArrayBuiltinsAssembler) {
652 : Node* context = Parameter(Descriptor::kContext);
653 : Node* receiver = Parameter(Descriptor::kReceiver);
654 : GenerateTypedArrayPrototypeGetter(context, receiver,
655 : "get TypedArray.prototype.byteLength",
656 43 : JSTypedArray::kByteLengthOffset);
657 43 : }
658 :
659 : // ES6 #sec-get-%typedarray%.prototype.byteoffset
660 172 : TF_BUILTIN(TypedArrayPrototypeByteOffset, TypedArrayBuiltinsAssembler) {
661 : Node* context = Parameter(Descriptor::kContext);
662 : Node* receiver = Parameter(Descriptor::kReceiver);
663 : GenerateTypedArrayPrototypeGetter(context, receiver,
664 : "get TypedArray.prototype.byteOffset",
665 43 : JSTypedArray::kByteOffsetOffset);
666 43 : }
667 :
668 : // ES6 #sec-get-%typedarray%.prototype.length
669 172 : TF_BUILTIN(TypedArrayPrototypeLength, TypedArrayBuiltinsAssembler) {
670 : Node* context = Parameter(Descriptor::kContext);
671 : Node* receiver = Parameter(Descriptor::kReceiver);
672 : GenerateTypedArrayPrototypeGetter(context, receiver,
673 : "get TypedArray.prototype.length",
674 43 : JSTypedArray::kLengthOffset);
675 43 : }
676 :
677 129 : void TypedArrayBuiltinsAssembler::GenerateTypedArrayPrototypeIterationMethod(
678 : Node* context, Node* receiver, const char* method_name,
679 : IterationKind iteration_kind) {
680 129 : Label throw_bad_receiver(this, Label::kDeferred);
681 129 : Label throw_typeerror(this, Label::kDeferred);
682 :
683 129 : GotoIf(TaggedIsSmi(receiver), &throw_bad_receiver);
684 :
685 129 : Node* map = LoadMap(receiver);
686 129 : Node* instance_type = LoadMapInstanceType(map);
687 : GotoIf(Word32NotEqual(instance_type, Int32Constant(JS_TYPED_ARRAY_TYPE)),
688 129 : &throw_bad_receiver);
689 :
690 : // Check if the {receiver}'s JSArrayBuffer was neutered.
691 : Node* receiver_buffer =
692 129 : LoadObjectField(receiver, JSTypedArray::kBufferOffset);
693 129 : Label if_receiverisneutered(this, Label::kDeferred);
694 129 : GotoIf(IsDetachedBuffer(receiver_buffer), &if_receiverisneutered);
695 :
696 : Return(CreateArrayIterator(receiver, map, instance_type, context,
697 129 : iteration_kind));
698 :
699 258 : VARIABLE(var_message, MachineRepresentation::kTagged);
700 129 : BIND(&throw_bad_receiver);
701 129 : var_message.Bind(SmiConstant(MessageTemplate::kNotTypedArray));
702 129 : Goto(&throw_typeerror);
703 :
704 129 : BIND(&if_receiverisneutered);
705 : var_message.Bind(
706 129 : SmiConstant(Smi::FromInt(MessageTemplate::kDetachedOperation)));
707 129 : Goto(&throw_typeerror);
708 :
709 129 : BIND(&throw_typeerror);
710 : {
711 : Node* method_arg = HeapConstant(
712 258 : isolate()->factory()->NewStringFromAsciiChecked(method_name, TENURED));
713 : Node* result = CallRuntime(Runtime::kThrowTypeError, context,
714 129 : var_message.value(), method_arg);
715 129 : Return(result);
716 129 : }
717 129 : }
718 :
719 : // ES6 #sec-%typedarray%.prototype.values
720 172 : TF_BUILTIN(TypedArrayPrototypeValues, TypedArrayBuiltinsAssembler) {
721 : Node* context = Parameter(Descriptor::kContext);
722 : Node* receiver = Parameter(Descriptor::kReceiver);
723 : GenerateTypedArrayPrototypeIterationMethod(context, receiver,
724 : "%TypedArray%.prototype.values()",
725 43 : IterationKind::kValues);
726 43 : }
727 :
728 : // ES6 #sec-%typedarray%.prototype.entries
729 172 : TF_BUILTIN(TypedArrayPrototypeEntries, TypedArrayBuiltinsAssembler) {
730 : Node* context = Parameter(Descriptor::kContext);
731 : Node* receiver = Parameter(Descriptor::kReceiver);
732 : GenerateTypedArrayPrototypeIterationMethod(context, receiver,
733 : "%TypedArray%.prototype.entries()",
734 43 : IterationKind::kEntries);
735 43 : }
736 :
737 : // ES6 #sec-%typedarray%.prototype.keys
738 172 : TF_BUILTIN(TypedArrayPrototypeKeys, TypedArrayBuiltinsAssembler) {
739 : Node* context = Parameter(Descriptor::kContext);
740 : Node* receiver = Parameter(Descriptor::kReceiver);
741 : GenerateTypedArrayPrototypeIterationMethod(
742 43 : context, receiver, "%TypedArray%.prototype.keys()", IterationKind::kKeys);
743 43 : }
744 :
745 : } // namespace internal
746 : } // namespace v8
|