• 1 overview
  • 2 class loading
  • 3 the bytecode
  • 4 Simple Use

https://mp.weixin.qq.com/s/wDwU7ndRwHdxqIIR43YNuQ

ASM: https://asm.ow2.io/ Guid: Geek time: https://www.baeldung.com/java-asm https://github.com/AndroidAdvanceWithGeektime/Chapter-ASM

1 overview

  • Background: Efficient modification of bytecode, encoding source code functions need to be expanded, modified, and enhanced

  • ASM: bytecode modification tool that can modify class files to modify bytecode during non-runtime

  • ASM is platform independent and provides the ability to modify bytecode

  • scenario

    • Code in the pile
    • Performance optimization
    • Hot repair
    • AOP programming
Bytecode processing

2 class loading

Method area: save Class related information, save a copy of the same type, method and other related information




Custom class loaders


3 the bytecode


Operands are pushed onto the stack

(The lowest level register that controls the thread stack execution method eBP ESP)

Each method constitutes a stack frame running in the thread

Threads themselves execute methods as stacks

At the bottom of the thread stack is main or run

The called method is then pushed above it for execution

The push method takes up stack space, and the execution ends with a pop from the thread stack

When a piece of code is executed by more than one thread, registers or stored variables in memory can be manipulated by more than one thread at a time, causing concurrency problems


For modifying bytecode, you can use tools to generate the modified code directly

IDE Plugin: ASM ByteCode Outline



java code

public class MainActivity {

    private int id;

    public MainActivity(int id) {
 this.id = id;  }   @Override  public String toString() {  return super.toString();  }   public int sum(int l, int r){  int result = l + r;  return result;  } }  Copy the code

bytecode

// class version 49.0 (49)
// access flags 0x21
public class MainActivity {

  // compiled from: MainActivity.java
  // access flags 0x2  private I id   // access flags 0x1  public <init>(I)V  L0  LINENUMBER 5 L0  ALOAD 0  INVOKESPECIAL java/lang/Object.<init> ()V  L1  LINENUMBER 6 L1  ALOAD 0  ILOAD 1  PUTFIELD MainActivity.id : I  L2  LINENUMBER 7 L2  RETURN  L3  LOCALVARIABLE this LMainActivity; L0 L3 0  LOCALVARIABLE id I L0 L3 1  MAXSTACK = 2  MAXLOCALS = 2   // access flags 0x1  public toString()Ljava/lang/String;  L0  LINENUMBER 11 L0  ALOAD 0  INVOKESPECIAL java/lang/Object.toString ()Ljava/lang/String;  ARETURN  L1  LOCALVARIABLE this LMainActivity; L0 L1 0  MAXSTACK = 1  MAXLOCALS = 1   // access flags 0x1  public sum(II)I  L0  LINENUMBER 15 L0  ILOAD 1  ILOAD 2  IADD  ISTORE 3  L1  LINENUMBER 16 L1  ILOAD 3  IRETURN  L2  LOCALVARIABLE this LMainActivity; L0 L2 0  LOCALVARIABLE l I L0 L2 1  LOCALVARIABLE r I L0 L2 2  LOCALVARIABLE result I L1 L2 3  MAXSTACK = 2  MAXLOCALS = 4 }  Copy the code

ASMifed

import java.util.*;

import org.objectweb.asm.*;

public class MainActivityDump implements Opcodes {
  public static byte[] dump() throws Exception {   ClassWriter cw = new ClassWriter(0);  FieldVisitor fv;  MethodVisitor mv;  AnnotationVisitor av0;   cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "MainActivity", null, "java/lang/Object", null);   cw.visitSource("MainActivity.java", null);   {  fv = cw.visitField(ACC_PRIVATE, "id"."I", null, null);  fv.visitEnd();  }  {  mv = cw.visitMethod(ACC_PUBLIC, "<init>"."(I)V", null, null);  mv.visitCode();  Label l0 = new Label();  mv.visitLabel(l0);  mv.visitLineNumber(5, l0);  mv.visitVarInsn(ALOAD, 0);  mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object"."<init>"."()V".false);  Label l1 = new Label();  mv.visitLabel(l1);  mv.visitLineNumber(6, l1);  mv.visitVarInsn(ALOAD, 0);  mv.visitVarInsn(ILOAD, 1);  mv.visitFieldInsn(PUTFIELD, "MainActivity"."id"."I");  Label l2 = new Label();  mv.visitLabel(l2);  mv.visitLineNumber(7, l2);  mv.visitInsn(RETURN);  Label l3 = new Label();  mv.visitLabel(l3);  mv.visitLocalVariable("this"."LMainActivity;", null, l0, l3, 0);  mv.visitLocalVariable("id"."I", null, l0, l3, 1);  mv.visitMaxs(2, 2);  mv.visitEnd();  }  {  mv = cw.visitMethod(ACC_PUBLIC, "toString"."()Ljava/lang/String;", null, null);  mv.visitCode();  Label l0 = new Label();  mv.visitLabel(l0);  mv.visitLineNumber(11, l0);  mv.visitVarInsn(ALOAD, 0);  mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object"."toString"."()Ljava/lang/String;".false);  mv.visitInsn(ARETURN);  Label l1 = new Label();  mv.visitLabel(l1);  mv.visitLocalVariable("this"."LMainActivity;", null, l0, l1, 0);  mv.visitMaxs(1, 1);  mv.visitEnd();  }  {  mv = cw.visitMethod(ACC_PUBLIC, "sum"."(II)I", null, null);  mv.visitCode();  Label l0 = new Label();  mv.visitLabel(l0);  mv.visitLineNumber(15, l0);  mv.visitVarInsn(ILOAD, 1);  mv.visitVarInsn(ILOAD, 2);  mv.visitInsn(IADD);  mv.visitVarInsn(ISTORE, 3);  Label l1 = new Label();  mv.visitLabel(l1);  mv.visitLineNumber(16, l1);  mv.visitVarInsn(ILOAD, 3);  mv.visitInsn(IRETURN);  Label l2 = new Label();  mv.visitLabel(l2);  mv.visitLocalVariable("this"."LMainActivity;", null, l0, l2, 0);  mv.visitLocalVariable("l"."I", null, l0, l2, 1);  mv.visitLocalVariable("r"."I", null, l0, l2, 2);  mv.visitLocalVariable("result"."I", null, l1, l2, 3);  mv.visitMaxs(2, 4);  mv.visitEnd();  }  cw.visitEnd();   return cw.toByteArray();  } }  Copy the code

