Bug 1007383 - Include asm.js frames in JS::DescribeStack (r=jandem,robcee)
authorLuke Wagner <luke@mozilla.com>
Thu, 15 May 2014 12:30:45 -0500
changeset 183304 b8533fbcc8116d7bf6888756961d979d2d5158aa
parent 183303 2b99b42f1337a55f81ba879eb773288776f72be8
child 183398 b35a9f3c3ebe18b1604b1be273beb40ee4bfd14f
push id43527
push userlwagner@mozilla.com
push dateThu, 15 May 2014 23:15:11 +0000
treeherdermozilla-inbound@b8533fbcc811 [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.isNonEvalFunctionFrame())
+        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);