mirror of
https://github.com/pxb1988/dex2jar.git
synced 2024-11-23 13:19:46 +00:00
--HG--
branch : 0.0.9.x
This commit is contained in:
parent
42ccfe45d3
commit
8c1cc56165
@ -37,7 +37,7 @@ public class DexLabel {
|
||||
|
||||
public String toString() {
|
||||
if (offset >= 0) {
|
||||
return String.format("L%x", offset);
|
||||
return String.format("L%04x", offset);
|
||||
}
|
||||
return String.format("L%08x", this.hashCode());
|
||||
}
|
||||
|
@ -0,0 +1,159 @@
|
||||
package com.googlecode.dex2jar.analysis;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Stack;
|
||||
|
||||
import com.googlecode.dex2jar.DexLabel;
|
||||
import com.googlecode.dex2jar.OdexOpcodes;
|
||||
|
||||
public abstract class Analyzer implements OdexOpcodes {
|
||||
|
||||
private static void link(Node f, Node t) {
|
||||
if (!f._cfg_tos.contains(t)) {
|
||||
f._cfg_tos.add(t);
|
||||
t._cfg_froms++;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean mayThrow(int opcode) {
|
||||
switch (opcode) {
|
||||
case OP_AGET:
|
||||
case OP_ARRAY_LENGTH:
|
||||
case OP_APUT:
|
||||
case OP_CHECK_CAST:
|
||||
case OP_CONST_CLASS:
|
||||
case OP_DIV:
|
||||
case OP_DIV_INT_LIT_X:
|
||||
case OP_EXECUTE_INLINE:
|
||||
case OP_IGET:
|
||||
case OP_IGET_QUICK:
|
||||
case OP_INVOKE_DIRECT:
|
||||
case OP_INVOKE_INTERFACE:
|
||||
case OP_INVOKE_STATIC:
|
||||
case OP_INVOKE_SUPER:
|
||||
case OP_INVOKE_SUPER_QUICK:
|
||||
case OP_INVOKE_VIRTUAL:
|
||||
case OP_INVOKE_VIRTUAL_QUICK:
|
||||
case OP_IPUT:
|
||||
case OP_IPUT_QUICK:
|
||||
case OP_SGET:
|
||||
case OP_SPUT:
|
||||
case OP_THROW:
|
||||
case OP_THROW_VERIFICATION_ERROR:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Analyzer(CodeNode cn) {
|
||||
super();
|
||||
this.cn = cn;
|
||||
|
||||
this.totalReg = cn.total;
|
||||
}
|
||||
|
||||
final protected CodeNode cn;
|
||||
|
||||
final protected int totalReg;;
|
||||
|
||||
public void analyze() {
|
||||
// clean
|
||||
for (Node p = cn.first; p != null; p = p.next) {
|
||||
p._cfg_froms = 0;
|
||||
if (p._cfg_tos != null) {
|
||||
p._cfg_tos.clear();
|
||||
} else {
|
||||
p._cfg_tos = new ArrayList<Node>(5);
|
||||
}
|
||||
p._cfg_visited = false;
|
||||
p.frame = null;
|
||||
}
|
||||
|
||||
for (Node p = cn.first; p != null; p = p.next) {
|
||||
switch (p.opcode) {
|
||||
case OP_IF_EQZ:
|
||||
case OP_IF_NEZ:
|
||||
case OP_IF_LTZ:
|
||||
case OP_IF_GEZ:
|
||||
case OP_IF_GTZ:
|
||||
case OP_IF_LEZ:
|
||||
case OP_IF_EQ:
|
||||
case OP_IF_NE:
|
||||
case OP_IF_LT:
|
||||
case OP_IF_GE:
|
||||
case OP_IF_GT:
|
||||
case OP_IF_LE:
|
||||
link(p, p.next);
|
||||
link(p, (Node) p.la.info);
|
||||
break;
|
||||
case OP_GOTO:
|
||||
link(p, (Node) p.la.info);
|
||||
break;
|
||||
case OP_PACKED_SWITCH:
|
||||
case OP_SPARSE_SWITCH:
|
||||
link(p, (Node) p.la.info);
|
||||
for (DexLabel dl : p.ls) {
|
||||
link(p, (Node) dl.info);
|
||||
}
|
||||
break;
|
||||
case OP_RETURN:
|
||||
case OP_RETURN_VOID:
|
||||
case OP_THROW:
|
||||
case OP_THROW_VERIFICATION_ERROR:
|
||||
break;
|
||||
default:
|
||||
if (p.opcode == CodeNode.OP_LABEL && p.next == null) {
|
||||
// ignore last label
|
||||
} else {
|
||||
link(p, p.next);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (Node t : cn.trys) {
|
||||
Node endNode = (Node) t.lb.info;
|
||||
Node handlerNode = (Node) t.lc.info;
|
||||
Node pre = (Node) t.la.info;
|
||||
for (Node p = pre.next; p != endNode; p = p.next) {
|
||||
if (mayThrow(p.opcode)) {
|
||||
link(pre, handlerNode);
|
||||
}
|
||||
pre = p;
|
||||
}
|
||||
handlerNode.frame = createExceptionHandlerFrame(handlerNode, t.type == null ? "Ljava/lang/Throwable;"
|
||||
: t.type);
|
||||
}
|
||||
|
||||
Node p = cn.first;
|
||||
p.frame = initFirstFrame(p);
|
||||
Stack<Node> stack = new Stack<Node>();
|
||||
|
||||
// System.out.println();
|
||||
|
||||
stack.push(p);
|
||||
while (!stack.empty()) {
|
||||
p = stack.pop();
|
||||
if (p._cfg_visited) {
|
||||
continue;
|
||||
}
|
||||
p._cfg_visited = true;
|
||||
// System.out.print(p);
|
||||
Object frame = exec(p);
|
||||
for (Node x : p._cfg_tos) {
|
||||
merge(frame, x);
|
||||
stack.push(x);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract Object exec(Node p);
|
||||
|
||||
protected abstract void merge(Object frame, Node target);
|
||||
|
||||
protected abstract Object initFirstFrame(Node p);
|
||||
|
||||
protected abstract Object createExceptionHandlerFrame(Node handlerNode, String string);
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.googlecode.dex2jar.xir;
|
||||
package com.googlecode.dex2jar.analysis;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -13,129 +13,138 @@ public class CodeNode implements OdexCodeVisitor {
|
||||
public static final int OP_LABEL = -1;
|
||||
public static final int OP_TRYS = -2;
|
||||
|
||||
public List<Node> insns = new ArrayList<Node>();
|
||||
|
||||
public Node first;
|
||||
public Node last;
|
||||
public List<Node> trys = new ArrayList<Node>(5);
|
||||
|
||||
int total;
|
||||
int args[];
|
||||
public int total;
|
||||
public int args[];
|
||||
|
||||
public void addNode(Node node) {
|
||||
if (first == null) {
|
||||
first = last = node;
|
||||
} else {
|
||||
last.next = node;
|
||||
last = node;
|
||||
}
|
||||
}
|
||||
|
||||
// public List<Insn> lines = new ArrayList(5);
|
||||
|
||||
@Override
|
||||
public void visitArrayStmt(int opcode, int formOrToReg, int arrayReg, int indexReg, int xt) {
|
||||
insns.add(new Node(opcode, formOrToReg, arrayReg, indexReg, xt));
|
||||
addNode(new Node(opcode, formOrToReg, arrayReg, indexReg, xt));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitBinopLitXStmt(int opcode, int distReg, int srcReg, int content) {
|
||||
insns.add(new Node(opcode, distReg, srcReg, content));
|
||||
addNode(new Node(opcode, distReg, srcReg, content));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitBinopStmt(int opcode, int toReg, int r1, int r2, int xt) {
|
||||
insns.add(new Node(opcode, toReg, r1, r2, xt));
|
||||
addNode(new Node(opcode, toReg, r1, r2, xt));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitClassStmt(int opcode, int a, int b, String type) {
|
||||
insns.add(new Node(opcode, a, b, type));
|
||||
addNode(new Node(opcode, a, b, type));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitClassStmt(int opcode, int saveTo, String type) {
|
||||
insns.add(new Node(opcode, saveTo, type));
|
||||
addNode(new Node(opcode, saveTo, type));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCmpStmt(int opcode, int distReg, int bB, int cC, int xt) {
|
||||
insns.add(new Node(opcode, distReg, bB, cC, xt));
|
||||
addNode(new Node(opcode, distReg, bB, cC, xt));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitConstStmt(int opcode, int toReg, Object value, int xt) {
|
||||
insns.add(new Node(opcode, toReg, value, xt));
|
||||
addNode(new Node(opcode, toReg, value, xt));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFieldStmt(int opcode, int fromOrToReg, Field field, int xt) {
|
||||
insns.add(new Node(opcode, fromOrToReg, xt, field));
|
||||
addNode(new Node(opcode, fromOrToReg, xt, field));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFieldStmt(int opcode, int fromOrToReg, int objReg, Field field, int xt) {
|
||||
insns.add(new Node(opcode, fromOrToReg, objReg, xt, field));
|
||||
addNode(new Node(opcode, fromOrToReg, objReg, xt, field));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFillArrayStmt(int opcode, int aA, int elemWidth, int initLength, Object[] values) {
|
||||
insns.add(new Node(opcode, aA, elemWidth, values));
|
||||
addNode(new Node(opcode, aA, elemWidth, values));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFilledNewArrayStmt(int opcode, int[] args, String type) {
|
||||
insns.add(new Node(opcode, args, type));
|
||||
addNode(new Node(opcode, args, type));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitJumpStmt(int opcode, int a, int b, DexLabel label) {
|
||||
insns.add(new Node(opcode, a, b, label));
|
||||
addNode(new Node(opcode, a, b, label));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitJumpStmt(int opcode, int reg, DexLabel label) {
|
||||
insns.add(new Node(opcode, reg, label));
|
||||
addNode(new Node(opcode, reg, label));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitJumpStmt(int opcode, DexLabel label) {
|
||||
insns.add(new Node(opcode, label));
|
||||
addNode(new Node(opcode, label));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLookupSwitchStmt(int opcode, int aA, DexLabel label, int[] cases, DexLabel[] labels) {
|
||||
insns.add(new Node(opcode, aA, label, cases, labels));
|
||||
addNode(new Node(opcode, aA, label, cases, labels));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodStmt(int opcode, int[] args, Method method) {
|
||||
insns.add(new Node(opcode, args, method));
|
||||
addNode(new Node(opcode, args, method));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMonitorStmt(int opcode, int reg) {
|
||||
insns.add(new Node(opcode, reg));
|
||||
addNode(new Node(opcode, reg));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMoveStmt(int opcode, int toReg, int xt) {
|
||||
insns.add(new Node(opcode, toReg, xt));
|
||||
addNode(new Node(opcode, toReg, xt));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMoveStmt(int opcode, int toReg, int fromReg, int xt) {
|
||||
insns.add(new Node(opcode, toReg, fromReg, xt));
|
||||
addNode(new Node(opcode, toReg, fromReg, xt));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitReturnStmt(int opcode) {
|
||||
insns.add(new Node(opcode));
|
||||
addNode(new Node(opcode));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitReturnStmt(int opcode, int reg, int xt) {
|
||||
insns.add(new Node(opcode, reg, xt));
|
||||
addNode(new Node(opcode, reg, xt));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTableSwitchStmt(int opcode, int aA, DexLabel label, int first_case, int last_case,
|
||||
DexLabel[] labels) {
|
||||
insns.add(new Node(opcode, aA, label, first_case, last_case, labels));
|
||||
addNode(new Node(opcode, aA, label, first_case, last_case, labels));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitUnopStmt(int opcode, int toReg, int fromReg, int xt) {
|
||||
insns.add(new Node(opcode, toReg, fromReg, xt));
|
||||
addNode(new Node(opcode, toReg, fromReg, xt));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -155,7 +164,9 @@ public class CodeNode implements OdexCodeVisitor {
|
||||
|
||||
@Override
|
||||
public void visitLabel(DexLabel label) {
|
||||
insns.add(new Node(OP_LABEL, label));
|
||||
Node node = new Node(OP_LABEL, label);
|
||||
label.info = node;
|
||||
addNode(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -172,22 +183,34 @@ public class CodeNode implements OdexCodeVisitor {
|
||||
|
||||
@Override
|
||||
public void visitReturnStmt(int opcode, int cause, Object ref) {
|
||||
insns.add(new Node(opcode, cause, ref));
|
||||
addNode(new Node(opcode, cause, ref));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodStmt(int opcode, int[] args, int a) {
|
||||
insns.add(new Node(opcode, args, a));
|
||||
addNode(new Node(opcode, args, a));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFieldStmt(int opcode, int fromOrToReg, int objReg, int fieldoff, int xt) {
|
||||
insns.add(new Node(opcode, fromOrToReg, objReg, fieldoff, xt));
|
||||
addNode(new Node(opcode, fromOrToReg, objReg, fieldoff, xt));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitUnopStmt(int opcode, int toReg, int fromReg, int xta, int xtb) {
|
||||
insns.add(new Node(opcode, fromReg, xta, xtb));
|
||||
addNode(new Node(opcode, toReg, fromReg, xta, xtb));
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
NodeDump nd = new NodeDump();
|
||||
for (Node p = first; p != null; p = p.next) {
|
||||
p.accept(nd);
|
||||
}
|
||||
|
||||
sb.append(nd.toString());
|
||||
// TODO add trys
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
package com.googlecode.dex2jar.xir;
|
||||
package com.googlecode.dex2jar.analysis;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.googlecode.dex2jar.DexLabel;
|
||||
import com.googlecode.dex2jar.Field;
|
||||
@ -17,6 +19,12 @@ public class Node implements OdexOpcodes {
|
||||
public DexLabel la, lb, lc;
|
||||
public DexLabel[] ls;
|
||||
|
||||
public Node next;
|
||||
public Object frame;
|
||||
public int _cfg_froms;
|
||||
public List<Node> _cfg_tos;
|
||||
public boolean _cfg_visited;
|
||||
|
||||
public Node(int opcode, int a, int b, String type) {
|
||||
super();
|
||||
this.opcode = opcode;
|
||||
@ -158,11 +166,11 @@ public class Node implements OdexOpcodes {
|
||||
this.c = c;
|
||||
}
|
||||
|
||||
public Node(int opcode, int a, DexLabel la, int[] cst, DexLabel[] ls) {
|
||||
public Node(int opcode, int a, DexLabel la, int[] args, DexLabel[] ls) {
|
||||
this.opcode = opcode;
|
||||
this.a = a;
|
||||
this.la = la;
|
||||
this.cst = cst;
|
||||
this.args = args;
|
||||
this.ls = ls;
|
||||
}
|
||||
|
||||
@ -198,7 +206,7 @@ public class Node implements OdexOpcodes {
|
||||
case OP_SHL_INT_LIT_X:
|
||||
case OP_SHR_INT_LIT_X:
|
||||
case OP_USHR_INT_LIT_X:
|
||||
dcv.visitBinopLitXStmt(opcode, a, a, c);
|
||||
dcv.visitBinopLitXStmt(opcode, a, b, c);
|
||||
break;
|
||||
case OP_ADD:
|
||||
case OP_SUB:
|
||||
@ -213,12 +221,12 @@ public class Node implements OdexOpcodes {
|
||||
case OP_USHR:
|
||||
dcv.visitBinopStmt(opcode, a, b, c, d);
|
||||
break;
|
||||
case OP_INSTANCE_OF:
|
||||
case OP_NEW_ARRAY:
|
||||
dcv.visitClassStmt(opcode, a, type);
|
||||
break;
|
||||
case OP_CHECK_CAST:
|
||||
case OP_NEW_INSTANCE:
|
||||
dcv.visitClassStmt(opcode, a, type);
|
||||
break;
|
||||
case OP_NEW_ARRAY:
|
||||
case OP_INSTANCE_OF:
|
||||
dcv.visitClassStmt(opcode, a, b, type);
|
||||
break;
|
||||
case OP_CMPL:
|
||||
@ -275,7 +283,7 @@ public class Node implements OdexOpcodes {
|
||||
case OP_INVOKE_DIRECT:
|
||||
case OP_INVOKE_STATIC:
|
||||
case OP_INVOKE_INTERFACE:
|
||||
dcv.visitMethodStmt(opcode, args, a);
|
||||
dcv.visitMethodStmt(opcode, args, method);
|
||||
break;
|
||||
case OP_MONITOR_ENTER:
|
||||
case OP_MONITOR_EXIT:
|
@ -1,4 +1,4 @@
|
||||
package com.googlecode.dex2jar.xir;
|
||||
package com.googlecode.dex2jar.analysis;
|
||||
|
||||
import com.googlecode.dex2jar.DexLabel;
|
||||
import com.googlecode.dex2jar.DexOpcodeDump;
|
||||
@ -22,6 +22,12 @@ public class NodeDump extends AbstractDumpDexCodeAdapter {
|
||||
sb.append(String.format("%-20s|%5s:\n", "LABEL", labelToString(label)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTryCatch(DexLabel start, DexLabel end, DexLabel handler, String type) {
|
||||
sb.append(String.format("TRY %s %s %s > %s\n", labelToString(start), labelToString(end), labelToString(handler),
|
||||
type == null ? "ALL" : type));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String labelToString(DexLabel label) {
|
||||
return label.toString();
|
@ -0,0 +1,256 @@
|
||||
package com.googlecode.dex2jar.analysis.type;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.googlecode.dex2jar.Method;
|
||||
import com.googlecode.dex2jar.analysis.Analyzer;
|
||||
import com.googlecode.dex2jar.analysis.CodeNode;
|
||||
import com.googlecode.dex2jar.analysis.Node;
|
||||
|
||||
public class TypeAnalyzer extends Analyzer {
|
||||
public static class Reg {
|
||||
/**
|
||||
* Phi
|
||||
*/
|
||||
public Set<Reg> phi = new HashSet<Reg>(3);
|
||||
public Set<String> typs = new HashSet<String>();
|
||||
public boolean used;
|
||||
|
||||
public String toString() {
|
||||
if (typs.size() == 0) {
|
||||
return "?";
|
||||
}
|
||||
if (typs.size() == 1) {
|
||||
return typs.iterator().next().substring(0, 1);
|
||||
}
|
||||
return sizeOf(this) == 1 ? "S" : "W";
|
||||
}
|
||||
}
|
||||
|
||||
public static int sizeOf(Reg reg) {
|
||||
for (String s : reg.typs) {
|
||||
switch (s.charAt(0)) {
|
||||
case 'J':
|
||||
case 'D':
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
final TypeVisitor<Reg> typeVisitor;
|
||||
|
||||
private final Reg[] frame;
|
||||
final boolean isStatic;
|
||||
|
||||
final Method method;
|
||||
|
||||
public TypeAnalyzer(CodeNode cn, boolean isStatic, Method method) {
|
||||
super(cn);
|
||||
this.isStatic = isStatic;
|
||||
this.method = method;
|
||||
this.typeVisitor = new TypeVisitor<Reg>(totalReg, method.getReturnType()) {
|
||||
|
||||
@Override
|
||||
public Reg _new() {
|
||||
return new Reg();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void _put(int reg, Reg n) {
|
||||
frame[reg] = n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int _size(int i) {
|
||||
return sizeOf((Reg) frame[i]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void _type(Reg r, String desc) {
|
||||
Set<String> ts = r.typs;
|
||||
if (ts.size() == 0) {
|
||||
ts.add(desc);
|
||||
return;
|
||||
}
|
||||
if (ts.contains(desc)) {
|
||||
return;
|
||||
}
|
||||
switch (desc.charAt(0)) {
|
||||
case '_': {
|
||||
boolean f = false;
|
||||
for (String s : ts) {
|
||||
if (!s.startsWith("_")) {
|
||||
f = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!f) {
|
||||
switch (desc.charAt(1)) {
|
||||
case '1':
|
||||
break;
|
||||
case '2':
|
||||
break;
|
||||
case '3':
|
||||
ts.remove(TypeVisitor.IFL);
|
||||
ts.add(desc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case '[': {
|
||||
if (desc.length() > 1) {
|
||||
if (desc.charAt(1) == '_') {
|
||||
boolean f = false;
|
||||
for (String s : ts) {
|
||||
if (s.length() >= 2 && s.charAt(0) == '[' && s.charAt(1) != '_') {
|
||||
f = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!f) {
|
||||
ts.add(desc);
|
||||
}
|
||||
} else {
|
||||
ts.remove(TypeVisitor.AIFL);
|
||||
ts.remove(TypeVisitor.AJD);
|
||||
}
|
||||
}
|
||||
if (ts.contains(TypeVisitor.IFL) || ts.contains(TypeVisitor.IL)) {
|
||||
ts.remove(TypeVisitor.IFL);
|
||||
ts.remove(TypeVisitor.IL);
|
||||
ts.add("L");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'L':
|
||||
if (desc.length() == 1) {
|
||||
boolean f = false;
|
||||
for (String s : ts) {
|
||||
if (s.startsWith("L")) {
|
||||
f = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!f) {
|
||||
ts.add(desc);
|
||||
}
|
||||
} else {
|
||||
ts.remove("L");
|
||||
ts.add(desc);
|
||||
}
|
||||
ts.remove(TypeVisitor.IFL);
|
||||
ts.remove(TypeVisitor.IL);
|
||||
ts.remove(TypeVisitor.JD);
|
||||
break;
|
||||
default: {
|
||||
ts.remove(TypeVisitor.IFL);
|
||||
ts.remove(TypeVisitor.IL);
|
||||
ts.remove(TypeVisitor.JD);
|
||||
ts.add(desc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Reg _use(int reg) {
|
||||
Reg r = (Reg) frame[reg];
|
||||
if (r != null) {
|
||||
r.used = true;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
};
|
||||
frame = new Reg[totalReg + 2];
|
||||
}
|
||||
|
||||
protected Object createExceptionHandlerFrame(Node handler, String type) {
|
||||
Reg[] frame = (Reg[]) handler.frame;
|
||||
Reg obj;
|
||||
if (frame == null) {
|
||||
frame = new Reg[totalReg + 2];
|
||||
obj = frame[totalReg + 1] = typeVisitor._new();
|
||||
} else {
|
||||
obj = frame[totalReg + 1];
|
||||
}
|
||||
typeVisitor._type(obj, type);
|
||||
return frame;
|
||||
}
|
||||
|
||||
protected Object exec(Node p) {
|
||||
Object[] frame = (Object[]) p.frame;
|
||||
if (frame == null) {
|
||||
for (int i = 0; i < this.frame.length; i++) {
|
||||
this.frame[i] = null;
|
||||
}
|
||||
} else {
|
||||
System.arraycopy(frame, 0, this.frame, 0, totalReg + 2);
|
||||
}
|
||||
p.accept(typeVisitor);
|
||||
return this.frame;
|
||||
}
|
||||
|
||||
protected Object initFirstFrame(Node p) {
|
||||
int i = 0;
|
||||
Reg[] frame = new Reg[totalReg + 2];
|
||||
if (!isStatic) {
|
||||
Reg obj = frame[cn.args[i]] = typeVisitor._new();
|
||||
typeVisitor._type(obj, method.getOwner());
|
||||
i++;
|
||||
}
|
||||
for (String pt : method.getParameterTypes()) {
|
||||
Reg obj = frame[cn.args[i]] = typeVisitor._new();
|
||||
typeVisitor._type(obj, pt);
|
||||
i++;
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
|
||||
protected void merge(Object f, Node target) {
|
||||
Object[] frame = (Object[]) f;
|
||||
if (target._cfg_froms == 1) {// only from one node, direct copy node
|
||||
if (target.frame == null) {
|
||||
target.frame = new Object[totalReg + 2];
|
||||
System.arraycopy(frame, 0, target.frame, 0, totalReg + 2);
|
||||
} else {
|
||||
Object[] targetFrame = (Object[]) target.frame;
|
||||
for (int i = 0; i < totalReg + 2; i++) {
|
||||
if (targetFrame[i] == null) {
|
||||
targetFrame[i] = frame[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {// from 2+ node, insert a phi
|
||||
if (target.frame == null) {
|
||||
Object[] targetFrame;
|
||||
target.frame = targetFrame = new Object[totalReg + 2];
|
||||
for (int i = 0; i < totalReg + 2; i++) {
|
||||
if (frame[i] != null) {
|
||||
Reg reg = new Reg();
|
||||
reg.phi.add((Reg) frame[i]);
|
||||
targetFrame[i] = reg;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Object[] targetFrame = (Object[]) target.frame;
|
||||
for (int i = 0; i < totalReg + 2; i++) {
|
||||
if (frame[i] != null) {
|
||||
Reg reg = (Reg) targetFrame[i];
|
||||
if (reg == null) {
|
||||
if (!target._cfg_visited) {
|
||||
reg = new Reg();
|
||||
targetFrame[i] = reg;
|
||||
reg.phi.add((Reg) frame[i]);
|
||||
}
|
||||
} else {
|
||||
reg.phi.add((Reg) frame[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,293 @@
|
||||
package com.googlecode.dex2jar.analysis.type;
|
||||
|
||||
import com.googlecode.dex2jar.DexLabel;
|
||||
import com.googlecode.dex2jar.Field;
|
||||
import com.googlecode.dex2jar.Method;
|
||||
import com.googlecode.dex2jar.OdexOpcodes;
|
||||
import com.googlecode.dex2jar.visitors.OdexCodeVisitor;
|
||||
|
||||
public abstract class TypeVisitor<T> implements OdexCodeVisitor, OdexOpcodes {
|
||||
|
||||
protected void useAndType(int reg, String desc) {
|
||||
_type(_use(reg), desc);
|
||||
}
|
||||
|
||||
final protected int totalReg;
|
||||
final protected String returnType;
|
||||
|
||||
public TypeVisitor(int totalReg, String returnType) {
|
||||
super();
|
||||
this.totalReg = totalReg;
|
||||
this.returnType = returnType;
|
||||
}
|
||||
|
||||
protected void newAndType(int reg, String desc) {
|
||||
T n = _new();
|
||||
_type(n, desc);
|
||||
_put(reg, n);
|
||||
}
|
||||
|
||||
public abstract void _put(int reg, T n);
|
||||
|
||||
public abstract T _new();
|
||||
|
||||
public abstract T _use(int reg);
|
||||
|
||||
public abstract void _type(T n, String desc);
|
||||
|
||||
public abstract int _size(int i);
|
||||
|
||||
public static String IFL = "_1IFL";
|
||||
public static String IL = "_3IL";
|
||||
public static String JD = "_2JD";
|
||||
public static String AIFL = "[_1IFL";
|
||||
public static String AJD = "[_2JD";
|
||||
|
||||
static String[] descs = new String[] { IFL, JD, "L", "Z", "B", "C", "S", "I", "F", "J", "D" };
|
||||
static String[] descsArray = new String[] { AIFL, AJD, "[L", "[Z", "[B", "[C", "[S", "[I", "[F", "[J", "[D" };
|
||||
|
||||
@Override
|
||||
public void visitArrayStmt(int opcode, int formOrToReg, int arrayReg, int indexReg, int xt) {
|
||||
useAndType(arrayReg, descsArray[xt]);
|
||||
useAndType(indexReg, "I");
|
||||
if (opcode == OP_AGET) {
|
||||
newAndType(formOrToReg, descs[xt]);
|
||||
} else {
|
||||
useAndType(formOrToReg, descs[xt]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitBinopLitXStmt(int opcode, int distReg, int srcReg, int content) {
|
||||
useAndType(srcReg, "I");
|
||||
newAndType(distReg, "I");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitBinopStmt(int opcode, int toReg, int r1, int r2, int xt) {
|
||||
useAndType(r1, "I");
|
||||
useAndType(r2, "I");
|
||||
newAndType(toReg, "I");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitClassStmt(int opcode, int a, int b, String type) {
|
||||
if (opcode == OP_INSTANCE_OF) {
|
||||
useAndType(b, "L");
|
||||
newAndType(a, "Z");
|
||||
} else {
|
||||
useAndType(b, "I");
|
||||
newAndType(a, type);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitClassStmt(int opcode, int saveTo, String type) {
|
||||
if (opcode == OP_CHECK_CAST) {
|
||||
useAndType(saveTo, "L");
|
||||
}
|
||||
newAndType(saveTo, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCmpStmt(int opcode, int distReg, int bB, int cC, int xt) {
|
||||
useAndType(bB, descs[xt]);
|
||||
useAndType(cC, descs[xt]);
|
||||
newAndType(distReg, "I");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitConstStmt(int opcode, int toReg, Object value, int xt) {
|
||||
if (opcode == OP_CONST_CLASS) {
|
||||
newAndType(toReg, "Ljava/lang/Class;");
|
||||
} else if (opcode == OP_CONST_STRING) {
|
||||
newAndType(toReg, "Ljava/lang/String;");
|
||||
} else {
|
||||
newAndType(toReg, descs[xt]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFieldStmt(int opcode, int fromOrToReg, Field field, int xt) {
|
||||
if (opcode == OP_SGET) {
|
||||
newAndType(fromOrToReg, field.getType());
|
||||
} else {
|
||||
useAndType(fromOrToReg, field.getType());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFieldStmt(int opcode, int fromOrToReg, int objReg, Field field, int xt) {
|
||||
useAndType(objReg, field.getOwner());
|
||||
if (opcode == OP_IGET) {
|
||||
newAndType(fromOrToReg, field.getType());
|
||||
} else {
|
||||
useAndType(fromOrToReg, field.getType());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFillArrayStmt(int opcode, int aA, int elemWidth, int initLength, Object[] values) {
|
||||
newAndType(aA, "[");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFilledNewArrayStmt(int opcode, int[] args, String type) {
|
||||
String eType = type.substring(1);
|
||||
for (int arg : args) {
|
||||
useAndType(arg, eType);
|
||||
}
|
||||
newAndType(totalReg, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitJumpStmt(int opcode, int a, int b, DexLabel label) {
|
||||
if (opcode == OP_IF_EQ || opcode == OP_IF_NE) {
|
||||
useAndType(a, IL);
|
||||
useAndType(b, IL);
|
||||
} else {
|
||||
useAndType(a, "I");
|
||||
useAndType(b, "I");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitJumpStmt(int opcode, int a, DexLabel label) {
|
||||
if (opcode == OP_IF_EQZ || opcode == OP_IF_NEZ) {
|
||||
useAndType(a, IL);
|
||||
} else {
|
||||
useAndType(a, "I");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitJumpStmt(int opcode, DexLabel label) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLookupSwitchStmt(int opcode, int aA, DexLabel label, int[] cases, DexLabel[] labels) {
|
||||
useAndType(aA, "I");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodStmt(int opcode, int[] args, Method method) {
|
||||
int i = 0;
|
||||
if (opcode != OP_INVOKE_STATIC) {
|
||||
useAndType(args[i], method.getOwner());
|
||||
i++;
|
||||
}
|
||||
for (String ps : method.getParameterTypes()) {
|
||||
useAndType(args[i], ps);
|
||||
i++;
|
||||
}
|
||||
if (!"V".equals(method.getReturnType())) {
|
||||
newAndType(totalReg, method.getReturnType());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMonitorStmt(int opcode, int reg) {
|
||||
useAndType(reg, "L");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMoveStmt(int opcode, int toReg, int xt) {
|
||||
if (opcode == OP_MOVE_RESULT) {
|
||||
useAndType(totalReg, descs[xt]);
|
||||
_put(totalReg, null);
|
||||
} else {
|
||||
useAndType(totalReg + 1, descs[xt]);
|
||||
_put(totalReg + 1, null);
|
||||
}
|
||||
newAndType(toReg, descs[xt]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMoveStmt(int opcode, int toReg, int fromReg, int xt) {
|
||||
useAndType(fromReg, descs[xt]);
|
||||
newAndType(toReg, descs[xt]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitReturnStmt(int opcode) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitReturnStmt(int opcode, int reg, int xt) {
|
||||
if (opcode == OP_THROW) {
|
||||
useAndType(reg, "Ljava/lang/Throwable;");
|
||||
} else {
|
||||
useAndType(reg, this.returnType);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTableSwitchStmt(int opcode, int aA, DexLabel label, int first_case, int last_case,
|
||||
DexLabel[] labels) {
|
||||
useAndType(aA, "I");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitUnopStmt(int opcode, int toReg, int fromReg, int xt) {
|
||||
if (opcode == OP_ARRAY_LENGTH) {
|
||||
useAndType(fromReg, "[");
|
||||
newAndType(toReg, "I");
|
||||
} else {
|
||||
useAndType(fromReg, descs[xt]);
|
||||
newAndType(toReg, descs[xt]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitUnopStmt(int opcode, int toReg, int fromReg, int xta, int xtb) {
|
||||
useAndType(fromReg, descs[xta]);
|
||||
newAndType(toReg, descs[xtb]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTryCatch(DexLabel start, DexLabel end, DexLabel handler, String type) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitArguments(int total, int[] args) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLabel(DexLabel label) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLineNumber(int line, DexLabel label) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLocalVariable(String name, String type, String signature, DexLabel start, DexLabel end, int reg) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitReturnStmt(int opcode, int cause, Object ref) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodStmt(int opcode, int[] args, int a) {
|
||||
for (int i = 0; i < args.length; i += _size(args[i])) {
|
||||
_use(args[i]);
|
||||
}
|
||||
_put(totalReg, _new());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFieldStmt(int opcode, int fromOrToReg, int objReg, int fieldoff, int xt) {
|
||||
useAndType(objReg, "L");
|
||||
if (opcode == OP_IGET_QUICK) {
|
||||
newAndType(fromOrToReg, descs[xt]);
|
||||
} else {
|
||||
useAndType(fromOrToReg, descs[xt]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -648,7 +648,7 @@ import com.googlecode.dex2jar.visitors.DexCodeVisitor;
|
||||
*/
|
||||
void order(int offset) {
|
||||
if (!labels.containsKey(offset)) {
|
||||
labels.put(offset, new DexLabel());
|
||||
labels.put(offset, new DexLabel(offset));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -278,7 +278,7 @@ public abstract class AbstractDumpDexCodeAdapter extends EmptyVisitor {
|
||||
public void visitJumpStmt(int opcode, DexLabel label) {
|
||||
switch (opcode) {
|
||||
case OP_GOTO:
|
||||
info(opcode, "goto L%s", labelToString(label));
|
||||
info(opcode, "goto %s", labelToString(label));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -286,22 +286,22 @@ public abstract class AbstractDumpDexCodeAdapter extends EmptyVisitor {
|
||||
public void visitJumpStmt(int opcode, int reg, DexLabel label) {
|
||||
switch (opcode) {
|
||||
case OP_IF_EQZ:
|
||||
info(opcode, "if v%d == 0 goto L%s", reg, labelToString(label));
|
||||
info(opcode, "if v%d == 0 goto %s", reg, labelToString(label));
|
||||
break;
|
||||
case OP_IF_NEZ:
|
||||
info(opcode, "if v%d != 0 goto L%s", reg, labelToString(label));
|
||||
info(opcode, "if v%d != 0 goto %s", reg, labelToString(label));
|
||||
break;
|
||||
case OP_IF_LTZ:
|
||||
info(opcode, "if v%d < 0 goto L%s", reg, labelToString(label));
|
||||
info(opcode, "if v%d < 0 goto %s", reg, labelToString(label));
|
||||
break;
|
||||
case OP_IF_GEZ:
|
||||
info(opcode, "if v%d >= 0 goto L%s", reg, labelToString(label));
|
||||
info(opcode, "if v%d >= 0 goto %s", reg, labelToString(label));
|
||||
break;
|
||||
case OP_IF_GTZ:
|
||||
info(opcode, "if v%d > 0 goto L%s", reg, labelToString(label));
|
||||
info(opcode, "if v%d > 0 goto %s", reg, labelToString(label));
|
||||
break;
|
||||
case OP_IF_LEZ:
|
||||
info(opcode, "if v%d <= 0 goto L%s", reg, labelToString(label));
|
||||
info(opcode, "if v%d <= 0 goto %s", reg, labelToString(label));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -315,22 +315,22 @@ public abstract class AbstractDumpDexCodeAdapter extends EmptyVisitor {
|
||||
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 L%s", reg1, reg2, labelToString(label));
|
||||
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 L%s", reg1, reg2, labelToString(label));
|
||||
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 L%s", reg1, reg2, labelToString(label));
|
||||
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 L%s", reg1, reg2, labelToString(label));
|
||||
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 L%s", reg1, reg2, labelToString(label));
|
||||
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 L%s", reg1, reg2, labelToString(label));
|
||||
info(opcode, "if v%d <= v%d goto %s", reg1, reg2, labelToString(label));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -344,9 +344,9 @@ public abstract class AbstractDumpDexCodeAdapter extends EmptyVisitor {
|
||||
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 L%s", cases[i], labelToString(label2[i]));
|
||||
info(-1, "case %d: goto %s", cases[i], labelToString(label2[i]));
|
||||
}
|
||||
info(-1, "default: goto L%s", labelToString(label));
|
||||
info(-1, "default: goto %s", labelToString(label));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -413,7 +413,6 @@ public abstract class AbstractDumpDexCodeAdapter extends EmptyVisitor {
|
||||
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;
|
||||
@ -441,9 +440,11 @@ public abstract class AbstractDumpDexCodeAdapter extends EmptyVisitor {
|
||||
public void visitMoveStmt(int opcode, int reg, int xt) {
|
||||
switch (opcode) {
|
||||
case OP_MOVE_RESULT:
|
||||
case OP_MOVE_EXCEPTION:
|
||||
info(opcode, "v%d=TEMP", reg);
|
||||
break;
|
||||
case OP_MOVE_EXCEPTION:
|
||||
info(opcode, "v%d=@Exception", reg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -497,9 +498,9 @@ public abstract class AbstractDumpDexCodeAdapter extends EmptyVisitor {
|
||||
DexLabel[] labels) {
|
||||
info(opcode, "switch(v%d)", reg);
|
||||
for (int i = 0; i < labels.length; i++) {
|
||||
info(opcode, "case %d: goto L%s", first_case + i, labelToString(labels[i]));
|
||||
info(opcode, "case %d: goto %s", first_case + i, labelToString(labels[i]));
|
||||
}
|
||||
info(opcode, "default: goto L%s", labelToString(label));
|
||||
info(opcode, "default: goto %s", labelToString(label));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,4 +0,0 @@
|
||||
package com.googlecode.dex2jar.xir;
|
||||
|
||||
public class Ax {
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package com.googlecode.dex2jar.test;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import com.googlecode.dex2jar.Method;
|
||||
import com.googlecode.dex2jar.analysis.CodeNode;
|
||||
import com.googlecode.dex2jar.analysis.type.TypeAnalyzer;
|
||||
import com.googlecode.dex2jar.reader.DexFileReader;
|
||||
import com.googlecode.dex2jar.v3.Main;
|
||||
import com.googlecode.dex2jar.visitors.DexClassVisitor;
|
||||
import com.googlecode.dex2jar.visitors.DexCodeVisitor;
|
||||
import com.googlecode.dex2jar.visitors.DexMethodVisitor;
|
||||
import com.googlecode.dex2jar.visitors.EmptyVisitor;
|
||||
|
||||
public class TypeAnalyzerTest {
|
||||
@Test
|
||||
public void test() throws Exception {
|
||||
try {
|
||||
for (File f : TestUtils.listTestDexFiles()) {
|
||||
System.out.println("tyep-analyze file " + f);
|
||||
new DexFileReader(f).accept(new EmptyVisitor() {
|
||||
|
||||
@Override
|
||||
public DexClassVisitor visit(int access_flags, String className, String superClass,
|
||||
String[] interfaceNames) {
|
||||
return this;
|
||||
}
|
||||
|
||||
Method method;
|
||||
boolean isStatic;
|
||||
|
||||
@Override
|
||||
public DexMethodVisitor visitMethod(int accessFlags, Method method) {
|
||||
this.method = method;
|
||||
this.isStatic = (accessFlags & Opcodes.ACC_STATIC) != 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DexCodeVisitor visitCode() {
|
||||
return new CodeNode() {
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
new TypeAnalyzer(this, isStatic, method).analyze();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}, DexFileReader.SKIP_DEBUG);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Main.niceExceptionMessage(e, 0);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user