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

Static call analysis: overwriting and implementing calls #13

Open
pdolland opened this issue Oct 13, 2017 · 9 comments
Open

Static call analysis: overwriting and implementing calls #13

pdolland opened this issue Oct 13, 2017 · 9 comments

Comments

@pdolland
Copy link

In order to see, what may be called from what, two further types of method calls seem to me important: (1.) a method is overwritten by another one. (this is not just a true call, but however, I think java-callgraph could do so, since the overwriting method is executed instead of the overwritten one.)
(2.) an interface method is implemented by a concrete method. (java-callgraph is already listing calls of interface method. So for completeness (respective to the given jars) I want to see also the implementing methods.)

@matthieu-vergne
Copy link
Contributor

These are different kinds of relationships, though. They are not call relationships, and thus should not be mixed with them.

@pdolland
Copy link
Author

pdolland commented Oct 23, 2018 via email

@matthieu-vergne
Copy link
Contributor

I think I see what you mean, and I could understand the need. But it seems to me that such addition would have a significant impact on the tool.

I just proposed a pull request for a better identification of lambdas in #22 (and I am glad it was accepted), but what I did was fix to better identify a one-to-one relationships between the lambda and the method instantiating it.

In your case it is not a one-to-one relationships. If you take the first point, with overriding. Here is an example:

class Root {
  void doSomething() {
    System.out.println("I am root!");
  }
}
class Child1 extends Root {
  @Override
  void doSomething() {
    System.out.println("I am child1!");
  }
}
class Child2 extends Root {
  @Override
  void doSomething() {
    System.out.println("I am child2!");
  }
}

Now you have a method accepting a Root:

void execute(Root root) {
  root.doSomething();
}

The point is that the call graph here would depend on the agument's value:

execute(new Root());
execute(new Child1());
execute(new Child2());

All three are valid calls, but then each of them calls the doSomething() of a different class. You would need to look for all the calls to execute(...) in the JAR, look which type of instance is passed in argument, and consequently add the call in the list. Basically, it would mean that the analysis should say that the method execute() calls all 3.

The second point, with interface implementation, is exactly the same, excepted that Root has no implementation. So you won't have the code details of Root.doSomething() since it does not exist, but the result in terms of execute() calls would be the same.

The problem here is that you cannot do it in a single pass without consuming huge amounts of space (RAM or hard disk, depending on the strategy used). Indeed, execute may be called anywhere in the JAR, and thus you won't know all the calls before you have looked everywhere.

If you do it in 2 passes, then during the first pass you need to store the types of the various instances passed to execute(). You need only one per type, so if you have few override/implement then it is only few, but you need to store them recursively, since the value given to execute() might come from its own caller, which may have received it from its own caller, and so on recursively. You need to store all the types for each level during the first pass (space complexity O(NxM)), and then exploit this information during the second pass to add all the calls relationships. If you it happens to be done to an object that pass more or less everywhere (e.g. a context state), then you may fill your RAM with a huge amount of data.

If you do it in 1 pass, you need to remember instead which methods might be impacted by such behaviours. But since you cannot guess in advance all the override/implement relationships, then you must assume it may happen for any (non-final) type. Which means you need to remember more or less every method you have seen, or said another way store a complete graph of the calls in the JAR. Which means that you need to store at least the same amount of information that you generate in output, which is really costly for huge JARs. More other, you need then to browse this graph when you discover an override/implement relationships, in order to know where to add it. This is one again really costly, but this time in terms of computation time. You would need to use even more space to optimize it.

Briefly, with 1 pass you obtain a tool which consumes more than it generates at the end, and with 2 passes you still need to consume way more than what you currently do. So it might be useful, but the impact would be huge.

Maybe it would make sense in the dynamic analysis (which I am not familiar with and I did not use), but in static analysis it seems to me like a tool killer.

Just too finish on a good note : if it is about adding the override/implement relationships between methods, then it might be OK, because you only need to list them once for all. Then all the calls relationships can be inferred from what is generated in output. So the information would be there. But retrieving the calls themselves... I would not recommend it for this tool. Just make a script/additional tool which builds on the result of the JAR analysis for that.

@gousiosg
Copy link
Owner

java-callgraph was supposed to be a quick-and-dirty call graph generator. Resolving virtual dispatch calls (which is what we are discussing here) is not a trivial problem and other, more advanced tools (such as Soot and Wala) feature multiple implementations of algorithms proposed in scientific literature. I am not sure that we should implement those in java-callgraph.

@pdolland
Copy link
Author

pdolland commented Oct 24, 2018 via email

