[builtins] Copy array contents using JS in ConstructByArrayLike.
The last CL https://chromium-review.googlesource.com/c/456707/ caused some pretty heavy performance regressions. After experimenting, it seems the easiest and most straight-forward way to copy the elements into the new typed array is to do it in JS. Adds a fast path for typed arrays, where the source typed array has the same elements kind, in which case we can just copy the backing store using memcpy. This CL also removes regression test 319120 which is from a pwn2own vulnerability. The old code path enforced a maximum byte_length that was too low, which this change removes. The length property of the typed array must be a Smi, but the byte_length, which can be up to 8x larger than length for a Float64Array, can be a heap number. We can also re-use some of the logic from ConstructByLength when deciding whether to allocate the buffer on- or off-heap, so that is factored out into InitializeBasedOnLength. We can also re-use the DoInitialize helper instead of calling into the runtime, meaning we can remove InitializeFromArrayLike. BUG=v8:5977,chromium:705503,chromium:705394 Change-Id: I63372652091d4bdf3a9491acef9b4e3ac793a755 Reviewed-on: https://chromium-review.googlesource.com/459621 Reviewed-by: Benedikt Meurer <bmeurer@chromium.org> Commit-Queue: Peter Marshall <petermarshall@chromium.org> Cr-Commit-Position: refs/heads/master@{#44301}
This commit is contained in:
parent
42f285fcbb
commit
a450c18544
@ -1545,8 +1545,17 @@ ExternalReference ExternalReference::libc_memchr_function(Isolate* isolate) {
|
||||
return ExternalReference(Redirect(isolate, FUNCTION_ADDR(libc_memchr)));
|
||||
}
|
||||
|
||||
void* libc_memset(void* string, int character, size_t n) {
|
||||
return memset(string, character, n);
|
||||
void* libc_memcpy(void* dest, const void* src, size_t n) {
|
||||
return memcpy(dest, src, n);
|
||||
}
|
||||
|
||||
ExternalReference ExternalReference::libc_memcpy_function(Isolate* isolate) {
|
||||
return ExternalReference(Redirect(isolate, FUNCTION_ADDR(libc_memcpy)));
|
||||
}
|
||||
|
||||
void* libc_memset(void* dest, int byte, size_t n) {
|
||||
DCHECK_EQ(static_cast<char>(byte), byte);
|
||||
return memset(dest, byte, n);
|
||||
}
|
||||
|
||||
ExternalReference ExternalReference::libc_memset_function(Isolate* isolate) {
|
||||
|
@ -989,6 +989,7 @@ class ExternalReference BASE_EMBEDDED {
|
||||
static ExternalReference ieee754_tanh_function(Isolate* isolate);
|
||||
|
||||
static ExternalReference libc_memchr_function(Isolate* isolate);
|
||||
static ExternalReference libc_memcpy_function(Isolate* isolate);
|
||||
static ExternalReference libc_memset_function(Isolate* isolate);
|
||||
|
||||
static ExternalReference page_flags(Page* page);
|
||||
@ -1085,7 +1086,6 @@ V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream&, ExternalReference);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Utility functions
|
||||
void* libc_memchr(void* string, int character, size_t search_length);
|
||||
|
||||
inline int NumberOfBitsSet(uint32_t x) {
|
||||
unsigned int num_bits_set;
|
||||
|
@ -28,12 +28,17 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
|
||||
void LoadMapAndElementsSize(Node* const array, Variable* typed_map,
|
||||
Variable* size);
|
||||
|
||||
void CalculateExternalPointer(Node* const backing_store,
|
||||
Node* const byte_offset,
|
||||
Variable* external_pointer);
|
||||
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,
|
||||
@ -88,27 +93,9 @@ void TypedArrayBuiltinsAssembler::LoadMapAndElementsSize(Node* const array,
|
||||
// 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.
|
||||
void TypedArrayBuiltinsAssembler::CalculateExternalPointer(
|
||||
Node* const backing_store, Node* const byte_offset,
|
||||
Variable* external_pointer) {
|
||||
Label offset_is_smi(this), offset_not_smi(this), done(this);
|
||||
Branch(TaggedIsSmi(byte_offset), &offset_is_smi, &offset_not_smi);
|
||||
|
||||
Bind(&offset_is_smi);
|
||||
{
|
||||
external_pointer->Bind(IntPtrAdd(backing_store, SmiToWord(byte_offset)));
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
Bind(&offset_not_smi);
|
||||
{
|
||||
Node* heap_number = LoadHeapNumberValue(byte_offset);
|
||||
Node* intrptr_value = ChangeFloat64ToUintPtr(heap_number);
|
||||
external_pointer->Bind(IntPtrAdd(backing_store, intrptr_value));
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
Bind(&done);
|
||||
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,
|
||||
@ -263,15 +250,15 @@ void TypedArrayBuiltinsAssembler::DoInitialize(Node* const holder, Node* length,
|
||||
StoreObjectFieldNoWriteBarrier(
|
||||
elements, FixedTypedArrayBase::kBasePointerOffset, SmiConstant(0));
|
||||
|
||||
Variable external_pointer(this, MachineType::PointerRepresentation());
|
||||
Node* backing_store =
|
||||
LoadObjectField(maybe_buffer, JSArrayBuffer::kBackingStoreOffset,
|
||||
MachineType::Pointer());
|
||||
|
||||
CalculateExternalPointer(backing_store, byte_offset, &external_pointer);
|
||||
Node* external_pointer =
|
||||
CalculateExternalPointer(backing_store, byte_offset);
|
||||
StoreObjectFieldNoWriteBarrier(
|
||||
elements, FixedTypedArrayBase::kExternalPointerOffset,
|
||||
external_pointer.value(), MachineType::PointerRepresentation());
|
||||
elements, FixedTypedArrayBase::kExternalPointerOffset, external_pointer,
|
||||
MachineType::PointerRepresentation());
|
||||
|
||||
StoreObjectField(holder, JSObject::kElementsOffset, elements);
|
||||
Goto(&done);
|
||||
@ -294,6 +281,41 @@ TF_BUILTIN(TypedArrayInitialize, TypedArrayBuiltinsAssembler) {
|
||||
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), do_init(this);
|
||||
|
||||
Variable maybe_buffer(this, 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);
|
||||
Branch(SmiLessThanOrEqual(byte_length,
|
||||
SmiConstant(FLAG_typed_array_max_size_in_heap)),
|
||||
&do_init, &allocate_buffer);
|
||||
|
||||
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(&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.
|
||||
@ -302,12 +324,10 @@ TF_BUILTIN(TypedArrayConstructByLength, TypedArrayBuiltinsAssembler) {
|
||||
Node* element_size = Parameter(Descriptor::kElementSize);
|
||||
Node* context = Parameter(Descriptor::kContext);
|
||||
|
||||
Variable maybe_buffer(this, MachineRepresentation::kTagged);
|
||||
maybe_buffer.Bind(NullConstant());
|
||||
Node* byte_offset = SmiConstant(0);
|
||||
Node* initialize = BooleanConstant(true);
|
||||
|
||||
Label external_buffer(this), call_init(this), invalid_length(this);
|
||||
Label invalid_length(this);
|
||||
|
||||
length = ToInteger(context, length, CodeStubAssembler::kTruncateMinusZero);
|
||||
// The maximum length of a TypedArray is MaxSmi().
|
||||
@ -316,29 +336,9 @@ TF_BUILTIN(TypedArrayConstructByLength, TypedArrayBuiltinsAssembler) {
|
||||
GotoIf(TaggedIsNotSmi(length), &invalid_length);
|
||||
GotoIf(SmiLessThan(length, SmiConstant(0)), &invalid_length);
|
||||
|
||||
// For byte_length < typed_array_max_size_in_heap, we allocate the buffer on
|
||||
// the heap. Otherwise we allocate it externally and attach it.
|
||||
Node* byte_length = SmiMul(length, element_size);
|
||||
GotoIf(TaggedIsNotSmi(byte_length), &external_buffer);
|
||||
Branch(SmiLessThanOrEqual(byte_length,
|
||||
SmiConstant(FLAG_typed_array_max_size_in_heap)),
|
||||
&call_init, &external_buffer);
|
||||
|
||||
Bind(&external_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(&call_init);
|
||||
}
|
||||
|
||||
Bind(&call_init);
|
||||
{
|
||||
DoInitialize(holder, length, maybe_buffer.value(), byte_offset, byte_length,
|
||||
initialize, context);
|
||||
Return(UndefinedConstant());
|
||||
}
|
||||
InitializeBasedOnLength(holder, length, element_size, byte_offset, initialize,
|
||||
context);
|
||||
Return(UndefinedConstant());
|
||||
|
||||
Bind(&invalid_length);
|
||||
{
|
||||
@ -489,6 +489,39 @@ TF_BUILTIN(TypedArrayConstructByArrayBuffer, TypedArrayBuiltinsAssembler) {
|
||||
}
|
||||
}
|
||||
|
||||
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(this, 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);
|
||||
@ -497,31 +530,52 @@ TF_BUILTIN(TypedArrayConstructByArrayLike, TypedArrayBuiltinsAssembler) {
|
||||
CSA_ASSERT(this, TaggedIsSmi(element_size));
|
||||
Node* const context = Parameter(Descriptor::kContext);
|
||||
|
||||
Label call_init(this), call_runtime(this), invalid_length(this);
|
||||
Node* byte_offset = SmiConstant(0);
|
||||
Node* initialize = BooleanConstant(false);
|
||||
|
||||
Label invalid_length(this), fast_copy(this);
|
||||
|
||||
// The caller has looked up length on array_like, which is observable.
|
||||
length = ToSmiLength(length, context, &invalid_length);
|
||||
|
||||
// For byte_length < typed_array_max_size_in_heap, we allocate the buffer on
|
||||
// the heap. Otherwise we allocate it externally and attach it.
|
||||
Node* byte_length = SmiMul(length, element_size);
|
||||
GotoIf(TaggedIsNotSmi(byte_length), &call_runtime);
|
||||
Branch(SmiLessThanOrEqual(byte_length,
|
||||
SmiConstant(FLAG_typed_array_max_size_in_heap)),
|
||||
&call_init, &call_runtime);
|
||||
InitializeBasedOnLength(holder, length, element_size, byte_offset, initialize,
|
||||
context);
|
||||
|
||||
Bind(&call_init);
|
||||
{
|
||||
DoInitialize(holder, length, NullConstant(), SmiConstant(0), byte_length,
|
||||
BooleanConstant(false), context);
|
||||
Return(CallRuntime(Runtime::kTypedArrayCopyElements, context, holder,
|
||||
array_like, length));
|
||||
}
|
||||
Node* holder_kind = LoadMapElementsKind(LoadMap(holder));
|
||||
Node* source_kind = LoadMapElementsKind(LoadMap(array_like));
|
||||
GotoIf(Word32Equal(holder_kind, source_kind), &fast_copy);
|
||||
|
||||
Bind(&call_runtime);
|
||||
// Call to JS to copy the contents of the array in.
|
||||
Callable callable = CodeFactory::Call(isolate());
|
||||
Node* copy_array_contents = LoadContextElement(
|
||||
LoadNativeContext(context), Context::TYPED_ARRAY_SET_FROM_ARRAY_LIKE);
|
||||
CallJS(callable, context, copy_array_contents, UndefinedConstant(), holder,
|
||||
array_like, length, SmiConstant(0));
|
||||
Return(UndefinedConstant());
|
||||
|
||||
Bind(&fast_copy);
|
||||
{
|
||||
Return(CallRuntime(Runtime::kTypedArrayInitializeFromArrayLike, context,
|
||||
holder, array_like, length));
|
||||
Node* holder_data_ptr = LoadDataPtr(holder);
|
||||
Node* source_data_ptr = LoadDataPtr(array_like);
|
||||
|
||||
// When the typed arrays have the same elements kind, their byte_length will
|
||||
// be exactly the same, so we don't need to calculate it. The byte_length
|
||||
// already takes into account the byte_offset, so we don't need to use that
|
||||
// here.
|
||||
Node* byte_length =
|
||||
LoadObjectField(array_like, JSArrayBufferView::kByteLengthOffset);
|
||||
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);
|
||||
|
@ -2741,7 +2741,7 @@ Node* CodeStubAssembler::ToThisString(Node* context, Node* value,
|
||||
return var_value.value();
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::ChangeNumberToFloat64(compiler::Node* value) {
|
||||
Node* CodeStubAssembler::ChangeNumberToFloat64(Node* value) {
|
||||
Variable result(this, MachineRepresentation::kFloat64);
|
||||
Label smi(this);
|
||||
Label done(this, &result);
|
||||
@ -2760,6 +2760,23 @@ Node* CodeStubAssembler::ChangeNumberToFloat64(compiler::Node* value) {
|
||||
return result.value();
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::ChangeNumberToIntPtr(Node* value) {
|
||||
Variable result(this, MachineType::PointerRepresentation());
|
||||
Label smi(this), done(this, &result);
|
||||
GotoIf(TaggedIsSmi(value), &smi);
|
||||
|
||||
CSA_ASSERT(this, IsHeapNumber(value));
|
||||
result.Bind(ChangeFloat64ToUintPtr(LoadHeapNumberValue(value)));
|
||||
Goto(&done);
|
||||
|
||||
Bind(&smi);
|
||||
result.Bind(SmiToWord(value));
|
||||
Goto(&done);
|
||||
|
||||
Bind(&done);
|
||||
return result.value();
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::ToThisValue(Node* context, Node* value,
|
||||
PrimitiveType primitive_type,
|
||||
char const* method_name) {
|
||||
@ -3056,6 +3073,19 @@ Node* CodeStubAssembler::IsJSFunction(Node* object) {
|
||||
return HasInstanceType(object, JS_FUNCTION_TYPE);
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::IsJSTypedArray(Node* object) {
|
||||
return HasInstanceType(object, JS_TYPED_ARRAY_TYPE);
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::IsFixedTypedArray(Node* object) {
|
||||
Node* instance_type = LoadInstanceType(object);
|
||||
return Word32And(
|
||||
Int32GreaterThanOrEqual(instance_type,
|
||||
Int32Constant(FIRST_FIXED_TYPED_ARRAY_TYPE)),
|
||||
Int32LessThanOrEqual(instance_type,
|
||||
Int32Constant(LAST_FIXED_TYPED_ARRAY_TYPE)));
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::StringCharCodeAt(Node* string, Node* index,
|
||||
ParameterMode parameter_mode) {
|
||||
CSA_ASSERT(this, IsString(string));
|
||||
|
@ -676,6 +676,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
Node* ChangeInt32ToTagged(Node* value);
|
||||
Node* ChangeUint32ToTagged(Node* value);
|
||||
Node* ChangeNumberToFloat64(Node* value);
|
||||
Node* ChangeNumberToIntPtr(Node* value);
|
||||
|
||||
// Type conversions.
|
||||
// Throws a TypeError for {method_name} if {value} is not coercible to Object,
|
||||
@ -731,6 +732,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
Node* IsUnseededNumberDictionary(Node* object);
|
||||
Node* IsConstructorMap(Node* map);
|
||||
Node* IsJSFunction(Node* object);
|
||||
Node* IsJSTypedArray(Node* object);
|
||||
Node* IsFixedTypedArray(Node* object);
|
||||
|
||||
// ElementsKind helpers:
|
||||
Node* IsFastElementsKind(Node* elements_kind);
|
||||
|
@ -75,6 +75,8 @@ enum ContextLookupFlags {
|
||||
V(TYPED_ARRAY_CONSTRUCT_BY_LENGTH_INDEX, JSFunction, \
|
||||
typed_array_construct_by_length) \
|
||||
V(TYPED_ARRAY_INITIALIZE_INDEX, JSFunction, typed_array_initialize) \
|
||||
V(TYPED_ARRAY_SET_FROM_ARRAY_LIKE, JSFunction, \
|
||||
typed_array_set_from_array_like) \
|
||||
V(MATH_FLOOR_INDEX, JSFunction, math_floor) \
|
||||
V(MATH_POW_INDEX, JSFunction, math_pow) \
|
||||
V(NEW_PROMISE_CAPABILITY_INDEX, JSFunction, new_promise_capability) \
|
||||
|
@ -234,6 +234,8 @@ void ExternalReferenceTable::AddReferences(Isolate* isolate) {
|
||||
"wasm::call_trap_callback_for_testing");
|
||||
Add(ExternalReference::libc_memchr_function(isolate).address(),
|
||||
"libc_memchr");
|
||||
Add(ExternalReference::libc_memcpy_function(isolate).address(),
|
||||
"libc_memcpy");
|
||||
Add(ExternalReference::libc_memset_function(isolate).address(),
|
||||
"libc_memset");
|
||||
Add(ExternalReference::log_enter_external_function(isolate).address(),
|
||||
|
@ -237,6 +237,9 @@ function TypedArraySetFromArrayLike(target, source, sourceLength, offset) {
|
||||
}
|
||||
}
|
||||
|
||||
%InstallToContext([
|
||||
'typed_array_set_from_array_like', TypedArraySetFromArrayLike]);
|
||||
|
||||
function TypedArraySetFromOverlappingTypedArray(target, source, offset) {
|
||||
var sourceElementSize = source.BYTES_PER_ELEMENT;
|
||||
var targetElementSize = target.BYTES_PER_ELEMENT;
|
||||
|
@ -4379,6 +4379,17 @@ class FixedTypedArrayBase: public FixedArrayBase {
|
||||
|
||||
static const int kDataOffset = kHeaderSize;
|
||||
|
||||
static const int kMaxElementSize = 8;
|
||||
|
||||
#ifdef V8_HOST_ARCH_32_BIT
|
||||
static const size_t kMaxByteLength = std::numeric_limits<size_t>::max();
|
||||
#else
|
||||
static const size_t kMaxByteLength =
|
||||
static_cast<size_t>(Smi::kMaxValue) * kMaxElementSize;
|
||||
#endif // V8_HOST_ARCH_32_BIT
|
||||
|
||||
static const size_t kMaxLength = Smi::kMaxValue;
|
||||
|
||||
class BodyDescriptor;
|
||||
|
||||
inline int size();
|
||||
@ -4426,17 +4437,18 @@ class FixedTypedArray: public FixedTypedArrayBase {
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(FixedTypedArray);
|
||||
};
|
||||
|
||||
#define FIXED_TYPED_ARRAY_TRAITS(Type, type, TYPE, elementType, size) \
|
||||
class Type##ArrayTraits { \
|
||||
public: /* NOLINT */ \
|
||||
typedef elementType ElementType; \
|
||||
static const InstanceType kInstanceType = FIXED_##TYPE##_ARRAY_TYPE; \
|
||||
static const char* Designator() { return #type " array"; } \
|
||||
static inline Handle<Object> ToHandle(Isolate* isolate, \
|
||||
elementType scalar); \
|
||||
static inline elementType defaultValue(); \
|
||||
}; \
|
||||
\
|
||||
#define FIXED_TYPED_ARRAY_TRAITS(Type, type, TYPE, elementType, size) \
|
||||
STATIC_ASSERT(size <= FixedTypedArrayBase::kMaxElementSize); \
|
||||
class Type##ArrayTraits { \
|
||||
public: /* NOLINT */ \
|
||||
typedef elementType ElementType; \
|
||||
static const InstanceType kInstanceType = FIXED_##TYPE##_ARRAY_TYPE; \
|
||||
static const char* Designator() { return #type " array"; } \
|
||||
static inline Handle<Object> ToHandle(Isolate* isolate, \
|
||||
elementType scalar); \
|
||||
static inline elementType defaultValue(); \
|
||||
}; \
|
||||
\
|
||||
typedef FixedTypedArray<Type##ArrayTraits> Fixed##Type##Array;
|
||||
|
||||
TYPED_ARRAYS(FIXED_TYPED_ARRAY_TRAITS)
|
||||
|
@ -42,118 +42,6 @@ RUNTIME_FUNCTION(Runtime_ArrayBufferNeuter) {
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
Object* CopyElements(Isolate* isolate, Handle<JSTypedArray> holder,
|
||||
Handle<JSReceiver> source, size_t length) {
|
||||
ElementsAccessor* holder_accessor = holder->GetElementsAccessor();
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
LookupIterator get_it(isolate, source, i);
|
||||
Handle<Object> element;
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, element,
|
||||
Object::GetProperty(&get_it));
|
||||
// Convert the incoming value to a number for storing into typed arrays.
|
||||
if (!element->IsNumber() && !element->IsUndefined(isolate)) {
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, element,
|
||||
Object::ToNumber(element));
|
||||
}
|
||||
holder_accessor->Set(holder, i, *element);
|
||||
}
|
||||
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_TypedArrayCopyElements) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(3, args.length());
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, source, 1);
|
||||
CONVERT_NUMBER_ARG_HANDLE_CHECKED(length_obj, 2);
|
||||
|
||||
size_t length;
|
||||
CHECK(TryNumberToSize(*length_obj, &length));
|
||||
|
||||
return CopyElements(isolate, holder, source, length);
|
||||
}
|
||||
|
||||
// Initializes a typed array from an array-like object, and its backing store as
|
||||
// well.
|
||||
RUNTIME_FUNCTION(Runtime_TypedArrayInitializeFromArrayLike) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(3, args.length());
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, source, 1);
|
||||
CONVERT_NUMBER_ARG_HANDLE_CHECKED(length_obj, 2);
|
||||
|
||||
ElementsKind fixed_elements_kind = holder->map()->elements_kind();
|
||||
ExternalArrayType array_type =
|
||||
isolate->factory()->GetArrayTypeFromElementsKind(fixed_elements_kind);
|
||||
size_t element_size =
|
||||
isolate->factory()->GetExternalArrayElementSize(array_type);
|
||||
|
||||
Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer();
|
||||
size_t length = 0;
|
||||
if (source->IsJSTypedArray() &&
|
||||
JSTypedArray::cast(*source)->type() == array_type) {
|
||||
length = JSTypedArray::cast(*source)->length_value();
|
||||
} else {
|
||||
CHECK(TryNumberToSize(*length_obj, &length));
|
||||
}
|
||||
|
||||
if ((length > static_cast<unsigned>(Smi::kMaxValue)) ||
|
||||
(length > (kMaxInt / element_size))) {
|
||||
THROW_NEW_ERROR_RETURN_FAILURE(
|
||||
isolate, NewRangeError(MessageTemplate::kInvalidTypedArrayLength));
|
||||
}
|
||||
size_t byte_length = length * element_size;
|
||||
|
||||
DCHECK_EQ(v8::ArrayBufferView::kEmbedderFieldCount,
|
||||
holder->GetEmbedderFieldCount());
|
||||
for (int i = 0; i < v8::ArrayBufferView::kEmbedderFieldCount; i++) {
|
||||
holder->SetEmbedderField(i, Smi::kZero);
|
||||
}
|
||||
|
||||
if (!JSArrayBuffer::SetupAllocatingData(buffer, isolate, byte_length,
|
||||
false)) {
|
||||
THROW_NEW_ERROR_RETURN_FAILURE(
|
||||
isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferLength));
|
||||
}
|
||||
|
||||
holder->set_buffer(*buffer);
|
||||
holder->set_byte_offset(Smi::kZero);
|
||||
Handle<Object> byte_length_obj(
|
||||
isolate->factory()->NewNumberFromSize(byte_length));
|
||||
holder->set_byte_length(*byte_length_obj);
|
||||
length_obj = isolate->factory()->NewNumberFromSize(length);
|
||||
holder->set_length(*length_obj);
|
||||
|
||||
Handle<FixedTypedArrayBase> elements =
|
||||
isolate->factory()->NewFixedTypedArrayWithExternalPointer(
|
||||
static_cast<int>(length), array_type,
|
||||
static_cast<uint8_t*>(buffer->backing_store()));
|
||||
holder->set_elements(*elements);
|
||||
|
||||
// Initialize the backing store. We can use a special path for typed arrays of
|
||||
// the same type, but we need to make sure everything is properly observable
|
||||
// for other types.
|
||||
if (source->IsJSTypedArray()) {
|
||||
Handle<JSTypedArray> typed_array(JSTypedArray::cast(*source));
|
||||
|
||||
if (typed_array->type() == holder->type()) {
|
||||
uint8_t* backing_store =
|
||||
static_cast<uint8_t*>(typed_array->GetBuffer()->backing_store());
|
||||
size_t source_byte_offset = NumberToSize(typed_array->byte_offset());
|
||||
memcpy(buffer->backing_store(), backing_store + source_byte_offset,
|
||||
byte_length);
|
||||
return isolate->heap()->true_value();
|
||||
}
|
||||
}
|
||||
return CopyElements(isolate, holder, source, length);
|
||||
}
|
||||
|
||||
|
||||
#define BUFFER_VIEW_GETTER(Type, getter, accessor) \
|
||||
RUNTIME_FUNCTION(Runtime_##Type##Get##getter) { \
|
||||
HandleScope scope(isolate); \
|
||||
|
@ -617,8 +617,6 @@ namespace internal {
|
||||
#define FOR_EACH_INTRINSIC_TYPEDARRAY(F) \
|
||||
F(ArrayBufferGetByteLength, 1, 1) \
|
||||
F(ArrayBufferNeuter, 1, 1) \
|
||||
F(TypedArrayCopyElements, 3, 1) \
|
||||
F(TypedArrayInitializeFromArrayLike, 3, 1) \
|
||||
F(ArrayBufferViewGetByteLength, 1, 1) \
|
||||
F(ArrayBufferViewGetByteOffset, 1, 1) \
|
||||
F(TypedArrayGetLength, 1, 1) \
|
||||
|
@ -68,11 +68,39 @@ function TestLengthIsMaxSmi(constr) {
|
||||
}, RangeError);
|
||||
}
|
||||
|
||||
function TestOffsetIsUsedRunner(constr, n) {
|
||||
var buffer = new ArrayBuffer(constr.BYTES_PER_ELEMENT * n);
|
||||
|
||||
var whole_ta = new constr(buffer);
|
||||
assertEquals(n, whole_ta.length);
|
||||
for (var i = 0; i < whole_ta.length; i++) {
|
||||
whole_ta[i] = i;
|
||||
}
|
||||
|
||||
var half_ta = new constr(buffer, constr.BYTES_PER_ELEMENT * n / 2);
|
||||
assertEquals(n / 2, half_ta.length);
|
||||
|
||||
var arr = new constr(half_ta);
|
||||
|
||||
assertEquals(n / 2, arr.length);
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
assertEquals(n / 2 + i, arr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function TestOffsetIsUsed(constr, n) {
|
||||
TestOffsetIsUsedRunner(constr, 4);
|
||||
TestOffsetIsUsedRunner(constr, 16);
|
||||
TestOffsetIsUsedRunner(constr, 32);
|
||||
TestOffsetIsUsedRunner(constr, 128);
|
||||
}
|
||||
|
||||
Test(TestConstructSmallObject);
|
||||
Test(TestConstructLargeObject);
|
||||
Test(TestConstructFromArray);
|
||||
Test(TestConstructFromTypedArray);
|
||||
Test(TestLengthIsMaxSmi);
|
||||
Test(TestOffsetIsUsed);
|
||||
|
||||
function Test(func) {
|
||||
func(Uint8Array);
|
||||
|
@ -1,28 +0,0 @@
|
||||
// Copyright 2013 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
assertThrows('x = new Float64Array({length: 0x24924925})');
|
Loading…
Reference in New Issue
Block a user