Handle array construction on native code.
authorsgjesse@chromium.org <sgjesse@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 16 Sep 2009 11:17:57 +0000 (11:17 +0000)
committersgjesse@chromium.org <sgjesse@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 16 Sep 2009 11:17:57 +0000 (11:17 +0000)
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
src/bootstrapper.cc
src/builtins.cc
src/builtins.h
src/ia32/builtins-ia32.cc
src/v8-counters.h
src/x64/builtins-x64.cc
test/mjsunit/array-construtor.js [new file with mode: 0644]

index 920110f..cdea1cb 100644 (file)
@@ -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<Code> 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<Code> 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
index 3ddca3f..5f38485 100644 (file)
@@ -654,6 +654,8 @@ void Genesis::CreateRoots(v8::Handle<v8::ObjectTemplate> 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
index 195fe54..5fe4ba9 100644 (file)
@@ -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.
index 8df767a..141d5b7 100644 (file)
@@ -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
index 7793e49..2db8fbe 100644 (file)
@@ -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, &not_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(&not_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, &not_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(&not_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<Code> 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<Code> generic_construct_stub(code);
+  __ jmp(generic_construct_stub, RelocInfo::CODE_TARGET);
+}
+
+
 static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) {
   __ push(ebp);
   __ mov(ebp, Operand(esp));
index 0b941f6..e360b55 100644 (file)
@@ -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)                         \
index 170a15b..d399a88 100644 (file)
@@ -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<Code> 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<Code> 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 (file)
index 0000000..063ccde
--- /dev/null
@@ -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)');