Revision: 10218
Author:   [email protected]
Date:     Fri Dec  9 00:50:19 2011
Log:      Support Smi->Double->HeapObject transitions in constructed Arrays.

Also several bugs with Smi/double elements handling and make Ensure* routines more flexible.

BUG=none
TEST=test/mjsunit/array-construct-transition.js

Review URL: http://codereview.chromium.org/8820014
http://code.google.com/p/v8/source/detail?r=10218

Added:
 /branches/bleeding_edge/test/mjsunit/array-construct-transition.js
Modified:
 /branches/bleeding_edge/src/arm/builtins-arm.cc
 /branches/bleeding_edge/src/builtins.cc
 /branches/bleeding_edge/src/elements.cc
 /branches/bleeding_edge/src/elements.h
 /branches/bleeding_edge/src/factory.cc
 /branches/bleeding_edge/src/factory.h
 /branches/bleeding_edge/src/ia32/builtins-ia32.cc
 /branches/bleeding_edge/src/objects-inl.h
 /branches/bleeding_edge/src/objects.cc
 /branches/bleeding_edge/src/objects.h
 /branches/bleeding_edge/src/runtime.cc
 /branches/bleeding_edge/src/x64/builtins-x64.cc

=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/array-construct-transition.js Fri Dec 9 00:50:19 2011
@@ -0,0 +1,39 @@
+// Copyright 2011 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.
+
+// Flags: --allow-natives-syntax --smi-only-arrays
+
+support_smi_only_arrays = %HasFastSmiOnlyElements([1,2,3,4,5,6,7,8,9,10]);
+
+if (support_smi_only_arrays) {
+  var a = new Array(0, 1, 2);
+  assertTrue(%HasFastSmiOnlyElements(a));
+  var b = new Array(0.5, 1.2, 2.3);
+  assertTrue(%HasFastDoubleElements(b));
+  var c = new Array(0.5, 1.2, new Object());
+  assertTrue(%HasFastElements(c));
+}
=======================================
--- /branches/bleeding_edge/src/arm/builtins-arm.cc     Thu Oct 27 04:11:59 2011
+++ /branches/bleeding_edge/src/arm/builtins-arm.cc     Fri Dec  9 00:50:19 2011
@@ -394,13 +394,18 @@
   // r5: elements_array_end (untagged)
   // sp[0]: last argument
   Label loop, entry;
+  __ mov(r7, sp);
   __ jmp(&entry);
   __ bind(&loop);
-  __ ldr(r2, MemOperand(sp, kPointerSize, PostIndex));
+  __ ldr(r2, MemOperand(r7, kPointerSize, PostIndex));
+  if (FLAG_smi_only_arrays) {
+    __ JumpIfNotSmi(r2, call_generic_code);
+  }
   __ str(r2, MemOperand(r5, -kPointerSize, PreIndex));
   __ bind(&entry);
   __ cmp(r4, r5);
   __ b(lt, &loop);
+  __ mov(sp, r7);

// Remove caller arguments and receiver from the stack, setup return value and
   // return.
=======================================
--- /branches/bleeding_edge/src/builtins.cc     Tue Nov 15 04:34:55 2011
+++ /branches/bleeding_edge/src/builtins.cc     Fri Dec  9 00:50:19 2011
@@ -232,31 +232,58 @@
   if (args.length() == 1) {
     return array->Initialize(JSArray::kPreallocatedArrayElements);
   }
-
-  // Take the arguments as elements.
-  int number_of_elements = args.length() - 1;
-  Smi* len = Smi::FromInt(number_of_elements);
-  Object* obj;
- { MaybeObject* maybe_obj = heap->AllocateFixedArrayWithHoles(len->value());
-    if (!maybe_obj->ToObject(&obj)) return maybe_obj;
-  }

   // Set length and elements on the array.
+  int number_of_elements = args.length() - 1;
   MaybeObject* maybe_object =
-      array->EnsureCanContainElements(FixedArray::cast(obj));
+      array->EnsureCanContainElements(&args, 1, number_of_elements,
+                                      ALLOW_CONVERTED_DOUBLE_ELEMENTS);
   if (maybe_object->IsFailure()) return maybe_object;

