Skip to content
This repository was archived by the owner on Jan 11, 2023. It is now read-only.

More advanced original source scopes mapping.#4521

Merged
jasonLaster merged 2 commits into
firefox-devtools:masterfrom
yurydelendik:advanced-scopes
Nov 17, 2017
Merged

More advanced original source scopes mapping.#4521
jasonLaster merged 2 commits into
firefox-devtools:masterfrom
yurydelendik:advanced-scopes

Conversation

@yurydelendik

Copy link
Copy Markdown
Contributor

Associated Issue: Display synthesized scopes (e.g. ES6) instead of transpiled

Test case:

  1. Open https://yurydelendik.github.io/old-man-sandbox/sorted-es6/test.html in debugger
  2. Set breakpoint at webpack/sorted.js line 9 (return na < nb ? -1 : na > nb ? 1 : 0;)
  3. Reload the page

@yurydelendik yurydelendik force-pushed the advanced-scopes branch 2 times, most recently from 3a11f5c to 69d857a Compare October 27, 2017 23:42

@jasonLaster jasonLaster left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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();
  })

})

@yurydelendik yurydelendik force-pushed the advanced-scopes branch 4 times, most recently from 2d67e7a to f89a920 Compare November 1, 2017 22:22
@yurydelendik yurydelendik changed the title [WIP] More advanced original source scopes mapping. More advanced original source scopes mapping. Nov 1, 2017

@jasonLaster jasonLaster left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left some style comments…

Comment thread src/actions/sources.js Outdated

await dispatch(loadSourceText(generatedSource));
originalSources.forEach(async source => {
await dispatch(loadSourceText(source));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this necessary? It could be a very expensive operation

Comment thread src/utils/scopes.js Outdated
: L10N.getStr("anonymous");
} else {
title = L10N.getStr("scopes.block");
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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");
  }
}

Comment thread src/utils/scopes.js Outdated
scopeIndex,
lastScopeIndex: syntheticScopes.scopes.length - 1,
generatedScopes,
foundGeneratedNames: Object.create(null),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style nit: foundGeneratedNames: {}

Comment thread src/utils/scopes.js Outdated
path: `${key}/${generatedName}`,
contents: { value: { type: "undefined" } }
});
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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" } }
     });
   });
}

Comment thread src/utils/scopes.js Outdated
});
}
return acc;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
}

Comment thread src/utils/scopes.js Outdated
s = s.parent;
}

const { result } = syntheticScopes.scopes.reduce(synthesizeScope, {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Comment thread src/utils/scopes.js Outdated
});
}
return acc;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@yurydelendik yurydelendik force-pushed the advanced-scopes branch 4 times, most recently from 4ebcba5 to 0578470 Compare November 8, 2017 22:57

@jasonLaster jasonLaster left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking really good

Comment thread src/utils/pause.js
return generatedScopes;
},
async getOriginalSourceScopes(location) {
return getScopes(location);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets first load the source here...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can we track if we already loaded the source?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can call await loadSourceText and if it is loaded it will come back quickly

);

if (sourceRecord.get("isWasm")) {
if (generatedSourceRecord.get("isWasm")) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ryanjduffy

ryanjduffy commented Nov 10, 2017

Copy link
Copy Markdown
Contributor

I ran a quick test with this and wanted to verify what I'm seeing.

ES6 Source:

function test () {
	let a = 1;
	if (a) {
	  const b = 2;
	}
}

document.addEventListener('click', test);

Transpiled source:

'use strict';

function test() {
	var a = 1;
	if (a) {
		var b = 2;
	}
}

document.addEventListener('click', test);

//# sourceMappingURL=scopes-compiled.js.map

When pausing on line 4, const b = 2;, both a and b show within the test scope and only <this> within the if block.

I expected <this> to be restricted to function scope and b to be within the block scope.

Thoughts?

@jasonLaster

jasonLaster commented Nov 10, 2017

Copy link
Copy Markdown
Contributor

@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.

@yurydelendik

Copy link
Copy Markdown
Contributor Author

maybe devtools-examples.

I would rather to see it as a complete example online with source maps. I wonder if babel spits out names.

@jasonLaster

Copy link
Copy Markdown
Contributor

i'll look for a codepen that has babel

@yurydelendik

yurydelendik commented Nov 10, 2017

Copy link
Copy Markdown
Contributor Author

I expected to be restricted to function scope and b to be within the block scope.

Based on debugger.html logic at https://github.com/devtools-html/debugger.html/blob/60c59811df6e82ab7831d8faab4cee23ec48bd63/src/utils/scopes.js#L173-L179 we place <this> on the selected scope.

@ryanjduffy

Copy link
Copy Markdown
Contributor

Here's a live version of the sample: https://ryanjduffy.github.io/debugger-examples/scopes/

@yurydelendik

Copy link
Copy Markdown
Contributor Author

(The devtools/client/shared/source-map/worker.js needs to be updated to address the issue above, works in debugger.html now)

@yurydelendik

Copy link
Copy Markdown
Contributor Author

Here's a live version of the sample: https://ryanjduffy.github.io/debugger-examples/scopes/

@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?

@ryanjduffy

Copy link
Copy Markdown
Contributor

Babel cli

return L10N.getStr("scopes.block");
}

export function translateScope(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does the name translate mean anything special?

would getScope, which is parallel to getScopes work?

@jasonLaster jasonLaster force-pushed the advanced-scopes branch 4 times, most recently from c89600c to 5ff01ad Compare November 17, 2017 22:41
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants