shape_vm/feature_tests/
backends.rs1use super::FeatureTest;
7use super::parity::ExecutionResult;
8use shape_runtime::engine::{ProgramExecutor, ShapeEngine};
9
10pub trait BackendExecutor: Send + Sync {
12 fn name(&self) -> &'static str;
14
15 fn execute(&self, test: &FeatureTest) -> ExecutionResult;
17
18 fn is_available(&self) -> bool;
20
21 fn unsupported_features(&self) -> &'static [&'static str] {
23 &[]
24 }
25}
26
27fn run_async<F, T>(future: F) -> T
29where
30 F: std::future::Future<Output = T>,
31{
32 tokio::runtime::Builder::new_current_thread()
33 .enable_all()
34 .build()
35 .unwrap()
36 .block_on(future)
37}
38
39fn execute_with_executor<E: ProgramExecutor>(
41 executor: &mut E,
42 test: &FeatureTest,
43) -> ExecutionResult {
44 let mut engine = match ShapeEngine::new() {
45 Ok(e) => e,
46 Err(e) => return ExecutionResult::Error(format!("Engine init failed: {}", e)),
47 };
48
49 if let Err(e) = engine.load_stdlib() {
50 return ExecutionResult::Error(format!("Stdlib load failed: {}", e));
51 }
52
53 run_async(async {
54 match engine.execute_repl(executor, test.code).await {
56 Ok(result) => {
57 if !test.function.is_empty() && test.function != "main" {
58 let call = format!("{}()", test.function);
60 match engine.execute_repl(executor, &call).await {
61 Ok(r) => ExecutionResult::Success(format!("{:?}", r.value)),
62 Err(e) => ExecutionResult::Error(format!("{}", e)),
63 }
64 } else {
65 ExecutionResult::Success(format!("{:?}", result.value))
66 }
67 }
68 Err(e) => ExecutionResult::Error(format!("{}", e)),
69 }
70 })
71}
72
73pub struct InterpreterBackend;
76
77impl BackendExecutor for InterpreterBackend {
78 fn name(&self) -> &'static str {
79 "Interpreter (VM)"
80 }
81
82 fn execute(&self, test: &FeatureTest) -> ExecutionResult {
83 execute_with_executor(&mut crate::BytecodeExecutor::new(), test)
85 }
86
87 fn is_available(&self) -> bool {
88 true
89 }
90}
91
92pub struct VMBackend;
94
95impl BackendExecutor for VMBackend {
96 fn name(&self) -> &'static str {
97 "VM"
98 }
99
100 fn execute(&self, test: &FeatureTest) -> ExecutionResult {
101 execute_with_executor(&mut crate::BytecodeExecutor::new(), test)
102 }
103
104 fn is_available(&self) -> bool {
105 true
106 }
107}
108
109pub struct JITBackend;
111
112impl BackendExecutor for JITBackend {
113 fn name(&self) -> &'static str {
114 "JIT"
115 }
116
117 fn execute(&self, test: &FeatureTest) -> ExecutionResult {
118 for unsupported in self.unsupported_features() {
120 if test.covers.contains(unsupported) {
121 return ExecutionResult::NotSupported(unsupported);
122 }
123 }
124
125 execute_with_executor(&mut crate::BytecodeExecutor::new(), test)
128 }
129
130 fn is_available(&self) -> bool {
131 true }
133
134 fn unsupported_features(&self) -> &'static [&'static str] {
135 &["pattern_def", "stream_handler", "async_block", "try_catch"]
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143
144 #[test]
145 fn test_interpreter_available() {
146 let backend = InterpreterBackend;
147 assert!(backend.is_available());
148 assert_eq!(backend.name(), "Interpreter (VM)");
149 }
150
151 #[test]
152 fn test_vm_available() {
153 let backend = VMBackend;
154 assert!(backend.is_available());
155 assert_eq!(backend.name(), "VM");
156 }
157
158 #[test]
159 fn test_jit_available() {
160 let backend = JITBackend;
161 assert!(backend.is_available());
162 assert_eq!(backend.name(), "JIT");
163 }
164
165 #[test]
166 fn test_jit_unsupported_features() {
167 let backend = JITBackend;
168 let unsupported = backend.unsupported_features();
169 assert!(unsupported.contains(&"pattern_def"));
170 assert!(unsupported.contains(&"stream_handler"));
171 }
172}