-  AssertNoAllocation no_gc;
-  FixedArray* elms = FixedArray::cast(obj);
-  WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
+  // Allocate an appropriately typed elements array.
+  MaybeObject* maybe_elms;
+  ElementsKind elements_kind = array->GetElementsKind();
+  if (elements_kind == FAST_DOUBLE_ELEMENTS) {
+    maybe_elms = heap->AllocateUninitializedFixedDoubleArray(
+        number_of_elements);
+  } else {
+    maybe_elms = heap->AllocateFixedArrayWithHoles(number_of_elements);
+  }
+  FixedArrayBase* elms;
+  if (!maybe_elms->To<FixedArrayBase>(&elms)) return maybe_elms;
+
   // Fill in the content
-  for (int index = 0; index < number_of_elements; index++) {
-    elms->set(index, args[index+1], mode);
+  switch (array->GetElementsKind()) {
+    case FAST_SMI_ONLY_ELEMENTS: {
+      FixedArray* smi_elms = FixedArray::cast(elms);
+      for (int index = 0; index < number_of_elements; index++) {
+        smi_elms->set(index, args[index+1], SKIP_WRITE_BARRIER);
+      }
+      break;
+    }
+    case FAST_ELEMENTS: {
+      AssertNoAllocation no_gc;
+      WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
+      FixedArray* object_elms = FixedArray::cast(elms);
+      for (int index = 0; index < number_of_elements; index++) {
+        object_elms->set(index, args[index+1], mode);
+      }
+      break;
+    }
+    case FAST_DOUBLE_ELEMENTS: {
+      FixedDoubleArray* double_elms = FixedDoubleArray::cast(elms);
+      for (int index = 0; index < number_of_elements; index++) {
+        double_elms->set(index, args[index+1]->Number());
+      }
+      break;
+    }
+    default:
+      UNREACHABLE();
+      break;
   }

-  array->set_elements(FixedArray::cast(obj));
-  array->set_length(len);
-
+  array->set_elements(elms);
+  array->set_length(Smi::FromInt(number_of_elements));
   return array;
 }

@@ -424,7 +451,8 @@
   MaybeObject* maybe_array = array->EnsureCanContainElements(
       args,
       first_added_arg,
-      args_length - first_added_arg);
+      args_length - first_added_arg,
+      DONT_ALLOW_DOUBLE_ELEMENTS);
   if (maybe_array->IsFailure()) return maybe_array;
   return array->elements();
 }
@@ -627,7 +655,8 @@
   ASSERT(to_add <= (Smi::kMaxValue - len));

   MaybeObject* maybe_object =
-      array->EnsureCanContainElements(&args, 1, to_add);
+      array->EnsureCanContainElements(&args, 1, to_add,
+                                      DONT_ALLOW_DOUBLE_ELEMENTS);
   if (maybe_object->IsFailure()) return maybe_object;

   if (new_length > elms->length()) {
@@ -758,7 +787,8 @@
   FixedArray* result_elms = FixedArray::cast(result);

   MaybeObject* maybe_object =
-      result_array->EnsureCanContainElements(result_elms);
+      result_array->EnsureCanContainElements(result_elms,
+                                             DONT_ALLOW_DOUBLE_ELEMENTS);
   if (maybe_object->IsFailure()) return maybe_object;

   AssertNoAllocation no_gc;
@@ -1022,7 +1052,7 @@
     for (int i = 0; i < n_arguments; i++) {
       JSArray* array = JSArray::cast(args[i]);
       if (!array->HasFastSmiOnlyElements()) {
-        result_array->EnsureCanContainNonSmiElements();
+        result_array->EnsureCanContainHeapObjectElements();
         break;
       }
     }
=======================================
--- /branches/bleeding_edge/src/elements.cc     Fri Nov 18 03:08:46 2011
+++ /branches/bleeding_edge/src/elements.cc     Fri Dec  9 00:50:19 2011
@@ -133,6 +133,22 @@
   static MaybeObject* SetLength(BackingStoreClass* backing_store,
                                 JSObject* obj,
                                 Object* length);
+
+  virtual MaybeObject* SetCapacityAndLength(JSArray* array,
+                                            int capacity,
+                                            int length) {
+    return ElementsAccessorSubclass::SetFastElementsCapacityAndLength(
+        array,
+        capacity,
+        length);
+  }
+
+  static MaybeObject* SetFastElementsCapacityAndLength(JSObject* obj,
+                                                       int capacity,
+                                                       int length) {
+    UNIMPLEMENTED();
+    return obj;
+  }

   virtual MaybeObject* Delete(JSObject* obj,
                               uint32_t key,
@@ -375,11 +391,6 @@
     }
     return heap->true_value();
   }
