refactor: enable class unloading after code generation
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<CodePosition, Object> 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
|
||||
|
||||
@@ -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<ArgType> getArguments() {
|
||||
if (mth.getMethodInfo().getArgumentsTypes().isEmpty()) {
|
||||
List<ArgType> infoArgTypes = mth.getMethodInfo().getArgumentsTypes();
|
||||
if (infoArgTypes.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<RegisterArg> arguments = mth.getArguments(false);
|
||||
Stream<ArgType> argTypeStream;
|
||||
if (arguments == null || arguments.isEmpty() || mth.isNoCode()) {
|
||||
argTypeStream = mth.getMethodInfo().getArgumentsTypes().stream();
|
||||
} else {
|
||||
argTypeStream = arguments.stream().map(RegisterArg::getType);
|
||||
List<ArgType> 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() {
|
||||
|
||||
@@ -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<String, ICodeInfo> 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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<NMethod> methods, MethodNode mth, AccessInfo accessFlags) {
|
||||
List<RegisterArg> args = mth.getArguments(false);
|
||||
List<ArgType> 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;
|
||||
|
||||
@@ -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<RegisterArg> args = callMth.getArguments(false);
|
||||
List<RegisterArg> 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<RegisterArg> arguments = callMth.getArguments(false);
|
||||
if (arguments == null || arguments.isEmpty()) {
|
||||
List<ArgType> 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<RegisterArg> 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();
|
||||
|
||||
@@ -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<RegisterArg> args = mth.getArguments(false);
|
||||
List<RegisterArg> 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 {
|
||||
|
||||
@@ -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<T extends IAttribute> {
|
||||
|
||||
public static final AType<AttrList<JumpInfo>> JUMP = new AType<>();
|
||||
public static final AType<AttrList<LoopInfo>> LOOP = new AType<>();
|
||||
public static final AType<AttrList<EdgeInsnAttr>> EDGE_INSN = new AType<>();
|
||||
// class, method, field
|
||||
public static final AType<AnnotationsList> ANNOTATION_LIST = new AType<>();
|
||||
public static final AType<RenameReasonAttr> RENAME_REASON = new AType<>();
|
||||
|
||||
// class, method
|
||||
public static final AType<AttrList<JadxError>> JADX_ERROR = new AType<>(); // code failed to decompile completely
|
||||
public static final AType<AttrList<String>> JADX_WARN = new AType<>(); // mark code as inconsistent (code can be viewed)
|
||||
public static final AType<AttrList<String>> COMMENTS = new AType<>(); // any additional info about decompilation
|
||||
|
||||
public static final AType<ExcHandlerAttr> EXC_HANDLER = new AType<>();
|
||||
public static final AType<CatchAttr> CATCH_BLOCK = new AType<>();
|
||||
public static final AType<SplitterBlockAttr> SPLITTER_BLOCK = new AType<>();
|
||||
public static final AType<ForceReturnAttr> FORCE_RETURN = new AType<>();
|
||||
public static final AType<FieldInitAttr> FIELD_INIT = new AType<>();
|
||||
public static final AType<FieldReplaceAttr> FIELD_REPLACE = new AType<>();
|
||||
public static final AType<MethodInlineAttr> METHOD_INLINE = new AType<>();
|
||||
// class
|
||||
public static final AType<SourceFileAttr> SOURCE_FILE = new AType<>();
|
||||
public static final AType<EnumClassAttr> ENUM_CLASS = new AType<>();
|
||||
public static final AType<EnumMapAttr> ENUM_MAP = new AType<>();
|
||||
public static final AType<AnnotationsList> ANNOTATION_LIST = new AType<>();
|
||||
public static final AType<MethodParameters> ANNOTATION_MTH_PARAMETERS = new AType<>();
|
||||
public static final AType<PhiListAttr> PHI_LIST = new AType<>();
|
||||
public static final AType<SourceFileAttr> SOURCE_FILE = new AType<>();
|
||||
public static final AType<DeclareVariablesAttr> DECLARE_VARIABLES = new AType<>();
|
||||
public static final AType<LoopLabelAttr> LOOP_LABEL = new AType<>();
|
||||
public static final AType<IgnoreEdgeAttr> IGNORE_EDGE = new AType<>();
|
||||
public static final AType<RenameReasonAttr> RENAME_REASON = new AType<>();
|
||||
|
||||
// field
|
||||
public static final AType<FieldInitAttr> FIELD_INIT = new AType<>();
|
||||
public static final AType<FieldReplaceAttr> FIELD_REPLACE = new AType<>();
|
||||
|
||||
// method
|
||||
public static final AType<LocalVarsDebugInfoAttr> LOCAL_VARS_DEBUG_INFO = new AType<>();
|
||||
public static final AType<MethodInlineAttr> METHOD_INLINE = new AType<>();
|
||||
public static final AType<MethodParameters> ANNOTATION_MTH_PARAMETERS = new AType<>();
|
||||
public static final AType<SkipMethodArgsAttr> SKIP_MTH_ARGS = new AType<>();
|
||||
|
||||
// registers
|
||||
// region
|
||||
public static final AType<DeclareVariablesAttr> DECLARE_VARIABLES = new AType<>();
|
||||
|
||||
// block
|
||||
public static final AType<PhiListAttr> PHI_LIST = new AType<>();
|
||||
public static final AType<IgnoreEdgeAttr> IGNORE_EDGE = new AType<>();
|
||||
public static final AType<ForceReturnAttr> FORCE_RETURN = new AType<>();
|
||||
public static final AType<CatchAttr> CATCH_BLOCK = new AType<>();
|
||||
public static final AType<SplitterBlockAttr> SPLITTER_BLOCK = new AType<>();
|
||||
public static final AType<AttrList<LoopInfo>> LOOP = new AType<>();
|
||||
public static final AType<AttrList<EdgeInsnAttr>> EDGE_INSN = new AType<>();
|
||||
|
||||
// block or insn
|
||||
public static final AType<ExcHandlerAttr> EXC_HANDLER = new AType<>();
|
||||
|
||||
// instruction
|
||||
public static final AType<LoopLabelAttr> LOOP_LABEL = new AType<>();
|
||||
public static final AType<AttrList<JumpInfo>> JUMP = new AType<>();
|
||||
|
||||
// register
|
||||
public static final AType<RegDebugInfoAttr> REG_DEBUG_INFO = new AType<>();
|
||||
|
||||
public static final Set<AType<?>> SKIP_ON_UNLOAD = new HashSet<>(Arrays.asList(
|
||||
FIELD_REPLACE,
|
||||
METHOD_INLINE,
|
||||
SKIP_MTH_ARGS));
|
||||
}
|
||||
|
||||
@@ -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<String> getAttributesStringsList() {
|
||||
return storage.getAttributeStrings();
|
||||
|
||||
@@ -20,11 +20,11 @@ import jadx.core.utils.Utils;
|
||||
public class AttributeStorage {
|
||||
|
||||
private final Set<AFlag> flags;
|
||||
private final Map<AType<?>, IAttribute> attributes;
|
||||
private Map<AType<?>, 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 <T> void add(AType<AttrList<T>> 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 <T extends IAttribute> void remove(AType<T> type) {
|
||||
attributes.remove(type);
|
||||
}
|
||||
|
||||
public void remove(IAttribute attr) {
|
||||
AType<? extends IAttribute> 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<? extends IAttribute> type = attr.getType();
|
||||
IAttribute a = attributes.get(type);
|
||||
if (a == attr) {
|
||||
attributes.remove(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<AType<?>, 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<AType<?>> skipOnUnload = AType.SKIP_ON_UNLOAD;
|
||||
attributes.keySet().removeIf(attrType -> !skipOnUnload.contains(attrType));
|
||||
}
|
||||
|
||||
public List<String> getAttributeStrings() {
|
||||
|
||||
@@ -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<RegisterArg> 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<MethodInlineAttr> getType() {
|
||||
return AType.METHOD_INLINE;
|
||||
|
||||
@@ -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<SkipMethodArgsAttr> getType() {
|
||||
return AType.SKIP_MTH_ARGS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SKIP_MTH_ARGS: " + skipArgs;
|
||||
}
|
||||
}
|
||||
@@ -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<MethodNode> methods;
|
||||
private final List<FieldNode> fields;
|
||||
private List<ClassNode> innerClasses = new ArrayList<>();
|
||||
private List<ClassNode> 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;
|
||||
}
|
||||
|
||||
@@ -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<RegisterArg> argsList;
|
||||
private List<SSAVar> sVars;
|
||||
private List<ArgType> argTypes;
|
||||
private List<GenericInfo> generics;
|
||||
|
||||
// decompilation data, reset on unload
|
||||
private RegisterArg thisArg;
|
||||
private List<RegisterArg> argsList;
|
||||
private InsnNode[] instructions;
|
||||
private List<BlockNode> blocks;
|
||||
private BlockNode enterBlock;
|
||||
private List<BlockNode> exitBlocks;
|
||||
|
||||
private Region region;
|
||||
private List<SSAVar> sVars;
|
||||
private List<ExceptionHandler> exceptionHandlers;
|
||||
private List<LoopInfo> 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<ArgType> 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<ArgType> parseSignature() {
|
||||
SignatureParser sp = SignatureParser.fromNode(this);
|
||||
if (sp == null) {
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
generics = sp.consumeGenericMap();
|
||||
this.generics = sp.consumeGenericMap();
|
||||
List<ArgType> argsTypes = sp.consumeMethodArgs();
|
||||
retType = sp.consumeType();
|
||||
this.retType = sp.consumeType();
|
||||
|
||||
List<ArgType> 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<RegisterArg> getArguments(boolean includeThis) {
|
||||
if (includeThis && thisArg != null) {
|
||||
List<RegisterArg> 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<ArgType> getArgTypes() {
|
||||
return argTypes;
|
||||
}
|
||||
|
||||
public List<RegisterArg> 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<RegisterArg> getAllArgRegs() {
|
||||
List<RegisterArg> argRegs = getArgRegs();
|
||||
if (thisArg != null) {
|
||||
List<RegisterArg> 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();
|
||||
|
||||
@@ -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<DexNode> 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<InputFile> inputFiles) {
|
||||
@@ -287,4 +291,8 @@ public class RootNode {
|
||||
public TypeUpdate getTypeUpdate() {
|
||||
return typeUpdate;
|
||||
}
|
||||
|
||||
public ICodeCache getCodeCache() {
|
||||
return codeCache;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<RegisterArg> args = mth.getArguments(false);
|
||||
List<RegisterArg> 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<RegisterArg> args = mth.getArguments(false);
|
||||
List<RegisterArg> 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<BlockNode> bb = mth.getBasicBlocks();
|
||||
if (bb == null || bb.isEmpty() || BlockUtils.isAllBlocksEmpty(bb)) {
|
||||
if (af.isStatic() && mth.getMethodInfo().isClassInit()) {
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,7 +277,7 @@ public class ModVisitor extends AbstractVisitor {
|
||||
}
|
||||
classNode.loadAndProcess();
|
||||
Map<InsnArg, FieldNode> 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<RegisterArg> argList = callMthNode.getArguments(false);
|
||||
List<RegisterArg> argList = callMthNode.getArgRegs();
|
||||
int startArg = 0;
|
||||
if (callMth.getArgsCount() != 0 && callMth.getArgumentsTypes().get(0).equals(parentClass.getClassInfo().getType())) {
|
||||
startArg = 1;
|
||||
|
||||
@@ -220,7 +220,7 @@ public class PrepareForCodeGen extends AbstractVisitor {
|
||||
Set<RegisterArg> 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 {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -58,7 +58,7 @@ public class DebugInfoParser {
|
||||
int line = section.readUleb128();
|
||||
|
||||
int paramsCount = section.readUleb128();
|
||||
List<RegisterArg> mthArgs = mth.getArguments(false);
|
||||
List<RegisterArg> mthArgs = mth.getArgRegs();
|
||||
|
||||
for (int i = 0; i < paramsCount; i++) {
|
||||
int nameId = section.readUleb128() - 1;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(" */");
|
||||
|
||||
@@ -165,6 +165,20 @@ public class Utils {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static <T> int indexInList(List<T> 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 <T> List<T> lockList(List<T> list) {
|
||||
if (list.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
|
||||
@@ -163,7 +163,7 @@ public abstract class IntegrationTest extends TestUtils {
|
||||
|
||||
protected void decompileAndCheck(JadxDecompiler d, List<ClassNode> 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());
|
||||
|
||||
Reference in New Issue
Block a user