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>(executor: &E, test: &FeatureTest) -> ExecutionResult {
41 let mut engine = match ShapeEngine::new() {
42 Ok(e) => e,
43 Err(e) => return ExecutionResult::Error(format!("Engine init failed: {}", e)),
44 };
45
46 if let Err(e) = engine.load_stdlib() {
47 return ExecutionResult::Error(format!("Stdlib load failed: {}", e));
48 }
49
50 run_async(async {
51 match engine.execute_repl(executor, test.code).await {
53 Ok(result) => {
54 if !test.function.is_empty() && test.function != "main" {
55 let call = format!("{}()", test.function);
57 match engine.execute_repl(executor, &call).await {
58 Ok(r) => ExecutionResult::Success(format!("{:?}", r.value)),
59 Err(e) => ExecutionResult::Error(format!("{}", e)),
60 }
61 } else {
62 ExecutionResult::Success(format!("{:?}", result.value))
63 }
64 }
65 Err(e) => ExecutionResult::Error(format!("{}", e)),
66 }
67 })
68}
69
70pub struct InterpreterBackend;
73
74impl BackendExecutor for InterpreterBackend {
75 fn name(&self) -> &'static str {
76 "Interpreter (VM)"
77 }
78
79 fn execute(&self, test: &FeatureTest) -> ExecutionResult {
80 execute_with_executor(&crate::BytecodeExecutor::new(), test)
82 }
83
84 fn is_available(&self) -> bool {
85 true
86 }
87}
88
89pub struct VMBackend;
91
92impl BackendExecutor for VMBackend {
93 fn name(&self) -> &'static str {
94 "VM"
95 }
96
97 fn execute(&self, test: &FeatureTest) -> ExecutionResult {
98 execute_with_executor(&crate::BytecodeExecutor::new(), test)
99 }
100
101 fn is_available(&self) -> bool {
102 true
103 }
104}
105
106pub struct JITBackend;
108
109impl BackendExecutor for JITBackend {
110 fn name(&self) -> &'static str {
111 "JIT"
112 }
113
114 fn execute(&self, test: &FeatureTest) -> ExecutionResult {
115 for unsupported in self.unsupported_features() {
117 if test.covers.contains(unsupported) {
118 return ExecutionResult::NotSupported(unsupported);
119 }
120 }
121
122 execute_with_executor(&crate::BytecodeExecutor::new(), test)
125 }
126
127 fn is_available(&self) -> bool {
128 true }
130
131 fn unsupported_features(&self) -> &'static [&'static str] {
132 &["pattern_def", "stream_handler", "async_block", "try_catch"]
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 #[test]
142 fn test_interpreter_available() {
143 let backend = InterpreterBackend;
144 assert!(backend.is_available());
145 assert_eq!(backend.name(), "Interpreter (VM)");
146 }
147
148 #[test]
149 fn test_vm_available() {
150 let backend = VMBackend;
151 assert!(backend.is_available());
152 assert_eq!(backend.name(), "VM");
153 }
154
155 #[test]
156 fn test_jit_available() {
157 let backend = JITBackend;
158 assert!(backend.is_available());
159 assert_eq!(backend.name(), "JIT");
160 }
161
162 #[test]
163 fn test_jit_unsupported_features() {
164 let backend = JITBackend;
165 let unsupported = backend.unsupported_features();
166 assert!(unsupported.contains(&"pattern_def"));
167 assert!(unsupported.contains(&"stream_handler"));
168 }
169}