Revision: 12277
Author:   [email protected]
Date:     Wed Aug  8 07:43:45 2012
Log:      Fix handling of accessors on trunk.  This is a combination of
r12271, r12264 and r12247, and is intended to fix bugs 140473,
140430 and 138388.  Unfortunately it probably breaks the MIPS
port.
Review URL: https://chromiumcodereview.appspot.com/10827220
http://code.google.com/p/v8/source/detail?r=12277

Modified:
 /trunk/src/arm/stub-cache-arm.cc
 /trunk/src/bootstrapper.cc
 /trunk/src/heap.cc
 /trunk/src/hydrogen-instructions.cc
 /trunk/src/hydrogen.cc
 /trunk/src/ia32/stub-cache-ia32.cc
 /trunk/src/ic.cc
 /trunk/src/objects-inl.h
 /trunk/src/objects.cc
 /trunk/src/objects.h
 /trunk/src/stub-cache.h
 /trunk/src/type-info.cc
 /trunk/src/version.cc
 /trunk/src/x64/stub-cache-x64.cc
 /trunk/test/cctest/test-api.cc

=======================================
--- /trunk/src/arm/stub-cache-arm.cc    Fri Jul 27 01:03:27 2012
+++ /trunk/src/arm/stub-cache-arm.cc    Wed Aug  8 07:43:45 2012
@@ -1228,6 +1228,42 @@
   __ LoadHeapObject(r0, value);
   __ Ret();
 }
+
+
+void StubCompiler::GenerateDictionaryLoadCallback(Register receiver,
+                                                  Register name_reg,
+                                                  Register scratch1,
+                                                  Register scratch2,
+                                                  Register scratch3,
+ Handle<AccessorInfo> callback,
+                                                  Handle<String> name,
+                                                  Label* miss) {
+  Register dictionary = scratch1;
+  Register index = scratch2;
+ __ ldr(dictionary, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
+
+  // Probe the dictionary.
+  Label probe_done;
+  StringDictionaryLookupStub::GeneratePositiveLookup(masm(),
+                                                     miss,
+                                                     &probe_done,
+                                                     dictionary,
+                                                     name_reg,
+ index, // Set if we hit.
+                                                     scratch3);
+  __ bind(&probe_done);
+
+ // If probing finds an entry in the dictionary, check that the value is the
+  // callback.
+  const int kElementsStartOffset =
+      StringDictionary::kHeaderSize +
+      StringDictionary::kElementsStartIndex * kPointerSize;
+  const int kValueOffset = kElementsStartOffset + kPointerSize;
+  __ add(scratch1, dictionary, Operand(kValueOffset - kHeapObjectTag));
+  __ ldr(scratch3, MemOperand(scratch1, index, LSL, kPointerSizeLog2));
+  __ cmp(scratch3, Operand(callback));
+  __ b(ne, miss);
+}


 void StubCompiler::GenerateLoadCallback(Handle<JSObject> object,
@@ -1247,6 +1283,11 @@
   Register reg = CheckPrototypes(object, receiver, holder, scratch1,
                                  scratch2, scratch3, name, miss);

+  if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) {
+    GenerateDictionaryLoadCallback(
+ receiver, name_reg, scratch1, scratch2, scratch3, callback, name, miss);
+  }
+
// Build AccessorInfo::args_ list on the stack and push property name below
   // the exit frame to make GC aware of them and store pointers to them.
   __ push(receiver);
=======================================
--- /trunk/src/bootstrapper.cc  Tue Jul 24 00:59:48 2012
+++ /trunk/src/bootstrapper.cc  Wed Aug  8 07:43:45 2012
@@ -747,6 +747,7 @@
   }

   js_global_function->initial_map()->set_is_hidden_prototype();
