Replace HeapNumber as doublebox with an explicit MutableHeapNumber.
authorishell@chromium.org <ishell@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 30 Jun 2014 09:44:43 +0000 (09:44 +0000)
committerishell@chromium.org <ishell@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 30 Jun 2014 09:44:43 +0000 (09:44 +0000)
R=verwaest@chromium.org

Review URL: https://codereview.chromium.org/355793003

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22082 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

39 files changed:
include/v8.h
src/arm/macro-assembler-arm.cc
src/arm/macro-assembler-arm.h
src/arm/stub-cache-arm.cc
src/arm64/macro-assembler-arm64.cc
src/arm64/macro-assembler-arm64.h
src/arm64/stub-cache-arm64.cc
src/deoptimizer.cc
src/factory.cc
src/factory.h
src/heap-snapshot-generator.cc
src/heap.cc
src/heap.h
src/hydrogen.cc
src/ia32/macro-assembler-ia32.cc
src/ia32/macro-assembler-ia32.h
src/ia32/stub-cache-ia32.cc
src/json-parser.h
src/json-stringifier.h
src/mark-compact.cc
src/mips/macro-assembler-mips.cc
src/mips/macro-assembler-mips.h
src/mips/stub-cache-mips.cc
src/objects-debug.cc
src/objects-inl.h
src/objects-printer.cc
src/objects-visiting.cc
src/objects.cc
src/objects.h
src/runtime.cc
src/x64/macro-assembler-x64.cc
src/x64/macro-assembler-x64.h
src/x64/stub-cache-x64.cc
src/x87/macro-assembler-x87.cc
src/x87/macro-assembler-x87.h
src/x87/stub-cache-x87.cc
test/cctest/test-heap-profiler.cc
test/mjsunit/migrations.js [new file with mode: 0644]
test/mjsunit/mjsunit.status

