From 677c043feea6b94812ee7e9f43ef097c787cbfc1 Mon Sep 17 00:00:00 2001 From: "sgjesse@chromium.org" Date: Wed, 16 Sep 2009 11:17:57 +0000 Subject: [PATCH] Handle array construction on native code. The construction of arrays when using the the Array function either as a constructor or a normal function is now handled fully in generated code in most cases. Only when Array is called with one argument which is either negative or abowe JSObject::kInitialMaxFastElementArray (which is currently 1000) or if the allocated object cannot fit in the room left in new space is the runtime system entered. Two new native code built-in functions are added one for normal invocation and one for the construct call. The existing C++ builtin is renamed, but kept. When the normal invocation cannot be handled in generated code the C++ builtin is called. When the construct invocation cannot be handled in native code the generic construct stub is called (which will end up in the C++ builtin through a construct trampoline). One thing that might be changed is preserving esi (constructor function) during the handling of a construct call. We know precisily what function we where calling anyway and can just reload it. This could remove the parameter construct_call to ArrayNativeCode and remove the handling of this from that function. The X64 and ARM implementations are not part of this changelist. Review URL: http://codereview.chromium.org/193125 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2899 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/builtins-arm.cc | 16 ++ src/bootstrapper.cc | 2 + src/builtins.cc | 6 +- src/builtins.h | 11 +- src/ia32/builtins-ia32.cc | 459 +++++++++++++++++++++++++++++++++++++++ src/v8-counters.h | 2 + src/x64/builtins-x64.cc | 16 ++ test/mjsunit/array-construtor.js | 119 ++++++++++ 8 files changed, 626 insertions(+), 5 deletions(-) create mode 100644 test/mjsunit/array-construtor.js diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc index 920110f..cdea1cb 100644 --- a/src/arm/builtins-arm.cc +++ b/src/arm/builtins-arm.cc @@ -51,6 +51,22 @@ void Builtins::Generate_Adaptor(MacroAssembler* masm, CFunctionId id) { } +void Builtins::Generate_ArrayCode(MacroAssembler* masm) { + // Just jump to the generic array code. + Code* code = Builtins::builtin(Builtins::ArrayCodeGeneric); + Handle array_code(code); + __ Jump(array_code, RelocInfo::CODE_TARGET); +} + + +void Builtins::Generate_ArrayConstructCode(MacroAssembler* masm) { + // Just jump to the generic construct code. + Code* code = Builtins::builtin(Builtins::JSConstructStubGeneric); + Handle generic_construct_stub(code); + __ Jump(generic_construct_stub, RelocInfo::CODE_TARGET); +} + + void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- r0 : number of arguments diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 3ddca3f..5f38485 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -654,6 +654,8 @@ void Genesis::CreateRoots(v8::Handle global_template, InstallFunction(global, "Array", JS_ARRAY_TYPE, JSArray::kSize, Top::initial_object_prototype(), Builtins::ArrayCode, true); + array_function->shared()->set_construct_stub( + Builtins::builtin(Builtins::ArrayConstructCode)); array_function->shared()->DontAdaptArguments(); // This seems a bit hackish, but we need to make sure Array.length diff --git a/src/builtins.cc b/src/builtins.cc index 195fe54..5fe4ba9 100644 --- a/src/builtins.cc +++ b/src/builtins.cc @@ -135,7 +135,9 @@ BUILTIN(EmptyFunction) { BUILTIN_END -BUILTIN(ArrayCode) { +BUILTIN(ArrayCodeGeneric) { + Counters::array_function_runtime.Increment(); + JSArray* array; if (CalledAsConstructor()) { array = JSArray::cast(*receiver); @@ -166,7 +168,7 @@ BUILTIN(ArrayCode) { // Take the argument as the length. obj = array->Initialize(0); if (obj->IsFailure()) return obj; - if (args.length() == 2) return array->SetElementsLength(args[1]); + return array->SetElementsLength(args[1]); } // Optimize the case where there are no parameters passed. diff --git a/src/builtins.h b/src/builtins.h index 8df767a..141d5b7 100644 --- a/src/builtins.h +++ b/src/builtins.h @@ -37,7 +37,7 @@ namespace internal { \ V(EmptyFunction) \ \ - V(ArrayCode) \ + V(ArrayCodeGeneric) \ \ V(ArrayPush) \ V(ArrayPop) \ @@ -83,8 +83,10 @@ namespace internal { \ /* Uses KeyedLoadIC_Initialize; must be after in list. */ \ V(FunctionCall, BUILTIN, UNINITIALIZED) \ - V(FunctionApply, BUILTIN, UNINITIALIZED) - + V(FunctionApply, BUILTIN, UNINITIALIZED) \ + \ + V(ArrayCode, BUILTIN, UNINITIALIZED) \ + V(ArrayConstructCode, BUILTIN, UNINITIALIZED) #ifdef ENABLE_DEBUGGER_SUPPORT // Define list of builtins used by the debugger implemented in assembly. @@ -217,6 +219,9 @@ class Builtins : public AllStatic { static void Generate_FunctionCall(MacroAssembler* masm); static void Generate_FunctionApply(MacroAssembler* masm); + + static void Generate_ArrayCode(MacroAssembler* masm); + static void Generate_ArrayConstructCode(MacroAssembler* masm); }; } } // namespace v8::internal diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc index 7793e49..2db8fbe 100644 --- a/src/ia32/builtins-ia32.cc +++ b/src/ia32/builtins-ia32.cc @@ -658,6 +658,465 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { } +// Load the built-in Array function from the current context. +static void GenerateLoadArrayFunction(MacroAssembler* masm, Register result) { + // Load the global context. + __ mov(result, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX))); + __ mov(result, FieldOperand(result, GlobalObject::kGlobalContextOffset)); + // Load the Array function from the global context. + __ mov(result, + Operand(result, Context::SlotOffset(Context::ARRAY_FUNCTION_INDEX))); +} + + +// Number of empty elements to allocate for an empty array. +static const int kPreallocatedArrayElements = 4; + + +// Allocate an empty JSArray. The allocated array is put into the result +// register. If the parameter holes is larger than zero an elements backing +// store is allocated with this size and filled with the hole values. Otherwise +// the elements backing store is set to the empty FixedArray. +static void AllocateEmptyJSArray(MacroAssembler* masm, + Register array_function, + Register result, + Register scratch1, + Register scratch2, + Register scratch3, + int holes, + Label* gc_required) { + ASSERT(holes >= 0); + + // Load the initial map from the array function. + __ mov(scratch1, FieldOperand(array_function, + JSFunction::kPrototypeOrInitialMapOffset)); + + // Allocate the JSArray object together with space for a fixed array with the + // requested elements. + int size = JSArray::kSize; + if (holes > 0) { + size += FixedArray::SizeFor(holes); + } + __ AllocateObjectInNewSpace(size, + result, + scratch2, + scratch3, + gc_required, + TAG_OBJECT); + + // Allocated the JSArray. Now initialize the fields except for the elements + // array. + // result: JSObject + // scratch1: initial map + // scratch2: start of next object + __ mov(FieldOperand(result, JSObject::kMapOffset), scratch1); + __ mov(FieldOperand(result, JSArray::kPropertiesOffset), + Factory::empty_fixed_array()); + // Field JSArray::kElementsOffset is initialized later. + __ mov(FieldOperand(result, JSArray::kLengthOffset), Immediate(0)); + + // If no storage is requested for the elements array just set the empty + // fixed array. + if (holes == 0) { + __ mov(FieldOperand(result, JSArray::kElementsOffset), + Factory::empty_fixed_array()); + return; + } + + // Calculate the location of the elements array and set elements array member + // of the JSArray. + // result: JSObject + // scratch2: start of next object + __ lea(scratch1, Operand(result, JSArray::kSize)); + __ mov(FieldOperand(result, JSArray::kElementsOffset), scratch1); + + // Initialize the FixedArray and fill it with holes. FixedArray length is not + // stored as a smi. + // result: JSObject + // scratch1: elements array + // scratch2: start of next object + __ mov(FieldOperand(scratch1, JSObject::kMapOffset), + Factory::fixed_array_map()); + __ mov(FieldOperand(scratch1, Array::kLengthOffset), Immediate(holes)); + + // Fill the FixedArray with the hole value. Inline the code if short. + if (holes <= 4) { + // Use a scratch register here to have only one reloc info when unfolding + // the loop. + __ mov(scratch3, Factory::the_hole_value()); + for (int i = 0; i < holes; i++) { + __ mov(FieldOperand(scratch1, + FixedArray::kHeaderSize + i * kPointerSize), + scratch3); + } + } else { + Label loop, entry; + __ jmp(&entry); + __ bind(&loop); + __ mov(Operand(scratch1, 0), Factory::the_hole_value()); + __ add(Operand(scratch1), Immediate(kPointerSize)); + __ bind(&entry); + __ cmp(scratch1, Operand(scratch2)); + __ j(below, &loop); + } +} + + +// Allocate a JSArray with the number of elements stored in a register. The +// register array_function holds the built-in Array function and the register +// array_size holds the size of the array as a smi. The allocated array is put +// into the result register and beginning and end of the FixedArray elements +// storage is put into registers elements_array and elements_array_end (see +// below for when that is not the case). If the parameter fill_with_holes is +// true the allocated elements backing store is filled with the hole values +// otherwise it is left uninitialized. When the backing store is filled the +// register elements_array is scratched. +static void AllocateJSArray(MacroAssembler* masm, + Register array_function, // Array function. + Register array_size, // As a smi. + Register result, + Register elements_array, + Register elements_array_end, + Register scratch, + bool fill_with_hole, + Label* gc_required) { + Label not_empty, allocated; + + // Load the initial map from the array function. + __ mov(elements_array, + FieldOperand(array_function, + JSFunction::kPrototypeOrInitialMapOffset)); + + // Check whether an empty sized array is requested. + __ test(array_size, Operand(array_size)); + __ j(not_zero, ¬_empty); + + // If an empty array is requested allocate a small elements array anyway. This + // keeps the code below free of special casing for the empty array. + int size = JSArray::kSize + FixedArray::SizeFor(kPreallocatedArrayElements); + __ AllocateObjectInNewSpace(size, + result, + elements_array_end, + scratch, + gc_required, + TAG_OBJECT); + __ jmp(&allocated); + + // Allocate the JSArray object together with space for a FixedArray with the + // requested elements. + __ bind(¬_empty); + ASSERT(kSmiTagSize == 1 && kSmiTag == 0); + __ AllocateObjectInNewSpace(JSArray::kSize + FixedArray::kHeaderSize, + times_half_pointer_size, // array_size is a smi. + array_size, + result, + elements_array_end, + scratch, + gc_required, + TAG_OBJECT); + + // Allocated the JSArray. Now initialize the fields except for the elements + // array. + // result: JSObject + // elements_array: initial map + // elements_array_end: start of next object + // array_size: size of array (smi) + __ bind(&allocated); + __ mov(FieldOperand(result, JSObject::kMapOffset), elements_array); + __ mov(elements_array, Factory::empty_fixed_array()); + __ mov(FieldOperand(result, JSArray::kPropertiesOffset), elements_array); + // Field JSArray::kElementsOffset is initialized later. + __ mov(FieldOperand(result, JSArray::kLengthOffset), array_size); + + // Calculate the location of the elements array and set elements array member + // of the JSArray. + // result: JSObject + // elements_array_end: start of next object + // array_size: size of array (smi) + __ lea(elements_array, Operand(result, JSArray::kSize)); + __ mov(FieldOperand(result, JSArray::kElementsOffset), elements_array); + + // Initialize the fixed array and fill it with holes. FixedArray length is not + // stored as a smi. + // result: JSObject + // elements_array: elements array + // elements_array_end: start of next object + // array_size: size of array (smi) + ASSERT(kSmiTag == 0); + __ shr(array_size, kSmiTagSize); // Convert from smi to value. + __ mov(FieldOperand(elements_array, JSObject::kMapOffset), + Factory::fixed_array_map()); + Label not_empty_2, fill_array; + __ test(array_size, Operand(array_size)); + __ j(not_zero, ¬_empty_2); + // Length of the FixedArray is the number of pre-allocated elements even + // though the actual JSArray has length 0. + __ mov(FieldOperand(elements_array, Array::kLengthOffset), + Immediate(kPreallocatedArrayElements)); + __ jmp(&fill_array); + __ bind(¬_empty_2); + // For non-empty JSArrays the length of the FixedArray and the JSArray is the + // same. + __ mov(FieldOperand(elements_array, Array::kLengthOffset), array_size); + + // Fill the allocated FixedArray with the hole value if requested. + // result: JSObject + // elements_array: elements array + // elements_array_end: start of next object + __ bind(&fill_array); + if (fill_with_hole) { + Label loop, entry; + __ mov(scratch, Factory::the_hole_value()); + __ lea(elements_array, Operand(elements_array, + FixedArray::kHeaderSize - kHeapObjectTag)); + __ jmp(&entry); + __ bind(&loop); + __ mov(Operand(elements_array, 0), scratch); + __ add(Operand(elements_array), Immediate(kPointerSize)); + __ bind(&entry); + __ cmp(elements_array, Operand(elements_array_end)); + __ j(below, &loop); + } +} + + +// Create a new array for the built-in Array function. This function allocates +// the JSArray object and the FixedArray elements array and initializes these. +// If the Array cannot be constructed in native code the runtime is called. This +// function assumes the following state: +// edi: constructor (built-in Array function) +// eax: argc +// esp[0]: return address +// esp[4]: last argument +// This function is used for both construct and normal calls of Array. Whether +// it is a construct call or not is indicated by the construct_call parameter. +// The only difference between handling a construct call and a normal call is +// that for a construct call the constructor function in edi needs to be +// preserved for entering the generic code. In both cases argc in eax needs to +// be preserved. +static void ArrayNativeCode(MacroAssembler* masm, + bool construct_call, + Label *call_generic_code) { + Label argc_one_or_more, argc_two_or_more, prepare_generic_code_call; + + // Push the constructor and argc. No need to tag argc as a smi, as there will + // be no garbage collection with this on the stack. + int push_count = 0; + if (construct_call) { + push_count++; + __ push(edi); + } + push_count++; + __ push(eax); + + // Check for array construction with zero arguments or one. + __ test(eax, Operand(eax)); + __ j(not_zero, &argc_one_or_more); + + // Handle construction of an empty array. + AllocateEmptyJSArray(masm, + edi, + eax, + ebx, + ecx, + edi, + kPreallocatedArrayElements, + &prepare_generic_code_call); + __ IncrementCounter(&Counters::array_function_native, 1); + __ pop(ebx); + if (construct_call) { + __ pop(edi); + } + __ ret(kPointerSize); + + // Check for one argument. Bail out if argument is not smi or if it is + // negative. + __ bind(&argc_one_or_more); + __ cmp(eax, 1); + __ j(not_equal, &argc_two_or_more); + ASSERT(kSmiTag == 0); + __ test(Operand(esp, (push_count + 1) * kPointerSize), + Immediate(kIntptrSignBit | kSmiTagMask)); + __ j(not_zero, &prepare_generic_code_call); + + // Handle construction of an empty array of a certain size. Get the size from + // the stack and bail out if size is to large to actually allocate an elements + // array. + __ mov(edx, Operand(esp, (push_count + 1) * kPointerSize)); + ASSERT(kSmiTag == 0); + __ cmp(edx, JSObject::kInitialMaxFastElementArray << kSmiTagSize); + __ j(greater_equal, &prepare_generic_code_call); + + // edx: array_size (smi) + // edi: constructor + // esp[0]: argc + // esp[4]: constructor (only if construct_call) + // esp[8]: return address + // esp[C]: argument + AllocateJSArray(masm, + edi, + edx, + eax, + ebx, + ecx, + edi, + true, + &prepare_generic_code_call); + __ IncrementCounter(&Counters::array_function_native, 1); + __ pop(ebx); + if (construct_call) { + __ pop(edi); + } + __ ret(2 * kPointerSize); + + // Handle construction of an array from a list of arguments. + __ bind(&argc_two_or_more); + ASSERT(kSmiTag == 0); + __ shl(eax, kSmiTagSize); // Convet argc to a smi. + // eax: array_size (smi) + // edi: constructor + // esp[0] : argc + // esp[4]: constructor (only if construct_call) + // esp[8] : return address + // esp[C] : last argument + AllocateJSArray(masm, + edi, + eax, + ebx, + ecx, + edx, + edi, + false, + &prepare_generic_code_call); + __ IncrementCounter(&Counters::array_function_native, 1); + __ mov(eax, ebx); + __ pop(ebx); + if (construct_call) { + __ pop(edi); + } + __ push(eax); + // eax: JSArray + // ebx: argc + // edx: elements_array_end (untagged) + // esp[0]: JSArray + // esp[4]: return address + // esp[8]: last argument + + // Location of the last argument + __ lea(edi, Operand(esp, 2 * kPointerSize)); + + // Location of the first array element (fill_with_holes is false, so the + // FixedArray is returned). + __ lea(edx, Operand(ecx, FixedArray::kHeaderSize - kHeapObjectTag)); + + // ebx: argc + // edx: location of the first array element + // edi: location of the last argument + // esp[0]: JSArray + // esp[4]: return address + // esp[8]: last argument + Label loop, entry; + __ mov(ecx, ebx); + __ jmp(&entry); + __ bind(&loop); + __ mov(eax, Operand(edi, ecx, times_pointer_size, 0)); + __ mov(Operand(edx, 0), eax); + __ add(Operand(edx), Immediate(kPointerSize)); + __ bind(&entry); + __ dec(ecx); + __ j(greater_equal, &loop); + + // Remove caller arguments from the stack and return. + // ebx: argc + // esp[0]: JSArray + // esp[4]: return address + // esp[8]: last argument + __ pop(eax); + __ pop(ecx); + __ lea(esp, Operand(esp, ebx, times_pointer_size, 1 * kPointerSize)); + __ push(ecx); + __ ret(0); + + // Restore argc and constructor before running the generic code. + __ bind(&prepare_generic_code_call); + __ pop(eax); + if (construct_call) { + __ pop(edi); + } + __ jmp(call_generic_code); +} + + +void Builtins::Generate_ArrayCode(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : argc + // -- esp[0] : return address + // -- esp[4] : last argument + // ----------------------------------- + Label generic_array_code, one_or_more_arguments, two_or_more_arguments; + + // Get the Array function. + GenerateLoadArrayFunction(masm, edi); + + if (FLAG_debug_code) { + // Initial map for the builtin Array function shoud be a map. + __ mov(ebx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset)); + // Will both indicate a NULL and a Smi. + __ test(ebx, Immediate(kSmiTagMask)); + __ Assert(not_zero, "Unexpected initial map for Array function"); + __ CmpObjectType(ebx, MAP_TYPE, ecx); + __ Assert(equal, "Unexpected initial map for Array function"); + } + + // Run the native code for the Array function called as constructor. + ArrayNativeCode(masm, false, &generic_array_code); + + // Jump to the generic array code in case the specialized code cannot handle + // the construction. + __ bind(&generic_array_code); + Code* code = Builtins::builtin(Builtins::ArrayCodeGeneric); + Handle array_code(code); + __ jmp(array_code, RelocInfo::CODE_TARGET); +} + + +void Builtins::Generate_ArrayConstructCode(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : argc + // -- edi : constructor + // -- esp[0] : return address + // -- esp[4] : last argument + // ----------------------------------- + Label generic_constructor; + + if (FLAG_debug_code) { + // The array construct code is only set for the builtin Array function which + // does always have a map. + GenerateLoadArrayFunction(masm, ebx); + __ cmp(edi, Operand(ebx)); + __ Assert(equal, "Unexpected Array function"); + // Initial map for the builtin Array function should be a map. + __ mov(ebx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset)); + // Will both indicate a NULL and a Smi. + __ test(ebx, Immediate(kSmiTagMask)); + __ Assert(not_zero, "Unexpected initial map for Array function"); + __ CmpObjectType(ebx, MAP_TYPE, ecx); + __ Assert(equal, "Unexpected initial map for Array function"); + } + + // Run the native code for the Array function called as constructor. + ArrayNativeCode(masm, false, &generic_constructor); + + // Jump to the generic construct code in case the specialized code cannot + // handle the construction. + __ bind(&generic_constructor); + GenerateLoadArrayFunction(masm, edi); + Code* code = Builtins::builtin(Builtins::JSConstructStubGeneric); + Handle generic_construct_stub(code); + __ jmp(generic_construct_stub, RelocInfo::CODE_TARGET); +} + + static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) { __ push(ebp); __ mov(ebp, Operand(esp)); diff --git a/src/v8-counters.h b/src/v8-counters.h index 0b941f6..e360b55 100644 --- a/src/v8-counters.h +++ b/src/v8-counters.h @@ -142,6 +142,8 @@ namespace internal { SC(constructed_objects, V8.ConstructedObjects) \ SC(constructed_objects_runtime, V8.ConstructedObjectsRuntime) \ SC(constructed_objects_stub, V8.ConstructedObjectsStub) \ + SC(array_function_runtime, V8.ArrayFunctionRuntime) \ + SC(array_function_native, V8.ArrayFunctionNative) \ SC(for_in, V8.ForIn) \ SC(enum_cache_hits, V8.EnumCacheHits) \ SC(enum_cache_misses, V8.EnumCacheMisses) \ diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc index 170a15b..d399a88 100644 --- a/src/x64/builtins-x64.cc +++ b/src/x64/builtins-x64.cc @@ -452,6 +452,22 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { } +void Builtins::Generate_ArrayCode(MacroAssembler* masm) { + // Just jump to the generic array code. + Code* code = Builtins::builtin(Builtins::ArrayCodeGeneric); + Handle array_code(code); + __ Jump(array_code, RelocInfo::CODE_TARGET); +} + + +void Builtins::Generate_ArrayConstructCode(MacroAssembler* masm) { + // Just jump to the generic construct code. + Code* code = Builtins::builtin(Builtins::JSConstructStubGeneric); + Handle generic_construct_stub(code); + __ Jump(generic_construct_stub, RelocInfo::CODE_TARGET); +} + + void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- rax: number of arguments diff --git a/test/mjsunit/array-construtor.js b/test/mjsunit/array-construtor.js new file mode 100644 index 0000000..063ccde --- /dev/null +++ b/test/mjsunit/array-construtor.js @@ -0,0 +1,119 @@ +// Copyright 2008 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. + + +var loop_count = 5 + + +for (var i = 0; i < loop_count; i++) { + var a = new Array(); + var b = Array(); + assertEquals(0, a.length); + assertEquals(0, b.length); + for (var k = 0; k < 10; k++) { + assertEquals('undefined', typeof a[k]); + assertEquals('undefined', typeof b[k]); + } +} + + +for (var i = 0; i < loop_count; i++) { + for (var j = 0; j < 100; j++) { + var a = new Array(j); + var b = Array(j); + assertEquals(j, a.length); + assertEquals(j, b.length); + for (var k = 0; k < j; k++) { + assertEquals('undefined', typeof a[k]); + assertEquals('undefined', typeof b[k]); + } + } +} + + +for (var i = 0; i < loop_count; i++) { + a = new Array(0, 1); + assertArrayEquals([0, 1], a); + a = new Array(0, 1, 2); + assertArrayEquals([0, 1, 2], a); + a = new Array(0, 1, 2, 3); + assertArrayEquals([0, 1, 2, 3], a); + a = new Array(0, 1, 2, 3, 4); + assertArrayEquals([0, 1, 2, 3, 4], a); + a = new Array(0, 1, 2, 3, 4, 5); + assertArrayEquals([0, 1, 2, 3, 4, 5], a); + a = new Array(0, 1, 2, 3, 4, 5, 6); + assertArrayEquals([0, 1, 2, 3, 4, 5, 6], a); + a = new Array(0, 1, 2, 3, 4, 5, 6, 7); + assertArrayEquals([0, 1, 2, 3, 4, 5, 6, 7], a); + a = new Array(0, 1, 2, 3, 4, 5, 6, 7, 8); + assertArrayEquals([0, 1, 2, 3, 4, 5, 6, 7, 8], a); + a = new Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + assertArrayEquals([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], a); +} + + +function innerArrayLiteral(n) { + var a = new Array(n); + for (var i = 0; i < n; i++) { + a[i] = i * 2 + 7; + } + return a.join(); +} + + +function testConstructOfSizeSize(n) { + var str = innerArrayLiteral(n); + var a = eval('[' + str + ']'); + var b = eval('new Array(' + str + ')') + var c = eval('Array(' + str + ')') + assertEquals(n, a.length); + assertArrayEquals(a, b); + assertArrayEquals(a, c); +} + + +for (var i = 0; i < loop_count; i++) { + // JSObject::kInitialMaxFastElementArray is 10000. + for (var j = 1000; j < 12000; j += 1000) { + testConstructOfSizeSize(j); + } +} + + +for (var i = 0; i < loop_count; i++) { + assertArrayEquals(['xxx'], new Array('xxx')); + assertArrayEquals(['xxx'], Array('xxx')); + assertArrayEquals([true], new Array(true)); + assertArrayEquals([false], Array(false)); + assertArrayEquals([{a:1}], new Array({a:1})); + assertArrayEquals([{b:2}], Array({b:2})); +} + + +assertThrows('new Array(3.14)'); +assertThrows('Array(2.72)'); -- 2.7.4