提交 cd7e5bf0 编写于 作者: S Skylot

Merge branch 'master' into rename

name: "Validate Gradle Wrapper"
on: [push]
jobs:
validation:
name: "Validation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: gradle/wrapper-validation-action@v1
......@@ -7,6 +7,7 @@ import jadx.api.JadxArgs;
import jadx.api.JadxDecompiler;
import jadx.api.impl.NoOpCodeCache;
import jadx.core.utils.exceptions.JadxArgsValidateException;
import jadx.core.utils.files.FileUtils;
public class JadxCLI {
private static final Logger LOG = LoggerFactory.getLogger(JadxCLI.class);
......@@ -22,6 +23,7 @@ public class JadxCLI {
LOG.error("jadx error: {}", e.getMessage(), e);
result = 1;
} finally {
FileUtils.deleteTempRootDir();
System.exit(result);
}
}
......
......@@ -52,6 +52,8 @@ public class ClassGen {
private final Set<ClassInfo> imports = new HashSet<>();
private int clsDeclLine;
private boolean bodyGenStarted;
public ClassGen(ClassNode cls, JadxArgs jadxArgs) {
this(cls, null, jadxArgs.isUseImports(), jadxArgs.isFallbackMode(), jadxArgs.isShowInconsistentCode());
}
......@@ -222,6 +224,7 @@ public class ClassGen {
public void addClassBody(CodeWriter clsCode) throws CodegenException {
clsCode.add('{');
setBodyGenStarted(true);
clsDeclLine = clsCode.getLine();
clsCode.incIndent();
addFields(clsCode);
......@@ -656,4 +659,12 @@ public class ClassGen {
public boolean isFallbackMode() {
return fallback;
}
public boolean isBodyGenStarted() {
return bodyGenStarted;
}
public void setBodyGenStarted(boolean bodyGenStarted) {
this.bodyGenStarted = bodyGenStarted;
}
}
......@@ -183,7 +183,7 @@ public class InsnGen {
ClassInfo declClass = field.getDeclClass();
// TODO
boolean fieldFromThisClass = clsGen.getClassNode().getClassInfo().equals(declClass);
if (!fieldFromThisClass) {
if (!fieldFromThisClass || !clsGen.isBodyGenStarted()) {
// Android specific resources class handler
if (!handleAppResField(code, clsGen, declClass)) {
clsGen.useClass(code, declClass);
......
......@@ -28,6 +28,7 @@ import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.parser.FieldInitAttr;
import jadx.core.dex.regions.Region;
import jadx.core.dex.regions.SwitchRegion;
import jadx.core.dex.regions.SwitchRegion.CaseInfo;
import jadx.core.dex.regions.SynchronizedRegion;
import jadx.core.dex.regions.TryCatchRegion;
import jadx.core.dex.regions.conditions.IfCondition;
......@@ -270,10 +271,9 @@ public class RegionGen extends InsnGen {
code.add(") {");
code.incIndent();
int size = sw.getKeys().size();
for (int i = 0; i < size; i++) {
List<Object> keys = sw.getKeys().get(i);
IContainer c = sw.getCases().get(i);
for (CaseInfo caseInfo : sw.getCases()) {
List<Object> keys = caseInfo.getKeys();
IContainer c = caseInfo.getContainer();
for (Object k : keys) {
code.startLine("case ");
if (k instanceof FieldNode) {
......
......@@ -118,6 +118,7 @@ public abstract class AttrNode implements IAttributeNode {
return storage.toString();
}
@Override
public boolean isAttrStorageEmpty() {
return storage.isEmpty();
}
......
......@@ -35,4 +35,6 @@ public interface IAttributeNode {
List<String> getAttributesStringsList();
String getAttributesString();
boolean isAttrStorageEmpty();
}
......@@ -110,12 +110,7 @@ public class ConstStorage {
}
private ValueStorage getClsValues(ClassNode cls) {
ValueStorage classValues = classes.get(cls);
if (classValues == null) {
classValues = new ValueStorage();
classes.put(cls, classValues);
}
return classValues;
return classes.computeIfAbsent(cls, c -> new ValueStorage());
}
@Nullable
......
......@@ -3,6 +3,7 @@ package jadx.core.dex.instructions;
import java.util.Arrays;
import java.util.List;
import jadx.core.codegen.CodeWriter;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.InsnNode;
......@@ -110,15 +111,13 @@ public class SwitchNode extends TargetInsnNode {
@Override
public String toString() {
StringBuilder targ = new StringBuilder();
targ.append('[');
StringBuilder sb = new StringBuilder();
sb.append(super.toString());
for (int i = 0; i < targets.length; i++) {
targ.append(InsnUtils.formatOffset(targets[i]));
if (i < targets.length - 1) {
targ.append(", ");
}
sb.append(" case ").append(keys[i])
.append(": goto ").append(InsnUtils.formatOffset(targets[i]));
sb.append(CodeWriter.NL);
}
targ.append(']');
return super.toString() + " k:" + Arrays.toString(keys) + " t:" + targ;
return sb.toString();
}
}
package jadx.core.dex.nodes;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
......@@ -555,11 +556,24 @@ public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode {
public String getSmali() {
if (smali == null) {
smali = SmaliUtils.getSmaliCode(dex, clsDefOffset);
StringWriter stringWriter = new StringWriter(4096);
getSmali(this, stringWriter);
stringWriter.append(System.lineSeparator());
for (ClassNode innerClass : innerClasses) {
getSmali(innerClass, stringWriter);
stringWriter.append(System.lineSeparator());
}
smali = stringWriter.toString();
}
return smali;
}
protected static boolean getSmali(ClassNode classNode, StringWriter stringWriter) {
stringWriter.append(String.format("###### Class %s (%s)", classNode.getFullName(), classNode.getRawName()));
stringWriter.append(System.lineSeparator());
return SmaliUtils.getSmaliCode(classNode.dex, classNode.clsDefOffset, stringWriter);
}
public ProcessState getState() {
return state;
}
......
......@@ -4,33 +4,50 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import jadx.core.codegen.CodeWriter;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.IBranchRegion;
import jadx.core.dex.nodes.IContainer;
import jadx.core.dex.nodes.IRegion;
import jadx.core.utils.Utils;
public final class SwitchRegion extends AbstractRegion implements IBranchRegion {
private final BlockNode header;
private final List<List<Object>> keys;
private final List<IContainer> cases;
private final List<CaseInfo> cases;
private IContainer defCase;
public SwitchRegion(IRegion parent, BlockNode header) {
super(parent);
this.header = header;
this.keys = new ArrayList<>();
this.cases = new ArrayList<>();
}
public static final class CaseInfo {
private final List<Object> keys;
private final IContainer container;
public CaseInfo(List<Object> keys, IContainer container) {
this.keys = keys;
this.container = container;
}
public List<Object> getKeys() {
return keys;
}
public IContainer getContainer() {
return container;
}
}
public BlockNode getHeader() {
return header;
}
public void addCase(List<Object> keysList, IContainer c) {
keys.add(keysList);
cases.add(c);
cases.add(new CaseInfo(keysList, c));
}
public void setDefaultCase(IContainer block) {
......@@ -41,19 +58,19 @@ public final class SwitchRegion extends AbstractRegion implements IBranchRegion
return defCase;
}
public List<List<Object>> getKeys() {
return keys;
public List<CaseInfo> getCases() {
return cases;
}
public List<IContainer> getCases() {
return cases;
public List<IContainer> getCaseContainers() {
return Utils.collectionMap(cases, caseInfo -> caseInfo.container);
}
@Override
public List<IContainer> getSubBlocks() {
List<IContainer> all = new ArrayList<>(cases.size() + 2);
all.add(header);
all.addAll(cases);
all.addAll(getCaseContainers());
if (defCase != null) {
all.add(defCase);
}
......@@ -63,7 +80,7 @@ public final class SwitchRegion extends AbstractRegion implements IBranchRegion
@Override
public List<IContainer> getBranches() {
List<IContainer> branches = new ArrayList<>(cases.size() + 1);
branches.addAll(cases);
branches.addAll(getCaseContainers());
if (defCase != null) {
branches.add(defCase);
}
......@@ -77,6 +94,16 @@ public final class SwitchRegion extends AbstractRegion implements IBranchRegion
@Override
public String toString() {
return "Switch: " + cases.size() + ", default: " + defCase;
StringBuilder sb = new StringBuilder();
sb.append("Switch: ").append(cases.size());
for (CaseInfo caseInfo : cases) {
sb.append(CodeWriter.NL).append(" case ")
.append(Utils.listToString(caseInfo.getKeys()))
.append(" -> ").append(caseInfo.getContainer());
}
if (defCase != null) {
sb.append(CodeWriter.NL).append(" default -> ").append(defCase);
}
return sb.toString();
}
}
......@@ -11,6 +11,9 @@ import org.slf4j.LoggerFactory;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.AttrNode;
import jadx.core.dex.attributes.annotations.Annotation;
import jadx.core.dex.attributes.annotations.AnnotationsList;
import jadx.core.dex.attributes.nodes.FieldReplaceAttr;
import jadx.core.dex.info.FieldInfo;
import jadx.core.dex.info.MethodInfo;
......@@ -46,6 +49,7 @@ import jadx.core.dex.visitors.shrink.CodeShrinkVisitor;
import jadx.core.utils.ErrorsCounter;
import jadx.core.utils.InsnRemover;
import jadx.core.utils.InsnUtils;
import jadx.core.utils.exceptions.JadxException;
import jadx.core.utils.exceptions.JadxRuntimeException;
import static jadx.core.utils.BlockUtils.replaceInsn;
......@@ -68,6 +72,12 @@ public class ModVisitor extends AbstractVisitor {
private static final long DOUBLE_TO_BITS = Double.doubleToLongBits(1);
private static final long FLOAT_TO_BITS = Float.floatToIntBits(1);
@Override
public boolean visit(ClassNode cls) throws JadxException {
replaceConstInAnnotations(cls);
return true;
}
@Override
public void visit(MethodNode mth) {
if (mth.isNoCode()) {
......@@ -171,6 +181,33 @@ public class ModVisitor extends AbstractVisitor {
}
}
private void replaceConstInAnnotations(ClassNode cls) {
if (cls.root().getArgs().isReplaceConsts()) {
replaceConstsInAnnotationForAttrNode(cls, cls);
cls.getFields().forEach(f -> replaceConstsInAnnotationForAttrNode(cls, f));
cls.getMethods().forEach(m -> replaceConstsInAnnotationForAttrNode(cls, m));
}
}
private void replaceConstsInAnnotationForAttrNode(ClassNode parentCls, AttrNode attrNode) {
AnnotationsList annotationsList = attrNode.get(AType.ANNOTATION_LIST);
if (annotationsList == null) {
return;
}
for (Annotation annotation : annotationsList.getAll()) {
if (annotation.getVisibility() == Annotation.Visibility.SYSTEM) {
continue;
}
for (Map.Entry<String, Object> entry : annotation.getValues().entrySet()) {
Object value = entry.getValue();
FieldNode constField = parentCls.getConstField(value);
if (constField != null) {
entry.setValue(constField.getFieldInfo());
}
}
}
}
private static void replaceConst(MethodNode mth, ClassNode parentClass, BlockNode block, int i, InsnNode insn) {
FieldNode f;
if (insn.getType() == InsnType.CONST_STR) {
......
......@@ -16,8 +16,6 @@ import org.jetbrains.annotations.Nullable;
import jadx.core.utils.exceptions.JadxRuntimeException;
import static jadx.core.utils.files.FileUtils.close;
/**
* Simple template engine
* Syntax for replace variable with value: '{{variable}}'
......@@ -56,21 +54,15 @@ public class TemplateFile {
}
public String build() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
process(out);
} finally {
close(out);
return out.toString();
}
return out.toString();
}
public void save(File outFile) throws IOException {
OutputStream out = new FileOutputStream(outFile);
try {
try (OutputStream out = new FileOutputStream(outFile)) {
process(out);
} finally {
close(out);
}
}
......
package jadx.core.utils;
import java.io.File;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.jetbrains.annotations.TestOnly;
import org.slf4j.Logger;
......@@ -12,6 +15,7 @@ import org.slf4j.LoggerFactory;
import jadx.core.codegen.CodeWriter;
import jadx.core.codegen.InsnGen;
import jadx.core.codegen.MethodGen;
import jadx.core.dex.attributes.IAttributeNode;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.IBlock;
import jadx.core.dex.nodes.IContainer;
......@@ -85,51 +89,74 @@ public class DebugUtils {
printRegions(mth, false);
}
public static void printRegion(MethodNode mth, IRegion region, boolean printInsn) {
printRegion(mth, region, "", printInsn);
public static void printRegions(MethodNode mth, boolean printInsns) {
printRegion(mth, mth.getRegion(), printInsns);
}
public static void printRegions(MethodNode mth, boolean printInsns) {
LOG.debug("|{}", mth);
printRegion(mth, mth.getRegion(), "| ", printInsns);
public static void printRegion(MethodNode mth, IRegion region, boolean printInsns) {
CodeWriter cw = new CodeWriter();
cw.startLine('|').add(mth.toString());
printRegion(mth, region, cw, "| ", printInsns);
LOG.debug("\n{}", cw.finish().getCodeStr());
}
private static void printRegion(MethodNode mth, IRegion region, String indent, boolean printInsns) {
LOG.debug("{}{} {}", indent, region, region.getAttributesString());
private static void printRegion(MethodNode mth, IRegion region, CodeWriter cw, String indent, boolean printInsns) {
printWithAttributes(cw, indent, region.toString(), region);
indent += "| ";
for (IContainer container : region.getSubBlocks()) {
if (container instanceof IRegion) {
printRegion(mth, (IRegion) container, indent, printInsns);
printRegion(mth, (IRegion) container, cw, indent, printInsns);
} else {
LOG.debug("{}{} {}", indent, container, container.getAttributesString());
printWithAttributes(cw, indent, container.toString(), container);
if (printInsns && container instanceof IBlock) {
IBlock block = (IBlock) container;
printInsns(mth, indent, block);
printInsns(mth, cw, indent, block);
}
}
}
}
private static void printInsns(MethodNode mth, String indent, IBlock block) {
private static void printInsns(MethodNode mth, CodeWriter cw, String indent, IBlock block) {
for (InsnNode insn : block.getInstructions()) {
try {
MethodGen mg = MethodGen.getFallbackMethodGen(mth);
InsnGen ig = new InsnGen(mg, true);
CodeWriter code = new CodeWriter();
ig.makeInsn(insn, code);
String insnStr = code.toString().substring(CodeWriter.NL.length());
String attrStr = insn.isAttrStorageEmpty() ? "" : '\t' + insn.getAttributesString();
LOG.debug("{}|> {}{}", indent, insnStr, attrStr);
String codeStr = code.finish().getCodeStr();
List<String> insnStrings = Arrays.stream(codeStr.split(CodeWriter.NL))
.filter(StringUtils::notBlank)
.map(s -> "|> " + s)
.collect(Collectors.toList());
Iterator<String> it = insnStrings.iterator();
while (true) {
String insnStr = it.next();
if (it.hasNext()) {
cw.startLine(indent).add(insnStr);
} else {
printWithAttributes(cw, indent, insnStr, insn);
break;
}
}
} catch (CodegenException e) {
LOG.debug("{}|>!! {}", indent, insn);
cw.startLine(indent).add(">!! ").add(insn.toString());
}
}
}
public static void printMap(String desc, Map<?, ?> map) {
LOG.debug("Map of {}, size: {}", desc, map.size());
for (Map.Entry<?, ?> entry : map.entrySet()) {
LOG.debug(" {} : {}", entry.getKey(), entry.getValue());
private static void printWithAttributes(CodeWriter cw, String indent, String codeStr, IAttributeNode attrNode) {
String str = attrNode.isAttrStorageEmpty() ? codeStr : codeStr + ' ' + attrNode.getAttributesString();
List<String> attrStrings = Arrays.stream(str.split(CodeWriter.NL))
.filter(StringUtils::notBlank)
.collect(Collectors.toList());
Iterator<String> it = attrStrings.iterator();
if (!it.hasNext()) {
return;
}
cw.startLine(indent).add(it.next());
while (it.hasNext()) {
cw.startLine(indent).add("|+ ").add(it.next());
}
}
}
......@@ -4,7 +4,6 @@ import java.io.IOException;
import java.io.StringWriter;
import java.nio.file.Path;
import org.jetbrains.annotations.NotNull;
import org.jf.baksmali.Adaptors.ClassDefinition;
import org.jf.baksmali.BaksmaliOptions;
import org.jf.dexlib2.DexFileFactory;
......@@ -34,24 +33,25 @@ public class SmaliUtils {
}
}
@NotNull
public static String getSmaliCode(DexNode dex, int clsDefOffset) {
public static boolean getSmaliCode(DexNode dex, int clsDefOffset, StringWriter stringWriter) {
try {
Path path = dex.getDexFile().getPath();
DexBackedDexFile dexFile = DexFileFactory.loadDexFile(path.toFile(), null);
DexBackedClassDef dexBackedClassDef = new DexBackedClassDef(dexFile, clsDefOffset);
return getSmaliCode(dexBackedClassDef);
getSmaliCode(dexBackedClassDef, stringWriter);
return true;
} catch (Exception e) {
LOG.error("Error generating smali", e);
return "Error generating smali code: " + e.getMessage()
+ '\n' + Utils.getStackTrace(e);
stringWriter.append("Error generating smali code: ");
stringWriter.append(e.getMessage());
stringWriter.append(System.lineSeparator());
stringWriter.append(Utils.getStackTrace(e));
return false;
}
}
private static String getSmaliCode(DexBackedClassDef classDef) throws IOException {
private static void getSmaliCode(DexBackedClassDef classDef, StringWriter stringWriter) throws IOException {
ClassDefinition classDefinition = new ClassDefinition(new BaksmaliOptions(), classDef);
StringWriter sw = new StringWriter();
classDefinition.writeTo(new IndentingWriter(sw));
return sw.toString();
classDefinition.writeTo(new IndentingWriter(stringWriter));
}
}
......@@ -211,6 +211,10 @@ public class StringUtils {
return str == null || str.isEmpty();
}
public static boolean notBlank(String str) {
return notEmpty(str) && !str.trim().isEmpty();
}
public static int countMatches(String str, String subStr) {
if (str == null || str.isEmpty() || subStr == null || subStr.isEmpty()) {
return 0;
......
......@@ -10,12 +10,15 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
......@@ -32,6 +35,9 @@ public class FileUtils {
public static final int READ_BUFFER_SIZE = 8 * 1024;
private static final int MAX_FILENAME_LENGTH = 128;
public static final String JADX_TMP_INSTANCE_PREFIX = "jadx-instance-";
public static final String JADX_TMP_PREFIX = "jadx-tmp-";
private FileUtils() {
}
......@@ -70,6 +76,12 @@ public class FileUtils {
}
}
public static void makeDirs(@Nullable Path dir) {
if (dir != null) {
makeDirs(dir.toFile());
}
}
public static boolean deleteDir(File dir) {
File[] content = dir.listFiles();
if (content != null) {
......@@ -80,11 +92,27 @@ public class FileUtils {
return dir.delete();
}
public static void deleteDir(Path dir) {
try (Stream<Path> pathStream = Files.walk(dir)) {
pathStream.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
} catch (Exception e) {
throw new JadxRuntimeException("Failed to delete directory " + dir, e);
}
}
private static final Path TEMP_ROOT_DIR = createTempRootDir();
private static Path createTempRootDir() {
try {
Path dir = Files.createTempDirectory("jadx-instance-");
String jadxTmpDir = System.getenv("JADX_TMP_DIR");
Path dir;
if (jadxTmpDir != null) {
dir = Files.createTempDirectory(Paths.get(jadxTmpDir), "jadx-instance-");
} else {
dir = Files.createTempDirectory(JADX_TMP_INSTANCE_PREFIX);
}
dir.toFile().deleteOnExit();
return dir;
} catch (Exception e) {
......@@ -92,6 +120,15 @@ public class FileUtils {
}
}
public static void deleteTempRootDir() {
deleteDir(TEMP_ROOT_DIR);
}
public static void clearTempRootDir() {
deleteDir(TEMP_ROOT_DIR);
makeDirs(TEMP_ROOT_DIR);
}
public static Path createTempDir(String prefix) {
try {
Path dir = Files.createTempDirectory(TEMP_ROOT_DIR, prefix);
......@@ -104,7 +141,7 @@ public class FileUtils {
public static Path createTempFile(String suffix) {
try {
Path path = Files.createTempFile(TEMP_ROOT_DIR, "jadx-tmp-", suffix);
Path path = Files.createTempFile(TEMP_ROOT_DIR, JADX_TMP_PREFIX, suffix);
path.toFile().deleteOnExit();
return path;
} catch (Exception e) {
......@@ -112,6 +149,14 @@ public class FileUtils {
}
}
public static Path createTempFileNoDelete(String suffix) {
try {
return Files.createTempFile(TEMP_ROOT_DIR, JADX_TMP_PREFIX, suffix);
} catch (Exception e) {
throw new JadxRuntimeException("Failed to create temp file with suffix: " + suffix, e);
}
}
public static void copyStream(InputStream input, OutputStream output) throws IOException {
byte[] buffer = new byte[READ_BUFFER_SIZE];
while (true) {
......
......@@ -6,7 +6,6 @@ import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
......@@ -21,6 +20,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.jar.JarOutputStream;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import jadx.api.ICodeInfo;
......@@ -119,6 +119,11 @@ public abstract class IntegrationTest extends TestUtils {
args.setFsCaseSensitive(false); // use same value on all systems
}
@AfterEach
public void after() {
FileUtils.clearTempRootDir();
}
public String getTestName() {
return this.getClass().getSimpleName();
}
......@@ -414,7 +419,7 @@ public abstract class IntegrationTest extends TestUtils {
temp = FileUtils.createTempFile(suffix);
} else {
// don't delete on exit
temp = Files.createTempFile("jadx", suffix);
temp = FileUtils.createTempFileNoDelete(suffix);
System.out.println("Temporary file saved: " + temp.toAbsolutePath());
}
return temp.toFile();
......
package jadx.tests.integration.inner;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.Test;
import jadx.tests.api.IntegrationTest;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestRFieldRestore3 extends IntegrationTest {
public static class TestCls {
@T(2131230730)
public static class A {
@F(2131230730)
private int f;
@M(bind = 2137373737)
private void mth() {
}
@T(2137373737)
private class D {
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface T {
int value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD })
@interface F {
int value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
@interface M {
int bind();
}
public static class R {
}
}
@Test
public void test() {
Map<Integer, String> map = new HashMap<>();
map.put(2131230730, "id.Button");
map.put(2137373737, "id.MyId");
setResMap(map);
assertThat(getClassNode(TestCls.class))
.code()
.containsOnlyOnce("@T(R.id.Button)")
.containsOnlyOnce("@T(R.id.MyId)")
.containsOnlyOnce("@F(R.id.Button)")
.containsOnlyOnce("@M(bind = R.id.MyId)");
}
}
package jadx.tests.integration.inner;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.jupiter.api.Test;
import jadx.tests.api.IntegrationTest;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestReplaceConstsInAnnotations extends IntegrationTest {
public static class TestCls {
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface A {
int i();
float f();
}
@A(i = -1, f = C.FLOAT_CONST)
public static class C {
public static final float FLOAT_CONST = 3.14f;
}
}
@Test
public void test() {
assertThat(getClassNode(TestCls.class))
.code()
.containsOnlyOnce("f = C.FLOAT_CONST");
}
}
package jadx.tests.integration.switches;
import org.junit.jupiter.api.Test;
import jadx.NotYetImplemented;
import jadx.tests.api.IntegrationTest;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestSwitchFallThrough extends IntegrationTest {
public static class TestCls {
public int r;
public void test(int a) {
int i = 10;
switch (a) {
case 1:
i = 1000;
// fallthrough
case 2:
r = i;
break;
default:
r = -1;
break;
}
r *= 2;
System.out.println("in: " + a + ", out: " + r);
}
public int testWrap(int a) {
r = 0;
test(a);
return r;
}
public void check() {
assertThat(testWrap(1)).isEqualTo(2000);
assertThat(testWrap(2)).isEqualTo(20);
assertThat(testWrap(0)).isEqualTo(-2);
}
}
@NotYetImplemented("switch fallthrough")
@Test
public void test() {
assertThat(getClassNode(TestCls.class))
.code()
.containsOnlyOnce("switch");
// code correctness checks done in 'check' method
}
}
......@@ -77,6 +77,7 @@ import jadx.api.JadxArgs;
import jadx.api.JavaClass;
import jadx.api.JavaNode;
import jadx.api.ResourceFile;
import jadx.core.utils.files.FileUtils;
import jadx.gui.JadxWrapper;
import jadx.gui.jobs.BackgroundExecutor;
import jadx.gui.jobs.BackgroundWorker;
......@@ -1109,6 +1110,8 @@ public class MainWindow extends JFrame {
settings.setMainWindowExtendedState(getExtendedState());
cancelBackgroundJobs();
dispose();
FileUtils.deleteTempRootDir();
System.exit(0);
}
......
......@@ -40,7 +40,6 @@ class SearchBar extends JToolBar {
private final JCheckBox wholeWordCB;
private final JCheckBox matchCaseCB;
private ActionListener forwardListener = e -> search(0);
public SearchBar(RSyntaxTextArea textArea) {
rTextArea = textArea;
......@@ -81,6 +80,8 @@ class SearchBar extends JToolBar {
nextButton.setBorderPainted(false);
add(nextButton);
ActionListener forwardListener = e -> search(0);
markAllCB = new JCheckBox(NLS.str("search.mark_all"));
markAllCB.addActionListener(forwardListener);
add(markAllCB);
......
......@@ -17,4 +17,7 @@ public class SystemInfo {
public static final boolean IS_WINDOWS = LOWER_OS_NAME.startsWith("windows");
public static final boolean IS_MAC = LOWER_OS_NAME.startsWith("mac");
public static final boolean IS_LINUX = LOWER_OS_NAME.startsWith("linux");
private SystemInfo() {
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册