More advanced original source scopes mapping.#4521
More advanced original source scopes mapping.#4521jasonLaster merged 2 commits intofirefox-devtools:masterfrom
Conversation
3a11f5c to
69d857a
Compare
There was a problem hiding this comment.
The next step is to add some unit tests, which will help us refactor more confidently and not regress w/ this complicated logic.
I think that these utils might be happier in the mappings library where they can take advantage of the other utils to get good input: i.e.
describe("synthesizeScopes", () => {
it("basic", () => {
const fixture = getFixture("sum")
const mappings ..
const scopes ...
// we'll need to fake the frame and pause packets, but we don't have a good mechanism to do that in dbg.html either
expect(synthesizeScopes(scope, frame, pause)).toMatchSnapshot();
})
})69d857a to
a5b2d25
Compare
2d67e7a to
f89a920
Compare
f89a920 to
a2ac5d1
Compare
a2ac5d1 to
485e63c
Compare
jasonLaster
left a comment
There was a problem hiding this comment.
left some style comments…
src/actions/sources.js
Outdated
|
|
||
| await dispatch(loadSourceText(generatedSource)); | ||
| originalSources.forEach(async source => { | ||
| await dispatch(loadSourceText(source)); |
There was a problem hiding this comment.
why is this necessary? It could be a very expensive operation
src/utils/scopes.js
Outdated
| : L10N.getStr("anonymous"); | ||
| } else { | ||
| title = L10N.getStr("scopes.block"); | ||
| } |
There was a problem hiding this comment.
perhaps a small helper for getting the scope title
function getScopeTitle(...) {
if (type === "function") {
// FIXME Use original function name here
const lastGeneratedScope = generatedScopes[generatedScopes.length - 1];
const isLastGeneratedScopeFn =
lastGeneratedScope && lastGeneratedScope.type === "function";
title =
isLastGeneratedScopeFn && lastGeneratedScope.function.displayName
? simplifyDisplayName(lastGeneratedScope.function.displayName)
: L10N.getStr("anonymous");
} else {
title = L10N.getStr("scopes.block");
}
}
src/utils/scopes.js
Outdated
| scopeIndex, | ||
| lastScopeIndex: syntheticScopes.scopes.length - 1, | ||
| generatedScopes, | ||
| foundGeneratedNames: Object.create(null), |
There was a problem hiding this comment.
style nit: foundGeneratedNames: {}
src/utils/scopes.js
Outdated
| path: `${key}/${generatedName}`, | ||
| contents: { value: { type: "undefined" } } | ||
| }); | ||
| }); |
There was a problem hiding this comment.
can we move this to a small helper:
function findOriginalBindings(...) {
let vars = [];
bindingsNames.forEach(name => {
// Find binding name in the original source bindings
const generatedScope = generatedScopes.find(
gs => gs.sourceBindings && name in gs.sourceBindings
);
if (!generatedScope || !generatedScope.sourceBindings) {
return;
}
// .. and map it to the generated name
const generatedName = generatedScope.sourceBindings[name];
// Skip if we already use the generated name
if (generatedName && !foundGeneratedNames[generatedName]) {
if (generatedScope.bindings.variables[generatedName]) {
vars.push({
name,
generatedName,
path: `${key}/${generatedName}`,
contents: generatedScope.bindings.variables[generatedName]
});
foundGeneratedNames[generatedName] = true;
return;
}
const arg = generatedScope.bindings.arguments.find(
arg_ => arg_[generatedName]
);
if (arg) {
vars.push({
name,
generatedName,
path: `${key}/${generatedName}`,
contents: arg[generatedName]
});
foundGeneratedNames[generatedName] = true;
return;
}
}
vars.push({
name,
generatedName,
path: `${key}/${generatedName}`,
contents: { value: { type: "undefined" } }
});
});
}
src/utils/scopes.js
Outdated
| }); | ||
| } | ||
| return acc; | ||
| } |
There was a problem hiding this comment.
synthesize scope is complicated enough where it takes some time to see what it is trying to do. I think it would be nice to extract the "real" work so that the code flow is more readable.
function synthesizeScope(
acc: SynthesizeScopeContext,
syntheticScope: SyntheticScope,
index: number
): SynthesizeScopeContext {
let bindings = findOriginalBindings(...)
if (isLast) {
bindings = [...bindings, ...findUnusedBindings(...)]
}
if (isFirst) {
bindings = [...bindings, ...getSpecialVariables(...)]
// ...
}
if (bindings.length > 0) {
const title = getScopeTitle(...);
result.push({
name: title,
path: key,
contents: vars
});
}
return acc
}
src/utils/scopes.js
Outdated
| s = s.parent; | ||
| } | ||
|
|
||
| const { result } = syntheticScopes.scopes.reduce(synthesizeScope, { |
There was a problem hiding this comment.
I think the fact that synthesizeScope is mutating makes synthesize a bit more complicated... perhaps, we could have it return the bindings...
const { result } = syntheticScopes.scopes.reduce((bloby, syntheticScope, index) => {
const bindings = getBindingsFromScope(bloby,..)
// it's tempting to update foundGeneratedNames here too so that `getBindingsFromScope` is pure,
// but not sure if that is needed...
if (bindings.length) {
const title = getScopeTitle(...);
blob.result.push({
name: title,
path: key,
contents: vars
});
}
}, blobOStuff)
src/utils/scopes.js
Outdated
| }); | ||
| } | ||
| return acc; | ||
| } |
There was a problem hiding this comment.
It would be nice to create the synthetic scope bindings in the map-bindings space. This would be closer to the data structure we'd get from source-maps
- we can add the special vars in the debugger side
4ebcba5 to
0578470
Compare
jasonLaster
left a comment
There was a problem hiding this comment.
This is looking really good
| return generatedScopes; | ||
| }, | ||
| async getOriginalSourceScopes(location) { | ||
| return getScopes(location); |
There was a problem hiding this comment.
lets first load the source here...
There was a problem hiding this comment.
How can we track if we already loaded the source?
There was a problem hiding this comment.
we can call await loadSourceText and if it is loaded it will come back quickly
| ); | ||
|
|
||
| if (sourceRecord.get("isWasm")) { | ||
| if (generatedSourceRecord.get("isWasm")) { |
There was a problem hiding this comment.
should we move this down below add_scopes. can we move this to a different PR? b/c wasm is different and scary for some :)
0578470 to
89b30ce
Compare
|
I ran a quick test with this and wanted to verify what I'm seeing. ES6 Source: Transpiled source: When pausing on line 4, I expected Thoughts? |
|
@ryanjduffy could you "remix" the dbg example: http://dbg.glitch.me/ or... if it is using babel, publish the example somewhere else... maybe devtools-examples. |
I would rather to see it as a complete example online with source maps. I wonder if babel spits out names. |
|
i'll look for a codepen that has babel |
Based on debugger.html logic at https://github.com/devtools-html/debugger.html/blob/60c59811df6e82ab7831d8faab4cee23ec48bd63/src/utils/scopes.js#L173-L179 we place |
|
Here's a live version of the sample: https://ryanjduffy.github.io/debugger-examples/scopes/ |
|
(The devtools/client/shared/source-map/worker.js needs to be updated to address the issue above, works in debugger.html now) |
@ryanjduffy the names section is empty at https://ryanjduffy.github.io/debugger-examples/scopes/scopes-compiled.js.map , what did you use to generate the .map file? |
ca06ec1 to
dc0f31a
Compare
|
Babel cli |
dc0f31a to
e94870d
Compare
| return L10N.getStr("scopes.block"); | ||
| } | ||
|
|
||
| export function translateScope( |
There was a problem hiding this comment.
does the name translate mean anything special?
would getScope, which is parallel to getScopes work?
c89600c to
5ff01ad
Compare
5ff01ad to
172677a
Compare
172677a to
25c2fe5
Compare

Associated Issue: Display synthesized scopes (e.g. ES6) instead of transpiled
Test case:
return na < nb ? -1 : na > nb ? 1 : 0;)