Bug 961494 - Adjust an assertion to properly handle objects with built-in properties stored in reserved slots, where the last property of such an object may use a reserved slot that's not the last reserved slot. r=jorendorff, f=bhackett, a=sledru
authorJeff Walden <jwalden@mit.edu>
Wed, 12 Feb 2014 13:21:16 -0800
changeset 176304 56bd156c98ebc137c4ac5c35cb41b71ff8ed0a95
parent 176303 c1f340448e727b52c07b5ab5ba1fab80711a7057
child 176305 fdf71716eef04e4161e29d9201ebb597fb68b0b3
push id445
push userffxbld
push dateMon, 10 Mar 2014 22:05:19 +0000
treeherdermozilla-release@dc38b741b04e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff, sledru
bugs961494
milestone28.0
Bug 961494 - Adjust an assertion to properly handle objects with built-in properties stored in reserved slots, where the last property of such an object may use a reserved slot that's not the last reserved slot. r=jorendorff, f=bhackett, a=sledru
js/src/jsapi-tests/moz.build
js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp
js/src/tests/ecma_5/Exceptions/error-expando-reconfigure.js
js/src/vm/Shape.cpp
--- a/js/src/jsapi-tests/moz.build
+++ b/js/src/jsapi-tests/moz.build
@@ -24,16 +24,17 @@ UNIFIED_SOURCES += [
     'testDeepFreeze.cpp',
     'testDefineGetterSetterNonEnumerable.cpp',
     'testDefineProperty.cpp',
     'testEnclosingFunction.cpp',
     'testErrorCopying.cpp',
     'testException.cpp',
     'testExternalStrings.cpp',
     'testFindSCCs.cpp',
+    'testFreshGlobalEvalRedefinition.cpp',
     'testFuncCallback.cpp',
     'testFunctionProperties.cpp',
     'testGCExactRooting.cpp',
     'testGCFinalizeCallback.cpp',
     'testGCOutOfMemory.cpp',
     'testGCStoreBufferRemoval.cpp',
     'testHashTable.cpp',
     'testHashTableInit.cpp',
new file mode 100644
--- /dev/null
+++ b/js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "jsapi-tests/tests.h"
+
+static bool
+GlobalEnumerate(JSContext *cx, JS::Handle<JSObject*> obj)
+{
+    return JS_EnumerateStandardClasses(cx, obj);
+}
+
+static bool
+GlobalResolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id)
+{
+    bool resolved = false;
+    return JS_ResolveStandardClass(cx, obj, id, &resolved);
+}
+
+BEGIN_TEST(testRedefineGlobalEval)
+{
+    static const JSClass cls = {
+        "global", JSCLASS_GLOBAL_FLAGS,
+        JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
+        GlobalEnumerate, GlobalResolve, JS_ConvertStub
+    };
+
+    /* Create the global object. */
+    JS::CompartmentOptions options;
+    options.setVersion(JSVERSION_LATEST);
+    JS::Rooted<JSObject*> g(cx, JS_NewGlobalObject(cx, &cls, nullptr, JS::FireOnNewGlobalHook, options));
+    if (!g)
+        return false;
+
+    JSAutoCompartment ac(cx, g);
+    JS::Rooted<JS::Value> v(cx);
+    CHECK(JS_GetProperty(cx, g, "Object", &v));
+
+    static const char data[] = "Object.defineProperty(this, 'eval', { configurable: false });";
+    CHECK(JS_EvaluateScript(cx, g, data, mozilla::ArrayLength(data) - 1, __FILE__, __LINE__, v.address()));
+
+    return true;
+}
+END_TEST(testRedefineGlobalEval)
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/Exceptions/error-expando-reconfigure.js
@@ -0,0 +1,28 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+var gTestfile = "error-expando-reconfigure.js"
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 961494;
+var summary =
+  "Reconfiguring the first expando property added to an Error object " +
+  "shouldn't assert";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+var err = new Error(); // no message argument => no err.message property
+err.expando = 17;
+Object.defineProperty(err, "expando", { configurable: false });
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -360,20 +360,30 @@ JSObject::getChildPropertyOnDictionary(T
         child.setSlot(parent->maybeSlot());
     } else {
         if (child.hasMissingSlot()) {
             uint32_t slot;
             if (!allocSlot(cx, obj, &slot))
                 return nullptr;
             child.setSlot(slot);
         } else {
-            /* Slots can only be allocated out of order on objects in dictionary mode. */
+            /*
+             * Slots can only be allocated out of order on objects in
+             * dictionary mode.  Otherwise the child's slot must be after the
+             * parent's slot (if it has one), because slot number determines
+             * slot span for objects with that shape.  Usually child slot
+             * *immediately* follows parent slot, but there may be a slot gap
+             * when the object uses some -- but not all -- of its reserved
+             * slots to store properties.
+             */
             JS_ASSERT(obj->inDictionaryMode() ||
                       parent->hasMissingSlot() ||
-                      child.slot() == parent->maybeSlot() + 1);
+                      child.slot() == parent->maybeSlot() + 1 ||
+                      (parent->maybeSlot() + 1 < JSSLOT_FREE(obj->getClass()) &&
+                       child.slot() == JSSLOT_FREE(obj->getClass())));
         }
     }
 
     RootedShape shape(cx);
 
     if (obj->inDictionaryMode()) {
         JS_ASSERT(parent == obj->lastProperty());
         StackShape::AutoRooter childRoot(cx, &child);