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
//! WASM engine configuration and AOT compilation.
//!
//! This module provides the core wasmtime engine with settings optimized
//! for the Barbacane plugin runtime.
use wasmtime::{Config, Engine, Module, OptLevel};
use crate::error::WasmError;
use crate::limits::PluginLimits;
/// A compiled WASM module ready for instantiation.
#[derive(Clone)]
pub struct CompiledModule {
module: Module,
/// The plugin name this module belongs to.
pub name: String,
/// The plugin version.
pub version: String,
/// Whether this plugin needs the request body in `on_request`.
pub body_access: bool,
}
impl CompiledModule {
/// Get a reference to the underlying wasmtime module.
pub fn module(&self) -> &Module {
&self.module
}
}
/// The WASM engine that compiles and manages plugin modules.
pub struct WasmEngine {
engine: Engine,
limits: PluginLimits,
}
impl WasmEngine {
/// Create a new WASM engine with default configuration.
pub fn new() -> Result<Self, WasmError> {
Self::with_limits(PluginLimits::default())
}
/// Create a new WASM engine with custom resource limits.
pub fn with_limits(limits: PluginLimits) -> Result<Self, WasmError> {
let mut config = Config::new();
// Disable async support for now - we use synchronous instantiation
// Can re-enable when we need async host functions
config.async_support(false);
// Use Cranelift for AOT compilation with speed optimization
config.cranelift_opt_level(OptLevel::Speed);
// Enable fuel consumption for execution time limiting
config.consume_fuel(true);
// Configure memory settings
config.max_wasm_stack(limits.max_stack_bytes);
// Enable reference types (for modern WASM features)
config.wasm_reference_types(true);
// Enable bulk memory operations
config.wasm_bulk_memory(true);
// Enable multi-value returns
config.wasm_multi_value(true);
// Disable WASM features we don't need
config.wasm_threads(false);
let engine = Engine::new(&config).map_err(|e| WasmError::EngineCreation(e.to_string()))?;
Ok(Self { engine, limits })
}
/// Get a reference to the underlying wasmtime engine.
pub fn engine(&self) -> &Engine {
&self.engine
}
/// Get the configured resource limits.
pub fn limits(&self) -> &PluginLimits {
&self.limits
}
/// AOT-compile a WASM module from bytes.
///
/// The compiled module can be instantiated multiple times efficiently.
pub fn compile(
&self,
wasm_bytes: &[u8],
name: String,
version: String,
body_access: bool,
) -> Result<CompiledModule, WasmError> {
let module = Module::new(&self.engine, wasm_bytes)
.map_err(|e| WasmError::Compilation(e.to_string()))?;
Ok(CompiledModule {
module,
name,
version,
body_access,
})
}
/// Validate a WASM module without fully compiling it.
///
/// This is faster than full compilation and useful for quick validation.
pub fn validate(&self, wasm_bytes: &[u8]) -> Result<(), WasmError> {
Module::validate(&self.engine, wasm_bytes)
.map_err(|e| WasmError::Compilation(e.to_string()))
}
}
impl Default for WasmEngine {
fn default() -> Self {
Self::new().expect("failed to create default WASM engine")
}
}
#[cfg(test)]
mod tests {
use super::*;
// Minimal valid WASM module (empty module)
const MINIMAL_WASM: &[u8] = &[
0x00, 0x61, 0x73, 0x6d, // magic number
0x01, 0x00, 0x00, 0x00, // version
];
#[test]
fn create_engine() {
let engine = WasmEngine::new();
assert!(engine.is_ok());
}
#[test]
fn create_engine_with_limits() {
let limits = PluginLimits::default().with_memory(32 * 1024 * 1024);
let engine = WasmEngine::with_limits(limits);
assert!(engine.is_ok());
}
#[test]
fn validate_minimal_wasm() {
let engine = WasmEngine::new().unwrap();
assert!(engine.validate(MINIMAL_WASM).is_ok());
}
#[test]
fn validate_invalid_wasm() {
let engine = WasmEngine::new().unwrap();
let invalid = &[0x00, 0x00, 0x00, 0x00];
assert!(engine.validate(invalid).is_err());
}
#[test]
fn compile_minimal_wasm() {
let engine = WasmEngine::new().unwrap();
let result = engine.compile(MINIMAL_WASM, "test".into(), "1.0.0".into(), false);
assert!(result.is_ok());
}
#[test]
fn compiled_module_has_metadata() {
let engine = WasmEngine::new().unwrap();
let module = engine
.compile(MINIMAL_WASM, "my-plugin".into(), "2.1.0".into(), false)
.unwrap();
assert_eq!(module.name, "my-plugin");
assert_eq!(module.version, "2.1.0");
}
}