+  js_global_function->initial_map()->set_dictionary_map(true);
   Handle<GlobalObject> inner_global =
       factory()->NewGlobalObject(js_global_function);
   if (inner_global_out != NULL) {
@@ -1431,6 +1432,7 @@

   Handle<String> name = factory()->LookupAsciiSymbol("builtins");
   builtins_fun->shared()->set_instance_class_name(*name);
+  builtins_fun->initial_map()->set_dictionary_map(true);

   // Allocate the builtins object.
   Handle<JSBuiltinsObject> builtins =
=======================================
--- /trunk/src/heap.cc  Wed Aug  1 04:14:42 2012
+++ /trunk/src/heap.cc  Wed Aug  8 07:43:45 2012
@@ -2424,6 +2424,7 @@
     if (!maybe_obj->ToObject(&obj)) return false;
   }
   Map* global_context_map = Map::cast(obj);
+  global_context_map->set_dictionary_map(true);
global_context_map->set_visitor_id(StaticVisitorBase::kVisitGlobalContext);
   set_global_context_map(global_context_map);

@@ -4108,6 +4109,7 @@
 MaybeObject* Heap::AllocateGlobalObject(JSFunction* constructor) {
   ASSERT(constructor->has_initial_map());
   Map* map = constructor->initial_map();
+  ASSERT(map->is_dictionary_map());

   // Make sure no field properties are described in the initial map.
   // This guarantees us that normalizing the properties does not
@@ -4158,6 +4160,7 @@
   Map* new_map;
   MaybeObject* maybe_map = map->CopyDropDescriptors();
   if (!maybe_map->To(&new_map)) return maybe_map;
+  new_map->set_dictionary_map(true);

   // Set up the global object as a normalized object.
   global->set_map(new_map);
=======================================
--- /trunk/src/hydrogen-instructions.cc Tue Jul 24 00:59:48 2012
+++ /trunk/src/hydrogen-instructions.cc Wed Aug  8 07:43:45 2012
@@ -1682,6 +1682,7 @@
     if (current->IsJSGlobalProxy() ||
         current->IsGlobalObject() ||
         !current->IsJSObject() ||
+        JSObject::cast(current)->map()->has_named_interceptor() ||
         JSObject::cast(current)->IsAccessCheckNeeded() ||
         !JSObject::cast(current)->HasFastProperties()) {
       return false;
@@ -1743,6 +1744,11 @@
           break;
       }
     } else if (lookup.IsCacheable() &&
+ // For dicts the lookup on the map will fail, but the object may + // contain the property so we cannot generate a negative lookup
+               // (which would just be a map check and return undefined).
+               !map->is_dictionary_map() &&
+               !map->has_named_interceptor() &&
                PrototypeChainCanNeverResolve(map, name)) {
       negative_lookups.Add(types->at(i), zone);
     }
=======================================
--- /trunk/src/hydrogen.cc      Fri Aug  3 03:44:23 2012
+++ /trunk/src/hydrogen.cc      Wed Aug  8 07:43:45 2012
@@ -5215,8 +5215,13 @@

     HInstruction* instr = NULL;
     SmallMapList* types = expr->GetReceiverTypes();
