Commit db1b027d authored by Skylot's avatar Skylot

fix: improve bridge methods renaming (#397)

parent 7f4e6418
...@@ -4,15 +4,15 @@ import java.util.Iterator; ...@@ -4,15 +4,15 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import com.android.dx.rop.code.AccessFlags; import com.android.dx.rop.code.AccessFlags;
import jadx.core.dex.info.ClassInfo;
import jadx.core.utils.Utils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import jadx.core.Consts;
import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.annotations.MethodParameters; import jadx.core.dex.attributes.annotations.MethodParameters;
import jadx.core.dex.info.AccessInfo; import jadx.core.dex.info.AccessInfo;
import jadx.core.dex.info.ClassInfo;
import jadx.core.dex.instructions.InsnType; import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.instructions.args.RegisterArg;
...@@ -24,6 +24,7 @@ import jadx.core.dex.visitors.DepthTraversal; ...@@ -24,6 +24,7 @@ import jadx.core.dex.visitors.DepthTraversal;
import jadx.core.dex.visitors.FallbackModeVisitor; import jadx.core.dex.visitors.FallbackModeVisitor;
import jadx.core.utils.ErrorsCounter; import jadx.core.utils.ErrorsCounter;
import jadx.core.utils.InsnUtils; import jadx.core.utils.InsnUtils;
import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.CodegenException; import jadx.core.utils.exceptions.CodegenException;
import jadx.core.utils.exceptions.DecodeException; import jadx.core.utils.exceptions.DecodeException;
...@@ -85,6 +86,9 @@ public class MethodGen { ...@@ -85,6 +86,9 @@ public class MethodGen {
} }
code.startLineWithNum(mth.getSourceLine()); code.startLineWithNum(mth.getSourceLine());
code.add(ai.makeString()); code.add(ai.makeString());
if (Consts.DEBUG) {
code.add(mth.isVirtual() ? "/* virtual */ " : "/* direct */ ");
}
if (classGen.addGenericMap(code, mth.getGenericMap())) { if (classGen.addGenericMap(code, mth.getGenericMap())) {
code.add(' '); code.add(' ');
......
...@@ -3,6 +3,8 @@ package jadx.core.dex.visitors; ...@@ -3,6 +3,8 @@ package jadx.core.dex.visitors;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import com.android.dx.rop.code.AccessFlags;
import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.nodes.FieldReplaceAttr; import jadx.core.dex.attributes.nodes.FieldReplaceAttr;
...@@ -31,7 +33,10 @@ import jadx.core.utils.exceptions.JadxException; ...@@ -31,7 +33,10 @@ import jadx.core.utils.exceptions.JadxException;
@JadxVisitor( @JadxVisitor(
name = "ClassModifier", name = "ClassModifier",
desc = "Remove synthetic classes, methods and fields", desc = "Remove synthetic classes, methods and fields",
runAfter = ModVisitor.class runAfter = {
ModVisitor.class,
FixAccessModifiers.class
}
) )
public class ClassModifier extends AbstractVisitor { public class ClassModifier extends AbstractVisitor {
...@@ -218,6 +223,10 @@ public class ClassModifier extends AbstractVisitor { ...@@ -218,6 +223,10 @@ public class ClassModifier extends AbstractVisitor {
MethodInfo callMth = ((InvokeNode) insn).getCallMth(); MethodInfo callMth = ((InvokeNode) insn).getCallMth();
MethodNode wrappedMth = mth.root().deepResolveMethod(callMth); MethodNode wrappedMth = mth.root().deepResolveMethod(callMth);
if (wrappedMth != null) { if (wrappedMth != null) {
AccessInfo wrappedAccFlags = wrappedMth.getAccessFlags();
if (wrappedAccFlags.isStatic()) {
return false;
}
if (callMth.getArgsCount() != mth.getMethodInfo().getArgsCount()) { if (callMth.getArgsCount() != mth.getMethodInfo().getArgsCount()) {
return false; return false;
} }
...@@ -235,8 +244,9 @@ public class ClassModifier extends AbstractVisitor { ...@@ -235,8 +244,9 @@ public class ClassModifier extends AbstractVisitor {
if (Objects.equals(wrappedMth.getAlias(), alias)) { if (Objects.equals(wrappedMth.getAlias(), alias)) {
return true; return true;
} }
if (!wrappedMth.isVirtual()) { if (!wrappedAccFlags.isPublic()) {
return false; // must be public
FixAccessModifiers.changeVisibility(wrappedMth, AccessFlags.ACC_PUBLIC);
} }
wrappedMth.getMethodInfo().setAlias(alias); wrappedMth.getMethodInfo().setAlias(alias);
return true; return true;
......
...@@ -26,22 +26,27 @@ public class FixAccessModifiers extends AbstractVisitor { ...@@ -26,22 +26,27 @@ public class FixAccessModifiers extends AbstractVisitor {
if (respectAccessModifiers) { if (respectAccessModifiers) {
return; return;
} }
AccessInfo accessFlags = mth.getAccessFlags(); int newVisFlag = fixVisibility(mth);
int newVisFlag = fixVisibility(mth, accessFlags);
if (newVisFlag != 0) { if (newVisFlag != 0) {
changeVisibility(mth, newVisFlag);
}
}
public static void changeVisibility(MethodNode mth, int newVisFlag) {
AccessInfo accessFlags = mth.getAccessFlags();
AccessInfo newAccFlags = accessFlags.changeVisibility(newVisFlag); AccessInfo newAccFlags = accessFlags.changeVisibility(newVisFlag);
if (newAccFlags != accessFlags) { if (newAccFlags != accessFlags) {
mth.setAccFlags(newAccFlags); mth.setAccFlags(newAccFlags);
mth.addAttr(AType.COMMENTS, "Access modifiers changed, original: " + accessFlags.rawString()); mth.addAttr(AType.COMMENTS, "access modifiers changed from: " + accessFlags.rawString());
}
} }
} }
private int fixVisibility(MethodNode mth, AccessInfo accessFlags) { private static int fixVisibility(MethodNode mth) {
if (mth.isVirtual()) { if (mth.isVirtual()) {
// make virtual methods public // make virtual methods public
return AccessFlags.ACC_PUBLIC; return AccessFlags.ACC_PUBLIC;
} else { } else {
AccessInfo accessFlags = mth.getAccessFlags();
if (accessFlags.isAbstract()) { if (accessFlags.isAbstract()) {
// make abstract methods public // make abstract methods public
return AccessFlags.ACC_PUBLIC; return AccessFlags.ACC_PUBLIC;
......
package jadx.tests.integration.inner;
import org.junit.Test;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.IntegrationTest;
import static jadx.tests.api.utils.JadxMatchers.containsOne;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat;
public class TestInner2Samples extends IntegrationTest {
public static class TestInner2 {
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;
}
}
private String c;
private void setC(String c) {
this.c = c;
}
public class C {
public String c() {
setC("c");
return c;
}
}
private static String d;
private static void setD(String s) {
d = s;
}
public static class D {
public String d() {
setD("d");
return d;
}
}
}
@Test
public void test() {
ClassNode cls = getClassNode(TestInner2.class);
String code = cls.getCode().toString();
assertThat(code, containsOne("setD(\"d\");"));
assertThat(code, not(containsString("synthetic")));
assertThat(code, not(containsString("access$")));
}
}
package jadx.tests.integration.inner;
import org.junit.Test;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.SmaliTest;
import static jadx.tests.api.utils.JadxMatchers.containsOne;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat;
/**
* Issue: https://github.com/skylot/jadx/issues/397
*/
public class TestSyntheticMthRename extends SmaliTest {
// public class TestCls {
// public interface I<R, P> {
// R call(P... p);
// }
//
// public static final class A implements I<String, Runnable> {
// public /* synthetic */ /* virtual */ Object call(Object[] objArr) {
// return renamedCall((Runnable[]) objArr);
// }
//
// private /* varargs */ /* direct */ String renamedCall(Runnable... p) {
// return "str";
// }
// }
// }
@Test
public void test() {
ClassNode cls = getClassNodeFromSmaliFiles("inner", "TestSyntheticMthRename", "TestCls",
"TestCls", "TestCls$I", "TestCls$A"
);
String code = cls.getCode().toString();
assertThat(code, containsOne("public String call(Runnable... p) {"));
assertThat(code, not(containsString("synthetic")));
}
}
.class public final Linner/TestCls$A;
.super Ljava/lang/Object;
.source "TestCls.java"
# interfaces
.implements Linner/TestCls$I;
# annotations
.annotation system Ldalvik/annotation/EnclosingClass;
value = Linner/TestCls;
.end annotation
.annotation system Ldalvik/annotation/InnerClass;
accessFlags = 0x19
name = "A"
.end annotation
.annotation system Ldalvik/annotation/Signature;
value = {
"Ljava/lang/Object;",
"Linner/TestCls$I",
"<",
"Ljava/lang/String;",
"Ljava/lang/Runnable;",
">;"
}
.end annotation
# direct methods
.method public constructor <init>()V
.registers 1
.prologue
.line 9
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
return-void
.end method
.method private varargs renamedCall([Ljava/lang/Runnable;)Ljava/lang/String;
.registers 3
.param p1, "p" # [Ljava/lang/Runnable;
.prologue
.line 12
const-string v0, "str"
return-object v0
.end method
# virtual methods
.method public synthetic call([Ljava/lang/Object;)Ljava/lang/Object;
.registers 3
.prologue
.line 9
check-cast p1, [Ljava/lang/Runnable;
invoke-virtual {p0, p1}, Linner/TestCls$A;->renamedCall([Ljava/lang/Runnable;)Ljava/lang/String;
move-result-object v0
return-object v0
.end method
.class public interface abstract Linner/TestCls$I;
.super Ljava/lang/Object;
.source "TestCls.java"
# annotations
.annotation system Ldalvik/annotation/EnclosingClass;
value = Linner/TestCls;
.end annotation
.annotation system Ldalvik/annotation/InnerClass;
accessFlags = 0x609
name = "I"
.end annotation
.annotation system Ldalvik/annotation/Signature;
value = {
"<R:",
"Ljava/lang/Object;",
"P:",
"Ljava/lang/Object;",
">",
"Ljava/lang/Object;"
}
.end annotation
# virtual methods
.method public varargs abstract call([Ljava/lang/Object;)Ljava/lang/Object;
.annotation system Ldalvik/annotation/Signature;
value = {
"([TP;)TR;"
}
.end annotation
.end method
.class public Linner/TestCls;
.super Ljava/lang/Object;
.source "TestCls.java"
# annotations
.annotation system Ldalvik/annotation/MemberClasses;
value = {
Linner/TestCls$A;,
Linner/TestCls$I;
}
.end annotation
# direct methods
.method public constructor <init>()V
.registers 1
.prologue
.line 3
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
return-void
.end method
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