Accessibility of nonexported types (was: Project Jigsaw: The Big Picture (part 1))
Jesse Glick
jesse.glick at oracle.com
Tue Dec 27 07:22:48 PST 2011
On 12/23/2011 06:54 PM, David M. Lloyd wrote:
>>>> access to non-exported types is specifically disallowed at run time by the JVM.
>>>
>>> Do you mean that you cannot pass a reference of a
>>> non-exported class instance from a module to another module?
>>
>> No, I mean that if you somehow do get a reference to such an object
>> you won't be able to do anything with it. The JVM's access-checking
>> machinery won't let you
>
> There needs to be a release valve for this equivalent to setAccessible()
To make a potentially confusing subject more concrete, let us say you have
module m1 {exports m1;}
package m1;
public abstract class SomeAPI {
public abstract void run();
public static SomeAPI instance() {
return new m1.internal.SomeImpl();
}
}
package m1.internal; // not exported!
public class SomeImpl extends SomeAPI {
@Override public void run() {...}
public void other() {...}
}
module m2 {requires m1;}
package m2;
public class APIClient {
void use() {
m1.SomeAPI obj = m1.SomeAPI.instance();
// ...now see below
}
}
Obviously APIClient.use can call "obj.run()". Almost as obviously, it cannot refer statically to m1.internal.SomeImpl: using modular javac that would not even compile;
and if you used nonmodular javac or an assembler to force it to do so, the module system would throw some sort of linkage error when loading or running APIClient. (A
module system based on a current JRE would throw NoClassDefFoundError, wrapping a ClassNotFoundException intentionally thrown from a ClassLoader.loadClass override on
m2's loader.)
The subtlety is whether APIClient can use reflection to bypass the intent of package exports. In the analogous situation in the NetBeans module system, and I guess in
OSGi as well, the following will run:
obj.getClass().getMethod("other").invoke(obj);
since this bypasses the trigger - attempting to load "m1.internal.SomeImpl" from m2's ClassLoader.
Under Jigsaw I would expect the call to Method.invoke to throw IllegalAccessException, unless you rewrite to
Method m = obj.getClass().getMethod("other");
m.setAccessible(true);
m.invoke(obj);
and the SecurityManager does not complain.
By the way - since Class does not extend AccessibleObject, it is unclear whether
obj.getClass().newInstance();
should also throw IllegalAccessException, and if so, how you could suppress that (given adequate permissions). Since Class.newInstance seems to essentially be a
convenience method, I suppose you could rewrite as
obj.getClass().getConstructor().newInstance();
and if necessary make it accessible:
Constructor<?> c = obj.getClass().getConstructor();
c.setAccessible(true);
c.newInstance();
Here I am assuming that for a "public" class in a package not exported to the caller, every member defined in that class is considered inaccessible, just as if the class
had default access and the caller was in another package. On that topic, a minor nit: the IllegalAccessException detail message needs to explain that the entire class,
not just one member of it, is inaccessible; the following in JDK 7:
package p1;
class Private implements Runnable {
public Private() {}
@Override public void run() {
System.out.println("run");
}
public void other() {
System.out.println("other");
}
}
package p1;
public class Access {
public static Runnable r() {
return new Private();
}
}
package p2;
public class Main {
public static void main(String[] args) throws Exception {
Runnable r = p1.Access.r();
r.run();
r.getClass().getMethod("other").invoke(r);
}
}
prints
run
Exception in thread "main" java.lang.IllegalAccessException: Class p2.Main can not access a member of class p1.Private with modifiers "public"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:95)
at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:261)
at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:253)
at java.lang.reflect.Method.invoke(Method.java:594)
at p2.Main.main(Main.java:6)
which is a little misleading - the modifiers of the member are irrelevant.
More information about the jigsaw-dev
mailing list