I am currently utilizing org.objectweb.asm.util.CheckClassAdapter
for bytecode verification in my Java project. However, I've observed that this class prints errors to stderr instead of throwing exceptions, which makes it challenging to handle verification failures programmatically.
Here is an example of the existing code snippet:
final byte[] bytes = …; // class bytes are generated previously
CheckClassAdapter.verify(
new ClassReader(bytes),
false,
new PrintWriter(System.err)
);
It appears that a potential workaround could be:
final byte[] bytes = …; // class bytes are generated previously
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
CheckClassAdapter.verify(
new ClassReader(bytes),
false,
pw
);
if (sw.toString().contains("AnalyzerException")) {
System.err.println(sw.toString());
fail();
}
However, this solution feels like a "crutch" to me. Is there a way to modify or extend the CheckClassAdapter
to make it throw an exception when bytecode verification fails, rather than printing errors to stderr? I aim to catch and handle verification errors within my code. Any guidance or examples would be greatly appreciated.
ASM version: org.ow2.asm:asm:9.6
Since I haven't found how I can use CheckClassAdapter
to verify bytecode and throw an AnalyzerException
exception, I decided to modify the source code of the verify method, as Holger suggested. Here's how you can verify the bytecode:
/**
* Verify the bytecode.
* @param bytes The bytecode to verify.
*/
private void verify(final byte[] bytes) throws AnalyzerException {
final ClassNode clazz = new ClassNode();
new ClassReader(bytes)
.accept(new CheckClassAdapter(clazz, false), ClassReader.SKIP_DEBUG);
final Optional<Type> syper = Optional.ofNullable(clazz.superName).map(Type::getObjectType);
final List<Type> interfaces = clazz.interfaces.stream().map(Type::getObjectType)
.collect(Collectors.toList());
for (final MethodNode method : clazz.methods) {
final SimpleVerifier verifier =
new SimpleVerifier(
Type.getObjectType(clazz.name),
syper.orElse(null),
interfaces,
(clazz.access & Opcodes.ACC_INTERFACE) != 0
);
// You might need to set your own ClassLoader here.
// verifier.setClassLoader(Thread.currentThread().getContextClassLoader());
new Analyzer<>(verifier).analyze(clazz.name, method);
}
}
Imports:
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.analysis.Analyzer;
import org.objectweb.asm.tree.analysis.AnalyzerException;
import org.objectweb.asm.tree.analysis.SimpleVerifier;
import org.objectweb.asm.util.CheckClassAdapter;
Also, it's worth mentioning that this method isn't perfect since you generate bytecode and then parse it again. Most probably, you can simplify this process by integrating bytecode verification directly into the generation process using CheckClassAdapter
and CheckMethodAdapter
classes