Fix bugs 992, 1083 and 1092
authorpeterhal@chromium.org <peterhal@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 3 Feb 2011 19:29:10 +0000 (19:29 +0000)
committerpeterhal@chromium.org <peterhal@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 3 Feb 2011 19:29:10 +0000 (19:29 +0000)
My previous patch added an assert which uncovered 1092 in the sputnik tests.
This patch adds the fix for 1092, which is to ensure that NormalizeProperties
does not get called for a JSGlobalProxy along all code paths.

Add sputnik tests to .gitignore.

BUG=
TEST=

Review URL: http://codereview.chromium.org/6286060

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

.gitignore
src/handles.cc
src/objects.cc
src/runtime.cc
src/v8natives.js
test/mjsunit/object-define-property.js
test/mjsunit/regress/regress-1083.js [new file with mode: 0644]
test/mjsunit/regress/regress-1092.js [new file with mode: 0644]
test/mjsunit/regress/regress-992.js [new file with mode: 0644]

index d85ef64..c68dadb 100644 (file)
@@ -20,6 +20,7 @@ d8_g
 shell
 shell_g
 /obj/
+/test/sputnik/sputniktests/
 /tools/oom_dump/oom_dump
 /tools/oom_dump/oom_dump.o
 /tools/visual_studio/Debug
