Backed out 20 changesets (bug 1138499, bug 1148750, bug 1149563, bug 1148652, bug 1123875, bug 1145636, bug 1147660, bug 1148568, bug 1142828) for talos other timeouts CLOSED TREE
authorWes Kocher <wkocher@mozilla.com>
Wed, 01 Apr 2015 19:17:45 -0700
changeset 237170 386c8b5b73c09b6186118258f524ce4c86c8af23
parent 237169 f06f8b6c444b037c83808ae28fe122d8296df8be
child 237171 7b75233a273c56691f5673dc71e5912ba1530f6a
push id57879
push userkwierso@gmail.com
push dateThu, 02 Apr 2015 02:25:28 +0000
treeherdermozilla-inbound@386c8b5b73c0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1138499, 1148750, 1149563, 1148652, 1123875, 1145636, 1147660, 1148568, 1142828
milestone40.0a1
backs out3fc49391f7fe66c18ecbdbb277adb8aca0b6f9ae
75e867c1c39e2ac635497f04081acd1e42655b14
7f6a4e3976f0c3af1fac9c01004008219a03e45a
91d7260118357c888df6c39dc4a0e0aacc2379c7
fc7c3cf0e52680a1c12e92c8c181913eb4fa1635
35a01c7e0f8d554472a2da83f7d032820fd4a05a
3125cc5a7a65ce1eb685fda3f64badebef9f8265
a9f10724b83bf8b28475545dd93c3eb4ead700c5
87132a806ab074da5f86249ad6f9a07d107977d5
e6b410c7b8478a14105af6072b76bba5e653fbb6
c350fe54d9c0f083be5d00ed40774c4fe43c2bcd
e4b971996b9467eb546c1ee3eadb9a70bc3b7f39
f221db19fb7591c55a9270601aef38623d75a427
c4599f0cff00124ad15dad719e125c41906c6fa3
c7388a9c393519117d55d2cc5326ae8c54ccae47
9b51b38317d6a85390d73074fd6344e576d2605d
ad243a3cd06f235a806329bde39c146ee9eeea88
fafda276abd9ec6a5f9f74461e3b1b36a0fae335
0a00470fdc2ac488d917602d5969a54ca87b8014
06dbe25231c283191c1cc4fea66c1c76add2c91c
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
Backed out 20 changesets (bug 1138499, bug 1148750, bug 1149563, bug 1148652, bug 1123875, bug 1145636, bug 1147660, bug 1148568, bug 1142828) for talos other timeouts CLOSED TREE Backed out changeset 3fc49391f7fe (bug 1148750) Backed out changeset 75e867c1c39e (bug 1148750) Backed out changeset 7f6a4e3976f0 (bug 1148750) Backed out changeset 91d726011835 (bug 1149563) Backed out changeset fc7c3cf0e526 (bug 1148652) Backed out changeset 35a01c7e0f8d (bug 1148652) Backed out changeset 3125cc5a7a65 (bug 1148652) Backed out changeset a9f10724b83b (bug 1148568) Backed out changeset 87132a806ab0 (bug 1147660) Backed out changeset e6b410c7b847 (bug 1147660) Backed out changeset c350fe54d9c0 (bug 1147660) Backed out changeset e4b971996b94 (bug 1147660) Backed out changeset f221db19fb75 (bug 1147660) Backed out changeset c4599f0cff00 (bug 1142828) Backed out changeset c7388a9c3935 (bug 1138499) Backed out changeset 9b51b38317d6 (bug 1138499) Backed out changeset ad243a3cd06f (bug 1138499) Backed out changeset fafda276abd9 (bug 1138499) Backed out changeset 0a00470fdc2a (bug 1145636) Backed out changeset 06dbe25231c2 (bug 1123875)
dom/bindings/test/mochitest.ini
dom/bindings/test/test_bug1123875.html
js/src/ctypes/CTypes.cpp
js/src/jit-test/tests/collections/Array-of-nonconfigurable-1.js
js/src/jit-test/tests/collections/Array-of-nonconfigurable-2.js
js/src/jsapi-tests/README
js/src/jsapi-tests/testDefinePropertyIgnoredAttributes.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsarray.cpp
js/src/jsgc.cpp
js/src/jsobj.cpp
js/src/jsobjinlines.h
js/src/json.cpp
js/src/proxy/BaseProxyHandler.cpp
js/src/tests/ecma_6/Class/classPrototype.js
js/src/vm/NativeObject-inl.h
js/src/vm/NativeObject.cpp
js/src/vm/NativeObject.h
js/src/vm/Shape.cpp
js/src/vm/TypedArrayObject.cpp
js/src/vm/TypedArrayObject.h
js/xpconnect/tests/chrome/test_xrayToJS.xul
--- a/dom/bindings/test/mochitest.ini
+++ b/dom/bindings/test/mochitest.ini
@@ -18,17 +18,16 @@ support-files =
 [test_bug788369.html]
 [test_bug852846.html]
 [test_bug862092.html]
 [test_bug1036214.html]
 skip-if = debug == false
 [test_bug963382.html]
 skip-if = debug == false
 [test_bug1041646.html]
-[test_bug1123875.html]
 [test_barewordGetsWindow.html]
 [test_callback_default_thisval.html]
 [test_cloneAndImportNode.html]
 [test_defineProperty.html]
 [test_enums.html]
 [test_exceptionThrowing.html]
 [test_exception_messages.html]
 [test_forOf.html]
deleted file mode 100644
--- a/dom/bindings/test/test_bug1123875.html
+++ /dev/null
@@ -1,14 +0,0 @@
-<!doctype html>
-<meta charset=utf-8>
-<title>Test for Bug 1123875</title>
-<script src=/resources/testharness.js></script>
-<script src=/resources/testharnessreport.js></script>
-<div id=log></div>
-<script>
-  test(() => {
-    assert_throws(new TypeError, () => {
-      "use strict";
-      document.childNodes.length = 0;
-    });
-  }, "setting a readonly attribute on a proxy in strict mode should throw a TypeError");
-</script>
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -778,25 +778,25 @@ static const JSClass sUInt64Class = {
   nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, Int64Base::Finalize
 };
 
 static const JSFunctionSpec sInt64StaticFunctions[] = {
   JS_FN("compare", Int64::Compare, 2, CTYPESFN_FLAGS),
   JS_FN("lo", Int64::Lo, 1, CTYPESFN_FLAGS),
   JS_FN("hi", Int64::Hi, 1, CTYPESFN_FLAGS),
-  // "join" is defined specially; see InitInt64Class.
+  JS_FN("join", Int64::Join, 2, CTYPESFN_FLAGS),
   JS_FS_END
 };
 
 static const JSFunctionSpec sUInt64StaticFunctions[] = {
   JS_FN("compare", UInt64::Compare, 2, CTYPESFN_FLAGS),
   JS_FN("lo", UInt64::Lo, 1, CTYPESFN_FLAGS),
   JS_FN("hi", UInt64::Hi, 1, CTYPESFN_FLAGS),
-  // "join" is defined specially; see InitInt64Class.
+  JS_FN("join", UInt64::Join, 2, CTYPESFN_FLAGS),
   JS_FS_END
 };
 
 static const JSFunctionSpec sInt64Functions[] = {
   JS_FN("toString", Int64::ToString, 0, CTYPESFN_FLAGS),
   JS_FN("toSource", Int64::ToSource, 0, CTYPESFN_FLAGS),
   JS_FS_END
 };
@@ -1097,31 +1097,31 @@ InitInt64Class(JSContext* cx,
   RootedObject prototype(cx, JS_InitClass(cx, parent, js::NullPtr(), clasp, construct,
                                           0, nullptr, fs, nullptr, static_fs));
   if (!prototype)
     return nullptr;
 
   RootedObject ctor(cx, JS_GetConstructor(cx, prototype));
   if (!ctor)
     return nullptr;
-
-  // Define the 'join' function as an extended native and stash
+  if (!JS_FreezeObject(cx, ctor))
+    return nullptr;
+
+  // Redefine the 'join' function as an extended native and stash
   // ctypes.{Int64,UInt64}.prototype in a reserved slot of the new function.
   MOZ_ASSERT(clasp == &sInt64ProtoClass || clasp == &sUInt64ProtoClass);
   JSNative native = (clasp == &sInt64ProtoClass) ? Int64::Join : UInt64::Join;
   JSFunction* fun = js::DefineFunctionWithReserved(cx, ctor, "join", native,
                       2, CTYPESFN_FLAGS);
   if (!fun)
     return nullptr;
 
   js::SetFunctionNativeReserved(fun, SLOT_FN_INT64PROTO,
     OBJECT_TO_JSVAL(prototype));
 
-  if (!JS_FreezeObject(cx, ctor))
-    return nullptr;
   if (!JS_FreezeObject(cx, prototype))
     return nullptr;
 
   return prototype;
 }
 
 static void
 AttachProtos(JSObject* proto, const AutoObjectVector& protos)
deleted file mode 100644
--- a/js/src/jit-test/tests/collections/Array-of-nonconfigurable-1.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// If Array.of tries to overwrite a non-configurable property, it throws a TypeError.
-
-load(libdir + "asserts.js");
-
-function C() {
-    Object.defineProperty(this, 0, {value: "v", configurable: false});
-}
-assertThrowsInstanceOf(() => Array.of.call(C, 1, 2, 3), TypeError);
deleted file mode 100644
--- a/js/src/jit-test/tests/collections/Array-of-nonconfigurable-2.js
+++ /dev/null
@@ -1,16 +0,0 @@
-// Array.of does not overwrite non-configurable properties.
-
-load(libdir + "asserts.js");
-
-var obj;
-function C() {
-    obj = this;
-    Object.defineProperty(this, 0, {value: "v", configurable: false});
-}
-try { Array.of.call(C, 1); } catch (e) {}
-assertDeepEq(Object.getOwnPropertyDescriptor(obj, 0), {
-    configurable: false,
-    enumerable: false,
-    value: "v",
-    writable: false
-});
--- a/js/src/jsapi-tests/README
+++ b/js/src/jsapi-tests/README
@@ -1,42 +1,40 @@
-# JSAPI Test Suite
+=== JSAPI Test Suite
 
 The tests in this directory exercise the JSAPI.
 
 
-## Building and running the tests
+--- Building and running the tests
 
 If you built JS, you already built the tests.
 
 The tests are built by default when you build JS. All the tests are compiled
 into a single binary named jsapi-tests. They all run in a single process.
 
 To run the tests:
 
     cd $OBJDIR/dist/bin
     ./jsapi-tests
 
 To run the tests in a debugger:
 
     cd $OBJDIR/dist/bin
     gdb ./jsapi-tests
 
+--- Creating new tests
 
-## Creating new tests
-
-1.  You can either add to an existing test*.cpp file or make a new one.
+ 1. You can either add to an existing test*.cpp file or make a new one.
     Copy an existing test and replace the body with your test code.
     The test harness provides `cx`, `rt`, and `global` for your use.
 
