marine_core/module/
wit_instance.rs

1/*
2 * Copyright 2020 Fluence Labs Limited
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17use super::wit_prelude::*;
18use super::marine_module::MModule;
19use super::IRecordType;
20use crate::MResult;
21
22use marine_wasm_backend_traits::AsContextMut;
23use marine_wasm_backend_traits::STANDARD_MEMORY_EXPORT_NAME;
24use marine_wasm_backend_traits::DelayedContextLifetime;
25use marine_wasm_backend_traits::WasmBackend;
26use marine_wasm_backend_traits::Instance;
27
28use marine_it_interfaces::MITInterfaces;
29use marine_it_interfaces::ITAstType;
30
31use wasmer_it::interpreter::wasm;
32use wasmer_it::interpreter::wasm::structures::LocalImportIndex;
33use wasmer_it::interpreter::wasm::structures::Memory;
34use wasmer_it::interpreter::wasm::structures::TypedIndex;
35
36use std::collections::HashMap;
37use std::sync::Arc;
38
39pub type MRecordTypes = HashMap<u64, Arc<IRecordType>>;
40
41/// Contains all import and export functions that could be called from IT context by call-core.
42#[derive(Clone)]
43pub(super) struct ITInstance<WB: WasmBackend> {
44    /// IT functions indexed by id.
45    funcs: HashMap<usize, WITFunction<WB>>,
46
47    /// IT memories.
48    memories: Vec<<WB as WasmBackend>::Memory>,
49
50    /// All record types that instance contains.
51    record_types_by_id: MRecordTypes,
52}
53
54impl<WB: WasmBackend> ITInstance<WB> {
55    pub(super) fn new(
56        wasm_instance: &<WB as WasmBackend>::Instance,
57        store: &mut <WB as WasmBackend>::Store,
58        module_name: &str,
59        wit: &MITInterfaces<'_>,
60        modules: &HashMap<String, MModule<WB>>,
61    ) -> MResult<Self> {
62        let mut exports = Self::extract_raw_exports(wasm_instance, store, wit)?;
63        let imports = Self::extract_imports(module_name, modules, wit, exports.len())?;
64        let memories = Self::extract_memories(wasm_instance, store);
65
66        exports.extend(imports);
67        let funcs = exports;
68
69        let record_types_by_id = Self::extract_record_types(wit);
70
71        Ok(Self {
72            funcs,
73            memories,
74            record_types_by_id,
75        })
76    }
77
78    fn extract_raw_exports(
79        wasm_instance: &<WB as WasmBackend>::Instance,
80        store: &mut <WB as WasmBackend>::Store,
81        it: &MITInterfaces<'_>,
82    ) -> MResult<HashMap<usize, WITFunction<WB>>> {
83        it.exports()
84            .enumerate()
85            .map(|(export_id, export)| {
86                let export_func = wasm_instance.get_function(store, export.name)?;
87                Ok((
88                    export_id,
89                    WITFunction::from_export(store, export_func, export.name.to_string())?,
90                ))
91            })
92            .collect()
93    }
94
95    /// Extracts only those imports that don't have implementations.
96    fn extract_imports(
97        module_name: &str,
98        modules: &HashMap<String, MModule<WB>>,
99        wit: &MITInterfaces<'_>,
100        start_index: usize,
101    ) -> MResult<HashMap<usize, WITFunction<WB>>> {
102        wit.imports()
103            .filter(|import| {
104                wit.adapter_types_by_core_type(import.function_type)
105                    .is_some()
106            })
107            .enumerate()
108            .map(|(idx, import)| match modules.get(import.namespace) {
109                Some(module) => {
110                    use wasmer_it::ast::Type;
111                    let (arguments, output_types) =
112                        match wit.type_by_idx_r(import.function_type - 2)? {
113                            Type::Function {
114                                arguments,
115                                output_types,
116                            } => (arguments.clone(), output_types.clone()),
117                            ty => {
118                                return Err(MError::IncorrectWIT(format!(
119                                    "IT should has Type::Function, but {:?} met",
120                                    ty
121                                )))
122                            }
123                        };
124
125                    let func = WITFunction::from_import(
126                        module,
127                        module_name,
128                        import.name,
129                        arguments,
130                        output_types,
131                    )?;
132
133                    Ok((start_index + idx, func))
134                }
135                None => Err(MError::NoSuchModule(import.namespace.to_string())),
136            })
137            .collect::<MResult<HashMap<_, _>>>()
138    }
139
140    fn extract_memories(
141        wasm_instance: &<WB as WasmBackend>::Instance,
142        store: &mut <WB as WasmBackend>::Store,
143    ) -> Vec<<WB as WasmBackend>::Memory> {
144        use marine_wasm_backend_traits::Export::Memory;
145
146        let mut memories = wasm_instance
147            .export_iter(store.as_context_mut())
148            .filter_map(|(_, export)| match export {
149                Memory(memory) => Some(memory),
150                _ => None,
151            })
152            .collect::<Vec<_>>();
153
154        if let Ok(memory) = wasm_instance.get_memory(store, STANDARD_MEMORY_EXPORT_NAME) {
155            memories.push(memory);
156        }
157
158        memories
159    }
160
161    fn extract_record_types(wit: &MITInterfaces<'_>) -> MRecordTypes {
162        let (record_types_by_id, _) = wit.types().fold(
163            (HashMap::new(), 0u64),
164            |(mut record_types_by_id, id), ty| {
165                match ty {
166                    ITAstType::Record(record_type) => {
167                        record_types_by_id.insert(id, record_type.clone());
168                    }
169                    ITAstType::Function { .. } => {}
170                };
171                (record_types_by_id, id + 1)
172            },
173        );
174
175        record_types_by_id
176    }
177}
178
179impl<WB: WasmBackend>
180    wasm::structures::Instance<
181        ITExport,
182        WITFunction<WB>,
183        <WB as WasmBackend>::Memory,
184        <WB as WasmBackend>::MemoryView,
185        DelayedContextLifetime<WB>,
186    > for ITInstance<WB>
187{
188    fn export(&self, _export_name: &str) -> Option<&ITExport> {
189        // exports aren't used in this version of IT
190        None
191    }
192
193    fn local_or_import<I: TypedIndex + LocalImportIndex>(
194        &self,
195        index: I,
196    ) -> Option<&WITFunction<WB>> {
197        self.funcs.get(&index.index())
198    }
199
200    fn memory(&self, index: usize) -> Option<&<WB as WasmBackend>::Memory> {
201        if index >= self.memories.len() {
202            None
203        } else {
204            Some(&self.memories[index])
205        }
206    }
207
208    fn memory_view(&self, index: usize) -> Option<<WB as WasmBackend>::MemoryView> {
209        if index >= self.memories.len() {
210            return None;
211        }
212
213        let memory = &self.memories[index];
214        let view: <WB as WasmBackend>::MemoryView = memory.view();
215        Some(view)
216    }
217
218    fn wit_record_by_id(&self, index: u64) -> Option<&Arc<IRecordType>> {
219        self.record_types_by_id.get(&index)
220    }
221}