Backed out 7 changesets (bug 1002737) for sm-rootanalysis test failures.
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 03 Jun 2014 17:07:47 -0400
changeset 205678 b922ed24938fae6fe2150377eb547d253a443ed1
parent 205677 cbc90d9dafdbe49f500d5e640a9f5f5ca2c24d52
child 205679 9a6bafdf9a286e3881cd3e9ae320085709f1612f
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-beta@4d6f46f5af68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1002737
milestone32.0a1
backs outec411f0ce167f6b6ad38909b4d9bb4b0e0b00f3b
8a63bad8faedc052e7011c943dd89647ca858454
5afce70dad1fb3411d40fb5043aa02df9bb0a3ef
6d4043272a0a2084ad587fda3bae0e625911cc83
ad09630ae9a35a1aaec723d0df1ebc5b0569dee0
c0dd6b9cc07a1d80131f9193a2ed36e2cb48553c
b82adb960c543fd559fe201b8dd69a96e22eda25
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 7 changesets (bug 1002737) for sm-rootanalysis test failures. CLOSED TREE Backed out changeset ec411f0ce167 (bug 1002737) Backed out changeset 8a63bad8faed (bug 1002737) Backed out changeset 5afce70dad1f (bug 1002737) Backed out changeset 6d4043272a0a (bug 1002737) Backed out changeset ad09630ae9a3 (bug 1002737) Backed out changeset c0dd6b9cc07a (bug 1002737) Backed out changeset b82adb960c54 (bug 1002737)
js/src/gc/RootMarking.cpp
js/src/jsapi.h
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jsproxy.cpp
js/src/vm/Debugger.cpp
js/src/vm/Debugger.h
js/src/vm/ObjectImpl.cpp
js/src/vm/PropDesc.h
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -383,21 +383,26 @@ AutoGCRooter::trace(JSTracer *trc)
         return;
 
       case IDARRAY: {
         JSIdArray *ida = static_cast<AutoIdArray *>(this)->idArray;
         MarkIdRange(trc, ida->length, ida->vector, "JS::AutoIdArray.idArray");
         return;
       }
 
