10

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

Holger
  • 285,553
  • 42
  • 434
  • 765
Andrei Pozolotin
  • 897
  • 3
  • 14
  • 21
  • 3
    You should provide the specific code which throws the exception. Generally I would be surprised if it turned out that you can customize any aspect of the behavior of lambdas. The semantics of the lambda syntax are subject to precise specification in the JLS. – Marko Topolnik Jan 28 '15 at 08:53

1 Answers1

11

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.
Holger
  • 285,553
  • 42
  • 434
  • 765
  • 2
    Thank you for the pointers @holger. We have no control of the target class, so your Option 1 does not apply. Option 2 works fine, reproduced here: [PrivateTargetLambda.java](https://gist.github.com/Andrei-Pozolotin/dc8b448dc590183f5459) Do you think there is a way to avoid "black magic" or Option 2 would be a final answer? – Andrei Pozolotin Jan 28 '15 at 14:37