[builtins] Reland of Port TypedArrayInitialize to CodeStubAssembler.
Turbofan is a lot slower than Crankshaft at constructing TypedArrays, because we always go to the C++ builtin. Port the builtin to CSA to improve performance, and to clean up the implementation, which is split across multiple files and pieces at the moment. This CL increases the performance with --future to roughly the same as with crankshaft. BUG=v8:5977 Change-Id: Id0d91a4592de41a3a308846d79bd44a608931762 Reviewed-on: https://chromium-review.googlesource.com/448537 Reviewed-by: Camillo Bruni <cbruni@chromium.org> Commit-Queue: Peter Marshall <petermarshall@chromium.org> Cr-Commit-Position: refs/heads/master@{#43548}
This commit is contained in:
parent
d7cb3cfc91
commit
ff8b1abb1a
@ -1546,6 +1546,14 @@ 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);
|
||||
}
|
||||
|
||||
ExternalReference ExternalReference::libc_memset_function(Isolate* isolate) {
|
||||
return ExternalReference(Redirect(isolate, FUNCTION_ADDR(libc_memset)));
|
||||
}
|
||||
|
||||
ExternalReference ExternalReference::page_flags(Page* page) {
|
||||
return ExternalReference(reinterpret_cast<Address>(page) +
|
||||
MemoryChunk::kFlagsOffset);
|
||||
|
@ -978,6 +978,7 @@ class ExternalReference BASE_EMBEDDED {
|
||||
static ExternalReference ieee754_tanh_function(Isolate* isolate);
|
||||
|
||||
static ExternalReference libc_memchr_function(Isolate* isolate);
|
||||
static ExternalReference libc_memset_function(Isolate* isolate);
|
||||
|
||||
static ExternalReference page_flags(Page* page);
|
||||
|
||||
|
@ -2620,6 +2620,13 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
|
||||
}
|
||||
TYPED_ARRAYS(INSTALL_TYPED_ARRAY)
|
||||
#undef INSTALL_TYPED_ARRAY
|
||||
|
||||
// %typed_array_initialize
|
||||
Handle<JSFunction> typed_array_initialize = SimpleCreateFunction(
|
||||
isolate, factory->NewStringFromAsciiChecked("typedArrayInitialize"),
|
||||
Builtins::kTypedArrayInitialize, 6, false);
|
||||
InstallWithIntrinsicDefaultProto(isolate, typed_array_initialize,
|
||||
Context::TYPED_ARRAY_INITIALIZE_INDEX);
|
||||
}
|
||||
|
||||
{ // -- D a t a V i e w
|
||||
|
@ -21,8 +21,231 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
|
||||
int object_offset);
|
||||
template <IterationKind kIterationKind>
|
||||
void GenerateTypedArrayPrototypeIterationMethod(const char* method_name);
|
||||
|
||||
void LoadMapAndElementsSize(Node* array, Variable* typed_map, Variable* size);
|
||||
};
|
||||
|
||||
void TypedArrayBuiltinsAssembler::LoadMapAndElementsSize(Node* 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);
|
||||
}
|
||||
|
||||
TF_BUILTIN(TypedArrayInitialize, TypedArrayBuiltinsAssembler) {
|
||||
Node* holder = Parameter(1);
|
||||
Node* length = Parameter(2);
|
||||
Node* maybe_buffer = Parameter(3);
|
||||
Node* byte_offset = Parameter(4);
|
||||
Node* byte_length = Parameter(5);
|
||||
Node* initialize = Parameter(6);
|
||||
Node* context = Parameter(9);
|
||||
|
||||
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(this, MachineRepresentation::kTagged);
|
||||
Variable element_size(this, MachineRepresentation::kTagged);
|
||||
Variable total_size(this, 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_offset =
|
||||
ToInteger(context, byte_offset, CodeStubAssembler::kTruncateMinusZero);
|
||||
CSA_ASSERT(this, TaggedIsSmi(byte_offset));
|
||||
|
||||
// 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 InternalFields 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::kSizeWithInternalFields;
|
||||
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* buffer = Allocate(JSArrayBuffer::kSizeWithInternalFields);
|
||||
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 internal 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::kInternalFieldCount; 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 = Allocate(total_size.value());
|
||||
|
||||
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 = IntPtrAdd(backing_store, SmiToWord(byte_offset));
|
||||
StoreObjectFieldNoWriteBarrier(
|
||||
elements, FixedTypedArrayBase::kExternalPointerOffset, external_pointer,
|
||||
MachineType::PointerRepresentation());
|
||||
|
||||
StoreObjectField(holder, JSObject::kElementsOffset, elements);
|
||||
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
Bind(&done);
|
||||
{ Return(UndefinedConstant()); }
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// ES6 section 22.2 TypedArray Objects
|
||||
|
||||
|
@ -805,6 +805,7 @@ class Isolate;
|
||||
TFJ(SymbolPrototypeValueOf, 0) \
|
||||
\
|
||||
/* TypedArray */ \
|
||||
TFJ(TypedArrayInitialize, 6) \
|
||||
CPP(TypedArrayPrototypeBuffer) \
|
||||
/* ES6 section 22.2.3.2 get %TypedArray%.prototype.byteLength */ \
|
||||
TFJ(TypedArrayPrototypeByteLength, 0) \
|
||||
|
@ -68,6 +68,7 @@ enum ContextLookupFlags {
|
||||
V(REFLECT_DELETE_PROPERTY_INDEX, JSFunction, reflect_delete_property) \
|
||||
V(SPREAD_ARGUMENTS_INDEX, JSFunction, spread_arguments) \
|
||||
V(SPREAD_ITERABLE_INDEX, JSFunction, spread_iterable) \
|
||||
V(TYPED_ARRAY_INITIALIZE_INDEX, JSFunction, typed_array_initialize) \
|
||||
V(MATH_FLOOR_INDEX, JSFunction, math_floor) \
|
||||
V(MATH_POW_INDEX, JSFunction, math_pow) \
|
||||
V(NEW_PROMISE_CAPABILITY_INDEX, JSFunction, new_promise_capability) \
|
||||
|
@ -233,6 +233,8 @@ void ExternalReferenceTable::AddReferences(Isolate* isolate) {
|
||||
"wasm::call_trap_callback_for_testing");
|
||||
Add(ExternalReference::libc_memchr_function(isolate).address(),
|
||||
"libc_memchr");
|
||||
Add(ExternalReference::libc_memset_function(isolate).address(),
|
||||
"libc_memset");
|
||||
Add(ExternalReference::log_enter_external_function(isolate).address(),
|
||||
"Logger::EnterExternal");
|
||||
Add(ExternalReference::log_leave_external_function(isolate).address(),
|
||||
|
@ -2017,6 +2017,31 @@ Handle<JSSetIterator> Factory::NewJSSetIterator() {
|
||||
JSSetIterator);
|
||||
}
|
||||
|
||||
ExternalArrayType Factory::GetArrayTypeFromElementsKind(ElementsKind kind) {
|
||||
switch (kind) {
|
||||
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
|
||||
case TYPE##_ELEMENTS: \
|
||||
return kExternal##Type##Array;
|
||||
TYPED_ARRAYS(TYPED_ARRAY_CASE)
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return kExternalInt8Array;
|
||||
}
|
||||
#undef TYPED_ARRAY_CASE
|
||||
}
|
||||
|
||||
size_t Factory::GetExternalArrayElementSize(ExternalArrayType type) {
|
||||
switch (type) {
|
||||
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
|
||||
case kExternal##Type##Array: \
|
||||
return size;
|
||||
TYPED_ARRAYS(TYPED_ARRAY_CASE)
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
#undef TYPED_ARRAY_CASE
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
@ -2032,21 +2057,6 @@ ElementsKind GetExternalArrayElementsKind(ExternalArrayType type) {
|
||||
#undef TYPED_ARRAY_CASE
|
||||
}
|
||||
|
||||
|
||||
size_t GetExternalArrayElementSize(ExternalArrayType type) {
|
||||
switch (type) {
|
||||
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
|
||||
case kExternal##Type##Array: \
|
||||
return size;
|
||||
TYPED_ARRAYS(TYPED_ARRAY_CASE)
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
#undef TYPED_ARRAY_CASE
|
||||
}
|
||||
|
||||
|
||||
size_t GetFixedTypedArraysElementSize(ElementsKind kind) {
|
||||
switch (kind) {
|
||||
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
|
||||
@ -2061,20 +2071,6 @@ size_t GetFixedTypedArraysElementSize(ElementsKind kind) {
|
||||
}
|
||||
|
||||
|
||||
ExternalArrayType GetArrayTypeFromElementsKind(ElementsKind kind) {
|
||||
switch (kind) {
|
||||
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
|
||||
case TYPE##_ELEMENTS: \
|
||||
return kExternal##Type##Array;
|
||||
TYPED_ARRAYS(TYPED_ARRAY_CASE)
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return kExternalInt8Array;
|
||||
}
|
||||
#undef TYPED_ARRAY_CASE
|
||||
}
|
||||
|
||||
|
||||
JSFunction* GetTypedArrayFun(ExternalArrayType type, Isolate* isolate) {
|
||||
Context* native_context = isolate->context()->native_context();
|
||||
switch (type) {
|
||||
|
@ -536,6 +536,9 @@ class V8_EXPORT_PRIVATE Factory final {
|
||||
SharedFlag shared = SharedFlag::kNotShared,
|
||||
PretenureFlag pretenure = NOT_TENURED);
|
||||
|
||||
ExternalArrayType GetArrayTypeFromElementsKind(ElementsKind kind);
|
||||
size_t GetExternalArrayElementSize(ExternalArrayType type);
|
||||
|
||||
Handle<JSTypedArray> NewJSTypedArray(ExternalArrayType type,
|
||||
PretenureFlag pretenure = NOT_TENURED);
|
||||
|
||||
|
@ -169,13 +169,14 @@ function NAMEConstructByArrayBuffer(obj, buffer, byteOffset, length) {
|
||||
throw %make_range_error(kInvalidTypedArrayLength);
|
||||
}
|
||||
}
|
||||
%_TypedArrayInitialize(obj, ARRAY_ID, buffer, offset, newByteLength, true);
|
||||
var newLength = newByteLength / ELEMENT_SIZE;
|
||||
%typed_array_initialize(obj, newLength, buffer, offset, newByteLength, true);
|
||||
}
|
||||
|
||||
function NAMEConstructByLength(obj, length) {
|
||||
var l = IS_UNDEFINED(length) ?
|
||||
0 : ToIndex(length, kInvalidTypedArrayLength);
|
||||
if (length > %_MaxSmi()) {
|
||||
if (l > %_MaxSmi()) {
|
||||
// Note: this is not per spec, but rather a constraint of our current
|
||||
// representation (which uses smi's).
|
||||
throw %make_range_error(kInvalidTypedArrayLength);
|
||||
@ -183,9 +184,9 @@ function NAMEConstructByLength(obj, length) {
|
||||
var byteLength = l * ELEMENT_SIZE;
|
||||
if (byteLength > %_TypedArrayMaxSizeInHeap()) {
|
||||
var buffer = new GlobalArrayBuffer(byteLength);
|
||||
%_TypedArrayInitialize(obj, ARRAY_ID, buffer, 0, byteLength, true);
|
||||
%typed_array_initialize(obj, l, buffer, 0, byteLength, true);
|
||||
} else {
|
||||
%_TypedArrayInitialize(obj, ARRAY_ID, null, 0, byteLength, true);
|
||||
%typed_array_initialize(obj, l, null, 0, byteLength, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -198,7 +199,7 @@ function NAMEConstructByArrayLike(obj, arrayLike, length) {
|
||||
var initialized = false;
|
||||
var byteLength = l * ELEMENT_SIZE;
|
||||
if (byteLength <= %_TypedArrayMaxSizeInHeap()) {
|
||||
%_TypedArrayInitialize(obj, ARRAY_ID, null, 0, byteLength, false);
|
||||
%typed_array_initialize(obj, l, null, 0, byteLength, false);
|
||||
} else {
|
||||
initialized =
|
||||
%TypedArrayInitializeFromArrayLike(obj, ARRAY_ID, arrayLike, l);
|
||||
|
@ -822,3 +822,12 @@ function TestNonConfigurableProperties(constructor) {
|
||||
for(i = 0; i < typedArrayConstructors.length; i++) {
|
||||
TestNonConfigurableProperties(typedArrayConstructors[i]);
|
||||
}
|
||||
|
||||
(function TestInitialization() {
|
||||
for (var i = 0; i <= 128; i++) {
|
||||
var arr = new Uint8Array(i);
|
||||
for (var j = 0; j < i; j++) {
|
||||
assertEquals(0, arr[j]);
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
Loading…
Reference in New Issue
Block a user