-      case DESCVECTOR: {
-        AutoPropDescVector::VectorImpl &descriptors =
-            static_cast<AutoPropDescVector *>(this)->vector;
-        for (size_t i = 0, len = descriptors.length(); i < len; i++)
-            descriptors[i].trace(trc);
+      case DESCRIPTORS: {
+        PropDescArray &descriptors =
+            static_cast<AutoPropDescArrayRooter *>(this)->descriptors;
+        for (size_t i = 0, len = descriptors.length(); i < len; i++) {
+            PropDesc &desc = descriptors[i];
+            MarkValueRoot(trc, &desc.pd_, "PropDesc::pd_");
+            MarkValueRoot(trc, &desc.value_, "PropDesc::value_");
+            MarkValueRoot(trc, &desc.get_, "PropDesc::get_");
+            MarkValueRoot(trc, &desc.set_, "PropDesc::set_");
+        }
         return;
       }
 
       case VALVECTOR: {
         AutoValueVector::VectorImpl &vector = static_cast<AutoValueVector *>(this)->vector;
         MarkValueRootRange(trc, vector.length(), vector.begin(), "js::AutoValueVector.vector");
         return;
       }
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -101,17 +101,17 @@ class JS_PUBLIC_API(AutoGCRooter) {
      */
     ptrdiff_t tag_;
 
     enum {
         VALARRAY =     -2, /* js::AutoValueArray */
         PARSER =       -3, /* js::frontend::Parser */
         SHAPEVECTOR =  -4, /* js::AutoShapeVector */
         IDARRAY =      -6, /* js::AutoIdArray */
-        DESCVECTOR =   -7, /* js::AutoPropDescVector */
+        DESCRIPTORS =  -7, /* js::AutoPropDescArrayRooter */
         VALVECTOR =   -10, /* js::AutoValueVector */
         IDVECTOR =    -13, /* js::AutoIdVector */
         OBJVECTOR =   -14, /* js::AutoObjectVector */
         STRINGVECTOR =-15, /* js::AutoStringVector */
         SCRIPTVECTOR =-16, /* js::AutoScriptVector */
         NAMEVECTOR =  -17, /* js::AutoNameVector */
         HASHABLEVALUE=-18, /* js::HashableValue */
         IONMASM =     -19, /* js::jit::MacroAssembler */
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -490,51 +490,16 @@ JSCompartment::wrap(JSContext *cx, AutoI
     int length = props.length();
     for (size_t n = 0; n < size_t(length); ++n) {
         if (!wrapId(cx, &vector[n]))
             return false;
     }
     return true;
 }
 
-bool
-JSCompartment::wrap(JSContext *cx, MutableHandle<PropDesc> desc)
-{
-    if (desc.isUndefined())
-        return true;
-
-    JSCompartment *comp = cx->compartment();
-
-    if (desc.hasValue()) {
-        RootedValue value(cx, desc.value());
-        if (!comp->wrap(cx, &value))
-            return false;
-        desc.setValue(value);
-    }
-    if (desc.hasGet()) {
-        RootedValue get(cx, desc.getterValue());
-        if (!comp->wrap(cx, &get))
-            return false;
-        desc.setGetter(get);
-    }
-    if (desc.hasSet()) {
-        RootedValue set(cx, desc.setterValue());
-        if (!comp->wrap(cx, &set))
-            return false;
-        desc.setSetter(set);
-    }
-    if (desc.descriptorValue().isObject()) {
-        RootedObject descObj(cx, &desc.descriptorValue().toObject());
-        if (!comp->wrap(cx, &descObj))
-            return false;
-        desc.setDescriptorObject(descObj);
-    }
-    return true;
-}
-
 /*
  * This method marks pointers that cross compartment boundaries. It should be
  * called only for per-compartment GCs, since full GCs naturally follow pointers
  * across compartments.
  */
 void
 JSCompartment::markCrossCompartmentWrappers(JSTracer *trc)
 {
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -313,17 +313,16 @@ struct JSCompartment
     bool wrap(JSContext *cx, js::HeapPtrString *strp);
     bool wrap(JSContext *cx, JS::MutableHandleObject obj,
               JS::HandleObject existingArg = js::NullPtr());
     bool wrapId(JSContext *cx, jsid *idp);
     bool wrap(JSContext *cx, js::PropertyOp *op);
     bool wrap(JSContext *cx, js::StrictPropertyOp *op);
     bool wrap(JSContext *cx, JS::MutableHandle<js::PropertyDescriptor> desc);
     bool wrap(JSContext *cx, js::AutoIdVector &props);
-    bool wrap(JSContext *cx, JS::MutableHandle<js::PropDesc> desc);
 
     bool putWrapper(JSContext *cx, const js::CrossCompartmentKey& wrapped, const js::Value& wrapper);
 
     js::WrapperMap::Ptr lookupWrapper(const js::Value& wrapped) {
         return crossCompartmentWrappers.lookup(js::CrossCompartmentKey(wrapped));
     }
 
     void removeWrapper(js::WrapperMap::Ptr p) {
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -263,27 +263,25 @@ js::NewPropertyDescriptorObject(JSContex
         return true;
     }
 
     Rooted<PropDesc> d(cx);
 
     d.initFromPropertyDescriptor(desc);
     if (!d.makeObject(cx))
         return false;
-    vp.set(d.descriptorValue());
+    vp.set(d.pd());
     return true;
 }
 
 void
 PropDesc::initFromPropertyDescriptor(Handle<PropertyDescriptor> desc)
 {
-    MOZ_ASSERT(isUndefined());
-
     isUndefined_ = false;
-    descObj_ = nullptr;
+    pd_.setUndefined();
     attrs = uint8_t(desc.attributes());
     JS_ASSERT_IF(attrs & JSPROP_READONLY, !(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
     if (desc.hasGetterOrSetterObject()) {
         hasGet_ = true;
         get_ = desc.hasGetterObject() && desc.getterObject()
                ? ObjectValue(*desc.getterObject())
                : UndefinedValue();
         hasSet_ = true;
@@ -330,17 +328,17 @@ PropDesc::makeObject(JSContext *cx)
         (hasValue() &&
          !JSObject::defineProperty(cx, obj, names.value, value())) ||
         (hasWritable() &&
          !JSObject::defineProperty(cx, obj, names.writable, writableVal)))
     {
         return false;
     }
 
-    descObj_ = obj;
+    pd_.setObject(*obj);
     return true;
 }
 
 bool
 js::GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id,
                              MutableHandle<PropertyDescriptor> desc)
 {
     // FIXME: Call TrapGetOwnProperty directly once ScriptedIndirectProxies is removed
@@ -430,29 +428,27 @@ HasProperty(JSContext *cx, HandleObject 
      * js::GetOwnPropertyDescriptor, as the getter or setter callable object.
      */
     return !!JSObject::getGeneric(cx, obj, obj, id, vp);
 }
 
 bool
 PropDesc::initialize(JSContext *cx, const Value &origval, bool checkAccessors)
 {
-    MOZ_ASSERT(isUndefined());
-
     RootedValue v(cx, origval);
 
     /* 8.10.5 step 1 */
     if (v.isPrimitive()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT);
         return false;
     }
     RootedObject desc(cx, &v.toObject());
 
     /* Make a copy of the descriptor. We might need it later. */
-    descObj_ = desc;
+    pd_ = v;
 
     isUndefined_ = false;
 
     /*
      * Start with the proper defaults.  XXX shouldn't be necessary when we get
      * rid of PropDesc::attributes()
      */
     attrs = JSPROP_PERMANENT | JSPROP_READONLY;
@@ -534,18 +530,16 @@ PropDesc::initialize(JSContext *cx, cons
     JS_ASSERT_IF(attrs & JSPROP_READONLY, !(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
 
     return true;
 }
 
 void
 PropDesc::complete()
 {
-    MOZ_ASSERT(!isUndefined());
-
     if (isGenericDescriptor() || isDataDescriptor()) {
         if (!hasValue_) {
             hasValue_ = true;
             value_.setUndefined();
         }
         if (!hasWritable_) {
             hasWritable_ = true;
             attrs |= JSPROP_READONLY;
@@ -1049,17 +1043,17 @@ js::DefineProperty(JSContext *cx, Handle
     }
 
     if (obj->getOps()->lookupGeneric) {
         /*
          * FIXME: Once ScriptedIndirectProxies are removed, this code should call
          * TrapDefineOwnProperty directly
          */
         if (obj->is<ProxyObject>()) {
-            RootedValue pd(cx, desc.descriptorValue());
+            RootedValue pd(cx, desc.pd());
             return Proxy::defineProperty(cx, obj, id, pd);
         }
         return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
     }
 
     return DefinePropertyOnObject(cx, obj, id, desc, throwError, rval);
 }
 
@@ -1091,41 +1085,41 @@ js::DefineOwnProperty(JSContext *cx, Han
         return false;
     *bp = !!rval;
     return true;
 }
 
 
 bool
 js::ReadPropertyDescriptors(JSContext *cx, HandleObject props, bool checkAccessors,
-                            AutoIdVector *ids, AutoPropDescVector *descs)
+                            AutoIdVector *ids, AutoPropDescArrayRooter *descs)
 {
     if (!GetPropertyNames(cx, props, JSITER_OWNONLY, ids))
         return false;
 
     RootedId id(cx);
     for (size_t i = 0, len = ids->length(); i < len; i++) {
         id = (*ids)[i];
-        Rooted<PropDesc> desc(cx);
+        PropDesc* desc = descs->append();
         RootedValue v(cx);
-        if (!JSObject::getGeneric(cx, props, props, id, &v) ||
-            !desc.initialize(cx, v, checkAccessors) ||
-            !descs->append(desc))
+        if (!desc ||
+            !JSObject::getGeneric(cx, props, props, id, &v) ||
+            !desc->initialize(cx, v, checkAccessors))
         {
             return false;
         }
     }
     return true;
 }
 
 bool
 js::DefineProperties(JSContext *cx, HandleObject obj, HandleObject props)
 {
     AutoIdVector ids(cx);
-    AutoPropDescVector descs(cx);
+    AutoPropDescArrayRooter descs(cx);
     if (!ReadPropertyDescriptors(cx, props, true, &ids, &descs))
         return false;
 
     if (obj->is<ArrayObject>()) {
         bool dummy;
         Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
         for (size_t i = 0, len = ids.length(); i < len; i++) {
             if (!DefinePropertyOnArray(cx, arr, ids[i], descs[i], true, &dummy))
@@ -1136,17 +1130,17 @@ js::DefineProperties(JSContext *cx, Hand
 
     if (obj->getOps()->lookupGeneric) {
         /*
          * FIXME: Once ScriptedIndirectProxies are removed, this code should call
          * TrapDefineOwnProperty directly
          */
         if (obj->is<ProxyObject>()) {
             for (size_t i = 0, len = ids.length(); i < len; i++) {
-                RootedValue pd(cx, descs[i].descriptorValue());
+                RootedValue pd(cx, descs[i].pd());
                 if (!Proxy::defineProperty(cx, obj, ids[i], pd))
                     return false;
             }
             return true;
         }
         bool dummy;
         return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, true, &dummy);
     }
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -27,17 +27,17 @@
 #include "vm/Xdr.h"
 
 namespace JS {
 struct ObjectsExtraSizes;
 }
 
 namespace js {
 
-class AutoPropDescVector;
+class AutoPropDescArrayRooter;
 struct GCMarker;
 struct NativeIterator;
 class Nursery;
 struct StackShape;
 
 inline JSObject *
 CastAsObject(PropertyOp op)
 {
@@ -1396,17 +1396,17 @@ bool
 DefineProperties(JSContext *cx, HandleObject obj, HandleObject props);
 
 /*
  * Read property descriptors from props, as for Object.defineProperties. See
  * ES5 15.2.3.7 steps 3-5.
  */
 extern bool
 ReadPropertyDescriptors(JSContext *cx, HandleObject props, bool checkAccessors,
-                        AutoIdVector *ids, AutoPropDescVector *descs);
+                        AutoIdVector *ids, AutoPropDescArrayRooter *descs);
 
 /* Read the name using a dynamic lookup on the scopeChain. */
 extern bool
 LookupName(JSContext *cx, HandlePropertyName name, HandleObject scopeChain,
            MutableHandleObject objp, MutableHandleObject pobjp, MutableHandleShape propp);
 
 extern bool
 LookupNameNoGC(JSContext *cx, PropertyName *name, JSObject *scopeChain,
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -639,17 +639,17 @@ JSObject::isOwnGlobal() const
 {
     return &global() == this;
 }
 
 namespace js {
 
 PropDesc::PropDesc(const Value &getter, const Value &setter,
                    Enumerability enumerable, Configurability configurable)
-  : descObj_(nullptr),
+  : pd_(UndefinedValue()),
     value_(UndefinedValue()),
     get_(getter), set_(setter),
     attrs(JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED |
           (enumerable ? JSPROP_ENUMERATE : 0) |
           (configurable ? 0 : JSPROP_PERMANENT)),
     hasGet_(true), hasSet_(true),
     hasValue_(false), hasWritable_(false), hasEnumerable_(true), hasConfigurable_(true),
     isUndefined_(false)
@@ -767,27 +767,42 @@ ToPrimitive(JSContext *cx, JSType prefer
  */
 inline bool
 IsInternalFunctionObject(JSObject *funobj)
 {
     JSFunction *fun = &funobj->as<JSFunction>();
     return fun->isLambda() && !funobj->getParent();
 }
 
-class AutoPropDescVector : public AutoVectorRooter<PropDesc>
+class AutoPropDescArrayRooter : private AutoGCRooter
 {
   public:
-    explicit AutoPropDescVector(JSContext *cx
-                    MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-        : AutoVectorRooter<PropDesc>(cx, DESCVECTOR)
-    {
-        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+    explicit AutoPropDescArrayRooter(JSContext *cx)
+      : AutoGCRooter(cx, DESCRIPTORS), descriptors(cx)
+    { }
+
+    PropDesc *append() {
+        if (!descriptors.append(PropDesc()))
+            return nullptr;
+        return &descriptors.back();
     }
 
-    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+    bool reserve(size_t n) {
+        return descriptors.reserve(n);
+    }
+
+    PropDesc& operator[](size_t i) {
+        JS_ASSERT(i < descriptors.length());
+        return descriptors[i];
+    }
+
+    friend void AutoGCRooter::trace(JSTracer *trc);
+
+  private:
+    PropDescArray descriptors;
 };
 
 /*
  * Make an object with the specified prototype. If parent is null, it will
  * default to the prototype's global if the prototype is non-null.
  */
 JSObject *
 NewObjectWithGivenProto(ExclusiveContext *cx, const js::Class *clasp, TaggedProto proto, JSObject *parent,
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -1106,17 +1106,17 @@ FromGenericPropertyDescriptor(JSContext 
     if (desc.isUndefined()) {
         rval.setUndefined();
         return true;
     }
 
     // steps 3-9
     if (!desc.makeObject(cx))
         return false;
-    rval.set(desc.descriptorValue());
+    rval.set(desc.pd());
     return true;
 }
 
 /*
  * Aux.3 NormalizePropertyDescriptor(Attributes)
  *
  * NOTE: to minimize code duplication, the code for this function is shared with
  * that for Aux.4 NormalizeAndCompletePropertyDescriptor (see below). The
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -804,73 +804,16 @@ Debugger::unwrapDebuggeeValue(JSContext 
             return false;
         }
 
         vp.setObject(*static_cast<JSObject*>(dobj->getPrivate()));
     }
     return true;
 }
 
-/*
- * Convert Debugger.Objects in desc to debuggee values.
- * Reject non-callable getters and setters.
- */
-static bool
-CheckArgCompartment(JSContext *cx, JSObject *obj, HandleValue v,
-                    const char *methodname, const char *propname)
-{
-    if (v.isObject() && v.toObject().compartment() != obj->compartment()) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_COMPARTMENT_MISMATCH,
-                             methodname, propname);
-        return false;
-    }
-    return true;
-}
-
-bool
-Debugger::unwrapPropDescInto(JSContext *cx, HandleObject obj, Handle<PropDesc> wrapped,
-                             MutableHandle<PropDesc> unwrapped)
-{
-    MOZ_ASSERT(!wrapped.isUndefined());
-
-    unwrapped.set(wrapped);
-
-    if (unwrapped.hasValue()) {
-        RootedValue value(cx, unwrapped.value());
-        if (!unwrapDebuggeeValue(cx, &value) ||
-            !CheckArgCompartment(cx, obj, value, "defineProperty", "value"))
-        {
-            return false;
-        }
-        unwrapped.setValue(value);
-    }
-
-    if (unwrapped.hasGet()) {
-        RootedValue get(cx, unwrapped.getterValue());
-        if (!unwrapDebuggeeValue(cx, &get) ||
-            !CheckArgCompartment(cx, obj, get, "defineProperty", "get"))
-        {
-            return false;
-        }
-        unwrapped.setGetter(get);
-    }
-
-    if (unwrapped.hasSet()) {
-        RootedValue set(cx, unwrapped.setterValue());
-        if (!unwrapDebuggeeValue(cx, &set) ||
-            !CheckArgCompartment(cx, obj, set, "defineProperty", "set"))
-        {
-            return false;
-        }
-        unwrapped.setSetter(set);
-    }
-
-    return true;
-}
-
 JSTrapStatus
 Debugger::handleUncaughtExceptionHelper(Maybe<AutoCompartment> &ac,
                                         MutableHandleValue *vp, bool callHook)
 {
     JSContext *cx = ac.ref().context()->asJSContext();
     if (cx->isExceptionPending()) {
         if (callHook && uncaughtExceptionHook) {
             RootedValue exc(cx);
@@ -5312,36 +5255,34 @@ DebuggerObject_defineProperty(JSContext 
 
     RootedId id(cx);
     if (!ValueToId<CanGC>(cx, args[0], &id))
         return false;
 
     Rooted<PropDesc> desc(cx);
     if (!desc.initialize(cx, args[1], false))
         return false;
-    desc.clearDescriptorObject();
-
-    if (!dbg->unwrapPropDescInto(cx, obj, desc, &desc))
+    desc.clearPd();
+
+    if (!desc.get().unwrapDebuggerObjectsInto(cx, dbg, obj, desc.address()))
         return false;
     if (!desc.checkGetter(cx) || !desc.checkSetter(cx))
         return false;
 
     {
+        RootedId wrappedId(cx);
+
         Maybe<AutoCompartment> ac;
         ac.construct(cx, obj);
-        if (!cx->compartment()->wrapId(cx, id.address()))
-            return false;
-        if (!cx->compartment()->wrap(cx, &desc))
-            return false;
-        if (!desc.makeObject(cx))
+        if (!desc.get().wrapInto(cx, obj, id, wrappedId.address(), desc.address()))
             return false;
 
         ErrorCopier ec(ac, dbg->toJSObject());
         bool dummy;
-        if (!DefineProperty(cx, obj, id, desc, true, &dummy))
+        if (!DefineProperty(cx, obj, wrappedId, desc, true, &dummy))
             return false;
     }
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
@@ -5351,45 +5292,54 @@ DebuggerObject_defineProperties(JSContex
     REQUIRE_ARGC("Debugger.Object.defineProperties", 1);
 
     RootedValue arg(cx, args[0]);
     RootedObject props(cx, ToObject(cx, arg));
     if (!props)
         return false;
 
     AutoIdVector ids(cx);
-    AutoPropDescVector descs(cx);
+    AutoPropDescArrayRooter descs(cx);
     if (!ReadPropertyDescriptors(cx, props, false, &ids, &descs))
         return false;
     size_t n = ids.length();
 
+    AutoPropDescArrayRooter unwrappedDescs(cx);
     for (size_t i = 0; i < n; i++) {
-        if (!dbg->unwrapPropDescInto(cx, obj, descs[i], descs[i]))
+        if (!unwrappedDescs.append())
             return false;
-        if (!descs[i].checkGetter(cx) || !descs[i].checkSetter(cx))
+        if (!descs[i].unwrapDebuggerObjectsInto(cx, dbg, obj, &unwrappedDescs[i]))
+            return false;
+        if (!unwrappedDescs[i].checkGetter(cx) || !unwrappedDescs[i].checkSetter(cx))
             return false;
     }
 
     {
+        AutoIdVector rewrappedIds(cx);
+        AutoPropDescArrayRooter rewrappedDescs(cx);
+
         Maybe<AutoCompartment> ac;
         ac.construct(cx, obj);
+        RootedId id(cx);
         for (size_t i = 0; i < n; i++) {
-            if (!cx->compartment()->wrapId(cx, ids[i].address()))
+            if (!rewrappedIds.append(JSID_VOID) || !rewrappedDescs.append())
                 return false;
-            if (!cx->compartment()->wrap(cx, descs[i]))
-                return false;
-            if (descs[i].descriptorValue().isUndefined() && !descs[i].makeObject(cx))
+            id = ids[i];
+            if (!unwrappedDescs[i].wrapInto(cx, obj, id, rewrappedIds[i].address(), &rewrappedDescs[i]))
                 return false;
         }
 
         ErrorCopier ec(ac, dbg->toJSObject());
         for (size_t i = 0; i < n; i++) {
             bool dummy;
-            if (!DefineProperty(cx, obj, ids[i], descs[i], true, &dummy))
+            if (!DefineProperty(cx, obj, rewrappedIds[i],
+                                rewrappedDescs[i], true, &dummy))
+            {
                 return false;
+            }
         }
     }
 
     args.rval().setUndefined();
     return true;
 }
 
 /*
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -501,18 +501,16 @@ class Debugger : private mozilla::Linked
      *
      * (Extreme nerd sidebar: Unwrapping happens in two steps because there are
      * two different kinds of symmetry at work: regardless of which direction
      * we're going, we want any exceptions to be created and thrown in the
      * debugger compartment--mirror symmetry. But compartment wrapping always
      * happens in the target compartment--rotational symmetry.)
      */
     bool unwrapDebuggeeValue(JSContext *cx, MutableHandleValue vp);
-    bool unwrapPropDescInto(JSContext *cx, HandleObject obj, Handle<PropDesc> wrapped,
-                            MutableHandle<PropDesc> unwrapped);
 
     /*
      * Store the Debugger.Frame object for frame in *vp.
      *
      * Use this if you have already access to a frame pointer without having
      * to incur the cost of walking the stack.
      */
     bool getScriptFrame(JSContext *cx, AbstractFramePtr frame, MutableHandleValue vp) {
--- a/js/src/vm/ObjectImpl.cpp
+++ b/js/src/vm/ObjectImpl.cpp
@@ -13,39 +13,31 @@
 #include "jsobjinlines.h"
 #include "vm/Shape-inl.h"
 
 using namespace js;
 
 using JS::GenericNaN;
 
 PropDesc::PropDesc()
-{
-    setUndefined();
-}
-
-void
-PropDesc::setUndefined()
+  : pd_(UndefinedValue()),
+    value_(UndefinedValue()),
+    get_(UndefinedValue()),
+    set_(UndefinedValue()),
+    attrs(0),
+    hasGet_(false),
+    hasSet_(false),
+    hasValue_(false),
+    hasWritable_(false),
+    hasEnumerable_(false),
+    hasConfigurable_(false),
+    isUndefined_(true)
 {
-    descObj_ = nullptr;
-    value_ = UndefinedValue();
-    get_ = UndefinedValue();
-    set_ = UndefinedValue();
-    attrs = 0;
-    hasGet_ = false;
-    hasSet_ = false;
-    hasValue_ = false;
-    hasWritable_ = false;
-    hasEnumerable_ = false;
-    hasConfigurable_ = false;
-
-    isUndefined_ = true;
 }
 
-
 bool
 PropDesc::checkGetter(JSContext *cx)
 {
     if (hasGet_) {
         if (!js_IsCallable(get_) && !get_.isUndefined()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
                                  js_getter_str);
             return false;
@@ -62,16 +54,104 @@ PropDesc::checkSetter(JSContext *cx)
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
                                  js_setter_str);
             return false;
         }
     }
     return true;
 }
 
+static bool
+CheckArgCompartment(JSContext *cx, JSObject *obj, HandleValue v,
+                    const char *methodname, const char *propname)
+{
+    if (v.isObject() && v.toObject().compartment() != obj->compartment()) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_COMPARTMENT_MISMATCH,
+                             methodname, propname);
+        return false;
+    }
+    return true;
+}
+
+/*
+ * Convert Debugger.Objects in desc to debuggee values.
+ * Reject non-callable getters and setters.
+ */
+bool
+PropDesc::unwrapDebuggerObjectsInto(JSContext *cx, Debugger *dbg, HandleObject obj,
+                                    PropDesc *unwrapped) const
+{
+    MOZ_ASSERT(!isUndefined());
+
+    *unwrapped = *this;
+
+    if (unwrapped->hasValue()) {
+        RootedValue value(cx, unwrapped->value_);
+        if (!dbg->unwrapDebuggeeValue(cx, &value) ||
+            !CheckArgCompartment(cx, obj, value, "defineProperty", "value"))
+        {
+            return false;
+        }
+        unwrapped->value_ = value;
+    }
+
+    if (unwrapped->hasGet()) {
+        RootedValue get(cx, unwrapped->get_);
+        if (!dbg->unwrapDebuggeeValue(cx, &get) ||
+            !CheckArgCompartment(cx, obj, get, "defineProperty", "get"))
+        {
+            return false;
+        }
+        unwrapped->get_ = get;
+    }
+
+    if (unwrapped->hasSet()) {
+        RootedValue set(cx, unwrapped->set_);
+        if (!dbg->unwrapDebuggeeValue(cx, &set) ||
+            !CheckArgCompartment(cx, obj, set, "defineProperty", "set"))
+        {
+            return false;
+        }
+        unwrapped->set_ = set;
+    }
+
+    return true;
+}
+
+/*
+ * Rewrap *idp and the fields of *desc for the current compartment.  Also:
+ * defining a property on a proxy requires pd_ to contain a descriptor object,
+ * so reconstitute desc->pd_ if needed.
+ */
+bool
+PropDesc::wrapInto(JSContext *cx, HandleObject obj, const jsid &id, jsid *wrappedId,
+                   PropDesc *desc) const
+{
+    MOZ_ASSERT(!isUndefined());
+
+    JSCompartment *comp = cx->compartment();
+
+    *wrappedId = id;
+    if (!comp->wrapId(cx, wrappedId))
+        return false;
+
+    *desc = *this;
+    RootedValue value(cx, desc->value_);
+    RootedValue get(cx, desc->get_);
+    RootedValue set(cx, desc->set_);
+
+    if (!comp->wrap(cx, &value) || !comp->wrap(cx, &get) || !comp->wrap(cx, &set))
+        return false;
+
+    desc->value_ = value;
+    desc->get_ = get;
+    desc->set_ = set;
+    return !obj->is<ProxyObject>() || desc->makeObject(cx);
+}
+
 static const ObjectElements emptyElementsHeader(0, 0);
 
 /* Objects with no elements share one empty set of elements. */
 HeapSlot *const js::emptyObjectElements =
     reinterpret_cast<HeapSlot *>(uintptr_t(&emptyElementsHeader) + sizeof(ObjectElements));
 
 #ifdef DEBUG
 
@@ -297,13 +377,13 @@ js::ObjectImpl::markChildren(JSTracer *t
         MarkObjectSlots(trc, obj, 0, obj->slotSpan());
         gc::MarkArraySlots(trc, obj->getDenseInitializedLength(), obj->getDenseElements(), "objectElements");
     }
 }
 
 void
 PropDesc::trace(JSTracer *trc)
 {
-    gc::MarkObjectRoot(trc, &descObj_, "PropDesc descriptor object");
+    gc::MarkValueRoot(trc, &pd_, "PropDesc pd");
     gc::MarkValueRoot(trc, &value_, "PropDesc value");
     gc::MarkValueRoot(trc, &get_, "PropDesc get");
     gc::MarkValueRoot(trc, &set_, "PropDesc set");
 }
--- a/js/src/vm/PropDesc.h
+++ b/js/src/vm/PropDesc.h
@@ -7,16 +7,18 @@
 #ifndef vm_PropDesc_h
 #define vm_PropDesc_h
 
 #include "jsapi.h"
 #include "NamespaceImports.h"
 
 namespace js {
 
+class Debugger;
+
 static inline JSPropertyOp
 CastAsPropertyOp(JSObject *object)
 {
     return JS_DATA_TO_FUNC_PTR(JSPropertyOp, object);
 }
 
 static inline JSStrictPropertyOp
 CastAsStrictPropertyOp(JSObject *object)
@@ -29,17 +31,17 @@ CastAsStrictPropertyOp(JSObject *object)
  * structure.
  */
 struct PropDesc {
   private:
     /*
      * Original object from which this descriptor derives, passed through for
      * the benefit of proxies.
      */
-    JSObject *descObj_;
+    Value pd_;
 
     Value value_, get_, set_;
 
     /* Property descriptor boolean fields. */
     uint8_t attrs;
 
     /* Bits indicating which values are set. */
     bool hasGet_ : 1;
@@ -48,43 +50,44 @@ struct PropDesc {
     bool hasWritable_ : 1;
     bool hasEnumerable_ : 1;
     bool hasConfigurable_ : 1;
 
     /* Or maybe this represents a property's absence, and it's undefined. */
     bool isUndefined_ : 1;
 
     explicit PropDesc(const Value &v)
-      : descObj_(nullptr),
+      : pd_(UndefinedValue()),
         value_(v),
         get_(UndefinedValue()), set_(UndefinedValue()),
         attrs(0),
         hasGet_(false), hasSet_(false),
         hasValue_(true), hasWritable_(false), hasEnumerable_(false), hasConfigurable_(false),
         isUndefined_(false)
     {
     }
 
   public:
+    friend void JS::AutoGCRooter::trace(JSTracer *trc);
     friend struct GCMethods<PropDesc>;
 
     void trace(JSTracer *trc);
 
     enum Enumerability { Enumerable = true, NonEnumerable = false };
     enum Configurability { Configurable = true, NonConfigurable = false };
     enum Writability { Writable = true, NonWritable = false };
 
     PropDesc();
 
     static PropDesc undefined() { return PropDesc(); }
     static PropDesc valueOnly(const Value &v) { return PropDesc(v); }
 
     PropDesc(const Value &v, Writability writable,
              Enumerability enumerable, Configurability configurable)
-      : descObj_(nullptr),
+      : pd_(UndefinedValue()),
         value_(v),
         get_(UndefinedValue()), set_(UndefinedValue()),
         attrs((writable ? 0 : JSPROP_READONLY) |
               (enumerable ? JSPROP_ENUMERATE : 0) |
               (configurable ? 0 : JSPROP_PERMANENT)),
         hasGet_(false), hasSet_(false),
         hasValue_(true), hasWritable_(true), hasEnumerable_(true), hasConfigurable_(true),
         isUndefined_(false)
@@ -121,33 +124,29 @@ struct PropDesc {
      * other fields of this PropDesc from desc.
      *
      * makeObject populates pd based on the other fields of *this, creating a
      * new property descriptor JSObject and defining properties on it.
      */
     void initFromPropertyDescriptor(Handle<JSPropertyDescriptor> desc);
     bool makeObject(JSContext *cx);
 
-    /* Reset the descriptor entirely. */
-    void setUndefined();
+    void setUndefined() { isUndefined_ = true; }
+
     bool isUndefined() const { return isUndefined_; }
 
     bool hasGet() const { MOZ_ASSERT(!isUndefined()); return hasGet_; }
     bool hasSet() const { MOZ_ASSERT(!isUndefined()); return hasSet_; }
     bool hasValue() const { MOZ_ASSERT(!isUndefined()); return hasValue_; }
     bool hasWritable() const { MOZ_ASSERT(!isUndefined()); return hasWritable_; }
     bool hasEnumerable() const { MOZ_ASSERT(!isUndefined()); return hasEnumerable_; }
     bool hasConfigurable() const { MOZ_ASSERT(!isUndefined()); return hasConfigurable_; }
 
-    Value descriptorValue() const {
-        MOZ_ASSERT(!isUndefined());
-        return descObj_ ? ObjectValue(*descObj_) : UndefinedValue();
-    }
-    void setDescriptorObject(JSObject *obj) { descObj_ = obj; }
-    void clearDescriptorObject() { setDescriptorObject(nullptr); }
+    Value pd() const { MOZ_ASSERT(!isUndefined()); return pd_; }
+    void clearPd() { pd_ = UndefinedValue(); }
 
     uint8_t attributes() const { MOZ_ASSERT(!isUndefined()); return attrs; }
 
     /* 8.10.1 IsAccessorDescriptor(desc) */
     bool isAccessorDescriptor() const {
         return !isUndefined() && (hasGet() || hasSet());
     }
 
@@ -178,21 +177,16 @@ struct PropDesc {
         MOZ_ASSERT(hasWritable());
         return (attrs & JSPROP_READONLY) == 0;
     }
 
     HandleValue value() const {
         MOZ_ASSERT(hasValue());
         return HandleValue::fromMarkedLocation(&value_);
     }
-    void setValue(const Value &value) {
-        MOZ_ASSERT(!isUndefined());
-        value_ = value;
-        hasValue_ = true;
-    }
 
     JSObject * getterObject() const {
         MOZ_ASSERT(!isUndefined());
         MOZ_ASSERT(hasGet());
         return get_.isUndefined() ? nullptr : &get_.toObject();
     }
     JSObject * setterObject() const {
         MOZ_ASSERT(!isUndefined());
@@ -206,27 +200,16 @@ struct PropDesc {
         return HandleValue::fromMarkedLocation(&get_);
     }
     HandleValue setterValue() const {
         MOZ_ASSERT(!isUndefined());
         MOZ_ASSERT(hasSet());
         return HandleValue::fromMarkedLocation(&set_);
     }
 
-    void setGetter(const Value &getter) {
-        MOZ_ASSERT(!isUndefined());
-        get_ = getter;
-        hasGet_ = true;
-    }
-    void setSetter(const Value &setter) {
-        MOZ_ASSERT(!isUndefined());
-        set_ = setter;
-        hasSet_ = true;
-    }
-
     /*
      * Unfortunately the values produced by these methods are used such that
      * we can't assert anything here.  :-(
      */
     JSPropertyOp getter() const {
         return CastAsPropertyOp(get_.isUndefined() ? nullptr : &get_.toObject());
     }
     JSStrictPropertyOp setter() const {
@@ -235,16 +218,22 @@ struct PropDesc {
 
     /*
      * Throw a TypeError if a getter/setter is present and is neither callable
      * nor undefined. These methods do exactly the type checks that are skipped
      * by passing false as the checkAccessors parameter of initialize.
      */
     bool checkGetter(JSContext *cx);
     bool checkSetter(JSContext *cx);
+
+    bool unwrapDebuggerObjectsInto(JSContext *cx, Debugger *dbg, HandleObject obj,
+                                   PropDesc *unwrapped) const;
+
+    bool wrapInto(JSContext *cx, HandleObject obj, const jsid &id, jsid *wrappedId,
+                  PropDesc *wrappedDesc) const;
 };
 
 } /* namespace js */
 
 namespace JS {
 
 template <typename Outer>
 class PropDescOperations
@@ -256,17 +245,17 @@ class PropDescOperations
 
     bool hasGet() const { return desc()->hasGet(); }
     bool hasSet() const { return desc()->hasSet(); }
     bool hasValue() const { return desc()->hasValue(); }
     bool hasWritable() const { return desc()->hasWritable(); }
     bool hasEnumerable() const { return desc()->hasEnumerable(); }
     bool hasConfigurable() const { return desc()->hasConfigurable(); }
 
-    Value descriptorValue() const { return desc()->descriptorValue(); }
+    Value pd() const { return desc()->pd(); }
 
     uint8_t attributes() const { return desc()->attributes(); }
 
     bool isAccessorDescriptor() const { return desc()->isAccessorDescriptor(); }
     bool isDataDescriptor() const { return desc()->isDataDescriptor(); }
     bool isGenericDescriptor() const { return desc()->isGenericDescriptor(); }
     bool configurable() const { return desc()->configurable(); }
     bool enumerable() const { return desc()->enumerable(); }
@@ -275,16 +264,20 @@ class PropDescOperations
     HandleValue value() const { return desc()->value(); }
     JSObject *getterObject() const { return desc()->getterObject(); }
     JSObject *setterObject() const { return desc()->setterObject(); }
     HandleValue getterValue() const { return desc()->getterValue(); }
     HandleValue setterValue() const { return desc()->setterValue(); }
 
     JSPropertyOp getter() const { return desc()->getter(); }
     JSStrictPropertyOp setter() const { return desc()->setter(); }
+
+    // We choose not to expose the debugger-specific parts of PropDesc, both
+    // because they are not really general use, but also because they are a
+    // pain to expose.
 };
 
 template <typename Outer>
 class MutablePropDescOperations : public PropDescOperations<Outer>
 {
     js::PropDesc * desc() { return static_cast<Outer*>(this)->extractMutable(); }
 
   public:
@@ -301,42 +294,31 @@ class MutablePropDescOperations : public
 
     void initFromPropertyDescriptor(Handle<JSPropertyDescriptor> descriptor) {
         desc()->initFromPropertyDescriptor(descriptor);
     }
     bool makeObject(JSContext *cx) {
         return desc()->makeObject(cx);
     }
 
-    void setValue(const Value &value) {
-        desc()->setValue(value);
-    }
-    void setGetter(const Value &getter) {
-        desc()->setGetter(getter);
-    }
-    void setSetter(const Value &setter) {
-        desc()->setSetter(setter);
-    }
-
     void setUndefined() { desc()->setUndefined(); }
-
-    void setDescriptorObject(JSObject *obj) { desc()->setDescriptorObject(obj); }
-    void clearDescriptorObject() { desc()->clearDescriptorObject(); }
+    void clearPd() { desc()->clearPd(); }
 };
 
 } /* namespace JS */
 
 namespace js {
 
 template <>
 struct GCMethods<PropDesc> {
     static PropDesc initial() { return PropDesc(); }
     static ThingRootKind kind() { return THING_ROOT_PROP_DESC; }
     static bool poisoned(const PropDesc &desc) {
-        return JS::IsPoisonedPtr(desc.descObj_) ||
+        return (desc.pd_.isGCThing() &&
+                JS::IsPoisonedPtr(desc.pd_.toGCThing())) ||
                (desc.value_.isGCThing() &&
                 JS::IsPoisonedPtr(desc.value_.toGCThing())) ||
                (desc.get_.isGCThing() &&
                 JS::IsPoisonedPtr(desc.get_.toGCThing())) ||
                (desc.set_.isGCThing() &&
                 JS::IsPoisonedPtr(desc.set_.toGCThing()));
     }
 };