| 510 | /// @returns {object} An object like `{ "program.aleo": ["fn1", "fn2"] }` |
| 511 | #[wasm_bindgen(js_name = "getCallGraph")] |
| 512 | pub fn get_call_graph(&self, entry_function: &str) -> Result<JsValue, String> { |
| 513 | use snarkvm_synthesizer_program::CallOperator; |
| 514 | use std::collections::HashMap; |
| 515 | |
| 516 | let entry_id = IdentifierNative::from_str(entry_function).map_err(|e| e.to_string())?; |
| 517 | |
| 518 | // Collect results in a Rust HashMap first, then convert to JS at the end. |
| 519 | let mut external_calls: HashMap<String, Vec<String>> = HashMap::new(); |
| 520 | let mut visited = HashSet::new(); |
| 521 | let mut queue = VecDeque::new(); |
| 522 | queue.push_back(entry_id); |
| 523 | |
| 524 | while let Some(fn_id) = queue.pop_front() { |
| 525 | if !visited.insert(fn_id.clone()) { |
| 526 | continue; |
| 527 | } |
| 528 | |
| 529 | // Get instructions from either a function or a closure |
| 530 | let instructions: &[_] = if let Some(func) = self.0.functions().get(&fn_id) { |
| 531 | func.instructions() |
| 532 | } else if let Some(closure) = self.0.closures().get(&fn_id) { |
| 533 | closure.instructions() |
| 534 | } else { |
| 535 | continue; |
| 536 | }; |
| 537 | |
| 538 | for instruction in instructions { |
| 539 | if let Some(call_op) = instruction.call_operator() { |
| 540 | match call_op { |
| 541 | CallOperator::Locator(locator) => { |
| 542 | let prog_name = locator.program_id().to_string(); |
| 543 | let fn_name = locator.resource().to_string(); |
| 544 | external_calls.entry(prog_name).or_default().push(fn_name); |
| 545 | } |
| 546 | CallOperator::Resource(local_id) => { |
| 547 | if !visited.contains(local_id) { |
| 548 | queue.push_back(local_id.clone()); |
| 549 | } |
| 550 | } |
| 551 | } |
| 552 | } |
| 553 | } |
| 554 | } |
| 555 | |
| 556 | // Convert HashMap to JS object |
| 557 | let result = Object::new(); |
| 558 | for (prog_name, fn_names) in &external_calls { |
| 559 | let arr = Array::new_with_length(fn_names.len() as u32); |
| 560 | for (i, fn_name) in fn_names.iter().enumerate() { |
| 561 | arr.set(i as u32, JsValue::from_str(fn_name)); |
| 562 | } |
| 563 | Reflect::set(&result, &JsValue::from_str(prog_name), &arr).map_err(|_| "Failed to set property")?; |
| 564 | } |
| 565 | |
| 566 | Ok(result.into()) |
| 567 | } |
| 568 | |
| 569 | /// Get the checksum of the program. |