-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JS: Add library for exporting graphs as type models #15386
Conversation
4751f68
to
2f8bbb0
Compare
9042409
to
d2a3093
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good 👍
I only have a few minor questions/comments.
javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsExport.qll
Outdated
Show resolved
Hide resolved
javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsExport.qll
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall this work LGTM 👍 There are some points I would like to understand better though (see inline discussions).
While reading through the commits I noticed that javascript/ql/test/library-tests/ModelGeneration/ModelGeneration.expected
is nearing a level of complexity where I'm starting to think inline-expectation-tests would pay off
* Holds if the edge `pred -> succ` labelled with `path` exists in the API graph. | ||
*/ | ||
bindingset[pred] | ||
predicate apiGraphHasEdge(API::Node pred, string path, API::Node succ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we have any guarantees (or tests) to show that the edge names we generate here are valid MaD access-paths?
Say we changed Member[xxx]
to be Property[xxx]
in the rest of our MaD modeling, would the whole type-model export just silently produce models that couldn't be parsed until a human realized there was a problem? 🤔
I can imagine scenarios where we try to parse the access-path generated as if it had been in a YAML file, maybe even checking that if we start from a node that we generate an MaD access-path for, we will reach the same node from parsing and following that access-path
(I realize it's not so much a question about this specific predicate, as a thing in whole... and the question might be answered from reading some of the next commits)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No guarantees and no currently no tests. I spent a bit of time refactoring the ModelOutput::getAWarning
into a parameterised module so we could check these as well, but it gets a bit fiddly. I backed out of it for now.
Some tests are going to exist in an internal repo where we use model generation, in the sense that those tests would fail if there was a mismatch here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be nice with these kinds of tests, however I'm not going to block this PR to get them in NOW.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
however I'm going to block this PR to get them in NOW.
I assume a "not" was accidentally left out of that sentence 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indeed 🙈 (fixed by edit)
javascript/ql/test/library-tests/ModelGeneration/ModelGeneration.expected
Show resolved
Hide resolved
* Holds if `name` is a good name for `node` that should be used in case the node needs | ||
* to be named with a type name. | ||
* | ||
* Should not hold for nodes that are named via `exposedName`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sounds like we could make some consistency check for this 😊
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But that would be more work 😱
javascript/ql/lib/semmle/javascript/frameworks/data/ModelsAsData.qll
Outdated
Show resolved
Hide resolved
| (return-this).FluentInterface.prototype | (return-this).FluentInterface.prototype.bar | ReturnValue | | ||
| (return-this).FluentInterface.prototype | (return-this).FluentInterface.prototype.baz | ReturnValue | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is there not this row?
| (return-this).FluentInterface.prototype | (return-this).FluentInterface.prototype.foo | ReturnValue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh, good question. The flow is captured by the summary model so it shouldn't be a problem, but it is a bit strange that it's generated for bar,baz
but not foo
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't quite know what to say around this. I think it would be nice if we could at least explain why it's happening, but I might be a little too cautious around this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I figured it out, turns out this was due to a bug in API graphs. See 3c885f3.
Nice catch, btw!
javascript/ql/test/library-tests/ModelGeneration/ModelGeneration.expected
Show resolved
Hide resolved
javascript/ql/test/library-tests/ModelGeneration/ModelGeneration.ext.yml
Show resolved
Hide resolved
…ta.qll Co-authored-by: Rasmus Wriedt Larsen <[email protected]>
Co-authored-by: Rasmus Wriedt Larsen <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just a few qldoc suggestions
} | ||
|
||
/** | ||
* Holds if a named type exists or will be generated for `node`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* Holds if a named type exists or will be generated for `node`. | |
* Holds if a named type exists or will be generated for `node`, and `node` doesn't have a pretty name. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* Holds if a named type exists or will be generated for `node`. | |
* Holds if a synthetic name must be generated for `node`. |
Actually the existing parts of the qldoc was wrong, let me just fix that.
Co-authored-by: Rasmus Wriedt Larsen <[email protected]>
javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsExport.qll
Outdated
Show resolved
Hide resolved
This only worked when the RHS was a SourceNode, which is not generally the case
Thanks for the reviews everyone! |
Adds a parameterised module
GraphExport
which converts an arbitrary graph labelled with access paths, into a set oftypeModel
rows. This module doesn't really know about API graphs, it just sees its input as a directed graph, which simplified the logic a lot IMO.In
ApiGraphModelsExport.qll
we defined a wrapper aroundGraphExport
which additionally re-exportedtypeModels
from upstream packages (see example below). This is complicated enough that it's nice to factor it out, and it also know a bit more about API nodes and currently needs access to the otherApiGraphModels*.qll
files.Lastly, we expose a JS-specific module
ModelExport
which exports the paths leading to a specific set of API nodes. The idea is that each dynamic language eventually adds its own version ofModelExport
which uses the above shared components under the hood. I've prototyped one for Ruby in this draft PR, but I'd like to move ahead with just JS for now.Re-exporting type information
This also supports re-exporting information about types from upstream libraries.
This part is contained in the second commit, and to manage complexity, it is implemented as a wrapper module around
GraphExport
.Below are a few examples to demonstrate what it means to re-export type information and some of the complexity involved.
JavaScript example 1
For example, if the following is the entry point for a package
bar
:then this would generate the following type model:
That is, we export the fact that
require('bar').xxx
is an alias for thefoo
package.JavaScript example 2
For a more complex case, suppose the following type model exists:
And the package exports something that matches a prefix of the access path above:
This would result in the following type model:
That is, we export the fact that
require('bar').blah.z
is an instance offoo.XYZ
.Notice that the access path
Member[blah].Member[z]
consists of an access path generated from the APIgraph, with pieces of the access path from the original type model appended to it.