Hi Rony,
On 01/22/18 16:35, Rony G. Flatscher wrote:
Hi Peter,
thank you *very* much also for your kind explanations and even coming up with
agent code to
demonstrate how one could use that approach!
In fact I have been doing a static analysis in the past (since Java 1.1) to
determine whether
members should be accessible to Rexx, restricting access to public members and
to protected
inherited members, if accessing them from a dynamically created subclass.
So you *are* loading dynamically generated subclasses. It's just that
their logic is implemented in Rexx scripting and therefore delegates
invocations to Rexx runtime and those invocations include invocations to
protected methods of their superclasses.
In that case, why don't you come up with some mechanism for dynamically
generated classes to hand over their Lookup(s) to Rexx runtime. For
example, the Rexx runtime could have the following public registration API:
---
package runtime;
import java.lang.invoke.MethodHandles;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class LookupRegistry {
private static final Map<Class<?>, MethodHandles.Lookup> registry =
new ConcurrentHashMap<>();
public static void register(MethodHandles.Lookup lookup) {
if (registry.putIfAbsent(lookup.lookupClass(), lookup) != null) {
throw new IllegalStateException(
"Lookup for " + lookup.lookupClass() + " is already
registered");
}
}
/* non-public */ static MethodHandles.Lookup getLookup(Class<?>
clazz) {
return registry.get(clazz);
}
}
...then each Rexx dynamically generated class could include the
following static initialization block:
public class GeneratedClass {
static {
runtime.LookupRegistry.register(
java.lang.invoke.MethodHandles.lookup()
);
}
...
That way Rexx runtime could have access to Lookup(s) of each of it's
dynamically generated classes as soon as they are loaded and initialized
and can use a particular class's Lookup to lookup a MethodHandle for the
protected methods of its superclass. It can even simulate a
"super.method()" invocation, etc...
Regards, Peter
One thing I have to make sure is, that I keep compatible with the Java 1.6/6.0
baseline, as there
are some deployments where the shops are still employing that environment. This
should not really be
a problem as I am rewriting the entire reflection part and will be able to
employ different
approaches for pre-Java 9 deplyoments, where the reflection class employed is
dependent on the Java
runtime version.
Currently I am exploring the implications of the new Java 9 module system,
trying to adhere to its
rules. In essence the goal is to allow reflectively everything for Rexx peers
that a compiled Java
program allows for in the Java 9 environment. For that the reflecting class
(currently in the
unnamed module) goes up the inheritance tree until it finds an exported class
and analyses it
reflectively.
Experimenting with variations of classes residing in different modules with
different exports, it is
possible to mix-up the relationships, what gets exported to what, and when
should protected members
in superclasses be accessible and when not (and yes, this part should belong to
dynamically created
subclasses, which also need adjustments to the module system).
Best regards,
---rony
On 22.01.2018 10:58, Peter Levart wrote:
Hi Rony,
On 01/18/2018 04:11 PM, Rony G. Flatscher wrote:
On 18.01.2018 10:58, Alan Bateman wrote:
On 17/01/2018 18:53, Rony G. Flatscher wrote:
:
Would you have concrete suggestions for this use-case, i.e. a framework that is
not part of a
module, but having a need to access public types from exported packages and get
reflective access
to objects supertype's protected members?
I think it would be better to start with public members as protected is
complicated (and hasn't
changed with modules once you establish the class declaring the member is
accessible).
For your example, you've got a reference to a java.awt.Graphics2D object, the
actual
implementation type is sun.java2d.SunGraphics2D. The user is attempting to
invoke one of the
public setRenderingHint methods that Graphics2D defines. You said in one of
your mails that the
bridge "iterates over all its superclasses" which I take to mean that it
recursively looks at the
superclass and interfaces to find a public class or interface that defines the
target
setRenderingHint method. In the example, I expect it would skip
sun.java2d.SunGraphics2D if it
were non public.
Can you extend this check to test if the class is in a package exported by its
module. For the
example, sun.java2d.SunGraphics2D is in the java.desktop module and this module
does not export
sun.java2d to everyone. Here is a code fragment to test this:
Class<?> clazz = graphicsObj.getClass();
boolean isExportedToAll = clazz.getModule().isExported(clazz.getPackageName());
(I'm deliberately avoiding the 2-arg isExported to keep things simple for this
discussion).
If you can incorporate this check into the bridge then I suspect you'll find
most of the examples
will work.
Yes, I understand (not being able to use methods in an unexported type's
instance, hence the need to
find an accessible member in a superclass, which means to have a need to also
access protected
members in the superclass) and that is actually my current approach. However, I
started out with
reflecting Fields first and see, whether I can reflectively get access.
The rewritten method resolution would follow next, which would allow me to
tackle that warning and
see whether I can get rid of it. However, before going a wrong route I would
like to learn what the
"official" Java 9 solution would be and try to implement that.
---rony
Yes, I think you are dealing with two problems here which you have been using
the same solution
for in the past.
The 1st thing you have been doing incorrectly for Java 9, as Alan explained, is
the idiom:
o.getClass().getMethod(...) and the 2nd is that you are trying to access
protected members on
behalf of some other class which is a subclass of the protected member's
declaring class.
The 1st problem has different solutions which are all doable in Java 9, since
you are dealing
within the confines of public types, public members and exported packages. One
solution is to
search for the most specific member in the inheritance hierarchy which is also
accessible
(declared in public type in exported package) which is what Alan suggests.
There might also be another elegant solution which requires some re-design of
your Rexx
interpreter. When you deal with reference values in Rexx (the values that
refer to objects in
Java), you could track not only the value itself but also the "static" type of
that value. A
reference value is always obtained either by calling a constructor, accessing a
field (either
static or instance), by calling a method (static or instance) or by accessing
an element of some
array:
- calling constructor: the "static type" is the class upon which the
constructor has been called
- accessing a field: the "static type" is the type of the field (i.e.
Field.getDeclaringClass())
- calling a method: the "static type" is the return type of the method (i.e.
Method.getReturnType())
- accessing an element of some array: the "static type" is the array's "static
type"'s component
type (i.e. Class.getComponentType() invoked on array's "static type" Class).
When you take the "static" type as the starting Class when searching for a
public member with
standard Class.getMethod() or Class.getField(), you would then get the correct
publicly accessible
reflected member. With a caveat that this only works when there's no generics
involved. If there's
generics, the logic to compute the correct static type is more involved and
would sometimes
require passing the generic type parameters (when invoking constructors of
generic classes or
generic methods) in the syntax of your Rexx language. So you may or may not
want to do that.
Perhaps some library for deep resolving could be of help here (Google Guava has
some support for
that). I guess searching for the most specific member in the hierarchy that is
also accessible is
your best bet currently if the goal is to be syntactically backwards compatible
in the Rexx language.
The 2nd problem is not trivial as you want to access a protected member on
behalf of some other
sub-class of the member's declaring class which is not cooperating (voluntarily
handing you an
instance of its Lookup object). This currently requires the package containing
the member's
declaring class to be opened at least to you (the Rexx interpreter) and using
the
member.setAccessible(true) trick or
MethodHandles.privateLookupIn(declaringClass) equivalent for
method handles. Which is awkward because libraries packed as modules would
normally not specify
that in their module descriptors and system modules don't either. So you are
left with either
--add-opens command line switches or deploying a javaagent to the JVM and using
it's API point
java.lang.instrument.Instrumentation#redefineModule to add opens to modules
that way. Both
approaches are not elegant, but that's what is currently available, I think.
Regards, Peter