1use crate::debugger::VmDebuggerHandle;
2use intuicio_core::{
3 context::Context,
4 function::FunctionBody,
5 registry::Registry,
6 script::{ScriptExpression, ScriptFunctionGenerator, ScriptHandle, ScriptOperation},
7};
8use typid::ID;
9
10pub type VmScopeSymbol = ID<()>;
11
12pub struct VmScope<'a, SE: ScriptExpression> {
13 handle: ScriptHandle<'a, SE>,
14 symbol: VmScopeSymbol,
15 position: usize,
16 child: Option<Box<Self>>,
17 debugger: Option<VmDebuggerHandle<SE>>,
18}
19
20impl<'a, SE: ScriptExpression> VmScope<'a, SE> {
21 pub fn new(handle: ScriptHandle<'a, SE>, symbol: VmScopeSymbol) -> Self {
22 Self {
23 handle,
24 symbol,
25 position: 0,
26 child: None,
27 debugger: None,
28 }
29 }
30
31 pub fn with_debugger(mut self, debugger: Option<VmDebuggerHandle<SE>>) -> Self {
32 self.debugger = debugger;
33 self
34 }
35
36 pub fn symbol(&self) -> VmScopeSymbol {
37 self.symbol
38 }
39
40 pub fn position(&self) -> usize {
41 self.position
42 }
43
44 pub fn has_completed(&self) -> bool {
45 self.position >= self.handle.len()
46 }
47
48 pub fn run(&mut self, context: &mut Context, registry: &Registry) {
49 while self.step(context, registry) {}
50 }
51
52 pub fn step(&mut self, context: &mut Context, registry: &Registry) -> bool {
53 if let Some(child) = &mut self.child {
54 if child.step(context, registry) {
55 return true;
56 } else {
57 self.child = None;
58 }
59 }
60 if self.position == 0 {
61 if let Some(debugger) = self.debugger.as_ref() {
62 if let Ok(mut debugger) = debugger.try_write() {
63 debugger.on_enter_scope(self, context, registry);
64 }
65 }
66 }
67 let result = if let Some(operation) = self.handle.get(self.position) {
68 if let Some(debugger) = self.debugger.as_ref() {
69 if let Ok(mut debugger) = debugger.try_write() {
70 debugger.on_enter_operation(self, operation, self.position, context, registry);
71 }
72 }
73 let position = self.position;
74 let result = match operation {
75 ScriptOperation::None => {
76 self.position += 1;
77 true
78 }
79 ScriptOperation::Expression { expression } => {
80 expression.evaluate(context, registry);
81 self.position += 1;
82 true
83 }
84 ScriptOperation::DefineRegister { query } => {
85 let handle = registry
86 .types()
87 .find(|handle| query.is_valid(handle))
88 .unwrap_or_else(|| {
89 panic!(
90 "Could not define register for non-existent type: {:#?}",
91 query
92 )
93 });
94 unsafe {
95 context
96 .registers()
97 .push_register_raw(handle.type_hash(), *handle.layout())
98 };
99 self.position += 1;
100 true
101 }
102 ScriptOperation::DropRegister { index } => {
103 let index = context.absolute_register_index(*index);
104 context
105 .registers()
106 .access_register(index)
107 .unwrap_or_else(|| {
108 panic!("Could not access non-existent register: {}", index)
109 })
110 .free();
111 self.position += 1;
112 true
113 }
114 ScriptOperation::PushFromRegister { index } => {
115 let index = context.absolute_register_index(*index);
116 let (stack, registers) = context.stack_and_registers();
117 let mut register = registers.access_register(index).unwrap_or_else(|| {
118 panic!("Could not access non-existent register: {}", index)
119 });
120 if !stack.push_from_register(&mut register) {
121 panic!("Could not push data from register: {}", index);
122 }
123 self.position += 1;
124 true
125 }
126 ScriptOperation::PopToRegister { index } => {
127 let index = context.absolute_register_index(*index);
128 let (stack, registers) = context.stack_and_registers();
129 let mut register = registers.access_register(index).unwrap_or_else(|| {
130 panic!("Could not access non-existent register: {}", index)
131 });
132 if !stack.pop_to_register(&mut register) {
133 panic!("Could not pop data to register: {}", index);
134 }
135 self.position += 1;
136 true
137 }
138 ScriptOperation::MoveRegister { from, to } => {
139 let from = context.absolute_register_index(*from);
140 let to = context.absolute_register_index(*to);
141 let (mut source, mut target) = context
142 .registers()
143 .access_registers_pair(from, to)
144 .unwrap_or_else(|| {
145 panic!(
146 "Could not access non-existent registers pair: {} and {}",
147 from, to
148 )
149 });
150 source.move_to(&mut target);
151 self.position += 1;
152 true
153 }
154 ScriptOperation::CallFunction { query } => {
155 let handle = registry
156 .functions()
157 .find(|handle| query.is_valid(handle.signature()))
158 .unwrap_or_else(|| {
159 panic!("Could not call non-existent function: {:#?}", query)
160 });
161 handle.invoke(context, registry);
162 self.position += 1;
163 true
164 }
165 ScriptOperation::BranchScope {
166 scope_success,
167 scope_failure,
168 } => {
169 if context.stack().pop::<bool>().unwrap() {
170 self.child = Some(Box::new(
171 Self::new(scope_success.clone(), self.symbol)
172 .with_debugger(self.debugger.clone()),
173 ));
174 } else if let Some(scope_failure) = scope_failure {
175 self.child = Some(Box::new(
176 Self::new(scope_failure.clone(), self.symbol)
177 .with_debugger(self.debugger.clone()),
178 ));
179 }
180 self.position += 1;
181 true
182 }
183 ScriptOperation::LoopScope { scope } => {
184 if !context.stack().pop::<bool>().unwrap() {
185 self.position += 1;
186 } else {
187 self.child = Some(Box::new(
188 Self::new(scope.clone(), self.symbol)
189 .with_debugger(self.debugger.clone()),
190 ));
191 }
192 true
193 }
194 ScriptOperation::PushScope { scope } => {
195 context.store_registers();
196 self.child = Some(Box::new(
197 Self::new(scope.clone(), self.symbol).with_debugger(self.debugger.clone()),
198 ));
199 self.position += 1;
200 true
201 }
202 ScriptOperation::PopScope => {
203 context.restore_registers();
204 self.position = self.handle.len();
205 false
206 }
207 ScriptOperation::ContinueScopeConditionally => {
208 let result = context.stack().pop::<bool>().unwrap();
209 if result {
210 self.position += 1;
211 } else {
212 self.position = self.handle.len();
213 }
214 result
215 }
216 };
217 if let Some(debugger) = self.debugger.as_ref() {
218 if let Ok(mut debugger) = debugger.try_write() {
219 debugger.on_exit_operation(self, operation, position, context, registry);
220 }
221 }
222 result
223 } else {
224 false
225 };
226 if !result || self.position >= self.handle.len() {
227 if let Some(debugger) = self.debugger.as_ref() {
228 if let Ok(mut debugger) = debugger.try_write() {
229 debugger.on_exit_scope(self, context, registry);
230 }
231 }
232 }
233 result
234 }
235}
236
237impl<SE: ScriptExpression + 'static> ScriptFunctionGenerator<SE> for VmScope<'static, SE> {
238 type Input = Option<VmDebuggerHandle<SE>>;
239 type Output = VmScopeSymbol;
240
241 fn generate_function_body(
242 script: ScriptHandle<'static, SE>,
243 debugger: Self::Input,
244 ) -> Option<(FunctionBody, Self::Output)> {
245 let symbol = VmScopeSymbol::new();
246 Some((
247 FunctionBody::closure(move |context, registry| {
248 Self::new(script.clone(), symbol)
249 .with_debugger(debugger.clone())
250 .run(context, registry);
251 }),
252 symbol,
253 ))
254 }
255}
256
257#[cfg(test)]
258mod tests {
259 use crate::scope::*;
260 use intuicio_core::prelude::*;
261
262 #[test]
263 fn test_vm_scope() {
264 let i32_handle = NativeStructBuilder::new::<i32>()
265 .build()
266 .into_type()
267 .into_handle();
268 let mut registry = Registry::default().with_basic_types();
269 registry.add_function(Function::new(
270 FunctionSignature::new("add")
271 .with_input(FunctionParameter::new("a", i32_handle.clone()))
272 .with_input(FunctionParameter::new("b", i32_handle.clone()))
273 .with_output(FunctionParameter::new("result", i32_handle.clone())),
274 FunctionBody::closure(|context, _| {
275 let a = context.stack().pop::<i32>().unwrap();
276 let b = context.stack().pop::<i32>().unwrap();
277 context.stack().push(a + b);
278 }),
279 ));
280 registry.add_function(
281 VmScope::<()>::generate_function(
282 &ScriptFunction {
283 signature: ScriptFunctionSignature {
284 meta: None,
285 name: "add_script".to_owned(),
286 module_name: None,
287 type_query: None,
288 visibility: Visibility::Public,
289 inputs: vec![
290 ScriptFunctionParameter {
291 meta: None,
292 name: "a".to_owned(),
293 type_query: TypeQuery::of::<i32>(),
294 },
295 ScriptFunctionParameter {
296 meta: None,
297 name: "b".to_owned(),
298 type_query: TypeQuery::of::<i32>(),
299 },
300 ],
301 outputs: vec![ScriptFunctionParameter {
302 meta: None,
303 name: "result".to_owned(),
304 type_query: TypeQuery::of::<i32>(),
305 }],
306 },
307 script: ScriptBuilder::<()>::default()
308 .define_register(TypeQuery::of::<i32>())
309 .pop_to_register(0)
310 .push_from_register(0)
311 .call_function(FunctionQuery {
312 name: Some("add".into()),
313 ..Default::default()
314 })
315 .build(),
316 },
317 ®istry,
318 None,
319 )
320 .unwrap()
321 .0,
322 );
323 registry.add_type_handle(i32_handle);
324 let mut context = Context::new(10240, 10240);
325 let (result,) = registry
326 .find_function(FunctionQuery {
327 name: Some("add".into()),
328 ..Default::default()
329 })
330 .unwrap()
331 .call::<(i32,), _>(&mut context, ®istry, (40, 2), true);
332 assert_eq!(result, 42);
333 assert_eq!(context.stack().position(), 0);
334 assert_eq!(context.registers().position(), 0);
335 let (result,) = registry
336 .find_function(FunctionQuery {
337 name: Some("add_script".into()),
338 ..Default::default()
339 })
340 .unwrap()
341 .call::<(i32,), _>(&mut context, ®istry, (40, 2), true);
342 assert_eq!(result, 42);
343 assert_eq!(context.stack().position(), 0);
344 assert_eq!(context.registers().position(), 0);
345 }
346}