Bug 1423937: Add FrameIter::hasInitialEnvironment to guard FrameIter::callObj r=tcampbell
authorIain Ireland <iireland@mozilla.com>
Thu, 11 Oct 2018 18:12:28 +0000
changeset 496508 5caf51de3bc86a59b0c6ce236456ffc5685bcf06
parent 496507 a2a823a4e70774370332e612a7d12f9aa5456673
child 496509 d1c5f2c89b170fc8f60e1b81da1f22d5218dd706
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstcampbell
bugs1423937
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1423937: Add FrameIter::hasInitialEnvironment to guard FrameIter::callObj r=tcampbell Differential Revision: https://phabricator.services.mozilla.com/D8395
js/src/jit-test/tests/arguments/bug1423937.js
js/src/jit/JSJitFrameIter.h
js/src/jit/RematerializedFrame.cpp
js/src/jsfriendapi.cpp
js/src/vm/Stack.cpp
js/src/vm/Stack.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/arguments/bug1423937.js
@@ -0,0 +1,17 @@
+// |jit-test| exitstatus: 6;
+
+var global = 0;
+setInterruptCallback(function() {
+    foo("A");
+});
+function foo(x) {
+    for (var i = 0; i < 1000; i++) {
+	var stack = getBacktrace({args: true});
+    }
+    if (global > 2) return;
+    global++;
+    interruptIf(true);
+    foo("B");
+    (function()  { g = x;});
+}
+foo("C");
--- a/js/src/jit/JSJitFrameIter.h
+++ b/js/src/jit/JSJitFrameIter.h
@@ -839,22 +839,23 @@ class InlineFrameIterator
     }
     SnapshotIterator snapshotIterator() const {
         return si_;
     }
     bool isFunctionFrame() const;
     bool isModuleFrame() const;
     bool isConstructing() const;
 
-    JSObject* environmentChain(MaybeReadFallback& fallback) const {
+    JSObject* environmentChain(MaybeReadFallback& fallback,
+                               bool* hasInitialEnvironment = nullptr) const {
         SnapshotIterator s(si_);
 
         // envChain
         Value v = s.maybeRead(fallback);
-        return computeEnvironmentChain(v, fallback);
+        return computeEnvironmentChain(v, fallback, hasInitialEnvironment);
     }
 
     Value thisArgument(MaybeReadFallback& fallback) const {
         SnapshotIterator s(si_);
 
         // envChain
         s.skip();
 
--- a/js/src/jit/RematerializedFrame.cpp
+++ b/js/src/jit/RematerializedFrame.cpp
@@ -127,16 +127,17 @@ RematerializedFrame::FreeInVector(GCVect
     }
     frames.clear();
 }
 
 CallObject&
 RematerializedFrame::callObj() const
 {
     MOZ_ASSERT(hasInitialEnvironment());
+    MOZ_ASSERT(callee()->needsCallObject());
 
     JSObject* env = environmentChain();
     while (!env->is<CallObject>()) {
         env = env->enclosingEnvironment();
     }
     return env->as<CallObject>();
 }
 
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -919,17 +919,21 @@ FormatFrame(JSContext* cx, const FrameIt
     }
 
     if (showArgs && iter.hasArgs()) {
         PositionalFormalParameterIter fi(script);
         bool first = true;
         for (unsigned i = 0; i < iter.numActualArgs(); i++) {
             RootedValue arg(cx);
             if (i < iter.numFormalArgs() && fi.closedOver()) {
-                arg = iter.callObj(cx).aliasedBinding(fi);
+                if (iter.hasInitialEnvironment(cx)) {
+                    arg = iter.callObj(cx).aliasedBinding(fi);
+                } else {
+                    arg = MagicValue(JS_OPTIMIZED_OUT);
+                }
             } else if (iter.hasUsableAbstractFramePtr()) {
                 if (script->analyzedArgsUsage() &&
                     script->argsObjAliasesFormals() &&
                     iter.hasArgsObj())
                 {
                     arg = iter.argsObj().arg(i);
                 } else {
                     arg = iter.unaliasedActual(i, DONT_CHECK_ALIASING);
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -1415,20 +1415,40 @@ FrameIter::environmentChain(JSContext* c
         MOZ_ASSERT(isWasm());
         return wasmFrame().debugFrame()->environmentChain();
       case INTERP:
         return interpFrame()->environmentChain();
     }
     MOZ_CRASH("Unexpected state");
 }
 
+bool
+FrameIter::hasInitialEnvironment(JSContext *cx) const {
+    if (hasUsableAbstractFramePtr()) {
+        return abstractFramePtr().hasInitialEnvironment();
+    }
+
+    if (isWasm()) {
+        // See JSFunction::needsFunctionEnvironmentObjects().
+        return false;
+    }
+
+    MOZ_ASSERT(isJSJit() && isIonScripted());
+    bool hasInitialEnv = false;
+    jit::MaybeReadFallback recover(cx, activation()->asJit(), &jsJitFrame());
+    ionInlineFrames_.environmentChain(recover, &hasInitialEnv);
+
+    return hasInitialEnv;
+}
+
 CallObject&
 FrameIter::callObj(JSContext* cx) const
 {
     MOZ_ASSERT(calleeTemplate()->needsCallObject());
+    MOZ_ASSERT(hasInitialEnvironment(cx));
 
     JSObject* pobj = environmentChain(cx);
     while (!pobj->is<CallObject>()) {
         pobj = pobj->enclosingEnvironment();
     }
     return pobj->as<CallObject>();
 }
 
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -2146,16 +2146,17 @@ class FrameIter
     bool        matchCallee(JSContext* cx, HandleFunction fun) const;
 
     unsigned    numActualArgs() const;
     unsigned    numFormalArgs() const;
     Value       unaliasedActual(unsigned i, MaybeCheckAliasing = CHECK_ALIASING) const;
     template <class Op> inline void unaliasedForEachActual(JSContext* cx, Op op);
 
     JSObject*  environmentChain(JSContext* cx) const;
+    bool hasInitialEnvironment(JSContext* cx) const;
     CallObject& callObj(JSContext* cx) const;
 
     bool        hasArgsObj() const;
     ArgumentsObject& argsObj() const;
 
     // Get the original |this| value passed to this function. May not be the
     // actual this-binding (for instance, derived class constructors will
     // change their this-value later and non-strict functions will box