1use crate::{
18 Annotation,
19 CompositeType,
20 Function,
21 FutureType,
22 Identifier,
23 Input,
24 Location,
25 Mode,
26 Node,
27 NodeID,
28 Output,
29 Path,
30 ProgramId,
31 TupleType,
32 TypeKind,
33 TypeNode,
34 Variant,
35};
36use leo_span::{Span, Symbol, sym};
37
38fn stub_type(kind: TypeKind) -> TypeNode {
41 TypeNode::unchecked(kind, Span::default())
42}
43
44use itertools::Itertools;
45use serde::Serialize;
46use snarkvm::{
47 console::program::RegisterType,
48 prelude::{FinalizeType, Network, ValueType},
49 synthesizer::program::{ClosureCore, FunctionCore, ViewCore},
50};
51use std::fmt;
52
53#[derive(Clone, Serialize)]
55pub struct FunctionStub {
56 pub annotations: Vec<Annotation>,
58 pub variant: Variant,
60 pub identifier: Identifier,
62 pub input: Vec<Input>,
64 pub output: Vec<Output>,
66 pub output_type: TypeKind,
68 pub span: Span,
70 pub id: NodeID,
72}
73
74impl PartialEq for FunctionStub {
75 fn eq(&self, other: &Self) -> bool {
76 self.identifier == other.identifier
77 }
78}
79
80impl Eq for FunctionStub {}
81
82impl FunctionStub {
83 #[allow(clippy::too_many_arguments)]
85 pub fn new(
86 annotations: Vec<Annotation>,
87 _is_async: bool,
88 variant: Variant,
89 identifier: Identifier,
90 input: Vec<Input>,
91 output: Vec<Output>,
92 span: Span,
93 id: NodeID,
94 ) -> Self {
95 let output_type = match output.len() {
96 0 => TypeKind::Unit,
97 1 => output[0].type_.kind().clone(),
98 _ => TypeKind::Tuple(TupleType::new(output.iter().map(|o| o.type_.kind().clone()).collect())),
99 };
100
101 FunctionStub { annotations, variant, identifier, input, output, output_type, span, id }
102 }
103
104 pub fn name(&self) -> Symbol {
106 self.identifier.name
107 }
108
109 pub fn is_main(&self) -> bool {
111 self.name() == sym::main
112 }
113
114 pub fn has_final_output(&self) -> bool {
116 self.output.iter().any(|o| matches!(o.type_.kind(), TypeKind::Future(_)))
117 }
118
119 fn format(&self, f: &mut fmt::Formatter) -> fmt::Result {
121 match self.variant {
122 Variant::FinalFn => write!(f, "final fn ")?,
123 Variant::Finalize => write!(f, "finalize ")?,
124 Variant::Fn => write!(f, "fn ")?,
125 Variant::EntryPoint => write!(f, "entry ")?,
126 Variant::View => write!(f, "view fn ")?,
127 }
128 write!(f, "{}", self.identifier)?;
129
130 let parameters = self.input.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(",");
131 let returns = match self.output.len() {
132 0 => "()".to_string(),
133 1 => self.output[0].to_string(),
134 _ => self.output.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(","),
135 };
136 write!(f, "({parameters}) -> {returns}")?;
137
138 Ok(())
139 }
140
141 pub fn from_function_core<N: Network>(function: &FunctionCore<N>, program_id: ProgramId) -> Self {
143 let outputs = function
144 .outputs()
145 .iter()
146 .map(|output| match output.value_type() {
147 ValueType::Constant(val) => vec![Output {
148 mode: Mode::Constant,
149 type_: stub_type(TypeKind::from_snarkvm(val, program_id)),
150 span: Default::default(),
151 id: Default::default(),
152 }],
153 ValueType::Public(val) => vec![Output {
154 mode: Mode::Public,
155 type_: stub_type(TypeKind::from_snarkvm(val, program_id)),
156 span: Default::default(),
157 id: Default::default(),
158 }],
159 ValueType::Private(val) => vec![Output {
160 mode: Mode::Private,
161 type_: stub_type(TypeKind::from_snarkvm(val, program_id)),
162 span: Default::default(),
163 id: Default::default(),
164 }],
165 ValueType::Record(id) => vec![Output {
166 mode: Mode::None,
167 type_: stub_type(TypeKind::Composite(CompositeType {
168 path: {
169 let ident = Identifier::from(id);
170 Path::from(ident)
171 .to_global(Location::new(program_id.as_symbol(), vec![ident.name]))
172 .with_user_program(program_id)
173 },
174 const_arguments: Vec::new(),
175 })),
176 span: Default::default(),
177 id: Default::default(),
178 }],
179 ValueType::ExternalRecord(loc) => {
180 let external_program_id = ProgramId::from(loc.program_id());
181 vec![Output {
182 mode: Mode::None,
183 span: Default::default(),
184 id: Default::default(),
185 type_: stub_type(TypeKind::Composite(CompositeType {
186 path: {
187 let ident = Identifier::from(loc.resource());
188 Path::from(ident)
189 .to_global(Location::new(external_program_id.as_symbol(), vec![ident.name]))
190 .with_user_program(external_program_id)
191 },
192 const_arguments: Vec::new(),
193 })),
194 }]
195 }
196 ValueType::Future(_) => vec![Output {
197 mode: Mode::None,
198 span: Default::default(),
199 id: Default::default(),
200 type_: stub_type(TypeKind::Future(FutureType::new(
201 Vec::new(),
202 Some(Location::new(program_id.as_symbol(), vec![Symbol::intern(&function.name().to_string())])),
203 false,
204 ))),
205 }],
206 ValueType::DynamicRecord => vec![Output {
207 mode: Mode::None,
208 span: Default::default(),
209 id: Default::default(),
210 type_: stub_type(TypeKind::DynRecord),
211 }],
212 ValueType::DynamicFuture => vec![Output {
213 mode: Mode::None,
214 span: Default::default(),
215 id: Default::default(),
216 type_: stub_type(TypeKind::Future(FutureType::new(
217 Vec::new(),
218 Some(Location::new(program_id.as_symbol(), vec![Symbol::intern(&function.name().to_string())])),
219 false,
220 ))),
221 }],
222 })
223 .collect_vec()
224 .concat();
225 let output_vec = outputs.iter().map(|output| output.type_.kind().clone()).collect_vec();
226 let output_type = match output_vec.len() {
227 0 => TypeKind::Unit,
228 1 => output_vec[0].clone(),
229 _ => TypeKind::Tuple(TupleType::new(output_vec)),
230 };
231
232 Self {
233 annotations: Vec::new(),
234 variant: Variant::EntryPoint,
235 identifier: Identifier::from(function.name()),
236 input: function
237 .inputs()
238 .iter()
239 .enumerate()
240 .map(|(index, input)| {
241 let arg_name = Identifier::new(Symbol::intern(&format!("arg{}", index + 1)), Default::default());
242 match input.value_type() {
243 ValueType::Constant(val) => Input {
244 identifier: arg_name,
245 mode: Mode::Constant,
246 type_: stub_type(TypeKind::from_snarkvm(val, program_id)),
247 span: Default::default(),
248 id: Default::default(),
249 },
250 ValueType::Public(val) => Input {
251 identifier: arg_name,
252 mode: Mode::Public,
253 type_: stub_type(TypeKind::from_snarkvm(val, program_id)),
254 span: Default::default(),
255 id: Default::default(),
256 },
257 ValueType::Private(val) => Input {
258 identifier: arg_name,
259 mode: Mode::Private,
260 type_: stub_type(TypeKind::from_snarkvm(val, program_id)),
261 span: Default::default(),
262 id: Default::default(),
263 },
264 ValueType::Record(id) => Input {
265 identifier: arg_name,
266 mode: Mode::None,
267 type_: stub_type(TypeKind::Composite(CompositeType {
268 path: {
269 let ident = Identifier::from(id);
270 Path::from(ident)
271 .to_global(Location::new(program_id.as_symbol(), vec![ident.name]))
272 .with_user_program(program_id)
273 },
274 const_arguments: Vec::new(),
275 })),
276 span: Default::default(),
277 id: Default::default(),
278 },
279 ValueType::ExternalRecord(loc) => {
280 let external_program = ProgramId::from(loc.program_id());
281 Input {
282 identifier: arg_name,
283 mode: Mode::None,
284 span: Default::default(),
285 id: Default::default(),
286 type_: stub_type(TypeKind::Composite(CompositeType {
287 path: {
288 let ident = Identifier::from(loc.resource());
289 Path::from(ident)
290 .to_global(Location::new(external_program.as_symbol(), vec![ident.name]))
291 .with_user_program(external_program)
292 },
293 const_arguments: Vec::new(),
294 })),
295 }
296 }
297 ValueType::Future(_) | ValueType::DynamicFuture => {
298 panic!("Functions do not contain futures as inputs")
299 }
300
301 ValueType::DynamicRecord => Input {
302 identifier: arg_name,
303 mode: Mode::None,
304 span: Default::default(),
305 id: Default::default(),
306 type_: stub_type(TypeKind::DynRecord),
307 },
308 }
309 })
310 .collect_vec(),
311 output: outputs,
312 output_type,
313 span: Default::default(),
314 id: Default::default(),
315 }
316 }
317
318 pub fn from_finalize<N: Network>(function: &FunctionCore<N>, key_name: Symbol, program_id: ProgramId) -> Self {
319 Self {
320 annotations: Vec::new(),
321 variant: Variant::Finalize,
322 identifier: Identifier::new(key_name, Default::default()),
323 input: function
324 .finalize_logic()
325 .unwrap()
326 .inputs()
327 .iter()
328 .enumerate()
329 .map(|(index, input)| Input {
330 identifier: Identifier::new(Symbol::intern(&format!("arg{}", index + 1)), Default::default()),
331 mode: Mode::None,
332 type_: match input.finalize_type() {
333 FinalizeType::Plaintext(val) => stub_type(TypeKind::from_snarkvm(val, program_id)),
334 FinalizeType::Future(val) => stub_type(TypeKind::Future(FutureType::new(
335 Vec::new(),
336 Some(Location::new(ProgramId::from(val.program_id()).as_symbol(), vec![Symbol::intern(
337 &format!("finalize/{}", val.resource()),
338 )])),
339 false,
340 ))),
341 FinalizeType::DynamicFuture => {
342 stub_type(TypeKind::Future(FutureType::new(Vec::new(), None, false)))
343 }
344 },
345 span: Default::default(),
346 id: Default::default(),
347 })
348 .collect_vec(),
349 output: Vec::new(),
350 output_type: TypeKind::Unit,
351 span: Default::default(),
352 id: 0,
353 }
354 }
355
356 pub fn from_view<N: Network>(view: &ViewCore<N>, program_id: ProgramId) -> Self {
366 let plaintext_or_panic = |finalize_type: &FinalizeType<N>| match finalize_type {
367 FinalizeType::Plaintext(val) => TypeKind::from_snarkvm(val, program_id),
368 FinalizeType::Future(_) | FinalizeType::DynamicFuture => {
369 panic!("Views do not contain futures as inputs or outputs")
370 }
371 };
372
373 let outputs = view
374 .outputs()
375 .iter()
376 .map(|output| Output {
377 mode: Mode::None,
378 type_: stub_type(plaintext_or_panic(output.finalize_type())),
379 span: Default::default(),
380 id: Default::default(),
381 })
382 .collect_vec();
383 let output_vec = outputs.iter().map(|o| o.type_.kind().clone()).collect_vec();
384 let output_type = match output_vec.len() {
385 0 => TypeKind::Unit,
386 1 => output_vec[0].clone(),
387 _ => TypeKind::Tuple(TupleType::new(output_vec)),
388 };
389
390 Self {
391 annotations: Vec::new(),
392 variant: Variant::View,
393 identifier: Identifier::from(view.name()),
394 input: view
395 .inputs()
396 .iter()
397 .enumerate()
398 .map(|(index, input)| Input {
399 identifier: Identifier::new(Symbol::intern(&format!("arg{}", index + 1)), Default::default()),
400 mode: Mode::None,
401 type_: stub_type(plaintext_or_panic(input.finalize_type())),
402 span: Default::default(),
403 id: Default::default(),
404 })
405 .collect_vec(),
406 output: outputs,
407 output_type,
408 span: Default::default(),
409 id: 0,
410 }
411 }
412
413 pub fn from_closure<N: Network>(closure: &ClosureCore<N>, program_id: ProgramId) -> Self {
414 let outputs = closure
415 .outputs()
416 .iter()
417 .map(|output| match output.register_type() {
418 RegisterType::Plaintext(val) => Output {
419 mode: Mode::None,
420 type_: stub_type(TypeKind::from_snarkvm(val, program_id)),
421 span: Default::default(),
422 id: Default::default(),
423 },
424 RegisterType::Record(_) | RegisterType::DynamicRecord => panic!("Closures do not return records"),
425 RegisterType::ExternalRecord(_) => panic!("Closures do not return external records"),
426 RegisterType::Future(_) | RegisterType::DynamicFuture => panic!("Closures do not return futures"),
427 })
428 .collect_vec();
429 let output_vec = outputs.iter().map(|output| output.type_.kind().clone()).collect_vec();
430 let output_type = match output_vec.len() {
431 0 => TypeKind::Unit,
432 1 => output_vec[0].clone(),
433 _ => TypeKind::Tuple(TupleType::new(output_vec)),
434 };
435 Self {
436 annotations: Vec::new(),
437 variant: Variant::Fn,
438 identifier: Identifier::from(closure.name()),
439 input: closure
440 .inputs()
441 .iter()
442 .enumerate()
443 .map(|(index, input)| {
444 let arg_name = Identifier::new(Symbol::intern(&format!("arg{}", index + 1)), Default::default());
445 match input.register_type() {
446 RegisterType::Plaintext(val) => Input {
447 identifier: arg_name,
448 mode: Mode::None,
449 type_: stub_type(TypeKind::from_snarkvm(val, program_id)),
450 span: Default::default(),
451 id: Default::default(),
452 },
453 RegisterType::Record(_) | RegisterType::DynamicRecord => {
454 panic!("Closures do not contain records as inputs")
455 }
456 RegisterType::ExternalRecord(_) => panic!("Closures do not contain external records as inputs"),
457 RegisterType::Future(_) | RegisterType::DynamicFuture => {
458 panic!("Closures do not contain futures as inputs")
459 }
460 }
461 })
462 .collect_vec(),
463 output: outputs,
464 output_type,
465 span: Default::default(),
466 id: Default::default(),
467 }
468 }
469}
470
471impl From<Function> for FunctionStub {
472 fn from(function: Function) -> Self {
473 Self {
474 annotations: function.annotations,
475 variant: function.variant,
476 identifier: function.identifier,
477 input: function.input,
478 output: function.output,
479 output_type: function.output_type,
480 span: function.span,
481 id: function.id,
482 }
483 }
484}
485
486impl fmt::Debug for FunctionStub {
487 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
488 self.format(f)
489 }
490}
491
492impl fmt::Display for FunctionStub {
493 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
494 self.format(f)
495 }
496}
497
498crate::simple_node_impl!(FunctionStub);