Closed
Bug 1102441
Opened 10 years ago
Closed 10 years ago
Clean up the BreakpointStore
Categories
(DevTools :: Debugger, defect)
Tracking
(Not tracked)
RESOLVED
FIXED
Firefox 38
People
(Reporter: ejpbruel, Assigned: ejpbruel)
References
(Blocks 1 open bug)
Details
Attachments
(4 files, 9 obsolete files)
19.02 KB,
patch
|
jimb
:
review+
|
Details | Diff | Splinter Review |
30.34 KB,
patch
|
jimb
:
review+
|
Details | Diff | Splinter Review |
10.09 KB,
patch
|
fitzgen
:
review+
|
Details | Diff | Splinter Review |
12.28 KB,
patch
|
fitzgen
:
review+
|
Details | Diff | Splinter Review |
No description provided.
Assignee | ||
Updated•10 years ago
|
Blocks: dbg-breakpoint
Assignee | ||
Comment 1•10 years ago
|
||
The BreakpointStore currently stores breakpoint locations to which actors are associated. These breakpoint locations are shared between other objects. Moreover, they are mutable, which has led to some problems throughout our code.
It would make much more sense to think of BreakpointStore as a map from breakpoint locations to breakpoint actors, only explicitly storing the latter, and only using the former as keys.
This refactor is also the first step in cleaning up the way we handle breakpoints when source maps are present.
Assignee | ||
Comment 2•10 years ago
|
||
_createAndStoreBreakpoint creates a breakpoint in the breakpoint store before calling _setBreakpoint. The latter then ends up calling _getOrCreateBreakpointActor, which
expects the breakpoint to be there. The only reason _createAndStoreBreakpointActor exists is to create the breakpoint for _getOrCreateBreakpointActor. If we moved creation of the breakpoint to _getOrCreateBreakpointActor itself (only doing so if the breakpoint does not already exist), _createAndStoreBreakpoint would no longer be necessary.
Assignee: nobody → ejpbruel
Attachment #8526232 -
Flags: review?(nfitzgerald)
Assignee | ||
Comment 3•10 years ago
|
||
Attachment #8526233 -
Flags: review?(nfitzgerald)
Assignee | ||
Comment 4•10 years ago
|
||
This should have gone with comment 2 :-(
moveBreakpoint is only called from _setBreakpoint. When thinking about the BreakpointStore as a
map from locations to actors, moving a breakpoint can be thought of us a deleting the actor
from the previous location and then setting it to the new location. I think it would be clearer to make
this explicit, in which case this method would no longer be necessary.
Assignee | ||
Comment 5•10 years ago
|
||
getBreakpoint can be thought of as an variant of hasBreakpoint that throws if no
breakpoint exists at the given location. However, all of our calls to getBreakpoint
are actually infallible in the sense that they will never throw unless we have a
programmer error. For programmer errors, assertions are usually better than
exceptions, but having a separate method for that is overkill.
Attachment #8526237 -
Flags: review?(nfitzgerald)
Assignee | ||
Comment 6•10 years ago
|
||
Now that getBreakpoint is gone, getBreakpoint would actually be a better name for hasBreakpoint. The latter suggests that it returns a boolean, while it actually returns the breakpoint itself.
Attachment #8526239 -
Flags: review?(nfitzgerald)
Assignee | ||
Comment 7•10 years ago
|
||
Now that we create the breakpoint in _getOrCreateBreakpointActor, there is never a case where a breakpoint exists, but doesn’t have an actor associated with it, so we can simplify some of the checks in this method.
Attachment #8526243 -
Flags: review?(nfitzgerald)
Assignee | ||
Updated•10 years ago
|
Attachment #8526243 -
Attachment is patch: true
Attachment #8526243 -
Attachment mime type: text/x-patch → text/plain
Assignee | ||
Comment 8•10 years ago
|
||
Now that we’ve simplified _getOrCreateBreakpointActor, whenever addBreakpoint is
called to create a breakpoint, we immediately associate an actor with it. By moving
the latter operation to addBreakpoint we can think of it as associating an actor with the given breakpoint/location.
Attachment #8526245 -
Flags: review?(nfitzgerald)
Assignee | ||
Comment 9•10 years ago
|
||
Finally, by making getBreakpoint return the actor associated with the breakpoint
rather than the breakpoint itself, we no longer have to store the breakpoint passed
to addBreakpoint and associate an actor with it: instead we can just store the
actor itself.
Attachment #8526247 -
Flags: review?(nfitzgerald)
Comment 11•10 years ago
|
||
Comment on attachment 8526232 [details] [diff] [review]
Step 1. Remove ThreadActor.prototype._createAndStoreBreakpoint
Review of attachment 8526232 [details] [diff] [review]:
-----------------------------------------------------------------
::: toolkit/devtools/server/actors/script.js
@@ +1448,5 @@
> let actor;
> + let storedBp = this.breakpointStore.hasBreakpoint(location);
> + if (!storedBp) {
> + storedBp = this.breakpointStore.addBreakpoint(location);
> + }
BreakpointStore.prototype.getOrAddBreakpoint?
Attachment #8526232 -
Flags: review?(nfitzgerald) → review+
Comment 12•10 years ago
|
||
Comment on attachment 8526233 [details] [diff] [review]
Step 2. Remove BreakpointStore.prototype.moveBreakpoint
Review of attachment 8526233 [details] [diff] [review]:
-----------------------------------------------------------------
I'm not convinced this is ok -- what about the case where we set a "pending" BP b/c we don't have any scripts, and then later, when we actually get the script and set the BP, we discover we have to slide the BP down? In this case, I think we still need to move the BP in the store. Convince me that this case is handled, and I'll r+.
Attachment #8526233 -
Flags: review?(nfitzgerald)
Comment 13•10 years ago
|
||
Comment on attachment 8526237 [details] [diff] [review]
Step 3. Remove BreakpointStore.prototype.getBreakpoint
Review of attachment 8526237 [details] [diff] [review]:
-----------------------------------------------------------------
r+ with assertions added.
::: toolkit/devtools/server/actors/script.js
@@ +1198,5 @@
> if (resp.error) {
> reportError(new Error("Unable to set breakpoint on event listener"));
> return;
> }
> + let bp = this.breakpointStore.hasBreakpoint(location);
Ok, runtime assertions are better for this, but where are they? Here and below, add:
dbg_assert(bp, "should have bp or whatever lol...");
Attachment #8526237 -
Flags: review?(nfitzgerald) → review+
Updated•10 years ago
|
Attachment #8526239 -
Flags: review?(nfitzgerald) → review+
Comment 14•10 years ago
|
||
Comment on attachment 8526243 [details] [diff] [review]
Step 5. Refactor ThreadActor.prototype._getOrCreateBreakpointActor
Review of attachment 8526243 [details] [diff] [review]:
-----------------------------------------------------------------
::: toolkit/devtools/server/actors/script.js
@@ +1412,5 @@
> return actor;
> }
>
> + actor = storedBp.actor;
> + actor.condition = location.condition;
Kind of nitty, but while we're here, let's nix the actor binding:
storedBP.actor.condition = location.condition;
return storedBP.actor;
The actor variable looks like its going to be shared due to the block its defined in, but isn't, and this is mostly just historical from all the refactorings this code has been through and moved around.
Alternatively, we could create two different actor bindings:
if (!storedBP) {
...
let actor = storedBP.actor = new BreakpointActor(...);
...
return actor;
}
let actor = storedBP.actor;
...
return actor;
The point is that right now it's funky and misleading, like its going to be shared between the if branches (like it used to be, long ago) when it really isn't.
I realize I should have done this when I first moved this code, but alas...
Attachment #8526243 -
Flags: review?(nfitzgerald) → review+
Comment 15•10 years ago
|
||
Comment on attachment 8526245 [details] [diff] [review]
Step 6. Refactor ThreadActor.prototype.addBreakpoint
Review of attachment 8526245 [details] [diff] [review]:
-----------------------------------------------------------------
Really like the spirit of this change, but want the API cleaned up a little. Want to take a quick look at it again once you've updated it, before I rubber stamp it.
::: toolkit/devtools/server/actors/script.js
@@ +113,3 @@
> let { url, line, column } = aBreakpoint;
>
> + aBreakpoint.actor = aActor;
Either use the actor property we document `aBreakpoint` to optionally (no longer optional?) have, or remove that property from usage and the docs.
We shouldn't have both an actor parameter and an actor property.
In fact, could this take only a breakpoint actor, since the breakpoint actor has its location as a public property that we can just use directly?
Attachment #8526245 -
Flags: review?(nfitzgerald)
Comment 16•10 years ago
|
||
Comment on attachment 8526247 [details] [diff] [review]
Step 7. Refactor ThreadActor.prototype.getBreakpoint
Review of attachment 8526247 [details] [diff] [review]:
-----------------------------------------------------------------
r+ asssuming we fix the API stuff I commented about in the last patch.
Attachment #8526247 -
Flags: review?(nfitzgerald) → review+
Assignee | ||
Comment 17•10 years ago
|
||
(In reply to Nick Fitzgerald [:fitzgen] from comment #12)
> Comment on attachment 8526233 [details] [diff] [review]
> Step 2. Remove BreakpointStore.prototype.moveBreakpoint
>
> Review of attachment 8526233 [details] [diff] [review]:
> -----------------------------------------------------------------
>
> I'm not convinced this is ok -- what about the case where we set a "pending"
> BP b/c we don't have any scripts, and then later, when we actually get the
> script and set the BP, we discover we have to slide the BP down? In this
> case, I think we still need to move the BP in the store. Convince me that
> this case is handled, and I'll r+.
In the scenario you outlined, we would first end up in setBreakpoint via onSetBreakpoint. setBreakpoint would create a new breakpoint actor, and store it in the breakpoint store at the original location. We don't have any scripts that match the given original location yet, so we are done for now.
When a new script arrives, we end up in _addScript. We then query the breakpoint store for all actors at locations in range of the new script, and call _setBreakpoint again, getting the exact location from the actor, and passing the script as an extra parameter, so that we will only try to add the actor to that script.
For the second call to _setBreakpoint, we know there will be at least one script that matches the given original location (otherwise, _setBreakpoint would not have been called in the first place). Now we figure out that there aren't actually any offsets in any of these scripts for the original location, so we compute the actual location, destroy the existing actor, remove it from the breakpoint store, create a new actor for the actual location, and add that to the breakpoint store.
This is how things happen right now. This patch actually does not affect that at all. It simply replaces the call to moveBreakpoint with an explicit call to removeBreakpoint and setBreakpoint. This is more in the spirit of treating the BreakpointStore as a location -> actor map. But that's all it does.
As an aside, what happens if somebody sends a request to set breakpoint on a location for which we don't have any scripts, gets back an actor, and later we learn that the breakpoint should have been moved. Right now, the original actor is destroyed, and we create a new one for the actual location. But is there a way for the client to know about this and get a handle to the new actor?
Assignee | ||
Comment 18•10 years ago
|
||
(In reply to Nick Fitzgerald [:fitzgen] from comment #15)
> Comment on attachment 8526245 [details] [diff] [review]
> Step 6. Refactor ThreadActor.prototype.addBreakpoint
>
> Review of attachment 8526245 [details] [diff] [review]:
> -----------------------------------------------------------------
>
> Really like the spirit of this change, but want the API cleaned up a little.
> Want to take a quick look at it again once you've updated it, before I
> rubber stamp it.
>
> ::: toolkit/devtools/server/actors/script.js
> @@ +113,3 @@
> > let { url, line, column } = aBreakpoint;
> >
> > + aBreakpoint.actor = aActor;
>
> Either use the actor property we document `aBreakpoint` to optionally (no
> longer optional?) have, or remove that property from usage and the docs.
>
> We shouldn't have both an actor parameter and an actor property.
>
> In fact, could this take only a breakpoint actor, since the breakpoint actor
> has its location as a public property that we can just use directly?
Yes, you are right, that was just an oversight. We don't need to store the actor as a property on the key, that was the whole point of this refactor! :-)
Assignee | ||
Comment 19•10 years ago
|
||
(In reply to Eddy Bruel [:ejpbruel] from comment #18)
> (In reply to Nick Fitzgerald [:fitzgen] from comment #15)
> > Comment on attachment 8526245 [details] [diff] [review]
> > Step 6. Refactor ThreadActor.prototype.addBreakpoint
> >
> > Review of attachment 8526245 [details] [diff] [review]:
> > -----------------------------------------------------------------
> >
> > Really like the spirit of this change, but want the API cleaned up a little.
> > Want to take a quick look at it again once you've updated it, before I
> > rubber stamp it.
> >
> > ::: toolkit/devtools/server/actors/script.js
> > @@ +113,3 @@
> > > let { url, line, column } = aBreakpoint;
> > >
> > > + aBreakpoint.actor = aActor;
> >
> > Either use the actor property we document `aBreakpoint` to optionally (no
> > longer optional?) have, or remove that property from usage and the docs.
> >
> > We shouldn't have both an actor parameter and an actor property.
> >
> > In fact, could this take only a breakpoint actor, since the breakpoint actor
> > has its location as a public property that we can just use directly?
>
> Yes, you are right, that was just an oversight. We don't need to store the
> actor as a property on the key, that was the whole point of this refactor!
> :-)
Actually, this was deliberate. I still have to store the actor as a property on the key at this point, because getBreakpoint still returns the key instead of the actor. Step 7 addresses that problem, so at that point I can avoid redundantly storing the actor on the key as well as separately.
I want to keep the location to which the actor is to be set as a separate argument for now, because we're going to end up having a N:1 relationship between locations and actors in the case of generated locations, and it's not yet clear how the API will evolve. For now, sticking as close to the Map interface as possible seemed like the simplest/cleanest thing to do.
Since all your review comments are either already addressed in the next patch, or in the case of the location argument, would be best left untouched for now, could you r+ it? I could put up the exact same patch again, but that wouldn't be very helpful ;-)
Assignee | ||
Comment 20•10 years ago
|
||
I've held off from putting the next patches up for review because I wanted to make sure you liked the prerequisite changes first.
This patch renames BreakpointStorage to BreakpointActorMap, changes the method names to more closely resemble that of Map, and also improves the comments a bit while we're at it.
The idea behind this change is that instead of a single BreakpointStorage, we will end up having two BreakpointActorMaps. One from original locations to actors (i.e. the one we currently have), which is 1:1, and one from generated locations to actors, which is N:1.
Attachment #8527657 -
Flags: review?(nfitzgerald)
Assignee | ||
Comment 21•10 years ago
|
||
This patch refactors BreakpointActorMap to use colspans internally. This change is necessary because we eventually want to map generated colspans instead of locations to actors.
This change has the additional advantage that it allows us to get rid of the distinction between whole line breakpoints and column breakpoints. A whole line breakpoint is equivalent to a breakpoint with a column span [0, Infinity]. A column breakpoint is equivalent to a breakpoint with a column span [column, column + 1] (Note that the latter will change to [beginColumn, endColumn] once we can query source maps for the column span of each mapping).
Note that the interface still requires locations rather than colspans to be passed as keys. The actual colspan is then computed from the location using the above observation. Changing the interface would require several callers to be updated as well, so I will do that in a future patch.
Attachment #8527684 -
Flags: review?(nfitzgerald)
Assignee | ||
Comment 22•10 years ago
|
||
Another reason I didn't update the interface yet is that I couldn't really come up with a good name for the concept of url + line + column span. In my mind, a column span is simply a range of columns without any additional location information, so using colspan for the former seems like a misnomer. I can't come up with anything better though.
Nick, do you have a good name in mind? Or should we just use 'colspan', even though that isn't really accurate?
Flags: needinfo?(nfitzgerald)
Updated•10 years ago
|
Attachment #8526245 -
Flags: review+
Updated•10 years ago
|
Attachment #8526233 -
Flags: review+
Assignee | ||
Comment 23•10 years ago
|
||
I had to do a fairly substantial rebase due to Debugger.Source landing, so I wanted to give you the opportunity to look over the rebased patches one more time before landing them.
Attachment #8526232 -
Attachment is obsolete: true
Attachment #8526233 -
Attachment is obsolete: true
Attachment #8526237 -
Attachment is obsolete: true
Attachment #8526239 -
Attachment is obsolete: true
Attachment #8526243 -
Attachment is obsolete: true
Attachment #8526245 -
Attachment is obsolete: true
Attachment #8526247 -
Attachment is obsolete: true
Attachment #8529614 -
Flags: review?(nfitzgerald)
Assignee | ||
Comment 24•10 years ago
|
||
Attachment #8527657 -
Attachment is obsolete: true
Attachment #8527657 -
Flags: review?(nfitzgerald)
Attachment #8529615 -
Flags: review?(nfitzgerald)
Assignee | ||
Comment 25•10 years ago
|
||
Attachment #8529616 -
Flags: review?(nfitzgerald)
Updated•10 years ago
|
Attachment #8529614 -
Flags: feedback+
Updated•10 years ago
|
Attachment #8529614 -
Flags: review?(nfitzgerald) → review+
Updated•10 years ago
|
Attachment #8529614 -
Flags: feedback+
Comment 26•10 years ago
|
||
Comment on attachment 8529614 [details] [diff] [review]
Step 1 - 7. Rebased patches
Review of attachment 8529614 [details] [diff] [review]:
-----------------------------------------------------------------
::: toolkit/devtools/server/actors/script.js
@@ +106,5 @@
> * - actor (optional)
> * @returns Object aBreakpoint
> * The new or existing breakpoint.
> */
> + addBreakpoint: function (aBreakpoint, aActor) {
If I'm reading the code right, we are now careful to check that no actor exists before calling this function. Indeed, if there is an existing actor, we don't do anything at all with aActor, so the caller would need to be sure to remove it from the pool; the callers we have now don't.
So, would it make sense for this function to dbg_assert that there is no extant actor? Granted, that's a deviation from the 'Map'-like interface, but it seems like the 'replace existing' behavior is never actually desired.
Comment 27•10 years ago
|
||
Comment on attachment 8529615 [details] [diff] [review]
Step 8. Clean up the BreakpointStore interface (rebased)
Review of attachment 8529615 [details] [diff] [review]:
-----------------------------------------------------------------
::: toolkit/devtools/server/actors/script.js
@@ +57,5 @@
> };
> };
>
> /**
> + * A BreakpointActorMap is a map from locations to instances of BreakpointActor.
I think "instances of" can go here, and elsewhere.
@@ +92,5 @@
> + * BreakpointActorMap.
> + *
> + * @returns Number
> + * The number of instances of BreakpointACtor in this instance of
> + * BreakpointActorMap.
Yeah, let's drop "instance" as much as possible. It's totally okay to use the category noun ("BreakpointActorMap"; "BreakpointActor") to refer to instances of the category.
@@ +177,5 @@
> + * @param BreakpointActor actor
> + * The instance of BreakpointActor to be set to the given location.
> + */
> + setActor: function (location, actor) {
> + let { source, line, column } = location;
Do we want a dbg_assert(source.actor) here, to avoid creating entries named "undefined" in the case of a bug which would be a compile-time error in a reasonable language?
@@ +208,5 @@
> },
>
> /**
> + * Delete the instance of BreakpointActor from the given location in this
> + * instance of BreakpointActorMap.
Instances everywhere!
Attachment #8529615 -
Flags: review?(nfitzgerald) → review+
Updated•10 years ago
|
Attachment #8527684 -
Attachment is obsolete: true
Attachment #8527684 -
Flags: review?(nfitzgerald)
Assignee | ||
Comment 28•10 years ago
|
||
Whiteboard: [leave-open]
Comment 29•10 years ago
|
||
(In reply to Eddy Bruel [:ejpbruel] from comment #28)
> https://hg.mozilla.org/integration/fx-team/rev/49218eb557ad
sorry had to back this out for test failures like https://treeherder.mozilla.org/ui/logviewer.html#?job_id=1343284&repo=fx-team
Flags: needinfo?(ejpbruel)
Assignee | ||
Comment 30•10 years ago
|
||
Sorry about that, this patch should work, I forgot to include a single line fix.
This patch should work:
https://hg.mozilla.org/integration/fx-team/rev/8c23d884cd91
Flags: needinfo?(ejpbruel)
Comment 31•10 years ago
|
||
Assignee | ||
Comment 32•10 years ago
|
||
The BreakpointStore uses locations with a source property, representing a source form. In most other places, we use locations with a sourceActor property, representing a source actor. This inconsistenty leads to confusion, so this patch refactors the breakpoint store to use the latter form as well.
Attachment #8532127 -
Flags: review?(nfitzgerald)
Comment 33•10 years ago
|
||
Comment on attachment 8529616 [details] [diff] [review]
Step 9. Clean up the BreakpointActorMap implementation (rebased)
Review of attachment 8529616 [details] [diff] [review]:
-----------------------------------------------------------------
::: toolkit/devtools/server/actors/script.js
@@ +61,5 @@
> * A BreakpointActorMap is a map from locations to instances of BreakpointActor.
> */
> function BreakpointActorMap() {
> this._size = 0;
> + this._actors = Object.create(null);
We had nice comments describing the form of the data we stored before, and I'd love to continue having a comment here. Something that tells me what the object keys are and what the form of the values are.
@@ +96,5 @@
> }
> + }
> + else {
> + for (let key of Object.keys(object)) {
> + yield key;
Instead of for/of + yield each item manually, you can just do:
yield* Object.keys(object);
@@ +102,5 @@
> }
> }
> +
> + query.actor = query.source ? query.source.actor : undefined;
> + query.beginColumn = query.column ? query.column : undefined;
This can just be
query.beginColumn = query.column;
@@ +103,5 @@
> }
> +
> + query.actor = query.source ? query.source.actor : undefined;
> + query.beginColumn = query.column ? query.column : undefined;
> + query.endColumn = query.column ? query.column + 1 : undefined;
Rather than modifying the in param `query`, let's just declare these as variables.
@@ +151,5 @@
> setActor: function (location, actor) {
> let { source, line, column } = location;
>
> + let beginColumn = column ? column : 0;
> + let endColumn = column ? column + 1 : Infinity;
What if column is 0? Shouldn't these tests be `column != null`?
@@ +184,3 @@
>
> + let beginColumn = column ? column : 0;
> + let endColumn = column ? column + 1 : Infinity;
column != null?
@@ +193,5 @@
> + }
> + delete this._actors[source.actor][line][beginColumn][endColumn];
> + if (Object.keys(this._actors[source.actor][line][beginColumn]).length === 0) {
> + delete this._actors[source.actor][line][beginColumn];
> + }
I think this stuff can (and should?) be moved into the block above that is decrementing this._size.
Attachment #8529616 -
Flags: review?(nfitzgerald) → review+
Comment 34•10 years ago
|
||
Comment on attachment 8532127 [details] [diff] [review]
Step 10. Use source actors instead of source forms in locations
Review of attachment 8532127 [details] [diff] [review]:
-----------------------------------------------------------------
::: toolkit/devtools/server/actors/script.js
@@ +2948,5 @@
> }
>
> const { line, entryPoints } = result;
> const actualLocation = line !== location.line
> + ? { sourceActor: this, line }
funky indent
Attachment #8532127 -
Flags: review?(nfitzgerald) → review+
Updated•10 years ago
|
Flags: needinfo?(nfitzgerald)
Assignee | ||
Comment 35•10 years ago
|
||
Comment 36•10 years ago
|
||
Flags: in-testsuite+
Assignee | ||
Comment 37•10 years ago
|
||
Assignee | ||
Comment 38•10 years ago
|
||
Try run for step 10:
https://treeherder.mozilla.org/ui/#/jobs?repo=try&revision=799ba1f19719
Assignee | ||
Comment 39•10 years ago
|
||
Ugh, for some reason that try push contains an empty patch :-S
Second attempt:
https://treeherder.mozilla.org/#/jobs?repo=try&revision=3d65a044c02a
Assignee | ||
Comment 40•10 years ago
|
||
Try looks green, pushing to fx-team:
https://hg.mozilla.org/integration/fx-team/rev/f82edb30c66d
Whiteboard: [leave-open]
Comment 41•10 years ago
|
||
Status: NEW → RESOLVED
Closed: 10 years ago
Resolution: --- → FIXED
Target Milestone: --- → Firefox 38
Updated•7 years ago
|
Product: Firefox → DevTools
You need to log in
before you can comment on or make changes to this bug.
Description
•