Proposal: #ReflectiveAccessToNonExportedTypes (revised) & #AwkwardStrongEncapsulation: Weak modules & private exports
Tim Ellison
Tim_Ellison at uk.ibm.com
Wed Oct 12 08:45:16 UTC 2016
mark.reinhold at oracle.com wrote on 11/10/2016 16:14:14:
> 2016/9/16 6:44:17 -0700, forax at univ-mlv.fr:
> > Hi all,
> > the goals of this proposal is
> > - to explicitly mark packages that allow 'deep' reflection,
> > - disable 'deep' reflection by default even for exported packages
> > - ease the transition by adding a new migration state between
automatic
> > module and strong module (weak module).
> >
> > so 3 goals with one stone :)
>
> Indeed!
>
> > Have i miss something obvious but the last goal, easing migration is
not
> > listed in the issue summary ? While adding a new kind of module to
ease
> > migration is maybe a good idea, it also add a new kind of modules when
> > thinking about the modules and their relationship, which seems a
terrible
> > idea. We are loosing the KISS principle of the current module system.
>
> I didn't list ease of migration in the issue summary but I didn't think
> that necessary, since it's an overarching goal of this entire effort.
Overall the concept of weak module does not provide an attractive
migration
step for existing module systems because it requires each module to
"export
private" all of its packages in order to retain existing behavior with
respect to reflection.
This will effectively expose all of the internals of the existing module
as
if it were API at runtime and at compile/tool time.
It can be argued that even in existing modular systems there is a limited
set of packages that must be allowed for deep reflection and the wholesale
enablement of all packages concealed and exported for deep reflection is
not needed. But this is not the case when mapping an existing modular
system's custom class loaders into a JPMS Layer (e.g. OSGi). In that case
we are motivated to preserve the existing behavior for the modules that
represent the pre-existing modules/bundles.
> I understand your reluctance to introduce what appears to be another
kind
> of module, but semantically one can read
>
> weak module foo.bar {
> requires hibernate.core;
> requires hibernate.entitymanager;
> }
>
> as a simple shorthand for the more verbose
>
> module foo.bar {
> exports private com.foo.bar.model;
> exports private ...; // for other packages as
needed
> requires hibernate.core;
> requires hibernate.entitymanager;
> }
>
> or, if we were to add wildcards, then
>
> module foo.bar {
> exports private *;
> requires hibernate.core;
> requires hibernate.entitymanager;
> }
>
> The advantage of introducing weak modules as an explicit concept is that
> they nicely capture the reliable-configuration part of our story and, by
> their very name, explicitly do not promise strong encapsulation.
>
> I think weak modules are, moreover, easier to explain to developers just
> starting to work with modules. Telling someone that they have to type
>
> module foo.bar {
> exports private *;
> }
>
> in order to make a simple module's packages available for deep
reflection
> raises all kinds of questions. What does `exports` mean? What does
> `private` mean? What does `*` range over? It's all a bit like telling
> them that they need to type
>
> public class X {
> public static void main(String... args) { ... }
> }
>
> in order to print "Hello, world!". With weak modules you can simply
> explain that a weak module has no strong encapsulation and so will work
> whether it has a programmatic API or, as is the case here, it just
> contains model classes to be manipulated by a reflective framework.
> Strong encapsulation can be added later.
The declaration of a package as exported for tool-time linkage/compilation
and normal runtime class loader delegation is orthogonal to the
declaration
of a package as available for 'deep' and/or 'shallow' reflection.
The proposal mandates that 'shallow' reflection is only allowed on
exported
packages, and any concealed packages are not available for any type of
reflection, so the use of the keyword 'private' also is confusing.
Here the 'private' keyword is used to allow broader access to an exported
package which is counter intuitive. One would think a private export
would
be less accessible than a 'public' export, but in general the proposed
semantics are troublesome regardless of the keywords used.
> > I fully agree that 'deep' reflection should be disallowed by default
and can
> > be allowed package by package. But as Stephen Colebourne, i fail to
see why
> > enabling 'deep' reflection on a package is related to the export
> > keyword. While 'deep' reflection is somewhat related to visibility,
it's not
>
> (I assume you mean accessibility here, rather than visibility.)
>
> > only about visibility of a class/method/field only, by example youcan
change
> > the value of a public final field of a public class using
setAccessible. In
> > my opinion, exposing the API to 'deep' reflection is a different
concern as
> > exporting a package, thus using 'private' as a modifier on export is a
bad
> > idea (it also doesn't work well with the notion of restricted export
(export
> > to ...)). Adding a new keyword 'expose' as Stephen propose to enable
'deep'
> > reflection is a better way to answer to the goal 1 and 2.
>
> Reflective access to the elements of packages, deep or otherwise, is
> intimately associated with language/bytecode-level access because the
> reflection API has always aimed to be an API-level simulation of what's
> possible at the language/bytecode level. This is an extremely valuable
> property, since it makes it easy to reason about the correctness of
> reflective code. (There are, obviously, some exceptions to this
analogy,
> namely the long-standing `setAccessible` method and, more recently, the
> changes we made for #ReflectionWithoutReadability.) Both the language
> and the reflection API are subject to the access-control rules of the
> module system, so using different keywords to express how they're made
> subject to those rules strikes me as confusing.
>
> I've read Colebourne's proposal [1], and I can see its intuitive appeal
> to developers (like us!) who are used to thinking about all these
things.
> To a new developer it might be easier to explain his `exposes *` than
the
> above `exports private *`, but I think explaining weak modules is even
> easier. A new developer is likely to hear the "reliable configuration +
> strong encapsulation" mantra fairly early on, and weak modules are
simply
> the former, without the latter.
I disagree. I like the Colebourne's approach because it keeps to two
declarations ('exports' and 'exposes') as orthogonal concepts. However,
I don't see the need to open the syntax up for wildcards '*' at this time.
> A couple of other problems I see with Colebourne's approach:
>
> - The only way to allow an exported API package to be used
reflectively
> is to expose it for deep reflection. That requires typing another
> directive and, worse, makes the internals of that package available
> for deep reflection, which is probably not what was intended.
>
> - The interactions between `exports`, `exposes`, and the qualified
> forms of these directives turns out to be pretty complicated once
you
> work out all the cases. (We've done this internally, for a somewhat
> similar proposal that allows both `private` and `dynamic` modifiers
> on the `exports` directive.) They wind up being nearly as bad as
the
> existing rules for protected members [2]. I'm not convinced that
> this relatively small bit of expressive power is worth the
additional
> complexity.
It would be interesting to see that exploration exposed, because as you
say
the Colebourne expression appears intuitive.
> > About the other proposed items:
> > - using transitive instead of public for require, i fully agree.
>
> Good.
>
> > - about removing the dynamic exports feature, a package declared as
non
> > exported but declared as exposed will have the same meaning,
> no ? if yes,
> > i agree that the notion dynamic export should be removed.
>
> I'm not sure if you're thinking of my proposal here, or Colebourne's.
>
> In my proposal you can't "expose" a package for reflection without also
> exporting it. If you export a package then it's available for shallow
> reflection at run time; if you add the `private` modifier then it's also
> available for deep reflection via `setAccessible`.
As discussed, it would be useful to explore the options for changing the
proposal's constraint that you can't "expose" a package for reflection
without
also exporting it.
Regards,
Tim
> As I mentioned in one of the notes in the proposal, removing `dynamic`
> does remove some expressive power, since you can no longer export a
> package only at run time. This means that an IDE or a compiler cannot
> (as easily) prevent you from writing code against a module that
contains,
> e.g., JPA POJOs that are not also APIs (which most aren't). I
understand
> the use case, but the cost of supporting it by retaining the `dynamic`
> modifier (or its semantic equivalent) is rather high, as I noted above.
> If experience shows that it's really necessary, we could add it in a
> future release.
>
> - Mark
>
>
> [1] http://mail.openjdk.java.net/pipermail/jigsaw-dev/2016-
> September/009370.html
> [2]
http://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.6.2
>
Unless stated otherwise above:
IBM United Kingdom Limited - Registered in England and Wales with number
741598.
Registered office: PO Box 41, North Harbour, Portsmouth, Hampshire PO6 3AU
More information about the jpms-spec-experts
mailing list