Bug 1007383 - Include asm.js frames in JS::DescribeStack (r=jandem,robcee)
☠☠ backed out by 904012cb4646 ☠ ☠
authorLuke Wagner <luke@mozilla.com>
Thu, 15 May 2014 12:30:45 -0500
changeset 202615 ce34c13388f2e45d763ad017b757d4ce965bd93f
parent 202614 928020e0a91e5a2e72cd19eab1914f89f88ce450
child 202616 904012cb4646fa029951c83235d4d095a86532fd
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)
reviewersjandem, robcee
bugs1007383
milestone32.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 1007383 - Include asm.js frames in JS::DescribeStack (r=jandem,robcee)
js/public/OldDebugAPI.h
js/src/vm/OldDebugAPI.cpp
toolkit/devtools/webconsole/test/test_consoleapi.html
--- a/js/public/OldDebugAPI.h
+++ b/js/public/OldDebugAPI.h
@@ -19,64 +19,78 @@
 #include "js/CallArgs.h"
 #include "js/TypeDecls.h"
 
 class JSAtom;
 class JSFreeOp;
 
 namespace js {
 class InterpreterFrame;
-class ScriptFrameIter;
+class FrameIter;
+class ScriptSource;
 }
 
 // Raw JSScript* because this needs to be callable from a signal handler.
 extern JS_PUBLIC_API(unsigned)
 JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc);
 
 extern JS_PUBLIC_API(const char *)
 JS_GetScriptFilename(JSScript *script);
 
 namespace JS {
 
 class FrameDescription
 {
   public:
-    explicit FrameDescription(const js::ScriptFrameIter& iter);
+    explicit FrameDescription(const js::FrameIter& iter);
+    FrameDescription(const FrameDescription &rhs);
+    ~FrameDescription();
 
     unsigned lineno() {
-        if (!linenoComputed) {
+        if (!linenoComputed_) {
             lineno_ = JS_PCToLineNumber(nullptr, script_, pc_);
-            linenoComputed = true;
+            linenoComputed_ = true;
         }
         return lineno_;
     }
 
     const char *filename() const {
-        return JS_GetScriptFilename(script_);
+        return filename_;
     }
 
     JSFlatString *funDisplayName() const {
         return funDisplayName_ ? JS_ASSERT_STRING_IS_FLAT(funDisplayName_) : nullptr;
     }
 
     // Both these locations should be traced during GC but otherwise not used;
     // they are implementation details.
     Heap<JSScript*> &markedLocation1() {
         return script_;
     }
     Heap<JSString*> &markedLocation2() {
         return funDisplayName_;
     }
 
   private:
+    void operator=(const FrameDescription &) MOZ_DELETE;
+
+    // These fields are always initialized:
+    Heap<JSString*> funDisplayName_;
+    const char *filename_;
+
+    // One of script_ xor scriptSource_ is non-null.
     Heap<JSScript*> script_;
-    Heap<JSString*> funDisplayName_;
+    js::ScriptSource *scriptSource_;
+
+    // For script_-having frames, lineno_ is lazily computed as an optimization.
+    bool linenoComputed_;
+    unsigned lineno_;
+
+    // pc_ is non-null iff script_ is non-null. If !pc_, linenoComputed_ = true.
     jsbytecode *pc_;
-    unsigned lineno_;
-    bool linenoComputed;
 };
 
 struct StackDescription
 {
     unsigned nframes;
     FrameDescription *frames;
 };
 
--- a/js/src/vm/OldDebugAPI.cpp
+++ b/js/src/vm/OldDebugAPI.cpp
@@ -892,34 +892,65 @@ js_CallContextDebugHandler(JSContext *cx
     }
 }
 
 /*
  * A contructor that crates a FrameDescription from a ScriptFrameIter, to avoid
  * constructing a FrameDescription on the stack just to append it to a vector.
  * FrameDescription contains Heap<T> fields that should not live on the stack.
  */
-JS::FrameDescription::FrameDescription(const ScriptFrameIter& iter)
-  : script_(iter.script()),
-    funDisplayName_(nullptr),
-    pc_(iter.pc()),
-    linenoComputed(false)
+JS::FrameDescription::FrameDescription(const FrameIter& iter)
+  : scriptSource_(nullptr),
+    linenoComputed_(false),
+    pc_(nullptr)
 {
-    if (JSFunction *fun = iter.maybeCallee())
-        funDisplayName_ = fun->displayAtom();
+    if (iter.isFunctionFrame())
+        funDisplayName_ = iter.functionDisplayAtom();
+
+    if (iter.hasScript()) {
+        script_ = iter.script();
+        pc_ = iter.pc();
+        filename_ = script_->filename();
+    } else {
+        scriptSource_ = iter.scriptSource();
+        scriptSource_->incref();
+        filename_ = scriptSource_->filename();
+        lineno_ = iter.computeLine();
+        linenoComputed_ = true;
+    }
+}
+
+JS::FrameDescription::FrameDescription(const FrameDescription &rhs)
+  : funDisplayName_(rhs.funDisplayName_),
+    filename_(rhs.filename_),
+    script_(rhs.script_),
+    scriptSource_(rhs.scriptSource_),
+    linenoComputed_(rhs.linenoComputed_),
+    lineno_(rhs.lineno_),
+    pc_(rhs.pc_)
+{
+    if (scriptSource_)
+        scriptSource_->incref();
+}
+
+
+JS::FrameDescription::~FrameDescription()
+{
+    if (scriptSource_)
+        scriptSource_->decref();
 }
 
 JS_PUBLIC_API(JS::StackDescription *)
 JS::DescribeStack(JSContext *cx, unsigned maxFrames)
 {
     Vector<FrameDescription> frames(cx);
 
-    NonBuiltinScriptFrameIter i(cx, ScriptFrameIter::ALL_CONTEXTS,
-                                ScriptFrameIter::GO_THROUGH_SAVED,
-                                cx->compartment()->principals);
+    NonBuiltinFrameIter i(cx, FrameIter::ALL_CONTEXTS,
+                          FrameIter::GO_THROUGH_SAVED,
+                          cx->compartment()->principals);
     for ( ; !i.done(); ++i) {
         if (!frames.append(i))
             return nullptr;
         if (frames.length() == maxFrames)
             break;
     }
 
     JS::StackDescription *desc = js_new<JS::StackDescription>();
--- a/toolkit/devtools/webconsole/test/test_consoleapi.html
+++ b/toolkit/devtools/webconsole/test/test_consoleapi.html
@@ -23,16 +23,28 @@ function doConsoleCalls(aState)
   top.console.log("foobarBaz-log", undefined);
   top.console.info("foobarBaz-info", null);
   top.console.warn("foobarBaz-warn", top.document.documentElement);
   top.console.debug(null);
   top.console.trace();
   top.console.dir(top.document, top.location);
   top.console.log("foo", longString);
 
+  function fromAsmJS() {
+      top.console.error("foobarBaz-asmjs-error", undefined);
+  }
+
+  (function(global, foreign) {
+    "use asm";
+    var fromAsmJS = foreign.fromAsmJS;
+    function inAsmJS2() { fromAsmJS() }
+    function inAsmJS1() { inAsmJS2() }
+    return inAsmJS1
+  })(null, { fromAsmJS:fromAsmJS })();
+
   expectedConsoleCalls = [
     {
       level: "log",
       filename: /test_consoleapi/,
       functionName: "doConsoleCalls",
       timeStamp: /^\d+$/,
       arguments: ["foobarBaz-log", { type: "undefined" }],
     },
@@ -102,16 +114,46 @@ function doConsoleCalls(aState)
           type: "longString",
           initial: longString.substring(0,
             DebuggerServer.LONG_STRING_INITIAL_LENGTH),
           length: longString.length,
           actor: /[a-z]/,
         },
       ],
     },
+    {
+      level: "error",
+      filename: /test_consoleapi/,
+      functionName: "fromAsmJS",
+      timeStamp: /^\d+$/,
+      arguments: ["foobarBaz-asmjs-error", { type: "undefined" }],
+
+      stacktrace: [
+        {
+          filename: /test_consoleapi/,
+          functionName: "fromAsmJS",
+        },
+        {
+          filename: /test_consoleapi/,
+          functionName: "inAsmJS2",
+        },
+        {
+          filename: /test_consoleapi/,
+          functionName: "inAsmJS1",
+        },
+        {
+          filename: /test_consoleapi/,
+          functionName: "doConsoleCalls",
+        },
+        {
+          filename: /test_consoleapi/,
+          functionName: "onAttach",
+        },
+      ],
+    },
   ];
 }
 
 function startTest()
 {
   removeEventListener("load", startTest);
 
   attachConsole(["ConsoleAPI"], onAttach, true);