88import de .thetaphi .forbiddenapis .SuppressForbidden ;
99import java .io .IOException ;
1010import java .io .PrintWriter ;
11+ import java .lang .reflect .Method ;
1112import java .util .ArrayList ;
1213import java .util .Arrays ;
1314import java .util .HashSet ;
1617import java .util .Map ;
1718import java .util .ServiceLoader ;
1819import java .util .Set ;
20+ import java .util .function .BiConsumer ;
1921import net .bytebuddy .dynamic .ClassFileLocator ;
2022
2123/**
@@ -75,9 +77,9 @@ public static void assertInstrumentationMuzzled(
7577 // verify helper consistency
7678 final String [] helperClassNames = module .helperClassNames ();
7779 if (helperClassNames .length > 0 ) {
78- HelperClassLoader helperClassLoader = new HelperClassLoader (testApplicationLoader );
80+ BiConsumer < String , byte []> injectClassHelper = injectClassHelper (testApplicationLoader );
7981 for (Map .Entry <String , byte []> helper : createHelperMap (module ).entrySet ()) {
80- helperClassLoader . injectClass (helper .getKey (), helper .getValue ());
82+ injectClassHelper . accept (helper .getKey (), helper .getValue ());
8183 }
8284 }
8385 } catch (final Throwable e ) {
@@ -90,6 +92,29 @@ public static void assertInstrumentationMuzzled(
9092 }
9193 }
9294
95+ /** Simulates instrumentation-based access to defineClass feature. */
96+ private static BiConsumer <String , byte []> injectClassHelper (ClassLoader cl ) {
97+ try {
98+ Method findLoadedClass = ClassLoader .class .getDeclaredMethod ("findLoadedClass" , String .class );
99+ Method defineClass =
100+ ClassLoader .class .getDeclaredMethod (
101+ "defineClass" , String .class , byte [].class , int .class , int .class );
102+ findLoadedClass .setAccessible (true );
103+ defineClass .setAccessible (true );
104+ return (name , bytes ) -> {
105+ try {
106+ if (findLoadedClass .invoke (cl , name ) == null ) {
107+ defineClass .invoke (cl , name , bytes , 0 , bytes .length );
108+ }
109+ } catch (ReflectiveOperationException e ) {
110+ throw new RuntimeException (e );
111+ }
112+ };
113+ } catch (ReflectiveOperationException e ) {
114+ return new HelperClassLoader (cl )::injectClass ;
115+ }
116+ }
117+
93118 // build modules to test while single-threaded to match installer assumptions
94119 private static synchronized List <InstrumenterModule > toBeTested (
95120 ClassLoader instrumentationLoader , String muzzleDirective ) {
@@ -114,7 +139,9 @@ static final class HelperClassLoader extends ClassLoader {
114139 }
115140
116141 public void injectClass (String name , byte [] bytecode ) {
117- defineClass (name , bytecode , 0 , bytecode .length );
142+ if (findLoadedClass (name ) == null ) {
143+ defineClass (name , bytecode , 0 , bytecode .length );
144+ }
118145 }
119146 }
120147
0 commit comments