wrap DynFunc

This commit is contained in:
Valery Antopol 2022-03-01 19:07:43 +03:00
parent 6c94964f8e
commit daaa626c02
6 changed files with 155 additions and 66 deletions

View File

@ -1,9 +1,10 @@
//pub mod errors; //pub mod errors;
//pub mod it_memory_traits; //pub mod it_memory_traits;
use std::fmt::Display; //use std::fmt::Display;
use std::path::PathBuf; use std::path::PathBuf;
use thiserror::Error; use thiserror::Error;
use wasmer_core::types::FuncSig;
use it_memory_traits::{SequentialMemoryView, SequentialReader, SequentialWriter}; use it_memory_traits::{SequentialMemoryView, SequentialReader, SequentialWriter};
pub struct Value {} pub struct Value {}
@ -23,11 +24,13 @@ pub trait WasmBackend: Clone + 'static {
type WITMemory: Memory<Self> + it_memory_traits::Memory<Self::WITMemoryView> + Clone + 'static; type WITMemory: Memory<Self> + it_memory_traits::Memory<Self::WITMemoryView> + Clone + 'static;
//type SR: SequentialReader; //type SR: SequentialReader;
//type SW: SequentialWriter; //type SW: SequentialWriter;
type WITMemoryView: for<'a> SequentialMemoryView<'a,/* SR = Self::SR, SW = Self::SW*/> + 'static; type DynamicFunc: DynamicFunc<'static>;
type WITMemoryView: for<'a> SequentialMemoryView<'a /* SR = Self::SR, SW = Self::SW*/> + 'static;
type FunctionExport: FunctionExport; type FunctionExport: FunctionExport;
type M: Module<Self>; type M: Module<Self>;
type I: Instance<Self>; type I: Instance<Self>;
type Wasi: WasiImplementation<Self>; type Wasi: WasiImplementation<Self>;
type Namespace: Namespace<Self>;
fn compile(wasm: &[u8]) -> WasmBackendResult<Self::M>; fn compile(wasm: &[u8]) -> WasmBackendResult<Self::M>;
} }
@ -83,17 +86,17 @@ pub trait ImportObject<WB: WasmBackend>:
fn new() -> Self; fn new() -> Self;
fn extend_with_self(&mut self, other: Self); fn extend_with_self(&mut self, other: Self);
fn register<S, N>( fn register<S>(
&mut self, &mut self,
name: S, name: S,
namespace: N, namespace: <WB as WasmBackend>::Namespace,
) -> Option<Box<dyn wasmer_runtime::LikeNamespace>> ) -> Option<Box<dyn LikeNamespace<WB>>>
where where
S: Into<String>, S: Into<String>;
N: wasmer_runtime::LikeNamespace + Send + 'static;
fn get_memory_env(
fn get_memory_env(&self) -> Option<Export<<WB as WasmBackend>::MemoryExport, <WB as WasmBackend>::FunctionExport>>; &self,
) -> Option<Export<<WB as WasmBackend>::MemoryExport, <WB as WasmBackend>::FunctionExport>>;
/* /*
fn maybe_with_namespace<Func, InnerRet>(&self, namespace: &str, f: Func) -> Option<InnerRet> fn maybe_with_namespace<Func, InnerRet>(&self, namespace: &str, f: Func) -> Option<InnerRet>
where where
@ -111,12 +114,32 @@ pub trait WasiImplementation<WB: WasmBackend> {
) -> Result<<WB as WasmBackend>::IO, String>; ) -> Result<<WB as WasmBackend>::IO, String>;
} }
pub trait MemoryExport { pub trait MemoryExport {}
}
pub trait FunctionExport {} pub trait FunctionExport {}
pub trait Memory<WB: WasmBackend> { pub trait Memory<WB: WasmBackend> {
fn new(export: <WB as WasmBackend>::MemoryExport) -> Self; fn new(export: <WB as WasmBackend>::MemoryExport) -> Self;
fn view_from_ctx(ctx: &wasmer_runtime::Ctx, memory_index: u32) -> <WB as WasmBackend>::WITMemoryView; fn view_from_ctx(
ctx: &wasmer_runtime::Ctx,
memory_index: u32,
) -> <WB as WasmBackend>::WITMemoryView;
} }
pub trait DynamicFunc<'a> {
fn new<F>(sig: std::sync::Arc<FuncSig>, func: F) -> Self
where
F: Fn(
&mut wasmer_core::vm::Ctx,
&[wasmer_core::types::Value],
) -> Vec<wasmer_core::types::Value>
+ 'static;
}
pub trait Namespace<WB: WasmBackend>: LikeNamespace<WB> {
fn new() -> Self;
fn insert(&mut self, name: impl Into<String>, func: <WB as WasmBackend>::DynamicFunc);
}
pub trait LikeNamespace<WB: WasmBackend> {}

View File

@ -1,5 +1,7 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use marine_wasm_backend_traits::{Export, Memory, WasmBackend}; use marine_wasm_backend_traits::{
DynamicFunc, Export, LikeNamespace, Memory, Namespace, Value, WasmBackend,
};
use marine_wasm_backend_traits::WasmBackendResult; use marine_wasm_backend_traits::WasmBackendResult;
use marine_wasm_backend_traits::WasmBackendError; use marine_wasm_backend_traits::WasmBackendError;
use marine_wasm_backend_traits::Module; use marine_wasm_backend_traits::Module;
@ -15,6 +17,7 @@ use std::slice::Windows;
use std::sync::Arc; use std::sync::Arc;
use wasmer_core::fault::raw::longjmp; use wasmer_core::fault::raw::longjmp;
use wasmer_core::prelude::vm::Ctx; use wasmer_core::prelude::vm::Ctx;
use wasmer_core::types::FuncSig;
mod memory_access; mod memory_access;
mod memory; mod memory;
@ -25,11 +28,11 @@ use crate::memory::WITMemory;
use crate::memory_access::{WasmerSequentialReader, WasmerSequentialWriter}; use crate::memory_access::{WasmerSequentialReader, WasmerSequentialWriter};
#[derive(Clone)] #[derive(Clone)]
pub struct WasmerBackend/*<'a>*/ { pub struct WasmerBackend /*<'a>*/ {
// _data: &'a PhantomData<i32>, // _data: &'a PhantomData<i32>,
} }
impl<'b> WasmBackend for WasmerBackend/*<'b>*/ { impl<'b> WasmBackend for WasmerBackend /*<'b>*/ {
type Exports = WasmerInstance; type Exports = WasmerInstance;
type MemoryExport = WasmerMemoryExport; type MemoryExport = WasmerMemoryExport;
type FunctionExport = WasmerFunctionExport; type FunctionExport = WasmerFunctionExport;
@ -41,6 +44,8 @@ impl<'b> WasmBackend for WasmerBackend/*<'b>*/ {
type WITMemory = WITMemory; type WITMemory = WITMemory;
type WITMemoryView = WITMemoryView<'static>; type WITMemoryView = WITMemoryView<'static>;
type Wasi = WasmerWasiImplementation; type Wasi = WasmerWasiImplementation;
type DynamicFunc = WasmerDynamicFunc;
type Namespace = WasmerNamespace;
fn compile(wasm: &[u8]) -> WasmBackendResult<WasmerModule> { fn compile(wasm: &[u8]) -> WasmBackendResult<WasmerModule> {
wasmer_runtime::compile(wasm) wasmer_runtime::compile(wasm)
@ -123,9 +128,9 @@ impl
{ {
self.import_object self.import_object
.extend(iter.into_iter().map(|(s1, s2, export)| match export { .extend(iter.into_iter().map(|(s1, s2, export)| match export {
Export::Memory(memory) => (s1, s2, memory.into()), Export::Memory(memory) => (s1, s2, memory.into()),
Export::Function(func) => (s1, s2, func.into()), Export::Function(func) => (s1, s2, func.into()),
_ => unreachable!() _ => unreachable!(),
})) }))
} }
} }
@ -141,26 +146,32 @@ impl ImportObject<WasmerBackend> for WasmerImportObject {
self.import_object.extend(other.import_object); self.import_object.extend(other.import_object);
} }
fn register<S, N>( fn register<S>(
&mut self, &mut self,
name: S, name: S,
namespace: N, namespace: WasmerNamespace,
) -> Option<Box<dyn wasmer_runtime::LikeNamespace>> ) -> Option<Box<dyn LikeNamespace<WasmerBackend>>>
where where
S: Into<String>, S: Into<String>,
N: wasmer_runtime::LikeNamespace + Send + 'static,
{ {
self.import_object.register(name, namespace) self.import_object
.register(name, namespace.namespace)
.map(|namespace| {
let boxed: Box<
(dyn marine_wasm_backend_traits::LikeNamespace<WasmerBackend> + 'static),
> = Box::new(WasmerLikeNamespace { namespace });
boxed
})
} }
fn get_memory_env(&self) -> Option<Export<WasmerMemoryExport, WasmerFunctionExport>> { fn get_memory_env(&self) -> Option<Export<WasmerMemoryExport, WasmerFunctionExport>> {
self.import_object self.import_object
.maybe_with_namespace("env", |env| env.get_export("memory")) .maybe_with_namespace("env", |env| env.get_export("memory"))
.map(|export| { .map(|export| match export {
match export { wasmer_runtime::Export::Memory(memory) => {
wasmer_runtime::Export::Memory(memory) => Export::Memory(WasmerMemoryExport {memory}), Export::Memory(WasmerMemoryExport { memory })
_ => Export::Other
} }
_ => Export::Other,
}) })
} }
@ -188,8 +199,7 @@ pub struct WasmerMemoryExport {
memory: wasmer_runtime::Memory, memory: wasmer_runtime::Memory,
} }
impl MemoryExport for WasmerMemoryExport { impl MemoryExport for WasmerMemoryExport {}
}
pub struct WasmerWasiImplementation {} pub struct WasmerWasiImplementation {}
@ -221,7 +231,9 @@ impl Exports<WasmerBackend> for WasmerInstance {
} }
} }
fn export_from_wasmer_export(export: wasmer_core::export::Export) -> Export<WasmerMemoryExport, WasmerFunctionExport> { fn export_from_wasmer_export(
export: wasmer_core::export::Export,
) -> Export<WasmerMemoryExport, WasmerFunctionExport> {
match export { match export {
wasmer_core::export::Export::Function { wasmer_core::export::Export::Function {
func, func,
@ -233,14 +245,10 @@ fn export_from_wasmer_export(export: wasmer_core::export::Export) -> Export<Wasm
signature, signature,
}), }),
wasmer_core::export::Export::Memory(memory) => { wasmer_core::export::Export::Memory(memory) => {
Export::Memory(WasmerMemoryExport{memory}) Export::Memory(WasmerMemoryExport { memory })
}
wasmer_core::export::Export::Table(_table) => {
Export::Other
}
wasmer_core::export::Export::Global(_global) => {
Export::Other
} }
wasmer_core::export::Export::Table(_table) => Export::Other,
wasmer_core::export::Export::Global(_global) => Export::Other,
} }
} }
@ -252,11 +260,10 @@ impl Into<wasmer_runtime::Export> for WasmerMemoryExport {
impl Into<wasmer_runtime::Export> for WasmerFunctionExport { impl Into<wasmer_runtime::Export> for WasmerFunctionExport {
fn into(self) -> wasmer_core::export::Export { fn into(self) -> wasmer_core::export::Export {
wasmer_runtime::Export::Function{ wasmer_runtime::Export::Function {
func: self.func, func: self.func,
ctx: self.ctx, ctx: self.ctx,
signature: self.signature, signature: self.signature,
} }
} }
} }
@ -268,9 +275,50 @@ impl Memory<WasmerBackend> for WITMemory {
fn view_from_ctx(ctx: &Ctx, memory_index: u32) -> WITMemoryView<'static> { fn view_from_ctx(ctx: &Ctx, memory_index: u32) -> WITMemoryView<'static> {
let memory = unsafe { let memory = unsafe {
std::mem::transmute::<&'_ wasmer_runtime::Memory, &'static wasmer_runtime::Memory>(ctx.memory(memory_index)) std::mem::transmute::<&'_ wasmer_runtime::Memory, &'static wasmer_runtime::Memory>(
ctx.memory(memory_index),
)
}; };
WITMemoryView(memory.view::<u8>()) WITMemoryView(memory.view::<u8>())
} }
} }
pub struct WasmerDynamicFunc {
func: wasmer_core::typed_func::DynamicFunc<'static>,
}
impl<'a> DynamicFunc<'a> for WasmerDynamicFunc {
fn new<F>(sig: Arc<FuncSig>, func: F) -> Self
where
F: Fn(&mut Ctx, &[wasmer_core::prelude::Value]) -> Vec<wasmer_core::prelude::Value>
+ 'static,
{
let func = wasmer_core::typed_func::DynamicFunc::new(sig, func);
Self { func }
}
}
pub struct WasmerNamespace {
namespace: wasmer_core::import::Namespace,
}
impl LikeNamespace<WasmerBackend> for WasmerNamespace {}
impl Namespace<WasmerBackend> for WasmerNamespace {
fn new() -> Self {
Self {
namespace: wasmer_core::import::Namespace::new(),
}
}
fn insert(&mut self, name: impl Into<String>, func: WasmerDynamicFunc) {
self.namespace.insert(name, func.func);
}
}
struct WasmerLikeNamespace {
namespace: Box<dyn wasmer_core::import::LikeNamespace + 'static>,
}
impl LikeNamespace<WasmerBackend> for WasmerLikeNamespace {}

View File

@ -101,7 +101,7 @@ impl<'s, 'v> it_memory_traits::SequentialMemoryView<'v> for WITMemoryView<'s> {
} }
} }
impl<'a> /*wasm::structures::Memory*/it_memory_traits::Memory<WITMemoryView<'a>> for WITMemory { impl<'a> it_memory_traits::Memory<WITMemoryView<'a>> for WITMemory {
fn view(&self) -> WITMemoryView<'a> { fn view(&self) -> WITMemoryView<'a> {
let LocalMemory { base, .. } = unsafe { *self.0.vm_local_memory() }; let LocalMemory { base, .. } = unsafe { *self.0.vm_local_memory() };
let length = self.0.size().bytes().0 / std::mem::size_of::<u8>(); let length = self.0.size().bytes().0 / std::mem::size_of::<u8>();

View File

@ -28,9 +28,9 @@ use crate::call_wasm_func;
use crate::HostImportDescriptor; use crate::HostImportDescriptor;
//use crate::module::wit_prelude::WITMemoryView; //use crate::module::wit_prelude::WITMemoryView;
use wasmer_core::Func; //use wasmer_core::Func;
use wasmer_core::vm::Ctx; use wasmer_core::vm::Ctx;
use wasmer_core::typed_func::DynamicFunc; //use wasmer_core::typed_func::DynamicFunc;
use wasmer_core::types::Value as WValue; use wasmer_core::types::Value as WValue;
use wasmer_core::types::FuncSig; use wasmer_core::types::FuncSig;
use it_lilo::lifter::ILifter; use it_lilo::lifter::ILifter;
@ -41,11 +41,12 @@ use std::rc::Rc;
use marine_wasm_backend_traits::WasmBackend; use marine_wasm_backend_traits::WasmBackend;
use marine_wasm_backend_traits::Memory; use marine_wasm_backend_traits::Memory;
use marine_wasm_backend_traits::DynamicFunc;
pub(crate) fn create_host_import_func<WB: WasmBackend>( pub(crate) fn create_host_import_func<WB: WasmBackend>(
descriptor: HostImportDescriptor, descriptor: HostImportDescriptor,
record_types: Rc<MRecordTypes>, record_types: Rc<MRecordTypes>,
) -> DynamicFunc<'static> { ) -> <WB as WasmBackend>::DynamicFunc {
let allocate_func: AllocateFunc = Box::new(RefCell::new(None)); let allocate_func: AllocateFunc = Box::new(RefCell::new(None));
let set_result_ptr_func: SetResultPtrFunc = Box::new(RefCell::new(None)); let set_result_ptr_func: SetResultPtrFunc = Box::new(RefCell::new(None));
let set_result_size_func: SetResultSizeFunc = Box::new(RefCell::new(None)); let set_result_size_func: SetResultSizeFunc = Box::new(RefCell::new(None));
@ -74,7 +75,6 @@ pub(crate) fn create_host_import_func<WB: WasmBackend>(
let li_helper = LiHelper::new(record_types.clone()); let li_helper = LiHelper::new(record_types.clone());
let lifter = ILifter::new(memory_view, &li_helper); let lifter = ILifter::new(memory_view, &li_helper);
match wvalues_to_ivalues(&lifter, inputs, &argument_types) { match wvalues_to_ivalues(&lifter, inputs, &argument_types) {
Ok(ivalues) => host_exported_func(ctx, ivalues), Ok(ivalues) => host_exported_func(ctx, ivalues),
Err(e) => { Err(e) => {
@ -142,7 +142,7 @@ pub(crate) fn create_host_import_func<WB: WasmBackend>(
} }
}; };
DynamicFunc::new( <WB as WasmBackend>::DynamicFunc::new(
std::sync::Arc::new(FuncSig::new(raw_args, raw_output)), std::sync::Arc::new(FuncSig::new(raw_args, raw_output)),
func, func,
) )

View File

@ -27,12 +27,14 @@ use marine_wasm_backend_traits::Instance;
use marine_wasm_backend_traits::ImportObject; use marine_wasm_backend_traits::ImportObject;
use marine_wasm_backend_traits::WasiImplementation; use marine_wasm_backend_traits::WasiImplementation;
use marine_wasm_backend_traits::Exports; use marine_wasm_backend_traits::Exports;
use marine_wasm_backend_traits::Namespace;
use marine_wasm_backend_traits::DynamicFunc;
use marine_it_interfaces::MITInterfaces; use marine_it_interfaces::MITInterfaces;
use marine_it_parser::extract_it_from_module; use marine_it_parser::extract_it_from_module;
use marine_utils::SharedString; use marine_utils::SharedString;
//use wasmer_core::Instance as WasmerInstance; //use wasmer_core::Instance as WasmerInstance;
use wasmer_core::import::Namespace; //use wasmer_core::import::Namespace;
//use wasmer_runtime::compile; //use wasmer_runtime::compile;
//use wasmer_runtime::ImportObject; //use wasmer_runtime::ImportObject;
use wasmer_it::interpreter::Interpreter; use wasmer_it::interpreter::Interpreter;
@ -43,8 +45,13 @@ use std::mem::MaybeUninit;
use std::sync::Arc; use std::sync::Arc;
use std::rc::Rc; use std::rc::Rc;
type ITInterpreter<WB> = type ITInterpreter<WB> = Interpreter<
Interpreter<ITInstance<WB>, ITExport, WITFunction<WB>, <WB as WasmBackend>::WITMemory, <WB as WasmBackend>::WITMemoryView>; ITInstance<WB>,
ITExport,
WITFunction<WB>,
<WB as WasmBackend>::WITMemory,
<WB as WasmBackend>::WITMemoryView,
>;
#[derive(Clone)] #[derive(Clone)]
pub(super) struct ITModuleFunc<WB: WasmBackend> { pub(super) struct ITModuleFunc<WB: WasmBackend> {
@ -244,7 +251,7 @@ impl<WB: WasmBackend> MModule<WB> {
) )
.map_err(MError::WASIPrepareError)?; .map_err(MError::WASIPrepareError)?;
let mut host_closures_namespace = Namespace::new(); let mut host_closures_namespace = <WB as WasmBackend>::Namespace::new();
let record_types = mit let record_types = mit
.record_types() .record_types()
.map(|(id, r)| (id, r.clone())) .map(|(id, r)| (id, r.clone()))
@ -303,24 +310,30 @@ impl<WB: WasmBackend> MModule<WB> {
wit_instance: Arc<MaybeUninit<ITInstance<WB>>>, wit_instance: Arc<MaybeUninit<ITInstance<WB>>>,
) -> MResult<<WB as WasmBackend>::IO> { ) -> MResult<<WB as WasmBackend>::IO> {
use marine_it_interfaces::ITAstType; use marine_it_interfaces::ITAstType;
use wasmer_core::typed_func::DynamicFunc; //use wasmer_core::typed_func::DynamicFunc;
use wasmer_core::vm::Ctx; use wasmer_core::vm::Ctx;
// returns function that will be called from imports of Wasmer module // returns function that will be called from imports of Wasmer module
fn dyn_func_from_raw_import<'a, 'b, F>( fn dyn_func_from_raw_import<'a, 'b, F, WB, I1, I2>(
inputs: impl Iterator<Item = &'a IType>, inputs: I1,
outputs: impl Iterator<Item = &'b IType>, outputs: I2,
raw_import: F, raw_import: F,
) -> DynamicFunc<'static> ) -> <WB as WasmBackend>::DynamicFunc
where where
F: Fn(&mut Ctx, &[WValue]) -> Vec<WValue> + 'static, F: Fn(&mut Ctx, &[WValue]) -> Vec<WValue> + 'static,
WB: WasmBackend,
I1: Iterator<Item = &'a IType>,
I2: Iterator<Item = &'b IType>,
{ {
use wasmer_core::types::FuncSig; use wasmer_core::types::FuncSig;
use super::type_converters::itype_to_wtype; use super::type_converters::itype_to_wtype;
let inputs = inputs.map(itype_to_wtype).collect::<Vec<_>>(); let inputs = inputs.map(itype_to_wtype).collect::<Vec<_>>();
let outputs = outputs.map(itype_to_wtype).collect::<Vec<_>>(); let outputs = outputs.map(itype_to_wtype).collect::<Vec<_>>();
DynamicFunc::new(Arc::new(FuncSig::new(inputs, outputs)), raw_import) <WB as WasmBackend>::DynamicFunc::new(
Arc::new(FuncSig::new(inputs, outputs)),
raw_import,
)
} }
// creates a closure that is represent a IT module import // creates a closure that is represent a IT module import
@ -400,7 +413,7 @@ impl<WB: WasmBackend> MModule<WB> {
import_name.to_string(), import_name.to_string(),
); );
let wit_import = dyn_func_from_raw_import( let wit_import = dyn_func_from_raw_import::<_, WB, _, _>(
arguments.iter().map(|IFunctionArg { ty, .. }| ty), arguments.iter().map(|IFunctionArg { ty, .. }| ty),
output_types.iter(), output_types.iter(),
raw_import, raw_import,
@ -420,7 +433,7 @@ impl<WB: WasmBackend> MModule<WB> {
// TODO: refactor this // TODO: refactor this
for (namespace_name, funcs) in wit_import_funcs.into_iter() { for (namespace_name, funcs) in wit_import_funcs.into_iter() {
let mut namespace = Namespace::new(); let mut namespace = <WB as WasmBackend>::Namespace::new();
for (import_name, import_func) in funcs.into_iter() { for (import_name, import_func) in funcs.into_iter() {
namespace.insert(import_name.to_string(), import_func); namespace.insert(import_name.to_string(), import_func);
} }

View File

@ -144,7 +144,9 @@ impl<WB: WasmBackend> ITInstance<WB> {
.collect::<MResult<HashMap<_, _>>>() .collect::<MResult<HashMap<_, _>>>()
} }
fn extract_memories(wasmer_instance: &<WB as WasmBackend>::I) -> Vec<<WB as WasmBackend>::WITMemory> { fn extract_memories(
wasmer_instance: &<WB as WasmBackend>::I,
) -> Vec<<WB as WasmBackend>::WITMemory> {
use marine_wasm_backend_traits::Export::Memory; use marine_wasm_backend_traits::Export::Memory;
let mut memories = wasmer_instance let mut memories = wasmer_instance
@ -155,9 +157,8 @@ impl<WB: WasmBackend> ITInstance<WB> {
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if let Some(Memory(memory)) = wasmer_instance if let Some(Memory(memory)) = wasmer_instance.import_object().get_memory_env()
.import_object().get_memory_env() //.maybe_with_namespace("env", |env| env.get_export("memory"))
//.maybe_with_namespace("env", |env| env.get_export("memory"))
{ {
memories.push(<WB as WasmBackend>::WITMemory::new(memory)); memories.push(<WB as WasmBackend>::WITMemory::new(memory));
} }
@ -184,8 +185,12 @@ impl<WB: WasmBackend> ITInstance<WB> {
} }
impl<'v, WB: WasmBackend> impl<'v, WB: WasmBackend>
wasm::structures::Instance<ITExport, WITFunction<WB>, <WB as WasmBackend>::WITMemory, <WB as WasmBackend>::WITMemoryView> wasm::structures::Instance<
for ITInstance<WB> ITExport,
WITFunction<WB>,
<WB as WasmBackend>::WITMemory,
<WB as WasmBackend>::WITMemoryView,
> for ITInstance<WB>
{ {
fn export(&self, _export_name: &str) -> Option<&ITExport> { fn export(&self, _export_name: &str) -> Option<&ITExport> {
// exports aren't used in this version of IT // exports aren't used in this version of IT