go_engine/
engine.rs

1// Copyright 2022 The Goscript Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5use crate::ffi::Ffi;
6#[cfg(feature = "go_std")]
7use crate::std::os;
8#[cfg(feature = "serde_borsh")]
9use borsh::BorshSerialize;
10use std::path::Path;
11use std::rc::Rc;
12
13#[cfg(feature = "codegen")]
14pub use {cg::SourceRead, types::ImportKey};
15#[cfg(feature = "codegen")]
16extern crate go_codegen as cg;
17#[cfg(feature = "codegen")]
18extern crate go_parser as parser;
19#[cfg(feature = "codegen")]
20extern crate go_types as types;
21extern crate go_vm as vm;
22
23#[derive(Default)]
24pub struct Config {
25    /// print debug info in parser
26    pub trace_parser: bool,
27    /// print debug info in checker
28    pub trace_checker: bool,
29    /// custom std in
30    pub std_in: Option<Box<dyn std::io::Read + Sync + Send>>,
31    /// custom std out
32    pub std_out: Option<Box<dyn std::io::Write + Sync + Send>>,
33    /// custom std err
34    pub std_err: Option<Box<dyn std::io::Write + Sync + Send>>,
35}
36
37pub struct Engine {
38    ffi: vm::FfiFactory,
39}
40
41impl Engine {
42    pub fn new() -> Engine {
43        #[cfg(not(feature = "go_std"))]
44        {
45            Engine {
46                ffi: vm::FfiFactory::new(),
47            }
48        }
49
50        #[cfg(feature = "go_std")]
51        {
52            let mut e = Engine {
53                ffi: vm::FfiFactory::new(),
54            };
55            crate::std::register(&mut e.ffi);
56            e
57        }
58    }
59
60    pub fn with_user_data(data: usize) -> Engine {
61        #[cfg(not(feature = "go_std"))]
62        {
63            Engine {
64                ffi: vm::FfiFactory::with_user_data(data),
65            }
66        }
67
68        #[cfg(feature = "go_std")]
69        {
70            let mut e = Engine {
71                ffi: vm::FfiFactory::with_user_data(data),
72            };
73            crate::std::register(&mut e.ffi);
74            e
75        }
76    }
77
78    #[cfg(feature = "go_std")]
79    pub fn set_std_io(
80        &self,
81        std_in: Option<Box<dyn std::io::Read + Sync + Send>>,
82        std_out: Option<Box<dyn std::io::Write + Sync + Send>>,
83        std_err: Option<Box<dyn std::io::Write + Sync + Send>>,
84    ) {
85        os::set_std_io(std_in, std_out, std_err);
86    }
87
88    pub fn register_extension(&mut self, name: &'static str, proto: Rc<dyn Ffi>) {
89        self.ffi.register(name, proto);
90    }
91
92    #[cfg(feature = "codegen")]
93    pub fn compile<S: SourceRead>(
94        &self,
95        reader: &S,
96        path: &Path,
97        debug_info: bool,
98        trace_parser: bool,
99        trace_checker: bool,
100    ) -> Result<vm::Bytecode, parser::ErrorList> {
101        let cfg = types::TraceConfig {
102            trace_parser,
103            trace_checker,
104        };
105        cg::parse_check_gen(path, &cfg, reader, debug_info)
106    }
107
108    #[cfg(all(feature = "codegen", feature = "serde_borsh"))]
109    pub fn compile_serialize<S: SourceRead>(
110        &self,
111        reader: &S,
112        path: &Path,
113        debug_info: bool,
114        trace_parser: bool,
115        trace_checker: bool,
116    ) -> Result<Vec<u8>, parser::ErrorList> {
117        self.compile(reader, path, debug_info, trace_parser, trace_checker)
118            .map(|code| code.try_to_vec().unwrap())
119    }
120
121    pub fn run_bytecode(&self, bc: &vm::Bytecode) -> Option<vm::PanicData> {
122        vm::run(bc, &self.ffi)
123    }
124
125    #[cfg(feature = "codegen")]
126    pub fn run_source<S: SourceRead>(
127        &self,
128        trace_parser: bool,
129        trace_checker: bool,
130        reader: &S,
131        path: &Path,
132        panic_handler: Option<Rc<dyn Fn(String, String)>>,
133    ) -> Result<(), parser::ErrorList> {
134        self.compile(reader, path, true, trace_parser, trace_checker)
135            .map(|code| {
136                // let mut decoded;
137                // #[cfg(feature = "serde_borsh")]
138                // {
139                //     let encoded = code.try_to_vec().unwrap();
140                //     decoded = go_vm::Bytecode::try_from_slice(&encoded).unwrap();
141                // }
142                // #[cfg(not(feature = "serde_borsh"))]
143                // {
144                //     decoded = code;
145                // }
146                let pdata = vm::run(&code, &self.ffi);
147                if let Some(pdata) = pdata {
148                    let call_stack = vm::CallStackDisplay::new(&pdata, &code);
149                    if let Some(handler) = panic_handler {
150                        handler(format!("{}", pdata.msg), format!("{}", call_stack));
151                    } else {
152                        eprintln!("{}\n", pdata.msg);
153                        eprintln!("{}\n", call_stack);
154                    }
155                }
156            })
157    }
158}