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
authorJeff Walden <jwalden@mit.edu>
Wed, 12 Feb 2014 13:21:16 -0800
changeset 168542 c2d3aca06e007a3fc8c49432771423d10be470bc
parent 168541 06a9e307dcc13acf09b415effed175d4edace374
child 168543 387b5a9167b0f2d1f43ea6b42722aa79679cb2dd
push id26209
push userryanvm@gmail.com
push dateThu, 13 Feb 2014 15:30:03 +0000
treeherdermozilla-central@a2939bac372b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs961494
milestone30.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 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
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
@@ -347,20 +347,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());
         RootedGeneric<StackShape*> childRoot(cx, &child);