A simplified Android ART nterp interpreter implemented in Java, with support for ADD (addition) and SUB (subtraction) instructions and much more.
- Jump Table Instruction Dispatch - O(1) time complexity to find the corresponding instruction handler
- ShadowFrame Execution Context Management - Virtual registers, operand stack, program counter
- Simplified DEX Bytecode Execution - Includes basic instruction support
java_nterp/
├── src/main/java/rprop/java/nterp/
│ ├── ShadowFrame.java # Shadow frame, manages execution context
│ ├── Nterp.java # Interpreter core, jump table and handlers
│ └── Main.java # Test program
└── README.md
| Instruction | Opcode | Description |
|---|---|---|
| NOP | 0x00 | No operation |
| CONST_4 | 0x14 | Load 4-bit constant |
| CONST | 0x12 | Load 32-bit constant |
| MOVE | 0x01 | Move register |
| MOVE_WIDE | 0x02 | Move wide register |
| ADD_INT | 0x90 | Integer addition |
| SUB_INT | 0x91 | Integer subtraction |
| RETURN | 0x11 | Return value |
| RETURN_VOID | 0x0E | No return value |
cd /workspace/java_nterp
# Compile
javac -d . src/main/java/rprop/java/nterp/*.java
# Run
java rprop.java.nterp.MainImport the project into IntelliJ IDEA or Eclipse, and run the Main class directly.
Bytecode:
byte[] code = {
0x14, 0x05, // v0 = 5
0x14, 0x17, // v1 = 7
0x90, 0x20, 0x01, // v2 = v0 + v1
0x11, 0x20 // return v2
};
Nterp nterp = new Nterp();
Nterp.InterpResult result = nterp.execute(code);
System.out.println("Result: " + result.returnValue); // Output: 12Bytecode:
byte[] code = {
0x12, 0x0A, 0x00, 0x00, 0x00, 0x00, // v0 = 10
0x14, 0x13, // v1 = 3
0x91, 0x20, 0x01, // v2 = v0 - v1
0x11, 0x20 // return v2
};
// Run... // Returns 7// Jump table in Nterp.java
private final InstructionHandler[] handlerTable;
private void initHandlers() {
handlerTable[OP_ADD_INT] = this::handleAddInt;
handlerTable[OP_SUB_INT] = this::handleSubInt;
// ... other instructions
}// Main loop
while (running) {
int opcode = sf.readByteUnsigned();
InstructionHandler handler = handlerTable[opcode]; // O(1) lookup
handler.handle(sf);
}private void handleAddInt(ShadowFrame sf) {
int b1 = sf.readByteUnsigned();
int vA = b1 & 0x0F; // Destination register
int b2 = sf.readByteUnsigned();
int vB = b2 & 0x0F;
int vC = (b2 & 0xF0) >> 4;
long result = sf.getVReg(vB) + sf.getVReg(vC);
sf.setVReg(vA, result);
}private void handleSubInt(ShadowFrame sf) {
int b1 = sf.readByteUnsigned();
int vA = b1 & 0x0F;
int b2 = sf.readByteUnsigned();
int vB = b2 & 0x0F;
int vC = (b2 & 0xF0) >> 4;
long result = sf.getVReg(vB) - sf.getVReg(vC);
sf.setVReg(vA, result);
}The following instructions can be added:
- Multiplication (MUL_INT, 0x92)
- Division (DIV_INT, 0x93)
- Modulo (REM_INT, 0x94)
- Logical operations (AND/OR/XOR)
- Comparison jumps (IF_EQ, IF_NE, etc.)
- Method invocation (INVOKE_*)
| Feature | Java nterp | Real ART |
|---|---|---|
| Architecture | Pure Java | C++ + Assembly (mterp) |
| JIT | No | Yes |
| Complete Instruction Set | Only 8 instructions | Full DEX instruction set |
| Optimization | Basic jump table | Extensive optimizations (inline, tail-call, etc.) |
=== Java nterp Interpreter Tests ===
--- Test 1: ADD (5 + 7 = 12) ---
Bytecode: [0x14, 0x05, 0x14, 0x17, 0x90, 0x20, 0x01, 0x11, 0x20]
Result:
=== ShadowFrame Dump ===
PC: 9
Method: 0
SP: 0
Registers (first 8):
v0: 0x0000000000000005 (5)
v1: 0x0000000000000007 (7)
v2: 0x000000000000000C (12)
...
Stack: []
=======================
Expected: 12
Actual: 12
Test passed!
--- Test 2: SUB (10 - 3 = 7) ---
...
=== Tests completed ===
For educational and research purposes only.