Bug 1455750 - Provide method for server to skip pausing. r=jimb
authorJason Laster <jason.laster.11@gmail.com>
Tue, 22 May 2018 14:25:22 -0400
changeset 419721 135af0c54dd7
parent 419720 f38805d4a389
child 419722 8f1d10d34e9c
push id103601
push userjlaster@mozilla.com
push dateThu, 24 May 2018 20:57:29 +0000
treeherdermozilla-inbound@135af0c54dd7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimb
bugs1455750
milestone62.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 1455750 - Provide method for server to skip pausing. r=jimb
devtools/server/actors/breakpoint.js
devtools/server/actors/thread.js
devtools/server/tests/unit/head_dbg.js
devtools/server/tests/unit/test_stepping-with-skip-breakpoints.js
devtools/server/tests/unit/xpcshell.ini
devtools/shared/client/thread-client.js
--- a/devtools/server/actors/breakpoint.js
+++ b/devtools/server/actors/breakpoint.js
@@ -144,16 +144,17 @@ let BreakpointActor = ActorClassWithSpec
       originalSourceActor,
       originalLine,
       originalColumn
     } = this.threadActor.unsafeSynchronize(
       this.threadActor.sources.getOriginalLocation(generatedLocation));
     let url = originalSourceActor.url;
 
     if (this.threadActor.sources.isBlackBoxed(url)
+        || this.threadActor.skipBreakpoints
         || frame.onStep) {
       return undefined;
     }
 
     // If we're trying to pop this frame, and we see a breakpoint at
     // the spot at which popping started, ignore it.  See bug 970469.
     const locationAtFinish = frame.onPop && frame.onPop.originalLocation;
     if (locationAtFinish &&
--- a/devtools/server/actors/thread.js
+++ b/devtools/server/actors/thread.js
@@ -1506,19 +1506,26 @@ const ThreadActor = ActorClassWithSpec(t
   onDebuggerStatement: function(frame) {
     // Don't pause if we are currently stepping (in or over) or the frame is
     // black-boxed.
     const generatedLocation = this.sources.getFrameLocation(frame);
     const { originalSourceActor } = this.unsafeSynchronize(
       this.sources.getOriginalLocation(generatedLocation));
     const url = originalSourceActor ? originalSourceActor.url : null;
 
-    return this.sources.isBlackBoxed(url) || frame.onStep
-      ? undefined
-      : this._pauseAndRespond(frame, { type: "debuggerStatement" });
+    if (this.skipBreakpoints || this.sources.isBlackBoxed(url) || frame.onStep) {
+      return undefined;
+    }
+
+    return this._pauseAndRespond(frame, { type: "debuggerStatement" });
+  },
+
+  onSkipBreakpoints: function({ skip }) {
+    this.skipBreakpoints = skip;
+    return { skip };
   },
 
   /**
    * A function that the engine calls when an exception has been thrown and has
    * propagated to the specified frame.
    *
    * @param youngestFrame Debugger.Frame
    *        The youngest remaining stack frame.
@@ -1547,17 +1554,17 @@ const ThreadActor = ActorClassWithSpec(t
 
     const generatedLocation = this.sources.getFrameLocation(youngestFrame);
     const { originalSourceActor } = this.unsafeSynchronize(
       this.sources.getOriginalLocation(generatedLocation));
     const url = originalSourceActor ? originalSourceActor.url : null;
 
     // We ignore sources without a url because we do not
     // want to pause at console evaluations or watch expressions.
-    if (!url || this.sources.isBlackBoxed(url)) {
+    if (!url || this.skipBreakpoints || this.sources.isBlackBoxed(url)) {
       return undefined;
     }
 
     try {
       let packet = this._paused(youngestFrame);
       if (!packet) {
         return undefined;
       }
@@ -1750,17 +1757,18 @@ Object.assign(ThreadActor.prototype.requ
   "resume": ThreadActor.prototype.onResume,
   "clientEvaluate": ThreadActor.prototype.onClientEvaluate,
   "frames": ThreadActor.prototype.onFrames,
   "interrupt": ThreadActor.prototype.onInterrupt,
   "eventListeners": ThreadActor.prototype.onEventListeners,
   "releaseMany": ThreadActor.prototype.onReleaseMany,
   "sources": ThreadActor.prototype.onSources,
   "threadGrips": ThreadActor.prototype.onThreadGrips,
-  "prototypesAndProperties": ThreadActor.prototype.onPrototypesAndProperties
+  "prototypesAndProperties": ThreadActor.prototype.onPrototypesAndProperties,
+  "skipBreakpoints": ThreadActor.prototype.onSkipBreakpoints
 });
 
 exports.ThreadActor = ThreadActor;
 
 /**
  * Creates a PauseActor.
  *
  * PauseActors exist for the lifetime of a given debuggee pause.  Used to
--- a/devtools/server/tests/unit/head_dbg.js
+++ b/devtools/server/tests/unit/head_dbg.js
@@ -214,16 +214,26 @@ function findSource(sources, url) {
   return null;
 }
 
 function waitForPause(threadClient) {
   dump("Waiting for pause.\n");
   return waitForEvent(threadClient, "paused");
 }
 
+function waitForProperty(dbg, property) {
+  return new Promise(resolve => {
+    Object.defineProperty(dbg, property, {
+      set(newValue) {
+        resolve(newValue);
+      }
+    });
+  });
+}
+
 function setBreakpoint(sourceClient, location) {
   dump("Setting breakpoint.\n");
   return sourceClient.setBreakpoint(location);
 }
 
 function getPrototypeAndProperties(objClient) {
   dump("getting prototype and properties.\n");
 
new file mode 100644
--- /dev/null
+++ b/devtools/server/tests/unit/test_stepping-with-skip-breakpoints.js
@@ -0,0 +1,100 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+/* eslint-disable no-shadow, max-nested-callbacks */
+
+"use strict";
+
+/**
+ * Check basic step-over functionality with pause points
+ * for the first statement and end of the last statement.
+ */
+
+var gDebuggee;
+var gClient;
+var gCallback;
+
+function run_test() {
+  do_test_pending();
+  run_test_with_server(DebuggerServer, function() {
+    run_test_with_server(WorkerDebuggerServer, do_test_finished);
+  });
+}
+
+function run_test_with_server(server, callback) {
+  gCallback = callback;
+  initTestDebuggerServer(server);
+  gDebuggee = addTestGlobal("test-stepping", server);
+  gClient = new DebuggerClient(server.connectPipe());
+  gClient.connect(test_simple_stepping);
+}
+
+async function test_simple_stepping() {
+  const [attachResponse,, threadClient] = await attachTestTabAndResume(gClient,
+                                                                       "test-stepping");
+  ok(!attachResponse.error, "Should not get an error attaching");
+
+  dumpn("Evaluating test code and waiting for first debugger statement");
+  const dbgStmt = await executeOnNextTickAndWaitForPause(evaluateTestCode, gClient);
+  equal(dbgStmt.frame.where.line, 2, "Should be at debugger statement on line 2");
+  equal(gDebuggee.a, undefined);
+  equal(gDebuggee.b, undefined);
+
+  const source = await getSource(threadClient, "test_stepping-01-test-code.js");
+
+  // Add pause points for the first and end of the last statement.
+  // Note: we intentionally ignore the second statement.
+  source.setPausePoints([{
+    location: {line: 3, column: 8},
+    types: {breakpoint: true, stepOver: true}
+  },
+  {
+    location: {line: 4, column: 14},
+    types: {breakpoint: true, stepOver: true}
+  }]);
+
+  dumpn("Step Over to line 3");
+  const step1 = await stepOver(gClient, threadClient);
+  equal(step1.type, "paused");
+  equal(step1.why.type, "resumeLimit");
+  equal(step1.frame.where.line, 3);
+  equal(step1.frame.where.column, 8);
+
+  equal(gDebuggee.a, undefined);
+  equal(gDebuggee.b, undefined);
+
+  dumpn("Step Over to line 4");
+  const step2 = await stepOver(gClient, threadClient);
+  equal(step2.type, "paused");
+  equal(step2.why.type, "resumeLimit");
+  equal(step2.frame.where.line, 4);
+  equal(step2.frame.where.column, 8);
+
+  equal(gDebuggee.a, 1);
+  equal(gDebuggee.b, undefined);
+
+  dumpn("Step Over to the end of line 4");
+  const step3 = await stepOver(gClient, threadClient);
+  equal(step3.type, "paused");
+  equal(step3.why.type, "resumeLimit");
+  equal(step3.frame.where.line, 4);
+  equal(step3.frame.where.column, 14);
+  equal(gDebuggee.a, 1);
+  equal(gDebuggee.b, 2);
+
+  finishClient(gClient, gCallback);
+}
+
+function evaluateTestCode() {
+  /* eslint-disable */
+  Cu.evalInSandbox(
+    `                                   // 1
+    debugger;                           // 2
+    var a = 1;                          // 3
+    var b = 2;`,                        // 4
+    gDebuggee,
+    "1.8",
+    "test_stepping-01-test-code.js",
+    1
+  );
+  /* eslint-disable */
+}
--- a/devtools/server/tests/unit/xpcshell.ini
+++ b/devtools/server/tests/unit/xpcshell.ini
@@ -184,16 +184,17 @@ reason = bug 1104838
 [test_stepping-02.js]
 [test_stepping-03.js]
 [test_stepping-04.js]
 [test_stepping-05.js]
 [test_stepping-06.js]
 [test_stepping-07.js]
 [test_stepping-08.js]
 [test_stepping-with-pause-points.js]
+[test_stepping-with-skip-breakpoints.js]
 [test_framebindings-01.js]
 [test_framebindings-02.js]
 [test_framebindings-03.js]
 [test_framebindings-04.js]
 [test_framebindings-05.js]
 [test_framebindings-06.js]
 [test_framebindings-07.js]
 [test_pause_exceptions-01.js]
--- a/devtools/shared/client/thread-client.js
+++ b/devtools/shared/client/thread-client.js
@@ -391,16 +391,27 @@ ThreadClient.prototype = {
    */
   getFrames: DebuggerClient.requester({
     type: "frames",
     start: arg(0),
     count: arg(1)
   }),
 
   /**
+   * Toggle pausing via breakpoints in the server.
+   *
+   * @param skip boolean
+   *        Whether the server should skip pausing via breakpoints
+   */
+  skipBreakpoints: DebuggerClient.requester({
+    type: "skipBreakpoints",
+    skip: arg(0),
+  }),
+
+  /**
    * An array of cached frames. Clients can observe the framesadded and
    * framescleared event to keep up to date on changes to this cache,
    * and can fill it using the fillFrames method.
    */
   get cachedFrames() {
     return this._frameCache;
   },