Bug 1316265 - Reps: display when a function is a generator or an async function. r=nchevobbe draft
authorTim Nguyen <ntim.bugs@gmail.com>
Tue, 20 Dec 2016 15:34:50 +0100
changeset 451498 a6a2f2c3a6673915811993db852f0c799a96d3c6
parent 451111 d4b3146a5567a7ddbcdfa5244945db55616cb8d1
child 451499 86deeffa31ebc0c552859f89c33f28a3be8edc75
push id39206
push userbmo:ntim.bugs@gmail.com
push dateTue, 20 Dec 2016 14:38:11 +0000
reviewersnchevobbe
bugs1316265
milestone53.0a1
Bug 1316265 - Reps: display when a function is a generator or an async function. r=nchevobbe MozReview-Commit-ID: C79EAU4vN1u
devtools/client/shared/components/reps/function.js
devtools/client/shared/components/test/mochitest/test_reps_function.html
devtools/server/actors/object.js
--- a/devtools/client/shared/components/reps/function.js
+++ b/devtools/client/shared/components/reps/function.js
@@ -22,26 +22,34 @@ define(function (require, exports, modul
   let Func = React.createClass({
     displayName: "Func",
 
     propTypes: {
       object: React.PropTypes.object.isRequired
     },
 
     getTitle: function (grip) {
+      let title = "function ";
+      if (grip.isGenerator) {
+        title = "function* ";
+      }
+      if (grip.isAsync) {
+        title = "async " + title;
+      }
+
       if (this.props.objectLink) {
         return this.props.objectLink({
           object: grip
-        }, "function ");
+        }, title);
       }
-      return "";
+      return title;
     },
 
     summarizeFunction: function (grip) {
-      let name = grip.userDisplayName || grip.displayName || grip.name || "function";
+      let name = grip.userDisplayName || grip.displayName || grip.name || "";
       return cropString(name + "()", 100);
     },
 
     render: function () {
       let grip = this.props.object;
 
       return (
         // Set dir="ltr" to prevent function parentheses from
--- a/devtools/client/shared/components/test/mochitest/test_reps_function.html
+++ b/devtools/client/shared/components/test/mochitest/test_reps_function.html
@@ -14,141 +14,253 @@ Test Func rep
 </head>
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 window.onload = Task.async(function* () {
   let { Rep } = browserRequire("devtools/client/shared/components/reps/rep");
   let { Func } = browserRequire("devtools/client/shared/components/reps/function");
+  const { MODE } = browserRequire("devtools/client/shared/components/reps/constants");
 
   const componentUnderTest = Func;
 
   try {
     // Test that correct rep is chosen
     const gripStub = getGripStub("testNamed");
     const renderedRep = shallowRenderComponent(Rep, { object: gripStub });
     is(renderedRep.type, Func.rep, `Rep correctly selects ${Func.rep.displayName}`);
 
     yield testNamed();
     yield testVarNamed();
     yield testAnon();
     yield testLongName();
+    yield testAsyncFunction();
+    yield testAnonAsyncFunction();
+    yield testGeneratorFunction();
   } catch(e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
   }
 
   function testNamed() {
-    // Test declaration: `function testName{ let innerVar = "foo" }`
+    // Test declaration: `function testName() { let innerVar = "foo" }`
     const testName = "testNamed";
 
-    const defaultOutput = `testName()`;
+    const defaultOutput = "function testName()";
 
     const modeTests = [
       {
         mode: undefined,
         expectedOutput: defaultOutput,
       }
     ];
 
     testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
   }
 
   function testUserNamed() {
-    // Test declaration: `function testName{ let innerVar = "foo" }`
+    // Test declaration: `function testName() { let innerVar = "foo" }`
     const testName = "testUserNamed";
 
-    const defaultOutput = `testUserName()`;
+    const defaultOutput = "function testUserName()";
 
     const modeTests = [
       {
         mode: undefined,
         expectedOutput: defaultOutput,
       }
     ];
 
     testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
   }
 
   function testVarNamed() {
     // Test declaration: `let testVarName = function() { }`
     const testName = "testVarNamed";
 
-    const defaultOutput = `testVarName()`;
+    const defaultOutput = "function testVarName()";
 
     const modeTests = [
       {
         mode: undefined,
         expectedOutput: defaultOutput,
       }
     ];
 
     testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
   }
 
   function testAnon() {
     // Test declaration: `() => {}`
     const testName = "testAnon";
 
-    const defaultOutput = `function()`;
+    const defaultOutput = "function ()";
 
     const modeTests = [
       {
         mode: undefined,
         expectedOutput: defaultOutput,
       }
     ];
 
     testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
   }
 
   function testLongName() {
     // Test declaration: `let f = function loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong() { }`
     const testName = "testLongName";
 
-    const defaultOutput = `looooooooooooooooooooooooooooooooooooooooooooooooo\u2026ooooooooooooooooooooooooooooooooooooooooooooong()`;
+    const defaultOutput = "function looooooooooooooooooooooooooooooooooooooooooooooooo\u2026ooooooooooooooooooooooooooooooooooooooooooooong()";
 
     const modeTests = [
       {
         mode: undefined,
         expectedOutput: defaultOutput,
       }
     ];
 
     testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
   }
 
+  function testAsyncFunction() {
+    const testName = "testAsyncFunction";
+
+    const defaultOutput = "async function waitUntil2017()";
+
+    const modeTests = [
+      {
+        mode: undefined,
+        expectedOutput: defaultOutput,
+      },
+      {
+        mode: MODE.TINY,
+        expectedOutput: defaultOutput,
+      },
+      {
+        mode: MODE.SHORT,
+        expectedOutput: defaultOutput,
+      },
+      {
+        mode: MODE.LONG,
+        expectedOutput: defaultOutput,
+      }
+    ];
+    testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
+  }
+
+  function testAnonAsyncFunction() {
+    const testName = "testAnonAsyncFunction";
+
+    const defaultOutput = "async function ()";
+
+    const modeTests = [
+      {
+        mode: undefined,
+        expectedOutput: defaultOutput,
+      },
+      {
+        mode: MODE.TINY,
+        expectedOutput: defaultOutput,
+      },
+      {
+        mode: MODE.SHORT,
+        expectedOutput: defaultOutput,
+      },
+      {
+        mode: MODE.LONG,
+        expectedOutput: defaultOutput,
+      }
+    ];
+    testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
+  }
+
+  function testGeneratorFunction() {
+    const testName = "testGeneratorFunction";
+
+    const defaultOutput = "function* fib()";
+
+    const modeTests = [
+      {
+        mode: undefined,
+        expectedOutput: defaultOutput,
+      },
+      {
+        mode: MODE.TINY,
+        expectedOutput: defaultOutput,
+      },
+      {
+        mode: MODE.SHORT,
+        expectedOutput: defaultOutput,
+      },
+      {
+        mode: MODE.LONG,
+        expectedOutput: defaultOutput,
+      }
+    ];
+    testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
+  }
+
+  function testAnonGeneratorFunction() {
+    const testName = "testAnonGeneratorFunction";
+
+    const defaultOutput = "function* ()";
+
+    const modeTests = [
+      {
+        mode: undefined,
+        expectedOutput: defaultOutput,
+      },
+      {
+        mode: MODE.TINY,
+        expectedOutput: defaultOutput,
+      },
+      {
+        mode: MODE.SHORT,
+        expectedOutput: defaultOutput,
+      },
+      {
+        mode: MODE.LONG,
+        expectedOutput: defaultOutput,
+      }
+    ];
+    testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
+  }
+
   function getGripStub(functionName) {
     switch (functionName) {
       case "testNamed":
         return {
           "type": "object",
           "class": "Function",
           "actor": "server1.conn6.obj35",
           "extensible": true,
           "frozen": false,
           "sealed": false,
+          "isAsync": false,
+          "isGenerator": false,
           "name": "testName",
           "displayName": "testName",
           "location": {
             "url": "debugger eval code",
             "line": 1
           }
         };
 
       case "testUserNamed":
         return {
           "type": "object",
           "class": "Function",
           "actor": "server1.conn6.obj35",
           "extensible": true,
           "frozen": false,
           "sealed": false,
+          "isAsync": false,
+          "isGenerator": false,
           "name": "testName",
           "userDisplayName": "testUserName",
           "displayName": "testName",
           "location": {
             "url": "debugger eval code",
             "line": 1
           }
         };
@@ -156,51 +268,126 @@ window.onload = Task.async(function* () 
       case "testVarNamed":
         return {
           "type": "object",
           "class": "Function",
           "actor": "server1.conn7.obj41",
           "extensible": true,
           "frozen": false,
           "sealed": false,
+          "isAsync": false,
+          "isGenerator": false,
           "displayName": "testVarName",
           "location": {
             "url": "debugger eval code",
             "line": 1
           }
         };
 
       case "testAnon":
         return {
           "type": "object",
           "class": "Function",
           "actor": "server1.conn7.obj45",
           "extensible": true,
           "frozen": false,
           "sealed": false,
+          "isAsync": false,
+          "isGenerator": false,
           "location": {
             "url": "debugger eval code",
             "line": 1
           }
         };
 
       case "testLongName":
         return {
           "type": "object",
           "class": "Function",
           "actor": "server1.conn7.obj67",
           "extensible": true,
           "frozen": false,
           "sealed": false,
+          "isAsync": false,
+          "isGenerator": false,
           "name": "loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong",
           "displayName": "loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong",
           "location": {
             "url": "debugger eval code",
             "line": 1
           }
         };
+
+
+      case "testAsyncFunction":
+        return {
+          "type": "object",
+          "class": "Function",
+          "actor": "server1.conn7.obj45",
+          "extensible": true,
+          "frozen": false,
+          "sealed": false,
+          "isAsync": true,
+          "isGenerator": false,
+          "name": "waitUntil2017",
+          "displayName": "waitUntil2017",
+          "location": {
+            "url": "debugger eval code",
+            "line": 1
+          }
+        };
+
+      case "testAnonAsyncFunction":
+        return {
+          "type": "object",
+          "class": "Function",
+          "actor": "server1.conn7.obj45",
+          "extensible": true,
+          "frozen": false,
+          "sealed": false,
+          "isAsync": true,
+          "isGenerator": false,
+          "location": {
+            "url": "debugger eval code",
+            "line": 1
+          }
+        };
+
+      case "testGeneratorFunction":
+        return {
+          "type": "object",
+          "class": "Function",
+          "actor": "server1.conn7.obj45",
+          "extensible": true,
+          "frozen": false,
+          "sealed": false,
+          "isAsync": false,
+          "isGenerator": true,
+          "name": "fib",
+          "displayName": "fib",
+          "location": {
+            "url": "debugger eval code",
+            "line": 1
+          }
+        };
+
+      case "testAnonGeneratorFunction":
+        return {
+          "type": "object",
+          "class": "Function",
+          "actor": "server1.conn7.obj45",
+          "extensible": true,
+          "frozen": false,
+          "sealed": false,
+          "isAsync": false,
+          "isGenerator": true,
+          "location": {
+            "url": "debugger eval code",
+            "line": 1
+          }
+        };
     }
   }
 });
 </script>
 </pre>
 </body>
 </html>
--- a/devtools/server/actors/object.js
+++ b/devtools/server/actors/object.js
@@ -1122,16 +1122,20 @@ DebuggerServer.ObjectActorPreviewers = {
     if (obj.displayName) {
       grip.displayName = obj.displayName.substr(0, 500);
     }
 
     if (obj.parameterNames) {
       grip.parameterNames = obj.parameterNames;
     }
 
+    let type = DevToolsUtils.getProperty(obj, "constructor").name;
+    grip.isAsync = type === "AsyncFunction";
+    grip.isGenerator = type === "GeneratorFunction";
+
     // Check if the developer has added a de-facto standard displayName
     // property for us to use.
     let userDisplayName;
     try {
       userDisplayName = obj.getOwnPropertyDescriptor("displayName");
     } catch (e) {
       // Calling getOwnPropertyDescriptor with displayName might throw
       // with "permission denied" errors for some functions.