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
use anyhow::Error;
use serde::{Deserialize, Serialize};
use wasmer::{Module, Store};

/// Creates an instnace of [Store].
///
/// This function exists because we need to disable simd.
#[cfg(not(target_arch = "wasm32"))]
#[allow(unused_mut)]
pub(crate) fn new_store() -> Store {
    // Use empty enumset to disable simd.
    use enumset::EnumSet;
    use wasmer::{BaseTunables, CompilerConfig, EngineBuilder, Target, Triple};
    let mut set = EnumSet::new();

    // [TODO]: Should we use is_x86_feature_detected! macro instead?
    #[cfg(target_arch = "x86_64")]
    set.insert(wasmer::CpuFeature::SSE2);
    let target = Target::new(Triple::host(), set);

    let config = wasmer_compiler_cranelift::Cranelift::default();
    let mut engine = EngineBuilder::new(Box::new(config) as Box<dyn CompilerConfig>)
        .set_target(Some(target))
        .engine();
    let tunables = BaseTunables::for_target(engine.target());
    engine.set_tunables(tunables);

    Store::new(engine)
}

#[cfg(target_arch = "wasm32")]
fn new_store() -> Store {
    Store::default()
}

// A trait abstracts plugin's wasm compilation and instantiation.
// Depends on the caller, this could be a simple clone from existing module, or
// load from file system cache.
pub trait PluginModuleBytes {
    // Returns a name to the module, typically either path to the plugin or its
    // package name.
    fn get_module_name(&self) -> &str;
    // Returns a compiled wasmer::Module for the plugin module.
    fn compile_module(&self) -> Result<(Store, Module), Error>;
}

/// A struct for the plugin contains raw bytes can be compiled into Wasm Module.
#[derive(Debug, Clone, Eq, Serialize, Deserialize, PartialEq)]
pub struct RawPluginModuleBytes {
    plugin_name: String,
    bytes: Vec<u8>,
}

impl PluginModuleBytes for RawPluginModuleBytes {
    fn get_module_name(&self) -> &str {
        &self.plugin_name
    }

    fn compile_module(&self) -> Result<(Store, Module), Error> {
        let store = new_store();
        let module = Module::new(&store, &self.bytes)?;
        Ok((store, module))
    }
}

impl RawPluginModuleBytes {
    pub fn new(identifier: String, bytes: Vec<u8>) -> Self {
        Self {
            plugin_name: identifier,
            bytes,
        }
    }
}

/// A struct for the plugin contains pre-compiled binary.
/// This is for the cases would like to reuse the compiled module, or either
/// load from FileSystemCache.
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct CompiledPluginModuleBytes {
    plugin_name: String,
    #[serde(skip)]
    bytes: Option<wasmer::Module>,
    #[serde(skip)]
    store: Option<wasmer::Store>,
}

impl Eq for CompiledPluginModuleBytes {}

impl Clone for CompiledPluginModuleBytes {
    fn clone(&self) -> Self {
        Self {
            plugin_name: self.plugin_name.clone(),
            bytes: self.bytes.clone(),
            store: Some(Store::new(
                self.store
                    .as_ref()
                    .expect("Store should be available")
                    .engine()
                    .clone(),
            )),
        }
    }
}

impl CompiledPluginModuleBytes {
    pub fn new(identifier: String, bytes: wasmer::Module, store: wasmer::Store) -> Self {
        Self {
            plugin_name: identifier,
            bytes: Some(bytes),
            store: Some(store),
        }
    }
}

// Allow to `pre` compile wasm module when there is a raw bytes, want to avoid
// to skip the compilation step per each trasform.
impl From<RawPluginModuleBytes> for CompiledPluginModuleBytes {
    fn from(raw: RawPluginModuleBytes) -> Self {
        let (store, module) = raw.compile_module().unwrap();
        Self::new(raw.plugin_name, module, store)
    }
}

impl PluginModuleBytes for CompiledPluginModuleBytes {
    fn get_module_name(&self) -> &str {
        &self.plugin_name
    }

    fn compile_module(&self) -> Result<(Store, Module), Error> {
        Ok((
            Store::new(
                self.store
                    .as_ref()
                    .expect("Store should be available")
                    .engine()
                    .clone(),
            ),
            self.bytes
                .as_ref()
                .expect("Module should be available")
                .clone(),
        ))
    }
}