Projects STRLCPY jadx Commits ed385e8c
🤬
Revision indexing in progress... (symbol navigation in revisions will be accurate after indexed)
  • ■ ■ ■ ■ ■ ■
    README.md
    skipped 65 lines
    66 66   -j, --threads-count - processing threads count
    67 67   -r, --no-res - do not decode resources
    68 68   -s, --no-src - do not decompile source code
     69 + --single-class - decompile a single class
     70 + --output-format - can be 'java' or 'json' (default: java)
    69 71   -e, --export-gradle - save as android gradle project
    70 72   --show-bad-code - show inconsistent code (incorrectly decompiled)
    71 73   --no-imports - disable use of imports, always write entire package name
    skipped 39 lines
  • ■ ■ ■ ■ ■ ■
    jadx-cli/src/main/java/jadx/cli/JCommanderWrapper.java
    skipped 111 lines
    112 112   // ignore
    113 113   }
    114 114   }
     115 + if (fieldType == String.class) {
     116 + try {
     117 + String val = (String) f.get(args);
     118 + if (val != null) {
     119 + opt.append(" (default: ").append(val).append(')');
     120 + }
     121 + } catch (Exception e) {
     122 + // ignore
     123 + }
     124 + }
    115 125   }
    116 126   
    117 127   private static void addSpaces(StringBuilder str, int count) {
    skipped 6 lines
  • ■ ■ ■ ■ ■ ■
    jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java
    skipped 45 lines
    46 46   @Parameter(names = { "--single-class" }, description = "decompile a single class")
    47 47   protected String singleClass = null;
    48 48   
     49 + @Parameter(names = { "--output-format" }, description = "can be 'java' or 'json'")
     50 + protected String outputFormat = "java";
     51 + 
    49 52   @Parameter(names = { "-e", "--export-gradle" }, description = "save as android gradle project")
    50 53   protected boolean exportAsGradleProject = false;
    51 54   
    skipped 34 lines
    86 89   protected boolean deobfuscationForceSave = false;
    87 90   
    88 91   @Parameter(names = { "--deobf-use-sourcename" }, description = "use source file name as class name alias")
    89  - protected boolean deobfuscationUseSourceNameAsAlias = true;
     92 + protected boolean deobfuscationUseSourceNameAsAlias = false;
     93 + 
     94 + @Parameter(
     95 + names = { "--rename-flags" },
     96 + description = "what to rename, comma-separated,"
     97 + + " 'case' for system case sensitivity,"
     98 + + " 'valid' for java identifiers,"
     99 + + " 'printable' characters,"
     100 + + " 'none' or 'all' (default)",
     101 + converter = RenameConverter.class
     102 + )
     103 + protected Set<RenameEnum> renameFlags = EnumSet.allOf(RenameEnum.class);
    90 104   
    91 105   @Parameter(names = { "--fs-case-sensitive" }, description = "treat filesystem as case sensitive, false by default")
    92 106   protected boolean fsCaseSensitive = false;
    skipped 6 lines
    99 113   
    100 114   @Parameter(names = { "-f", "--fallback" }, description = "make simple dump (using goto instead of 'if', 'for', etc)")
    101 115   protected boolean fallbackMode = false;
    102  - 
    103  - @Parameter(
    104  - names = { "--rename-flags" },
    105  - description = "what to rename, comma-separated,"
    106  - + " 'case' for system case sensitivity,"
    107  - + " 'valid' for java identifiers,"
    108  - + " 'printable' characters,"
    109  - + " 'none' or 'all' (default)",
    110  - converter = RenameConverter.class
    111  - )
    112  - protected Set<RenameEnum> renameFlags = EnumSet.allOf(RenameEnum.class);
    113 116   
    114 117   @Parameter(names = { "-v", "--verbose" }, description = "verbose output")
    115 118   protected boolean verbose = false;
    skipped 62 lines
    178 181   args.setOutDir(FileUtils.toFile(outDir));
    179 182   args.setOutDirSrc(FileUtils.toFile(outDirSrc));
    180 183   args.setOutDirRes(FileUtils.toFile(outDirRes));
     184 + args.setOutputFormat(JadxArgs.OutputFormatEnum.valueOf(outputFormat.toUpperCase()));
    181 185   args.setThreadsCount(threadsCount);
    182 186   args.setSkipSources(skipSources);
    183 187   if (singleClass != null) {
    skipped 167 lines
  • ■ ■ ■ ■ ■
    jadx-core/build.gradle
    skipped 7 lines
    8 8   compile 'org.ow2.asm:asm:7.1'
    9 9   compile 'org.jetbrains:annotations:17.0.0'
    10 10   compile 'uk.com.robust-it:cloning:1.9.12'
     11 + compile 'com.google.code.gson:gson:2.8.5'
    11 12   
    12 13   compile 'org.smali:baksmali:2.2.7'
    13 14   compile('org.smali:smali:2.2.7') {
    skipped 7 lines
  • ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/api/CodePosition.java
    skipped 56 lines
    57 57   
    58 58   @Override
    59 59   public String toString() {
    60  - return line + ':' + offset + (node != null ? " " + node : "");
     60 + StringBuilder sb = new StringBuilder();
     61 + sb.append(line);
     62 + if (offset != 0) {
     63 + sb.append(':').append(offset);
     64 + }
     65 + if (node != null) {
     66 + sb.append(' ').append(node);
     67 + }
     68 + return sb.toString();
    61 69   }
    62 70  }
    63 71   
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/api/JadxArgs.java
    skipped 61 lines
    62 62   
    63 63   private Set<RenameEnum> renameFlags = EnumSet.allOf(RenameEnum.class);
    64 64   
     65 + public enum OutputFormatEnum {
     66 + JAVA, JSON
     67 + }
     68 + 
     69 + private OutputFormatEnum outputFormat = OutputFormatEnum.JAVA;
     70 + 
    65 71   public JadxArgs() {
    66 72   // use default options
    67 73   }
    skipped 240 lines
    308 314   }
    309 315   }
    310 316   
     317 + public OutputFormatEnum getOutputFormat() {
     318 + return outputFormat;
     319 + }
     320 + 
     321 + public boolean isJsonOutput() {
     322 + return outputFormat == OutputFormatEnum.JSON;
     323 + }
     324 + 
     325 + public void setOutputFormat(OutputFormatEnum outputFormat) {
     326 + this.outputFormat = outputFormat;
     327 + }
     328 + 
    311 329   @Override
    312 330   public String toString() {
    313 331   return "JadxArgs{" + "inputFiles=" + inputFiles
    skipped 19 lines
    333 351   + ", exportAsGradleProject=" + exportAsGradleProject
    334 352   + ", fsCaseSensitive=" + fsCaseSensitive
    335 353   + ", renameFlags=" + renameFlags
     354 + + ", outputFormat=" + outputFormat
    336 355   + '}';
    337 356   }
    338 357  }
    skipped 1 lines
  • ■ ■ ■ ■
    jadx-core/src/main/java/jadx/api/JadxDecompiler.java
    skipped 214 lines
    215 215   executor.execute(() -> {
    216 216   try {
    217 217   cls.decompile();
    218  - SaveCode.save(outDir, args, cls.getClassNode());
     218 + SaveCode.save(outDir, cls.getClassNode());
    219 219   } catch (Exception e) {
    220 220   LOG.error("Error saving class: {}", cls.getFullName(), e);
    221 221   }
    skipped 181 lines
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/codegen/ClassGen.java
    skipped 18 lines
    19 19  import jadx.core.dex.attributes.nodes.EnumClassAttr.EnumField;
    20 20  import jadx.core.dex.attributes.nodes.JadxError;
    21 21  import jadx.core.dex.attributes.nodes.LineAttrNode;
    22  -import jadx.core.dex.attributes.nodes.SourceFileAttr;
    23 22  import jadx.core.dex.info.AccessInfo;
    24 23  import jadx.core.dex.info.ClassInfo;
    25 24  import jadx.core.dex.instructions.args.ArgType;
    skipped 102 lines
    128 127   }
    129 128   
    130 129   annotationGen.addForClass(clsCode);
    131  - insertSourceFileInfo(clsCode, cls);
    132 130   insertRenameInfo(clsCode, cls);
     131 + CodeGenUtils.addSourceFileInfo(clsCode, cls);
    133 132   clsCode.startLine(af.makeString());
    134 133   if (af.isInterface()) {
    135 134   if (af.isAnnotation()) {
    skipped 154 lines
    290 289   return false;
    291 290   }
    292 291   
    293  - private void addMethod(CodeWriter code, MethodNode mth) throws CodegenException {
     292 + public void addMethod(CodeWriter code, MethodNode mth) throws CodegenException {
     293 + CodeGenUtils.addComments(code, mth);
    294 294   if (mth.getAccessFlags().isAbstract() || mth.getAccessFlags().isNative()) {
    295 295   MethodGen mthGen = new MethodGen(this, mth);
    296 296   mthGen.addDefinition(code);
    297  - if (cls.getAccessFlags().isAnnotation()) {
    298  - Object def = annotationGen.getAnnotationDefaultValue(mth.getName());
    299  - if (def != null) {
    300  - code.add(" default ");
    301  - annotationGen.encodeValue(code, def);
    302  - }
    303  - }
    304 297   code.add(';');
    305 298   } else {
    306  - CodeGenUtils.addComments(code, mth);
    307 299   insertDecompilationProblems(code, mth);
    308 300   boolean badCode = mth.contains(AFlag.INCONSISTENT_CODE);
    309 301   if (badCode && showInconsistentCode) {
    310  - code.startLine("/* Code decompiled incorrectly, please refer to instructions dump. */");
    311 302   mth.remove(AFlag.INCONSISTENT_CODE);
    312 303   badCode = false;
    313 304   }
    314 305   MethodGen mthGen;
    315  - if (badCode || mth.contains(AType.JADX_ERROR) || fallback) {
     306 + if (badCode || fallback || mth.contains(AType.JADX_ERROR) || mth.getRegion() == null) {
    316 307   mthGen = MethodGen.getFallbackMethodGen(mth);
    317 308   } else {
    318 309   mthGen = new MethodGen(this, mth);
    skipped 3 lines
    322 313   }
    323 314   code.add('{');
    324 315   code.incIndent();
    325  - insertSourceFileInfo(code, mth);
    326  - if (fallback) {
    327  - mthGen.addFallbackMethodCode(code);
    328  - } else {
    329  - mthGen.addInstructions(code);
    330  - }
     316 + mthGen.addInstructions(code);
    331 317   code.decIndent();
    332 318   code.startLine('}');
    333 319   }
    skipped 23 lines
    357 343   private void addFields(CodeWriter code) throws CodegenException {
    358 344   addEnumFields(code);
    359 345   for (FieldNode f : cls.getFields()) {
    360  - if (f.contains(AFlag.DONT_GENERATE)) {
    361  - continue;
    362  - }
    363  - CodeGenUtils.addComments(code, f);
    364  - annotationGen.addForField(code, f);
     346 + addField(code, f);
     347 + }
     348 + }
    365 349   
    366  - if (f.getFieldInfo().isRenamed()) {
    367  - code.newLine();
    368  - CodeGenUtils.addRenamedComment(code, f, f.getName());
    369  - }
    370  - code.startLine(f.getAccessFlags().makeString());
    371  - useType(code, f.getType());
    372  - code.add(' ');
    373  - code.attachDefinition(f);
    374  - code.add(f.getAlias());
    375  - FieldInitAttr fv = f.get(AType.FIELD_INIT);
    376  - if (fv != null) {
    377  - code.add(" = ");
    378  - if (fv.getValue() == null) {
    379  - code.add(TypeGen.literalToString(0, f.getType(), cls, fallback));
    380  - } else {
    381  - if (fv.getValueType() == InitType.CONST) {
    382  - annotationGen.encodeValue(code, fv.getValue());
    383  - } else if (fv.getValueType() == InitType.INSN) {
    384  - InsnGen insnGen = makeInsnGen(fv.getInsnMth());
    385  - addInsnBody(insnGen, code, fv.getInsn());
    386  - }
     350 + public void addField(CodeWriter code, FieldNode f) {
     351 + if (f.contains(AFlag.DONT_GENERATE)) {
     352 + return;
     353 + }
     354 + CodeGenUtils.addComments(code, f);
     355 + annotationGen.addForField(code, f);
     356 + 
     357 + if (f.getFieldInfo().isRenamed()) {
     358 + code.newLine();
     359 + CodeGenUtils.addRenamedComment(code, f, f.getName());
     360 + }
     361 + code.startLine(f.getAccessFlags().makeString());
     362 + useType(code, f.getType());
     363 + code.add(' ');
     364 + code.attachDefinition(f);
     365 + code.add(f.getAlias());
     366 + FieldInitAttr fv = f.get(AType.FIELD_INIT);
     367 + if (fv != null) {
     368 + code.add(" = ");
     369 + if (fv.getValue() == null) {
     370 + code.add(TypeGen.literalToString(0, f.getType(), cls, fallback));
     371 + } else {
     372 + if (fv.getValueType() == InitType.CONST) {
     373 + annotationGen.encodeValue(code, fv.getValue());
     374 + } else if (fv.getValueType() == InitType.INSN) {
     375 + InsnGen insnGen = makeInsnGen(fv.getInsnMth());
     376 + addInsnBody(insnGen, code, fv.getInsn());
    387 377   }
    388 378   }
    389  - code.add(';');
    390 379   }
     380 + code.add(';');
    391 381   }
    392 382   
    393 383   private boolean isFieldsPresents() {
    skipped 175 lines
    569 559   }
    570 560   }
    571 561   
    572  - private Set<ClassInfo> getImports() {
     562 + public Set<ClassInfo> getImports() {
    573 563   if (parentGen != null) {
    574 564   return parentGen.getImports();
    575 565   } else {
    skipped 37 lines
    613 603   }
    614 604   }
    615 605   return searchCollision(dex, useCls.getParentClass(), searchCls);
    616  - }
    617  - 
    618  - private void insertSourceFileInfo(CodeWriter code, AttrNode node) {
    619  - SourceFileAttr sourceFileAttr = node.get(AType.SOURCE_FILE);
    620  - if (sourceFileAttr != null) {
    621  - code.startLine("/* compiled from: ").add(sourceFileAttr.getFileName()).add(" */");
    622  - }
    623 606   }
    624 607   
    625 608   private void insertRenameInfo(CodeWriter code, ClassNode cls) {
    skipped 19 lines
  • ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/codegen/CodeGen.java
    1 1  package jadx.core.codegen;
    2 2   
     3 +import java.util.concurrent.Callable;
     4 + 
     5 +import jadx.api.JadxArgs;
     6 +import jadx.core.codegen.json.JsonCodeGen;
    3 7  import jadx.core.dex.attributes.AFlag;
    4 8  import jadx.core.dex.nodes.ClassNode;
    5  -import jadx.core.utils.exceptions.CodegenException;
    6 9  import jadx.core.utils.exceptions.JadxRuntimeException;
    7 10   
    8 11  public class CodeGen {
    9 12   
    10  - public static void generate(ClassNode cls) throws CodegenException {
     13 + public static void generate(ClassNode cls) {
    11 14   if (cls.contains(AFlag.DONT_GENERATE)) {
    12 15   cls.setCode(CodeWriter.EMPTY);
    13 16   } else {
    14  - ClassGen clsGen = new ClassGen(cls, cls.root().getArgs());
    15  - CodeWriter code;
    16  - try {
    17  - code = clsGen.makeClass();
    18  - } catch (Exception e) {
    19  - if (cls.contains(AFlag.RESTART_CODEGEN)) {
    20  - cls.remove(AFlag.RESTART_CODEGEN);
    21  - code = clsGen.makeClass();
    22  - } else {
    23  - throw new JadxRuntimeException("Code generation error", e);
     17 + JadxArgs args = cls.root().getArgs();
     18 + switch (args.getOutputFormat()) {
     19 + case JAVA:
     20 + generateJavaCode(cls, args);
     21 + break;
     22 + 
     23 + case JSON:
     24 + generateJson(cls);
     25 + break;
     26 + }
     27 + }
     28 + }
     29 + 
     30 + private static void generateJavaCode(ClassNode cls, JadxArgs args) {
     31 + ClassGen clsGen = new ClassGen(cls, args);
     32 + CodeWriter code = wrapCodeGen(cls, clsGen::makeClass);
     33 + cls.setCode(code);
     34 + }
     35 + 
     36 + private static void generateJson(ClassNode cls) {
     37 + JsonCodeGen codeGen = new JsonCodeGen(cls);
     38 + String clsJson = wrapCodeGen(cls, codeGen::process);
     39 + cls.setCode(new CodeWriter(clsJson));
     40 + }
     41 + 
     42 + private static <R> R wrapCodeGen(ClassNode cls, Callable<R> codeGenFunc) {
     43 + try {
     44 + return codeGenFunc.call();
     45 + } catch (Exception e) {
     46 + if (cls.contains(AFlag.RESTART_CODEGEN)) {
     47 + cls.remove(AFlag.RESTART_CODEGEN);
     48 + try {
     49 + return codeGenFunc.call();
     50 + } catch (Exception ex) {
     51 + throw new JadxRuntimeException("Code generation error after restart", ex);
    24 52   }
     53 + } else {
     54 + throw new JadxRuntimeException("Code generation error", e);
    25 55   }
    26  - cls.setCode(code);
    27 56   }
    28 57   }
    29 58   
    skipped 4 lines
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java
    skipped 3 lines
    4 4  import java.io.PrintWriter;
    5 5  import java.util.Collections;
    6 6  import java.util.HashMap;
    7  -import java.util.Iterator;
    8 7  import java.util.Map;
    9 8  import java.util.TreeMap;
    10 9   
    skipped 26 lines
    37 36   INDENT_STR + INDENT_STR + INDENT_STR + INDENT_STR + INDENT_STR,
    38 37   };
    39 38   
    40  - private StringBuilder buf = new StringBuilder();
     39 + private StringBuilder buf;
    41 40   @Nullable
    42 41   private String code;
    43 42   private String indentStr;
    skipped 5 lines
    49 48   private Map<Integer, Integer> lineMap = Collections.emptyMap();
    50 49   
    51 50   public CodeWriter() {
     51 + this.buf = new StringBuilder();
    52 52   this.indent = 0;
    53 53   this.indentStr = "";
    54 54   if (ADD_LINE_NUMBERS) {
    55 55   incIndent(2);
    56 56   }
     57 + }
     58 + 
     59 + // create filled instance (just string wrapper)
     60 + public CodeWriter(String code) {
     61 + this.buf = null;
     62 + this.code = code;
    57 63   }
    58 64   
    59 65   public CodeWriter startLine() {
    skipped 165 lines
    225 231   attachAnnotation(obj, new CodePosition(line, offset + 1));
    226 232   }
    227 233   
     234 + public void attachLineAnnotation(Object obj) {
     235 + attachAnnotation(obj, new CodePosition(line, 0));
     236 + }
     237 + 
    228 238   private Object attachAnnotation(Object obj, CodePosition pos) {
    229 239   if (annotations.isEmpty()) {
    230 240   annotations = new HashMap<>();
    skipped 29 lines
    260 270   code = buf.toString();
    261 271   buf = null;
    262 272   
    263  - Iterator<Map.Entry<CodePosition, Object>> it = annotations.entrySet().iterator();
    264  - while (it.hasNext()) {
    265  - Map.Entry<CodePosition, Object> entry = it.next();
     273 + annotations.entrySet().removeIf(entry -> {
    266 274   Object v = entry.getValue();
    267 275   if (v instanceof DefinitionWrapper) {
    268 276   LineAttrNode l = ((DefinitionWrapper) v).getNode();
    269 277   l.setDecompiledLine(entry.getKey().getLine());
    270  - it.remove();
     278 + return true;
    271 279   }
    272  - }
     280 + return false;
     281 + });
    273 282   return this;
    274 283   }
    275 284   
    skipped 47 lines
  • ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/codegen/InsnGen.java
    skipped 62 lines
    63 63   protected final MethodNode mth;
    64 64   protected final RootNode root;
    65 65   protected final boolean fallback;
     66 + protected final boolean attachInsns;
    66 67   
    67 68   protected enum Flags {
    68 69   BODY_ONLY,
    skipped 4 lines
    73 74   public InsnGen(MethodGen mgen, boolean fallback) {
    74 75   this.mgen = mgen;
    75 76   this.mth = mgen.getMethodNode();
    76  - this.root = mth.dex().root();
     77 + this.root = mth.root();
    77 78   this.fallback = fallback;
     79 + this.attachInsns = root.getArgs().isJsonOutput();
    78 80   }
    79 81   
    80 82   private boolean isFallback() {
    skipped 141 lines
    222 224   } else {
    223 225   if (flag != Flags.INLINE) {
    224 226   code.startLineWithNum(insn.getSourceLine());
     227 + if (attachInsns) {
     228 + code.attachLineAnnotation(insn);
     229 + }
    225 230   }
    226 231   if (insn.getResult() != null) {
    227 232   SSAVar var = insn.getResult().getSVar();
    skipped 758 lines
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/codegen/MethodGen.java
    skipped 84 lines
    85 85   ai = ai.remove(AccessFlags.ACC_PUBLIC);
    86 86   }
    87 87   
    88  - if (mth.getMethodInfo().isRenamed() && !ai.isConstructor()) {
     88 + if (mth.getMethodInfo().hasAlias() && !ai.isConstructor()) {
    89 89   CodeGenUtils.addRenamedComment(code, mth, mth.getName());
    90 90   }
     91 + CodeGenUtils.addSourceFileInfo(code, mth);
     92 + if (mth.contains(AFlag.INCONSISTENT_CODE)) {
     93 + code.startLine("/* Code decompiled incorrectly, please refer to instructions dump. */");
     94 + }
     95 + 
    91 96   code.startLineWithNum(mth.getSourceLine());
    92 97   code.add(ai.makeString());
    93 98   if (Consts.DEBUG) {
    skipped 31 lines
    125 130   code.add(')');
    126 131   
    127 132   annotationGen.addThrows(mth, code);
     133 + 
     134 + // add default value if in annotation class
     135 + if (mth.getParentClass().getAccessFlags().isAnnotation()) {
     136 + Object def = annotationGen.getAnnotationDefaultValue(mth.getName());
     137 + if (def != null) {
     138 + code.add(" default ");
     139 + annotationGen.encodeValue(code, def);
     140 + }
     141 + }
    128 142   return true;
    129 143   }
    130 144   
    skipped 50 lines
    181 195   }
    182 196   
    183 197   public void addInstructions(CodeWriter code) throws CodegenException {
    184  - if (mth.contains(AType.JADX_ERROR)
    185  - || mth.contains(AFlag.INCONSISTENT_CODE)
    186  - || mth.getRegion() == null) {
    187  - code.startLine("/*");
     198 + if (mth.root().getArgs().isFallbackMode()) {
    188 199   addFallbackMethodCode(code);
    189  - code.startLine("*/");
    190  - 
    191  - code.startLine("throw new UnsupportedOperationException(\"Method not decompiled: ")
    192  - .add(mth.getParentClass().getClassInfo().getAliasFullName())
    193  - .add('.')
    194  - .add(mth.getAlias())
    195  - .add('(')
    196  - .add(Utils.listToString(mth.getMethodInfo().getArgumentsTypes()))
    197  - .add("):")
    198  - .add(mth.getMethodInfo().getReturnType().toString())
    199  - .add("\");");
     200 + } else if (classGen.isFallbackMode()) {
     201 + dumpInstructions(code);
    200 202   } else {
    201  - try {
    202  - RegionGen regionGen = new RegionGen(this);
    203  - regionGen.makeRegion(code, mth.getRegion());
    204  - } catch (StackOverflowError | BootstrapMethodError e) {
    205  - mth.addError("Method code generation error", new JadxOverflowException("StackOverflow"));
    206  - classGen.insertDecompilationProblems(code, mth);
    207  - addInstructions(code);
    208  - } catch (Exception e) {
    209  - if (mth.getParentClass().getTopParentClass().contains(AFlag.RESTART_CODEGEN)) {
    210  - throw e;
    211  - }
    212  - mth.addError("Method code generation error", e);
    213  - classGen.insertDecompilationProblems(code, mth);
    214  - addInstructions(code);
     203 + addRegionInsns(code);
     204 + }
     205 + }
     206 + 
     207 + public void addRegionInsns(CodeWriter code) throws CodegenException {
     208 + try {
     209 + RegionGen regionGen = new RegionGen(this);
     210 + regionGen.makeRegion(code, mth.getRegion());
     211 + } catch (StackOverflowError | BootstrapMethodError e) {
     212 + mth.addError("Method code generation error", new JadxOverflowException("StackOverflow"));
     213 + classGen.insertDecompilationProblems(code, mth);
     214 + dumpInstructions(code);
     215 + } catch (Exception e) {
     216 + if (mth.getParentClass().getTopParentClass().contains(AFlag.RESTART_CODEGEN)) {
     217 + throw e;
    215 218   }
     219 + mth.addError("Method code generation error", e);
     220 + classGen.insertDecompilationProblems(code, mth);
     221 + dumpInstructions(code);
    216 222   }
    217 223   }
    218 224   
     225 + public void dumpInstructions(CodeWriter code) {
     226 + code.startLine("/*");
     227 + addFallbackMethodCode(code);
     228 + code.startLine("*/");
     229 + 
     230 + code.startLine("throw new UnsupportedOperationException(\"Method not decompiled: ")
     231 + .add(mth.getParentClass().getClassInfo().getAliasFullName())
     232 + .add('.')
     233 + .add(mth.getAlias())
     234 + .add('(')
     235 + .add(Utils.listToString(mth.getMethodInfo().getArgumentsTypes()))
     236 + .add("):")
     237 + .add(mth.getMethodInfo().getReturnType().toString())
     238 + .add("\");");
     239 + }
     240 + 
    219 241   public void addFallbackMethodCode(CodeWriter code) {
    220 242   if (mth.getInstructions() == null) {
    221 243   // load original instructions
    skipped 22 lines
    244 266   
    245 267   public static void addFallbackInsns(CodeWriter code, MethodNode mth, InsnNode[] insnArr, boolean addLabels) {
    246 268   InsnGen insnGen = new InsnGen(getFallbackMethodGen(mth), true);
     269 + boolean attachInsns = mth.root().getArgs().isJsonOutput();
    247 270   InsnNode prevInsn = null;
    248 271   for (InsnNode insn : insnArr) {
    249 272   if (insn == null) {
    skipped 9 lines
    259 282   }
    260 283   try {
    261 284   code.startLine();
     285 + if (attachInsns) {
     286 + code.attachLineAnnotation(insn);
     287 + }
    262 288   RegisterArg resArg = insn.getResult();
    263 289   if (resArg != null) {
    264 290   ArgType varType = resArg.getInitType();
    skipped 39 lines
    304 330   * Return fallback variant of method codegen
    305 331   */
    306 332   public static MethodGen getFallbackMethodGen(MethodNode mth) {
    307  - ClassGen clsGen = new ClassGen(mth.getParentClass(), null, true, true, true);
     333 + ClassGen clsGen = new ClassGen(mth.getParentClass(), null, false, true, true);
    308 334   return new MethodGen(clsGen, mth);
    309 335   }
    310 336   
    skipped 5 lines
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/codegen/RegionGen.java
    skipped 120 lines
    121 121   } else {
    122 122   code.attachSourceLine(region.getSourceLine());
    123 123   }
     124 + if (attachInsns) {
     125 + List<BlockNode> conditionBlocks = region.getConditionBlocks();
     126 + if (!conditionBlocks.isEmpty()) {
     127 + BlockNode blockNode = conditionBlocks.get(0);
     128 + InsnNode lastInsn = BlockUtils.getLastInsn(blockNode);
     129 + if (lastInsn != null) {
     130 + code.attachLineAnnotation(lastInsn);
     131 + }
     132 + }
     133 + }
     134 + 
    124 135   code.add("if (");
    125 136   new ConditionGen(this).add(code, region.getCondition());
    126 137   code.add(") {");
    skipped 1 lines
    128 139   code.startLine('}');
    129 140   
    130 141   IContainer els = region.getElseRegion();
    131  - if (els != null && RegionUtils.notEmpty(els)) {
     142 + if (RegionUtils.notEmpty(els)) {
    132 143   code.add(" else ");
    133 144   if (connectElseIf(code, els)) {
    134 145   return;
    skipped 209 lines
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/codegen/json/JsonCodeGen.java
     1 +package jadx.core.codegen.json;
     2 + 
     3 +import java.util.ArrayList;
     4 +import java.util.Collections;
     5 +import java.util.List;
     6 +import java.util.Map;
     7 +import java.util.Objects;
     8 + 
     9 +import org.jetbrains.annotations.Nullable;
     10 + 
     11 +import com.google.gson.FieldNamingPolicy;
     12 +import com.google.gson.Gson;
     13 +import com.google.gson.GsonBuilder;
     14 + 
     15 +import jadx.api.CodePosition;
     16 +import jadx.api.JadxArgs;
     17 +import jadx.core.codegen.ClassGen;
     18 +import jadx.core.codegen.CodeWriter;
     19 +import jadx.core.codegen.MethodGen;
     20 +import jadx.core.codegen.json.cls.JsonClass;
     21 +import jadx.core.codegen.json.cls.JsonCodeLine;
     22 +import jadx.core.codegen.json.cls.JsonField;
     23 +import jadx.core.codegen.json.cls.JsonMethod;
     24 +import jadx.core.dex.attributes.AFlag;
     25 +import jadx.core.dex.info.ClassInfo;
     26 +import jadx.core.dex.instructions.args.ArgType;
     27 +import jadx.core.dex.nodes.ClassNode;
     28 +import jadx.core.dex.nodes.FieldNode;
     29 +import jadx.core.dex.nodes.InsnNode;
     30 +import jadx.core.dex.nodes.MethodNode;
     31 +import jadx.core.dex.nodes.RootNode;
     32 +import jadx.core.utils.CodeGenUtils;
     33 +import jadx.core.utils.Utils;
     34 +import jadx.core.utils.exceptions.JadxRuntimeException;
     35 + 
     36 +public class JsonCodeGen {
     37 + 
     38 + private static final Gson GSON = new GsonBuilder()
     39 + .setPrettyPrinting()
     40 + .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES)
     41 + .disableHtmlEscaping()
     42 + .create();
     43 + 
     44 + private final ClassNode cls;
     45 + private final JadxArgs args;
     46 + private final RootNode root;
     47 + 
     48 + public JsonCodeGen(ClassNode cls) {
     49 + this.cls = cls;
     50 + this.root = cls.root();
     51 + this.args = root.getArgs();
     52 + }
     53 + 
     54 + public String process() {
     55 + JsonClass jsonCls = processCls(cls, null);
     56 + return GSON.toJson(jsonCls);
     57 + }
     58 + 
     59 + private JsonClass processCls(ClassNode cls, @Nullable ClassGen parentCodeGen) {
     60 + ClassGen classGen;
     61 + if (parentCodeGen == null) {
     62 + classGen = new ClassGen(cls, args);
     63 + } else {
     64 + classGen = new ClassGen(cls, parentCodeGen);
     65 + }
     66 + ClassInfo classInfo = cls.getClassInfo();
     67 + 
     68 + JsonClass jsonCls = new JsonClass();
     69 + jsonCls.setPkg(classInfo.getAliasPkg());
     70 + jsonCls.setDex(cls.dex().getDexFile().getName());
     71 + jsonCls.setName(classInfo.getFullName());
     72 + if (classInfo.hasAlias()) {
     73 + jsonCls.setAlias(classInfo.getAliasFullName());
     74 + }
     75 + jsonCls.setType(getClassTypeStr(cls));
     76 + jsonCls.setAccessFlags(cls.getAccessFlags().rawValue());
     77 + if (!Objects.equals(cls.getSuperClass(), ArgType.OBJECT)) {
     78 + jsonCls.setSuperClass(getTypeAlias(cls.getSuperClass()));
     79 + }
     80 + if (!cls.getInterfaces().isEmpty()) {
     81 + jsonCls.setInterfaces(Utils.collectionMap(cls.getInterfaces(), this::getTypeAlias));
     82 + }
     83 + 
     84 + CodeWriter cw = new CodeWriter();
     85 + CodeGenUtils.addComments(cw, cls);
     86 + classGen.insertDecompilationProblems(cw, cls);
     87 + classGen.addClassDeclaration(cw);
     88 + jsonCls.setDeclaration(cw.finish().toString());
     89 + 
     90 + addFields(cls, jsonCls, classGen);
     91 + addMethods(cls, jsonCls, classGen);
     92 + addInnerClasses(cls, jsonCls, classGen);
     93 + 
     94 + if (!cls.getClassInfo().isInner()) {
     95 + List<String> imports = Utils.collectionMap(classGen.getImports(), ClassInfo::getAliasFullName);
     96 + Collections.sort(imports);
     97 + jsonCls.setImports(imports);
     98 + }
     99 + return jsonCls;
     100 + }
     101 + 
     102 + private void addInnerClasses(ClassNode cls, JsonClass jsonCls, ClassGen classGen) {
     103 + List<ClassNode> innerClasses = cls.getInnerClasses();
     104 + if (innerClasses.isEmpty()) {
     105 + return;
     106 + }
     107 + jsonCls.setInnerClasses(new ArrayList<>(innerClasses.size()));
     108 + for (ClassNode innerCls : innerClasses) {
     109 + if (innerCls.contains(AFlag.DONT_GENERATE)) {
     110 + continue;
     111 + }
     112 + JsonClass innerJsonCls = processCls(innerCls, classGen);
     113 + jsonCls.getInnerClasses().add(innerJsonCls);
     114 + }
     115 + }
     116 + 
     117 + private void addFields(ClassNode cls, JsonClass jsonCls, ClassGen classGen) {
     118 + jsonCls.setFields(new ArrayList<>());
     119 + for (FieldNode field : cls.getFields()) {
     120 + if (field.contains(AFlag.DONT_GENERATE)) {
     121 + continue;
     122 + }
     123 + JsonField jsonField = new JsonField();
     124 + jsonField.setName(field.getName());
     125 + if (field.getFieldInfo().hasAlias()) {
     126 + jsonField.setAlias(field.getAlias());
     127 + }
     128 + 
     129 + CodeWriter cw = new CodeWriter();
     130 + classGen.addField(cw, field);
     131 + jsonField.setDeclaration(cw.finish().toString());
     132 + jsonField.setAccessFlags(field.getAccessFlags().rawValue());
     133 + 
     134 + jsonCls.getFields().add(jsonField);
     135 + }
     136 + }
     137 + 
     138 + private void addMethods(ClassNode cls, JsonClass jsonCls, ClassGen classGen) {
     139 + jsonCls.setMethods(new ArrayList<>());
     140 + for (MethodNode mth : cls.getMethods()) {
     141 + if (mth.contains(AFlag.DONT_GENERATE)) {
     142 + continue;
     143 + }
     144 + JsonMethod jsonMth = new JsonMethod();
     145 + jsonMth.setName(mth.getName());
     146 + if (mth.getMethodInfo().hasAlias()) {
     147 + jsonMth.setAlias(mth.getAlias());
     148 + }
     149 + jsonMth.setSignature(mth.getMethodInfo().getShortId());
     150 + jsonMth.setReturnType(getTypeAlias(mth.getReturnType()));
     151 + jsonMth.setArguments(Utils.collectionMap(mth.getMethodInfo().getArgumentsTypes(), this::getTypeAlias));
     152 + 
     153 + MethodGen mthGen = new MethodGen(classGen, mth);
     154 + CodeWriter cw = new CodeWriter();
     155 + mthGen.addDefinition(cw);
     156 + jsonMth.setDeclaration(cw.finish().toString());
     157 + jsonMth.setAccessFlags(mth.getAccessFlags().rawValue());
     158 + jsonMth.setLines(fillMthCode(mth, mthGen));
     159 + jsonMth.setOffset("0x" + Long.toHexString(mth.getMethodCodeOffset()));
     160 + jsonCls.getMethods().add(jsonMth);
     161 + }
     162 + }
     163 + 
     164 + private List<JsonCodeLine> fillMthCode(MethodNode mth, MethodGen mthGen) {
     165 + if (mth.isNoCode()) {
     166 + return Collections.emptyList();
     167 + }
     168 + 
     169 + CodeWriter code = new CodeWriter();
     170 + try {
     171 + mthGen.addInstructions(code);
     172 + } catch (Exception e) {
     173 + throw new JadxRuntimeException("Method generation error", e);
     174 + }
     175 + code.finish();
     176 + String codeStr = code.toString();
     177 + if (codeStr.isEmpty()) {
     178 + return Collections.emptyList();
     179 + }
     180 + 
     181 + String[] lines = codeStr.split(CodeWriter.NL);
     182 + Map<Integer, Integer> lineMapping = code.getLineMapping();
     183 + Map<CodePosition, Object> annotations = code.getAnnotations();
     184 + long mthCodeOffset = mth.getMethodCodeOffset() + 16;
     185 + 
     186 + int linesCount = lines.length;
     187 + List<JsonCodeLine> codeLines = new ArrayList<>(linesCount);
     188 + for (int i = 0; i < linesCount; i++) {
     189 + String codeLine = lines[i];
     190 + int line = i + 2;
     191 + JsonCodeLine jsonCodeLine = new JsonCodeLine();
     192 + jsonCodeLine.setCode(codeLine);
     193 + jsonCodeLine.setSourceLine(lineMapping.get(line));
     194 + Object obj = annotations.get(new CodePosition(line, 0));
     195 + if (obj instanceof InsnNode) {
     196 + int offset = ((InsnNode) obj).getOffset();
     197 + jsonCodeLine.setOffset("0x" + Long.toHexString(mthCodeOffset + offset * 2));
     198 + }
     199 + codeLines.add(jsonCodeLine);
     200 + }
     201 + return codeLines;
     202 + }
     203 + 
     204 + private String getTypeAlias(ArgType clsType) {
     205 + if (Objects.equals(clsType, ArgType.OBJECT)) {
     206 + return ArgType.OBJECT.getObject();
     207 + }
     208 + if (clsType.isObject()) {
     209 + ClassInfo classInfo = ClassInfo.fromType(root, clsType);
     210 + return classInfo.getAliasFullName();
     211 + }
     212 + return clsType.toString();
     213 + }
     214 + 
     215 + private String getClassTypeStr(ClassNode cls) {
     216 + if (cls.isEnum()) {
     217 + return "enum";
     218 + }
     219 + if (cls.getAccessFlags().isInterface()) {
     220 + return "interface";
     221 + }
     222 + return "class";
     223 + }
     224 +}
     225 + 
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/codegen/json/JsonMappingGen.java
     1 +package jadx.core.codegen.json;
     2 + 
     3 +import java.io.File;
     4 +import java.io.FileWriter;
     5 +import java.io.Writer;
     6 +import java.util.ArrayList;
     7 +import java.util.List;
     8 + 
     9 +import org.slf4j.Logger;
     10 +import org.slf4j.LoggerFactory;
     11 + 
     12 +import com.google.gson.FieldNamingPolicy;
     13 +import com.google.gson.Gson;
     14 +import com.google.gson.GsonBuilder;
     15 + 
     16 +import jadx.api.JadxArgs;
     17 +import jadx.core.codegen.json.mapping.JsonClsMapping;
     18 +import jadx.core.codegen.json.mapping.JsonFieldMapping;
     19 +import jadx.core.codegen.json.mapping.JsonMapping;
     20 +import jadx.core.codegen.json.mapping.JsonMthMapping;
     21 +import jadx.core.dex.info.ClassInfo;
     22 +import jadx.core.dex.info.MethodInfo;
     23 +import jadx.core.dex.nodes.ClassNode;
     24 +import jadx.core.dex.nodes.FieldNode;
     25 +import jadx.core.dex.nodes.MethodNode;
     26 +import jadx.core.dex.nodes.RootNode;
     27 +import jadx.core.utils.exceptions.JadxRuntimeException;
     28 +import jadx.core.utils.files.FileUtils;
     29 + 
     30 +public class JsonMappingGen {
     31 + private static final Logger LOG = LoggerFactory.getLogger(JsonMappingGen.class);
     32 + 
     33 + private static final Gson GSON = new GsonBuilder()
     34 + .setPrettyPrinting()
     35 + .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES)
     36 + .disableHtmlEscaping()
     37 + .create();
     38 + 
     39 + public static void dump(RootNode root) {
     40 + JsonMapping mapping = new JsonMapping();
     41 + fillMapping(mapping, root);
     42 + 
     43 + JadxArgs args = root.getArgs();
     44 + File outDirSrc = args.getOutDirSrc().getAbsoluteFile();
     45 + File mappingFile = new File(outDirSrc, "mapping.json");
     46 + FileUtils.makeDirsForFile(mappingFile);
     47 + try (Writer writer = new FileWriter(mappingFile)) {
     48 + GSON.toJson(mapping, writer);
     49 + LOG.info("Save mappings to {}", mappingFile.getAbsolutePath());
     50 + } catch (Exception e) {
     51 + throw new JadxRuntimeException("Failed to save mapping json", e);
     52 + }
     53 + }
     54 + 
     55 + private static void fillMapping(JsonMapping mapping, RootNode root) {
     56 + List<ClassNode> classes = root.getClasses(true);
     57 + mapping.setClasses(new ArrayList<>(classes.size()));
     58 + for (ClassNode cls : classes) {
     59 + ClassInfo classInfo = cls.getClassInfo();
     60 + JsonClsMapping jsonCls = new JsonClsMapping();
     61 + jsonCls.setName(classInfo.getRawName());
     62 + jsonCls.setAlias(classInfo.getAliasFullName());
     63 + jsonCls.setInner(classInfo.isInner());
     64 + jsonCls.setJson(cls.getTopParentClass().getClassInfo().getAliasFullPath() + ".json");
     65 + if (classInfo.isInner()) {
     66 + jsonCls.setTopClass(cls.getTopParentClass().getClassInfo().getFullName());
     67 + }
     68 + addFields(cls, jsonCls);
     69 + addMethods(cls, jsonCls);
     70 + mapping.getClasses().add(jsonCls);
     71 + }
     72 + }
     73 + 
     74 + private static void addMethods(ClassNode cls, JsonClsMapping jsonCls) {
     75 + List<MethodNode> methods = cls.getMethods();
     76 + if (methods.isEmpty()) {
     77 + return;
     78 + }
     79 + jsonCls.setMethods(new ArrayList<>(methods.size()));
     80 + for (MethodNode method : methods) {
     81 + JsonMthMapping jsonMethod = new JsonMthMapping();
     82 + MethodInfo methodInfo = method.getMethodInfo();
     83 + jsonMethod.setSignature(methodInfo.getShortId());
     84 + jsonMethod.setName(methodInfo.getName());
     85 + jsonMethod.setAlias(methodInfo.getAlias());
     86 + jsonMethod.setOffset("0x" + Long.toHexString(method.getMethodCodeOffset()));
     87 + jsonCls.getMethods().add(jsonMethod);
     88 + }
     89 + }
     90 + 
     91 + private static void addFields(ClassNode cls, JsonClsMapping jsonCls) {
     92 + List<FieldNode> fields = cls.getFields();
     93 + if (fields.isEmpty()) {
     94 + return;
     95 + }
     96 + jsonCls.setFields(new ArrayList<>(fields.size()));
     97 + for (FieldNode field : fields) {
     98 + JsonFieldMapping jsonField = new JsonFieldMapping();
     99 + jsonField.setName(field.getName());
     100 + jsonField.setAlias(field.getAlias());
     101 + jsonCls.getFields().add(jsonField);
     102 + }
     103 + }
     104 + 
     105 + private JsonMappingGen() {
     106 + }
     107 +}
     108 + 
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/codegen/json/cls/JsonClass.java
     1 +package jadx.core.codegen.json.cls;
     2 + 
     3 +import java.util.List;
     4 + 
     5 +import com.google.gson.annotations.SerializedName;
     6 + 
     7 +public class JsonClass extends JsonNode {
     8 + @SerializedName("package")
     9 + private String pkg;
     10 + private String type; // class, interface, enum
     11 + @SerializedName("extends")
     12 + private String superClass;
     13 + @SerializedName("implements")
     14 + private List<String> interfaces;
     15 + private String dex;
     16 + 
     17 + private List<JsonField> fields;
     18 + private List<JsonMethod> methods;
     19 + private List<JsonClass> innerClasses;
     20 + 
     21 + private List<String> imports;
     22 + 
     23 + public String getType() {
     24 + return type;
     25 + }
     26 + 
     27 + public void setType(String type) {
     28 + this.type = type;
     29 + }
     30 + 
     31 + public String getSuperClass() {
     32 + return superClass;
     33 + }
     34 + 
     35 + public void setSuperClass(String superClass) {
     36 + this.superClass = superClass;
     37 + }
     38 + 
     39 + public List<String> getInterfaces() {
     40 + return interfaces;
     41 + }
     42 + 
     43 + public void setInterfaces(List<String> interfaces) {
     44 + this.interfaces = interfaces;
     45 + }
     46 + 
     47 + public List<JsonField> getFields() {
     48 + return fields;
     49 + }
     50 + 
     51 + public void setFields(List<JsonField> fields) {
     52 + this.fields = fields;
     53 + }
     54 + 
     55 + public List<JsonMethod> getMethods() {
     56 + return methods;
     57 + }
     58 + 
     59 + public void setMethods(List<JsonMethod> methods) {
     60 + this.methods = methods;
     61 + }
     62 + 
     63 + public List<JsonClass> getInnerClasses() {
     64 + return innerClasses;
     65 + }
     66 + 
     67 + public void setInnerClasses(List<JsonClass> innerClasses) {
     68 + this.innerClasses = innerClasses;
     69 + }
     70 + 
     71 + public String getPkg() {
     72 + return pkg;
     73 + }
     74 + 
     75 + public void setPkg(String pkg) {
     76 + this.pkg = pkg;
     77 + }
     78 + 
     79 + public String getDex() {
     80 + return dex;
     81 + }
     82 + 
     83 + public void setDex(String dex) {
     84 + this.dex = dex;
     85 + }
     86 + 
     87 + public List<String> getImports() {
     88 + return imports;
     89 + }
     90 + 
     91 + public void setImports(List<String> imports) {
     92 + this.imports = imports;
     93 + }
     94 +}
     95 + 
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/codegen/json/cls/JsonCodeLine.java
     1 +package jadx.core.codegen.json.cls;
     2 + 
     3 +import org.jetbrains.annotations.Nullable;
     4 + 
     5 +public class JsonCodeLine {
     6 + private String code;
     7 + private String offset;
     8 + private Integer sourceLine;
     9 + 
     10 + public String getCode() {
     11 + return code;
     12 + }
     13 + 
     14 + public void setCode(String code) {
     15 + this.code = code;
     16 + }
     17 + 
     18 + public String getOffset() {
     19 + return offset;
     20 + }
     21 + 
     22 + public void setOffset(String offset) {
     23 + this.offset = offset;
     24 + }
     25 + 
     26 + public Integer getSourceLine() {
     27 + return sourceLine;
     28 + }
     29 + 
     30 + public void setSourceLine(@Nullable Integer sourceLine) {
     31 + this.sourceLine = sourceLine;
     32 + }
     33 +}
     34 + 
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/codegen/json/cls/JsonField.java
     1 +package jadx.core.codegen.json.cls;
     2 + 
     3 +public class JsonField extends JsonNode {
     4 + String type;
     5 +}
     6 + 
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/codegen/json/cls/JsonMethod.java
     1 +package jadx.core.codegen.json.cls;
     2 + 
     3 +import java.util.List;
     4 + 
     5 +public class JsonMethod extends JsonNode {
     6 + private String signature;
     7 + private String returnType;
     8 + private List<String> arguments;
     9 + private List<JsonCodeLine> lines;
     10 + private String offset;
     11 + 
     12 + public String getSignature() {
     13 + return signature;
     14 + }
     15 + 
     16 + public void setSignature(String signature) {
     17 + this.signature = signature;
     18 + }
     19 + 
     20 + public String getReturnType() {
     21 + return returnType;
     22 + }
     23 + 
     24 + public void setReturnType(String returnType) {
     25 + this.returnType = returnType;
     26 + }
     27 + 
     28 + public List<String> getArguments() {
     29 + return arguments;
     30 + }
     31 + 
     32 + public void setArguments(List<String> arguments) {
     33 + this.arguments = arguments;
     34 + }
     35 + 
     36 + public List<JsonCodeLine> getLines() {
     37 + return lines;
     38 + }
     39 + 
     40 + public void setLines(List<JsonCodeLine> lines) {
     41 + this.lines = lines;
     42 + }
     43 + 
     44 + public String getOffset() {
     45 + return offset;
     46 + }
     47 + 
     48 + public void setOffset(String offset) {
     49 + this.offset = offset;
     50 + }
     51 +}
     52 + 
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/codegen/json/cls/JsonNode.java
     1 +package jadx.core.codegen.json.cls;
     2 + 
     3 +public class JsonNode {
     4 + private String name;
     5 + private String alias;
     6 + private String declaration;
     7 + private int accessFlags;
     8 + 
     9 + public String getName() {
     10 + return name;
     11 + }
     12 + 
     13 + public void setName(String name) {
     14 + this.name = name;
     15 + }
     16 + 
     17 + public String getAlias() {
     18 + return alias;
     19 + }
     20 + 
     21 + public void setAlias(String alias) {
     22 + this.alias = alias;
     23 + }
     24 + 
     25 + public String getDeclaration() {
     26 + return declaration;
     27 + }
     28 + 
     29 + public void setDeclaration(String declaration) {
     30 + this.declaration = declaration;
     31 + }
     32 + 
     33 + public int getAccessFlags() {
     34 + return accessFlags;
     35 + }
     36 + 
     37 + public void setAccessFlags(int accessFlags) {
     38 + this.accessFlags = accessFlags;
     39 + }
     40 +}
     41 + 
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/codegen/json/mapping/JsonClsMapping.java
     1 +package jadx.core.codegen.json.mapping;
     2 + 
     3 +import java.util.List;
     4 + 
     5 +public class JsonClsMapping {
     6 + private String name;
     7 + private String alias;
     8 + 
     9 + private String json;
     10 + private boolean inner;
     11 + private String topClass;
     12 + 
     13 + private List<JsonFieldMapping> fields;
     14 + private List<JsonMthMapping> methods;
     15 + 
     16 + public String getName() {
     17 + return name;
     18 + }
     19 + 
     20 + public void setName(String name) {
     21 + this.name = name;
     22 + }
     23 + 
     24 + public String getAlias() {
     25 + return alias;
     26 + }
     27 + 
     28 + public void setAlias(String alias) {
     29 + this.alias = alias;
     30 + }
     31 + 
     32 + public String getJson() {
     33 + return json;
     34 + }
     35 + 
     36 + public void setJson(String json) {
     37 + this.json = json;
     38 + }
     39 + 
     40 + public boolean isInner() {
     41 + return inner;
     42 + }
     43 + 
     44 + public void setInner(boolean inner) {
     45 + this.inner = inner;
     46 + }
     47 + 
     48 + public String getTopClass() {
     49 + return topClass;
     50 + }
     51 + 
     52 + public void setTopClass(String topClass) {
     53 + this.topClass = topClass;
     54 + }
     55 + 
     56 + public List<JsonFieldMapping> getFields() {
     57 + return fields;
     58 + }
     59 + 
     60 + public void setFields(List<JsonFieldMapping> fields) {
     61 + this.fields = fields;
     62 + }
     63 + 
     64 + public List<JsonMthMapping> getMethods() {
     65 + return methods;
     66 + }
     67 + 
     68 + public void setMethods(List<JsonMthMapping> methods) {
     69 + this.methods = methods;
     70 + }
     71 +}
     72 + 
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/codegen/json/mapping/JsonFieldMapping.java
     1 +package jadx.core.codegen.json.mapping;
     2 + 
     3 +public class JsonFieldMapping {
     4 + private String name;
     5 + private String alias;
     6 + 
     7 + public String getName() {
     8 + return name;
     9 + }
     10 + 
     11 + public void setName(String name) {
     12 + this.name = name;
     13 + }
     14 + 
     15 + public String getAlias() {
     16 + return alias;
     17 + }
     18 + 
     19 + public void setAlias(String alias) {
     20 + this.alias = alias;
     21 + }
     22 +}
     23 + 
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/codegen/json/mapping/JsonMapping.java
     1 +package jadx.core.codegen.json.mapping;
     2 + 
     3 +import java.util.List;
     4 + 
     5 +public class JsonMapping {
     6 + private List<JsonClsMapping> classes;
     7 + 
     8 + public List<JsonClsMapping> getClasses() {
     9 + return classes;
     10 + }
     11 + 
     12 + public void setClasses(List<JsonClsMapping> classes) {
     13 + this.classes = classes;
     14 + }
     15 +}
     16 + 
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/codegen/json/mapping/JsonMthMapping.java
     1 +package jadx.core.codegen.json.mapping;
     2 + 
     3 +public class JsonMthMapping {
     4 + private String signature;
     5 + private String name;
     6 + private String alias;
     7 + private String offset;
     8 + 
     9 + public String getSignature() {
     10 + return signature;
     11 + }
     12 + 
     13 + public void setSignature(String signature) {
     14 + this.signature = signature;
     15 + }
     16 + 
     17 + public String getName() {
     18 + return name;
     19 + }
     20 + 
     21 + public void setName(String name) {
     22 + this.name = name;
     23 + }
     24 + 
     25 + public String getAlias() {
     26 + return alias;
     27 + }
     28 + 
     29 + public void setAlias(String alias) {
     30 + this.alias = alias;
     31 + }
     32 + 
     33 + public String getOffset() {
     34 + return offset;
     35 + }
     36 + 
     37 + public void setOffset(String offset) {
     38 + this.offset = offset;
     39 + }
     40 +}
     41 + 
  • ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java
    skipped 141 lines
    142 142   }
    143 143   for (MethodInfo mth : o.getMethods()) {
    144 144   if (aliasToUse == null) {
    145  - if (mth.isRenamed() && !mth.isAliasFromPreset()) {
     145 + if (mth.hasAlias() && !mth.isAliasFromPreset()) {
    146 146   mth.setAlias(String.format("mo%d%s", id, prepareNamePart(mth.getName())));
    147 147   }
    148 148   aliasToUse = mth.getAlias();
    skipped 445 lines
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/dex/info/AccessInfo.java
    skipped 200 lines
    201 201   }
    202 202   }
    203 203   
     204 + public int rawValue() {
     205 + return accFlags;
     206 + }
     207 + 
    204 208   @Override
    205 209   public String toString() {
    206 210   return "AccessInfo: " + type + " 0x" + Integer.toHexString(accFlags) + " (" + rawString() + ')';
    skipped 3 lines
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/dex/info/FieldInfo.java
    1 1  package jadx.core.dex.info;
    2 2   
     3 +import java.util.Objects;
     4 + 
    3 5  import com.android.dex.FieldId;
    4 6   
    5 7  import jadx.core.codegen.TypeGen;
    skipped 45 lines
    51 53   
    52 54   public void setAlias(String alias) {
    53 55   this.alias = alias;
     56 + }
     57 + 
     58 + public boolean hasAlias() {
     59 + return !Objects.equals(name, alias);
    54 60   }
    55 61   
    56 62   public String getFullId() {
    skipped 43 lines
  • ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/dex/info/MethodInfo.java
    skipped 129 lines
    130 130   this.alias = alias;
    131 131   }
    132 132   
    133  - public boolean isRenamed() {
     133 + public boolean hasAlias() {
    134 134   return !name.equals(alias);
    135 135   }
    136 136   
    skipped 37 lines
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java
    skipped 666 lines
    667 667   return mthInfo;
    668 668   }
    669 669   
     670 + public long getMethodCodeOffset() {
     671 + return noCode ? 0 : methodData.getCodeOffset();
     672 + }
     673 + 
    670 674   /**
    671 675   * Stat method.
    672 676   * Calculate instructions count as a measure of method size
    skipped 36 lines
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/dex/visitors/FallbackModeVisitor.java
    1 1  package jadx.core.dex.visitors;
    2 2   
     3 +import jadx.core.codegen.json.JsonMappingGen;
    3 4  import jadx.core.dex.attributes.AType;
    4 5  import jadx.core.dex.nodes.InsnNode;
    5 6  import jadx.core.dex.nodes.MethodNode;
     7 +import jadx.core.dex.nodes.RootNode;
    6 8  import jadx.core.dex.trycatch.CatchAttr;
    7 9  import jadx.core.utils.exceptions.JadxException;
    8 10   
    9 11  public class FallbackModeVisitor extends AbstractVisitor {
     12 + 
     13 + @Override
     14 + public void init(RootNode root) {
     15 + if (root.getArgs().isJsonOutput()) {
     16 + JsonMappingGen.dump(root);
     17 + }
     18 + }
    10 19   
    11 20   @Override
    12 21   public void visit(MethodNode mth) throws JadxException {
    skipped 34 lines
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java
    skipped 9 lines
    10 10   
    11 11  import jadx.api.JadxArgs;
    12 12  import jadx.core.Consts;
     13 +import jadx.core.codegen.json.JsonMappingGen;
    13 14  import jadx.core.deobf.Deobfuscator;
    14 15  import jadx.core.deobf.NameMapper;
    15 16  import jadx.core.dex.attributes.AFlag;
    skipped 34 lines
    50 51   if (args.isDeobfuscationOn()) {
    51 52   deobfuscator.savePresets();
    52 53   deobfuscator.clear();
     54 + }
     55 + if (args.isJsonOutput()) {
     56 + JsonMappingGen.dump(root);
    53 57   }
    54 58   }
    55 59   
    skipped 177 lines
  • ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/dex/visitors/SaveCode.java
    skipped 12 lines
    13 13   private SaveCode() {
    14 14   }
    15 15   
    16  - public static void save(File dir, JadxArgs args, ClassNode cls) {
     16 + public static void save(File dir, ClassNode cls) {
    17 17   if (cls.contains(AFlag.DONT_GENERATE)) {
    18 18   return;
    19 19   }
    skipped 1 lines
    21 21   if (clsCode == null) {
    22 22   throw new JadxRuntimeException("Code not generated for class " + cls.getFullName());
    23 23   }
    24  - String fileName = cls.getClassInfo().getAliasFullPath() + ".java";
    25  - if (args.isFallbackMode()) {
    26  - fileName += ".jadx";
     24 + if (clsCode == CodeWriter.EMPTY) {
     25 + return;
    27 26   }
     27 + String fileName = cls.getClassInfo().getAliasFullPath() + getFileExtension(cls);
    28 28   clsCode.save(dir, fileName);
     29 + }
     30 + 
     31 + private static String getFileExtension(ClassNode cls) {
     32 + JadxArgs.OutputFormatEnum outputFormat = cls.root().getArgs().getOutputFormat();
     33 + switch (outputFormat) {
     34 + case JAVA:
     35 + return ".java";
     36 + 
     37 + case JSON:
     38 + return ".json";
     39 + 
     40 + default:
     41 + throw new JadxRuntimeException("Unknown output format: " + outputFormat);
     42 + }
    29 43   }
    30 44  }
    31 45   
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/utils/CodeGenUtils.java
    skipped 5 lines
    6 6  import jadx.core.dex.attributes.AType;
    7 7  import jadx.core.dex.attributes.AttrNode;
    8 8  import jadx.core.dex.attributes.nodes.RenameReasonAttr;
     9 +import jadx.core.dex.attributes.nodes.SourceFileAttr;
    9 10   
    10 11  public class CodeGenUtils {
    11 12   
    skipped 13 lines
    25 26   code.add(renameReasonAttr.getDescription());
    26 27   }
    27 28   code.add(" */");
     29 + }
     30 + 
     31 + public static void addSourceFileInfo(CodeWriter code, AttrNode node) {
     32 + SourceFileAttr sourceFileAttr = node.get(AType.SOURCE_FILE);
     33 + if (sourceFileAttr != null) {
     34 + code.startLine("/* compiled from: ").add(sourceFileAttr.getFileName()).add(" */");
     35 + }
    28 36   }
    29 37   
    30 38   private CodeGenUtils() {
    skipped 3 lines
  • ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/utils/ImmutableList.java
    skipped 209 lines
    210 210   if (this == o) {
    211 211   return true;
    212 212   }
    213  - if (o == null || getClass() != o.getClass()) {
    214  - return false;
     213 + if (o instanceof ImmutableList) {
     214 + ImmutableList<?> other = (ImmutableList<?>) o;
     215 + return Arrays.equals(arr, other.arr);
     216 + }
     217 + if (o instanceof List) {
     218 + List<?> other = (List<?>) o;
     219 + int size = size();
     220 + if (size != other.size()) {
     221 + return false;
     222 + }
     223 + for (int i = 0; i < size; i++) {
     224 + E e1 = arr[i];
     225 + Object e2 = other.get(i);
     226 + if (!Objects.equals(e1, e2)) {
     227 + return false;
     228 + }
     229 + }
     230 + return true;
    215 231   }
    216  - ImmutableList<?> that = (ImmutableList<?>) o;
    217  - return Arrays.equals(arr, that.arr);
     232 + return false;
    218 233   }
    219 234   
    220 235   @Override
    skipped 10 lines
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/utils/Utils.java
    skipped 2 lines
    3 3  import java.io.OutputStream;
    4 4  import java.io.PrintWriter;
    5 5  import java.io.StringWriter;
     6 +import java.util.ArrayList;
    6 7  import java.util.Arrays;
     8 +import java.util.Collection;
    7 9  import java.util.Collections;
    8 10  import java.util.HashMap;
    9 11  import java.util.Iterator;
    skipped 142 lines
    152 154   if (cutIndex > 0 && cutIndex < length) {
    153 155   th.setStackTrace(Arrays.copyOfRange(stackTrace, 0, cutIndex));
    154 156   }
     157 + }
     158 + 
     159 + public static <T, R> List<R> collectionMap(Collection<T> list, Function<T, R> mapFunc) {
     160 + if (list == null || list.isEmpty()) {
     161 + return Collections.emptyList();
     162 + }
     163 + List<R> result = new ArrayList<>(list.size());
     164 + for (T t : list) {
     165 + result.add(mapFunc.apply(t));
     166 + }
     167 + return result;
    155 168   }
    156 169   
    157 170   public static <T> List<T> lockList(List<T> list) {
    skipped 30 lines
  • ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/utils/files/InputFile.java
    skipped 96 lines
    97 97   }
    98 98   
    99 99   private void addDexFile(Path path) throws IOException {
    100  - addDexFile("", path);
     100 + addDexFile(path.getFileName().toString(), path);
    101 101   }
    102 102   
    103 103   private void addDexFile(String fileName, Path path) throws IOException {
    skipped 121 lines
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/test/java/jadx/tests/integration/others/TestJsonOutput.java
     1 +package jadx.tests.integration.others;
     2 + 
     3 +import java.util.List;
     4 + 
     5 +import org.junit.jupiter.api.Test;
     6 + 
     7 +import jadx.api.JadxArgs;
     8 +import jadx.core.dex.nodes.ClassNode;
     9 +import jadx.tests.api.IntegrationTest;
     10 + 
     11 +import static jadx.tests.api.utils.JadxMatchers.containsOne;
     12 +import static org.hamcrest.MatcherAssert.assertThat;
     13 +import static org.hamcrest.Matchers.containsString;
     14 + 
     15 +public class TestJsonOutput extends IntegrationTest {
     16 + 
     17 + public static class TestCls {
     18 + private final String prefix = "list: ";
     19 + 
     20 + static {
     21 + System.out.println("test");
     22 + }
     23 + 
     24 + public void test(boolean b, List<String> list) {
     25 + if (b) {
     26 + System.out.println(prefix + list);
     27 + }
     28 + }
     29 + 
     30 + public static class Inner implements Runnable {
     31 + @Override
     32 + public void run() {
     33 + System.out.println("run");
     34 + }
     35 + }
     36 + }
     37 + 
     38 + @Test
     39 + public void test() {
     40 + disableCompilation();
     41 + args.setOutputFormat(JadxArgs.OutputFormatEnum.JSON);
     42 + 
     43 + ClassNode cls = getClassNode(TestCls.class);
     44 + String code = cls.getCode().toString();
     45 + 
     46 + assertThat(code, containsString("\"offset\": \"0x"));
     47 + assertThat(code, containsOne("public static class Inner implements Runnable"));
     48 + }
     49 + 
     50 + @Test
     51 + public void testFallback() {
     52 + disableCompilation();
     53 + setFallback();
     54 + args.setOutputFormat(JadxArgs.OutputFormatEnum.JSON);
     55 + 
     56 + ClassNode cls = getClassNode(TestCls.class);
     57 + String code = cls.getCode().toString();
     58 + 
     59 + assertThat(code, containsString("\"offset\": \"0x"));
     60 + assertThat(code, containsOne("public static class Inner implements java.lang.Runnable"));
     61 + }
     62 +}
     63 + 
  • ■ ■ ■ ■ ■
    jadx-gui/build.gradle
    skipped 14 lines
    15 15   compile(project(":jadx-cli"))
    16 16   
    17 17   compile 'com.fifesoft:rsyntaxtextarea:3.0.2'
    18  - compile 'com.google.code.gson:gson:2.8.5'
    19 18   compile files('libs/jfontchooser-1.0.5.jar')
    20 19   compile 'hu.kazocsaba:image-viewer:1.2.3'
    21 20   
    skipped 61 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java
    1 1  package jadx.gui.settings;
    2 2   
    3  -import java.awt.Font;
    4  -import java.awt.GraphicsDevice;
    5  -import java.awt.GraphicsEnvironment;
    6  -import java.awt.Window;
     3 +import java.awt.*;
    7 4  import java.nio.file.Path;
    8 5  import java.nio.file.Paths;
    9 6  import java.util.ArrayList;
    skipped 5 lines
    15 12  import java.util.Set;
    16 13  import java.util.function.Consumer;
    17 14   
    18  -import javax.swing.JFrame;
     15 +import javax.swing.*;
    19 16   
    20 17  import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
    21 18  import org.jetbrains.annotations.Nullable;
    skipped 20 lines
    42 39   private static final Font DEFAULT_FONT = new RSyntaxTextArea().getFont();
    43 40   
    44 41   static final Set<String> SKIP_FIELDS = new HashSet<>(Arrays.asList(
    45  - "files", "input", "outDir", "outDirSrc", "outDirRes", "verbose", "printVersion", "printHelp"));
     42 + "files", "input", "outDir", "outDirSrc", "outDirRes", "outputFormat",
     43 + "verbose", "printVersion", "printHelp"));
     44 + 
    46 45   private Path lastSaveProjectPath = USER_HOME;
    47 46   private Path lastOpenFilePath = USER_HOME;
    48 47   private Path lastSaveFilePath = USER_HOME;
    skipped 390 lines
Please wait...
Page is in error, reload to recover