title: Java ASM Bytecode Modification-Changing method bodies
body: <p>I have a method of a class in a jar whose body I want to exchange with my own.  In this case I just want to have the method print out "GOT IT" to the console and return true;</p>
  
  <p>I am using the system loader to load the classes of the jar.  I am using reflection to make the system classloader be able to load classes by bytecode.  This part seems to be working correctly. </p>
  
  <p>I am following the method replacement example found here: asm.ow2.org/current/asm-transformations.pdf.</p>
  
  <p>My code is as follows:</p>
  
  <pre><code>public class Main 
  {
      public static void main(String[] args) 
      {
          URL[] url = new URL[1];
          try
          {
              url[0] = new URL("file:////C://Users//emist//workspace//tmloader//bin//runtime//tmgames.jar");
              verifyValidPath(url[0]);
          }
          catch (Exception ex)
          {
              System.out.println("URL error");
          }
          Loader l = new Loader();
          l.loadobjection(url);
      }
  
      public static void verifyValidPath(URL url) throws FileNotFoundException
      {
          File filePath = new File(url.getFile());
          if (!filePath.exists()) 
          {
            throw new FileNotFoundException(filePath.getPath());
          }
      }
  }
  
  class Loader
  {
      private static final Class[] parameters = new Class[] {URL.class};
  
      public static void addURL(URL u) throws IOException
      {
          URLClassLoader sysloader = (URLClassLoader)  ClassLoader.getSystemClassLoader();
          Class sysclass = URLClassLoader.class;
  
          try 
          {
              Method method = sysclass.getDeclaredMethod("addURL", parameters);
              method.setAccessible(true);
              method.invoke(sysloader, new Object[] {u});
          }
          catch (Throwable t) 
          {
              t.printStackTrace();
              throw new IOException("Error, could not add URL to system classloader");
          }
  
      }
  
      private Class loadClass(byte[] b, String name) 
      {
          //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);
              try 
              {
                  Object[] args = new Object[] {name, b, new Integer(0), new Integer(b.length)};
                  clazz = (Class) method.invoke(loader, args);
              }
              finally 
              {
                  method.setAccessible(false);
              }
          }
          catch (Exception e) 
          {
              e.printStackTrace();
              System.exit(1);
          }
          return clazz;
      }
  
      public void loadobjection(URL[] myJar)
      {
          try 
          {
              Loader.addURL(myJar[0]);            
              //tmcore.game is the class that holds the main method in the jar
              /*
              Class&lt;?&gt; classToLoad = Class.forName("tmcore.game", true, this.getClass().getClassLoader());
              if(classToLoad == null)
              {
                  System.out.println("No tmcore.game");
                  return;
              }
              */
              MethodReplacer mr = null;
  
              ClassReader cr = new ClassReader("tmcore.objwin");
              ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
              MethodVisitor mv = null;
              try
              {
                  mr = new MethodReplacer(cw, "Test", "(Ljava/lang/String;ZLjava/lang/String;)Z");
              }
              catch (Exception e)
              {
                  System.out.println("Method Replacer Exception");
              }
              cr.accept(mr, ClassReader.EXPAND_FRAMES);
  
              PrintWriter pw = new PrintWriter(System.out);
              loadClass(cw.toByteArray(), "tmcore.objwin");
              Class&lt;?&gt; classToLoad = Class.forName("tmcore.game", true, this.getClass().getClassLoader());
              if(classToLoad == null)
              {
                  System.out.println("No tmcore.game");
                  return;
              }
  
              //game doesn't have a default constructor, so we need to get the reference to public game(String[] args)
              Constructor ctor = classToLoad.getDeclaredConstructor(String[].class);
              if(ctor == null)
              {
                  System.out.println("can't find constructor");
                  return;
              }
  
              //Instantiate the class by calling the constructor
              String[] args = {"tmgames.jar"};
              Object instance = ctor.newInstance(new Object[]{args});
              if(instance == null)
              {
                  System.out.println("Can't instantiate constructor");
              }
  
              //get reference to main(String[] args)
              Method method = classToLoad.getDeclaredMethod("main", String[].class);
              //call the main method
              method.invoke(instance);
  
          }   
          catch (Exception ex)
          {
              System.out.println(ex.getMessage());
              ex.printStackTrace();
          }
      }
  }
  
  
  public class MethodReplacer extends ClassVisitor implements Opcodes
  {
      private String mname;
      private String mdesc;
      private String cname;
  
      public MethodReplacer(ClassVisitor cv, String mname, String mdesc)
      {
          super(Opcodes.ASM4, cv);
          this.mname = mname;
          this.mdesc = mdesc;
      }
  
      public void visit(int version, int access, String name, String signature, 
                        String superName, String[] interfaces)
      {
          this.cname = name;
          cv.visit(version, access, name, signature, superName, interfaces);
      }
  
      public MethodVisitor visitMethod(int access, String name, String desc, String signature,
                                       String[] exceptions)
      {
          String newName = name;
          if(name.equals(mname) &amp;&amp; desc.equals(mdesc))
          {
              newName = "orig$" + name;
              generateNewBody(access, desc, signature, exceptions, name, newName);
              System.out.println("Replacing");
          }
          return super.visitMethod(access,  newName,  desc,  signature,  exceptions);
      }
  
      private void generateNewBody(int access, String desc, String signature, String[] exceptions,
                                  String name, String newName)
      {
          MethodVisitor mv = cv.visitMethod(access,  name,  desc,  signature,  exceptions);
          mv.visitCode();
          mv.visitVarInsn(Opcodes.ALOAD, 0);
          mv.visitMethodInsn(access, cname, newName, desc);
          mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
          mv.visitLdcInsn("GOTit!");
          mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
          mv.visitInsn(ICONST_0);
          mv.visitInsn(IRETURN);
          mv.visitMaxs(0, 0);
          mv.visitEnd();
      }
  }
  </code></pre>
  
  <p>The problem seems to be at <code>mv.visitMethodInsn(access, cname, newName, desc);</code> in <code>generateMethodBody</code> inside <code>MethodReplacer</code>. </p>
  
  <p>I get an "Illegal Type in constant pool" error.</p>
  
  <p>I'm not sure what I'm missing...but after reading and testing for about 3 days I'm still not getting anywhere. </p>
  
  <p><strong>[Edit]</strong></p>
  
  <p>In case you were wondering, <code>tmcore</code> is a single player "Objection" game for lawyers. I'm doing this for the fun of it.  The program successfully launches the game and everything is fine, removing the modifications from <code>MethodReplacer</code> makes the game behave as designed.  So the issue seems to be isolated to bad bytecode/modifications by me inside the method replacer.</p>
  
  <p><strong>[EDIT2]</strong></p>
  
  <p><code>CheckClassAdapter.verify(cr, true, pw);</code> returns the exact same bytecode that the function is supposed to have before editing.  It is as if the changes are not being done.</p>
  
  <p><strong>[EDIT3]</strong></p>
  
  <p>copy of <code>classtoload</code> commented out as per comments</p>