Skip to content
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

C++: Add an abstract class that can be used to extend viableCallable #14482

Merged
merged 5 commits into from Oct 17, 2023

Conversation

MathiasVP
Copy link
Contributor

@MathiasVP MathiasVP commented Oct 12, 2023

This is motivated by a use-case from @bdrodes. It's now possible to extend the class AdditionalCallTarget to specify additional call targets. For example, given this snippet:

void sink(int);
int source();

void f(int);

void g(int x) {
  sink(x);
}

void test() {
  int x = source();
  f(x);
}

there's normally no flow from source to sink, but if we extend AdditionalCallTarget like:

class MyAdditionalCallTarget extends DataFlow::AdditionalCallTarget {
  override Function viableTarget(Call call) {
    call.toString() = "call to f" and
    result.hasName("g")
  }
}

then we now get flow because the call to f is resolved to target g.

Before merging this PR I'd like to confirm with @bdrodes that this solves the problem he's having. Once that's done I'll add a test.

Note that since this provides an abstract class that modifies a cached predicate any class that extends AdditionalCallTarget should be imported globally in all queries to prevent re-evaluation of cached predicates.

@MathiasVP MathiasVP requested a review from a team as a code owner October 12, 2023 09:40
@github-actions github-actions bot added the C++ label Oct 12, 2023
Copy link
Contributor

@geoffw0 geoffw0 left a comment

Choose a reason for hiding this comment

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

call.toString() = "call to f"

In your PR description should be something like call.getTarget().getName() = "f" (assuming this isn't circular). The PR description doesn't really matter, but if we add a test or documentation we should set a good example.

Note that since this provides an abstract class that modifies a cached predicate any class that extends AdditionalCallTarget should be imported globally in all queries to prevent re-evaluation of cached predicates.

This should be stated in the QLDoc for the class.

It would also be nice to have an example in that QLDoc - I find the name AdditionalCallTarget is almost but not quite entirely self-explanatory.

@MathiasVP
Copy link
Contributor Author

In your PR description should be something like call.getTarget().getName() = "f" (assuming this isn't circular). The PR description doesn't really matter, but if we add a test or documentation we should set a good example.

Agreed. This was really just me posting an example of how it could be used. I agree that we shouldn't be using toString for this kind of logic 😅.

Note that since this provides an abstract class that modifies a cached predicate any class that extends AdditionalCallTarget should be imported globally in all queries to prevent re-evaluation of cached predicates.

This should be stated in the QLDoc for the class.

It would also be nice to have an example in that QLDoc - I find the name AdditionalCallTarget is almost but not quite entirely self-explanatory.

Sure, I can add this. For now I just stole the QLDoc from the Ruby draft PR. Once I have 👍 from @bdrodes that this does what he would like, I'l be happy to expand on the QLDoc.

@bdrodes
Copy link
Contributor

bdrodes commented Oct 16, 2023

@MathiasVP and I chatted about this in a separate call. It appears the solution will work for our purposes, but there is still some inefficiency in resolving calls with this mechanic. This PR at least addresses the issue better than before, but we may have to re-assess resolving targets in the future. For now, I've found a workaround for my specific problem to get performance to something reasonable.

The TLDR of the issue I had seen is in some large code bases, even resolving a small number of calls and using this solution can result in increased run times from ~1-2 hours to 10+ hours. This is still better than my prior observations of 17+ hours without this PR. Again, I've reduced my problem in other ways not relevant for this discussion such that combining this solution leads to very efficient performance, but with minor changes, run times would drastically increase.

@MathiasVP
Copy link
Contributor Author

@MathiasVP and I chatted about this in a separate call. It appears the solution will work for our purposes, but there is still some inefficiency in resolving calls with this mechanic.

Just to clear up any confusion for other people reading this: resolving the calls isn't the inefficient part. Rather, the issue we ran into with @bdrodes was that the additional calls resolved with his changes causes performance issues.

Copy link
Contributor

@geoffw0 geoffw0 left a comment

Choose a reason for hiding this comment

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

QLDoc LGTM.

Still deserves a test as well I think.

@MathiasVP
Copy link
Contributor Author

Still deserves a test as well I think.

Oh yes, sure. I forgot about that. Let me add one real quick.

@MathiasVP
Copy link
Contributor Author

9a2c1da adds a test for this.

Copy link
Contributor

@geoffw0 geoffw0 left a comment

Choose a reason for hiding this comment

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

LGTM. :)

@MathiasVP MathiasVP merged commit bbf9bcd into github:main Oct 17, 2023
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants