feat(vm): [Phase 1 & 2] Dalvik VM interpreter — Interpreter Basic & Core Instruction Set Support#2
Open
haeter525 wants to merge 12 commits intoev-flow:mainfrom
Open
feat(vm): [Phase 1 & 2] Dalvik VM interpreter — Interpreter Basic & Core Instruction Set Support#2haeter525 wants to merge 12 commits intoev-flow:mainfrom
haeter525 wants to merge 12 commits intoev-flow:mainfrom
Conversation
- vm/decoder.py: walk_method() static walk adapter over DalvikDisassembler DecoderTables type alias, MethodNotFound exception - cli/cmd_trace.py: dextrace trace subcommand with JSON envelope output matching cmd_disasm schema (version/format/source/method/instructions) stderr-only errors, exit codes 0/1/3 - cli/main.py: wire trace subcommand - tests/fixtures/samples/p1_const_return.dex: Lp1;->main()I fixture (const/16 v0, 42; return v0) built by tools/gen_p1_fixture.py - tests/test_vm_trace_p1.py: 9 tests — unit + CLI integration Verification: result['instructions'][0]['mnemonic'] == 'const/16' ✓ 59/59 tests pass, 0 regressions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
wrap_method() now catches ValueError/struct.error from DexResolver and DalvikDisassembler and re-raises as DexParseError. cmd_trace.py catches DexParseError and prints [ERROR] message, exits 3. Before: python traceback to stderr, exit 1 After: [ERROR] malformed DEX: File too small to contain a valid DEX header, exit 3 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
apk_reader.read_file() raises KeyError when the entry doesn't exist. The old `if not dex_bytes` guard was dead code — the exception happens before it. Fix: check list_entries() before reading. Before: KeyError traceback to stderr, exit 1 After: [ERROR] APK does not contain classes.dex, exit 3 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ISSUE-002: malformed DEX must raise DexParseError (not raw ValueError traceback) ISSUE-003: APK without classes.dex must exit 3 with [ERROR] (not KeyError) Found by /qa on 2026-04-11 Report: .gstack/qa-reports/qa-report-dextrace-2026-04-11.md Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add dextrace run subcommand: executes a method in the Dalvik VM interpreter with static analysis semantics (no JNI, no heap) - Add vm/engine.py: step-budget interpreter loop with opcode dispatch - Add vm/handlers/: arithmetic, branch, compare, field, move, array, type_conv instruction handlers - Add vm/state.py, call_frame.py, register_file.py, errors.py, int_ops.py - Add p2 fixture (Fib recursive DEX) + gen_p2_fixture.py - Add Phase 2 tests: test_vm_run_p1, test_vm_run_p2, tests/vm/ unit suite Review fixes (from /review): - decoder.py: widen DexParseError catch to include IndexError, KeyError, OverflowError — real malformed DEX triggers opcode table KeyError - cmd_trace.py: add is_file() guard to prevent IsADirectoryError traceback - cmd_trace.py: stage BadZipFile catch, flags filter, resolve() path fix, REVIEW-001 regression test (non-ZIP APK → exit 3) - decoder.py: remove unused DecoderTables type alias + dead imports Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extract load_dex_bytes() to shared cli/_io.py and use it from both cmd_trace and cmd_run. dextrace run now accepts .apk files, extracts classes.dex, and returns clean errors when the APK is malformed or missing classes.dex. Previously gave "expected a .dex file, got: '.apk'". Also fixes ISSUE-005: add is_file() guard to cmd_run so a directory named *.dex gives exit 1 (user error) instead of exit 3 (parse error) with an internal OSError traceback. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ISSUE-004: APK files rejected with wrong error in dextrace run ISSUE-005: directory-as-input gives wrong exit code (3 instead of 1) Found by /qa on 2026-04-11 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- cmd_run.py: suppress too-many-return-statements/branches on run() - move.py: suppress unused-argument on handle_nop (intentional no-op) - register_file.py: suppress protected-access in snapshot() (same class) - gen_p1/p2_fixture.py: rename HEADER_SIZE→header_size, _uleb128 out→buf - test_handlers.py: move imports to module top-level, remove unused imports Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
gen_p1 and gen_p2 intentionally share boilerplate structure as standalone tools building different DEX test fixtures. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- cmd_run.py: add --dump-regs flag to print non-zero register values after execution; add _print_registers() helper - engine.py: save final VMState after run(); expose final_registers property for post-execution register inspection - gen_p1_fixture.py: change method from ()I/return to ()V/return-void so Phase 1 demo exercises const/16 + return-void with --dump-regs - Regenerate p1_const_return.dex with updated bytecode and proto - Update all tests to use new entry sig Lp1;->main()V and void semantics Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Key Changes
Phase 1 - Interpreter Basic
Execution Engine
engine.py:DalvikVMmain loop with call-frame stack and invoke handlingerrors.py: DalvikVM runtime error typescmd_run.py: newdextrace runsubcommand to run the Dalvik VM interpreter_io.py: shared DEX/APK loader; handles APK with missingclasses.dexand malformed DEX inputRegister File
register_file.py: Dalvik register file (v0–v15) with bounds checkingint_ops.py: integer arithmetic with overflow semanticsCall Stack & Frame
call_frame.py: per-call register snapshot/restore for callee isolationstate.py:VMStatedataclass withpending_resultlifecycle forinvoke/move-resultPhase 2 - Core Instruction Set Support
handlers/: arithmetic, array, branch, compare, field, move, type_conv opcode handlersHow to use
Installation
Phase 1 — execute
const/16 v0, #42 → return-voidv0should be42voidPhase 2 — execute
recursive Fibonacci55Tests
pytest tests