leo_bindings_core/
shared_interpreter.rs

1use crate::types::ToValue;
2use anyhow::{Result, anyhow};
3use leo_ast::Location;
4use leo_ast::NetworkName;
5use leo_interpreter::{Element, Frame, FunctionVariant, Interpreter};
6use leo_parser;
7use leo_span::source_map::FileName;
8use leo_span::{SESSION_GLOBALS, SessionGlobals, Symbol, with_session_globals};
9use snarkvm::prelude::{Program, TestnetV0};
10use std::{cell::RefCell, collections::HashMap, fs, path::Path, rc::Rc};
11
12pub struct SharedInterpreterState {
13    pub interpreter: RefCell<Interpreter>,
14    pub session: SessionGlobals,
15}
16
17thread_local! {
18    static SHARED_INTERPRETER: RefCell<Option<Rc<SharedInterpreterState>>> = const { RefCell::new(None) };
19}
20
21pub fn initialize_shared_interpreter(interpreter: Interpreter, session: SessionGlobals) -> bool {
22    SHARED_INTERPRETER.with(|shared| {
23        let mut state = shared.borrow_mut();
24        if state.is_none() {
25            *state = Some(Rc::new(SharedInterpreterState {
26                interpreter: RefCell::new(interpreter),
27                session,
28            }));
29            true
30        } else {
31            false
32        }
33    })
34}
35
36pub fn with_shared_interpreter<T, F>(f: F) -> Option<T>
37where
38    F: FnOnce(&SharedInterpreterState) -> T,
39{
40    SHARED_INTERPRETER.with(|shared| {
41        shared
42            .borrow()
43            .as_ref()
44            .map(|state| SESSION_GLOBALS.set(&state.session, || f(state)))
45    })
46}
47
48pub trait InterpreterExtensions {
49    fn load_leo_program(&mut self, path: &Path) -> Result<()>;
50
51    fn load_aleo_program_from_string(&mut self, bytecode: &str) -> Result<()>;
52
53    fn is_program_loaded(&self, program_name: &str) -> bool;
54
55    fn get_loaded_programs(&self) -> Vec<String>;
56
57    fn set_signer(&mut self, signer: snarkvm::prelude::Address<TestnetV0>);
58}
59
60impl InterpreterExtensions for Interpreter {
61    fn load_leo_program(&mut self, path: &Path) -> Result<()> {
62        let text = fs::read_to_string(path)
63            .map_err(|e| anyhow!("Failed to read file {:?}: {}", path, e))?;
64        let source_file = with_session_globals(|s| {
65            s.source_map
66                .new_source(&text, FileName::Real(path.to_path_buf()))
67        });
68
69        let ast = leo_parser::parse_ast(
70            self.handler.clone(),
71            &self.node_builder,
72            &source_file,
73            &[],
74            NetworkName::TestnetV0,
75        )?;
76
77        for (&program, scope) in ast.ast.program_scopes.iter() {
78            self.filename_to_program
79                .insert(path.to_path_buf(), program.to_string());
80
81            for (name, function) in scope.functions.iter() {
82                self.cursor.functions.insert(
83                    Location::new(program, vec![*name]),
84                    FunctionVariant::Leo(function.clone()),
85                );
86            }
87
88            for (name, composite) in scope.structs.iter() {
89                self.cursor.structs.insert(
90                    vec![*name],
91                    composite
92                        .members
93                        .iter()
94                        .map(|m| (m.identifier.name, m.type_.clone()))
95                        .collect(),
96                );
97            }
98
99            for (name, _mapping) in scope.mappings.iter() {
100                self.cursor
101                    .mappings
102                    .insert(Location::new(program, vec![*name]), HashMap::new());
103            }
104
105            for (name, const_declaration) in scope.consts.iter() {
106                self.cursor.frames.push(Frame {
107                    step: 0,
108                    element: Element::Expression(
109                        const_declaration.value.clone(),
110                        Some(const_declaration.type_.clone()),
111                    ),
112                    user_initiated: false,
113                });
114                self.cursor.over()?;
115                let value = self.cursor.values.pop().unwrap();
116                self.cursor
117                    .globals
118                    .insert(Location::new(program, vec![*name]), value);
119            }
120        }
121
122        Ok(())
123    }
124
125    fn load_aleo_program_from_string(&mut self, bytecode: &str) -> Result<()> {
126        let aleo_program: Program<TestnetV0> = bytecode.parse()?;
127        let program = Symbol::intern(&aleo_program.id().name().to_string());
128
129        for (name, struct_type) in aleo_program.structs().iter() {
130            self.cursor.structs.insert(
131                vec![Symbol::intern(&name.to_string())],
132                struct_type
133                    .members()
134                    .iter()
135                    .map(|(id, type_)| {
136                        (
137                            leo_ast::Identifier::from(id).name,
138                            leo_ast::Type::from_snarkvm(type_, None),
139                        )
140                    })
141                    .collect(),
142            );
143        }
144
145        for (name, record_type) in aleo_program.records().iter() {
146            use snarkvm::prelude::EntryType;
147            let type_name = Symbol::intern(&name.to_string());
148
149            let mut members: indexmap::IndexMap<Symbol, leo_ast::Type> = indexmap::IndexMap::new();
150
151            members.insert(Symbol::intern("owner"), leo_ast::Type::Address);
152
153            for (id, entry) in record_type.entries().iter() {
154                let inner_type = match entry {
155                    EntryType::Public(t) | EntryType::Private(t) | EntryType::Constant(t) => t,
156                };
157                members.insert(
158                    leo_ast::Identifier::from(id).name,
159                    leo_ast::Type::from_snarkvm(inner_type, None),
160                );
161            }
162
163            self.cursor
164                .records
165                .insert((program, vec![type_name]), members);
166        }
167
168        for (name, _mapping) in aleo_program.mappings().iter() {
169            self.cursor.mappings.insert(
170                Location::new(program, vec![Symbol::intern(&name.to_string())]),
171                HashMap::new(),
172            );
173        }
174
175        for (name, function) in aleo_program.functions().iter() {
176            self.cursor.functions.insert(
177                Location::new(program, vec![Symbol::intern(&name.to_string())]),
178                FunctionVariant::AleoFunction(function.clone()),
179            );
180        }
181
182        for (name, closure) in aleo_program.closures().iter() {
183            self.cursor.functions.insert(
184                Location::new(program, vec![Symbol::intern(&name.to_string())]),
185                FunctionVariant::AleoClosure(closure.clone()),
186            );
187        }
188
189        Ok(())
190    }
191
192    fn is_program_loaded(&self, program_name: &str) -> bool {
193        let program_symbol = Symbol::intern(program_name);
194
195        self.cursor
196            .functions
197            .keys()
198            .any(|gid| gid.program == program_symbol)
199            || self
200                .cursor
201                .mappings
202                .keys()
203                .any(|gid| gid.program == program_symbol)
204            || self
205                .cursor
206                .globals
207                .keys()
208                .any(|gid| gid.program == program_symbol)
209            || self
210                .cursor
211                .records
212                .keys()
213                .any(|(p, _)| *p == program_symbol)
214    }
215
216    fn get_loaded_programs(&self) -> Vec<String> {
217        let mut programs = std::collections::HashSet::new();
218        for global_id in self.cursor.functions.keys() {
219            programs.insert(global_id.program.to_string());
220        }
221        for global_id in self.cursor.mappings.keys() {
222            programs.insert(global_id.program.to_string());
223        }
224        for global_id in self.cursor.globals.keys() {
225            programs.insert(global_id.program.to_string());
226        }
227        for (program, _) in self.cursor.records.keys() {
228            programs.insert(program.to_string());
229        }
230        programs.into_iter().collect()
231    }
232
233    fn set_signer(&mut self, signer: snarkvm::prelude::Address<TestnetV0>) {
234        self.cursor.signer = signer.to_value().into();
235    }
236}