2828import static com .oracle .svm .jvmtiagentbase .Support .check ;
2929import static com .oracle .svm .jvmtiagentbase .Support .jvmtiFunctions ;
3030import static com .oracle .svm .jvmtiagentbase .jvmti .JvmtiEvent .JVMTI_EVENT_CLASS_FILE_LOAD_HOOK ;
31-
31+ import static java .lang .classfile .ClassFile .ACC_PUBLIC ;
32+
33+ import java .io .Serial ;
34+ import java .lang .classfile .ClassBuilder ;
35+ import java .lang .classfile .ClassElement ;
36+ import java .lang .classfile .ClassFile ;
37+ import java .lang .classfile .ClassModel ;
38+ import java .lang .classfile .ClassTransform ;
39+ import java .lang .classfile .MethodModel ;
40+ import java .lang .constant .ConstantDescs ;
3241import java .util .ArrayList ;
3342import java .util .List ;
3443import java .util .Map ;
6574import com .oracle .svm .jvmtiagentbase .jvmti .JvmtiEventCallbacks ;
6675import com .oracle .svm .jvmtiagentbase .jvmti .JvmtiEventMode ;
6776import com .oracle .svm .jvmtiagentbase .jvmti .JvmtiInterface ;
68- import com .oracle .svm .shaded .org .objectweb .asm .ClassReader ;
69- import com .oracle .svm .shaded .org .objectweb .asm .ClassWriter ;
7077
7178/**
7279 * JVMTI agent that provides diagnostics information that helps resolve native-image build failures.
@@ -380,25 +387,13 @@ private static void onClassFileLoadHook(@SuppressWarnings("unused") JvmtiEnv jvm
380387 newClassDataLen .write (newClassDataLength );
381388 }
382389
383- static final int ASM8 = 8 << 16 ;
384- static final int ASM_TARGET_VERSION = ASM8 ;
385-
386390 private byte [] maybeInstrumentClassWithClinit (String clazzName , byte [] clazzData ) {
387- if (clazzName != null && !advisor .shouldTraceClassInitialization (clazzName .replace ('/' , '.' ))) {
388- return null ;
389- }
390-
391391 try {
392- ClassReader reader = new ClassReader (clazzData );
393- ClassWriter writer = new ClassWriter (reader , 0 );
394- ClinitGenerationVisitor visitor = new ClinitGenerationVisitor (ASM_TARGET_VERSION , writer );
395- reader .accept (visitor , 0 );
396-
397- if (!visitor .didGeneration ()) {
398- return null ;
392+ TracingAdvisor advisor = JvmtiAgentBase .<NativeImageDiagnosticsAgentJNIHandleSet , NativeImageDiagnosticsAgent > singleton ().advisor ;
393+ if (advisor .shouldTraceClassInitialization (clazzName .replace ("/" , "." ))) {
394+ return instrumentClassWithClinit (clazzData );
399395 }
400-
401- return writer .toByteArray ();
396+ return null ;
402397 } catch (Throwable e ) {
403398 String targetClazzName = clazzName != null ? clazzName : "<unknown class>" ;
404399 System .err .println ("[native-image-diagnostics-agent] Failed to instrument class " + targetClazzName + ": " );
@@ -407,6 +402,48 @@ private byte[] maybeInstrumentClassWithClinit(String clazzName, byte[] clazzData
407402 }
408403 }
409404
405+ /**
406+ * Instruments the given class data with a synthetic <clinit> method if missing. Returns null if
407+ * no changes are made, otherwise the modified classfile bytes.
408+ */
409+ public byte [] instrumentClassWithClinit (byte [] classData ) {
410+ class ClinitAlreadyExistsException extends RuntimeException {
411+ @ Serial private static final long serialVersionUID = 1L ;
412+ }
413+ try {
414+ ClassModel cm = ClassFile .of ().parse (classData );
415+ return ClassFile .of ().transformClass (cm , new ClassTransform () {
416+ /**
417+ * Copies over all elements from the original class.
418+ */
419+ @ Override
420+ public void accept (ClassBuilder clb , ClassElement ce ) {
421+ if (ce instanceof MethodModel mm && ConstantDescs .CLASS_INIT_NAME .equals ((mm .methodName ().stringValue ()))) {
422+ // already has a <clinit> method
423+ throw new ClinitAlreadyExistsException ();
424+ }
425+ clb .with (ce );
426+ }
427+
428+ /**
429+ * Add an empty <clinit> method to the class.
430+ */
431+ @ Override
432+ public void atEnd (ClassBuilder clb ) {
433+ clb .withMethodBody (
434+ ConstantDescs .CLASS_INIT_NAME ,
435+ ConstantDescs .MTD_void ,
436+ ACC_PUBLIC | ClassFile .ACC_STATIC ,
437+ cob -> {
438+ cob .return_ ();
439+ });
440+ }
441+ });
442+ } catch (ClinitAlreadyExistsException e ) {
443+ return null ;
444+ }
445+ }
446+
410447 @ CEntryPoint
411448 @ CEntryPointOptions (prologue = AgentIsolate .Prologue .class )
412449 @ SuppressWarnings ("unused" )
0 commit comments