[Breakpoints] + [SourceMaps] breakpoints are out of sync with the server on reload #3039
Conversation
75ea4ed to
4853833
Compare
Codecov Report
@@ Coverage Diff @@
## master #3039 +/- ##
==========================================
+ Coverage 66.15% 66.56% +0.41%
==========================================
Files 74 75 +1
Lines 2603 2668 +65
Branches 527 541 +14
==========================================
+ Hits 1722 1776 +54
- Misses 881 892 +11
Continue to review full report at Codecov.
|
|
|
||
| const breakpoint = _createBreakpoint(location, referenceBreakpoint); | ||
|
|
||
| let promise = addClientBreakpoint( |
There was a problem hiding this comment.
I'm a bit confused, so I'll walk through the bug and two workflows as I see them. I could be missing something, it could be what you're already doing too. I'll outline a simple case and syncing case for both workflows. To be clear, we can do the simple case and fix the bug.
Bug
- User is working on an app that uses source maps (i.e. namely bundle.js w/ file a.js, b.js, c.js)
- User debugs the app and adds a bp to b.js line 12, with the generated location bundle.js#45
- User adds code to a.js
- User reloads the app and see the debugger paused on b.js line 4, which happens to line up with the generated location bundle.js#45!
We will fix this, either simply moving the breakpoint to b.js line 4 when the debugger reloads or ideally re-removing the breakpoint at line bundle.js#45 and adding a breakpoint at the new location...
simple case
- User adds a Breakpoint
- addBreakpoint is called with an original location
- generated location is calculated based on the source's sourcemap
- a pending breakpoint is added with the generated location
- User reloads page / User re-opens debugger
- breakpoints reducer adds pending breakpoints
- newSource is called and the debugger attempts to sync the breakpoint
- checks if there is a pending breakpoint
- uses the pending bps generated location to recalculate the original location
- adds a new breakpoint to the store
syncing case
- User adds a Breakpoint
- addBreakpoint is called with an original location
- generated location is calculated based on the source's sourcemap
- a pending breakpoint is added with the generated location
- User reloads page / User re-opens debugger
- breakpoints reducer adds pending breakpoints
- newSource is called and the debugger attempts to sync the breakpoint
- checks if there is a pending breakpoint
- uses the pending bps generated location to recalculate the original location
- if the new original location is different from the pending breakpoint original location
- removes the old breakpoint on the server
- adds the new breakpoint to the server
- adds the new breakpoint to the store
| ) { | ||
| console.log("IM CALLED NOW"); | ||
| const location = breakpoint.location; | ||
| await client.removeBreakpoint(referenceBreakpoint.id); |
There was a problem hiding this comment.
how do we know we have an ID? the pending bp doesn't have an id
jasonLaster
left a comment
There was a problem hiding this comment.
This is really great work. When the dust settles we'll have a simpler system.
I left lots of comments to over share.
| import { updateObj } from "../utils"; | ||
| // Return the first argument that is a string, or null if nothing is a | ||
| // string. | ||
| export function firstString(...args) { |
There was a problem hiding this comment.
this can be moved to utils/utils.js
| return location; | ||
| } | ||
| return Object.assign({}, location, { column: undefined }); | ||
| } |
There was a problem hiding this comment.
2 things:
- I think we should do this in
firefox/commands.js#onNewBreakpoint onNewBreakpointshould be moved to firefox/create#createBreakpoint
The rationale for this is to give us consistent data from the server... we did the same thing for source URLs when we worked on pending bps
| return await sourceMaps.getGeneratedLocation(location, source.toJS()); | ||
| } | ||
|
|
||
| async function syncClientBreakpoint( |
There was a problem hiding this comment.
Lets add a function comment, we don't typically have comments but we should lol
There was a problem hiding this comment.
minor style nit, but i move actiony things to options i.e.
syncClientBreakpoint(source, pendingBreakpoint, location, {client, sourceMaps})
There was a problem hiding this comment.
we now have just 4 items, so this should be ok
|
|
||
| const originalLocation = await sourceMaps.getOriginalLocation( | ||
| Object.assign({}, generatedLocation, { | ||
| line: pendingBreakpoint.generatedLocation.line |
There was a problem hiding this comment.
this seems weird to me... can you explain the rationale
There was a problem hiding this comment.
yes. and i agree that this needs to be expressed better. I didn't quite finish fixing this
when the source is reloaded, the source id changes, and i was confused by that. I wasn't sure what the source was so I quickly threw this in to see if it would work. Basically, there is a new connection. This is why we can't delete / find the line after the debuggee is reloaded.
for example,
pendingBreakpoint.generatedLocation
/* {
id: "server1.conn3.child1/34",
line: 51234
} */
generatedLocation //
/* {
id: "server1.conn4.child1/34",
line: 51235
} */I didn't get around to doing this properly, but we now have the source available in this function, and this should actually be:
const originalLocation = await sourceMaps.getOriginalLocation({
id: source.id,
line: pendingBreakpoint.generatedLocation.line
});Also, i don't think we need to save the generated location in full, just the line. will keep hacking away at it
| client, | ||
| sourceMaps, | ||
| pendingBreakpoint, | ||
| location |
There was a problem hiding this comment.
It's not really clear to me if you need location because it's a composite of many things
| } | ||
| return Object.assign({}, args, { status: "done" }); | ||
| }); | ||
| } |
There was a problem hiding this comment.
can we move this try/catch to the firefox/commands side?
|
|
||
| // If the breakpoint is already disabled, we need to communicate | ||
| // with the server, but we also have to remove the breakpoint immediately. | ||
| if (pendingBreakpoint.disabled) { |
There was a problem hiding this comment.
why do we need to talk to the server if it's disabled, i think that means that the server doesn't have a bp?
There was a problem hiding this comment.
Good point here. Originally, my thinking is that we need to verify our breakpoints with the server, and the only way to do that (at the time at least) was to add a breakpoint to the server. If its disabled we need to then remove it. Otherwise we have the weird behavior of disabled breakpoints in places that don't make sense.
Better solution: change the client code so we can get a breakpoint location. I haven't had a chance yet to try to make this better but it feels like it should be possible
c84a0c8 to
6729db6
Compare
jasonLaster
left a comment
There was a problem hiding this comment.
here are some random thoughts
| prefs.debuggerVersion = DEBUGGER_VERSION; | ||
| } | ||
|
|
||
| module.exports = { prefs, setDebuggerVersion }; |
There was a problem hiding this comment.
I think it might be nice to keep the schema version private so that we only manage it here...
if (prefs.debuggerPrefsSchemaVersion != prefsSchemaVersion) {
prefs.clear()
prefs.debuggerPrefsSchemaVersion = prefsSchemaVersion
}There was a problem hiding this comment.
I think the best place to implement the clear function would be in devtool-module -- i added a todo note, shall i make a separate issue?
| } | ||
|
|
||
| // Syncing Methods | ||
| function _optimisticlyAddBreakpoint(state, breakpoint) { |
There was a problem hiding this comment.
nit: not sure if we need the _. I think everything is assumed to be private
| }, | ||
| "location": Object { | ||
| "column": undefined, | ||
| "line": 7, |
There was a problem hiding this comment.
it's weird that generatedLocation and location are different line numbers
There was a problem hiding this comment.
good catch, that might have been a bug later on
| const endBps = selectors.getBreakpoints(getState()); | ||
| const returnedBp = endBps.get(expectedId); | ||
| expect(returnedBp).not.toBe(bp); | ||
| expect(returnedBp.location).toEqual(correctedLocation); |
There was a problem hiding this comment.
might be nice to have a snapshot of returnedBp here as well
| delete bpClients[breakpointId]; | ||
| return bpClient.remove(); | ||
| } catch (_error) { | ||
| console.info("No breakpoint to delete on server"); |
| const location = overrides.location || breakpoint.location; | ||
| const id = makeLocationId(location); | ||
| const updatedOpts = { ...overrides, loading: false }; | ||
| const updatedBreakpoint = updateObj(breakpoint, updatedOpts); |
There was a problem hiding this comment.
I think updateObj has outlived it's purpose.
I'd also try to be more aggressive about what we want to do.
function updateBreakpoint(state, newBreakpoint, oldBreakpoint) {
const id = makeLocationId(newBreakpoint.location)
state.setIn(["breakpoints", id], {
...breakpoint,
...overrides,
loading: false
});
}6729db6 to
bc23496
Compare
| breakpoint, | ||
| [PROMISE]: promise | ||
| }; | ||
|
|
There was a problem hiding this comment.
nit: now that we've simplified this I think we can inline everything
dispatch({ type: SYNC_BREAKPOINT, [PROMISE]: syncClientBreakpoint() })
| ) | ||
| }) | ||
| ); | ||
| return dispatch(action); |
There was a problem hiding this comment.
we can also inline this action too i think
| // TODO: replace this with an implementation from devtools-modules | ||
| prefs.clear = () => { | ||
| prefs.pendingBreakpoints = getDefaultPref("pendingBreakpoints"); | ||
| }; |
There was a problem hiding this comment.
function clearPrefs() {
prefs.pendingBreakpoints = getDefaultPref("pendingBreakpoints");
}| }; | ||
|
|
||
| return defaults[prefName]; | ||
| } |
There was a problem hiding this comment.
I think you can get the default for a pref.
…sync-with-the-server-on-reload' of https://github.com/codehag/debugger.html into bug/2904-breakpoints-sourcemaps-breakpoints-are-out-of-sync-with-the-server-on-reload
…sync-with-the-server-on-reload' of https://github.com/codehag/debugger.html into bug/2904-breakpoints-sourcemaps-breakpoints-are-out-of-sync-with-the-server-on-reload
Associated Issue: #2904
WIP