Commit e8151494 authored by jgruber's avatar jgruber Committed by Commit Bot

[typedarray] Add set fast path for JSArray source arguments

This adds a fast path that avoids the runtime transition for JSArray
source arguments with {packed,holey} {smi,double} elements kinds.

The fast path currently calls straight into C and copies there using
elements accessor logic.

Local tests show a 4x speedup when copying from 1-element JSArrays.
As the source array becomes larger, the time spent copying elements
begins to dominate.

Bug: v8:3590
Change-Id: I05ebe54d7b255d0a76ad46ac11ce7cfd516b8ac8
Reviewed-on: https://chromium-review.googlesource.com/789010
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49634}
parent a2aff67f
......@@ -52,6 +52,7 @@
#include "src/debug/debug.h"
#include "src/deoptimizer.h"
#include "src/disassembler.h"
#include "src/elements.h"
#include "src/execution.h"
#include "src/ic/ic.h"
#include "src/ic/stub-cache.h"
......@@ -1427,6 +1428,13 @@ ExternalReference ExternalReference::jsreceiver_create_identity_hash(
return ExternalReference(Redirect(isolate, FUNCTION_ADDR(f)));
}
ExternalReference
ExternalReference::copy_fast_number_jsarray_elements_to_typed_array(
Isolate* isolate) {
return ExternalReference(Redirect(
isolate, FUNCTION_ADDR(CopyFastNumberJSArrayElementsToTypedArray)));
}
ExternalReference ExternalReference::try_internalize_string_function(
Isolate* isolate) {
return ExternalReference(Redirect(
......
......@@ -988,6 +988,9 @@ class ExternalReference BASE_EMBEDDED {
static ExternalReference get_or_create_hash_raw(Isolate* isolate);
static ExternalReference jsreceiver_create_identity_hash(Isolate* isolate);
static ExternalReference copy_fast_number_jsarray_elements_to_typed_array(
Isolate* isolate);
static ExternalReference page_flags(Page* page);
static ExternalReference ForDeoptEntry(Address entry);
......
......@@ -55,9 +55,20 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
// Fast path for setting a TypedArray (source) onto another TypedArray
// (target) at an element offset. Currently, only handles when the source and
// target types match.
void SetTypedArraySource(TNode<Context> context, TNode<HeapObject> source,
TNode<HeapObject> target, TNode<IntPtrT> offset,
Label* if_not_same_type);
void SetTypedArraySource(TNode<Context> context, TNode<JSTypedArray> source,
TNode<JSTypedArray> target, TNode<IntPtrT> offset,
Label* call_runtime, Label* if_source_too_large);
void SetJSArraySource(TNode<Context> context, TNode<JSArray> source,
TNode<JSTypedArray> target, TNode<IntPtrT> offset,
Label* call_runtime, Label* if_source_too_large);
void CallCMemmove(TNode<IntPtrT> dest_ptr, TNode<IntPtrT> src_ptr,
TNode<IntPtrT> byte_length);
void CallCCopyFastNumberJSArrayElementsToTypedArray(
TNode<Context> context, TNode<JSArray> source, TNode<JSTypedArray> dest,
TNode<IntPtrT> source_length, TNode<IntPtrT> offset);
};
Node* TypedArrayBuiltinsAssembler::LoadMapForType(Node* array) {
......@@ -746,24 +757,19 @@ TNode<IntPtrT> TypedArrayBuiltinsAssembler::GetTypedArrayElementSize(
return element_size;
}
void TypedArrayBuiltinsAssembler::SetTypedArraySource(TNode<Context> context,
TNode<HeapObject> source,
TNode<HeapObject> target,
TNode<IntPtrT> offset,
Label* if_not_same_type) {
CSA_ASSERT(this, IsJSTypedArray(source));
CSA_ASSERT(this, IsJSTypedArray(target));
void TypedArrayBuiltinsAssembler::SetTypedArraySource(
TNode<Context> context, TNode<JSTypedArray> source,
TNode<JSTypedArray> target, TNode<IntPtrT> offset, Label* call_runtime,
Label* if_source_too_large) {
CSA_ASSERT(this, IntPtrGreaterThanOrEqual(offset, IntPtrConstant(0)));
CSA_ASSERT(this,
IntPtrLessThanOrEqual(offset, IntPtrConstant(Smi::kMaxValue)));
Label next(this), if_source_too_large(this, Label::kDeferred);
// TODO(pwong): Widen this fast path. See CopyElementsHandleFromTypedArray for
// the corresponding check in runtime.
TNode<Word32T> source_el_kind = LoadElementsKind(source);
TNode<Word32T> target_el_kind = LoadElementsKind(target);
GotoIfNot(Word32Equal(source_el_kind, target_el_kind), if_not_same_type);
GotoIfNot(Word32Equal(source_el_kind, target_el_kind), call_runtime);
TNode<IntPtrT> source_byte_length =
LoadAndUntagObjectField(source, JSTypedArray::kByteLengthOffset);
......@@ -773,29 +779,84 @@ void TypedArrayBuiltinsAssembler::SetTypedArraySource(TNode<Context> context,
TNode<IntPtrT> required_byte_length =
IntPtrAdd(IntPtrMul(offset, source_el_size), source_byte_length);
GotoIf(IntPtrGreaterThan(required_byte_length, target_byte_length),
&if_source_too_large);
if_source_too_large);
// If source and target are the same TypedArray type.
TNode<IntPtrT> target_el_size = GetTypedArrayElementSize(target_el_kind);
TNode<IntPtrT> target_data_ptr = UncheckedCast<IntPtrT>(LoadDataPtr(target));
TNode<IntPtrT> source_data_ptr = UncheckedCast<IntPtrT>(LoadDataPtr(source));
TNode<IntPtrT> target_start =
IntPtrAdd(target_data_ptr, IntPtrMul(offset, target_el_size));
CallCMemmove(target_start, source_data_ptr, source_byte_length);
}
void TypedArrayBuiltinsAssembler::SetJSArraySource(
TNode<Context> context, TNode<JSArray> source, TNode<JSTypedArray> target,
TNode<IntPtrT> offset, Label* call_runtime, Label* if_source_too_large) {
CSA_ASSERT(this, IsFastJSArray(source, context));
CSA_ASSERT(this, IntPtrGreaterThanOrEqual(offset, IntPtrConstant(0)));
CSA_ASSERT(this,
IntPtrLessThanOrEqual(offset, IntPtrConstant(Smi::kMaxValue)));
TNode<IntPtrT> source_length = SmiUntag(LoadFastJSArrayLength(source));
TNode<IntPtrT> target_length =
LoadAndUntagObjectField(target, JSTypedArray::kLengthOffset);
// Maybe out of bounds?
GotoIf(IntPtrGreaterThan(IntPtrAdd(source_length, offset), target_length),
if_source_too_large);
// Nothing to do if {source} is empty.
Label out(this), fast_c_call(this);
GotoIf(IntPtrEqual(source_length, IntPtrConstant(0)), &out);
// Dispatch based on the source elements kind.
{
TNode<ExternalReference> memmove =
ExternalConstant(ExternalReference::libc_memmove_function(isolate()));
TNode<IntPtrT> target_el_size = GetTypedArrayElementSize(target_el_kind);
TNode<IntPtrT> target_data_ptr =
UncheckedCast<IntPtrT>(LoadDataPtr(target));
TNode<IntPtrT> source_data_ptr =
UncheckedCast<IntPtrT>(LoadDataPtr(source));
TNode<IntPtrT> target_start =
IntPtrAdd(target_data_ptr, IntPtrMul(offset, target_el_size));
CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(),
MachineType::Pointer(), MachineType::UintPtr(), memmove,
target_start, source_data_ptr, source_byte_length);
Goto(&next);
// These are the supported elements kinds in TryCopyElementsFastNumber.
int32_t values[] = {
PACKED_SMI_ELEMENTS, HOLEY_SMI_ELEMENTS, PACKED_DOUBLE_ELEMENTS,
HOLEY_DOUBLE_ELEMENTS,
};
Label* labels[] = {
&fast_c_call, &fast_c_call, &fast_c_call, &fast_c_call,
};
STATIC_ASSERT(arraysize(values) == arraysize(labels));
TNode<Int32T> source_elements_kind = LoadMapElementsKind(LoadMap(source));
Switch(source_elements_kind, call_runtime, values, labels,
arraysize(values));
}
BIND(&if_source_too_large);
ThrowRangeError(context, MessageTemplate::kTypedArraySetSourceTooLarge);
BIND(&fast_c_call);
CallCCopyFastNumberJSArrayElementsToTypedArray(context, source, target,
source_length, offset);
Goto(&out);
BIND(&out);
}
BIND(&next);
void TypedArrayBuiltinsAssembler::CallCMemmove(TNode<IntPtrT> dest_ptr,
TNode<IntPtrT> src_ptr,
TNode<IntPtrT> byte_length) {
TNode<ExternalReference> memmove =
ExternalConstant(ExternalReference::libc_memmove_function(isolate()));
CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(),
MachineType::Pointer(), MachineType::UintPtr(), memmove,
dest_ptr, src_ptr, byte_length);
}
void TypedArrayBuiltinsAssembler::
CallCCopyFastNumberJSArrayElementsToTypedArray(TNode<Context> context,
TNode<JSArray> source,
TNode<JSTypedArray> dest,
TNode<IntPtrT> source_length,
TNode<IntPtrT> offset) {
TNode<ExternalReference> f = ExternalConstant(
ExternalReference::copy_fast_number_jsarray_elements_to_typed_array(
isolate()));
CallCFunction5(MachineType::AnyTagged(), MachineType::AnyTagged(),
MachineType::AnyTagged(), MachineType::AnyTagged(),
MachineType::UintPtr(), MachineType::UintPtr(), f, context,
source, dest, source_length, offset);
}
// ES #sec-get-%typedarray%.prototype.set
......@@ -804,7 +865,9 @@ TF_BUILTIN(TypedArrayPrototypeSet, TypedArrayBuiltinsAssembler) {
CodeStubArguments args(
this, ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount)));
Label if_offset_is_out_of_bounds(this, Label::kDeferred),
Label if_source_is_typed_array(this), if_source_is_fast_jsarray(this),
if_offset_is_out_of_bounds(this, Label::kDeferred),
if_source_too_large(this, Label::kDeferred),
if_receiver_is_neutered(this, Label::kDeferred),
if_receiver_is_not_typedarray(this, Label::kDeferred);
......@@ -833,16 +896,28 @@ TF_BUILTIN(TypedArrayPrototypeSet, TypedArrayBuiltinsAssembler) {
Label call_runtime(this);
TNode<Object> source = args.GetOptionalArgumentValue(0);
GotoIf(TaggedIsSmi(source), &call_runtime);
GotoIfNot(IsJSTypedArray(source), &call_runtime);
GotoIf(IsJSTypedArray(source), &if_source_is_typed_array);
BranchIfFastJSArray(source, context, &if_source_is_fast_jsarray,
&call_runtime);
// Fast path for a typed array source argument.
BIND(&if_source_is_typed_array);
{
CSA_ASSERT(this, IsJSTypedArray(source));
SetTypedArraySource(context, CAST(source), CAST(receiver),
SmiUntag(offset_smi), &call_runtime);
SmiUntag(offset_smi), &call_runtime,
&if_source_too_large);
args.PopAndReturn(UndefinedConstant());
}
// Fast path for a fast JSArray source argument.
BIND(&if_source_is_fast_jsarray);
{
SetJSArraySource(context, CAST(source), CAST(receiver),
SmiUntag(offset_smi), &call_runtime, &if_source_too_large);
args.PopAndReturn(UndefinedConstant());
}
// TODO(pwong): This is an opportunity to add a fast path for fast JS Array.
BIND(&call_runtime);
args.PopAndReturn(CallRuntime(Runtime::kTypedArraySet, context, receiver,
source, offset_smi));
......@@ -850,6 +925,9 @@ TF_BUILTIN(TypedArrayPrototypeSet, TypedArrayBuiltinsAssembler) {
BIND(&if_offset_is_out_of_bounds);
ThrowRangeError(context, MessageTemplate::kTypedArraySetOffsetOutOfBounds);
BIND(&if_source_too_large);
ThrowRangeError(context, MessageTemplate::kTypedArraySetSourceTooLarge);
BIND(&if_receiver_is_neutered);
ThrowTypeError(context, MessageTemplate::kDetachedOperation,
"%TypedArray%.prototype.set");
......
......@@ -1184,6 +1184,16 @@ Node* CodeAssembler::CallCFunction3WithCallerSavedRegisters(
mode);
}
Node* CodeAssembler::CallCFunction5(
MachineType return_type, MachineType arg0_type, MachineType arg1_type,
MachineType arg2_type, MachineType arg3_type, MachineType arg4_type,
Node* function, Node* arg0, Node* arg1, Node* arg2, Node* arg3,
Node* arg4) {
return raw_assembler()->CallCFunction5(
return_type, arg0_type, arg1_type, arg2_type, arg3_type, arg4_type,
function, arg0, arg1, arg2, arg3, arg4);
}
Node* CodeAssembler::CallCFunction6(
MachineType return_type, MachineType arg0_type, MachineType arg1_type,
MachineType arg2_type, MachineType arg3_type, MachineType arg4_type,
......
......@@ -1006,6 +1006,13 @@ class V8_EXPORT_PRIVATE CodeAssembler {
MachineType arg2_type, Node* function, Node* arg0, Node* arg1, Node* arg2,
SaveFPRegsMode mode);
// Call to a C function with five arguments.
Node* CallCFunction5(MachineType return_type, MachineType arg0_type,
MachineType arg1_type, MachineType arg2_type,
MachineType arg3_type, MachineType arg4_type,
Node* function, Node* arg0, Node* arg1, Node* arg2,
Node* arg3, Node* arg4);
// Call to a C function with six arguments.
Node* CallCFunction6(MachineType return_type, MachineType arg0_type,
MachineType arg1_type, MachineType arg2_type,
......
......@@ -301,6 +301,25 @@ Node* RawMachineAssembler::CallCFunction3WithCallerSavedRegisters(
arg0, arg1, arg2);
}
Node* RawMachineAssembler::CallCFunction5(
MachineType return_type, MachineType arg0_type, MachineType arg1_type,
MachineType arg2_type, MachineType arg3_type, MachineType arg4_type,
Node* function, Node* arg0, Node* arg1, Node* arg2, Node* arg3,
Node* arg4) {
MachineSignature::Builder builder(zone(), 1, 5);
builder.AddReturn(return_type);
builder.AddParam(arg0_type);
builder.AddParam(arg1_type);
builder.AddParam(arg2_type);
builder.AddParam(arg3_type);
builder.AddParam(arg4_type);
const CallDescriptor* descriptor =
Linkage::GetSimplifiedCDescriptor(zone(), builder.Build());
return AddNode(common()->Call(descriptor), function, arg0, arg1, arg2, arg3,
arg4);
}
Node* RawMachineAssembler::CallCFunction6(
MachineType return_type, MachineType arg0_type, MachineType arg1_type,
MachineType arg2_type, MachineType arg3_type, MachineType arg4_type,
......
......@@ -778,6 +778,12 @@ class V8_EXPORT_PRIVATE RawMachineAssembler {
MachineType return_type, MachineType arg0_type, MachineType arg1_type,
MachineType arg2_type, Node* function, Node* arg0, Node* arg1, Node* arg2,
SaveFPRegsMode mode = kSaveFPRegs);
// Call to a C function with five arguments.
Node* CallCFunction5(MachineType return_type, MachineType arg0_type,
MachineType arg1_type, MachineType arg2_type,
MachineType arg3_type, MachineType arg4_type,
Node* function, Node* arg0, Node* arg1, Node* arg2,
Node* arg3, Node* arg4);
// Call to a C function with six arguments.
Node* CallCFunction6(MachineType return_type, MachineType arg0_type,
MachineType arg1_type, MachineType arg2_type,
......
......@@ -3269,23 +3269,27 @@ class TypedElementsAccessor
}
}
static bool HoleyPrototypeLookupRequired(Isolate* isolate,
Handle<JSArray> source) {
static bool HoleyPrototypeLookupRequired(Isolate* isolate, Context* context,
JSArray* source) {
DisallowHeapAllocation no_gc;
DisallowJavascriptExecution no_js(isolate);
Object* source_proto = source->map()->prototype();
// Null prototypes are OK - we don't need to do prototype chain lookups on
// them.
if (source_proto->IsNull(isolate)) return false;
if (source_proto->IsJSProxy()) return true;
DCHECK(source_proto->IsJSObject());
if (!isolate->is_initial_array_prototype(JSObject::cast(source_proto))) {
if (!context->is_initial_array_prototype(JSObject::cast(source_proto))) {
return true;
}
return !isolate->IsNoElementsProtectorIntact();
return !isolate->IsNoElementsProtectorIntact(context);
}
static bool TryCopyElementsHandleFastNumber(Handle<JSArray> source,
Handle<JSTypedArray> destination,
size_t length, uint32_t offset) {
static bool TryCopyElementsFastNumber(Context* context, JSArray* source,
JSTypedArray* destination,
size_t length, uint32_t offset) {
Isolate* isolate = source->GetIsolate();
DisallowHeapAllocation no_gc;
DisallowJavascriptExecution no_js(isolate);
......@@ -3298,7 +3302,7 @@ class TypedElementsAccessor
// When the array has the original array prototype, and that prototype has
// not been changed in a way that would affect lookups, we can just convert
// the hole into undefined.
if (HoleyPrototypeLookupRequired(isolate, source)) return false;
if (HoleyPrototypeLookupRequired(isolate, context, source)) return false;
Object* undefined = isolate->heap()->undefined_value();
......@@ -3408,8 +3412,8 @@ class TypedElementsAccessor
// Fast cases for packed numbers kinds where we don't need to allocate.
if (source->IsJSArray()) {
Handle<JSArray> source_array = Handle<JSArray>::cast(source);
if (TryCopyElementsHandleFastNumber(source_array, destination_ta, length,
offset)) {
if (TryCopyElementsFastNumber(isolate->context(), *source_array,
*destination_ta, length, offset)) {
return *isolate->factory()->undefined_value();
}
}
......@@ -4357,6 +4361,27 @@ MaybeHandle<Object> ArrayConstructInitializeElements(Handle<JSArray> array,
return array;
}
void CopyFastNumberJSArrayElementsToTypedArray(Context* context,
JSArray* source,
JSTypedArray* destination,
uintptr_t length,
uintptr_t offset) {
DCHECK(context->IsContext());
DCHECK(source->IsJSArray());
DCHECK(destination->IsJSTypedArray());
switch (destination->GetElementsKind()) {
#define TYPED_ARRAYS_CASE(Type, type, TYPE, ctype, size) \
case TYPE##_ELEMENTS: \
CHECK(Fixed##Type##ElementsAccessor::TryCopyElementsFastNumber( \
context, source, destination, length, static_cast<uint32_t>(offset))); \
break;
TYPED_ARRAYS(TYPED_ARRAYS_CASE)
#undef TYPED_ARRAYS_CASE
default:
UNREACHABLE();
}
}
void ElementsAccessor::InitializeOncePerProcess() {
static ElementsAccessor* accessor_array[] = {
......
......@@ -237,6 +237,14 @@ MUST_USE_RESULT MaybeHandle<Object> ArrayConstructInitializeElements(
Handle<JSArray> array,
Arguments* args);
// Called directly from CSA.
class JSTypedArray;
void CopyFastNumberJSArrayElementsToTypedArray(Context* context,
JSArray* source,
JSTypedArray* destination,
uintptr_t length,
uintptr_t offset);
} // namespace internal
} // namespace v8
......
......@@ -270,6 +270,10 @@ void ExternalReferenceTable::AddReferences(Isolate* isolate) {
"get_or_create_hash_raw");
Add(ExternalReference::jsreceiver_create_identity_hash(isolate).address(),
"jsreceiver_create_identity_hash");
Add(ExternalReference::copy_fast_number_jsarray_elements_to_typed_array(
isolate)
.address(),
"copy_fast_number_jsarray_elements_to_typed_array");
Add(ExternalReference::log_enter_external_function(isolate).address(),
"Logger::EnterExternal");
Add(ExternalReference::log_leave_external_function(isolate).address(),
......
......@@ -3127,16 +3127,17 @@ bool Isolate::IsInAnyContext(Object* object, uint32_t index) {
return false;
}
bool Isolate::IsNoElementsProtectorIntact() {
bool Isolate::IsNoElementsProtectorIntact(Context* context) {
PropertyCell* no_elements_cell = heap()->no_elements_protector();
bool cell_reports_intact =
no_elements_cell->value()->IsSmi() &&
Smi::ToInt(no_elements_cell->value()) == kProtectorValid;
#ifdef DEBUG
Context* native_context = context->native_context();
Map* root_array_map =
raw_native_context()->GetInitialJSArrayMap(GetInitialFastElementsKind());
Context* native_context = context()->native_context();
native_context->GetInitialJSArrayMap(GetInitialFastElementsKind());
JSObject* initial_array_proto = JSObject::cast(
native_context->get(Context::INITIAL_ARRAY_PROTOTYPE_INDEX));
JSObject* initial_object_proto = JSObject::cast(
......@@ -3177,13 +3178,16 @@ bool Isolate::IsNoElementsProtectorIntact() {
PrototypeIterator iter(this, initial_array_proto);
if (iter.IsAtEnd() || iter.GetCurrent() != initial_object_proto) {
DCHECK_EQ(false, cell_reports_intact);
DCHECK(!has_pending_exception());
return cell_reports_intact;
}
iter.Advance();
if (!iter.IsAtEnd()) {
DCHECK_EQ(false, cell_reports_intact);
DCHECK(!has_pending_exception());
return cell_reports_intact;
}
DCHECK(!has_pending_exception());
// Check that the String.prototype hasn't been altered WRT empty elements.
elements = initial_string_proto->elements();
......@@ -3199,12 +3203,15 @@ bool Isolate::IsNoElementsProtectorIntact() {
DCHECK_EQ(false, cell_reports_intact);
return cell_reports_intact;
}
#endif
return cell_reports_intact;
}
bool Isolate::IsNoElementsProtectorIntact() {
return Isolate::IsNoElementsProtectorIntact(context());
}
bool Isolate::IsIsConcatSpreadableLookupChainIntact() {
Cell* is_concat_spreadable_cell = heap()->is_concat_spreadable_protector();
bool is_is_concat_spreadable_set =
......
......@@ -1084,7 +1084,13 @@ class Isolate {
static const int kProtectorInvalid = 0;
inline bool IsArrayConstructorIntact();
// The version with an explicit context parameter can be used when
// Isolate::context is not set up, e.g. when calling directly into C++ from
// CSA.
bool IsNoElementsProtectorIntact(Context* context);
bool IsNoElementsProtectorIntact();
inline bool IsArraySpeciesLookupChainIntact();
bool IsIsConcatSpreadableLookupChainIntact();
bool IsIsConcatSpreadableLookupChainIntact(JSReceiver* receiver);
......
......@@ -900,6 +900,7 @@ RUNTIME_FUNCTION(Runtime_GetWasmRecoveredTrapCount) {
return isolate->heap()->ToBoolean(obj->Has##Name()); \
}
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastElements)
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(SmiElements)
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(ObjectElements)
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(SmiOrObjectElements)
......
......@@ -594,6 +594,7 @@ namespace internal {
F(TraceExit, 1, 1) \
F(HaveSameMap, 2, 1) \
F(InNewSpace, 1, 1) \
F(HasFastElements, 1, 1) \
F(HasSmiElements, 1, 1) \
F(HasObjectElements, 1, 1) \
F(HasSmiOrObjectElements, 1, 1) \
......
......@@ -636,6 +636,73 @@ function TestTypedArraySet() {
};
assertThrows(() => Int8Array.prototype.set.call(1, tmp), TypeError);
assertThrows(() => Int8Array.prototype.set.call([], tmp), TypeError);
// Detached array buffer when converting offset.
{
const xs = new Int8Array(10);
let detached = false;
const offset = {
[Symbol.toPrimitive]() {
%ArrayBufferNeuter(xs.buffer);
detached = true;
return 0;
}
};
assertThrows(() => xs.set(xs, offset), TypeError);
assertEquals(true, detached);
}
// Various offset edge cases.
{
const xs = new Int8Array(10);
assertThrows(() => xs.set(xs, -1), RangeError);
assertThrows(() => xs.set(xs, -1 * 2**64), RangeError);
xs.set(xs, -0.0);
xs.set(xs, 0.0);
xs.set(xs, 0.5);
assertThrows(() => xs.set(xs, 2**64), RangeError);
}
// Exhaustively test elements kind combinations with JSArray source arg.
{
const kSize = 3;
const targets = typedArrayConstructors.map(klass => new klass(kSize));
const sources = [ [0,1,2] // PACKED_SMI
, [0,,2] // HOLEY_SMI
, [0.1,0.2,0.3] // PACKED_DOUBLE
, [0.1,,0.3] // HOLEY_DOUBLE
, [{},{},{}] // PACKED
, [{},,{}] // HOLEY
, [] // DICTIONARY (patched later)
];
// Migrate to DICTIONARY_ELEMENTS.
Object.defineProperty(sources[6], 0, {});
assertTrue(%HasSmiElements(sources[0]));
assertTrue(%HasFastElements(sources[0]) && !%HasHoleyElements(sources[0]));
assertTrue(%HasSmiElements(sources[1]));
assertTrue(%HasFastElements(sources[1]) && %HasHoleyElements(sources[1]));
assertTrue(%HasDoubleElements(sources[2]));
assertTrue(%HasFastElements(sources[2]) && !%HasHoleyElements(sources[2]));
assertTrue(%HasDoubleElements(sources[3]));
assertTrue(%HasFastElements(sources[3]) && %HasHoleyElements(sources[3]));
assertTrue(%HasObjectElements(sources[4]));
assertTrue(%HasFastElements(sources[4]) && !%HasHoleyElements(sources[4]));
assertTrue(%HasObjectElements(sources[4]));
assertTrue(%HasFastElements(sources[4]) && !%HasHoleyElements(sources[4]));
assertTrue(%HasObjectElements(sources[5]));
assertTrue(%HasFastElements(sources[5]) && %HasHoleyElements(sources[5]));
assertTrue(%HasDictionaryElements(sources[6]));
for (const target of targets) {
for (const source of sources) {
target.set(source);
%HeapObjectVerify(target);
%HeapObjectVerify(source);
}
}
}
}
TestTypedArraySet();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment