[jsr294-modularity-eg] EG response to 294 comments

Bryan Atsatt bryan.atsatt at oracle.com
Wed Feb 20 13:46:16 EST 2008


Ugh. Please excuse the '/blah /' syntax; Thunderbird converted italics 
this way. I should know better :^)

Bryan Atsatt wrote:
> Hi Alex,
>
> I understand your points, but have a different take on them (surprise :^).
>
> First, of course the compiler doesn't have to use modules, it would do 
> so only when dependencies are expressed on them.
>
> Second, a new 'module' access qualifier is clearly /not /public; it is a 
> new mode more akin to default ('package private'). And, like default, I 
> think it is better referred to in prose using more explicit terms, such 
> as 'module private'.
>
> Your position on the legacy compilation issue is interesting: a compile 
> time warning of a /potential /runtime failure (the asymmetry alone sets 
> off red flags for me). Under what circumstances would it /not /fail at 
> runtime? Isn't the whole idea of a module private class that it cannot 
> be accessed from outside the module, ever?
>
> Like any other access violation, this should /fail /at compile time 
> exactly as it will at runtime.
>
> Changing an existing public class to module private is a potential 
> breaking change. But this is no different than any other access 
> reduction; it must be done with care.
>
> Third, the legacy loader issue comes into play only when module private 
> classes are packaged as .jar files ("simple module archives"). A legacy 
> loader will not be able to deal with .jam files (yes, it is possible to 
> configure a legacy loader to point at jars from an unpacked .jam file, 
> but this is a clear encapsulation violation, similar to a non-container 
> created loader in EE pointing at an unpacked .war, and should not be 
> supported). So what do we do about module private classes and legacy 
> loaders?
>
> There is a fundamental question here, on which I think you and I may 
> differ: should we support /two /overlapping but different module 
> concepts (294 "modules" and 277 Modules)?
>
> I don't think we should; the extra complexity and cognitive load is not 
> worth it.
>
> Instead, we should recognize that 277 Modules are (or will be) just as 
> integral to Java as are ClassLoaders, and make them first class citizens 
> as well (like it or not, the line between language and runtime is 
> already blurred in this fashion).
>
> This crystallizes for me when I think of java.lang.Class. It is very 
> easy for me to grasp:
>
>     public Module getModule(); // null if class is not a module member
>
> But that doesn't work very well with the current proposal, so we have 
> instead:
>
>     public Superpackage getSuperpackage(); // null if class is not a 
> superpackage member
>
> But what about Module? Do we just leave it out? Or introduce both 
> getModule() and getSuperpackage()? Or do we say that there is a subclass 
> relationship, and you can downcast, sometimes? Shudder.
>
> One module concept. One Class.getModule() method. Compile and runtime 
> symmetry for both dependency resolution and access checks. No new binary 
> for the JVM.
>
> And /runtime /binding of class to Module, exactly as classes are 
> currently bound to ClassLoaders for equality and package private access 
> checks.
>
> And yes, this means that a legacy loader calling defineClass() with a 
> module private class requires some special handling. But this could be 
> as simple as a generated Module bound to the ClassLoader instance. So 
> /all /classes are bound to a Module at runtime, either provided or 
> generated, and Class.getModule() can never return null. (Notice that 
> this is quite easy to do in ClassLoader if we add a Module parameter to 
> defineClass(): the existing methods create and cache the Module 
> instance. This approach worked well in the 277 prototype I built.)
>
> // Bryan 
>
> Alex Buckley wrote:
>   
>> Hi Bryan,
>>
>> Bryan Atsatt wrote:
>>   
>>     
>>>  > All points from (A) thru (M) to (Z) recognize that a type's membership
>>>  > of a superpackage is declared by a static artifact of the Java language.
>>>  > This is essential because it makes reasoning about membership
>>>  > straightforward. Any compile-time module system aimed at millions of
>>>  > programmers cannot have module membership depend on artifacts outside
>>>  > the language or on evaluation of arbitrary code.
>>>
>>> OSGi is living proof that this is not true.
>>>     
>>>       
>> I went a bit too far. Having module membership depend on evaluation of 
>> arbitrary code is clearly unreasonable if any semblance of readability 
>> is to be maintained. Having module membership depend on static artifacts 
>> is the only reasonable option; the question is where they live.
>>
>>   
>>     
>>> OSGi assumes that the "module" is self-describing, and clearly does not 
>>> depend on the language to do so; 277 could do the same. Membership could 
>>> be as simple as deployment package containment, as it is in OSGi.
>>>     
>>>       
>> It could be. But a compile-time module system aimed at millions of 
>> programmers should, in the first instance, be thoroughly visible in the 
>> language. A compilation unit whose types are members of a module should 
>> document this fact. This is a moral position which has benefited the 
>> Java language hugely over the years.
>>
>> In addition, let me present two scenarios which fall under the heading 
>> of "migration compatibility". They recognize - as we had to with 
>> generics - that not everyone will start using modules at the same time.
>>
>> 1) Legacy clients. If you compile a legacy client program which has no 
>> knowledge of modules, and it accesses a public type which is to be 
>> packaged in a module but not exported, the compilation MUST NOT give an 
>> error. It is unacceptable to deny access to public types. But the legacy 
>> client may now fail at run-time, which is unacceptable without some kind 
>> of compile-time notification. Hence my proposal for a warning if a 
>> non-exported public type is accessed at compile-time; it presages a 
>> run-time failure in the same way an "unchecked" warning presages a 
>> run-time failure due to heap pollution. (JLS 4.12.2.1)
>>
>> To give this warning, the language must be in charge of which module the 
>> accessed public type is a member of.
>>
>> 2) Legacy classloaders. A legacy classloader running on JDK7 must 
>> continue to be able to load classes even if those classes are members of 
>>   module. To break such classloaders is unacceptable. This implies that 
>> classes must be able to be members of a module without being packaged in 
>> a module. It also implies that the VM is solely responsible for run-time 
>> access control. (Indeed, with 'module' accessibility for members, *only* 
>> the VM can possibly perform access control.)
>>
>> To support these classloaders, the classfile must be in charge of which 
>> module it is a member of.
>>
>> Finally, I would point out that if you package classes in a module 
>> archive, then purely at the conceptual level it seems reasonable for the 
>> classfiles to claim membership of that module. Under what conditions 
>> would the classfiles claim membership of another module?
>>
>>   
>>     
>>> But we want to add compile-time access checks, and so, by definition, 
>>> the language must support them in some fashion. But what do we really 
>>> need here?
>>>
>>> (You may recall a document I sent around prior to the official formation 
>>> of this JSR that described "module private" semantics; I'd re-send it 
>>> here for context but no longer have it due to a drive crash plus an IT 
>>> dept. backup disaster.)
>>>
>>> For both compilation and runtime we need two functions:
>>>
>>> 1. Membership: given a class and some artifact, determine its module.
>>> 2. Access: given a class, determine if it is accessible to a class in 
>>> another module.
>>>
>>> Exports can then be defined using only these functions: the set of all 
>>> member classes which are accessible to other modules.
>>>
>>> The access function seems obvious, and is inline with your suggestion 
>>> Andreas (and my original document): a 'module' qualifier which 
>>> translates to an access flag in the class file.
>>>
>>> The membership function is clearly more interesting. At one extreme, we 
>>> could add a module name declaration to each source file; the class 
>>> itself becomes the artifact. At the other extreme we have a new 
>>> compilation unit and artifact, which lists all class members.
>>>
>>> But both of these are brittle, and, just as bad, both ignore the version 
>>> problem. Given the value of module versions at runtime, shouldn't they 
>>> be just as important during compilation?
>>>
>>> I argued in my original document that there is a happy medium between 
>>> these two extremes, one which supports versions during compilation 
>>> EXACTLY as will the runtime:
>>>
>>> The compiler must USE the runtime.
>>>
>>> That is, module membership should be defined by an abstraction, and the 
>>> compiler must use that abstraction. Given that pre-compiled dependencies 
>>> exist in the form of modules, the module system itself provides that 
>>> abstraction (ModuleDefinition) AND the access mechanism (Repository); 
>>> the compiler must merely use them.
>>>
>>> Ok, so this makes sense once we know the dependencies, but... how do we 
>>> define these for the compiler? Today we give it a list of jars called a 
>>> classpath, so it seems to make sense that we could extend this to pass 
>>> in a list of module name/version pairs. But this is not as flexible as 
>>> the runtime, where version ranges and other constraints can be used to 
>>> select dependencies, nor does it allow the compiler to implement the 
>>> membership function for the source files.
>>>
>>> So, back to something like a "super-package.java" file, but this time 
>>> lets support modules in a first class manner. Call it a "module.java" 
>>> file, and have it contain:
>>>
>>> 1. A list of member *packages*, as in the current superpackage proposal.
>>> 2. A list of import declarations, exactly as required by 277.
>>>
>>> The first enables the membership function; the second provides data for 
>>> the compiler to call Repository methods to find dependencies.
>>>
>>> The module file can contain any additional annotations required by 277 
>>> (or any other module system). The binary form can then be used by a tool 
>>> to package up a .jam file (or an OSGi bundle with a bit more work).
>>>     
>>>       
>> I agree with the majority of the above (though an explicit export list 
>> is good for readability). I expect the 277 list will soon discuss how 
>> javac could build modules and check their dependencies on other modules.
>>
>> Where I disagree is here: a Java compiler doesn't *have* to use modules. 
>> It cannot demand that every type it compiles is a member of a module or 
>> declares a dependency on a module or will be packaged in a module 
>> archive. The language does not demand such things. Ironically, the 
>> language does have to know about modules to handle legacy programs which 
>> unknowingly interact with them (as in my first scenario for migration 
>> compatibility) - but only to the degree of membership + access, not 
>> import dependencies and versioning and so on.
>>
>>   
>>     
>>> At runtime, the module system binds each class to a Module by passing 
>>> the Module instance on the ClassLoader.defineClass() invocation.
>>>
>>> This model does not require that the .class files contain module name 
>>> declarations, nor does it require a separate runtime binary solely for 
>>> the JVM's consumption. Either or both could certainly be added, if this 
>>> EG felt strongly that an extra level of membership enforcement is called 
>>> for. I believe that the module packaging is sufficient to define 
>>> membership, but others may disagree.
>>>
>>> And it doesn't required nesting, the complications of which I don't 
>>> believe are worth the benefit.
>>>     
>>>       
>> I believe I have stated in this mail why I disagree that module 
>> packaging is sufficient to define membership. For now, I'm with you on 
>> nesting.
>>
>> Alex
>> _______________________________________________
>> jsr294-modularity-eg mailing list
>> jsr294-modularity-eg at cs.oswego.edu
>> http://cs.oswego.edu/mailman/listinfo/jsr294-modularity-eg
>>
>>   
>>     
> _______________________________________________
> jsr294-modularity-eg mailing list
> jsr294-modularity-eg at cs.oswego.edu
> http://cs.oswego.edu/mailman/listinfo/jsr294-modularity-eg
>
>   


More information about the jsr294-modularity-eg mailing list