Bug 837630 - Stop hiding __proto__ from O.getOwnPropertyNames. r=Waldo,peterv,past
authorTom Schuster <evilpies@gmail.com>
Thu, 19 Mar 2015 17:42:15 +0100
changeset 263325 2cbe6a336b82be50f8ab4b5aaf7b3d38a21fd1f5
parent 263324 242dcd4086ffc0297b6b36b89883f35b91487e1b
child 263326 be4f8924a7ce2cc465e393e080b17089a473f349
push id4718
push userraliiev@mozilla.com
push dateMon, 11 May 2015 18:39:53 +0000
treeherdermozilla-beta@c20c4ef55f08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo, peterv, past
bugs837630
milestone39.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 837630 - Stop hiding __proto__ from O.getOwnPropertyNames. r=Waldo,peterv,past
browser/devtools/webconsole/test/browser_webconsole_bug_585991_autocomplete_keys.js
dom/bindings/test/test_Object.prototype_props.html
js/src/jsiter.cpp
js/src/tests/ecma_5/extensions/getOwnPropertyNames-__proto__.js
js/xpconnect/tests/chrome/test_xrayToJS.xul
toolkit/devtools/server/actors/script.js
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_585991_autocomplete_keys.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_585991_autocomplete_keys.js
@@ -45,44 +45,45 @@ let consoleOpened = Task.async(function*
 
   popup._panel.addEventListener("popupshown", function onShown() {
     popup._panel.removeEventListener("popupshown", onShown, false);
 
     ok(popup.isOpen, "popup is open");
 
     // 4 values, and the following properties:
     // __defineGetter__  __defineSetter__ __lookupGetter__ __lookupSetter__
-    // hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString
-    // toSource unwatch valueOf watch constructor.
-    is(popup.itemCount, 18, "popup.itemCount is correct");
+    // __proto__ hasOwnProperty isPrototypeOf propertyIsEnumerable
+    // toLocaleString toString toSource unwatch valueOf watch constructor.
+    is(popup.itemCount, 19, "popup.itemCount is correct");
 
     let sameItems = popup.getItems().reverse().map(function(e) {return e.label;});
     ok(sameItems.every(function(prop, index) {
       return [
         "__defineGetter__",
         "__defineSetter__",
         "__lookupGetter__",
         "__lookupSetter__",
+        "__proto__",
         "constructor",
         "hasOwnProperty",
         "isPrototypeOf",
         "item0",
         "item1",
         "item2",
         "item3",
         "propertyIsEnumerable",
         "toLocaleString",
         "toSource",
         "toString",
         "unwatch",
         "valueOf",
         "watch",
       ][index] === prop}), "getItems returns the items we expect");
 
-    is(popup.selectedIndex, 17,
+    is(popup.selectedIndex, 18,
        "Index of the first item from bottom is selected.");
     EventUtils.synthesizeKey("VK_DOWN", {});
 
     let prefix = jsterm.inputNode.value.replace(/[\S]/g, " ");
 
     is(popup.selectedIndex, 0, "index 0 is selected");
     is(popup.selectedItem.label, "watch", "watch is selected");
     is(completeNode.value, prefix + "watch",
@@ -110,17 +111,17 @@ let consoleOpened = Task.async(function*
       "Index is greater after PGDN");
 
     currentSelectionIndex = popup.selectedIndex;
     EventUtils.synthesizeKey("VK_PAGE_UP", {});
 
     ok(popup.selectedIndex < currentSelectionIndex, "Index is less after Page UP");
 
     EventUtils.synthesizeKey("VK_END", {});
-    is(popup.selectedIndex, 17, "index is last after End");
+    is(popup.selectedIndex, 18, "index is last after End");
 
     EventUtils.synthesizeKey("VK_HOME", {});
     is(popup.selectedIndex, 0, "index is first after Home");
 
     info("press Tab and wait for popup to hide");
     popup._panel.addEventListener("popuphidden", function popupHidden() {
       popup._panel.removeEventListener("popuphidden", popupHidden, false);
       deferred.resolve();
@@ -147,19 +148,19 @@ function popupHideAfterTab()
 
   ok(!completeNode.value, "completeNode is empty");
 
   popup._panel.addEventListener("popupshown", function onShown() {
     popup._panel.removeEventListener("popupshown", onShown, false);
 
     ok(popup.isOpen, "popup is open");
 
-    is(popup.itemCount, 18, "popup.itemCount is correct");
+    is(popup.itemCount, 19, "popup.itemCount is correct");
 
-    is(popup.selectedIndex, 17, "First index from bottom is selected");
+    is(popup.selectedIndex, 18, "First index from bottom is selected");
     EventUtils.synthesizeKey("VK_DOWN", {});
 
     let prefix = jsterm.inputNode.value.replace(/[\S]/g, " ");
 
     is(popup.selectedIndex, 0, "index 0 is selected");
     is(popup.selectedItem.label, "watch", "watch is selected");
     is(completeNode.value, prefix + "watch",
         "completeNode.value holds watch");
@@ -196,19 +197,19 @@ function testReturnKey()
 {
   let deferred = promise.defer();
 
   popup._panel.addEventListener("popupshown", function onShown() {
     popup._panel.removeEventListener("popupshown", onShown, false);
 
     ok(popup.isOpen, "popup is open");
 
-    is(popup.itemCount, 18, "popup.itemCount is correct");
+    is(popup.itemCount, 19, "popup.itemCount is correct");
 
-    is(popup.selectedIndex, 17, "First index from bottom is selected");
+    is(popup.selectedIndex, 18, "First index from bottom is selected");
     EventUtils.synthesizeKey("VK_DOWN", {});
 
     let prefix = jsterm.inputNode.value.replace(/[\S]/g, " ");
 
     is(popup.selectedIndex, 0, "index 0 is selected");
     is(popup.selectedItem.label, "watch", "watch is selected");
     is(completeNode.value, prefix + "watch",
         "completeNode.value holds watch");
--- a/dom/bindings/test/test_Object.prototype_props.html
+++ b/dom/bindings/test/test_Object.prototype_props.html
@@ -2,19 +2,16 @@
 <meta charset=utf-8>
 <title>Test for bug 987110</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <div id="log"></div>
 <script>
 test(function() {
   var props = Object.getOwnPropertyNames(Object.prototype);
-  // getOwnPropertyNames intentionally filters out the non-standard
-  // "__proto__" property.
-  props.push("__proto__");
   // If you change this list, make sure it continues to match the list in
   // Codegen.py's CGDictionary.getMemberDefinition method.
   var expected = [
       "constructor", "toSource", "toString", "toLocaleString", "valueOf",
       "watch", "unwatch", "hasOwnProperty", "isPrototypeOf",
       "propertyIsEnumerable", "__defineGetter__", "__defineSetter__",
       "__lookupGetter__", "__lookupSetter__", "__proto__"
     ];
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -93,25 +93,16 @@ NewKeyValuePair(JSContext *cx, jsid id, 
     rval.setObject(*aobj);
     return true;
 }
 
 static inline bool
 Enumerate(JSContext *cx, HandleObject pobj, jsid id,
           bool enumerable, unsigned flags, Maybe<IdSet>& ht, AutoIdVector *props)
 {
-    // We implement __proto__ using a property on |Object.prototype|, but
-    // because __proto__ is highly deserving of removal, we don't want it to
-    // show up in property enumeration, even if only for |Object.prototype|
-    // (think introspection by Prototype-like frameworks that add methods to
-    // the built-in prototypes).  So exclude __proto__ if the object where the
-    // property was found has no [[Prototype]] and might be |Object.prototype|.
-    if (MOZ_UNLIKELY(!pobj->getTaggedProto().isObject() && JSID_IS_ATOM(id, cx->names().proto)))
-        return true;
-
     if (!(flags & JSITER_OWNONLY) || pobj->is<ProxyObject>() || pobj->getOps()->enumerate) {
         if (!ht) {
             ht.emplace(cx);
             // Most of the time there are only a handful of entries.
             if (!ht->init(5))
                 return false;
         }
 
--- a/js/src/tests/ecma_5/extensions/getOwnPropertyNames-__proto__.js
+++ b/js/src/tests/ecma_5/extensions/getOwnPropertyNames-__proto__.js
@@ -1,28 +1,26 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
 //-----------------------------------------------------------------------------
-var BUGNUMBER = 690031;
-var summary =
-  'Exclude __proto__ from showing up when enumerating properties of ' +
-  'Object.prototype again';
+var BUGNUMBER = 837630;
+var summary ='__proto__ should show up with O.getOwnPropertyNames(O.prototype)';
 
 print(BUGNUMBER + ": " + summary);
 
 /**************
  * BEGIN TEST *
  **************/
 
 var keys = Object.getOwnPropertyNames(Object.prototype);
-assertEq(keys.indexOf("__proto__"), -1,
-         "shouldn't have gotten __proto__ as a property of Object.prototype " +
+assertEq(keys.indexOf("__proto__") >= 0, true,
+         "should have gotten __proto__ as a property of Object.prototype " +
          "(got these properties: " + keys + ")");
 
 /******************************************************************************/
 
 if (typeof reportCompare === "function")
   reportCompare(true, true);
 
 print("Tests complete");
--- a/js/xpconnect/tests/chrome/test_xrayToJS.xul
+++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul
@@ -155,21 +155,21 @@ https://bugzilla.mozilla.org/show_bug.cg
     "getUTCSeconds", "getMilliseconds", "getUTCMilliseconds", "setTime",
     "setYear", "setFullYear", "setUTCFullYear", "setMonth", "setUTCMonth",
     "setDate", "setUTCDate", "setHours", "setUTCHours", "setMinutes",
     "setUTCMinutes", "setSeconds", "setUTCSeconds", "setMilliseconds",
     "setUTCMilliseconds", "toUTCString", "toLocaleFormat", "toLocaleString",
     "toLocaleDateString", "toLocaleTimeString", "toDateString", "toTimeString",
     "toISOString", "toJSON", "toSource", "toString", "valueOf", "constructor",
     "toGMTString"];
-  gPrototypeProperties['Object'] = /* __proto__ is intentionally excluded here, because
-                                      the JS engine filters it out of getOwnPropertyNames */
+  gPrototypeProperties['Object'] =
     ["constructor", "toSource", "toString", "toLocaleString", "valueOf", "watch",
      "unwatch", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable",
-     "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__"];
+     "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__",
+     "__proto__"];
   gPrototypeProperties['Array'] =
     ["length", "toSource", "toString", "toLocaleString", "join", "reverse", "sort", "push",
       "pop", "shift", "unshift", "splice", "concat", "slice", "lastIndexOf", "indexOf",
       "forEach", "map", "reduce", "reduceRight", "filter", "some", "every", "find",
       "findIndex", "copyWithin", "fill", Symbol.iterator, "entries", "keys", "constructor"];
   if (isNightlyBuild) {
     gPrototypeProperties['Array'].push('includes');
   }
--- a/toolkit/devtools/server/actors/script.js
+++ b/toolkit/devtools/server/actors/script.js
@@ -3435,16 +3435,21 @@ ObjectActor.prototype = {
         // Avoid overwriting properties from prototypes closer to this.obj. Also
         // avoid providing safeGetterValues from prototypes if property |name|
         // is already defined as an own property.
         if (name in safeGetterValues ||
             (obj != this.obj && name in aOwnProperties)) {
           continue;
         }
 
+        // Ignore __proto__ on Object.prototye.
+        if (!obj.proto && name == "__proto__") {
+          continue;
+        }
+
         let desc = null, getter = null;
         try {
           desc = obj.getOwnPropertyDescriptor(name);
           getter = desc.get;
         } catch (ex) {
           // The above can throw if the cache becomes stale.
         }
         if (!getter) {