Skip to main content

datex_core/runtime/execution/
mod.rs

1use crate::{
2    global::protocol_structures::instructions::*,
3    libs::core::{CoreLibPointerId, get_core_lib_value},
4    runtime::{
5        RuntimeInternal,
6        execution::{
7            context::{ExecutionMode, RemoteExecutionContext},
8            execution_loop::interrupts::{
9                ExternalExecutionInterrupt, InterruptResult,
10            },
11        },
12    },
13    traits::apply::Apply,
14    values::{pointer::PointerAddress, value_container::ValueContainer},
15};
16
17use crate::prelude::*;
18use core::{result::Result, unreachable};
19pub use errors::*;
20pub use execution_input::{ExecutionInput, ExecutionOptions};
21pub use memory_dump::*;
22
23pub mod context;
24mod errors;
25mod execution_input;
26pub mod execution_loop;
27pub mod macros;
28mod memory_dump;
29
30#[cfg(all(test, feature = "std"))]
31mod test_remote_execution;
32
33pub fn execute_dxb_sync(
34    input: ExecutionInput,
35) -> Result<Option<ValueContainer>, ExecutionError> {
36    let runtime_internal = input.runtime.clone();
37    let (interrupt_provider, execution_loop) = input.execution_loop();
38
39    for output in execution_loop {
40        match output? {
41            ExternalExecutionInterrupt::Result(result) => return Ok(result),
42            ExternalExecutionInterrupt::ResolvePointer(address) => {
43                interrupt_provider.provide_result(
44                    InterruptResult::ResolvedValue(get_pointer_value(
45                        &runtime_internal,
46                        address,
47                    )?),
48                )
49            }
50            ExternalExecutionInterrupt::ResolveLocalPointer(address) => {
51                // TODO #401: in the future, local pointer addresses should be relative to the block sender, not the local runtime
52                interrupt_provider.provide_result(
53                    InterruptResult::ResolvedValue(get_local_pointer_value(
54                        &runtime_internal,
55                        address,
56                    )?),
57                );
58            }
59            ExternalExecutionInterrupt::ResolveInternalPointer(address) => {
60                interrupt_provider.provide_result(
61                    InterruptResult::ResolvedValue(Some(
62                        get_internal_pointer_value(&runtime_internal, address)?,
63                    )),
64                );
65            }
66            ExternalExecutionInterrupt::Apply(callee, args) => {
67                let res = handle_apply(&callee, &args)?;
68                interrupt_provider
69                    .provide_result(InterruptResult::ResolvedValue(res));
70            }
71            _ => return Err(ExecutionError::RequiresAsyncExecution),
72        }
73    }
74
75    Err(ExecutionError::RequiresAsyncExecution)
76}
77
78pub async fn execute_dxb(
79    input: ExecutionInput<'_>,
80) -> Result<Option<ValueContainer>, ExecutionError> {
81    let runtime_internal = input.runtime.clone();
82    let (interrupt_provider, execution_loop) = input.execution_loop();
83
84    for output in execution_loop {
85        match output? {
86            ExternalExecutionInterrupt::Result(result) => return Ok(result),
87            ExternalExecutionInterrupt::ResolvePointer(address) => {
88                interrupt_provider.provide_result(
89                    InterruptResult::ResolvedValue(get_pointer_value(
90                        &runtime_internal,
91                        address,
92                    )?),
93                );
94            }
95            ExternalExecutionInterrupt::ResolveLocalPointer(address) => {
96                // TODO #402: in the future, local pointer addresses should be relative to the block sender, not the local runtime
97                interrupt_provider.provide_result(
98                    InterruptResult::ResolvedValue(get_local_pointer_value(
99                        &runtime_internal,
100                        address,
101                    )?),
102                );
103            }
104            ExternalExecutionInterrupt::ResolveInternalPointer(address) => {
105                interrupt_provider.provide_result(
106                    InterruptResult::ResolvedValue(Some(
107                        get_internal_pointer_value(&runtime_internal, address)?,
108                    )),
109                );
110            }
111            ExternalExecutionInterrupt::RemoteExecution(receivers, body) => {
112                if let Some(runtime) = &runtime_internal {
113                    // assert that receivers is a single endpoint
114                    // TODO #230: support advanced receivers
115                    let receiver_endpoint = receivers
116                        .to_value()
117                        .borrow()
118                        .cast_to_endpoint()
119                        .unwrap();
120                    let mut remote_execution_context =
121                        RemoteExecutionContext::new(
122                            receiver_endpoint,
123                            ExecutionMode::Static,
124                        );
125                    let res = RuntimeInternal::execute_remote(
126                        runtime.clone(),
127                        &mut remote_execution_context,
128                        body,
129                    )
130                    .await?;
131                    interrupt_provider
132                        .provide_result(InterruptResult::ResolvedValue(res));
133                } else {
134                    return Err(ExecutionError::RequiresRuntime);
135                }
136            }
137            ExternalExecutionInterrupt::Apply(callee, args) => {
138                let res = handle_apply(&callee, &args)?;
139                interrupt_provider
140                    .provide_result(InterruptResult::ResolvedValue(res));
141            }
142        }
143    }
144
145    unreachable!("Execution loop should always return a result");
146}
147
148fn handle_apply(
149    callee: &ValueContainer,
150    args: &[ValueContainer],
151) -> Result<Option<ValueContainer>, ExecutionError> {
152    // callee is guaranteed to be Some here
153    // apply_single if one arg, apply otherwise
154    Ok(if args.len() == 1 {
155        callee.apply_single(&args[0])?
156    } else {
157        callee.apply(args)?
158    })
159}
160
161fn get_pointer_value(
162    runtime_internal: &Option<Rc<RuntimeInternal>>,
163    address: RawFullPointerAddress,
164) -> Result<Option<ValueContainer>, ExecutionError> {
165    if let Some(runtime) = &runtime_internal {
166        let memory = runtime.memory.borrow();
167        let resolved_address =
168            memory.get_pointer_address_from_raw_full_address(address);
169        // convert slot to InternalSlot enum
170        Ok(memory
171            .get_reference(&resolved_address)
172            .map(|r| ValueContainer::Reference(r.clone())))
173    } else {
174        Err(ExecutionError::RequiresRuntime)
175    }
176}
177
178fn get_internal_pointer_value(
179    runtime_internal: &Option<Rc<RuntimeInternal>>,
180    address: RawInternalPointerAddress,
181) -> Result<ValueContainer, ExecutionError> {
182    // first try to get from memory
183    if let Some(runtime_internal) = runtime_internal
184        && let Ok(core_lib_id) =
185            get_internal_pointer_value_from_memory(runtime_internal, &address)
186    {
187        return Ok(core_lib_id);
188    }
189
190    let core_lib_id =
191        CoreLibPointerId::try_from(&PointerAddress::Internal(address.id));
192    core_lib_id
193        .map_err(|_| ExecutionError::ReferenceNotFound)
194        .map(|id| {
195            get_core_lib_value(id).ok_or(ExecutionError::ReferenceNotFound)
196        })?
197}
198
199fn get_internal_pointer_value_from_memory(
200    runtime_internal: &Rc<RuntimeInternal>,
201    address: &RawInternalPointerAddress,
202) -> Result<ValueContainer, ExecutionError> {
203    let pointer_address = PointerAddress::Internal(address.id);
204    let memory = runtime_internal.memory.borrow();
205    if let Some(reference) = memory.get_reference(&pointer_address) {
206        Ok(ValueContainer::Reference(reference.clone()))
207    } else {
208        Err(ExecutionError::ReferenceNotFound)
209    }
210}
211
212fn get_local_pointer_value(
213    runtime_internal: &Option<Rc<RuntimeInternal>>,
214    address: RawLocalPointerAddress,
215) -> Result<Option<ValueContainer>, ExecutionError> {
216    if let Some(runtime) = &runtime_internal {
217        // convert slot to InternalSlot enum
218        Ok(runtime
219            .memory
220            .borrow()
221            .get_reference(&PointerAddress::Local(address.id))
222            .map(|r| ValueContainer::Reference(r.clone())))
223    } else {
224        Err(ExecutionError::RequiresRuntime)
225    }
226}
227
228#[cfg(test)]
229#[cfg(feature = "compiler")]
230mod tests {
231    use core::assert_matches;
232
233    use super::*;
234    use crate::{
235        assert_structural_eq, assert_value_eq,
236        compiler::{CompileOptions, compile_script, scope::CompilationScope},
237        datex_list,
238        global::instruction_codes::InstructionCode,
239        libs::core::get_core_lib_type_reference,
240        references::reference::Reference,
241        runtime::{
242            RuntimeConfig, RuntimeRunner,
243            execution::{
244                context::{ExecutionContext, LocalExecutionContext},
245                execution_input::ExecutionOptions,
246            },
247        },
248        traits::{structural_eq::StructuralEq, value_eq::ValueEq},
249        values::{
250            core_value::CoreValue,
251            core_values::{
252                decimal::Decimal,
253                integer::{Integer, typed_integer::TypedInteger},
254                list::List,
255                map::Map,
256            },
257        },
258    };
259    use log::{debug, info};
260
261    fn execute_datex_script_debug(
262        datex_script: &str,
263    ) -> Option<ValueContainer> {
264        let (dxb, _) =
265            compile_script(datex_script, CompileOptions::default()).unwrap();
266        let context =
267            ExecutionInput::new(&dxb, ExecutionOptions { verbose: true }, None);
268        execute_dxb_sync(context).unwrap_or_else(|err| {
269            core::panic!("Execution failed: {err}");
270        })
271    }
272
273    fn execute_datex_script_debug_unbounded(
274        datex_script_parts: impl Iterator<Item = &'static str>,
275    ) -> impl Iterator<Item = Result<Option<ValueContainer>, ExecutionError>>
276    {
277        gen move {
278            let datex_script_parts = datex_script_parts.collect::<Vec<_>>();
279            let mut execution_context = ExecutionContext::Local(
280                LocalExecutionContext::new(ExecutionMode::unbounded()),
281            );
282            let mut compilation_scope =
283                CompilationScope::new(ExecutionMode::unbounded());
284
285            let len = datex_script_parts.len();
286            for (index, script_part) in
287                datex_script_parts.into_iter().enumerate()
288            {
289                // if last part, compile and return static value if possible
290                if index == len - 1 {
291                    compilation_scope.mark_as_last_execution();
292                }
293
294                let (dxb, new_compilation_scope) = compile_script(
295                    script_part,
296                    CompileOptions::new_with_scope(compilation_scope),
297                )
298                .unwrap();
299                compilation_scope = new_compilation_scope;
300                yield execution_context.execute_dxb_sync(&dxb)
301            }
302        }
303    }
304
305    fn assert_unbounded_input_matches_output(
306        input: Vec<&'static str>,
307        expected_output: Vec<Option<ValueContainer>>,
308    ) {
309        let input = input.into_iter();
310        let expected_output = expected_output.into_iter();
311        for (result, expected) in
312            execute_datex_script_debug_unbounded(input.into_iter())
313                .zip(expected_output.into_iter())
314        {
315            let result = result.unwrap();
316            assert_eq!(result, expected);
317        }
318    }
319
320    fn execute_datex_script_debug_with_error(
321        datex_script: &str,
322    ) -> Result<Option<ValueContainer>, ExecutionError> {
323        let (dxb, _) =
324            compile_script(datex_script, CompileOptions::default()).unwrap();
325        let context =
326            ExecutionInput::new(&dxb, ExecutionOptions { verbose: true }, None);
327        execute_dxb_sync(context)
328    }
329
330    fn execute_datex_script_debug_with_result(
331        datex_script: &str,
332    ) -> ValueContainer {
333        execute_datex_script_debug(datex_script).unwrap()
334    }
335
336    fn execute_dxb_debug(
337        dxb_body: &[u8],
338    ) -> Result<Option<ValueContainer>, ExecutionError> {
339        let context = ExecutionInput::new(
340            dxb_body,
341            ExecutionOptions { verbose: true },
342            None,
343        );
344        execute_dxb_sync(context)
345    }
346
347    async fn execute_datex_script_with_runtime(
348        config: RuntimeConfig,
349        datex_script: &str,
350    ) -> Result<Option<ValueContainer>, ExecutionError> {
351        RuntimeRunner::new(config)
352            .run(async |runtime| {
353                let (dxb, _) =
354                    compile_script(datex_script, CompileOptions::default())
355                        .unwrap();
356                let context = ExecutionInput::new(
357                    &dxb,
358                    ExecutionOptions { verbose: true },
359                    Some(runtime.internal),
360                );
361                execute_dxb(context).await
362            })
363            .await
364    }
365
366    #[test]
367    fn empty_script() {
368        assert_eq!(execute_datex_script_debug(""), None);
369    }
370
371    #[test]
372    fn empty_script_semicolon() {
373        assert_eq!(execute_datex_script_debug(";;;"), None);
374    }
375
376    #[test]
377    fn single_value() {
378        assert_eq!(
379            execute_datex_script_debug_with_result("42"),
380            Integer::from(42i8).into()
381        );
382    }
383
384    #[test]
385    fn single_value_semicolon() {
386        assert_eq!(execute_datex_script_debug("42;"), None)
387    }
388
389    #[test]
390    fn is() {
391        let result = execute_datex_script_debug_with_result("1 is 1");
392        assert_eq!(result, false.into());
393        assert_structural_eq!(result, ValueContainer::from(false));
394    }
395
396    #[test]
397    fn equality() {
398        let result = execute_datex_script_debug_with_result("1 == 1");
399        assert_eq!(result, true.into());
400        assert_structural_eq!(result, ValueContainer::from(true));
401
402        let result = execute_datex_script_debug_with_result("1 == 2");
403        assert_eq!(result, false.into());
404        assert_structural_eq!(result, ValueContainer::from(false));
405
406        let result = execute_datex_script_debug_with_result("1 != 2");
407        assert_eq!(result, true.into());
408        assert_structural_eq!(result, ValueContainer::from(true));
409
410        let result = execute_datex_script_debug_with_result("1 != 1");
411        assert_eq!(result, false.into());
412        assert_structural_eq!(result, ValueContainer::from(false));
413        let result = execute_datex_script_debug_with_result("1 === 1");
414        assert_eq!(result, true.into());
415
416        assert_structural_eq!(result, ValueContainer::from(true));
417        let result = execute_datex_script_debug_with_result("1 !== 2");
418        assert_eq!(result, true.into());
419        assert_structural_eq!(result, ValueContainer::from(true));
420
421        let result = execute_datex_script_debug_with_result("1 !== 1");
422        assert_eq!(result, false.into());
423        assert_structural_eq!(result, ValueContainer::from(false));
424    }
425
426    #[test]
427    fn single_value_scope() {
428        let result = execute_datex_script_debug_with_result("(42)");
429        assert_eq!(result, Integer::from(42i8).into());
430        assert_structural_eq!(result, ValueContainer::from(42_u128));
431    }
432
433    #[test]
434    fn add() {
435        let result = execute_datex_script_debug_with_result("1 + 2");
436        assert_eq!(result, Integer::from(3i8).into());
437        assert_structural_eq!(result, ValueContainer::from(3i8));
438    }
439
440    #[test]
441    fn nested_scope() {
442        let result = execute_datex_script_debug_with_result("1 + (2 + 3)");
443        assert_eq!(result, Integer::from(6i8).into());
444    }
445
446    #[test]
447    fn empty_list() {
448        let result = execute_datex_script_debug_with_result("[]");
449        let list: List = result.to_value().borrow().cast_to_list().unwrap();
450        assert_eq!(list.len(), 0);
451        assert_eq!(result, Vec::<ValueContainer>::new().into());
452        assert_eq!(result, ValueContainer::from(Vec::<ValueContainer>::new()));
453    }
454
455    #[test]
456    fn list() {
457        let result = execute_datex_script_debug_with_result("[1, 2, 3]");
458        let list: List = result.to_value().borrow().cast_to_list().unwrap();
459        let expected = datex_list![
460            Integer::from(1i8),
461            Integer::from(2i8),
462            Integer::from(3i8)
463        ];
464        assert_eq!(list.len(), 3);
465        assert_eq!(result, expected.into());
466        assert_ne!(result, ValueContainer::from(vec![1, 2, 3]));
467        assert_structural_eq!(result, ValueContainer::from(vec![1, 2, 3]));
468    }
469
470    #[test]
471    fn list_with_nested_scope() {
472        let result = execute_datex_script_debug_with_result("[1, (2 + 3), 4]");
473        let expected = datex_list![
474            Integer::from(1i8),
475            Integer::from(5i8),
476            Integer::from(4i8)
477        ];
478
479        assert_eq!(result, expected.into());
480        assert_ne!(result, ValueContainer::from(vec![1_u8, 5_u8, 4_u8]));
481        assert_structural_eq!(
482            result,
483            ValueContainer::from(vec![1_u8, 5_u8, 4_u8])
484        );
485    }
486
487    #[test]
488    fn boolean() {
489        let result = execute_datex_script_debug_with_result("true");
490        assert_eq!(result, true.into());
491        assert_structural_eq!(result, ValueContainer::from(true));
492
493        let result = execute_datex_script_debug_with_result("false");
494        assert_eq!(result, false.into());
495        assert_structural_eq!(result, ValueContainer::from(false));
496    }
497
498    #[test]
499    fn decimal() {
500        let result = execute_datex_script_debug_with_result("1.5");
501        assert_eq!(result, Decimal::from_string("1.5").unwrap().into());
502        assert_structural_eq!(result, ValueContainer::from(1.5));
503    }
504
505    #[test]
506    fn decimal_and_integer() {
507        let result = execute_datex_script_debug_with_result("-2341324.0");
508        assert_eq!(result, Decimal::from_string("-2341324").unwrap().into());
509        assert!(!result.structural_eq(&ValueContainer::from(-2341324)));
510    }
511
512    #[test]
513    fn integer() {
514        let result = execute_datex_script_debug_with_result("2");
515        assert_eq!(result, Integer::from(2).into());
516        assert_ne!(result, 2_u8.into());
517        assert_structural_eq!(result, ValueContainer::from(2_i8));
518    }
519
520    #[test]
521    fn typed_integer() {
522        let result = execute_datex_script_debug_with_result("-2i16");
523        assert_eq!(result, TypedInteger::from(-2i16).into());
524        assert_structural_eq!(result, ValueContainer::from(-2_i16));
525
526        let result = execute_datex_script_debug_with_result("2i32");
527        assert_eq!(result, TypedInteger::from(2i32).into());
528        assert_structural_eq!(result, ValueContainer::from(2_i32));
529
530        let result = execute_datex_script_debug_with_result("-2i64");
531        assert_eq!(result, TypedInteger::from(-2i64).into());
532        assert_structural_eq!(result, ValueContainer::from(-2_i64));
533
534        let result = execute_datex_script_debug_with_result("2i128");
535        assert_eq!(result, TypedInteger::from(2i128).into());
536        assert_structural_eq!(result, ValueContainer::from(2_i128));
537
538        let result = execute_datex_script_debug_with_result("2u8");
539        assert_eq!(result, TypedInteger::from(2_u8).into());
540        assert_structural_eq!(result, ValueContainer::from(2_u8));
541
542        let result = execute_datex_script_debug_with_result("2u16");
543        assert_eq!(result, TypedInteger::from(2_u16).into());
544        assert_structural_eq!(result, ValueContainer::from(2_u16));
545
546        let result = execute_datex_script_debug_with_result("2u32");
547        assert_eq!(result, TypedInteger::from(2_u32).into());
548        assert_structural_eq!(result, ValueContainer::from(2_u32));
549
550        let result = execute_datex_script_debug_with_result("2u64");
551        assert_eq!(result, TypedInteger::from(2_u64).into());
552        assert_structural_eq!(result, ValueContainer::from(2_u64));
553
554        let result = execute_datex_script_debug_with_result("2u128");
555        assert_eq!(result, TypedInteger::from(2_u128).into());
556        assert_structural_eq!(result, ValueContainer::from(2_u128));
557
558        let result = execute_datex_script_debug_with_result("2ibig");
559        assert_eq!(result, TypedInteger::IBig(Integer::from(2)).into());
560        assert_structural_eq!(result, ValueContainer::from(2));
561    }
562
563    #[test]
564    fn null() {
565        let result = execute_datex_script_debug_with_result("null");
566        assert_eq!(result, ValueContainer::from(CoreValue::Null));
567        assert_eq!(result, CoreValue::Null.into());
568        assert_structural_eq!(result, ValueContainer::from(CoreValue::Null));
569    }
570
571    #[test]
572    fn map() {
573        let result =
574            execute_datex_script_debug_with_result("{x: 1, y: 2, z: 42}");
575        let map: CoreValue = result.clone().to_value().borrow().clone().inner;
576        let map: Map = map.try_into().unwrap();
577
578        // form and size
579        assert_eq!(map.to_string(), "{\"x\": 1, \"y\": 2, \"z\": 42}");
580        assert_eq!(map.size(), 3);
581
582        info!("Map: {:?}", map);
583
584        // access by key
585        assert_eq!(map.get("x"), Ok(&Integer::from(1).into()));
586        assert_eq!(map.get("y"), Ok(&Integer::from(2).into()));
587        assert_eq!(map.get("z"), Ok(&Integer::from(42).into()));
588
589        // structural equality checks
590        let expected_se: Map = Map::from(vec![
591            ("x".to_string(), 1.into()),
592            ("y".to_string(), 2.into()),
593            ("z".to_string(), 42.into()),
594        ]);
595        assert_structural_eq!(map, expected_se);
596
597        // strict equality checks
598        let expected_strict: Map = Map::from(vec![
599            ("x".to_string(), Integer::from(1).into()),
600            ("y".to_string(), Integer::from(2).into()),
601            ("z".to_string(), Integer::from(42).into()),
602        ]);
603        debug!("Expected map: {expected_strict}");
604        debug!("Map result: {map}");
605        // FIXME #104 type information gets lost on compile
606        // assert_eq!(result, expected.into());
607    }
608
609    #[test]
610    fn empty_map() {
611        let result = execute_datex_script_debug_with_result("{}");
612        let map: CoreValue = result.clone().to_value().borrow().clone().inner;
613        let map: Map = map.try_into().unwrap();
614
615        // form and size
616        assert_eq!(map.to_string(), "{}");
617        assert_eq!(map.size(), 0);
618
619        info!("Map: {:?}", map);
620    }
621
622    #[test]
623    fn statements() {
624        let result = execute_datex_script_debug_with_result("1; 2; 3");
625        assert_eq!(result, Integer::from(3).into());
626    }
627
628    #[test]
629    fn single_terminated_statement() {
630        let result = execute_datex_script_debug("1;");
631        assert_eq!(result, None);
632    }
633
634    #[test]
635    fn const_declaration() {
636        let result = execute_datex_script_debug_with_result("const x = 42; x");
637        assert_eq!(result, Integer::from(42).into());
638    }
639
640    #[test]
641    fn const_declaration_with_addition() {
642        let result =
643            execute_datex_script_debug_with_result("const x = 1 + 2; x");
644        assert_eq!(result, Integer::from(3).into());
645    }
646
647    #[test]
648    fn deref() {
649        let result =
650            execute_datex_script_debug_with_result("const x = &42; *x");
651        assert_eq!(result, ValueContainer::from(Integer::from(42)));
652    }
653
654    #[test]
655    fn ref_assignment() {
656        let result =
657            execute_datex_script_debug_with_result("const x = &mut 42; x");
658        assert_matches!(result, ValueContainer::Reference(..));
659        assert_value_eq!(result, ValueContainer::from(Integer::from(42)));
660    }
661
662    #[test]
663    fn ref_add_assignment() {
664        let result = execute_datex_script_debug_with_result(
665            "const x = &mut 42; *x += 1",
666        );
667        assert_value_eq!(result, ValueContainer::from(Integer::from(43)));
668
669        let result = execute_datex_script_debug_with_result(
670            "const x = &mut 42; *x += 1; x",
671        );
672
673        assert_matches!(result, ValueContainer::Reference(..));
674        assert_value_eq!(result, ValueContainer::from(Integer::from(43)));
675    }
676
677    #[test]
678    fn ref_sub_assignment() {
679        let result = execute_datex_script_debug_with_result(
680            "const x = &mut 42; *x -= 1",
681        );
682        assert_value_eq!(result, ValueContainer::from(Integer::from(41)));
683
684        let result = execute_datex_script_debug_with_result(
685            "const x = &mut 42; *x -= 1; x",
686        );
687
688        // FIXME #414 due to addition the resulting value container of the slot
689        // is no longer a reference but a value what is incorrect.
690        // assert_matches!(result, ValueContainer::Reference(..));
691        assert_value_eq!(result, ValueContainer::from(Integer::from(41)));
692    }
693
694    #[test]
695    fn endpoint_slot_no_runtime() {
696        let result = execute_datex_script_debug_with_error("#endpoint");
697        assert_matches!(result.unwrap_err(), ExecutionError::RequiresRuntime);
698    }
699
700    #[test]
701    fn env_slot_no_runtime() {
702        let result = execute_datex_script_debug_with_error("#env");
703        assert_matches!(result.unwrap_err(), ExecutionError::RequiresRuntime);
704    }
705
706    #[tokio::test]
707    async fn env_slot() {
708        let res = execute_datex_script_with_runtime(
709            RuntimeConfig {
710                env: Some(HashMap::from([(
711                    "TEST_ENV_VAR".to_string(),
712                    "test_value".to_string(),
713                )])),
714                ..Default::default()
715            },
716            "#env",
717        )
718        .await
719        .unwrap();
720        assert!(res.is_some());
721        let env = res.unwrap().to_value().borrow().cast_to_map().unwrap();
722        assert_eq!(env.get("TEST_ENV_VAR"), Ok(&"test_value".into()));
723    }
724
725    #[test]
726    fn shebang() {
727        let result = execute_datex_script_debug_with_result("#!datex\n42");
728        assert_eq!(result, Integer::from(42).into());
729    }
730
731    #[test]
732    fn single_line_comment() {
733        let result =
734            execute_datex_script_debug_with_result("// this is a comment\n42");
735        assert_eq!(result, Integer::from(42).into());
736
737        let result = execute_datex_script_debug_with_result(
738            "// this is a comment\n// another comment\n42",
739        );
740        assert_eq!(result, Integer::from(42).into());
741    }
742
743    #[test]
744    fn multi_line_comment() {
745        let result = execute_datex_script_debug_with_result(
746            "/* this is a comment */\n42",
747        );
748        assert_eq!(result, Integer::from(42).into());
749
750        let result = execute_datex_script_debug_with_result(
751            "/* this is a comment\n   with multiple lines */\n42",
752        );
753        assert_eq!(result, Integer::from(42).into());
754
755        let result = execute_datex_script_debug_with_result("[1, /* 2, */ 3]");
756        let expected = datex_list![Integer::from(1), Integer::from(3)];
757        assert_eq!(result, expected.into());
758    }
759
760    #[test]
761    fn continuous_execution() {
762        assert_unbounded_input_matches_output(
763            vec!["1", "2"],
764            vec![Some(Integer::from(1).into()), Some(Integer::from(2).into())],
765        )
766    }
767
768    #[test]
769    fn continuous_execution_multiple_external_interrupts() {
770        assert_unbounded_input_matches_output(
771            vec!["1", "integer", "integer"],
772            vec![
773                Some(Integer::from(1).into()),
774                Some(ValueContainer::Reference(Reference::TypeReference(
775                    get_core_lib_type_reference(CoreLibPointerId::Integer(
776                        None,
777                    )),
778                ))),
779                Some(ValueContainer::Reference(Reference::TypeReference(
780                    get_core_lib_type_reference(CoreLibPointerId::Integer(
781                        None,
782                    )),
783                ))),
784            ],
785        )
786    }
787}