Hi Rafael,

FYI.  I have sent a proposal [1] for item #1 to serviceability-dev and you for discussion.  JBS issue is:

[1] http://mail.openjdk.java.net/pipermail/serviceability-dev/2018-April/023529.html

On 4/2/18 5:02 AM, Rafael Winterhalter wrote:
1. Java agents cannot define auxiliary classes.

Byte Buddy does support the JPMS fully, however, it still relies on
sun.misc.Unsafe::defineClass for its Java agent API and currently breaks on
Java 11 as this method was removed in a recent EA build. The reason for
using Unsafe is that many instrumentations need to define auxiliary classes
to aid an instrumentation similar to javac which sometimes needs to define
anonymous classes or even synthetic classes. For example, if a Java agent
wants to register an event listener to some framework, such listeners often
declare multiple methods what makes it impossible to fullfil the listener
contract using a lambda expression. Instead, one typically injects an
additional class into the same package as the instrumented class. In this
case, it is not possible to use MethodHandles.Lookup::defineClass as the
class file transformer does not necessarily have private access to the
lookup of the instrumented class.

The current workarounds are:

a) Open the package jdk.internal.misc to gain access to this package's
Unsafe class. This can be done via Instrumentation::redefineModule.
b) Open the java.lang package to access ClassLoader via reflection.
c) Open the java.lang package to access the internal lookup with global
access rights.

Of these solutions only (b) relies on standard API and is guaranteed to
function in the future but the solution still feels hacky and does not work
for instrumentations of classes on the bootstrap loader. Opening packages
also implies a risk of being applied carelessly since opening the package
to the agent's module most likely opens the package to the unnamed module
of the system class loader what invites to breaches of the JPMS
encapsulation by code that does not ship with the agent.
To offer a better solution, I would like to suggest one of the following:

a) Add a method defineClass(ClassLoader, byte[], ProtectionDomain) to the
Instrumentation interface that works similar to Unsafe::defineClass. This
would provide a very simple migration path. Since agents have access to
jdk.internal.misc, adding this method does not add any capabilities to the
agent, it merley avoids using internal API that might change.
b) Supply a MethodHandles.Lookup instance to the
ClassFileTransformer::transform API where the instance represents the
instrumented class's access rights. This does however carry the risk of
invoking the lookupClass method which would either load the instrumented
class prematurely causing a circularity error or return an unexpected value
such as null. Since the lookup API generally relies on loaded types, there
are probably other problems such as invoking Lookup::privateLookupIn before
all involved types are loaded.

For the sake of simlicity and since easy migration paths make a quick
adoption easier, I would suggestion solution (a), also in the light that
quick and dirty migrations might still choose option (b) to save time and
also since (b) might cause problems when types are not yet loaded.

Reply via email to