}
+// 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<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));
--- /dev/null
+// 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)');