diff --git a/wasmtime-api/src/externals.rs b/wasmtime-api/src/externals.rs index 09cf3ec752..1491f7d356 100644 --- a/wasmtime-api/src/externals.rs +++ b/wasmtime-api/src/externals.rs @@ -1,13 +1,13 @@ use crate::callable::{Callable, WasmtimeFn}; use crate::runtime::Store; use crate::trap::Trap; -use crate::types::{ExternType, FuncType, GlobalType, MemoryType}; +use crate::types::{ExternType, FuncType, GlobalType, MemoryType, TableType, ValType}; use crate::values::Val; use std::cell::RefCell; use std::rc::Rc; use std::result::Result; -use crate::trampoline::generate_func_export; +use crate::trampoline::{generate_func_export, generate_global_export, generate_memory_export}; use wasmtime_runtime::InstanceHandle; // Externals @@ -48,7 +48,8 @@ impl Extern { match self { Extern::Func(ft) => ExternType::ExternFunc(ft.borrow().r#type().clone()), Extern::Memory(ft) => ExternType::ExternMemory(ft.borrow().r#type().clone()), - _ => unimplemented!("ExternType::type"), + Extern::Table(tt) => ExternType::ExternTable(tt.borrow().r#type().clone()), + Extern::Global(gt) => ExternType::ExternGlobal(gt.borrow().r#type().clone()), } } @@ -60,6 +61,8 @@ impl Extern { } f.borrow().anchor.as_ref().unwrap().1.clone() } + Extern::Global(g) => g.borrow().wasmtime_export().clone(), + Extern::Memory(m) => m.borrow().wasmtime_export().clone(), _ => unimplemented!("get_wasmtime_export"), } } @@ -69,7 +72,6 @@ impl Extern { instance_handle: InstanceHandle, export: wasmtime_runtime::Export, ) -> Extern { - use cranelift_wasm::GlobalInit; match export { wasmtime_runtime::Export::Function { address, @@ -82,33 +84,19 @@ impl Extern { f.anchor = Some((instance_handle, export.clone())); Extern::Func(Rc::new(RefCell::new(f))) } - wasmtime_runtime::Export::Memory { + wasmtime_runtime::Export::Memory { .. } => Extern::Memory(Rc::new(RefCell::new( + Memory::from_wasmtime_memory(export, store, instance_handle), + ))), + wasmtime_runtime::Export::Global { .. } => Extern::Global(Rc::new(RefCell::new( + Global::from_wasmtime_global(export, store), + ))), + wasmtime_runtime::Export::Table { definition: _, vmctx: _, - memory, + table, } => { - let ty = MemoryType::from_cranelift_memory(memory.memory.clone()); - let m = Memory::new(store, ty); - Extern::Memory(Rc::new(RefCell::new(m))) - } - wasmtime_runtime::Export::Global { - definition: _, - vmctx: _, - global, - } => { - let ty = GlobalType::from_cranelift_global(global.clone()); - let val = match global.initializer { - GlobalInit::I32Const(i) => Val::from(i), - GlobalInit::I64Const(i) => Val::from(i), - GlobalInit::F32Const(f) => Val::from_f32_bits(f), - GlobalInit::F64Const(f) => Val::from_f64_bits(f), - _ => unimplemented!("from_wasmtime_export initializer"), - }; - Extern::Global(Rc::new(RefCell::new(Global::new(store, ty, val)))) - } - wasmtime_runtime::Export::Table { .. } => { - // TODO Extern::Table(Rc::new(RefCell::new(Table::new(store, ty, val)))) - Extern::Table(Rc::new(RefCell::new(Table))) + let ty = TableType::from_cranelift_table(table.table.clone()); + Extern::Table(Rc::new(RefCell::new(Table::new(store, ty)))) } } } @@ -161,15 +149,20 @@ impl Func { pub struct Global { _store: Rc>, r#type: GlobalType, - val: Val, + wasmtime_export: wasmtime_runtime::Export, + #[allow(dead_code)] + wasmtime_state: Option, } impl Global { pub fn new(store: Rc>, r#type: GlobalType, val: Val) -> Global { + let (wasmtime_export, wasmtime_state) = + generate_global_export(&r#type, val).expect("generated global"); Global { _store: store, r#type, - val, + wasmtime_export, + wasmtime_state: Some(wasmtime_state), } } @@ -177,27 +170,119 @@ impl Global { &self.r#type } - pub fn get(&self) -> &Val { - &self.val + fn wasmtime_global_definition(&self) -> *mut wasmtime_runtime::VMGlobalDefinition { + match self.wasmtime_export { + wasmtime_runtime::Export::Global { definition, .. } => definition, + _ => panic!("global definition not found"), + } + } + + pub fn get(&self) -> Val { + let definition = unsafe { &mut *self.wasmtime_global_definition() }; + unsafe { + match self.r#type().content() { + ValType::I32 => Val::from(*definition.as_i32()), + ValType::I64 => Val::from(*definition.as_i64()), + ValType::F32 => Val::from_f32_bits(*definition.as_u32()), + ValType::F64 => Val::from_f64_bits(*definition.as_u64()), + _ => unimplemented!("Global::get for {:?}", self.r#type().content()), + } + } } pub fn set(&mut self, val: Val) { - self.val = val; + if val.r#type() != *self.r#type().content() { + panic!( + "global of type {:?} cannot be set to {:?}", + self.r#type().content(), + val.r#type() + ); + } + let definition = unsafe { &mut *self.wasmtime_global_definition() }; + unsafe { + match val { + Val::I32(i) => *definition.as_i32_mut() = i, + Val::I64(i) => *definition.as_i64_mut() = i, + Val::F32(f) => *definition.as_u32_mut() = f, + Val::F64(f) => *definition.as_u64_mut() = f, + _ => unimplemented!("Global::set for {:?}", val.r#type()), + } + } + } + + pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::Export { + &self.wasmtime_export + } + + pub(crate) fn from_wasmtime_global( + export: wasmtime_runtime::Export, + store: Rc>, + ) -> Global { + let global = if let wasmtime_runtime::Export::Global { ref global, .. } = export { + global + } else { + panic!("wasmtime export is not memory") + }; + let ty = GlobalType::from_cranelift_global(global.clone()); + Global { + _store: store, + r#type: ty, + wasmtime_export: export, + wasmtime_state: None, + } } } -pub struct Table; +pub struct Table { + _store: Rc>, + r#type: TableType, +} + +impl Table { + pub fn new(store: Rc>, r#type: TableType) -> Table { + Table { + _store: store, + r#type, + } + } + + pub fn r#type(&self) -> &TableType { + &self.r#type + } + + pub fn get(&self, _index: u32) -> Val { + unimplemented!("Table::get") + } + + pub fn set(&self, _index: u32, _val: &Val) -> usize { + unimplemented!("Table::set") + } + + pub fn size(&self) -> u32 { + unimplemented!("Table::size") + } + + pub fn grow(&mut self, _delta: u32) -> bool { + unimplemented!("Table::grow") + } +} pub struct Memory { _store: Rc>, r#type: MemoryType, + wasmtime_handle: InstanceHandle, + wasmtime_export: wasmtime_runtime::Export, } impl Memory { pub fn new(store: Rc>, r#type: MemoryType) -> Memory { + let (wasmtime_handle, wasmtime_export) = + generate_memory_export(&r#type).expect("generated memory"); Memory { _store: store, r#type, + wasmtime_handle, + wasmtime_export, } } @@ -205,19 +290,56 @@ impl Memory { &self.r#type } - pub fn data(&self) -> *const u8 { - unimplemented!("Memory::data") + fn wasmtime_memory_definition(&self) -> *mut wasmtime_runtime::VMMemoryDefinition { + match self.wasmtime_export { + wasmtime_runtime::Export::Memory { definition, .. } => definition, + _ => panic!("memory definition not found"), + } + } + + pub fn data(&self) -> *mut u8 { + unsafe { (*self.wasmtime_memory_definition()).base } } pub fn data_size(&self) -> usize { - unimplemented!("Memory::data_size") + unsafe { (*self.wasmtime_memory_definition()).current_length } } pub fn size(&self) -> u32 { - unimplemented!("Memory::size") + (self.data_size() / wasmtime_environ::WASM_PAGE_SIZE as usize) as u32 } - pub fn grow(&mut self, _delta: u32) -> bool { - unimplemented!("Memory::grow") + pub fn grow(&mut self, delta: u32) -> bool { + match self.wasmtime_export { + wasmtime_runtime::Export::Memory { definition, .. } => { + let definition = unsafe { &(*definition) }; + let index = self.wasmtime_handle.memory_index(definition); + self.wasmtime_handle.memory_grow(index, delta).is_some() + } + _ => panic!("memory definition not found"), + } + } + + pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::Export { + &self.wasmtime_export + } + + pub(crate) fn from_wasmtime_memory( + export: wasmtime_runtime::Export, + store: Rc>, + instance_handle: wasmtime_runtime::InstanceHandle, + ) -> Memory { + let memory = if let wasmtime_runtime::Export::Memory { ref memory, .. } = export { + memory + } else { + panic!("wasmtime export is not memory") + }; + let ty = MemoryType::from_cranelift_memory(memory.memory.clone()); + Memory { + _store: store, + r#type: ty, + wasmtime_handle: instance_handle, + wasmtime_export: export, + } } } diff --git a/wasmtime-api/src/module.rs b/wasmtime-api/src/module.rs index f8a64e3a18..e2ed946a18 100644 --- a/wasmtime-api/src/module.rs +++ b/wasmtime-api/src/module.rs @@ -1,7 +1,7 @@ use crate::runtime::Store; use crate::types::{ ExportType, ExternType, FuncType, GlobalType, ImportType, Limits, MemoryType, Mutability, - ValType, + TableType, ValType, }; use failure::Error; use std::cell::RefCell; @@ -33,6 +33,8 @@ fn into_valtype(ty: &wasmparser::Type) -> ValType { I64 => ValType::I64, F32 => ValType::F32, F64 => ValType::F64, + AnyFunc => ValType::FuncRef, + AnyRef => ValType::AnyRef, _ => unimplemented!("types in into_valtype"), } } @@ -44,6 +46,16 @@ fn into_func_type(mt: wasmparser::FuncType) -> FuncType { FuncType::new(params.into_boxed_slice(), returns.into_boxed_slice()) } +fn into_table_type(tt: wasmparser::TableType) -> TableType { + assert!(tt.element_type == wasmparser::Type::AnyFunc); + let ty = into_valtype(&tt.element_type); + let limits = Limits::new( + tt.limits.initial, + tt.limits.maximum.unwrap_or(::std::u32::MAX), + ); + TableType::new(ty, limits) +} + fn read_imports_and_exports( binary: &[u8], ) -> Result<(Box<[ImportType]>, Box<[ExportType]>), Error> { @@ -51,6 +63,7 @@ fn read_imports_and_exports( let mut imports = Vec::new(); let mut exports = Vec::new(); let mut memories = Vec::new(); + let mut tables = Vec::new(); let mut func_sig = Vec::new(); let mut sigs = Vec::new(); let mut globals = Vec::new(); @@ -85,6 +98,13 @@ fn read_imports_and_exports( globals.push(into_global_type(&entry?.ty)); } } + SectionCode::Table => { + let section = section.get_table_section_reader()?; + tables.reserve_exact(section.get_count() as usize); + for entry in section { + tables.push(into_table_type(entry?)) + } + } SectionCode::Import => { let section = section.get_import_section_reader()?; imports.reserve_exact(section.get_count() as usize); @@ -127,7 +147,9 @@ fn read_imports_and_exports( let sig = &sigs[sig_index]; ExternType::ExternFunc(sig.clone()) } - ExternalKind::Table => unimplemented!("ExternalKind::Table"), + ExternalKind::Table => { + ExternType::ExternTable(tables[entry.index as usize].clone()) + } ExternalKind::Memory => { ExternType::ExternMemory(memories[entry.index as usize].clone()) } diff --git a/wasmtime-api/src/trampoline/create_handle.rs b/wasmtime-api/src/trampoline/create_handle.rs index d6462aa813..d1f4f2b888 100644 --- a/wasmtime-api/src/trampoline/create_handle.rs +++ b/wasmtime-api/src/trampoline/create_handle.rs @@ -1,248 +1,35 @@ //! Support for a calling of an imported function. -use crate::trampoline::code_memory::CodeMemory; -use cranelift_codegen::ir::types; -use cranelift_codegen::ir::{InstBuilder, StackSlotData, StackSlotKind, TrapCode}; -use cranelift_codegen::Context; -use cranelift_codegen::{binemit, ir, isa}; -use cranelift_entity::{EntityRef, PrimaryMap}; -use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; -use cranelift_wasm::{DefinedFuncIndex, FuncIndex}; +use cranelift_entity::PrimaryMap; +use cranelift_wasm::DefinedFuncIndex; //use target_lexicon::HOST; use failure::Error; -use wasmtime_environ::{Export, Module}; -use wasmtime_runtime::{Imports, InstanceHandle, VMContext, VMFunctionBody}; +use wasmtime_environ::Module; +use wasmtime_runtime::{Imports, InstanceHandle, VMFunctionBody}; -use core::cmp; +use std::any::Any; use std::cell::RefCell; use std::collections::{HashMap, HashSet}; use std::rc::Rc; -use crate::{Func, Trap, Val}; - -struct TrampolineState { - func: Rc>, - trap: Option>>, - #[allow(dead_code)] - code_memory: CodeMemory, -} - -unsafe extern "C" fn stub_fn(vmctx: *mut VMContext, call_id: u32, values_vec: *mut i64) -> u32 { - let mut instance = InstanceHandle::from_vmctx(vmctx); - - let (args, returns_len) = { - let module = instance.module_ref(); - let signature = &module.signatures[module.functions[FuncIndex::new(call_id as usize)]]; - - let mut args = Vec::new(); - for i in 1..signature.params.len() { - args.push(Val::read_value_from( - values_vec.offset(i as isize - 1), - signature.params[i].value_type, - )) - } - (args, signature.returns.len()) - }; - - let func = instance - .host_state() - .downcast_mut::() - .expect("state") - .func - .borrow(); - - match func.call(&args) { - Ok(returns) => { - for i in 0..returns_len { - // TODO check signature.returns[i].value_type ? - returns[i].write_value_to(values_vec.offset(i as isize)); - } - 0 - } - Err(trap) => { - // TODO read custom exception - InstanceHandle::from_vmctx(vmctx) - .host_state() - .downcast_mut::() - .expect("state") - .trap = Some(trap); - 1 - } - } -} - -/// Create a trampoline for invoking a Callable. -fn make_trampoline( - isa: &dyn isa::TargetIsa, - code_memory: &mut CodeMemory, - fn_builder_ctx: &mut FunctionBuilderContext, - call_id: u32, - signature: &ir::Signature, -) -> *const VMFunctionBody { - // Mostly reverse copy of the similar method from wasmtime's - // wasmtime-jit/src/compiler.rs. - let pointer_type = isa.pointer_type(); - let mut stub_sig = ir::Signature::new(isa.frontend_config().default_call_conv); - - // Add the `vmctx` parameter. - stub_sig.params.push(ir::AbiParam::special( - pointer_type, - ir::ArgumentPurpose::VMContext, - )); - - // Add the `call_id` parameter. - stub_sig.params.push(ir::AbiParam::new(types::I32)); - - // Add the `values_vec` parameter. - stub_sig.params.push(ir::AbiParam::new(pointer_type)); - - // Add error/trap return. - stub_sig.returns.push(ir::AbiParam::new(types::I32)); - - let values_vec_len = 8 * cmp::max(signature.params.len() - 1, signature.returns.len()) as u32; - - let mut context = Context::new(); - context.func = - ir::Function::with_name_signature(ir::ExternalName::user(0, 0), signature.clone()); - - let ss = context.func.create_stack_slot(StackSlotData::new( - StackSlotKind::ExplicitSlot, - values_vec_len, - )); - let value_size = 8; - - { - let mut builder = FunctionBuilder::new(&mut context.func, fn_builder_ctx); - let block0 = builder.create_ebb(); - - builder.append_ebb_params_for_function_params(block0); - builder.switch_to_block(block0); - builder.seal_block(block0); - - let values_vec_ptr_val = builder.ins().stack_addr(pointer_type, ss, 0); - let mflags = ir::MemFlags::trusted(); - for i in 1..signature.params.len() { - if i == 0 { - continue; - } - - let val = builder.func.dfg.ebb_params(block0)[i]; - builder.ins().store( - mflags, - val, - values_vec_ptr_val, - ((i - 1) * value_size) as i32, - ); - } - - let vmctx_ptr_val = builder.func.dfg.ebb_params(block0)[0]; - let call_id_val = builder.ins().iconst(types::I32, call_id as i64); - - let callee_args = vec![vmctx_ptr_val, call_id_val, values_vec_ptr_val]; - - let new_sig = builder.import_signature(stub_sig.clone()); - - let callee_value = builder - .ins() - .iconst(pointer_type, stub_fn as *const VMFunctionBody as i64); - let call = builder - .ins() - .call_indirect(new_sig, callee_value, &callee_args); - - let call_result = builder.func.dfg.inst_results(call)[0]; - builder.ins().trapnz(call_result, TrapCode::User(0)); - - let mflags = ir::MemFlags::trusted(); - let mut results = Vec::new(); - for (i, r) in signature.returns.iter().enumerate() { - let load = builder.ins().load( - r.value_type, - mflags, - values_vec_ptr_val, - (i * value_size) as i32, - ); - results.push(load); - } - builder.ins().return_(&results); - builder.finalize() - } - - let mut code_buf: Vec = Vec::new(); - let mut reloc_sink = RelocSink {}; - let mut trap_sink = binemit::NullTrapSink {}; - let mut stackmap_sink = binemit::NullStackmapSink {}; - context - .compile_and_emit( - isa, - &mut code_buf, - &mut reloc_sink, - &mut trap_sink, - &mut stackmap_sink, - ) - .expect("compile_and_emit"); - - code_memory - .allocate_copy_of_byte_slice(&code_buf) - .expect("allocate_copy_of_byte_slice") - .as_ptr() -} - -pub fn create_handle(func: &Rc>) -> Result { - let sig = func.borrow().r#type().get_cranelift_signature().clone(); - - let isa = { - let isa_builder = - cranelift_native::builder().expect("host machine is not a supported target"); - let flag_builder = cranelift_codegen::settings::builder(); - isa_builder.finish(cranelift_codegen::settings::Flags::new(flag_builder)) - }; - +pub fn create_handle( + module: Module, + finished_functions: PrimaryMap, + state: Box, +) -> Result { let global_exports: Rc>>> = Rc::new(RefCell::new(HashMap::new())); - let mut fn_builder_ctx = FunctionBuilderContext::new(); - let mut module = Module::new(); - let mut finished_functions: PrimaryMap = - PrimaryMap::new(); - let mut code_memory = CodeMemory::new(); - - //let pointer_type = types::Type::triple_pointer_type(&HOST); - //let call_conv = isa::CallConv::triple_default(&HOST); - - let dependencies = HashSet::new(); - let memories = PrimaryMap::new(); - - let sig_id = module.signatures.push(sig.clone()); - let func_id = module.functions.push(sig_id); - module - .exports - .insert("trampoline".to_string(), Export::Function(func_id)); - let trampoline = make_trampoline( - isa.as_ref(), - &mut code_memory, - &mut fn_builder_ctx, - func_id.index() as u32, - &sig, - ); - code_memory.publish(); - - finished_functions.push(trampoline); let imports = Imports::new( - dependencies, + HashSet::new(), + PrimaryMap::new(), PrimaryMap::new(), PrimaryMap::new(), - memories, PrimaryMap::new(), ); let data_initializers = Vec::new(); let signatures = PrimaryMap::new(); - let trampoline_state = TrampolineState { - func: func.clone(), - trap: None, - code_memory, - }; - Ok(InstanceHandle::new( Rc::new(module), global_exports, @@ -251,39 +38,7 @@ pub fn create_handle(func: &Rc>) -> Result &data_initializers, signatures.into_boxed_slice(), None, - Box::new(trampoline_state), + state, ) .expect("instance")) } - -/// We don't expect trampoline compilation to produce any relocations, so -/// this `RelocSink` just asserts that it doesn't recieve any. -struct RelocSink {} - -impl binemit::RelocSink for RelocSink { - fn reloc_ebb( - &mut self, - _offset: binemit::CodeOffset, - _reloc: binemit::Reloc, - _ebb_offset: binemit::CodeOffset, - ) { - panic!("trampoline compilation should not produce ebb relocs"); - } - fn reloc_external( - &mut self, - _offset: binemit::CodeOffset, - _reloc: binemit::Reloc, - _name: &ir::ExternalName, - _addend: binemit::Addend, - ) { - panic!("trampoline compilation should not produce external symbol relocs"); - } - fn reloc_jt( - &mut self, - _offset: binemit::CodeOffset, - _reloc: binemit::Reloc, - _jt: ir::JumpTable, - ) { - panic!("trampoline compilation should not produce jump table relocs"); - } -} diff --git a/wasmtime-api/src/trampoline/func.rs b/wasmtime-api/src/trampoline/func.rs new file mode 100644 index 0000000000..3982a8642b --- /dev/null +++ b/wasmtime-api/src/trampoline/func.rs @@ -0,0 +1,265 @@ +//! Support for a calling of an imported function. + +use crate::trampoline::code_memory::CodeMemory; +use cranelift_codegen::ir::types; +use cranelift_codegen::ir::{InstBuilder, StackSlotData, StackSlotKind, TrapCode}; +use cranelift_codegen::Context; +use cranelift_codegen::{binemit, ir, isa}; +use cranelift_entity::{EntityRef, PrimaryMap}; +use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; +use cranelift_wasm::{DefinedFuncIndex, FuncIndex}; +//use target_lexicon::HOST; +use failure::Error; +use wasmtime_environ::{Export, Module}; +use wasmtime_runtime::{InstanceHandle, VMContext, VMFunctionBody}; + +use core::cmp; +use std::cell::RefCell; +use std::rc::Rc; + +use crate::{Func, Trap, Val}; + +use super::create_handle::create_handle; + +struct TrampolineState { + func: Rc>, + trap: Option>>, + #[allow(dead_code)] + code_memory: CodeMemory, +} + +unsafe extern "C" fn stub_fn(vmctx: *mut VMContext, call_id: u32, values_vec: *mut i64) -> u32 { + let mut instance = InstanceHandle::from_vmctx(vmctx); + + let (args, returns_len) = { + let module = instance.module_ref(); + let signature = &module.signatures[module.functions[FuncIndex::new(call_id as usize)]]; + + let mut args = Vec::new(); + for i in 1..signature.params.len() { + args.push(Val::read_value_from( + values_vec.offset(i as isize - 1), + signature.params[i].value_type, + )) + } + (args, signature.returns.len()) + }; + + let func = instance + .host_state() + .downcast_mut::() + .expect("state") + .func + .borrow(); + + match func.call(&args) { + Ok(returns) => { + for i in 0..returns_len { + // TODO check signature.returns[i].value_type ? + returns[i].write_value_to(values_vec.offset(i as isize)); + } + 0 + } + Err(trap) => { + // TODO read custom exception + InstanceHandle::from_vmctx(vmctx) + .host_state() + .downcast_mut::() + .expect("state") + .trap = Some(trap); + 1 + } + } +} + +/// Create a trampoline for invoking a Callable. +fn make_trampoline( + isa: &dyn isa::TargetIsa, + code_memory: &mut CodeMemory, + fn_builder_ctx: &mut FunctionBuilderContext, + call_id: u32, + signature: &ir::Signature, +) -> *const VMFunctionBody { + // Mostly reverse copy of the similar method from wasmtime's + // wasmtime-jit/src/compiler.rs. + let pointer_type = isa.pointer_type(); + let mut stub_sig = ir::Signature::new(isa.frontend_config().default_call_conv); + + // Add the `vmctx` parameter. + stub_sig.params.push(ir::AbiParam::special( + pointer_type, + ir::ArgumentPurpose::VMContext, + )); + + // Add the `call_id` parameter. + stub_sig.params.push(ir::AbiParam::new(types::I32)); + + // Add the `values_vec` parameter. + stub_sig.params.push(ir::AbiParam::new(pointer_type)); + + // Add error/trap return. + stub_sig.returns.push(ir::AbiParam::new(types::I32)); + + let values_vec_len = 8 * cmp::max(signature.params.len() - 1, signature.returns.len()) as u32; + + let mut context = Context::new(); + context.func = + ir::Function::with_name_signature(ir::ExternalName::user(0, 0), signature.clone()); + + let ss = context.func.create_stack_slot(StackSlotData::new( + StackSlotKind::ExplicitSlot, + values_vec_len, + )); + let value_size = 8; + + { + let mut builder = FunctionBuilder::new(&mut context.func, fn_builder_ctx); + let block0 = builder.create_ebb(); + + builder.append_ebb_params_for_function_params(block0); + builder.switch_to_block(block0); + builder.seal_block(block0); + + let values_vec_ptr_val = builder.ins().stack_addr(pointer_type, ss, 0); + let mflags = ir::MemFlags::trusted(); + for i in 1..signature.params.len() { + if i == 0 { + continue; + } + + let val = builder.func.dfg.ebb_params(block0)[i]; + builder.ins().store( + mflags, + val, + values_vec_ptr_val, + ((i - 1) * value_size) as i32, + ); + } + + let vmctx_ptr_val = builder.func.dfg.ebb_params(block0)[0]; + let call_id_val = builder.ins().iconst(types::I32, call_id as i64); + + let callee_args = vec![vmctx_ptr_val, call_id_val, values_vec_ptr_val]; + + let new_sig = builder.import_signature(stub_sig.clone()); + + let callee_value = builder + .ins() + .iconst(pointer_type, stub_fn as *const VMFunctionBody as i64); + let call = builder + .ins() + .call_indirect(new_sig, callee_value, &callee_args); + + let call_result = builder.func.dfg.inst_results(call)[0]; + builder.ins().trapnz(call_result, TrapCode::User(0)); + + let mflags = ir::MemFlags::trusted(); + let mut results = Vec::new(); + for (i, r) in signature.returns.iter().enumerate() { + let load = builder.ins().load( + r.value_type, + mflags, + values_vec_ptr_val, + (i * value_size) as i32, + ); + results.push(load); + } + builder.ins().return_(&results); + builder.finalize() + } + + let mut code_buf: Vec = Vec::new(); + let mut reloc_sink = RelocSink {}; + let mut trap_sink = binemit::NullTrapSink {}; + let mut stackmap_sink = binemit::NullStackmapSink {}; + context + .compile_and_emit( + isa, + &mut code_buf, + &mut reloc_sink, + &mut trap_sink, + &mut stackmap_sink, + ) + .expect("compile_and_emit"); + + code_memory + .allocate_copy_of_byte_slice(&code_buf) + .expect("allocate_copy_of_byte_slice") + .as_ptr() +} + +pub fn create_handle_with_function(func: &Rc>) -> Result { + let sig = func.borrow().r#type().get_cranelift_signature().clone(); + + let isa = { + let isa_builder = + cranelift_native::builder().expect("host machine is not a supported target"); + let flag_builder = cranelift_codegen::settings::builder(); + isa_builder.finish(cranelift_codegen::settings::Flags::new(flag_builder)) + }; + + let mut fn_builder_ctx = FunctionBuilderContext::new(); + let mut module = Module::new(); + let mut finished_functions: PrimaryMap = + PrimaryMap::new(); + let mut code_memory = CodeMemory::new(); + + //let pointer_type = types::Type::triple_pointer_type(&HOST); + //let call_conv = isa::CallConv::triple_default(&HOST); + + let sig_id = module.signatures.push(sig.clone()); + let func_id = module.functions.push(sig_id); + module + .exports + .insert("trampoline".to_string(), Export::Function(func_id)); + let trampoline = make_trampoline( + isa.as_ref(), + &mut code_memory, + &mut fn_builder_ctx, + func_id.index() as u32, + &sig, + ); + code_memory.publish(); + + finished_functions.push(trampoline); + + let trampoline_state = TrampolineState { + func: func.clone(), + trap: None, + code_memory, + }; + + create_handle(module, finished_functions, Box::new(trampoline_state)) +} + +/// We don't expect trampoline compilation to produce any relocations, so +/// this `RelocSink` just asserts that it doesn't recieve any. +struct RelocSink {} + +impl binemit::RelocSink for RelocSink { + fn reloc_ebb( + &mut self, + _offset: binemit::CodeOffset, + _reloc: binemit::Reloc, + _ebb_offset: binemit::CodeOffset, + ) { + panic!("trampoline compilation should not produce ebb relocs"); + } + fn reloc_external( + &mut self, + _offset: binemit::CodeOffset, + _reloc: binemit::Reloc, + _name: &ir::ExternalName, + _addend: binemit::Addend, + ) { + panic!("trampoline compilation should not produce external symbol relocs"); + } + fn reloc_jt( + &mut self, + _offset: binemit::CodeOffset, + _reloc: binemit::Reloc, + _jt: ir::JumpTable, + ) { + panic!("trampoline compilation should not produce jump table relocs"); + } +} diff --git a/wasmtime-api/src/trampoline/global.rs b/wasmtime-api/src/trampoline/global.rs new file mode 100644 index 0000000000..fa6b2d790b --- /dev/null +++ b/wasmtime-api/src/trampoline/global.rs @@ -0,0 +1,47 @@ +use cranelift_entity::PrimaryMap; +use failure::Error; +use wasmtime_environ::Module; +use wasmtime_runtime::{InstanceHandle, VMGlobalDefinition}; + +use super::create_handle::create_handle; +use crate::{GlobalType, Mutability, Val}; + +#[allow(dead_code)] +pub struct GlobalState { + definition: Box, + handle: InstanceHandle, +} + +pub fn create_global( + gt: &GlobalType, + val: Val, +) -> Result<(wasmtime_runtime::Export, GlobalState), Error> { + let mut definition = Box::new(VMGlobalDefinition::new()); + unsafe { + match val { + Val::I32(i) => *definition.as_i32_mut() = i, + Val::I64(i) => *definition.as_i64_mut() = i, + Val::F32(f) => *definition.as_u32_mut() = f, + Val::F64(f) => *definition.as_u64_mut() = f, + _ => unimplemented!("create_global for {:?}", gt), + } + } + + let global = cranelift_wasm::Global { + ty: gt.content().get_cranelift_type(), + mutability: match gt.mutability() { + Mutability::Const => false, + Mutability::Var => true, + }, + initializer: cranelift_wasm::GlobalInit::Import, // TODO is it right? + }; + let mut handle = create_handle(Module::new(), PrimaryMap::new(), Box::new(())).expect("handle"); + Ok(( + wasmtime_runtime::Export::Global { + definition: definition.as_mut(), + vmctx: handle.vmctx_mut_ptr(), + global, + }, + GlobalState { definition, handle }, + )) +} diff --git a/wasmtime-api/src/trampoline/memory.rs b/wasmtime-api/src/trampoline/memory.rs new file mode 100644 index 0000000000..23a966ae15 --- /dev/null +++ b/wasmtime-api/src/trampoline/memory.rs @@ -0,0 +1,33 @@ +use cranelift_entity::PrimaryMap; +use failure::Error; +use wasmtime_environ::Module; +use wasmtime_runtime::InstanceHandle; + +use super::create_handle::create_handle; +use crate::MemoryType; + +#[allow(dead_code)] + +pub fn create_handle_with_memory(memory: &MemoryType) -> Result { + let mut module = Module::new(); + + let memory = cranelift_wasm::Memory { + minimum: memory.limits().min(), + maximum: if memory.limits().max() == std::u32::MAX { + None + } else { + Some(memory.limits().max()) + }, + shared: false, // TODO + }; + let tunable = Default::default(); + + let memory_plan = wasmtime_environ::MemoryPlan::for_memory(memory, &tunable); + let memory_id = module.memory_plans.push(memory_plan); + module.exports.insert( + "memory".to_string(), + wasmtime_environ::Export::Memory(memory_id), + ); + + create_handle(module, PrimaryMap::new(), Box::new(())) +} diff --git a/wasmtime-api/src/trampoline/mod.rs b/wasmtime-api/src/trampoline/mod.rs index 37d080194a..7ed0a63e26 100644 --- a/wasmtime-api/src/trampoline/mod.rs +++ b/wasmtime-api/src/trampoline/mod.rs @@ -2,18 +2,40 @@ mod code_memory; mod create_handle; +mod func; +mod global; +mod memory; use failure::Error; use std::cell::RefCell; use std::rc::Rc; -use self::create_handle::create_handle; -use super::externals::Func; +use self::func::create_handle_with_function; +use self::global::create_global; +use self::memory::create_handle_with_memory; +use super::{Func, GlobalType, MemoryType, Val}; + +pub use self::global::GlobalState; pub fn generate_func_export(f: &Rc>) -> Result<(), Error> { - let mut instance = create_handle(f)?; + let mut instance = create_handle_with_function(f)?; let export = instance.lookup("trampoline").expect("trampoline export"); f.borrow_mut().anchor = Some((instance, export)); Ok(()) } + +pub fn generate_global_export( + gt: &GlobalType, + val: Val, +) -> Result<(wasmtime_runtime::Export, GlobalState), Error> { + create_global(gt, val) +} + +pub fn generate_memory_export( + m: &MemoryType, +) -> Result<(wasmtime_runtime::InstanceHandle, wasmtime_runtime::Export), Error> { + let mut instance = create_handle_with_memory(m)?; + let export = instance.lookup("memory").expect("memory export"); + Ok((instance, export)) +} diff --git a/wasmtime-api/src/types.rs b/wasmtime-api/src/types.rs index 692ed99e1d..93b13e80d8 100644 --- a/wasmtime-api/src/types.rs +++ b/wasmtime-api/src/types.rs @@ -27,11 +27,19 @@ impl Limits { max: ::std::u32::MAX, } } + + pub fn min(&self) -> u32 { + self.min + } + + pub fn max(&self) -> u32 { + self.max + } } // Value Types -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq)] pub enum ValType { I32, I64, @@ -237,6 +245,17 @@ impl TableType { pub fn limits(&self) -> &Limits { &self.limits } + + pub(crate) fn from_cranelift_table(table: cranelift_wasm::Table) -> TableType { + assert!(if let cranelift_wasm::TableElementType::Func = table.ty { + true + } else { + false + }); + let ty = ValType::FuncRef; + let limits = Limits::new(table.minimum, table.maximum.unwrap_or(::std::u32::MAX)); + TableType::new(ty, limits) + } } // Memory Types diff --git a/wasmtime-api/src/wasm.rs b/wasmtime-api/src/wasm.rs index bd68a05cc5..d260d89081 100644 --- a/wasmtime-api/src/wasm.rs +++ b/wasmtime-api/src/wasm.rs @@ -6,8 +6,9 @@ // TODO complete the C API use super::{ - Callable, Engine, ExportType, Extern, Func, FuncType, ImportType, Instance, Module, Store, - Trap, Val, ValType, + Callable, Engine, ExportType, Extern, ExternType, Func, FuncType, Global, GlobalType, + ImportType, Instance, Limits, Memory, MemoryType, Module, Name, Store, TableType, Trap, Val, + ValType, }; use std::boxed::Box; use std::cell::RefCell; @@ -16,16 +17,146 @@ use std::ptr; use std::rc::Rc; use std::slice; -pub type byte_t = ::std::os::raw::c_char; +macro_rules! declare_vec { + ($name:ident, $elem_ty:path) => { + #[repr(C)] + #[derive(Clone)] + pub struct $name { + pub size: usize, + pub data: *mut $elem_ty, + } + + impl $name { + #[allow(dead_code)] + fn set_from_slice(&mut self, source: &[$elem_ty]) { + let mut buffer = Vec::with_capacity(source.len()); + buffer.extend_from_slice(source); + assert!(buffer.len() == buffer.capacity()); + self.size = buffer.len(); + self.data = buffer.as_mut_ptr(); + mem::forget(buffer); + } + + #[allow(dead_code)] + fn set_buffer(&mut self, mut buffer: Vec<$elem_ty>) { + assert!(buffer.len() == buffer.capacity()); + self.size = buffer.len(); + self.data = buffer.as_mut_ptr(); + mem::forget(buffer); + } + + #[allow(dead_code)] + fn set_uninitialized(&mut self, size: usize) { + let mut buffer = vec![Default::default(); size]; + self.size = size; + self.data = buffer.as_mut_ptr(); + mem::forget(buffer); + } + + #[allow(dead_code)] + fn uninitialize(&mut self) { + let _ = unsafe { Vec::from_raw_parts(self.data, self.size, self.size) }; + } + + #[allow(dead_code)] + fn as_slice(&self) -> &[$elem_ty] { + unsafe { slice::from_raw_parts(self.data as *const $elem_ty, self.size) } + } + } + + impl From> for $name { + fn from(mut vec: Vec<$elem_ty>) -> Self { + assert!(vec.len() == vec.capacity()); + let result = $name { + size: vec.len(), + data: vec.as_mut_ptr(), + }; + mem::forget(vec); + result + } + } + + impl Drop for $name { + fn drop(&mut self) { + self.uninitialize(); + } + } + }; + + ($name:ident, *mut $elem_ty:path) => { + #[repr(C)] + #[derive(Clone)] + pub struct $name { + pub size: usize, + pub data: *mut *mut $elem_ty, + } + + impl $name { + #[allow(dead_code)] + fn set_from_slice(&mut self, source: &[*mut $elem_ty]) { + let mut buffer = Vec::with_capacity(source.len()); + buffer.extend_from_slice(source); + assert!(buffer.len() == buffer.capacity()); + self.size = buffer.len(); + self.data = buffer.as_mut_ptr(); + mem::forget(buffer); + } + + #[allow(dead_code)] + fn set_buffer(&mut self, mut buffer: Vec<*mut $elem_ty>) { + assert!(buffer.len() == buffer.capacity()); + self.size = buffer.len(); + self.data = buffer.as_mut_ptr(); + mem::forget(buffer); + } + + #[allow(dead_code)] + fn set_uninitialized(&mut self, size: usize) { + let mut buffer = vec![ptr::null_mut(); size]; + self.size = size; + self.data = buffer.as_mut_ptr(); + mem::forget(buffer); + } + + #[allow(dead_code)] + fn uninitialize(&mut self) { + for element in unsafe { Vec::from_raw_parts(self.data, self.size, self.size) } { + let _ = unsafe { Box::from_raw(element) }; + } + } + + #[allow(dead_code)] + fn as_slice(&self) -> &[$elem_ty] { + unsafe { slice::from_raw_parts(self.data as *const $elem_ty, self.size) } + } + } + + impl From> for $name { + fn from(mut vec: Vec<*mut $elem_ty>) -> Self { + assert!(vec.len() == vec.capacity()); + let result = $name { + size: vec.len(), + data: vec.as_mut_ptr(), + }; + mem::forget(vec); + result + } + } + + impl Drop for $name { + fn drop(&mut self) { + self.uninitialize(); + } + } + }; +} + pub type float32_t = f32; pub type float64_t = f64; -pub type wasm_byte_t = byte_t; -#[repr(C)] -#[derive(Clone)] -pub struct wasm_byte_vec_t { - pub size: usize, - pub data: *mut wasm_byte_t, -} +pub type wasm_byte_t = u8; + +declare_vec!(wasm_byte_vec_t, wasm_byte_t); + pub type wasm_name_t = wasm_byte_vec_t; #[repr(C)] #[derive(Clone)] @@ -55,91 +186,84 @@ pub struct wasm_limits_t { pub struct wasm_valtype_t { ty: ValType, } -#[repr(C)] -#[derive(Clone)] -pub struct wasm_valtype_vec_t { - pub size: usize, - pub data: *mut *mut wasm_valtype_t, -} + +declare_vec!(wasm_valtype_vec_t, *mut wasm_valtype_t); + pub type wasm_valkind_t = u8; #[repr(C)] #[derive(Clone)] pub struct wasm_functype_t { functype: FuncType, + params_cache: Option, + returns_cache: Option, } -#[repr(C)] -#[derive(Clone)] -pub struct wasm_functype_vec_t { - pub size: usize, - pub data: *mut *mut wasm_functype_t, -} + +declare_vec!(wasm_functype_vec_t, *mut wasm_functype_t); + #[repr(C)] #[derive(Clone)] pub struct wasm_globaltype_t { - _unused: [u8; 0], -} -#[repr(C)] -#[derive(Clone)] -pub struct wasm_globaltype_vec_t { - pub size: usize, - pub data: *mut *mut wasm_globaltype_t, + globaltype: GlobalType, + content_cache: Option, } + +declare_vec!(wasm_globaltype_vec_t, *mut wasm_globaltype_t); + #[repr(C)] #[derive(Clone)] pub struct wasm_tabletype_t { - _unused: [u8; 0], -} -#[repr(C)] -#[derive(Clone)] -pub struct wasm_tabletype_vec_t { - pub size: usize, - pub data: *mut *mut wasm_tabletype_t, + tabletype: TableType, + element_cache: Option, + limits_cache: Option, } + +declare_vec!(wasm_tabletype_vec_t, *mut wasm_tabletype_t); + #[repr(C)] #[derive(Clone)] pub struct wasm_memorytype_t { - _unused: [u8; 0], -} -#[repr(C)] -#[derive(Clone)] -pub struct wasm_memorytype_vec_t { - pub size: usize, - pub data: *mut *mut wasm_memorytype_t, + memorytype: MemoryType, + limits_cache: Option, } + +declare_vec!(wasm_memorytype_vec_t, *mut wasm_memorytype_t); + #[repr(C)] #[derive(Clone)] pub struct wasm_externtype_t { - _unused: [u8; 0], + ty: ExternType, + cache: wasm_externtype_t_type_cache, } -#[repr(C)] + #[derive(Clone)] -pub struct wasm_externtype_vec_t { - pub size: usize, - pub data: *mut *mut wasm_externtype_t, +enum wasm_externtype_t_type_cache { + Empty, + Func(wasm_functype_t), + Global(wasm_globaltype_t), + Memory(wasm_memorytype_t), + Table(wasm_tabletype_t), } + +declare_vec!(wasm_externtype_vec_t, *mut wasm_externtype_t); + pub type wasm_externkind_t = u8; #[repr(C)] #[derive(Clone)] pub struct wasm_importtype_t { ty: ImportType, } -#[repr(C)] -#[derive(Clone)] -pub struct wasm_importtype_vec_t { - pub size: usize, - pub data: *mut *mut wasm_importtype_t, -} + +declare_vec!(wasm_importtype_vec_t, *mut wasm_importtype_t); + #[repr(C)] #[derive(Clone)] pub struct wasm_exporttype_t { ty: ExportType, + name_cache: Option, } -#[repr(C)] -#[derive(Clone)] -pub struct wasm_exporttype_vec_t { - pub size: usize, - pub data: *mut *mut wasm_exporttype_t, -} + +declare_vec!(wasm_exporttype_vec_t, *mut wasm_exporttype_t); + #[doc = ""] #[repr(C)] #[derive(Clone)] @@ -152,33 +276,39 @@ pub struct wasm_val_t { pub kind: wasm_valkind_t, pub of: wasm_val_t__bindgen_ty_1, } + #[repr(C)] #[derive(Copy, Clone)] pub union wasm_val_t__bindgen_ty_1 { pub i32: i32, pub i64: i64, + pub u32: u32, + pub u64: u64, pub f32: float32_t, pub f64: float64_t, pub ref_: *mut wasm_ref_t, _bindgen_union_align: u64, } -#[repr(C)] -#[derive(Clone)] -pub struct wasm_val_vec_t { - pub size: usize, - pub data: *mut wasm_val_t, + +impl Default for wasm_val_t { + fn default() -> Self { + wasm_val_t { + kind: 0, + of: wasm_val_t__bindgen_ty_1 { i32: 0 }, + } + } } + +declare_vec!(wasm_val_vec_t, wasm_val_t); + #[repr(C)] #[derive(Clone)] pub struct wasm_frame_t { _unused: [u8; 0], } -#[repr(C)] -#[derive(Clone)] -pub struct wasm_frame_vec_t { - pub size: usize, - pub data: *mut *mut wasm_frame_t, -} + +declare_vec!(wasm_frame_vec_t, *mut wasm_frame_t); + #[repr(C)] #[derive(Clone)] pub struct wasm_instance_t { @@ -225,7 +355,7 @@ pub type wasm_func_callback_with_env_t = ::std::option::Option< #[repr(C)] #[derive(Clone)] pub struct wasm_global_t { - _unused: [u8; 0], + global: Rc>, } #[repr(C)] #[derive(Clone)] @@ -236,7 +366,7 @@ pub type wasm_table_size_t = u32; #[repr(C)] #[derive(Clone)] pub struct wasm_memory_t { - _unused: [u8; 0], + memory: Rc>, } pub type wasm_memory_pages_t = u32; #[repr(C)] @@ -244,25 +374,17 @@ pub type wasm_memory_pages_t = u32; pub struct wasm_extern_t { ext: Rc>, } -#[repr(C)] -#[derive(Clone)] -pub struct wasm_extern_vec_t { - pub size: usize, - pub data: *mut *mut wasm_extern_t, -} + +declare_vec!(wasm_extern_vec_t, *mut wasm_extern_t); #[no_mangle] pub unsafe extern "C" fn wasm_byte_vec_delete(v: *mut wasm_byte_vec_t) { - let _ = Vec::from_raw_parts((*v).data, 0, (*v).size); + (*v).uninitialize(); } #[no_mangle] pub unsafe extern "C" fn wasm_byte_vec_new_uninitialized(out: *mut wasm_byte_vec_t, size: usize) { - let mut buffer = vec![0; size]; - let result = out.as_mut().unwrap(); - result.size = buffer.capacity(); - result.data = buffer.as_mut_ptr(); - mem::forget(buffer); + (*out).set_uninitialized(size); } #[no_mangle] @@ -287,11 +409,7 @@ pub unsafe extern "C" fn wasm_extern_as_func(e: *mut wasm_extern_t) -> *mut wasm #[no_mangle] pub unsafe extern "C" fn wasm_extern_vec_delete(v: *mut wasm_extern_vec_t) { - let buffer = Vec::from_raw_parts((*v).data, (*v).size, (*v).size); - for p in buffer { - // TODO wasm_extern_delete - let _ = Box::from_raw(p); - } + (*v).uninitialize(); } #[no_mangle] @@ -338,12 +456,46 @@ impl wasm_val_t { } } + fn set(&mut self, val: Val) { + match val { + Val::I32(i) => { + self.kind = from_valtype(&ValType::I32); + self.of = wasm_val_t__bindgen_ty_1 { i32: i }; + } + Val::I64(i) => { + self.kind = from_valtype(&ValType::I64); + self.of = wasm_val_t__bindgen_ty_1 { i64: i }; + } + Val::F32(f) => { + self.kind = from_valtype(&ValType::F32); + self.of = wasm_val_t__bindgen_ty_1 { u32: f }; + } + Val::F64(f) => { + self.kind = from_valtype(&ValType::F64); + self.of = wasm_val_t__bindgen_ty_1 { u64: f }; + } + _ => unimplemented!("wasm_val_t::from_val {:?}", val), + } + } + fn from_val(val: &Val) -> wasm_val_t { match val { Val::I32(i) => wasm_val_t { - kind: from_valtype(ValType::I32), + kind: from_valtype(&ValType::I32), of: wasm_val_t__bindgen_ty_1 { i32: *i }, }, + Val::I64(i) => wasm_val_t { + kind: from_valtype(&ValType::I64), + of: wasm_val_t__bindgen_ty_1 { i64: *i }, + }, + Val::F32(f) => wasm_val_t { + kind: from_valtype(&ValType::F32), + of: wasm_val_t__bindgen_ty_1 { u32: *f }, + }, + Val::F64(f) => wasm_val_t { + kind: from_valtype(&ValType::F64), + of: wasm_val_t__bindgen_ty_1 { u64: *f }, + }, _ => unimplemented!("wasm_val_t::from_val {:?}", val), } } @@ -351,6 +503,9 @@ impl wasm_val_t { fn val(&self) -> Val { match into_valtype(self.kind) { ValType::I32 => Val::from(unsafe { self.of.i32 }), + ValType::I64 => Val::from(unsafe { self.of.i64 }), + ValType::F32 => Val::from(unsafe { self.of.f32 }), + ValType::F64 => Val::from(unsafe { self.of.f64 }), _ => unimplemented!("wasm_val_t::val {:?}", self.kind), } } @@ -452,7 +607,11 @@ pub unsafe extern "C" fn wasm_functype_new( .map(|vt| (*vt).ty.clone()) .collect::>(); let functype = FuncType::new(params.into_boxed_slice(), results.into_boxed_slice()); - let functype = Box::new(wasm_functype_t { functype }); + let functype = Box::new(wasm_functype_t { + functype, + params_cache: None, // TODO get from args? + returns_cache: None, // TODO get from args? + }); Box::into_raw(functype) } @@ -471,7 +630,7 @@ pub unsafe extern "C" fn wasm_instance_new( store: *mut wasm_store_t, module: *const wasm_module_t, imports: *const *const wasm_extern_t, - _result: *mut *mut wasm_trap_t, + result: *mut *mut wasm_trap_t, ) -> *mut wasm_instance_t { let store = (*store).store.clone(); let mut externs: Vec>> = Vec::with_capacity((*module).imports.len()); @@ -485,9 +644,23 @@ pub unsafe extern "C" fn wasm_instance_new( let instance = Box::new(wasm_instance_t { instance: Rc::new(RefCell::new(instance)), }); + if !result.is_null() { + (*result) = ptr::null_mut(); + } Box::into_raw(instance) } - _ => unimplemented!(), + Err(_) => { + if !result.is_null() { + // TODO Unwrap trap from failure::Error + let trap = Box::new(wasm_trap_t { + trap: Rc::new(RefCell::new(Trap::new( + "trap during instantiation".to_string(), + ))), + }); + (*result) = Box::into_raw(trap); + } + ptr::null_mut() + } } } @@ -503,9 +676,7 @@ pub unsafe extern "C" fn wasm_instance_exports( let ext = Box::new(wasm_extern_t { ext: e.clone() }); buffer.push(Box::into_raw(ext)); } - (*out).size = buffer.capacity(); - (*out).data = buffer.as_mut_ptr(); - mem::forget(buffer); + (*out).set_buffer(buffer); } #[no_mangle] @@ -513,12 +684,21 @@ pub unsafe extern "C" fn wasm_module_delete(module: *mut wasm_module_t) { let _ = Box::from_raw(module); } +impl wasm_name_t { + fn from_name(name: &Name) -> wasm_name_t { + let s = name.to_string(); + let mut buffer = Vec::new(); + buffer.extend_from_slice(s.as_bytes()); + buffer.into() + } +} + #[no_mangle] pub unsafe extern "C" fn wasm_module_new( store: *mut wasm_store_t, binary: *const wasm_byte_vec_t, ) -> *mut wasm_module_t { - let binary = slice::from_raw_parts((*binary).data as *const u8, (*binary).size); + let binary = (*binary).as_slice(); let store = (*store).store.clone(); let module = Module::new(store, binary).expect("module"); let imports = module @@ -529,7 +709,10 @@ pub unsafe extern "C" fn wasm_module_new( let exports = module .exports() .iter() - .map(|e| wasm_exporttype_t { ty: e.clone() }) + .map(|e| wasm_exporttype_t { + ty: e.clone(), + name_cache: None, + }) .collect::>(); let module = Box::new(wasm_module_t { module: Rc::new(RefCell::new(module)), @@ -555,8 +738,7 @@ pub unsafe extern "C" fn wasm_store_new(engine: *mut wasm_engine_t) -> *mut wasm #[no_mangle] pub unsafe extern "C" fn wasm_valtype_vec_new_empty(out: *mut wasm_valtype_vec_t) { - (*out).data = ptr::null_mut(); - (*out).size = 0; + (*out).set_uninitialized(0); } #[no_mangle] @@ -600,7 +782,7 @@ fn into_valtype(kind: wasm_valkind_t) -> ValType { } } -fn from_valtype(ty: ValType) -> wasm_valkind_t { +fn from_valtype(ty: &ValType) -> wasm_valkind_t { match ty { ValType::I32 => 0, ValType::I64 => 1, @@ -626,13 +808,9 @@ pub unsafe extern "C" fn wasm_valtype_vec_new( data: *const *mut wasm_valtype_t, ) { let slice = slice::from_raw_parts(data, size); - let mut buffer = Vec::with_capacity(size); - buffer.extend_from_slice(slice); - assert!(size == buffer.capacity()); - (*out).size = size; - (*out).data = buffer.as_mut_ptr(); - mem::forget(buffer); + (*out).set_from_slice(slice); } + #[no_mangle] pub unsafe extern "C" fn wasm_byte_vec_new( out: *mut wasm_byte_vec_t, @@ -640,12 +818,7 @@ pub unsafe extern "C" fn wasm_byte_vec_new( data: *const wasm_byte_t, ) { let slice = slice::from_raw_parts(data, size); - let mut buffer = Vec::with_capacity(size); - buffer.extend_from_slice(slice); - assert!(size == buffer.capacity()); - (*out).size = size; - (*out).data = buffer.as_mut_ptr(); - mem::forget(buffer); + (*out).set_from_slice(slice); } #[no_mangle] @@ -675,10 +848,7 @@ pub unsafe extern "C" fn wasm_frame_module_offset(_arg1: *const wasm_frame_t) -> #[no_mangle] pub unsafe extern "C" fn wasm_frame_vec_delete(frames: *mut wasm_frame_vec_t) { - let frames = Vec::from_raw_parts((*frames).data, (*frames).size, (*frames).size); - for _frame in frames { - unimplemented!("wasm_frame_vec_delete for frame") - } + (*frames).uninitialize(); } #[no_mangle] @@ -691,7 +861,7 @@ pub unsafe extern "C" fn wasm_trap_new( _store: *mut wasm_store_t, message: *const wasm_message_t, ) -> *mut wasm_trap_t { - let message = slice::from_raw_parts((*message).data as *const u8, (*message).size); + let message = (*message).as_slice(); if message[message.len() - 1] != 0 { panic!("wasm_trap_new message stringz expected"); } @@ -708,10 +878,7 @@ pub unsafe extern "C" fn wasm_trap_message(trap: *const wasm_trap_t, out: *mut w buffer.extend_from_slice((*trap).trap.borrow().message().as_bytes()); buffer.reserve_exact(1); buffer.push(0); - assert!(buffer.len() == buffer.capacity()); - (*out).size = buffer.capacity(); - (*out).data = buffer.as_mut_ptr() as *mut i8; - mem::forget(buffer); + (*out).set_buffer(buffer); } #[no_mangle] @@ -721,8 +888,449 @@ pub unsafe extern "C" fn wasm_trap_origin(_trap: *const wasm_trap_t) -> *mut was #[no_mangle] pub unsafe extern "C" fn wasm_trap_trace(_trap: *const wasm_trap_t, out: *mut wasm_frame_vec_t) { - let mut buffer = Vec::new(); - (*out).size = 0; - (*out).data = buffer.as_mut_ptr(); - mem::forget(buffer); + (*out).set_uninitialized(0); +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_exporttype_name(et: *const wasm_exporttype_t) -> *const wasm_name_t { + if (*et).name_cache.is_none() { + let et = (et as *mut wasm_exporttype_t).as_mut().unwrap(); + et.name_cache = Some(wasm_name_t::from_name(&et.ty.name())); + } + (*et).name_cache.as_ref().unwrap() +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_exporttype_type( + et: *const wasm_exporttype_t, +) -> *const wasm_externtype_t { + let ty = Box::new(wasm_externtype_t { + ty: (*et).ty.r#type().clone(), + cache: wasm_externtype_t_type_cache::Empty, + }); + Box::into_raw(ty) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_exporttype_vec_delete(et: *mut wasm_exporttype_vec_t) { + (*et).uninitialize(); +} + +fn from_externtype(ty: &ExternType) -> wasm_externkind_t { + match ty { + ExternType::ExternFunc(_) => 0, + ExternType::ExternGlobal(_) => 1, + ExternType::ExternTable(_) => 2, + ExternType::ExternMemory(_) => 3, + } +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_extern_kind(e: *const wasm_extern_t) -> wasm_externkind_t { + from_externtype(&(*e).ext.borrow().r#type()) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_extern_type(e: *const wasm_extern_t) -> *mut wasm_externtype_t { + let et = Box::new(wasm_externtype_t { + ty: (*e).ext.borrow().r#type(), + cache: wasm_externtype_t_type_cache::Empty, + }); + Box::into_raw(et) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_externtype_as_functype_const( + et: *const wasm_externtype_t, +) -> *const wasm_functype_t { + if let wasm_externtype_t_type_cache::Empty = (*et).cache { + let functype = (*et).ty.func().clone(); + let f = wasm_functype_t { + functype, + params_cache: None, + returns_cache: None, + }; + let et = (et as *mut wasm_externtype_t).as_mut().unwrap(); + et.cache = wasm_externtype_t_type_cache::Func(f); + } + match &(*et).cache { + wasm_externtype_t_type_cache::Func(f) => f, + _ => panic!("wasm_externtype_as_functype_const"), + } +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_externtype_as_globaltype_const( + et: *const wasm_externtype_t, +) -> *const wasm_globaltype_t { + if let wasm_externtype_t_type_cache::Empty = (*et).cache { + let globaltype = (*et).ty.global().clone(); + let g = wasm_globaltype_t { + globaltype, + content_cache: None, + }; + let et = (et as *mut wasm_externtype_t).as_mut().unwrap(); + et.cache = wasm_externtype_t_type_cache::Global(g); + } + match &(*et).cache { + wasm_externtype_t_type_cache::Global(g) => g, + _ => panic!("wasm_externtype_as_globaltype_const"), + } +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_externtype_as_tabletype_const( + et: *const wasm_externtype_t, +) -> *const wasm_tabletype_t { + if let wasm_externtype_t_type_cache::Empty = (*et).cache { + let tabletype = (*et).ty.table().clone(); + let t = wasm_tabletype_t { + tabletype, + element_cache: None, + limits_cache: None, + }; + let et = (et as *mut wasm_externtype_t).as_mut().unwrap(); + et.cache = wasm_externtype_t_type_cache::Table(t); + } + match &(*et).cache { + wasm_externtype_t_type_cache::Table(t) => t, + _ => panic!("wasm_externtype_as_tabletype_const"), + } +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_externtype_as_memorytype_const( + et: *const wasm_externtype_t, +) -> *const wasm_memorytype_t { + if let wasm_externtype_t_type_cache::Empty = (*et).cache { + let memorytype = (*et).ty.memory().clone(); + let m = wasm_memorytype_t { + memorytype, + limits_cache: None, + }; + let et = (et as *mut wasm_externtype_t).as_mut().unwrap(); + et.cache = wasm_externtype_t_type_cache::Memory(m); + } + match &(*et).cache { + wasm_externtype_t_type_cache::Memory(m) => m, + _ => panic!("wasm_externtype_as_memorytype_const"), + } +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_externtype_delete(et: *mut wasm_externtype_t) { + let _ = Box::from_raw(et); +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_externtype_kind(et: *const wasm_externtype_t) -> wasm_externkind_t { + from_externtype(&(*et).ty) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_func_param_arity(f: *const wasm_func_t) -> usize { + (*f).func.borrow().param_arity() +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_func_result_arity(f: *const wasm_func_t) -> usize { + (*f).func.borrow().result_arity() +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_functype_params( + ft: *const wasm_functype_t, +) -> *const wasm_valtype_vec_t { + if (*ft).params_cache.is_none() { + let ft = (ft as *mut wasm_functype_t).as_mut().unwrap(); + let buffer = ft + .functype + .params() + .iter() + .map(|p| { + let ty = Box::new(wasm_valtype_t { ty: p.clone() }); + Box::into_raw(ty) + }) + .collect::>(); + ft.params_cache = Some(buffer.into()); + } + (*ft).params_cache.as_ref().unwrap() +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_functype_results( + ft: *const wasm_functype_t, +) -> *const wasm_valtype_vec_t { + if (*ft).returns_cache.is_none() { + let ft = (ft as *mut wasm_functype_t).as_mut().unwrap(); + let buffer = ft + .functype + .results() + .iter() + .map(|p| { + let ty = Box::new(wasm_valtype_t { ty: p.clone() }); + Box::into_raw(ty) + }) + .collect::>(); + ft.returns_cache = Some(buffer.into()); + } + (*ft).returns_cache.as_ref().unwrap() +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_globaltype_content( + gt: *const wasm_globaltype_t, +) -> *const wasm_valtype_t { + if (*gt).content_cache.is_none() { + let gt = (gt as *mut wasm_globaltype_t).as_mut().unwrap(); + gt.content_cache = Some(wasm_valtype_t { + ty: (*gt).globaltype.content().clone(), + }); + } + (*gt).content_cache.as_ref().unwrap() +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_globaltype_mutability( + gt: *const wasm_globaltype_t, +) -> wasm_mutability_t { + use super::Mutability::*; + match (*gt).globaltype.mutability() { + Const => 0, + Var => 1, + } +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_memorytype_limits( + mt: *const wasm_memorytype_t, +) -> *const wasm_limits_t { + if (*mt).limits_cache.is_none() { + let mt = (mt as *mut wasm_memorytype_t).as_mut().unwrap(); + let limits = (*mt).memorytype.limits(); + mt.limits_cache = Some(wasm_limits_t { + min: limits.min(), + max: limits.max(), + }); + } + (*mt).limits_cache.as_ref().unwrap() +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_module_exports( + module: *const wasm_module_t, + out: *mut wasm_exporttype_vec_t, +) { + let buffer = (*module) + .exports + .iter() + .map(|et| { + let et = Box::new(et.clone()); + Box::into_raw(et) + }) + .collect::>(); + (*out).set_buffer(buffer); +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_module_imports( + _module: *const wasm_module_t, + _out: *mut wasm_importtype_vec_t, +) { + unimplemented!("wasm_module_imports"); +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_tabletype_element( + tt: *const wasm_tabletype_t, +) -> *const wasm_valtype_t { + if (*tt).element_cache.is_none() { + let tt = (tt as *mut wasm_tabletype_t).as_mut().unwrap(); + tt.element_cache = Some(wasm_valtype_t { + ty: (*tt).tabletype.element().clone(), + }); + } + (*tt).element_cache.as_ref().unwrap() +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_tabletype_limits( + tt: *const wasm_tabletype_t, +) -> *const wasm_limits_t { + if (*tt).limits_cache.is_none() { + let tt = (tt as *mut wasm_tabletype_t).as_mut().unwrap(); + let limits = (*tt).tabletype.limits(); + tt.limits_cache = Some(wasm_limits_t { + min: limits.min(), + max: limits.max(), + }); + } + (*tt).limits_cache.as_ref().unwrap() +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_valtype_kind(vt: *const wasm_valtype_t) -> wasm_valkind_t { + from_valtype(&(*vt).ty) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_extern_as_global(e: *mut wasm_extern_t) -> *mut wasm_global_t { + let g = Box::new(wasm_global_t { + global: (*e).ext.borrow().global().clone(), + }); + Box::into_raw(g) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_global_as_extern(g: *mut wasm_global_t) -> *mut wasm_extern_t { + let ext = Rc::new(RefCell::new(Extern::Global((*g).global.clone()))); + let ext = Box::new(wasm_extern_t { ext }); + Box::into_raw(ext) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_global_delete(g: *mut wasm_global_t) { + let _ = Box::from_raw(g); +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_global_copy(g: *const wasm_global_t) -> *mut wasm_global_t { + Box::into_raw(Box::new((*g).clone())) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_global_same( + g1: *const wasm_global_t, + g2: *const wasm_global_t, +) -> bool { + (*g1).global.as_ptr() == (*g2).global.as_ptr() +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_global_new( + store: *mut wasm_store_t, + gt: *const wasm_globaltype_t, + val: *const wasm_val_t, +) -> *mut wasm_global_t { + let global = Rc::new(RefCell::new(Global::new( + (*store).store.clone(), + (*gt).globaltype.clone(), + (*val).val(), + ))); + let g = Box::new(wasm_global_t { global }); + Box::into_raw(g) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_global_get(g: *const wasm_global_t, out: *mut wasm_val_t) { + (*out).set((*g).global.borrow_mut().get()); +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_global_set(g: *mut wasm_global_t, val: *const wasm_val_t) { + (*g).global.borrow_mut().set((*val).val()) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_globaltype_delete(gt: *mut wasm_globaltype_t) { + let _ = Box::from_raw(gt); +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_globaltype_new( + ty: *mut wasm_valtype_t, + mutability: wasm_mutability_t, +) -> *mut wasm_globaltype_t { + use super::Mutability::*; + let ty = Box::from_raw(ty); + let mutability = match mutability { + 0 => Const, + 1 => Var, + _ => panic!("mutability out-of-range"), + }; + let globaltype = GlobalType::new(ty.ty.clone(), mutability); + let gt = Box::new(wasm_globaltype_t { + globaltype, + content_cache: Some(*ty), + }); + Box::into_raw(gt) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_extern_as_memory(e: *mut wasm_extern_t) -> *mut wasm_memory_t { + let g = Box::new(wasm_memory_t { + memory: (*e).ext.borrow().memory().clone(), + }); + Box::into_raw(g) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_memory_delete(m: *mut wasm_memory_t) { + let _ = Box::from_raw(m); +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_memory_copy(m: *const wasm_memory_t) -> *mut wasm_memory_t { + Box::into_raw(Box::new((*m).clone())) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_memory_same( + m1: *const wasm_memory_t, + m2: *const wasm_memory_t, +) -> bool { + (*m1).memory.as_ptr() == (*m2).memory.as_ptr() +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_memory_data(m: *mut wasm_memory_t) -> *mut u8 { + (*m).memory.borrow().data() +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_memory_data_size(m: *const wasm_memory_t) -> usize { + (*m).memory.borrow().data_size() +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_memory_size(m: *const wasm_memory_t) -> wasm_memory_pages_t { + (*m).memory.borrow().size() +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_memory_grow( + m: *mut wasm_memory_t, + delta: wasm_memory_pages_t, +) -> bool { + (*m).memory.borrow_mut().grow(delta) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_memory_new( + store: *mut wasm_store_t, + mt: *const wasm_memorytype_t, +) -> *mut wasm_memory_t { + let memory = Rc::new(RefCell::new(Memory::new( + (*store).store.clone(), + (*mt).memorytype.clone(), + ))); + let m = Box::new(wasm_memory_t { memory }); + Box::into_raw(m) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_memorytype_delete(mt: *mut wasm_memorytype_t) { + let _ = Box::from_raw(mt); +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_memorytype_new( + limits: *const wasm_limits_t, +) -> *mut wasm_memorytype_t { + let limits = Limits::new((*limits).min, (*limits).max); + let mt = Box::new(wasm_memorytype_t { + memorytype: MemoryType::new(limits), + limits_cache: None, + }); + Box::into_raw(mt) }