Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 access private member with lambda?

Invoke private method with java.lang.invoke.MethodHandle gives an answer to private member access, while Java access bean methods with LambdaMetafactory gives an answer to lambda based member access. However, by combining the two, I still can not find a way to access private members via lambda. The error:

Caused by: java.lang.IllegalAccessException: member is private: XXX from ZZZ
at java.lang.invoke.MethodHandles$Lookup.revealDirect(MethodHandles.java:1353)
at java.lang.invoke.AbstractValidatingLambdaMetafactory.<init>(AbstractValidatingLambdaMetafactory.java:131)
at java.lang.invoke.InnerClassLambdaMetafactory.<init>(InnerClassLambdaMetafactory.java:155)
at java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:299)

points to revealDirect which is part of metafactory call site builder. How can I customize the builder to control its access checks?

UPDATE: Example of Working Solution Option #3 per Holger

like image 682
Andrei Pozolotin Avatar asked Jan 28 '15 03:01

Andrei Pozolotin


1 Answers

The critical part is the Lookup object that is passed to the LambdaMetafactory on which then revealDirect is invoked.

From the documententation:

Security and access checks are performed to ensure that this lookup object is capable of reproducing the target method handle. This means that the cracking may fail if target is a direct method handle but was created by an unrelated lookup object.

As a consequence, lambda expression can only access methods accessible by the class containing the lambda expression as the JVM provided Lookup object will reflect exactly these access permissions.

This also works for Java Beans methods as these are public by convention.


So if you want to invoke private methods you have three options:

  • Generate the lambda instance from within the declaring class of the private method which has access to it. When this class calls MethodHandles.lookup() it will get an appropriate Lookup instance

    A class may also create such a Lookup instance with the desired capabilities and hand it over to another (trusted) class which may use it to perform such reflective operations. This is exactly what happens implicitly when an invokedynamic instruction is executed. A class containing invokedynamic instructions pointing to a bootstrap method within LambdaMetaFactory implies such trust.

    So using all-ordinary operations, its always the class having the access permissions which has to enable the access for another class

  • Starting with Java 9, use MethodHandles.privateLookupIn(Class, MethodHandles.Lookup) to acquire a method handle with private access rights on the specified target class. This is checked against the module access rules. For access within the same module, this should always succeed.
  • Use even more black magic to get hands on an appropriate Lookup instance. The question you have linked mentions the non-public Lookup.IMPL_LOOKUP. If you get hands on this instance and call in(declaringClass) on it, you get an instance with the desired properties. Alternatively you can create a restricted lookup object via MethodHandles.publicLookup() .in(declaringClass) and overwrite its access modifiers (the ones reported by lookupModes() to enable full access. Obviously, both require access override on fields that are not part of the public Java API.
like image 146
Holger Avatar answered Nov 03 '22 03:11

Holger