use std::mem;
use std::slice;
use std::ffi::{CStr, CString};
use std::io;
use std::rc::Rc;
use libc::{c_int, c_char};
use ffi;
use {ErrorType, Type, Pointer, InterpretResult};
fn default_write(_: &mut VM, text: &str) {
print!("{}", text);
}
fn default_error(_: &mut VM, _type: ErrorType, module: &str, line: i32, message: &str) {
match _type {
ErrorType::Compile => println!("[{} line {}] {}", module, line, message),
ErrorType::Runtime => println!("{}", message),
ErrorType::StackTrace => println!("[{} line {}] in {}", module, line, message),
}
}
fn default_load_module(_: &mut VM, name: &str) -> Option<String> {
use std::path::PathBuf;
use std::fs::File;
use std::io::Read;
let mut buffer = String::new();
let mut name_path = PathBuf::new();
name_path.push(name);
name_path.set_extension("wren");
let result = File::open(&name_path).map(|mut f| f.read_to_string(&mut buffer));
if result.is_ok() {
return Some(buffer);
}
name_path.set_extension("");
name_path.push("module");
name_path.set_extension("wren");
buffer.clear();
let result = File::open(&name_path).map(|mut f| f.read_to_string(&mut buffer));
if result.is_ok() { Some(buffer) } else { None }
}
pub struct Configuration(ffi::WrenConfiguration);
impl Configuration {
pub fn new() -> Configuration {
let mut raw: ffi::WrenConfiguration = unsafe { mem::uninitialized() };
unsafe { ffi::wrenInitConfiguration(&mut raw) }
let mut cfg = Configuration(raw);
cfg.set_write_fn(wren_write_fn!(default_write));
cfg.set_error_fn(wren_error_fn!(default_error));
cfg.set_load_module_fn(wren_load_module_fn!(default_load_module));
cfg
}
pub fn set_reallocate_fn(&mut self, f: ::ReallocateFn) {
self.0.reallocate_fn = f;
}
pub fn set_load_module_fn(&mut self, f: ::LoadModuleFn) {
self.0.load_module_fn = f;
}
pub fn set_bind_foreign_method_fn(&mut self, f: ::BindForeignMethodFn) {
self.0.bind_foreign_method_fn = f;
}
pub fn set_bind_foreign_class_fn(&mut self, f: ::BindForeignClassFn) {
self.0.bind_foreign_class_fn = f;
}
pub fn set_write_fn(&mut self, f: ::WriteFn) {
self.0.write_fn = f;
}
pub fn set_error_fn(&mut self, f: ::ErrorFn) {
self.0.error_fn = f;
}
pub fn set_initial_heap_size(&mut self, size: usize) {
self.0.initial_heap_size = size;
}
pub fn set_min_heap_size(&mut self, size: usize) {
self.0.min_heap_size = size;
}
pub fn set_heap_growth_percent(&mut self, percent: i32) {
self.0.heap_growth_percent = percent;
}
pub fn set_user_data(&mut self, data: Pointer) {
self.0.user_data = data;
}
}
#[derive(Clone)]
pub struct Handle(Rc<RawHandle>);
struct RawHandle {
raw: *mut ffi::WrenHandle,
vm: *mut ffi::WrenVM,
}
impl Drop for RawHandle {
fn drop(&mut self) {
unsafe { ffi::wrenReleaseHandle(self.vm, self.raw) }
}
}
#[derive(Copy, Clone)]
pub struct ForeignClassMethods(ffi::WrenForeignClassMethods);
impl ForeignClassMethods {
pub fn new() -> ForeignClassMethods {
ForeignClassMethods(ffi::WrenForeignClassMethods {
allocate: None,
finalize: None,
})
}
pub fn set_allocate_fn(&mut self, f: ::ForeignMethodFn) {
self.0.allocate = f;
}
pub fn set_finalize_fn(&mut self, f: ::FinalizerFn) {
self.0.finalize = f;
}
#[doc(hidden)]
pub fn get(&self) -> ffi::WrenForeignClassMethods {
self.0
}
}
pub struct VM {
raw: *mut ffi::WrenVM,
owned: bool,
}
impl VM {
pub fn new(cfg: Configuration) -> VM {
let mut cfg = cfg;
let raw = unsafe { ffi::wrenNewVM(&mut cfg.0) };
VM { raw, owned: true }
}
pub unsafe fn from_ptr(ptr: *mut ffi::WrenVM) -> VM {
VM {
raw: ptr,
owned: false,
}
}
pub fn collect_garbage(&mut self) {
unsafe { ffi::wrenCollectGarbage(self.raw) }
}
pub fn interpret(&mut self, source: &str) -> InterpretResult {
let source_cstr = CString::new(source).unwrap();
unsafe { ffi::wrenInterpret(self.raw, source_cstr.as_ptr()) }
}
pub fn interpret_file(&mut self, path: &str) -> Result<InterpretResult, io::Error> {
use std::fs::File;
use std::io::Read;
let mut buffer = String::new();
let mut file = File::open(path)?;
file.read_to_string(&mut buffer)?;
Ok(self.interpret(&buffer))
}
pub fn make_call_handle(&mut self, signature: &str) -> Handle {
let signature_cstr = CString::new(signature).unwrap();
let handle = RawHandle {
raw: unsafe { ffi::wrenMakeCallHandle(self.raw, signature_cstr.as_ptr()) },
vm: self.raw,
};
Handle(Rc::new(handle))
}
pub fn call(&mut self, method: &Handle) -> InterpretResult {
unsafe { ffi::wrenCall(self.raw, method.0.raw) }
}
pub fn get_slot_count(&mut self) -> i32 {
unsafe { ffi::wrenGetSlotCount(self.raw) }
}
fn ensure_slots(&mut self, num_slots: i32) {
unsafe { ffi::wrenEnsureSlots(self.raw, num_slots) }
}
pub fn get_slot_type(&mut self, slot: i32) -> Type {
assert!(self.get_slot_count() > slot,
"Slot {} is out of bounds",
slot);
unsafe { ffi::wrenGetSlotType(self.raw, slot) }
}
pub fn get_slot_bool(&mut self, slot: i32) -> Option<bool> {
if self.get_slot_type(slot) == Type::Bool {
Some(unsafe { ffi::wrenGetSlotBool(self.raw, slot) != 0 })
} else {
None
}
}
pub fn get_slot_bytes(&mut self, slot: i32) -> Option<&[u8]> {
if self.get_slot_type(slot) == Type::String {
let mut length = unsafe { mem::uninitialized() };
let ptr = unsafe { ffi::wrenGetSlotBytes(self.raw, slot, &mut length) };
Some(unsafe { slice::from_raw_parts(ptr as *const u8, length as usize) })
} else {
None
}
}
pub fn get_slot_double(&mut self, slot: i32) -> Option<f64> {
if self.get_slot_type(slot) == Type::Num {
Some(unsafe { ffi::wrenGetSlotDouble(self.raw, slot) })
} else {
None
}
}
pub fn get_slot_foreign(&mut self, slot: i32) -> Option<Pointer> {
if self.get_slot_type(slot) == Type::Foreign {
Some(unsafe { ffi::wrenGetSlotForeign(self.raw, slot) })
} else {
None
}
}
pub unsafe fn get_slot_foreign_typed<T>(&mut self, slot: i32) -> &mut T {
assert!(self.get_slot_type(slot) == Type::Foreign,
"Slot {} must contain a foreign object",
slot);
mem::transmute::<Pointer, &mut T>(ffi::wrenGetSlotForeign(self.raw, slot))
}
pub fn get_slot_string(&mut self, slot: i32) -> Option<&str> {
if self.get_slot_type(slot) == Type::String {
let ptr = unsafe { ffi::wrenGetSlotString(self.raw, slot) };
Some(unsafe { CStr::from_ptr(ptr).to_str().unwrap() })
} else {
None
}
}
pub fn get_slot_handle(&mut self, slot: i32) -> Handle {
assert!(self.get_slot_count() > slot,
"Slot {} is out of bounds",
slot);
let handle = RawHandle {
raw: unsafe { ffi::wrenGetSlotHandle(self.raw, slot) },
vm: self.raw,
};
Handle(Rc::new(handle))
}
pub fn set_slot_bool(&mut self, slot: i32, value: bool) {
self.ensure_slots(slot + 1);
unsafe { ffi::wrenSetSlotBool(self.raw, slot, value as c_int) }
}
pub fn set_slot_bytes(&mut self, slot: i32, bytes: &[u8]) {
self.ensure_slots(slot + 1);
let ptr = bytes.as_ptr() as *const c_char;
let len = bytes.len();
unsafe { ffi::wrenSetSlotBytes(self.raw, slot, ptr, len) }
}
pub fn set_slot_double(&mut self, slot: i32, value: f64) {
self.ensure_slots(slot + 1);
unsafe { ffi::wrenSetSlotDouble(self.raw, slot, value) }
}
pub fn set_slot_new_foreign(&mut self, slot: i32, class_slot: i32, size: usize) -> Pointer {
self.ensure_slots(slot + 1);
unsafe { ffi::wrenSetSlotNewForeign(self.raw, slot, class_slot, size) }
}
pub fn set_slot_new_foreign_typed<T>(&mut self, slot: i32, class_slot: i32) -> *mut T {
self.set_slot_new_foreign(slot, class_slot, mem::size_of::<T>()) as *mut T
}
pub fn set_slot_new_list(&mut self, slot: i32) {
self.ensure_slots(slot + 1);
unsafe { ffi::wrenSetSlotNewList(self.raw, slot) }
}
pub fn set_slot_null(&mut self, slot: i32) {
self.ensure_slots(slot + 1);
unsafe { ffi::wrenSetSlotNull(self.raw, slot) }
}
pub fn set_slot_string(&mut self, slot: i32, s: &str) {
self.ensure_slots(slot + 1);
let cstr = CString::new(s).unwrap();
unsafe { ffi::wrenSetSlotString(self.raw, slot, cstr.as_ptr()) }
}
pub fn set_slot_handle(&mut self, slot: i32, handle: &Handle) {
self.ensure_slots(slot + 1);
unsafe { ffi::wrenSetSlotHandle(self.raw, slot, handle.0.raw) }
}
pub fn get_list_count(&mut self, slot: i32) -> i32 {
if self.get_slot_type(slot) == Type::List {
unsafe { ffi::wrenGetListCount(self.raw, slot) }
} else {
0
}
}
fn check_index(&mut self, list_slot: i32, index: i32) -> i32 {
assert!(self.get_slot_type(list_slot) == Type::List,
"Slot {} must contain a list",
list_slot);
let list_count = self.get_list_count(list_slot);
let index = if index < 0 {
list_count + 1 + index
} else {
index
};
assert!(index <= list_count, "List index out of bounds");
index
}
pub fn get_list_element(&mut self, list_slot: i32, index: i32, element_slot: i32) {
self.ensure_slots(element_slot + 1);
let index = self.check_index(list_slot, index);
unsafe { ffi::wrenGetListElement(self.raw, list_slot, index, element_slot) };
}
pub fn insert_in_list(&mut self, list_slot: i32, index: i32, element_slot: i32) {
assert!(element_slot < self.get_slot_count(),
"No element in slot {}",
element_slot);
let index = self.check_index(list_slot, index);
unsafe { ffi::wrenInsertInList(self.raw, list_slot, index, element_slot) };
}
pub fn get_variable(&mut self, module: &str, name: &str, slot: i32) {
self.ensure_slots(slot + 1);
let module_cstr = CString::new(module).unwrap();
let name_cstr = CString::new(name).unwrap();
unsafe { ffi::wrenGetVariable(self.raw, module_cstr.as_ptr(), name_cstr.as_ptr(), slot) }
}
pub fn abort_fiber(&mut self, slot: i32) {
unsafe { ffi::wrenAbortFiber(self.raw, slot) }
}
pub fn get_user_data(&mut self) -> Pointer {
unsafe { ffi::wrenGetUserData(self.raw) }
}
pub fn set_user_data(&mut self, data: Pointer) {
unsafe { ffi::wrenSetUserData(self.raw, data) }
}
}
impl Drop for VM {
fn drop(&mut self) {
if self.owned {
unsafe { ffi::wrenFreeVM(self.raw) }
}
}
}