blob: 9ccf8a3b85202aa1573d38aa5a431b250d2b762b [file] [log] [blame]
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/builtins/builtins-utils-gen.h"
#include "src/builtins/builtins.h"
#include "src/code-stub-assembler.h"
namespace v8 {
namespace internal {
// -----------------------------------------------------------------------------
// ES6 section 22.2 TypedArray Objects
class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
public:
explicit TypedArrayBuiltinsAssembler(compiler::CodeAssemblerState* state)
: CodeStubAssembler(state) {}
protected:
void GenerateTypedArrayPrototypeGetter(Node* context, Node* receiver,
const char* method_name,
int object_offset);
void GenerateTypedArrayPrototypeIterationMethod(Node* context, Node* receiver,
const char* method_name,
IterationKind iteration_kind);
void LoadMapAndElementsSize(Node* const array, Variable* typed_map,
Variable* size);
Node* CalculateExternalPointer(Node* const backing_store,
Node* const byte_offset);
void DoInitialize(Node* const holder, Node* length, Node* const maybe_buffer,
Node* const byte_offset, Node* byte_length,
Node* const initialize, Node* const context);
void InitializeBasedOnLength(Node* const holder, Node* const length,
Node* const element_size,
Node* const byte_offset, Node* const initialize,
Node* const context);
Node* LoadDataPtr(Node* typed_array);
Node* ByteLengthIsValid(Node* byte_length);
};
void TypedArrayBuiltinsAssembler::LoadMapAndElementsSize(Node* const array,
Variable* typed_map,
Variable* size) {
Label unreachable(this), done(this);
Label uint8_elements(this), uint8_clamped_elements(this), int8_elements(this),
uint16_elements(this), int16_elements(this), uint32_elements(this),
int32_elements(this), float32_elements(this), float64_elements(this);
Label* elements_kind_labels[] = {
&uint8_elements, &uint8_clamped_elements, &int8_elements,
&uint16_elements, &int16_elements, &uint32_elements,
&int32_elements, &float32_elements, &float64_elements};
int32_t elements_kinds[] = {
UINT8_ELEMENTS, UINT8_CLAMPED_ELEMENTS, INT8_ELEMENTS,
UINT16_ELEMENTS, INT16_ELEMENTS, UINT32_ELEMENTS,
INT32_ELEMENTS, FLOAT32_ELEMENTS, FLOAT64_ELEMENTS};
const size_t kTypedElementsKindCount = LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND -
FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND +
1;
DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kinds));
DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kind_labels));
Node* array_map = LoadMap(array);
Node* elements_kind = LoadMapElementsKind(array_map);
Switch(elements_kind, &unreachable, elements_kinds, elements_kind_labels,
kTypedElementsKindCount);
for (int i = 0; i < static_cast<int>(kTypedElementsKindCount); i++) {
Bind(elements_kind_labels[i]);
{
ElementsKind kind = static_cast<ElementsKind>(elements_kinds[i]);
ExternalArrayType type =
isolate()->factory()->GetArrayTypeFromElementsKind(kind);
Handle<Map> map(isolate()->heap()->MapForFixedTypedArray(type));
typed_map->Bind(HeapConstant(map));
size->Bind(SmiConstant(static_cast<int>(
isolate()->factory()->GetExternalArrayElementSize(type))));
Goto(&done);
}
}
BIND(&unreachable);
{ Unreachable(); }
BIND(&done);
}
// The byte_offset can be higher than Smi range, in which case to perform the
// pointer arithmetic necessary to calculate external_pointer, converting
// byte_offset to an intptr is more difficult. The max byte_offset is 8 * MaxSmi
// on the particular platform. 32 bit platforms are self-limiting, because we
// can't allocate an array bigger than our 32-bit arithmetic range anyway. 64
// bit platforms could theoretically have an offset up to 2^35 - 1, so we may
// need to convert the float heap number to an intptr.
compiler::Node* TypedArrayBuiltinsAssembler::CalculateExternalPointer(
Node* const backing_store, Node* const byte_offset) {
return IntPtrAdd(backing_store, ChangeNumberToIntPtr(byte_offset));
}
void TypedArrayBuiltinsAssembler::DoInitialize(Node* const holder, Node* length,
Node* const maybe_buffer,
Node* const byte_offset,
Node* byte_length,
Node* const initialize,
Node* const context) {
static const int32_t fta_base_data_offset =
FixedTypedArrayBase::kDataOffset - kHeapObjectTag;
Label setup_holder(this), alloc_array_buffer(this), aligned(this),
allocate_elements(this), attach_buffer(this), done(this);
VARIABLE(fixed_typed_map, MachineRepresentation::kTagged);
VARIABLE(element_size, MachineRepresentation::kTagged);
VARIABLE(total_size, MachineType::PointerRepresentation());
// Make sure length is a Smi. The caller guarantees this is the case.
length = ToInteger(context, length, CodeStubAssembler::kTruncateMinusZero);
CSA_ASSERT(this, TaggedIsSmi(length));
// byte_length can be -0, get rid of it.
byte_length =
ToInteger(context, byte_length, CodeStubAssembler::kTruncateMinusZero);
GotoIfNot(IsNull(maybe_buffer), &setup_holder);
// If the buffer is null, then we need a Smi byte_length. The caller
// guarantees this is the case, because when byte_length >
// TypedArrayMaxSizeInHeap, a buffer is allocated and passed in here.
CSA_ASSERT(this, TaggedIsSmi(byte_length));
Goto(&setup_holder);
BIND(&setup_holder);
{
LoadMapAndElementsSize(holder, &fixed_typed_map, &element_size);
// Setup the holder (JSArrayBufferView).
// - Set the length.
// - Set the byte_offset.
// - Set the byte_length.
// - Set EmbedderFields to 0.
StoreObjectField(holder, JSTypedArray::kLengthOffset, length);
StoreObjectField(holder, JSArrayBufferView::kByteOffsetOffset, byte_offset);
StoreObjectField(holder, JSArrayBufferView::kByteLengthOffset, byte_length);
for (int offset = JSTypedArray::kSize;
offset < JSTypedArray::kSizeWithEmbedderFields;
offset += kPointerSize) {
StoreObjectField(holder, offset, SmiConstant(Smi::kZero));
}
Branch(IsNull(maybe_buffer), &alloc_array_buffer, &attach_buffer);
}
BIND(&alloc_array_buffer);
{
// Allocate a new ArrayBuffer and initialize it with empty properties and
// elements.
Node* const native_context = LoadNativeContext(context);
Node* const map =
LoadContextElement(native_context, Context::ARRAY_BUFFER_MAP_INDEX);
Node* empty_fixed_array = LoadRoot(Heap::kEmptyFixedArrayRootIndex);
Node* const buffer = Allocate(JSArrayBuffer::kSizeWithEmbedderFields);
StoreMapNoWriteBarrier(buffer, map);
StoreObjectFieldNoWriteBarrier(buffer, JSArray::kPropertiesOffset,
empty_fixed_array);
StoreObjectFieldNoWriteBarrier(buffer, JSArray::kElementsOffset,
empty_fixed_array);
// Setup the ArrayBuffer.
// - Set BitField to 0.
// - Set IsExternal and IsNeuterable bits of BitFieldSlot.
// - Set the byte_length field to byte_length.
// - Set backing_store to null/Smi(0).
// - Set all embedder fields to Smi(0).
StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBitFieldSlot,
SmiConstant(Smi::kZero));
int32_t bitfield_value = (1 << JSArrayBuffer::IsExternal::kShift) |
(1 << JSArrayBuffer::IsNeuterable::kShift);
StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBitFieldOffset,
Int32Constant(bitfield_value),
MachineRepresentation::kWord32);
StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kByteLengthOffset,
byte_length);
StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBackingStoreOffset,
SmiConstant(Smi::kZero));
for (int i = 0; i < v8::ArrayBuffer::kEmbedderFieldCount; i++) {
int offset = JSArrayBuffer::kSize + i * kPointerSize;
StoreObjectFieldNoWriteBarrier(buffer, offset, SmiConstant(Smi::kZero));
}
StoreObjectField(holder, JSArrayBufferView::kBufferOffset, buffer);
// Check the alignment.
GotoIf(SmiEqual(SmiMod(element_size.value(), SmiConstant(kObjectAlignment)),
SmiConstant(0)),
&aligned);
// Fix alignment if needed.
DCHECK_EQ(0, FixedTypedArrayBase::kHeaderSize & kObjectAlignmentMask);
Node* aligned_header_size =
IntPtrConstant(FixedTypedArrayBase::kHeaderSize + kObjectAlignmentMask);
Node* size = IntPtrAdd(SmiToWord(byte_length), aligned_header_size);
total_size.Bind(WordAnd(size, IntPtrConstant(~kObjectAlignmentMask)));
Goto(&allocate_elements);
}
BIND(&aligned);
{
Node* header_size = IntPtrConstant(FixedTypedArrayBase::kHeaderSize);
total_size.Bind(IntPtrAdd(SmiToWord(byte_length), header_size));
Goto(&allocate_elements);
}
BIND(&allocate_elements);
{
// Allocate a FixedTypedArray and set the length, base pointer and external
// pointer.
CSA_ASSERT(this, IsRegularHeapObjectSize(total_size.value()));
Node* elements;
int heap_alignment =
ElementSizeLog2Of(MachineType::PointerRepresentation());
if (UnalignedLoadSupported(MachineType::Float64(), heap_alignment) &&
UnalignedStoreSupported(MachineType::Float64(), heap_alignment)) {
elements = AllocateInNewSpace(total_size.value());
} else {
elements = AllocateInNewSpace(total_size.value(), kDoubleAlignment);
}
StoreMapNoWriteBarrier(elements, fixed_typed_map.value());
StoreObjectFieldNoWriteBarrier(elements, FixedArray::kLengthOffset, length);
StoreObjectFieldNoWriteBarrier(
elements, FixedTypedArrayBase::kBasePointerOffset, elements);
StoreObjectFieldNoWriteBarrier(elements,
FixedTypedArrayBase::kExternalPointerOffset,
IntPtrConstant(fta_base_data_offset),
MachineType::PointerRepresentation());
StoreObjectField(holder, JSObject::kElementsOffset, elements);
GotoIf(IsFalse(initialize), &done);
// Initialize the backing store by filling it with 0s.
Node* backing_store = IntPtrAdd(BitcastTaggedToWord(elements),
IntPtrConstant(fta_base_data_offset));
// Call out to memset to perform initialization.
Node* memset =
ExternalConstant(ExternalReference::libc_memset_function(isolate()));
CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(),
MachineType::IntPtr(), MachineType::UintPtr(), memset,
backing_store, IntPtrConstant(0), SmiToWord(byte_length));
Goto(&done);
}
BIND(&attach_buffer);
{
StoreObjectField(holder, JSArrayBufferView::kBufferOffset, maybe_buffer);
Node* elements = Allocate(FixedTypedArrayBase::kHeaderSize);
StoreMapNoWriteBarrier(elements, fixed_typed_map.value());
StoreObjectFieldNoWriteBarrier(elements, FixedArray::kLengthOffset, length);
StoreObjectFieldNoWriteBarrier(
elements, FixedTypedArrayBase::kBasePointerOffset, SmiConstant(0));
Node* backing_store =
LoadObjectField(maybe_buffer, JSArrayBuffer::kBackingStoreOffset,
MachineType::Pointer());
Node* external_pointer =
CalculateExternalPointer(backing_store, byte_offset);
StoreObjectFieldNoWriteBarrier(
elements, FixedTypedArrayBase::kExternalPointerOffset, external_pointer,
MachineType::PointerRepresentation());
StoreObjectField(holder, JSObject::kElementsOffset, elements);
Goto(&done);
}
BIND(&done);
}
TF_BUILTIN(TypedArrayInitialize, TypedArrayBuiltinsAssembler) {
Node* const holder = Parameter(Descriptor::kHolder);
Node* length = Parameter(Descriptor::kLength);
Node* const maybe_buffer = Parameter(Descriptor::kBuffer);
Node* const byte_offset = Parameter(Descriptor::kByteOffset);
Node* byte_length = Parameter(Descriptor::kByteLength);
Node* const initialize = Parameter(Descriptor::kInitialize);
Node* const context = Parameter(Descriptor::kContext);
DoInitialize(holder, length, maybe_buffer, byte_offset, byte_length,
initialize, context);
Return(UndefinedConstant());
}
// Small buffers with byte_length <= typed_array_max_size_in_heap are allocated
// on the heap, but larger buffer must be externally allocated with the
// ArrayBuffer constructor. This helper allocates the buffer externally if
// necessary, and then calls into DoInitialize, which will allocate small
// on-heap buffers.
void TypedArrayBuiltinsAssembler::InitializeBasedOnLength(
Node* const holder, Node* const length, Node* const element_size,
Node* const byte_offset, Node* const initialize, Node* const context) {
Label allocate_buffer(this), allocate_buffer_noinit(this), do_init(this);
VARIABLE(maybe_buffer, MachineRepresentation::kTagged, NullConstant());
// SmiMul returns a heap number in case of Smi overflow.
Node* byte_length = SmiMul(length, element_size);
GotoIf(TaggedIsNotSmi(byte_length), &allocate_buffer);
GotoIf(SmiLessThanOrEqual(byte_length,
SmiConstant(FLAG_typed_array_max_size_in_heap)),
&do_init);
Branch(IsTrue(initialize), &allocate_buffer, &allocate_buffer_noinit);
BIND(&allocate_buffer);
{
Node* const buffer_constructor = LoadContextElement(
LoadNativeContext(context), Context::ARRAY_BUFFER_FUN_INDEX);
maybe_buffer.Bind(ConstructJS(CodeFactory::Construct(isolate()), context,
buffer_constructor, byte_length));
Goto(&do_init);
}
Bind(&allocate_buffer_noinit);
{
Node* const buffer_constructor_noinit = LoadContextElement(
LoadNativeContext(context), Context::ARRAY_BUFFER_NOINIT_FUN_INDEX);
maybe_buffer.Bind(CallJS(CodeFactory::Call(isolate()), context,
buffer_constructor_noinit, UndefinedConstant(),
byte_length));
Goto(&do_init);
}
Bind(&do_init);
{
DoInitialize(holder, length, maybe_buffer.value(), byte_offset, byte_length,
initialize, context);
}
}
// ES6 #sec-typedarray-length
TF_BUILTIN(TypedArrayConstructByLength, TypedArrayBuiltinsAssembler) {
// We know that holder cannot be an object if this builtin was called.
Node* holder = Parameter(Descriptor::kHolder);
Node* length = Parameter(Descriptor::kLength);
Node* element_size = Parameter(Descriptor::kElementSize);
Node* context = Parameter(Descriptor::kContext);
Node* byte_offset = SmiConstant(0);
Node* initialize = BooleanConstant(true);
Label invalid_length(this);
length = ToInteger(context, length, CodeStubAssembler::kTruncateMinusZero);
// The maximum length of a TypedArray is MaxSmi().
// Note: this is not per spec, but rather a constraint of our current
// representation (which uses smi's).
GotoIf(TaggedIsNotSmi(length), &invalid_length);
GotoIf(SmiLessThan(length, SmiConstant(0)), &invalid_length);
InitializeBasedOnLength(holder, length, element_size, byte_offset, initialize,
context);
Return(UndefinedConstant());
BIND(&invalid_length);
{
CallRuntime(Runtime::kThrowRangeError, context,
SmiConstant(MessageTemplate::kInvalidTypedArrayLength), length);
Unreachable();
}
}
// ES6 #sec-typedarray-buffer-byteoffset-length
TF_BUILTIN(TypedArrayConstructByArrayBuffer, TypedArrayBuiltinsAssembler) {
Node* const holder = Parameter(Descriptor::kHolder);
Node* const buffer = Parameter(Descriptor::kBuffer);
Node* const byte_offset = Parameter(Descriptor::kByteOffset);
Node* const length = Parameter(Descriptor::kLength);
Node* const element_size = Parameter(Descriptor::kElementSize);
CSA_ASSERT(this, TaggedIsSmi(element_size));
Node* const context = Parameter(Descriptor::kContext);
Node* const initialize = BooleanConstant(true);
VARIABLE(new_byte_length, MachineRepresentation::kTagged, SmiConstant(0));
VARIABLE(offset, MachineRepresentation::kTagged, SmiConstant(0));
Label start_offset_error(this, Label::kDeferred),
byte_length_error(this, Label::kDeferred),
invalid_offset_error(this, Label::kDeferred);
Label offset_is_smi(this), offset_not_smi(this, Label::kDeferred),
check_length(this), call_init(this), invalid_length(this),
length_undefined(this), length_defined(this);
Callable add = CodeFactory::Add(isolate());
Callable div = CodeFactory::Divide(isolate());
Callable equal = CodeFactory::Equal(isolate());
Callable greater_than = CodeFactory::GreaterThan(isolate());
Callable less_than = CodeFactory::LessThan(isolate());
Callable mod = CodeFactory::Modulus(isolate());
Callable sub = CodeFactory::Subtract(isolate());
GotoIf(IsUndefined(byte_offset), &check_length);
offset.Bind(
ToInteger(context, byte_offset, CodeStubAssembler::kTruncateMinusZero));
Branch(TaggedIsSmi(offset.value()), &offset_is_smi, &offset_not_smi);
// Check that the offset is a multiple of the element size.
BIND(&offset_is_smi);
{
GotoIf(SmiEqual(offset.value(), SmiConstant(0)), &check_length);
GotoIf(SmiLessThan(offset.value(), SmiConstant(0)), &invalid_length);
Node* remainder = SmiMod(offset.value(), element_size);
Branch(SmiEqual(remainder, SmiConstant(0)), &check_length,
&start_offset_error);
}
BIND(&offset_not_smi);
{
GotoIf(IsTrue(CallStub(less_than, context, offset.value(), SmiConstant(0))),
&invalid_length);
Node* remainder = CallStub(mod, context, offset.value(), element_size);
// Remainder can be a heap number.
Branch(IsTrue(CallStub(equal, context, remainder, SmiConstant(0))),
&check_length, &start_offset_error);
}
BIND(&check_length);
// TODO(petermarshall): Throw on detached typedArray.
Branch(IsUndefined(length), &length_undefined, &length_defined);
BIND(&length_undefined);
{
Node* buffer_byte_length =
LoadObjectField(buffer, JSArrayBuffer::kByteLengthOffset);
Node* remainder = CallStub(mod, context, buffer_byte_length, element_size);
// Remainder can be a heap number.
GotoIf(IsFalse(CallStub(equal, context, remainder, SmiConstant(0))),
&byte_length_error);
new_byte_length.Bind(
CallStub(sub, context, buffer_byte_length, offset.value()));
Branch(IsTrue(CallStub(less_than, context, new_byte_length.value(),
SmiConstant(0))),
&invalid_offset_error, &call_init);
}
BIND(&length_defined);
{
Node* new_length = ToSmiIndex(length, context, &invalid_length);
new_byte_length.Bind(SmiMul(new_length, element_size));
// Reading the byte length must come after the ToIndex operation, which
// could cause the buffer to become detached.
Node* buffer_byte_length =
LoadObjectField(buffer, JSArrayBuffer::kByteLengthOffset);
Node* end = CallStub(add, context, offset.value(), new_byte_length.value());
Branch(IsTrue(CallStub(greater_than, context, end, buffer_byte_length)),
&invalid_length, &call_init);
}
BIND(&call_init);
{
Node* new_length =
CallStub(div, context, new_byte_length.value(), element_size);
// Force the result into a Smi, or throw a range error if it doesn't fit.
new_length = ToSmiIndex(new_length, context, &invalid_length);
DoInitialize(holder, new_length, buffer, offset.value(),
new_byte_length.value(), initialize, context);
Return(UndefinedConstant());
}
BIND(&invalid_offset_error);
{
CallRuntime(Runtime::kThrowRangeError, context,
SmiConstant(MessageTemplate::kInvalidOffset), byte_offset);
Unreachable();
}
BIND(&start_offset_error);
{
Node* holder_map = LoadMap(holder);
Node* problem_string = HeapConstant(
factory()->NewStringFromAsciiChecked("start offset", TENURED));
CallRuntime(Runtime::kThrowInvalidTypedArrayAlignment, context, holder_map,
problem_string);
Unreachable();
}
BIND(&byte_length_error);
{
Node* holder_map = LoadMap(holder);
Node* problem_string = HeapConstant(
factory()->NewStringFromAsciiChecked("byte length", TENURED));
CallRuntime(Runtime::kThrowInvalidTypedArrayAlignment, context, holder_map,
problem_string);
Unreachable();
}
BIND(&invalid_length);
{
CallRuntime(Runtime::kThrowRangeError, context,
SmiConstant(MessageTemplate::kInvalidTypedArrayLength), length);
Unreachable();
}
}
compiler::Node* TypedArrayBuiltinsAssembler::LoadDataPtr(Node* typed_array) {
CSA_ASSERT(this, IsJSTypedArray(typed_array));
Node* elements = LoadElements(typed_array);
CSA_ASSERT(this, IsFixedTypedArray(elements));
Node* base_pointer = BitcastTaggedToWord(
LoadObjectField(elements, FixedTypedArrayBase::kBasePointerOffset));
Node* external_pointer = BitcastTaggedToWord(
LoadObjectField(elements, FixedTypedArrayBase::kExternalPointerOffset));
return IntPtrAdd(base_pointer, external_pointer);
}
compiler::Node* TypedArrayBuiltinsAssembler::ByteLengthIsValid(
Node* byte_length) {
Label smi(this), done(this);
VARIABLE(is_valid, MachineRepresentation::kWord32);
GotoIf(TaggedIsSmi(byte_length), &smi);
CSA_ASSERT(this, IsHeapNumber(byte_length));
Node* float_value = LoadHeapNumberValue(byte_length);
Node* max_byte_length_double =
Float64Constant(FixedTypedArrayBase::kMaxByteLength);
is_valid.Bind(Float64LessThanOrEqual(float_value, max_byte_length_double));
Goto(&done);
BIND(&smi);
Node* max_byte_length = IntPtrConstant(FixedTypedArrayBase::kMaxByteLength);
is_valid.Bind(UintPtrLessThanOrEqual(SmiUntag(byte_length), max_byte_length));
Goto(&done);
BIND(&done);
return is_valid.value();
}
TF_BUILTIN(TypedArrayConstructByArrayLike, TypedArrayBuiltinsAssembler) {
Node* const holder = Parameter(Descriptor::kHolder);
Node* const array_like = Parameter(Descriptor::kArrayLike);
Node* initial_length = Parameter(Descriptor::kLength);
Node* const element_size = Parameter(Descriptor::kElementSize);
CSA_ASSERT(this, TaggedIsSmi(element_size));
Node* const context = Parameter(Descriptor::kContext);
Node* byte_offset = SmiConstant(0);
Node* initialize = BooleanConstant(false);
Label invalid_length(this), fill(this), fast_copy(this);
// The caller has looked up length on array_like, which is observable.
Node* length = ToSmiLength(initial_length, context, &invalid_length);
InitializeBasedOnLength(holder, length, element_size, byte_offset, initialize,
context);
GotoIf(SmiNotEqual(length, SmiConstant(0)), &fill);
Return(UndefinedConstant());
BIND(&fill);
Node* holder_kind = LoadMapElementsKind(LoadMap(holder));
Node* source_kind = LoadMapElementsKind(LoadMap(array_like));
GotoIf(Word32Equal(holder_kind, source_kind), &fast_copy);
// Copy using the elements accessor.
CallRuntime(Runtime::kTypedArrayCopyElements, context, holder, array_like,
length);
Return(UndefinedConstant());
Bind(&fast_copy);
{
Node* holder_data_ptr = LoadDataPtr(holder);
Node* source_data_ptr = LoadDataPtr(array_like);
// Calculate the byte length. We shouldn't be trying to copy if the typed
// array was neutered.
CSA_ASSERT(this, SmiNotEqual(length, SmiConstant(0)));
CSA_ASSERT(this, Word32Equal(IsDetachedBuffer(LoadObjectField(
array_like, JSTypedArray::kBufferOffset)),
Int32Constant(0)));
Node* byte_length = SmiMul(length, element_size);
CSA_ASSERT(this, ByteLengthIsValid(byte_length));
Node* byte_length_intptr = ChangeNumberToIntPtr(byte_length);
CSA_ASSERT(this, UintPtrLessThanOrEqual(
byte_length_intptr,
IntPtrConstant(FixedTypedArrayBase::kMaxByteLength)));
Node* memcpy =
ExternalConstant(ExternalReference::libc_memcpy_function(isolate()));
CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(),
MachineType::Pointer(), MachineType::UintPtr(), memcpy,
holder_data_ptr, source_data_ptr, byte_length_intptr);
Return(UndefinedConstant());
}
BIND(&invalid_length);
{
CallRuntime(Runtime::kThrowRangeError, context,
SmiConstant(MessageTemplate::kInvalidTypedArrayLength),
initial_length);
Unreachable();
}
}
void TypedArrayBuiltinsAssembler::GenerateTypedArrayPrototypeGetter(
Node* context, Node* receiver, const char* method_name, int object_offset) {
// Check if the {receiver} is actually a JSTypedArray.
Label receiver_is_incompatible(this, Label::kDeferred);
GotoIf(TaggedIsSmi(receiver), &receiver_is_incompatible);
GotoIfNot(HasInstanceType(receiver, JS_TYPED_ARRAY_TYPE),
&receiver_is_incompatible);
// Check if the {receiver}'s JSArrayBuffer was neutered.
Node* receiver_buffer =
LoadObjectField(receiver, JSTypedArray::kBufferOffset);
Label if_receiverisneutered(this, Label::kDeferred);
GotoIf(IsDetachedBuffer(receiver_buffer), &if_receiverisneutered);
Return(LoadObjectField(receiver, object_offset));
BIND(&if_receiverisneutered);
{
// The {receiver}s buffer was neutered, default to zero.
Return(SmiConstant(0));
}
BIND(&receiver_is_incompatible);
{
// The {receiver} is not a valid JSTypedArray.
CallRuntime(Runtime::kThrowIncompatibleMethodReceiver, context,
HeapConstant(
factory()->NewStringFromAsciiChecked(method_name, TENURED)),
receiver);
Unreachable();
}
}
// ES6 #sec-get-%typedarray%.prototype.bytelength
TF_BUILTIN(TypedArrayPrototypeByteLength, TypedArrayBuiltinsAssembler) {
Node* context = Parameter(Descriptor::kContext);
Node* receiver = Parameter(Descriptor::kReceiver);
GenerateTypedArrayPrototypeGetter(context, receiver,
"get TypedArray.prototype.byteLength",
JSTypedArray::kByteLengthOffset);
}
// ES6 #sec-get-%typedarray%.prototype.byteoffset
TF_BUILTIN(TypedArrayPrototypeByteOffset, TypedArrayBuiltinsAssembler) {
Node* context = Parameter(Descriptor::kContext);
Node* receiver = Parameter(Descriptor::kReceiver);
GenerateTypedArrayPrototypeGetter(context, receiver,
"get TypedArray.prototype.byteOffset",
JSTypedArray::kByteOffsetOffset);
}
// ES6 #sec-get-%typedarray%.prototype.length
TF_BUILTIN(TypedArrayPrototypeLength, TypedArrayBuiltinsAssembler) {
Node* context = Parameter(Descriptor::kContext);
Node* receiver = Parameter(Descriptor::kReceiver);
GenerateTypedArrayPrototypeGetter(context, receiver,
"get TypedArray.prototype.length",
JSTypedArray::kLengthOffset);
}
void TypedArrayBuiltinsAssembler::GenerateTypedArrayPrototypeIterationMethod(
Node* context, Node* receiver, const char* method_name,
IterationKind iteration_kind) {
Label throw_bad_receiver(this, Label::kDeferred);
Label throw_typeerror(this, Label::kDeferred);
GotoIf(TaggedIsSmi(receiver), &throw_bad_receiver);
Node* map = LoadMap(receiver);
Node* instance_type = LoadMapInstanceType(map);
GotoIf(Word32NotEqual(instance_type, Int32Constant(JS_TYPED_ARRAY_TYPE)),
&throw_bad_receiver);
// Check if the {receiver}'s JSArrayBuffer was neutered.
Node* receiver_buffer =
LoadObjectField(receiver, JSTypedArray::kBufferOffset);
Label if_receiverisneutered(this, Label::kDeferred);
GotoIf(IsDetachedBuffer(receiver_buffer), &if_receiverisneutered);
Return(CreateArrayIterator(receiver, map, instance_type, context,
iteration_kind));
VARIABLE(var_message, MachineRepresentation::kTagged);
BIND(&throw_bad_receiver);
var_message.Bind(SmiConstant(MessageTemplate::kNotTypedArray));
Goto(&throw_typeerror);
BIND(&if_receiverisneutered);
var_message.Bind(
SmiConstant(Smi::FromInt(MessageTemplate::kDetachedOperation)));
Goto(&throw_typeerror);
BIND(&throw_typeerror);
{
Node* method_arg = HeapConstant(
isolate()->factory()->NewStringFromAsciiChecked(method_name, TENURED));
Node* result = CallRuntime(Runtime::kThrowTypeError, context,
var_message.value(), method_arg);
Return(result);
}
}
// ES6 #sec-%typedarray%.prototype.values
TF_BUILTIN(TypedArrayPrototypeValues, TypedArrayBuiltinsAssembler) {
Node* context = Parameter(Descriptor::kContext);
Node* receiver = Parameter(Descriptor::kReceiver);
GenerateTypedArrayPrototypeIterationMethod(context, receiver,
"%TypedArray%.prototype.values()",
IterationKind::kValues);
}
// ES6 #sec-%typedarray%.prototype.entries
TF_BUILTIN(TypedArrayPrototypeEntries, TypedArrayBuiltinsAssembler) {
Node* context = Parameter(Descriptor::kContext);
Node* receiver = Parameter(Descriptor::kReceiver);
GenerateTypedArrayPrototypeIterationMethod(context, receiver,
"%TypedArray%.prototype.entries()",
IterationKind::kEntries);
}
// ES6 #sec-%typedarray%.prototype.keys
TF_BUILTIN(TypedArrayPrototypeKeys, TypedArrayBuiltinsAssembler) {
Node* context = Parameter(Descriptor::kContext);
Node* receiver = Parameter(Descriptor::kReceiver);
GenerateTypedArrayPrototypeIterationMethod(
context, receiver, "%TypedArray%.prototype.keys()", IterationKind::kKeys);
}
} // namespace internal
} // namespace v8