5

Let's say I wanted to assert that a given Java source folder contained all of the source code required to run any of the programs contained in the source folder. Simply showing that the whole source folder compiles would be insufficient because perhaps some of those programs use reflection to instantiate objects. As such I could search through all the code and look for invocations of newInstance() to learn what classes are expected to be present at runtime. But what about calls to Class.forName(...) that are not involved in calls to newInstance() ? Better check for those as well. But how many such things do I need to check for?

Is there any sort of exhaustive list I could consult to ensure that I am considering each way in Java that such a runtime dependency could be introduced? Restated, does there exist some list of operations such that if I can show none of the source in a source folder use those operations (and that folder compiles) that all (code) dependencies are present?

If no such list exists, could we start one in this thread and make a best effort to cover all operations that we know of?

Edit

I'd like to narrow the question a little. What I'm most interested in showing is that if a codebase compiles, all dependencies are present. It seems to me to do this I would need to first compile the codebase and then check to see if any of the code ever calls certain methods (e.g. newInstance) that could introduce a runtime dependency. If no such methods are found, I'm reasonably sure that all required code is present in source form and running the program will not generate a ClassNotFoundException.

Dejas
  • 3,511
  • 4
  • 24
  • 36
  • I doubt it's possible. – shmosel May 22 '17 at 23:27
  • 2
    Obfuscators have to deal with this stuff, too (at least parts of it). Maybe it will help to check out [ProGuard](https://www.guardsquare.com/en/proguard) as it is open source and well documented. – beatngu13 May 22 '17 at 23:38

3 Answers3

3

Answer to the original question

There is no extensive way You can do this as far as I know. I mean consider the following code:

public static Object calleableFromAnywhere(Object o) throws IllegalAccessException, InstantiationException {
    Object ret = null;
    if(!Objects.isNull(o)){
        ret = o.getClass().newInstance();
    }
    return ret;
}

In this case You do not even know what kind of dependency You will have at runtime.

If you restrict your search to constructor based object creation only you have a lot of choices also:

Constructor<?> constructor = o.getClass().getConstructor();
//there could be a lot of constructors enclosing different paramethers

Object something = o.getClass().newInstance();
//default constructor called

And even those could be obtained by reflection... The rabbit hole is infinitely deep.

The call You mentioned Class.forName(...) could take any parameter String and those could be given from a database or an input field from the user.

I do not think it is possible to predict all the String variables you could encounter at runtime. If you have anything like this You are not likely to succeed.

TL.DR.: there is no exact solution I know of. According to the question You are not only interested in newInstance or instance creations as you could have static methods on classes as dependency as well. The idea is nice, but there is no 100% solution to this problem.

Narrowed/clarified questions answer

The a newInstance call is not introducing a new dependency. It could be called on a loaded class definition only. (Basically you never get to the newInstance if the class could not be loaded.) So if your goal is to answer with high certainty that if a class will not be represented is an another problem.

Altering the previous example shows that you are not likely to have the definit dependent classes:

public static Class getClass(String name) throws ClassNotFoundException {
    return Class.forName(name);
}

As name could be anything that you can recieve runtime. Also there is an other ways to load classes as the following example shows.

public Class getClassExample1(String name) throws ClassNotFoundException {
    return this.getClass().getClassLoader().loadClass(name);
}

According to the JavaDoc of ClassNotFoundException exception these places are using it:

  • @see java.lang.Class#forName(java.lang.String)
  • @see java.lang.ClassLoader#findSystemClass(java.lang.String)
  • @see java.lang.ClassLoader#loadClass(java.lang.String, boolean)

Those are the places where it is thrown, but you have to check every path to those methods like someInstance.getClass(name).

In addition to runtime dependencies You can encounter NoClassDefFoundError as well. If you are using some containers and set some dependencies as provided for example in maven You can also have some problems runtime, while the compilation was totally fine.

TL.DR.V2: You can only minimize the risk of not having a dependency with a static analysis, but if you want to try it you should check the mentioned methods and all of the call chains (even via reflection) that could lead to these.

Hash
  • 4,647
  • 5
  • 21
  • 39
  • Maybe I should narrow my request. You show how hard it can be to determine *what* the runtime dependency is, but what if I simply want to prove a codebase does or does not introduce a runtime dependency, even if I cant show what that dependency would be. Would it be correct to say "if the codebase never calls getClass() then no runtime dependency is created?" If not just that method, is there a list of methods I could check for that, if none found, would mean all the dependencies of the codebase are present if the codebase compiles? This is actually of larger interest to me. – Dejas May 26 '17 at 15:35
  • Added an edit above that might make my intent more clear. – Dejas May 26 '17 at 15:42
  • I think it is a great idea to have this, but besides some basic project it could not be that useful, because of the wide range of method chains you could do to get to the actual point where the exception is thrown. – Hash May 27 '17 at 08:05
  • Would You mind accepting the reply if it answers your question? :) – Hash Jul 03 '17 at 10:54
1

Not generally possible

You can, of course, answer this question for trivial cases, but not universally. Its not possible to tell for the general case if the code will ever attempt to load a class not available.

Think of any code that takes input from any external source and loads a class in response (e.g. user input is a JDBC database url, requiring a JDBC driver). Same goes for cases where a class may be resolved in response to internal program state, but the state isn't trivially predictable.

TLDR: Because its the same as solving the halting problem

Durandal
  • 19,919
  • 4
  • 36
  • 70
1

Actually something like Annotation Processing would add extra complexity to the problem, you can generate Java code with it at compile time, which can have dependencies you don't see directly in code and would need further understanding of the subject and its limitations on making new dependencies, you can read more about it here: AnnotationProcessing 101

hussein.g
  • 11
  • 2