substrate_wasmtime/linker.rs
1use crate::{
2 Extern, ExternType, Func, FuncType, GlobalType, ImportType, Instance, IntoFunc, Module, Store,
3 Trap,
4};
5use anyhow::{anyhow, bail, Context, Error, Result};
6use log::warn;
7use std::collections::hash_map::{Entry, HashMap};
8use std::rc::Rc;
9
10/// Structure used to link wasm modules/instances together.
11///
12/// This structure is used to assist in instantiating a [`Module`]. A `Linker`
13/// is a way of performing name resolution to make instantiating a module easier
14/// (as opposed to calling [`Instance::new`]). `Linker` is a name-based resolver
15/// where names are dynamically defined and then used to instantiate a
16/// [`Module`]. The goal of a `Linker` is to have a one-argument method,
17/// [`Linker::instantiate`], which takes a [`Module`] and produces an
18/// [`Instance`]. This method will automatically select all the right imports
19/// for the [`Module`] to be instantiated, and will otherwise return an error
20/// if an import isn't satisfied.
21///
22/// ## Name Resolution
23///
24/// As mentioned previously, `Linker` is a form of name resolver. It will be
25/// using the string-based names of imports on a module to attempt to select a
26/// matching item to hook up to it. This name resolution has two-levels of
27/// namespaces, a module level and a name level. Each item is defined within a
28/// module and then has its own name. This basically follows the wasm standard
29/// for modularization.
30///
31/// Names in a `Linker` can be defined twice, but only for different signatures
32/// of items. This means that every item defined in a `Linker` has a unique
33/// name/type pair. For example you can define two functions with the module
34/// name `foo` and item name `bar`, so long as they have different function
35/// signatures. Currently duplicate memories and tables are not allowed, only
36/// one-per-name is allowed.
37///
38/// Note that allowing duplicates by shadowing the previous definition can be
39/// controlled with the [`Linker::allow_shadowing`] method as well.
40pub struct Linker {
41 store: Store,
42 string2idx: HashMap<Rc<str>, usize>,
43 strings: Vec<Rc<str>>,
44 map: HashMap<ImportKey, Extern>,
45 allow_shadowing: bool,
46}
47
48#[derive(Hash, PartialEq, Eq)]
49struct ImportKey {
50 name: usize,
51 module: usize,
52 kind: ImportKind,
53}
54
55#[derive(Hash, PartialEq, Eq, Debug)]
56enum ImportKind {
57 Func(FuncType),
58 Global(GlobalType),
59 Memory,
60 Table,
61}
62
63impl Linker {
64 /// Creates a new [`Linker`].
65 ///
66 /// This function will create a new [`Linker`] which is ready to start
67 /// linking modules. All items defined in this linker and produced by this
68 /// linker will be connected with `store` and must come from the same
69 /// `store`.
70 ///
71 /// # Examples
72 ///
73 /// ```
74 /// use wasmtime::{Linker, Store};
75 ///
76 /// let store = Store::default();
77 /// let mut linker = Linker::new(&store);
78 /// // ...
79 /// ```
80 pub fn new(store: &Store) -> Linker {
81 Linker {
82 store: store.clone(),
83 map: HashMap::new(),
84 string2idx: HashMap::new(),
85 strings: Vec::new(),
86 allow_shadowing: false,
87 }
88 }
89
90 /// Configures whether this [`Linker`] will shadow previous duplicate
91 /// definitions of the same signature.
92 ///
93 /// By default a [`Linker`] will disallow duplicate definitions of the same
94 /// signature. This method, however, can be used to instead allow duplicates
95 /// and have the latest definition take precedence when linking modules.
96 ///
97 /// # Examples
98 ///
99 /// ```
100 /// # use wasmtime::*;
101 /// # fn main() -> anyhow::Result<()> {
102 /// # let store = Store::default();
103 /// let mut linker = Linker::new(&store);
104 /// linker.func("", "", || {})?;
105 ///
106 /// // by default, duplicates are disallowed
107 /// assert!(linker.func("", "", || {}).is_err());
108 ///
109 /// // but shadowing can be configured to be allowed as well
110 /// linker.allow_shadowing(true);
111 /// linker.func("", "", || {})?;
112 /// # Ok(())
113 /// # }
114 /// ```
115 pub fn allow_shadowing(&mut self, allow: bool) -> &mut Linker {
116 self.allow_shadowing = allow;
117 self
118 }
119
120 /// Defines a new item in this [`Linker`].
121 ///
122 /// This method will add a new definition, by name, to this instance of
123 /// [`Linker`]. The `module` and `name` provided are what to name the
124 /// `item`.
125 ///
126 /// # Errors
127 ///
128 /// Returns an error if the `module` and `name` already identify an item
129 /// of the same type as the `item` provided and if shadowing is disallowed.
130 /// For more information see the documentation on [`Linker`].
131 ///
132 /// Also returns an error if `item` comes from a different store than this
133 /// [`Linker`] was created with.
134 ///
135 /// # Examples
136 ///
137 /// ```
138 /// # use wasmtime::*;
139 /// # fn main() -> anyhow::Result<()> {
140 /// # let store = Store::default();
141 /// let mut linker = Linker::new(&store);
142 /// let ty = GlobalType::new(ValType::I32, Mutability::Const);
143 /// let global = Global::new(&store, ty, Val::I32(0x1234))?;
144 /// linker.define("host", "offset", global)?;
145 ///
146 /// let wat = r#"
147 /// (module
148 /// (import "host" "offset" (global i32))
149 /// (memory 1)
150 /// (data (global.get 0) "foo")
151 /// )
152 /// "#;
153 /// let module = Module::new(store.engine(), wat)?;
154 /// linker.instantiate(&module)?;
155 /// # Ok(())
156 /// # }
157 /// ```
158 pub fn define(
159 &mut self,
160 module: &str,
161 name: &str,
162 item: impl Into<Extern>,
163 ) -> Result<&mut Self> {
164 self._define(module, name, item.into())
165 }
166
167 fn _define(&mut self, module: &str, name: &str, item: Extern) -> Result<&mut Self> {
168 if !item.comes_from_same_store(&self.store) {
169 bail!("all linker items must be from the same store");
170 }
171 self.insert(module, name, item)?;
172 Ok(self)
173 }
174
175 /// Convenience wrapper to define a function import.
176 ///
177 /// This method is a convenience wrapper around [`Linker::define`] which
178 /// internally delegates to [`Func::wrap`].
179 ///
180 /// # Errors
181 ///
182 /// Returns an error if the `module` and `name` already identify an item
183 /// of the same type as the `item` provided and if shadowing is disallowed.
184 /// For more information see the documentation on [`Linker`].
185 ///
186 /// # Examples
187 ///
188 /// ```
189 /// # use wasmtime::*;
190 /// # fn main() -> anyhow::Result<()> {
191 /// # let store = Store::default();
192 /// let mut linker = Linker::new(&store);
193 /// linker.func("host", "double", |x: i32| x * 2)?;
194 /// linker.func("host", "log_i32", |x: i32| println!("{}", x))?;
195 /// linker.func("host", "log_str", |caller: Caller, ptr: i32, len: i32| {
196 /// // ...
197 /// })?;
198 ///
199 /// let wat = r#"
200 /// (module
201 /// (import "host" "double" (func (param i32) (result i32)))
202 /// (import "host" "log_i32" (func (param i32)))
203 /// (import "host" "log_str" (func (param i32 i32)))
204 /// )
205 /// "#;
206 /// let module = Module::new(store.engine(), wat)?;
207 /// linker.instantiate(&module)?;
208 /// # Ok(())
209 /// # }
210 /// ```
211 pub fn func<Params, Args>(
212 &mut self,
213 module: &str,
214 name: &str,
215 func: impl IntoFunc<Params, Args>,
216 ) -> Result<&mut Self> {
217 self._define(module, name, Func::wrap(&self.store, func).into())
218 }
219
220 /// Convenience wrapper to define an entire [`Instance`] in this linker.
221 ///
222 /// This function is a convenience wrapper around [`Linker::define`] which
223 /// will define all exports on `instance` into this linker. The module name
224 /// for each export is `module_name`, and the name for each export is the
225 /// name in the instance itself.
226 ///
227 /// # Errors
228 ///
229 /// Returns an error if the any item is redefined twice in this linker (for
230 /// example the same `module_name` was already defined) and shadowing is
231 /// disallowed, or if `instance` comes from a different [`Store`] than this
232 /// [`Linker`] originally was created with.
233 ///
234 /// # Examples
235 ///
236 /// ```
237 /// # use wasmtime::*;
238 /// # fn main() -> anyhow::Result<()> {
239 /// # let store = Store::default();
240 /// let mut linker = Linker::new(&store);
241 ///
242 /// // Instantiate a small instance...
243 /// let wat = r#"(module (func (export "run") ))"#;
244 /// let module = Module::new(store.engine(), wat)?;
245 /// let instance = linker.instantiate(&module)?;
246 ///
247 /// // ... and inform the linker that the name of this instance is
248 /// // `instance1`. This defines the `instance1::run` name for our next
249 /// // module to use.
250 /// linker.instance("instance1", &instance)?;
251 ///
252 /// let wat = r#"
253 /// (module
254 /// (import "instance1" "run" (func $instance1_run))
255 /// (func (export "run")
256 /// call $instance1_run
257 /// )
258 /// )
259 /// "#;
260 /// let module = Module::new(store.engine(), wat)?;
261 /// let instance = linker.instantiate(&module)?;
262 /// # Ok(())
263 /// # }
264 /// ```
265 pub fn instance(&mut self, module_name: &str, instance: &Instance) -> Result<&mut Self> {
266 if !Store::same(&self.store, instance.store()) {
267 bail!("all linker items must be from the same store");
268 }
269 for export in instance.exports() {
270 self.insert(module_name, export.name(), export.into_extern())?;
271 }
272 Ok(self)
273 }
274
275 /// Define automatic instantiations of a [`Module`] in this linker.
276 ///
277 /// This automatically handles [Commands and Reactors] instantiation and
278 /// initialization.
279 ///
280 /// Exported functions of a Command module may be called directly, however
281 /// instead of having a single instance which is reused for each call,
282 /// each call creates a new instance, which lives for the duration of the
283 /// call. The imports of the Command are resolved once, and reused for
284 /// each instantiation, so all dependencies need to be present at the time
285 /// when `Linker::module` is called.
286 ///
287 /// For Reactors, a single instance is created, and an initialization
288 /// function is called, and then its exports may be called.
289 ///
290 /// Ordinary modules which don't declare themselves to be either Commands
291 /// or Reactors are treated as Reactors without any initialization calls.
292 ///
293 /// [Commands and Reactors]: https://github.com/WebAssembly/WASI/blob/master/design/application-abi.md#current-unstable-abi
294 ///
295 /// # Errors
296 ///
297 /// Returns an error if the any item is redefined twice in this linker (for
298 /// example the same `module_name` was already defined) and shadowing is
299 /// disallowed, if `instance` comes from a different [`Store`] than this
300 /// [`Linker`] originally was created with, or if a Reactor initialization
301 /// function traps.
302 ///
303 /// # Examples
304 ///
305 /// ```
306 /// # use wasmtime::*;
307 /// # fn main() -> anyhow::Result<()> {
308 /// # let store = Store::default();
309 /// let mut linker = Linker::new(&store);
310 ///
311 /// // Instantiate a small instance and inform the linker that the name of
312 /// // this instance is `instance1`. This defines the `instance1::run` name
313 /// // for our next module to use.
314 /// let wat = r#"(module (func (export "run") ))"#;
315 /// let module = Module::new(store.engine(), wat)?;
316 /// linker.module("instance1", &module)?;
317 ///
318 /// let wat = r#"
319 /// (module
320 /// (import "instance1" "run" (func $instance1_run))
321 /// (func (export "run")
322 /// call $instance1_run
323 /// )
324 /// )
325 /// "#;
326 /// let module = Module::new(store.engine(), wat)?;
327 /// let instance = linker.instantiate(&module)?;
328 /// # Ok(())
329 /// # }
330 /// ```
331 ///
332 /// For a Command, a new instance is created for each call.
333 ///
334 /// ```
335 /// # use wasmtime::*;
336 /// # fn main() -> anyhow::Result<()> {
337 /// # let store = Store::default();
338 /// let mut linker = Linker::new(&store);
339 ///
340 /// // Create a Command that attempts to count the number of times it is run, but is
341 /// // foiled by each call getting a new instance.
342 /// let wat = r#"
343 /// (module
344 /// (global $counter (mut i32) (i32.const 0))
345 /// (func (export "_start")
346 /// (global.set $counter (i32.add (global.get $counter) (i32.const 1)))
347 /// )
348 /// (func (export "read_counter") (result i32)
349 /// (global.get $counter)
350 /// )
351 /// )
352 /// "#;
353 /// let module = Module::new(store.engine(), wat)?;
354 /// linker.module("commander", &module)?;
355 /// let run = linker.get_default("")?.get0::<()>()?;
356 /// run()?;
357 /// run()?;
358 /// run()?;
359 ///
360 /// let wat = r#"
361 /// (module
362 /// (import "commander" "_start" (func $commander_start))
363 /// (import "commander" "read_counter" (func $commander_read_counter (result i32)))
364 /// (func (export "run") (result i32)
365 /// call $commander_start
366 /// call $commander_start
367 /// call $commander_start
368 /// call $commander_read_counter
369 /// )
370 /// )
371 /// "#;
372 /// let module = Module::new(store.engine(), wat)?;
373 /// linker.module("", &module)?;
374 /// let count = linker.get_one_by_name("", "run")?.into_func().unwrap().get0::<i32>()?()?;
375 /// assert_eq!(count, 0, "a Command should get a fresh instance on each invocation");
376 ///
377 /// # Ok(())
378 /// # }
379 /// ```
380 pub fn module(&mut self, module_name: &str, module: &Module) -> Result<&mut Self> {
381 match ModuleKind::categorize(module)? {
382 ModuleKind::Command => self.command(module_name, module),
383 ModuleKind::Reactor => {
384 let instance = self.instantiate(&module)?;
385
386 if let Some(export) = instance.get_export("_initialize") {
387 if let Extern::Func(func) = export {
388 func.get0::<()>()
389 .and_then(|f| f().map_err(Into::into))
390 .context("calling the Reactor initialization function")?;
391 }
392 }
393
394 self.instance(module_name, &instance)
395 }
396 }
397 }
398
399 fn command(&mut self, module_name: &str, module: &Module) -> Result<&mut Self> {
400 for export in module.exports() {
401 if let Some(func_ty) = export.ty().func() {
402 let imports = self.compute_imports(module)?;
403 let store = self.store.clone();
404 let module = module.clone();
405 let export_name = export.name().to_owned();
406 let func = Func::new(&self.store, func_ty.clone(), move |_, params, results| {
407 // Create a new instance for this command execution.
408 let instance = Instance::new(&store, &module, &imports)?;
409
410 // `unwrap()` everything here because we know the instance contains a
411 // function export with the given name and signature because we're
412 // iterating over the module it was instantiated from.
413 let command_results = instance
414 .get_export(&export_name)
415 .unwrap()
416 .into_func()
417 .unwrap()
418 .call(params)
419 .map_err(|error| error.downcast::<Trap>().unwrap())?;
420
421 // Copy the return values into the output slice.
422 for (result, command_result) in
423 results.iter_mut().zip(command_results.into_vec())
424 {
425 *result = command_result;
426 }
427
428 Ok(())
429 });
430 self.insert(module_name, export.name(), func.into())?;
431 } else if export.name() == "memory" && export.ty().memory().is_some() {
432 // Allow an exported "memory" memory for now.
433 } else if export.name() == "__indirect_function_table" && export.ty().table().is_some()
434 {
435 // Allow an exported "__indirect_function_table" table for now.
436 } else if export.name() == "table" && export.ty().table().is_some() {
437 // Allow an exported "table" table for now.
438 } else if export.name() == "__data_end" && export.ty().global().is_some() {
439 // Allow an exported "__data_end" memory for compatibility with toolchains
440 // which use --export-dynamic, which unfortunately doesn't work the way
441 // we want it to.
442 warn!("command module exporting '__data_end' is deprecated");
443 } else if export.name() == "__heap_base" && export.ty().global().is_some() {
444 // Allow an exported "__data_end" memory for compatibility with toolchains
445 // which use --export-dynamic, which unfortunately doesn't work the way
446 // we want it to.
447 warn!("command module exporting '__heap_base' is deprecated");
448 } else if export.name() == "__dso_handle" && export.ty().global().is_some() {
449 // Allow an exported "__dso_handle" memory for compatibility with toolchains
450 // which use --export-dynamic, which unfortunately doesn't work the way
451 // we want it to.
452 warn!("command module exporting '__dso_handle' is deprecated")
453 } else if export.name() == "__rtti_base" && export.ty().global().is_some() {
454 // Allow an exported "__rtti_base" memory for compatibility with
455 // AssemblyScript.
456 warn!("command module exporting '__rtti_base' is deprecated; pass `--runtime half` to the AssemblyScript compiler");
457 } else {
458 bail!("command export '{}' is not a function", export.name());
459 }
460 }
461
462 Ok(self)
463 }
464
465 /// Aliases one module's name as another.
466 ///
467 /// This method will alias all currently defined under `module` to also be
468 /// defined under the name `as_module` too.
469 ///
470 /// # Errors
471 ///
472 /// Returns an error if any shadowing violations happen while defining new
473 /// items.
474 pub fn alias(&mut self, module: &str, as_module: &str) -> Result<()> {
475 let items = self
476 .iter()
477 .filter(|(m, _, _)| *m == module)
478 .map(|(_, name, item)| (name.to_string(), item))
479 .collect::<Vec<_>>();
480 for (name, item) in items {
481 self.define(as_module, &name, item)?;
482 }
483 Ok(())
484 }
485
486 fn insert(&mut self, module: &str, name: &str, item: Extern) -> Result<()> {
487 let key = self.import_key(module, name, item.ty());
488 match self.map.entry(key) {
489 Entry::Occupied(o) if !self.allow_shadowing => bail!(
490 "import of `{}::{}` with kind {:?} defined twice",
491 module,
492 name,
493 o.key().kind,
494 ),
495 Entry::Occupied(mut o) => {
496 o.insert(item);
497 }
498 Entry::Vacant(v) => {
499 v.insert(item);
500 }
501 }
502 Ok(())
503 }
504
505 fn import_key(&mut self, module: &str, name: &str, ty: ExternType) -> ImportKey {
506 ImportKey {
507 module: self.intern_str(module),
508 name: self.intern_str(name),
509 kind: self.import_kind(ty),
510 }
511 }
512
513 fn import_kind(&self, ty: ExternType) -> ImportKind {
514 match ty {
515 ExternType::Func(f) => ImportKind::Func(f),
516 ExternType::Global(f) => ImportKind::Global(f),
517 ExternType::Memory(_) => ImportKind::Memory,
518 ExternType::Table(_) => ImportKind::Table,
519 }
520 }
521
522 fn intern_str(&mut self, string: &str) -> usize {
523 if let Some(idx) = self.string2idx.get(string) {
524 return *idx;
525 }
526 let string: Rc<str> = string.into();
527 let idx = self.strings.len();
528 self.strings.push(string.clone());
529 self.string2idx.insert(string, idx);
530 idx
531 }
532
533 /// Attempts to instantiate the `module` provided.
534 ///
535 /// This method will attempt to assemble a list of imports that correspond
536 /// to the imports required by the [`Module`] provided. This list
537 /// of imports is then passed to [`Instance::new`] to continue the
538 /// instantiation process.
539 ///
540 /// Each import of `module` will be looked up in this [`Linker`] and must
541 /// have previously been defined. If it was previously defined with an
542 /// incorrect signature or if it was not previously defined then an error
543 /// will be returned because the import can not be satisfied.
544 ///
545 /// Per the WebAssembly spec, instantiation includes running the module's
546 /// start function, if it has one (not to be confused with the `_start`
547 /// function, which is not run).
548 ///
549 /// # Errors
550 ///
551 /// This method can fail because an import may not be found, or because
552 /// instantiation itself may fail. For information on instantiation
553 /// failures see [`Instance::new`].
554 ///
555 /// # Examples
556 ///
557 /// ```
558 /// # use wasmtime::*;
559 /// # fn main() -> anyhow::Result<()> {
560 /// # let store = Store::default();
561 /// let mut linker = Linker::new(&store);
562 /// linker.func("host", "double", |x: i32| x * 2)?;
563 ///
564 /// let wat = r#"
565 /// (module
566 /// (import "host" "double" (func (param i32) (result i32)))
567 /// )
568 /// "#;
569 /// let module = Module::new(store.engine(), wat)?;
570 /// linker.instantiate(&module)?;
571 /// # Ok(())
572 /// # }
573 /// ```
574 pub fn instantiate(&self, module: &Module) -> Result<Instance> {
575 let imports = self.compute_imports(module)?;
576
577 Instance::new(&self.store, module, &imports)
578 }
579
580 fn compute_imports(&self, module: &Module) -> Result<Vec<Extern>> {
581 module
582 .imports()
583 .map(|import| self.get(&import).ok_or_else(|| self.link_error(&import)))
584 .collect()
585 }
586
587 fn link_error(&self, import: &ImportType) -> Error {
588 let mut options = Vec::new();
589 for i in self.map.keys() {
590 if &*self.strings[i.module] != import.module()
591 || &*self.strings[i.name] != import.name()
592 {
593 continue;
594 }
595 options.push(format!(" * {:?}\n", i.kind));
596 }
597 if options.is_empty() {
598 return anyhow!(
599 "unknown import: `{}::{}` has not been defined",
600 import.module(),
601 import.name()
602 );
603 }
604
605 options.sort();
606
607 anyhow!(
608 "incompatible import type for `{}::{}` specified\n\
609 desired signature was: {:?}\n\
610 signatures available:\n\n{}",
611 import.module(),
612 import.name(),
613 import.ty(),
614 options.concat(),
615 )
616 }
617
618 /// Returns the [`Store`] that this linker is connected to.
619 pub fn store(&self) -> &Store {
620 &self.store
621 }
622
623 /// Returns an iterator over all items defined in this `Linker`, in arbitrary order.
624 ///
625 /// The iterator returned will yield 3-tuples where the first two elements
626 /// are the module name and item name for the external item, and the third
627 /// item is the item itself that is defined.
628 ///
629 /// Note that multiple `Extern` items may be defined for the same
630 /// module/name pair.
631 pub fn iter(&self) -> impl Iterator<Item = (&str, &str, Extern)> {
632 self.map.iter().map(move |(key, item)| {
633 (
634 &*self.strings[key.module],
635 &*self.strings[key.name],
636 item.clone(),
637 )
638 })
639 }
640
641 /// Looks up a value in this `Linker` which matches the `import` type
642 /// provided.
643 ///
644 /// Returns `None` if no match was found.
645 pub fn get(&self, import: &ImportType) -> Option<Extern> {
646 let key = ImportKey {
647 module: *self.string2idx.get(import.module())?,
648 name: *self.string2idx.get(import.name())?,
649 kind: self.import_kind(import.ty()),
650 };
651 self.map.get(&key).cloned()
652 }
653
654 /// Returns all items defined for the `module` and `name` pair.
655 ///
656 /// This may return an empty iterator, but it may also return multiple items
657 /// if the module/name have been defined twice.
658 pub fn get_by_name<'a: 'p, 'p>(
659 &'a self,
660 module: &'p str,
661 name: &'p str,
662 ) -> impl Iterator<Item = &'a Extern> + 'p {
663 self.map
664 .iter()
665 .filter(move |(key, _item)| {
666 &*self.strings[key.module] == module && &*self.strings[key.name] == name
667 })
668 .map(|(_, item)| item)
669 }
670
671 /// Returns the single item defined for the `module` and `name` pair.
672 ///
673 /// Unlike the similar [`Linker::get_by_name`] method this function returns
674 /// a single `Extern` item. If the `module` and `name` pair isn't defined
675 /// in this linker then an error is returned. If more than one value exists
676 /// for the `module` and `name` pairs, then an error is returned as well.
677 pub fn get_one_by_name(&self, module: &str, name: &str) -> Result<Extern> {
678 let mut items = self.get_by_name(module, name);
679 let ret = items
680 .next()
681 .ok_or_else(|| anyhow!("no item named `{}` in `{}`", name, module))?;
682 if items.next().is_some() {
683 bail!("too many items named `{}` in `{}`", name, module);
684 }
685 Ok(ret.clone())
686 }
687
688 /// Returns the "default export" of a module.
689 ///
690 /// An export with an empty string is considered to be a "default export".
691 /// "_start" is also recognized for compatibility.
692 pub fn get_default(&self, module: &str) -> Result<Func> {
693 let mut items = self.get_by_name(module, "");
694 if let Some(external) = items.next() {
695 if items.next().is_some() {
696 bail!("too many items named `` in `{}`", module);
697 }
698 if let Extern::Func(func) = external {
699 return Ok(func.clone());
700 }
701 bail!("default export in '{}' is not a function", module);
702 }
703
704 // For compatibility, also recognize "_start".
705 let mut items = self.get_by_name(module, "_start");
706 if let Some(external) = items.next() {
707 if items.next().is_some() {
708 bail!("too many items named `_start` in `{}`", module);
709 }
710 if let Extern::Func(func) = external {
711 return Ok(func.clone());
712 }
713 bail!("`_start` in '{}' is not a function", module);
714 }
715
716 // Otherwise return a no-op function.
717 Ok(Func::new(
718 &self.store,
719 FuncType::new(Vec::new().into_boxed_slice(), Vec::new().into_boxed_slice()),
720 move |_, _, _| Ok(()),
721 ))
722 }
723}
724
725/// Modules can be interpreted either as Commands or Reactors.
726enum ModuleKind {
727 /// The instance is a Command, meaning an instance is created for each
728 /// exported function and lives for the duration of the function call.
729 Command,
730
731 /// The instance is a Reactor, meaning one instance is created which
732 /// may live across multiple calls.
733 Reactor,
734}
735
736impl ModuleKind {
737 /// Determine whether the given module is a Command or a Reactor.
738 fn categorize(module: &Module) -> Result<ModuleKind> {
739 let command_start = module.get_export("_start");
740 let reactor_start = module.get_export("_initialize");
741 match (command_start, reactor_start) {
742 (Some(command_start), None) => {
743 if let Some(_) = command_start.func() {
744 Ok(ModuleKind::Command)
745 } else {
746 bail!("`_start` must be a function")
747 }
748 }
749 (None, Some(reactor_start)) => {
750 if let Some(_) = reactor_start.func() {
751 Ok(ModuleKind::Reactor)
752 } else {
753 bail!("`_initialize` must be a function")
754 }
755 }
756 (None, None) => {
757 // Module declares neither of the recognized functions, so treat
758 // it as a reactor with no initialization function.
759 Ok(ModuleKind::Reactor)
760 }
761 (Some(_), Some(_)) => {
762 // Module declares itself to be both a Command and a Reactor.
763 bail!("Program cannot be both a Command and a Reactor")
764 }
765 }
766 }
767}