tinywasm_cli/
engine_flags.rs1use clap::{Args, ValueEnum};
2use eyre::{Result, bail};
3use tinywasm::{Engine, StackConfig, engine::FuelPolicy};
4
5#[derive(Args, Clone, Default)]
6pub struct EngineFlags {
7 #[arg(long, value_enum)]
9 pub fuel_policy: Option<FuelPolicyArg>,
10
11 #[arg(long)]
13 pub trap_on_oom: bool,
14
15 #[arg(long, value_enum)]
17 pub memory_backend: Option<MemoryBackendArg>,
18
19 #[arg(long, default_value_t = 64 * 1024)]
21 pub memory_page_chunk_size: usize,
22
23 #[arg(long, conflicts_with = "value_stack_dynamic")]
25 pub value_stack_size: Option<usize>,
26
27 #[arg(long, value_name = "INITIAL:MAX", conflicts_with = "value_stack_size")]
29 pub value_stack_dynamic: Option<StackSpec>,
30
31 #[arg(long, conflicts_with = "call_stack_dynamic")]
33 pub call_stack_size: Option<usize>,
34
35 #[arg(long, value_name = "INITIAL:MAX", conflicts_with = "call_stack_size")]
37 pub call_stack_dynamic: Option<StackSpec>,
38}
39
40#[derive(Clone, Copy, ValueEnum)]
41pub enum FuelPolicyArg {
42 PerInstruction,
43 Weighted,
44}
45
46#[derive(Clone, Copy, ValueEnum)]
47pub enum MemoryBackendArg {
48 Vec,
49 Paged,
50}
51
52#[derive(Clone)]
53pub struct StackSpec {
54 initial: usize,
55 max: usize,
56}
57
58impl core::str::FromStr for StackSpec {
59 type Err = String;
60
61 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
62 let (initial, max) = s.split_once(':').ok_or_else(|| "expected INITIAL:MAX".to_string())?;
63 let initial = initial.parse::<usize>().map_err(|e| format!("invalid initial stack size: {e}"))?;
64 let max = max.parse::<usize>().map_err(|e| format!("invalid max stack size: {e}"))?;
65 if initial > max {
66 return Err("initial stack size must be less than or equal to max stack size".to_string());
67 }
68 Ok(Self { initial, max })
69 }
70}
71
72impl StackSpec {
73 fn into_stack_config(self) -> StackConfig {
74 StackConfig::dynamic(self.initial, self.max)
75 }
76}
77
78impl EngineFlags {
79 pub fn build_engine(&self) -> Result<Engine> {
80 let mut config = tinywasm::engine::Config::new();
81
82 if let Some(fuel_policy) = self.fuel_policy {
83 config = config.with_fuel_policy(match fuel_policy {
84 FuelPolicyArg::PerInstruction => FuelPolicy::PerInstruction,
85 FuelPolicyArg::Weighted => FuelPolicy::Weighted,
86 });
87 }
88
89 if let Some(memory_backend) = self.memory_backend {
90 config = config.with_memory_backend(match memory_backend {
91 MemoryBackendArg::Vec => tinywasm::MemoryBackend::vec(),
92 MemoryBackendArg::Paged => {
93 if self.memory_page_chunk_size == 0 {
94 bail!("--memory-page-chunk-size must be greater than zero")
95 }
96 tinywasm::MemoryBackend::paged(self.memory_page_chunk_size)
97 }
98 });
99 }
100
101 if let Some(value_stack_size) = self.value_stack_size {
102 config = config.with_value_stack(StackConfig::fixed(value_stack_size));
103 }
104
105 if let Some(value_stack_dynamic) = self.value_stack_dynamic.clone() {
106 config = config.with_value_stack(value_stack_dynamic.into_stack_config());
107 }
108
109 if let Some(call_stack_size) = self.call_stack_size {
110 config = config.with_call_stack(StackConfig::fixed(call_stack_size));
111 }
112
113 if let Some(call_stack_dynamic) = self.call_stack_dynamic.clone() {
114 config = config.with_call_stack(call_stack_dynamic.into_stack_config());
115 }
116
117 if self.trap_on_oom {
118 config = config.with_trap_on_oom(true);
119 }
120
121 Ok(Engine::new(config))
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 #[test]
130 fn parses_stack_spec() {
131 let spec: StackSpec = "16:64".parse().unwrap();
132 let cfg = spec.into_stack_config();
133 assert_eq!(cfg.initial_size, 16);
134 assert_eq!(cfg.max_size, 64);
135 assert!(cfg.dynamic);
136 }
137
138 #[test]
139 fn builds_dynamic_stack_engine() {
140 let flags = EngineFlags {
141 value_stack_dynamic: Some("8:32".parse().unwrap()),
142 call_stack_dynamic: Some("4:12".parse().unwrap()),
143 ..Default::default()
144 };
145
146 let engine = flags.build_engine().unwrap();
147 assert!(engine.config().value_stack_32.dynamic);
148 assert_eq!(engine.config().value_stack_32.initial_size, 8);
149 assert_eq!(engine.config().value_stack_32.max_size, 32);
150 assert!(engine.config().call_stack.dynamic);
151 assert_eq!(engine.config().call_stack.initial_size, 4);
152 assert_eq!(engine.config().call_stack.max_size, 12);
153 }
154}