Part 3: Documents and Delegation - JEB Decompiler
Author: PNF Software

JEB Plugin Development Tutorial part 3/8

# Documents and Delegation

The source code for part 3 of this sample plugin is located on GitHub:

- Clone this repo: `git clone https://github.com/pnfsoftware/jeb2-plugindemo-js.git`
- Switch to the tutorial3 branch: `git checkout tutorial3`

## Process the input

In the previous part, we built a single document that displays the whole JavaScript file.

Imagine we have large functions that we would like to split using the following simple rule: one function per document.

Let's detail the flow of execution around `IUnit`:

- When opening a new file, all the `IPlugin.canIdentify` methods are called.
- If identified, the method `IPlugin.prepare` is called, which will create a new unit.
- When opening the unit, if the unit was not previously processed, the method `IUnit.process` method will be called.
- At last, `IUnit.getFormatter` is used to display the result of processing.

The `process` method is where the heavy-lifting of your parser should be located. If you need to pre-process data, you may call the process method in `IUnitIdentifier.prepare`. Since we don't need it for now, we won't do it.

So let's start with implementing `process()`. First, we need to extract the JavaScript and split this test block into two functions. For the sake of this tutorial, we will use Mozilla Rhino to deal with JavaScript lexing and parsing. You can download it here.

First, add rhino-*.jar to your eclipse project (you can attach the javadoc to this .jar, the process is like adding javadoc to jeb.jar). Then, use the following snippet to extract the Functions from Javascript:

    private static final ILogger logger = GlobalLog.getLogger(SampleUnit.class);

    private AstRoot root = null;
    private List<FunctionNode> functions = new ArrayList<FunctionNode>();

    @Override
    public boolean process() {
        // parse the javascript
        IRFactory factory = new IRFactory();
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(getInput().getStream()));
            reader.readLine(); // ignore first line with #Javascript
            root = factory.parse(reader, null, 0);
        }
        catch(IOException e) {
            logger.catching(e);
            return false;
        }

        // save functions
        List<AstNode> statements = root.getStatements();
        for(AstNode statement: statements) {
            if(statement.getType() == Token.FUNCTION) {
                functions.add((FunctionNode)statement);
            }
        }

        // done
        setProcessed(true);
        return true;
    }

As you can see here, `process()` is only extracting data: FunctionNode objects are saved in the functions list.

Note: JEB provides global logging facility that you can use anytime to send messages in the Logger panel. Do not hesitate to use it to investigate your code. We also recommend you enable the Development Mode in the Options panel: this will allow logging of debug and trace messages.

    import com.pnfsoftware.jeb.util.logging.GlobalLog;
    import com.pnfsoftware.jeb.util.logging.ILogger;

    public class Xxx {
        private static final ILogger logger = GlobalLog.getLogger(Xxx.class);
        ...

## Add a Document per function

Now, let's modify the `getFormatter` method to change rendering, adding as many tabs (documents) as defined functions.

    @Override
    public IUnitFormatter getFormatter() {
        UnitFormatterAdapter adapter = new UnitFormatterAdapter(new AbstractUnitRepresentation("javascript raw code", true) {
            @Override
            public IGenericDocument getDocument() {
                    return new AsciiDocument(getInput());
            }
        });
        if(functions != null) {
            for(final FunctionNode function: functions) {
                adapter.addDocumentPresentation(new AbstractUnitRepresentation(function.getName()) {
                    @Override
                    public IGenericDocument getDocument() {
                        return new AsciiDocument(new BytesInput(function.toSource().getBytes()));
                    }
                });
            }
        }
        return adapter;
    }

We called the method `UnitFormatterAdapter.addDocumentPresentation` to add more documents.

> Note: since `AsciiDocument` requires an `IInput` as entry, we used the most appropriate implementation:

Now, start JEB.

> Note: You will probably see this exception: `java.lang.NoClassDefFoundError: org/mozilla/javascript/IRFactory`. Remember that js.jar was not added to JEB classpath. Add it to your plugin classpath (in the Options panel - refer to Part 1 of this tutorial).

If all works well, you shall to see two additional tabs:

## Delegate to another Unit

These new documents are great but what if we have lots of functions? We will have lots of tabs and it will become unreadable. One solution would be to add these FunctionNodes as children unit of the test.js unit node. We can delegate the creation of those units to the unit processor.

Here is a short explanation of delegation: assume have a zip file containing pdf, apk, js... You do not want to write a single parser that manages all those file types. A better approach is to have a Zip parser that will delegate parsing of its contents. That puts the Unit Processor in charge of finding the best Unit that match the file type: DEX Unit, Pdf Unit... and even our new Javascript Unit.

> More on delegation here.

So how to do it? You need to slightly modify process():

    // save functions
    List<AstNode> statements = root.getStatements();
    for(AstNode statement: statements) {
            if(statement.getType() == Token.FUNCTION) {
                FunctionNode function = (FunctionNode)statement;
                functions.add(function);
                IUnit jsUnit = getUnitProcessor().process(function.getName(), new BytesInput(function.toSource().getBytes()), this);
                if(jsUnit != null) {
                    addChildUnit(jsUnit);
                }
            }
    }

You should be able to see a generic binary output for a and b.

### Exercice

Build a simple JavascriptPlugin that takes advantage of the delegation. The solution lies in branch tutorial3 of this sample plugin repository.

Do not forget to register the parser plugin's classname in the options panel. You should obtain something like this:

Next: Part 4

#### Search

From here you can search these documents. Enter your search terms below.

#### Keyboard Shortcuts

Keys | Action
--- | ---
`?` | Open this help
`n` | Next page
`p` | Previous page
`s` | Search