Bug 1148750, part 6 - Implement ValidateAndApplyPropertyDescriptor up to step 5. r=efaust.
authorJason Orendorff <jorendorff@mozilla.com>
Thu, 09 Apr 2015 14:18:24 -0500
changeset 240425 8991a055ad452f47262bce7e7380b8956a541b17
parent 240424 6c70dc0045ddcf2f7e3296b4b865667fbe4d08f0
child 240426 393a6ac6cdcb63bdea3bd909aad01a8f066b2ea0
push id28636
push userkwierso@gmail.com
push dateThu, 23 Apr 2015 00:16:12 +0000
treeherdermozilla-central@a5af73b32ac8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersefaust
bugs1148750
milestone40.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 1148750, part 6 - Implement ValidateAndApplyPropertyDescriptor up to step 5. r=efaust.
js/src/tests/ecma_6/Class/classPrototype.js
js/src/vm/NativeObject.cpp
--- a/js/src/tests/ecma_6/Class/classPrototype.js
+++ b/js/src/tests/ecma_6/Class/classPrototype.js
@@ -17,26 +17,16 @@ for (let test of [a,b]) {
     var desiredPrototype = {};
     Object.defineProperty(desiredPrototype, "constructor", { writable: true,
                                                             configurable: true,
                                                             enumerable: false,
                                                             value: test });
     assertDeepEq(prototype, desiredPrototype);
 }
 
-try {
-    eval(\`class a {
-            constructor() { };
-            static [\"prototype\"]() { };
-          }\`);
-} catch (e if e instanceof TypeError) {
-    throw new Error("Congrats on making initprop respect non-writable " +
-                    "non-configurable properties. Uncomment the test below " +
-                    "for bonus points.");
-/*
 // As such, it should by a TypeError to try and overwrite "prototype" with a
 // static member. The only way to try is with a computed property name; the rest
 // are early errors.
 assertThrowsInstanceOf(() => eval(\`
                                   class a {
                                     constructor() { };
                                     static ["prototype"]() { }
                                   }
@@ -67,17 +57,15 @@ assertThrowsInstanceOf(() => eval(\`(
                                   }
                                   )\`), TypeError);
 assertThrowsInstanceOf(() => eval(\`(
                                   class a {
                                     constructor() { };
                                     static set ["prototype"](x) { }
                                   }
                                   )\`), TypeError);
-*/
-}
 `;
 
 if (classesEnabled())
     eval(test);
 
 if (typeof reportCompare === "function")
     reportCompare(0, 0, "OK");
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -1242,16 +1242,19 @@ AddOrChangeProperty(ExclusiveContext *cx
             MOZ_ASSERT(!desc.setter());
             return CallAddPropertyHookDense(cx, obj, index, desc.value());
         }
     }
 
     return CallAddPropertyHook(cx, obj, shape, desc.value());
 }
 
+static bool IsConfigurable(unsigned attrs) { return (attrs & JSPROP_PERMANENT) == 0; }
+static bool IsEnumerable(unsigned attrs) { return (attrs & JSPROP_ENUMERATE) != 0; }
+
 bool
 js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId id,
                          Handle<PropertyDescriptor> desc_,
                          ObjectOpResult& result)
 {
     desc_.assertValid();
 
     // Section numbers and step numbers below refer to ES6 draft rev 36
@@ -1339,16 +1342,39 @@ js::NativeDefineProperty(ExclusiveContex
 
         if (!AddOrChangeProperty(cx, obj, id, desc))
             return false;
         return result.succeed();
     }
 
     MOZ_ASSERT(shape);
 
+    // Non-standard hack: Allow redefining non-configurable properties if
+    // JSPROP_REDEFINE_NONCONFIGURABLE is set _and_ the object is a non-DOM
+    // global. The idea is that a DOM object can never have such a thing on
+    // its proto chain directly on the web, so we should be OK optimizing
+    // access to accessors found on such an object. Bug 1105518 contemplates
+    // removing this hack.
+    bool skipRedefineChecks = (desc.attributes() & JSPROP_REDEFINE_NONCONFIGURABLE) &&
+                              obj->is<GlobalObject>() &&
+                              !obj->getClass()->isDOMClass();
+
+    // Steps 3-4 are redundant.
+
+    // Step 5. We use shapeAttrs as a stand-in for shape in many places below
+    // since shape might not be a pointer to a real Shape (see
+    // IsImplicitDenseOrTypedArrayElement).
+    unsigned shapeAttrs = GetShapeAttributes(obj, shape);
+    if (!IsConfigurable(shapeAttrs) && !skipRedefineChecks) {
+        if (desc.hasConfigurable() && desc.configurable())
+            return result.fail(JSMSG_CANT_REDEFINE_PROP);
+        if (desc.hasEnumerable() && desc.enumerable() != IsEnumerable(shapeAttrs))
+            return result.fail(JSMSG_CANT_REDEFINE_PROP);
+    }
+
     // If defining a getter or setter, we must check for its counterpart and
     // update the attributes and property ops.  A getter or setter is really
     // only half of a property.
     if (desc.isAccessorDescriptor()) {
         // If we are defining a getter whose setter was already defined, or
         // vice versa, finish the job via obj->changeProperty.
         if (IsImplicitDenseOrTypedArrayElement(shape)) {
             if (IsAnyTypedArray(obj)) {