Bug 1082662 - Don't crash trying to mark a dictionary accessor shape whose getter/setter field has previously been mutated from a callable to |undefined|. r=terrence
authorJeff Walden <jwalden@mit.edu>
Tue, 14 Oct 2014 14:43:53 -0700
changeset 210467 cbde563962735c0acae142c53e1e625626d13ca1
parent 210466 d0e6c1ac03918a153e9b2c5d3df0b2280d57da09
child 210468 fb08ab7ef68ef659b6b80f45e86a1c6c14e1ff42
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersterrence
bugs1082662
milestone36.0a1
Bug 1082662 - Don't crash trying to mark a dictionary accessor shape whose getter/setter field has previously been mutated from a callable to |undefined|. r=terrence
js/src/jspropertytree.cpp
js/src/tests/ecma_5/Object/clear-dictionary-accessor-getset.js
--- a/js/src/jspropertytree.cpp
+++ b/js/src/jspropertytree.cpp
@@ -356,16 +356,19 @@ void
 ShapeGetterSetterRef::mark(JSTracer *trc)
 {
     // Update the current shape's entry in the parent KidsHash table if needed.
     // This is necessary as the computed hash includes the getter/setter
     // pointers.
 
     JSObject *obj = *objp;
     JSObject *prior = obj;
+    if (!prior)
+        return;
+
     trc->setTracingLocation(&*prior);
     gc::Mark(trc, &obj, "AccessorShape getter or setter");
     if (obj == *objp)
         return;
 
     Shape *parent = shape->parent;
     if (shape->inDictionary() || !parent->kids.isHash()) {
         *objp = obj;
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/Object/clear-dictionary-accessor-getset.js
@@ -0,0 +1,55 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+var gTestfile = "clear-dictionary-accessor-getset.js";
+var BUGNUMBER = 1082662;
+var summary =
+  "Properly handle GC of a dictionary accessor property whose [[Get]] or " +
+  "[[Set]] has been changed to |undefined|";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+function test(field)
+{
+  var prop = "[[" + field[0].toUpperCase() + field.substring(1) + "]]";
+  print("Testing for GC crashes after setting " + prop + " to undefined...");
+
+  function inner()
+  {
+     // Create an object with an accessor property.
+     var obj = { x: 42, get y() {}, set y(v) {} };
+
+     // 1) convert it to dictionary mode, in the process 2) creating a new
+     // version of that accessor property whose [[Get]] and [[Set]] are objects
+     // that trigger post barriers.
+     delete obj.x;
+
+     var desc = {};
+     desc[field] = undefined;
+
+     // Overwrite [[field]] with undefined.  Note #1 above is necessary so this
+     // is an actual *overwrite*, and not (possibly) a shape-tree fork that
+     // doesn't overwrite.
+     Object.defineProperty(obj, "y", desc);
+
+  }
+
+  inner();
+  gc(); // In unfixed code, this crashes trying to mark a null [[field]].
+}
+
+test("get");
+test("set");
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");