Backed out 7 changesets (bug 1117242) for mochitest failures.
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 06 Feb 2015 13:40:53 -0500
changeset 245418 3c88c70e1e643dbef30f2b1dfb4f412e4d1be1aa
parent 245417 37321f61780e9436bab65fd35654f06d2a4a6203
child 245419 7d87dc37c707e896242a5420e29ff19acd0a0a18
push id7677
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 18:11:24 +0000
treeherdermozilla-aurora@f531d838c055 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1117242
milestone38.0a1
backs out694f7ac58964606b5d87f4b6700ebdc69b77c3c5
b3f8122dd9908f97ae8638d1beb63d25213af8d9
442d41779bd866c924aa84fd52a6ce478b7aeb52
ccf6dfe1ac7534cc3ecfb15fc45de4e707c08d21
86421767cd26f6931934f2acc1fa619c78788353
2f996950fb2fc21e2fc09db925ca704364d0251b
4414e9d0b66b776e71a2274dbc1f699a580f8ae1
Backed out 7 changesets (bug 1117242) for mochitest failures. Backed out changeset 694f7ac58964 (bug 1117242) Backed out changeset b3f8122dd990 (bug 1117242) Backed out changeset 442d41779bd8 (bug 1117242) Backed out changeset ccf6dfe1ac75 (bug 1117242) Backed out changeset 86421767cd26 (bug 1117242) Backed out changeset 2f996950fb2f (bug 1117242) Backed out changeset 4414e9d0b66b (bug 1117242) CLOSED TREE
js/src/builtin/TestingFunctions.cpp
js/src/doc/SavedFrame/SavedFrame.md
js/src/gc/Rooting.h
js/src/jit-test/tests/saved-stacks/principals-01.js
js/src/jit-test/tests/saved-stacks/principals-02.js
js/src/jit-test/tests/saved-stacks/principals-03.js
js/src/jit-test/tests/saved-stacks/principals-04.js
js/src/jsprototypes.h
js/src/jsscript.cpp
js/src/vm/GlobalObject.h
js/src/vm/SavedStacks.cpp
js/src/vm/SavedStacks.h
js/xpconnect/src/xpcprivate.h
js/xpconnect/tests/unit/test_xray_SavedFrame.js
js/xpconnect/tests/unit/xpcshell.ini
js/xpconnect/wrappers/XrayWrapper.cpp
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -962,40 +962,19 @@ SaveStack(JSContext *cx, unsigned argc, 
             js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
                                      JSDVG_SEARCH_STACK, args[0], JS::NullPtr(),
                                      "not a valid maximum frame count", NULL);
             return false;
         }
         maxFrameCount = d;
     }
 
-    JSCompartment *targetCompartment = cx->compartment();
-    if (args.length() >= 2) {
-        if (!args[1].isObject()) {
-            js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
-                                     JSDVG_SEARCH_STACK, args[0], JS::NullPtr(),
-                                     "not an object", NULL);
-            return false;
-        }
-        RootedObject obj(cx, UncheckedUnwrap(&args[1].toObject()));
-        if (!obj)
-            return false;
-        targetCompartment = obj->compartment();
-    }
-
-    RootedObject stack(cx);
-    {
-        AutoCompartment ac(cx, targetCompartment);
-        if (!JS::CaptureCurrentStack(cx, &stack, maxFrameCount))
-            return false;
-    }
-
-    if (stack && !cx->compartment()->wrap(cx, &stack))
+    Rooted<JSObject*> stack(cx);
+    if (!JS::CaptureCurrentStack(cx, &stack, maxFrameCount))
         return false;