-
- protected:
-  friend class FastElementsAccessor<FastObjectElementsAccessor,
-                                    FixedArray,
-                                    kPointerSize>;

   static MaybeObject* SetFastElementsCapacityAndLength(JSObject* obj,
                                                        uint32_t capacity,
@@ -392,6 +403,11 @@
                                                  length,
                                                  set_capacity_mode);
   }
+
+ protected:
+  friend class FastElementsAccessor<FastObjectElementsAccessor,
+                                    FixedArray,
+                                    kPointerSize>;

   virtual MaybeObject* Delete(JSObject* obj,
                               uint32_t key,
@@ -405,18 +421,18 @@
     : public FastElementsAccessor<FastDoubleElementsAccessor,
                                   FixedDoubleArray,
                                   kDoubleSize> {
+  static MaybeObject* SetFastElementsCapacityAndLength(JSObject* obj,
+                                                       uint32_t capacity,
+                                                       uint32_t length) {
+    return obj->SetFastDoubleElementsCapacityAndLength(capacity, length);
+  }
+
  protected:
   friend class ElementsAccessorBase<FastDoubleElementsAccessor,
                                     FixedDoubleArray>;
   friend class FastElementsAccessor<FastDoubleElementsAccessor,
                                     FixedDoubleArray,
                                     kDoubleSize>;
-
-  static MaybeObject* SetFastElementsCapacityAndLength(JSObject* obj,
-                                                       uint32_t capacity,
-                                                       uint32_t length) {
-    return obj->SetFastDoubleElementsCapacityAndLength(capacity, length);
-  }

   virtual MaybeObject* Delete(JSObject* obj,
                               uint32_t key,
=======================================
--- /branches/bleeding_edge/src/elements.h      Tue Nov  8 03:59:56 2011
+++ /branches/bleeding_edge/src/elements.h      Fri Dec  9 00:50:19 2011
@@ -44,11 +44,24 @@
                            JSObject* holder,
                            Object* receiver) = 0;

- // Modifies the length data property as specified for JSArrays and resizes
-  // the underlying backing store accordingly.
+ // Modifies the length data property as specified for JSArrays and resizes the + // underlying backing store accordingly. The method honors the semantics of + // changing array sizes as defined in EcmaScript 5.1 15.4.5.2, i.e. array that
+  // have non-deletable elements can only be shrunk to the size of highest
+  // element that is non-deletable.
   virtual MaybeObject* SetLength(JSObject* holder,
                                  Object* new_length) = 0;

+ // Modifies both the length and capacity of a JSArray, resizing the underlying + // backing store as necessary. This method does NOT honor the semantics of
+  // EcmaScript 5.1 15.4.5.2, arrays can be shrunk beyond non-deletable
+  // elements. This method should only be called for array expansion OR by
+  // runtime JavaScript code that use InternalArrays and don't care about
+  // EcmaScript 5.1 semantics.
+  virtual MaybeObject* SetCapacityAndLength(JSArray* array,
+                                            int capacity,
+                                            int length) = 0;
+
   virtual MaybeObject* Delete(JSObject* holder,
                               uint32_t key,
                               JSReceiver::DeleteMode mode) = 0;
=======================================
--- /branches/bleeding_edge/src/factory.cc      Thu Nov 24 07:17:04 2011
+++ /branches/bleeding_edge/src/factory.cc      Fri Dec  9 00:50:19 2011
@@ -926,28 +926,48 @@
 }


-Handle<JSArray> Factory::NewJSArrayWithElements(Handle<FixedArray> elements, +Handle<JSArray> Factory::NewJSArrayWithElements(Handle<FixedArrayBase> elements,
                                                 PretenureFlag pretenure) {
   Handle<JSArray> result =
       Handle<JSArray>::cast(NewJSObject(isolate()->array_function(),
                                         pretenure));
+  result->set_length(Smi::FromInt(0));
   SetContent(result, elements);
   return result;
 }
+
+
+void Factory::SetElementsCapacityAndLength(Handle<JSArray> array,
+                                           int capacity,
+                                           int length) {
+  ElementsAccessor* accessor = array->GetElementsAccessor();
+  CALL_HEAP_FUNCTION_VOID(
+      isolate(),
+      accessor->SetCapacityAndLength(*array, capacity, length));
+}


 void Factory::SetContent(Handle<JSArray> array,
-                         Handle<FixedArray> elements) {
+                         Handle<FixedArrayBase> elements) {
   CALL_HEAP_FUNCTION_VOID(
       isolate(),
       array->SetContent(*elements));
 }


-void Factory::EnsureCanContainNonSmiElements(Handle<JSArray> array) {
+void Factory::EnsureCanContainHeapObjectElements(Handle<JSArray> array) {
   CALL_HEAP_FUNCTION_VOID(
       isolate(),
-      array->EnsureCanContainNonSmiElements());
+      array->EnsureCanContainHeapObjectElements());
+}
+
+
+void Factory::EnsureCanContainElements(Handle<JSArray> array,
+                                       Handle<FixedArrayBase> elements,
+                                       EnsureElementsMode mode) {
+  CALL_HEAP_FUNCTION_VOID(
+      isolate(),
+      array->EnsureCanContainElements(*elements, mode));
 }


=======================================
--- /branches/bleeding_edge/src/factory.h       Thu Nov 24 07:17:04 2011
+++ /branches/bleeding_edge/src/factory.h       Fri Dec  9 00:50:19 2011
@@ -259,12 +259,19 @@
                              PretenureFlag pretenure = NOT_TENURED);

   Handle<JSArray> NewJSArrayWithElements(
-      Handle<FixedArray> elements,
+      Handle<FixedArrayBase> elements,
       PretenureFlag pretenure = NOT_TENURED);

-  void SetContent(Handle<JSArray> array, Handle<FixedArray> elements);
-
-  void EnsureCanContainNonSmiElements(Handle<JSArray> array);
+  void SetElementsCapacityAndLength(Handle<JSArray> array,
+                                    int capacity,
+                                    int length);
+
+  void SetContent(Handle<JSArray> array, Handle<FixedArrayBase> elements);
+
+  void EnsureCanContainHeapObjectElements(Handle<JSArray> array);
+  void EnsureCanContainElements(Handle<JSArray> array,
+                                Handle<FixedArrayBase> elements,
+                                EnsureElementsMode mode);

Handle<JSProxy> NewJSProxy(Handle<Object> handler, Handle<Object> prototype);

=======================================
--- /branches/bleeding_edge/src/ia32/builtins-ia32.cc Tue Nov 8 06:39:37 2011 +++ /branches/bleeding_edge/src/ia32/builtins-ia32.cc Fri Dec 9 00:50:19 2011
@@ -1238,37 +1238,42 @@
                   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
+  __ push(ebx);
+  __ mov(ebx, Operand(esp, kPointerSize));
   // ebx: argc
   // edx: elements_array_end (untagged)
   // esp[0]: JSArray
-  // esp[4]: return address
-  // esp[8]: last argument
+  // esp[4]: argc
+  // esp[8]: constructor (only if construct_call)
+  // esp[12]: return address
+  // esp[16]: last argument

   // Location of the last argument
-  __ lea(edi, Operand(esp, 2 * kPointerSize));
+  int last_arg_offset = (construct_call ? 4 : 3) * kPointerSize;
+  __ lea(edi, Operand(esp, last_arg_offset));

   // Location of the first array element (Parameter fill_with_holes to
-  // AllocateJSArrayis false, so the FixedArray is returned in ecx).
+  // AllocateJSArray is false, so the FixedArray is returned in ecx).
   __ lea(edx, Operand(ecx, FixedArray::kHeaderSize - kHeapObjectTag));

+  Label has_non_smi_element;
+
   // 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
+  // esp[4]: argc
+  // esp[8]: constructor (only if construct_call)
+  // esp[12]: return address
+  // esp[16]: last argument
   Label loop, entry;
   __ mov(ecx, ebx);
   __ jmp(&entry);
   __ bind(&loop);
   __ mov(eax, Operand(edi, ecx, times_pointer_size, 0));
+  if (FLAG_smi_only_arrays) {
+    __ JumpIfNotSmi(eax, &has_non_smi_element);
+  }
   __ mov(Operand(edx, 0), eax);
   __ add(edx, Immediate(kPointerSize));
   __ bind(&entry);
@@ -1278,13 +1283,20 @@
   // Remove caller arguments from the stack and return.
   // ebx: argc
   // esp[0]: JSArray
-  // esp[4]: return address
-  // esp[8]: last argument
+  // esp[4]: argc
+  // esp[8]: constructor (only if construct_call)
+  // esp[12]: return address
+  // esp[16]: last argument
+  __ mov(ecx, Operand(esp, last_arg_offset - kPointerSize));
   __ pop(eax);
-  __ pop(ecx);
-  __ lea(esp, Operand(esp, ebx, times_pointer_size, 1 * kPointerSize));
-  __ push(ecx);
-  __ ret(0);
+  __ pop(ebx);
+  __ lea(esp, Operand(esp, ebx, times_pointer_size,
+                      last_arg_offset - kPointerSize));
+  __ jmp(ecx);
+
+  __ bind(&has_non_smi_element);
+  // Throw away the array that's only been partially constructed.
+  __ pop(eax);

   // Restore argc and constructor before running the generic code.
   __ bind(&prepare_generic_code_call);
=======================================
--- /branches/bleeding_edge/src/objects-inl.h   Wed Dec  7 00:43:18 2011
+++ /branches/bleeding_edge/src/objects-inl.h   Fri Dec  9 00:50:19 2011
@@ -1183,6 +1183,22 @@
 ACCESSORS(JSObject, properties, FixedArray, kPropertiesOffset)


+Object** FixedArray::GetFirstElementAddress() {
+ return reinterpret_cast<Object**>(FIELD_ADDR(this, OffsetOfElementAt(0)));
+}
+
+
+bool FixedArray::ContainsOnlySmisOrHoles() {
+  Object* the_hole = GetHeap()->the_hole_value();
+  Object** current = GetFirstElementAddress();
+  for (int i = 0; i < length(); ++i) {
+    Object* candidate = *current++;
+    if (!candidate->IsSmi() && candidate != the_hole) return false;
+  }
+  return true;
+}
+
+
 FixedArrayBase* JSObject::elements() {
   Object* array = READ_FIELD(this, kElementsOffset);
   return static_cast<FixedArrayBase*>(array);
@@ -1211,38 +1227,66 @@
 }


-MaybeObject* JSObject::EnsureCanContainNonSmiElements() {
+MaybeObject* JSObject::EnsureCanContainHeapObjectElements() {
 #if DEBUG
   ValidateSmiOnlyElements();
 #endif
-  if ((map()->elements_kind() == FAST_SMI_ONLY_ELEMENTS)) {
-    Object* obj;
-    MaybeObject* maybe_obj = GetElementsTransitionMap(FAST_ELEMENTS);
-    if (!maybe_obj->ToObject(&obj)) return maybe_obj;
-    set_map(Map::cast(obj));
+  if ((map()->elements_kind() != FAST_ELEMENTS)) {
+    return TransitionElementsKind(FAST_ELEMENTS);
   }
   return this;
 }


 MaybeObject* JSObject::EnsureCanContainElements(Object** objects,
-                                                uint32_t count) {
-  if (map()->elements_kind() == FAST_SMI_ONLY_ELEMENTS) {
-    for (uint32_t i = 0; i < count; ++i) {
-      Object* current = *objects++;
-      if (!current->IsSmi() && current != GetHeap()->the_hole_value()) {
-        return EnsureCanContainNonSmiElements();
+                                                uint32_t count,
+                                                EnsureElementsMode mode) {
+  ElementsKind current_kind = map()->elements_kind();
+  ElementsKind target_kind = current_kind;
+  ASSERT(mode != ALLOW_COPIED_DOUBLE_ELEMENTS);
+  if (current_kind == FAST_ELEMENTS) return this;
+
+  Heap* heap = GetHeap();
+  Object* the_hole = heap->the_hole_value();
+  Object* heap_number_map = heap->heap_number_map();
+  for (uint32_t i = 0; i < count; ++i) {
+    Object* current = *objects++;
+    if (!current->IsSmi() && current != the_hole) {
+      if (mode == ALLOW_CONVERTED_DOUBLE_ELEMENTS &&
+          HeapObject::cast(current)->map() == heap_number_map) {
+        target_kind = FAST_DOUBLE_ELEMENTS;
+      } else {
+        target_kind = FAST_ELEMENTS;
+        break;
       }
     }
   }
+
+  if (target_kind != current_kind) {
+    return TransitionElementsKind(target_kind);
+  }
   return this;
 }


-MaybeObject* JSObject::EnsureCanContainElements(FixedArray* elements) {
-  Object** objects = reinterpret_cast<Object**>(
-      FIELD_ADDR(elements, elements->OffsetOfElementAt(0)));
-  return EnsureCanContainElements(objects, elements->length());
+MaybeObject* JSObject::EnsureCanContainElements(FixedArrayBase* elements,
+                                                EnsureElementsMode mode) {
+  if (elements->map() != GetHeap()->fixed_double_array_map()) {
+    ASSERT(elements->map() == GetHeap()->fixed_array_map() ||
+           elements->map() == GetHeap()->fixed_cow_array_map());
+    if (mode == ALLOW_COPIED_DOUBLE_ELEMENTS) {
+      mode = DONT_ALLOW_DOUBLE_ELEMENTS;
+    }
+ Object** objects = FixedArray::cast(elements)->GetFirstElementAddress();
+    return EnsureCanContainElements(objects, elements->length(), mode);
+  }
+
+  ASSERT(mode == ALLOW_COPIED_DOUBLE_ELEMENTS);
+  if (GetElementsKind() == FAST_SMI_ONLY_ELEMENTS) {
+    return TransitionElementsKind(FAST_DOUBLE_ELEMENTS);
+  }
+
+  return this;
 }


@@ -4120,7 +4164,8 @@
             (map == GetHeap()->fixed_array_map() ||
              map == GetHeap()->fixed_cow_array_map())) ||
            (kind == FAST_DOUBLE_ELEMENTS &&
-            fixed_array->IsFixedDoubleArray()) ||
+            (fixed_array->IsFixedDoubleArray() ||
+            fixed_array == GetHeap()->empty_fixed_array())) ||
            (kind == DICTIONARY_ELEMENTS &&
             fixed_array->IsFixedArray() &&
             fixed_array->IsDictionary()) ||
@@ -4579,11 +4624,18 @@
 }


-MaybeObject* JSArray::SetContent(FixedArray* storage) {
-  MaybeObject* maybe_object = EnsureCanContainElements(storage);
-  if (maybe_object->IsFailure()) return maybe_object;
-  set_length(Smi::FromInt(storage->length()));
+MaybeObject* JSArray::SetContent(FixedArrayBase* storage) {
+  MaybeObject* maybe_result = EnsureCanContainElements(
+      storage, ALLOW_COPIED_DOUBLE_ELEMENTS);
+  if (maybe_result->IsFailure()) return maybe_result;
+  ASSERT((storage->map() == GetHeap()->fixed_double_array_map() &&
+          GetElementsKind() == FAST_DOUBLE_ELEMENTS) ||
+         ((storage->map() != GetHeap()->fixed_double_array_map()) &&
+          ((GetElementsKind() == FAST_ELEMENTS) ||
+           (GetElementsKind() == FAST_SMI_ONLY_ELEMENTS &&
+            FixedArray::cast(storage)->ContainsOnlySmisOrHoles()))));
   set_elements(storage);
+  set_length(Smi::FromInt(storage->length()));
   return this;
 }

=======================================
--- /branches/bleeding_edge/src/objects.cc      Wed Dec  7 00:43:18 2011
+++ /branches/bleeding_edge/src/objects.cc      Fri Dec  9 00:50:19 2011
@@ -8133,17 +8133,17 @@
                                    FixedArray* destination,
                                    WriteBarrierMode mode) {
   int count = source->length();
+  int copy_size = Min(count, destination->length());
   if (mode == SKIP_WRITE_BARRIER ||
       !Page::FromAddress(destination->address())->IsFlagSet(
           MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING)) {
-    ASSERT(count <= destination->length());
     Address to = destination->address() + FixedArray::kHeaderSize;
     Address from = source->address() + FixedArray::kHeaderSize;
     memcpy(reinterpret_cast<void*>(to),
            reinterpret_cast<void*>(from),
-           kPointerSize * count);
+           kPointerSize * copy_size);
   } else {
-    for (int i = 0; i < count; ++i) {
+    for (int i = 0; i < copy_size; ++i) {
       destination->set(i, source->get(i), mode);
     }
   }
@@ -8153,11 +8153,14 @@
 static void CopySlowElementsToFast(NumberDictionary* source,
                                    FixedArray* destination,
                                    WriteBarrierMode mode) {
+  int destination_length = destination->length();
   for (int i = 0; i < source->Capacity(); ++i) {
     Object* key = source->KeyAt(i);
     if (key->IsNumber()) {
       uint32_t entry = static_cast<uint32_t>(key->Number());
-      destination->set(entry, source->ValueAt(i), mode);
+      if (entry < static_cast<uint32_t>(destination_length)) {
+        destination->set(entry, source->ValueAt(i), mode);
+      }
     }
   }
 }
@@ -8368,14 +8371,8 @@


 void JSArray::Expand(int required_size) {
-  Handle<JSArray> self(this);
-  Handle<FixedArray> old_backing(FixedArray::cast(elements()));
-  int old_size = old_backing->length();
-  int new_size = required_size > old_size ? required_size : old_size;
-  Handle<FixedArray> new_backing = FACTORY->NewFixedArray(new_size);
-  // Can't use this any more now because we may have had a GC!
- for (int i = 0; i < old_size; i++) new_backing->set(i, old_backing->get(i));
-  GetIsolate()->factory()->SetContent(self, new_backing);
+  GetIsolate()->factory()->SetElementsCapacityAndLength(
+      Handle<JSArray>(this), required_size, Smi::cast(length())->value());
 }


@@ -8529,13 +8526,14 @@

 MaybeObject* JSObject::EnsureCanContainElements(Arguments* args,
                                                 uint32_t first_arg,
-                                                uint32_t arg_count) {
+                                                uint32_t arg_count,
+                                                EnsureElementsMode mode) {
   // Elements in |Arguments| are ordered backwards (because they're on the
// stack), but the method that's called here iterates over them in forward
   // direction.
   return EnsureCanContainElements(
       args->arguments() - first_arg - (arg_count - 1),
-      arg_count);
+      arg_count, mode);
 }


@@ -9487,31 +9485,45 @@
   FixedArrayBase* elms = FixedArrayBase::cast(elements());
   uint32_t capacity = static_cast<uint32_t>(elms->length());
   uint32_t length = capacity;
+
   if (IsJSArray()) {
-    CHECK(JSArray::cast(this)->length()->ToArrayIndex(&length));
-  }
-  if (from_kind == FAST_SMI_ONLY_ELEMENTS) {
-    if (to_kind == FAST_DOUBLE_ELEMENTS) {
-      MaybeObject* maybe_result =
-          SetFastDoubleElementsCapacityAndLength(capacity, length);
-      if (maybe_result->IsFailure()) return maybe_result;
-      return this;
-    } else if (to_kind == FAST_ELEMENTS) {
-      MaybeObject* maybe_new_map = GetElementsTransitionMap(FAST_ELEMENTS);
-      Map* new_map;
-      if (!maybe_new_map->To(&new_map)) return maybe_new_map;
-      if (FLAG_trace_elements_transitions) {
- PrintElementsTransition(stdout, from_kind, elms, FAST_ELEMENTS, elms);
-      }
-      set_map(new_map);
-      return this;
-    }
- } else if (from_kind == FAST_DOUBLE_ELEMENTS && to_kind == FAST_ELEMENTS) {
+    Object* raw_length = JSArray::cast(this)->length();
+    if (raw_length->IsUndefined()) {
+ // If length is undefined, then JSArray is being initialized and has no
+      // elements, assume a length of zero.
+      length = 0;
+    } else {
+      CHECK(JSArray::cast(this)->length()->ToArrayIndex(&length));
+    }
+  }
+
+  if ((from_kind == FAST_SMI_ONLY_ELEMENTS && to_kind == FAST_ELEMENTS) ||
+      (length == 0)) {
+    MaybeObject* maybe_new_map = GetElementsTransitionMap(to_kind);
+    Map* new_map;
+    if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+    if (FLAG_trace_elements_transitions) {
+      PrintElementsTransition(stdout, from_kind, elms, to_kind, elms);
+    }
+    set_map(new_map);
+    return this;
+  }
+
+  if (from_kind == FAST_SMI_ONLY_ELEMENTS &&
+      to_kind == FAST_DOUBLE_ELEMENTS) {
+    MaybeObject* maybe_result =
+        SetFastDoubleElementsCapacityAndLength(capacity, length);
+    if (maybe_result->IsFailure()) return maybe_result;
+    return this;
+  }
+
+  if (from_kind == FAST_DOUBLE_ELEMENTS && to_kind == FAST_ELEMENTS) {
     MaybeObject* maybe_result = SetFastElementsCapacityAndLength(
         capacity, length, kDontAllowSmiOnlyElements);
     if (maybe_result->IsFailure()) return maybe_result;
     return this;
   }
+
   // This method should never be called for any other case than the ones
   // handled above.
   UNREACHABLE();
=======================================
--- /branches/bleeding_edge/src/objects.h       Thu Dec  8 08:07:07 2011
+++ /branches/bleeding_edge/src/objects.h       Fri Dec  9 00:50:19 2011
@@ -1322,6 +1322,13 @@
 };


+enum EnsureElementsMode {
+  DONT_ALLOW_DOUBLE_ELEMENTS,
+  ALLOW_COPIED_DOUBLE_ELEMENTS,
+  ALLOW_CONVERTED_DOUBLE_ELEMENTS
+};
+
+
 // JSReceiver includes types on which properties can be defined, i.e.,
 // JSObject and JSProxy.
 class JSReceiver: public HeapObject {
@@ -1615,16 +1622,19 @@

   inline void ValidateSmiOnlyElements();

-  // Makes sure that this object can contain non-smi Object as elements.
-  inline MaybeObject* EnsureCanContainNonSmiElements();
+  // Makes sure that this object can contain HeapObject as elements.
+  inline MaybeObject* EnsureCanContainHeapObjectElements();

   // Makes sure that this object can contain the specified elements.
   inline MaybeObject* EnsureCanContainElements(Object** elements,
-                                               uint32_t count);
-  inline MaybeObject* EnsureCanContainElements(FixedArray* elements);
+                                               uint32_t count,
+                                               EnsureElementsMode mode);
+  inline MaybeObject* EnsureCanContainElements(FixedArrayBase* elements,
+                                               EnsureElementsMode mode);
   MaybeObject* EnsureCanContainElements(Arguments* arguments,
                                         uint32_t first_arg,
-                                        uint32_t arg_count);
+                                        uint32_t arg_count,
+                                        EnsureElementsMode mode);

   // Do we want to keep the elements in fast case when increasing the
   // capacity?
@@ -2124,6 +2134,9 @@
   // Gives access to raw memory which stores the array's data.
   inline Object** data_start();

+  inline Object** GetFirstElementAddress();
+  inline bool ContainsOnlySmisOrHoles();
+
   // Copy operations.
   MUST_USE_RESULT inline MaybeObject* Copy();
   MUST_USE_RESULT MaybeObject* CopySize(int new_length);
@@ -7386,7 +7399,7 @@
   MUST_USE_RESULT MaybeObject* Initialize(int capacity);

   // Set the content of the array to the content of storage.
-  inline MaybeObject* SetContent(FixedArray* storage);
+  inline MaybeObject* SetContent(FixedArrayBase* storage);

   // Casting.
   static inline JSArray* cast(Object* obj);
=======================================
--- /branches/bleeding_edge/src/runtime.cc      Wed Dec  7 08:15:18 2011
+++ /branches/bleeding_edge/src/runtime.cc      Fri Dec  9 00:50:19 2011
@@ -6296,7 +6296,7 @@
   int part_count = indices.length();

   Handle<JSArray> result = isolate->factory()->NewJSArray(part_count);
-  MaybeObject* maybe_result = result->EnsureCanContainNonSmiElements();
+  MaybeObject* maybe_result = result->EnsureCanContainHeapObjectElements();
   if (maybe_result->IsFailure()) return maybe_result;
   result->set_length(Smi::FromInt(part_count));

@@ -6672,7 +6672,7 @@
   // This assumption is used by the slice encoding in one or two smis.
   ASSERT(Smi::kMaxValue >= String::kMaxLength);

-  MaybeObject* maybe_result = array->EnsureCanContainNonSmiElements();
+  MaybeObject* maybe_result = array->EnsureCanContainHeapObjectElements();
   if (maybe_result->IsFailure()) return maybe_result;

   int special_length = special->length();
@@ -9318,7 +9318,7 @@
   CONVERT_ARG_CHECKED(JSArray, output, 1);

   MaybeObject* maybe_result_array =
-      output->EnsureCanContainNonSmiElements();
+      output->EnsureCanContainHeapObjectElements();
   if (maybe_result_array->IsFailure()) return maybe_result_array;
   RUNTIME_ASSERT(output->HasFastElements());

=======================================
--- /branches/bleeding_edge/src/x64/builtins-x64.cc     Thu Oct 27 04:11:59 2011
+++ /branches/bleeding_edge/src/x64/builtins-x64.cc     Fri Dec  9 00:50:19 2011
@@ -1305,6 +1305,9 @@
   __ jmp(&entry);
   __ bind(&loop);
   __ movq(kScratchRegister, Operand(r9, rcx, times_pointer_size, 0));
+  if (FLAG_smi_only_arrays) {
+    __ JumpIfNotSmi(kScratchRegister, call_generic_code);
+  }
   __ movq(Operand(rdx, 0), kScratchRegister);
   __ addq(rdx, Immediate(kPointerSize));
   __ bind(&entry);

--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to