141 lines
4.8 KiB
Markdown
141 lines
4.8 KiB
Markdown
# 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
|
|
|
|
```rust
|
|
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
|
|
|
|
```rust
|
|
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` |