Reland r22082 "Replace HeapNumber as doublebox with an explicit MutableHeapNumber."
authorishell@chromium.org <ishell@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 1 Jul 2014 15:02:31 +0000 (15:02 +0000)
committerishell@chromium.org <ishell@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 1 Jul 2014 15:02:31 +0000 (15:02 +0000)
R=verwaest@chromium.org

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22129 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 6607c45f6ea9290f63775a3b6b421ecd9e10372b..5730fdf8e7fa5bccdf55eaa2de8b45145c606297 100644 (file)
@@ -5587,7 +5587,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.
@@ -5602,10 +5602,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 c35fb9e506a5cd237f596962afd9d2b025a207fa..0d7a1d8bcb52e830f7fda05efb5a04334206bdf9 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 2d915757bccf345c44c630296bc74d868d96e0ac..e30096ae91e2d597455c232c3efc8780d78ed3a4 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 bdf04c05f0096aef3d5ee619e22b67a2aa962e12..1f7c152495592e4234f66f4a6a0e9be636d66f8c 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 eb8f0d282ef43f5056d80a367ed53cc01d99bc97..343a9e39968ea172cf79a88b972c264a70dd5d86 100644 (file)
@@ -3570,7 +3570,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);
 
@@ -3579,6 +3580,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
@@ -3588,7 +3593,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;
@@ -3598,7 +3603,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 544d7bd25fe87d637aa1db62c9ddb5587dba1186..95d69990d2f2b62608c3877c5d05fd130ca534d6 100644 (file)
@@ -1372,7 +1372,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 a0ce997fef47f4995a97e1b89480257b15b61de6..85774d9825c43edf19b20f316932b77e7e45055f 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 08fb148c0db1df66d455fe34656c740e428a2bde..4c98b8473569ec20c6f255de1d587f0afaeee12a 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 8e903f16516fc4db91310ccd28cabc2505fee3f5..a09c7841e37655e6572f5be9a42b6a15a3e96676 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 25051da051bfc2a1d731c4d04653ab6cee750b8d..3736b4bd6afc874faf07694b5d101abd224798ed 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 80d3c1061450bb3026e8b2b4eeffd77335745ddf..a713d402769ea933d3ac534e259918b81c78c722 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 812fec127ad34973d6d2a0115fc51e98a9cbab72..3d9797423e0c817b287fac2a513df3b4f66c13ba 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,13 @@ 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(base::OS::nan_value(), TENURED));
-  set_infinity_value(*factory->NewHeapNumber(V8_INFINITY, TENURED));
+  set_nan_value(
+      *factory->NewHeapNumber(base::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 754b5588e05f89b11c8fffc7d77df82d5d29872a..f3b02ce6388e28423ac9390fa3631357f1924a43 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 a4b72a72aacd2b5a721360d8b13b1194f3d2c22c..d21541b281c6b5d9a6aa6a10e1d0c74abf06ad18 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(),
@@ -10950,11 +10951,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 b3cb507640d9ffc52a09189f2652c6ad7e3ab836..8276bc1fbc9ea284c453a80995a52d2bdcefda67 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 4b5d169734fc0bfe18fb96276fdda2fcdbd48c39..487800142871a70505b6028ace046f6826019ecb 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 e4f8f8ceacf6254605badb42638760237cdaa4ac..78ac378c1e09dc4ffc9d8bc4c462e94857257377 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 8846f19bb0dbaeec80192bf9709b3e3f1029cf2c..191b94bfe6cc424dff880d0d848bf6bd546324d9 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 ce5cd90d9ca7a63e28ad616a98ad404ff8543d88..5060e6884cadc3f279003210d0c5dd81ae45a560 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 c07b64bf91183fb6d2c91a41591c643c3cdbf849..8644827f8e9b6bf4cc8f309f52add3e3d805ad8d 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 bca5a5522d8a90c2e210ce659262e3106241be9f..a581d9eb24f563800289eda162036137b65aaf8a 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)
@@ -5949,6 +5966,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 176828ec9e7b111422dc0a3b304167cbcf2aa6a8..785bd4ce4f579d7b04d43fda5e97360d4233dcdc 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);
@@ -4644,6 +4654,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);
@@ -5811,7 +5826,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);
 }
 
 
@@ -7002,7 +7017,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 526760afff9c1a04abc120f69fc8ed0e4a23204c..600c371a3c790ef05101065a1eb17361f56daf4a 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 44f09161c2f78849a10e66815840b6f91cd72c18..9b665a272f53b9371fd7510a8793c00067b85f3a 100644 (file)
@@ -14542,8 +14542,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 67ad2a91951578ae07686026aa9971c031967672..b821177882a3b49dcc90c4ba3554b41c396b4f87 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 9e334239e988cc40430acac0fcdaffdd80c43842..b305392ea29472921a5ae8b90cf7f732d4a625cb 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 c549668038158496da82dd4ea39456977d5d258b..65682b855c843133a4f2adf40c50c68602d772c3 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 f8ec4ba8d1efccb7922ecc14949c496468c3a0f8..faab67a8c4c5c9dd145cfaed6d272b065f533ef4 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 bef9897fd45cd635364f2b54320144e6edae9119..db6b9da2a15aa1ea59a5342c1b8b83d619e6782e 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..6a2ea64
--- /dev/null
@@ -0,0 +1,311 @@
+// 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) {
+      if (typeof (o) !== 'function') %EnableAccessChecks(o);
+    },
+  },
+  {
+    name: "%DisableAccessChecks",
+    migr: function(o, i) {
+      if ((typeof (o) !== 'function') && (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 c18a9acef05618612c3dba21d12bcc29cce9e7b4..2e656a9ffc7e078a1750e52e2627f2efd4727ea5 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