I'm trying to use ASM4.1 (I have to use this version for some reason) to add
code before method exit. I am extending AdviceAdapter and following the
technique described in ”ASM 4.0 Guide” under 3.3.4 ”AdviceAdapter” p64-65. I
use LocalVariablesSorter (a superclass of AdviceAdapter) to add local variables.
I have a method “onAdd” that takes one arg and has two locals of its own. My
understanding is that the variable indexes 0 (this), 1 (arg) and 2 & 3
(existing locals) are already taken, and the LocalVariablesSorter.newLocal()
should give me a index 4 or larger. But instead, it gives 2 as first call in
the inserted code before method exit.
I have made a test case for this (I intentionally made the code with mutiple
variables to show the issue):
1. the method to add code before the exit:
public void onAdd(View v) {
String addedText = inputText.getText().toString();
inputTextDisplay.setText(String.format("%s :%s",
getString(R.string.text_added), addedText));
inputTextDisplay.setVisibility(View.VISIBLE);
/**
* the following code needs to be inserted
String s = getClass().getName();
int i = s.indexOf("$");
if (i != -1) {
s = s.substring(0, i);
}
View view = v.getRootView();
String packageName = v.getContext().getPackageName();
InstrumentationInterface.getInstance().performInstrumentation(
view, s, packageName, ActionType.VIEW_CLICK);
*/
}
2. the MethidExistAdapter extends AdviceAdapter to implement the code insertion
@Override
protected void onMethodExit(int opcode) {
if (opcode != Opcodes.ATHROW) {
/**
* Add the following code before return of the method onAdd:
String s = getClass().getName();
int i = s.indexOf("$");
if (i != -1) {
s = s.substring(0, i);
}
View view = v.getRootView();
String packageName = v.getContext().getPackageName();
InstrumentationInterface.getInstance().performInstrumentation(
view, s, packageName);
*/
mv.visitVarInsn(ALOAD, 0); //0 this, 1 - v
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass",
"()Ljava/lang/Class;");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getName",
"()Ljava/lang/String;");
int var1 = newLocal(Type.LONG_TYPE); //s variable 2
mv.visitVarInsn(ASTORE, var1); //2
mv.visitVarInsn(ALOAD, var1); //2
mv.visitLdcInsn("$");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "indexOf",
"(Ljava/lang/String;)I");
int var2 = newLocal(Type.INT_TYPE); //variable 3
mv.visitVarInsn(ISTORE, var2); //3
mv.visitVarInsn(ILOAD, var2); //3
mv.visitInsn(ICONST_M1);
Label label = new Label();
mv.visitJumpInsn(IF_ICMPEQ, label);
mv.visitVarInsn(ALOAD, var1); //2
mv.visitInsn(ICONST_0);
mv.visitVarInsn(ILOAD, var2); //3
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "substring",
"(II)Ljava/lang/String;");
mv.visitVarInsn(ASTORE, var1); //2
mv.visitLabel(label);
mv.visitVarInsn(ALOAD, 1); // v
mv.visitMethodInsn(INVOKEVIRTUAL, "android/view/View", "getRootView",
"()Landroid/view/View;");
int var3 = newLocal(Type.LONG_TYPE); //variable index 4
mv.visitVarInsn(ASTORE, var3); // 4
mv.visitVarInsn(ALOAD, 1); // v
mv.visitMethodInsn(INVOKEVIRTUAL, "android/view/View", "getContext",
"()Landroid/content/Context;");
mv.visitMethodInsn(INVOKEVIRTUAL, "android/content/Context",
"getPackageName", "()Ljava/lang/String;");
int var4 = newLocal(Type.LONG_TYPE); //variable index 5
mv.visitVarInsn(ASTORE, var4); //5
mv.visitMethodInsn(INVOKESTATIC,
"com/ibm/haac/mac/util/InstrumentationInterface", "getInstance",
"()Lcom/ibm/haac/mac/util/InstrumentationInterface;");
mv.visitVarInsn(ALOAD, var3); //4
mv.visitVarInsn(ALOAD, var1); //2
mv.visitVarInsn(ALOAD, var4); //5
mv.visitFieldInsn(GETSTATIC,
"com/ibm/haac/mac/constants/InstrumentationUtil$ActionType", "VIEW_CLICK",
"Lcom/ibm/haac/mac/constants/InstrumentationUtil$ActionType;");
mv.visitMethodInsn(INVOKEVIRTUAL,
"com/ibm/haac/mac/util/InstrumentationInterface", "performInstrumentation",
"(Landroid/view/View;Ljava/lang/String;Ljava/lang/String;Lcom/ibm/haac/mac/constants/InstrumentationUtil$ActionType;)V");
System.out.println(var1 +"," +var2 +", " + var3 +","+var4);
}
super.onMethodExit(opcode);
}
3. After the code insertion, the code varies each time, but they all wrong,
and unable to pass the verification. Here is “onAdd” method from one of the
generated classed after decompilation:
public void onAdd(View view) {
view = inputText.getText().toString();
inputTextDisplay.setText(String.format("%s :%s", new Object[] {
getString(0x7f060005), view
}));
inputTextDisplay.setVisibility(0);
String s = getClass().getName();
int i = s.indexOf("$");
if(i != -1)
s = s.substring(0, i);
View view1 = view.getRootView();
String s1 = view.getContext().getPackageName();
InstrumentationInterface.getInstance().performInstrumentation(view1,
s, s1, com.ibm.haac.mac.constants.InstrumentationUtil.ActionType.VIEW_CLICK);
}
The method is totally screwed up. You can see the local variable is
overwritten even in the very first line (the view is rescued for String!!).
I include the following test code in the zip file:
CustomClassVistor: a class visitor class
TestMethodExitAdapter: implementation of insertion logic
CustomTest: test driver, containing the main, you can run the program.
MainActivity.class: a test java class. Please copy over to a local
folder, and modify the file path in the CustomTest to run.
NewMainActivity.class: generated class after the code insertion.