Make Scratchpad's evaluations that throw faster (bug 1109692). r=fitzgen
authorPanos Astithas <past@mozilla.com>
Wed, 10 Dec 2014 09:00:17 +0200
changeset 219136 7ff9e2fcc572f447524f82aa0234a4c895439aff
parent 219135 a4f02951aea9367d0021b079f6dcddc21aece443
child 219137 0f7e5b1c4b622e8ad431630f24c7d1ac4f6458a8
push id27956
push userkwierso@gmail.com
push dateFri, 12 Dec 2014 00:47:19 +0000
treeherdermozilla-central@32a2c5bd2f68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfitzgen
bugs1109692
milestone37.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
Make Scratchpad's evaluations that throw faster (bug 1109692). r=fitzgen
browser/devtools/scratchpad/scratchpad.js
browser/devtools/scratchpad/test/browser_scratchpad_display_non_error_exceptions.js
browser/devtools/scratchpad/test/browser_scratchpad_display_outputs_errors.js
browser/devtools/scratchpad/test/head.js
--- a/browser/devtools/scratchpad/scratchpad.js
+++ b/browser/devtools/scratchpad/scratchpad.js
@@ -511,17 +511,17 @@ var Scratchpad = {
   {
     let deferred = promise.defer();
     let reject = aReason => deferred.reject(aReason);
 
     this.execute().then(([aString, aError, aResult]) => {
       let resolve = () => deferred.resolve([aString, aError, aResult]);
 
       if (aError) {
-        this.writeAsErrorComment(aError.exception).then(resolve, reject);
+        this.writeAsErrorComment(aError).then(resolve, reject);
       }
       else {
         this.editor.dropSelection();
         resolve();
       }
     }, reject);
 
     return deferred.promise;
@@ -538,17 +538,17 @@ var Scratchpad = {
   {
     let deferred = promise.defer();
     let reject = aReason => deferred.reject(aReason);
 
     this.execute().then(([aString, aError, aResult]) => {
       let resolve = () => deferred.resolve([aString, aError, aResult]);
 
       if (aError) {
-        this.writeAsErrorComment(aError.exception).then(resolve, reject);
+        this.writeAsErrorComment(aError).then(resolve, reject);
       }
       else {
         this.editor.dropSelection();
         this.sidebar.open(aString, aResult).then(resolve, reject);
       }
     }, reject);
 
     return deferred.promise;
@@ -603,17 +603,17 @@ var Scratchpad = {
   {
     let deferred = promise.defer();
     let reject = aReason => deferred.reject(aReason);
 
     this.execute().then(([aString, aError, aResult]) => {
       let resolve = () => deferred.resolve([aString, aError, aResult]);
 
       if (aError) {
-        this.writeAsErrorComment(aError.exception).then(resolve, reject);
+        this.writeAsErrorComment(aError).then(resolve, reject);
       }
       else if (VariablesView.isPrimitive({ value: aResult })) {
         this._writePrimitiveAsComment(aResult).then(resolve, reject);
       }
       else {
         let objectClient = new ObjectClient(this.debuggerClient, aResult);
         objectClient.getDisplayString(aResponse => {
           if (aResponse.error) {
@@ -664,17 +664,17 @@ var Scratchpad = {
     const onReply = ({ data }) => {
       if (data.id !== id) {
         return;
       }
       this.prettyPrintWorker.removeEventListener("message", onReply, false);
 
       if (data.error) {
         let errorString = DevToolsUtils.safeErrorString(data.error);
-        this.writeAsErrorComment(errorString);
+        this.writeAsErrorComment({ exception: errorString });
         deferred.reject(errorString);
       } else {
         this.editor.setText(data.code);
         deferred.resolve(data.code);
       }
     };
 
     this.prettyPrintWorker.addEventListener("message", onReply, false);
@@ -691,17 +691,17 @@ var Scratchpad = {
   /**
    * Parse the text and return an AST. If we can't parse it, write an error
    * comment and return false.
    */
   _parseText: function SP__parseText(aText) {
     try {
       return Reflect.parse(aText);
     } catch (e) {
-      this.writeAsErrorComment(DevToolsUtils.safeErrorString(e));
+      this.writeAsErrorComment({ exception: DevToolsUtils.safeErrorString(e) });
       return false;
     }
   },
 
   /**
    * Determine if the given AST node location contains the given cursor
    * position.
    *
@@ -908,43 +908,54 @@ var Scratchpad = {
 
     let [ from, to ] = this.editor.getPosition(text.length, (text + value).length);
     this.editor.setSelection(from, to);
   },
 
   /**
    * Write out an error at the current insertion point as a block comment
    * @param object aValue
-   *        The Error object to write out the message and stack trace
+   *        The error object to write out the message and stack trace. It must
+   *        contain an |exception| property with the actual error thrown, but it
+   *        will often be the entire response of an evaluateJS request.
    * @return Promise
    *         The promise that indicates when writing the comment completes.
    */
   writeAsErrorComment: function SP_writeAsErrorComment(aError)
   {
     let deferred = promise.defer();
 
-    if (VariablesView.isPrimitive({ value: aError })) {
-      let type = aError.type;
+    if (VariablesView.isPrimitive({ value: aError.exception })) {
+      let error = aError.exception;
+      let type = error.type;
       if (type == "undefined" ||
           type == "null" ||
           type == "Infinity" ||
           type == "-Infinity" ||
           type == "NaN" ||
           type == "-0") {
         deferred.resolve(type);
       }
       else if (type == "longString") {
-        deferred.resolve(aError.initial + "\u2026");
+        deferred.resolve(error.initial + "\u2026");
       }
       else {
-        deferred.resolve(aError);
+        deferred.resolve(error);
       }
-    }
-    else {
-      let objectClient = new ObjectClient(this.debuggerClient, aError);
+    } else if ("preview" in aError.exception) {
+      let error = aError.exception;
+      let stack = this._constructErrorStack(error.preview);
+      if (typeof aError.exceptionMessage == "string") {
+        deferred.resolve(aError.exceptionMessage + stack);
+      } else {
+        deferred.resolve(stack);
+      }
+    } else {
+      // If there is no preview information, we need to ask the server for more.
+      let objectClient = new ObjectClient(this.debuggerClient, aError.exception);
       objectClient.getPrototypeAndProperties(aResponse => {
         if (aResponse.error) {
           deferred.reject(aResponse);
           return;
         }
 
         let { ownProperties, safeGetterValues } = aResponse;
         let error = Object.create(null);
@@ -953,32 +964,17 @@ var Scratchpad = {
         for (let key of Object.keys(safeGetterValues)) {
           error[key] = safeGetterValues[key].getterValue;
         }
 
         for (let key of Object.keys(ownProperties)) {
           error[key] = ownProperties[key].value;
         }
 
-        // Assemble the best possible stack we can given the properties we have.
-        let stack;
-        if (typeof error.stack == "string" && error.stack) {
-          stack = error.stack;
-        }
-        else if (typeof error.fileName == "string") {
-          stack = "@" + error.fileName;
-          if (typeof error.lineNumber == "number") {
-            stack += ":" + error.lineNumber;
-          }
-        }
-        else if (typeof error.lineNumber == "number") {
-          stack = "@" + error.lineNumber;
-        }
-
-        stack = stack ? "\n" + stack.replace(/\n$/, "") : "";
+        let stack = this._constructErrorStack(error);
 
         if (typeof error.message == "string") {
           deferred.resolve(error.message + stack);
         }
         else {
           objectClient.getDisplayString(aResponse => {
             if (aResponse.error) {
               deferred.reject(aResponse);
@@ -995,16 +991,47 @@ var Scratchpad = {
     }
 
     return deferred.promise.then(aMessage => {
       console.error(aMessage);
       this.writeAsComment("Exception: " + aMessage);
     });
   },
 
+  /**
+   * Assembles the best possible stack from the properties of the provided
+   * error.
+   */
+  _constructErrorStack(error) {
+    let stack;
+    if (typeof error.stack == "string" && error.stack) {
+      stack = error.stack;
+    } else if (typeof error.fileName == "string") {
+      stack = "@" + error.fileName;
+      if (typeof error.lineNumber == "number") {
+        stack += ":" + error.lineNumber;
+      }
+    } else if (typeof error.filename == "string") {
+      stack = "@" + error.filename;
+      if (typeof error.lineNumber == "number") {
+        stack += ":" + error.lineNumber;
+        if (typeof error.columnNumber == "number") {
+          stack += ":" + error.columnNumber;
+        }
+      }
+    } else if (typeof error.lineNumber == "number") {
+      stack = "@" + error.lineNumber;
+      if (typeof error.columnNumber == "number") {
+        stack += ":" + error.columnNumber;
+      }
+    }
+
+    return stack ? "\n" + stack.replace(/\n$/, "") : "";
+  },
+
   // Menu Operations
 
   /**
    * Open a new Scratchpad window.
    *
    * @return nsIWindow
    */
   openScratchpad: function SP_openScratchpad()
--- a/browser/devtools/scratchpad/test/browser_scratchpad_display_non_error_exceptions.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_display_non_error_exceptions.js
@@ -36,17 +36,17 @@ function runTests()
     result: message + openComment + "Hello World!" + closeComment,
     label: "message display output"
   },
   {
     // Display error1, throw new Error("Ouch")
     method: "display",
     code: error1,
     result: error1 + openComment +
-            "Exception: Ouch!\n@" + scratchpad.uniqueName + ":1:7" + closeComment,
+            "Exception: Error: Ouch!\n@" + scratchpad.uniqueName + ":1:7" + closeComment,
     label: "error display output"
   },
   {
     // Display error2, throw "A thrown string"
     method: "display",
     code: error2,
     result: error2 + openComment + "Exception: A thrown string" + closeComment,
     label: "thrown string display output"
@@ -57,34 +57,34 @@ function runTests()
     code: error3,
     result: error3 + openComment + "Exception: [object Object]" + closeComment,
     label: "thrown object display output"
   },
   {
     // Display error4, document.body.appendChild(document.body)
     method: "display",
     code: error4,
-    result: error4 + openComment + "Exception: Node cannot be inserted " +
+    result: error4 + openComment + "Exception: HierarchyRequestError: Node cannot be inserted " +
             "at the specified point in the hierarchy\n@" +
             scratchpad.uniqueName + ":1:0" + closeComment,
     label: "Alternative format error display output"
   },
   {
     // Run message
     method: "run",
     code: message,
     result: message,
     label: "message run output"
   },
   {
     // Run error1, throw new Error("Ouch")
     method: "run",
     code: error1,
     result: error1 + openComment +
-            "Exception: Ouch!\n@" + scratchpad.uniqueName + ":1:7" + closeComment,
+            "Exception: Error: Ouch!\n@" + scratchpad.uniqueName + ":1:7" + closeComment,
     label: "error run output"
   },
   {
     // Run error2, throw "A thrown string"
     method: "run",
     code: error2,
     result: error2 + openComment + "Exception: A thrown string" + closeComment,
     label: "thrown string run output"
@@ -95,16 +95,16 @@ function runTests()
     code: error3,
     result: error3 + openComment + "Exception: [object Object]" + closeComment,
     label: "thrown object run output"
   },
   {
     // Run error4, document.body.appendChild(document.body)
     method: "run",
     code: error4,
-    result: error4 + openComment + "Exception: Node cannot be inserted " +
+    result: error4 + openComment + "Exception: HierarchyRequestError: Node cannot be inserted " +
             "at the specified point in the hierarchy\n@" +
             scratchpad.uniqueName + ":1:0" + closeComment,
     label: "Alternative format error run output"
   }];
 
   runAsyncTests(scratchpad, tests).then(finish);
 }
--- a/browser/devtools/scratchpad/test/browser_scratchpad_display_outputs_errors.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_display_outputs_errors.js
@@ -31,42 +31,42 @@ function runTests()
     method: "display",
     code: message,
     result: message + openComment + "Hello World!" + closeComment,
     label: "message display output"
   },
   {
     method: "display",
     code: error,
-    result: error + openComment + "Exception: Ouch!\n@" +
+    result: error + openComment + "Exception: Error: Ouch!\n@" +
             scratchpad.uniqueName + ":1:7" + closeComment,
     label: "error display output",
   },
   {
     method: "display",
     code: syntaxError,
-    result: syntaxError + openComment + "Exception: expected expression, got end of script\n@" +
+    result: syntaxError + openComment + "Exception: SyntaxError: expected expression, got end of script\n@" +
             scratchpad.uniqueName + ":1" + closeComment,
     label: "syntaxError display output",
   },
   {
     method: "run",
     code: message,
     result: message,
     label: "message run output",
   },
   {
     method: "run",
     code: error,
-    result: error + openComment + "Exception: Ouch!\n@" +
+    result: error + openComment + "Exception: Error: Ouch!\n@" +
             scratchpad.uniqueName + ":1:7" + closeComment,
     label: "error run output",
   },
   {
     method: "run",
     code: syntaxError,
-    result: syntaxError + openComment + "Exception: expected expression, got end of script\n@" +
+    result: syntaxError + openComment + "Exception: SyntaxError: expected expression, got end of script\n@" +
             scratchpad.uniqueName + ":1" + closeComment,
     label: "syntaxError run output",
   }];
 
   runAsyncTests(scratchpad, tests).then(finish);
 }
--- a/browser/devtools/scratchpad/test/head.js
+++ b/browser/devtools/scratchpad/test/head.js
@@ -4,16 +4,17 @@
 
 "use strict";
 
 const {NetUtil} = Cu.import("resource://gre/modules/NetUtil.jsm", {});
 const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", {});
 const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 const {console} = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
 const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
+const {DevToolsUtils} = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
 
 let gScratchpadWindow; // Reference to the Scratchpad chrome window object
 
 gDevTools.testing = true;
 SimpleTest.registerCleanupFunction(() => {
   gDevTools.testing = false;
 });