use std::sync::Arc;
use std::cell::RefCell;
use std::mem;
use failure::Error;
use smpl::{ FnId, byte_gen };
use smpl::metadata::Metadata;
use smpl::byte_gen::{ InstructionPointerType, Instruction, Location, Arg, FieldAccess };
use crate::err::*;
use crate::env::Env;
use crate::value::{ Value, ReferableValue, Struct, Array };
use crate::vm_i::{ FnHandle, BuiltinFn };
use crate::vm::{ MappedBuiltins, CompiledProgram };
pub enum ExecResult<T, E> {
Ok(T),
Pending,
Err(E),
}
#[derive(Debug)]
pub struct Executor {
metadata: Arc<Metadata>,
top: StackInfo,
stack: Vec<StackInfo>,
compiled: CompiledProgram,
builtins: MappedBuiltins,
return_register: Option<Value>,
module_env: Env,
finished: bool,
}
impl Executor {
pub(super) fn new(metadata: Arc<Metadata>,
fn_handle: FnHandle,
compiled: CompiledProgram,
builtins: MappedBuiltins,
args: Vec<Value>) -> Result<Executor, InternalError> {
let mut module_env = Env::new();
for (fn_id, _) in compiled.iter() {
let scoped_handle = Value::Function(
FnHandle::new(fn_handle.mod_id(), *fn_id)
);
module_env.map_value(byte_gen::to_fn_id(*fn_id), scoped_handle);
}
for (fn_id, _) in builtins.iter() {
let scoped_handle = Value::Function(
FnHandle::new(fn_handle.mod_id(), *fn_id)
);
module_env.map_value(byte_gen::to_fn_id(*fn_id), scoped_handle);
}
let current =
Executor::create_stack_info(&*metadata,
fn_handle,
compiled.clone(),
builtins.clone(),
&module_env,
args)?;
let executor = Executor {
metadata: metadata,
top: current,
stack: Vec::new(),
compiled: compiled,
builtins: builtins,
return_register: None,
module_env: module_env,
finished: false,
};
Ok(executor)
}
pub fn execute_sync(mut self) -> Result<Value, Error> {
futures::executor::block_on(self.execute())
}
pub async fn execute(mut self) -> Result<Value, Error> {
while !self.finished {
self.step().await?;
}
Ok(self.return_register.take().unwrap_or(Value::Unit))
}
fn create_stack_info(metadata: &Metadata,
fn_handle: FnHandle,
compiled: CompiledProgram,
builtins: MappedBuiltins,
module_env: &Env,
args: Vec<Value>) -> Result<StackInfo, InternalError> {
let fn_id = fn_handle.fn_id();
if metadata.is_builtin(fn_id) {
Ok(StackInfo::BuiltinStack(BuiltinStack::new(fn_handle,
compiled.clone(),
builtins.clone(),
args)))
} else {
let param_info: &[_]= metadata.function_param_ids(fn_id);
let args_len = args.len();
if param_info.len() != args_len {
return Err(InternalError::InvalidArgCount(args_len,
ExpectedArgCount::Exact(param_info.len())));
}
let mut stack_info = ByteCodeStack::new(
fn_handle, compiled.clone(), builtins.clone(), module_env);
for (arg, param_info) in args
.into_iter()
.zip(param_info) {
stack_info.env
.map_value(param_info.name().to_string(), arg);
}
Ok(StackInfo::ByteCodeStack(stack_info))
}
}
async fn step(&mut self) -> Result<(), Error> {
let exec_action = match self.top {
StackInfo::BuiltinStack(BuiltinStack {
ref current_fn,
ref mut args,
..
}) => {
let mut arg_buff = Vec::new();
std::mem::swap(args, &mut arg_buff);
let result = (*current_fn)(arg_buff).await?;
ExecuteAction::PopStack(result)
}
StackInfo::ByteCodeStack(ByteCodeStack {
ref current_fn,
ref compiled,
ref builtins,
ref mut env,
ref mut instruction_pointer,
..
}) => {
let instructions = current_fn.instructions();
let instruction = instructions
.get(*instruction_pointer as usize)
.ok_or(InternalError::InstructionPointerOutOfBounds {
ip: *instruction_pointer,
max: instructions.len()
})?;
let execute_action = Executor::execute_instruction(
instruction,
*instruction_pointer,
env,
&mut self.return_register,
)?;
match execute_action {
ExecuteAction::PushStack(..) | ExecuteAction::IncrementIP => {
let (result, overflow) =
instruction_pointer.overflowing_add(1);
if overflow {
Err(InternalError::RuntimeInstructionError(
RuntimeInstructionError::IPOverflow {
current: *instruction_pointer,
addition: 1,
}))?;
} else {
*instruction_pointer = result;
}
}
ExecuteAction::SetIP(new_ip) => {
*instruction_pointer = new_ip;
}
ExecuteAction::AddIP(to_add) => {
if to_add >= 0 {
let (result, overflow) =
instruction_pointer.overflowing_add(to_add as u64);
if overflow {
Err(InternalError::RuntimeInstructionError(
RuntimeInstructionError::IPOverflow {
current: *instruction_pointer,
addition: to_add,
}))?;
} else {
*instruction_pointer = result;
}
} else {
let (result, underflow) =
instruction_pointer.overflowing_sub((-to_add) as u64);
if underflow {
Err(InternalError::RuntimeInstructionError(
RuntimeInstructionError::IPUnderflow {
current: *instruction_pointer,
addition: to_add,
}))?;
} else {
*instruction_pointer = result;
}
}
}
ExecuteAction::PopStack(_) => (),
};
execute_action
}
};
match exec_action {
ExecuteAction::PushStack(fn_handle, args) => {
let mut stack_frame = Executor::create_stack_info(
&*self.metadata,
fn_handle,
self.compiled.clone(),
self.builtins.clone(),
&self.module_env,
args
)?;
mem::swap(&mut stack_frame, &mut self.top);
let old_top = stack_frame;
self.stack.push(old_top);
Ok(())
}
ExecuteAction::PopStack(value) => {
match self.stack.pop() {
Some(mut stack_top) => {
mem::swap(&mut stack_top, &mut self.top);
let _to_drop = stack_top;
self.return_register = Some(value);
Ok(())
}
None => {
self.finished = true;
self.return_register = Some(value);
Ok(())
}
}
}
ExecuteAction::IncrementIP | ExecuteAction::SetIP(_) | ExecuteAction::AddIP(_) => {
Ok(())
}
}
}
fn fetch(env: &Env, location: &Location) -> ReferableValue {
match location {
Location::Compound {
ref root,
ref root_index,
ref path,
} => {
let root_ref: ReferableValue = env.get_ref(root).unwrap();
let root_ref: ReferableValue = match root_index {
Some(index_name) => {
let index_value: Value = env.get(index_name).unwrap();
let index = match index_value {
Value::Int(i) => {
i as usize
}
_ => unimplemented!(),
};
let inner_ref = root_ref.inner_ref();
match *inner_ref {
Value::Array(ref v) => {
v
.get(index)
.expect(&format!("Invalid index: {}", index))
.ref_clone()
}
_ => unimplemented!(),
}
},
None => root_ref,
};
let mut next_ref: ReferableValue = root_ref;
for field_access in path {
match field_access {
FieldAccess::Field(ref field_name) => {
let new_ref = {
let inner_ref = next_ref.inner_ref();
match *inner_ref {
Value::Struct(ref internal) => {
let field: ReferableValue = internal
.ref_field(field_name)
.unwrap()
.ref_clone();
field
},
_ => unimplemented!(),
}
};
next_ref = new_ref;
}
FieldAccess::FieldIndex {
ref field,
ref index_tmp,
} => {
let field_ref = {
let inner_ref = next_ref.inner_ref();
match *inner_ref {
Value::Struct(ref internal) => {
let field: ReferableValue = internal
.ref_field(field)
.unwrap()
.ref_clone();
field
},
_ => unimplemented!(),
}
};
let new_ref = {
let index_value: Value = env.get(index_tmp).unwrap();
let index = match index_value {
Value::Int(i) => {
i as usize
}
_ => unimplemented!(),
};
let inner_ref = field_ref.inner_ref();
match *inner_ref {
Value::Array(ref v) => {
v
.get(index)
.unwrap()
.ref_clone()
}
_ => unimplemented!(),
}
};
next_ref = new_ref;
}
}
}
next_ref
}
Location::Namespace(ref name) => {
env.ref_value(name).unwrap()
}
Location::Tmp(ref name) => {
env.ref_tmp(name).unwrap()
}
}
}
fn store(env: &mut Env, location: &Location, value: Value) {
match location {
Location::Compound { .. } => {
let reference: ReferableValue = Executor::fetch(env, location);
*reference.inner_ref_mut() = value;
}
Location::Namespace(ref name) => {
env.map_value(name.clone(), value);
}
Location::Tmp(ref name) => {
env.map_tmp(name.clone(), value);
}
}
}
fn arg_to_value(env: &Env, arg: &Arg) -> Value {
match arg {
Arg::Location(ref arg_loc) => Executor::fetch(env, arg_loc).clone_value(),
Arg::Int(ref i) => Value::Int(*i),
Arg::Float(ref f) => Value::Float(*f),
Arg::Bool(ref b) => Value::Bool(*b),
Arg::String(ref s) => Value::String(s.clone()),
}
}
fn execute_instruction(instruction: &Instruction, ip: InstructionPointerType,
env: &mut Env, return_register: &mut Option<Value>)
-> Result<ExecuteAction, InternalError> {
macro_rules! integer_from_arg {
($arg: expr, $instr: expr) => {{
let from_arg = match $arg {
Arg::Location(ref arg_loc) => Executor::fetch(env, arg_loc).clone_value(),
Arg::Int(ref i) => Value::Int(*i),
_ => return Err(InternalError::InvalidInstruction(
IIReason::ExpectedInt($instr.clone()))),
};
match from_arg {
Value::Int(i) => i,
_ => return Err(InternalError::RuntimeInstructionError(
RuntimeInstructionError::ExpectedInt($instr.clone()))),
}
}}
}
macro_rules! float_from_arg {
($arg: expr, $instr: expr) => {{
let from_arg = match $arg {
Arg::Location(ref arg_loc) => Executor::fetch(env, arg_loc).clone_value(),
Arg::Float(ref f) => Value::Float(*f),
_ => return Err(InternalError::InvalidInstruction(
IIReason::ExpectedFloat($instr.clone()))),
};
match from_arg {
Value::Float(f) => f,
_ => return Err(InternalError::RuntimeInstructionError(
RuntimeInstructionError::ExpectedFloat($instr.clone()))),
}
}}
}
macro_rules! bool_from_arg {
($arg: expr, $instr: expr) => {{
let from_arg = match $arg {
Arg::Location(ref arg_loc) => Executor::fetch(env, arg_loc).clone_value(),
Arg::Bool(ref b) => Value::Bool(*b),
_ => return Err(InternalError::InvalidInstruction(
IIReason::ExpectedBool($instr.clone()))),
};
match from_arg {
Value::Bool(b) => b,
_ => return Err(InternalError::RuntimeInstructionError(
RuntimeInstructionError::ExpectedBool($instr.clone()))),
}
}}
}
macro_rules! int_op {
($env: expr, $instruction: expr, $store_loc: expr, $arg1: expr, $arg2: expr, $op: tt) => {{
let lhs = integer_from_arg!($arg1, $instruction);
let rhs = integer_from_arg!($arg2, $instruction);
let to_store = Value::Int(lhs $op rhs);
Executor::store($env, $store_loc, to_store);
Ok(ExecuteAction::IncrementIP)
}}
}
macro_rules! float_op {
($env: expr, $instruction: expr, $store_loc: expr, $arg1: expr, $arg2: expr, $op: tt) => {{
let lhs = float_from_arg!($arg1, $instruction);
let rhs = float_from_arg!($arg2, $instruction);
let to_store = Value::Float(lhs $op rhs);
Executor::store($env, $store_loc, to_store);
Ok(ExecuteAction::IncrementIP)
}}
}
macro_rules! comp_int_op {
($env: expr, $instruction: expr, $store_loc: expr, $arg1: expr, $arg2: expr, $op: tt) => {{
let lhs = integer_from_arg!($arg1, $instruction);
let rhs = integer_from_arg!($arg2, $instruction);
let to_store = Value::Bool(lhs $op rhs);
Executor::store($env, $store_loc, to_store);
Ok(ExecuteAction::IncrementIP)
}}
}
macro_rules! comp_float_op {
($env: expr, $instruction: expr, $store_loc: expr, $arg1: expr, $arg2: expr, $op: tt) => {{
let lhs = float_from_arg!($arg1, $instruction);
let rhs = float_from_arg!($arg2, $instruction);
let to_store = Value::Bool(lhs $op rhs);
Executor::store($env, $store_loc, to_store);
Ok(ExecuteAction::IncrementIP)
}}
}
match instruction {
Instruction::Store(ref store_loc, ref arg) => {
let to_store = Executor::arg_to_value(env, arg);
Executor::store(env, store_loc, to_store);
Ok(ExecuteAction::IncrementIP)
},
Instruction::StoreStructure(ref store_loc, ref string_value_map) => {
let mut internal_struct = Struct::new();
string_value_map
.iter()
.for_each(|(key, arg)| {
let value = Executor::arg_to_value(env, arg);
internal_struct.set_field(key.clone(), value);
});
Executor::store(env, store_loc, Value::Struct(internal_struct));
Ok(ExecuteAction::IncrementIP)
}
Instruction::StoreArray1(ref store_loc, ref value) => {
let internal_array: Array = value
.iter()
.map(|arg| {
let raw_value = Executor::arg_to_value(env, arg);
ReferableValue::new(raw_value)
})
.collect();
let to_store = Value::Array(internal_array);
Executor::store(env, store_loc, to_store);
Ok(ExecuteAction::IncrementIP)
},
Instruction::StoreArray2(ref store_loc, ref value, size) => {
let cached_value = Executor::arg_to_value(env, value);
let internal_array: Array = (0..*size)
.map(|_index| {
ReferableValue::new(cached_value.clone())
})
.collect();
let to_store = Value::Array(internal_array);
Executor::store(env, store_loc, to_store);
Ok(ExecuteAction::IncrementIP)
}
Instruction::AddI(ref store_loc, ref arg1, ref arg2) =>
int_op!(env, instruction, store_loc, arg1, arg2, +),
Instruction::SubI(ref store_loc, ref arg1, ref arg2) =>
int_op!(env, instruction, store_loc, arg1, arg2, -),
Instruction::MulI(ref store_loc, ref arg1, ref arg2) =>
int_op!(env, instruction, store_loc, arg1, arg2, *),
Instruction::DivI(ref store_loc, ref arg1, ref arg2) =>
int_op!(env, instruction, store_loc, arg1, arg2, /),
Instruction::ModI(ref store_loc, ref arg1, ref arg2) =>
int_op!(env, instruction, store_loc, arg1, arg2, %),
Instruction::AddF(ref store_loc, ref arg1, ref arg2) =>
float_op!(env, instruction, store_loc, arg1, arg2, +),
Instruction::SubF(ref store_loc, ref arg1, ref arg2) =>
float_op!(env, instruction, store_loc, arg1, arg2, -),
Instruction::MulF(ref store_loc, ref arg1, ref arg2) =>
float_op!(env, instruction, store_loc, arg1, arg2, *),
Instruction::DivF(ref store_loc, ref arg1, ref arg2) =>
float_op!(env, instruction, store_loc, arg1, arg2, /),
Instruction::ModF(ref store_loc, ref arg1, ref arg2) =>
float_op!(env, instruction, store_loc, arg1, arg2, %),
Instruction::And(ref store_loc, ref arg1, ref arg2) => {
let lhs = bool_from_arg!(arg1, instruction);
let rhs = bool_from_arg!(arg2, instruction);
let to_store = Value::Bool(lhs && rhs);
Executor::store(env, store_loc, to_store);
Ok(ExecuteAction::IncrementIP)
}
Instruction::Or(ref store_loc, ref arg1, ref arg2) => {
let lhs = bool_from_arg!(arg1, instruction);
let rhs = bool_from_arg!(arg2, instruction);
let to_store = Value::Bool(lhs || rhs);
Executor::store(env, store_loc, to_store);
Ok(ExecuteAction::IncrementIP)
}
Instruction::GEqI(ref store_loc, ref arg1, ref arg2) =>
comp_int_op!(env, instruction, store_loc, arg1, arg2, >=),
Instruction::LEqI(ref store_loc, ref arg1, ref arg2) =>
comp_int_op!(env, instruction, store_loc, arg1, arg2, <=),
Instruction::GEI(ref store_loc, ref arg1, ref arg2) =>
comp_int_op!(env, instruction, store_loc, arg1, arg2, >),
Instruction::LEI(ref store_loc, ref arg1, ref arg2) =>
comp_int_op!(env, instruction, store_loc, arg1, arg2, <),
Instruction::GEqF(ref store_loc, ref arg1, ref arg2) =>
comp_float_op!(env, instruction, store_loc, arg1, arg2, >=),
Instruction::LEqF(ref store_loc, ref arg1, ref arg2) =>
comp_float_op!(env, instruction, store_loc, arg1, arg2, <=),
Instruction::GEF(ref store_loc, ref arg1, ref arg2) =>
comp_float_op!(env, instruction, store_loc, arg1, arg2, >),
Instruction::LEF(ref store_loc, ref arg1, ref arg2) =>
comp_float_op!(env, instruction, store_loc, arg1, arg2, <),
Instruction::Eq(ref store_loc, ref arg1, ref arg2) => {
let v1 = Executor::arg_to_value(env, arg1);
let v2 = Executor::arg_to_value(env, arg2);
let to_store = Value::Bool(v1 == v2);
Executor::store(env, store_loc, to_store);
Ok(ExecuteAction::IncrementIP)
}
Instruction::InEq(ref store_loc, ref arg1, ref arg2) => {
let v1 = Executor::arg_to_value(env, arg1);
let v2 = Executor::arg_to_value(env, arg2);
let to_store = Value::Bool(v1 != v2);
Executor::store(env, store_loc, to_store);
Ok(ExecuteAction::IncrementIP)
}
Instruction::Negate(ref store_loc, ref arg1) => {
let to_store = match Executor::arg_to_value(env, arg1) {
Value::Int(i) => Value::Int(-i),
Value::Float(f) => Value::Float(-f),
_ => return Err(InternalError::RuntimeInstructionError(
RuntimeInstructionError::ExpectedInt(instruction.clone())))
};
Executor::store(env, store_loc, to_store);
Ok(ExecuteAction::IncrementIP)
}
Instruction::Invert(ref store_loc, ref arg1) => {
let b = bool_from_arg!(arg1, instruction);
let to_store = Value::Bool(!b);
Executor::store(env, store_loc, to_store);
Ok(ExecuteAction::IncrementIP)
}
Instruction::FnCall(ref fn_loc, ref args) => {
let func = Executor::fetch(env, fn_loc);
let inner = func.inner_ref();
match *inner {
Value::Function(ref handle) => {
let args = if args.len() == 0 {
None
} else {
let args = args
.iter()
.map(|a| Executor::arg_to_value(env, a))
.collect();
Some(args)
};
Ok(ExecuteAction::PushStack(handle.clone(), args.unwrap_or(vec![])))
}
_ => Err(InternalError::RuntimeInstructionError(
RuntimeInstructionError::ExpectedFunction(instruction.clone()))),
}
},
Instruction::Return(ref return_value) => {
let return_value = return_value
.as_ref()
.map(|a| Executor::arg_to_value(env, a))
.unwrap_or(Value::Unit);
Ok(ExecuteAction::PopStack(return_value))
}
Instruction::TakeReturn(ref store_loc) => {
let to_store = return_register
.take()
.ok_or(InternalError::RuntimeInstructionError(
RuntimeInstructionError::NoReturnValue(ip)))?;
Executor::store(env, store_loc, to_store);
Ok(ExecuteAction::IncrementIP)
}
Instruction::Jump(ref jump_target) => {
Ok(ExecuteAction::SetIP(jump_target.absolute_target()))
}
Instruction::JumpCondition(ref jump_target, ref arg1) => {
let condition = bool_from_arg!(arg1, instruction);
let action = if condition {
ExecuteAction::SetIP(jump_target.absolute_target())
} else {
ExecuteAction::IncrementIP
};
Ok(action)
}
Instruction::JumpNegateCondition(ref jump_target, ref arg1) => {
let condition = bool_from_arg!(arg1, instruction);
let action = if !condition {
ExecuteAction::SetIP(jump_target.absolute_target())
} else {
ExecuteAction::IncrementIP
};
Ok(action)
}
Instruction::RelJump(ref rel_jump_target) => {
Ok(ExecuteAction::AddIP(rel_jump_target.relative_target()))
}
Instruction::RelJumpCondition(ref rel_jump_target, ref arg1) => {
let condition = bool_from_arg!(arg1, instruction);
let action = if condition {
ExecuteAction::AddIP(rel_jump_target.relative_target())
} else {
ExecuteAction::IncrementIP
};
Ok(action)
}
Instruction::RelJumpNegateCondition(ref rel_jump_target, ref arg1) => {
let condition = bool_from_arg!(arg1, instruction);
let action = if !condition {
ExecuteAction::AddIP(rel_jump_target.relative_target())
} else {
ExecuteAction::IncrementIP
};
Ok(action)
}
}
}
}
enum FetchResult {
Value(Value),
ValueRef(ReferableValue)
}
enum ExecuteAction {
IncrementIP,
SetIP(InstructionPointerType),
AddIP(i64),
PushStack(FnHandle, Vec<Value>),
PopStack(Value),
}
#[derive(Debug)]
enum StackInfo {
ByteCodeStack(ByteCodeStack),
BuiltinStack(BuiltinStack),
}
#[derive(Debug)]
struct BuiltinStack {
handle: FnHandle,
current_fn: Arc<BuiltinFn>,
compiled: CompiledProgram,
builtins: MappedBuiltins,
args: Vec<Value>,
}
impl BuiltinStack {
fn new(handle: FnHandle, compiled: CompiledProgram,
builtins: MappedBuiltins, args: Vec<Value>) -> BuiltinStack {
let current_fn = builtins.get(&handle.fn_id()).unwrap().clone();
BuiltinStack {
handle: handle,
current_fn: current_fn,
compiled: compiled,
builtins: builtins,
args: args,
}
}
}
#[derive(Debug)]
struct ByteCodeStack {
handle: FnHandle,
current_fn: Arc<byte_gen::ByteCodeFunction>,
compiled: CompiledProgram,
builtins: MappedBuiltins,
env: Env,
instruction_pointer: InstructionPointerType,
}
impl ByteCodeStack {
fn new(handle: FnHandle, compiled: CompiledProgram,
builtins: MappedBuiltins, module_env: &Env) -> ByteCodeStack {
let current_fn: Arc<byte_gen::ByteCodeFunction> = compiled.get(&handle.fn_id()).unwrap().clone();
let env = Env::new();
ByteCodeStack {
handle: handle,
current_fn: current_fn,
compiled: compiled,
builtins: builtins,
env: module_env.fork(),
instruction_pointer: 0,
}
}
}