fix: inline nested anonymous classes (#1379)
This commit is contained in:
@@ -11,6 +11,8 @@ import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.nodes.AnonymousClassAttr;
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
@@ -241,7 +243,7 @@ public final class JavaClass implements JavaNode {
|
||||
|
||||
@Override
|
||||
public JavaClass getTopParentClass() {
|
||||
if (cls.contains(AFlag.ANONYMOUS_CLASS)) {
|
||||
if (cls.contains(AType.ANONYMOUS_CLASS)) {
|
||||
// moved to usage class
|
||||
return getParentForAnonymousClass();
|
||||
}
|
||||
@@ -249,15 +251,9 @@ public final class JavaClass implements JavaNode {
|
||||
}
|
||||
|
||||
private JavaClass getParentForAnonymousClass() {
|
||||
List<JavaNode> useIn = getUseIn();
|
||||
if (useIn.isEmpty()) {
|
||||
return this;
|
||||
}
|
||||
JavaNode useNode = useIn.get(0);
|
||||
if (useNode.equals(this)) {
|
||||
return this;
|
||||
}
|
||||
return useNode.getTopParentClass();
|
||||
AnonymousClassAttr attr = cls.get(AType.ANONYMOUS_CLASS);
|
||||
ClassNode topParentClass = attr.getOuterCls().getTopParentClass();
|
||||
return getRootDecompiler().convertClassNode(topParentClass);
|
||||
}
|
||||
|
||||
public AccessInfo getAccessInfo() {
|
||||
|
||||
@@ -34,11 +34,11 @@ public final class ProcessClass {
|
||||
if (cls.contains(AFlag.CLASS_DEEP_RELOAD)) {
|
||||
cls.remove(AFlag.CLASS_DEEP_RELOAD);
|
||||
cls.deepUnload();
|
||||
cls.root().runPreDecompileStageForClass(cls);
|
||||
cls.add(AFlag.CLASS_UNLOADED);
|
||||
}
|
||||
if (cls.contains(AFlag.CLASS_UNLOADED)) {
|
||||
cls.remove(AFlag.CLASS_UNLOADED);
|
||||
cls.root().runPreDecompileStageForClass(cls);
|
||||
cls.remove(AFlag.CLASS_UNLOADED);
|
||||
}
|
||||
if (cls.getState() == GENERATED_AND_UNLOADED) {
|
||||
// force loading code again
|
||||
|
||||
@@ -285,7 +285,7 @@ public class ClassGen {
|
||||
|
||||
private boolean isInnerClassesPresents() {
|
||||
for (ClassNode innerCls : cls.getInnerClasses()) {
|
||||
if (!innerCls.contains(AFlag.ANONYMOUS_CLASS)) {
|
||||
if (!innerCls.contains(AType.ANONYMOUS_CLASS)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -719,13 +719,13 @@ public class InsnGen {
|
||||
|
||||
private void inlineAnonymousConstructor(ICodeWriter code, ClassNode cls, ConstructorInsn insn) throws CodegenException {
|
||||
if (this.mth.getParentClass() == cls) {
|
||||
cls.remove(AFlag.ANONYMOUS_CLASS);
|
||||
cls.remove(AType.ANONYMOUS_CLASS);
|
||||
cls.remove(AFlag.DONT_GENERATE);
|
||||
mth.getParentClass().getTopParentClass().add(AFlag.RESTART_CODEGEN);
|
||||
throw new CodegenException("Anonymous inner class unlimited recursion detected."
|
||||
+ " Convert class to inner: " + cls.getClassInfo().getFullName());
|
||||
}
|
||||
ArgType parent = cls.get(AType.ANONYMOUS_CLASS_BASE).getBaseType();
|
||||
ArgType parent = cls.get(AType.ANONYMOUS_CLASS).getBaseType();
|
||||
// hide empty anonymous constructors
|
||||
for (MethodNode ctor : cls.getMethods()) {
|
||||
if (ctor.contains(AFlag.ANONYMOUS_CONSTRUCTOR)
|
||||
|
||||
@@ -35,7 +35,6 @@ public enum AFlag {
|
||||
SKIP_ARG, // skip argument in invoke call
|
||||
NO_SKIP_ARGS,
|
||||
ANONYMOUS_CONSTRUCTOR,
|
||||
ANONYMOUS_CLASS,
|
||||
|
||||
THIS,
|
||||
SUPER,
|
||||
|
||||
@@ -2,7 +2,7 @@ package jadx.core.dex.attributes;
|
||||
|
||||
import jadx.api.plugins.input.data.attributes.IJadxAttrType;
|
||||
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
|
||||
import jadx.core.dex.attributes.nodes.AnonymousClassBaseAttr;
|
||||
import jadx.core.dex.attributes.nodes.AnonymousClassAttr;
|
||||
import jadx.core.dex.attributes.nodes.ClassTypeVarsAttr;
|
||||
import jadx.core.dex.attributes.nodes.DeclareVariablesAttr;
|
||||
import jadx.core.dex.attributes.nodes.EdgeInsnAttr;
|
||||
@@ -54,7 +54,7 @@ public final class AType<T extends IJadxAttribute> implements IJadxAttrType<T> {
|
||||
public static final AType<EnumClassAttr> ENUM_CLASS = new AType<>();
|
||||
public static final AType<EnumMapAttr> ENUM_MAP = new AType<>();
|
||||
public static final AType<ClassTypeVarsAttr> CLASS_TYPE_VARS = new AType<>();
|
||||
public static final AType<AnonymousClassBaseAttr> ANONYMOUS_CLASS_BASE = new AType<>();
|
||||
public static final AType<AnonymousClassAttr> ANONYMOUS_CLASS = new AType<>();
|
||||
|
||||
// field
|
||||
public static final AType<FieldInitInsnAttr> FIELD_INIT_INSN = new AType<>();
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package jadx.core.dex.attributes.nodes;
|
||||
|
||||
import jadx.api.plugins.input.data.attributes.PinnedAttribute;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
|
||||
public class AnonymousClassAttr extends PinnedAttribute {
|
||||
|
||||
private final ClassNode outerCls;
|
||||
private final ArgType baseType;
|
||||
|
||||
public AnonymousClassAttr(ClassNode outerCls, ArgType baseType) {
|
||||
this.outerCls = outerCls;
|
||||
this.baseType = baseType;
|
||||
}
|
||||
|
||||
public ClassNode getOuterCls() {
|
||||
return outerCls;
|
||||
}
|
||||
|
||||
public ArgType getBaseType() {
|
||||
return baseType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AType<AnonymousClassAttr> getAttrType() {
|
||||
return AType.ANONYMOUS_CLASS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AnonymousClass{" + outerCls + ", base: " + baseType + '}';
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package jadx.core.dex.attributes.nodes;
|
||||
|
||||
import jadx.api.plugins.input.data.attributes.PinnedAttribute;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
|
||||
public class AnonymousClassBaseAttr extends PinnedAttribute {
|
||||
|
||||
private final ArgType baseType;
|
||||
|
||||
public AnonymousClassBaseAttr(ArgType baseType) {
|
||||
this.baseType = baseType;
|
||||
}
|
||||
|
||||
public ArgType getBaseType() {
|
||||
return baseType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AType<AnonymousClassBaseAttr> getAttrType() {
|
||||
return AType.ANONYMOUS_CLASS_BASE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AnonymousClassBaseAttr{" + baseType + '}';
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ import jadx.api.plugins.input.data.impl.ListConsumer;
|
||||
import jadx.core.Consts;
|
||||
import jadx.core.ProcessClass;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.nodes.NotificationAttrNode;
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
import jadx.core.dex.info.AccessInfo.AFType;
|
||||
@@ -42,6 +43,7 @@ import jadx.core.dex.info.MethodInfo;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.LiteralArg;
|
||||
import jadx.core.dex.nodes.utils.TypeUtils;
|
||||
import jadx.core.utils.ListUtils;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
@@ -619,7 +621,7 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN
|
||||
}
|
||||
|
||||
public boolean isAnonymous() {
|
||||
return contains(AFlag.ANONYMOUS_CLASS);
|
||||
return contains(AType.ANONYMOUS_CLASS);
|
||||
}
|
||||
|
||||
public boolean isInner() {
|
||||
@@ -750,6 +752,10 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN
|
||||
this.dependencies = dependencies;
|
||||
}
|
||||
|
||||
public void removeDependency(ClassNode dep) {
|
||||
this.dependencies = ListUtils.safeRemoveAndTrim(this.dependencies, dep);
|
||||
}
|
||||
|
||||
public List<ClassNode> getCodegenDeps() {
|
||||
return codegenDeps;
|
||||
}
|
||||
@@ -758,6 +764,10 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN
|
||||
this.codegenDeps = codegenDeps;
|
||||
}
|
||||
|
||||
public void addCodegenDep(ClassNode dep) {
|
||||
this.codegenDeps = ListUtils.safeAdd(this.codegenDeps, dep);
|
||||
}
|
||||
|
||||
public int getTotalDepsCount() {
|
||||
return dependencies.size() + codegenDeps.size();
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.nodes.FieldReplaceAttr;
|
||||
import jadx.core.dex.attributes.nodes.SkipMethodArgsAttr;
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
@@ -36,7 +37,7 @@ public class AnonymousClassVisitor extends AbstractVisitor {
|
||||
|
||||
@Override
|
||||
public boolean visit(ClassNode cls) throws JadxException {
|
||||
if (cls.contains(AFlag.ANONYMOUS_CLASS)) {
|
||||
if (cls.contains(AType.ANONYMOUS_CLASS)) {
|
||||
for (MethodNode mth : cls.getMethods()) {
|
||||
if (mth.contains(AFlag.ANONYMOUS_CONSTRUCTOR)) {
|
||||
processAnonymousConstructor(mth);
|
||||
|
||||
@@ -15,6 +15,7 @@ import jadx.api.plugins.input.data.AccessFlags;
|
||||
import jadx.core.codegen.TypeGen;
|
||||
import jadx.core.deobf.NameMapper;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.nodes.EnumClassAttr;
|
||||
import jadx.core.dex.attributes.nodes.EnumClassAttr.EnumField;
|
||||
import jadx.core.dex.attributes.nodes.SkipMethodArgsAttr;
|
||||
@@ -389,7 +390,7 @@ public class EnumVisitor extends AbstractVisitor {
|
||||
}
|
||||
if (constrCls.equals(cls)) {
|
||||
// allow same class
|
||||
} else if (constrCls.contains(AFlag.ANONYMOUS_CLASS)) {
|
||||
} else if (constrCls.contains(AType.ANONYMOUS_CLASS)) {
|
||||
// allow external class already marked as anonymous
|
||||
} else {
|
||||
return null;
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package jadx.core.dex.visitors;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.nodes.AnonymousClassBaseAttr;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.nodes.AnonymousClassAttr;
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
@@ -25,20 +29,32 @@ import jadx.core.utils.exceptions.JadxException;
|
||||
)
|
||||
public class ProcessAnonymous extends AbstractVisitor {
|
||||
|
||||
private boolean inlineAnonymous;
|
||||
private boolean inlineAnonymousClasses;
|
||||
|
||||
@Override
|
||||
public void init(RootNode root) {
|
||||
inlineAnonymous = root.getArgs().isInlineAnonymousClasses();
|
||||
inlineAnonymousClasses = root.getArgs().isInlineAnonymousClasses();
|
||||
if (!inlineAnonymousClasses) {
|
||||
return;
|
||||
}
|
||||
for (ClassNode cls : root.getClasses()) {
|
||||
markAnonymousClass(cls);
|
||||
}
|
||||
mergeAnonymousDeps(root);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(ClassNode cls) throws JadxException {
|
||||
if (!inlineAnonymous) {
|
||||
return false;
|
||||
if (inlineAnonymousClasses && cls.contains(AFlag.CLASS_UNLOADED)) {
|
||||
// enter only on class reload
|
||||
visitClassAndInners(cls);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void visitClassAndInners(ClassNode cls) {
|
||||
markAnonymousClass(cls);
|
||||
return true;
|
||||
cls.getInnerClasses().forEach(this::visitClassAndInners);
|
||||
}
|
||||
|
||||
private static void markAnonymousClass(ClassNode cls) {
|
||||
@@ -53,27 +69,66 @@ public class ProcessAnonymous extends AbstractVisitor {
|
||||
if (baseType == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
cls.add(AFlag.ANONYMOUS_CLASS);
|
||||
cls.addAttr(new AnonymousClassBaseAttr(baseType));
|
||||
ClassNode outerCls = anonymousConstructor.getUseIn().get(0).getParentClass();
|
||||
cls.addAttr(new AnonymousClassAttr(outerCls, baseType));
|
||||
cls.add(AFlag.DONT_GENERATE);
|
||||
|
||||
anonymousConstructor.add(AFlag.ANONYMOUS_CONSTRUCTOR);
|
||||
|
||||
// force anonymous class to be processed before outer class,
|
||||
// actual usage of outer class will be removed at anonymous class process,
|
||||
// see ModVisitor.processAnonymousConstructor method
|
||||
ClassNode outerCls = anonymousConstructor.getUseIn().get(0).getParentClass();
|
||||
ClassNode topOuterCls = outerCls.getTopParentClass();
|
||||
ListUtils.safeRemove(cls.getDependencies(), topOuterCls);
|
||||
cls.removeDependency(topOuterCls);
|
||||
ListUtils.safeRemove(outerCls.getUseIn(), cls);
|
||||
|
||||
// move dependency to codegen stage
|
||||
if (cls.isTopClass()) {
|
||||
topOuterCls.setDependencies(ListUtils.safeRemoveAndTrim(topOuterCls.getDependencies(), cls));
|
||||
topOuterCls.setCodegenDeps(ListUtils.safeAdd(topOuterCls.getCodegenDeps(), cls));
|
||||
topOuterCls.removeDependency(cls);
|
||||
topOuterCls.addCodegenDep(cls);
|
||||
}
|
||||
}
|
||||
|
||||
private void mergeAnonymousDeps(RootNode root) {
|
||||
// Collect edges to build bidirectional tree:
|
||||
// inline edge: anonymous -> outer (one-to-one)
|
||||
// use edges: outer -> *anonymous (one-to-many)
|
||||
Map<ClassNode, ClassNode> inlineMap = new HashMap<>();
|
||||
Map<ClassNode, List<ClassNode>> useMap = new HashMap<>();
|
||||
for (ClassNode anonymousCls : root.getClasses()) {
|
||||
AnonymousClassAttr attr = anonymousCls.get(AType.ANONYMOUS_CLASS);
|
||||
if (attr != null) {
|
||||
ClassNode outerCls = attr.getOuterCls();
|
||||
useMap.computeIfAbsent(outerCls, k -> new ArrayList<>()).add(anonymousCls);
|
||||
useMap.putIfAbsent(anonymousCls, new ArrayList<>()); // put leaf explicitly
|
||||
inlineMap.put(anonymousCls, outerCls);
|
||||
}
|
||||
}
|
||||
if (inlineMap.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// starting from leaf process deps in nodes up to root
|
||||
useMap.forEach((key, list) -> {
|
||||
if (list.isEmpty()) {
|
||||
updateDeps(key, inlineMap);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateDeps(ClassNode leafCls, Map<ClassNode, ClassNode> inlineMap) {
|
||||
List<ClassNode> list = new ArrayList<>();
|
||||
ClassNode current = leafCls;
|
||||
while (current != null) {
|
||||
list.add(current.getTopParentClass());
|
||||
current = inlineMap.get(current);
|
||||
}
|
||||
if (list.size() <= 2) {
|
||||
// first level deps already processed
|
||||
return;
|
||||
}
|
||||
ClassNode topNode = list.remove(list.size() - 1);
|
||||
topNode.setCodegenDeps(ListUtils.distinctMergeSortedLists(topNode.getCodegenDeps(), list));
|
||||
}
|
||||
|
||||
private static boolean canBeAnonymous(ClassNode cls) {
|
||||
if (cls.getAccessFlags().isSynthetic()) {
|
||||
return true;
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@ import jadx.core.Consts;
|
||||
import jadx.core.clsp.ClspGraph;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.nodes.AnonymousClassBaseAttr;
|
||||
import jadx.core.dex.attributes.nodes.AnonymousClassAttr;
|
||||
import jadx.core.dex.attributes.nodes.PhiListAttr;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
import jadx.core.dex.instructions.ArithNode;
|
||||
@@ -321,7 +321,7 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
|
||||
if (ctr.isNewInstance()) {
|
||||
ClassNode ctrCls = root.resolveClass(ctr.getClassType());
|
||||
if (ctrCls != null && ctrCls.contains(AFlag.DONT_GENERATE)) {
|
||||
AnonymousClassBaseAttr baseTypeAttr = ctrCls.get(AType.ANONYMOUS_CLASS_BASE);
|
||||
AnonymousClassAttr baseTypeAttr = ctrCls.get(AType.ANONYMOUS_CLASS);
|
||||
if (baseTypeAttr != null) {
|
||||
return baseTypeAttr.getBaseType();
|
||||
}
|
||||
|
||||
@@ -6,13 +6,13 @@ import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
|
||||
public class ListUtils {
|
||||
|
||||
public static <T> boolean isSingleElement(@Nullable List<T> list, T obj) {
|
||||
@@ -48,7 +48,19 @@ public class ListUtils {
|
||||
return list.get(list.size() - 1);
|
||||
}
|
||||
|
||||
public static List<BlockNode> distinctList(List<BlockNode> list) {
|
||||
public static <T extends Comparable<T>> List<T> distinctMergeSortedLists(List<T> first, List<T> second) {
|
||||
if (first.isEmpty()) {
|
||||
return second;
|
||||
}
|
||||
if (second.isEmpty()) {
|
||||
return first;
|
||||
}
|
||||
Set<T> set = new TreeSet<>(first);
|
||||
set.addAll(second);
|
||||
return new ArrayList<>(set);
|
||||
}
|
||||
|
||||
public static <T> List<T> distinctList(List<T> list) {
|
||||
return new ArrayList<>(new LinkedHashSet<>(list));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package jadx.tests.integration.inner;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.tests.api.SmaliTest;
|
||||
|
||||
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
|
||||
|
||||
public class TestNestedAnonymousClass extends SmaliTest {
|
||||
|
||||
@SuppressWarnings("Convert2Lambda")
|
||||
public static class TestCls {
|
||||
public void test() {
|
||||
use(new Callable<Runnable>() {
|
||||
@Override
|
||||
public Runnable call() {
|
||||
return new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
System.out.println("run");
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void testLambda() {
|
||||
use(() -> () -> System.out.println("lambda"));
|
||||
}
|
||||
|
||||
public void use(Callable<Runnable> r) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
assertThat(getClassNode(TestCls.class))
|
||||
.code()
|
||||
.containsOne("use(new Callable<Runnable>() {")
|
||||
.containsOne("return new Runnable() {");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSmali() {
|
||||
getArgs().setRenameFlags(Collections.emptySet());
|
||||
List<ClassNode> classes = loadFromSmaliFiles();
|
||||
assertThat(searchCls(classes, "A"))
|
||||
.code()
|
||||
.containsOne("use(new Callable<Runnable>() {")
|
||||
.containsOne("return new Runnable() {");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
.class public Linner/A;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
.method public constructor <init>()V
|
||||
.registers 1
|
||||
.prologue
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public test()V
|
||||
.registers 2
|
||||
|
||||
.prologue
|
||||
new-instance v0, Linner/B;
|
||||
invoke-direct {v0, p0}, Linner/B;-><init>(Linner/A;)V
|
||||
invoke-virtual {p0, v0}, Linner/A;->use(Ljava/util/concurrent/Callable;)V
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public use(Ljava/util/concurrent/Callable;)V
|
||||
.registers 2
|
||||
.annotation system Ldalvik/annotation/Signature;
|
||||
value = {
|
||||
"(",
|
||||
"Ljava/util/concurrent/Callable",
|
||||
"<",
|
||||
"Ljava/lang/Runnable;",
|
||||
">;)V"
|
||||
}
|
||||
.end annotation
|
||||
.prologue
|
||||
return-void
|
||||
.end method
|
||||
@@ -0,0 +1,46 @@
|
||||
.class synthetic Linner/B;
|
||||
.super Ljava/lang/Object;
|
||||
.implements Ljava/util/concurrent/Callable;
|
||||
|
||||
.annotation system Ldalvik/annotation/Signature;
|
||||
value = {
|
||||
"Ljava/lang/Object;",
|
||||
"Ljava/util/concurrent/Callable",
|
||||
"<",
|
||||
"Ljava/lang/Runnable;",
|
||||
">;"
|
||||
}
|
||||
.end annotation
|
||||
|
||||
.field final synthetic this$0:Linner/A;
|
||||
|
||||
.method constructor <init>(Linner/A;)V
|
||||
.registers 2
|
||||
.prologue
|
||||
iput-object p1, p0, Linner/B;->this$0:Linner/A;
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
.method public bridge synthetic call()Ljava/lang/Object;
|
||||
.registers 2
|
||||
.annotation system Ldalvik/annotation/Throws;
|
||||
value = {
|
||||
Ljava/lang/Exception;
|
||||
}
|
||||
.end annotation
|
||||
|
||||
.prologue
|
||||
invoke-virtual {p0}, Linner/B;->call()Ljava/lang/Runnable;
|
||||
move-result-object v0
|
||||
return-object v0
|
||||
.end method
|
||||
|
||||
.method public call()Ljava/lang/Runnable;
|
||||
.registers 2
|
||||
.prologue
|
||||
new-instance v0, Linner/C;
|
||||
invoke-direct {v0, p0}, Linner/C;-><init>(Linner/B;)V
|
||||
return-object v0
|
||||
.end method
|
||||
@@ -0,0 +1,23 @@
|
||||
.class synthetic Linner/C;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
.implements Ljava/lang/Runnable;
|
||||
|
||||
.field final synthetic this$1:Linner/B;
|
||||
|
||||
.method constructor <init>(Linner/B;)V
|
||||
.registers 2
|
||||
.prologue
|
||||
iput-object p1, p0, Linner/C;->this$1:Linner/B;
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public run()V
|
||||
.registers 3
|
||||
.prologue
|
||||
sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
|
||||
const-string v1, "run"
|
||||
invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
|
||||
return-void
|
||||
.end method
|
||||
Reference in New Issue
Block a user