ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.


AddThis Social Bookmark Button

Introduction to the ASM 2.0 Bytecode Framework
Pages: 1, 2, 3, 4, 5, 6, 7, 8

Bytecode consumers can be linked together in a "Chain of responsibility" pattern by either manually delegating events to the next visitor in the chain, or with using visitors derived from ClassAdapter and/or MethodAdapter that delegate all visit methods to their underlying visitors. Those delegators act as bytecode consumers from one side and as bytecode producers from the other. They can decide to modify natural delegation in order to implement specific bytecode transformation:

  • Visit call delegation can be omitted in order to remove class fields, methods, method instructions, etc.
  • Visit call parameters can be modified in order to rename classes, methods, types, etc.
  • New visit calls can be added in order to introduce new fields or methods, or to inject new code into existing methods.

The chain can be ended by a ClassWriter visitor, which will produce the resulting bytecode. For example:

  ClassWriter cw = new ClassWriter(computeMax);
  ClassVisitor cc = new CheckClassAdapter(cw);
  ClassVisitor tv = 
    new TraceClassVisitor(cc, new PrintWriter(System.out));
  ClassVisitor cv = new TransformingClassAdapter(tv);
  ClassReader cr = new ClassReader(bytecode);
  cr.accept(cv, skipDebug);
  byte[] newBytecode = cw.toByteArray();

In the above code, the TransformingClassAdapter implements custom class transformations and sends the results to the TraceClassVisitor passed to its constructor. TraceClassVisitor prints the transformed class and delegates the same events to CheckClassAdapter, which does simple bytecode verification and then passes the event to the ClassWriter.

Most of the visit methods receive simple parameters such as int, boolean, and String. In all visit methods where String parameters refer to constants within the bytecode, ASM uses the same representation as used by the JVM. For instance, all class names (e.g. super class, interfaces, exceptions, owner classes for the field, and methods referenced from method code) should be specified in the Internal Form. Field and method descriptors (usually the desc parameter) also should be in JVM representation. The same approach is taken for signature parameters used to represent generics information, so they should follow the grammar defined in Section 4.4.4 of the Revised Class File Format (PDF). This approach helps avoid unnecessary calculations when no transformation is required. To help construct and parse such descriptors, there is a Type class that provides several static methods:

  • String getMethodDescriptor(Type returnType, Type[] argumentTypes)
  • String getInternalName(Class c)
  • String getDescriptor(Class c)
  • String getMethodDescriptor(Method m)
  • Type getType(String typeDescriptor)
  • Type getType(Class c)
  • Type getReturnType(String methodDescriptor)
  • Type getReturnType(Method m)
  • Type[] getArgumentTypes(String methodDescriptor)
  • Type[] getArgumentTypes(Method m)

Note that these descriptors are using an "erasured" representation, which does not contain generics information. Generics info is actually stored as a separate bytecode attribute, but ASM takes care of this attribute and passes the generic signature string in a signature parameter of the appropriate visit methods. The value of the signature string also uses the JVM representation (see Section 4.4.4 in the "Class File Format" (PDF) as revised for Java 5), which uniquely maps from the generic declarations in Java code, but presents an additional challenge for tools that need to retrieve details from generic types. To handle signatures, ASM provides SignatureVisitor, SignatureReader, and SignatureWriter classes modelled in a way similar to other ASM visitors, as illustrated in Figure 3.

Sequence diagram for Signature classes
Figure 3. Sequence diagram for Signature classes

The Util package contains TraceSignatureVisitor, which implements SignatureVisitor and allows you to convert a signature value into a Java declaration with generic types. The following example converts a method signature into a Java method declaration.

  TraceSignatureVisitor v = 
      new TraceSignatureVisitor(access);
  SignatureReader r = new SignatureReader(sign);
  String genericDecl = v.getDeclaration();
  String genericReturn = v.getReturnType();
  String genericExceptions = v.getExceptions();

  String methodDecl = genericReturn + " " + 
    methodName + genericDecl;
  if(genericExceptions!=null) {
    methodDecl += " throws " + genericExceptions;

Up to this point, we have talked about the general design of the ASM framework and manipulating class structure. However, the most interesting part is how ASM handles method code.

Pages: 1, 2, 3, 4, 5, 6, 7, 8

Next Pagearrow