iota_move/
disassemble.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{
6    fs::File,
7    io::{BufReader, Read},
8    path::{Path, PathBuf},
9};
10
11use clap::Parser;
12use move_binary_format::CompiledModule;
13use move_bytecode_source_map::utils::serialize_to_json_string;
14use move_cli::base;
15use move_disassembler::disassembler::Disassembler;
16use move_ir_types::location::Spanned;
17use move_package::BuildConfig;
18
19/// Disassemble move code.
20#[derive(Parser)]
21#[group(id = "iota-move-disassemmble")]
22pub struct Disassemble {
23    /// Path to a .mv file to disassemble
24    #[arg(name = "module_path")]
25    module_path: PathBuf,
26
27    /// Whether to display the disassembly in raw Debug format
28    #[arg(long = "Xdebug")]
29    debug: bool,
30
31    #[arg(short = 'i', long)]
32    interactive: bool,
33
34    /// Print the "bytecode map" (source map for disassembled bytecode)
35    #[clap(long = "bytecode-map")]
36    pub bytecode_map: bool,
37}
38
39impl Disassemble {
40    pub fn execute(
41        self,
42        package_path: Option<&Path>,
43        build_config: BuildConfig,
44    ) -> anyhow::Result<()> {
45        if base::reroot_path(Some(&self.module_path)).is_ok() {
46            // disassembling bytecode inside the source package that produced it--use the
47            // source info
48            let module_name = self
49                .module_path
50                .file_stem()
51                .expect("Bad module path")
52                .to_str()
53                .expect("Cannot convert module name to string")
54                .to_owned();
55            move_cli::base::disassemble::Disassemble {
56                interactive: self.interactive,
57                package_name: None,
58                module_or_script_name: module_name,
59                debug: self.debug,
60                bytecode_map: self.bytecode_map,
61            }
62            .execute(package_path, build_config)?;
63            return Ok(());
64        }
65
66        // disassembling a bytecode file with no source info
67        assert!(
68            Path::new(&self.module_path).exists(),
69            "Bad path to .mv file"
70        );
71
72        let mut bytes = Vec::new();
73        let mut file = BufReader::new(File::open(self.module_path)?);
74        file.read_to_end(&mut bytes)?;
75        // this deserialized a module to the max version of the bytecode but it's OK
76        // here because it's not run as part of the deterministic replicated
77        // state machine.
78        let module = CompiledModule::deserialize_with_defaults(&bytes)?;
79
80        if self.debug {
81            println!("{module:#?}");
82        } else {
83            let d = Disassembler::from_module(&module, Spanned::unsafe_no_loc(()).loc)?;
84            let (disassemble_string, bcode_map) = d.disassemble_with_source_map()?;
85            if self.bytecode_map {
86                println!("{}", serialize_to_json_string(&bcode_map)?);
87            }
88            println!("{}", disassemble_string);
89        }
90
91        Ok(())
92    }
93}