From a17f9136dd495df4c9bd301b3297153e5d166b98 Mon Sep 17 00:00:00 2001 From: Skylot Date: Thu, 1 Aug 2019 20:59:46 +0300 Subject: [PATCH] refactor: enable class unloading after code generation --- jadx-cli/src/main/java/jadx/cli/JadxCLI.java | 2 + .../src/main/java/jadx/api/ICodeCache.java | 11 +++ .../src/main/java/jadx/api/JadxArgs.java | 13 +++ .../main/java/jadx/api/JadxDecompiler.java | 4 +- .../src/main/java/jadx/api/JavaClass.java | 32 +++---- .../src/main/java/jadx/api/JavaMethod.java | 21 ++--- .../java/jadx/api/impl/InMemoryCodeCache.java | 24 +++++ .../java/jadx/api/impl/NoOpCodeCache.java | 19 ++++ .../src/main/java/jadx/core/ProcessClass.java | 9 +- .../src/main/java/jadx/core/clsp/ClsSet.java | 6 +- .../main/java/jadx/core/codegen/InsnGen.java | 23 +++-- .../java/jadx/core/codegen/MethodGen.java | 6 +- .../java/jadx/core/dex/attributes/AType.java | 62 ++++++++---- .../jadx/core/dex/attributes/AttrNode.java | 11 +++ .../core/dex/attributes/AttributeStorage.java | 45 ++++++--- .../attributes/nodes/MethodInlineAttr.java | 27 +++++- .../attributes/nodes/SkipMethodArgsAttr.java | 67 +++++++++++++ .../java/jadx/core/dex/nodes/ClassNode.java | 41 ++++---- .../java/jadx/core/dex/nodes/MethodNode.java | 94 +++++++++++++------ .../java/jadx/core/dex/nodes/RootNode.java | 8 ++ .../jadx/core/dex/visitors/ClassModifier.java | 15 +-- .../core/dex/visitors/DotGraphVisitor.java | 2 +- .../core/dex/visitors/InitCodeVariables.java | 6 +- .../dex/visitors/MethodInlineVisitor.java | 2 +- .../jadx/core/dex/visitors/ModVisitor.java | 4 +- .../core/dex/visitors/PrepareForCodeGen.java | 2 +- .../java/jadx/core/dex/visitors/SaveCode.java | 3 +- .../debuginfo/DebugInfoParseVisitor.java | 6 +- .../visitors/debuginfo/DebugInfoParser.java | 2 +- .../core/dex/visitors/ssa/RenameState.java | 8 +- .../core/dex/visitors/ssa/SSATransform.java | 13 ++- .../java/jadx/core/utils/CodeGenUtils.java | 3 +- .../src/main/java/jadx/core/utils/Utils.java | 14 +++ .../java/jadx/tests/api/IntegrationTest.java | 5 +- 34 files changed, 446 insertions(+), 164 deletions(-) create mode 100644 jadx-core/src/main/java/jadx/api/ICodeCache.java create mode 100644 jadx-core/src/main/java/jadx/api/impl/InMemoryCodeCache.java create mode 100644 jadx-core/src/main/java/jadx/api/impl/NoOpCodeCache.java create mode 100644 jadx-core/src/main/java/jadx/core/dex/attributes/nodes/SkipMethodArgsAttr.java diff --git a/jadx-cli/src/main/java/jadx/cli/JadxCLI.java b/jadx-cli/src/main/java/jadx/cli/JadxCLI.java index 64c8ee4e0..9f4cf9b52 100644 --- a/jadx-cli/src/main/java/jadx/cli/JadxCLI.java +++ b/jadx-cli/src/main/java/jadx/cli/JadxCLI.java @@ -5,6 +5,7 @@ import org.slf4j.LoggerFactory; import jadx.api.JadxArgs; import jadx.api.JadxDecompiler; +import jadx.api.impl.NoOpCodeCache; import jadx.core.utils.exceptions.JadxArgsValidateException; public class JadxCLI { @@ -27,6 +28,7 @@ public class JadxCLI { static int processAndSave(JadxCLIArgs inputArgs) { JadxArgs args = inputArgs.toJadxArgs(); + args.setCodeCache(new NoOpCodeCache()); JadxDecompiler jadx = new JadxDecompiler(args); try { jadx.load(); diff --git a/jadx-core/src/main/java/jadx/api/ICodeCache.java b/jadx-core/src/main/java/jadx/api/ICodeCache.java new file mode 100644 index 000000000..a078f79cf --- /dev/null +++ b/jadx-core/src/main/java/jadx/api/ICodeCache.java @@ -0,0 +1,11 @@ +package jadx.api; + +import org.jetbrains.annotations.Nullable; + +public interface ICodeCache { + + void add(String clsFullName, ICodeInfo codeInfo); + + @Nullable + ICodeInfo get(String clsFullName); +} diff --git a/jadx-core/src/main/java/jadx/api/JadxArgs.java b/jadx-core/src/main/java/jadx/api/JadxArgs.java index c484e33d4..a7cc08e8a 100644 --- a/jadx-core/src/main/java/jadx/api/JadxArgs.java +++ b/jadx-core/src/main/java/jadx/api/JadxArgs.java @@ -8,6 +8,8 @@ import java.util.List; import java.util.Set; import java.util.function.Predicate; +import jadx.api.impl.InMemoryCodeCache; + public class JadxArgs { public static final int DEFAULT_THREADS_COUNT = Math.max(1, Runtime.getRuntime().availableProcessors() / 2); @@ -22,6 +24,8 @@ public class JadxArgs { private File outDirSrc; private File outDirRes; + private ICodeCache codeCache = new InMemoryCodeCache(); + private int threadsCount = DEFAULT_THREADS_COUNT; private boolean cfgOutput = false; @@ -326,6 +330,14 @@ public class JadxArgs { this.outputFormat = outputFormat; } + public ICodeCache getCodeCache() { + return codeCache; + } + + public void setCodeCache(ICodeCache codeCache) { + this.codeCache = codeCache; + } + @Override public String toString() { return "JadxArgs{" + "inputFiles=" + inputFiles @@ -352,6 +364,7 @@ public class JadxArgs { + ", fsCaseSensitive=" + fsCaseSensitive + ", renameFlags=" + renameFlags + ", outputFormat=" + outputFormat + + ", codeCache=" + codeCache + '}'; } } diff --git a/jadx-core/src/main/java/jadx/api/JadxDecompiler.java b/jadx-core/src/main/java/jadx/api/JadxDecompiler.java index c4a65c2c2..f1089ce5a 100644 --- a/jadx-core/src/main/java/jadx/api/JadxDecompiler.java +++ b/jadx-core/src/main/java/jadx/api/JadxDecompiler.java @@ -208,8 +208,8 @@ public final class JadxDecompiler { } executor.execute(() -> { try { - cls.decompile(); - SaveCode.save(outDir, cls.getClassNode()); + ICodeInfo code = cls.getCodeInfo(); + SaveCode.save(outDir, cls.getClassNode(), code); } catch (Exception e) { LOG.error("Error saving class: {}", cls.getFullName(), e); } diff --git a/jadx-core/src/main/java/jadx/api/JavaClass.java b/jadx-core/src/main/java/jadx/api/JavaClass.java index 2e7d3b998..faa8d5a1c 100644 --- a/jadx-core/src/main/java/jadx/api/JavaClass.java +++ b/jadx-core/src/main/java/jadx/api/JavaClass.java @@ -43,24 +43,19 @@ public final class JavaClass implements JavaNode { } public String getCode() { - ICodeInfo code = cls.getCode(); + ICodeInfo code = getCodeInfo(); if (code == null) { - decompile(); - code = cls.getCode(); - if (code == null) { - return ""; - } + return ""; } return code.getCodeStr(); } - public synchronized void decompile() { - if (decompiler == null) { - return; - } - if (cls.getCode() == null) { - cls.decompile(); - } + public ICodeInfo getCodeInfo() { + return cls.decompile(); + } + + public void decompile() { + cls.decompile(); } public synchronized String getSmali() { @@ -140,8 +135,7 @@ public final class JavaClass implements JavaNode { } private Map getCodeAnnotations() { - decompile(); - ICodeInfo code = cls.getCode(); + ICodeInfo code = getCodeInfo(); if (code == null) { return Collections.emptyMap(); } @@ -168,19 +162,19 @@ public final class JavaClass implements JavaNode { } @Nullable + @Deprecated public JavaNode getJavaNodeAtPosition(int line, int offset) { - decompile(); - return getRootDecompiler().getJavaNodeAtPosition(cls.getCode(), line, offset); + return getRootDecompiler().getJavaNodeAtPosition(getCodeInfo(), line, offset); } @Nullable + @Deprecated public CodePosition getDefinitionPosition() { return getRootDecompiler().getDefinitionPosition(this); } public Integer getSourceLine(int decompiledLine) { - decompile(); - return cls.getCode().getLineMapping().get(decompiledLine); + return getCodeInfo().getLineMapping().get(decompiledLine); } @Override diff --git a/jadx-core/src/main/java/jadx/api/JavaMethod.java b/jadx-core/src/main/java/jadx/api/JavaMethod.java index 498139464..a27240f2c 100644 --- a/jadx-core/src/main/java/jadx/api/JavaMethod.java +++ b/jadx-core/src/main/java/jadx/api/JavaMethod.java @@ -2,13 +2,11 @@ package jadx.api; import java.util.Collections; import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; import jadx.core.dex.info.AccessInfo; import jadx.core.dex.instructions.args.ArgType; -import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.nodes.MethodNode; +import jadx.core.utils.Utils; public final class JavaMethod implements JavaNode { private final MethodNode mth; @@ -44,19 +42,16 @@ public final class JavaMethod implements JavaNode { } public List getArguments() { - if (mth.getMethodInfo().getArgumentsTypes().isEmpty()) { + List infoArgTypes = mth.getMethodInfo().getArgumentsTypes(); + if (infoArgTypes.isEmpty()) { return Collections.emptyList(); } - List arguments = mth.getArguments(false); - Stream argTypeStream; - if (arguments == null || arguments.isEmpty() || mth.isNoCode()) { - argTypeStream = mth.getMethodInfo().getArgumentsTypes().stream(); - } else { - argTypeStream = arguments.stream().map(RegisterArg::getType); + List arguments = mth.getArgTypes(); + if (arguments == null) { + arguments = infoArgTypes; } - return argTypeStream - .map(type -> ArgType.tryToResolveClassAlias(mth.dex(), type)) - .collect(Collectors.toList()); + return Utils.collectionMap(arguments, + type -> ArgType.tryToResolveClassAlias(mth.dex(), type)); } public ArgType getReturnType() { diff --git a/jadx-core/src/main/java/jadx/api/impl/InMemoryCodeCache.java b/jadx-core/src/main/java/jadx/api/impl/InMemoryCodeCache.java new file mode 100644 index 000000000..04d80fef8 --- /dev/null +++ b/jadx-core/src/main/java/jadx/api/impl/InMemoryCodeCache.java @@ -0,0 +1,24 @@ +package jadx.api.impl; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.jetbrains.annotations.Nullable; + +import jadx.api.ICodeCache; +import jadx.api.ICodeInfo; + +public class InMemoryCodeCache implements ICodeCache { + + private final Map storage = new ConcurrentHashMap<>(); + + @Override + public void add(String clsFullName, ICodeInfo codeInfo) { + storage.put(clsFullName, codeInfo); + } + + @Override + public @Nullable ICodeInfo get(String clsFullName) { + return storage.get(clsFullName); + } +} diff --git a/jadx-core/src/main/java/jadx/api/impl/NoOpCodeCache.java b/jadx-core/src/main/java/jadx/api/impl/NoOpCodeCache.java new file mode 100644 index 000000000..f764cde4e --- /dev/null +++ b/jadx-core/src/main/java/jadx/api/impl/NoOpCodeCache.java @@ -0,0 +1,19 @@ +package jadx.api.impl; + +import org.jetbrains.annotations.Nullable; + +import jadx.api.ICodeCache; +import jadx.api.ICodeInfo; + +public class NoOpCodeCache implements ICodeCache { + + @Override + public void add(String clsFullName, ICodeInfo codeInfo) { + // do nothing + } + + @Override + public @Nullable ICodeInfo get(String clsFullName) { + return null; + } +} diff --git a/jadx-core/src/main/java/jadx/core/ProcessClass.java b/jadx-core/src/main/java/jadx/core/ProcessClass.java index b639042f2..dd8726115 100644 --- a/jadx-core/src/main/java/jadx/core/ProcessClass.java +++ b/jadx-core/src/main/java/jadx/core/ProcessClass.java @@ -14,6 +14,7 @@ import static jadx.core.dex.nodes.ProcessState.LOADED; import static jadx.core.dex.nodes.ProcessState.NOT_LOADED; import static jadx.core.dex.nodes.ProcessState.PROCESS_COMPLETE; import static jadx.core.dex.nodes.ProcessState.PROCESS_STARTED; +import static jadx.core.dex.nodes.ProcessState.UNLOADED; public final class ProcessClass { @@ -26,7 +27,8 @@ public final class ProcessClass { process(topParentClass); return; } - if (cls.getState() == PROCESS_COMPLETE) { + if (cls.getState() == PROCESS_COMPLETE + || cls.getState() == UNLOADED) { // nothing to do return; } @@ -58,8 +60,9 @@ public final class ProcessClass { process(cls); cls.getDependencies().forEach(ProcessClass::process); - // TODO: unload class (need to build dependency tree or allow to load class several times) - return CodeGen.generate(cls); + ICodeInfo code = CodeGen.generate(cls); + cls.unload(); + return code; } catch (Throwable e) { throw new JadxRuntimeException("Failed to generate code for class: " + cls.getFullName(), e); } diff --git a/jadx-core/src/main/java/jadx/core/clsp/ClsSet.java b/jadx-core/src/main/java/jadx/core/clsp/ClsSet.java index d241c1339..9bf4a3acc 100644 --- a/jadx-core/src/main/java/jadx/core/clsp/ClsSet.java +++ b/jadx-core/src/main/java/jadx/core/clsp/ClsSet.java @@ -25,7 +25,6 @@ import org.slf4j.LoggerFactory; import jadx.core.dex.info.AccessInfo; import jadx.core.dex.instructions.args.ArgType; -import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.GenericInfo; import jadx.core.dex.nodes.MethodNode; @@ -113,7 +112,7 @@ public class ClsSet { } private void processMethodDetails(List methods, MethodNode mth, AccessInfo accessFlags) { - List args = mth.getArguments(false); + List args = mth.getArgTypes(); boolean genericArg = false; ArgType[] genericArgs; if (args.isEmpty()) { @@ -122,8 +121,7 @@ public class ClsSet { int argsCount = args.size(); genericArgs = new ArgType[argsCount]; for (int i = 0; i < argsCount; i++) { - RegisterArg arg = args.get(i); - ArgType argType = arg.getType(); + ArgType argType = args.get(i); if (argType.isGeneric() || argType.isGenericType()) { genericArgs[i] = argType; genericArg = true; diff --git a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java index 0f1e5b90f..c89ebaeb5 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java @@ -17,6 +17,7 @@ import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.nodes.FieldReplaceAttr; import jadx.core.dex.attributes.nodes.LoopLabelAttr; import jadx.core.dex.attributes.nodes.MethodInlineAttr; +import jadx.core.dex.attributes.nodes.SkipMethodArgsAttr; import jadx.core.dex.info.ClassInfo; import jadx.core.dex.info.FieldInfo; import jadx.core.dex.info.MethodInfo; @@ -756,8 +757,7 @@ public class InsnGen { if (arg.contains(AFlag.SKIP_ARG)) { continue; } - RegisterArg callArg = getCallMthArg(callMth, i - startArgNum); - if (callArg != null && callArg.contains(AFlag.SKIP_ARG)) { + if (SkipMethodArgsAttr.isSkip(callMth, i - startArgNum)) { continue; } if (!firstArg) { @@ -778,7 +778,7 @@ public class InsnGen { if (callMth == null) { return null; } - List args = callMth.getArguments(false); + List args = callMth.getArgRegs(); if (args != null && num < args.size()) { return args.get(num); } @@ -789,18 +789,18 @@ public class InsnGen { * Add additional cast for overloaded method argument. */ private boolean processOverloadedArg(CodeWriter code, MethodNode callMth, InsnArg arg, int origPos) { - List arguments = callMth.getArguments(false); - if (arguments == null || arguments.isEmpty()) { + List argTypes = callMth.getArgTypes(); + if (argTypes == null) { // try to load class callMth.getParentClass().loadAndProcess(); - arguments = callMth.getArguments(false); + argTypes = callMth.getArgTypes(); } ArgType origType; - if (arguments == null || arguments.isEmpty()) { + if (argTypes == null) { mth.addComment("JADX INFO: used method not loaded: " + callMth + ", types can be incorrect"); origType = callMth.getMethodInfo().getArgumentsTypes().get(origPos); } else { - origType = arguments.get(origPos).getInitType(); + origType = argTypes.get(origPos); if (origType.isGenericType() && !callMth.getParentClass().equals(mth.getParentClass())) { // cancel cast return false; @@ -887,11 +887,10 @@ public class InsnGen { } else { // remap args InsnArg[] regs = new InsnArg[callMthNode.getRegsCount()]; - List callArgs = callMthNode.getArguments(true); - for (int i = 0; i < callArgs.size(); i++) { + int[] regNums = mia.getArgsRegNums(); + for (int i = 0; i < regNums.length; i++) { InsnArg arg = insn.getArg(i); - RegisterArg callArg = callArgs.get(i); - regs[callArg.getRegNum()] = arg; + regs[regNums[i]] = arg; } // replace args InsnNode inlCopy = inl.copy(); 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 13fac1918..b9ee51edb 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java @@ -1,5 +1,6 @@ package jadx.core.codegen; +import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -88,7 +89,6 @@ public class MethodGen { if (mth.getMethodInfo().hasAlias() && !ai.isConstructor()) { CodeGenUtils.addRenamedComment(code, mth, mth.getName()); } - CodeGenUtils.addSourceFileInfo(code, mth); if (mth.contains(AFlag.INCONSISTENT_CODE)) { code.startLine("/* Code decompiled incorrectly, please refer to instructions dump. */"); } @@ -113,11 +113,11 @@ public class MethodGen { } code.add('('); - List args = mth.getArguments(false); + List args = mth.getArgRegs(); if (mth.getMethodInfo().isConstructor() && mth.getParentClass().contains(AType.ENUM_CLASS)) { if (args.size() == 2) { - args.clear(); + args = Collections.emptyList(); } else if (args.size() > 2) { args = args.subList(2, args.size()); } else { 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 57918c8af..2cdab5b73 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 @@ -1,5 +1,9 @@ package jadx.core.dex.attributes; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + import jadx.core.dex.attributes.annotations.AnnotationsList; import jadx.core.dex.attributes.annotations.MethodParameters; import jadx.core.dex.attributes.nodes.DeclareVariablesAttr; @@ -18,6 +22,7 @@ import jadx.core.dex.attributes.nodes.MethodInlineAttr; import jadx.core.dex.attributes.nodes.PhiListAttr; import jadx.core.dex.attributes.nodes.RegDebugInfoAttr; import jadx.core.dex.attributes.nodes.RenameReasonAttr; +import jadx.core.dex.attributes.nodes.SkipMethodArgsAttr; import jadx.core.dex.attributes.nodes.SourceFileAttr; import jadx.core.dex.nodes.parser.FieldInitAttr; import jadx.core.dex.trycatch.CatchAttr; @@ -32,35 +37,54 @@ import jadx.core.dex.trycatch.SplitterBlockAttr; */ public class AType { - public static final AType> JUMP = new AType<>(); - public static final AType> LOOP = new AType<>(); - public static final AType> EDGE_INSN = new AType<>(); + // class, method, field + public static final AType ANNOTATION_LIST = new AType<>(); + public static final AType RENAME_REASON = new AType<>(); + // class, method public static final AType> JADX_ERROR = new AType<>(); // code failed to decompile completely public static final AType> JADX_WARN = new AType<>(); // mark code as inconsistent (code can be viewed) public static final AType> COMMENTS = new AType<>(); // any additional info about decompilation - public static final AType EXC_HANDLER = new AType<>(); - public static final AType CATCH_BLOCK = new AType<>(); - public static final AType SPLITTER_BLOCK = new AType<>(); - public static final AType FORCE_RETURN = new AType<>(); - public static final AType FIELD_INIT = new AType<>(); - public static final AType FIELD_REPLACE = new AType<>(); - public static final AType METHOD_INLINE = new AType<>(); + // class + public static final AType SOURCE_FILE = new AType<>(); public static final AType ENUM_CLASS = new AType<>(); public static final AType ENUM_MAP = new AType<>(); - public static final AType ANNOTATION_LIST = new AType<>(); - public static final AType ANNOTATION_MTH_PARAMETERS = new AType<>(); - public static final AType PHI_LIST = new AType<>(); - public static final AType SOURCE_FILE = new AType<>(); - public static final AType DECLARE_VARIABLES = new AType<>(); - public static final AType LOOP_LABEL = new AType<>(); - public static final AType IGNORE_EDGE = new AType<>(); - public static final AType RENAME_REASON = new AType<>(); + + // field + public static final AType FIELD_INIT = new AType<>(); + public static final AType FIELD_REPLACE = new AType<>(); // method public static final AType LOCAL_VARS_DEBUG_INFO = new AType<>(); + public static final AType METHOD_INLINE = new AType<>(); + public static final AType ANNOTATION_MTH_PARAMETERS = new AType<>(); + public static final AType SKIP_MTH_ARGS = new AType<>(); - // registers + // region + public static final AType DECLARE_VARIABLES = new AType<>(); + + // block + public static final AType PHI_LIST = new AType<>(); + public static final AType IGNORE_EDGE = new AType<>(); + public static final AType FORCE_RETURN = new AType<>(); + public static final AType CATCH_BLOCK = new AType<>(); + public static final AType SPLITTER_BLOCK = new AType<>(); + public static final AType> LOOP = new AType<>(); + public static final AType> EDGE_INSN = new AType<>(); + + // block or insn + public static final AType EXC_HANDLER = new AType<>(); + + // instruction + public static final AType LOOP_LABEL = new AType<>(); + public static final AType> JUMP = new AType<>(); + + // register public static final AType REG_DEBUG_INFO = new AType<>(); + + public static final Set> SKIP_ON_UNLOAD = new HashSet<>(Arrays.asList( + FIELD_REPLACE, + METHOD_INLINE, + SKIP_MTH_ARGS)); } diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/AttrNode.java b/jadx-core/src/main/java/jadx/core/dex/attributes/AttrNode.java index 3d18035db..4079c71a6 100644 --- a/jadx-core/src/main/java/jadx/core/dex/attributes/AttrNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/AttrNode.java @@ -97,6 +97,17 @@ public abstract class AttrNode implements IAttributeNode { unloadIfEmpty(); } + /** + * Remove all attribute with exceptions from {@link AType#SKIP_ON_UNLOAD} + */ + public void unloadAttributes() { + if (storage == EMPTY_ATTR_STORAGE) { + return; + } + storage.unloadAttributes(); + unloadIfEmpty(); + } + @Override public List getAttributesStringsList() { return storage.getAttributeStrings(); diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/AttributeStorage.java b/jadx-core/src/main/java/jadx/core/dex/attributes/AttributeStorage.java index 1cf0f63a4..789bac206 100644 --- a/jadx-core/src/main/java/jadx/core/dex/attributes/AttributeStorage.java +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/AttributeStorage.java @@ -20,11 +20,11 @@ import jadx.core.utils.Utils; public class AttributeStorage { private final Set flags; - private final Map, IAttribute> attributes; + private Map, IAttribute> attributes; public AttributeStorage() { flags = EnumSet.noneOf(AFlag.class); - attributes = new IdentityHashMap<>(); + attributes = Collections.emptyMap(); } public void add(AFlag flag) { @@ -32,7 +32,7 @@ public class AttributeStorage { } public void add(IAttribute attr) { - attributes.put(attr.getType(), attr); + writeAttributes().put(attr.getType(), attr); } public void add(AType> type, T obj) { @@ -46,7 +46,7 @@ public class AttributeStorage { public void addAll(AttributeStorage otherList) { flags.addAll(otherList.flags); - attributes.putAll(otherList.attributes); + writeAttributes().putAll(otherList.attributes); } public boolean contains(AFlag flag) { @@ -80,20 +80,41 @@ public class AttributeStorage { } public void remove(AType type) { - attributes.remove(type); - } - - public void remove(IAttribute attr) { - AType type = attr.getType(); - IAttribute a = attributes.get(type); - if (a == attr) { + if (!attributes.isEmpty()) { attributes.remove(type); } } + public void remove(IAttribute attr) { + if (!attributes.isEmpty()) { + AType type = attr.getType(); + IAttribute a = attributes.get(type); + if (a == attr) { + attributes.remove(type); + } + } + } + + private Map, IAttribute> writeAttributes() { + if (attributes.isEmpty()) { + attributes = new IdentityHashMap<>(5); + } + return attributes; + } + public void clear() { flags.clear(); - attributes.clear(); + if (!attributes.isEmpty()) { + attributes.clear(); + } + } + + public synchronized void unloadAttributes() { + if (attributes.isEmpty()) { + return; + } + Set> skipOnUnload = AType.SKIP_ON_UNLOAD; + attributes.keySet().removeIf(attrType -> !skipOnUnload.contains(attrType)); } public List getAttributeStrings() { diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/MethodInlineAttr.java b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/MethodInlineAttr.java index 60b8f729f..272fb6ccd 100644 --- a/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/MethodInlineAttr.java +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/MethodInlineAttr.java @@ -1,21 +1,46 @@ package jadx.core.dex.attributes.nodes; +import java.util.List; + import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.IAttribute; +import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.nodes.InsnNode; +import jadx.core.dex.nodes.MethodNode; public class MethodInlineAttr implements IAttribute { + public static void markForInline(MethodNode mth, InsnNode replaceInsn) { + List allArgRegs = mth.getAllArgRegs(); + int argsCount = allArgRegs.size(); + int[] regNums = new int[argsCount]; + for (int i = 0; i < argsCount; i++) { + RegisterArg reg = allArgRegs.get(i); + regNums[i] = reg.getRegNum(); + } + mth.addAttr(new MethodInlineAttr(replaceInsn, regNums)); + } + private final InsnNode insn; - public MethodInlineAttr(InsnNode insn) { + /** + * Store method arguments register numbers to allow remap registers + */ + private final int[] argsRegNums; + + private MethodInlineAttr(InsnNode insn, int[] argsRegNums) { this.insn = insn; + this.argsRegNums = argsRegNums; } public InsnNode getInsn() { return insn; } + public int[] getArgsRegNums() { + return argsRegNums; + } + @Override public AType getType() { return AType.METHOD_INLINE; diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/SkipMethodArgsAttr.java b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/SkipMethodArgsAttr.java new file mode 100644 index 000000000..06248ed7b --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/SkipMethodArgsAttr.java @@ -0,0 +1,67 @@ +package jadx.core.dex.attributes.nodes; + +import java.util.BitSet; + +import org.jetbrains.annotations.Nullable; + +import jadx.core.dex.attributes.AType; +import jadx.core.dex.attributes.IAttribute; +import jadx.core.dex.instructions.args.RegisterArg; +import jadx.core.dex.nodes.MethodNode; +import jadx.core.utils.Utils; +import jadx.core.utils.exceptions.JadxRuntimeException; + +public class SkipMethodArgsAttr implements IAttribute { + + public static void skipArg(MethodNode mth, RegisterArg arg) { + int argNum = Utils.indexInList(mth.getArgRegs(), arg); + if (argNum == -1) { + throw new JadxRuntimeException("Arg not found: " + arg); + } + skipArg(mth, argNum); + } + + public static void skipArg(MethodNode mth, int argNum) { + SkipMethodArgsAttr attr = mth.get(AType.SKIP_MTH_ARGS); + if (attr == null) { + attr = new SkipMethodArgsAttr(mth); + mth.addAttr(attr); + } + attr.skip(argNum); + } + + public static boolean isSkip(@Nullable MethodNode mth, int argNum) { + if (mth == null) { + return false; + } + SkipMethodArgsAttr attr = mth.get(AType.SKIP_MTH_ARGS); + if (attr == null) { + return false; + } + return attr.isSkip(argNum); + } + + private final BitSet skipArgs; + + private SkipMethodArgsAttr(MethodNode mth) { + this.skipArgs = new BitSet(mth.getArgRegs().size()); + } + + public void skip(int argNum) { + skipArgs.set(argNum); + } + + public boolean isSkip(int argNum) { + return skipArgs.get(argNum); + } + + @Override + public AType getType() { + return AType.SKIP_MTH_ARGS; + } + + @Override + public String toString() { + return "SKIP_MTH_ARGS: " + skipArgs; + } +} 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 42cea5c8e..eb916d77e 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 @@ -16,6 +16,7 @@ import com.android.dex.ClassData.Method; import com.android.dex.ClassDef; import com.android.dex.Dex; +import jadx.api.ICodeCache; import jadx.api.ICodeInfo; import jadx.core.Consts; import jadx.core.ProcessClass; @@ -52,10 +53,8 @@ public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode { private final List methods; private final List fields; - private List innerClasses = new ArrayList<>(); + private List innerClasses = Collections.emptyList(); - // store decompiled code - private ICodeInfo code; // store smali private String smali; // store parent for inner classes or 'this' otherwise @@ -248,16 +247,23 @@ public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode { ProcessClass.process(this); } - public ICodeInfo decompile() { + public synchronized ICodeInfo decompile() { + ICodeCache codeCache = root().getCodeCache(); + ClassNode topParentClass = getTopParentClass(); + String clsRawName = topParentClass.getRawName(); + ICodeInfo code = codeCache.get(clsRawName); if (code != null) { return code; } - ICodeInfo codeInfo = ProcessClass.generateCode(this); - // TODO: don't store code in class node - setCode(codeInfo); + ICodeInfo codeInfo = ProcessClass.generateCode(topParentClass); + codeCache.add(clsRawName, codeInfo); return codeInfo; } + public ICodeInfo getCode() { + return decompile(); + } + @Override public void load() { for (MethodNode mth : getMethods()) { @@ -275,12 +281,10 @@ public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode { @Override public void unload() { - for (MethodNode mth : getMethods()) { - mth.unload(); - } - for (ClassNode innerCls : getInnerClasses()) { - innerCls.unload(); - } + methods.forEach(MethodNode::unload); + innerClasses.forEach(ClassNode::unload); + fields.forEach(FieldNode::unloadAttributes); + unloadAttributes(); setState(UNLOADED); } @@ -411,6 +415,9 @@ public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode { } public void addInnerClass(ClassNode cls) { + if (innerClasses.isEmpty()) { + innerClasses = new ArrayList<>(5); + } innerClasses.add(cls); cls.parentClass = this; } @@ -488,14 +495,6 @@ public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode { return clsInfo.getAliasPkg(); } - public void setCode(ICodeInfo code) { - this.code = code; - } - - public ICodeInfo getCode() { - return code; - } - public void setSmali(String smali) { this.smali = smali; } diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java index 4f62faf6a..9c5a78905 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java @@ -58,23 +58,27 @@ public class MethodNode extends LineAttrNode implements ILoadable, ICodeNode { private boolean noCode; private int regsCount; - private InsnNode[] instructions; private int codeSize; private int debugInfoOffset; + private boolean loaded; + + // additional info available after load, keep on unload private ArgType retType; - private RegisterArg thisArg; - private List argsList; - private List sVars; + private List argTypes; private List generics; + // decompilation data, reset on unload + private RegisterArg thisArg; + private List argsList; + private InsnNode[] instructions; private List blocks; private BlockNode enterBlock; private List exitBlocks; - - private Region region; + private List sVars; private List exceptionHandlers; private List loops; + private Region region; public MethodNode(ClassNode classNode, Method mthData, boolean isVirtual) { this.mthInfo = MethodInfo.fromDex(classNode.dex(), mthData.getMethodIndex()); @@ -88,14 +92,14 @@ public class MethodNode extends LineAttrNode implements ILoadable, ICodeNode { @Override public void unload() { - regsCount = -1; + loaded = false; if (noCode) { return; } - // don't unload retType and argsList, will be used in jadx-gui after class unload + // don't unload retType, argTypes, generics thisArg = null; + argsList = null; sVars = Collections.emptyList(); - generics = Collections.emptyList(); instructions = null; blocks = null; enterBlock = null; @@ -103,15 +107,17 @@ public class MethodNode extends LineAttrNode implements ILoadable, ICodeNode { region = null; exceptionHandlers = Collections.emptyList(); loops = Collections.emptyList(); + unloadAttributes(); } @Override public void load() throws DecodeException { - if (regsCount != -1) { + if (loaded) { // method already loaded return; } try { + loaded = true; if (noCode) { regsCount = 0; codeSize = 0; @@ -135,6 +141,7 @@ public class MethodNode extends LineAttrNode implements ILoadable, ICodeNode { this.debugInfoOffset = mthCode.getDebugInfoOffset(); } catch (Exception e) { if (!noCode) { + unload(); noCode = true; // load without code load(); @@ -166,30 +173,35 @@ public class MethodNode extends LineAttrNode implements ILoadable, ICodeNode { } private void initMethodTypes() { - if (!parseSignature()) { - retType = mthInfo.getReturnType(); - initArguments(mthInfo.getArgumentsTypes()); + List types = parseSignature(); + if (types == null) { + this.retType = mthInfo.getReturnType(); + this.argTypes = mthInfo.getArgumentsTypes(); + } else { + this.argTypes = types; } + initArguments(this.argTypes); } - private boolean parseSignature() { + @Nullable + private List parseSignature() { SignatureParser sp = SignatureParser.fromNode(this); if (sp == null) { - return false; + return null; } try { - generics = sp.consumeGenericMap(); + this.generics = sp.consumeGenericMap(); List argsTypes = sp.consumeMethodArgs(); - retType = sp.consumeType(); + this.retType = sp.consumeType(); List mthArgs = mthInfo.getArgumentsTypes(); if (argsTypes.size() != mthArgs.size()) { if (argsTypes.isEmpty()) { - return false; + return null; } if (!mthInfo.isConstructor()) { LOG.warn("Wrong signature parse result: {} -> {}, not generic version: {}", sp, argsTypes, mthArgs); - return false; + return null; } else if (getParentClass().getAccessFlags().isEnum()) { // TODO: argsTypes.add(0, mthArgs.get(0)); @@ -199,14 +211,13 @@ public class MethodNode extends LineAttrNode implements ILoadable, ICodeNode { argsTypes.add(0, mthArgs.get(0)); } if (argsTypes.size() != mthArgs.size()) { - return false; + return null; } } - initArguments(argsTypes); - return true; + return argsTypes; } catch (JadxRuntimeException e) { LOG.error("Method signature parse error: {}", this, e); - return false; + return null; } } @@ -242,18 +253,31 @@ public class MethodNode extends LineAttrNode implements ILoadable, ICodeNode { } } - public List getArguments(boolean includeThis) { - if (includeThis && thisArg != null) { - List list = new ArrayList<>(argsList.size() + 1); - list.add(thisArg); - list.addAll(argsList); - return list; + /** + * Return null only if method not yet loaded + */ + @Nullable + public List getArgTypes() { + return argTypes; + } + + public List getArgRegs() { + if (argsList == null) { + throw new JadxRuntimeException("Method args not loaded: " + this + + ", class status: " + parentClass.getTopParentClass().getState()); } return argsList; } - public void skipFirstArgument() { - this.add(AFlag.SKIP_FIRST_ARG); + public List getAllArgRegs() { + List argRegs = getArgRegs(); + if (thisArg != null) { + List list = new ArrayList<>(argRegs.size() + 1); + list.add(thisArg); + list.addAll(argRegs); + return list; + } + return argRegs; } @Nullable @@ -261,6 +285,10 @@ public class MethodNode extends LineAttrNode implements ILoadable, ICodeNode { return thisArg; } + public void skipFirstArgument() { + this.add(AFlag.SKIP_FIRST_ARG); + } + public ArgType getReturnType() { return retType; } @@ -690,6 +718,10 @@ public class MethodNode extends LineAttrNode implements ILoadable, ICodeNode { return -1; } + public boolean isLoaded() { + return loaded; + } + @Override public int hashCode() { return mthInfo.hashCode(); diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java index 2dd006853..4b24a933b 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java @@ -8,6 +8,7 @@ import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import jadx.api.ICodeCache; import jadx.api.JadxArgs; import jadx.api.ResourceFile; import jadx.api.ResourceType; @@ -46,6 +47,8 @@ public class RootNode { private final CacheStorage cacheStorage = new CacheStorage(); private final TypeUpdate typeUpdate; + private final ICodeCache codeCache; + private ClspGraph clsp; private List dexNodes; @Nullable @@ -59,6 +62,7 @@ public class RootNode { this.stringUtils = new StringUtils(args); this.constValues = new ConstStorage(args); this.typeUpdate = new TypeUpdate(this); + this.codeCache = args.getCodeCache(); } public void load(List inputFiles) { @@ -287,4 +291,8 @@ public class RootNode { public TypeUpdate getTypeUpdate() { return typeUpdate; } + + public ICodeCache getCodeCache() { + return codeCache; + } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java index 6e42f635b..508adb3e3 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java @@ -10,6 +10,7 @@ import jadx.core.Consts; 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.AccessInfo; import jadx.core.dex.info.ClassInfo; import jadx.core.dex.info.FieldInfo; @@ -103,7 +104,7 @@ public class ClassModifier extends AbstractVisitor { if (mth.isNoCode() || !mth.getAccessFlags().isConstructor()) { return false; } - List args = mth.getArguments(false); + List args = mth.getArgRegs(); if (args.isEmpty() || mth.contains(AFlag.SKIP_FIRST_ARG)) { return false; } @@ -157,7 +158,7 @@ public class ClassModifier extends AbstractVisitor { } // remove synthetic constructor for inner classes if (af.isConstructor() && mth.getBasicBlocks().size() == 2) { - List args = mth.getArguments(false); + List args = mth.getArgRegs(); if (isRemovedClassInArgs(cls, args)) { modifySyntheticMethod(cls, mth, args); } @@ -198,13 +199,15 @@ public class ClassModifier extends AbstractVisitor { // remove first arg for non-static class (references to outer class) RegisterArg firstArg = args.get(0); if (firstArg.getType().equals(cls.getParentClass().getClassInfo().getType())) { - firstArg.add(AFlag.SKIP_ARG); + SkipMethodArgsAttr.skipArg(mth, 0); } // remove unused args - for (RegisterArg arg : args) { + int argsCount = args.size(); + for (int i = 0; i < argsCount; i++) { + RegisterArg arg = args.get(i); SSAVar sVar = arg.getSVar(); if (sVar != null && sVar.getUseCount() == 0) { - arg.add(AFlag.SKIP_ARG); + SkipMethodArgsAttr.skipArg(mth, i); } } mth.add(AFlag.DONT_GENERATE); @@ -306,7 +309,7 @@ public class ClassModifier extends AbstractVisitor { // remove public empty constructors (static or default) if (af.isConstructor() && (af.isPublic() || af.isStatic()) - && mth.getArguments(false).isEmpty()) { + && mth.getArgRegs().isEmpty()) { List bb = mth.getBasicBlocks(); if (bb == null || bb.isEmpty() || BlockUtils.isAllBlocksEmpty(bb)) { if (af.isStatic() && mth.getMethodInfo().isClassInit()) { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java index 7c9207f04..5b866a8e9 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java @@ -97,7 +97,7 @@ public class DotGraphVisitor extends AbstractVisitor { dot.add(escape(mth.getAccessFlags().makeString())); dot.add(escape(mth.getReturnType() + " " + mth.getParentClass() + '.' + mth.getName() - + '(' + Utils.listToString(mth.getArguments(true)) + ") ")); + + '(' + Utils.listToString(mth.getAllArgRegs()) + ") ")); String attrs = attributesString(mth); if (!attrs.isEmpty()) { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/InitCodeVariables.java b/jadx-core/src/main/java/jadx/core/dex/visitors/InitCodeVariables.java index 896e1b44e..c04b7b2b4 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/InitCodeVariables.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/InitCodeVariables.java @@ -39,7 +39,11 @@ public class InitCodeVariables extends AbstractVisitor { } private static void initCodeVars(MethodNode mth) { - for (RegisterArg mthArg : mth.getArguments(true)) { + RegisterArg thisArg = mth.getThisArg(); + if (thisArg != null) { + initCodeVar(thisArg.getSVar()); + } + for (RegisterArg mthArg : mth.getArgRegs()) { initCodeVar(mthArg.getSVar()); } for (SSAVar ssaVar : mth.getSVars()) { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/MethodInlineVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/MethodInlineVisitor.java index 9c09f4334..2e29e1080 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/MethodInlineVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/MethodInlineVisitor.java @@ -101,7 +101,7 @@ public class MethodInlineVisitor extends AbstractVisitor { for (RegisterArg regArg : regArgs) { copy.replaceArg(regArg, regArg.duplicate(regArg.getRegNum(), null)); } - mth.addAttr(new MethodInlineAttr(copy)); + MethodInlineAttr.markForInline(mth, copy); mth.add(AFlag.DONT_GENERATE); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java index f28adb5b9..2a7a38132 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java @@ -277,7 +277,7 @@ public class ModVisitor extends AbstractVisitor { } classNode.loadAndProcess(); Map argsMap = getArgsToFieldsMapping(callMthNode, co); - if (argsMap.isEmpty() && !callMthNode.getArguments(true).isEmpty()) { + if (argsMap.isEmpty() && !callMthNode.getArgRegs().isEmpty()) { return; } @@ -306,7 +306,7 @@ public class ModVisitor extends AbstractVisitor { MethodInfo callMth = callMthNode.getMethodInfo(); ClassNode cls = callMthNode.getParentClass(); ClassNode parentClass = cls.getParentClass(); - List argList = callMthNode.getArguments(false); + List argList = callMthNode.getArgRegs(); int startArg = 0; if (callMth.getArgsCount() != 0 && callMth.getArgumentsTypes().get(0).equals(parentClass.getClassInfo().getType())) { startArg = 1; diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/PrepareForCodeGen.java b/jadx-core/src/main/java/jadx/core/dex/visitors/PrepareForCodeGen.java index 6cc675f04..987f93365 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/PrepareForCodeGen.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/PrepareForCodeGen.java @@ -220,7 +220,7 @@ public class PrepareForCodeGen extends AbstractVisitor { Set regArgs = new HashSet<>(); constrInsn.getRegisterArgs(regArgs); regArgs.remove(mth.getThisArg()); - regArgs.removeAll(mth.getArguments(false)); + regArgs.removeAll(mth.getArgRegs()); if (!regArgs.isEmpty()) { mth.addWarn("Illegal instructions before constructor call"); } else { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/SaveCode.java b/jadx-core/src/main/java/jadx/core/dex/visitors/SaveCode.java index 214b7e0bc..0c0e02c62 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/SaveCode.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/SaveCode.java @@ -14,11 +14,10 @@ public class SaveCode { private SaveCode() { } - public static void save(File dir, ClassNode cls) { + public static void save(File dir, ClassNode cls, ICodeInfo code) { if (cls.contains(AFlag.DONT_GENERATE)) { return; } - ICodeInfo code = cls.getCode(); if (code == null) { throw new JadxRuntimeException("Code not generated for class " + cls.getFullName()); } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/debuginfo/DebugInfoParseVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/debuginfo/DebugInfoParseVisitor.java index 15cfb31bc..6cd79c7ac 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/debuginfo/DebugInfoParseVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/debuginfo/DebugInfoParseVisitor.java @@ -66,7 +66,11 @@ public class DebugInfoParseVisitor extends AbstractVisitor { RegDebugInfoAttr debugInfoAttr = new RegDebugInfoAttr(var); if (start < 0) { // attach to method arguments - for (RegisterArg arg : mth.getArguments(true)) { + RegisterArg thisArg = mth.getThisArg(); + if (thisArg != null) { + attachDebugInfo(thisArg, var, debugInfoAttr); + } + for (RegisterArg arg : mth.getArgRegs()) { attachDebugInfo(arg, var, debugInfoAttr); } start = 0; diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/debuginfo/DebugInfoParser.java b/jadx-core/src/main/java/jadx/core/dex/visitors/debuginfo/DebugInfoParser.java index 9e8776e7e..6b2e8e7e9 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/debuginfo/DebugInfoParser.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/debuginfo/DebugInfoParser.java @@ -58,7 +58,7 @@ public class DebugInfoParser { int line = section.readUleb128(); int paramsCount = section.readUleb128(); - List mthArgs = mth.getArguments(false); + List mthArgs = mth.getArgRegs(); for (int i = 0; i < paramsCount; i++) { int nameId = section.readUleb128() - 1; diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/RenameState.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/RenameState.java index 0d084bd87..95e5f5512 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/RenameState.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/RenameState.java @@ -21,7 +21,13 @@ final class RenameState { mth.getEnterBlock(), new SSAVar[regsCount], new int[regsCount]); - for (RegisterArg arg : mth.getArguments(true)) { + RegisterArg thisArg = mth.getThisArg(); + if (thisArg != null) { + SSAVar ssaVar = state.startVar(thisArg); + ssaVar.add(AFlag.THIS); + ssaVar.add(AFlag.METHOD_ARGUMENT); + } + for (RegisterArg arg : mth.getArgRegs()) { SSAVar ssaVar = state.startVar(arg); ssaVar.add(AFlag.METHOD_ARGUMENT); } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/SSATransform.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/SSATransform.java index 081b5848f..e4aca096f 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/SSATransform.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/SSATransform.java @@ -107,10 +107,15 @@ public class SSATransform extends AbstractVisitor { } int size = block.getPredecessors().size(); if (mth.getEnterBlock() == block) { - for (RegisterArg arg : mth.getArguments(true)) { - if (arg.getRegNum() == regNum) { - size++; - break; + RegisterArg thisArg = mth.getThisArg(); + if (thisArg != null && thisArg.getRegNum() == regNum) { + size++; + } else { + for (RegisterArg arg : mth.getArgRegs()) { + if (arg.getRegNum() == regNum) { + size++; + break; + } } } } diff --git a/jadx-core/src/main/java/jadx/core/utils/CodeGenUtils.java b/jadx-core/src/main/java/jadx/core/utils/CodeGenUtils.java index a16501079..688aa0613 100644 --- a/jadx-core/src/main/java/jadx/core/utils/CodeGenUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/CodeGenUtils.java @@ -7,6 +7,7 @@ import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.AttrNode; import jadx.core.dex.attributes.nodes.RenameReasonAttr; import jadx.core.dex.attributes.nodes.SourceFileAttr; +import jadx.core.dex.nodes.ClassNode; public class CodeGenUtils { @@ -28,7 +29,7 @@ public class CodeGenUtils { code.add(" */"); } - public static void addSourceFileInfo(CodeWriter code, AttrNode node) { + public static void addSourceFileInfo(CodeWriter code, ClassNode node) { SourceFileAttr sourceFileAttr = node.get(AType.SOURCE_FILE); if (sourceFileAttr != null) { code.startLine("/* compiled from: ").add(sourceFileAttr.getFileName()).add(" */"); diff --git a/jadx-core/src/main/java/jadx/core/utils/Utils.java b/jadx-core/src/main/java/jadx/core/utils/Utils.java index 79a2c3f0f..a5494977c 100644 --- a/jadx-core/src/main/java/jadx/core/utils/Utils.java +++ b/jadx-core/src/main/java/jadx/core/utils/Utils.java @@ -165,6 +165,20 @@ public class Utils { return result; } + public static int indexInList(List list, T element) { + if (list == null || list.isEmpty()) { + return -1; + } + int size = list.size(); + for (int i = 0; i < size; i++) { + T t = list.get(i); + if (t == element) { + return i; + } + } + return -1; + } + public static List lockList(List list) { if (list.isEmpty()) { return Collections.emptyList(); diff --git a/jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java b/jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java index 3eb9c83aa..9fda49316 100644 --- a/jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java +++ b/jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java @@ -163,7 +163,7 @@ public abstract class IntegrationTest extends TestUtils { protected void decompileAndCheck(JadxDecompiler d, List clsList) { if (unloadCls) { - clsList.forEach(cls -> cls.decompile()); + clsList.forEach(ClassNode::decompile); } else { clsList.forEach(cls -> decompileWithoutUnload(d, cls)); } @@ -221,7 +221,8 @@ public abstract class IntegrationTest extends TestUtils { protected void generateClsCode(ClassNode cls) { try { - cls.setCode(CodeGen.generate(cls)); + ICodeInfo code = CodeGen.generate(cls); + cls.root().getCodeCache().add(cls.getTopParentClass().getRawName(), code); } catch (Exception e) { e.printStackTrace(); fail(e.getMessage());