-2.  If you made a new .cpp file, add it to the UNIFIED_SOURCES list
-    in moz.build.
+ 2. If you made a new .cpp file, add it to the CPPSRCS list in Makefile.in.
 
 
-## Writing test code
+--- Writing test code
 
 Here is a sample test:
 
     #include "tests.h"
 
     BEGIN_TEST(testIntString_bug515273)
     {
         RootedValue v(cx);
@@ -132,17 +130,17 @@ tests.h:
         True if v1 and v2 are the same value according to the ES5 SameValue()
         function, to wit:
 
         SameValue(NaN, NaN) is true.
         SameValue(-0, 0) is false.
         Otherwise SameValue(a, b) iff a === b.
 
 
-## Custom test setup
+--- Custom test setup
 
 Before executing each test, the test framework calls the tests' init() member
 function, which populates the rt, cx, and global member variables.
 
 A test can customize the test setup process by overloading virtual member
 functions, like this:
 
     const JSClass globalClassWithResolve = { ... };
--- a/js/src/jsapi-tests/testDefinePropertyIgnoredAttributes.cpp
+++ b/js/src/jsapi-tests/testDefinePropertyIgnoredAttributes.cpp
@@ -2,16 +2,25 @@
  * 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 const unsigned IgnoreWithValue = JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_READONLY |
+                               JSPROP_IGNORE_PERMANENT;
+static const unsigned IgnoreAll = IgnoreWithValue | JSPROP_IGNORE_VALUE;
+
+static const unsigned AllowConfigure = IgnoreAll & ~JSPROP_IGNORE_PERMANENT;
+static const unsigned AllowEnumerate = IgnoreAll & ~JSPROP_IGNORE_ENUMERATE;
+static const unsigned AllowWritable  = IgnoreAll & ~JSPROP_IGNORE_READONLY;
+static const unsigned ValueWithConfigurable = IgnoreWithValue & ~JSPROP_IGNORE_PERMANENT;
+
 static bool
 Getter(JSContext* cx, unsigned argc, JS::Value* vp)
 {
     JS::CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setBoolean(true);
     return true;
 }
 
@@ -37,64 +46,56 @@ CheckDescriptor(JS::Handle<JSPropertyDes
 }
 
 BEGIN_TEST(testDefinePropertyIgnoredAttributes)
 {
     JS::RootedObject obj(cx, JS_NewPlainObject(cx));
     JS::Rooted<JSPropertyDescriptor> desc(cx);
     JS::RootedValue defineValue(cx);
 
-    // Try a getter. Allow it to fill in the defaults. Because we're passing a
-    // JSNative, JS_DefineProperty will infer JSPROP_GETTER even though we
-    // aren't passing it.
+    // Try a getter. Allow it to fill in the defaults.
     CHECK(JS_DefineProperty(cx, obj, "foo", defineValue,
-                            JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_PERMANENT | JSPROP_SHARED,
+                            IgnoreAll | JSPROP_SHARED,
                             Getter));
 
     CHECK(JS_GetPropertyDescriptor(cx, obj, "foo", &desc));
 
     // Note that JSPROP_READONLY is meaningless for accessor properties.
     CHECK(CheckDescriptor(desc, AccessorDescriptor, false, true, false));
 
     // Install another configurable property, so we can futz with it.
     CHECK(JS_DefineProperty(cx, obj, "bar", defineValue,
-                            JSPROP_IGNORE_ENUMERATE | JSPROP_SHARED,
+                            AllowConfigure | JSPROP_SHARED,
                             Getter));
     CHECK(JS_GetPropertyDescriptor(cx, obj, "bar", &desc));
     CHECK(CheckDescriptor(desc, AccessorDescriptor, false, true, true));
 
-    // Rewrite the descriptor to now be enumerable, leaving the configurability
-    // unchanged.
+    // Rewrite the descriptor to now be enumerable, ensuring that the lack of
+    // configurablity stayed.
     CHECK(JS_DefineProperty(cx, obj, "bar", defineValue,
-                            JSPROP_IGNORE_PERMANENT | JSPROP_ENUMERATE | JSPROP_SHARED,
+                            AllowEnumerate |
+                            JSPROP_ENUMERATE |
+                            JSPROP_SHARED,
                             Getter));
     CHECK(JS_GetPropertyDescriptor(cx, obj, "bar", &desc));
     CHECK(CheckDescriptor(desc, AccessorDescriptor, true, true, true));
 
     // Now try the same game with a value property
     defineValue.setObject(*obj);
-    CHECK(JS_DefineProperty(cx, obj, "baz", defineValue,
-                            JSPROP_IGNORE_ENUMERATE |
-                            JSPROP_IGNORE_READONLY |
-                            JSPROP_IGNORE_PERMANENT));
+    CHECK(JS_DefineProperty(cx, obj, "baz", defineValue, IgnoreWithValue));
     CHECK(JS_GetPropertyDescriptor(cx, obj, "baz", &desc));
     CHECK(CheckDescriptor(desc, DataDescriptor, false, false, false));
 
     // Now again with a configurable property
-    CHECK(JS_DefineProperty(cx, obj, "quux", defineValue,
-                            JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_READONLY));
-    CHECK(JS_GetPropertyDescriptor(cx, obj, "quux", &desc));
+    CHECK(JS_DefineProperty(cx, obj, "quox", defineValue, ValueWithConfigurable));
+    CHECK(JS_GetPropertyDescriptor(cx, obj, "quox", &desc));
     CHECK(CheckDescriptor(desc, DataDescriptor, false, false, true));
 
-    // Just make it writable. Leave the old value and everything else alone.
+    // Just make it writable. Leave the old value and everythign else alone.
     defineValue.setUndefined();
-    CHECK(JS_DefineProperty(cx, obj, "quux", defineValue,
-                            JSPROP_IGNORE_ENUMERATE |
-                            JSPROP_IGNORE_PERMANENT |
-                            JSPROP_IGNORE_VALUE));
-
-    CHECK(JS_GetPropertyDescriptor(cx, obj, "quux", &desc));
+    CHECK(JS_DefineProperty(cx, obj, "quox", defineValue, AllowWritable));
+    CHECK(JS_GetPropertyDescriptor(cx, obj, "quox", &desc));
     CHECK(CheckDescriptor(desc, DataDescriptor, false, true, true));
     CHECK_SAME(JS::ObjectValue(*obj), desc.value());
 
     return true;
 }
 END_TEST(testDefinePropertyIgnoredAttributes)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3433,17 +3433,16 @@ JS_DefineFunctions(JSContext* cx, Handle
             if (!(flags & JSPROP_DEFINE_LATE))
                 continue;
             break;
           default:
             MOZ_ASSERT(behavior == DontDefineLateProperties);
             if (flags & JSPROP_DEFINE_LATE)
                 continue;
         }
-        flags &= ~JSPROP_DEFINE_LATE;
 
         /*
          * Define a generic arity N+1 static method for the arity N prototype
          * method if flags contains JSFUN_GENERIC_NATIVE.
          */
         if (flags & JSFUN_GENERIC_NATIVE) {
             // We require that any consumers using JSFUN_GENERIC_NATIVE stash
             // the prototype and constructor in the global slots before invoking
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2085,17 +2085,17 @@ inline int CheckIsSetterOp(JSSetterOp op
   (static_cast<void>(sizeof(JS::detail::CheckIsNative(v))), \
    reinterpret_cast<To>(v))
 
 #define JS_CAST_STRING_TO(s, To) \
   (static_cast<void>(sizeof(JS::detail::CheckIsCharacterLiteral(s))), \
    reinterpret_cast<To>(s))
 
 #define JS_CHECK_ACCESSOR_FLAGS(flags) \
-  (static_cast<mozilla::EnableIf<((flags) & ~(JSPROP_ENUMERATE | JSPROP_PERMANENT)) == 0>::Type>(0), \
+  (static_cast<mozilla::EnableIf<!((flags) & (JSPROP_READONLY | JSPROP_SHARED | JSPROP_PROPOP_ACCESSORS))>::Type>(0), \
    (flags))
 
 #define JS_PROPERTYOP_GETTER(v) \
   (static_cast<void>(sizeof(JS::detail::CheckIsGetterOp(v))), \
    reinterpret_cast<JSNative>(v))
 
 #define JS_PROPERTYOP_SETTER(v) \
   (static_cast<void>(sizeof(JS::detail::CheckIsSetterOp(v))), \
@@ -2516,23 +2516,16 @@ class PropertyDescriptorOperations
         MOZ_ASSERT((bit & (bit - 1)) == 0);  // only a single bit
         return (desc()->attrs & bit) != 0;
     }
 
     bool hasAny(unsigned bits) const {
         return (desc()->attrs & bits) != 0;
     }
 
-    bool hasAll(unsigned bits) const {
-        return (desc()->attrs & bits) == bits;
-    }
-
-    // Non-API attributes bit used internally for arguments objects.
-    enum { SHADOWABLE = JSPROP_INTERNAL_USE_BIT };
-
   public:
     // Descriptors with JSGetterOp/JSSetterOp are considered data
     // descriptors. It's complicated.
     bool isAccessorDescriptor() const { return hasAny(JSPROP_GETTER | JSPROP_SETTER); }
     bool isGenericDescriptor() const {
         return (desc()->attrs&
                 (JSPROP_GETTER | JSPROP_SETTER | JSPROP_IGNORE_READONLY | JSPROP_IGNORE_VALUE)) ==
                (JSPROP_IGNORE_READONLY | JSPROP_IGNORE_VALUE);
@@ -2570,69 +2563,16 @@ class PropertyDescriptorOperations
     bool isShared() const { return has(JSPROP_SHARED); }
 
     JS::HandleObject object() const {
         return JS::HandleObject::fromMarkedLocation(&desc()->obj);
     }
     unsigned attributes() const { return desc()->attrs; }
     JSGetterOp getter() const { return desc()->getter; }
     JSSetterOp setter() const { return desc()->setter; }
-
-    void assertValid() const {
-#ifdef DEBUG
-        MOZ_ASSERT((attributes() & ~(JSPROP_ENUMERATE | JSPROP_IGNORE_ENUMERATE |
-                                     JSPROP_PERMANENT | JSPROP_IGNORE_PERMANENT |
-                                     JSPROP_READONLY | JSPROP_IGNORE_READONLY |
-                                     JSPROP_IGNORE_VALUE |
-                                     JSPROP_GETTER |
-                                     JSPROP_SETTER |
-                                     JSPROP_SHARED |
-                                     JSPROP_REDEFINE_NONCONFIGURABLE |
-                                     SHADOWABLE)) == 0);
-        MOZ_ASSERT(!hasAll(JSPROP_IGNORE_ENUMERATE | JSPROP_ENUMERATE));
-        MOZ_ASSERT(!hasAll(JSPROP_IGNORE_PERMANENT | JSPROP_PERMANENT));
-        if (isAccessorDescriptor()) {
-            MOZ_ASSERT(has(JSPROP_SHARED));
-            MOZ_ASSERT(!has(JSPROP_READONLY));
-            MOZ_ASSERT(!has(JSPROP_IGNORE_READONLY));
-            MOZ_ASSERT(!has(JSPROP_IGNORE_VALUE));
-            MOZ_ASSERT(!has(SHADOWABLE));
-            MOZ_ASSERT(value().isUndefined());
-            MOZ_ASSERT_IF(!has(JSPROP_GETTER), !getter());
-            MOZ_ASSERT_IF(!has(JSPROP_SETTER), !setter());
-        } else {
-            MOZ_ASSERT(!hasAll(JSPROP_IGNORE_READONLY | JSPROP_READONLY));
-            MOZ_ASSERT_IF(has(JSPROP_IGNORE_VALUE), value().isUndefined());
-        }
-        MOZ_ASSERT(getter() != JS_PropertyStub);
-        MOZ_ASSERT(setter() != JS_StrictPropertyStub);
-#endif
-    }
-
-    void assertComplete() const {
-#ifdef DEBUG
-        assertValid();
-        MOZ_ASSERT((attributes() & ~(JSPROP_ENUMERATE |
-                                     JSPROP_PERMANENT |
-                                     JSPROP_READONLY |
-                                     JSPROP_GETTER |
-                                     JSPROP_SETTER |
-                                     JSPROP_SHARED |
-                                     JSPROP_REDEFINE_NONCONFIGURABLE |
-                                     SHADOWABLE)) == 0);
-        MOZ_ASSERT_IF(isAccessorDescriptor(), has(JSPROP_GETTER) && has(JSPROP_SETTER));
-#endif
-    }
-
-    void assertCompleteIfFound() const {
-#ifdef DEBUG
-        if (object())
-            assertComplete();
-#endif
-    }
 };
 
 template <typename Outer>
 class MutablePropertyDescriptorOperations : public PropertyDescriptorOperations<Outer>
 {
     JSPropertyDescriptor * desc() { return static_cast<Outer*>(this)->extractMutable(); }
 
   public:
@@ -2688,49 +2628,29 @@ class MutablePropertyDescriptorOperation
         return JS::MutableHandleValue::fromMarkedLocation(&desc()->value);
     }
     void setValue(JS::HandleValue v) {
         MOZ_ASSERT(!(desc()->attrs & (JSPROP_GETTER | JSPROP_SETTER)));
         attributesRef() &= ~JSPROP_IGNORE_VALUE;
         value().set(v);
     }
 
-    void setConfigurable(bool configurable) {
-        setAttributes((desc()->attrs & ~(JSPROP_IGNORE_PERMANENT | JSPROP_PERMANENT)) |
-                      (configurable ? 0 : JSPROP_PERMANENT));
-    }
-    void setEnumerable(bool enumerable) {
-        setAttributes((desc()->attrs & ~(JSPROP_IGNORE_ENUMERATE | JSPROP_ENUMERATE)) |
-                      (enumerable ? JSPROP_ENUMERATE : 0));
-    }
-    void setWritable(bool writable) {
-        MOZ_ASSERT(!(desc()->attrs & (JSPROP_GETTER | JSPROP_SETTER)));
-        setAttributes((desc()->attrs & ~(JSPROP_IGNORE_READONLY | JSPROP_READONLY)) |
-                      (writable ? 0 : JSPROP_READONLY));
-    }
+    void setEnumerable() { desc()->attrs |= JSPROP_ENUMERATE; }
     void setAttributes(unsigned attrs) { desc()->attrs = attrs; }
 
     void setGetter(JSGetterOp op) {
         MOZ_ASSERT(op != JS_PropertyStub);
         desc()->getter = op;
     }
     void setSetter(JSSetterOp op) {
         MOZ_ASSERT(op != JS_StrictPropertyStub);
         desc()->setter = op;
     }
-    void setGetterObject(JSObject* obj) {
-        desc()->getter = reinterpret_cast<JSGetterOp>(obj);
-        desc()->attrs &= ~(JSPROP_IGNORE_VALUE | JSPROP_IGNORE_READONLY);
-        desc()->attrs |= JSPROP_GETTER | JSPROP_SHARED;
-    }
-    void setSetterObject(JSObject* obj) {
-        desc()->setter = reinterpret_cast<JSSetterOp>(obj);
-        desc()->attrs &= ~(JSPROP_IGNORE_VALUE | JSPROP_IGNORE_READONLY);
-        desc()->attrs |= JSPROP_SETTER | JSPROP_SHARED;
-    }
+    void setGetterObject(JSObject* obj) { desc()->getter = reinterpret_cast<JSGetterOp>(obj); }
+    void setSetterObject(JSObject* obj) { desc()->setter = reinterpret_cast<JSSetterOp>(obj); }
 
     JS::MutableHandleObject getterObject() {
         MOZ_ASSERT(this->hasGetterObject());
         return JS::MutableHandleObject::fromMarkedLocation(
                 reinterpret_cast<JSObject**>(&desc()->getter));
     }
     JS::MutableHandleObject setterObject() {
         MOZ_ASSERT(this->hasSetterObject());
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -500,77 +500,66 @@ js::CanonicalizeArrayLengthValue(JSConte
 
     if (d == *newLen)
         return true;
 
     JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
     return false;
 }
 
-/* ES6 draft rev 34 (2015 Feb 20) 9.4.2.4 ArraySetLength */
+/* ES6 20130308 draft 8.4.2.4 ArraySetLength */
 bool
 js::ArraySetLength(JSContext* cx, Handle<ArrayObject*> arr, HandleId id,
                    unsigned attrs, HandleValue value, ObjectOpResult& result)
 {
     MOZ_ASSERT(id == NameToId(cx->names().length));
 
     if (!arr->maybeCopyElementsForWrite(cx))
         return false;
 
-    // Step 1.
+    /* Steps 1-2 are irrelevant in our implementation. */
+
+    /* Steps 3-5. */
     uint32_t newLen;
-    if (attrs & JSPROP_IGNORE_VALUE) {
-        // The spec has us calling OrdinaryDefineOwnProperty if
-        // Desc.[[Value]] is absent, but our implementation is so different that
-        // this is impossible. Instead, set newLen to the current length and
-        // proceed to step 9.
-        newLen = arr->length();
-    } else {
-        // Step 2 is irrelevant in our implementation.
-
-        // Steps 3-7.
-        MOZ_ASSERT_IF(attrs & JSPROP_IGNORE_VALUE, value.isUndefined());
-        if (!CanonicalizeArrayLengthValue(cx, value, &newLen))
-            return false;
-
-        // Step 8 is irrelevant in our implementation.
-    }
-
-    // Steps 9-11.
+    if (!CanonicalizeArrayLengthValue(cx, value, &newLen))
+        return false;
+
+    // Abort if we're being asked to change enumerability or configurability.
+    // (The length property of arrays is non-configurable, so such attempts
+    // must fail.)  This behavior is spread throughout the ArraySetLength spec
+    // algorithm, but we only need check it once as our array implementation
+    // is internally so different from the spec algorithm.  (ES5 and ES6 define
+    // behavior by delegating to the default define-own-property algorithm --
+    // OrdinaryDefineOwnProperty in ES6, the default [[DefineOwnProperty]] in
+    // ES5 -- but we reimplement all the conflict-detection bits ourselves here
+    // so that we can use a customized length representation.)
+    if (!(attrs & JSPROP_PERMANENT) || (attrs & JSPROP_ENUMERATE))
+        return result.fail(JSMSG_CANT_REDEFINE_PROP);
+
+    /* Steps 6-7. */
     bool lengthIsWritable = arr->lengthIsWritable();
 #ifdef DEBUG
     {
         RootedShape lengthShape(cx, arr->lookupPure(id));
         MOZ_ASSERT(lengthShape);
         MOZ_ASSERT(lengthShape->writable() == lengthIsWritable);
     }
 #endif
+
     uint32_t oldLen = arr->length();
 
-    // Part of steps 1.a, 12.a, and 16: Fail if we're being asked to change
-    // enumerability or configurability, or otherwise break the object
-    // invariants. (ES6 checks these by calling OrdinaryDefineOwnProperty, but
-    // in SM, the array length property is hardly ordinary.)
-    if ((attrs & (JSPROP_PERMANENT | JSPROP_IGNORE_PERMANENT)) == 0 ||
-        (attrs & (JSPROP_ENUMERATE | JSPROP_IGNORE_ENUMERATE)) == JSPROP_ENUMERATE ||
-        (attrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0 ||
-        (!lengthIsWritable && (attrs & (JSPROP_READONLY | JSPROP_IGNORE_READONLY)) == 0))
-    {
-        return result.fail(JSMSG_CANT_REDEFINE_PROP);
-    }
-
-    // Steps 12-13 for arrays with non-writable length.
+    /* Steps 8-9 for arrays with non-writable length. */
     if (!lengthIsWritable) {
         if (newLen == oldLen)
             return result.succeed();
 
         return result.fail(JSMSG_CANT_REDEFINE_ARRAY_LENGTH);
     }
 
-    // Step 19.
+    /* Step 8. */
     bool succeeded = true;
     do {
         // The initialized length and capacity of an array only need updating
         // when non-hole elements are added or removed, which doesn't happen
         // when array length stays the same or increases.
         if (newLen >= oldLen)
             break;
 
@@ -622,20 +611,20 @@ js::ArraySetLength(JSContext* cx, Handle
         // |succeeded| to false.  Then we exit the loop, define the length
         // appropriately, and only then throw a TypeError, if necessary.
         uint32_t gap = oldLen - newLen;
         const uint32_t RemoveElementsFastLimit = 1 << 24;
         if (gap < RemoveElementsFastLimit) {
             // If we're removing a relatively small number of elements, just do
             // it exactly by the spec.
             while (newLen < oldLen) {
-                // Step 15a.
+                /* Step 15a. */
                 oldLen--;
 
-                // Steps 15b-d.
+                /* Steps 15b-d. */
                 ObjectOpResult deleteSucceeded;
                 if (!DeleteElement(cx, arr, oldLen, deleteSucceeded))
                     return false;
                 if (!deleteSucceeded) {
                     newLen = oldLen + 1;
                     succeeded = false;
                     break;
                 }
@@ -685,48 +674,45 @@ js::ArraySetLength(JSContext* cx, Handle
                                           ReverseIndexComparator()));
             }
 
             uint32_t index = UINT32_MAX;
             for (uint32_t i = 0; i < count; i++) {
                 MOZ_ASSERT(indexes[i] < index, "indexes should never repeat");
                 index = indexes[i];
 
-                // Steps 15b-d.
+                /* Steps 15b-d. */
                 ObjectOpResult deleteSucceeded;
                 if (!DeleteElement(cx, arr, index, deleteSucceeded))
                     return false;
                 if (!deleteSucceeded) {
                     newLen = index + 1;
                     succeeded = false;
                     break;
                 }
             }
         }
     } while (false);
 
-    // Update array length. Technically we should have been doing this
-    // throughout the loop, in step 19.d.iii.
+    /* Steps 12, 16. */
+
+    // Yes, we totally drop a non-stub getter/setter from a defineProperty
+    // API call on the floor here.  Given that getter/setter will go away in
+    // the long run, with accessors replacing them both internally and at the
+    // API level, just run with this.
+    RootedShape lengthShape(cx, arr->lookup(cx, id));
+    if (!NativeObject::changeProperty(cx, arr, lengthShape, attrs,
+                                      JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_SHARED,
+                                      array_length_getter, array_length_setter))
+    {
+        return false;
+    }
+
     arr->setLength(cx, newLen);
 
-    // Step 20.
-    if (attrs & JSPROP_READONLY) {
-        // Yes, we totally drop a non-stub getter/setter from a defineProperty
-        // API call on the floor here.  Given that getter/setter will go away in
-        // the long run, with accessors replacing them both internally and at the
-        // API level, just run with this.
-        RootedShape lengthShape(cx, arr->lookup(cx, id));
-        if (!NativeObject::changeProperty(cx, arr, lengthShape,
-                                          lengthShape->attributes() | JSPROP_READONLY,
-                                          array_length_getter, array_length_setter))
-        {
-            return false;
-        }
-    }
-
     // All operations past here until the |!succeeded| code must be infallible,
     // so that all element fields remain properly synchronized.
 
     // Trim the initialized length, if needed, to preserve the <= length
     // invariant.  (Capacity was already reduced during element deletion, if
     // necessary.)
     ObjectElements* header = arr->getElementsHeader();
     header->initializedLength = Min(header->initializedLength, newLen);
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -7259,17 +7259,17 @@ NewMemoryInfoObject(JSContext* cx)
         { "gcIsHighFrequencyMode", GCHighFreqGetter },
         { "gcNumber", GCNumberGetter },
         { "majorGCCount", MajorGCCountGetter },
         { "minorGCCount", MinorGCCountGetter }
     };
 
     for (size_t i = 0; i < mozilla::ArrayLength(getters); i++) {
         if (!JS_DefineProperty(cx, obj, getters[i].name, UndefinedHandleValue,
-                               JSPROP_ENUMERATE | JSPROP_SHARED,
+                               JSPROP_READONLY | JSPROP_SHARED | JSPROP_ENUMERATE,
                                getters[i].getter, nullptr))
         {
             return nullptr;
         }
     }
 
     RootedObject zoneObj(cx, JS_NewObject(cx, nullptr));
     if (!zoneObj)
@@ -7289,17 +7289,17 @@ NewMemoryInfoObject(JSContext* cx)
         { "maxMalloc", ZoneMaxMallocGetter },
         { "delayBytes", ZoneGCDelayBytesGetter },
         { "heapGrowthFactor", ZoneGCHeapGrowthFactorGetter },
         { "gcNumber", ZoneGCNumberGetter }
     };
 
     for (size_t i = 0; i < mozilla::ArrayLength(zoneGetters); i++) {
         if (!JS_DefineProperty(cx, zoneObj, zoneGetters[i].name, UndefinedHandleValue,
-                               JSPROP_ENUMERATE | JSPROP_SHARED,
+                               JSPROP_READONLY | JSPROP_SHARED | JSPROP_ENUMERATE,
                                zoneGetters[i].getter, nullptr))
         {
             return nullptr;
         }
     }
 
     return obj;
 }
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -622,18 +622,54 @@ DefinePropertyOnArray(JSContext* cx, Han
 // ES6 draft rev31 9.4.5.3 [[DefineOwnProperty]]
 static bool
 DefinePropertyOnTypedArray(JSContext* cx, HandleObject obj, HandleId id,
                            Handle<PropertyDescriptor> desc, ObjectOpResult& result)
 {
     MOZ_ASSERT(IsAnyTypedArray(obj));
     // Steps 3.a-c.
     uint64_t index;
-    if (IsTypedArrayIndex(id, &index))
-        return DefineTypedArrayElement(cx, obj, index, desc, result);
+    if (IsTypedArrayIndex(id, &index)) {
+        // These are all substeps of 3.c.
+        // Steps i-vi.
+        // We (wrongly) ignore out of range defines with a value.
+        if (index >= AnyTypedArrayLength(obj))
+            return result.succeed();
+
+        // Step vii.
+        if (desc.isAccessorDescriptor())
+            return result.fail(JSMSG_CANT_REDEFINE_PROP);
+
+        // Step viii.
+        if (desc.hasConfigurable() && desc.configurable())
+            return result.fail(JSMSG_CANT_REDEFINE_PROP);
+
+        // Step ix.
+        if (desc.hasEnumerable() && !desc.enumerable())
+            return result.fail(JSMSG_CANT_REDEFINE_PROP);
+
+        // Step x.
+        if (desc.hasWritable() && !desc.writable())
+            return result.fail(JSMSG_CANT_REDEFINE_PROP);
+
+        // Step xi.
+        if (desc.hasValue()) {
+            double d;
+            if (!ToNumber(cx, desc.value(), &d))
+                return false;
+
+            if (obj->is<TypedArrayObject>())
+                TypedArrayObject::setElement(obj->as<TypedArrayObject>(), index, d);
+            else
+                SharedTypedArrayObject::setElement(obj->as<SharedTypedArrayObject>(), index, d);
+        }
+
+        // Step xii.
+        return result.succeed();
+    }
 
     // Step 4.
     return DefinePropertyOnObject(cx, obj.as<NativeObject>(), id, desc, result);
 }
 
 bool
 js::StandardDefineProperty(JSContext* cx, HandleObject obj, HandleId id,
                            Handle<PropertyDescriptor> desc, ObjectOpResult& result)
@@ -809,34 +845,34 @@ js::CheckPropertyDescriptorAccessors(JSC
             return false;
     }
     return true;
 }
 
 void
 js::CompletePropertyDescriptor(MutableHandle<PropertyDescriptor> desc)
 {
-    desc.assertValid();
-
     if (desc.isGenericDescriptor() || desc.isDataDescriptor()) {
+        if (!desc.hasValue())
+            desc.value().setUndefined();
         if (!desc.hasWritable())
             desc.attributesRef() |= JSPROP_READONLY;
         desc.attributesRef() &= ~(JSPROP_IGNORE_READONLY | JSPROP_IGNORE_VALUE);
     } else {
         if (!desc.hasGetterObject())
             desc.setGetterObject(nullptr);
         if (!desc.hasSetterObject())
             desc.setSetterObject(nullptr);
         desc.attributesRef() |= JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED;
     }
+    if (!desc.hasEnumerable())
+        desc.attributesRef() &= ~JSPROP_ENUMERATE;
     if (!desc.hasConfigurable())
         desc.attributesRef() |= JSPROP_PERMANENT;
     desc.attributesRef() &= ~(JSPROP_IGNORE_PERMANENT | JSPROP_IGNORE_ENUMERATE);
-
-    desc.assertComplete();
 }
 
 bool
 js::ReadPropertyDescriptors(JSContext* cx, HandleObject props, bool checkAccessors,
                             AutoIdVector* ids, AutoPropertyDescriptorVector* descs)
 {
     if (!GetPropertyKeys(cx, props, JSITER_OWNONLY | JSITER_SYMBOLS, ids))
         return false;
@@ -3082,22 +3118,18 @@ js::PreventExtensions(JSContext* cx, Han
     ObjectOpResult result;
     return PreventExtensions(cx, obj, result) && result.checkStrict(cx, obj);
 }
 
 bool
 js::GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
                              MutableHandle<PropertyDescriptor> desc)
 {
-    if (GetOwnPropertyOp op = obj->getOps()->getOwnPropertyDescriptor) {
-        bool ok = op(cx, obj, id, desc);
-        if (ok)
-            desc.assertCompleteIfFound();
-        return ok;
-    }
+    if (GetOwnPropertyOp op = obj->getOps()->getOwnPropertyDescriptor)
+        return op(cx, obj, id, desc);
 
     RootedShape shape(cx);
     if (!NativeLookupOwnProperty<CanGC>(cx, obj.as<NativeObject>(), id, &shape))
         return false;
     if (!shape) {
         desc.object().set(nullptr);
         return true;
     }
@@ -3137,25 +3169,23 @@ js::GetOwnPropertyDescriptor(JSContext* 
     }
 
     RootedValue value(cx);
     if (doGet && !GetProperty(cx, obj, obj, id, &value))
         return false;
 
     desc.value().set(value);
     desc.object().set(obj);
-    desc.assertComplete();
     return true;
 }
 
 bool
 js::DefineProperty(JSContext* cx, HandleObject obj, HandleId id, Handle<PropertyDescriptor> desc,
                    ObjectOpResult& result)
 {
-    desc.assertValid();
     if (DefinePropertyOp op = obj->getOps()->defineProperty)
         return op(cx, obj, id, desc, result);
     return NativeDefineProperty(cx, obj.as<NativeObject>(), id, desc, result);
 }
 
 bool
 js::DefineProperty(ExclusiveContext* cx, HandleObject obj, HandleId id, HandleValue value,
                    JSGetterOp getter, JSSetterOp setter, unsigned attrs,
@@ -3253,22 +3283,18 @@ js::SetImmutablePrototype(ExclusiveConte
 
 bool
 js::GetPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
                           MutableHandle<PropertyDescriptor> desc)
 {
     RootedObject pobj(cx);
 
     for (pobj = obj; pobj;) {
-        if (pobj->is<ProxyObject>()) {
-            bool ok = Proxy::getPropertyDescriptor(cx, pobj, id, desc);
-            if (ok)
-                desc.assertCompleteIfFound();
-            return ok;
-        }
+        if (pobj->is<ProxyObject>())
+            return Proxy::getPropertyDescriptor(cx, pobj, id, desc);
 
         if (!GetOwnPropertyDescriptor(cx, pobj, id, desc))
             return false;
 
         if (desc.object())
             return true;
 
         if (!GetPrototype(cx, pobj, &pobj))
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -835,16 +835,50 @@ Unbox(JSContext* cx, HandleObject obj, M
     else if (obj->is<DateObject>())
         vp.set(obj->as<DateObject>().UTCTime());
     else
         vp.setUndefined();
 
     return true;
 }
 
+static inline unsigned
+ApplyAttributes(unsigned attrs, bool enumerable, bool writable, bool configurable)
+{
+    /*
+     * Respect the fact that some callers may want to preserve existing attributes as much as
+     * possible, or install defaults otherwise.
+     */
+    if (attrs & JSPROP_IGNORE_ENUMERATE) {
+        attrs &= ~JSPROP_IGNORE_ENUMERATE;
+        if (enumerable)
+            attrs |= JSPROP_ENUMERATE;
+        else
+            attrs &= ~JSPROP_ENUMERATE;
+    }
+    if (attrs & JSPROP_IGNORE_READONLY) {
+        attrs &= ~JSPROP_IGNORE_READONLY;
+        // Only update the writability if it's relevant
+        if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
+            if (!writable)
+                attrs |= JSPROP_READONLY;
+            else
+                attrs &= ~JSPROP_READONLY;
+        }
+    }
+    if (attrs & JSPROP_IGNORE_PERMANENT) {
+        attrs &= ~JSPROP_IGNORE_PERMANENT;
+        if (!configurable)
+            attrs |= JSPROP_PERMANENT;
+        else
+            attrs &= ~JSPROP_PERMANENT;
+    }
+    return attrs;
+}
+
 extern NativeObject*
 InitClass(JSContext* cx, HandleObject obj, HandleObject parent_proto,
           const Class* clasp, JSNative constructor, unsigned nargs,
           const JSPropertySpec* ps, const JSFunctionSpec* fs,
           const JSPropertySpec* static_ps, const JSFunctionSpec* static_fs,
           NativeObject** ctorp = nullptr,
           gc::AllocKind ctorKind = JSFunction::FinalizeKind);
 
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -705,26 +705,26 @@ Walk(JSContext* cx, HandleObject holder,
             for (uint32_t i = 0; i < length; i++) {
                 if (!IndexToId(cx, i, &id))
                     return false;
 
                 /* Step 2a(iii)(1). */
                 if (!Walk(cx, obj, id, reviver, &newElement))
                     return false;
 
-                ObjectOpResult ignored;
                 if (newElement.isUndefined()) {
-                    /* Step 2a(iii)(2). The spec deliberately ignores strict failure. */
+                    /* Step 2a(iii)(2). */
+                    ObjectOpResult ignored;
                     if (!DeleteProperty(cx, obj, id, ignored))
                         return false;
                 } else {
-                    /* Step 2a(iii)(3). The spec deliberately ignores strict failure. */
-                    Rooted<PropertyDescriptor> desc(cx);
-                    desc.setDataDescriptor(newElement, JSPROP_ENUMERATE);
-                    if (!StandardDefineProperty(cx, obj, id, desc, ignored))
+                    /* Step 2a(iii)(3). */
+                    // XXX This definition should ignore success/failure, when
+                    //     our property-definition APIs indicate that.
+                    if (!DefineProperty(cx, obj, id, newElement))
                         return false;
                 }
             }
         } else {
             /* Step 2b(i). */
             AutoIdVector keys(cx);
             if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY, &keys))
                 return false;
@@ -733,26 +733,26 @@ Walk(JSContext* cx, HandleObject holder,
             RootedId id(cx);
             RootedValue newElement(cx);
             for (size_t i = 0, len = keys.length(); i < len; i++) {
                 /* Step 2b(ii)(1). */
                 id = keys[i];
                 if (!Walk(cx, obj, id, reviver, &newElement))
                     return false;
 
-                ObjectOpResult ignored;
                 if (newElement.isUndefined()) {
-                    /* Step 2b(ii)(2). The spec deliberately ignores strict failure. */
+                    /* Step 2b(ii)(2). */
+                    ObjectOpResult ignored;
                     if (!DeleteProperty(cx, obj, id, ignored))
                         return false;
                 } else {
-                    /* Step 2b(ii)(3). The spec deliberately ignores strict failure. */
-                    Rooted<PropertyDescriptor> desc(cx);
-                    desc.setDataDescriptor(newElement, JSPROP_ENUMERATE);
-                    if (!StandardDefineProperty(cx, obj, id, desc, ignored))
+                    /* Step 2b(ii)(3). */
+                    // XXX This definition should ignore success/failure, when
+                    //     our property-definition APIs indicate that.
+                    if (!DefineProperty(cx, obj, id, newElement))
                         return false;
                 }
             }
         }
     }
 
     /* Step 3. */
     RootedString key(cx, IdToString(cx, name));
--- a/js/src/proxy/BaseProxyHandler.cpp
+++ b/js/src/proxy/BaseProxyHandler.cpp
@@ -50,17 +50,16 @@ BaseProxyHandler::get(JSContext* cx, Han
 
     Rooted<PropertyDescriptor> desc(cx);
     if (!getPropertyDescriptor(cx, proxy, id, &desc))
         return false;
     if (!desc.object()) {
         vp.setUndefined();
         return true;
     }
-    desc.assertComplete();
     MOZ_ASSERT(desc.getter() != JS_PropertyStub);
     if (!desc.getter()) {
         vp.set(desc.value());
         return true;
     }
     if (desc.hasGetterObject())
         return InvokeGetter(cx, receiver, ObjectValue(*desc.getterObject()), vp);
     if (!desc.isShared())
@@ -80,17 +79,16 @@ BaseProxyHandler::set(JSContext* cx, Han
     // This method is not covered by any spec, but we follow ES6 draft rev 28
     // (2014 Oct 14) 9.1.9 fairly closely, adapting it slightly for
     // SpiderMonkey's particular foibles.
 
     // Steps 2-3.  (Step 1 is a superfluous assertion.)
     Rooted<PropertyDescriptor> ownDesc(cx);
     if (!getOwnPropertyDescriptor(cx, proxy, id, &ownDesc))
         return false;
-    ownDesc.assertCompleteIfFound();
 
     // The rest is factored out into a separate function with a weird name.
     // This algorithm continues just below.
     return SetPropertyIgnoringNamedGetter(cx, proxy, id, v, receiver, ownDesc, result);
 }
 
 bool
 js::SetPropertyIgnoringNamedGetter(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
@@ -182,18 +180,16 @@ BaseProxyHandler::getOwnEnumerableProper
         id = props[j];
         if (JSID_IS_SYMBOL(id))
             continue;
 
         AutoWaivePolicy policy(cx, proxy, id, BaseProxyHandler::GET);
         Rooted<PropertyDescriptor> desc(cx);
         if (!getOwnPropertyDescriptor(cx, proxy, id, &desc))
             return false;
-        desc.assertCompleteIfFound();
-
         if (desc.object() && desc.enumerable())
             props[i++].set(id);
     }
 
     MOZ_ASSERT(i <= props.length());
     props.resize(i);
 
     return true;
--- a/js/src/tests/ecma_6/Class/classPrototype.js
+++ b/js/src/tests/ecma_6/Class/classPrototype.js
@@ -17,16 +17,26 @@ 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"]() { }
                                   }
@@ -57,15 +67,17 @@ 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-inl.h
+++ b/js/src/vm/NativeObject-inl.h
@@ -23,16 +23,23 @@ namespace js {
 inline uint8_t*
 NativeObject::fixedData(size_t nslots) const
 {
     MOZ_ASSERT(ClassCanHaveFixedData(getClass()));
     MOZ_ASSERT(nslots == numFixedSlots() + (hasPrivate() ? 1 : 0));
     return reinterpret_cast<uint8_t*>(&fixedSlots()[nslots]);
 }
 
+/* static */ inline bool
+NativeObject::changePropertyAttributes(JSContext* cx, HandleNativeObject obj,
+                                       HandleShape shape, unsigned attrs)
+{
+    return !!changeProperty(cx, obj, shape, attrs, 0, shape->getter(), shape->setter());
+}
+
 inline void
 NativeObject::removeLastProperty(ExclusiveContext* cx)
 {
     MOZ_ASSERT(canRemoveLastProperty());
     JS_ALWAYS_TRUE(setLastProperty(cx, lastProperty()->previous()));
 }
 
 inline bool
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -1098,16 +1098,128 @@ UpdateShapeTypeAndValue(ExclusiveContext
     if (!shape->hasSlot() || !shape->hasDefaultGetter() || !shape->hasDefaultSetter())
         MarkTypePropertyNonData(cx, obj, id);
     if (!shape->writable())
         MarkTypePropertyNonWritable(cx, obj, id);
     return true;
 }
 
 static bool
+NativeSetExistingDataProperty(JSContext* cx, HandleNativeObject obj, HandleShape shape,
+                              HandleValue v, HandleValue receiver, ObjectOpResult& result);
+
+static inline bool
+DefinePropertyOrElement(ExclusiveContext* cx, HandleNativeObject obj, HandleId id,
+                        GetterOp getter, SetterOp setter, unsigned attrs, HandleValue value,
+                        bool callSetterAfterwards, ObjectOpResult& result)
+{
+    MOZ_ASSERT(getter != JS_PropertyStub);
+    MOZ_ASSERT(setter != JS_StrictPropertyStub);
+
+    /* Use dense storage for new indexed properties where possible. */
+    if (JSID_IS_INT(id) &&
+        !getter &&
+        !setter &&
+        attrs == JSPROP_ENUMERATE &&
+        (!obj->isIndexed() || !obj->containsPure(id)) &&
+        !IsAnyTypedArray(obj))
+    {
+        uint32_t index = JSID_TO_INT(id);
+        if (WouldDefinePastNonwritableLength(obj, index))
+            return result.fail(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH);
+
+        NativeObject::EnsureDenseResult edResult = obj->ensureDenseElements(cx, index, 1);
+        if (edResult == NativeObject::ED_FAILED)
+            return false;
+        if (edResult == NativeObject::ED_OK) {
+            obj->setDenseElementWithType(cx, index, value);
+            if (!CallAddPropertyHookDense(cx, obj, index, value))
+                return false;
+            return result.succeed();
+        }
+    }
+
+    if (obj->is<ArrayObject>()) {
+        Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
+        if (id == NameToId(cx->names().length)) {
+            if (!cx->shouldBeJSContext())
+                return false;
+            return ArraySetLength(cx->asJSContext(), arr, id, attrs, value, result);
+        }
+
+        uint32_t index;
+        if (IdIsIndex(id, &index)) {
+            if (WouldDefinePastNonwritableLength(obj, index))
+                return result.fail(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH);
+        }
+    }
+
+    // Don't define new indexed properties on typed arrays.
+    if (IsAnyTypedArray(obj)) {
+        uint64_t index;
+        if (IsTypedArrayIndex(id, &index))
+            return result.succeed();
+    }
+
+    AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
+    RootedShape shape(cx, NativeObject::putProperty(cx, obj, id, getter, setter,
+                                                    SHAPE_INVALID_SLOT, attrs, 0));
+    if (!shape)
+        return false;
+
+    if (!UpdateShapeTypeAndValue(cx, obj, shape, value))
+        return false;
+
+    /*
+     * Clear any existing dense index after adding a sparse indexed property,
+     * and investigate converting the object to dense indexes.
+     */
+    if (JSID_IS_INT(id)) {
+        if (!obj->maybeCopyElementsForWrite(cx))
+            return false;
+
+        uint32_t index = JSID_TO_INT(id);
+        NativeObject::removeDenseElementForSparseIndex(cx, obj, index);
+        NativeObject::EnsureDenseResult edResult = NativeObject::maybeDensifySparseElements(cx, obj);
+        if (edResult == NativeObject::ED_FAILED)
+            return false;
+        if (edResult == NativeObject::ED_OK) {
+            MOZ_ASSERT(!setter);
+            if (!CallAddPropertyHookDense(cx, obj, index, value))
+                return false;
+            return result.succeed();
+        }
+    }
+
+    if (!CallAddPropertyHook(cx, obj, shape, value))
+        return false;
+
+    if (callSetterAfterwards && setter) {
+        MOZ_ASSERT(!(attrs & JSPROP_GETTER));
+        MOZ_ASSERT(!(attrs & JSPROP_SETTER));
+        if (!cx->shouldBeJSContext())
+            return false;
+        RootedValue receiver(cx, ObjectValue(*obj));
+        return NativeSetExistingDataProperty(cx->asJSContext(), obj, shape, value, receiver,
+                                             result);
+    }
+
+    return result.succeed();
+}
+
+static unsigned
+ApplyOrDefaultAttributes(unsigned attrs, const Shape* shape = nullptr)
+{
+    bool enumerable = shape ? shape->enumerable() : false;
+    bool writable = shape ? shape->writable() : false;
+    bool configurable = shape ? shape->configurable() : false;
+    return ApplyAttributes(attrs, enumerable, writable, configurable);
+}
+
+static bool
 PurgeProtoChain(ExclusiveContext* cx, JSObject* objArg, HandleId id)
 {
     /* Root locally so we can re-assign. */
     RootedObject obj(cx, objArg);
 
     RootedShape shape(cx);
     while (obj) {
         /* Lookups will not be cached through non-native protos. */
@@ -1165,315 +1277,205 @@ PurgeScopeChainHelper(ExclusiveContext* 
 static inline bool
 PurgeScopeChain(ExclusiveContext* cx, HandleObject obj, HandleId id)
 {
     if (obj->isDelegate() && obj->isNative())
         return PurgeScopeChainHelper(cx, obj, id);
     return true;
 }
 
-static bool
-AddOrChangeProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId id,
-                    Handle<PropertyDescriptor> desc)
+/*
+ * Check whether we're redefining away a non-configurable getter, and
+ * throw if so.
+ */
+static inline bool
+CheckAccessorRedefinition(ExclusiveContext* cx, HandleObject obj, HandleShape shape,
+                          GetterOp getter, SetterOp setter, HandleId id, unsigned attrs)
 {
-    desc.assertComplete();
-
-    if (!PurgeScopeChain(cx, obj, id))
-        return false;
+    MOZ_ASSERT(shape->isAccessorDescriptor());
+    if (shape->configurable() || (getter == shape->getter() && setter == shape->setter()))
+        return true;
 
-    // Use dense storage for new indexed properties where possible.
-    if (JSID_IS_INT(id) &&
-        !desc.getter() &&
-        !desc.setter() &&
-        desc.attributes() == JSPROP_ENUMERATE &&
-        (!obj->isIndexed() || !obj->containsPure(id)) &&
-        !IsAnyTypedArray(obj))
+    /*
+     *  Only allow redefining 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.
+     */
+    if ((attrs & JSPROP_REDEFINE_NONCONFIGURABLE) &&
+        obj->is<GlobalObject>() &&
+        !obj->getClass()->isDOMClass())
     {
-        uint32_t index = JSID_TO_INT(id);
-        NativeObject::EnsureDenseResult edResult = obj->ensureDenseElements(cx, index, 1);
-        if (edResult == NativeObject::ED_FAILED)
-            return false;
-        if (edResult == NativeObject::ED_OK) {
-            obj->setDenseElementWithType(cx, index, desc.value());
-            if (!CallAddPropertyHookDense(cx, obj, index, desc.value()))
-                return false;
-            return true;
-        }
+        return true;
     }
 
-    RootedShape shape(cx, NativeObject::putProperty(cx, obj, id, desc.getter(), desc.setter(),
-                                                    SHAPE_INVALID_SLOT, desc.attributes(), 0));
-    if (!shape)
-        return false;
-
-    if (!UpdateShapeTypeAndValue(cx, obj, shape, desc.value()))
+    if (!cx->isJSContext())
         return false;
 
-    // Clear any existing dense index after adding a sparse indexed property,
-    // and investigate converting the object to dense indexes.
-    if (JSID_IS_INT(id)) {
-        if (!obj->maybeCopyElementsForWrite(cx))
-            return false;
-
-        uint32_t index = JSID_TO_INT(id);
-        NativeObject::removeDenseElementForSparseIndex(cx, obj, index);
-        NativeObject::EnsureDenseResult edResult =
-            NativeObject::maybeDensifySparseElements(cx, obj);
-        if (edResult == NativeObject::ED_FAILED)
-            return false;
-        if (edResult == NativeObject::ED_OK) {
-            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; }
-static bool IsWritable(unsigned attrs) { return (attrs & JSPROP_READONLY) == 0; }
-
-static bool IsAccessorDescriptor(unsigned attrs) {
-    return (attrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0;
-}
-
-static bool IsDataDescriptor(unsigned attrs) {
-    MOZ_ASSERT((attrs & (JSPROP_IGNORE_VALUE | JSPROP_IGNORE_READONLY)) == 0);
-    return !IsAccessorDescriptor(attrs);
-}
-
-template <AllowGC allowGC>
-static MOZ_ALWAYS_INLINE bool
-GetExistingProperty(JSContext* cx,
-                    typename MaybeRooted<JSObject*, allowGC>::HandleType receiver,
-                    typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
-                    typename MaybeRooted<Shape*, allowGC>::HandleType shape,
-                    typename MaybeRooted<Value, allowGC>::MutableHandleType vp);
-
-static bool
-GetExistingPropertyValue(ExclusiveContext* cx, HandleNativeObject obj, HandleId id,
-                         HandleShape shape, MutableHandleValue vp)
-{
-    if (IsImplicitDenseOrTypedArrayElement(shape)) {
-        vp.set(obj->getDenseOrTypedArrayElement(JSID_TO_INT(id)));
-        return true;
-    }
-    if (!cx->shouldBeJSContext())
-        return false;
-    return GetExistingProperty<CanGC>(cx->asJSContext(), obj, obj, shape, vp);
+    return Throw(cx->asJSContext(), id, JSMSG_CANT_REDEFINE_PROP);
 }
 
 bool
 js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId id,
-                         Handle<PropertyDescriptor> desc_,
+                         Handle<JSPropertyDescriptor> desc,
                          ObjectOpResult& result)
 {
-    desc_.assertValid();
-
-    // Section numbers and step numbers below refer to ES6 draft rev 36
-    // (17 March 2015).
-    //
-    // This function aims to implement 9.1.6 [[DefineOwnProperty]] as well as
-    // the [[DefineOwnProperty]] methods described in 9.4.2.1 (arrays), 9.4.4.2
-    // (arguments), and 9.4.5.3 (typed array views).
+    GetterOp getter = desc.getter();
+    SetterOp setter = desc.setter();
+    unsigned attrs = desc.attributes();
+    MOZ_ASSERT(getter != JS_PropertyStub);
+    MOZ_ASSERT(setter != JS_StrictPropertyStub);
+    MOZ_ASSERT(!(attrs & JSPROP_PROPOP_ACCESSORS));
 
-    // Dispense with custom behavior of exotic native objects first.
-    if (obj->is<ArrayObject>()) {
-        // 9.4.2.1 step 2. Redefining an array's length is very special.
-        Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
-        if (id == NameToId(cx->names().length)) {
-            if (!cx->shouldBeJSContext())
-                return false;
-            return ArraySetLength(cx->asJSContext(), arr, id, desc_.attributes(), desc_.value(),
-                                  result);
-        }
+    AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
+
+    RootedShape shape(cx);
+    RootedValue updateValue(cx, desc.value());
+    bool shouldDefine = true;
 
-        // 9.4.2.1 step 3. Don't extend a fixed-length array.
-        uint32_t index;
-        if (IdIsIndex(id, &index)) {
-            if (WouldDefinePastNonwritableLength(obj, index))
-                return result.fail(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH);
-        }
-    } else if (IsAnyTypedArray(obj)) {
-        // 9.4.5.3 step 3. Indexed properties of typed arrays are special.
-        uint64_t index;
-        if (IsTypedArrayIndex(id, &index)) {
-            if (!cx->shouldBeJSContext())
-                return false;
-            return DefineTypedArrayElement(cx->asJSContext(), obj, index, desc_, result);
+    /*
+     * 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 (!NativeLookupOwnProperty<CanGC>(cx, obj, id, &shape))
+            return false;
+        if (shape) {
+            /*
+             * 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)) {
+                    /* Ignore getter/setter properties added to typed arrays. */
+                    return result.succeed();
+                }
+                if (!NativeObject::sparsifyDenseElement(cx, obj, JSID_TO_INT(id)))
+                    return false;
+                shape = obj->lookup(cx, id);
+            }
+            if (shape->isAccessorDescriptor()) {
+                if (!CheckAccessorRedefinition(cx, obj, shape, getter, setter, id, attrs))
+                    return false;
+                attrs = ApplyOrDefaultAttributes(attrs, shape);
+                shape = NativeObject::changeProperty(cx, obj, shape, attrs,
+                                                     JSPROP_GETTER | JSPROP_SETTER,
+                                                     (attrs & JSPROP_GETTER)
+                                                     ? getter
+                                                     : shape->getter(),
+                                                     (attrs & JSPROP_SETTER)
+                                                     ? setter
+                                                     : shape->setter());
+                if (!shape)
+                    return false;
+                shouldDefine = false;
+            }
         }
-    } else if (obj->is<ArgumentsObject>()) {
-        if (id == NameToId(cx->names().length)) {
-            // Either we are resolving the .length property on this object, or
-            // redefining it. In the latter case only, we must set a bit. To
-            // distinguish the two cases, we note that when resolving, the
-            // property won't already exist; whereas the first time it is
-            // redefined, it will.
-            if (obj->containsPure(id))
-                obj->as<ArgumentsObject>().markLengthOverridden();
-        }
-    }
-
-    // 9.1.6.1 OrdinaryDefineOwnProperty steps 1-2.
-    RootedShape shape(cx);
-    if (desc_.hasValue()) {
+    } else if (desc.hasValue()) {
         // If we did a normal lookup here, it would cause resolve hook recursion in
         // the following case. Suppose the first script we run in a lazy global is
         // |parseInt()|.
         //   - js::InitNumberClass is called to resolve parseInt.
         //   - js::InitNumberClass tries to define the Number constructor on the
         //     global.
         //   - We end up here.
         //   - This lookup for 'Number' triggers the global resolve hook.
         //   - js::InitNumberClass is called again, this time to resolve Number.
         //   - It creates a second Number constructor, which trips an assertion.
         //
         // Therefore we do a special lookup that does not call the resolve hook.
         NativeLookupOwnPropertyNoResolve(cx, obj, id, &shape);
+
+        if (shape) {
+            // If any other JSPROP_IGNORE_* attributes are present, copy the
+            // corresponding JSPROP_* attributes from the existing property.
+            if (IsImplicitDenseOrTypedArrayElement(shape)) {
+                attrs = ApplyAttributes(attrs, true, true, !IsAnyTypedArray(obj));
+            } else {
+                attrs = ApplyOrDefaultAttributes(attrs, shape);
+
+                // Do not redefine a nonconfigurable accessor property.
+                if (shape->isAccessorDescriptor()) {
+                    if (!CheckAccessorRedefinition(cx, obj, shape, getter, setter, id, attrs))
+                        return false;
+                }
+            }
+        }
     } else {
+        // We have been asked merely to update some attributes. If the
+        // property already exists and it's a data property, we can just
+        // call JSObject::changeProperty.
         if (!NativeLookupOwnProperty<CanGC>(cx, obj, id, &shape))
             return false;
-    }
 
-    // From this point, the step numbers refer to
-    // 9.1.6.3, ValidateAndApplyPropertyDescriptor.
-    // Step 1 is a redundant assertion.
-
-    // Filling in desc: Here we make a copy of the desc_ argument. We will turn
-    // it into a complete descriptor before updating obj. The spec algorithm
-    // does not explicitly do this, but the end result is the same. Search for
-    // "fill in" below for places where the filling-in actually occurs.
-    Rooted<PropertyDescriptor> desc(cx, desc_);
-
-    // Step 2.
-    if (!shape) {
-        if (!obj->nonProxyIsExtensible())
-            return result.fail(JSMSG_OBJECT_NOT_EXTENSIBLE);
-
-        // Fill in missing desc fields with defaults.
-        CompletePropertyDescriptor(&desc);
-
-        if (!AddOrChangeProperty(cx, obj, id, desc))
-            return false;
-        return result.succeed();
-    }
-
-    // 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);
-    }
-
-    // Fill in desc.[[Configurable]] and desc.[[Enumerable]] if missing.
-    if (!desc.hasConfigurable())
-        desc.setConfigurable(IsConfigurable(shapeAttrs));
-    if (!desc.hasEnumerable())
-        desc.setEnumerable(IsEnumerable(shapeAttrs));
-
-    // Steps 6-9.
-    if (desc.isGenericDescriptor()) {
-        // Step 6. No further validation is required.
+        if (shape) {
+            // Don't forget about arrays.
+            if (IsImplicitDenseOrTypedArrayElement(shape)) {
+                if (IsAnyTypedArray(obj)) {
+                    /*
+                     * Silently ignore attempts to change individual index attributes.
+                     * FIXME: Uses the same broken behavior as for accessors. This should
+                     *        fail.
+                     */
+                    return result.succeed();
+                }
+                if (!NativeObject::sparsifyDenseElement(cx, obj, JSID_TO_INT(id)))
+                    return false;
+                shape = obj->lookup(cx, id);
+            }
 
-        // Fill in desc. A generic descriptor has none of these fields, so copy
-        // everything from shape.
-        MOZ_ASSERT(!desc.hasValue());
-        MOZ_ASSERT(!desc.hasWritable());
-        MOZ_ASSERT(!desc.hasGetterObject());
-        MOZ_ASSERT(!desc.hasSetterObject());
-        if (IsDataDescriptor(shapeAttrs)) {
-            RootedValue currentValue(cx);
-            if (!GetExistingPropertyValue(cx, obj, id, shape, &currentValue))
+            if (shape->isAccessorDescriptor() &&
+                !CheckAccessorRedefinition(cx, obj, shape, getter, setter, id, attrs))
+            {
                 return false;
-            desc.setValue(currentValue);
-            desc.setWritable(IsWritable(shapeAttrs));
-        } else {
-            desc.setGetterObject(shape->getterObject());
-            desc.setSetterObject(shape->setterObject());
-        }
-    } else if (desc.isDataDescriptor() != IsDataDescriptor(shapeAttrs)) {
-        // Step 7.
-        if (!IsConfigurable(shapeAttrs) && !skipRedefineChecks)
-            return result.fail(JSMSG_CANT_REDEFINE_PROP);
+            }
+
+            attrs = ApplyOrDefaultAttributes(attrs, shape);
 
-        // Fill in desc fields with default values (steps 7.b.i and 7.c.i).
-        CompletePropertyDescriptor(&desc);
-    } else if (desc.isDataDescriptor()) {
-        // Step 8.
-        bool frozen = !IsConfigurable(shapeAttrs) && !IsWritable(shapeAttrs);
-        if (frozen && desc.hasWritable() && desc.writable() && !skipRedefineChecks)
-            return result.fail(JSMSG_CANT_REDEFINE_PROP);
-
-        if (frozen || !desc.hasValue()) {
-            RootedValue currentValue(cx);
-            if (!GetExistingPropertyValue(cx, obj, id, shape, &currentValue))
-                return false;
-            if (!desc.hasValue()) {
-                // Fill in desc.[[Value]].
-                desc.setValue(currentValue);
+            if (shape->isAccessorDescriptor() && !(attrs & JSPROP_IGNORE_READONLY)) {
+                // ES6 draft 2014-10-14 9.1.6.3 step 7.c: Since [[Writable]]
+                // is present, change the existing accessor property to a data
+                // property.
+                updateValue = UndefinedValue();
             } else {
-                // Step 8.a.ii.1.
-                bool same;
-                if (!cx->shouldBeJSContext())
-                    return false;
-                if (!SameValue(cx->asJSContext(), desc.value(), currentValue, &same))
-                    return false;
-                if (!same && !skipRedefineChecks)
-                    return result.fail(JSMSG_CANT_REDEFINE_PROP);
+                // We are at most changing some attributes, and cannot convert
+                // from data descriptor to accessor, or vice versa. Take
+                // everything from the shape that we aren't changing.
+                uint32_t propMask = JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT;
+                attrs = (shape->attributes() & ~propMask) | (attrs & propMask);
+                getter = shape->getter();
+                setter = shape->setter();
+                if (shape->hasSlot())
+                    updateValue = obj->getSlot(shape->slot());
             }
         }
-
-        if (!desc.hasWritable())
-            desc.setWritable(IsWritable(shapeAttrs));
-    } else {
-        // Step 9. The spec says to use SameValue, but since the values in
-        // question are objects, we can just compare pointers.
-        if (desc.hasSetterObject()) {
-            if (!IsConfigurable(shapeAttrs) &&
-                desc.setterObject() != shape->setterObject() &&
-                !skipRedefineChecks)
-            {
-                return result.fail(JSMSG_CANT_REDEFINE_PROP);
-            }
-        } else {
-            // Fill in desc.[[Set]] from shape.
-            desc.setSetterObject(shape->setterObject());
-        }
-        if (desc.hasGetterObject()) {
-            if (!IsConfigurable(shapeAttrs) &&
-                desc.getterObject() != shape->getterObject() &&
-                !skipRedefineChecks)
-            {
-                return result.fail(JSMSG_CANT_REDEFINE_PROP);
-            }
-        } else {
-            // Fill in desc.[[Get]] from shape.
-            desc.setGetterObject(shape->getterObject());
-        }
     }
 
-    // Step 10.
-    if (!AddOrChangeProperty(cx, obj, id, desc))
+    /*
+     * Purge the property cache of any properties named by id that are about
+     * to be shadowed in obj's scope chain.
+     */
+    if (!PurgeScopeChain(cx, obj, id))
+        return false;
+
+    if (shouldDefine) {
+        // Handle the default cases here. Anyone that wanted to set non-default attributes has
+        // cleared the IGNORE flags by now. Since we can never get here with JSPROP_IGNORE_VALUE
+        // relevant, just clear it.
+        attrs = ApplyOrDefaultAttributes(attrs) & ~JSPROP_IGNORE_VALUE;
+        return DefinePropertyOrElement(cx, obj, id, getter, setter,
+                                       attrs, updateValue, false, result);
+    }
+
+    MOZ_ASSERT(shape);
+
+    JS_ALWAYS_TRUE(UpdateShapeTypeAndValue(cx, obj, shape, updateValue));
+
+    if (!CallAddPropertyHook(cx, obj, shape, updateValue))
         return false;
     return result.succeed();
 }
 
 bool
 js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId id,
                          HandleValue value, GetterOp getter, SetterOp setter, unsigned attrs,
                          ObjectOpResult& result)
@@ -1942,65 +1944,16 @@ MaybeReportUndeclaredVarAssignment(JSCon
            JS_ReportErrorFlagsAndNumber(cx,
                                         (JSREPORT_WARNING | JSREPORT_STRICT
                                          | JSREPORT_STRICT_MODE_ERROR),
                                         GetErrorMessage, nullptr,
                                         JSMSG_UNDECLARED_VAR, bytes.ptr());
 }
 
 /*
- * Finish assignment to a shapeful data property of a native object obj. This
- * conforms to no standard and there is a lot of legacy baggage here.
- */
-static bool
-NativeSetExistingDataProperty(JSContext* cx, HandleNativeObject obj, HandleShape shape,
-                              HandleValue v, HandleValue receiver, ObjectOpResult& result)
-{
-    MOZ_ASSERT(obj->isNative());
-    MOZ_ASSERT(shape->isDataDescriptor());
-
-    if (shape->hasDefaultSetter()) {
-        if (shape->hasSlot()) {
-            // The common path. Standard data property.
-
-            // Global properties declared with 'var' will be initially
-            // defined with an undefined value, so don't treat the initial
-            // assignments to such properties as overwrites.
-            bool overwriting = !obj->is<GlobalObject>() || !obj->getSlot(shape->slot()).isUndefined();
-            obj->setSlotWithType(cx, shape, v, overwriting);
-            return result.succeed();
-        }
-
-        // Bizarre: shared (slotless) property that's writable but has no
-        // JSSetterOp. JS code can't define such a property, but it can be done
-        // through the JSAPI. Treat it as non-writable.
-        return result.fail(JSMSG_GETTER_ONLY);
-    }
-
-    MOZ_ASSERT(!obj->is<DynamicWithObject>());  // See bug 1128681.
-
-    uint32_t sample = cx->runtime()->propertyRemovals;
-    RootedId id(cx, shape->propid());
-    RootedValue value(cx, v);
-    if (!CallJSSetterOp(cx, shape->setterOp(), obj, id, &value, result))
-        return false;
-
-    // Update any slot for the shape with the value produced by the setter,
-    // unless the setter deleted the shape.
-    if (shape->hasSlot() &&
-        (MOZ_LIKELY(cx->runtime()->propertyRemovals == sample) ||
-         obj->contains(cx, shape)))
-    {
-        obj->setSlot(shape->slot(), value);
-    }
-
-    return true;  // result is populated by CallJSSetterOp above.
-}
-
-/*
  * When a [[Set]] operation finds no existing property with the given id
  * or finds a writable data property on the prototype chain, we end up here.
  * Finish the [[Set]] by defining a new property on receiver.
  *
  * This implements ES6 draft rev 28, 9.1.9 [[Set]] steps 5.b-f, but it
  * is really old code and there are a few barnacles.
  */
 bool
@@ -2029,58 +1982,50 @@ js::SetPropertyByDefining(JSContext* cx,
         MOZ_ASSERT(existing == objHasOwn);
 #endif
         existing = objHasOwn;
     } else {
         if (!HasOwnProperty(cx, receiver, id, &existing))
             return false;
     }
 
+    // If the property doesn't already exist, check for an inextensible
+    // receiver. (According to the specification, this is supposed to be
+    // enforced by [[DefineOwnProperty]], but we haven't implemented that yet.)
+    if (!existing) {
+        bool extensible;
+        if (!IsExtensible(cx, receiver, &extensible))
+            return false;
+        if (!extensible)
+            return result.fail(JSMSG_OBJECT_NOT_EXTENSIBLE);
+    }
+
     // Invalidate SpiderMonkey-specific caches or bail.
     const Class* clasp = receiver->getClass();
 
     // Purge the property cache of now-shadowed id in receiver's scope chain.
     if (!PurgeScopeChain(cx, receiver, id))
         return false;
 
     // Step 5.e-f. Define the new data property.
     unsigned attrs =
         existing
         ? JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_READONLY | JSPROP_IGNORE_PERMANENT
         : JSPROP_ENUMERATE;
     JSGetterOp getter = clasp->getProperty;
     JSSetterOp setter = clasp->setProperty;
     MOZ_ASSERT(getter != JS_PropertyStub);
     MOZ_ASSERT(setter != JS_StrictPropertyStub);
-    if (!DefineProperty(cx, receiver, id, v, getter, setter, attrs, result))
-        return false;
+    if (!receiver->is<NativeObject>())
+        return DefineProperty(cx, receiver, id, v, getter, setter, attrs, result);
 
     // If the receiver is native, there is one more legacy wrinkle: the class
     // JSSetterOp is called after defining the new property.
-    if (setter && receiver->is<NativeObject>()) {
-        if (!result)
-            return true;
-
-        Rooted<NativeObject*> nativeReceiver(cx, &receiver->as<NativeObject>());
-        if (!cx->shouldBeJSContext())
-            return false;
-        RootedValue receiverValue(cx, ObjectValue(*receiver));
-
-        // This lookup is a bit unfortunate, but not nearly the most
-        // unfortunate thing about Class getters and setters. Since the above
-        // DefineProperty call succeeded, receiver is native, and the property
-        // has a setter (and thus can't be a dense element), this lookup is
-        // guaranteed to succeed.
-        RootedShape shape(cx, nativeReceiver->lookup(cx, id));
-        MOZ_ASSERT(shape);
-        return NativeSetExistingDataProperty(cx->asJSContext(), nativeReceiver, shape, v,
-                                             receiverValue, result);
-    }
-
-    return true;
+    Rooted<NativeObject*> nativeReceiver(cx, &receiver->as<NativeObject>());
+    return DefinePropertyOrElement(cx, nativeReceiver, id, getter, setter, attrs, v, true, result);
 }
 
 // When setting |id| for |receiver| and |obj| has no property for id, continue
 // the search up the prototype chain.
 bool
 js::SetPropertyOnProto(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
                        HandleValue receiver, ObjectOpResult& result)
 {
@@ -2146,16 +2091,65 @@ SetDenseOrTypedArrayElement(JSContext* c
     if (!obj->maybeCopyElementsForWrite(cx))
         return false;
 
     obj->setDenseElementWithType(cx, index, v);
     return result.succeed();
 }
 
 /*
+ * Finish assignment to a shapeful data property of a native object obj. This
+ * conforms to no standard and there is a lot of legacy baggage here.
+ */
+static bool
+NativeSetExistingDataProperty(JSContext* cx, HandleNativeObject obj, HandleShape shape,
+                              HandleValue v, HandleValue receiver, ObjectOpResult& result)
+{
+    MOZ_ASSERT(obj->isNative());
+    MOZ_ASSERT(shape->isDataDescriptor());
+
+    if (shape->hasDefaultSetter()) {
+        if (shape->hasSlot()) {
+            // The common path. Standard data property.
+
+            // Global properties declared with 'var' will be initially
+            // defined with an undefined value, so don't treat the initial
+            // assignments to such properties as overwrites.
+            bool overwriting = !obj->is<GlobalObject>() || !obj->getSlot(shape->slot()).isUndefined();
+            obj->setSlotWithType(cx, shape, v, overwriting);
+            return result.succeed();
+        }
+
+        // Bizarre: shared (slotless) property that's writable but has no
+        // JSSetterOp. JS code can't define such a property, but it can be done
+        // through the JSAPI. Treat it as non-writable.
+        return result.fail(JSMSG_GETTER_ONLY);
+    }
+
+    MOZ_ASSERT(!obj->is<DynamicWithObject>());  // See bug 1128681.
+
+    uint32_t sample = cx->runtime()->propertyRemovals;
+    RootedId id(cx, shape->propid());
+    RootedValue value(cx, v);
+    if (!CallJSSetterOp(cx, shape->setterOp(), obj, id, &value, result))
+        return false;
+
+    // Update any slot for the shape with the value produced by the setter,
+    // unless the setter deleted the shape.
+    if (shape->hasSlot() &&
+        (MOZ_LIKELY(cx->runtime()->propertyRemovals == sample) ||
+         obj->contains(cx, shape)))
+    {
+        obj->setSlot(shape->slot(), value);
+    }
+
+    return true;  // result is populated by CallJSSetterOp above.
+}
+
+/*
  * Finish the assignment `receiver[id] = v` when an existing property (shape)
  * has been found on a native object (pobj). This implements ES6 draft rev 32
  * (2015 Feb 2) 9.1.9 steps 5 and 6.
  *
  * It is necessary to pass both id and shape because shape could be an implicit
  * dense or typed array element (i.e. not actually a pointer to a Shape).
  */
 static bool
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -686,18 +686,22 @@ class NativeObject : public JSObject
     static inline Shape*
     putProperty(ExclusiveContext* cx, HandleObject obj, PropertyName* name,
                 JSGetterOp getter, JSSetterOp setter,
                 uint32_t slot, unsigned attrs,
                 unsigned flags);
 
     /* Change the given property into a sibling with the same id in this scope. */
     static Shape*
-    changeProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleShape shape,
-                   unsigned attrs, JSGetterOp getter, JSSetterOp setter);
+    changeProperty(ExclusiveContext* cx, HandleNativeObject obj,
+                   HandleShape shape, unsigned attrs, unsigned mask,
+                   JSGetterOp getter, JSSetterOp setter);
+
+    static inline bool changePropertyAttributes(JSContext* cx, HandleNativeObject obj,
+                                                HandleShape shape, unsigned attrs);
 
     /* Remove the property named by id from this object. */
     bool removeProperty(ExclusiveContext* cx, jsid id);
 
     /* Clear the scope, making it empty. */
     static void clear(ExclusiveContext* cx, HandleNativeObject obj);
 
   protected:
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -861,21 +861,23 @@ NativeObject::putProperty(ExclusiveConte
 
     obj->checkShapeConsistency();
 
     return shape;
 }
 
 /* static */ Shape*
 NativeObject::changeProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleShape shape,
-                             unsigned attrs, GetterOp getter, SetterOp setter)
+                             unsigned attrs, unsigned mask, GetterOp getter, SetterOp setter)
 {
     MOZ_ASSERT(obj->containsPure(shape));
     MOZ_ASSERT(getter != JS_PropertyStub);
     MOZ_ASSERT(setter != JS_StrictPropertyStub);
+
+    attrs |= shape->attrs & mask;
     MOZ_ASSERT_IF(attrs & (JSPROP_GETTER | JSPROP_SETTER), attrs & JSPROP_SHARED);
 
     /* Allow only shared (slotless) => unshared (slotful) transition. */
     MOZ_ASSERT(!((attrs ^ shape->attrs) & JSPROP_SHARED) ||
                !(attrs & JSPROP_SHARED));
 
     MarkTypePropertyNonData(cx, obj, shape->propid());
 
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -2100,61 +2100,16 @@ js::StringIsTypedArrayIndex(const CharT*
 }
 
 template bool
 js::StringIsTypedArrayIndex(const char16_t* s, size_t length, uint64_t* indexp);
 
 template bool
 js::StringIsTypedArrayIndex(const Latin1Char* s, size_t length, uint64_t* indexp);
 
-/* ES6 draft rev 34 (2015 Feb 20) 9.4.5.3 [[DefineOwnProperty]] step 3.c. */
-bool
-js::DefineTypedArrayElement(JSContext *cx, HandleObject obj, uint64_t index,
-                            Handle<PropertyDescriptor> desc, ObjectOpResult &result)
-{
-    MOZ_ASSERT(IsAnyTypedArray(obj));
-
-    // These are all substeps of 3.c.
-    // Steps i-vi.
-    // We (wrongly) ignore out of range defines with a value.
-    if (index >= AnyTypedArrayLength(obj))
-        return result.succeed();
-
-    // Step vii.
-    if (desc.isAccessorDescriptor())
-        return result.fail(JSMSG_CANT_REDEFINE_PROP);
-
-    // Step viii.
-    if (desc.hasConfigurable() && desc.configurable())
-        return result.fail(JSMSG_CANT_REDEFINE_PROP);
-
-    // Step ix.
-    if (desc.hasEnumerable() && !desc.enumerable())
-        return result.fail(JSMSG_CANT_REDEFINE_PROP);
-
-    // Step x.
-    if (desc.hasWritable() && !desc.writable())
-        return result.fail(JSMSG_CANT_REDEFINE_PROP);
-
-    // Step xi.
-    if (desc.hasValue()) {
-        double d;
-        if (!ToNumber(cx, desc.value(), &d))
-            return false;
-
-        if (obj->is<TypedArrayObject>())
-            TypedArrayObject::setElement(obj->as<TypedArrayObject>(), index, d);
-        else
-            SharedTypedArrayObject::setElement(obj->as<SharedTypedArrayObject>(), index, d);
-    }
-
-    // Step xii.
-    return result.succeed();
-}
-
 /* JS Friend API */
 
 JS_FRIEND_API(bool)
 JS_IsTypedArrayObject(JSObject* obj)
 {
     obj = CheckedUnwrap(obj);
     return obj ? obj->is<TypedArrayObject>() : false;
 }
--- a/js/src/vm/TypedArrayObject.h
+++ b/js/src/vm/TypedArrayObject.h
@@ -272,24 +272,16 @@ IsTypedArrayIndex(jsid id, uint64_t* ind
     }
 
     const char16_t* s = atom->twoByteChars(nogc);
     if (!JS7_ISDEC(*s) && *s != '-')
         return false;
     return StringIsTypedArrayIndex(s, length, indexp);
 }
 
-/*
- * Implements [[DefineOwnProperty]] for TypedArrays and SharedTypedArrays
- * when the property key is a TypedArray index.
- */
-bool
-DefineTypedArrayElement(JSContext *cx, HandleObject arr, uint64_t index,
-                        Handle<PropertyDescriptor> desc, ObjectOpResult &result);
-
 static inline unsigned
 TypedArrayShift(Scalar::Type viewType)
 {
     switch (viewType) {
       case Scalar::Int8:
       case Scalar::Uint8:
       case Scalar::Uint8Clamped:
         return 0;
--- a/js/xpconnect/tests/chrome/test_xrayToJS.xul
+++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul
@@ -493,26 +493,23 @@ https://bugzilla.mozilla.org/show_bug.cg
     is(trickyObject.wrappedJSObject.getterSetterProp, 'redefined', "Redefinition forwards");
 
     // We should NOT be able to overwrite an existing non-configurable accessor
     // prop, though.
     is(trickyObject.wrappedJSObject.nonConfigurableGetterSetterProp, 5,
        "Underlying object has getter");
     is(trickyObject.nonConfigurableGetterSetterProp, undefined,
        "Filtering properly over Xray here too");
-    is((trickyObject.nonConfigurableGetterSetterProp = 'redefined'), 'redefined',
-       "Assigning to non-configurable prop should fail silently in non-strict mode");
     checkThrows(function() {
-      "use strict";
-      trickyObject.nonConfigurableGetterSetterProp = 'redefined';
-    }, /config/, "Should throw when redefining non-configurable prop in strict mode");
+	trickyObject.nonConfigurableGetterSetterProp = 'redefined';
+    }, /config/, "Should throw when redefining non-configurable prop");
     is(trickyObject.nonConfigurableGetterSetterProp, undefined,
-       "Redefinition should have failed");
+       "Redefinition should have thrown");
     is(trickyObject.wrappedJSObject.nonConfigurableGetterSetterProp, 5,
-       "Redefinition really should have failed");
+       "Redefinition really should have thrown");
 
     checkThrows(function() { trickyObject.hasOwnProperty = 33; }, /shadow/,
                 "Should reject shadowing of pre-existing inherited properties over Xrays");
 
     checkThrows(function() { Object.defineProperty(trickyObject, 'rejectedProp', { get: function() {}}); },
                 /accessor property/, "Should reject accessor property definition");
   }