mirror of
https://github.com/skylot/jadx.git
synced 2024-11-27 06:31:15 +00:00
core: allow to disable constant dereference (#106)
This commit is contained in:
parent
7cba2c3f81
commit
5f302238ad
11
README.md
11
README.md
@ -44,19 +44,20 @@ jadx[-gui] [options] <input file> (.dex, .apk, .jar or .class)
|
|||||||
options:
|
options:
|
||||||
-d, --output-dir - output directory
|
-d, --output-dir - output directory
|
||||||
-j, --threads-count - processing threads count
|
-j, --threads-count - processing threads count
|
||||||
-f, --fallback - make simple dump (using goto instead of 'if', 'for', etc)
|
|
||||||
-r, --no-res - do not decode resources
|
-r, --no-res - do not decode resources
|
||||||
-s, --no-src - do not decompile source code
|
-s, --no-src - do not decompile source code
|
||||||
--show-bad-code - show inconsistent code (incorrectly decompiled)
|
--show-bad-code - show inconsistent code (incorrectly decompiled)
|
||||||
--cfg - save methods control flow graph to dot file
|
--no-replace-consts - don't replace constant value with matching constant field
|
||||||
--raw-cfg - save methods control flow graph (use raw instructions)
|
--escape-unicode - escape non latin characters in strings (with \u)
|
||||||
-v, --verbose - verbose output
|
|
||||||
--deobf - activate deobfuscation
|
--deobf - activate deobfuscation
|
||||||
--deobf-min - min length of name
|
--deobf-min - min length of name
|
||||||
--deobf-max - max length of name
|
--deobf-max - max length of name
|
||||||
--deobf-rewrite-cfg - force to save deobfuscation map
|
--deobf-rewrite-cfg - force to save deobfuscation map
|
||||||
--deobf-use-sourcename - use source file name as class name alias
|
--deobf-use-sourcename - use source file name as class name alias
|
||||||
--escape-unicode - escape non latin characters in strings (with \u)
|
--cfg - save methods control flow graph to dot file
|
||||||
|
--raw-cfg - save methods control flow graph (use raw instructions)
|
||||||
|
-f, --fallback - make simple dump (using goto instead of 'if', 'for', etc)
|
||||||
|
-v, --verbose - verbose output
|
||||||
-h, --help - print this help
|
-h, --help - print this help
|
||||||
Example:
|
Example:
|
||||||
jadx -d out classes.dex
|
jadx -d out classes.dex
|
||||||
|
@ -17,6 +17,7 @@ import java.util.Map;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.beust.jcommander.IStringConverter;
|
||||||
import com.beust.jcommander.JCommander;
|
import com.beust.jcommander.JCommander;
|
||||||
import com.beust.jcommander.Parameter;
|
import com.beust.jcommander.Parameter;
|
||||||
import com.beust.jcommander.ParameterDescription;
|
import com.beust.jcommander.ParameterDescription;
|
||||||
@ -33,9 +34,6 @@ public class JadxCLIArgs implements IJadxArgs {
|
|||||||
@Parameter(names = {"-j", "--threads-count"}, description = "processing threads count")
|
@Parameter(names = {"-j", "--threads-count"}, description = "processing threads count")
|
||||||
protected int threadsCount = Math.max(1, Runtime.getRuntime().availableProcessors() / 2);
|
protected int threadsCount = Math.max(1, Runtime.getRuntime().availableProcessors() / 2);
|
||||||
|
|
||||||
@Parameter(names = {"-f", "--fallback"}, description = "make simple dump (using goto instead of 'if', 'for', etc)")
|
|
||||||
protected boolean fallbackMode = false;
|
|
||||||
|
|
||||||
@Parameter(names = {"-r", "--no-res"}, description = "do not decode resources")
|
@Parameter(names = {"-r", "--no-res"}, description = "do not decode resources")
|
||||||
protected boolean skipResources = false;
|
protected boolean skipResources = false;
|
||||||
|
|
||||||
@ -45,14 +43,12 @@ public class JadxCLIArgs implements IJadxArgs {
|
|||||||
@Parameter(names = {"--show-bad-code"}, description = "show inconsistent code (incorrectly decompiled)")
|
@Parameter(names = {"--show-bad-code"}, description = "show inconsistent code (incorrectly decompiled)")
|
||||||
protected boolean showInconsistentCode = false;
|
protected boolean showInconsistentCode = false;
|
||||||
|
|
||||||
@Parameter(names = {"--cfg"}, description = "save methods control flow graph to dot file")
|
@Parameter(names = "--no-replace-consts", converter = InvertedBooleanConverter.class,
|
||||||
protected boolean cfgOutput = false;
|
description = "don't replace constant value with matching constant field")
|
||||||
|
protected boolean replaceConsts = true;
|
||||||
|
|
||||||
@Parameter(names = {"--raw-cfg"}, description = "save methods control flow graph (use raw instructions)")
|
@Parameter(names = {"--escape-unicode"}, description = "escape non latin characters in strings (with \\u)")
|
||||||
protected boolean rawCfgOutput = false;
|
protected boolean escapeUnicode = false;
|
||||||
|
|
||||||
@Parameter(names = {"-v", "--verbose"}, description = "verbose output")
|
|
||||||
protected boolean verbose = false;
|
|
||||||
|
|
||||||
@Parameter(names = {"--deobf"}, description = "activate deobfuscation")
|
@Parameter(names = {"--deobf"}, description = "activate deobfuscation")
|
||||||
protected boolean deobfuscationOn = false;
|
protected boolean deobfuscationOn = false;
|
||||||
@ -69,8 +65,17 @@ public class JadxCLIArgs implements IJadxArgs {
|
|||||||
@Parameter(names = {"--deobf-use-sourcename"}, description = "use source file name as class name alias")
|
@Parameter(names = {"--deobf-use-sourcename"}, description = "use source file name as class name alias")
|
||||||
protected boolean deobfuscationUseSourceNameAsAlias = false;
|
protected boolean deobfuscationUseSourceNameAsAlias = false;
|
||||||
|
|
||||||
@Parameter(names = {"--escape-unicode"}, description = "escape non latin characters in strings (with \\u)")
|
@Parameter(names = {"--cfg"}, description = "save methods control flow graph to dot file")
|
||||||
protected boolean escapeUnicode = false;
|
protected boolean cfgOutput = false;
|
||||||
|
|
||||||
|
@Parameter(names = {"--raw-cfg"}, description = "save methods control flow graph (use raw instructions)")
|
||||||
|
protected boolean rawCfgOutput = false;
|
||||||
|
|
||||||
|
@Parameter(names = {"-f", "--fallback"}, description = "make simple dump (using goto instead of 'if', 'for', etc)")
|
||||||
|
protected boolean fallbackMode = false;
|
||||||
|
|
||||||
|
@Parameter(names = {"-v", "--verbose"}, description = "verbose output")
|
||||||
|
protected boolean verbose = false;
|
||||||
|
|
||||||
@Parameter(names = {"-h", "--help"}, description = "print this help", help = true)
|
@Parameter(names = {"-h", "--help"}, description = "print this help", help = true)
|
||||||
protected boolean printHelp = false;
|
protected boolean printHelp = false;
|
||||||
@ -178,6 +183,13 @@ public class JadxCLIArgs implements IJadxArgs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class InvertedBooleanConverter implements IStringConverter<Boolean> {
|
||||||
|
@Override
|
||||||
|
public Boolean convert(String value) {
|
||||||
|
return "false".equals(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public List<File> getInput() {
|
public List<File> getInput() {
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
@ -264,4 +276,9 @@ public class JadxCLIArgs implements IJadxArgs {
|
|||||||
public boolean escapeUnicode() {
|
public boolean escapeUnicode() {
|
||||||
return escapeUnicode;
|
return escapeUnicode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isReplaceConsts() {
|
||||||
|
return replaceConsts;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
22
jadx-cli/src/test/java/jadx/cli/JadxCLIArgsTest.java
Normal file
22
jadx-cli/src/test/java/jadx/cli/JadxCLIArgsTest.java
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package jadx.cli;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
public class JadxCLIArgsTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvertedBooleanOption() throws Exception {
|
||||||
|
assertThat(parse("--no-replace-consts").isReplaceConsts(), is(false));
|
||||||
|
assertThat(parse("").isReplaceConsts(), is(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
private JadxCLIArgs parse(String... args) {
|
||||||
|
JadxCLIArgs jadxArgs = new JadxCLIArgs();
|
||||||
|
boolean res = jadxArgs.processArgs(args);
|
||||||
|
assertThat(res, is(true));
|
||||||
|
return jadxArgs;
|
||||||
|
}
|
||||||
|
}
|
@ -32,4 +32,9 @@ public interface IJadxArgs {
|
|||||||
boolean useSourceNameAsClassAlias();
|
boolean useSourceNameAsClassAlias();
|
||||||
|
|
||||||
boolean escapeUnicode();
|
boolean escapeUnicode();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace constant values with static final fields with same value
|
||||||
|
*/
|
||||||
|
boolean isReplaceConsts();
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ public class JadxArgs implements IJadxArgs {
|
|||||||
private int deobfuscationMaxLength = Integer.MAX_VALUE;
|
private int deobfuscationMaxLength = Integer.MAX_VALUE;
|
||||||
|
|
||||||
private boolean escapeUnicode = false;
|
private boolean escapeUnicode = false;
|
||||||
|
private boolean replaceConsts = true;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public File getOutDir() {
|
public File getOutDir() {
|
||||||
@ -160,4 +161,13 @@ public class JadxArgs implements IJadxArgs {
|
|||||||
public void setEscapeUnicode(boolean escapeUnicode) {
|
public void setEscapeUnicode(boolean escapeUnicode) {
|
||||||
this.escapeUnicode = escapeUnicode;
|
this.escapeUnicode = escapeUnicode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isReplaceConsts() {
|
||||||
|
return replaceConsts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReplaceConsts(boolean replaceConsts) {
|
||||||
|
this.replaceConsts = replaceConsts;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -167,7 +167,7 @@ public final class ClassInfo {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return fullName.hashCode();
|
return type.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
187
jadx-core/src/main/java/jadx/core/dex/info/ConstStorage.java
Normal file
187
jadx-core/src/main/java/jadx/core/dex/info/ConstStorage.java
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
package jadx.core.dex.info;
|
||||||
|
|
||||||
|
import jadx.api.IJadxArgs;
|
||||||
|
import jadx.core.dex.attributes.AType;
|
||||||
|
import jadx.core.dex.instructions.args.LiteralArg;
|
||||||
|
import jadx.core.dex.instructions.args.PrimitiveType;
|
||||||
|
import jadx.core.dex.nodes.ClassNode;
|
||||||
|
import jadx.core.dex.nodes.DexNode;
|
||||||
|
import jadx.core.dex.nodes.FieldNode;
|
||||||
|
import jadx.core.dex.nodes.ResRefField;
|
||||||
|
import jadx.core.dex.nodes.parser.FieldInitAttr;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public class ConstStorage {
|
||||||
|
|
||||||
|
private static final class Values {
|
||||||
|
private final Map<Object, FieldNode> values = new HashMap<Object, FieldNode>();
|
||||||
|
private final Set<Object> duplicates = new HashSet<Object>();
|
||||||
|
|
||||||
|
public Map<Object, FieldNode> getValues() {
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FieldNode get(Object key) {
|
||||||
|
return values.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if this value is duplicated
|
||||||
|
*/
|
||||||
|
public boolean put(Object value, FieldNode fld) {
|
||||||
|
FieldNode prev = values.put(value, fld);
|
||||||
|
if (prev != null) {
|
||||||
|
values.remove(value);
|
||||||
|
duplicates.add(value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (duplicates.contains(value)) {
|
||||||
|
values.remove(value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(Object value) {
|
||||||
|
return duplicates.contains(value) || values.containsKey(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final boolean replaceEnabled;
|
||||||
|
private final Values globalValues = new Values();
|
||||||
|
private final Map<ClassNode, Values> classes = new HashMap<ClassNode, Values>();
|
||||||
|
|
||||||
|
private Map<Integer, String> resourcesNames = new HashMap<Integer, String>();
|
||||||
|
|
||||||
|
public ConstStorage(IJadxArgs args) {
|
||||||
|
this.replaceEnabled = args.isReplaceConsts();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void processConstFields(ClassNode cls, List<FieldNode> staticFields) {
|
||||||
|
if (!replaceEnabled || staticFields.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (FieldNode f : staticFields) {
|
||||||
|
AccessInfo accFlags = f.getAccessFlags();
|
||||||
|
if (accFlags.isStatic() && accFlags.isFinal()) {
|
||||||
|
FieldInitAttr fv = f.get(AType.FIELD_INIT);
|
||||||
|
if (fv != null
|
||||||
|
&& fv.getValue() != null
|
||||||
|
&& fv.getValueType() == FieldInitAttr.InitType.CONST
|
||||||
|
&& fv != FieldInitAttr.NULL_VALUE) {
|
||||||
|
addConstField(cls, f, fv.getValue(), accFlags.isPublic());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addConstField(ClassNode cls, FieldNode fld, Object value, boolean isPublic) {
|
||||||
|
if (isPublic) {
|
||||||
|
globalValues.put(value, fld);
|
||||||
|
} else {
|
||||||
|
getClsValues(cls).put(value, fld);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Values getClsValues(ClassNode cls) {
|
||||||
|
Values classValues = classes.get(cls);
|
||||||
|
if (classValues == null) {
|
||||||
|
classValues = new Values();
|
||||||
|
classes.put(cls, classValues);
|
||||||
|
}
|
||||||
|
return classValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public FieldNode getConstField(ClassNode cls, Object value, boolean searchGlobal) {
|
||||||
|
DexNode dex = cls.dex();
|
||||||
|
if (value instanceof Integer) {
|
||||||
|
String str = resourcesNames.get(value);
|
||||||
|
if (str != null) {
|
||||||
|
return new ResRefField(dex, str.replace('/', '.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!replaceEnabled) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
boolean foundInGlobal = globalValues.contains(value);
|
||||||
|
if (foundInGlobal && !searchGlobal) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ClassNode current = cls;
|
||||||
|
while (current != null) {
|
||||||
|
Values classValues = classes.get(current);
|
||||||
|
if (classValues != null) {
|
||||||
|
FieldNode field = classValues.get(value);
|
||||||
|
if (field != null) {
|
||||||
|
if (foundInGlobal) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClassInfo parentClass = current.getClassInfo().getParentClass();
|
||||||
|
if (parentClass == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
current = dex.resolveClass(parentClass);
|
||||||
|
}
|
||||||
|
if (searchGlobal) {
|
||||||
|
return globalValues.get(value);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public FieldNode getConstFieldByLiteralArg(ClassNode cls, LiteralArg arg) {
|
||||||
|
PrimitiveType type = arg.getType().getPrimitiveType();
|
||||||
|
if (type == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
long literal = arg.getLiteral();
|
||||||
|
switch (type) {
|
||||||
|
case BOOLEAN:
|
||||||
|
return getConstField(cls, literal == 1, false);
|
||||||
|
case CHAR:
|
||||||
|
return getConstField(cls, (char) literal, Math.abs(literal) > 10);
|
||||||
|
case BYTE:
|
||||||
|
return getConstField(cls, (byte) literal, Math.abs(literal) > 10);
|
||||||
|
case SHORT:
|
||||||
|
return getConstField(cls, (short) literal, Math.abs(literal) > 100);
|
||||||
|
case INT:
|
||||||
|
return getConstField(cls, (int) literal, Math.abs(literal) > 100);
|
||||||
|
case LONG:
|
||||||
|
return getConstField(cls, literal, Math.abs(literal) > 1000);
|
||||||
|
case FLOAT:
|
||||||
|
float f = Float.intBitsToFloat((int) literal);
|
||||||
|
return getConstField(cls, f, f != 0.0);
|
||||||
|
case DOUBLE:
|
||||||
|
double d = Double.longBitsToDouble(literal);
|
||||||
|
return getConstField(cls, d, d != 0);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResourcesNames(Map<Integer, String> resourcesNames) {
|
||||||
|
this.resourcesNames = resourcesNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Integer, String> getResourcesNames() {
|
||||||
|
return resourcesNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Object, FieldNode> getGlobalConstFields() {
|
||||||
|
return globalValues.getValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isReplaceEnabled() {
|
||||||
|
return replaceEnabled;
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,6 @@ package jadx.core.dex.nodes;
|
|||||||
|
|
||||||
import jadx.core.Consts;
|
import jadx.core.Consts;
|
||||||
import jadx.core.codegen.CodeWriter;
|
import jadx.core.codegen.CodeWriter;
|
||||||
import jadx.core.dex.attributes.AType;
|
|
||||||
import jadx.core.dex.attributes.annotations.Annotation;
|
import jadx.core.dex.attributes.annotations.Annotation;
|
||||||
import jadx.core.dex.attributes.nodes.JadxErrorAttr;
|
import jadx.core.dex.attributes.nodes.JadxErrorAttr;
|
||||||
import jadx.core.dex.attributes.nodes.LineAttrNode;
|
import jadx.core.dex.attributes.nodes.LineAttrNode;
|
||||||
@ -14,10 +13,8 @@ import jadx.core.dex.info.FieldInfo;
|
|||||||
import jadx.core.dex.info.MethodInfo;
|
import jadx.core.dex.info.MethodInfo;
|
||||||
import jadx.core.dex.instructions.args.ArgType;
|
import jadx.core.dex.instructions.args.ArgType;
|
||||||
import jadx.core.dex.instructions.args.LiteralArg;
|
import jadx.core.dex.instructions.args.LiteralArg;
|
||||||
import jadx.core.dex.instructions.args.PrimitiveType;
|
|
||||||
import jadx.core.dex.nodes.parser.AnnotationsParser;
|
import jadx.core.dex.nodes.parser.AnnotationsParser;
|
||||||
import jadx.core.dex.nodes.parser.FieldInitAttr;
|
import jadx.core.dex.nodes.parser.FieldInitAttr;
|
||||||
import jadx.core.dex.nodes.parser.FieldInitAttr.InitType;
|
|
||||||
import jadx.core.dex.nodes.parser.SignatureParser;
|
import jadx.core.dex.nodes.parser.SignatureParser;
|
||||||
import jadx.core.dex.nodes.parser.StaticValuesParser;
|
import jadx.core.dex.nodes.parser.StaticValuesParser;
|
||||||
import jadx.core.utils.exceptions.DecodeException;
|
import jadx.core.utils.exceptions.DecodeException;
|
||||||
@ -27,7 +24,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -41,6 +37,7 @@ import com.android.dex.ClassData;
|
|||||||
import com.android.dex.ClassData.Field;
|
import com.android.dex.ClassData.Field;
|
||||||
import com.android.dex.ClassData.Method;
|
import com.android.dex.ClassData.Method;
|
||||||
import com.android.dex.ClassDef;
|
import com.android.dex.ClassDef;
|
||||||
|
import com.android.dex.Dex;
|
||||||
import com.android.dx.rop.code.AccessFlags;
|
import com.android.dx.rop.code.AccessFlags;
|
||||||
|
|
||||||
public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
|
public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||||
@ -55,7 +52,6 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
|
|||||||
|
|
||||||
private final List<MethodNode> methods;
|
private final List<MethodNode> methods;
|
||||||
private final List<FieldNode> fields;
|
private final List<FieldNode> fields;
|
||||||
private Map<Object, FieldNode> constFields = Collections.emptyMap();
|
|
||||||
private List<ClassNode> innerClasses = Collections.emptyList();
|
private List<ClassNode> innerClasses = Collections.emptyList();
|
||||||
|
|
||||||
// store decompiled code
|
// store decompiled code
|
||||||
@ -168,24 +164,12 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
|
|||||||
if (offset == 0) {
|
if (offset == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
StaticValuesParser parser = new StaticValuesParser(dex, dex.openSection(offset));
|
Dex.Section section = dex.openSection(offset);
|
||||||
int count = parser.processFields(staticFields);
|
StaticValuesParser parser = new StaticValuesParser(dex, section);
|
||||||
if (count == 0) {
|
parser.processFields(staticFields);
|
||||||
return;
|
|
||||||
}
|
// process const fields
|
||||||
constFields = new LinkedHashMap<Object, FieldNode>(count);
|
root().getConstValues().processConstFields(this, staticFields);
|
||||||
for (FieldNode f : staticFields) {
|
|
||||||
AccessInfo accFlags = f.getAccessFlags();
|
|
||||||
if (accFlags.isStatic() && accFlags.isFinal()) {
|
|
||||||
FieldInitAttr fv = f.get(AType.FIELD_INIT);
|
|
||||||
if (fv != null && fv.getValue() != null && fv.getValueType() == InitType.CONST) {
|
|
||||||
if (accFlags.isPublic()) {
|
|
||||||
dex.getConstFields().put(fv.getValue(), f);
|
|
||||||
}
|
|
||||||
constFields.put(fv.getValue(), f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parseClassSignature() {
|
private void parseClassSignature() {
|
||||||
@ -315,61 +299,14 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
|
|||||||
return getConstField(obj, true);
|
return getConstField(obj, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public FieldNode getConstField(Object obj, boolean searchGlobal) {
|
public FieldNode getConstField(Object obj, boolean searchGlobal) {
|
||||||
ClassNode cn = this;
|
return root().getConstValues().getConstField(this, obj, searchGlobal);
|
||||||
FieldNode field;
|
|
||||||
do {
|
|
||||||
field = cn.constFields.get(obj);
|
|
||||||
}
|
|
||||||
while (field == null
|
|
||||||
&& cn.clsInfo.getParentClass() != null
|
|
||||||
&& (cn = dex.resolveClass(cn.clsInfo.getParentClass())) != null);
|
|
||||||
|
|
||||||
if (field == null && searchGlobal) {
|
|
||||||
field = dex.getConstFields().get(obj);
|
|
||||||
}
|
|
||||||
if (obj instanceof Integer) {
|
|
||||||
String str = dex.root().getResourcesNames().get(obj);
|
|
||||||
if (str != null) {
|
|
||||||
ResRefField resField = new ResRefField(dex, str.replace('/', '.'));
|
|
||||||
if (field == null) {
|
|
||||||
return resField;
|
|
||||||
}
|
|
||||||
if (!field.getName().equals(resField.getName())) {
|
|
||||||
field = resField;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return field;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public FieldNode getConstFieldByLiteralArg(LiteralArg arg) {
|
public FieldNode getConstFieldByLiteralArg(LiteralArg arg) {
|
||||||
PrimitiveType type = arg.getType().getPrimitiveType();
|
return root().getConstValues().getConstFieldByLiteralArg(this, arg);
|
||||||
if (type == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
long literal = arg.getLiteral();
|
|
||||||
switch (type) {
|
|
||||||
case BOOLEAN:
|
|
||||||
return getConstField(literal == 1, false);
|
|
||||||
case CHAR:
|
|
||||||
return getConstField((char) literal, Math.abs(literal) > 10);
|
|
||||||
case BYTE:
|
|
||||||
return getConstField((byte) literal, Math.abs(literal) > 10);
|
|
||||||
case SHORT:
|
|
||||||
return getConstField((short) literal, Math.abs(literal) > 100);
|
|
||||||
case INT:
|
|
||||||
return getConstField((int) literal, Math.abs(literal) > 100);
|
|
||||||
case LONG:
|
|
||||||
return getConstField(literal, Math.abs(literal) > 1000);
|
|
||||||
case FLOAT:
|
|
||||||
float f = Float.intBitsToFloat((int) literal);
|
|
||||||
return getConstField(f, f != 0.0);
|
|
||||||
case DOUBLE:
|
|
||||||
double d = Double.longBitsToDouble(literal);
|
|
||||||
return getConstField(d, d != 0);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public FieldNode searchFieldById(int id) {
|
public FieldNode searchFieldById(int id) {
|
||||||
@ -532,6 +469,24 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
|
|||||||
return dependencies;
|
return dependencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return clsInfo.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o instanceof ClassNode) {
|
||||||
|
ClassNode other = (ClassNode) o;
|
||||||
|
return clsInfo.equals(other.clsInfo);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return clsInfo.getFullName();
|
return clsInfo.getFullName();
|
||||||
|
@ -39,8 +39,6 @@ public class DexNode implements IDexNode {
|
|||||||
private final List<ClassNode> classes = new ArrayList<ClassNode>();
|
private final List<ClassNode> classes = new ArrayList<ClassNode>();
|
||||||
private final Map<ClassInfo, ClassNode> clsMap = new HashMap<ClassInfo, ClassNode>();
|
private final Map<ClassInfo, ClassNode> clsMap = new HashMap<ClassInfo, ClassNode>();
|
||||||
|
|
||||||
private final Map<Object, FieldNode> constFields = new HashMap<Object, FieldNode>();
|
|
||||||
|
|
||||||
private final InfoStorage infoStorage = new InfoStorage();
|
private final InfoStorage infoStorage = new InfoStorage();
|
||||||
|
|
||||||
public DexNode(RootNode root, DexFile input) {
|
public DexNode(RootNode root, DexFile input) {
|
||||||
@ -155,10 +153,6 @@ public class DexNode implements IDexNode {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<Object, FieldNode> getConstFields() {
|
|
||||||
return constFields;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InfoStorage getInfoStorage() {
|
public InfoStorage getInfoStorage() {
|
||||||
return infoStorage;
|
return infoStorage;
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import jadx.api.ResourceType;
|
|||||||
import jadx.api.ResourcesLoader;
|
import jadx.api.ResourcesLoader;
|
||||||
import jadx.core.clsp.ClspGraph;
|
import jadx.core.clsp.ClspGraph;
|
||||||
import jadx.core.dex.info.ClassInfo;
|
import jadx.core.dex.info.ClassInfo;
|
||||||
|
import jadx.core.dex.info.ConstStorage;
|
||||||
import jadx.core.utils.ErrorsCounter;
|
import jadx.core.utils.ErrorsCounter;
|
||||||
import jadx.core.utils.StringUtils;
|
import jadx.core.utils.StringUtils;
|
||||||
import jadx.core.utils.exceptions.DecodeException;
|
import jadx.core.utils.exceptions.DecodeException;
|
||||||
@ -19,9 +20,7 @@ import jadx.core.xmlgen.ResourceStorage;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -33,9 +32,9 @@ public class RootNode {
|
|||||||
private final ErrorsCounter errorsCounter = new ErrorsCounter();
|
private final ErrorsCounter errorsCounter = new ErrorsCounter();
|
||||||
private final IJadxArgs args;
|
private final IJadxArgs args;
|
||||||
private final StringUtils stringUtils;
|
private final StringUtils stringUtils;
|
||||||
|
private final ConstStorage constValues;
|
||||||
|
|
||||||
private List<DexNode> dexNodes;
|
private List<DexNode> dexNodes;
|
||||||
private Map<Integer, String> resourcesNames = new HashMap<Integer, String>();
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private String appPackage;
|
private String appPackage;
|
||||||
private ClassNode appResClass;
|
private ClassNode appResClass;
|
||||||
@ -44,6 +43,7 @@ public class RootNode {
|
|||||||
public RootNode(IJadxArgs args) {
|
public RootNode(IJadxArgs args) {
|
||||||
this.args = args;
|
this.args = args;
|
||||||
this.stringUtils = new StringUtils(args);
|
this.stringUtils = new StringUtils(args);
|
||||||
|
this.constValues = new ConstStorage(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void load(List<InputFile> inputFiles) throws DecodeException {
|
public void load(List<InputFile> inputFiles) throws DecodeException {
|
||||||
@ -92,7 +92,7 @@ public class RootNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ResourceStorage resStorage = parser.getResStorage();
|
ResourceStorage resStorage = parser.getResStorage();
|
||||||
resourcesNames = resStorage.getResourcesNames();
|
constValues.setResourcesNames(resStorage.getResourcesNames());
|
||||||
appPackage = resStorage.getAppPackage();
|
appPackage = resStorage.getAppPackage();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,10 +181,6 @@ public class RootNode {
|
|||||||
return errorsCounter;
|
return errorsCounter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<Integer, String> getResourcesNames() {
|
|
||||||
return resourcesNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public String getAppPackage() {
|
public String getAppPackage() {
|
||||||
return appPackage;
|
return appPackage;
|
||||||
@ -201,4 +197,8 @@ public class RootNode {
|
|||||||
public StringUtils getStringUtils() {
|
public StringUtils getStringUtils() {
|
||||||
return stringUtils;
|
return stringUtils;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ConstStorage getConstValues() {
|
||||||
|
return constValues;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -367,7 +367,8 @@ public class BlockProcessor extends AbstractVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void cleanExitNodes(MethodNode mth) {
|
private static void cleanExitNodes(MethodNode mth) {
|
||||||
for (Iterator<BlockNode> iterator = mth.getExitBlocks().iterator(); iterator.hasNext(); ) {
|
Iterator<BlockNode> iterator = mth.getExitBlocks().iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
BlockNode exitBlock = iterator.next();
|
BlockNode exitBlock = iterator.next();
|
||||||
if (exitBlock.getPredecessors().isEmpty()) {
|
if (exitBlock.getPredecessors().isEmpty()) {
|
||||||
mth.getBasicBlocks().remove(exitBlock);
|
mth.getBasicBlocks().remove(exitBlock);
|
||||||
|
@ -2,8 +2,8 @@ package jadx.core.xmlgen;
|
|||||||
|
|
||||||
import jadx.api.ResourcesLoader;
|
import jadx.api.ResourcesLoader;
|
||||||
import jadx.core.codegen.CodeWriter;
|
import jadx.core.codegen.CodeWriter;
|
||||||
|
import jadx.core.dex.info.ConstStorage;
|
||||||
import jadx.core.dex.instructions.args.ArgType;
|
import jadx.core.dex.instructions.args.ArgType;
|
||||||
import jadx.core.dex.nodes.DexNode;
|
|
||||||
import jadx.core.dex.nodes.FieldNode;
|
import jadx.core.dex.nodes.FieldNode;
|
||||||
import jadx.core.dex.nodes.RootNode;
|
import jadx.core.dex.nodes.RootNode;
|
||||||
import jadx.core.utils.StringUtils;
|
import jadx.core.utils.StringUtils;
|
||||||
@ -64,17 +64,16 @@ public class BinaryXMLParser extends CommonBinaryParser {
|
|||||||
LOG.error("R class loading failed", th);
|
LOG.error("R class loading failed", th);
|
||||||
}
|
}
|
||||||
// add application constants
|
// add application constants
|
||||||
for (DexNode dexNode : root.getDexNodes()) {
|
ConstStorage constStorage = root.getConstValues();
|
||||||
for (Map.Entry<Object, FieldNode> entry : dexNode.getConstFields().entrySet()) {
|
Map<Object, FieldNode> constFields = constStorage.getGlobalConstFields();
|
||||||
Object key = entry.getKey();
|
for (Map.Entry<Object, FieldNode> entry : constFields.entrySet()) {
|
||||||
FieldNode field = entry.getValue();
|
Object key = entry.getKey();
|
||||||
if (field.getType().equals(ArgType.INT) && key instanceof Integer) {
|
FieldNode field = entry.getValue();
|
||||||
localStyleMap.put((Integer) key, field);
|
if (field.getType().equals(ArgType.INT) && key instanceof Integer) {
|
||||||
}
|
localStyleMap.put((Integer) key, field);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
resNames = constStorage.getResourcesNames();
|
||||||
resNames = root.getResourcesNames();
|
|
||||||
|
|
||||||
attributes = new ManifestAttributes();
|
attributes = new ManifestAttributes();
|
||||||
attributes.parseAll();
|
attributes.parseAll();
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package jadx.tests.api;
|
package jadx.tests.api;
|
||||||
|
|
||||||
import jadx.api.IJadxArgs;
|
|
||||||
import jadx.api.JadxArgs;
|
import jadx.api.JadxArgs;
|
||||||
import jadx.api.JadxDecompiler;
|
import jadx.api.JadxDecompiler;
|
||||||
import jadx.api.JadxInternalAccess;
|
import jadx.api.JadxInternalAccess;
|
||||||
@ -51,8 +50,8 @@ public abstract class IntegrationTest extends TestUtils {
|
|||||||
private static final String TEST_DIRECTORY = "src/test/java";
|
private static final String TEST_DIRECTORY = "src/test/java";
|
||||||
private static final String TEST_DIRECTORY2 = "jadx-core/" + TEST_DIRECTORY;
|
private static final String TEST_DIRECTORY2 = "jadx-core/" + TEST_DIRECTORY;
|
||||||
|
|
||||||
protected boolean outputCFG = false;
|
private JadxArgs args;
|
||||||
protected boolean isFallback = false;
|
|
||||||
protected boolean deleteTmpFiles = true;
|
protected boolean deleteTmpFiles = true;
|
||||||
protected boolean withDebugInfo = true;
|
protected boolean withDebugInfo = true;
|
||||||
protected boolean unloadCls = true;
|
protected boolean unloadCls = true;
|
||||||
@ -64,6 +63,13 @@ public abstract class IntegrationTest extends TestUtils {
|
|||||||
protected boolean compile = true;
|
protected boolean compile = true;
|
||||||
private DynamicCompiler dynamicCompiler;
|
private DynamicCompiler dynamicCompiler;
|
||||||
|
|
||||||
|
public IntegrationTest() {
|
||||||
|
args = new JadxArgs();
|
||||||
|
args.setShowInconsistentCode(true);
|
||||||
|
args.setThreadsCount(1);
|
||||||
|
args.setSkipResources(true);
|
||||||
|
}
|
||||||
|
|
||||||
public ClassNode getClassNode(Class<?> clazz) {
|
public ClassNode getClassNode(Class<?> clazz) {
|
||||||
try {
|
try {
|
||||||
File jar = getJarForClass(clazz);
|
File jar = getJarForClass(clazz);
|
||||||
@ -76,7 +82,7 @@ public abstract class IntegrationTest extends TestUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ClassNode getClassNodeFromFile(File file, String clsName) {
|
public ClassNode getClassNodeFromFile(File file, String clsName) {
|
||||||
JadxDecompiler d = new JadxDecompiler(getArgs());
|
JadxDecompiler d = new JadxDecompiler(args);
|
||||||
try {
|
try {
|
||||||
d.loadFile(file);
|
d.loadFile(file);
|
||||||
} catch (JadxException e) {
|
} catch (JadxException e) {
|
||||||
@ -84,7 +90,7 @@ public abstract class IntegrationTest extends TestUtils {
|
|||||||
fail(e.getMessage());
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
RootNode root = JadxInternalAccess.getRoot(d);
|
RootNode root = JadxInternalAccess.getRoot(d);
|
||||||
root.getResourcesNames().putAll(resMap);
|
root.getConstValues().getResourcesNames().putAll(resMap);
|
||||||
|
|
||||||
ClassNode cls = root.searchClassByName(clsName);
|
ClassNode cls = root.searchClassByName(clsName);
|
||||||
assertThat("Class not found: " + clsName, cls, notNullValue());
|
assertThat("Class not found: " + clsName, cls, notNullValue());
|
||||||
@ -136,17 +142,6 @@ public abstract class IntegrationTest extends TestUtils {
|
|||||||
assertThat(cls.getCode().toString(), not(containsString("inconsistent")));
|
assertThat(cls.getCode().toString(), not(containsString("inconsistent")));
|
||||||
}
|
}
|
||||||
|
|
||||||
private IJadxArgs getArgs() {
|
|
||||||
JadxArgs args = new JadxArgs();
|
|
||||||
args.setCfgOutput(outputCFG);
|
|
||||||
args.setRawCFGOutput(outputCFG);
|
|
||||||
args.setFallbackMode(isFallback);
|
|
||||||
args.setShowInconsistentCode(true);
|
|
||||||
args.setThreadsCount(1);
|
|
||||||
args.setSkipResources(true);
|
|
||||||
return args;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void runAutoCheck(String clsName) {
|
private void runAutoCheck(String clsName) {
|
||||||
try {
|
try {
|
||||||
// run 'check' method from original class
|
// run 'check' method from original class
|
||||||
@ -228,12 +223,12 @@ public abstract class IntegrationTest extends TestUtils {
|
|||||||
return invoke(method, new Class<?>[0]);
|
return invoke(method, new Class<?>[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object invoke(String method, Class[] types, Object... args) throws Exception {
|
public Object invoke(String method, Class<?>[] types, Object... args) throws Exception {
|
||||||
Method mth = getReflectMethod(method, types);
|
Method mth = getReflectMethod(method, types);
|
||||||
return invoke(mth, args);
|
return invoke(mth, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Method getReflectMethod(String method, Class... types) {
|
public Method getReflectMethod(String method, Class<?>... types) {
|
||||||
assertNotNull("dynamicCompiler not ready", dynamicCompiler);
|
assertNotNull("dynamicCompiler not ready", dynamicCompiler);
|
||||||
try {
|
try {
|
||||||
return dynamicCompiler.getMethod(method, types);
|
return dynamicCompiler.getMethod(method, types);
|
||||||
@ -352,6 +347,14 @@ public abstract class IntegrationTest extends TestUtils {
|
|||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public JadxArgs getArgs() {
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setArgs(JadxArgs args) {
|
||||||
|
this.args = args;
|
||||||
|
}
|
||||||
|
|
||||||
public void setResMap(Map<Integer, String> resMap) {
|
public void setResMap(Map<Integer, String> resMap) {
|
||||||
this.resMap = resMap;
|
this.resMap = resMap;
|
||||||
}
|
}
|
||||||
@ -361,7 +364,7 @@ public abstract class IntegrationTest extends TestUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void setFallback() {
|
protected void setFallback() {
|
||||||
this.isFallback = true;
|
this.args.setFallbackMode(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void disableCompilation() {
|
protected void disableCompilation() {
|
||||||
@ -375,7 +378,8 @@ public abstract class IntegrationTest extends TestUtils {
|
|||||||
// Use only for debug purpose
|
// Use only for debug purpose
|
||||||
@Deprecated
|
@Deprecated
|
||||||
protected void setOutputCFG() {
|
protected void setOutputCFG() {
|
||||||
this.outputCFG = true;
|
this.args.setCfgOutput(true);
|
||||||
|
this.args.setRawCFGOutput(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use only for debug purpose
|
// Use only for debug purpose
|
||||||
|
@ -57,7 +57,7 @@ public class DynamicCompiler {
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Method getMethod(String method, Class[] types) throws Exception {
|
public Method getMethod(String method, Class<?>[] types) throws Exception {
|
||||||
for (Class<?> type : types) {
|
for (Class<?> type : types) {
|
||||||
checkType(type);
|
checkType(type);
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import jadx.tests.api.IntegrationTest;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.containsString;
|
import static org.hamcrest.CoreMatchers.containsString;
|
||||||
|
import static org.hamcrest.Matchers.not;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
public class TestSwitchLabels extends IntegrationTest {
|
public class TestSwitchLabels extends IntegrationTest {
|
||||||
@ -42,7 +43,23 @@ public class TestSwitchLabels extends IntegrationTest {
|
|||||||
assertThat(code, containsString("return CONST_CDE;"));
|
assertThat(code, containsString("return CONST_CDE;"));
|
||||||
|
|
||||||
cls.addInnerClass(getClassNode(TestCls.Inner.class));
|
cls.addInnerClass(getClassNode(TestCls.Inner.class));
|
||||||
assertThat(code, containsString("case CONST_CDE_PRIVATE"));
|
assertThat(code, not(containsString("case CONST_CDE_PRIVATE")));
|
||||||
assertThat(code, containsString(".CONST_ABC;"));
|
assertThat(code, containsString(".CONST_ABC;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWithDisabledConstReplace() {
|
||||||
|
getArgs().setReplaceConsts(false);
|
||||||
|
|
||||||
|
ClassNode cls = getClassNode(TestCls.class);
|
||||||
|
String code = cls.getCode().toString();
|
||||||
|
assertThat(code, not(containsString("case CONST_ABC")));
|
||||||
|
assertThat(code, containsString("case 2748"));
|
||||||
|
assertThat(code, not(containsString("return CONST_CDE;")));
|
||||||
|
assertThat(code, containsString("return 3294;"));
|
||||||
|
|
||||||
|
cls.addInnerClass(getClassNode(TestCls.Inner.class));
|
||||||
|
assertThat(code, not(containsString("case CONST_CDE_PRIVATE")));
|
||||||
|
assertThat(code, not(containsString(".CONST_ABC;")));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,6 +168,10 @@ public class JadxSettings extends JadxCLIArgs {
|
|||||||
this.escapeUnicode = escapeUnicode;
|
this.escapeUnicode = escapeUnicode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setReplaceConsts(boolean replaceConsts) {
|
||||||
|
this.replaceConsts = replaceConsts;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isAutoStartJobs() {
|
public boolean isAutoStartJobs() {
|
||||||
return autoStartJobs;
|
return autoStartJobs;
|
||||||
}
|
}
|
||||||
|
@ -257,11 +257,21 @@ public class JadxSettingsWindow extends JDialog {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
JCheckBox replaceConsts = new JCheckBox();
|
||||||
|
replaceConsts.setSelected(settings.isReplaceConsts());
|
||||||
|
replaceConsts.addItemListener(new ItemListener() {
|
||||||
|
public void itemStateChanged(ItemEvent e) {
|
||||||
|
settings.setReplaceConsts(e.getStateChange() == ItemEvent.SELECTED);
|
||||||
|
needReload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
SettingsGroup other = new SettingsGroup(NLS.str("preferences.decompile"));
|
SettingsGroup other = new SettingsGroup(NLS.str("preferences.decompile"));
|
||||||
other.addRow(NLS.str("preferences.threads"), threadsCount);
|
other.addRow(NLS.str("preferences.threads"), threadsCount);
|
||||||
other.addRow(NLS.str("preferences.start_jobs"), autoStartJobs);
|
other.addRow(NLS.str("preferences.start_jobs"), autoStartJobs);
|
||||||
other.addRow(NLS.str("preferences.showInconsistentCode"), showInconsistentCode);
|
other.addRow(NLS.str("preferences.showInconsistentCode"), showInconsistentCode);
|
||||||
other.addRow(NLS.str("preferences.escapeUnicode"), escapeUnicode);
|
other.addRow(NLS.str("preferences.escapeUnicode"), escapeUnicode);
|
||||||
|
other.addRow(NLS.str("preferences.replaceConsts"), replaceConsts);
|
||||||
other.addRow(NLS.str("preferences.fallback"), fallback);
|
other.addRow(NLS.str("preferences.fallback"), fallback);
|
||||||
other.addRow(NLS.str("preferences.skipResourcesDecode"), resourceDecode);
|
other.addRow(NLS.str("preferences.skipResourcesDecode"), resourceDecode);
|
||||||
return other;
|
return other;
|
||||||
|
@ -59,6 +59,7 @@ preferences.check_for_updates=Check for updates on startup
|
|||||||
preferences.fallback=Fallback mode (simple dump)
|
preferences.fallback=Fallback mode (simple dump)
|
||||||
preferences.showInconsistentCode=Show inconsistent code
|
preferences.showInconsistentCode=Show inconsistent code
|
||||||
preferences.escapeUnicode=Escape unicode
|
preferences.escapeUnicode=Escape unicode
|
||||||
|
preferences.replaceConsts=Replace constants
|
||||||
preferences.skipResourcesDecode=Don't decode resources
|
preferences.skipResourcesDecode=Don't decode resources
|
||||||
preferences.threads=Processing threads count
|
preferences.threads=Processing threads count
|
||||||
preferences.cfg=Generate methods CFG graphs (in 'dot' format)
|
preferences.cfg=Generate methods CFG graphs (in 'dot' format)
|
||||||
|
Loading…
Reference in New Issue
Block a user