Project Jigsaw: Scopes

Introduction

For module dependencies, the common case is code in one module that has static references to types in some other module. The other module must be present at compile-time and runtime. We say requires M; in a module declaration.

In less common cases, the need for the other module to be present at compile-time and/or runtime may be relaxed. The author of a module expresses this by adding scopes to module dependencies. A scope indicates a phase at which a type must be visible and accessible if the program is to ultimately run successfully. requires M; is shorthand for a dependency which must be present at both compile-time and runtime, i.e. in the compilation and execution scopes: requires M for compilation, execution;.

Service dependencies do not have scopes. A service dependency is a request for resolution in all phases to bind a service provider class to a service interface. The availability of service provider modules may vary across phases and still the program may ultimately run successfully. A module's service dependency may even be flagged as optional to allow the module to resolve with no service provider modules available - code is assumed to handle such a scenario at runtime. The fact that service provider classes are never known, in any phase, to a service consumer module, is markedly different than how ordinary classes referenced by a module are known to it, but not necessarily in every phase (e.g. reflection may be used to avoid a compile-time dependency).

(It is plausible to imagine resolution requiring that a minimum number of service provider classes be bindable in some phases but not in others. However, this is orthogonal to resolution requiring that referenced types be visible at some phases and not others.)

Static references

There are two special cases for code that has static references:

1) The static reference is an annotation whose declaration indicates a retention policy of SOURCE. This is a rare case in the Java language of a source artifact being compiled to nothing in the class file. There is no need for the module containing the annotation's declaration to be present after compilation. A similar situation is source code that makes a static reference to a member which is a constant variable; the member's value will be inlined in the referring code, so the module containing the member is not needed after compilation.

For (1), we say requires M for compilation; in a module declaration.

2) The code which makes static references to types is guarded dynamically by code which reflectively checks the existence of those types. At runtime, the program will typically continue to execute with reduced functionality if the statically-referenced types are not available. At compile-time, however, the compiler has no leeway: the statically-referenced types must be visible.

For (2), we say requires M for compilation, reflection; in a module declaration.

Dynamic references

The uncommon case is code that uses types in some other module but makes no static references to them. There are two sub-cases:

a) The code needs the other module to be visible at runtime, i.e. the other module must be installed so that its types may be used reflectively at runtime. This sub-case is likely to be very uncommon - code that uses types in some other module solely through reflection is surely flexible enough to execute even if those types are invisible. After all, if visibility of those types was essential, then the code could have simply made static references to them (possibly with a dynamic guard as in (2) above).

For (a), we say requires M for execution; in a module declaration.

b) The code does not need the other module to be visible at runtime, i.e. the other module may not be installed and the program will continue to execute without using that module's types. The other module is effectively always "optional", whether at compile-time, runtime, or any other time. This sub-case is likely to be more common than (a).

For (b), we say requires M for reflection; in a module declaration.

Table of scenarios

The policies above are summarized in the following table, where M is a module and T is a type in module M:

M needed at run-time M possibly needed at runtime M not needed at runtime
Static reference to T in M requires M; requires M for compilation, reflection;
(Dynamic guard + static references)
requires M for compilation;
(SOURCE annotations)
No static reference to T in M requires M for execution; requires M for reflection;
(Dynamic guard + no static references)
No requires clause mentioning M

The table is perhaps clearer if reflection is renamed as optional execution, and if requires M; is written out "in full":

M needed at run-time M possibly needed at runtime M not needed at runtime
Static reference to T in M requires M for compilation, execution; requires M for compilation, optional execution; requires M for compilation;
No static reference to T in M requires M for execution; requires M for optional execution; No requires clause mentioning M

The second row is the equivalent of the first row without the compilation scope, since the second row assumes no static references, hence no compile-time dependencies.

Annotation processing

In the above discussion, there are scopes for compilation, execution, and optional execution. What about optional compilation? This scope is plausible in the context of annotation processors, which run at compile-time and which may be able to continue running even if some of their referenced types are unavailable.

That said, the proper way to view an annotation processor is as a standalone module which has a) a main class that implements javax.annotation.processing.Processor and b) its own dependencies, some of which may be optional. The compile-time of a module undergoing annotation processing is simultaneously the run-time of the module doing the annotation processing. Therefore, there is no need for a module dependency which is optional at compilation, as such a dependency is instead optional at execution for some other module (the "annotation processor").