@gousiosg
Copy link
Owner

Thanks for the comment. I don't disagree with the algorithm you propose, what I am saying is that tools such as Wala and Soot already offer this functionality (actually, more advanced versions of it). It would take me a lot of time (which I don't have currently) to implement this and get it right, so I will probably pass. However, I am open to PRs!

@matthieu-vergne
Copy link
Contributor

matthieu-vergne commented Oct 24, 2018

If it is only about adding the overriding/implementing relationships then it should be doable in one pass with few impacts. This is a similar analysis than I did for the lambdas, so you may look and inspire from the class DynamicCallManager that I wrote for them (and the Cucumber feature tests to ensure it works). Actually, you may add your methods in the same class, since its purpose is to deal with such dynamic analysis.

I would gladly review your pull request if you make one.

@pdolland
Copy link
Author

pdolland commented Jan 4, 2019 via email

@gousiosg
Copy link
Owner

gousiosg commented Jan 7, 2019

Hi Peter, yes I am interested to see a PR that implements the functionality you are discussing here. Please also make sure you update the README file with the new output formats.

pombredanne pushed a commit to pombredanne/java-callgraph that referenced this issue May 29, 2024
* Started commit for heuristic processing

* Update configs

* Add heuristic class GetBest with integration to the Test step for JCallGraph

* Update GetBest to update color values, add hash for compare, and update algo used for path sum (to avoid infinite loop with previous all paths algo).

* Update mph-table.yaml

* Started commit for heuristic processing

* Update configs

* Add heuristic class GetBest with integration to the Test step for JCallGraph

* Update GetBest to update color values, add hash for compare, and update algo used for path sum (to avoid infinite loop with previous all paths algo).

* Update mph-table.yaml

* Added preliminary unit tests

* Failsafe plugin added to pom file

* Added jetty plugin to start server for integration testing

* Added RepoTool integration test, modified POM to reflect int-test stage

* Set 'skipTests' parameter to false to run int tests

* Updated Integration Tests

* Added Convex Integration Test

* Added 'runOrder' parameter in POM for Int Tests, Updated Integration tests to sequentially run and added assert statements

* Removed unnecessary test files on local branch

* Update with patched items based on feedback

* Add updates to GetBest score calculation and fix issue with output directory

* Update patch/yaml names to have correct extensions

* Add runall.sh and buildsvg.sh

* Add runall.sh and buildsvg.sh

* Updated MphTableIT to run without being platform specific

* Resolved ordering issue

* Started commit for heuristic processing

* Update configs

* Add heuristic class GetBest with integration to the Test step for JCallGraph

* Update GetBest to update color values, add hash for compare, and update algo used for path sum (to avoid infinite loop with previous all paths algo).

* Update mph-table.yaml

* Update with patched items based on feedback

* Add updates to GetBest score calculation and fix issue with output directory

* Update patch/yaml names to have correct extensions

* Add runall.sh and buildsvg.sh

* Add runall.sh and buildsvg.sh

* Started commit for heuristic processing

* Update configs

* Add heuristic class GetBest with integration to the Test step for JCallGraph

* Update GetBest to update color values, add hash for compare, and update algo used for path sum (to avoid infinite loop with previous all paths algo).

* Update mph-table.yaml

* Started commit for heuristic processing

* Update configs

* Add heuristic class GetBest with integration to the Test step for JCallGraph

* Update GetBest to update color values, add hash for compare, and update algo used for path sum (to avoid infinite loop with previous all paths algo).

* Update mph-table.yaml

* Update with patched items based on feedback

* Add updates to GetBest score calculation and fix issue with output directory

* Update patch/yaml names to have correct extensions

* Add runall.sh and buildsvg.sh

* Add runall.sh and buildsvg.sh

* Started commit for heuristic processing

* Update configs

* Add heuristic class GetBest with integration to the Test step for JCallGraph

* Update GetBest to update color values, add hash for compare, and update algo used for path sum (to avoid infinite loop with previous all paths algo).

* Update mph-table.yaml

* Update with patched items based on feedback

* Add updates to GetBest score calculation and fix issue with output directory

* Update patch/yaml names to have correct extensions

* Move fixed to new "project" directory (e.g. convex-fixed).

* Add green/yellow/red arrows for paths.

* Update so buildyaml looks for @Property attributes instead of just junit Test attribute

* Add basic documentation to the read me

* Rename files

* commit runAll

* Update yaml patch names for fixed

* Remove unneeded configs

Co-authored-by: ameka4 <[email protected]>
Co-authored-by: alekh <[email protected]>
Co-authored-by: Alekh Meka <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants