Skip to content
This repository was archived by the owner on Sep 21, 2021. It is now read-only.

AST scopes/binding to names mapping.#624

Merged
jasonLaster merged 1 commit intofirefox-devtools:masterfrom
yurydelendik:original-bindings
Sep 7, 2017
Merged

AST scopes/binding to names mapping.#624
jasonLaster merged 1 commit intofirefox-devtools:masterfrom
yurydelendik:original-bindings

Conversation

@yurydelendik
Copy link
Contributor

Associated Issue: firefox-devtools/debugger#3427

Summary of Changes

  • merges parsed scopes in generated source with names information from source maps

Test Plan

See firefox-devtools/debugger#3817

@yurydelendik yurydelendik changed the title [WIP] AST scopes/binding to names mapping. AST scopes/binding to names mapping. Aug 31, 2017
// these positions, check what original name is listed there. To make things
// more interesting the same generated name can potentially be used for
// different original names.
let trackedBindings: { [string]:
Copy link
Contributor

Choose a reason for hiding this comment

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

This type should be defined above:

let trackedBindings: TrackedBindings

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Copy link
Contributor

@codehag codehag left a comment

Choose a reason for hiding this comment

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

Hey yury, this looks super interesting. I made some comments but take them with a grain of salt for now -- i need to take another look at it to make sure that i got it right. The main improvement I could see here is readability and by extension, maintainability; When i revisit it tomorrow I will try to give better comments

// these positions, check what original name is listed there. To make things
// more interesting the same generated name can potentially be used for
// different original names.
let trackedBindings: { [string]:
Copy link
Contributor

Choose a reason for hiding this comment

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

we should use const instead of let when the variable doesnt get reassigned. It makes it easier to read the code, otherwise I end up searching for where trackBindings is reassigned.

It looks like this can be changed for all instances of let in this block of code. There is no reassignment of the objects, only modification of their properties.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

!originalPositionTest ||
originalPositionTest.name !== originalPosition.name ||
originalPositionTest.column !== originalPosition.column ||
originalPositionTest.line !== originalPosition.line
Copy link
Contributor

Choose a reason for hiding this comment

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

from line 231 - 241 -- we should factor this out into a function

function isExactPosition(originalPosition, position) {
  const originalPositionTest = map.originalPositionFor({
    line: position.line,
    column: position.column,
    bias: SourceMapConsumer.LEAST_UPPER_BOUND
  });
  return (
    !originalPositionTest ||
    originalPositionTest.name !== originalPosition.name ||
    originalPositionTest.column !== originalPosition.column ||
    originalPositionTest.line !== originalPosition.line
  );
}

and do if (!isExactPosition(originalPosition, position) here

if (
!originalPosition ||
!originalPosition.name ||
originalPosition.name === name
Copy link
Contributor

Choose a reason for hiding this comment

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

it would be good to save this boolean in a variable with a descriptive name; or we can extract this into a function

bestLocation: Location
}
} = Object.create(null);
Object.keys(scope.bindings).forEach(name => {
Copy link
Contributor

Choose a reason for hiding this comment

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

can you tell me a bit about what you put into this loop, and what should be the result when this loop is finished? I imagine at the moment that we have the following signature

Array<scopeBindingName> -> Map<name, validBestLocation>

and discarding cases of a scope name where there is no bestLocation

if this is the case, a reduce would fit better here; it would be more explicit what is happening

} = Object.create(null);
Object.keys(scope.bindings).forEach(name => {
const positions = scope.bindings[name];
positions.forEach(position => {
Copy link
Contributor

Choose a reason for hiding this comment

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

I suspect the same thing is happening here, where we have the following signature
Array<Position> -> Map<name, bestLocation>

And a reduce might be more explicit; we could have something like this:

const bestLocations = positions.reduce(findBestLocations(name))

and findBestLocation does (name) -> (bindings, Position) -> Map<name, bestLocation>

Copy link
Contributor

@jasonLaster jasonLaster Sep 1, 2017

Choose a reason for hiding this comment

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

positions.reduce might need the initial value positions.reduce(position => findBestLocations(position), {})

Copy link
Contributor Author

Choose a reason for hiding this comment

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

find best location shall be performed across all generated names -- I reviewed summary comment before algorithm and provided example why things are done the way they are.

});

let bindings: { [string]: string } = Object.create(null);
Object.keys(trackedBindings).forEach(name => {
Copy link
Contributor

Choose a reason for hiding this comment

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

this should be a reduce as well

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed here and above.

Copy link
Contributor

@jasonLaster jasonLaster left a comment

Choose a reason for hiding this comment

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

one style nit. @codehag is reviewing for accuracy :P

return originalPosition.name;
}

async function getLocationScopes(
Copy link
Contributor

Choose a reason for hiding this comment

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

I think getLocationScope is sufficiently complex to justify its own module with some helper methods.

function appendTrackedBinding() {}

function appendBinding() {}

function getScopeBindings() {}

function getLocationScope() {

  generatedSourceScopes.map(scope => {
    // ...
    const trackedBindings = bindings.reduce(appendTrackedBinding, {})
    const bindings = trackedBindings.reduce(appendBinding, {});
    return {
      type: scope.type,
      bindings,
    };
  })
}

module.exports = { getLocationScope}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

appendTrackedBinding and appendBinding extraction is not possible due to dependency on location/currentPosition.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

getLocationScope also depends on _getSourceMap

Copy link
Contributor

@jasonLaster jasonLaster Sep 1, 2017

Choose a reason for hiding this comment

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

we might need to do a bit more splitting to share _getSourceMap between modules.

we also might need to pass in the closure state:

bindings.reduce(binding => appendTrackedBinding(binding, location, ...), {})

If this feels like too much we can punt on it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Did not extracted _getSourceMap -- just exposed it from ./source-map

Moved scopes into separate file and refactored to have appendTrackedBinding.

@yurydelendik yurydelendik force-pushed the original-bindings branch 2 times, most recently from 99169f0 to 6c6bdb4 Compare September 5, 2017 23:17
@jasonLaster
Copy link
Contributor

I think it would be nice to switch to using a fixtures:

  1. we can visualize the mappings
  2. easily regenerate them
  3. be sure we didn't hardcode a bug in the mapping

Here is a commit where i setup uglify to make to do the minifying.

);
}

async function getLocationScopes(
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd like jsdoc for all new exported functions. Lack of documentation is one of the bigger problems in this entire module already.

Copy link
Contributor

@codehag codehag left a comment

Choose a reason for hiding this comment

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

this looks good from my side

Copy link
Contributor

@jasonLaster jasonLaster left a comment

Choose a reason for hiding this comment

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

👍

column: 1
},
name: "zero"
});
Copy link
Contributor

Choose a reason for hiding this comment

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

we can clean this up after we add the parser code and can improve the test in one pass.

@jasonLaster jasonLaster merged commit 0d0695d into firefox-devtools:master Sep 7, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants