Frequently Asked Questions

Here are some frequently asked questions about ASM, gathered by Mark Proctor .

1. How do I remove a method/field?

Use the ClassAdapter and return nothing:

              visitField (String name, ...) {
                if (removeField(name)) {
                  // do nothing, in order to remove this field
                } else {
                  // make the next visitor visit this field, in
      order to
            keep it
                  super.visitField(name, ...);
                }
              }
            
    

2. How do I replace a method/field? I end up with duplicated members!

You must either return the replacement method/field when you visit the original one using the ClassAdapter, or you must first remove the original method/field in the ClassAdapter (see "1. How do I remove a method/field?" ), and then add the new method/field by calling a visit method on the ClassWriter.

3. How do I make ASM calculate visitMaxs for me?

When calling the constructor for ClassWriter pass true. You must also still include the visitMaxs instruction, but the values you give are ignored, so visitMaxs(0,0) is fine.

4. Why do I get the error 'Register 1 contains wrong type'?

When making "return" on a method you must call the appropriate constant. Check Constants.java for a list of all constants. Likewise, make sure you use the appropriate constant for LOAD and STORE intructions, depending on the type of the local variable you want to load or store. The Type.getOpcode method can be used to get the constant corresponding to a given Type and a given bytecode instruction.

              IRETURN, ILOAD, ISTORE, ... // int (also use for
      char,byte)
              LRETURN, LLOAD, LSTORE, ... // long
              FRETURN, FLOAD, FSTORE, ... // float
              DRETURN, DLOAD, DSTORE, ... // double
              ARETURN, ALOAD, ASTORE, ... // reference to an object
              RETURN  // return void
            
    

5. How do I add my bytecode class to the system class loader?

You must first have the security to do this, as defined in the policy file - there are no security restrictions as default for a standard java install. Use ClassLoader.defineClass, this however is a "protected" method, so you must use reflection to gain access to it:

              private Class loadClass(byte[] b) {
                //override classDefine (as it is protected) and
      define
            the class.
                Class clazz = null;
                try {
                  ClassLoader loader =
            ClassLoader.getSystemClassLoader();
                  Class cls =
      Class.forName("java.lang.ClassLoader");
                  java.lang.reflect.Method method =
                    cls.getDeclaredMethod("defineClass", new
      Class[] {
            String.class, byte[] class, int.class, int.class });

                  // protected method invocaton
                  method.setAccessible(true);
                  Object[] args = new Object[] { className, b, new
            Integer(0), new Integer(b.length)};
                  clazz = (Class) method.invoke(loader, args);
                  method.setAccessible(false);
                } catch (Exception e) {
                  e.printStackTrace();
                  System.exit(1);
                }
                return clazz;
              }
            
    

Alternatively you can create your own ClassLoader by extending the existing class loader (example needed here).

6. How do I rename my class?

It is not enough to rename just the class, you must also rename all the references to class members using the CodeAdapter.

            class ClassRenamer extends ClassAdapter implements
      Constants
            {
              private Set oldNames;
              private final String newName;

              public ClassRenamer(ClassVisitor cv, Set oldNames,
      String
            newName) {
                super(cv);
                this.oldNames = oldNames;
                this.newName = newName;
              }

              public void visit(int access, String name, String
            superName, String[] interfaces, String sourceFile) {
                oldNames.add(name);
                cv.visit(ACC_PUBLIC, newName, superName,
      interfaces,
            null);
              }

              public void visitInnerClass(String name, String
      outerName,
            String innerName, int access) {
                cv.visitInnerClass(name, outerName, innerName,
      access);
            //TODO: handle innner classes with ASM
              }

              public void visitField (int access, final String
      name,
            String desc, Object value, Attribute attrs) {
                cv.visitField(access, name, desc, value, attrs);
              }

              public CodeVisitor visitMethod(int access, String
      name,
            String desc, String[] exceptions, Attribute attrs) {
                CodeVisitor cv = this.cv.visitMethod(access, name,
            fixDesc(desc), exceptions, attrs);
                if (cv != null && (access &
            Constants.ACC_ABSTRACT) == 0) {
                  cv = new CodeRenamer(cv);
                }
                return cv;
              }

              class CodeRenamer extends CodeAdapter {
                public CodeRenamer(final CodeVisitor cv) {
                  super(cv);
                }

                public void visitTypeInsn(int i, String s) {
                  if (oldNames.contains(s)) {
                    s = newName;
                  }
                  cv.visitTypeInsn(i, s);
                }

                public void visitFieldInsn(int opcode, String
      owner,
            String name, String desc) {
                  if (oldNames.contains(owner)) {
                    cv.visitFieldInsn(opcode, newName, name,
            fixDesc(desc));
                  } else {
                    cv.visitFieldInsn(opcode, owner, name,
            fixDesc(desc));
                  }
                }

                public void visitMethodInsn(int opcode, String
      owner,
            String name, String desc) {
                  if (oldNames.contains(owner)) {
                    cv.visitMethodInsn(opcode, newName, name,
            fixDesc(desc));
                  } else {
                    cv.visitMethodInsn(opcode, owner, name,
            fixDesc(desc));
                  }
                }
              }

              private String fixDesc(String desc) {
                Iterator it = oldNames.iterator();
                String name;
                while (it.hasNext()) {
                  name = (String) it.next();
                  if (desc.indexOf(name) != -1) {
                    desc = desc.replaceAll(name, newName);
                  }
                }
                return desc;
              }
            }
            
    

7. How do method descriptors work?

To understand this best it's good to read the source code of Type.java. Here is a quick overview:

Examples:

8. How can ASM help me create my descriptor types?

Types.java provides the static method Type.getDescriptor, which takes a Class as a parameter.

Examples:

9. How do I generate Setters and Getters for my class?

Use the following code (this assumes that visitMaxs are computed by ASM - see "3. How do I make ASM calculate visitMaxs for me?" ):

              private void createSetter(String propertyName, String
      type,
            Class c) {
                String methodName = "set" +
      propertyName.substring(0,
            1).toUpperCase() + propertyName.substring(1);

                CodeVisitor cv = cw.visitMethod(ACC_PUBLIC,
      methodName,
            "(" + type + ")V", null, null);
                cv.visitVarInsn(ALOAD, 0);

                //return is based on class type
                //TODO: make work with arrays
                if (!c.isPrimitive()) {
                      cv.visitVarInsn(ALOAD, 1);
                } else {
                    if (c == double.class) {
                          cv.visitVarInsn(DLOAD, 1);
                        } else if (c == float.class) {
                          cv.visitVarInsn(FLOAD, 1);
                        } else if (c == long.class) {
                          cv.visitVarInsn(LLOAD, 1);
                        } else { //byte,short,boolean,int are all
      IRETURN
                            cv.visitVarInsn(ILOAD, 1);
                        }
                }
                cv.visitFieldInsn(PUTFIELD, className,
      propertyName,
            type);
                cv.visitInsn(RETURN);
                cv.visitMaxs(0, 0);
              }

              private void createGetter(String propertyName, String
            returnType, Class c) {
                String methodName = "get" +
      propertyName.substring(0,
            1).toUpperCase() + propertyName.substring(1);

                CodeVisitor cv = cw.visitMethod(ACC_PUBLIC,
      methodName,
            "()" + returnType, null, null);
                cv.visitVarInsn(ALOAD, 0);
                cv.visitFieldInsn(GETFIELD, internalClassName,
            propertyName, returnType);

                //return is based on class type
                //TODO: make work with arrays
                if (!c.isPrimitive()) {
                      cv.visitInsn(ARETURN);
                } else {
                    if (c == double.class) {
                          cv.visitInsn(DRETURN);
                        } else if (c == float.class) {
                          cv.visitInsn(FRETURN);
                        } else if (c == long.class) {
                          cv.visitInsn(LRETURN);
                        } else { //byte,short,boolean,int are all
      IRETURN
                            cv.visitInsn(IRETURN);
                        }
                }
                cv.visitMaxs(0, 0);
              }
            
    

10. How do I get the bytecode of an existing class?

If you want the bytecode instructions themselves, use TraceClassVisitor. If you want the ASM code to generate these bytecode instructions, use DumpClassVisitor. Both classes provide a "main" method to allow them to be called from the command line, passing your fully qualified class name as a parameter. Example:

java -classpath "asm.jar;asm-util.jar;yourjar.jar" org.objectweb.asm.util.DumpClassVisitor org.domain.package.YourClass

11. How do I generate [some Java code] with ASM?

If you want to know how to generate a synchronized block, a try catch block, a finally statement, or any other Java construct, write the Java code you want to generate in a temporary class, compile it with javac, and then use DumpClassVisitor to get the ASM code that will generate this class (see "10. How do I get the bytecode of an existing class?" ).

12. How does the [xxx] bytecode instruction work?

See chapter 6 of the Java Virtual Machine Specification.

13. Is ASM thread safe?

The Type and ClassReader classes are thread safe, i.e. several threads can use a single Type object or a single ClassReader object concurrently without problems. The ClassWriter and CodeWriter classes are not thread safe, i.e. a single class cannot be generated by several concurrent threads (but, of course, several threads can generate distinct classes concurrently, if each thread uses its own ClassWriter instance). In order to generate a single class by using several concurrent threads, one should use ClassAdapter and CodeAdapter instances that delegate to normal ClassWriter and CodeWriter instances, and whose methods are all synchronized.

More generally, ClassVisitor and CodeVisitor implementations, such as ClassWriter and ClassAdapter, do not have to be thread safe. However, non thread safe visitors can be made thread safe just by using a synchronizing class adapter in front of them.

14. What is the earliest JDK required to use ASM?

The org.objectweb.asm package should work with JDK 1.1, or even with JDK 1.0 if Type is not use. Indeed, this package only requires the following JDK classes and methods:

The asm.util and asm.tree packages require JDK 1.2, since they use the List interface and the ArrayList class.