Avoiding module-boundaries when bootstrapping invokedyanmic/constantdynamic

Rafael Winterhalter rafael.wth at gmail.com
Sun Feb 26 23:05:05 UTC 2023


Over the recent years, I have leveraged especially invokedynamic when
instrumenting code. For example, when adding some additional behavior to a
method, a Java agent can add an invokedynamic instruction to the beginning
or the end of it. Of course, the bootstrap method is also supplied by the
agent. The obvious advantage is that the bootstrap method can bind a handle
that points to any class loader, which is often an issue when instrumenting
applications that can use very custom hierarchies of class loaders. Also,
the bootstrap method can create a new class loader as a child of both the
instrumented class and the agent’s class loader. This allows them to write
instrumentation code that interacts with both the agent and the
instrumented class. This makes it possible to define auxiliary classes, for
example when a reactive framework needs a callback, without the need of
class injection.

The remaining issue however is the bootstrap method. The instrumentation
API already offers a way of injecting classes into the bootstrap class
loader, which is of course the right place for such a class-loader
universal dispatcher. However, with the module system, an instrumented
class that is within a named module will not be able to read this bootstrap
class. And it is neither possible (without using internal API) to add a
class to the java.base module that would indeed be visible to all classes,
similar to the lambda metafactory. This leaves me with using the unnamed
module of the bootstrap class path, but this requires importing the module
from all named modules of instrumented classes, something that becomes a
visible reflective change to all code. And which is something I would
ideally want to avoid, also because it might interfere with other code that
uses the unnamed module of the boot loader.

Which brings me to my question; should it be possible to invoke a bootstrap
method without applying module checks? In a way, the bootstrap method is
not a real part of a class, but a mechanism to set up the class. Once it is
initialized, there is no more need to read the module of the bootstrap
loader, so ideally it should not be needed to permanently alter the module
graph.

As an alternative idea, I was wondering if it would be an idea to add a
“generic bootstrap method” to the java.base module. The bootstrap method
could look something like the following:

@FunctionalInterface
interface GenericMetafactory {
  ConcurrentMap<String, GenericMetafactory> factory;
  static CallSite metafactory(MethodHandles.Lookup caller, String
invokedName, MethodType invokedType, String identifier, Object… args) {
    factory.get(identifier).apply(caller, invokedName, args);
  }
  CallSite apply(MethodHandles.Lookup caller, String invokedName,
MethodType invokedType, Object… args);
}

This way, a Java agent could register a dispatcher identified on a random
string prior to instrumenting any code and then add invokedyanmic call
sites with this string to all instrumented code. Since this bootstrap class
lives in the java.base module, no more adjustments would be necessary.

A similar generic bootstrapper could be supplied for use for dynamic
constants.

Thanks, Rafael
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/core-libs-dev/attachments/20230227/9381af30/attachment-0001.htm>


More information about the core-libs-dev mailing list