Hard currency for Android programmers, baidu Java interview questions

At the same time, it should be noted that we use testImplementation, which means that we can only use this framework in Java unit testing, which has no impact on our dependencies in Android.

The Android project using gradle in AS will automatically create Java unit tests and Android unit tests. The test code is in test and Android test respectively.

3.2 preparation of pile to be inserted

Create a Java class under test/java:

public class InjectTest {
       public static void main(String[] args) {
    }
}

Since we operate bytecode instrumentation, we can go to test/java and use javac to compile this class to generate the corresponding class file.

javac InjectTest.java

3.3 pile insertion

Because there is no output code in the main method, we enter the command: javaInjectTest will not have any output when executing this Class. Next, we use ASM to insert the log output that records the execution time of the function in the starting diagram into the main method.

Write test method in unit test

/**
* 1,Prepare class es to be analyzed
*/
FileInputStream fis = new  FileInputStream
("xxxxx/test/java/InjectTest.class");
/**
* 2,Perform analysis and pile insertion
*/
//class bytecode reading and analysis engine
ClassReader cr = new  ClassReader(fis);
// Writer COMPUTE_FRAMES automatically calculates all contents, making subsequent operations easier
ClassWriter cw = new  ClassWriter(ClassWriter.COMPUTE_FRAMES);
//Analyze and write the processing results to cw EXPAND_FRAMES: the stack graph is accessed in an extended format
cr.accept(new  ClassAdapterVisitor(cw), ClassReader.EXPAND_FRAMES);
/**
* 3,Obtain results and output
*/
byte[] newClassBytes = cw.toByteArray();
File file = new  File("xxx/test/java2/");
file.mkdirs();
FileOutputStream fos = new  FileOutputStream
("xxx/test/java2/InjectTest.class");
fos.write(newClassBytes);
fos.close();

We will not discuss the design of ASM framework itself here. The above code will get the class generated in the previous step, and then the ASM will output the results to the test/java2 directory after pile insertion. The key point is how to insert piles in step 2.

Give the class data to ClassReader for analysis, which is similar to XML parsing. The analysis results will be notified to the first parameter ClassAdapterVisitor of accept in an event driven form.

public  class  ClassAdapterVisitor  extends  ClassVisitor {
	public  ClassAdapterVisitor(ClassVisitor cv) {
		super(Opcodes.ASM7, cv);
	}
	@Override
	public  MethodVisitor visitMethod(int access, String name, String desc, String signature,
	String[] exceptions) {
		System.out.println("method:" + name + " autograph:" + desc);
		MethodVisitor mv = super.visitMethod(access, name, desc, signature,
		exceptions);
		return  new  MethodAdapterVisitor(api,mv, access, name, desc);
	}
}

The analysis results are obtained through ClassAdapterVisitor. There will be methods, annotations and properties in a class. Therefore, ClassReader will call the corresponding visitMethod, visitAnnotation and visitField visitXX methods in ClassAdapterVisitor.

Our purpose is to perform function instrumentation, so we override the visitMethod method, in which we return a MethodVisitor method parser object. The parameters, annotations and method body of a method need to be analyzed and processed in MethodVisitor.

package com.enjoy.asminject.example;
import com.enjoy.asminject.ASMTest;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.AdviceAdapter;
import org.objectweb.asm.commons.Method;
/**
* AdviceAdapter: Subclass
* The methodVisitor is extended to make it easier for us to analyze methods
*/
public  class  MethodAdapterVisitor  extends  AdviceAdapter {
	private  Boolean inject;
	protected  MethodAdapterVisitor(int api, MethodVisitor methodVisitor, int access, String name, String descriptor) {
		super(api, methodVisitor, access, name, descriptor);
	}
	/**
* Notes above analysis method
* What are you doing here???
* <p>
* Judge whether the current method uses injecttime. If so, we need to insert piles into this method
* If it's not used, it doesn't matter.
*
* @param desc
* @param visible
* @return
*/
	@Override
	public  AnnotationVisitor visitAnnotation(String desc, Boolean visible) {
		if (Type.getDescriptor(ASMTest.class).equals(desc)) {
			System.out.println(desc);
			inject = true;
		}
		return  super.visitAnnotation(desc, visible);
	}
	private  int start;
	@Override
	protected  void onMethodEnter() {
		super.onMethodEnter();
		if (inject) {
			//What if it's done? Record to local variable
			invokeStatic(Type.getType("Ljava/lang/System;"),
			new  Method("currentTimeMillis", "()J"));
			start = newLocal(Type.LONG_TYPE);
			//Create local LONG type variable
			//Record the method execution result to the created local variable
			storeLocal(start);
		}
	}
	@Override
	protected  void onMethodExit(int opcode) {
		super.onMethodExit(opcode);
		if (inject){
			invokeStatic(Type.getType("Ljava/lang/System;"),
			new  Method("currentTimeMillis", "()J"));
			int end = newLocal(Type.LONG_TYPE);
			storeLocal(end);
			getStatic(Type.getType("Ljava/lang/System;"),"out",Type.getType("Ljava/io" +
			"/PrintStream;"));
			//Allocate memory and dup push it into the top of the stack so that the following INVOKESPECIAL knows whose construction method to execute to create StringBuilder
			newInstance(Type.getType("Ljava/lang/StringBuilder;"));
			dup();
			invokeConstructor(Type.getType("Ljava/lang/StringBuilder;"),new  Method("<init>","()V"));
			visitLdcInsn("execute:");
			invokeVirtual(Type.getType("Ljava/lang/StringBuilder;"),new  Method("append","(Ljava/lang/String;)Ljava/lang/StringBuilder;"));
			//subtraction
			loadLocal(end);
			loadLocal(start);
			math(SUB,Type.LONG_TYPE);
			invokeVirtual(Type.getType("Ljava/lang/StringBuilder;"),new  Method("append","(J)Ljava/lang/StringBuilder;"));
			invokeVirtual(Type.getType("Ljava/lang/StringBuilder;"),new  Method("toString","()Ljava/lang/String;"));
			invokeVirtual(Type.getType("Ljava/io/PrintStream;"),new  Method("println","(Ljava/lang/String;)V"));
		}
	}
}

MethodAdapterVisitor inherits from AdviceAdapter, which is actually a subclass of MethodVisitor. AdviceAdapter encapsulates the instruction insertion method, which is more intuitive and simple.

In the above code, onMethodEnter calls back when it enters a method, so inserting an instruction in this method is to add some code at the beginning of the whole method. We need to insert long = system currentTimeMillis();. Insert the output code in onmethodext, that is, at the end of the method.

@Override
protected  void onMethodEnter() {
	super.onMethodEnter();


# last

**Friends in need[You can get it for free here](https://gitee.com/vip204888/java-p7) this is Daniel's study note~**

![tencent T3 500 pages of Daniel's summary MySQL The actual combat notes exploded unexpectedly, P8 Read the call expert](https://img-blog.csdnimg.cn/img_convert/82aa6bc5be2d181753a68bdcd6a5829b.png)

d onMethodEnter() {
	super.onMethodEnter();


# last

**Friends in need[You can get it for free here](https://gitee.com/vip204888/java-p7) this is Daniel's study note~**

[External chain picture transfer...(img-pdOmAY6y-1628664744458)]

![tencent T3 500 pages of Daniel's summary MySQL The actual combat notes exploded unexpectedly, P8 Read the call expert](https://img-blog.csdnimg.cn/img_convert/1891635a06ed9fb6d3040e42922ff4f9.png)

Keywords: Java Back-end Interview Programmer

Added by gordo2dope on Sat, 25 Dec 2021 02:24:34 +0200