-    if (expr->IsMonomorphic()) {
-      Handle<Map> map = types->first();
+    bool monomorphic = expr->IsMonomorphic();
+    Handle<Map> map;
+    if (monomorphic) {
+      map = types->first();
+      if (map->is_dictionary_map()) monomorphic = false;
+    }
+    if (monomorphic) {
       Handle<AccessorPair> accessors;
       Handle<JSObject> holder;
       if (LookupAccessorPair(map, name, &accessors, &holder)) {
@@ -5392,8 +5397,14 @@
       Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
       Handle<Map> map;
       HInstruction* load;
-      if (prop->IsMonomorphic()) {
+      bool monomorphic = prop->IsMonomorphic();
+      if (monomorphic) {
         map = prop->GetReceiverTypes()->first();
+        // We can't generate code for a monomorphic dict mode load so
+        // just pretend it is not monomorphic.
+        if (map->is_dictionary_map()) monomorphic = false;
+      }
+      if (monomorphic) {
         Handle<AccessorPair> accessors;
         Handle<JSObject> holder;
         if (LookupAccessorPair(map, name, &accessors, &holder)) {
@@ -5416,7 +5427,7 @@
       if (instr->HasObservableSideEffects()) AddSimulate(operation->id());

       HInstruction* store;
-      if (map.is_null()) {
+      if (!monomorphic) {
         // If we don't know the monomorphic type, do a generic store.
         CHECK_ALIVE(store = BuildStoreNamedGeneric(object, name, instr));
       } else {
@@ -5717,6 +5728,7 @@
                                                        Property* expr,
                                                        Handle<Map> map) {
   // Handle a load from a known field.
+  ASSERT(!map->is_dictionary_map());
   LookupResult lookup(isolate());
   map->LookupDescriptor(NULL, *name, &lookup);
   if (lookup.IsField()) {
@@ -6350,8 +6362,13 @@
     Handle<String> name = expr->key()->AsLiteral()->AsPropertyName();
     SmallMapList* types = expr->GetReceiverTypes();

+    bool monomorphic = expr->IsMonomorphic();
+    Handle<Map> map;
     if (expr->IsMonomorphic()) {
-      Handle<Map> map = types->first();
+      map = types->first();
+      if (map->is_dictionary_map()) monomorphic = false;
+    }
+    if (monomorphic) {
       Handle<AccessorPair> accessors;
       Handle<JSObject> holder;
       if (LookupAccessorPair(map, name, &accessors, &holder)) {
@@ -7860,8 +7877,12 @@
       Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
       Handle<Map> map;
       HInstruction* load;
-      if (prop->IsMonomorphic()) {
+      bool monomorphic = prop->IsMonomorphic();
+      if (monomorphic) {
         map = prop->GetReceiverTypes()->first();
+        if (map->is_dictionary_map()) monomorphic = false;
+      }
+      if (monomorphic) {
         Handle<AccessorPair> accessors;
         Handle<JSObject> holder;
         if (LookupAccessorPair(map, name, &accessors, &holder)) {
@@ -7879,7 +7900,7 @@
       input = Pop();

       HInstruction* store;
-      if (map.is_null()) {
+      if (!monomorphic) {
         // If we don't know the monomorphic type, do a generic store.
         CHECK_ALIVE(store = BuildStoreNamedGeneric(object, name, after));
       } else {
=======================================
--- /trunk/src/ia32/stub-cache-ia32.cc  Thu Jul  5 01:22:44 2012
+++ /trunk/src/ia32/stub-cache-ia32.cc  Wed Aug  8 07:43:45 2012
@@ -1050,6 +1050,42 @@
   GenerateFastPropertyLoad(masm(), eax, reg, holder, index);
   __ ret(0);
 }
+
+
+void StubCompiler::GenerateDictionaryLoadCallback(Register receiver,
+                                                  Register name_reg,
+                                                  Register scratch1,
+                                                  Register scratch2,
+                                                  Register scratch3,
+ Handle<AccessorInfo> callback,
+                                                  Handle<String> name,
+                                                  Label* miss) {
+  Register dictionary = scratch1;
+  __ mov(dictionary, FieldOperand(receiver, JSObject::kPropertiesOffset));
+
+  // Probe the dictionary.
+  Label probe_done;
+  StringDictionaryLookupStub::GeneratePositiveLookup(masm(),
+                                                     miss,
+                                                     &probe_done,
+                                                     dictionary,
+                                                     name_reg,
+                                                     scratch2,
+                                                     scratch3);
+  __ bind(&probe_done);
+
+  // If probing finds an entry in the dictionary, scratch2 contains the
+  // index into the dictionary. Check that the value is the callback.
+  Register index = scratch2;
+  const int kElementsStartOffset =
+      StringDictionary::kHeaderSize +
+      StringDictionary::kElementsStartIndex * kPointerSize;
+  const int kValueOffset = kElementsStartOffset + kPointerSize;
+  __ mov(scratch3,
+ Operand(dictionary, index, times_4, kValueOffset - kHeapObjectTag));
+  __ cmp(scratch3, callback);
+  __ j(not_equal, miss);
+}


 void StubCompiler::GenerateLoadCallback(Handle<JSObject> object,
@@ -1069,6 +1105,11 @@
   Register reg = CheckPrototypes(object, receiver, holder, scratch1,
                                  scratch2, scratch3, name, miss);

+  if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) {
+    GenerateDictionaryLoadCallback(
+ receiver, name_reg, scratch1, scratch2, scratch3, callback, name, miss);
+  }
+
// Insert additional parameters into the stack frame above return address.
   ASSERT(!scratch3.is(reg));
   __ pop(scratch3);  // Get return address to place it below.
=======================================
--- /trunk/src/ic.cc    Tue Jul 24 00:59:48 2012
+++ /trunk/src/ic.cc    Wed Aug  8 07:43:45 2012
@@ -989,7 +989,6 @@
         if (callback->IsAccessorInfo()) {
           Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(callback);
           if (v8::ToCData<Address>(info->getter()) == 0) return;
-          if (!receiver->HasFastProperties()) return;
           if (!info->IsCompatibleReceiver(*receiver)) return;
           code = isolate()->stub_cache()->ComputeLoadCallback(
               name, receiver, holder, info);
@@ -997,7 +996,6 @@
Handle<Object> getter(Handle<AccessorPair>::cast(callback)->getter());
           if (!getter->IsJSFunction()) return;
           if (holder->IsGlobalObject()) return;
-          if (!receiver->HasFastProperties()) return;
           code = isolate()->stub_cache()->ComputeLoadViaGetter(
               name, receiver, holder, Handle<JSFunction>::cast(getter));
         } else {
@@ -1266,7 +1264,7 @@
         Handle<AccessorInfo> callback =
             Handle<AccessorInfo>::cast(callback_object);
         if (v8::ToCData<Address>(callback->getter()) == 0) return;
-        if (!receiver->HasFastProperties()) return;
+        if (!holder->HasFastProperties()) return;
         if (!callback->IsCompatibleReceiver(*receiver)) return;
         code = isolate()->stub_cache()->ComputeKeyedLoadCallback(
             name, receiver, holder, callback);
@@ -1325,7 +1323,9 @@
// that we explicitly exclude native accessors for now, because the stubs
     // are not yet prepared for this scenario.
     receiver->Lookup(*name, lookup);
-    if (!lookup->IsPropertyCallbacks()) return false;
+    if (!lookup->IsPropertyCallbacks()) {
+      return false;
+    }
     Handle<Object> callback(lookup->GetCallbackObject());
     return callback->IsAccessorPair() && StoreICableLookup(lookup);
   }
@@ -1487,9 +1487,10 @@
     case CALLBACKS: {
       Handle<Object> callback(lookup->GetCallbackObject());
       if (callback->IsAccessorInfo()) {
+        ASSERT(*holder == *receiver);  // LookupForWrite checks this.
         Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(callback);
         if (v8::ToCData<Address>(info->setter()) == 0) return;
-        if (!receiver->HasFastProperties()) return;
+        if (!holder->HasFastProperties()) return;
         ASSERT(info->IsCompatibleReceiver(*receiver));
         code = isolate()->stub_cache()->ComputeStoreCallback(
             name, receiver, info, strict_mode);
@@ -1497,7 +1498,7 @@
Handle<Object> setter(Handle<AccessorPair>::cast(callback)->setter());
         if (!setter->IsJSFunction()) return;
         if (holder->IsGlobalObject()) return;
-        if (!receiver->HasFastProperties()) return;
+        if (!holder->HasFastProperties()) return;
         code = isolate()->stub_cache()->ComputeStoreViaSetter(
             name, receiver, holder, Handle<JSFunction>::cast(setter),
             strict_mode);
=======================================
--- /trunk/src/objects-inl.h    Fri Jul 27 01:03:27 2012
+++ /trunk/src/objects-inl.h    Wed Aug  8 07:43:45 2012
@@ -1608,6 +1608,7 @@


 bool JSObject::HasFastProperties() {
+  ASSERT(properties()->IsDictionary() == map()->is_dictionary_map());
   return !properties()->IsDictionary();
 }

@@ -3006,10 +3007,21 @@
 void Map::set_is_shared(bool value) {
   set_bit_field3(IsShared::update(bit_field3(), value));
 }
+

 bool Map::is_shared() {
   return IsShared::decode(bit_field3());
 }
+
+
+void Map::set_dictionary_map(bool value) {
+  set_bit_field3(DictionaryMap::update(bit_field3(), value));
+}
+
+
+bool Map::is_dictionary_map() {
+  return DictionaryMap::decode(bit_field3());
+}


 JSFunction* Map::unchecked_constructor() {
=======================================
--- /trunk/src/objects.cc       Wed Aug  1 04:14:42 2012
+++ /trunk/src/objects.cc       Wed Aug  8 07:43:45 2012
@@ -516,6 +516,7 @@
         MaybeObject* maybe_new_map = map()->CopyDropDescriptors();
         if (!maybe_new_map->To(&new_map)) return maybe_new_map;

+        ASSERT(new_map->is_dictionary_map());
         set_map(new_map);
       }
       JSGlobalPropertyCell* cell =
@@ -3218,6 +3219,7 @@
         fast->CopyNormalized(mode, SHARED_NORMALIZED_MAP);
     if (!maybe_result->ToObject(&result)) return maybe_result;
   }
+  ASSERT(Map::cast(result)->is_dictionary_map());
   set(index, result);
   isolate->counters()->normalized_maps()->Increment();

@@ -3343,6 +3345,7 @@
       current_heap->isolate()->context()->global_context()->
       normalized_map_cache()->Get(this, mode);
   if (!maybe_map->To(&new_map)) return maybe_map;
+  ASSERT(new_map->is_dictionary_map());

   // We have now successfully allocated all the necessary objects.
// Changes can now be made with the guarantee that all of them take effect.
@@ -4512,6 +4515,7 @@
     Map* new_map;
     MaybeObject* maybe_new_map = map()->CopyDropDescriptors();
     if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+    ASSERT(new_map->is_dictionary_map());

     set_map(new_map);
     // When running crankshaft, changing the map is not enough. We
@@ -4873,6 +4877,7 @@

   result->set_code_cache(code_cache());
   result->set_is_shared(sharing == SHARED_NORMALIZED_MAP);
+  result->set_dictionary_map(true);

 #ifdef DEBUG
   if (FLAG_verify_heap && result->is_shared()) {
@@ -7330,7 +7335,8 @@
     bit_field2() == other->bit_field2() &&
     static_cast<uint32_t>(bit_field3()) ==
         LastAddedBits::update(
-            IsShared::update(other->bit_field3(), true),
+ IsShared::update(DictionaryMap::update(other->bit_field3(), true),
+                             true),
             kNoneAdded);
 }

@@ -12573,6 +12579,7 @@
   Map* new_map;
   MaybeObject* maybe_new_map = obj->map()->CopyDropDescriptors();
   if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+  new_map->set_dictionary_map(false);

   if (instance_descriptor_length == 0) {
     ASSERT_LE(unused_property_fields, inobject_props);
=======================================
--- /trunk/src/objects.h        Wed Aug  1 04:14:42 2012
+++ /trunk/src/objects.h        Wed Aug  8 07:43:45 2012
@@ -4684,7 +4684,8 @@

   class IsShared:              public BitField<bool, 0, 1> {};
   class FunctionWithPrototype: public BitField<bool, 1, 1> {};
-  class LastAddedBits:         public BitField<int, 2, 11> {};
+  class DictionaryMap:         public BitField<bool, 2, 1> {};
+  class LastAddedBits:         public BitField<int, 3, 11> {};

   // Tells whether the object in the prototype property will be used
   // for instances created from this function.  If the prototype
@@ -4830,6 +4831,13 @@
   inline void set_is_shared(bool value);
   inline bool is_shared();

+  // Tells whether the map is used for JSObjects in dictionary mode (ie
+ // normalized objects, ie objects for which HasFastProperties returns false). + // A map can never be used for both dictionary mode and fast mode JSObjects.
+  // False by default and for HeapObjects that are not JSObjects.
+  inline void set_dictionary_map(bool value);
+  inline bool is_dictionary_map();
+
   // Tells whether the instance needs security checks when accessing its
   // properties.
   inline void set_is_access_check_needed(bool access_check_needed);
@@ -5150,6 +5158,7 @@
   // Bit positions for bit field 3
   static const int kIsShared = 0;
   static const int kFunctionWithPrototype = 1;
+  static const int kDictionaryMap = 2;

   typedef FixedBodyDescriptor<kPointerFieldsBeginOffset,
                               kPointerFieldsEndOffset,
=======================================
--- /trunk/src/stub-cache.h     Thu Jul  5 01:22:44 2012
+++ /trunk/src/stub-cache.h     Wed Aug  8 07:43:45 2012
@@ -554,6 +554,15 @@
                             Handle<String> name,
                             Label* miss);

+  void GenerateDictionaryLoadCallback(Register receiver,
+                                      Register name_reg,
+                                      Register scratch1,
+                                      Register scratch2,
+                                      Register scratch3,
+                                      Handle<AccessorInfo> callback,
+                                      Handle<String> name,
+                                      Label* miss);
+
   void GenerateLoadConstant(Handle<JSObject> object,
                             Handle<JSObject> holder,
                             Register receiver,
=======================================
--- /trunk/src/type-info.cc     Wed Jun 27 04:12:38 2012
+++ /trunk/src/type-info.cc     Wed Aug  8 07:43:45 2012
@@ -95,11 +95,12 @@
   if (map_or_code->IsMap()) return true;
   if (map_or_code->IsCode()) {
     Handle<Code> code = Handle<Code>::cast(map_or_code);
-    return code->is_keyed_load_stub() &&
+    bool preliminary_checks = code->is_keyed_load_stub() &&
         code->ic_state() == MONOMORPHIC &&
-        Code::ExtractTypeFromFlags(code->flags()) == Code::NORMAL &&
-        code->FindFirstMap() != NULL &&
-        !CanRetainOtherContext(code->FindFirstMap(), *global_context_);
+        Code::ExtractTypeFromFlags(code->flags()) == Code::NORMAL;
+    if (!preliminary_checks) return false;
+    Map* map = code->FindFirstMap();
+    return map != NULL && !CanRetainOtherContext(map, *global_context_);
   }
   return false;
 }
@@ -126,12 +127,14 @@
     bool allow_growth =
         Code::GetKeyedAccessGrowMode(code->extra_ic_state()) ==
         ALLOW_JSARRAY_GROWTH;
-    return code->is_keyed_store_stub() &&
+    bool preliminary_checks =
+        code->is_keyed_store_stub() &&
         !allow_growth &&
         code->ic_state() == MONOMORPHIC &&
-        Code::ExtractTypeFromFlags(code->flags()) == Code::NORMAL &&
-        code->FindFirstMap() != NULL &&
-        !CanRetainOtherContext(code->FindFirstMap(), *global_context_);
+        Code::ExtractTypeFromFlags(code->flags()) == Code::NORMAL;
+    if (!preliminary_checks) return false;
+    Map* map = code->FindFirstMap();
+    return map != NULL && !CanRetainOtherContext(map, *global_context_);
   }
   return false;
 }
=======================================
--- /trunk/src/version.cc       Fri Aug  3 03:44:23 2012
+++ /trunk/src/version.cc       Wed Aug  8 07:43:45 2012
@@ -35,7 +35,7 @@
 #define MAJOR_VERSION     3
 #define MINOR_VERSION     12
 #define BUILD_NUMBER      19
-#define PATCH_LEVEL       1
+#define PATCH_LEVEL       2
 // Use 1 for candidates and 0 otherwise.
 // (Boolean macro values are not supported by all preprocessors.)
 #define IS_CANDIDATE_VERSION 0
=======================================
--- /trunk/src/x64/stub-cache-x64.cc    Thu Jul  5 01:22:44 2012
+++ /trunk/src/x64/stub-cache-x64.cc    Wed Aug  8 07:43:45 2012
@@ -1027,6 +1027,43 @@
   GenerateFastPropertyLoad(masm(), rax, reg, holder, index);
   __ ret(0);
 }
+
+
+void StubCompiler::GenerateDictionaryLoadCallback(Register receiver,
+                                                  Register name_reg,
+                                                  Register scratch1,
+                                                  Register scratch2,
+                                                  Register scratch3,
+ Handle<AccessorInfo> callback,
+                                                  Handle<String> name,
+                                                  Label* miss) {
+  Register dictionary = scratch1;
+  __ movq(dictionary, FieldOperand(receiver, JSObject::kPropertiesOffset));
+
+  // Probe the dictionary.
+  Label probe_done;
+  StringDictionaryLookupStub::GeneratePositiveLookup(masm(),
+                                                     miss,
+                                                     &probe_done,
+                                                     dictionary,
+                                                     name_reg,
+                                                     scratch2,
+                                                     scratch3);
+  __ bind(&probe_done);
+
+  // If probing finds an entry in the dictionary, scratch2 contains the
+  // index into the dictionary. Check that the value is the callback.
+  Register index = scratch2;
+  const int kElementsStartOffset =
+      StringDictionary::kHeaderSize +
+      StringDictionary::kElementsStartIndex * kPointerSize;
+  const int kValueOffset = kElementsStartOffset + kPointerSize;
+  __ movq(scratch3,
+ Operand(dictionary, index, times_8, kValueOffset - kHeapObjectTag));
+  __ movq(scratch2, callback, RelocInfo::EMBEDDED_OBJECT);
+  __ cmpq(scratch3, scratch2);
+  __ j(not_equal, miss);
+}


 void StubCompiler::GenerateLoadCallback(Handle<JSObject> object,
@@ -1046,6 +1083,11 @@
   Register reg = CheckPrototypes(object, receiver, holder, scratch1,
                                  scratch2, scratch3, name, miss);

+  if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) {
+    GenerateDictionaryLoadCallback(
+ receiver, name_reg, scratch1, scratch2, scratch3, callback, name, miss);
+  }
+
// Insert additional parameters into the stack frame above return address.
   ASSERT(!scratch2.is(reg));
   __ pop(scratch2);  // Get return address to place it below.
=======================================
--- /trunk/test/cctest/test-api.cc      Tue Jul 24 00:59:48 2012
+++ /trunk/test/cctest/test-api.cc      Wed Aug  8 07:43:45 2012
@@ -14731,6 +14731,22 @@
                                        const AccessorInfo& info) {
   info.This()->Set(v8_str("y"), v8_num(23));
 }
+
+
+Handle<Value> FooGetInterceptor(Local<String> name,
+                                const AccessorInfo& info) {
+  if (!name->Equals(v8_str("foo"))) return Handle<Value>();
+  return v8_num(42);
+}
+
+
+Handle<Value> FooSetInterceptor(Local<String> name,
+                                Local<Value> value,
+                                const AccessorInfo& info) {
+  if (!name->Equals(v8_str("foo"))) return Handle<Value>();
+  info.This()->Set(v8_str("y"), v8_num(23));
+  return v8_num(23);
+}


 TEST(SetterOnConstructorPrototype) {
@@ -16969,26 +16985,61 @@
 }


-THREADED_TEST(Regress137002a) {
-  i::FLAG_allow_natives_syntax = true;
-  v8::HandleScope scope;
+static void Helper137002(bool do_store,
+                         bool polymorphic,
+                         bool remove_accessor,
+                         bool interceptor) {
   LocalContext context;
   Local<ObjectTemplate> templ = ObjectTemplate::New();
-  templ->SetAccessor(v8_str("foo"),
-                     GetterWhichReturns42,
-                     SetterWhichSetsYOnThisTo23);
+  if (interceptor) {
+    templ->SetNamedPropertyHandler(FooGetInterceptor, FooSetInterceptor);
+  } else {
+    templ->SetAccessor(v8_str("foo"),
+                       GetterWhichReturns42,
+                       SetterWhichSetsYOnThisTo23);
+  }
   context->Global()->Set(v8_str("obj"), templ->NewInstance());

   // Turn monomorphic on slow object with native accessor, then turn
   // polymorphic, finally optimize to create negative lookup and fail.
-  CompileRun("function f(x) { return x.foo; }"
-             "%OptimizeObjectForAddingMultipleProperties(obj, 1);"
-             "obj.__proto__ = null;"
-             "f(obj); f(obj); f({});"
-             "%OptimizeFunctionOnNextCall(f);"
-             "var result = f(obj);");
-  CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
+  CompileRun(do_store ?
+             "function f(x) { x.foo = void 0; }" :
+             "function f(x) { return x.foo; }");
+  CompileRun("obj.y = void 0;");
+  if (!interceptor) {
+    CompileRun("%OptimizeObjectForAddingMultipleProperties(obj, 1);");
+  }
+  CompileRun("obj.__proto__ = null;"
+             "f(obj); f(obj); f(obj);");
+  if (polymorphic) {
+    CompileRun("f({});");
+  }
+  CompileRun("obj.y = void 0;"
+             "%OptimizeFunctionOnNextCall(f);");
+  if (remove_accessor) {
+    CompileRun("delete obj.foo;");
+  }
+  CompileRun("var result = f(obj);");
+  if (do_store) {
+    CompileRun("result = obj.y;");
+  }
+  if (remove_accessor && !interceptor) {
+    CHECK(context->Global()->Get(v8_str("result"))->IsUndefined());
+  } else {
+    CHECK_EQ(do_store ? 23 : 42,
+             context->Global()->Get(v8_str("result"))->Int32Value());
+  }
 }
+
+
+THREADED_TEST(Regress137002a) {
+  i::FLAG_allow_natives_syntax = true;
+  i::FLAG_compilation_cache = false;
+  v8::HandleScope scope;
+  for (int i = 0; i < 16; i++) {
+    Helper137002(i & 8, i & 4, i & 2, i & 1);
+  }
+}


 THREADED_TEST(Regress137002b) {
@@ -17003,12 +17054,48 @@

   // Turn monomorphic on slow object with native accessor, then just
   // delete the property and fail.
-  CompileRun("function f(x) { return x.foo; }"
-             "%OptimizeObjectForAddingMultipleProperties(obj, 1);"
+  CompileRun("function load(x) { return x.foo; }"
+             "function store(x) { x.foo = void 0; }"
+             "function keyed_load(x, key) { return x[key]; }"
+ // Second version of function has a different source (add void 0) + // so that it does not share code with the first version. This
+             // ensures that the ICs are monomorphic.
+             "function load2(x) { void 0; return x.foo; }"
+             "function store2(x) { void 0; x.foo = void 0; }"
+             "function keyed_load2(x, key) { void 0; return x[key]; }"
+
              "obj.__proto__ = null;"
-             "f(obj); f(obj); delete obj.foo;"
-             "var result = f(obj);");
-  CHECK(context->Global()->Get(v8_str("result"))->IsUndefined());
+             "var subobj = {};"
+             "subobj.__proto__ = obj;"
+             "%OptimizeObjectForAddingMultipleProperties(obj, 1);"
+
+             // Make the ICs monomorphic.
+             "load(obj); load(obj);"
+             "load2(subobj); load2(subobj);"
+             "store(obj);"
+             "store2(subobj);"
+             "keyed_load(obj, 'foo'); keyed_load(obj, 'foo');"
+             "keyed_load2(subobj, 'foo'); keyed_load2(subobj, 'foo');"
+
+             // Delete the accessor.  It better not be called any more now.
+             "delete obj.foo;"
+             "obj.y = void 0;"
+             "subobj.y = void 0;"
+
+             "var load_result = load(obj);"
+             "var load_result2 = load2(subobj);"
+             "var keyed_load_result = keyed_load(obj, 'foo');"
+             "var keyed_load_result2 = keyed_load2(subobj, 'foo');"
+             "store(obj);"
+             "store2(subobj);"
+             "var y_from_obj = obj.y;"
+             "var y_from_subobj = subobj.y;");
+  CHECK(context->Global()->Get(v8_str("load_result"))->IsUndefined());
+  CHECK(context->Global()->Get(v8_str("load_result2"))->IsUndefined());
+ CHECK(context->Global()->Get(v8_str("keyed_load_result"))->IsUndefined()); + CHECK(context->Global()->Get(v8_str("keyed_load_result2"))->IsUndefined());
+  CHECK(context->Global()->Get(v8_str("y_from_obj"))->IsUndefined());
+  CHECK(context->Global()->Get(v8_str("y_from_subobj"))->IsUndefined());
 }


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

Reply via email to