index 7d27029d23c8ee403588f88f8b4a3abe87a4fa4d..f734b9a44f0873a8265413053e848a58cda51316 100644 (file)
@@ -5586,7 +5586,7 @@ class Internals {
   static const int kNullValueRootIndex = 7;
   static const int kTrueValueRootIndex = 8;
   static const int kFalseValueRootIndex = 9;
-  static const int kEmptyStringRootIndex = 160;
+  static const int kEmptyStringRootIndex = 161;
 
   // The external allocation limit should be below 256 MB on all architectures
   // to avoid that resource-constrained embedders run low on memory.
@@ -5601,10 +5601,10 @@ class Internals {
   static const int kNodeIsIndependentShift = 4;
   static const int kNodeIsPartiallyDependentShift = 5;
 
-  static const int kJSObjectType = 0xbb;
+  static const int kJSObjectType = 0xbc;
   static const int kFirstNonstringType = 0x80;
   static const int kOddballType = 0x83;
-  static const int kForeignType = 0x87;
+  static const int kForeignType = 0x88;
 
   static const int kUndefinedOddballKind = 5;
   static const int kNullOddballKind = 3;
index ecb8b19793625e6c67538a50822628bbdb3a96bf..4205e5605041494ea2a21d26765705ad1910e8c3 100644 (file)
@@ -3274,14 +3274,19 @@ void MacroAssembler::AllocateHeapNumber(Register result,
                                         Register scratch2,
                                         Register heap_number_map,
                                         Label* gc_required,
-                                        TaggingMode tagging_mode) {
+                                        TaggingMode tagging_mode,
+                                        MutableMode mode) {
   // Allocate an object in the heap for the heap number and tag it as a heap
   // object.
   Allocate(HeapNumber::kSize, result, scratch1, scratch2, gc_required,
            tagging_mode == TAG_RESULT ? TAG_OBJECT : NO_ALLOCATION_FLAGS);
 
+  Heap::RootListIndex map_index = mode == MUTABLE
+      ? Heap::kMutableHeapNumberMapRootIndex
+      : Heap::kHeapNumberMapRootIndex;
+  AssertIsRoot(heap_number_map, map_index);
+
   // Store heap number map in the allocated object.
-  AssertIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
   if (tagging_mode == TAG_RESULT) {
     str(heap_number_map, FieldMemOperand(result, HeapObject::kMapOffset));
   } else {
index dbf305a4077c236303bd26781cc591d7088f3f86..32e9cb1d946fbd16d7f2d4d552cfd68bf3f344f6 100644 (file)
@@ -778,7 +778,8 @@ class MacroAssembler: public Assembler {
                           Register scratch2,
                           Register heap_number_map,
                           Label* gc_required,
-                          TaggingMode tagging_mode = TAG_RESULT);
+                          TaggingMode tagging_mode = TAG_RESULT,
+                          MutableMode mode = IMMUTABLE);
   void AllocateHeapNumberWithValue(Register result,
                                    DwVfpRegister value,
                                    Register scratch1,
index 1c772eec81152d77ade1e95a7ebe1135d93210fb..c30fa1f881a22134fa30787e0bab8aba9257cce7 100644 (file)
@@ -426,8 +426,9 @@ void StoreStubCompiler::GenerateStoreTransition(MacroAssembler* masm,
     }
   } else if (representation.IsDouble()) {
     Label do_store, heap_number;
-    __ LoadRoot(scratch3, Heap::kHeapNumberMapRootIndex);
-    __ AllocateHeapNumber(storage_reg, scratch1, scratch2, scratch3, slow);
+    __ LoadRoot(scratch3, Heap::kMutableHeapNumberMapRootIndex);
+    __ AllocateHeapNumber(storage_reg, scratch1, scratch2, scratch3, slow,
+                          TAG_RESULT, MUTABLE);
 
     __ JumpIfNotSmi(value_reg, &heap_number);
     __ SmiUntag(scratch1, value_reg);
index 43796b1f887bd3f2e52018e013b1eb41bc8302c4..d79256a875d604421ef50c5140e783ff9ddb96e1 100644 (file)
@@ -3560,7 +3560,8 @@ void MacroAssembler::AllocateHeapNumber(Register result,
                                         Register scratch1,
                                         Register scratch2,
                                         CPURegister value,
-                                        CPURegister heap_number_map) {
+                                        CPURegister heap_number_map,
+                                        MutableMode mode) {
   ASSERT(!value.IsValid() || value.Is64Bits());
   UseScratchRegisterScope temps(this);
 
@@ -3569,6 +3570,10 @@ void MacroAssembler::AllocateHeapNumber(Register result,
   Allocate(HeapNumber::kSize, result, scratch1, scratch2, gc_required,
            NO_ALLOCATION_FLAGS);
 
+  Heap::RootListIndex map_index = mode == MUTABLE
+      ? Heap::kMutableHeapNumberMapRootIndex
+      : Heap::kHeapNumberMapRootIndex;
+
   // Prepare the heap number map.
   if (!heap_number_map.IsValid()) {
     // If we have a valid value register, use the same type of register to store
@@ -3578,7 +3583,7 @@ void MacroAssembler::AllocateHeapNumber(Register result,
     } else {
       heap_number_map = scratch1;
     }
-    LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
+    LoadRoot(heap_number_map, map_index);
   }
   if (emit_debug_code()) {
     Register map;
@@ -3588,7 +3593,7 @@ void MacroAssembler::AllocateHeapNumber(Register result,
     } else {
       map = Register(heap_number_map);
     }
-    AssertRegisterIsRoot(map, Heap::kHeapNumberMapRootIndex);
+    AssertRegisterIsRoot(map, map_index);
   }
 
   // Store the heap number map and the value in the allocated object.
index 34182c0270741f3d8dc634c205a07ad2b2f4ca28..384d1f95acc30931f7ba42ba6eb97d0fd08e5fb8 100644 (file)
@@ -1371,7 +1371,8 @@ class MacroAssembler : public Assembler {
                           Register scratch1,
                           Register scratch2,
                           CPURegister value = NoFPReg,
-                          CPURegister heap_number_map = NoReg);
+                          CPURegister heap_number_map = NoReg,
+                          MutableMode mode = IMMUTABLE);
 
   // ---------------------------------------------------------------------------
   // Support functions.
index dc0220cada6cc4639d1d06566ed74f7f6523e2b0..eda834a89c3b06fc9710f457ff437295572b9d21 100644 (file)
@@ -399,7 +399,8 @@ void StoreStubCompiler::GenerateStoreTransition(MacroAssembler* masm,
     __ Ldr(temp_double, FieldMemOperand(value_reg, HeapNumber::kValueOffset));
 
     __ Bind(&do_store);
-    __ AllocateHeapNumber(storage_reg, slow, scratch1, scratch2, temp_double);
+    __ AllocateHeapNumber(storage_reg, slow, scratch1, scratch2, temp_double,
+                          NoReg, MUTABLE);
   }
 
   // Stub never generated for non-global objects that require access checks.
index d21e1c3c68c354446ba77d310ae70b80250bb178..e26a6d4c679647db951f04702f3224bdf57bed61 100644 (file)
@@ -1813,9 +1813,11 @@ Handle<Object> Deoptimizer::MaterializeNextHeapObject() {
     Handle<Map> map = Map::GeneralizeAllFieldRepresentations(
         Handle<Map>::cast(MaterializeNextValue()));
     switch (map->instance_type()) {
+      case MUTABLE_HEAP_NUMBER_TYPE:
       case HEAP_NUMBER_TYPE: {
         // Reuse the HeapNumber value directly as it is already properly
-        // tagged and skip materializing the HeapNumber explicitly.
+        // tagged and skip materializing the HeapNumber explicitly. Turn mutable
+        // heap numbers immutable.
         Handle<Object> object = MaterializeNextValue();
         if (object_index < prev_materialized_count_) {
           materialized_objects_->Add(Handle<Object>(
@@ -1877,6 +1879,9 @@ Handle<Object> Deoptimizer::MaterializeNextHeapObject() {
 Handle<Object> Deoptimizer::MaterializeNextValue() {
   int value_index = materialization_value_index_++;
   Handle<Object> value = materialized_values_->at(value_index);
+  if (value->IsMutableHeapNumber()) {
+    HeapNumber::cast(*value)->set_map(isolate_->heap()->heap_number_map());
+  }
   if (*value == isolate_->heap()->arguments_marker()) {
     value = MaterializeNextHeapObject();
   }
@@ -3383,6 +3388,7 @@ Handle<Object> SlotRefValueBuilder::GetNext(Isolate* isolate, int lvl) {
       // TODO(jarin) this should be unified with the code in
       // Deoptimizer::MaterializeNextHeapObject()
       switch (map->instance_type()) {
+        case MUTABLE_HEAP_NUMBER_TYPE:
         case HEAP_NUMBER_TYPE: {
           // Reuse the HeapNumber value directly as it is already properly
           // tagged and skip materializing the HeapNumber explicitly.
index 29e396b8db4c002d472a6aa8012e7e1326976166..af419ef5fcb747b2063999dfff291ab79a90443e 100644 (file)
@@ -1008,7 +1008,7 @@ Handle<Object> Factory::NewNumber(double value,
   // We need to distinguish the minus zero value and this cannot be
   // done after conversion to int. Doing this by comparing bit
   // patterns is faster than using fpclassify() et al.
-  if (IsMinusZero(value)) return NewHeapNumber(-0.0, pretenure);
+  if (IsMinusZero(value)) return NewHeapNumber(-0.0, IMMUTABLE, pretenure);
 
   int int_value = FastD2I(value);
   if (value == int_value && Smi::IsValid(int_value)) {
@@ -1016,15 +1016,15 @@ Handle<Object> Factory::NewNumber(double value,
   }
 
   // Materialize the value in the heap.
-  return NewHeapNumber(value, pretenure);
+  return NewHeapNumber(value, IMMUTABLE, pretenure);
 }
 
 
 Handle<Object> Factory::NewNumberFromInt(int32_t value,
                                          PretenureFlag pretenure) {
   if (Smi::IsValid(value)) return handle(Smi::FromInt(value), isolate());
-  // Bypass NumberFromDouble to avoid various redundant checks.
-  return NewHeapNumber(FastI2D(value), pretenure);
+  // Bypass NewNumber to avoid various redundant checks.
+  return NewHeapNumber(FastI2D(value), IMMUTABLE, pretenure);
 }
 
 
@@ -1034,15 +1034,17 @@ Handle<Object> Factory::NewNumberFromUint(uint32_t value,
   if (int32v >= 0 && Smi::IsValid(int32v)) {
     return handle(Smi::FromInt(int32v), isolate());
   }
-  return NewHeapNumber(FastUI2D(value), pretenure);
+  return NewHeapNumber(FastUI2D(value), IMMUTABLE, pretenure);
 }
 
 
 Handle<HeapNumber> Factory::NewHeapNumber(double value,
+                                          MutableMode mode,
                                           PretenureFlag pretenure) {
   CALL_HEAP_FUNCTION(
       isolate(),
-      isolate()->heap()->AllocateHeapNumber(value, pretenure), HeapNumber);
+      isolate()->heap()->AllocateHeapNumber(value, mode, pretenure),
+      HeapNumber);
 }
 
 
index cad25b552ebed2cd1b40330e8b69d79f2a91e90a..eb78f416d9bc9810eec48d19224de69b22dcc22e 100644 (file)
@@ -352,9 +352,9 @@ class Factory V8_FINAL {
     return NewNumber(static_cast<double>(value), pretenure);
   }
   Handle<HeapNumber> NewHeapNumber(double value,
+                                   MutableMode mode = IMMUTABLE,
                                    PretenureFlag pretenure = NOT_TENURED);
 
-
   // These objects are used by the api to create env-independent data
   // structures in the heap.
   inline Handle<JSObject> NewNeanderObject() {
index be970eeeeb3c19bcdccb59f02eba2ecc6a187873..376282115c097c26123fe922c17cc7d19bbf7463 100644 (file)
@@ -1641,6 +1641,8 @@ void V8HeapExplorer::ExtractPropertyReferences(JSObject* js_obj, int entry) {
     for (int i = 0; i < real_size; i++) {
       switch (descs->GetType(i)) {
         case FIELD: {
+          Representation r = descs->GetDetails(i).representation();
+          if (r.IsSmi() || r.IsDouble()) break;
           int index = descs->GetFieldIndex(i);
 
           Name* k = descs->GetKey(i);
index c4cf04ad1ed20bff3f813ee8ab220a01d416aa57..118e071fde51fd7c7570cfa39fd5fb64eb98efeb 100644 (file)
@@ -2528,6 +2528,8 @@ bool Heap::CreateInitialMaps() {
 
     ALLOCATE_VARSIZE_MAP(FIXED_ARRAY_TYPE, scope_info)
     ALLOCATE_MAP(HEAP_NUMBER_TYPE, HeapNumber::kSize, heap_number)
+    ALLOCATE_MAP(
+        MUTABLE_HEAP_NUMBER_TYPE, HeapNumber::kSize, mutable_heap_number)
     ALLOCATE_MAP(SYMBOL_TYPE, Symbol::kSize, symbol)
     ALLOCATE_MAP(FOREIGN_TYPE, Foreign::kSize, foreign)
 
@@ -2652,6 +2654,7 @@ bool Heap::CreateInitialMaps() {
 
 
 AllocationResult Heap::AllocateHeapNumber(double value,
+                                          MutableMode mode,
                                           PretenureFlag pretenure) {
   // Statically ensure that it is safe to allocate heap numbers in paged
   // spaces.
@@ -2665,7 +2668,8 @@ AllocationResult Heap::AllocateHeapNumber(double value,
     if (!allocation.To(&result)) return allocation;
   }
 
-  result->set_map_no_write_barrier(heap_number_map());
+  Map* map = mode == MUTABLE ? mutable_heap_number_map() : heap_number_map();
+  HeapObject::cast(result)->set_map_no_write_barrier(map);
   HeapNumber::cast(result)->set_value(value);
   return result;
 }
@@ -2771,12 +2775,12 @@ void Heap::CreateInitialObjects() {
   HandleScope scope(isolate());
   Factory* factory = isolate()->factory();
 
-  // The -0 value must be set before NumberFromDouble works.
-  set_minus_zero_value(*factory->NewHeapNumber(-0.0, TENURED));
+  // The -0 value must be set before NewNumber works.
+  set_minus_zero_value(*factory->NewHeapNumber(-0.0, IMMUTABLE, TENURED));
   ASSERT(std::signbit(minus_zero_value()->Number()) != 0);
 
-  set_nan_value(*factory->NewHeapNumber(OS::nan_value(), TENURED));
-  set_infinity_value(*factory->NewHeapNumber(V8_INFINITY, TENURED));
+  set_nan_value(*factory->NewHeapNumber(OS::nan_value(), IMMUTABLE, TENURED));
+  set_infinity_value(*factory->NewHeapNumber(V8_INFINITY, IMMUTABLE, TENURED));
 
   // The hole has not been created yet, but we want to put something
   // predictable in the gaps in the string table, so lets make that Smi zero.
index 85af3c93adab9742de436f2fd338a2f5b2428f26..2b8bcb0587fe7d17e23b04568ef7873e7f6a02f9 100644 (file)
@@ -42,6 +42,7 @@ namespace internal {
   V(Map, shared_function_info_map, SharedFunctionInfoMap)                      \
   V(Map, meta_map, MetaMap)                                                    \
   V(Map, heap_number_map, HeapNumberMap)                                       \
+  V(Map, mutable_heap_number_map, MutableHeapNumberMap)                        \
   V(Map, native_context_map, NativeContextMap)                                 \
   V(Map, fixed_array_map, FixedArrayMap)                                       \
   V(Map, code_map, CodeMap)                                                    \
@@ -230,6 +231,7 @@ namespace internal {
   V(shared_function_info_map)             \
   V(meta_map)                             \
   V(heap_number_map)                      \
+  V(mutable_heap_number_map)              \
   V(native_context_map)                   \
   V(fixed_array_map)                      \
   V(code_map)                             \
@@ -1460,7 +1462,9 @@ class Heap {
 
   // Allocated a HeapNumber from value.
   MUST_USE_RESULT AllocationResult AllocateHeapNumber(
-      double value, PretenureFlag pretenure = NOT_TENURED);
+      double value,
+      MutableMode mode = IMMUTABLE,
+      PretenureFlag pretenure = NOT_TENURED);
 
   // Allocate a byte array of the specified length
   MUST_USE_RESULT AllocationResult AllocateByteArray(
index ec5bb9bc8f91af24598efcb31b0242492af5be5a..da20c025e1a7d746977f9f5bc58894a1f472b144 100644 (file)
@@ -5781,8 +5781,9 @@ HInstruction* HOptimizedGraphBuilder::BuildStoreNamedField(
       HInstruction* heap_number = Add<HAllocate>(heap_number_size,
           HType::HeapObject(),
           NOT_TENURED,
-          HEAP_NUMBER_TYPE);
-      AddStoreMapConstant(heap_number, isolate()->factory()->heap_number_map());
+          MUTABLE_HEAP_NUMBER_TYPE);
+      AddStoreMapConstant(
+          heap_number, isolate()->factory()->mutable_heap_number_map());
       Add<HStoreNamedField>(heap_number, HObjectAccess::ForHeapNumberValue(),
                             value);
       instr = New<HStoreNamedField>(checked_object->ActualValue(),
@@ -10953,11 +10954,14 @@ void HOptimizedGraphBuilder::BuildEmitInObjectProperties(
         // 2) we can just use the mode of the parent object for pretenuring
         HInstruction* double_box =
             Add<HAllocate>(heap_number_constant, HType::HeapObject(),
-                pretenure_flag, HEAP_NUMBER_TYPE);
+                pretenure_flag, MUTABLE_HEAP_NUMBER_TYPE);
         AddStoreMapConstant(double_box,
-            isolate()->factory()->heap_number_map());
-        Add<HStoreNamedField>(double_box, HObjectAccess::ForHeapNumberValue(),
-                              Add<HConstant>(value));
+            isolate()->factory()->mutable_heap_number_map());
+        // Unwrap the mutable heap number from the boilerplate.
+        HValue* double_value =
+            Add<HConstant>(Handle<HeapNumber>::cast(value)->value());
+        Add<HStoreNamedField>(
+            double_box, HObjectAccess::ForHeapNumberValue(), double_value);
         value_instruction = double_box;
       } else if (representation.IsSmi()) {
         value_instruction = value->IsUninitialized()
index 07bc737c14cb667a8f9254119e8e22915b22ae65..5f9bd78ff00eb0b888d0dc682a244ab721f4afb7 100644 (file)
@@ -1683,14 +1683,18 @@ void MacroAssembler::UndoAllocationInNewSpace(Register object) {
 void MacroAssembler::AllocateHeapNumber(Register result,
                                         Register scratch1,
                                         Register scratch2,
-                                        Label* gc_required) {
+                                        Label* gc_required,
+                                        MutableMode mode) {
   // Allocate heap number in new space.
   Allocate(HeapNumber::kSize, result, scratch1, scratch2, gc_required,
            TAG_OBJECT);
 
+  Handle<Map> map = mode == MUTABLE
+      ? isolate()->factory()->mutable_heap_number_map()
+      : isolate()->factory()->heap_number_map();
+
   // Set the map.
-  mov(FieldOperand(result, HeapObject::kMapOffset),
-      Immediate(isolate()->factory()->heap_number_map()));
+  mov(FieldOperand(result, HeapObject::kMapOffset), Immediate(map));
 }
 
 
index b0b61f7a88f52d5242635cd67e07a77f73bebf87..fdb22286c7a72ad450fcd9b784b3e57da0174110 100644 (file)
@@ -638,7 +638,8 @@ class MacroAssembler: public Assembler {
   void AllocateHeapNumber(Register result,
                           Register scratch1,
                           Register scratch2,
-                          Label* gc_required);
+                          Label* gc_required,
+                          MutableMode mode = IMMUTABLE);
 
   // Allocate a sequential string. All the header fields of the string object
   // are initialized.
index ca6a5bbb31ddadb10bfe903ead7aee7cd57cfb3d..d36014026e3e0b8f79b85614e4ebb2d9c61afe5a 100644 (file)
@@ -523,7 +523,7 @@ void StoreStubCompiler::GenerateStoreTransition(MacroAssembler* masm,
     }
   } else if (representation.IsDouble()) {
     Label do_store, heap_number;
-    __ AllocateHeapNumber(storage_reg, scratch1, scratch2, slow);
+    __ AllocateHeapNumber(storage_reg, scratch1, scratch2, slow, MUTABLE);
 
     __ JumpIfNotSmi(value_reg, &heap_number);
     __ SmiUntag(value_reg);
index 09320ab2bddfe3ae1ff84e35ce776ca00ea1f8fb..cdbb50d6e88c3281b6a7fd83b1935f3c828cfe26 100644 (file)
@@ -387,11 +387,9 @@ Handle<Object> JsonParser<seq_ascii>::ParseJsonObject() {
           Representation expected_representation = details.representation();
 
           if (value->FitsRepresentation(expected_representation)) {
-            // If the target representation is double and the value is already
-            // double, use the existing box.
-            if (value->IsSmi() && expected_representation.IsDouble()) {
-              value = factory()->NewHeapNumber(
-                  Handle<Smi>::cast(value)->value());
+            if (expected_representation.IsDouble()) {
+              value = Object::NewStorageFor(isolate(), value,
+                                            expected_representation);
             } else if (expected_representation.IsHeapObject() &&
                        !target->instance_descriptors()->GetFieldType(
                            descriptor)->NowContains(value)) {
index 0b5abbcdc9e1c0303f890e3cb4d33de08ee114a7..0a453ef67f6d6404d714cf13e6a29a011014b426 100644 (file)
@@ -407,6 +407,7 @@ BasicJsonStringifier::Result BasicJsonStringifier::Serialize_(
 
   switch (HeapObject::cast(*object)->map()->instance_type()) {
     case HEAP_NUMBER_TYPE:
+    case MUTABLE_HEAP_NUMBER_TYPE:
       if (deferred_string_key) SerializeDeferredKey(comma, key);
       return SerializeHeapNumber(Handle<HeapNumber>::cast(object));
     case ODDBALL_TYPE:
index 2e05171a779855e3b0fcfb945c96bc0cf79ced5c..085e2421c65bc7239b12429cdafd268cdddd7813 100644 (file)
@@ -292,6 +292,7 @@ class VerifyNativeContextSeparationVisitor: public ObjectVisitor {
           case CODE_TYPE:
           case FIXED_DOUBLE_ARRAY_TYPE:
           case HEAP_NUMBER_TYPE:
+          case MUTABLE_HEAP_NUMBER_TYPE:
           case INTERCEPTOR_INFO_TYPE:
           case ODDBALL_TYPE:
           case SCRIPT_TYPE:
index 52e80d29f3a767fa1987e578db44f9af38f9531e..6ef33f4449507daf2359fcfa4d578a0ff2b4ec3e 100644 (file)
@@ -3274,14 +3274,19 @@ void MacroAssembler::AllocateHeapNumber(Register result,
                                         Register scratch2,
                                         Register heap_number_map,
                                         Label* need_gc,
-                                        TaggingMode tagging_mode) {
+                                        TaggingMode tagging_mode,
+                                        MutableMode mode) {
   // Allocate an object in the heap for the heap number and tag it as a heap
   // object.
   Allocate(HeapNumber::kSize, result, scratch1, scratch2, need_gc,
            tagging_mode == TAG_RESULT ? TAG_OBJECT : NO_ALLOCATION_FLAGS);
 
+  Heap::RootListIndex map_index = mode == MUTABLE
+      ? Heap::kMutableHeapNumberMapRootIndex
+      : Heap::kHeapNumberMapRootIndex;
+  AssertIsRoot(heap_number_map, map_index);
+
   // Store heap number map in the allocated object.
-  AssertIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
   if (tagging_mode == TAG_RESULT) {
     sw(heap_number_map, FieldMemOperand(result, HeapObject::kMapOffset));
   } else {
index 2fb04d2afd1d97eece2180464d96010f58d9e262..47d6bcf11ffc8dd463930810a6061f881d48b1c1 100644 (file)
@@ -546,7 +546,8 @@ class MacroAssembler: public Assembler {
                           Register scratch2,
                           Register heap_number_map,
                           Label* gc_required,
-                          TaggingMode tagging_mode = TAG_RESULT);
+                          TaggingMode tagging_mode = TAG_RESULT,
+                          MutableMode mode = IMMUTABLE);
   void AllocateHeapNumberWithValue(Register result,
                                    FPURegister value,
                                    Register scratch1,
index 16c69d57addad40d22644f29281a48ba7c8e6bb1..e3d8fe14d939bac5a876a8cb580631acd97536ce 100644 (file)
@@ -413,8 +413,9 @@ void StoreStubCompiler::GenerateStoreTransition(MacroAssembler* masm,
     }
   } else if (representation.IsDouble()) {
     Label do_store, heap_number;
-    __ LoadRoot(scratch3, Heap::kHeapNumberMapRootIndex);
-    __ AllocateHeapNumber(storage_reg, scratch1, scratch2, scratch3, slow);
+    __ LoadRoot(scratch3, Heap::kMutableHeapNumberMapRootIndex);
+    __ AllocateHeapNumber(storage_reg, scratch1, scratch2, scratch3, slow,
+                          TAG_RESULT, MUTABLE);
 
     __ JumpIfNotSmi(value_reg, &heap_number);
     __ SmiUntag(scratch1, value_reg);
index 344fe711bd4827895495c1bb869d8c0fa3f52149..ee141549ed1d5d4ca43efbecacd4c83b7c0f5444 100644 (file)
@@ -54,6 +54,7 @@ void HeapObject::HeapObjectVerify() {
       Map::cast(this)->MapVerify();
       break;
     case HEAP_NUMBER_TYPE:
+    case MUTABLE_HEAP_NUMBER_TYPE:
       HeapNumber::cast(this)->HeapNumberVerify();
       break;
     case FIXED_ARRAY_TYPE:
@@ -205,7 +206,7 @@ void Symbol::SymbolVerify() {
 
 
 void HeapNumber::HeapNumberVerify() {
-  CHECK(IsHeapNumber());
+  CHECK(IsHeapNumber() || IsMutableHeapNumber());
 }
 
 
@@ -263,7 +264,7 @@ void JSObject::JSObjectVerify() {
         Representation r = descriptors->GetDetails(i).representation();
         FieldIndex index = FieldIndex::ForDescriptor(map(), i);
         Object* value = RawFastPropertyAt(index);
-        if (r.IsDouble()) ASSERT(value->IsHeapNumber());
+        if (r.IsDouble()) ASSERT(value->IsMutableHeapNumber());
         if (value->IsUninitialized()) continue;
         if (r.IsSmi()) ASSERT(value->IsSmi());
         if (r.IsHeapObject()) ASSERT(value->IsHeapObject());
index 16d0acd6e3caf045cb3bc470b71f7b3b841b18fa..d600b4ec25103c250d527f6914fc91895f380543 100644 (file)
@@ -165,6 +165,7 @@ bool Object::IsHeapObject() const {
 
 
 TYPE_CHECKER(HeapNumber, HEAP_NUMBER_TYPE)
+TYPE_CHECKER(MutableHeapNumber, MUTABLE_HEAP_NUMBER_TYPE)
 TYPE_CHECKER(Symbol, SYMBOL_TYPE)
 
 
@@ -277,10 +278,27 @@ Handle<Object> Object::NewStorageFor(Isolate* isolate,
     return handle(Smi::FromInt(0), isolate);
   }
   if (!representation.IsDouble()) return object;
+  double value;
   if (object->IsUninitialized()) {
-    return isolate->factory()->NewHeapNumber(0);
+    value = 0;
+  } else if (object->IsMutableHeapNumber()) {
+    value = HeapNumber::cast(*object)->value();
+  } else {
+    value = object->Number();
+  }
+  return isolate->factory()->NewHeapNumber(value, MUTABLE);
+}
+
+
+Handle<Object> Object::WrapForRead(Isolate* isolate,
+                                   Handle<Object> object,
+                                   Representation representation) {
+  ASSERT(!object->IsUninitialized());
+  if (!representation.IsDouble()) {
+    ASSERT(object->FitsRepresentation(representation));
+    return object;
   }
-  return isolate->factory()->NewHeapNumber(object->Number());
+  return isolate->factory()->NewHeapNumber(HeapNumber::cast(*object)->value());
 }
 
 
@@ -3079,7 +3097,6 @@ CAST_ACCESSOR(FixedTypedArrayBase)
 CAST_ACCESSOR(Foreign)
 CAST_ACCESSOR(FreeSpace)
 CAST_ACCESSOR(GlobalObject)
-CAST_ACCESSOR(HeapNumber)
 CAST_ACCESSOR(HeapObject)
 CAST_ACCESSOR(JSArray)
 CAST_ACCESSOR(JSArrayBuffer)
@@ -5944,6 +5961,18 @@ ACCESSORS(JSModule, scope_info, ScopeInfo, kScopeInfoOffset)
 ACCESSORS(JSValue, value, Object, kValueOffset)
 
 
+HeapNumber* HeapNumber::cast(Object* object) {
+  SLOW_ASSERT(object->IsHeapNumber() || object->IsMutableHeapNumber());
+  return reinterpret_cast<HeapNumber*>(object);
+}
+
+
+const HeapNumber* HeapNumber::cast(const Object* object) {
+  SLOW_ASSERT(object->IsHeapNumber() || object->IsMutableHeapNumber());
+  return reinterpret_cast<const HeapNumber*>(object);
+}
+
+
 ACCESSORS(JSDate, value, Object, kValueOffset)
 ACCESSORS(JSDate, cache_stamp, Object, kCacheStampOffset)
 ACCESSORS(JSDate, year, Object, kYearOffset)
index e86059b7198631ce25d32437c35cd9cc9b64155c..5d9c517f4d243d528b8e7e11b012ef4983cabfcc 100644 (file)
@@ -64,6 +64,11 @@ void HeapObject::HeapObjectPrint(FILE* out) {
     case HEAP_NUMBER_TYPE:
       HeapNumber::cast(this)->HeapNumberPrint(out);
       break;
+    case MUTABLE_HEAP_NUMBER_TYPE:
+      PrintF(out, "<mutable ");
+      HeapNumber::cast(this)->HeapNumberPrint(out);
+      PrintF(out, ">");
+      break;
     case FIXED_DOUBLE_ARRAY_TYPE:
       FixedDoubleArray::cast(this)->FixedDoubleArrayPrint(out);
       break;
index f2f47b0f88f657dc987ef70b4ce4caa16aa5dacd..7d10fbd2791c0219cb6eb1e2a6a009ab0d504cd6 100644 (file)
@@ -148,6 +148,7 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
       return kVisitJSFunction;
 
     case HEAP_NUMBER_TYPE:
+    case MUTABLE_HEAP_NUMBER_TYPE:
 #define EXTERNAL_ARRAY_CASE(Type, type, TYPE, ctype, size)                     \
     case EXTERNAL_##TYPE##_ARRAY_TYPE:
 
index aab918e231badf963241cf7e08c564180bdb5e86..94364997c4e4cbbab591a5de3492852d8b8981d1 100644 (file)
@@ -1540,6 +1540,11 @@ void HeapObject::HeapObjectShortPrint(StringStream* accumulator) {
       HeapNumber::cast(this)->HeapNumberPrint(accumulator);
       accumulator->Put('>');
       break;
+    case MUTABLE_HEAP_NUMBER_TYPE:
+      accumulator->Add("<MutableNumber: ");
+      HeapNumber::cast(this)->HeapNumberPrint(accumulator);
+      accumulator->Put('>');
+      break;
     case JS_PROXY_TYPE:
       accumulator->Add("<JSProxy>");
       break;
@@ -1665,6 +1670,7 @@ void HeapObject::IterateBody(InstanceType type, int object_size,
       break;
 
     case HEAP_NUMBER_TYPE:
+    case MUTABLE_HEAP_NUMBER_TYPE:
     case FILLER_TYPE:
     case BYTE_ARRAY_TYPE:
     case FREE_SPACE_TYPE:
@@ -1706,7 +1712,7 @@ bool HeapNumber::HeapNumberBooleanValue() {
 
 
 void HeapNumber::HeapNumberPrint(FILE* out) {
-  PrintF(out, "%.16g", Number());
+  PrintF(out, "%.16g", value());
 }
 
 
@@ -1718,7 +1724,7 @@ void HeapNumber::HeapNumberPrint(StringStream* accumulator) {
   // print that using vsnprintf (which may truncate but never allocate if
   // there is no more space in the buffer).
   EmbeddedVector<char, 100> buffer;
-  SNPrintF(buffer, "%.16g", Number());
+  SNPrintF(buffer, "%.16g", value());
   accumulator->Add("%s", buffer.start());
 }
 
@@ -2070,8 +2076,8 @@ bool Map::InstancesNeedRewriting(Map* target,
   DescriptorArray* new_desc = target->instance_descriptors();
   int limit = NumberOfOwnDescriptors();
   for (int i = 0; i < limit; i++) {
-    if (new_desc->GetDetails(i).representation().IsDouble() &&
-        !old_desc->GetDetails(i).representation().IsDouble()) {
+    if (new_desc->GetDetails(i).representation().IsDouble() !=
+        old_desc->GetDetails(i).representation().IsDouble()) {
       return true;
     }
   }
@@ -2168,7 +2174,7 @@ void JSObject::MigrateFastToFast(Handle<JSObject> object, Handle<Map> new_map) {
     PropertyDetails details = new_map->GetLastDescriptorDetails();
     Handle<Object> value;
     if (details.representation().IsDouble()) {
-      value = isolate->factory()->NewHeapNumber(0);
+      value = isolate->factory()->NewHeapNumber(0, MUTABLE);
     } else {
       value = isolate->factory()->uninitialized_value();
     }
@@ -2216,6 +2222,9 @@ void JSObject::MigrateFastToFast(Handle<JSObject> object, Handle<Map> new_map) {
         value = handle(Smi::FromInt(0), isolate);
       }
       value = Object::NewStorageFor(isolate, value, details.representation());
+    } else if (old_details.representation().IsDouble() &&
+               !details.representation().IsDouble()) {
+      value = Object::WrapForRead(isolate, value, old_details.representation());
     }
     ASSERT(!(details.representation().IsDouble() && value->IsSmi()));
     int target_index = new_descriptors->GetFieldIndex(i) - inobject;
@@ -2228,7 +2237,7 @@ void JSObject::MigrateFastToFast(Handle<JSObject> object, Handle<Map> new_map) {
     if (details.type() != FIELD) continue;
     Handle<Object> value;
     if (details.representation().IsDouble()) {
-      value = isolate->factory()->NewHeapNumber(0);
+      value = isolate->factory()->NewHeapNumber(0, MUTABLE);
     } else {
       value = isolate->factory()->uninitialized_value();
     }
@@ -3975,6 +3984,7 @@ void JSObject::WriteToField(int descriptor, Object* value) {
     // Nothing more to be done.
     if (value->IsUninitialized()) return;
     HeapNumber* box = HeapNumber::cast(RawFastPropertyAt(index));
+    ASSERT(box->IsMutableHeapNumber());
     box->set_value(value->Number());
   } else {
     FastPropertyAtPut(index, value);
@@ -4622,6 +4632,11 @@ void JSObject::MigrateFastToSlow(Handle<JSObject> object,
         FieldIndex index = FieldIndex::ForDescriptor(*map, i);
         Handle<Object> value(
             object->RawFastPropertyAt(index), isolate);
+        if (details.representation().IsDouble()) {
+          ASSERT(value->IsMutableHeapNumber());
+          Handle<HeapNumber> old = Handle<HeapNumber>::cast(value);
+          value = isolate->factory()->NewHeapNumber(old->value());
+        }
         PropertyDetails d =
             PropertyDetails(details.attributes(), NORMAL, i + 1);
         dictionary = NameDictionary::Add(dictionary, key, value, d);
@@ -5794,7 +5809,7 @@ Handle<Object> JSObject::FastPropertyAt(Handle<JSObject> object,
                                         FieldIndex index) {
   Isolate* isolate = object->GetIsolate();
   Handle<Object> raw_value(object->RawFastPropertyAt(index), isolate);
-  return Object::NewStorageFor(isolate, raw_value, representation);
+  return Object::WrapForRead(isolate, raw_value, representation);
 }
 
 
@@ -6985,7 +7000,7 @@ Object* JSObject::SlowReverseLookup(Object* value) {
         Object* property =
             RawFastPropertyAt(FieldIndex::ForDescriptor(map(), i));
         if (descs->GetDetails(i).representation().IsDouble()) {
-          ASSERT(property->IsHeapNumber());
+          ASSERT(property->IsMutableHeapNumber());
           if (value->IsNumber() && property->Number() == value->Number()) {
             return descs->GetKey(i);
           }
index a494778a989c3c3963f8ba288b18ec5fa5359b58..94e974ca504942c1901e575e275483b26e8a4dea 100644 (file)
@@ -168,6 +168,12 @@ enum ContextualMode {
 };
 
 
+enum MutableMode {
+  MUTABLE,
+  IMMUTABLE
+};
+
+
 static const int kGrowICDelta = STORE_AND_GROW_NO_TRANSITION -
     STANDARD_STORE;
 STATIC_ASSERT(STANDARD_STORE == 0);
@@ -352,6 +358,7 @@ const int kStubMinorKeyBits = kBitsPerInt - kSmiTagSize - kStubMajorKeyBits;
   V(PROPERTY_CELL_TYPE)                                                        \
                                                                                \
   V(HEAP_NUMBER_TYPE)                                                          \
+  V(MUTABLE_HEAP_NUMBER_TYPE)                                                  \
   V(FOREIGN_TYPE)                                                              \
   V(BYTE_ARRAY_TYPE)                                                           \
   V(FREE_SPACE_TYPE)                                                           \
@@ -680,6 +687,7 @@ enum InstanceType {
   // "Data", objects that cannot contain non-map-word pointers to heap
   // objects.
   HEAP_NUMBER_TYPE,
+  MUTABLE_HEAP_NUMBER_TYPE,
   FOREIGN_TYPE,
   BYTE_ARRAY_TYPE,
   FREE_SPACE_TYPE,
@@ -900,6 +908,7 @@ template <class C> inline bool Is(Object* obj);
 
 #define HEAP_OBJECT_TYPE_LIST(V)               \
   V(HeapNumber)                                \
+  V(MutableHeapNumber)                         \
   V(Name)                                      \
   V(UniqueName)                                \
   V(String)                                    \
@@ -1427,7 +1436,7 @@ class Object {
     } else if (FLAG_track_fields && representation.IsSmi()) {
       return IsSmi();
     } else if (FLAG_track_double_fields && representation.IsDouble()) {
-      return IsNumber();
+      return IsMutableHeapNumber() || IsNumber();
     } else if (FLAG_track_heap_object_fields && representation.IsHeapObject()) {
       return IsHeapObject();
     }
@@ -1440,6 +1449,10 @@ class Object {
                                              Handle<Object> object,
                                              Representation representation);
 
+  inline static Handle<Object> WrapForRead(Isolate* isolate,
+                                           Handle<Object> object,
+                                           Representation representation);
+
   // Returns true if the object is of the correct type to be used as a
   // implementation of a JSObject's elements.
   inline bool HasValidElements();
index eba17a19437b86de7c502faf8f1f3d99cf2ba8f8..ba1389318c9428316d0dc033a83b6410f4cfad13 100644 (file)
@@ -14515,8 +14515,8 @@ RUNTIME_FUNCTION(Runtime_LoadMutableDouble) {
                    object->properties()->length());
   }
   Handle<Object> raw_value(object->RawFastPropertyAt(field_index), isolate);
-  RUNTIME_ASSERT(raw_value->IsNumber() || raw_value->IsUninitialized());
-  return *Object::NewStorageFor(isolate, raw_value, Representation::Double());
+  RUNTIME_ASSERT(raw_value->IsMutableHeapNumber());
+  return *Object::WrapForRead(isolate, raw_value, Representation::Double());
 }
 
 
index 73c59df78766cebbdf2920025c2fbdfd823b076c..83ef3628dd07bade4ca4eb4e53715a2def1fd14f 100644 (file)
@@ -4593,12 +4593,17 @@ void MacroAssembler::UndoAllocationInNewSpace(Register object) {
 
 void MacroAssembler::AllocateHeapNumber(Register result,
                                         Register scratch,
-                                        Label* gc_required) {
+                                        Label* gc_required,
+                                        MutableMode mode) {
   // Allocate heap number in new space.
   Allocate(HeapNumber::kSize, result, scratch, no_reg, gc_required, TAG_OBJECT);
 
+  Heap::RootListIndex map_index = mode == MUTABLE
+      ? Heap::kMutableHeapNumberMapRootIndex
+      : Heap::kHeapNumberMapRootIndex;
+
   // Set the map.
-  LoadRoot(kScratchRegister, Heap::kHeapNumberMapRootIndex);
+  LoadRoot(kScratchRegister, map_index);
   movp(FieldOperand(result, HeapObject::kMapOffset), kScratchRegister);
 }
 
index 8a0ffa61540b44bb4cf3dd55891049c4f0b56faa..7a27776a5e04dfeaa5767ba8d90b9f34cbaf8c35 100644 (file)
@@ -1186,7 +1186,8 @@ class MacroAssembler: public Assembler {
   // space is full.
   void AllocateHeapNumber(Register result,
                           Register scratch,
-                          Label* gc_required);
+                          Label* gc_required,
+                          MutableMode mode = IMMUTABLE);
 
   // Allocate a sequential string. All the header fields of the string object
   // are initialized.
index f7b2fe5b40e23c2065cbe2c0aa79c1ee0f7f4527..617f2b848a52a06f8d17c0a019758f6df09963f6 100644 (file)
@@ -489,7 +489,7 @@ void StoreStubCompiler::GenerateStoreTransition(MacroAssembler* masm,
     }
   } else if (representation.IsDouble()) {
     Label do_store, heap_number;
-    __ AllocateHeapNumber(storage_reg, scratch1, slow);
+    __ AllocateHeapNumber(storage_reg, scratch1, slow, MUTABLE);
 
     __ JumpIfNotSmi(value_reg, &heap_number);
     __ SmiToInteger32(scratch1, value_reg);
index 585f9c023b4b58129a0c4eddb4cd248be3c280a6..111aed9a2e6b6b9cf6591f223de96348943fe643 100644 (file)
@@ -1576,14 +1576,18 @@ void MacroAssembler::UndoAllocationInNewSpace(Register object) {
 void MacroAssembler::AllocateHeapNumber(Register result,
                                         Register scratch1,
                                         Register scratch2,
-                                        Label* gc_required) {
+                                        Label* gc_required,
+                                        MutableMode mode) {
   // Allocate heap number in new space.
   Allocate(HeapNumber::kSize, result, scratch1, scratch2, gc_required,
            TAG_OBJECT);
 
+  Handle<Map> map = mode == MUTABLE
+      ? isolate()->factory()->mutable_heap_number_map()
+      : isolate()->factory()->heap_number_map();
+
   // Set the map.
-  mov(FieldOperand(result, HeapObject::kMapOffset),
-      Immediate(isolate()->factory()->heap_number_map()));
+  mov(FieldOperand(result, HeapObject::kMapOffset), Immediate(map));
 }
 
 
index 84141e6bc36a721a91475405677d10ddb876aa85..70770a66261d38227877650791d892129ef7854f 100644 (file)
@@ -617,7 +617,8 @@ class MacroAssembler: public Assembler {
   void AllocateHeapNumber(Register result,
                           Register scratch1,
                           Register scratch2,
-                          Label* gc_required);
+                          Label* gc_required,
+                          MutableMode mode = IMMUTABLE);
 
   // Allocate a sequential string. All the header fields of the string object
   // are initialized.
index c963a528c30107ff7186d5666c314c7152032b43..a947e6e61369e59486e89db3bd5d964b305f6961 100644 (file)
@@ -523,7 +523,7 @@ void StoreStubCompiler::GenerateStoreTransition(MacroAssembler* masm,
     }
   } else if (representation.IsDouble()) {
     Label do_store, heap_number;
-    __ AllocateHeapNumber(storage_reg, scratch1, scratch2, slow);
+    __ AllocateHeapNumber(storage_reg, scratch1, scratch2, slow, MUTABLE);
 
     __ JumpIfNotSmi(value_reg, &heap_number);
     __ SmiUntag(value_reg);
index cd34d068860f00319c96c942f876cb0361f378dc..3b8e754e40b73fad2f0d2ca9d0657a302716de07 100644 (file)
@@ -1688,7 +1688,7 @@ TEST(GetHeapValueForNode) {
   v8::HandleScope scope(env->GetIsolate());
   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
 
-  CompileRun("a = { s_prop: \'value\', n_prop: 0.1 };");
+  CompileRun("a = { s_prop: \'value\', n_prop: \'value2\' };");
   const v8::HeapSnapshot* snapshot =
       heap_profiler->TakeHeapSnapshot(v8_str("value"));
   CHECK(ValidateSnapshot(snapshot));
@@ -1709,10 +1709,9 @@ TEST(GetHeapValueForNode) {
   CHECK(js_s_prop == heap_profiler->FindObjectById(s_prop->GetId()));
   const v8::HeapGraphNode* n_prop =
       GetProperty(obj, v8::HeapGraphEdge::kProperty, "n_prop");
-  v8::Local<v8::Number> js_n_prop =
-      js_obj->Get(v8_str("n_prop")).As<v8::Number>();
-  CHECK(js_n_prop->NumberValue() ==
-        heap_profiler->FindObjectById(n_prop->GetId())->NumberValue());
+  v8::Local<v8::String> js_n_prop =
+      js_obj->Get(v8_str("n_prop")).As<v8::String>();
+  CHECK(js_n_prop == heap_profiler->FindObjectById(n_prop->GetId()));
 }
 
 
diff --git a/test/mjsunit/migrations.js b/test/mjsunit/migrations.js
new file mode 100644 (file)
index 0000000..4faae8e
--- /dev/null
@@ -0,0 +1,307 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-ayle license that can be
+// found in the LICENSE file.
+
+// Flags: --allow-natives-syntax --track-fields --expose-gc
+
+var global = Function('return this')();
+var verbose = 0;
+
+function test(ctor_desc, use_desc, migr_desc) {
+  var n = 5;
+  var objects = [];
+  var results = [];
+
+  if (verbose) {
+    print();
+    print("===========================================================");
+    print("=== " + ctor_desc.name +
+          " | " + use_desc.name + " |--> " + migr_desc.name);
+    print("===========================================================");
+  }
+
+  // Clean ICs and transitions.
+  %NotifyContextDisposed();
+  gc(); gc(); gc();
+
+
+  // create objects
+  if (verbose) {
+    print("-----------------------------");
+    print("--- construct");
+    print();
+  }
+  for (var i = 0; i < n; i++) {
+    objects[i] = ctor_desc.ctor.apply(ctor_desc, ctor_desc.args(i));
+  }
+
+  try {
+    // use them
+    if (verbose) {
+      print("-----------------------------");
+      print("--- use 1");
+      print();
+    }
+    var use = use_desc.use1;
+    for (var i = 0; i < n; i++) {
+      if (i == 3) %OptimizeFunctionOnNextCall(use);
+      results[i] = use(objects[i], i);
+    }
+
+    // trigger migrations
+    if (verbose) {
+      print("-----------------------------");
+      print("--- trigger migration");
+      print();
+    }
+    var migr = migr_desc.migr;
+    for (var i = 0; i < n; i++) {
+      if (i == 3) %OptimizeFunctionOnNextCall(migr);
+      migr(objects[i], i);
+    }
+
+    // use again
+    if (verbose) {
+      print("-----------------------------");
+      print("--- use 2");
+      print();
+    }
+    var use = use_desc.use2 !== undefined ? use_desc.use2 : use_desc.use1;
+    for (var i = 0; i < n; i++) {
+      if (i == 3) %OptimizeFunctionOnNextCall(use);
+      results[i] = use(objects[i], i);
+      if (verbose >= 2) print(results[i]);
+    }
+
+  } catch (e) {
+    if (verbose) print("--- incompatible use: " + e);
+  }
+  return results;
+}
+
+
+var ctors = [
+  {
+    name: "none-to-double",
+    ctor: function(v) { return {a: v}; },
+    args: function(i) { return [1.5 + i]; },
+  },
+  {
+    name: "double",
+    ctor: function(v) { var o = {}; o.a = v; return o; },
+    args: function(i) { return [1.5 + i]; },
+  },
+  {
+    name: "none-to-smi",
+    ctor: function(v) { return {a: v}; },
+    args: function(i) { return [i]; },
+  },
+  {
+    name: "smi",
+    ctor: function(v) { var o = {}; o.a = v; return o; },
+    args: function(i) { return [i]; },
+  },
+  {
+    name: "none-to-object",
+    ctor: function(v) { return {a: v}; },
+    args: function(i) { return ["s"]; },
+  },
+  {
+    name: "object",
+    ctor: function(v) { var o = {}; o.a = v; return o; },
+    args: function(i) { return ["s"]; },
+  },
+  {
+    name: "{a:, b:, c:}",
+    ctor: function(v1, v2, v3) { return {a: v1, b: v2, c: v3}; },
+    args: function(i)    { return [1.5 + i, 1.6, 1.7]; },
+  },
+  {
+    name: "{a..h:}",
+    ctor: function(v) { var o = {}; o.h=o.g=o.f=o.e=o.d=o.c=o.b=o.a=v; return o; },
+    args: function(i) { return [1.5 + i]; },
+  },
+  {
+    name: "1",
+    ctor: function(v) { var o = 1; o.a = v; return o; },
+    args: function(i) { return [1.5 + i]; },
+  },
+  {
+    name: "f()",
+    ctor: function(v) { var o = function() { return v;}; o.a = v; return o; },
+    args: function(i) { return [1.5 + i]; },
+  },
+  {
+    name: "f().bind",
+    ctor: function(v) { var o = function(a,b,c) { return a+b+c; }; o = o.bind(o, v, v+1, v+2.2); return o; },
+    args: function(i) { return [1.5 + i]; },
+  },
+  {
+    name: "dictionary elements",
+    ctor: function(v) { var o = []; o[1] = v; o[200000] = v; return o; },
+    args: function(i) { return [1.5 + i]; },
+  },
+  {
+    name: "json",
+    ctor: function(v) { var json = '{"a":' + v + ',"b":' + v + '}'; return JSON.parse(json); },
+    args: function(i) { return [1.5 + i]; },
+  },
+  {
+    name: "fast accessors",
+    accessor: {
+        get: function() { return this.a_; },
+        set: function(value) {this.a_ = value; },
+        configurable: true,
+    },
+    ctor: function(v) {
+      var o = {a_:v};
+      Object.defineProperty(o, "a", this.accessor);
+      return o;
+    },
+    args: function(i) { return [1.5 + i]; },
+  },
+  {
+    name: "slow accessor",
+    accessor1: { value: this.a_, configurable: true },
+    accessor2: {
+        get: function() { return this.a_; },
+        set: function(value) {this.a_ = value; },
+        configurable: true,
+    },
+    ctor: function(v) {
+      var o = {a_:v};
+      Object.defineProperty(o, "a", this.accessor1);
+      Object.defineProperty(o, "a", this.accessor2);
+      return o;
+    },
+    args: function(i) { return [1.5 + i]; },
+  },
+  {
+    name: "slow",
+    proto: {},
+    ctor: function(v) {
+      var o = {__proto__: this.proto};
+      o.a = v;
+      for (var i = 0; %HasFastProperties(o); i++) o["f"+i] = v;
+      return o;
+    },
+    args: function(i) { return [1.5 + i]; },
+  },
+  {
+    name: "global",
+    ctor: function(v) { return global; },
+    args: function(i) { return [i]; },
+  },
+];
+
+
+
+var uses = [
+  {
+    name: "o.a+1.0",
+    use1: function(o, i) { return o.a + 1.0; },
+    use2: function(o, i) { return o.a + 1.1; },
+  },
+  {
+    name: "o.b+1.0",
+    use1: function(o, i) { return o.b + 1.0; },
+    use2: function(o, i) { return o.b + 1.1; },
+  },
+  {
+    name: "o[1]+1.0",
+    use1: function(o, i) { return o[1] + 1.0; },
+    use2: function(o, i) { return o[1] + 1.1; },
+  },
+  {
+    name: "o[-1]+1.0",
+    use1: function(o, i) { return o[-1] + 1.0; },
+    use2: function(o, i) { return o[-1] + 1.1; },
+  },
+  {
+    name: "()",
+    use1: function(o, i) { return o() + 1.0; },
+    use2: function(o, i) { return o() + 1.1; },
+  },
+];
+
+
+
+var migrations = [
+  {
+    name: "to smi",
+    migr: function(o, i) { if (i == 0) o.a = 1; },
+  },
+  {
+    name: "to double",
+    migr: function(o, i) { if (i == 0) o.a = 1.1; },
+  },
+  {
+    name: "to object",
+    migr: function(o, i) { if (i == 0) o.a = {}; },
+  },
+  {
+    name: "set prototype {}",
+    migr: function(o, i) { o.__proto__ = {}; },
+  },
+  {
+    name: "%FunctionSetPrototype",
+    migr: function(o, i) { %FunctionSetPrototype(o, null); },
+  },
+  {
+    name: "modify prototype",
+    migr: function(o, i) { if (i == 0) o.__proto__.__proto1__ = [,,,5,,,]; },
+  },
+  {
+    name: "freeze prototype",
+    migr: function(o, i) { if (i == 0) Object.freeze(o.__proto__); },
+  },
+  {
+    name: "delete and re-add property",
+    migr: function(o, i) { var v = o.a; delete o.a; o.a = v; },
+  },
+  {
+    name: "modify prototype",
+    migr: function(o, i) { if (i >= 0) o.__proto__ = {}; },
+  },
+  {
+    name: "set property callback",
+    migr: function(o, i) {
+      Object.defineProperty(o, "a", {
+        get: function() { return 1.5 + i; },
+        set: function(value) {},
+        configurable: true,
+      });
+    },
+  },
+  {
+    name: "observe",
+    migr: function(o, i) { Object.observe(o, function(){}); },
+  },
+  {
+    name: "%EnableAccessChecks",
+    migr: function(o, i) { %EnableAccessChecks(o); },
+  },
+  {
+    name: "%DisableAccessChecks",
+    migr: function(o, i) { if (o !== global) %DisableAccessChecks(o); },
+  },
+  {
+    name: "seal",
+    migr: function(o, i) { Object.seal(o); },
+  },
+  { // Must be the last in the sequence, because after the global object freeze
+    // the other modifications does not make sence.
+    name: "freeze",
+    migr: function(o, i) { Object.freeze(o); },
+  },
+];
+
+
+
+migrations.forEach(function(migr) {
+  uses.forEach(function(use) {
+    ctors.forEach(function(ctor) {
+      test(ctor, use, migr);
+    });
+  });
+});
index 967846bc8fce99b65e7438f569d8f0ec03dc98b0..277eb87dab8c8d0df2aaf51506909afc91f1a819 100644 (file)
@@ -87,6 +87,7 @@
   ##############################################################################
   # Skip long running tests that time out in debug mode.
   'generated-transition-stub': [PASS, ['mode == debug', SKIP]],
+  'migrations': [PASS, ['mode == debug', SLOW]],
 
   ##############################################################################
   # This test sets the umask on a per-process basis and hence cannot be