jvm-rs/docs/frame-interpreter.md

4.8 KiB

Frame and Bytecode Interpreter

Location: crates/core/src/frame/

Frame Structure

Each method invocation creates a Frame containing:

Component Description
Program Counter (PC) i64 tracking current bytecode instruction
Operand Stack Generic Vec-backed stack for intermediate values
Local Variables Indexed slots, handles wide values (long/double occupy 2 slots)
Constant Pool Arc reference to the class constant pool
Bytecode Instructions for the method

OperandStack (operand_stack.rs)

  • Generic Vec-backed stack with push/pop/peek operations
  • pop_n(n) returns values in push order (not pop order) for method arguments
  • Supports underflow detection

LocalVariables (local_vars.rs)

  • Vec-backed, indexed by slot
  • Handles wide values (long, double) that occupy 2 slots with padding
  • from_args() automatically spaces wide values correctly
  • Prevents access to padding slots with runtime panic

Execution Loop

loop {
    let (offset, op) = self.next().unwrap();
    self.pc = offset as i64;
    let result = self.execute_instruction(op.clone());
    match result {
        Ok(ExecutionResult::Advance(offset)) => self.pc += offset as i64,
        Ok(_) => self.pc += 1,
        Err(x) => return error with stack trace,
    }
}

Opcode Dispatch

Location: frame.rs execute_instruction() (lines 199-1516)

  • Single match statement over Ops enum variants (defined in instructions.rs)
  • 200+ opcodes: constants, loads/stores, math, stack ops, branches, references, method invocation
  • Uses helper macros (load!, store!, binary_op!, shift_op!, etc.) for common patterns
  • Each opcode returns one of:
    • ExecutionResult::Continue (auto-increment PC by 1)
    • ExecutionResult::Advance(offset) (jump)
    • ExecutionResult::Return(()) or ExecutionResult::ReturnValue(Value) (exit frame)
    • VmError on failure

Opcode Encoding (instructions.rs)

  • Uses deku derive for binary deserialization
  • Each opcode has a u8 ID (0x00-0xFF)
  • Some opcodes carry operands (e.g., iload(u8), goto(i16))
  • Wide instruction prefix (0xC4) for accessing local slots > 255

Method Invocation

Location: thread.rs (lines 308-338)

Invocation Types

  1. invoke(): Resolve method by class and descriptor, execute it
  2. invoke_virtual(): Virtual dispatch - find method on actual runtime class
  3. invoke_native(): Call native JNI method via FFI

Bytecode Invocation Instructions

  • invokevirtual: Virtual method dispatch - pop receiver + arguments, get actual class, call thread.invoke_virtual()
  • invokespecial: Non-virtual (constructors, private, super) - pop receiver + arguments, call thread.invoke() with static resolution
  • invokestatic: Static methods - pop arguments only (no receiver), call thread.invoke()
  • invokeinterface: Interface method dispatch - similar to invokevirtual with interface resolution

Frame Creation & Execution

fn execute_method(&self, class: &Arc<RuntimeClass>, method: &MethodData, args: Vec<Value>) {
    let mut frame = Frame::new(
        class.clone(),
        method_ref,
        code_attr,  // Contains max_stack, max_locals, bytecode
        args,       // Initialize local vars with parameters
        ...
    );
    self.frame_stack.lock().push(frame.clone());
    frame.execute()  // Bytecode interpretation loop
    self.frame_stack.lock().pop();
}

Supported Instructions

Constants

aconst_null, iconst_*, lconst_*, fconst_*, dconst_*, bipush, sipush, ldc, ldc_w, ldc2_w

Load/Store

iload, lload, fload, dload, aload, istore, lstore, fstore, dstore, astore (including _0-3 variants)

Array Operations

iaload, laload, faload, daload, aaload, baload, caload, saload, iastore, lastore, fastore, dastore, aastore, bastore, castore, sastore, arraylength

Stack Manipulation

pop, pop2, dup, dup_x1, dup_x2, dup2, dup2_x1, dup2_x2, swap

Arithmetic

All int/long/float/double add, sub, mul, div, rem, neg operations

Bitwise

ishl, lshl, ishr, lshr, iushr, lushr, iand, land, ior, lor, ixor, lxor

Type Conversions

All primitive type conversions (i2l, l2i, f2d, etc.)

Comparisons

lcmp, fcmpl, fcmpg, dcmpl, dcmpg

Control Flow

ifeq, ifne, iflt, ifge, ifgt, ifle, if_icmp*, if_acmp*, goto, ifnull, ifnonnull, tableswitch, lookupswitch

Object Operations

new, newarray, anewarray, multianewarray, checkcast, instanceof

Field Access

getstatic, putstatic, getfield, putfield

Method Invocation

invokevirtual, invokespecial, invokestatic, invokeinterface

Returns

ireturn, lreturn, freturn, dreturn, areturn, return

Synchronization

monitorenter, monitorexit