Skip to content

Commit d006b4f

Browse files
committed
[GR-61102] Migrate shaded ASM to Classfile API (JDK-8294982)
PullRequest: graal/21738
2 parents 2312c1c + e39f6e8 commit d006b4f

File tree

7 files changed

+242
-281
lines changed

7 files changed

+242
-281
lines changed

substratevm/mx.substratevm/suite.py

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -39,27 +39,6 @@
3939
},
4040

4141
"libraries" : {
42-
"ASM_9.7.1" : {
43-
"digest" : "sha512:4767b01603dad5c79cc1e2b5f3722f72b1059d928f184f446ba11badeb1b381b3a3a9a801cc43d25d396df950b09d19597c73173c411b1da890de808b94f1f50",
44-
"sourceDigest" : "sha512:d7c0de5912d04949a3d06cad366ff35a877da2682d9c74579625d62686032ea9349aff6102b17f92e9ec7eb4e9b1cd906b649c6a3ac798bfb9e31e5425de009d",
45-
"maven" : {
46-
"groupId" : "org.ow2.asm",
47-
"artifactId" : "asm",
48-
"version" : "9.7.1",
49-
},
50-
"license" : "BSD-new",
51-
},
52-
"ASM_TREE_9.7.1" : {
53-
"digest" : "sha512:e55008c392fdd35e95d3404766b12dd4b46e13d5c362fcd0ab42a65751a82737eaf0ebc857691d1916190d34407adfde4437615d69c278785416fd911e00978d",
54-
"sourceDigest" : "sha512:3cea80bc7b55679dfa3d2065c6cb6951007cc7817082e9fcf4c5e3cdc073c22eddf7c7899cff60b1092049ec9038e8d3aa9a8828ef731739bda8b5afcec30e86",
55-
"maven" : {
56-
"groupId" : "org.ow2.asm",
57-
"artifactId" : "asm-tree",
58-
"version" : "9.7.1",
59-
},
60-
"dependencies" : ["ASM_9.7.1"],
61-
"license" : "BSD-new",
62-
},
6342
"RENAISSANCE_HARNESS_v0.9" : {
6443
"urls" : ["https://lafo.ssw.uni-linz.ac.at/pub/graal-external-deps/renaissance/renaissance-harness_v0.9.0.tar.gz"],
6544
"digest" : "sha512:068207adf6bbd0a934429f7d6ddba8810e55992d06e131479658a7933bb352ea892d4304f745806dc342a6f7187a434ff2f106c6f8a6ee35ee696ea4fc998f7b",
@@ -306,36 +285,6 @@
306285
"graalCompilerSourceEdition": "ignore",
307286
},
308287

309-
# This shaded ASM project is just a quickfix.
310-
# Eventually, we should migrate to the Classfile API [JDK-8294982] (GR-61102).
311-
"com.oracle.svm.shaded.org.objectweb.asm": {
312-
# Shadowed ASM libraries (org.ow2.asm:asm,asm-tree)
313-
"subDir" : "src",
314-
"sourceDirs" : ["src"],
315-
"javaCompliance" : "17+",
316-
"spotbugsIgnoresGenerated" : True,
317-
"shadedDependencies" : [
318-
"ASM_9.7.1",
319-
"ASM_TREE_9.7.1",
320-
],
321-
"class" : "ShadedLibraryProject",
322-
"shade" : {
323-
"packages" : {
324-
"org.objectweb.asm" : "com.oracle.svm.shaded.org.objectweb.asm",
325-
},
326-
"exclude" : [
327-
"META-INF/MANIFEST.MF",
328-
"**/package.html",
329-
],
330-
},
331-
"description" : "ASM library shadowed for Native Iamge.",
332-
# We need to force javac because the generated sources in this project produce warnings in JDT.
333-
"forceJavac" : "true",
334-
"javac.lint.overrides" : "none",
335-
"jacoco" : "exclude",
336-
"graalCompilerSourceEdition": "ignore",
337-
},
338-
339288
"com.oracle.svm.processor" : {
340289
"subDir" : "src",
341290
"sourceDirs" : ["src"],
@@ -360,7 +309,6 @@
360309
],
361310
"dependencies": [
362311
"com.oracle.svm.common",
363-
"com.oracle.svm.shaded.org.objectweb.asm",
364312
"com.oracle.objectfile",
365313
"SVM_CONFIGURE",
366314
"espresso-shared:ESPRESSO_SVM",

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/PredefinedClassesSupport.java

Lines changed: 41 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,21 @@
2525
*/
2626
package com.oracle.svm.core.hub;
2727

28+
import static java.lang.classfile.ClassFile.ConstantPoolSharingOption.NEW_POOL;
29+
2830
import java.io.Serializable;
31+
import java.lang.classfile.ClassFile;
32+
import java.lang.classfile.ClassModel;
33+
import java.lang.classfile.ClassTransform;
34+
import java.lang.classfile.MethodTransform;
35+
import java.lang.classfile.constantpool.ClassEntry;
36+
import java.lang.classfile.instruction.FieldInstruction;
37+
import java.lang.classfile.instruction.InvokeInstruction;
38+
import java.lang.classfile.instruction.NewMultiArrayInstruction;
39+
import java.lang.classfile.instruction.NewObjectInstruction;
40+
import java.lang.classfile.instruction.NewReferenceArrayInstruction;
41+
import java.lang.classfile.instruction.TypeCheckInstruction;
42+
import java.lang.constant.ClassDesc;
2943
import java.lang.reflect.Method;
3044
import java.security.ProtectionDomain;
3145
import java.util.HashSet;
@@ -44,11 +58,6 @@
4458
import com.oracle.svm.core.reflect.serialize.SerializationSupport;
4559
import com.oracle.svm.core.util.ImageHeapMap;
4660
import com.oracle.svm.core.util.VMError;
47-
import com.oracle.svm.shaded.org.objectweb.asm.ClassReader;
48-
import com.oracle.svm.shaded.org.objectweb.asm.ClassVisitor;
49-
import com.oracle.svm.shaded.org.objectweb.asm.ClassWriter;
50-
import com.oracle.svm.shaded.org.objectweb.asm.MethodVisitor;
51-
import com.oracle.svm.shaded.org.objectweb.asm.Opcodes;
5261
import com.oracle.svm.util.ClassUtil;
5362

5463
import jdk.graal.compiler.api.replacements.Fold;
@@ -332,41 +341,32 @@ public static Class<?> maybeAdjustLambdaNestHost(String className, Class<?> java
332341

333342
@Platforms(Platform.HOSTED_ONLY.class)
334343
public static byte[] changeLambdaClassName(byte[] data, String oldName, String newName) {
335-
ClassReader cr = new ClassReader(data);
336-
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
337-
338-
cr.accept(new ClassVisitor(Opcodes.ASM5, cw) {
339-
// Change lambda class name in the bytecode
340-
@Override
341-
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
342-
super.visit(version, access, newName, signature, superName, interfaces);
343-
}
344-
345-
// Change all class references in the lambda class bytecode
346-
@Override
347-
public MethodVisitor visitMethod(int access, String originalName, String desc, String signature, String[] exceptions) {
348-
return new MethodVisitor(Opcodes.ASM5, super.visitMethod(access, originalName, desc, signature, exceptions)) {
349-
@Override
350-
public void visitTypeInsn(int opcode, String type) {
351-
String name = type.equals(oldName) ? newName : type;
352-
super.visitTypeInsn(opcode, name);
353-
}
354-
355-
@Override
356-
public void visitMethodInsn(int opcode, String owner, String methodName, String descriptor, boolean isInterface) {
357-
String name = owner.equals(oldName) ? newName : owner;
358-
super.visitMethodInsn(opcode, name, methodName, descriptor, isInterface);
359-
}
360-
361-
@Override
362-
public void visitFieldInsn(int opcode, String owner, String fieldName, String descriptor) {
363-
String name = owner.equals(oldName) ? newName : owner;
364-
super.visitFieldInsn(opcode, name, fieldName, descriptor);
365-
}
366-
};
367-
}
368-
}, ClassReader.EXPAND_FRAMES);
369-
370-
return cw.toByteArray();
344+
ClassDesc oldDesc = ClassDesc.ofInternalName(oldName);
345+
ClassDesc newDesc = ClassDesc.ofInternalName(newName);
346+
347+
ClassFile classFile = ClassFile.of(NEW_POOL);
348+
ClassModel original = classFile.parse(data);
349+
350+
return classFile.transformClass(original, newDesc,
351+
ClassTransform.transformingMethods(
352+
MethodTransform.transformingCode((builder, element) -> {
353+
ClassEntry newClassEntry = builder.constantPool().classEntry(newDesc);
354+
// Pass through any unhandled elements unchanged
355+
if (element instanceof TypeCheckInstruction ti && ti.type().asSymbol().equals(oldDesc)) {
356+
builder.with(TypeCheckInstruction.of(ti.opcode(), newClassEntry));
357+
} else if (element instanceof NewObjectInstruction ti && ti.className().asSymbol().equals(oldDesc)) {
358+
builder.with(NewObjectInstruction.of(newClassEntry));
359+
} else if (element instanceof NewReferenceArrayInstruction ti && ti.componentType().asSymbol().equals(oldDesc)) {
360+
builder.with(NewReferenceArrayInstruction.of(newClassEntry));
361+
} else if (element instanceof NewMultiArrayInstruction ti && ti.arrayType().asSymbol().equals(oldDesc)) {
362+
builder.with(NewMultiArrayInstruction.of(newClassEntry, ti.dimensions()));
363+
} else if (element instanceof InvokeInstruction mi && mi.owner().asSymbol().equals(oldDesc)) {
364+
builder.with(InvokeInstruction.of(mi.opcode(), newClassEntry, mi.name(), mi.type(), mi.isInterface()));
365+
} else if (element instanceof FieldInstruction fi && fi.owner().asSymbol().equals(oldDesc)) {
366+
builder.with(FieldInstruction.of(fi.opcode(), newClassEntry, fi.name(), fi.type()));
367+
} else {
368+
builder.with(element);
369+
}
370+
})));
371371
}
372372
}

substratevm/src/com.oracle.svm.diagnosticsagent/src/com/oracle/svm/diagnosticsagent/ClinitGenerationVisitor.java

Lines changed: 0 additions & 70 deletions
This file was deleted.

substratevm/src/com.oracle.svm.diagnosticsagent/src/com/oracle/svm/diagnosticsagent/NativeImageDiagnosticsAgent.java

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,16 @@
2828
import static com.oracle.svm.jvmtiagentbase.Support.check;
2929
import static com.oracle.svm.jvmtiagentbase.Support.jvmtiFunctions;
3030
import 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;
3241
import java.util.ArrayList;
3342
import java.util.List;
3443
import java.util.Map;
@@ -65,8 +74,6 @@
6574
import com.oracle.svm.jvmtiagentbase.jvmti.JvmtiEventCallbacks;
6675
import com.oracle.svm.jvmtiagentbase.jvmti.JvmtiEventMode;
6776
import 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

Comments
 (0)