Bug 583083 - Use the '//# sourceURL=display.js' directive's filename in Error.prototype.stack; r=shu
authorNick Fitzgerald <fitzgen@gmail.com>
Wed, 15 Oct 2014 18:42:00 +0200
changeset 210680 3dbe5e1f6a7d
parent 210679 b845c0769a85
child 210681 c4d577ba087a
push id27657
push usercbook@mozilla.com
push date2014-10-16 14:07 +0000
treeherdermozilla-central@77f3ca1fe052 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshu
bugs583083
milestone36.0a1
Bug 583083 - Use the '//# sourceURL=display.js' directive's filename in Error.prototype.stack; r=shu
js/src/jit-test/tests/basic/display-url-in-stack-trace.js
js/src/jsexn.cpp
js/src/vm/Stack.cpp
js/src/vm/Stack.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/display-url-in-stack-trace.js
@@ -0,0 +1,27 @@
+eval(`
+  function a() {
+    return b();
+  }
+  //# sourceURL=source-a.js
+`);
+
+eval(`
+  function b() {
+    return c();
+  }
+  //# sourceURL=source-b.js
+`);
+
+eval(`
+  function c() {
+    return Error().stack;
+  }
+  //# sourceURL=source-c.js
+`);
+
+let filenames = a().split(/\n/)
+                   .map(f => f.slice(f.indexOf("@") + 1, f.indexOf(":")));
+print(filenames.join("\n"));
+assertEq(filenames[0], "source-c.js");
+assertEq(filenames[1], "source-b.js");
+assertEq(filenames[2], "source-a.js");
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -284,21 +284,27 @@ js::ComputeStackString(JSContext *cx)
             if (atom && !sb.append(atom))
                 return nullptr;
 
             /* Next a @ separating function name from source location. */
             if (!sb.append('@'))
                 return nullptr;
 
             /* Now the filename. */
-            const char *cfilename = i.scriptFilename();
-            if (!cfilename)
-                cfilename = "";
-            if (!sb.append(cfilename, strlen(cfilename)))
-                return nullptr;
+
+            /* First, try the `//# sourceURL=some-display-url.js` directive. */
+            if (const char16_t *display = i.scriptDisplayURL()) {
+                if (!sb.append(display, js_strlen(display)))
+                    return nullptr;
+            }
+            /* Second, try the actual filename. */
+            else if (const char *filename = i.scriptFilename()) {
+                if (!sb.append(filename, strlen(filename)))
+                    return nullptr;
+            }
 
             uint32_t column = 0;
             uint32_t line = i.computeLine(&column);
             // Now the line number
             if (!sb.append(':') || !NumberValueToStringBuffer(cx, NumberValue(line), sb))
                 return nullptr;
 
             // Finally, : followed by the column number (1-based, as in other browsers)
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -952,16 +952,23 @@ FrameIter::scriptFilename() const
         return script()->filename();
       case ASMJS:
         return data_.activations_->asAsmJS()->module().scriptSource()->filename();
     }
 
     MOZ_CRASH("Unexpected state");
 }
 
+const char16_t *
+FrameIter::scriptDisplayURL() const
+{
+    ScriptSource *ss = scriptSource();
+    return ss->hasDisplayURL() ? ss->displayURL() : nullptr;
+}
+
 unsigned
 FrameIter::computeLine(uint32_t *column) const
 {
     switch (data_.state_) {
       case DONE:
         break;
       case INTERP:
       case JIT:
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -1639,22 +1639,19 @@ class FrameIter
 
     bool isFunctionFrame() const;
     bool isGlobalFrame() const;
     bool isEvalFrame() const;
     bool isNonEvalFunctionFrame() const;
     bool isGeneratorFrame() const;
     bool hasArgs() const { return isNonEvalFunctionFrame(); }
 
-    /*
-     * Get an abstract frame pointer dispatching to either an interpreter,
-     * baseline, or rematerialized optimized frame.
-     */
     ScriptSource *scriptSource() const;
     const char *scriptFilename() const;
+    const char16_t *scriptDisplayURL() const;
     unsigned computeLine(uint32_t *column = nullptr) const;
     JSAtom *functionDisplayAtom() const;
     bool mutedErrors() const;
 
     bool hasScript() const { return !isAsmJS(); }
 
     // -----------------------------------------------------------
     // The following functions can only be called when hasScript()