707 lines
27 KiB
Rust
707 lines
27 KiB
Rust
use crate::instruction::Instruction;
|
|
use crate::platform_function::PlatformFunction;
|
|
use crate::vm::class::Class;
|
|
use crate::vm::constant::Constant;
|
|
use crate::vm::function::Function;
|
|
use crate::vm::object::get_object;
|
|
use crate::vm::operand::Operand;
|
|
use crate::vm::operand_helpers::{
|
|
add_operand_to_value, location_or_integer_to_value, location_or_number_to_value,
|
|
move_operand_to_value, multiply_operand_to_value, push_operand_to_value,
|
|
return_operand_to_value, set_field_operand_to_value, subtract_operand_to_value,
|
|
};
|
|
use crate::vm::util::*;
|
|
use crate::vm::value::Value;
|
|
use std::cell::RefCell;
|
|
use std::collections::HashMap;
|
|
use std::ptr;
|
|
use std::rc::Rc;
|
|
|
|
pub mod class;
|
|
pub mod constant;
|
|
pub mod function;
|
|
pub mod object;
|
|
pub mod operand;
|
|
mod operand_helpers;
|
|
pub mod type_info;
|
|
mod util;
|
|
pub mod value;
|
|
|
|
pub struct DvmContext {
|
|
classes: HashMap<Rc<str>, Rc<Class>>,
|
|
functions: HashMap<Rc<str>, Function>,
|
|
platform_functions: HashMap<Rc<str>, PlatformFunction>,
|
|
constants: HashMap<Rc<str>, Constant>,
|
|
}
|
|
|
|
impl DvmContext {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
classes: HashMap::new(),
|
|
functions: HashMap::new(),
|
|
platform_functions: HashMap::new(),
|
|
constants: HashMap::new(),
|
|
}
|
|
}
|
|
|
|
pub fn classes(&self) -> &HashMap<Rc<str>, Rc<Class>> {
|
|
&self.classes
|
|
}
|
|
|
|
pub fn classes_mut(&mut self) -> &mut HashMap<Rc<str>, Rc<Class>> {
|
|
&mut self.classes
|
|
}
|
|
|
|
pub fn functions(&self) -> &HashMap<Rc<str>, Function> {
|
|
&self.functions
|
|
}
|
|
|
|
pub fn functions_mut(&mut self) -> &mut HashMap<Rc<str>, Function> {
|
|
&mut self.functions
|
|
}
|
|
|
|
pub fn platform_functions(&self) -> &HashMap<Rc<str>, PlatformFunction> {
|
|
&self.platform_functions
|
|
}
|
|
|
|
pub fn platform_functions_mut(&mut self) -> &mut HashMap<Rc<str>, PlatformFunction> {
|
|
&mut self.platform_functions
|
|
}
|
|
|
|
pub fn constants(&self) -> &HashMap<Rc<str>, Constant> {
|
|
&self.constants
|
|
}
|
|
|
|
pub fn constants_mut(&mut self) -> &mut HashMap<Rc<str>, Constant> {
|
|
&mut self.constants
|
|
}
|
|
}
|
|
|
|
pub struct CallFrame<'a> {
|
|
function: &'a Function,
|
|
instructions: &'a Vec<Instruction>,
|
|
ip: usize,
|
|
stack: Vec<Operand>,
|
|
fp: usize,
|
|
return_value: Option<Value>,
|
|
}
|
|
|
|
impl<'a> CallFrame<'a> {
|
|
fn new(function: &'a Function) -> Self {
|
|
Self {
|
|
function,
|
|
instructions: function.instructions(),
|
|
ip: 0,
|
|
stack: vec![],
|
|
fp: 0,
|
|
return_value: None,
|
|
}
|
|
}
|
|
|
|
fn function(&self) -> &'a Function {
|
|
self.function
|
|
}
|
|
|
|
fn instructions(&self) -> &'a [Instruction] {
|
|
self.instructions
|
|
}
|
|
|
|
fn ip(&self) -> usize {
|
|
self.ip
|
|
}
|
|
|
|
fn increment_ip(&mut self) {
|
|
self.ip += 1;
|
|
}
|
|
|
|
fn stack(&self) -> &[Operand] {
|
|
&self.stack
|
|
}
|
|
|
|
fn stack_mut(&mut self) -> &mut Vec<Operand> {
|
|
&mut self.stack
|
|
}
|
|
|
|
fn fp(&self) -> usize {
|
|
self.fp
|
|
}
|
|
|
|
fn set_fp(&mut self, fp: usize) {
|
|
self.fp = fp;
|
|
}
|
|
|
|
fn return_value(&self) -> Option<&Value> {
|
|
self.return_value.as_ref()
|
|
}
|
|
|
|
fn set_return_value(&mut self, return_value: Value) {
|
|
self.return_value = Some(return_value);
|
|
}
|
|
|
|
fn take_return_value(&mut self) -> Option<Value> {
|
|
self.return_value.take()
|
|
}
|
|
}
|
|
|
|
pub struct CallStack<'a> {
|
|
call_frames: Vec<CallFrame<'a>>,
|
|
}
|
|
|
|
impl<'a> CallStack<'a> {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
call_frames: vec![],
|
|
}
|
|
}
|
|
|
|
fn push(&mut self, call_frame: CallFrame<'a>) {
|
|
self.call_frames.push(call_frame);
|
|
}
|
|
|
|
fn pop(&mut self) -> CallFrame<'a> {
|
|
self.call_frames.pop().unwrap()
|
|
}
|
|
|
|
fn top(&self) -> &CallFrame<'a> {
|
|
&self.call_frames[self.call_frames.len() - 1]
|
|
}
|
|
|
|
fn top_mut(&mut self) -> &mut CallFrame<'a> {
|
|
let call_frames_len = self.call_frames.len();
|
|
&mut self.call_frames[call_frames_len - 1] // dang borrow checker
|
|
}
|
|
|
|
fn maybe_top(&self) -> Option<&CallFrame<'a>> {
|
|
self.call_frames.last()
|
|
}
|
|
|
|
fn maybe_top_mut(&mut self) -> Option<&mut CallFrame<'a>> {
|
|
self.call_frames.last_mut()
|
|
}
|
|
|
|
fn penultimate(&self) -> Option<&CallFrame<'a>> {
|
|
self.call_frames.get(self.call_frames.len() - 2)
|
|
}
|
|
|
|
fn penultimate_mut(&mut self) -> Option<&mut CallFrame<'a>> {
|
|
let call_frames_len = self.call_frames.len();
|
|
self.call_frames.get_mut(call_frames_len - 2) // dang borrow checker
|
|
}
|
|
}
|
|
|
|
pub fn call<'a>(
|
|
context: &'a DvmContext,
|
|
registers: &mut Vec<Operand>,
|
|
call_stack: &mut CallStack<'a>,
|
|
function_name: &str,
|
|
arguments: &[Value],
|
|
) -> Option<Value> {
|
|
// check if DVM_DEBUG is enabled
|
|
let debug = std::env::var("DVM_DEBUG")
|
|
.map(|s| s.eq("1"))
|
|
.unwrap_or(false);
|
|
|
|
let function = context
|
|
.functions
|
|
.get(function_name)
|
|
.expect(&format!("Function {} not found", function_name));
|
|
|
|
// push call frame
|
|
call_stack.push(CallFrame::new(function));
|
|
|
|
// put each arg on the stack
|
|
for argument in arguments {
|
|
call_stack
|
|
.top_mut()
|
|
.stack_mut()
|
|
.push(Operand::Value(argument.clone()));
|
|
}
|
|
|
|
// set fp for this function
|
|
call_stack.top_mut().set_fp(arguments.len());
|
|
|
|
// ensure enough stack space
|
|
call_stack.top_mut().stack_mut().resize(
|
|
arguments.len() + (function.stack_size() as usize),
|
|
Operand::Null,
|
|
);
|
|
|
|
// container for final return value
|
|
let mut return_value: Option<Value> = None;
|
|
|
|
while call_stack.maybe_top().is_some()
|
|
&& call_stack.top().ip() < call_stack.top().instructions().len()
|
|
{
|
|
let instruction = &call_stack.top().instructions()[call_stack.top().ip()];
|
|
if debug {
|
|
println!("{}", instruction);
|
|
}
|
|
|
|
match instruction {
|
|
/* Move instructions */
|
|
Instruction::Move(source, destination) => {
|
|
// move a value to a destination
|
|
// could be a copy or an immediate
|
|
let value =
|
|
move_operand_to_value(source, registers, call_stack.top(), context.constants());
|
|
put_value(registers, call_stack.top_mut(), destination, value);
|
|
call_stack.top_mut().increment_ip();
|
|
}
|
|
|
|
/* Push instructions */
|
|
Instruction::Push(operand) => {
|
|
let value = push_operand_to_value(
|
|
operand,
|
|
registers,
|
|
call_stack.top(),
|
|
context.constants(),
|
|
);
|
|
call_stack.top_mut().stack_mut().push(Operand::Value(value));
|
|
call_stack.top_mut().increment_ip();
|
|
}
|
|
|
|
/* Invoke instructions */
|
|
Instruction::InvokeStatic(function_name, arg_count) => {
|
|
let function = context
|
|
.functions
|
|
.get(function_name)
|
|
.expect(&format!("Function {} not found", function_name));
|
|
|
|
// get args
|
|
// this isn't perfect (yet), but for now it works and satisfies the borrow checkers
|
|
let mut args = call_stack.top().stack()
|
|
[call_stack.top().stack().len() - arg_count..]
|
|
.iter()
|
|
.map(|operand| operand.unwrap_value().clone())
|
|
.map(|value| Operand::Value(value))
|
|
.collect::<Vec<_>>();
|
|
|
|
// save registers
|
|
for register in registers.iter_mut() {
|
|
let value = std::mem::take(register);
|
|
call_stack.top_mut().stack_mut().push(value);
|
|
}
|
|
|
|
// increment caller frame ip
|
|
call_stack.top_mut().increment_ip();
|
|
|
|
// push a new call frame
|
|
call_stack.push(CallFrame::new(function));
|
|
|
|
// push args
|
|
call_stack.top_mut().stack_mut().append(&mut args);
|
|
|
|
// ensure enough stack space
|
|
call_stack
|
|
.top_mut()
|
|
.stack_mut()
|
|
.resize(arg_count + (function.stack_size() as usize), Operand::Null);
|
|
|
|
// set fp for callee frame
|
|
let callee_frame = call_stack.top_mut();
|
|
callee_frame.set_fp(*arg_count);
|
|
}
|
|
|
|
Instruction::InvokePlatformStatic(function_name, arg_count) => {
|
|
let platform_function = context
|
|
.platform_functions()
|
|
.get(function_name)
|
|
.expect(&format!("Platform function {} not found", function_name));
|
|
|
|
let arg_operands =
|
|
&call_stack.top().stack()[call_stack.top().stack().len() - arg_count..];
|
|
let args = arg_operands
|
|
.iter()
|
|
.map(|operand| operand.unwrap_value().clone())
|
|
.collect::<Vec<_>>();
|
|
|
|
let result = platform_function(&args);
|
|
match result {
|
|
Ok(return_value) => {
|
|
// push return value to top of caller stack if it's not null
|
|
if !matches!(return_value, Value::Null) {
|
|
call_stack
|
|
.top_mut()
|
|
.stack_mut()
|
|
.push(Operand::Value(return_value));
|
|
}
|
|
}
|
|
Err(error) => {
|
|
// Eventually we will have some kind of exception handling
|
|
panic!("{}", error);
|
|
}
|
|
}
|
|
call_stack.top_mut().increment_ip();
|
|
}
|
|
|
|
/* Object instructions */
|
|
Instruction::Allocate(class_fqn, destination) => {
|
|
let class = context
|
|
.classes()
|
|
.get(class_fqn)
|
|
.expect(&format!("No such class loaded: {}", class_fqn));
|
|
let object = get_object(class);
|
|
let as_rc = Rc::new(RefCell::new(object));
|
|
let value = Value::Object(as_rc);
|
|
put_value(registers, call_stack.top_mut(), destination, value);
|
|
call_stack.top_mut().increment_ip();
|
|
}
|
|
|
|
Instruction::GetFieldPointer(self_location, field_index, destination) => {
|
|
let object = load_value(
|
|
registers,
|
|
call_stack.top().stack(),
|
|
call_stack.top().fp(),
|
|
self_location,
|
|
)
|
|
.unwrap_object()
|
|
.clone();
|
|
let field_ref = ptr::from_ref(&object.borrow_mut().fields_mut()[*field_index]);
|
|
put_operand(
|
|
registers,
|
|
call_stack.top_mut(),
|
|
destination,
|
|
Operand::FieldPointer(field_ref),
|
|
);
|
|
call_stack.top_mut().increment_ip();
|
|
}
|
|
|
|
Instruction::GetFieldPointerMut(self_location, field_index, destination) => {
|
|
let object = load_value(
|
|
registers,
|
|
call_stack.top().stack(),
|
|
call_stack.top().fp(),
|
|
self_location,
|
|
)
|
|
.unwrap_object()
|
|
.clone();
|
|
let field_ref = ptr::from_mut(&mut object.borrow_mut().fields_mut()[*field_index]);
|
|
put_operand(
|
|
registers,
|
|
call_stack.top_mut(),
|
|
destination,
|
|
Operand::FieldPointerMut(field_ref),
|
|
);
|
|
call_stack.top_mut().increment_ip();
|
|
}
|
|
|
|
Instruction::ReadField(field_pointer_location, destination) => {
|
|
let field_pointer = load_operand(
|
|
registers,
|
|
call_stack.top().stack(),
|
|
call_stack.top().fp(),
|
|
field_pointer_location,
|
|
)
|
|
.unwrap_field_pointer();
|
|
// clone is very important, otherwise we mess up the self object!
|
|
let value = unsafe { field_pointer.read() }.clone();
|
|
put_value(registers, call_stack.top_mut(), destination, value);
|
|
call_stack.top_mut().increment_ip();
|
|
}
|
|
|
|
Instruction::SetField(field_point_mut_location, operand) => {
|
|
let fp = call_stack.top().fp(); // borrow checker :)
|
|
let field_pointer_mut = load_operand_mut(
|
|
registers,
|
|
call_stack.top_mut().stack_mut(),
|
|
fp,
|
|
field_point_mut_location,
|
|
)
|
|
.unwrap_field_pointer_mut();
|
|
let value = set_field_operand_to_value(
|
|
operand,
|
|
registers,
|
|
call_stack.top(),
|
|
context.constants(),
|
|
);
|
|
unsafe {
|
|
field_pointer_mut.write(value);
|
|
}
|
|
call_stack.top_mut().increment_ip();
|
|
}
|
|
|
|
/* Multiplicative instructions */
|
|
Instruction::Multiply(left_operand, right_operand, destination) => {
|
|
let left_value =
|
|
multiply_operand_to_value(left_operand, registers, call_stack.top());
|
|
let right_value =
|
|
multiply_operand_to_value(right_operand, registers, call_stack.top());
|
|
|
|
let result = match left_value {
|
|
Value::Int(li) => match right_value {
|
|
Value::Int(ri) => Value::Int(li * ri),
|
|
Value::Double(rd) => Value::Double(li as f64 * rd),
|
|
_ => panic!("Attempt to multiply {:?} and {:?}", left_value, right_value),
|
|
},
|
|
Value::Double(ld) => match right_value {
|
|
Value::Int(ri) => Value::Double(ld * ri as f64),
|
|
Value::Double(rd) => Value::Double(ld * rd),
|
|
_ => panic!("Attempt to multiply {:?} and {:?}", left_value, right_value),
|
|
},
|
|
_ => panic!("Attempt to multiply {:?} and {:?}", left_value, right_value),
|
|
};
|
|
|
|
put_value(registers, call_stack.top_mut(), destination, result);
|
|
call_stack.top_mut().increment_ip();
|
|
}
|
|
Instruction::Divide(left, right, destination) => {
|
|
let left_value = location_or_number_to_value(left, registers, call_stack.top());
|
|
let right_value = location_or_number_to_value(right, registers, call_stack.top());
|
|
|
|
let result = match left_value {
|
|
Value::Int(li) => match right_value {
|
|
Value::Int(ri) => Value::Double(li as f64 / ri as f64),
|
|
Value::Double(rd) => Value::Double(li as f64 / rd),
|
|
_ => panic!("Attempt to divide {:?} and {:?}", left_value, right_value),
|
|
},
|
|
Value::Double(ld) => match right_value {
|
|
Value::Int(ri) => Value::Double(ld / ri as f64),
|
|
Value::Double(rd) => Value::Double(ld / rd),
|
|
_ => panic!("Attempt to divide {:?} and {:?}", left_value, right_value),
|
|
},
|
|
_ => panic!("Attempt to divide {:?} and {:?}", left_value, right_value),
|
|
};
|
|
put_value(registers, call_stack.top_mut(), destination, result);
|
|
call_stack.top_mut().increment_ip();
|
|
}
|
|
Instruction::Modulo(left, right, destination) => {
|
|
let left_value = location_or_number_to_value(left, registers, call_stack.top());
|
|
let right_value = location_or_number_to_value(right, registers, call_stack.top());
|
|
|
|
let result = match left_value {
|
|
Value::Int(li) => match right_value {
|
|
Value::Int(ri) => Value::Int(li % ri),
|
|
Value::Double(rd) => Value::Double(li as f64 % rd),
|
|
_ => panic!("Attempt to modulo {:?} and {:?}", left_value, right_value),
|
|
},
|
|
Value::Double(ld) => match right_value {
|
|
Value::Int(ri) => Value::Double(ld % ri as f64),
|
|
Value::Double(rd) => Value::Double(ld % rd),
|
|
_ => panic!("Attempt to modulo {:?} and {:?}", left_value, right_value),
|
|
},
|
|
_ => panic!("Attempt to modulo {:?} and {:?}", left_value, right_value),
|
|
};
|
|
|
|
put_value(registers, call_stack.top_mut(), destination, result);
|
|
call_stack.top_mut().increment_ip();
|
|
}
|
|
|
|
/* Add instructions */
|
|
Instruction::Add(left_operand, right_operand, destination) => {
|
|
let left_value = add_operand_to_value(
|
|
&left_operand,
|
|
registers,
|
|
call_stack.top(),
|
|
context.constants(),
|
|
);
|
|
|
|
let right_value = add_operand_to_value(
|
|
&right_operand,
|
|
registers,
|
|
call_stack.top(),
|
|
context.constants(),
|
|
);
|
|
|
|
let result = match left_value {
|
|
Value::Int(li) => match right_value {
|
|
Value::Int(ri) => Value::Int(li + ri),
|
|
Value::Double(rd) => Value::Double(li as f64 + rd),
|
|
Value::String(rs) => Value::String(format!("{}{}", li, rs).into()),
|
|
_ => panic!(
|
|
"Attempt to add non-addable {:?} type to {:?}",
|
|
right_value, left_value
|
|
),
|
|
},
|
|
Value::Double(ld) => match right_value {
|
|
Value::Int(ri) => Value::Double(ld + ri as f64),
|
|
Value::Double(rd) => Value::Double(ld + rd),
|
|
Value::String(rs) => Value::String(format!("{}{}", ld, rs).into()),
|
|
_ => panic!(
|
|
"Attempt to add non-addable {:?} type to {:?}",
|
|
right_value, left_value
|
|
),
|
|
},
|
|
Value::String(ref ls) => match right_value {
|
|
Value::Int(ri) => Value::String(format!("{}{}", ls, ri).into()),
|
|
Value::Double(rd) => Value::String(format!("{}{}", ls, rd).into()),
|
|
Value::String(rs) => Value::String(format!("{}{}", ls, rs).into()),
|
|
_ => panic!(
|
|
"Attempt to add non-addable {:?} type to {:?}",
|
|
right_value, left_value
|
|
),
|
|
},
|
|
_ => panic!("Attempt to add with non-addable type: {:?}", left_value),
|
|
};
|
|
|
|
put_value(registers, call_stack.top_mut(), destination, result);
|
|
|
|
call_stack.top_mut().increment_ip();
|
|
}
|
|
|
|
Instruction::Subtract(left_operand, right_operand, destination) => {
|
|
let left = subtract_operand_to_value(left_operand, registers, call_stack.top());
|
|
let right = subtract_operand_to_value(right_operand, registers, call_stack.top());
|
|
|
|
let result = match left {
|
|
Value::Int(li) => match right {
|
|
Value::Int(ri) => Value::Int(li - ri),
|
|
Value::Double(rd) => Value::Double(li as f64 - rd),
|
|
_ => panic!("Attempt to subtract {:?} from {:?}", right, left),
|
|
},
|
|
Value::Double(ld) => match right {
|
|
Value::Int(ri) => Value::Double(ld - ri as f64),
|
|
Value::Double(rd) => Value::Double(ld - rd),
|
|
_ => panic!("Attempt to subtract {:?} from {:?}", right, left),
|
|
},
|
|
_ => panic!("Attempt to subtract {:?} from {:?}", right, left),
|
|
};
|
|
|
|
put_value(registers, call_stack.top_mut(), destination, result);
|
|
call_stack.top_mut().increment_ip();
|
|
}
|
|
|
|
/* Shift instructions */
|
|
Instruction::LeftShift(left, right, destination) => {
|
|
let left_value = location_or_integer_to_value(left, registers, call_stack.top());
|
|
let right_value = location_or_integer_to_value(right, registers, call_stack.top());
|
|
|
|
let result = match left_value {
|
|
Value::Int(li) => match right_value {
|
|
Value::Int(ri) => Value::Int(li << ri),
|
|
_ => panic!("Attempt to left shift {} by {}", left, right),
|
|
},
|
|
_ => panic!("Attempt to left shift {} by {}", left_value, right_value),
|
|
};
|
|
|
|
put_value(registers, call_stack.top_mut(), destination, result);
|
|
call_stack.top_mut().increment_ip();
|
|
}
|
|
Instruction::RightShift(left, right, destination) => {
|
|
let left_value = location_or_integer_to_value(left, registers, call_stack.top());
|
|
let right_value = location_or_integer_to_value(right, registers, call_stack.top());
|
|
|
|
let result = match left_value {
|
|
Value::Int(li) => match right_value {
|
|
Value::Int(ri) => Value::Int(li >> ri),
|
|
_ => panic!("Attempt to right shift {} by {}", left_value, right_value),
|
|
},
|
|
_ => panic!("Attempt to right shift {} by {}", left_value, right_value),
|
|
};
|
|
|
|
put_value(registers, call_stack.top_mut(), destination, result);
|
|
call_stack.top_mut().increment_ip();
|
|
}
|
|
|
|
/* Bitwise instructions */
|
|
Instruction::BitwiseAnd(left, right, destination) => {
|
|
let left_value = location_or_integer_to_value(left, registers, call_stack.top());
|
|
let right_value = location_or_integer_to_value(right, registers, call_stack.top());
|
|
|
|
let result = match left_value {
|
|
Value::Int(li) => match right_value {
|
|
Value::Int(ri) => Value::Int(li & ri),
|
|
_ => panic!("Attempt to bitwise and {} by {}", left_value, right_value),
|
|
},
|
|
_ => panic!("Attempt to bitwise and {} by {}", left_value, right_value),
|
|
};
|
|
|
|
put_value(registers, call_stack.top_mut(), destination, result);
|
|
call_stack.top_mut().increment_ip();
|
|
}
|
|
Instruction::BitwiseXor(left, right, destination) => {
|
|
let left_value = location_or_integer_to_value(left, registers, call_stack.top());
|
|
let right_value = location_or_integer_to_value(right, registers, call_stack.top());
|
|
|
|
let result = match left_value {
|
|
Value::Int(li) => match right_value {
|
|
Value::Int(ri) => Value::Int(li ^ ri),
|
|
_ => panic!("Attempt to bitwise xor {} by {}", left_value, right_value),
|
|
},
|
|
_ => panic!(
|
|
"Attempt to left bitwise xor {} by {}",
|
|
left_value, right_value
|
|
),
|
|
};
|
|
|
|
put_value(registers, call_stack.top_mut(), destination, result);
|
|
call_stack.top_mut().increment_ip();
|
|
}
|
|
Instruction::BitwiseOr(left, right, destination) => {
|
|
let left_value = location_or_integer_to_value(left, registers, call_stack.top());
|
|
let right_value = location_or_integer_to_value(right, registers, call_stack.top());
|
|
|
|
let result = match left_value {
|
|
Value::Int(li) => match right_value {
|
|
Value::Int(ri) => Value::Int(li | ri),
|
|
_ => panic!("Attempt to bitwise or {} by {}", left_value, right_value),
|
|
},
|
|
_ => panic!(
|
|
"Attempt to left bitwise or {} by {}",
|
|
left_value, right_value
|
|
),
|
|
};
|
|
|
|
put_value(registers, call_stack.top_mut(), destination, result);
|
|
call_stack.top_mut().increment_ip();
|
|
}
|
|
|
|
/* Pop instructions */
|
|
Instruction::Pop(maybe_location) => {
|
|
let value = call_stack.top_mut().stack_mut().pop().unwrap();
|
|
if let Some(location) = maybe_location {
|
|
put_operand(registers, call_stack.top_mut(), location, value);
|
|
}
|
|
call_stack.top_mut().increment_ip();
|
|
}
|
|
|
|
/* Return instructions */
|
|
Instruction::SetReturnValue(return_operand) => {
|
|
let value = return_operand_to_value(
|
|
return_operand,
|
|
registers,
|
|
call_stack.top(),
|
|
context.constants(),
|
|
);
|
|
call_stack.top_mut().set_return_value(value);
|
|
call_stack.top_mut().increment_ip();
|
|
}
|
|
|
|
Instruction::Return => {
|
|
let mut callee = call_stack.pop(); // pop this frame
|
|
let maybe_caller = call_stack.maybe_top_mut();
|
|
if let Some(caller) = maybe_caller {
|
|
// restore registers
|
|
for i in (0..registers.len()).rev() {
|
|
registers[i] = caller.stack_mut().pop().unwrap();
|
|
}
|
|
|
|
// pop args
|
|
for _ in 0..callee.function().parameter_count() {
|
|
caller.stack_mut().pop();
|
|
}
|
|
|
|
// push return value if some
|
|
if let Some(return_value) = callee.take_return_value() {
|
|
// n.b. callee
|
|
caller.stack_mut().push(Operand::Value(return_value));
|
|
}
|
|
} else {
|
|
// callee is the bottommost frame ("main" or whatever was called)
|
|
// set the return value
|
|
return_value = callee.take_return_value();
|
|
}
|
|
// do not increment ip of the caller; this was done above BEFORE the call
|
|
}
|
|
}
|
|
|
|
if debug {
|
|
if let Some(top) = call_stack.maybe_top() {
|
|
println!(" stack: {:?}", top.stack());
|
|
println!(" registers: {:?}", registers);
|
|
println!(" rv: {:?}", top.return_value());
|
|
}
|
|
}
|
|
}
|
|
return_value
|
|
}
|