Commit 96e3e887 authored by Skylot's avatar Skylot

Inline 'access' synthetic methods

parent 87794d25
......@@ -12,6 +12,7 @@ import jadx.dex.visitors.DotGraphVisitor;
import jadx.dex.visitors.EnumVisitor;
import jadx.dex.visitors.FallbackModeVisitor;
import jadx.dex.visitors.IDexTreeVisitor;
import jadx.dex.visitors.MethodInlinerVisitor;
import jadx.dex.visitors.ModVisitor;
import jadx.dex.visitors.regions.CheckRegions;
import jadx.dex.visitors.regions.CleanRegions;
......@@ -113,6 +114,7 @@ public class Main {
if (args.isCFGOutput())
passes.add(new DotGraphVisitor(args.getOutDir(), true));
passes.add(new MethodInlinerVisitor());
passes.add(new ClassModifier());
passes.add(new CleanRegions());
}
......
......@@ -194,6 +194,9 @@ public class ClassGen {
CodeWriter code = new CodeWriter(clsCode.getIndent() + 1);
for (Iterator<MethodNode> it = mthList.iterator(); it.hasNext();) {
MethodNode mth = it.next();
if (mth.getAttributes().contains(AttributeFlag.DONT_GENERATE))
continue;
try {
if (mth.getAccessFlags().isAbstract() || mth.getAccessFlags().isNative()) {
MethodGen mthGen = new MethodGen(this, mth);
......
package jadx.codegen;
import jadx.dex.attributes.AttributeType;
import jadx.dex.attributes.IAttribute;
import jadx.dex.attributes.MethodInlineAttr;
import jadx.dex.info.ClassInfo;
import jadx.dex.info.FieldInfo;
import jadx.dex.info.MethodInfo;
......@@ -27,7 +29,12 @@ import jadx.dex.nodes.RootNode;
import jadx.utils.StringUtils;
import jadx.utils.exceptions.CodegenException;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -324,6 +331,10 @@ public class InsnGen {
case TERNARY:
break;
case ARGS:
code.add(arg(insn.getArg(0)));
break;
/* fallback mode instructions */
case NOP:
state.add(InsnGenState.SKIP);
......@@ -462,6 +473,14 @@ public class InsnGen {
private void makeInvoke(InvokeNode insn, CodeWriter code) throws CodegenException {
MethodInfo callMth = insn.getCallMth();
// inline method if METHOD_INLINE attribute is attached
MethodNode callMthNode = mth.dex().resolveMethod(callMth);
if (callMthNode != null
&& callMthNode.getAttributes().contains(AttributeType.METHOD_INLINE)) {
inlineMethod(callMthNode, insn, code);
return;
}
int k = 0;
InvokeType type = insn.getInvokeType();
switch (type) {
......@@ -488,6 +507,45 @@ public class InsnGen {
addArgs(code, insn, k);
}
private void inlineMethod(MethodNode callMthNode, InvokeNode insn, CodeWriter code) throws CodegenException {
IAttribute mia = callMthNode.getAttributes().get(AttributeType.METHOD_INLINE);
InsnNode inl = ((MethodInlineAttr) mia).getInsn();
if (callMthNode.getMethodInfo().getArgumentsTypes().isEmpty()) {
makeInsn(inl, code, true);
} else {
// remap args
InsnArg[] regs = new InsnArg[callMthNode.getRegsCount()];
List<RegisterArg> callArgs = callMthNode.getArguments(true);
for (int i = 0; i < callArgs.size(); i++) {
InsnArg arg = insn.getArg(i);
RegisterArg callArg = callArgs.get(i);
regs[callArg.getRegNum()] = arg;
}
// replace args
List<RegisterArg> inlArgs = new ArrayList<RegisterArg>();
inl.getRegisterArgs(inlArgs);
Map<RegisterArg, InsnArg> toRevert = new HashMap<RegisterArg, InsnArg>();
for (RegisterArg r : inlArgs) {
if (r.getRegNum() >= regs.length) {
LOG.warn("Unknown register number {} in method call: {}, {}", r, callMthNode, mth);
} else {
InsnArg repl = regs[r.getRegNum()];
if (repl == null) {
LOG.warn("Not passed register {} in method call: {}, {}", r, callMthNode, mth);
} else {
inl.replaceArg(r, repl);
toRevert.put(r, repl);
}
}
}
makeInsn(inl, code, true);
// revert changes
for (Entry<RegisterArg, InsnArg> e : toRevert.entrySet()) {
inl.replaceArg(e.getValue(), e.getKey());
}
}
}
private void addArgs(CodeWriter code, InsnNode insn, int k) throws CodegenException {
code.add('(');
for (int i = k; i < insn.getArgsCount(); i++) {
......@@ -495,7 +553,7 @@ public class InsnGen {
if (i < insn.getArgsCount() - 1)
code.add(", ");
}
code.add(")");
code.add(')');
}
private void makeArith(ArithNode insn, CodeWriter code, EnumSet<InsnGenState> state) throws CodegenException {
......
......@@ -21,6 +21,7 @@ public enum AttributeType {
// methods
JADX_ERROR(true),
METHOD_INLINE(true),
// classes
ENUM_CLASS(true),
......
package jadx.dex.attributes;
import jadx.dex.nodes.InsnNode;
public class MethodInlineAttr implements IAttribute {
private final InsnNode insn;
public MethodInlineAttr(InsnNode insn) {
this.insn = insn;
}
public InsnNode getInsn() {
return insn;
}
@Override
public AttributeType getType() {
return AttributeType.METHOD_INLINE;
}
@Override
public String toString() {
return "INLINE: " + insn;
}
}
......@@ -77,6 +77,28 @@ public final class MethodInfo {
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + declClass.hashCode();
result = prime * result + retType.hashCode();
result = prime * result + shortId.hashCode();
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
MethodInfo other = (MethodInfo) obj;
if (!shortId.equals(other.shortId)) return false;
if (!retType.equals(other.retType)) return false;
if (!declClass.equals(other.declClass)) return false;
return true;
}
@Override
public String toString() {
return retType + " " + declClass.getFullName() + "." + name
+ "(" + Utils.listToString(args) + ")";
......
......@@ -50,6 +50,9 @@ public enum InsnType {
CONSTRUCTOR,
BREAK,
CONTINUE,
TERNARY,
ARGS, // just generate arguments
NEW_MULTIDIM_ARRAY // TODO: now multidimensional arrays created using Array.newInstance function
}
......@@ -239,7 +239,15 @@ public class ClassNode extends AttrNode implements ILoadable {
return null;
}
public MethodNode searchMethodById(String shortId) {
public MethodNode searchMethod(MethodInfo mth) {
for (MethodNode m : methods) {
if (m.getMethodInfo().equals(mth))
return m;
}
return null;
}
public MethodNode searchMethodByName(String shortId) {
for (MethodNode m : methods) {
if (m.getMethodInfo().getShortId().equals(shortId))
return m;
......@@ -248,7 +256,7 @@ public class ClassNode extends AttrNode implements ILoadable {
}
public MethodNode searchMethodById(int id) {
return searchMethodById(MethodInfo.fromDex(dex, id).getShortId());
return searchMethodByName(MethodInfo.fromDex(dex, id).getShortId());
}
public List<ClassNode> getInnerClasses() {
......
package jadx.dex.nodes;
import jadx.dex.info.ClassInfo;
import jadx.dex.info.MethodInfo;
import jadx.dex.instructions.args.ArgType;
import jadx.utils.exceptions.DecodeException;
import jadx.utils.files.InputFile;
......@@ -51,6 +52,14 @@ public class DexNode {
return root.resolveClass(clsInfo);
}
public MethodNode resolveMethod(MethodInfo mth) {
ClassNode cls = resolveClass(mth.getDeclClass());
if (cls != null) {
return cls.searchMethod(mth);
}
return null;
}
// DexBuffer wrappers
public String getString(int index) {
......
package jadx.dex.visitors;
import jadx.dex.attributes.AttributeFlag;
import jadx.dex.attributes.IAttribute;
import jadx.dex.attributes.MethodInlineAttr;
import jadx.dex.instructions.InsnType;
import jadx.dex.nodes.BlockNode;
import jadx.dex.nodes.InsnNode;
import jadx.dex.nodes.MethodNode;
import jadx.utils.exceptions.JadxException;
public class MethodInlinerVisitor extends AbstractVisitor {
@Override
public void visit(MethodNode mth) throws JadxException {
if (mth.getAccessFlags().isSynthetic() && mth.getAccessFlags().isStatic()) {
if (mth.getBasicBlocks().size() == 1) {
BlockNode block = mth.getBasicBlocks().get(0);
// synthetic field getter
if (block.getInstructions().size() == 1) {
InsnNode insn = block.getInstructions().get(0);
if (insn.getType() == InsnType.RETURN) {
InsnNode inl = new InsnNode(InsnType.ARGS, 1);
inl.addArg(insn.getArg(0));
addInlineAttr(mth, inl);
return;
}
}
// synthetic field setter
if (block.getInstructions().size() == 2) {
if (block.getInstructions().get(1).getType() == InsnType.RETURN) {
InsnNode insn = block.getInstructions().get(0);
addInlineAttr(mth, insn);
return;
}
}
// synthetic method invoke
if (block.getInstructions().size() == 1) {
InsnNode insn = block.getInstructions().get(0);
addInlineAttr(mth, insn);
return;
}
}
}
}
private static void addInlineAttr(MethodNode mth, InsnNode insn) {
IAttribute attr = new MethodInlineAttr(insn);
mth.getAttributes().add(attr);
mth.getAttributes().add(AttributeFlag.DONT_GENERATE);
}
}
package jadx.samples;
public class TestInner2 extends AbstractTest {
private String a;
public class A {
public A() {
a = "a";
}
public String a() {
return a;
}
}
private static String b;
public static class B {
public B() {
b = "b";
}
public String b() {
return b;
}
}
@Override
public boolean testRun() throws Exception {
assertTrue((new A()).a().equals("a"));
assertTrue(a.equals("a"));
assertTrue((new B()).b().equals("b"));
assertTrue(b.equals("b"));
return true;
}
public static void main(String[] args) throws Exception {
new TestInner2().testRun();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment