> For the complete documentation index, see [llms.txt](https://docs.hex-rays.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.hex-rays.com/ida-9.5/core/decompiler/concepts/dalvik-exception-handler.md).

# Dalvik exception handler

Hex-Rays' Dalvik decompiler uses the exception tables stored in DEX files to rebuild Java exception constructs in the decompilation: `try`, `catch`, multi-catch (`catch ( A | B e )`), `finally` and `synchronized`. This page explains how these constructs are encoded in Dalvik bytecode and describes cases where the decompiler cannot rebuild the original source construct and shows a lower-level equivalent instead.

The documentation describes:

* How Java exception constructs are encoded in Dalvik bytecode. It is recommended that users read this first.
* How `finally` blocks are recognized.
* Known limitations, with examples:
  * `try`-with-resources is shown as a plain `try`/catch-all.
  * `finally` with multiple exits is shown as a `try`/catch-all.
  * `finally` blocks that the compiler optimized differently in each copy (eg. due constant value propagation) are shown as a `try`/catch-all.

**BACKGROUND ON DALVIK EXCEPTIONS**

**TRY RANGES AND HANDLERS**

A DEX method may have an exception table. The table is a list of `try` ranges (address ranges of bytecode), and each range has a list of handlers. A handler is either:

* a typed handler, e.g. `catch(java.io.IOException) @35C`: taken when the thrown object is an instance of that class;
* a catch-all handler, e.g. `catch-all @29C`: taken for any `Throwable`.

This is all the table contains. Dalvik bytecode has no `finally`, `synchronized` or `try`-with-resources. The Java compiler (`javac`) and the DEX compiler (`d8`/`dx`) turn these constructs into ordinary code plus catch-all handlers. The decompiler has to recognize the patterns they produce to show the source constructs again.

A catch-all handler that the decompiler cannot turn into `finally` or `synchronized` is shown as:

```java
catch ( Throwable v3 )
{
  ...
}
```

**HELPER CALLS**

Internally, the decompiler marks the `try` ranges and handlers from the exception table with helper calls. Later these calls are turned into `try`, `catch`, `finally` and `synchronized` constructs and removed. The main ones are:

| Function name           | Description                                                              |
| ----------------------- | ------------------------------------------------------------------------ |
| `__eh_try()`            | beginning of a `try` block                                               |
| `__eh_catch()`          | beginning of the list of handlers for the `try` block                    |
| `__eh_catch_type(Type)` | a `catch` handler for exception type `Type`, e.g. `IOException`          |
| `__eh_catch_ellipsis()` | a catch-all handler (`finally`, `synchronized` or `catch ( Throwable )`) |

Normally these calls never appear in the output. In very rare cases, when the decompiler cannot build a structured construct from them (for example, because of unusual control flow in the `try` range or the handler), some of them may remain in the decompilation. The code is still correct: the helper calls just mark where the exception regions and handlers are.

**HOW `FINALLY` IS COMPILED**

Consider:

```java
try {
  body();
} finally {
  cleanup();
}
```

The compiler copies the `finally` block into every place where it runs:

1. At the end of each normal exit from the `try` block (falling through, `return`, `break`, `continue`), the copy runs before control leaves the block.
2. In a catch-all handler that covers the `try` block (and any `catch` blocks). This copy runs the `finally` code and rethrows the exception.

The bytecode looks like this:

```java
try {
  body();
} catch ( Throwable t ) {   // catch-all handler
  cleanup();                // copy of finally
  throw t;
}
cleanup();                  // copy of finally on the normal path
```

The decompiler rebuilds `finally` by comparing the catch-all handler body with the code on every normal exit. If each exit has an **identical** copy of the handler body (without the final `throw`), it removes the copies and shows a `finally` block.

**HOW `SYNCHRONIZED` IS COMPILED**

`synchronized ( obj ) { ... }` is compiled to `monitor-enter obj`, the body, and `monitor-exit obj` on every exit. A catch-all handler also runs `monitor-exit obj` and rethrows. The decompiler recognizes this pattern and shows a `synchronized` block. Nested blocks and blocks that lock the same object again are supported.

**KNOWN LIMITATIONS**

In all the cases below the decompiled code is **semantically correct**: it shows what the bytecode does. It is only less close to the original source than it could be. Look for this pattern:

```java
catch ( Throwable vN )
{
  ...          // cleanup code
  throw vN;
}
...            // the same (or similar) cleanup code after the try
```

It usually means the source had a `finally` block (or a `try`-with-resources) that was not rebuilt.

**1. TRY-WITH-RESOURCES IS SHOWN AS A PLAIN TRY/CATCH-ALL**

`try`-with-resources has no bytecode representation either. `javac` expands it into a `try` block, a catch-all handler that closes the resource, a nested `try` around `close()`, and a call to `Throwable.addSuppressed()` if `close()` also throws. When the app targets old Android API levels, `d8` also replaces the `addSuppressed()` call with a call to a synthetic helper class (backporting), which calls `addSuppressed` through reflection.

The decompiler does not rebuild `try ( ... )`. The expanded code is shown as it is.

Java source:

```java
public static void writeFully(File file, byte[] data) throws IOException {
  try (OutputStream out = new FileOutputStream(file)) {
    out.write(data);
  }
}
```

Decompilation:

```java
public static void writeFully(File p0, byte[] p1) throws java.io.IOException
{
  OutputStream v2; // V0

  v2 = new FileOutputStream(p0);
  try
  {
    v2.write(p1);
  }
  catch ( Throwable v3 )
  {
    try
    {
      v2.close();
    }
    catch ( Throwable v4 )
    {
      dalvik_eh_try_with_resources$0.m(v3, v4);   // addSuppressed
    }
    throw v3;
  }
  v2.close();
}
```

The synthetic helper that `d8` generated:

```java
public final class 0
{
  public static void m(Throwable p0, Throwable p1)
  {
    ...
    try
    {
      ...
      Throwable.class.getDeclaredMethod("addSuppressed", v2).invoke(p0, v3);
    }
    catch ( Exception e )
    {
    }
  }
}
```

How to read the output:

* `v2 = new FileOutputStream(p0)` just before the `try` is the resource declaration.
* The `try` body is the original `try`-with-resources body.
* `catch ( Throwable v3 ) { try { v2.close(); } catch ( Throwable v4 ) { ...addSuppressed... } throw v3; }` is the automatic close when an exception is thrown.
* `v2.close()` after the `try` is the automatic close on the normal path.

When the `try`-with-resources is inside another `try`, or has its own `catch` clauses, the expanded code is nested inside the outer `try`/`catch`:

```java
try
{
  v1 = new Sample(this.this$0);
  try
  {
    v1.do_something();
  }
  catch ( Throwable v2 )
  {
    try
    {
      v1.close();
    }
    catch ( Throwable v3 )
    {
      Sample$0.m(v2, v3);
    }
    throw v2;
  }
  v1.close();
  return;
}
catch ( IOException e )
{
}
```

which came from:

```java
try (Sample s = new Sample()) {
  s.do_something();
} catch (IOException e) {
}
```

**2. FINALLY WITH MULTIPLE EXITS IS SHOWN AS A TRY/CATCH-ALL**

If the `try` block has several exits (several `return` statements, `throw`, `break`, ...), the compiler creates one copy of the `finally` block for each exit. Each copy is then optimized with what the compiler knows at that exit. Often the copies are also merged with the code after them (for example, the `return`). The copies are no longer identical to the catch-all handler, so the decompiler cannot remove them. The catch-all handler is shown as a `catch ( Throwable ... )` and the `finally` code shows up in every exit path.

Java source:

```java
public int test(int counter) {
  try {
    if ( counter == 0 )
      throw new Exception();
    if ( counter == 1 )
      return counter * 3;
    if ( counter < 8 )
      return counter * 4;
    counter *= 2;
  } finally {
    return finFunc(counter);
  }
}
```

Decompilation:

```java
public int test(int p0)
{
  if ( p0 != 0 )
  {
    if ( p0 == 1 )
    {
      return this.finFunc(1);
    }
    else if ( p0 >= 8 )
    {
      return this.finFunc(2 * p0);
    }
    else
    {
      return this.finFunc(p0);
    }
  }
  else
  {
    try
    {
      throw new Exception();
    }
    catch ( Throwable e )
    {
      return this.finFunc(0);
    }
  }
}
```

Note:

* There are four exits: the `throw`, the two `return` statements, and the fall-through after `counter *= 2`. Each has its own copy of `return finFunc(counter)`.
* The `return` in `finally` overrides the `return counter * 3` and `return counter * 4` statements. The compiler removed the dead multiplications, which is why they do not appear in the output.
* In each copy `counter` was replaced by the value known at that point: `finFunc(1)` for `counter == 1`, `finFunc(0)` in the handler.
* The compiler only kept a `try` range around the instruction that can throw (`throw new Exception()`), so the `try` block is small and only contains that path.

**MISCELLANEOUS**

* A catch-all handler is shown as `catch ( Throwable vN )`. A handler that does not use the exception object is shown without a variable name, e.g. `catch ( Exception e )`.
* A handler for several types with the same body is shown as a multi-catch: `catch ( NullPointerException | ArithmeticException v6 )`.
* As with other compilers, DEX `try` ranges only cover the instructions that can throw. `try` blocks in the decompilation can therefore start later or end earlier than in the source. Statements that cannot throw can be shown just before or after the `try` block instead of inside it.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.hex-rays.com/ida-9.5/core/decompiler/concepts/dalvik-exception-handler.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