-
     args.rval().setObjectOrNull(stack);
     return true;
 }
 
 static bool
 EnableTrackAllocations(JSContext *cx, unsigned argc, jsval *vp)
 {
     SetObjectMetadataCallback(cx, SavedStacksMetadataCallback);
@@ -2412,25 +2391,23 @@ static const JSFunctionSpecWithHelp Test
 "  Set this compartment's SavedStacks' RNG state.\n"),
 
     JS_FN_HELP("getSavedFrameCount", GetSavedFrameCount, 0, 0,
 "getSavedFrameCount()",
 "  Return the number of SavedFrame instances stored in this compartment's\n"
 "  SavedStacks cache."),
 
     JS_FN_HELP("saveStack", SaveStack, 0, 0,
-"saveStack([maxDepth [, compartment]])",
-"  Capture a stack. If 'maxDepth' is given, capture at most 'maxDepth' number\n"
-"  of frames. If 'compartment' is given, allocate the js::SavedFrame instances\n"
-"  with the given object's compartment."),
+"saveStack()",
+"  Capture a stack.\n"),
 
     JS_FN_HELP("enableTrackAllocations", EnableTrackAllocations, 0, 0,
 "enableTrackAllocations()",
-"  Start capturing the JS stack at every allocation. Note that this sets an\n"
-"  object metadata callback that will override any other object metadata\n"
+"  Start capturing the JS stack at every allocation. Note that this sets an "
+"  object metadata callback that will override any other object metadata "
 "  callback that may be set."),
 
     JS_FN_HELP("disableTrackAllocations", DisableTrackAllocations, 0, 0,
 "disableTrackAllocations()",
 "  Stop capturing the JS stack at every allocation."),
 
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
     JS_FN_HELP("oomAfterAllocations", OOMAfterAllocations, 1, 0,
--- a/js/src/doc/SavedFrame/SavedFrame.md
+++ b/js/src/doc/SavedFrame/SavedFrame.md
@@ -1,53 +1,15 @@
 # `SavedFrame`
 
 A `SavedFrame` instance is a singly linked list of stack frames. It represents a
 JavaScript call stack at a past moment of execution. Younger frames hold a
 reference to the frames that invoked them. The older tails are shared across
 many younger frames.
 
-`SavedFrame` stacks should generally be captured, allocated, and live within the
-compartment that is being observed or debugged. Usually this is a content
-compartment.
-
-## Capturing `SavedFrame` Stacks
-
-### From C++
-
-Use `JS::CaptureCurrentStack` declared in `jsapi.h`.
-
-### From JS
-
-Use `saveStack`, accessible via `Components.utils.getJSTestingFunction()`.
-
-## Including and Excluding Chrome Frames
-
-Consider the following `SavedFrame` stack. Arrows represent links from child to
-parent frame, `content.js` is from a compartment with content principals, and
-`chrome.js` is from a compartment with chrome principals.
-
-    function A from content.js
-                |
-                V
-    function B from chrome.js
-                |
-                V
-    function C from content.js
-
-The content compartment will ever have one view of this stack: `A -> C`.
-
-However, a chrome compartment has a choice: it can either take the same view
-that the content compartment has (`A -> C`), or it can view all stack frames,
-including the frames from chrome compartments (`A -> B -> C`). To view
-everything, use an `XrayWrapper`. This is the default wrapper. To see the stack
-as the content compartment sees it, waive the xray wrapper with
-`Components.utils.waiveXrays`:
-
-    const contentViewOfStack = Components.utils.waiveXrays(someStack);
 
 ## Accessor Properties of the `SavedFrame.prototype` Object
 
 `source`
 :   The source URL for this stack frame, as a string.
 
 `line`
 :   The line number for this stack frame.
--- a/js/src/gc/Rooting.h
+++ b/js/src/gc/Rooting.h
@@ -12,32 +12,30 @@
 class JSAtom;
 class JSLinearString;
 
 namespace js {
 
 class PropertyName;
 class NativeObject;
 class ArrayObject;
-class GlobalObject;
 class PlainObject;
 class ScriptSourceObject;
 class Shape;
 class ObjectGroup;
 
 // These are internal counterparts to the public types such as HandleObject.
 
 typedef JS::Handle<NativeObject*>      HandleNativeObject;
 typedef JS::Handle<Shape*>             HandleShape;
 typedef JS::Handle<ObjectGroup*>       HandleObjectGroup;
 typedef JS::Handle<JSAtom*>            HandleAtom;
 typedef JS::Handle<JSLinearString*>    HandleLinearString;
 typedef JS::Handle<PropertyName*>      HandlePropertyName;
 typedef JS::Handle<ArrayObject*>       HandleArrayObject;
-typedef JS::Rooted<GlobalObject*>      RootedGlobalObject;
 typedef JS::Handle<PlainObject*>       HandlePlainObject;
 typedef JS::Handle<ScriptSourceObject*> HandleScriptSource;
 
 typedef JS::MutableHandle<Shape*>      MutableHandleShape;
 typedef JS::MutableHandle<JSAtom*>     MutableHandleAtom;
 typedef JS::MutableHandle<NativeObject*> MutableHandleNativeObject;
 
 typedef JS::Rooted<NativeObject*>      RootedNativeObject;
--- a/js/src/jit-test/tests/saved-stacks/principals-01.js
+++ b/js/src/jit-test/tests/saved-stacks/principals-01.js
@@ -37,17 +37,17 @@ const mid  = newGlobal({ principal: 0xff
 const high = newGlobal({ principal: 0xfffff });
 
 var count = 0;
 
      eval('function a() { check("a",        extract(saveStack())); b(); }');
 low .eval('function b() { check("b",        extract(saveStack())); c(); }');
 mid .eval('function c() { check("cba",      extract(saveStack())); d(); }');
 high.eval('function d() { check("dcba",     extract(saveStack())); e(); }');
-     eval('function e() { check("ecba",     extract(saveStack())); f(); }');
+     eval('function e() { check("edcba",    extract(saveStack())); f(); }'); // no principal, so checks skipped
 low .eval('function f() { check("fb",       extract(saveStack())); g(); }');
 mid .eval('function g() { check("gfecba",   extract(saveStack())); h(); }');
 high.eval('function h() { check("hgfedcba", extract(saveStack()));      }');
 
 // Make everyone's functions visible to each other, as needed.
      b = low .b;
 low .c = mid .c;
 mid .d = high.d;
--- a/js/src/jit-test/tests/saved-stacks/principals-02.js
+++ b/js/src/jit-test/tests/saved-stacks/principals-02.js
@@ -28,17 +28,17 @@ function check(expected, stack) {
 var low = newGlobal({ principal: 0 });
 var mid = newGlobal({ principal: 0xffff });
 var high = newGlobal({ principal: 0xfffff });
 
      eval('function a() { check("a",        saveStack().toString()); b(); }');
 low .eval('function b() { check("b",        saveStack().toString()); c(); }');
 mid .eval('function c() { check("cba",      saveStack().toString()); d(); }');
 high.eval('function d() { check("dcba",     saveStack().toString()); e(); }');
-     eval('function e() { check("ecba",     saveStack().toString()); f(); }');
+     eval('function e() { check("edcba",    saveStack().toString()); f(); }'); // no principal, so checks skipped
 low .eval('function f() { check("fb",       saveStack().toString()); g(); }');
 mid .eval('function g() { check("gfecba",   saveStack().toString()); h(); }');
 high.eval('function h() { check("hgfedcba", saveStack().toString());      }');
 
 // Make everyone's functions visible to each other, as needed.
      b = low .b;
 low .c = mid .c;
 mid .d = high.d;
deleted file mode 100644
--- a/js/src/jit-test/tests/saved-stacks/principals-03.js
+++ /dev/null
@@ -1,23 +0,0 @@
-// With arrows representing child-to-parent links, create a SavedFrame stack
-// like this:
-//
-//     high.a -> low.b
-//
-// in `low`'s compartment and give `low` a reference to this stack. Assert the
-// stack's youngest frame's properties doesn't leak information about `high.a`
-// that `low` shouldn't have access to, and instead returns information about
-// `low.b`.
-
-var low = newGlobal({ principal: 0 });
-var high = newGlobal({ principal: 0xfffff });
-
-low.high = high;
-high.low = low;
-
-high.eval("function a() { return saveStack(0, low); }");
-low.eval("function b() { return high.a(); }")
-
-var stack = low.b();
-
-assertEq(stack.functionDisplayName, "b");
-assertEq(stack.parent, null);
deleted file mode 100644
--- a/js/src/jit-test/tests/saved-stacks/principals-04.js
+++ /dev/null
@@ -1,15 +0,0 @@
-// Test what happens when a compartment gets a SavedFrame that it doesn't have
-// the principals to access any of its frames.
-
-var low  = newGlobal({ principal: 0       });
-var high = newGlobal({ principal: 0xfffff });
-
-low.high = high;
-high.low = low;
-
-high.eval("function a() { return saveStack(1, low); }");
-var stack = low.eval("high.a();")
-
-assertEq(stack.functionDisplayName, null);
-assertEq(stack.parent, null);
-assertEq(stack.toString(), "");
--- a/js/src/jsprototypes.h
+++ b/js/src/jsprototypes.h
@@ -106,14 +106,12 @@ IF_SAB(real,imaginary)(SharedInt16Array,
 IF_SAB(real,imaginary)(SharedUint16Array,       46,     js_InitViaClassSpec,       SHARED_TYPED_ARRAY_CLASP(Uint16)) \
 IF_SAB(real,imaginary)(SharedInt32Array,        47,     js_InitViaClassSpec,       SHARED_TYPED_ARRAY_CLASP(Int32)) \
 IF_SAB(real,imaginary)(SharedUint32Array,       48,     js_InitViaClassSpec,       SHARED_TYPED_ARRAY_CLASP(Uint32)) \
 IF_SAB(real,imaginary)(SharedFloat32Array,      49,     js_InitViaClassSpec,       SHARED_TYPED_ARRAY_CLASP(Float32)) \
 IF_SAB(real,imaginary)(SharedFloat64Array,      50,     js_InitViaClassSpec,       SHARED_TYPED_ARRAY_CLASP(Float64)) \
 IF_SAB(real,imaginary)(SharedUint8ClampedArray, 51,     js_InitViaClassSpec,       SHARED_TYPED_ARRAY_CLASP(Uint8Clamped)) \
     real(TypedArray,            52,      js_InitViaClassSpec,      &js::TypedArrayObject::sharedTypedArrayPrototypeClass) \
 IF_SAB(real,imaginary)(Atomics,                 53,     js_InitAtomicsClass, OCLASP(Atomics)) \
-    real(SavedFrame,            54,      js_InitViaClassSpec,      &js::SavedFrame::class_) \
-
 
 #define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro)
 
 #endif /* jsprototypes_h */
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -56,16 +56,18 @@
 using namespace js;
 using namespace js::gc;
 using namespace js::frontend;
 
 using mozilla::PodCopy;
 using mozilla::PodZero;
 using mozilla::RotateLeft;
 
+typedef Rooted<GlobalObject *> RootedGlobalObject;
+
 /* static */ BindingIter
 Bindings::argumentsBinding(ExclusiveContext *cx, InternalBindingsHandle bindings)
 {
     HandlePropertyName arguments = cx->names().arguments;
     BindingIter bi(bindings);
     while (bi->name() != arguments)
         bi++;
     return bi;
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -301,26 +301,26 @@ class GlobalObject : public NativeObject
     T *createBlankPrototype(JSContext *cx) {
         NativeObject *res = createBlankPrototype(cx, &T::class_);
         return res ? &res->template as<T>() : nullptr;
     }
 
     NativeObject *getOrCreateObjectPrototype(JSContext *cx) {
         if (functionObjectClassesInitialized())
             return &getPrototype(JSProto_Object).toObject().as<NativeObject>();
-        RootedGlobalObject self(cx, this);
+        Rooted<GlobalObject*> self(cx, this);
         if (!ensureConstructor(cx, self, JSProto_Object))
             return nullptr;
         return &self->getPrototype(JSProto_Object).toObject().as<NativeObject>();
     }
 
     NativeObject *getOrCreateFunctionPrototype(JSContext *cx) {
         if (functionObjectClassesInitialized())
             return &getPrototype(JSProto_Function).toObject().as<NativeObject>();
-        RootedGlobalObject self(cx, this);
+        Rooted<GlobalObject*> self(cx, this);
         if (!ensureConstructor(cx, self, JSProto_Object))
             return nullptr;
         return &self->getPrototype(JSProto_Function).toObject().as<NativeObject>();
     }
 
     static NativeObject *getOrCreateArrayPrototype(JSContext *cx, Handle<GlobalObject*> global) {
         if (!ensureConstructor(cx, global, JSProto_Array))
             return nullptr;
@@ -364,23 +364,16 @@ class GlobalObject : public NativeObject
     }
 
     JSObject *maybeGetRegExpPrototype() {
         if (regexpClassInitialized())
             return &getPrototype(JSProto_RegExp).toObject();
         return nullptr;
     }
 
-    static NativeObject *getOrCreateSavedFramePrototype(JSContext *cx,
-                                                        Handle<GlobalObject*> global) {
-        if (!ensureConstructor(cx, global, JSProto_SavedFrame))
-            return nullptr;
-        return &global->getPrototype(JSProto_SavedFrame).toObject().as<NativeObject>();
-    }
-
     static JSObject *getOrCreateArrayBufferPrototype(JSContext *cx, Handle<GlobalObject*> global) {
         if (!ensureConstructor(cx, global, JSProto_ArrayBuffer))
             return nullptr;
         return &global->getPrototype(JSProto_ArrayBuffer).toObject();
     }
 
     JSObject *getOrCreateSharedArrayBufferPrototype(JSContext *cx, Handle<GlobalObject*> global) {
         if (!ensureConstructor(cx, global, JSProto_SharedArrayBuffer))
@@ -470,17 +463,17 @@ class GlobalObject : public NativeObject
 
   private:
     typedef bool (*ObjectInitOp)(JSContext *cx, Handle<GlobalObject*> global);
 
     JSObject *getOrCreateObject(JSContext *cx, unsigned slot, ObjectInitOp init) {
         Value v = getSlotRef(slot);
         if (v.isObject())
             return &v.toObject();
-        RootedGlobalObject self(cx, this);
+        Rooted<GlobalObject*> self(cx, this);
         if (!init(cx, self))
             return nullptr;
         return &self->getSlot(slot).toObject();
     }
 
   public:
     static NativeObject *getOrCreateIteratorPrototype(JSContext *cx,
                                                       Handle<GlobalObject*> global)
@@ -548,17 +541,17 @@ class GlobalObject : public NativeObject
 
     static JSObject *getOrCreateSetIteratorPrototype(JSContext *cx,
                                                      Handle<GlobalObject*> global)
     {
         return global->getOrCreateObject(cx, SET_ITERATOR_PROTO, initSetIteratorProto);
     }
 
     JSObject *getOrCreateDataViewPrototype(JSContext *cx) {
-        RootedGlobalObject self(cx, this);
+        Rooted<GlobalObject*> self(cx, this);
         if (!ensureConstructor(cx, self, JSProto_DataView))
             return nullptr;
         return &self->getPrototype(JSProto_DataView).toObject();
     }
 
     NativeObject *intrinsicsHolder() {
         MOZ_ASSERT(!getSlot(INTRINSICS).isUndefined());
         return &getSlot(INTRINSICS).toObject().as<NativeObject>();
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -11,23 +11,22 @@
 #include <math.h>
 
 #include "jsapi.h"
 #include "jscompartment.h"
 #include "jsfriendapi.h"
 #include "jshashutil.h"
 #include "jsmath.h"
 #include "jsnum.h"
-#include "jsscript.h"
 #include "prmjtime.h"
 
 #include "gc/Marking.h"
-#include "gc/Rooting.h"
 #include "js/Vector.h"
 #include "vm/Debugger.h"
+#include "vm/GlobalObject.h"
 #include "vm/StringBuffer.h"
 
 #include "jscntxtinlines.h"
 
 #include "vm/NativeObject-inl.h"
 
 using mozilla::AddToHash;
 using mozilla::HashString;
@@ -132,77 +131,28 @@ SavedFrame::HashPolicy::match(SavedFrame
 }
 
 /* static */ void
 SavedFrame::HashPolicy::rekey(Key &key, const Key &newKey)
 {
     key = newKey;
 }
 
-/* static */ bool
-SavedFrame::finishSavedFrameInit(JSContext *cx, HandleObject ctor, HandleObject proto)
-{
-    // The only object with the SavedFrame::class_ that doesn't have a source
-    // should be the prototype.
-    proto->as<NativeObject>().setReservedSlot(SavedFrame::JSSLOT_SOURCE, NullValue());
-
-    return FreezeObject(cx, proto);
-}
-
 /* static */ const Class SavedFrame::class_ = {
     "SavedFrame",
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
-    JSCLASS_HAS_RESERVED_SLOTS(SavedFrame::JSSLOT_COUNT) |
-    JSCLASS_HAS_CACHED_PROTO(JSProto_SavedFrame) |
-    JSCLASS_IS_ANONYMOUS,
-    nullptr,                    // addProperty
-    nullptr,                    // delProperty
-    nullptr,                    // getProperty
-    nullptr,                    // setProperty
-    nullptr,                    // enumerate
-    nullptr,                    // resolve
-    nullptr,                    // convert
-    SavedFrame::finalize,       // finalize
-    nullptr,                    // call
-    nullptr,                    // hasInstance
-    nullptr,                    // construct
-    nullptr,                    // trace
-
-    // ClassSpec
-    {
-        GenericCreateConstructor<SavedFrame::construct, 0, JSFunction::FinalizeKind>,
-        GenericCreatePrototype,
-        SavedFrame::staticFunctions,
-        SavedFrame::protoFunctions,
-        SavedFrame::protoAccessors,
-        SavedFrame::finishSavedFrameInit,
-        ClassSpec::DontDefineConstructor
-    }
-};
-
-/* static */ const JSFunctionSpec
-SavedFrame::staticFunctions[] = {
-    JS_FS_END
-};
-
-/* static */ const JSFunctionSpec
-SavedFrame::protoFunctions[] = {
-    JS_FN("constructor", SavedFrame::construct, 0, 0),
-    JS_FN("toString", SavedFrame::toStringMethod, 0, 0),
-    JS_FS_END
-};
-
-/* static */ const JSPropertySpec
-SavedFrame::protoAccessors[] = {
-    JS_PSG("source", SavedFrame::sourceProperty, 0),
-    JS_PSG("line", SavedFrame::lineProperty, 0),
-    JS_PSG("column", SavedFrame::columnProperty, 0),
-    JS_PSG("functionDisplayName", SavedFrame::functionDisplayNameProperty, 0),
-    JS_PSG("parent", SavedFrame::parentProperty, 0),
-    JS_PS_END
+    JSCLASS_HAS_RESERVED_SLOTS(SavedFrame::JSSLOT_COUNT),
+    nullptr, // addProperty
+    nullptr, // delProperty
+    nullptr, // getProperty
+    nullptr, // setProperty
+    nullptr, // enumerate
+    nullptr, // resolve
+    nullptr, // convert
+    SavedFrame::finalize
 };
 
 /* static */ void
 SavedFrame::finalize(FreeOp *fop, JSObject *obj)
 {
     JSPrincipals *p = obj->as<SavedFrame>().getPrincipals();
     if (p) {
         JSRuntime *rt = obj->runtimeFromMainThread();
@@ -304,170 +254,166 @@ SavedFrame::isSelfHosted()
 /* static */ bool
 SavedFrame::construct(JSContext *cx, unsigned argc, Value *vp)
 {
     JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
                          "SavedFrame");
     return false;
 }
 
-// Return the first SavedFrame in the chain that starts with |frame| whose
-// principals are subsumed by |principals|, according to |subsumes|. If there is
-// no such frame, return nullptr.
-static SavedFrame *
-GetFirstSubsumedFrame(JSContext *cx, SavedFrame *frame)
-{
-    JSSubsumesOp subsumes = cx->runtime()->securityCallbacks->subsumes;
-    if (!subsumes)
-        return frame;
-
-    JSPrincipals *principals = cx->compartment()->principals;
-
-    while (frame && !subsumes(principals, frame->getPrincipals()))
-        frame = frame->getParent();
-
-    return frame;
-}
-
-/* static */ bool
-SavedFrame::checkThis(JSContext *cx, CallArgs &args, const char *fnName,
-                      MutableHandleSavedFrame frame)
+/* static */ SavedFrame *
+SavedFrame::checkThis(JSContext *cx, CallArgs &args, const char *fnName)
 {
     const Value &thisValue = args.thisv();
 
     if (!thisValue.isObject()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT);
-        return false;
+        return nullptr;
     }
 
-    JSObject *thisObject = CheckedUnwrap(&thisValue.toObject());
-    if (!thisObject || !thisObject->is<SavedFrame>()) {
+    JSObject &thisObject = thisValue.toObject();
+    if (!thisObject.is<SavedFrame>()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
-                             SavedFrame::class_.name, fnName,
-                             thisObject ? thisObject->getClass()->name : "object");
-        return false;
+                             SavedFrame::class_.name, fnName, thisObject.getClass()->name);
+        return nullptr;
     }
 
     // Check for SavedFrame.prototype, which has the same class as SavedFrame
     // instances, however doesn't actually represent a captured stack frame. It
     // is the only object that is<SavedFrame>() but doesn't have a source.
-    if (thisObject->as<SavedFrame>().getReservedSlot(JSSLOT_SOURCE).isNull()) {
+    if (thisObject.as<SavedFrame>().getReservedSlot(JSSLOT_SOURCE).isNull()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
                              SavedFrame::class_.name, fnName, "prototype object");
-        return false;
+        return nullptr;
     }
 
-    // The caller might not have the principals to see this frame's data, so get
-    // the first one they _do_ have access to.
-    frame.set(GetFirstSubsumedFrame(cx, &thisObject->as<SavedFrame>()));
-    return true;
+    return &thisObject.as<SavedFrame>();
 }
 
 // Get the SavedFrame * from the current this value and handle any errors that
 // might occur therein.
 //
 // These parameters must already exist when calling this macro:
 //   - JSContext  *cx
 //   - unsigned   argc
 //   - Value      *vp
 //   - const char *fnName
-//   - Value      defaultVal
 // These parameters will be defined after calling this macro:
 //   - CallArgs args
 //   - Rooted<SavedFrame *> frame (will be non-null)
-#define THIS_SAVEDFRAME(cx, argc, vp, fnName, defaultVal, args, frame) \
-    CallArgs args = CallArgsFromVp(argc, vp);                          \
-    RootedSavedFrame frame(cx);                                        \
-    if (!checkThis(cx, args, fnName, &frame))                          \
-        return false;                                                  \
-    if (!frame) {                                                      \
-        args.rval().set(defaultVal);                                   \
-        return true;                                                   \
-    }
+#define THIS_SAVEDFRAME(cx, argc, vp, fnName, args, frame)         \
+    CallArgs args = CallArgsFromVp(argc, vp);                      \
+    RootedSavedFrame frame(cx, checkThis(cx, args, fnName));   \
+    if (!frame)                                                    \
+        return false
 
 /* static */ bool
 SavedFrame::sourceProperty(JSContext *cx, unsigned argc, Value *vp)
 {
-    THIS_SAVEDFRAME(cx, argc, vp, "(get source)", NullValue(), args, frame);
+    THIS_SAVEDFRAME(cx, argc, vp, "(get source)", args, frame);
     args.rval().setString(frame->getSource());
     return true;
 }
 
 /* static */ bool
 SavedFrame::lineProperty(JSContext *cx, unsigned argc, Value *vp)
 {
-    THIS_SAVEDFRAME(cx, argc, vp, "(get line)", NullValue(), args, frame);
+    THIS_SAVEDFRAME(cx, argc, vp, "(get line)", args, frame);
     uint32_t line = frame->getLine();
     args.rval().setNumber(line);
     return true;
 }
 
 /* static */ bool
 SavedFrame::columnProperty(JSContext *cx, unsigned argc, Value *vp)
 {
-    THIS_SAVEDFRAME(cx, argc, vp, "(get column)", NullValue(), args, frame);
+    THIS_SAVEDFRAME(cx, argc, vp, "(get column)", args, frame);
     uint32_t column = frame->getColumn();
     args.rval().setNumber(column);
     return true;
 }
 
 /* static */ bool
 SavedFrame::functionDisplayNameProperty(JSContext *cx, unsigned argc, Value *vp)
 {
-    THIS_SAVEDFRAME(cx, argc, vp, "(get functionDisplayName)", NullValue(), args, frame);
+    THIS_SAVEDFRAME(cx, argc, vp, "(get functionDisplayName)", args, frame);
     RootedAtom name(cx, frame->getFunctionDisplayName());
     if (name)
         args.rval().setString(name);
     else
         args.rval().setNull();
     return true;
 }
 
 /* static */ bool
 SavedFrame::parentProperty(JSContext *cx, unsigned argc, Value *vp)
 {
-    THIS_SAVEDFRAME(cx, argc, vp, "(get parent)", NullValue(), args, frame);
-    args.rval().setObjectOrNull(GetFirstSubsumedFrame(cx, frame->getParent()));
+    THIS_SAVEDFRAME(cx, argc, vp, "(get parent)", args, frame);
+    JSSubsumesOp subsumes = cx->runtime()->securityCallbacks->subsumes;
+    JSPrincipals *principals = cx->compartment()->principals;
+
+    do
+        frame = frame->getParent();
+    while (frame && principals && subsumes &&
+           !subsumes(principals, frame->getPrincipals()));
+
+    args.rval().setObjectOrNull(frame);
     return true;
 }
 
+/* static */ const JSPropertySpec SavedFrame::properties[] = {
+    JS_PSG("source", SavedFrame::sourceProperty, 0),
+    JS_PSG("line", SavedFrame::lineProperty, 0),
+    JS_PSG("column", SavedFrame::columnProperty, 0),
+    JS_PSG("functionDisplayName", SavedFrame::functionDisplayNameProperty, 0),
+    JS_PSG("parent", SavedFrame::parentProperty, 0),
+    JS_PS_END
+};
+
 /* static */ bool
 SavedFrame::toStringMethod(JSContext *cx, unsigned argc, Value *vp)
 {
-    THIS_SAVEDFRAME(cx, argc, vp, "toString", StringValue(cx->runtime()->emptyString), args, frame);
+    THIS_SAVEDFRAME(cx, argc, vp, "toString", args, frame);
     StringBuffer sb(cx);
-    DebugOnly<JSSubsumesOp> subsumes = cx->runtime()->securityCallbacks->subsumes;
-    DebugOnly<JSPrincipals *> principals = cx->compartment()->principals;
+    JSSubsumesOp subsumes = cx->runtime()->securityCallbacks->subsumes;
+    JSPrincipals *principals = cx->compartment()->principals;
 
     do {
-        MOZ_ASSERT_IF(subsumes, (*subsumes)(principals, frame->getPrincipals()));
+        if (principals && subsumes && !subsumes(principals, frame->getPrincipals()))
+            continue;
         if (frame->isSelfHosted())
             continue;
 
         RootedAtom name(cx, frame->getFunctionDisplayName());
         if ((name && !sb.append(name))
             || !sb.append('@')
             || !sb.append(frame->getSource())
             || !sb.append(':')
             || !NumberValueToStringBuffer(cx, NumberValue(frame->getLine()), sb)
             || !sb.append(':')
             || !NumberValueToStringBuffer(cx, NumberValue(frame->getColumn()), sb)
             || !sb.append('\n'))
         {
             return false;
         }
-    } while ((frame = GetFirstSubsumedFrame(cx, frame->getParent())));
+    } while ((frame = frame->getParent()));
 
     JSString *str = sb.finishString();
     if (!str)
         return false;
     args.rval().setString(str);
     return true;
 }
 
+/* static */ const JSFunctionSpec SavedFrame::methods[] = {
+    JS_FN("constructor", SavedFrame::construct, 0, 0),
+    JS_FN("toString", SavedFrame::toStringMethod, 0, 0),
+    JS_FS_END
+};
+
 bool
 SavedStacks::init()
 {
     if (!pcLocationMap.init())
         return false;
 
     return frames.init();
 }
@@ -509,16 +455,22 @@ SavedStacks::sweep(JSRuntime *rt)
                                                     frame->getPrincipals()),
                                  ReadBarriered<SavedFrame *>(frame));
                 }
             }
         }
     }
 
     sweepPCLocationMap();
+
+    if (savedFrameProto.unbarrieredGet() &&
+        IsObjectAboutToBeFinalizedFromAnyThread(savedFrameProto.unsafeGet()))
+    {
+        savedFrameProto.set(nullptr);
+    }
 }
 
 void
 SavedStacks::trace(JSTracer *trc)
 {
     if (!pcLocationMap.initialized())
         return;
 
@@ -630,27 +582,59 @@ SavedStacks::getOrCreateSavedFrame(JSCon
         return nullptr;
 
     if (!p.add(cx, frames, lookup, frame))
         return nullptr;
 
     return frame;
 }
 
+JSObject *
+SavedStacks::getOrCreateSavedFramePrototype(JSContext *cx)
+{
+    if (savedFrameProto)
+        return savedFrameProto;
+
+    Rooted<GlobalObject *> global(cx, cx->compartment()->maybeGlobal());
+    if (!global)
+        return nullptr;
+
+    Rooted<SavedFrame *> proto(cx,
+        NewObjectWithGivenProto<SavedFrame>(cx, global->getOrCreateObjectPrototype(cx), global));
+    if (!proto
+        || !JS_DefineProperties(cx, proto, SavedFrame::properties)
+        || !JS_DefineFunctions(cx, proto, SavedFrame::methods)
+        || !FreezeObject(cx, proto))
+    {
+        return nullptr;
+    }
+
+    // The only object with the SavedFrame::class_ that doesn't have a source
+    // should be the prototype.
+    proto->setReservedSlot(SavedFrame::JSSLOT_SOURCE, NullValue());
+
+    savedFrameProto.set(proto);
+    return savedFrameProto;
+}
+
 SavedFrame *
 SavedStacks::createFrameFromLookup(JSContext *cx, SavedFrame::HandleLookup lookup)
 {
-    RootedGlobalObject global(cx, cx->global());
-    assertSameCompartment(cx, global);
-
-    RootedNativeObject proto(cx, GlobalObject::getOrCreateSavedFramePrototype(cx, global));
+    RootedObject proto(cx, getOrCreateSavedFramePrototype(cx));
     if (!proto)
         return nullptr;
+
     assertSameCompartment(cx, proto);
 
+    RootedObject global(cx, cx->compartment()->maybeGlobal());
+    if (!global)
+        return nullptr;
+
+    assertSameCompartment(cx, global);
+
     RootedObject frameObj(cx, NewObjectWithGivenProto(cx, &SavedFrame::class_, proto, global));
     if (!frameObj)
         return nullptr;
 
     RootedSavedFrame f(cx, &frameObj->as<SavedFrame>());
     f->initFromLookup(lookup);
 
     if (!FreezeObject(cx, frameObj))
--- a/js/src/vm/SavedStacks.h
+++ b/js/src/vm/SavedStacks.h
@@ -9,32 +9,26 @@
 
 #include "jscntxt.h"
 #include "jsmath.h"
 #include "js/HashTable.h"
 #include "vm/Stack.h"
 
 namespace js {
 
-class SavedFrame;
-typedef JS::Handle<SavedFrame*> HandleSavedFrame;
-typedef JS::MutableHandle<SavedFrame*> MutableHandleSavedFrame;
-typedef JS::Rooted<SavedFrame*> RootedSavedFrame;
-
 class SavedFrame : public NativeObject {
     friend class SavedStacks;
 
   public:
     static const Class          class_;
     static void finalize(FreeOp *fop, JSObject *obj);
-    static const JSPropertySpec protoAccessors[];
-    static const JSFunctionSpec protoFunctions[];
-    static const JSFunctionSpec staticFunctions[];
 
     // Prototype methods and properties to be exposed to JS.
+    static const JSPropertySpec properties[];
+    static const JSFunctionSpec methods[];
     static bool construct(JSContext *cx, unsigned argc, Value *vp);
     static bool sourceProperty(JSContext *cx, unsigned argc, Value *vp);
     static bool lineProperty(JSContext *cx, unsigned argc, Value *vp);
     static bool columnProperty(JSContext *cx, unsigned argc, Value *vp);
     static bool functionDisplayNameProperty(JSContext *cx, unsigned argc, Value *vp);
     static bool parentProperty(JSContext *cx, unsigned argc, Value *vp);
     static bool toStringMethod(JSContext *cx, unsigned argc, Value *vp);
 
@@ -54,17 +48,16 @@ class SavedFrame : public NativeObject {
     typedef HashSet<js::ReadBarriered<SavedFrame *>,
                     HashPolicy,
                     SystemAllocPolicy> Set;
 
     class AutoLookupRooter;
     class HandleLookup;
 
   private:
-    static bool finishSavedFrameInit(JSContext *cx, HandleObject ctor, HandleObject proto);
     void initFromLookup(HandleLookup lookup);
 
     enum {
         // The reserved slots in the SavedFrame class.
         JSSLOT_SOURCE,
         JSSLOT_LINE,
         JSSLOT_COLUMN,
         JSSLOT_FUNCTIONDISPLAYNAME,
@@ -79,23 +72,26 @@ class SavedFrame : public NativeObject {
     // Because we hash the parent pointer, we need to rekey a saved frame
     // whenever its parent was relocated by the GC. However, the GC doesn't
     // notify us when this occurs. As a work around, we keep a duplicate copy of
     // the parent pointer as a private value in a reserved slot. Whenever the
     // private value parent pointer doesn't match the regular parent pointer, we
     // know that GC moved the parent and we need to update our private value and
     // rekey the saved frame in its hash set. These two methods are helpers for
     // this process.
-    bool parentMoved();
-    void updatePrivateParent();
+    bool         parentMoved();
+    void         updatePrivateParent();
 
-    static bool checkThis(JSContext *cx, CallArgs &args, const char *fnName,
-                          MutableHandleSavedFrame frame);
+    static SavedFrame *checkThis(JSContext *cx, CallArgs &args, const char *fnName);
 };
 
+typedef JS::Handle<SavedFrame*> HandleSavedFrame;
+typedef JS::MutableHandle<SavedFrame*> MutableHandleSavedFrame;
+typedef JS::Rooted<SavedFrame*> RootedSavedFrame;
+
 struct SavedFrame::HashPolicy
 {
     typedef SavedFrame::Lookup               Lookup;
     typedef PointerHasher<SavedFrame *, 3>   SavedFramePtrHasher;
     typedef PointerHasher<JSPrincipals *, 3> JSPrincipalsPtrHasher;
 
     static HashNumber hash(const Lookup &lookup);
     static bool       match(SavedFrame *existing, const Lookup &lookup);
@@ -105,16 +101,17 @@ struct SavedFrame::HashPolicy
 };
 
 class SavedStacks {
     friend bool SavedStacksMetadataCallback(JSContext *cx, JSObject **pmetadata);
 
   public:
     SavedStacks()
       : frames(),
+        savedFrameProto(nullptr),
         allocationSamplingProbability(1.0),
         allocationSkipCount(0),
         // XXX: Initialize the RNG state to 0 so that random_initSeed is lazily
         // called for us on the first call to random_next (via
         // random_nextDouble). We need to do this here because /dev/urandom
         // doesn't exist on Android, resulting in assertion failures.
         rngState(0)
     { }
@@ -127,23 +124,27 @@ class SavedStacks {
     uint32_t count();
     void     clear();
     void     setRNGState(uint64_t state) { rngState = state; }
 
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
   private:
     SavedFrame::Set     frames;
+    ReadBarrieredObject savedFrameProto;
     double              allocationSamplingProbability;
     uint32_t            allocationSkipCount;
     uint64_t            rngState;
 
     bool       insertFrames(JSContext *cx, FrameIter &iter, MutableHandleSavedFrame frame,
                             unsigned maxFrameCount = 0);
     SavedFrame *getOrCreateSavedFrame(JSContext *cx, SavedFrame::HandleLookup lookup);
+    // |SavedFrame.prototype| is created lazily and held weakly. It should only
+    // be accessed through this method.
+    JSObject   *getOrCreateSavedFramePrototype(JSContext *cx);
     SavedFrame *createFrameFromLookup(JSContext *cx, SavedFrame::HandleLookup lookup);
     void       chooseSamplingProbability(JSContext* cx);
 
     // Cache for memoizing PCToLineNumber lookups.
 
     struct PCKey {
         PCKey(JSScript *script, jsbytecode *pc) : script(script), pc(pc) { }
 
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -502,17 +502,17 @@ public:
      */
 
 public:
     bool GetDoingFinalization() const {return mDoingFinalization;}
 
     // Mapping of often used strings to jsid atoms that live 'forever'.
     //
     // To add a new string: add to this list and to XPCJSRuntime::mStrings
-    // at the top of XPCJSRuntime.cpp
+    // at the top of xpcjsruntime.cpp
     enum {
         IDX_CONSTRUCTOR             = 0 ,
         IDX_TO_STRING               ,
         IDX_TO_SOURCE               ,
         IDX_LAST_RESULT             ,
         IDX_RETURN_CODE             ,
         IDX_VALUE                   ,
         IDX_QUERY_INTERFACE         ,
deleted file mode 100644
--- a/js/xpconnect/tests/unit/test_xray_SavedFrame.js
+++ /dev/null
@@ -1,108 +0,0 @@
-// Bug 1117242: Test calling SavedFrame getters from globals that don't subsume
-// that frame's principals.
-
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-const Cu = Components.utils;
-
-Cu.import("resource://gre/modules/jsdebugger.jsm");
-addDebuggerToGlobal(this);
-
-const lowP = Cc["@mozilla.org/nullprincipal;1"].createInstance(Ci.nsIPrincipal);
-const midP = [lowP, "http://other.com"];
-const highP = Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal);
-
-const low  = new Cu.Sandbox(lowP);
-const mid  = new Cu.Sandbox(midP);
-const high = new Cu.Sandbox(highP);
-
-function run_test() {
-  // Test that the priveleged view of a SavedFrame from a subsumed compartment
-  // is the same view that the subsumed compartment gets. Create the following
-  // chain of function calls (with some intermediate system-principaled frames
-  // due to implementation):
-  //
-  //     low.lowF -> mid.midF -> high.highF -> high.saveStack
-  //
-  // Where high.saveStack gets monkey patched to create stacks in each of our
-  // sandboxes.
-
-  Cu.evalInSandbox("function highF() { return saveStack(); }", high);
-
-  mid.highF = () => high.highF();
-  Cu.evalInSandbox("function midF() { return highF(); }", mid);
-
-  low.midF = () => mid.midF();
-  Cu.evalInSandbox("function lowF() { return midF(); }", low);
-
-  const expected = [
-    {
-      sandbox: low,
-      frames: ["lowF"],
-    },
-    {
-      sandbox: mid,
-      frames: ["midF", "lowF"],
-    },
-    {
-      sandbox: high,
-      frames: ["getSavedFrameInstanceFromSandbox",
-               "saveStack",
-               "highF",
-               "run_test/mid.highF",
-               "midF",
-               "run_test/low.midF",
-               "lowF",
-               "run_test",
-               "_execute_test",
-               null],
-    }
-  ];
-
-  for (let { sandbox, frames } of expected) {
-    high.saveStack = function saveStack() {
-      return getSavedFrameInstanceFromSandbox(sandbox);
-    };
-
-    const xrayStack = low.lowF();
-    equal(xrayStack.functionDisplayName, "getSavedFrameInstanceFromSandbox",
-          "Xrays should always be able to see everything.");
-
-    let waived = Cu.waiveXrays(xrayStack);
-    do {
-      ok(frames.length,
-         "There should still be more expected frames while we have actual frames.");
-      equal(waived.functionDisplayName, frames.shift(),
-            "The waived wrapper should give us the stack's compartment's view.");
-      waived = waived.parent;
-    } while (waived);
-  }
-}
-
-// Get a SavedFrame instance from inside the given sandbox.
-//
-// We can't use Cu.getJSTestingFunctions().saveStack() because Cu isn't
-// available to sandboxes that don't have the system principal. The easiest way
-// to get the SavedFrame is to use the Debugger API to track allocation sites
-// and then do an allocation.
-function getSavedFrameInstanceFromSandbox(sandbox) {
-  const dbg = new Debugger(sandbox);
-
-  dbg.memory.trackingAllocationSites = true;
-  Cu.evalInSandbox("new Object", sandbox);
-  const allocs = dbg.memory.drainAllocationsLog();
-  dbg.memory.trackingAllocationSites = false;
-
-  ok(allocs[0], "We should observe the allocation");
-  const { frame } = allocs[0];
-
-  if (sandbox !== high) {
-    ok(Cu.isXrayWrapper(frame), "`frame` should be an xray...");
-    equal(Object.prototype.toString.call(Cu.waiveXrays(frame)),
-          "[object SavedFrame]",
-          "...and that xray should wrap a SavedFrame");
-  }
-
-  return frame;
-}
-
--- a/js/xpconnect/tests/unit/xpcshell.ini
+++ b/js/xpconnect/tests/unit/xpcshell.ini
@@ -103,9 +103,8 @@ head = head_watchdog.js
 [test_watchdog_toggle.js]
 head = head_watchdog.js
 [test_watchdog_default.js]
 head = head_watchdog.js
 [test_watchdog_hibernate.js]
 head = head_watchdog.js
 [test_writeToGlobalPrototype.js]
 [test_xrayed_iterator.js]
-[test_xray_SavedFrame.js]
\ No newline at end of file
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -78,17 +78,16 @@ IsJSXraySupported(JSProtoKey key)
     if (IsErrorObjectKey(key))
         return true;
     switch (key) {
       case JSProto_Date:
       case JSProto_Object:
       case JSProto_Array:
       case JSProto_Function:
       case JSProto_TypedArray:
-      case JSProto_SavedFrame:
         return true;
       default:
         return false;
     }
 }
 
 XrayType
 GetXrayType(JSObject *obj)