index 461c3f5..274c34d 100644 (file)
@@ -873,7 +873,7 @@ OptimizedObjectForAddingMultipleProperties(Handle<JSObject> object,
                                            int expected_additional_properties,
                                            bool condition) {
   object_ = object;
-  if (condition && object_->HasFastProperties()) {
+  if (condition && object_->HasFastProperties() && !object->IsJSGlobalProxy()) {
     // Normalize the properties of object to avoid n^2 behavior
     // when extending the object multiple properties. Indicate the number of
     // properties to be added.
index 69727d2..524c024 100644 (file)
@@ -1213,6 +1213,8 @@ MaybeObject* JSObject::AddFastPropertyUsingMap(Map* new_map,
 MaybeObject* JSObject::AddFastProperty(String* name,
                                        Object* value,
                                        PropertyAttributes attributes) {
+  ASSERT(!IsJSGlobalProxy());
+
   // Normalize the object if the name is an actual string (not the
   // hidden symbols) and is not a real identifier.
   StringInputBuffer buffer(name);
@@ -2288,6 +2290,9 @@ MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode,
   // The global object is always normalized.
   ASSERT(!IsGlobalObject());
 
+  // JSGlobalProxy must never be normalized
+  ASSERT(!IsJSGlobalProxy());
+
   // Allocate new content.
   int property_count = map()->NumberOfDescribedProperties();
   if (expected_additional_properties > 0) {
index 239ba08..0c4237f 100644 (file)
@@ -3602,7 +3602,12 @@ static MaybeObject* Runtime_KeyedGetProperty(Arguments args) {
                                     args.at<Object>(1));
 }
 
-
+// Implements part of 8.12.9 DefineOwnProperty.
+// There are 3 cases that lead here:
+// Step 4b - define a new accessor property.
+// Steps 9c & 12 - replace an existing data property with an accessor property.
+// Step 12 - update an existing accessor property with an accessor or generic
+//           descriptor.
 static MaybeObject* Runtime_DefineOrRedefineAccessorProperty(Arguments args) {
   ASSERT(args.length() == 5);
   HandleScope scope;
@@ -3634,6 +3639,12 @@ static MaybeObject* Runtime_DefineOrRedefineAccessorProperty(Arguments args) {
   return obj->DefineAccessor(name, flag_setter->value() == 0, fun, attr);
 }
 
+// Implements part of 8.12.9 DefineOwnProperty.
+// There are 3 cases that lead here:
+// Step 4a - define a new data property.
+// Steps 9b & 12 - replace an existing accessor property with a data property.
+// Step 12 - update an existing data property with a data or generic
+//           descriptor.
 static MaybeObject* Runtime_DefineOrRedefineDataProperty(Arguments args) {
   ASSERT(args.length() == 4);
   HandleScope scope;
@@ -3657,7 +3668,9 @@ static MaybeObject* Runtime_DefineOrRedefineDataProperty(Arguments args) {
   if (((unchecked & (DONT_DELETE | DONT_ENUM | READ_ONLY)) != 0) &&
       is_element) {
     // Normalize the elements to enable attributes on the property.
-    NormalizeElements(js_object);
+    if (!js_object->IsJSGlobalProxy()) {
+      NormalizeElements(js_object);
+    }
     Handle<NumberDictionary> dictionary(js_object->element_dictionary());
     // Make sure that we never go back to fast case.
     dictionary->set_requires_slow_elements();
@@ -3677,7 +3690,9 @@ static MaybeObject* Runtime_DefineOrRedefineDataProperty(Arguments args) {
   if (result.IsProperty() &&
       (attr != result.GetAttributes() || result.type() == CALLBACKS)) {
     // New attributes - normalize to avoid writing to instance descriptor
-    NormalizeProperties(js_object, CLEAR_INOBJECT_PROPERTIES, 0);
+    if (!js_object->IsJSGlobalProxy()) {
+      NormalizeProperties(js_object, CLEAR_INOBJECT_PROPERTIES, 0);
+    }
     // Use IgnoreAttributes version since a readonly property may be
     // overridden and SetProperty does not allow this.
     return js_object->SetLocalPropertyIgnoreAttributes(*name,
@@ -4273,7 +4288,7 @@ static MaybeObject* Runtime_ToSlowProperties(Arguments args) {
 
   ASSERT(args.length() == 1);
   Handle<Object> object = args.at<Object>(0);
-  if (object->IsJSObject()) {
+  if (object->IsJSObject() && !object->IsJSGlobalProxy()) {
     Handle<JSObject> js_object = Handle<JSObject>::cast(object);
     NormalizeProperties(js_object, CLEAR_INOBJECT_PROPERTIES, 0);
   }
index 233f8b4..0444469 100644 (file)
@@ -545,10 +545,12 @@ function DefineOwnProperty(obj, p, desc, should_throw) {
   if (IS_UNDEFINED(current) && !extensible)
     throw MakeTypeError("define_disallowed", ["defineProperty"]);
 
-  if (!IS_UNDEFINED(current) && !current.isConfigurable()) {
+  if (!IS_UNDEFINED(current)) {
     // Step 5 and 6
-    if ((!desc.hasEnumerable() ||
-         SameValue(desc.isEnumerable() && current.isEnumerable())) &&
+    if ((IsGenericDescriptor(desc) ||
+         IsDataDescriptor(desc) == IsDataDescriptor(current)) &&
+        (!desc.hasEnumerable() ||
+         SameValue(desc.isEnumerable(), current.isEnumerable())) &&
         (!desc.hasConfigurable() ||
          SameValue(desc.isConfigurable(), current.isConfigurable())) &&
         (!desc.hasWritable() ||
@@ -561,29 +563,35 @@ function DefineOwnProperty(obj, p, desc, should_throw) {
          SameValue(desc.getSet(), current.getSet()))) {
       return true;
     }
-
-    // Step 7
-    if (desc.isConfigurable() || desc.isEnumerable() != current.isEnumerable())
-      throw MakeTypeError("redefine_disallowed", ["defineProperty"]);
-    // Step 9
-    if (IsDataDescriptor(current) != IsDataDescriptor(desc))
-      throw MakeTypeError("redefine_disallowed", ["defineProperty"]);
-    // Step 10
-    if (IsDataDescriptor(current) && IsDataDescriptor(desc)) {
-      if (!current.isWritable() && desc.isWritable())
-        throw MakeTypeError("redefine_disallowed", ["defineProperty"]);
-      if (!current.isWritable() && desc.hasValue() &&
-          !SameValue(desc.getValue(), current.getValue())) {
-        throw MakeTypeError("redefine_disallowed", ["defineProperty"]);
-      }
-    }
-    // Step 11
-    if (IsAccessorDescriptor(desc) && IsAccessorDescriptor(current)) {
-      if (desc.hasSetter() && !SameValue(desc.getSet(), current.getSet())){
+    if (!current.isConfigurable()) {
+      // Step 7
+      if (desc.isConfigurable() ||
+          (desc.hasEnumerable() &&
+           desc.isEnumerable() != current.isEnumerable()))
         throw MakeTypeError("redefine_disallowed", ["defineProperty"]);
+      // Step 8
+      if (!IsGenericDescriptor(desc)) {
+        // Step 9a
+        if (IsDataDescriptor(current) != IsDataDescriptor(desc))
+          throw MakeTypeError("redefine_disallowed", ["defineProperty"]);
+        // Step 10a
+        if (IsDataDescriptor(current) && IsDataDescriptor(desc)) {
+          if (!current.isWritable() && desc.isWritable())
+            throw MakeTypeError("redefine_disallowed", ["defineProperty"]);
+          if (!current.isWritable() && desc.hasValue() &&
+              !SameValue(desc.getValue(), current.getValue())) {
+            throw MakeTypeError("redefine_disallowed", ["defineProperty"]);
+          }
+        }
+        // Step 11
+        if (IsAccessorDescriptor(desc) && IsAccessorDescriptor(current)) {
+          if (desc.hasSetter() && !SameValue(desc.getSet(), current.getSet())){
+            throw MakeTypeError("redefine_disallowed", ["defineProperty"]);
+          }
+          if (desc.hasGetter() && !SameValue(desc.getGet(),current.getGet()))
+            throw MakeTypeError("redefine_disallowed", ["defineProperty"]);
+        }
       }
-      if (desc.hasGetter() && !SameValue(desc.getGet(),current.getGet()))
-        throw MakeTypeError("redefine_disallowed", ["defineProperty"]);
     }
   }
 
@@ -607,7 +615,16 @@ function DefineOwnProperty(obj, p, desc, should_throw) {
   } else
     flag |= DONT_DELETE;
 
-  if (IsDataDescriptor(desc) || IsGenericDescriptor(desc)) {
+  if (IsDataDescriptor(desc) ||
+      (IsGenericDescriptor(desc) &&
+       (IS_UNDEFINED(current) || IsDataDescriptor(current)))) {
+    // There are 3 cases that lead here:
+    // Step 4a - defining a new data property.
+    // Steps 9b & 12 - replacing an existing accessor property with a data
+    //                 property.
+    // Step 12 - updating an existing data property with a data or generic
+    //           descriptor.
+
     if (desc.hasWritable()) {
       flag |= desc.isWritable() ? 0 : READ_ONLY;
     } else if (!IS_UNDEFINED(current)) {
@@ -615,20 +632,30 @@ function DefineOwnProperty(obj, p, desc, should_throw) {
     } else {
       flag |= READ_ONLY;
     }
+
     var value = void 0;  // Default value is undefined.
     if (desc.hasValue()) {
       value = desc.getValue();
-    } else if (!IS_UNDEFINED(current)) {
+    } else if (!IS_UNDEFINED(current) && IsDataDescriptor(current)) {
       value = current.getValue();
     }
+
     %DefineOrRedefineDataProperty(obj, p, value, flag);
+  } else if (IsGenericDescriptor(desc)) {
+    // Step 12 - updating an existing accessor property with generic
+    //           descriptor. Changing flags only.
+    %DefineOrRedefineAccessorProperty(obj, p, GETTER, current.getGet(), flag);
   } else {
-    if (desc.hasGetter() &&
-        (IS_FUNCTION(desc.getGet()) || IS_UNDEFINED(desc.getGet()))) {
-       %DefineOrRedefineAccessorProperty(obj, p, GETTER, desc.getGet(), flag);
+    // There are 3 cases that lead here:
+    // Step 4b - defining a new accessor property.
+    // Steps 9c & 12 - replacing an existing data property with an accessor
+    //                 property.
+    // Step 12 - updating an existing accessor property with an accessor
+    //           descriptor.
+    if (desc.hasGetter()) {
+      %DefineOrRedefineAccessorProperty(obj, p, GETTER, desc.getGet(), flag);
     }
-    if (desc.hasSetter() &&
-        (IS_FUNCTION(desc.getSet()) || IS_UNDEFINED(desc.getSet()))) {
+    if (desc.hasSetter()) {
       %DefineOrRedefineAccessorProperty(obj, p, SETTER, desc.getSet(), flag);
     }
   }
index d24a4e5..a8a3213 100644 (file)
@@ -749,14 +749,33 @@ assertTrue(desc.writable);
 assertTrue(desc.enumerable);
 assertFalse(desc.configurable);
 
-// Ensure that we can't overwrite the non configurable element.
+// Can use defineProperty to change the value of a non
+// configurable property.
 try {
   Object.defineProperty(obj6, '2', descElement);
+  desc = Object.getOwnPropertyDescriptor(obj6, '2');
+  assertEquals(desc.value, 'foobar');
+} catch (e) {
+  assertUnreachable();
+}
+
+// Ensure that we can't change the descriptor of a
+// non configurable property.
+try {
+  var descAccessor = { get: function() { return 0; } };
+  Object.defineProperty(obj6, '2', descAccessor);
   assertUnreachable();
 } catch (e) {
   assertTrue(/Cannot redefine property/.test(e));
 }
 
+Object.defineProperty(obj6, '2', descElementNonWritable);
+desc = Object.getOwnPropertyDescriptor(obj6, '2');
+assertEquals(desc.value, 'foofoo');
+assertFalse(desc.writable);
+assertTrue(desc.enumerable);
+assertFalse(desc.configurable);
+
 Object.defineProperty(obj6, '3', descElementNonWritable);
 desc = Object.getOwnPropertyDescriptor(obj6, '3');
 assertEquals(desc.value, 'foofoo');
@@ -827,14 +846,33 @@ assertTrue(desc.writable);
 assertTrue(desc.enumerable);
 assertFalse(desc.configurable);
 
-// Ensure that we can't overwrite the non configurable element.
+// Can use defineProperty to change the value of a non
+// configurable property of an array.
 try {
   Object.defineProperty(arr, '2', descElement);
+  desc = Object.getOwnPropertyDescriptor(arr, '2');
+  assertEquals(desc.value, 'foobar');
+} catch (e) {
+  assertUnreachable();
+}
+
+// Ensure that we can't change the descriptor of a
+// non configurable property.
+try {
+  var descAccessor = { get: function() { return 0; } };
+  Object.defineProperty(arr, '2', descAccessor);
   assertUnreachable();
 } catch (e) {
   assertTrue(/Cannot redefine property/.test(e));
 }
 
+Object.defineProperty(arr, '2', descElementNonWritable);
+desc = Object.getOwnPropertyDescriptor(arr, '2');
+assertEquals(desc.value, 'foofoo');
+assertFalse(desc.writable);
+assertTrue(desc.enumerable);
+assertFalse(desc.configurable);
+
 Object.defineProperty(arr, '3', descElementNonWritable);
 desc = Object.getOwnPropertyDescriptor(arr, '3');
 assertEquals(desc.value, 'foofoo');
@@ -898,3 +936,98 @@ Object.defineProperty(o, "x", { writable: false });
 assertEquals(undefined, o.x);
 o.x = 37;
 assertEquals(undefined, o.x);
+
+function testDefineProperty(obj, propertyName, desc, resultDesc) {
+  Object.defineProperty(obj, propertyName, desc);
+  var actualDesc = Object.getOwnPropertyDescriptor(obj, propertyName);
+  assertEquals(resultDesc.enumerable, actualDesc.enumerable);
+  assertEquals(resultDesc.configurable, actualDesc.configurable);
+  if (resultDesc.hasOwnProperty('value')) {
+    assertEquals(resultDesc.value, actualDesc.value);
+    assertEquals(resultDesc.writable, actualDesc.writable);
+    assertFalse(resultDesc.hasOwnProperty('get'));
+    assertFalse(resultDesc.hasOwnProperty('set'));
+  } else {
+    assertEquals(resultDesc.get, actualDesc.get);
+    assertEquals(resultDesc.set, actualDesc.set);
+    assertFalse(resultDesc.hasOwnProperty('value'));
+    assertFalse(resultDesc.hasOwnProperty('writable'));
+  }
+}
+
+// tests redefining existing property with a generic descriptor
+o = { p : 42 };
+testDefineProperty(o, 'p',
+  { },
+  { value : 42, writable : true, enumerable : true, configurable : true });
+
+o = { p : 42 };
+testDefineProperty(o, 'p',
+  { enumerable : true },
+  { value : 42, writable : true, enumerable : true, configurable : true });
+
+o = { p : 42 };
+testDefineProperty(o, 'p',
+  { configurable : true },
+  { value : 42, writable : true, enumerable : true, configurable : true });
+
+o = { p : 42 };
+testDefineProperty(o, 'p',
+  { enumerable : false },
+  { value : 42, writable : true, enumerable : false, configurable : true });
+
+o = { p : 42 };
+testDefineProperty(o, 'p',
+  { configurable : false },
+  { value : 42, writable : true, enumerable : true, configurable : false });
+
+o = { p : 42 };
+testDefineProperty(o, 'p',
+  { enumerable : true, configurable : true },
+  { value : 42, writable : true, enumerable : true, configurable : true });
+
+o = { p : 42 };
+testDefineProperty(o, 'p',
+  { enumerable : false, configurable : true },
+  { value : 42, writable : true, enumerable : false, configurable : true });
+
+o = { p : 42 };
+testDefineProperty(o, 'p',
+  { enumerable : true, configurable : false },
+  { value : 42, writable : true, enumerable : true, configurable : false });
+
+o = { p : 42 };
+testDefineProperty(o, 'p',
+  { enumerable : false, configurable : false },
+  { value : 42, writable : true, enumerable : false, configurable : false });
+
+// can make a writable, non-configurable field non-writable
+o = { p : 42 };
+Object.defineProperty(o, 'p', { configurable: false });
+testDefineProperty(o, 'p',
+  { writable: false },
+  { value : 42, writable : false, enumerable : true, configurable : false });
+
+// redefine of get only property with generic descriptor
+o = {};
+Object.defineProperty(o, 'p',
+  { get : getter1, enumerable: true, configurable: true });
+testDefineProperty(o, 'p',
+  { enumerable : false, configurable : false },
+  { get: getter1, set: undefined, enumerable : false, configurable : false });
+
+// redefine of get/set only property with generic descriptor
+o = {};
+Object.defineProperty(o, 'p',
+  { get: getter1, set: setter1, enumerable: true, configurable: true });
+testDefineProperty(o, 'p',
+  { enumerable : false, configurable : false },
+  { get: getter1, set: setter1, enumerable : false, configurable : false });
+
+// redefine of set only property with generic descriptor
+o = {};
+Object.defineProperty(o, 'p',
+  { set : setter1, enumerable: true, configurable: true });
+testDefineProperty(o, 'p',
+  { enumerable : false, configurable : false },
+  { get: undefined, set: setter1, enumerable : false, configurable : false });
diff --git a/test/mjsunit/regress/regress-1083.js b/test/mjsunit/regress/regress-1083.js
new file mode 100644 (file)
index 0000000..d231899
--- /dev/null
@@ -0,0 +1,38 @@
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Test that changing the generic descriptor flags on a property
+// on the global object doesn't break invariants.
+Object.defineProperty(this, 'Object', {enumerable:true});
+
+var desc = Object.getOwnPropertyDescriptor(this, 'Object');
+assertTrue(desc.enumerable);
+assertTrue(desc.configurable);
+assertFalse(desc.hasOwnProperty('get'));
+assertFalse(desc.hasOwnProperty('set'));
+assertTrue(desc.hasOwnProperty('value'));
+assertTrue(desc.writable);
diff --git a/test/mjsunit/regress/regress-1092.js b/test/mjsunit/regress/regress-1092.js
new file mode 100644 (file)
index 0000000..0b29231
--- /dev/null
@@ -0,0 +1,35 @@
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Test that CodeGenerator::EmitKeyedPropertyAssignment for the start 
+// of an initialization block doesn't normalize the properties of the
+// JSGlobalProxy.
+this.w = 0;
+this.x = 1;
+this.y = 2;
+this.z = 3;
+
diff --git a/test/mjsunit/regress/regress-992.js b/test/mjsunit/regress/regress-992.js
new file mode 100644 (file)
index 0000000..dbe25a5
--- /dev/null
@@ -0,0 +1,43 @@
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Object.defineProperty with generic desc on existing property
+// should just update enumerable/configurable flags.
+
+var obj =  { get p() { return 42; }  };
+var desc = Object.getOwnPropertyDescriptor(obj, 'p');
+var getter = desc.get;
+
+Object.defineProperty(obj, 'p', {enumerable: false });
+assertEquals(obj.p, 42);
+desc = Object.getOwnPropertyDescriptor(obj, 'p');
+assertFalse(desc.enumerable);
+assertTrue(desc.configurable);
+assertEquals(desc.get, getter);
+assertEquals(desc.set, undefined);
+assertFalse(desc.hasOwnProperty('value'));
+assertFalse(desc.hasOwnProperty('writable'));