diff --git a/jadx-core/src/main/java/jadx/core/ProcessClass.java b/jadx-core/src/main/java/jadx/core/ProcessClass.java index 35e4e7d05..ff602ad28 100644 --- a/jadx-core/src/main/java/jadx/core/ProcessClass.java +++ b/jadx-core/src/main/java/jadx/core/ProcessClass.java @@ -1,16 +1,22 @@ package jadx.core; +import java.util.EnumMap; import java.util.List; +import java.util.Map; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import jadx.api.DecompilationMode; import jadx.api.ICodeInfo; +import jadx.api.JadxArgs; import jadx.api.impl.SimpleCodeInfo; import jadx.core.codegen.CodeGen; import jadx.core.dex.attributes.AFlag; +import jadx.core.dex.attributes.AType; +import jadx.core.dex.attributes.nodes.DecompileModeOverrideAttr; import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.LoadStage; import jadx.core.dex.nodes.RootNode; @@ -154,6 +160,43 @@ public class ProcessClass { } } + private final Map modesMap = new EnumMap<>(DecompilationMode.class); + + public @Nullable ICodeInfo forceGenerateCodeForMode(ClassNode cls, DecompilationMode mode) { + synchronized (modesMap) { + ProcessClass prCls = modesMap.computeIfAbsent(mode, m -> { + RootNode root = cls.root(); + ProcessClass newPrCls = new ProcessClass(getPassesForMode(root.getArgs(), m)); + newPrCls.initPasses(root); + return newPrCls; + }); + try { + cls.addAttr(new DecompileModeOverrideAttr(mode)); + return prCls.forceGenerateCode(cls); + } finally { + cls.remove(AType.DECOMPILE_MODE_OVERRIDE); + } + } + } + + private static List getPassesForMode(JadxArgs baseArgs, DecompilationMode mode) { + switch (mode) { + case FALLBACK: + return Jadx.getFallbackPassesList(); + + case SIMPLE: + // copy properties into new args + // keep in sync with properties usage in Jadx.getSimpleModePasses method + JadxArgs args = new JadxArgs(); + args.setDebugInfo(baseArgs.isDebugInfo()); + args.setCommentsLevel(baseArgs.getCommentsLevel()); + return Jadx.getSimpleModePasses(args); + + default: + throw new JadxRuntimeException("Unexpected decompilation mode: " + mode); + } + } + public void initPasses(RootNode root) { for (IDexTreeVisitor pass : passes) { try { diff --git a/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java b/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java index bf96e54cb..d4f3d71f5 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java @@ -9,6 +9,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jadx.api.CommentsLevel; +import jadx.api.DecompilationMode; import jadx.api.ICodeWriter; import jadx.api.JadxArgs; import jadx.api.args.IntegerFormat; @@ -23,6 +24,7 @@ import jadx.core.Jadx; import jadx.core.codegen.utils.CodeGenUtils; import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AType; +import jadx.core.dex.attributes.nodes.DecompileModeOverrideAttr; import jadx.core.dex.attributes.nodes.JadxError; import jadx.core.dex.attributes.nodes.JumpInfo; import jadx.core.dex.attributes.nodes.MethodOverrideAttr; @@ -266,7 +268,14 @@ public class MethodGen { public void addInstructions(ICodeWriter code) throws CodegenException { JadxArgs args = mth.root().getArgs(); - switch (args.getDecompilationMode()) { + DecompileModeOverrideAttr modeOverrideAttr = mth.getTopParentClass().get(AType.DECOMPILE_MODE_OVERRIDE); + DecompilationMode mode; + if (modeOverrideAttr != null) { + mode = modeOverrideAttr.getMode(); + } else { + mode = args.getDecompilationMode(); + } + switch (mode) { case AUTO: if (classGen.isFallbackMode() || mth.getRegion() == null) { // TODO: try simple mode first diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/AType.java b/jadx-core/src/main/java/jadx/core/dex/attributes/AType.java index 4fc87c2a5..e6622651b 100644 --- a/jadx-core/src/main/java/jadx/core/dex/attributes/AType.java +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/AType.java @@ -7,6 +7,7 @@ import jadx.core.dex.attributes.nodes.AnonymousClassAttr; import jadx.core.dex.attributes.nodes.ClassTypeVarsAttr; import jadx.core.dex.attributes.nodes.CodeFeaturesAttr; import jadx.core.dex.attributes.nodes.DeclareVariablesAttr; +import jadx.core.dex.attributes.nodes.DecompileModeOverrideAttr; import jadx.core.dex.attributes.nodes.EdgeInsnAttr; import jadx.core.dex.attributes.nodes.EnumClassAttr; import jadx.core.dex.attributes.nodes.EnumMapAttr; @@ -62,6 +63,7 @@ public final class AType implements IJadxAttrType { public static final AType CLASS_TYPE_VARS = new AType<>(); public static final AType ANONYMOUS_CLASS = new AType<>(); public static final AType INLINED = new AType<>(); + public static final AType DECOMPILE_MODE_OVERRIDE = new AType<>(); // field public static final AType FIELD_INIT_INSN = new AType<>(); diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/DecompileModeOverrideAttr.java b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/DecompileModeOverrideAttr.java new file mode 100644 index 000000000..54369477f --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/DecompileModeOverrideAttr.java @@ -0,0 +1,29 @@ +package jadx.core.dex.attributes.nodes; + +import jadx.api.DecompilationMode; +import jadx.api.plugins.input.data.attributes.IJadxAttrType; +import jadx.api.plugins.input.data.attributes.IJadxAttribute; +import jadx.core.dex.attributes.AType; + +public class DecompileModeOverrideAttr implements IJadxAttribute { + + private final DecompilationMode mode; + + public DecompileModeOverrideAttr(DecompilationMode mode) { + this.mode = mode; + } + + public DecompilationMode getMode() { + return mode; + } + + @Override + public IJadxAttrType getAttrType() { + return AType.DECOMPILE_MODE_OVERRIDE; + } + + @Override + public String toString() { + return "DECOMPILE_MODE_OVERRIDE: " + mode; + } +} diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java index 4d2429c89..1b46d8cfd 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java @@ -18,7 +18,6 @@ import org.slf4j.LoggerFactory; import jadx.api.DecompilationMode; import jadx.api.ICodeCache; import jadx.api.ICodeInfo; -import jadx.api.JadxArgs; import jadx.api.JavaClass; import jadx.api.impl.SimpleCodeInfo; import jadx.api.impl.SimpleCodeWriter; @@ -38,8 +37,6 @@ import jadx.api.plugins.input.data.attributes.types.SourceFileAttr; import jadx.api.plugins.input.data.impl.ListConsumer; import jadx.api.usage.IUsageInfoData; import jadx.core.Consts; -import jadx.core.Jadx; -import jadx.core.ProcessClass; import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.nodes.InlinedAttr; @@ -320,23 +317,25 @@ public class ClassNode extends NotificationAttrNode * WARNING: Slow operation! Use with caution! */ public ICodeInfo decompileWithMode(DecompilationMode mode) { - DecompilationMode baseMode = root.getArgs().getDecompilationMode(); - if (mode == baseMode) { - return decompile(true); - } - synchronized (DECOMPILE_WITH_MODE_SYNC) { - JadxArgs args = root.getArgs(); - try { - unload(); - args.setDecompilationMode(mode); - ProcessClass process = new ProcessClass(Jadx.getPassesList(args)); - process.initPasses(root); - ICodeInfo code = process.forceGenerateCode(this); - return Utils.getOrElse(code, ICodeInfo.EMPTY); - } finally { - args.setDecompilationMode(baseMode); - unload(); - } + switch (mode) { + case AUTO: + case RESTRUCTURE: + return decompile(true); + + case SIMPLE: + case FALLBACK: + synchronized (DECOMPILE_WITH_MODE_SYNC) { + try { + unload(); + ICodeInfo code = root.getProcessClasses().forceGenerateCodeForMode(this, mode); + return Utils.getOrElse(code, ICodeInfo.EMPTY); + } finally { + unload(); + } + } + + default: + throw new JadxRuntimeException("Unknown mode: " + mode); } }