wasmi/module/instantiate/mod.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
mod error;
mod pre;
#[cfg(test)]
mod tests;
pub use self::{error::InstantiationError, pre::InstancePre};
use super::{element::ElementSegmentKind, export, ConstExpr, InitDataSegment, Module};
use crate::{
core::UntypedVal,
func::WasmFuncEntity,
memory::{DataSegment, MemoryError},
value::WithType,
AsContext,
AsContextMut,
ElementSegment,
Error,
Extern,
ExternType,
FuncRef,
Global,
Instance,
InstanceEntity,
InstanceEntityBuilder,
Memory,
Table,
Val,
};
impl Module {
/// Instantiates a new [`Instance`] from the given compiled [`Module`].
///
/// Uses the given `context` to store the instance data to.
/// The given `externals` are joined with the imports in the same order in which they occurred.
///
/// # Note
///
/// This is a very low-level API. For a more high-level API users should use the
/// corresponding instantiation methods provided by the [`Linker`].
///
/// # Errors
///
/// If the given `externals` do not satisfy the required imports, e.g. if an externally
/// provided [`Func`] has a different function signature than required by the module import.
///
/// [`Linker`]: struct.Linker.html
/// [`Func`]: [`crate::Func`]
pub(crate) fn instantiate<I>(
&self,
mut context: impl AsContextMut,
externals: I,
) -> Result<InstancePre, Error>
where
I: IntoIterator<Item = Extern, IntoIter: ExactSizeIterator>,
{
context
.as_context_mut()
.store
.check_new_instances_limit(1)?;
let handle = context.as_context_mut().store.inner.alloc_instance();
let mut builder = InstanceEntity::build(self);
self.extract_imports(&context, &mut builder, externals)?;
self.extract_functions(&mut context, &mut builder, handle);
self.extract_tables(&mut context, &mut builder)?;
self.extract_memories(&mut context, &mut builder)?;
self.extract_globals(&mut context, &mut builder);
self.extract_exports(&mut builder);
self.extract_start_fn(&mut builder);
self.initialize_table_elements(&mut context, &mut builder)?;
self.initialize_memory_data(&mut context, &mut builder)?;
// At this point the module instantiation is nearly done.
// The only thing that is missing is to run the `start` function.
Ok(InstancePre::new(handle, builder))
}
/// Extract the Wasm imports from the module and zips them with the given external values.
///
/// This also stores imported references into the [`Instance`] under construction.
///
/// # Errors
///
/// - If too few or too many external values are given for the required module imports.
/// - If the zipped import and given external have mismatching types, e.g. on index `i`
/// the module requires a function import but on index `i` the externals provide a global
/// variable external value.
/// - If the externally provided [`Table`], [`Memory`], [`Func`] or [`Global`] has a type
/// mismatch with the expected module import type.
///
/// [`Func`]: [`crate::Func`]
fn extract_imports<I>(
&self,
store: impl AsContext,
builder: &mut InstanceEntityBuilder,
externals: I,
) -> Result<(), InstantiationError>
where
I: IntoIterator<Item = Extern, IntoIter: ExactSizeIterator>,
{
let imports = self.imports();
let externals = externals.into_iter();
if imports.len() != externals.len() {
return Err(InstantiationError::InvalidNumberOfImports {
required: imports.len(),
given: externals.len(),
});
}
for (import, external) in imports.zip(externals) {
match (import.ty(), external) {
(ExternType::Func(expected_signature), Extern::Func(func)) => {
let actual_signature = func.ty(&store);
if &actual_signature != expected_signature {
return Err(InstantiationError::SignatureMismatch {
actual: actual_signature,
expected: expected_signature.clone(),
});
}
builder.push_func(func);
}
(ExternType::Table(required), Extern::Table(table)) => {
let imported = table.dynamic_ty(&store);
imported.is_subtype_or_err(required)?;
builder.push_table(table);
}
(ExternType::Memory(required), Extern::Memory(memory)) => {
let imported = memory.dynamic_ty(&store);
imported.is_subtype_or_err(required)?;
builder.push_memory(memory);
}
(ExternType::Global(required), Extern::Global(global)) => {
let imported = global.ty(&store);
required.satisfies(&imported)?;
builder.push_global(global);
}
(expected_import, actual_extern_val) => {
return Err(InstantiationError::ImportsExternalsMismatch {
expected: expected_import.clone(),
actual: actual_extern_val,
});
}
}
}
Ok(())
}
/// Extracts the Wasm functions from the module and stores them into the [`Store`].
///
/// This also stores [`Func`] references into the [`Instance`] under construction.
///
/// [`Store`]: struct.Store.html
/// [`Func`]: [`crate::Func`]
fn extract_functions(
&self,
mut context: impl AsContextMut,
builder: &mut InstanceEntityBuilder,
handle: Instance,
) {
for (func_type, func_body) in self.internal_funcs() {
let wasm_func = WasmFuncEntity::new(func_type, func_body, handle);
let func = context
.as_context_mut()
.store
.inner
.alloc_func(wasm_func.into());
builder.push_func(func);
}
}
/// Extracts the Wasm tables from the module and stores them into the [`Store`].
///
/// This also stores [`Table`] references into the [`Instance`] under construction.
///
/// [`Store`]: struct.Store.html
fn extract_tables(
&self,
mut context: impl AsContextMut,
builder: &mut InstanceEntityBuilder,
) -> Result<(), InstantiationError> {
context
.as_context_mut()
.store
.check_new_tables_limit(self.len_tables())?;
for table_type in self.internal_tables().copied() {
let init = Val::default(table_type.element());
let table = Table::new(context.as_context_mut(), table_type, init)?;
builder.push_table(table);
}
Ok(())
}
/// Extracts the Wasm linear memories from the module and stores them into the [`Store`].
///
/// This also stores [`Memory`] references into the [`Instance`] under construction.
///
/// [`Store`]: struct.Store.html
fn extract_memories(
&self,
mut context: impl AsContextMut,
builder: &mut InstanceEntityBuilder,
) -> Result<(), MemoryError> {
context
.as_context_mut()
.store
.check_new_memories_limit(self.len_memories())?;
for memory_type in self.internal_memories().copied() {
let memory = Memory::new(context.as_context_mut(), memory_type)?;
builder.push_memory(memory);
}
Ok(())
}
/// Extracts the Wasm global variables from the module and stores them into the [`Store`].
///
/// This also stores [`Global`] references into the [`Instance`] under construction.
///
/// [`Store`]: struct.Store.html
fn extract_globals(&self, mut context: impl AsContextMut, builder: &mut InstanceEntityBuilder) {
for (global_type, global_init) in self.internal_globals() {
let value_type = global_type.content();
let init_value = Self::eval_init_expr(context.as_context_mut(), builder, global_init);
let mutability = global_type.mutability();
let global = Global::new(
context.as_context_mut(),
init_value.with_type(value_type),
mutability,
);
builder.push_global(global);
}
}
/// Evaluates the given initializer expression using the partially constructed [`Instance`].
fn eval_init_expr(
context: impl AsContext,
builder: &InstanceEntityBuilder,
init_expr: &ConstExpr,
) -> UntypedVal {
init_expr
.eval_with_context(
|global_index| builder.get_global(global_index).get(&context),
|func_index| FuncRef::new(builder.get_func(func_index)),
)
.expect("must evaluate to proper value")
}
/// Extracts the Wasm exports from the module and registers them into the [`Instance`].
fn extract_exports(&self, builder: &mut InstanceEntityBuilder) {
for (field, idx) in &self.module_header().exports {
let external = match idx {
export::ExternIdx::Func(func_index) => {
let func_index = func_index.into_u32();
let func = builder.get_func(func_index);
Extern::Func(func)
}
export::ExternIdx::Table(table_index) => {
let table_index = table_index.into_u32();
let table = builder.get_table(table_index);
Extern::Table(table)
}
export::ExternIdx::Memory(memory_index) => {
let memory_index = memory_index.into_u32();
let memory = builder.get_memory(memory_index);
Extern::Memory(memory)
}
export::ExternIdx::Global(global_index) => {
let global_index = global_index.into_u32();
let global = builder.get_global(global_index);
Extern::Global(global)
}
};
builder.push_export(field, external);
}
}
/// Extracts the optional start function for the build instance.
fn extract_start_fn(&self, builder: &mut InstanceEntityBuilder) {
if let Some(start_fn) = self.module_header().start {
builder.set_start(start_fn)
}
}
/// Initializes the [`Instance`] tables with the Wasm element segments of the [`Module`].
fn initialize_table_elements(
&self,
mut context: impl AsContextMut,
builder: &mut InstanceEntityBuilder,
) -> Result<(), Error> {
for segment in &self.module_header().element_segments[..] {
let get_global = |index| builder.get_global(index);
let get_func = |index| builder.get_func(index);
let element =
ElementSegment::new(context.as_context_mut(), segment, get_func, get_global);
if let ElementSegmentKind::Active(active) = segment.kind() {
let dst_index = u32::from(Self::eval_init_expr(
context.as_context(),
builder,
active.offset(),
));
let table = builder.get_table(active.table_index().into_u32());
// Note: This checks not only that the elements in the element segments properly
// fit into the table at the given offset but also that the element segment
// consists of at least 1 element member.
let len_table = table.size(&context);
let len_items = element.size(&context);
dst_index
.checked_add(len_items)
.filter(|&max_index| max_index <= len_table)
.ok_or(InstantiationError::ElementSegmentDoesNotFit {
table,
offset: dst_index,
amount: len_items,
})?;
let (table, elem) = context
.as_context_mut()
.store
.inner
.resolve_table_and_element_mut(&table, &element);
table.init(elem, dst_index, 0, len_items, None)?;
// Now drop the active element segment as commanded by the Wasm spec.
elem.drop_items();
}
builder.push_element_segment(element);
}
Ok(())
}
/// Initializes the [`Instance`] linear memories with the Wasm data segments of the [`Module`].
fn initialize_memory_data(
&self,
mut context: impl AsContextMut,
builder: &mut InstanceEntityBuilder,
) -> Result<(), Error> {
for segment in &self.inner.data_segments {
let segment = match segment {
InitDataSegment::Active {
memory_index,
offset,
bytes,
} => {
let offset =
u32::from(Self::eval_init_expr(context.as_context(), builder, offset))
as usize;
let memory = builder.get_memory(memory_index.into_u32());
memory.write(context.as_context_mut(), offset, bytes)?;
DataSegment::new_active(context.as_context_mut())
}
InitDataSegment::Passive { bytes } => {
DataSegment::new_passive(context.as_context_mut(), bytes)
}
};
builder.push_data_segment(segment);
}
Ok(())
}
}