Super store
authorarv <arv@chromium.org>
Thu, 19 Feb 2015 16:15:25 +0000 (08:15 -0800)
committerCommit bot <commit-bot@chromium.org>
Thu, 19 Feb 2015 16:15:35 +0000 (16:15 +0000)
When the property is not found on the [[HomeObject]] prototype chain
then we should do a [[DefineOwnProperty]] on the instance.

BUG=v8:3330
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#26754}

src/objects.cc
src/objects.h
src/runtime/runtime-classes.cc
test/mjsunit/harmony/super.js

index 5d210bd..0eda491 100644 (file)
@@ -291,7 +291,7 @@ MaybeHandle<Object> Object::GetPropertyWithAccessor(Handle<Object> receiver,
   if (structure->IsAccessorInfo()) {
     Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(structure);
     if (!info->IsCompatibleReceiver(*receiver)) {
-      Handle<Object> args[2] = { name, receiver };
+      Handle<Object> args[] = {name, receiver};
       THROW_NEW_ERROR(isolate,
                       NewTypeError("incompatible_method_receiver",
                                    HandleVector(args, arraysize(args))),
@@ -356,7 +356,7 @@ MaybeHandle<Object> Object::SetPropertyWithAccessor(
     // api style callbacks
     ExecutableAccessorInfo* info = ExecutableAccessorInfo::cast(*structure);
     if (!info->IsCompatibleReceiver(*receiver)) {
-      Handle<Object> args[2] = { name, receiver };
+      Handle<Object> args[] = {name, receiver};
       THROW_NEW_ERROR(isolate,
                       NewTypeError("incompatible_method_receiver",
                                    HandleVector(args, arraysize(args))),
@@ -383,10 +383,11 @@ MaybeHandle<Object> Object::SetPropertyWithAccessor(
           receiver, Handle<JSReceiver>::cast(setter), value);
     } else {
       if (is_sloppy(language_mode)) return value;
-      Handle<Object> args[2] = { name, holder };
-      THROW_NEW_ERROR(
-          isolate, NewTypeError("no_setter_in_callback", HandleVector(args, 2)),
-          Object);
+      Handle<Object> args[] = {name, holder};
+      THROW_NEW_ERROR(isolate,
+                      NewTypeError("no_setter_in_callback",
+                                   HandleVector(args, arraysize(args))),
+                      Object);
     }
   }
 
@@ -753,13 +754,12 @@ MaybeHandle<Object> Object::SetElementWithReceiver(
   Handle<JSObject> target = Handle<JSObject>::cast(receiver);
   ElementsAccessor* accessor = target->GetElementsAccessor();
   PropertyAttributes attrs = accessor->GetAttributes(target, index);
-  if ((attrs & READ_ONLY) != 0) {
-    return WriteToReadOnlyElement(isolate, receiver, index, value,
-                                  language_mode);
+  if (attrs == ABSENT) {
+    return JSObject::SetElement(target, index, value, NONE, language_mode,
+                                false);
   }
-  PropertyAttributes new_attrs = attrs != ABSENT ? attrs : NONE;
-  return JSObject::SetElement(target, index, value, new_attrs, language_mode,
-                              false);
+  return JSObject::SetElement(target, index, value, attrs, language_mode, false,
+                              DEFINE_PROPERTY);
 }
 
 
@@ -3047,15 +3047,17 @@ MaybeHandle<Object> Object::SetProperty(Handle<Object> object,
 }
 
 
-MaybeHandle<Object> Object::SetProperty(LookupIterator* it,
-                                        Handle<Object> value,
-                                        LanguageMode language_mode,
-                                        StoreFromKeyed store_mode,
-                                        StorePropertyMode data_store_mode) {
+MaybeHandle<Object> Object::SetPropertyInternal(LookupIterator* it,
+                                                Handle<Object> value,
+                                                LanguageMode language_mode,
+                                                StoreFromKeyed store_mode,
+                                                bool* found) {
   // Make sure that the top context does not change when doing callbacks or
   // interceptor calls.
   AssertNoContextChange ncc(it->isolate());
 
+  *found = true;
+
   bool done = false;
   for (; it->IsFound(); it->Next()) {
     switch (it->state()) {
@@ -3135,32 +3137,102 @@ MaybeHandle<Object> Object::SetProperty(LookupIterator* it,
   // the property did not exist yet on the global object itself, we have to
   // throw a reference error in strict mode.
   if (it->GetReceiver()->IsJSGlobalObject() && is_strict(language_mode)) {
-    Handle<Object> args[1] = {it->name()};
-    THROW_NEW_ERROR(it->isolate(),
-                    NewReferenceError("not_defined", HandleVector(args, 1)),
-                    Object);
+    Handle<Object> args[] = {it->name()};
+    THROW_NEW_ERROR(
+        it->isolate(),
+        NewReferenceError("not_defined", HandleVector(args, arraysize(args))),
+        Object);
   }
 
-  if (data_store_mode == SUPER_PROPERTY) {
-    LookupIterator own_lookup(it->GetReceiver(), it->name(),
-                              LookupIterator::OWN);
+  *found = false;
+  return MaybeHandle<Object>();
+}
 
-    return JSObject::SetProperty(&own_lookup, value, language_mode, store_mode,
-                                 NORMAL_PROPERTY);
-  }
 
+MaybeHandle<Object> Object::SetProperty(LookupIterator* it,
+                                        Handle<Object> value,
+                                        LanguageMode language_mode,
+                                        StoreFromKeyed store_mode) {
+  bool found = false;
+  MaybeHandle<Object> result =
+      SetPropertyInternal(it, value, language_mode, store_mode, &found);
+  if (found) return result;
   return AddDataProperty(it, value, NONE, language_mode, store_mode);
 }
 
 
+MaybeHandle<Object> Object::SetSuperProperty(LookupIterator* it,
+                                             Handle<Object> value,
+                                             LanguageMode language_mode,
+                                             StoreFromKeyed store_mode) {
+  bool found = false;
+  MaybeHandle<Object> result =
+      SetPropertyInternal(it, value, language_mode, store_mode, &found);
+  if (found) return result;
+
+  LookupIterator own_lookup(it->GetReceiver(), it->name(), LookupIterator::OWN);
+
+  switch (own_lookup.state()) {
+    case LookupIterator::NOT_FOUND:
+      return JSObject::AddDataProperty(&own_lookup, value, NONE, language_mode,
+                                       store_mode);
+
+    case LookupIterator::DATA: {
+      PropertyDetails details = own_lookup.property_details();
+      if (details.IsConfigurable() || !details.IsReadOnly()) {
+        return JSObject::SetOwnPropertyIgnoreAttributes(
+            Handle<JSObject>::cast(it->GetReceiver()), it->name(), value,
+            details.attributes());
+      }
+      return WriteToReadOnlyProperty(&own_lookup, value, language_mode);
+    }
+
+    case LookupIterator::ACCESSOR: {
+      PropertyDetails details = own_lookup.property_details();
+      if (details.IsConfigurable()) {
+        return JSObject::SetOwnPropertyIgnoreAttributes(
+            Handle<JSObject>::cast(it->GetReceiver()), it->name(), value,
+            details.attributes());
+      }
+
+      return RedefineNonconfigurableProperty(it->isolate(), it->name(), value,
+                                             language_mode);
+    }
+
+    case LookupIterator::TRANSITION:
+      UNREACHABLE();
+      break;
+
+    case LookupIterator::INTERCEPTOR:
+    case LookupIterator::JSPROXY:
+    case LookupIterator::ACCESS_CHECK: {
+      bool found = false;
+      MaybeHandle<Object> result = SetPropertyInternal(
+          &own_lookup, value, language_mode, store_mode, &found);
+      if (found) return result;
+      return SetDataProperty(&own_lookup, value);
+    }
+  }
+
+  UNREACHABLE();
+  return MaybeHandle<Object>();
+}
+
+
 MaybeHandle<Object> Object::WriteToReadOnlyProperty(
     LookupIterator* it, Handle<Object> value, LanguageMode language_mode) {
-  if (is_sloppy(language_mode)) return value;
+  return WriteToReadOnlyProperty(it->isolate(), it->GetReceiver(), it->name(),
+                                 value, language_mode);
+}
 
-  Handle<Object> args[] = {it->name(), it->GetReceiver()};
-  THROW_NEW_ERROR(it->isolate(),
-                  NewTypeError("strict_read_only_property",
-                               HandleVector(args, arraysize(args))),
+
+MaybeHandle<Object> Object::WriteToReadOnlyProperty(
+    Isolate* isolate, Handle<Object> receiver, Handle<Object> name,
+    Handle<Object> value, LanguageMode language_mode) {
+  if (is_sloppy(language_mode)) return value;
+  Handle<Object> args[] = {name, receiver};
+  THROW_NEW_ERROR(isolate, NewTypeError("strict_read_only_property",
+                                        HandleVector(args, arraysize(args))),
                   Object);
 }
 
@@ -3170,11 +3242,18 @@ MaybeHandle<Object> Object::WriteToReadOnlyElement(Isolate* isolate,
                                                    uint32_t index,
                                                    Handle<Object> value,
                                                    LanguageMode language_mode) {
-  if (is_sloppy(language_mode)) return value;
+  return WriteToReadOnlyProperty(isolate, receiver,
+                                 isolate->factory()->NewNumberFromUint(index),
+                                 value, language_mode);
+}
 
-  Handle<Object> args[] = {isolate->factory()->NewNumberFromUint(index),
-                           receiver};
-  THROW_NEW_ERROR(isolate, NewTypeError("strict_read_only_property",
+
+MaybeHandle<Object> Object::RedefineNonconfigurableProperty(
+    Isolate* isolate, Handle<Object> name, Handle<Object> value,
+    LanguageMode language_mode) {
+  if (is_sloppy(language_mode)) return value;
+  Handle<Object> args[] = {name};
+  THROW_NEW_ERROR(isolate, NewTypeError("redefine_disallowed",
                                         HandleVector(args, arraysize(args))),
                   Object);
 }
@@ -3243,7 +3322,7 @@ MaybeHandle<Object> Object::AddDataProperty(LookupIterator* it,
   if (it->state() != LookupIterator::TRANSITION) {
     if (is_sloppy(language_mode)) return value;
 
-    Handle<Object> args[1] = {it->name()};
+    Handle<Object> args[] = {it->name()};
     THROW_NEW_ERROR(it->isolate(),
                     NewTypeError("object_not_extensible",
                                  HandleVector(args, arraysize(args))),
@@ -3789,11 +3868,8 @@ MaybeHandle<Object> JSProxy::SetPropertyViaPrototypesWithHandler(
     DCHECK(writable->IsBoolean());
     *done = writable->IsFalse();
     if (!*done) return isolate->factory()->the_hole_value();
-    if (is_sloppy(language_mode)) return value;
-    Handle<Object> args[] = { name, receiver };
-    THROW_NEW_ERROR(isolate, NewTypeError("strict_read_only_property",
-                                          HandleVector(args, arraysize(args))),
-                    Object);
+    return WriteToReadOnlyProperty(isolate, receiver, name, value,
+                                   language_mode);
   }
 
   // We have an AccessorDescriptor.
@@ -5152,9 +5228,10 @@ MaybeHandle<Object> JSObject::DeleteElement(Handle<JSObject> object,
     if (is_strict(language_mode)) {
       // Deleting a non-configurable property in strict mode.
       Handle<Object> name = factory->NewNumberFromUint(index);
-      Handle<Object> args[2] = { name, object };
-      THROW_NEW_ERROR(isolate, NewTypeError("strict_delete_property",
-                                            HandleVector(args, 2)),
+      Handle<Object> args[] = {name, object};
+      THROW_NEW_ERROR(isolate,
+                      NewTypeError("strict_delete_property",
+                                   HandleVector(args, arraysize(args))),
                       Object);
     }
     return factory->false_value();
@@ -5285,7 +5362,7 @@ MaybeHandle<Object> JSObject::DeleteProperty(Handle<JSObject> object,
         if (!it.IsConfigurable()) {
           // Fail if the property is not configurable.
           if (is_strict(language_mode)) {
-            Handle<Object> args[2] = {name, object};
+            Handle<Object> args[] = {name, object};
             THROW_NEW_ERROR(it.isolate(),
                             NewTypeError("strict_delete_property",
                                          HandleVector(args, arraysize(args))),
@@ -12660,10 +12737,11 @@ MaybeHandle<Object> JSObject::SetElementWithCallback(
     } else {
       if (is_sloppy(language_mode)) return value;
       Handle<Object> key(isolate->factory()->NewNumberFromUint(index));
-      Handle<Object> args[2] = { key, holder };
-      THROW_NEW_ERROR(
-          isolate, NewTypeError("no_setter_in_callback", HandleVector(args, 2)),
-          Object);
+      Handle<Object> args[] = {key, holder};
+      THROW_NEW_ERROR(isolate,
+                      NewTypeError("no_setter_in_callback",
+                                   HandleVector(args, arraysize(args))),
+                      Object);
     }
   }
 
@@ -12850,25 +12928,30 @@ MaybeHandle<Object> JSObject::SetDictionaryElement(
     if (details.type() == ACCESSOR_CONSTANT && set_mode == SET_PROPERTY) {
       return SetElementWithCallback(object, element, index, value, object,
                                     language_mode);
+    } else if (set_mode == DEFINE_PROPERTY && !details.IsConfigurable() &&
+               details.kind() == kAccessor) {
+      return RedefineNonconfigurableProperty(
+          isolate, isolate->factory()->NewNumberFromUint(index),
+          isolate->factory()->undefined_value(), language_mode);
+
+    } else if ((set_mode == DEFINE_PROPERTY && !details.IsConfigurable() &&
+                details.IsReadOnly()) ||
+               (set_mode == SET_PROPERTY && details.IsReadOnly() &&
+                !element->IsTheHole())) {
+      // If a value has not been initialized we allow writing to it even if it
+      // is read-only (a declared const that has not been initialized).
+      return WriteToReadOnlyProperty(
+          isolate, object, isolate->factory()->NewNumberFromUint(index),
+          isolate->factory()->undefined_value(), language_mode);
     } else {
+      DCHECK(details.IsConfigurable() || !details.IsReadOnly() ||
+             element->IsTheHole());
       dictionary->UpdateMaxNumberKey(index);
-      // If a value has not been initialized we allow writing to it even if it
-      // is read-only (a declared const that has not been initialized).  If a
-      // value is being defined we skip attribute checks completely.
       if (set_mode == DEFINE_PROPERTY) {
         details = PropertyDetails(attributes, DATA, details.dictionary_index());
         dictionary->DetailsAtPut(entry, details);
-      } else if (details.IsReadOnly() && !element->IsTheHole()) {
-        if (is_sloppy(language_mode)) {
-          return isolate->factory()->undefined_value();
-        } else {
-          Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
-          Handle<Object> args[2] = { number, object };
-          THROW_NEW_ERROR(isolate, NewTypeError("strict_read_only_property",
-                                                HandleVector(args, 2)),
-                          Object);
-        }
       }
+
       // Elements of the arguments object in slow mode might be slow aliases.
       if (is_arguments && element->IsAliasedArgumentsEntry()) {
         Handle<AliasedArgumentsEntry> entry =
@@ -12900,9 +12983,10 @@ MaybeHandle<Object> JSObject::SetDictionaryElement(
       } else {
         Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
         Handle<String> name = isolate->factory()->NumberToString(number);
-        Handle<Object> args[1] = { name };
-        THROW_NEW_ERROR(isolate, NewTypeError("object_not_extensible",
-                                              HandleVector(args, 1)),
+        Handle<Object> args[] = {name};
+        THROW_NEW_ERROR(isolate,
+                        NewTypeError("object_not_extensible",
+                                     HandleVector(args, arraysize(args))),
                         Object);
       }
     }
@@ -13602,7 +13686,7 @@ bool JSArray::WouldChangeReadOnlyLength(Handle<JSArray> array,
 MaybeHandle<Object> JSArray::ReadOnlyLengthError(Handle<JSArray> array) {
   Isolate* isolate = array->GetIsolate();
   Handle<Name> length = isolate->factory()->length_string();
-  Handle<Object> args[2] = { length, array };
+  Handle<Object> args[] = {length, array};
   THROW_NEW_ERROR(isolate, NewTypeError("strict_read_only_property",
                                         HandleVector(args, arraysize(args))),
                   Object);
index 5aceb10..d4af4a6 100644 (file)
@@ -1017,8 +1017,6 @@ class Object {
     CERTAINLY_NOT_STORE_FROM_KEYED
   };
 
-  enum StorePropertyMode { NORMAL_PROPERTY, SUPER_PROPERTY };
-
   INLINE(bool IsFixedArrayBase() const);
   INLINE(bool IsExternal() const);
   INLINE(bool IsAccessorInfo() const);
@@ -1128,13 +1126,23 @@ class Object {
 
   MUST_USE_RESULT static MaybeHandle<Object> SetProperty(
       LookupIterator* it, Handle<Object> value, LanguageMode language_mode,
-      StoreFromKeyed store_mode,
-      StorePropertyMode data_store_mode = NORMAL_PROPERTY);
+      StoreFromKeyed store_mode);
+
+  MUST_USE_RESULT static MaybeHandle<Object> SetSuperProperty(
+      LookupIterator* it, Handle<Object> value, LanguageMode language_mode,
+      StoreFromKeyed store_mode);
+
   MUST_USE_RESULT static MaybeHandle<Object> WriteToReadOnlyProperty(
       LookupIterator* it, Handle<Object> value, LanguageMode language_mode);
+  MUST_USE_RESULT static MaybeHandle<Object> WriteToReadOnlyProperty(
+      Isolate* isolate, Handle<Object> reciever, Handle<Object> name,
+      Handle<Object> value, LanguageMode language_mode);
   MUST_USE_RESULT static MaybeHandle<Object> WriteToReadOnlyElement(
       Isolate* isolate, Handle<Object> receiver, uint32_t index,
       Handle<Object> value, LanguageMode language_mode);
+  MUST_USE_RESULT static MaybeHandle<Object> RedefineNonconfigurableProperty(
+      Isolate* isolate, Handle<Object> name, Handle<Object> value,
+      LanguageMode language_mode);
   MUST_USE_RESULT static MaybeHandle<Object> SetDataProperty(
       LookupIterator* it, Handle<Object> value);
   MUST_USE_RESULT static MaybeHandle<Object> AddDataProperty(
@@ -1254,6 +1262,11 @@ class Object {
   // Return the map of the root of object's prototype chain.
   Map* GetRootMap(Isolate* isolate);
 
+  // Helper for SetProperty and SetSuperProperty.
+  MUST_USE_RESULT static MaybeHandle<Object> SetPropertyInternal(
+      LookupIterator* it, Handle<Object> value, LanguageMode language_mode,
+      StoreFromKeyed store_mode, bool* found);
+
   DISALLOW_IMPLICIT_CONSTRUCTORS(Object);
 };
 
index 266f4d9..1be0341 100644 (file)
@@ -311,9 +311,8 @@ static Object* StoreToSuper(Isolate* isolate, Handle<JSObject> home_object,
   Handle<Object> result;
   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
       isolate, result,
-      Object::SetProperty(&it, value, language_mode,
-                          Object::CERTAINLY_NOT_STORE_FROM_KEYED,
-                          Object::SUPER_PROPERTY));
+      Object::SetSuperProperty(&it, value, language_mode,
+                               Object::CERTAINLY_NOT_STORE_FROM_KEYED));
   return *result;
 }
 
index d529642..988cef2 100644 (file)
       setCalled = 0;
       getCalled = 0;
       assertEquals('object', typeof this);
-      assertTrue(this instanceof Number)
+      assertInstanceof(this, Number)
       assertEquals(42, this.valueOf());
       assertEquals(1, super.x);
       assertEquals(1, getCalled);
       try {
         super.newProperty = 15;
       } catch (e) { ex = e; }
-      assertTrue(ex instanceof TypeError);
+      assertInstanceof(ex, TypeError);
     }
   }
 
         super.toString();
       } catch(e) { ex = e; }
 
-      assertTrue(ex instanceof TypeError);
+      assertInstanceof(ex, TypeError);
     }
   };
 
       setCalled = 0;
       getCalled = 0;
       assertEquals('object', typeof this);
-      assertTrue(this instanceof Number)
+      assertInstanceof(this, Number)
       assertEquals(42, this.valueOf());
       assertEquals(1, super[x]);
       assertEquals(1, getCalled);
       try {
         super[newProperty] = 15;
       } catch (e) { ex = e; }
-      assertTrue(ex instanceof TypeError);
+      assertInstanceof(ex,TypeError);
     }
   };
 
         super[toString]();
       } catch(e) { ex = e; }
 
-      assertTrue(ex instanceof TypeError);
+      assertInstanceof(ex, TypeError);
     }
   };
   DerivedFromString.prototype.f.call(42);
       setCalled = 0;
       getCalled = 0;
       assertEquals('object', typeof this);
-      assertTrue(this instanceof Number)
+      assertInstanceof(this, Number)
       assertEquals(42, this.valueOf());
       assertEquals(1, super[x]);
       assertEquals(1, getCalled);
       try {
         super[newProperty] = 15;
       } catch (e) { ex = e; }
-      assertTrue(ex instanceof TypeError);
+      assertInstanceof(ex, TypeError);
     }
   };
 
       try {
         super[5] = 'q';
       } catch(e) { ex = e; }
-      assertTrue(ex instanceof TypeError);
+      assertInstanceof(ex, TypeError);
 
       ex = null;
       try {
         super[1024] = 'q';
       } catch(e) { ex = e; }
-      assertTrue(ex instanceof TypeError);
+      assertInstanceof(ex, TypeError);
     }
   };
 
 }());
 
 
-(function TestSetterCreatingOwnProperties() {
-  var setterCalled;
+(function TestSetterCreatingOwnPropertiesReconfigurable() {
   function Base() {}
   function Derived() {}
   Derived.prototype = {
     mSloppy() {
       assertEquals(42, this.ownReadOnly);
       super.ownReadOnly = 55;
-      assertEquals(42, this.ownReadOnly);
+      assertEquals(55, this.ownReadOnly);
+      var descr = Object.getOwnPropertyDescriptor(this, 'ownReadOnly');
+      assertEquals(55, descr.value);
+      assertTrue(descr.configurable);
+      assertFalse(descr.enumerable);
+      assertFalse(descr.writable);
+      assertFalse(Base.prototype.hasOwnProperty('ownReadOnly'));
 
       assertEquals(15, this.ownReadonlyAccessor);
-      super.ownReadonlyAccessor = 55;
+      super.ownReadonlyAccessor = 25;
+      assertEquals(25, this.ownReadonlyAccessor);
+      var descr = Object.getOwnPropertyDescriptor(this, 'ownReadonlyAccessor');
+      assertEquals(25, descr.value);
+      assertTrue(descr.configurable);
+      assertFalse(descr.enumerable);
+      assertTrue(descr.writable);
+      assertFalse(Base.prototype.hasOwnProperty('ownReadonlyAccessor'));
+
+      super.ownSetter = 35;
+      assertEquals(35, this.ownSetter);
+      var descr = Object.getOwnPropertyDescriptor(this, 'ownSetter');
+      assertEquals(35, descr.value);
+      assertTrue(descr.configurable);
+      assertFalse(descr.enumerable);
+      assertTrue(descr.writable);
+      assertFalse(Base.prototype.hasOwnProperty('ownSetter'));
+    },
+    mStrict() {
+      'use strict';
+      assertEquals(42, this.ownReadOnly);
+      super.ownReadOnly = 55;
+      assertEquals(55, this.ownReadOnly);
+      var descr = Object.getOwnPropertyDescriptor(this, 'ownReadOnly');
+      assertEquals(55, descr.value);
+      assertTrue(descr.configurable);
+      assertFalse(descr.enumerable);
+      assertFalse(descr.writable);
+      assertFalse(Base.prototype.hasOwnProperty('ownReadOnly'));
+
       assertEquals(15, this.ownReadonlyAccessor);
+      super.ownReadonlyAccessor = 25;
+      assertEquals(25, this.ownReadonlyAccessor);
+      var descr = Object.getOwnPropertyDescriptor(this, 'ownReadonlyAccessor');
+      assertEquals(25, descr.value);
+      assertTrue(descr.configurable);
+      assertFalse(descr.enumerable);
+      assertTrue(descr.writable);
+      assertFalse(Base.prototype.hasOwnProperty('ownReadonlyAccessor'));
+
+      super.ownSetter = 35;
+      assertEquals(35, this.ownSetter);
+      var descr = Object.getOwnPropertyDescriptor(this, 'ownSetter');
+      assertEquals(35, descr.value);
+      assertTrue(descr.configurable);
+      assertFalse(descr.enumerable);
+      assertTrue(descr.writable);
+      assertFalse(Base.prototype.hasOwnProperty('ownSetter'));
+    },
+  };
+
+  var d = new Derived();
+  Object.defineProperty(d, 'ownReadOnly', {
+    value: 42,
+    writable: false,
+    configurable: true
+  });
+  Object.defineProperty(d, 'ownSetter', {
+    set: function() { assertUnreachable(); },
+    configurable: true
+  });
+  Object.defineProperty(d, 'ownReadonlyAccessor', {
+    get: function() { return 15; },
+    configurable: true
+  });
 
-      setterCalled = 0;
-      super.ownSetter = 42;
-      assertEquals(1, setterCalled);
+  d.mSloppy();
+
+  var d = new Derived();
+  Object.defineProperty(d, 'ownReadOnly', {
+    value: 42,
+    writable: false,
+    configurable: true
+  });
+  Object.defineProperty(d, 'ownSetter', {
+    set: function() { assertUnreachable(); },
+    configurable: true
+  });
+  Object.defineProperty(d, 'ownReadonlyAccessor', {
+    get: function() { return 15; },
+    configurable: true
+  });
+  d.mStrict();
+}());
+
+
+(function TestSetterCreatingOwnPropertiesNonConfigurable() {
+  function Base() {}
+  function Derived() {}
+  Derived.prototype = {
+    __proto__: Base.prototype,
+    mSloppy() {
+      assertEquals(42, this.ownReadOnly);
+      super.ownReadOnly = 55;
+      assertEquals(42, this.ownReadOnly);
+      var descr = Object.getOwnPropertyDescriptor(this, 'ownReadOnly');
+      assertEquals(42, descr.value);
+      assertFalse(descr.configurable);
+      assertFalse(descr.enumerable);
+      assertFalse(descr.writable);
+      assertFalse(Base.prototype.hasOwnProperty('ownReadOnly'));
+
+      assertEquals(15, this.ownReadonlyAccessor);
+      super.ownReadonlyAccessor = 25;
+      assertEquals(15, this.ownReadonlyAccessor);
+      var descr = Object.getOwnPropertyDescriptor(this, 'ownReadonlyAccessor');
+      assertFalse(descr.configurable);
+      assertFalse(descr.enumerable);
+      assertFalse(Base.prototype.hasOwnProperty('ownReadonlyAccessor'));
+
+      super.ownSetter = 35;
+      var descr = Object.getOwnPropertyDescriptor(this, 'ownSetter');
+      assertFalse(descr.configurable);
+      assertFalse(descr.enumerable);
+      assertFalse(Base.prototype.hasOwnProperty('ownSetter'));
     },
     mStrict() {
       'use strict';
-      assertEquals(42, this.ownReadOnly);
       var ex;
+      assertEquals(42, this.ownReadOnly);
       try {
         super.ownReadOnly = 55;
-      } catch(e) { ex = e; }
-      assertTrue(ex instanceof TypeError);
+      } catch (e) {
+        ex = e;
+      }
+      assertInstanceof(ex, TypeError);
+      assertEquals(
+          "Cannot assign to read only property 'ownReadOnly' of #<Base>",
+          ex.message);
       assertEquals(42, this.ownReadOnly);
 
-      assertEquals(15, this.ownReadonlyAccessor);
       ex = null;
+      assertEquals(15, this.ownReadonlyAccessor);
       try {
-        super.ownReadonlyAccessor = 55;
-      } catch(e) { ex = e; }
-      assertTrue(ex instanceof TypeError);
+        super.ownReadonlyAccessor = 25;
+      } catch (e) {
+        ex = e;
+      }
+      assertInstanceof(ex, TypeError);
+      assertEquals('Cannot redefine property: ownReadonlyAccessor', ex.message);
       assertEquals(15, this.ownReadonlyAccessor);
 
-      setterCalled = 0;
-      super.ownSetter = 42;
-      assertEquals(1, setterCalled);
+      ex = null;
+      try {
+        super.ownSetter = 35;
+      } catch (e) {
+        ex = e;
+      }
+      assertInstanceof(ex, TypeError);
+      assertEquals('Cannot redefine property: ownSetter', ex.message);
     }
   };
 
   var d = new Derived();
   Object.defineProperty(d, 'ownReadOnly', { value : 42, writable : false });
   Object.defineProperty(d, 'ownSetter',
-      { set : function() { setterCalled++; } });
+      { set : function() { assertUnreachable(); } });
   Object.defineProperty(d, 'ownReadonlyAccessor',
       { get : function() { return 15; }});
   d.mSloppy();
 }());
 
 
-(function TestKeyedSetterCreatingOwnProperties() {
-  var ownReadOnly = 'ownReadOnly';
-  var ownReadonlyAccessor = 'ownReadonlyAccessor';
-  var ownSetter = 'ownSetter';
-  var setterCalled;
+function TestKeyedSetterCreatingOwnPropertiesReconfigurable(ownReadOnly,
+    ownReadonlyAccessor, ownSetter) {
   function Base() {}
   function Derived() {}
   Derived.prototype = {
     mSloppy() {
       assertEquals(42, this[ownReadOnly]);
       super[ownReadOnly] = 55;
-      assertEquals(42, this[ownReadOnly]);
+      assertEquals(55, this[ownReadOnly]);
+      var descr = Object.getOwnPropertyDescriptor(this, ownReadOnly);
+      assertEquals(55, descr.value);
+      assertTrue(descr.configurable);
+      assertFalse(descr.enumerable);
+      assertFalse(descr.writable);
+      assertFalse(Base.prototype.hasOwnProperty(ownReadOnly));
 
       assertEquals(15, this[ownReadonlyAccessor]);
-      super[ownReadonlyAccessor] = 55;
-      assertEquals(15, this[ownReadonlyAccessor]);
-
-      setterCalled = 0;
-      super[ownSetter] = 42;
-      assertEquals(1, setterCalled);
+      super[ownReadonlyAccessor] = 25;
+      assertEquals(25, this[ownReadonlyAccessor]);
+      var descr = Object.getOwnPropertyDescriptor(this, ownReadonlyAccessor);
+      assertEquals(25, descr.value);
+      assertTrue(descr.configurable);
+      assertFalse(descr.enumerable);
+      assertTrue(descr.writable);
+      assertFalse(Base.prototype.hasOwnProperty(ownReadonlyAccessor));
+
+      super[ownSetter] = 35;
+      assertEquals(35, this[ownSetter]);
+      var descr = Object.getOwnPropertyDescriptor(this, ownSetter);
+      assertEquals(35, descr.value);
+      assertTrue(descr.configurable);
+      assertFalse(descr.enumerable);
+      assertTrue(descr.writable);
+      assertFalse(Base.prototype.hasOwnProperty(ownSetter));
     },
     mStrict() {
       'use strict';
       assertEquals(42, this[ownReadOnly]);
-      var ex;
-      try {
-        super[ownReadOnly] = 55;
-      } catch(e) { ex = e; }
-      assertTrue(ex instanceof TypeError);
-      assertEquals(42, this[ownReadOnly]);
+      super[ownReadOnly] = 55;
+      assertEquals(55, this[ownReadOnly]);
+      var descr = Object.getOwnPropertyDescriptor(this, ownReadOnly);
+      assertEquals(55, descr.value);
+      assertTrue(descr.configurable);
+      assertFalse(descr.enumerable);
+      assertFalse(descr.writable);
+      assertFalse(Base.prototype.hasOwnProperty(ownReadOnly));
 
       assertEquals(15, this[ownReadonlyAccessor]);
-      ex = null;
-      try {
-        super[ownReadonlyAccessor] = 55;
-      } catch(e) { ex = e; }
-      assertTrue(ex instanceof TypeError);
-      assertEquals(15, this[ownReadonlyAccessor]);
-
-      setterCalled = 0;
-      super[ownSetter] = 42;
-      assertEquals(1, setterCalled);
-    }
+      super[ownReadonlyAccessor] = 25;
+      assertEquals(25, this[ownReadonlyAccessor]);
+      var descr = Object.getOwnPropertyDescriptor(this, ownReadonlyAccessor);
+      assertEquals(25, descr.value);
+      assertTrue(descr.configurable);
+      assertFalse(descr.enumerable);
+      assertTrue(descr.writable);
+      assertFalse(Base.prototype.hasOwnProperty(ownReadonlyAccessor));
+
+      super[ownSetter] = 35;
+      assertEquals(35, this[ownSetter]);
+      var descr = Object.getOwnPropertyDescriptor(this, ownSetter);
+      assertEquals(35, descr.value);
+      assertTrue(descr.configurable);
+      assertFalse(descr.enumerable);
+      assertTrue(descr.writable);
+      assertFalse(Base.prototype.hasOwnProperty(ownSetter));
+    },
   };
 
   var d = new Derived();
-  Object.defineProperty(d, 'ownReadOnly', { value : 42, writable : false });
-  Object.defineProperty(d, 'ownSetter',
-      { set : function() { setterCalled++; } });
-  Object.defineProperty(d, 'ownReadonlyAccessor',
-      { get : function() { return 15; }});
+  Object.defineProperty(d, ownReadOnly, {
+    value: 42,
+    writable: false,
+    configurable: true
+  });
+  Object.defineProperty(d, ownSetter, {
+    set: function() { assertUnreachable(); },
+    configurable: true
+  });
+  Object.defineProperty(d, ownReadonlyAccessor, {
+    get: function() { return 15; },
+    configurable: true
+  });
+
   d.mSloppy();
+
+  var d = new Derived();
+  Object.defineProperty(d, ownReadOnly, {
+    value: 42,
+    writable: false,
+    configurable: true
+  });
+  Object.defineProperty(d, ownSetter, {
+    set: function() { assertUnreachable(); },
+    configurable: true
+  });
+  Object.defineProperty(d, ownReadonlyAccessor, {
+    get: function() { return 15; },
+    configurable: true
+  });
   d.mStrict();
-}());
+}
+TestKeyedSetterCreatingOwnPropertiesReconfigurable('ownReadOnly',
+                                                   'ownReadonlyAccessor',
+                                                   'ownSetter');
+TestKeyedSetterCreatingOwnPropertiesReconfigurable(42, 43, 44);
 
 
-(function TestKeyedNumericSetterCreatingOwnProperties() {
-  var ownReadOnly = 42;
-  var ownReadonlyAccessor = 43;
-  var ownSetter = 44;
-  var setterCalled;
+function TestKeyedSetterCreatingOwnPropertiesNonConfigurable(
+    ownReadOnly, ownReadonlyAccessor, ownSetter) {
   function Base() {}
   function Derived() {}
   Derived.prototype = {
       assertEquals(42, this[ownReadOnly]);
       super[ownReadOnly] = 55;
       assertEquals(42, this[ownReadOnly]);
+      var descr = Object.getOwnPropertyDescriptor(this, ownReadOnly);
+      assertEquals(42, descr.value);
+      assertFalse(descr.configurable);
+      assertFalse(descr.enumerable);
+      assertFalse(descr.writable);
+      assertFalse(Base.prototype.hasOwnProperty(ownReadOnly));
 
       assertEquals(15, this[ownReadonlyAccessor]);
-      super[ownReadonlyAccessor] = 55;
+      super[ownReadonlyAccessor] = 25;
       assertEquals(15, this[ownReadonlyAccessor]);
-
-      setterCalled = 0;
-      super[ownSetter] = 42;
-      assertEquals(1, setterCalled);
+      var descr = Object.getOwnPropertyDescriptor(this, ownReadonlyAccessor);
+      assertFalse(descr.configurable);
+      assertFalse(descr.enumerable);
+      assertFalse(Base.prototype.hasOwnProperty(ownReadonlyAccessor));
+
+      super[ownSetter] = 35;
+      var descr = Object.getOwnPropertyDescriptor(this, ownSetter);
+      assertFalse(descr.configurable);
+      assertFalse(descr.enumerable);
+      assertFalse(Base.prototype.hasOwnProperty(ownSetter));
     },
     mStrict() {
       'use strict';
-      assertEquals(42, this[ownReadOnly]);
       var ex;
+      assertEquals(42, this[ownReadOnly]);
       try {
         super[ownReadOnly] = 55;
-      } catch(e) { ex = e; }
-      assertTrue(ex instanceof TypeError);
+      } catch (e) {
+        ex = e;
+      }
+      assertInstanceof(ex, TypeError);
+      assertEquals(
+          "Cannot assign to read only property '" + ownReadOnly +
+              "' of #<Base>",
+          ex.message);
       assertEquals(42, this[ownReadOnly]);
 
-      assertEquals(15, this[ownReadonlyAccessor]);
       ex = null;
+      assertEquals(15, this[ownReadonlyAccessor]);
       try {
-        super[ownReadonlyAccessor] = 55;
-      } catch(e) { ex = e; }
-      assertTrue(ex instanceof TypeError);
+        super[ownReadonlyAccessor] = 25;
+      } catch (e) {
+        ex = e;
+      }
+      assertInstanceof(ex, TypeError);
+      assertEquals('Cannot redefine property: ' + ownReadonlyAccessor,
+                   ex.message);
       assertEquals(15, this[ownReadonlyAccessor]);
 
-      setterCalled = 0;
-      super[ownSetter] = 42;
-      assertEquals(1, setterCalled);
+      ex = null;
+      try {
+        super[ownSetter] = 35;
+      } catch (e) {
+        ex = e;
+      }
+      assertInstanceof(ex, TypeError);
+      assertEquals('Cannot redefine property: ' + ownSetter, ex.message);
     }
-  }
+  };
 
   var d = new Derived();
   Object.defineProperty(d, ownReadOnly, { value : 42, writable : false });
   Object.defineProperty(d, ownSetter,
-      { set : function() { setterCalled++; } });
+      { set : function() { assertUnreachable(); } });
   Object.defineProperty(d, ownReadonlyAccessor,
       { get : function() { return 15; }});
   d.mSloppy();
   d.mStrict();
-}());
+}
+TestKeyedSetterCreatingOwnPropertiesNonConfigurable('ownReadOnly',
+    'ownReadonlyAccessor', 'ownSetter');
+TestKeyedSetterCreatingOwnPropertiesNonConfigurable(42, 43, 44);
 
 
 (function TestSetterNoProtoWalk() {
       assertEquals(27, this.x);
       var ex = null;
       try { super.x = 10; } catch(e) { ex = e; }
-      assertTrue(ex instanceof TypeError);
+      assertInstanceof(ex, TypeError);
       assertEquals(27, super.x);
       assertEquals(27, this.x);
     }
       assertEquals(27, this[x]);
       var ex = null;
       try { super[x] = 10; } catch(e) { ex = e; }
-      assertTrue(ex instanceof TypeError);
+      assertInstanceof(ex, TypeError);
       assertEquals(27, super[x]);
       assertEquals(27, this[x]);
     }
       assertEquals(27, this[x]);
       var ex = null;
       try { super[x] = 10; } catch(e) { ex = e; }
-      assertTrue(ex instanceof TypeError);
+      assertInstanceof(ex, TypeError);
       assertEquals(27, super[x]);
       assertEquals(27, this[x]);
     }
   var f = new F(42);
 
   // TODO(dslomov,arv): Fix this. BUG=v8:3886.
-  assertTrue(f instanceof Number);
+  assertInstanceof(f, Number);
 }());
 
 (function TestSuperCallErrorCases() {