/*
* Copyright (c) 2009-2012 Panxiaobo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.googlecode.dex2jar.util;
import com.googlecode.dex2jar.DexLabel;
import com.googlecode.dex2jar.Field;
import com.googlecode.dex2jar.Method;
import com.googlecode.dex2jar.visitors.EmptyVisitor;
/**
* @author <a href="mailto:pxb1988@gmail.com">Panxiaobo</a>
* @version $Rev$
*/
public abstract class AbstractDumpDexCodeAdapter extends EmptyVisitor {
protected static final String[] causes = new String[] { "no-error", "generic-error", "no-such-class",
"no-such-field", "no-such-method", "illegal-class-access", "illegal-field-access", "illegal-method-access",
"class-change-error", "instantiation-error" };
protected static String toJavaClass(String type) {
return Dump.toJavaClass(type);
}
protected abstract void info(int opcode, String format, Object... args);
protected abstract String labelToString(DexLabel label);
@Override
public void visitArrayStmt(int opcode, int value, int array, int index, int xt) {
switch (opcode) {
case OP_APUT:
info(opcode, "v%d[v%d]=v%d", array, index, value);
break;
case OP_AGET:
info(opcode, "v%d=v%d[v%d]", value, array, index);
break;
}
}
/*
* (non-Javadoc)
*
* @see com.googlecode.dex2jar.visitors.DexCodeAdapter#visitBinopLitXStmt(int, int, int, int)
*/
@Override
public void visitBinopLitXStmt(int opcode, int saveToReg, int opReg, int value) {
switch (opcode) {
case OP_AND_INT_LIT_X:
info(opcode, "v%d = v%d & %d", saveToReg, opReg, value);
break;
case OP_ADD_INT_LIT_X:
info(opcode, "v%d = v%d + %d", saveToReg, opReg, value);
break;
case OP_REM_INT_LIT_X:
info(opcode, "v%d = v%d %% %d", saveToReg, opReg, value);
break;
case OP_DIV_INT_LIT_X:
info(opcode, "v%d = v%d / %d", saveToReg, opReg, value);
break;
case OP_MUL_INT_LIT_X:
info(opcode, "v%d = v%d * %d", saveToReg, opReg, value);
break;
case OP_SHR_INT_LIT_X:
info(opcode, "v%d = v%d >> %d", saveToReg, opReg, value);
break;
case OP_SHL_INT_LIT_X:
info(opcode, "v%d = v%d << %d", saveToReg, opReg, value);
break;
case OP_USHR_INT_LIT_X:
info(opcode, "v%d = v%d >>> %d", saveToReg, opReg, value);
break;
case OP_OR_INT_LIT_X:
info(opcode, "v%d = v%d | %d", saveToReg, opReg, value);
break;
case OP_XOR_INT_LIT_X:
info(opcode, "v%d = v%d ^ %d", saveToReg, opReg, value);
break;
}
}
@Override
public void visitBinopStmt(int opcode, int saveToReg, int opReg, int opReg2, int xt) {
switch (opcode) {
case OP_AND:
info(opcode, "v%d = v%d & v%d", saveToReg, opReg, opReg2);
break;
case OP_OR:
info(opcode, "v%d = v%d | v%d", saveToReg, opReg, opReg2);
break;
case OP_XOR:
info(opcode, "v%d = v%d ^ v%d", saveToReg, opReg, opReg2);
break;
case OP_SUB:
info(opcode, "v%d = v%d - v%d", saveToReg, opReg, opReg2);
break;
case OP_MUL:
info(opcode, "v%d = v%d * v%d", saveToReg, opReg, opReg2);
break;
case OP_DIV:
info(opcode, "v%d = v%d / v%d", saveToReg, opReg, opReg2);
break;
case OP_ADD:
info(opcode, "v%d = v%d + v%d", saveToReg, opReg, opReg2);
break;
case OP_REM:
info(opcode, "v%d = v%d %% v%d", saveToReg, opReg, opReg2);
break;
}
}
/*
* (non-Javadoc)
*
* @see com.googlecode.dex2jar.visitors.DexCodeAdapter#visitTypeInsn(int, java.lang.String, int, int)
*/
@Override
public void visitClassStmt(int opcode, int toReg, int fromReg, String type) {
switch (opcode) {
case OP_INSTANCE_OF:
info(opcode, "v%d=v%d instanceof %s", toReg, fromReg, Dump.toJavaClass(type));
break;
case OP_NEW_ARRAY:
info(opcode, "v%d=new %s[v%d]", toReg, Dump.toJavaClass(type), fromReg);
break;
}
}
/*
* (non-Javadoc)
*
* @see com.googlecode.dex2jar.visitors.DexCodeAdapter#visitTypeInsn(int, java.lang.String, int)
*/
@Override
public void visitClassStmt(int opcode, int toReg, String type) {
switch (opcode) {
case OP_NEW_INSTANCE:
info(opcode, "v%d=NEW %s", toReg, type);
break;
case OP_CHECK_CAST:
info(opcode, "v%d=(%s) v%d", toReg, Dump.toJavaClass(type), toReg);
break;
}
}
@Override
public void visitCmpStmt(int opcode, int distReg, int bB, int cC, int xt) {
switch (opcode) {
case OP_CMPL:
info(opcode, "v%d = v%d cmpl v%d ", distReg, bB, cC);
break;
case OP_CMPG:
info(opcode, "v%d = v%d cmpg v%d", distReg, bB, cC);
break;
case OP_CMP:
info(opcode, "v%d = v%d cmp v%d", distReg, bB, cC);
break;
}
}
/*
* (non-Javadoc)
*
* @see com.googlecode.dex2jar.visitors.DexCodeAdapter#visitLdcInsn(int, java.lang.Object, int)
*/
@Override
public void visitConstStmt(int opcode, int reg, Object value, int xt) {
switch (opcode) {
case OP_CONST:
if (xt == TYPE_SINGLE) {
info(opcode, "v%d=0x%08x // int:%d float:%f", reg, value, value,
Float.intBitsToFloat((Integer) value));
} else {
info(opcode, "v%d=0x%016x // long:%d double:%f", reg, value, value,
Double.longBitsToDouble((Long) value));
}
break;
case OP_CONST_STRING:
info(opcode, "v%d=\"%s\"", reg, value);
break;
case OP_CONST_CLASS:
info(opcode, "v%d=%s.class", reg, value);
break;
default:
info(opcode, "v%d=%s //", reg, value);
break;
}
}
@Override
public void visitFieldStmt(int opcode, int fromOrToReg, Field field, int xt) {
switch (opcode) {
case OP_SPUT:
info(opcode, "%s.%s=v%d //%s", toJavaClass(field.getOwner()), field.getName(), fromOrToReg, field);
break;
case OP_SGET:
info(opcode, "v%d=%s.%s //%s", fromOrToReg, toJavaClass(field.getOwner()), field.getName(), field);
break;
}
}
@Override
public void visitFieldStmt(int opcode, int regFromOrTo, int owner_reg, Field field, int xt) {
switch (opcode) {
case OP_IGET:
info(opcode, "v%d=v%d.%s //%s", regFromOrTo, owner_reg, field.getName(), field);
break;
case OP_IPUT:
info(opcode, "v%d.%s=v%d //%s", owner_reg, field.getName(), regFromOrTo, field);
break;
}
}
@Override
public void visitFieldStmt(int opcode, int fromOrToReg, int objReg, int fieldoff, int xt) {
switch (opcode) {
case OP_IGET_QUICK:
info(opcode, "Q+ v%d=v%d.fieldoff+%04x", fromOrToReg, objReg, fieldoff);
break;
case OP_IPUT_QUICK:
info(opcode, "Q+ v%d.fieldoff+%04x=v%d", objReg, fieldoff, fromOrToReg);
break;
}
}
/*
* (non-Javadoc)
*
* @see com.googlecode.dex2jar.visitors.DexCodeAdapter#visitFillArrayInsn(int, int, int, int, java.lang.Object[])
*/
@Override
public void visitFillArrayStmt(int opcode, int reg, int elemWidth, int initLength, Object[] values) {
StringBuilder sb = new StringBuilder();
for (Object value : values) {
sb.append(',').append(value);
}
if (sb.length() > 0) {
sb.deleteCharAt(0);
}
info(opcode, "v%d[0..%d]=[%s]", reg, initLength - 1, sb.toString());
}
/*
* (non-Javadoc)
*
* @see com.googlecode.dex2jar.visitors.DexCodeAdapter#visitFilledNewArrayIns(int, java.lang.String, int[])
*/
@Override
public void visitFilledNewArrayStmt(int opcode, int[] regs, String type) {
info(opcode, "TEMP=new %s[%d]", Dump.toJavaClass(type.substring(1)), regs.length);
for (int i = 0; i < regs.length; i++) {
info(opcode, "TEMP[%d]=v%d", i, regs[i]);
}
}
/*
* (non-Javadoc)
*
* @see com.googlecode.dex2jar.visitors.DexCodeAdapter#visitJumpInsn(int, int)
*/
@Override
public void visitJumpStmt(int opcode, DexLabel label) {
switch (opcode) {
case OP_GOTO:
info(opcode, "goto %s", labelToString(label));
break;
}
}
@Override
public void visitJumpStmt(int opcode, int reg, DexLabel label) {
switch (opcode) {
case OP_IF_EQZ:
info(opcode, "if v%d == 0 goto %s", reg, labelToString(label));
break;
case OP_IF_NEZ:
info(opcode, "if v%d != 0 goto %s", reg, labelToString(label));
break;
case OP_IF_LTZ:
info(opcode, "if v%d < 0 goto %s", reg, labelToString(label));
break;
case OP_IF_GEZ:
info(opcode, "if v%d >= 0 goto %s", reg, labelToString(label));
break;
case OP_IF_GTZ:
info(opcode, "if v%d > 0 goto %s", reg, labelToString(label));
break;
case OP_IF_LEZ:
info(opcode, "if v%d <= 0 goto %s", reg, labelToString(label));
break;
}
}
/*
* (non-Javadoc)
*
* @see com.googlecode.dex2jar.visitors.DexCodeAdapter#visitJumpInsn(int, int, int, int)
*/
@Override
public void visitJumpStmt(int opcode, int reg1, int reg2, DexLabel label) {
switch (opcode) {
case OP_IF_EQ:
info(opcode, "if v%d == v%d goto %s", reg1, reg2, labelToString(label));
break;
case OP_IF_NE:
info(opcode, "if v%d != v%d goto %s", reg1, reg2, labelToString(label));
break;
case OP_IF_LT:
info(opcode, "if v%d < v%d goto %s", reg1, reg2, labelToString(label));
break;
case OP_IF_GE:
info(opcode, "if v%d >= v%d goto %s", reg1, reg2, labelToString(label));
break;
case OP_IF_GT:
info(opcode, "if v%d > v%d goto %s", reg1, reg2, labelToString(label));
break;
case OP_IF_LE:
info(opcode, "if v%d <= v%d goto %s", reg1, reg2, labelToString(label));
break;
}
}
/*
* (non-Javadoc)
*
* @see com.googlecode.dex2jar.visitors.DexCodeAdapter#visitLookupSwitchInsn(int, int, int, int[], int[])
*/
@Override
public void visitLookupSwitchStmt(int opcode, int reg, DexLabel label, int[] cases, DexLabel[] label2) {
info(opcode, "switch(v%d)", reg);
for (int i = 0; i < cases.length; i++) {
info(-1, "case %d: goto %s", cases[i], labelToString(label2[i]));
}
info(-1, "default: goto %s", labelToString(label));
}
@Override
public void visitMethodStmt(int opcode, int[] args, int a) {
StringBuilder sb = new StringBuilder();
for (int arg : args) {
sb.append('v').append(arg).append(',');
}
if (sb.length() > 0) {
sb.setLength(sb.length() - 1);
}
switch (opcode) {
case OP_INVOKE_SUPER_QUICK:
case OP_INVOKE_VIRTUAL_QUICK:
info(opcode, "Q+ TEMP=taboff+%04x(%s)", a, sb.toString());
break;
case OP_EXECUTE_INLINE:
info(opcode, "Q+ TEMP=inline+%04x(%s)", a, sb.toString());
break;
}
}
/*
* (non-Javadoc)
*
* @see com.googlecode.dex2jar.visitors.DexCodeAdapter#visitMethodInsn(int, com.googlecode.dex2jar.Method, int[])
*/
@Override
public void visitMethodStmt(int opcode, int[] regs, Method method) {
switch (opcode) {
case OP_INVOKE_STATIC: {
int i = 0;
StringBuilder sb = new StringBuilder();
for (int j = 0; j < method.getParameterTypes().length; j++) {
sb.append('v').append(regs[i++]).append(',');
}
if (sb.length() > 0) {
sb.deleteCharAt(sb.length() - 1);
}
if (method.getReturnType().equals("V")) {
info(opcode, "%s.%s(%s) //%s", toJavaClass(method.getOwner()), method.getName(), sb.toString(),
method.toString());
} else {
info(opcode, "TEMP=%s.%s(%s) //%s", toJavaClass(method.getOwner()), method.getName(), sb.toString(),
method.toString());
}
}
break;
case OP_INVOKE_VIRTUAL:
case OP_INVOKE_DIRECT:
case OP_INVOKE_INTERFACE:
case OP_INVOKE_SUPER: {
int i = 1;
StringBuilder sb = new StringBuilder();
for (int j = 0; j < method.getParameterTypes().length; j++) {
sb.append(',').append('v').append(regs[i++]);
}
if (sb.length() > 0) {
sb.deleteCharAt(0);
}
if (method.getReturnType().equals("V")) {
info(opcode, "v%d.%s(%s) //%s", regs[0], method.getName(), sb.toString(), method.toString());
} else {
info(opcode, "TEMP=v%d.%s(%s) //%s", regs[0], method.getName(), sb.toString(), method.toString());
}
}
break;
}
}
/*
* (non-Javadoc)
*
* @see com.googlecode.dex2jar.visitors.DexCodeAdapter#visitMonitorStmt(int, int)
*/
@Override
public void visitMonitorStmt(int opcode, int reg) {
switch (opcode) {
case OP_MONITOR_ENTER:
info(opcode, "lock v%d", reg);
break;
case OP_MONITOR_EXIT:
info(opcode, "unlock v%d", reg);
break;
}
}
@Override
public void visitMoveStmt(int opcode, int reg, int xt) {
switch (opcode) {
case OP_MOVE_RESULT:
info(opcode, "v%d=TEMP", reg);
break;
case OP_MOVE_EXCEPTION:
info(opcode, "v%d=@Exception", reg);
break;
}
}
/*
* (non-Javadoc)
*
* @see com.googlecode.dex2jar.visitors.DexCodeAdapter#visitMoveStmt(int, int, int)
*/
@Override
public void visitMoveStmt(int opcode, int toReg, int fromReg, int xt) {
switch (opcode) {
case OP_MOVE:
info(opcode, "v%d = v%d", toReg, fromReg);
break;
}
}
@Override
public void visitReturnStmt(int opcode) {
switch (opcode) {
case OP_RETURN_VOID:
info(opcode, "return");
break;
}
}
@Override
public void visitReturnStmt(int opcode, int reg, int xt) {
switch (opcode) {
case OP_RETURN:
info(opcode, "return v%d", reg);
break;
case OP_THROW:
info(opcode, "throw v%d", reg);
break;
}
}
@Override
public void visitReturnStmt(int opcode, int cause, Object ref) {
String c = cause >= causes.length ? "unknown" : causes[cause];
info(opcode, "Q+ throw new VerificationError(%s)", c);
}
/*
* (non-Javadoc)
*
* @see com.googlecode.dex2jar.visitors.DexCodeAdapter#visitTableSwitchInsn(int, int, int, int, int, int[])
*/
@Override
public void visitTableSwitchStmt(int opcode, int reg, DexLabel label, int first_case, int last_case,
DexLabel[] labels) {
info(opcode, "switch(v%d)", reg);
for (int i = 0; i < labels.length; i++) {
info(opcode, "case %d: goto %s", first_case + i, labelToString(labels[i]));
}
info(opcode, "default: goto %s", labelToString(label));
}
@Override
public void visitUnopStmt(int opcode, int saveToReg, int opReg, int xt) {
switch (opcode) {
case OP_NEG:
info(opcode, "v%d = ~v%d", saveToReg, opReg);
break;
case OP_NOT:
info(opcode, "v%d = !v%d", saveToReg, opReg);
break;
case OP_ARRAY_LENGTH:
info(opcode, "v%d = v%d.length", saveToReg, opReg);
break;
}
}
@Override
public void visitUnopStmt(int opcode, int saveToReg, int opReg, int xta, int xtb) {
switch (opcode) {
case OP_X_TO_Y:
switch (xtb) {
case TYPE_BOOLEAN:
info(opcode, "v%d = (boolean)v%d", saveToReg, opReg);
break;
case TYPE_BYTE:
info(opcode, "v%d = (byte)v%d", saveToReg, opReg);
break;
case TYPE_CHAR:
info(opcode, "v%d = (char)v%d", saveToReg, opReg);
break;
case TYPE_DOUBLE:
info(opcode, "v%d = (double)v%d", saveToReg, opReg);
break;
case TYPE_FLOAT:
info(opcode, "v%d = (float)v%d", saveToReg, opReg);
break;
case TYPE_INT:
info(opcode, "v%d = (int)v%d", saveToReg, opReg);
break;
case TYPE_LONG:
info(opcode, "v%d = (long)v%d", saveToReg, opReg);
break;
case TYPE_SHORT:
info(opcode, "v%d = (short)v%d", saveToReg, opReg);
break;
}
break;
}
}
}