4 Simple Use

Learn how to use ASM by using an example

Purpose: To insert a line of code before the specified method is executed

Before the change

public class MainActivity {
    @Override
    public String toString() {
        return super.toString();
    }
} Copy the code

The modified

public class MainActivity {
    public String toString() {
        System.out.println("Insert: Hello World!);
        return super.toString();
    }
} Copy the code

Implementation:

/ * * * @author gongshijie
 *
* /public class Main {
  public static void main(String[] args) {   try { // Modify the bytecode ClassReader classReader = new ClassReader(MainActivity.class.getName());  ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS);  ClassVisitor classVisitor = new LogVisitor(Opcodes.ASM5, classWriter);  classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES);  byte[] bytecode = classWriter.toByteArray();  // The modified result FileOutputStream fos = new FileOutputStream(new File("MainActivity.class"));  fos.write(bytecode);  fos.flush();  fos.close();  } catch (IOException e) {  e.printStackTrace();  }  } }  Copy the code
/ * * * @author gongshijie
* /public class MainActivity {
    @Override
 public String toString() {  return super.toString();  } }  Copy the code

/ * * * @author gongshijie
* Traversal method locate the target method* /public class LogVisitor extends ClassVisitor {  public LogVisitor(int i) {  super(i);  }   public LogVisitor(int i, ClassVisitor classVisitor) {  super(i, classVisitor);  }   @Override  public MethodVisitor visitMethod(int i, String s, String s1, String s2, String[] strings) {  MethodVisitor methodVisitor = cv.visitMethod(i, s, s1, s2, strings);  if (s.equals("toString")) { // If we need the enhanced method toString() we go to our visitor to manipulate the bytecode methodVisitor = new LogAdapter(Opcodes.ASM5, methodVisitor, i, s, s1);  }  return methodVisitor;  } }  Copy the code

/ * * * @author gongshijie
* Make bytecode modifications to the target method* /public class LogAdapter extends AdviceAdapter {  protected LogAdapter(int i, MethodVisitor methodVisitor, int i1, String s, String s1) {  super(i, methodVisitor, i1, s, s1);  }   @Override  protected void onMethodEnter() {  mv.visitFieldInsn(GETSTATIC, "java/lang/System"."out"."Ljava/io/PrintStream;");  mv.visitLdcInsn("\u63d2\u5165\uff1aHello World!");  mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream"."println"."(Ljava/lang/String;) V".false);  } }  Copy the code

The results of


In summary, ASM provides the ability to modify bytecode. How can you use it to complete bytecode operations at compile time with no performance implications for scenarios