Coder Social home page Coder Social logo

jadx's Introduction

JADX

Build status GitHub contributors GitHub all releases GitHub release (latest by SemVer) Latest release Maven Central Java 11+ License

jadx - Dex to Java decompiler

Command line and GUI tools for producing Java source code from Android Dex and Apk files

Warning

Please note that in most cases jadx can't decompile all 100% of the code, so errors will occur.
Check Troubleshooting guide for workarounds.

Main features:

  • decompile Dalvik bytecode to Java code from APK, dex, aar, aab and zip files
  • decode AndroidManifest.xml and other resources from resources.arsc
  • deobfuscator included

jadx-gui features:

  • view decompiled code with highlighted syntax
  • jump to declaration
  • find usage
  • full text search
  • smali debugger, check wiki page for setup and usage

Jadx-gui key bindings can be found here

See these features in action here: jadx-gui features overview

Download

After download unpack zip file go to bin directory and run:

  • jadx - command line version
  • jadx-gui - UI version

On Windows run .bat files with double-click
Note: ensure you have installed Java 11 or later 64-bit version. For Windows, you can download it from oracle.com (select x64 Installer).

Install

  • Arch Linux Arch Linux package AUR Version
    sudo pacman -S jadx
  • macOS homebrew version
    brew install jadx
  • Flathub Flathub Version
    flatpak install flathub com.github.skylot.jadx

Use jadx as a library

You can use jadx in your java projects, check details on wiki page

Build from source

JDK 11 or higher must be installed:

git clone https://github.com/skylot/jadx.git
cd jadx
./gradlew dist

(on Windows, use gradlew.bat instead of ./gradlew)

Scripts for run jadx will be placed in build/jadx/bin and also packed to build/jadx-<version>.zip

Usage

jadx[-gui] [command] [options] <input files> (.apk, .dex, .jar, .class, .smali, .zip, .aar, .arsc, .aab, .xapk, .jadx.kts)
commands (use '<command> --help' for command options):
  plugins	  - manage jadx plugins

options:
  -d, --output-dir                    - output directory
  -ds, --output-dir-src               - output directory for sources
  -dr, --output-dir-res               - output directory for resources
  -r, --no-res                        - do not decode resources
  -s, --no-src                        - do not decompile source code
  --single-class                      - decompile a single class, full name, raw or alias
  --single-class-output               - file or dir for write if decompile a single class
  --output-format                     - can be 'java' or 'json', default: java
  -e, --export-gradle                 - save as android gradle project
  -j, --threads-count                 - processing threads count, default: 4
  -m, --decompilation-mode            - code output mode:
                                         'auto' - trying best options (default)
                                         'restructure' - restore code structure (normal java code)
                                         'simple' - simplified instructions (linear, with goto's)
                                         'fallback' - raw instructions without modifications
  --show-bad-code                     - show inconsistent code (incorrectly decompiled)
  --no-xml-pretty-print               - do not prettify XML
  --no-imports                        - disable use of imports, always write entire package name
  --no-debug-info                     - disable debug info parsing and processing
  --add-debug-lines                   - add comments with debug line numbers if available
  --no-inline-anonymous               - disable anonymous classes inline
  --no-inline-methods                 - disable methods inline
  --no-move-inner-classes             - disable move inner classes into parent
  --no-inline-kotlin-lambda           - disable inline for Kotlin lambdas
  --no-finally                        - don't extract finally block
  --no-replace-consts                 - don't replace constant value with matching constant field
  --escape-unicode                    - escape non latin characters in strings (with \u)
  --respect-bytecode-access-modifiers - don't change original access modifiers
  --mappings-path                     - deobfuscation mappings file or directory. Allowed formats: Tiny and Tiny v2 (both '.tiny'), Enigma (.mapping) or Enigma directory
  --mappings-mode                     - set mode for handling the deobfuscation mapping file:
                                         'read' - just read, user can always save manually (default)
                                         'read-and-autosave-every-change' - read and autosave after every change
                                         'read-and-autosave-before-closing' - read and autosave before exiting the app or closing the project
                                         'ignore' - don't read or save (can be used to skip loading mapping files referenced in the project file)
  --deobf                             - activate deobfuscation
  --deobf-min                         - min length of name, renamed if shorter, default: 3
  --deobf-max                         - max length of name, renamed if longer, default: 64
  --deobf-whitelist                   - space separated list of classes (full name) and packages (ends with '.*') to exclude from deobfuscation, default: android.support.v4.* android.support.v7.* android.support.v4.os.* android.support.annotation.Px androidx.core.os.* androidx.annotation.Px
  --deobf-cfg-file                    - deobfuscation mappings file used for JADX auto-generated names (in the JOBF file format), default: same dir and name as input file with '.jobf' extension
  --deobf-cfg-file-mode               - set mode for handling the JADX auto-generated names' deobfuscation map file:
                                         'read' - read if found, don't save (default)
                                         'read-or-save' - read if found, save otherwise (don't overwrite)
                                         'overwrite' - don't read, always save
                                         'ignore' - don't read and don't save
  --deobf-use-sourcename              - use source file name as class name alias
  --deobf-res-name-source             - better name source for resources:
                                         'auto' - automatically select best name (default)
                                         'resources' - use resources names
                                         'code' - use R class fields names
  --use-kotlin-methods-for-var-names  - use kotlin intrinsic methods to rename variables, values: disable, apply, apply-and-hide, default: apply
  --rename-flags                      - fix options (comma-separated list of):
                                         'case' - fix case sensitivity issues (according to --fs-case-sensitive option),
                                         'valid' - rename java identifiers to make them valid,
                                         'printable' - remove non-printable chars from identifiers,
                                        or single 'none' - to disable all renames
                                        or single 'all' - to enable all (default)
  --integer-format                    - how integers are displayed:
                                         'auto' - automatically select (default)
                                         'decimal' - use decimal
                                         'hexadecimal' - use hexadecimal
  --fs-case-sensitive                 - treat filesystem as case sensitive, false by default
  --cfg                               - save methods control flow graph to dot file
  --raw-cfg                           - save methods control flow graph (use raw instructions)
  -f, --fallback                      - set '--decompilation-mode' to 'fallback' (deprecated)
  --use-dx                            - use dx/d8 to convert java bytecode
  --comments-level                    - set code comments level, values: error, warn, info, debug, user-only, none, default: info
  --log-level                         - set log level, values: quiet, progress, error, warn, info, debug, default: progress
  -v, --verbose                       - verbose output (set --log-level to DEBUG)
  -q, --quiet                         - turn off output (set --log-level to QUIET)
  --version                           - print jadx version
  -h, --help                          - print this help

Plugin options (-P<name>=<value>):
 1) dex-input: Load .dex and .apk files
    - dex-input.verify-checksum       - verify dex file checksum before load, values: [yes, no], default: yes
 2) java-convert: Convert .class, .jar and .aar files to dex
    - java-convert.mode               - convert mode, values: [dx, d8, both], default: both
    - java-convert.d8-desugar         - use desugar in d8, values: [yes, no], default: no
 3) kotlin-metadata: Use kotlin.Metadata annotation for code generation
    - kotlin-metadata.class-alias     - rename class alias, values: [yes, no], default: yes
    - kotlin-metadata.method-args     - rename function arguments, values: [yes, no], default: yes
    - kotlin-metadata.fields          - rename fields, values: [yes, no], default: yes
    - kotlin-metadata.companion       - rename companion object, values: [yes, no], default: yes
    - kotlin-metadata.data-class      - add data class modifier, values: [yes, no], default: yes
    - kotlin-metadata.to-string       - rename fields using toString, values: [yes, no], default: yes
    - kotlin-metadata.getters         - rename simple getters to field names, values: [yes, no], default: yes
 4) rename-mappings: various mappings support
    - rename-mappings.format          - mapping format, values: [AUTO, TINY_FILE, TINY_2_FILE, ENIGMA_FILE, ENIGMA_DIR, SRG_FILE, XSRG_FILE, CSRG_FILE, TSRG_FILE, TSRG_2_FILE, PROGUARD_FILE], default: AUTO
    - rename-mappings.invert          - invert mapping on load, values: [yes, no], default: no

Environment variables:
  JADX_DISABLE_XML_SECURITY - set to 'true' to disable all security checks for XML files
  JADX_DISABLE_ZIP_SECURITY - set to 'true' to disable all security checks for zip files
  JADX_ZIP_MAX_ENTRIES_COUNT - maximum allowed number of entries in zip files (default: 100 000)
  JADX_CONFIG_DIR - custom config directory, using system by default
  JADX_CACHE_DIR - custom cache directory, using system by default
  JADX_TMP_DIR - custom temp directory, using system by default

Examples:
  jadx -d out classes.dex
  jadx --rename-flags "none" classes.dex
  jadx --rename-flags "valid, printable" classes.dex
  jadx --log-level ERROR app.apk
  jadx -Pdex-input.verify-checksum=no app.apk

These options also work in jadx-gui running from command line and override options from preferences' dialog

Troubleshooting

Please check wiki page Troubleshooting Q&A

Contributing

To support this project you can:

  • Post thoughts about new features/optimizations that important to you
  • Submit decompilation issues, please read before proceed: Open issue
  • Open pull request, please follow these rules: Pull Request Process

Licensed under the Apache 2.0 License

jadx's People

Contributors

13-beta2 avatar 5idereal avatar alissonlauffer avatar andreikud avatar asashour avatar away-pp avatar bagipro avatar busmaker avatar choiman1559 avatar daramos avatar dependabot[bot] avatar donlon avatar flxme avatar henry2o1o avatar iscle avatar jmlitfi avatar jpstotz avatar mino260806 avatar mrikso avatar nebelnidas avatar neospb avatar nitram84 avatar pubiqq avatar s-trace avatar skylot avatar surendrajat avatar trunkator avatar yawkat avatar yotamn avatar zhongqingsong avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jadx's Issues

Found issues in decompilation

Thanks for your brilliant work. I found two cases which provides wrong results. Hope you could fix it.

//Src 1

public class JavaTest {
    public static void main(String[] args) throws Exception {
        boolean a = true;
        int b = 3;
        if (a || b > 2)
          b++;

        if (!a || ( b >= 0 && b <=11))  
            System.out.println ("OK");
        else
            System.out.println ("Not Reach");   
        System.out.println ("End");
    }
}

//Src 2

public class JavaTest {
    public static void main(String[] args) throws Exception {
        int a = 3 * 5;
        String s = null;

        switch (a % 4) {
            case 1:
                s = "1";
                break;
            case 2:
                s = "2";
                break;
            case 3:
                s = "3";
                break;
            case 4:
                s = "4";
                break;
            default:
                System.out.println ("Not Reach");   
        }
        System.out.println (s);
    }
}

try within finally - inconsistent code

The following statement generates a inconsistent code UnsupportedOperationException.

public void tryWithinFinally() throws IOException {
    File file = File.createTempFile("test", "txt");
    OutputStream outputStream = new FileOutputStream(file);
    try {
        outputStream.write(1);
    } finally {
        try {
            outputStream.close();
            file.delete();
        } catch (IOException ignored) {
        }
    }
}

jadx-gui: Save all code is different

The code created by the "Save all" function is different from the code displayed in jadx-gui:

The method parameter names are not correctly named. Example:

jadx-gui shows:

public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
progress = (progress / 1000) * 1000;
...

"Save all" creates:

public void onProgressChanged(SeekBar r1_SeekBar, int r2i, boolean r3z) {
if (fromUser) {
int progress = (progress / 1000) * 1000;

broken native type support (boolean, int)

version 0.54 is a regression to 0.53 in that matter.
e.g.:
with 0.54:

            booleanValue = (z && threadStorage.getReducer(str).andReduction.booleanValue()) ? 1 : null;

while with 0.53:

           booleanValue = (z && threadStorage.getReducer(str).andReduction.booleanValue()) ? 1 : 0;

Deobfuscation search engine

Congrats in implementing the deob feature, but I have an idea that I like to propose to JADX first. Some sort of java search engine. or signature recognition to identify obfuscated opensource libraries online on either Github or Google code. For example, a quick search revealed the Google gson library was used. If we could automate this thing, wouldn't it be better?

deobfssearch

No verification of the same object references in different registers

The code below shows the meaning of the problem

.method private calc(DDLSomeObject;)LSomeObject;
    .locals 22
    .param p1, "arg1"    # D
    .param p3, "arg2"    # D
    .param p5, "arg3"    # LSomeObject;

    .prologue
    .line 54
    new-instance v17, LSomeObject;

    move-object/from16 v0, v17

    move-object/from16 v1, p5

    invoke-direct {v0, v1}, LSomeObject;-><init>(LSomeObject;)V

    .line 59
    .local v17, "localSomeObject":LSomeObject;

The Jadx produces:

    private SomeObject calc(double arg1, double arg2, SomeObject arg3) {

        SomeObject someObject = localSomeObject;
        SomeObject someObject2 = arg3;

I think there must be some mechanism to control where the reference to the object is located. (or make wrap instruction?)

Nested 'if'

I think you can find a solution faster than me. Below is a test that does not decompile.

package jadx.samples;

public class TestConditions2 extends AbstractTest {

    private boolean a0 = false;
    private int a1 = 1;
    private int a2 = 2;
    private int a3 = 1;
    private int a4 = 2;

    public void test1a() {

    }

    public void test1b() {

    }

    public void test1c() {

    }

    public boolean test1() {
        if (a0) {
            if ((a1 == 0) || (a2 == 0)) {
                return false;
            }
        } else {
            if ((a3 == 0) || (a4 == 0)) {
                return false;
            }
        }

        test1a();
        test1b();
        test1c();

        return true;
    }

    @Override
    public boolean testRun() throws Exception {
        assertTrue(test1());
        return true;
    }

    public static void main(String[] args) throws Exception {
        new TestConditions2().testRun();
    }
}

Run jadx-gui.bat show a windows console and jadx application

when run jadx-gui.bat, It's show a windows console and jadx application.
Why not use below command only show jadx applition? The command is:
start "" javaw.exe %DEFAULT_JVM_OPTS% %JAVA_OPTS% %JADX_GUI_OPTS% -classpath "%CLASSPATH%" jadx.gui.JadxGUI %CMD_LINE_ARGS%

a batch errors like this

static {
    /* JADX: method processing error */

/*
Error: jadx.core.utils.exceptions.DecodeException: Load method exception in method: com.android.contacts.dialpad.DialpadFragment.():void
at jadx.core.dex.nodes.MethodNode.load(MethodNode.java:113)
at jadx.core.dex.nodes.ClassNode.load(ClassNode.java:256)
at jadx.core.ProcessClass.process(ProcessClass.java:34)
at jadx.core.ProcessClass.processDependencies(ProcessClass.java:59)
at jadx.core.ProcessClass.process(ProcessClass.java:42)
at jadx.api.JadxDecompiler.processClass(JadxDecompiler.java:281)
at jadx.api.JavaClass.decompile(JavaClass.java:59)
at jadx.api.JadxDecompiler$1.run(JadxDecompiler.java:161)
Caused by: jadx.core.utils.exceptions.DecodeException: in method: com.android.contacts.dialpad.DialpadFragment.():void
at jadx.core.dex.instructions.InsnDecoder.decodeInsns(InsnDecoder.java:46)
at jadx.core.dex.nodes.MethodNode.load(MethodNode.java:98)
... 7 more
Caused by: java.lang.IllegalArgumentException: bogus opcode: 00e9
at com.android.dx.io.OpcodeInfo.get(OpcodeInfo.java:1196)
at com.android.dx.io.OpcodeInfo.getFormat(OpcodeInfo.java:1212)
at com.android.dx.io.instructions.DecodedInstruction.decode(DecodedInstruction.java:72)
at jadx.core.dex.instructions.InsnDecoder.decodeInsns(InsnDecoder.java:43)
... 8 more
/
/

// Can't load method instructions.
*/
throw new UnsupportedOperationException("Method not decompiled: com.android.contacts.dialpad.DialpadFragment.():void");
}

some static can work well. For example:
private static final int[] DEFAULT_RESOURCES_ID;
and
DEFAULT_RESOURCES_ID = new int[] { 21308......};

I think this maybe one bug for all statution like this.

Beat Regards.

Enhancement: THIS naming removal step

Could you please add a pass to optimize class calls from 'classname' to 'this' instead

class Something {
int a;
private void example() {
Something something = this;
something.whatever();
something.a = 123;
}
}

Would be nicer if it showed:

this.whatever();
this.a = 123;

etc

synchronized statement - inconsistent code

the following snipped generates inconstent code warning:

static class MyException extends Exception {
    MyException() {
    }

    MyException(String msg, Throwable rootCause) {
        super(msg);
    }
}

static class MyThread extends Thread {

    MyException e = null;

    MyThread()
    {
        super();
    }

    public void run()
    {
        synchronized (this)
        {
            try
            {
                throw new MyException();
            }
            catch (MyException e)
            {
                this.e = e;
            }
            catch (Exception x)
            {
                this.e = new MyException("MyThread", x);
            }
        }
    }
}

Class not found - classpath problem?

Hello,

something like this usually pops out on me, running jadx/jadx-gui.

WARN  - Class not found: android.app.SharedElementCallback
WARN  - Class not found: android.transition.Transition$EpicenterCallback
WARN  - Class not found: android.transition.Transition$EpicenterCallback
WARN  - Class not found: android.media.VolumeProvider
WARN  - Class not found: android.media.session.MediaController$Callback
WARN  - Class not found: android.media.session.MediaSession$Callback
WARN  - Class not found: android.print.PrintDocumentAdapter
WARN  - Class not found: android.print.PrintDocumentAdapter
WARN  - Class not found: android.view.View$OnApplyWindowInsetsListener
WARN  - Class not found: android.view.View$OnApplyWindowInsetsListener

Is this something we can fix with classpath or possibly upgrading dependency jadx-core/clsp-data/android-4.3.jar to newer version?

If so how do you generate/update core.jcst in said JAR to newer SDK version (such as 21/22) ?
Thank you

Inconsistent code error

Hi. First I would like to say thank you for this great tool. I use it a lot to fix compatibility issues of Android apps running on Blackberry Android Runtime.

The package com.google.android.gsf seems to have some major decompiling problems, especially the following files:

com/google/android/gsf/checkin/CheckinRequestBuilder.java
com/google/android/gsf/checkin/ServiceDumpSys.java

Can you look into this? A fix would be highly appreciated.

File on Mega:
https://mega.co.nz/#!xJUUxCbC!I6iZCpMk8dvCyKT5kBB5XXjZH3LoXF5V9YDUK5u3pWA

Try/catch wrap count limit reached

Hello,

I stumbled upon an apk which generates the " Try/catch wrap count limit reached" exception for 2 methods:
com/umeng/analytics/MobclickAgent.java
com/umeng/common/net/j.java

Also, other errors are present in:
com/umeng/common/b.java
com/umeng/common/net/DownloadingService.java

This has been run with --show-bad-code.

The link to the apk:
http://www37.zippyshare.com/v/44023399/file.html

Thank you.

if-else statement decomplite lose else branch

java source:

package com.tencent.map;

import com.tencent.map.AppUpgradeMgr;
import com.tencent.map.util.LogUtil;
import android.os.Handler;
import com.tencent.map.net.NetUser;

class AppUpgradeMgr$1 extends NetUser {
    private final /* synthetic */ Handler val$handler;
    private final /* synthetic */ String val$curVersion;

    @Override
    public void onResult(final boolean isResultOK, final byte[] data, final String charset) {
        if (!isResultOK) {
            this.val$handler.sendEmptyMessage(3);
            LogUtil.i("\u5e94\u7528\u7a0b\u5e8f\u5347\u7ea7\u670d\u52a1\u68c0\u67e5\u5931\u8d25");
        } else {
            try {
                AppUpgradeMgr.access$0(this.val$curVersion, data, charset, this.val$handler);
            } catch (Exception e) {
                e.printStackTrace();
                this.val$handler.sendEmptyMessage(3);
            }
        }
    }
}

but use jadx decomplite:

package com.tencent.map;

import android.os.Handler;
import com.tencent.map.net.NetUser;
import com.tencent.map.util.LogUtil;

class AppUpgradeMgr$1 extends NetUser {
    private final /* synthetic */ String val$curVersion;
    private final /* synthetic */ Handler val$handler;

    AppUpgradeMgr$1(Handler A_0, String A_1) {
        this.val$handler = A_0;
        this.val$curVersion = A_1;
    }

    public void onResult(boolean isResultOK, byte[] data, String charset) {
        if (isResultOK) {
            try {
                AppUpgradeMgr.access$0(this.val$curVersion, data, charset, this.val$handler);
                return;
            } catch (Exception e) {
                e.printStackTrace();
                this.val$handler.sendEmptyMessage(3); 
            }
        }
        this.val$handler.sendEmptyMessage(3);
        LogUtil.i("\u5e94\u7528\u7a0b\u5e8f\u5347\u7ea7\u670d\u52a1\u68c0\u67e5\u5931\u8d25");
    }
}

break from a synchronized block inside a while loop pulls trailing code into the synchronized block

The following code:

    public static class TestCls {

        private int x = 0;

        public void f() {}

        public void test() {
            while(true) {
                synchronized (this) {
                    if(x == 0) {
                        throw new IllegalStateException("bad luck");
                    }
                    x++;

                    if(x == 10) break;
                }

                x++;
                f();
            }
        }
    }

decompiles as:

public class TestCls {
    private int x;

    public TestCls() {
        this.x = 0;
    }

    public void f() {
    }

    public void test() {
        while (true) {
            synchronized (this) {
                if (this.x == 0) {
                    throw new IllegalStateException("bad luck");
                }
                this.x++;
                if (this.x == 10) {
                    return;
                }
                this.x++;
                f();
            }
        }
    }
}

As you can see, the block after the synchronized block:

                x++;
                f();

gets pulled into the synchronized block in the decompile.

inconsistent code

The following function fails to decompile. Thank you!

    public static byte[] decodeFromFile(String filename) throws IOException {

        byte[] decodedData = null;

        InputStream bis = null;
        try {
            File file = new File(filename);
            byte[] buffer;
            int length = 0;
            int numBytes;

            if (file.length() > Integer.MAX_VALUE) {
                throw new IOException("File is too big for this convenience method (" + file.length() + " bytes).");
            }
            buffer = new byte[(int) file.length()];

            bis = new BufferedInputStream(new FileInputStream(file));
            while ((numBytes = bis.read(buffer, length, 4096)) >= 0) {
                length += numBytes;
            }

            decodedData = new byte[length];
            System.arraycopy(buffer, 0, decodedData, 0, length);
        } catch (IOException e) {
            throw e;
        } finally {
            try {
                bis.close();
            } catch (Exception e) {
            }
        }

        return decodedData;
    }

Regenerate R.java file from values/public.xml and values/strings.xml in Android

I'm working on a feature for jadx that would regenerate the gen/R.java file (and modify the other .java files to use 'labels', like R.string.stringName in methods like setText() and getString()) -- from the values/public.xml and values/strings.xml files recovered from Android .apk files. (I assume that decompiling Android .apk files is a/the principal use of jadx.)

Would this be a feature you'd welcome in jadx?

The (Android) .java files frequently contain statements like:

setText(R.string.save_exists);    

R.string.save_exists is defined in gen/R.java as:

public static final class string {
    public static final int save_exists=0x7f050033;
}

The code jadx reconstructs looks like:

this.view.setText(2131034163);

The jadx feature would find functions like setText(number,...) and getString(number) and replace the number with 'R.string.save_exists' -- and would append a comment to line containing the text of the string:

this.view.setText(save_exists);  // String: "A file with the same name already exists" 0x7f05033

This would make it much easier to understand what was going on in decompiled Android code.

Premature Break in decompiled code

RegionMaker.makeEndlessLoop inserts 'break' statements too early. This is because the exitEdges are set too 'high'.

public static void writeMore(String fid, Bytes content) {
    boolean tryMkdir = true;
    File ff = new File(fid);
    while (true) {
        prt("before try");
        try {
            new FileOutputStream(fid).close();
            break;
        } catch (Exception e) {
            if (tryMkdir) {
                File parentFile = ff.getParentFile();
                tryMkdir = false;
                prt("  then block of if stmt in catch, before 'continue'");
            } else {
                break;   <------------------------------------------------------PREMATURE BREAK
                prt("  after if stmt in catch block");
                if (ff.exists()) {
                    prt("then more stuff");
                } else {
                    prt("else more stuff");
                }
                prt("  end of after if stmt in catch block");
            }
        }
    }
    prt("after catch, before break");
    prt("after while loop");
}

The original code was:

public static void writeMore(String fid, Bytes content) {
  boolean tryMkdir = true;
  File ff = new File(fid);      
  while (true) {
prt("before try");
try {
  FileOutputStream fos = new FileOutputStream(fid);
  fos.close();

} catch (Exception ex) {
  if (tryMkdir) {  // On first error, try creating the base dirs.
    File parent = ff.getParentFile();
    tryMkdir = false;
    prt("  then block of if stmt in catch, before 'continue'");
    continue;
  }
  prt("  after if stmt in catch block");
  if (ff.exists()) {
    prt("then more stuff");
  } else {
    prt("else more stuff");
  }
  prt("  end of after if stmt in catch block");
}
prt("after catch, before break");
break;
  } // end of while true, loop to allow retry of fos.write after mkdir
  prt("after while loop");
} // end of writeFile

-- The 'prt' and 'if (ff.exists())...' statements are to create more than just one block that are part of the 'tail' of blocks that need to be included in the loopBlocks.

I think either LoopInfo constructor, which set loopBlocks by calling BlockUtils.getAllPathsBlocks(start, end) must broadened to include these extra blocks; or getExitEdges() needs to traverse successors from what it thinks are the exit edges down to: include all successor blocks of any exit block which has not predecessor outside the loop (or its extension).

RegionMaker.makeEndlessLoop() calls getExitEdges and insertBreak which inserts the 'break' instruction prematurely.


I'm pretty much given up trying to use GIT/EclipseGit/a forked repository/GitHubPullRequests as means of trying to contribute code changes. I don't see how to get it to work where I have some debugging code in my environment that isn't to go in the master repo while contributing other code.

constructor usage decompiles wrong

The contructor of internal class ViewHolder in method doSomething() contains a additional comma after decompilation new ViewHolder(, root, name); Thank you.

    public class Main {

        void doSomething(String root, String name) {
            ViewHolder viewHolder = new ViewHolder(root, name);
        }

        private final class ViewHolder {
            private int mElements = 0;
            private final String mRoot;
            private String mName;

            private ViewHolder(String root, String name) {
                this.mRoot = root;
                this.mName = name;
            }
        }
    }

JadxOverflowException: Regions count limit reached

The following sample failes to decompile, it generates a JadxOverflowException.

public static int countBits(int k) {
    int accum = 0;
    while (true) {
        switch (k) {
            case 0:
                return accum;
            default:
                accum++;
                k >>= 1;
        }
    }
}

Rename class files

Need this feature, esp when dealing with classes without original names. IMHO, this feature should prioitize targeting classes first as it's impossible to save case insensitive filenames on certain OSs

Missing variable

This code will be wrong when decompile:

public class Main2 {
    static Vector2 targetPos = new Vector2();
    static int tileX, tileY;
    static Vector2 directVect = new Vector2();
    static Vector2 newPos = new Vector2();

    public static void main(String[] args) {

        while (true) {
            newTarget();
            System.out.printf("x: " + newPos.x + " y: " + newPos.y);
        }

    }

    private static void newTarget() {
        Random rd = new Random();
        int direction = rd.nextInt(7);
        switch (direction) {
        case 0:
            targetPos.x = (float) (((tileX + 1) * 55) + 55);
            targetPos.y = (float) (((tileY + 1) * 35) + 35);
            break;
        case 2:
            targetPos.x = (float) (((tileX + 1) * 55) + 55);
            targetPos.y = (float) (((tileY - 1) * 35) + 35);
            break;
        case 4:
            targetPos.x = (float) (((tileX - 1) * 55) + 55);
            targetPos.y = (float) (((tileY - 1) * 35) + 35);
            break;
        case 6:
            targetPos.x = (float) (((tileX - 1) * 55) + 55);
            targetPos.y = (float) (((tileY + 1) * 35) + 35);
            break;
        default:
            break;
        }
        directVect.x = targetPos.x - newPos.x;
        directVect.y = targetPos.y - newPos.y;

        float hPos= (float) Math
                .sqrt((double) ((directVect.x * directVect.x) + (directVect.y * directVect.y)));
        directVect.x /= hPos;
        directVect.y /= hPos;

    }

}

class Vector2

{

    public float x;

    public float y;

    public Vector2() {

        this.x = 0.0f;

        this.y = 0.0f;

    }

    public Vector2(float x, float y) {

        this.x = x;

        this.y = y;

    }

    public boolean equals(Vector2 other) {

        return (this.x == other.x && this.y == other.y);

    }

}

Codes:

        directVect.x = targetPos.x - newPos.x;
        directVect.y = targetPos.y - newPos.y;

Will become to:

        targetPos.x -= newPos.x;
        targetPos.y -= newPos.y;

small dex with UnsupportedOperationException

First, thank you for this wonderful decompiler. It outputs very nice code.

This seems to me very low priority for the jadx project, but I found this dex which has 2 small methods (only 1-2 instructions). Seems to be a part of a new apk packer. Instructions could be something more exotic. Each method has output for decompiled code (example for method a):

public java.lang.String a() {
throw new UnsupportedOperationException("Method not decompiled: a.a.a.a.a.a():java.lang.String");
/* JADX: method processing error /
/

Error: java.lang.NullPointerException
at jadx.core.dex.visitors.regions.ProcessVariables.addToUsageMap(ProcessVariables.java:286)
at jadx.core.dex.visitors.regions.ProcessVariables.visit(ProcessVariables.java:184)
at jadx.core.dex.visitors.DepthTraversal.visit(DepthTraversal.java:27)
at jadx.core.dex.visitors.DepthTraversal.visit(DepthTraversal.java:16)
at jadx.core.ProcessClass.process(ProcessClass.java:22)
at jadx.api.JadxDecompiler.processClass(JadxDecompiler.java:209)
at jadx.api.JavaClass.decompile(JavaClass.java:59)
at jadx.api.JadxDecompiler$1.run(JadxDecompiler.java:133)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
/
/

this = this;
r44 = (r40 > r32 ? 1 : (r40 == r32 ? 0 : -1));
L_0x0002:
if (r11 != r6) goto L_0x0002;
*/

Fallback mode for jadx prints the same, without the stack trace.

Thank you.

Enum class processing for obfuscated code

This fix was greate. But there is new issue, that in the generated code used field with not aliased name.

For example:

enum Example {
    ITEM1,
    ITEM2
}

and somewhere in the code we can found

Example.b

I made the fix, that processes the SPUT instruction and creates link with restored field name and the "real" enum class field (also I added FieldInfo caching). But there I found issue in the architecture that code generator worked before all resolve is done. So we get some code before restore the field name and some after.

I can make a patch that provides a workaround (2-pass like deobfuscator) to resolve this issue.

In the future, I think there is need to change the architecture to make some steps before generate the code. But this is not my privilege, I think so.

What do you think?

Wrong with complex if/else condition

Thanks your for very fast bug correction, but this is other error.
Below code will failed to decompile:

public class Main {
    static boolean cond1 = true;
    static boolean cond2 = false;
    static boolean backPlay = false;
    static boolean soundPlay = false;
    static boolean cond1_1 = false;
    static boolean result = false;
    static boolean vibration = false;

    public static void main(String[] args) {
        takeAction(1, 2);
    }

    public static void takeAction(int x, int y) {

        if (cond1) {

            if (cond1_1) {
                action(x);
                backPlay = true;
                soundPlay = true;
                action(y);
            } else {
                action(1);
                backPlay = false;
                soundPlay = false;
            }
        } else if (cond2) {
            action(x);
            if (vibration) {
                result = false;
            } else {
                result = true;
            }

            vibration = result;
        }
    }

    public static void action(int id) {

    }
}

Wrong type: PHI:

The method 'load' in the following sample failed to decompile with 'ERROR - Wrong type: PHI:'

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

public class ClassA {

public static final class Result {
    private final int mCode;
    public Result(int code) {
        mCode = code;
    }

    public int getCode() {
        return mCode;
    }
}

public Result load(byte[] data) throws IOException {
    InputStream inputStream = null;
    try {
        inputStream = getInputStream(data);
        decode(inputStream);
        return new Result(400);
    } finally {
        closeQuietly(inputStream);
    }
}

private InputStream getInputStream(byte[] data) throws IOException {
    return new ByteArrayInputStream(data);
}

private int decode(InputStream inputStream) throws IOException {
    return inputStream.available();
}

private void closeQuietly(InputStream is) {
    try {
        is.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

}

inconsistent code

The following function funcA() does not decompile. Thank you!

void funcA(List<Integer> data) throws InterruptedException {
    for ( ;; ) {
        try {
            funcB(data);
            break;
        } catch (Exception ex) {
            if (funcC()) {
                throw ex;
            }
            data.clear();
        }
        Thread.sleep(100);
    }
}

private boolean funcB(List<Integer> data) {
    return false;
}

private boolean funcC() {
    return true;
}

Issues in decompilation complicate if

Has been long time since i reported same bug, but it still exist (latest unstable version), below test code will wrong when decompile:

static int executedCount = 0;
static boolean finished =false;
static int repeatCount=2;

public static void main(String[] args) throws Exception {
    float delta = 5;
    Object object = null;
    check(delta, object);
}

static boolean check(float delta, Object object) {
    if (executedCount != repeatCount) {
        if (isRun(delta, object)) {
            if (finished) {
                return true;
            }

            if (repeatCount == -1) {
                ++executedCount;
                action();
                return false;
            }

            ++executedCount;
            if (executedCount >= repeatCount) {
                return true;
            }

            action();
        }

    }

    return false;
}

public static void action() {
}

public static boolean isRun(float delta, Object object) {
    return true;
};

Does not decompile apk method

Hello,

The method:
api.appgamefree.ftl.Application.attachBaseContext(java.lang.String):java.lang.String
present in the apk below:
https://play.google.com/store/apps/details?id=api.appgamefree.ftl
does not decompile.

The apk uses DexProtector from http://dexprotector.com.

Thank you.

Below is the output of jadx -d:

INFO - loading ...
DEBUG - processing threads count: 2
INFO - processing ...
INFO - processing class api.appgamefree.ftl.Application ...
DEBUG - Missing block: B:24:0x03d0 in api.appgamefree.ftl.Application.attachBaseContext(java.lang.String):java.lang.String
DEBUG - Missing block: B:26:0x03db in api.appgamefree.ftl.Application.attachBaseContext(java.lang.String):java.lang.String
DEBUG - Missing block: B:27:0x03e2 in api.appgamefree.ftl.Application.attachBaseContext(java.lang.String):java.lang.String
DEBUG - Missing block: B:28:0x03eb in api.appgamefree.ftl.Application.attachBaseContext(java.lang.String):java.lang.String
DEBUG - Missing block: B:29:0x03f2 in api.appgamefree.ftl.Application.attachBaseContext(java.lang.String):java.lang.String
DEBUG - Missing block: B:30:0x03fb in api.appgamefree.ftl.Application.attachBaseContext(java.lang.String):java.lang.String
DEBUG - Missing block: B:31:0x0402 in api.appgamefree.ftl.Application.attachBaseContext(java.lang.String):java.lang.String
DEBUG - Missing block: B:32:0x040b in api.appgamefree.ftl.Application.attachBaseContext(java.lang.String):java.lang.String
ERROR - Inconsistent code in method: api.appgamefree.ftl.Application.attachBaseContext(java.lang.String):java.lang.String
INFO - done
ERROR - 1 errors occured in following nodes:
ERROR - Method: api.appgamefree.ftl.Application.attachBaseContext(java.lang.String):java.lang.String

unresolved android attributes

The symbolic names of the attributes (R.attr.colorControlActivated and R.attr.colorAccent) are not resolved (showing 'only' the id), although this beneficial feature is working elsewhere.

public void androidAttrArray() {
    TypedArray obtainStyledAttributes = getActivity().obtainStyledAttributes(new int[]{
            R.attr.colorControlActivated,
            R.attr.colorAccent});
    if (obtainStyledAttributes != null) {
        obtainStyledAttributes.getColor(0, 0);
        obtainStyledAttributes.getColor(1, 0);
        obtainStyledAttributes.recycle();
    }
}

A trouble in case of the same names of a constructor and a method in a class

Thank you for a brilliant app!
But using it I've found some error.
If a class contents a constructor with the same name as a one of the class's methods, it
causes an error after decompilation a .dex file due to exchanging of the method's name from "Name" to "Name_", meanwhile the method is called from another class using it's actual name "Name".

Example.
The class Bug has a constructor named "Bug", of cource, and a method named "Bug" too.
origin_source
After decompilation it contents a method named "Bug_" instead of "Bug".
bug_source_after_decompilation
But in the main method it's being called as "Bug".
main_source_after_decompilation

decompiled line for instructions

Hello,

I needed to know the decompiled line for instructions (InsnNode). As the decompiled line was not set for InsnNode, I added the line:

insn.setDecompiledLine(code.getLine());

in jadx.core.codegen.InsnGen, at line 223 (in makeInsn function)

After this, I add insn.getDecompiledLine() to cls.getDecompiledLine() to find the real line in the final code.

Is this ok to be added to the official repo ?
Or is there a better way of setting the line for instructions ?

Thank you.

Wrong with complex if/else condition and for loop (while loop)

I found this bug long ago but today i find a simple test case to report.
This occur with latest code base.

public class Main {
    static boolean[][] occupied = new boolean[30][70];
    static boolean placingStone = true;

    public static void main(String[] args) {

        if (findPoint(14, 20)) {
            System.out.printf("Found!!");
        } else {
            System.out.printf("Not Found!!");
        }

    }

    private static boolean findPoint(int xx, int yy) {
        int[] extraArray = new int[] { 10, 45, 300, 600, 800 };

        if (extraArray != null && placingStone) {
            for (int i = 0; i < extraArray.length; i += 2) {
                int tX;
                int tY;
                if (yy % 2 == 0) {
                    if (extraArray[i + 1] % 2 == 0) {
                        tX = xx + extraArray[i];
                    } else {
                        tX = extraArray[i] + xx - 1;
                    }

                    tY = yy + extraArray[i + 1];
                } else {
                    tX = xx + extraArray[i];
                    tY = yy + extraArray[i + 1];
                }

                if (tX < 0 || tY < 0 || tY % 2 != 0 && tX > 28 || tY > 118
                        || occupied[tX][tY]) {
                    return false;
                }
            }
        }

        return true;
    }

}

inconsistent code

The following function cacheItems1() does not decompile, however cacheItems2() does. Thank you!

private static class MyItem {
    int idx;
    String name;
}

private final Map<Integer, MyItem> mCache = new HashMap<>();

void cacheItems1(MyItem[] items) {
    synchronized(mCache) {
        for (int i = 0; i < items.length; ++i) {
            MyItem existingItem = mCache.get(items[i].idx);
            if (null == existingItem) {
                mCache.put(items[i].idx, items[i]);
            } else {
                existingItem.name = items[i].name;
            }
        }
    }
}

void cacheItems2(MyItem[] items) {
    synchronized(mCache) {
        for (MyItem item : items) {
            MyItem existingItem = mCache.get(item.idx);
            if (null == existingItem) {
                mCache.put(item.idx, item);
            } else {
                existingItem.name = item.name;
            }
        }
    }
}

Exception in thread java.lang.OutOfMemoryError: Java heap space

I got OutOfMemoryError exception messages:

Exception in thread "pool-1-thread-8" java.lang.OutOfMemoryError: Java heap space
ERROR - OutOfMemoryError in pass: CodeShrinker in method: l.a(java.util.ArrayList, java.lang.String):void java.lang.OutOfMemoryError: Java heap space

Exception in thread "pool-1-thread-3" java.lang.OutOfMemoryError: Java heap space
at java.lang.String.valueOf(Unknown Source)
at jadx.core.utils.Utils.makeQualifiedObjectName(Utils.java:24)
at jadx.core.codegen.TypeGen.signature(TypeGen.java:14)
at jadx.core.dex.info.MethodInfo.(MethodInfo.java:34)
at jadx.core.dex.info.MethodInfo.fromDex(MethodInfo.java:43)
at jadx.core.dex.instructions.InsnDecoder.invoke(InsnDecoder.java:661)
at jadx.core.dex.instructions.InsnDecoder.decode(InsnDecoder.java:541)
at jadx.core.dex.instructions.InsnDecoder.process(InsnDecoder.java:58)
at jadx.core.dex.nodes.MethodNode.load(MethodNode.java:96)
at jadx.core.dex.nodes.ClassNode.load(ClassNode.java:211)
at jadx.core.dex.nodes.ClassNode.load(ClassNode.java:218)
at jadx.core.ProcessClass.process(ProcessClass.java:20)
at jadx.api.JadxDecompiler.processClass(JadxDecompiler.java:196)
at jadx.api.JavaClass.decompile(JavaClass.java:59)
at jadx.api.JadxDecompiler$1.run(JadxDecompiler.java:130)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)

Sombody help me how to fix this problem!

switch lost some break

package tutorial;

public class testSwitch {
boolean isLongtouchable;
boolean isMultiTouchZoom;
float multiTouchZoomOldDist;
boolean isCanZoomIn;
boolean isCanZoomOut;
boolean isScrolling;

void onTouchEvent(int action) {
    switch (action & 255) {
    case 0:
        this.isLongtouchable = true;
        break;
    case 1:
    case 6:
        if (this.isMultiTouchZoom) {
            this.isMultiTouchZoom = false;
        }
        break;
    case 2:
        if (this.isMultiTouchZoom) {
            float dist = multiTouchZoomOldDist;
            if (Math.abs(dist - this.multiTouchZoomOldDist) > 10.0f) {
                float scale = dist / this.multiTouchZoomOldDist;
                if ((scale > 1.0f && this.isCanZoomIn) || (scale < 1.0f && this.isCanZoomOut)) {
                    this.multiTouchZoomOldDist = dist;
                }
            }
            return;
        }
        break;
    case 5:
        this.multiTouchZoomOldDist = action;
        if (this.multiTouchZoomOldDist > 10.0f) {
            this.isMultiTouchZoom = true;
            this.isLongtouchable = false;
            return;
        }
        break;
    }
    if (this.isScrolling && action == 1) {
        this.isScrolling = false;
    }
}

}

Wrong variable initialization

The method-result of type String is initialized with boolean value 'true' after decompilation.

String getDisplayNameFromUri(Context context, Uri contentUri) {
    Cursor cursor = null;
    try {
        String[] projection = {OpenableColumns.DISPLAY_NAME};
        cursor = context.getContentResolver().query(contentUri, projection, null, null, null);
        int column_index = cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME);
        cursor.moveToFirst();
        return cursor.getString(column_index);
    } finally {
        if (cursor != null) {
            cursor.close();
        }
    }
}

Break From Labeled Block, Inconsitent Code error on decompile

A break from within a labeled block decompiles incorrectly and gives Missing Block and Inconsistent Code errors. I think the problem is near jadx/core/dex/visitors/regions/IfMakerHelper.java restructureIf() where it decides 'badThen' and 'badElse' for the if statement with the break in it. I don't understand what allPathsFromIf() is doing or what setOutBlock() does/means, but I think it is deciding that the 'then' and 'else' blocks are nops and ignore the fact that the IF instruction has the effect of breaking out of an enclosing block. (Not beautiful test case code, but I think it is the sort of thing I'm encountering in some real subject code in Android system code.

Test case:

package ribo;

import android.app.Activity;
public class LabelBrk extends Activity {

public static int a=1, b=2, c=8, d=9;
public void dBreakFromBlock() {
    //prt("before");
    label: {
        //prt("top of block");
        if (d == 10) {
            prt("outer if top");
            if (a >= b) break label;
        }
        prt("after cond break");
     }
     prt("afterBlock");
  }
  public static void prt(String str) { System.out.println(str); }
} // end of LabelBrk class

Console output during decompile:

09:16:03.494 [main] INFO  jadx.api.JadxDecompiler - loading ...  
09:16:03.644 [main] DEBUG jadx.api.JadxDecompiler - processing threads count: 8
09:16:03.644 [main] INFO  jadx.api.JadxDecompiler - processing ...
09:16:03.784 [pool-1-thread-5] DEBUG j.c.d.visitors.regions.IfMakerHelper - Stop processing blocks after 'if': B:3:0x000f, method: ribo.LabelBrk.dBreakFromBlock():void
09:16:03.784 [pool-1-thread-5] DEBUG j.c.d.visitors.regions.IfMakerHelper - Stop processing blocks after 'if': B:3:0x000f, method: ribo.LabelBrk.dBreakFromBlock():void
09:16:03.794 [pool-1-thread-5] DEBUG j.c.d.visitors.regions.CheckRegions -  Missing block: B:3:0x000f in ribo.LabelBrk.dBreakFromBlock():void
09:16:03.794 [pool-1-thread-5] ERROR jadx.core.utils.ErrorsCounter - Inconsistent code in method: ribo.LabelBrk.dBreakFromBlock():void 
09:16:03.804 [main] ERROR jadx.core.utils.ErrorsCounter - 1 errors occurred in following nodes:
09:16:03.804 [main] ERROR jadx.core.utils.ErrorsCounter -   Method: ribo.LabelBrk.dBreakFromBlock():void
09:16:03.804 [main] ERROR jadx.cli.JadxCLI - finished with errors

Instructions decoded, grouped by block:

b0     0 SGET r0 int  static d
b0     2 CONST r1 unknownType < a0 lit 10 

b1     4 IF  < a0 r0 unknownType, a1 r1 unknownType  NE 17 then: null else: null

b2     6 CONST_STR r0 String   str: "outer if top"
b2     8 INVOKE  < a0 r0 String  call prt
b2     B SGET r0 int  static a
b2     D SGET r1 int  static b

b3     F IF  < a0 r0 unknownType, a1 r1 unknownType  LT 17 then: null else: null

b4    11 CONST_STR r0 String   str: "afterBlock"
b4    13 INVOKE  < a0 r0 String  call prt

b5    16 RETURN   

b6    17 CONST_STR r0 String   str: "after cond break"
b6    19 INVOKE  < a0 r0 String  call prt
b6    1C GOTO   goto 11

The 'IF' in block 3, at address F above is the one that breaks from the labeled block. Block 3 gets elminated (I think in the RegionMaker pass)
Block tree after RegionMaker visitor:

{ reg 5012645
BLK B:0:0x0000
{ reg 12
    BLK B:1:0x0004
    18   b1     4 IF  < a0 wrap 0x0000: SGET  (r0_0 int) =   ribo.LabelBrk.d intarg??arg??, a1 lit 10  EQ 6 then: B:2:0x0006 else: B:6:0x0017
    { reg 12
    BLK B:2:0x0006
    19   b2     8 INVOKE  < a0 wrap 0x0006: CONST_STR  (r0_1 java.lang.String) =  "outer if top"arg??arg??  call prt
    }
}
BLK B:6:0x0017
22   b6    19 INVOKE  < a0 wrap 0x0017: CONST_STR  (r0_4 java.lang.String) =  "after cond break"arg??arg??  call prt
BLK B:4:0x0011
24   b4    13 INVOKE  < a0 wrap 0x0011: CONST_STR  (r0_3 java.lang.String) =  "afterBlock"arg??arg??  call prt
BLK B:5:0x0016
25   b5    16 RETURN   
}

Issues in decompilation (complicate if and else)

Thank for your great work ,
But this code will not decompile correctly and hope you can fix it:

    static boolean autoStop = true;
    static boolean qualityReading = false;
    static int lastValidRaw = -1;

    public static void main(String[] args) throws Exception {
        int a = 5;
        int b = 30;
        dataProcess(a,b);
    }

    public static void dataProcess(int raw, int quality) {
        if(quality >= 10 && raw != 0) {
            System.out.println ("OK"+ raw); 
           qualityReading = false;
        } else if(raw != 0 && quality >= 6 && qualityReading) {
            System.out.println ("Quit OK"+ raw); 
        } else {
            System.out.println ("Not OK"+ raw); 
        }

        if(quality < 30) {
           int timeLeft = 30 - quality;
           if(quality >= 10) {
               System.out.println ("Processing"+ timeLeft); 
           }
        } else {
            System.out.println ("Finish Processing");
           if(raw > 0) {
              lastValidRaw = raw;
           }
        }

        if(quality >= 30 && autoStop) {
            System.out.println ("Finished");
        }

        if(!autoStop && lastValidRaw > -1 && quality < 10) {
            System.out.println ("Finished");
        }

     }

JADX: method processing error - Error: java.lang.NullPointerException

Hi,

Recently I've decided to try and understand how android files work, and came across the whole .dex file - from everything I've seen its generally straight forward to decompile these files, but looks like this one has some sort of obfuscation on it or protection that is causing the method to fail to decompile.

//screen shot
https://www.dropbox.com/s/mi1on6a5igrtc7m/Untitled.png?dl=0

//classes.dex sample file
https://www.dropbox.com/s/la0rts3rc8iq2f9/classes.dex?dl=0

Thnxs for reading.

-Icy

Can't decompile one method, always the same

Hi.
I tried every known java decompiler and the result is always the same on one method, it just does not decompile., but the method is simple.

In jadx it is always inconsistent code error

/* JADX WARNING: inconsistent code. */
/* Code decompiled incorrectly, please refer to instructions dump. */

it is in utils.Api class, apiRequest method, jar file http://www59.zippyshare.com/v/27225685/file.html

Is it really that hard to fix it?

Why do ArrayList variables not get their Generic type added?

Testing this thing out. It works quite ok, although it uses a lot more memory than dex2jar since it doesn't stream the dex file, but loads it into mem.

Wondering about the ArrayList declarations:
Why don't you print out "ArrayList" but "ArrayList //signature ArrayList;" ?
It looks like you do have the data, but don't use it?

Anyway, it looks very promising! I do have it working on a Android device and can decompile classes on device, and the output looks very impressive.

Broken complex If statements

Input:

    int c;
    String d;
    public void testComplexIf(String a, int b) {
        if (d == null
        || (c == 0 && b != -1 && d.length() == 0)) {
            c = a.codePointAt(c);
        } else {
            if (a.hashCode() != 0xCDE) {
                c = f.compareTo(a);
            }
        }
    }

Output:

    int c;
    String d;
    public void testComplexIf(String a, int b) {
        if (d != null) {
            if (c != 0 || b == -1 || d.length() != 0) {
                if (a.hashCode() != 3294) {
                    c = f.compareTo(a);
                }
                // Fix option#1
                // return;
            }
        }
        // Fix option#2
        // else
        c = a.codePointAt(c);
    }

Nested anonymous classes variable assignment fails code generation

Consider the following example code:

    // a basic abstract class for nesting example
    public static abstract class BasicAbstract {
        public abstract void doSomething();
    }

    public static class TestCls {
        private BasicAbstract inner;

        public void simpleAnonymous() {
            inner = new BasicAbstract() {
                @Override
                public void doSomething() {
                    inner = null;
                }
            };
        }
    }

Code gets generated correctly here. Now let's add another level of anonymous class:

    // a basic abstract class for nesting example
    public static abstract class BasicAbstract {
        public abstract void doSomething();
    }

    public static class TestCls {
        private BasicAbstract outer, inner;

        public void nestedAnonymous() {
            outer = new BasicAbstract() {
                @Override
                public void doSomething() {
                    inner = new BasicAbstract() {
                        @Override
                        public void doSomething() {
                            inner = null;
                        }
                    };
                }
            };
        }
    }

Code generation fails. You can see that inner = null doesn't get generated correctly. Instead, it gets a whole new copy of inner = new BasicAbstract() ... with a lot of recursion of the same.

As far as I can tell, this is a regression from version 0.6.0, where anonymous classes were never transplanted into their original place, but instead left as external class AnonymousClassN { ... } definitions.

Switch statement with return statements fails to decompile correctly

The following code fails to decompile correctly:

    public static class TestCls {
        public int i;
        public void test(int a) {
            switch (a) {
                case 1:
                    i = 1;
                    return;
                case 2:
                case 3:
                    i = 2;
                    return;
                default:
                    i = 3;
            }
            i = 4;
        }
    }

The resulting decompile is missing return statements entirely:

public class TestCls {
        public int i;

        public void test(int a) {
            switch (a) {
                case 1:
                    this.i = 1;
                case 2:
                case 3:
                    this.i = 2;
                default:
                    this.i = 3;
                    this.i = 4;
            }
        }
    }

Wrong with complex if/else condition and for loop (while loop) with break inside

This code will fail to decompile, it produced wrong code. I have try with latest unstable build.
The problem inside the function checkRight().

public class Main {
    public static void main(String[] args) throws Exception {
        HashMap<String, Point> map = new HashMap();
        for (int i = 0; i < 100; i++) {
            Point point = new Point(i, i + 50);
            map.put(i + "", point);
        }
        int f = checkRight(map, 4);
        System.out.print(f);

    }

    private static int checkRight(HashMap<String, Point> map, int mapX) {
        int length = 1;
        for (int x = mapX + 1; x < 100; x++) {
            Point tile = map.get(x + "");
            if (tile == null || tile.y != 100) {
                break;
            }
            length++;
        }
        return length;
    }

}

class Point {
    public int x;
    public int y;

    public Point() {
    }

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

}

Missing blocks

First of all thanks for your amazing work! This is by far the best dex decompiler available.

I'm using the latest 0.5.2 version built from sources and encounter many 'Missing block' and therefore 'Inconsistent code' errors while decompiling Android frameworks, both AOSP compiled and LG custom ones. A few examples all from AOSP code:

1.) The error:

DEBUG -  Missing block: B:25:? in android.os.Parcel.readParcelable(java.lang.ClassLoader):T
ERROR - Inconsistent code in method: android.os.Parcel.readParcelable(java.lang.ClassLoader):T

Original source: https://github.com/android/platform_frameworks_base/blob/android-4.1.2_r2.1/core/java/android/os/Parcel.java#L2048
Decompiled source:

    /* JADX WARNING: inconsistent code. */
    /* Code decompiled incorrectly, please refer to instructions dump. */
    public final <T extends android.os.Parcelable> T readParcelable(java.lang.ClassLoader r11_loader) {
        throw new UnsupportedOperationException("Method not decompiled: android.os.Parcel.readParcelable(java.lang.ClassLoader):T");
        /*
        r10 = this;
        r6 = 0;
        r5 = r10.readString();
        if (r5 != 0) goto L_0x0008;
    L_0x0007:
        return r6;
    L_0x0008:
        r7 = mCreators;
        monitor-enter(r7);
        r6 = mCreators;  Catch:{ all -> 0x0055 }
        r4 = r6.get(r11);    Catch:{ all -> 0x0055 }
        r4 = (java.util.HashMap) r4;     Catch:{ all -> 0x0055 }
        if (r4 != 0) goto L_0x001f;
    L_0x0015:
        r4 = new java.util.HashMap;  Catch:{ all -> 0x0055 }
        r4.<init>();     Catch:{ all -> 0x0055 }
        r6 = mCreators;  Catch:{ all -> 0x0055 }
        r6.put(r11, r4);     Catch:{ all -> 0x0055 }
    L_0x001f:
        r1 = r4.get(r5);     Catch:{ all -> 0x0055 }
        r1 = (android.os.Parcelable.Creator) r1;     Catch:{ all -> 0x0055 }
        if (r1 != 0) goto L_0x010d;
    L_0x0027:
        if (r11 != 0) goto L_0x0058;
    L_0x0029:
        r0 = java.lang.Class.forName(r5);    Catch:{ IllegalAccessException -> 0x005e, ClassNotFoundException -> 0x009a, ClassCastException -> 0x00d6, NoSuchFieldException -> 0x00f0 }
    L_0x002d:
        r6 = "CREATOR";
        r3 = r0.getField(r6);    Catch:{ IllegalAccessException -> 0x005e, ClassNotFoundException -> 0x009a, ClassCastException -> 0x00d6, NoSuchFieldException -> 0x00f0 }
        r6 = 0;
        r1 = r3.get(r6);     Catch:{ IllegalAccessException -> 0x005e, ClassNotFoundException -> 0x009a, ClassCastException -> 0x00d6, NoSuchFieldException -> 0x00f0 }
        r1 = (android.os.Parcelable.Creator) r1;     Catch:{ IllegalAccessException -> 0x005e, ClassNotFoundException -> 0x009a, ClassCastException -> 0x00d6, NoSuchFieldException -> 0x00f0 }
        if (r1 != 0) goto L_0x010a;
    L_0x003c:
        r6 = new android.os.BadParcelableException;  Catch:{ all -> 0x0055 }
        r8 = new java.lang.StringBuilder;    Catch:{ all -> 0x0055 }
        r8.<init>();     Catch:{ all -> 0x0055 }
        r9 = "Parcelable protocol requires a Parcelable.Creator object called  CREATOR on class ";
        r8 = r8.append(r9);  Catch:{ all -> 0x0055 }
        r8 = r8.append(r5);  Catch:{ all -> 0x0055 }
        r8 = r8.toString();  Catch:{ all -> 0x0055 }
        r6.<init>(r8);   Catch:{ all -> 0x0055 }
        throw r6;    Catch:{ all -> 0x0055 }
    L_0x0055:
        r6 = move-exception;
        monitor-exit(r7);    Catch:{ all -> 0x0055 }
        throw r6;
    L_0x0058:
        r6 = 1;
        r0 = java.lang.Class.forName(r5, r6, r11);   Catch:{ IllegalAccessException -> 0x005e, ClassNotFoundException -> 0x009a, ClassCastException -> 0x00d6, NoSuchFieldException -> 0x00f0 }
        goto L_0x002d;
    L_0x005e:
        r2 = move-exception;
        r6 = "Parcel";
        r8 = new java.lang.StringBuilder;    Catch:{ all -> 0x0055 }
        r8.<init>();     Catch:{ all -> 0x0055 }
        r9 = "Class not found when unmarshalling: ";
        r8 = r8.append(r9);  Catch:{ all -> 0x0055 }
        r8 = r8.append(r5);  Catch:{ all -> 0x0055 }
        r9 = ", e: ";
        r8 = r8.append(r9);  Catch:{ all -> 0x0055 }
        r8 = r8.append(r2);  Catch:{ all -> 0x0055 }
        r8 = r8.toString();  Catch:{ all -> 0x0055 }
        android.util.Log.e(r6, r8);  Catch:{ all -> 0x0055 }
        r6 = new android.os.BadParcelableException;  Catch:{ all -> 0x0055 }
        r8 = new java.lang.StringBuilder;    Catch:{ all -> 0x0055 }
        r8.<init>();     Catch:{ all -> 0x0055 }
        r9 = "IllegalAccessException when unmarshalling: ";
        r8 = r8.append(r9);  Catch:{ all -> 0x0055 }
        r8 = r8.append(r5);  Catch:{ all -> 0x0055 }
        r8 = r8.toString();  Catch:{ all -> 0x0055 }
        r6.<init>(r8);   Catch:{ all -> 0x0055 }
        throw r6;    Catch:{ all -> 0x0055 }
    L_0x009a:
        r2 = move-exception;
        r6 = "Parcel";
        r8 = new java.lang.StringBuilder;    Catch:{ all -> 0x0055 }
        r8.<init>();     Catch:{ all -> 0x0055 }
        r9 = "Class not found when unmarshalling: ";
        r8 = r8.append(r9);  Catch:{ all -> 0x0055 }
        r8 = r8.append(r5);  Catch:{ all -> 0x0055 }
        r9 = ", e: ";
        r8 = r8.append(r9);  Catch:{ all -> 0x0055 }
        r8 = r8.append(r2);  Catch:{ all -> 0x0055 }
        r8 = r8.toString();  Catch:{ all -> 0x0055 }
        android.util.Log.e(r6, r8);  Catch:{ all -> 0x0055 }
        r6 = new android.os.BadParcelableException;  Catch:{ all -> 0x0055 }
        r8 = new java.lang.StringBuilder;    Catch:{ all -> 0x0055 }
        r8.<init>();     Catch:{ all -> 0x0055 }
        r9 = "ClassNotFoundException when unmarshalling: ";
        r8 = r8.append(r9);  Catch:{ all -> 0x0055 }
        r8 = r8.append(r5);  Catch:{ all -> 0x0055 }
        r8 = r8.toString();  Catch:{ all -> 0x0055 }
        r6.<init>(r8);   Catch:{ all -> 0x0055 }
        throw r6;    Catch:{ all -> 0x0055 }
    L_0x00d6:
        r2 = move-exception;
        r6 = new android.os.BadParcelableException;  Catch:{ all -> 0x0055 }
        r8 = new java.lang.StringBuilder;    Catch:{ all -> 0x0055 }
        r8.<init>();     Catch:{ all -> 0x0055 }
        r9 = "Parcelable protocol requires a Parcelable.Creator object called  CREATOR on class ";
        r8 = r8.append(r9);  Catch:{ all -> 0x0055 }
        r8 = r8.append(r5);  Catch:{ all -> 0x0055 }
        r8 = r8.toString();  Catch:{ all -> 0x0055 }
        r6.<init>(r8);   Catch:{ all -> 0x0055 }
        throw r6;    Catch:{ all -> 0x0055 }
    L_0x00f0:
        r2 = move-exception;
        r6 = new android.os.BadParcelableException;  Catch:{ all -> 0x0055 }
        r8 = new java.lang.StringBuilder;    Catch:{ all -> 0x0055 }
        r8.<init>();     Catch:{ all -> 0x0055 }
        r9 = "Parcelable protocol requires a Parcelable.Creator object called  CREATOR on class ";
        r8 = r8.append(r9);  Catch:{ all -> 0x0055 }
        r8 = r8.append(r5);  Catch:{ all -> 0x0055 }
        r8 = r8.toString();  Catch:{ all -> 0x0055 }
        r6.<init>(r8);   Catch:{ all -> 0x0055 }
        throw r6;    Catch:{ all -> 0x0055 }
    L_0x010a:
        r4.put(r5, r1);  Catch:{ all -> 0x0055 }
    L_0x010d:
        monitor-exit(r7);    Catch:{ all -> 0x0055 }
        r6 = r1 instanceof android.os.Parcelable.ClassLoaderCreator;
        if (r6 == 0) goto L_0x011c;
    L_0x0112:
        r1 = (android.os.Parcelable.ClassLoaderCreator) r1;
        r6 = r1.createFromParcel(r10, r11);
        r6 = (android.os.Parcelable) r6;
        goto L_0x0007;
    L_0x011c:
        r6 = r1.createFromParcel(r10);
        r6 = (android.os.Parcelable) r6;
        goto L_0x0007;
        */
    }

2.) The error:

DEBUG -  Missing block: B:24:0x01aa in com.android.server.location.GpsLocationProvider.<init>(android.content.Context, android.location.ILocationManager):void
ERROR - Inconsistent code in method: com.android.server.location.GpsLocationProvider.<init>(android.content.Context, android.location.ILocationManager):void

Original source: https://github.com/android/platform_frameworks_base/blob/android-4.1.2_r2.1/services/java/com/android/server/location/GpsLocationProvider.java#L384
Decompiled source:

    /* JADX WARNING: inconsistent code. */
    /* Code decompiled incorrectly, please refer to instructions dump. */
    public GpsLocationProvider(android.content.Context r12_context, android.location.ILocationManager r13_locationManager) {
        throw new UnsupportedOperationException("Method not decompiled: com.android.server.location.GpsLocationProvider.<init>(android.content.Context, android.location.ILocationManager):void");
        /*
        r11 = this;
        r10 = 1;
        r8 = 32;
        r9 = 0;
        r11.<init>();
        r11.mLocationFlags = r9;
        r11.mStatus = r10;
        r6 = android.os.SystemClock.elapsedRealtime();
        r11.mStatusUpdateTime = r6;
        r11.mInjectNtpTimePending = r9;
        r11.mDownloadXtraDataPending = r9;
        r6 = 1000; // 0x3e8 float:1.401E-42 double:4.94E-321;
        r11.mFixInterval = r6;
        r6 = 0;
        r11.mFixRequestTime = r6;
        r11.mTTFF = r9;
        r6 = new android.location.Location;
        r7 = "gps";
        r6.<init>(r7);
        r11.mLocation = r6;
        r6 = new android.os.Bundle;
        r6.<init>();
        r11.mLocationExtras = r6;
        r6 = new java.util.ArrayList;
        r6.<init>();
        r11.mListeners = r6;
        r6 = new java.util.concurrent.CountDownLatch;
        r6.<init>(r10);
        r11.mInitializedLatch = r6;
        r6 = new android.util.SparseIntArray;
        r6.<init>();
        r11.mClientUids = r6;
        r6 = new com.android.server.location.GpsLocationProvider$1;
        r6.<init>();
        r11.mGpsStatusProvider = r6;
        r6 = new com.android.server.location.GpsLocationProvider$2;
        r6.<init>();
        r11.mBroadcastReciever = r6;
        r6 = new com.android.server.location.GpsLocationProvider$3;
        r6.<init>();
        r11.mNetInitiatedListener = r6;
        r6 = new int[r8];
        r11.mSvs = r6;
        r6 = new float[r8];
        r11.mSnrs = r6;
        r6 = new float[r8];
        r11.mSvElevations = r6;
        r6 = new float[r8];
        r11.mSvAzimuths = r6;
        r6 = 3;
        r6 = new int[r6];
        r11.mSvMasks = r6;
        r6 = 120; // 0x78 float:1.68E-43 double:5.93E-322;
        r6 = new byte[r6];
        r11.mNmeaBuffer = r6;
        r11.mContext = r12;
        r6 = android.util.NtpTrustedTime.getInstance(r12);
        r11.mNtpTime = r6;
        r11.mLocationManager = r13;
        r6 = new com.android.internal.location.GpsNetInitiatedHandler;
        r6.<init>(r12);
        r11.mNIHandler = r6;
        r6 = r11.mLocation;
        r7 = r11.mLocationExtras;
        r6.setExtras(r7);
        r6 = r11.mContext;
        r7 = "power";
        r4 = r6.getSystemService(r7);
        r4 = (android.os.PowerManager) r4;
        r6 = "GpsLocationProvider";
        r6 = r4.newWakeLock(r10, r6);
        r11.mWakeLock = r6;
        r6 = r11.mWakeLock;
        r6.setReferenceCounted(r9);
        r6 = r11.mContext;
        r7 = "alarm";
        r6 = r6.getSystemService(r7);
        r6 = (android.app.AlarmManager) r6;
        r11.mAlarmManager = r6;
        r6 = r11.mContext;
        r7 = new android.content.Intent;
        r8 = "com.android.internal.location.ALARM_WAKEUP";
        r7.<init>(r8);
        r6 = android.app.PendingIntent.getBroadcast(r6, r9, r7, r9);
        r11.mWakeupIntent = r6;
        r6 = r11.mContext;
        r7 = new android.content.Intent;
        r8 = "com.android.internal.location.ALARM_TIMEOUT";
        r7.<init>(r8);
        r6 = android.app.PendingIntent.getBroadcast(r6, r9, r7, r9);
        r11.mTimeoutIntent = r6;
        r2 = new android.content.IntentFilter;
        r2.<init>();
        r6 = "android.intent.action.DATA_SMS_RECEIVED";
        r2.addAction(r6);
        r6 = "sms";
        r2.addDataScheme(r6);
        r6 = "localhost";
        r7 = "7275";
        r2.addDataAuthority(r6, r7);
        r6 = r11.mBroadcastReciever;
        r12.registerReceiver(r6, r2);
        r2 = new android.content.IntentFilter;
        r2.<init>();
        r6 = "android.provider.Telephony.WAP_PUSH_RECEIVED";
        r2.addAction(r6);
        r6 = "application/vnd.omaloc-supl-init";
        r2.addDataType(r6);  Catch:{ MalformedMimeTypeException -> 0x0186 }
    L_0x00f7:
        r6 = r11.mBroadcastReciever;
        r12.registerReceiver(r6, r2);
        r6 = "connectivity";
        r6 = r12.getSystemService(r6);
        r6 = (android.net.ConnectivityManager) r6;
        r11.mConnMgr = r6;
        r6 = "batteryinfo";
        r6 = android.os.ServiceManager.getService(r6);
        r6 = com.android.internal.app.IBatteryStats.Stub.asInterface(r6);
        r11.mBatteryStats = r6;
        r6 = new java.util.Properties;
        r6.<init>();
        r11.mProperties = r6;
        r1 = new java.io.File;   Catch:{ IOException -> 0x01aa }
        r6 = "/etc/gps.conf";
        r1.<init>(r6);   Catch:{ IOException -> 0x01aa }
        r5 = new java.io.FileInputStream;    Catch:{ IOException -> 0x01aa }
        r5.<init>(r1);   Catch:{ IOException -> 0x01aa }
        r6 = r11.mProperties;    Catch:{ IOException -> 0x01aa }
        r6.load(r5);     Catch:{ IOException -> 0x01aa }
        r5.close();  Catch:{ IOException -> 0x01aa }
        r6 = r11.mProperties;    Catch:{ IOException -> 0x01aa }
        r7 = "NTP_SERVER";
        r8 = 0;
        r6 = r6.getProperty(r7, r8);     Catch:{ IOException -> 0x01aa }
        r11.mNtpServer = r6;     Catch:{ IOException -> 0x01aa }
        r6 = r11.mProperties;    Catch:{ IOException -> 0x01aa }
        r7 = "SUPL_HOST";
        r6 = r6.getProperty(r7);     Catch:{ IOException -> 0x01aa }
        r11.mSuplServerHost = r6;    Catch:{ IOException -> 0x01aa }
        r6 = r11.mProperties;    Catch:{ IOException -> 0x01aa }
        r7 = "SUPL_PORT";
        r3 = r6.getProperty(r7);     Catch:{ IOException -> 0x01aa }
        r6 = r11.mSuplServerHost;    Catch:{ IOException -> 0x01aa }
        if (r6 == 0) goto L_0x0156;
    L_0x014e:
        if (r3 == 0) goto L_0x0156;
    L_0x0150:
        r6 = java.lang.Integer.parseInt(r3);     Catch:{ NumberFormatException -> 0x0190 }
        r11.mSuplServerPort = r6;    Catch:{ NumberFormatException -> 0x0190 }
    L_0x0156:
        r6 = r11.mProperties;    Catch:{ IOException -> 0x01aa }
        r7 = "C2K_HOST";
        r6 = r6.getProperty(r7);     Catch:{ IOException -> 0x01aa }
        r11.mC2KServerHost = r6;     Catch:{ IOException -> 0x01aa }
        r6 = r11.mProperties;    Catch:{ IOException -> 0x01aa }
        r7 = "C2K_PORT";
        r3 = r6.getProperty(r7);     Catch:{ IOException -> 0x01aa }
        r6 = r11.mC2KServerHost;     Catch:{ IOException -> 0x01aa }
        if (r6 == 0) goto L_0x0174;
    L_0x016c:
        if (r3 == 0) goto L_0x0174;
    L_0x016e:
        r6 = java.lang.Integer.parseInt(r3);     Catch:{ NumberFormatException -> 0x01b3 }
        r11.mC2KServerPort = r6;     Catch:{ NumberFormatException -> 0x01b3 }
    L_0x0174:
        r6 = new com.android.server.location.GpsLocationProvider$GpsLocationProviderThread;
        r6.<init>();
        r11.mThread = r6;
        r6 = r11.mThread;
        r6.start();
    L_0x0180:
        r6 = r11.mInitializedLatch;  Catch:{ InterruptedException -> 0x01cd }
        r6.await();  Catch:{ InterruptedException -> 0x01cd }
        return;
    L_0x0186:
        r0 = move-exception;
        r6 = "GpsLocationProvider";
        r7 = "Malformed SUPL init mime type";
        android.util.Log.w(r6, r7);
        goto L_0x00f7;
    L_0x0190:
        r0 = move-exception;
        r6 = "GpsLocationProvider";
        r7 = new java.lang.StringBuilder;    Catch:{ IOException -> 0x01aa }
        r7.<init>();     Catch:{ IOException -> 0x01aa }
        r8 = "unable to parse SUPL_PORT: ";
        r7 = r7.append(r8);  Catch:{ IOException -> 0x01aa }
        r7 = r7.append(r3);  Catch:{ IOException -> 0x01aa }
        r7 = r7.toString();  Catch:{ IOException -> 0x01aa }
        android.util.Log.e(r6, r7);  Catch:{ IOException -> 0x01aa }
        goto L_0x0156;
    L_0x01aa:
        r0 = move-exception;
        r6 = "GpsLocationProvider";
        r7 = "Could not open GPS configuration file /etc/gps.conf";
        android.util.Log.w(r6, r7);
        goto L_0x0174;
    L_0x01b3:
        r0 = move-exception;
        r6 = "GpsLocationProvider";
        r7 = new java.lang.StringBuilder;    Catch:{ IOException -> 0x01aa }
        r7.<init>();     Catch:{ IOException -> 0x01aa }
        r8 = "unable to parse C2K_PORT: ";
        r7 = r7.append(r8);  Catch:{ IOException -> 0x01aa }
        r7 = r7.append(r3);  Catch:{ IOException -> 0x01aa }
        r7 = r7.toString();  Catch:{ IOException -> 0x01aa }
        android.util.Log.e(r6, r7);  Catch:{ IOException -> 0x01aa }
        goto L_0x0174;
    L_0x01cd:
        r0 = move-exception;
        r6 = java.lang.Thread.currentThread();
        r6.interrupt();
        goto L_0x0180;
        */
    }

In this latter case the following block seems to be missing, but it isn't actually as it's there in decompiled source:

        } catch (IOException e) {
            Log.w(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE);
        }

Fail to detect out node in non trivial switch

Here is a test case to check this issue

--- jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchReturnFromCase.java ---
<<< Cut here ---

package jadx.tests.integration.switches;

import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.IntegrationTest;

import org.junit.Test;

import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;

public class TestSwitchReturnFromCase extends IntegrationTest {

    public static class TestCls {
        public void test(int a) {
            String s = null;

            if (a > 1000) {
                return;
            }

            switch (a % 4) {
                case 1:
                    s = "1";
                    break;
                case 2:
                    s = "2";
                    break;
                case 3:
                case 4:
                    s = "4";
                    break;
                case 5:
                    return;
            }

            s = "5";
        }
    }

    @Test
    public void test() {
        ClassNode cls = getClassNode(TestCls.class);
        String code = cls.getCode().toString();

        assertEquals(3, count(code, "break;"));
        assertThat(code, containsString("switch (a % 4) {"));
    }
}

<<< Cut here ---

I have an idea how to fix detecting out node in processSwitch() method of RegionMaker class.
What you think about multiple out nodes for a switch region?

We can think about three type of out edges:
- break
- return
- continue

And, yes, we need some non trivial algorithm, that can detects type of edge.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.