Fix treatment of hidden prototypes in SetProperty.
authorrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 19 Dec 2012 15:17:01 +0000 (15:17 +0000)
committerrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 19 Dec 2012 15:17:01 +0000 (15:17 +0000)
R=svenpanne@chromium.org
BUG=v8:2457

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

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

src/objects.cc
test/cctest/test-api.cc

index cea724f..8175341 100644 (file)
@@ -2920,6 +2920,9 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* lookup,
         lookup, name_raw, value_raw, attributes, strict_mode, store_mode);
   }
 
+  ASSERT(!lookup->IsFound() || lookup->holder() == this ||
+         lookup->holder()->map()->is_hidden_prototype());
+
   // From this point on everything needs to be handlified, because
   // SetPropertyViaPrototypes might call back into JavaScript.
   HandleScope scope(isolate);
@@ -2961,10 +2964,10 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* lookup,
   MaybeObject* result = *value;
   switch (lookup->type()) {
     case NORMAL:
-      result = self->SetNormalizedProperty(lookup, *value);
+      result = lookup->holder()->SetNormalizedProperty(lookup, *value);
       break;
     case FIELD:
-      result = self->FastPropertyAtPut(
+      result = lookup->holder()->FastPropertyAtPut(
           lookup->GetFieldIndex().field_index(), *value);
       break;
     case CONSTANT_FUNCTION:
@@ -2972,21 +2975,17 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* lookup,
       if (*value == lookup->GetConstantFunction()) return *value;
       // Preserve the attributes of this existing property.
       attributes = lookup->GetAttributes();
-      result = self->ConvertDescriptorToField(*name, *value, attributes);
+      result =
+          lookup->holder()->ConvertDescriptorToField(*name, *value, attributes);
       break;
     case CALLBACKS: {
       Object* callback_object = lookup->GetCallbackObject();
-      return self->SetPropertyWithCallback(callback_object,
-                                           *name,
-                                           *value,
-                                           lookup->holder(),
-                                           strict_mode);
+      return self->SetPropertyWithCallback(
+          callback_object, *name, *value, lookup->holder(), strict_mode);
     }
     case INTERCEPTOR:
-      result = self->SetPropertyWithInterceptor(*name,
-                                                *value,
-                                                attributes,
-                                                strict_mode);
+      result = lookup->holder()->SetPropertyWithInterceptor(
+          *name, *value, attributes, strict_mode);
       break;
     case TRANSITION: {
       Map* transition_map = lookup->GetTransitionTarget();
@@ -2998,15 +2997,15 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* lookup,
       if (details.type() == FIELD) {
         if (attributes == details.attributes()) {
           int field_index = descriptors->GetFieldIndex(descriptor);
-          result = self->AddFastPropertyUsingMap(transition_map,
-                                                 *name,
-                                                 *value,
-                                                 field_index);
+          result = lookup->holder()->AddFastPropertyUsingMap(
+              transition_map, *name, *value, field_index);
         } else {
-          result = self->ConvertDescriptorToField(*name, *value, attributes);
+          result = lookup->holder()->ConvertDescriptorToField(
+              *name, *value, attributes);
         }
       } else if (details.type() == CALLBACKS) {
-        result = self->ConvertDescriptorToField(*name, *value, attributes);
+        result = lookup->holder()->ConvertDescriptorToField(
+            *name, *value, attributes);
       } else {
         ASSERT(details.type() == CONSTANT_FUNCTION);
 
@@ -3014,12 +3013,12 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* lookup,
         if (constant_function == *value) {
           // If the same constant function is being added we can simply
           // transition to the target map.
-          self->set_map(transition_map);
+          lookup->holder()->set_map(transition_map);
           result = constant_function;
         } else {
           // Otherwise, replace with a map transition to a new map with a FIELD,
           // even if the value is a constant function.
-          result = self->ConvertTransitionToMapTransition(
+          result = lookup->holder()->ConvertTransitionToMapTransition(
               lookup->GetTransitionIndex(), *name, *value, attributes);
         }
       }
index 60405a6..a30fbda 100644 (file)
@@ -8233,6 +8233,66 @@ THREADED_TEST(HiddenPrototype) {
 }
 
 
+THREADED_TEST(HiddenPrototypeSet) {
+  v8::HandleScope handle_scope;
+  LocalContext context;
+
+  Local<v8::FunctionTemplate> ot = v8::FunctionTemplate::New();
+  Local<v8::FunctionTemplate> ht = v8::FunctionTemplate::New();
+  ht->SetHiddenPrototype(true);
+  Local<v8::FunctionTemplate> pt = v8::FunctionTemplate::New();
+  ht->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
+
+  Local<v8::Object> o = ot->GetFunction()->NewInstance();
+  Local<v8::Object> h = ht->GetFunction()->NewInstance();
+  Local<v8::Object> p = pt->GetFunction()->NewInstance();
+  o->Set(v8_str("__proto__"), h);
+  h->Set(v8_str("__proto__"), p);
+
+  // Setting a property that exists on the hidden prototype goes there.
+  o->Set(v8_str("x"), v8_num(7));
+  CHECK_EQ(7, o->Get(v8_str("x"))->Int32Value());
+  CHECK_EQ(7, h->Get(v8_str("x"))->Int32Value());
+  CHECK(p->Get(v8_str("x"))->IsUndefined());
+
+  // Setting a new property should not be forwarded to the hidden prototype.
+  o->Set(v8_str("y"), v8_num(6));
+  CHECK_EQ(6, o->Get(v8_str("y"))->Int32Value());
+  CHECK(h->Get(v8_str("y"))->IsUndefined());
+  CHECK(p->Get(v8_str("y"))->IsUndefined());
+
+  // Setting a property that only exists on a prototype of the hidden prototype
+  // is treated normally again.
+  p->Set(v8_str("z"), v8_num(8));
+  CHECK_EQ(8, o->Get(v8_str("z"))->Int32Value());
+  CHECK_EQ(8, h->Get(v8_str("z"))->Int32Value());
+  CHECK_EQ(8, p->Get(v8_str("z"))->Int32Value());
+  o->Set(v8_str("z"), v8_num(9));
+  CHECK_EQ(9, o->Get(v8_str("z"))->Int32Value());
+  CHECK_EQ(8, h->Get(v8_str("z"))->Int32Value());
+  CHECK_EQ(8, p->Get(v8_str("z"))->Int32Value());
+}
+
+
+// Regression test for issue 2457.
+THREADED_TEST(HiddenPrototypeIdentityHash) {
+  v8::HandleScope handle_scope;
+  LocalContext context;
+
+  Handle<FunctionTemplate> t = FunctionTemplate::New();
+  t->SetHiddenPrototype(true);
+  t->InstanceTemplate()->Set(v8_str("foo"), v8_num(75));
+  Handle<Object> p = t->GetFunction()->NewInstance();
+  Handle<Object> o = Object::New();
+  o->SetPrototype(p);
+
+  int hash = o->GetIdentityHash();
+  USE(hash);
+  o->Set(v8_str("foo"), v8_num(42));
+  ASSERT_EQ(hash, o->GetIdentityHash());
+}
+
+
 THREADED_TEST(SetPrototype) {
   v8::HandleScope handle_scope;
   LocalContext context;