From b09f231b58d0b5419502906b9f7b90597c937ab4 Mon Sep 17 00:00:00 2001 From: Runtian Zhou Date: Wed, 24 May 2023 10:54:25 -0700 Subject: [PATCH 1/3] [struct-api] Implement an extractor --- Cargo.lock | 5 +- language/move-prover/move-abigen/Cargo.toml | 1 + language/move-prover/move-abigen/src/lib.rs | 2 + .../move-prover/move-abigen/src/structgen.rs | 144 ++++++++++++++++++ 4 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 language/move-prover/move-abigen/src/structgen.rs diff --git a/Cargo.lock b/Cargo.lock index 642a0d2492..22dd8e0105 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2921,6 +2921,7 @@ dependencies = [ "move-prover", "move-prover-test-utils", "serde 1.0.145", + "serde-reflection", "tempfile", ] @@ -5354,9 +5355,9 @@ dependencies = [ [[package]] name = "serde-reflection" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167450ba550f903a2b35a81ba3ca387585189e2430e3df6b94b95f3bec2f26bd" +checksum = "f05a5f801ac62a51a49d378fdb3884480041b99aced450b28990673e8ff99895" dependencies = [ "once_cell", "serde 1.0.145", diff --git a/language/move-prover/move-abigen/Cargo.toml b/language/move-prover/move-abigen/Cargo.toml index 0c745b41b8..5070a2939a 100644 --- a/language/move-prover/move-abigen/Cargo.toml +++ b/language/move-prover/move-abigen/Cargo.toml @@ -21,6 +21,7 @@ heck = "0.3.2" # external dependencies log = "0.4.14" serde = { version = "1.0.124", features = ["derive"] } +serde-reflection = "0.3.6" [dev-dependencies] codespan-reporting = "0.11.1" diff --git a/language/move-prover/move-abigen/src/lib.rs b/language/move-prover/move-abigen/src/lib.rs index 5abe5ae7d2..1ad7999d05 100644 --- a/language/move-prover/move-abigen/src/lib.rs +++ b/language/move-prover/move-abigen/src/lib.rs @@ -7,5 +7,7 @@ extern crate core; mod abigen; +mod structgen; pub use crate::abigen::*; +pub use crate::structgen::*; \ No newline at end of file diff --git a/language/move-prover/move-abigen/src/structgen.rs b/language/move-prover/move-abigen/src/structgen.rs new file mode 100644 index 0000000000..a3b9ab63c8 --- /dev/null +++ b/language/move-prover/move-abigen/src/structgen.rs @@ -0,0 +1,144 @@ +// Copyright (c) The Diem Core Contributors +// Copyright (c) The Move Contributors +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::{bail, Result, anyhow}; +#[allow(unused_imports)] +use log::{debug, info, warn}; +use move_core_types::{ + account_address::AccountAddress, + language_storage::StructTag, +}; +use move_model::{ + model::{GlobalEnv, StructEnv}, + ty::{PrimitiveType, Type}, +}; +use serde::{Deserialize, Serialize}; +use serde_reflection::{ContainerFormat, Format, Registry, Named}; +use std::{collections::BTreeMap}; + +/// Options passed into the ABI generator. +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[serde(default, deny_unknown_fields)] +pub struct StructAbigenOptions { + /// Include struct's module address/name in the generated name, + pub use_canonical_name: bool, +} + +/// The ABI generator. +pub struct StructAbigen<'env> { + /// Options. + options: &'env StructAbigenOptions, + /// Input definitions. + env: &'env GlobalEnv, + /// Map from file name to generated script ABI (if any). + output: BTreeMap, +} + +impl<'env> StructAbigen<'env> { + /// Creates a new ABI generator. + pub fn new(env: &'env GlobalEnv, options: &'env StructAbigenOptions) -> Self { + Self { + options, + env, + output: BTreeMap::new(), + } + } + + /// Returns the result of ABI generation, a vector of pairs of filenames + /// and JSON content. + pub fn into_result(mut self) -> Registry { + std::mem::take(&mut self.output) + .into_iter() + .map(|(tag, format)| { + ( + self.struct_tag_to_name(&tag), + format, + ) + }) + .collect::>() + } + + /// Generates ABIs for all script modules in the environment (excluding the dependency set). + pub fn gen(&mut self) -> Result<()> { + for module in self.env.get_modules() { + for struct_ in module.get_structs() { + if struct_.get_type_parameters().is_empty() { + self.register_struct_tag(&StructTag { + address: *module.self_address(), + module: module.get_identifier(), + name: struct_.get_identifier().ok_or_else(|| anyhow!("Fail to get identifier"))?, + type_params: vec![], + })?; + } else { + // Struct has a generic type parameter. User need to create the directive to specify the monomorphized struct name. + unimplemented!() + } + } + } + Ok(()) + } + + fn register_struct_tag(&mut self, tag: &StructTag) -> Result<()> { + if self.output.contains_key(tag) { + return Ok(()) + } + let struct_id = self.env.find_struct_by_tag(tag).ok_or_else(|| anyhow!("Fail to get tag for {:?}", tag))?; + let type_args = tag.type_params.iter().map(|ty| Type::from_type_tag(&ty, &self.env)).collect::>(); + let struct_env = self.env.get_struct(struct_id); + let mut fields = vec![]; + for field in struct_env.get_fields() { + let fmt = self.type_to_format(field.get_type().instantiate(&type_args))?; + fields.push(Named { + name: field.get_identifier().ok_or_else(|| anyhow!("Fail to get tag for {:?}", tag))?.to_string(), + value: fmt, + }); + } + self.output.insert(tag.clone(), ContainerFormat::Struct(fields)); + Ok(()) + } + + fn struct_tag_to_name(&self, tag: &StructTag) -> String { + if self.options.use_canonical_name { + tag.to_canonical_string() + .replace(":", "_") + .replace("<", "_") + .replace(">", "_") + } else { + tag.name.as_str().to_string() + } + } + + fn type_to_format(&mut self, ty: Type) -> Result { + Ok(match ty { + Type::Primitive(primitive) => match primitive { + PrimitiveType::Address => Format::TupleArray { + content: Box::new(Format::Bytes), + size: AccountAddress::LENGTH, + }, + PrimitiveType::Bool => Format::Bool, + PrimitiveType::U8 => Format::I8, + PrimitiveType::U16 => Format::I16, + PrimitiveType::U32 => Format::I32, + PrimitiveType::U64 => Format::I64, + PrimitiveType::U128 => Format::I128, + PrimitiveType::U256 => Format::TupleArray { + content: Box::new(Format::Bytes), + size: move_core_types::u256::U256_NUM_BYTES, + }, + PrimitiveType::Signer + | PrimitiveType::EventStore + | PrimitiveType::Num + | PrimitiveType::Range => bail!("Unexpected type in struct field"), + }, + Type::Vector(ty) => Format::Seq(Box::new(self.type_to_format(*ty)?)), + Type::Struct(_, _, _) => { + let tag = ty.into_struct_tag(&self.env).ok_or_else(|| anyhow!("Failed to convert type into struct tag"))?; + self.register_struct_tag(&tag)?; + Format::TypeName(self.struct_tag_to_name(&tag)) + } + Type::Error | Type::Fun(_, _) | Type::Reference(_, _) | Type::ResourceDomain(_, _, _) | Type::TypeParameter(_) | Type::Tuple(_) | Type::Var(_) | Type::TypeDomain(_) => bail!("Unexpected type in struct field"), + + }) + } +} From c18621c6ba4cd1dd769112466656ee4d6f8007cb Mon Sep 17 00:00:00 2001 From: Runtian Zhou Date: Wed, 24 May 2023 16:16:08 -0700 Subject: [PATCH 2/3] fixup! [struct-api] Implement an extractor --- language/move-prover/move-abigen/src/lib.rs | 2 +- .../move-prover/move-abigen/src/structgen.rs | 138 ++++++++++++++---- .../move-abigen/tests/sources/bad_script.yaml | 10 ++ .../move-abigen/tests/sources/diem.yaml | 2 + .../tests/sources/script_fun_in_module.yaml | 18 +++ .../tests/sources/some_script.yaml | 2 + .../move-abigen/tests/sources/structs.move | 24 +++ .../move-abigen/tests/sources/structs.yaml | 20 +++ .../move-abigen/tests/testsuite.rs | 32 +++- language/move-prover/src/cli.rs | 17 ++- language/move-prover/src/lib.rs | 28 +++- 11 files changed, 262 insertions(+), 31 deletions(-) create mode 100644 language/move-prover/move-abigen/tests/sources/bad_script.yaml create mode 100644 language/move-prover/move-abigen/tests/sources/diem.yaml create mode 100644 language/move-prover/move-abigen/tests/sources/script_fun_in_module.yaml create mode 100644 language/move-prover/move-abigen/tests/sources/some_script.yaml create mode 100644 language/move-prover/move-abigen/tests/sources/structs.move create mode 100644 language/move-prover/move-abigen/tests/sources/structs.yaml diff --git a/language/move-prover/move-abigen/src/lib.rs b/language/move-prover/move-abigen/src/lib.rs index 1ad7999d05..bd93f0ff3e 100644 --- a/language/move-prover/move-abigen/src/lib.rs +++ b/language/move-prover/move-abigen/src/lib.rs @@ -10,4 +10,4 @@ mod abigen; mod structgen; pub use crate::abigen::*; -pub use crate::structgen::*; \ No newline at end of file +pub use crate::structgen::*; diff --git a/language/move-prover/move-abigen/src/structgen.rs b/language/move-prover/move-abigen/src/structgen.rs index a3b9ab63c8..6d7dd07944 100644 --- a/language/move-prover/move-abigen/src/structgen.rs +++ b/language/move-prover/move-abigen/src/structgen.rs @@ -2,27 +2,39 @@ // Copyright (c) The Move Contributors // SPDX-License-Identifier: Apache-2.0 -use anyhow::{bail, Result, anyhow}; +use anyhow::{anyhow, bail, Result}; #[allow(unused_imports)] use log::{debug, info, warn}; -use move_core_types::{ - account_address::AccountAddress, - language_storage::StructTag, -}; +use move_core_types::{account_address::AccountAddress, language_storage::StructTag}; use move_model::{ + ast::{Attribute, AttributeValue, Value}, model::{GlobalEnv, StructEnv}, ty::{PrimitiveType, Type}, }; use serde::{Deserialize, Serialize}; -use serde_reflection::{ContainerFormat, Format, Registry, Named}; -use std::{collections::BTreeMap}; +use serde_reflection::{ContainerFormat, Format, Named, Registry}; +use std::{collections::BTreeMap, str::FromStr}; + +const EXTRACT_ABI: &str = "extract_abi"; +const EXTRACT_ABI_TYPE: &str = "type"; /// Options passed into the ABI generator. -#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] #[serde(default, deny_unknown_fields)] pub struct StructAbigenOptions { /// Include struct's module address/name in the generated name, pub use_canonical_name: bool, + /// In which path to store output. + pub output_path: String, +} + +impl Default for StructAbigenOptions { + fn default() -> Self { + Self { + use_canonical_name: false, + output_path: "abi.yaml".to_string(), + } + } } /// The ABI generator. @@ -50,12 +62,7 @@ impl<'env> StructAbigen<'env> { pub fn into_result(mut self) -> Registry { std::mem::take(&mut self.output) .into_iter() - .map(|(tag, format)| { - ( - self.struct_tag_to_name(&tag), - format, - ) - }) + .map(|(tag, format)| (self.struct_tag_to_name(&tag), format)) .collect::>() } @@ -67,34 +74,105 @@ impl<'env> StructAbigen<'env> { self.register_struct_tag(&StructTag { address: *module.self_address(), module: module.get_identifier(), - name: struct_.get_identifier().ok_or_else(|| anyhow!("Fail to get identifier"))?, + name: struct_ + .get_identifier() + .ok_or_else(|| anyhow!("Fail to get identifier"))?, type_params: vec![], })?; } else { // Struct has a generic type parameter. User need to create the directive to specify the monomorphized struct name. - unimplemented!() + let tags = self.extract_tags_from_struct(&struct_); + if tags.is_empty() { + bail!("No monomorphization provided for generic struct"); + } + for tag in tags { + self.register_struct_tag(&tag)?; + } } } } Ok(()) } + fn extract_tags_from_struct(&self, struct_: &StructEnv<'env>) -> Vec { + let mut result = vec![]; + for attribute in struct_.get_attributes().iter() { + match attribute { + Attribute::Apply(_, name, attributes) + if self.env.symbol_pool().string(*name).as_str() == EXTRACT_ABI => + { + if attributes.len() != 1 { + self.env + .error(&struct_.get_loc(), "extract_abi must contain 1 parameters"); + } + + let value = if let Attribute::Assign(_, name, value) = &attributes[0] { + if self.env.symbol_pool().string(*name).as_str() != EXTRACT_ABI_TYPE { + self.env + .error(&struct_.get_loc(), "extract_abi lacks 'type' parameter"); + } + value + } else { + self.env + .error(&struct_.get_loc(), "extract_abi lacks 'type' parameter"); + continue; + }; + + let struct_tag = + if let AttributeValue::Value(_, Value::ByteArray(bytes)) = value { + if let Ok(tag) = String::from_utf8(bytes.clone()) + .map_err(|_| ()) + .and_then(|str| StructTag::from_str(&str).map_err(|_| ())) + { + tag + } else { + self.env.error( + &struct_.get_loc(), + "extract_abi 'type' parameter is not a valid struct tag", + ); + continue; + } + } else { + self.env + .error(&struct_.get_loc(), "extract_abi lacks 'type' parameter"); + continue; + }; + + result.push(struct_tag); + }, + _ => continue, + } + } + result + } + fn register_struct_tag(&mut self, tag: &StructTag) -> Result<()> { if self.output.contains_key(tag) { - return Ok(()) + return Ok(()); } - let struct_id = self.env.find_struct_by_tag(tag).ok_or_else(|| anyhow!("Fail to get tag for {:?}", tag))?; - let type_args = tag.type_params.iter().map(|ty| Type::from_type_tag(&ty, &self.env)).collect::>(); + let struct_id = self + .env + .find_struct_by_tag(tag) + .ok_or_else(|| anyhow!("Fail to get tag for {:?}", tag))?; + let type_args = tag + .type_params + .iter() + .map(|ty| Type::from_type_tag(&ty, &self.env)) + .collect::>(); let struct_env = self.env.get_struct(struct_id); - let mut fields = vec![]; + let mut fields = vec![]; for field in struct_env.get_fields() { let fmt = self.type_to_format(field.get_type().instantiate(&type_args))?; fields.push(Named { - name: field.get_identifier().ok_or_else(|| anyhow!("Fail to get tag for {:?}", tag))?.to_string(), + name: field + .get_identifier() + .ok_or_else(|| anyhow!("Fail to get tag for {:?}", tag))? + .to_string(), value: fmt, }); } - self.output.insert(tag.clone(), ContainerFormat::Struct(fields)); + self.output + .insert(tag.clone(), ContainerFormat::Struct(fields)); Ok(()) } @@ -133,12 +211,20 @@ impl<'env> StructAbigen<'env> { }, Type::Vector(ty) => Format::Seq(Box::new(self.type_to_format(*ty)?)), Type::Struct(_, _, _) => { - let tag = ty.into_struct_tag(&self.env).ok_or_else(|| anyhow!("Failed to convert type into struct tag"))?; + let tag = ty + .into_struct_tag(&self.env) + .ok_or_else(|| anyhow!("Failed to convert type into struct tag"))?; self.register_struct_tag(&tag)?; Format::TypeName(self.struct_tag_to_name(&tag)) - } - Type::Error | Type::Fun(_, _) | Type::Reference(_, _) | Type::ResourceDomain(_, _, _) | Type::TypeParameter(_) | Type::Tuple(_) | Type::Var(_) | Type::TypeDomain(_) => bail!("Unexpected type in struct field"), - + }, + Type::Error + | Type::Fun(_, _) + | Type::Reference(_, _) + | Type::ResourceDomain(_, _, _) + | Type::TypeParameter(_) + | Type::Tuple(_) + | Type::Var(_) + | Type::TypeDomain(_) => bail!("Unexpected type in struct field"), }) } } diff --git a/language/move-prover/move-abigen/tests/sources/bad_script.yaml b/language/move-prover/move-abigen/tests/sources/bad_script.yaml new file mode 100644 index 0000000000..ecd91d4542 --- /dev/null +++ b/language/move-prover/move-abigen/tests/sources/bad_script.yaml @@ -0,0 +1,10 @@ +Move prover struct abigen returns: exiting with model building errors +error: incompatible types + ┌─ tests/sources/bad_script.move:3:9 + │ +3 │ abort true // type error, abort code must be a u64 + │ ^^^^^^^^^^ + │ │ │ + │ │ Given: 'bool' + │ Invalid abort + │ Expected: 'u64' diff --git a/language/move-prover/move-abigen/tests/sources/diem.yaml b/language/move-prover/move-abigen/tests/sources/diem.yaml new file mode 100644 index 0000000000..d9e1a256f6 --- /dev/null +++ b/language/move-prover/move-abigen/tests/sources/diem.yaml @@ -0,0 +1,2 @@ +--- +{} diff --git a/language/move-prover/move-abigen/tests/sources/script_fun_in_module.yaml b/language/move-prover/move-abigen/tests/sources/script_fun_in_module.yaml new file mode 100644 index 0000000000..7494eacf7b --- /dev/null +++ b/language/move-prover/move-abigen/tests/sources/script_fun_in_module.yaml @@ -0,0 +1,18 @@ +Move prover struct abigen returns: No monomorphization provided for generic struct +warning: unused variable + ┌─ tests/sources/script_fun_in_module.move:29:53 + │ +29 │ public entry fun this_is_script_fun_with_signer(account: signer, _another_arg: u64) { } + │ ^^^^^^^ Unused parameter 'account'. Consider removing or prefixing with an underscore: '_account' + +warning: unused variable + ┌─ tests/sources/script_fun_in_module.move:26:57 + │ +26 │ public entry fun this_is_script_fun_with_signer_ref(account: &signer, _another_arg: u64) { } + │ ^^^^^^^ Unused parameter 'account'. Consider removing or prefixing with an underscore: '_account' + +warning: unused variable + ┌─ tests/sources/script_fun_in_module.move:32:58 + │ +32 │ public entry fun this_is_script_fun_with_string_args(account: &signer, _val: String) { } + │ ^^^^^^^ Unused parameter 'account'. Consider removing or prefixing with an underscore: '_account' diff --git a/language/move-prover/move-abigen/tests/sources/some_script.yaml b/language/move-prover/move-abigen/tests/sources/some_script.yaml new file mode 100644 index 0000000000..d9e1a256f6 --- /dev/null +++ b/language/move-prover/move-abigen/tests/sources/some_script.yaml @@ -0,0 +1,2 @@ +--- +{} diff --git a/language/move-prover/move-abigen/tests/sources/structs.move b/language/move-prover/move-abigen/tests/sources/structs.move new file mode 100644 index 0000000000..deec0ed8af --- /dev/null +++ b/language/move-prover/move-abigen/tests/sources/structs.move @@ -0,0 +1,24 @@ +address 0x1 { +module ScriptFunInModule { + struct Coin has drop { + value: u64, + } + + struct Wallet has drop { + coin1: Self::Coin, + coin2: Self::Coin, + } + + struct Marker {} + + #[extract_abi(type = b"0x1::ScriptFunInModule::CoinGeneric<0x1::ScriptFunInModule::Marker>")] + struct CoinGeneric { + value: u64, + } + + #[extract_abi(type = b"0x1::ScriptFunInModule::Container<0x1::ScriptFunInModule::Coin>")] + struct Container { + value: T, + } +} +} \ No newline at end of file diff --git a/language/move-prover/move-abigen/tests/sources/structs.yaml b/language/move-prover/move-abigen/tests/sources/structs.yaml new file mode 100644 index 0000000000..f73eba7b0c --- /dev/null +++ b/language/move-prover/move-abigen/tests/sources/structs.yaml @@ -0,0 +1,20 @@ +--- +Coin: + STRUCT: + - value: I64 +CoinGeneric: + STRUCT: + - value: I64 +Container: + STRUCT: + - value: + TYPENAME: Coin +Marker: + STRUCT: + - dummy_field: BOOL +Wallet: + STRUCT: + - coin1: + TYPENAME: Coin + - coin2: + TYPENAME: Coin diff --git a/language/move-prover/move-abigen/tests/testsuite.rs b/language/move-prover/move-abigen/tests/testsuite.rs index 7fb85a1419..8b5a7534d5 100644 --- a/language/move-prover/move-abigen/tests/testsuite.rs +++ b/language/move-prover/move-abigen/tests/testsuite.rs @@ -15,7 +15,7 @@ use std::{ }; use tempfile::TempDir; -const FLAGS: &[&str] = &["--verbose=warn", "--abigen"]; +const FLAGS: &[&str] = &["--verbose=warn"]; fn test_runner(path: &Path) -> datatest_stable::Result<()> { let mut args = vec!["mvp_test".to_string()]; @@ -30,7 +30,8 @@ fn test_runner(path: &Path) -> datatest_stable::Result<()> { .move_named_address_values .push("std=0x1".to_string()); - test_abigen(path, options, "abi")?; + test_abigen(path, options.clone(), "abi")?; + test_struct_abigen(path, options)?; Ok(()) } @@ -54,6 +55,7 @@ fn get_generated_abis(dir: &Path) -> std::io::Result> { fn test_abigen(path: &Path, mut options: Options, suffix: &str) -> anyhow::Result<()> { let temp_path = PathBuf::from(TempDir::new()?.path()); options.abigen.output_directory = temp_path.to_string_lossy().to_string(); + options.run_abigen = true; let mut error_writer = Buffer::no_color(); match run_move_prover(&mut error_writer, options) { @@ -80,4 +82,30 @@ fn test_abigen(path: &Path, mut options: Options, suffix: &str) -> anyhow::Resul Ok(()) } +fn test_struct_abigen(path: &Path, mut options: Options) -> anyhow::Result<()> { + let temp_dir = TempDir::new()?; + let temp_path = temp_dir.path().join("abi.yaml"); + options.run_struct_abigen = true; + options.struct_abigen.output_path = temp_path.to_string_lossy().to_string(); + + let baseline_path = path.with_extension("yaml"); + + let mut error_writer = Buffer::no_color(); + match run_move_prover(&mut error_writer, options) { + Ok(()) => { + let mut contents = String::new(); + if let Ok(mut file) = File::open(temp_path) { + file.read_to_string(&mut contents).unwrap(); + } + verify_or_update_baseline(&baseline_path, &contents)?; + }, + Err(err) => { + let mut contents = format!("Move prover struct abigen returns: {}\n", err); + contents += &String::from_utf8_lossy(&error_writer.into_inner()); + verify_or_update_baseline(&baseline_path, &contents)?; + }, + }; + Ok(()) +} + datatest_stable::harness!(test_runner, "tests/sources", r".*\.move",); diff --git a/language/move-prover/src/cli.rs b/language/move-prover/src/cli.rs index c4a84e9bc5..16faa4406a 100644 --- a/language/move-prover/src/cli.rs +++ b/language/move-prover/src/cli.rs @@ -10,7 +10,7 @@ use anyhow::anyhow; use clap::{Arg, Command}; use codespan_reporting::diagnostic::Severity; use log::LevelFilter; -use move_abigen::AbigenOptions; +use move_abigen::{AbigenOptions, StructAbigenOptions}; use move_compiler::shared::NumericalAddress; use move_docgen::DocgenOptions; use move_errmapgen::ErrmapOptions; @@ -53,6 +53,8 @@ pub struct Options { pub run_docgen: bool, /// Whether to run the ABI generator instead of the prover. pub run_abigen: bool, + /// Whether to run the struct ABI generator instead of the prover. + pub run_struct_abigen: bool, /// Whether to run the error map generator instead of the prover. pub run_errmapgen: bool, /// Whether to run the read write set analysis instead of the prover @@ -82,6 +84,8 @@ pub struct Options { pub backend: BoogieOptions, /// Options for the ABI generator. pub abigen: AbigenOptions, + /// Options for the struct ABI generator. + pub struct_abigen: StructAbigenOptions, /// Options for the error map generator. /// TODO: this currently create errors during deserialization, so skip them for this. #[serde(skip_serializing)] @@ -94,6 +98,7 @@ impl Default for Options { output_path: "output.bpl".to_string(), run_docgen: false, run_abigen: false, + run_struct_abigen: false, run_errmapgen: false, run_read_write_set: false, run_escape: false, @@ -106,6 +111,7 @@ impl Default for Options { backend: BoogieOptions::default(), docgen: DocgenOptions::default(), abigen: AbigenOptions::default(), + struct_abigen: StructAbigenOptions::default(), errmapgen: ErrmapOptions::default(), experimental_pipeline: false, script_reach: false, @@ -299,6 +305,12 @@ impl Options { .help("runs the ABI generator instead of the prover. \ Generated ABIs will be written into the directory `./abi` unless configured otherwise via toml"), ) + .arg( + Arg::new("struct-abigen") + .long("struct-abigen") + .help("runs the struct ABI generator instead of the prover. \ + Generated ABIs will be written into the file `abi.yaml` unless configured otherwise via toml"), + ) .arg( Arg::new("errmapgen") .long("errmapgen") @@ -680,6 +692,9 @@ impl Options { if matches.is_present("abigen") { options.run_abigen = true; } + if matches.is_present("struct-abigen") { + options.run_struct_abigen = true; + } if matches.is_present("errmapgen") { options.run_errmapgen = true; } diff --git a/language/move-prover/src/lib.rs b/language/move-prover/src/lib.rs index 49a32e60cc..994b27b9d7 100644 --- a/language/move-prover/src/lib.rs +++ b/language/move-prover/src/lib.rs @@ -12,7 +12,7 @@ use codespan_reporting::{ }; #[allow(unused_imports)] use log::{debug, info, warn}; -use move_abigen::Abigen; +use move_abigen::{Abigen, StructAbigen}; use move_compiler::shared::PackagePaths; use move_docgen::Docgen; use move_errmapgen::ErrmapGen; @@ -117,6 +117,12 @@ pub fn run_move_prover_with_model( if options.run_abigen { return run_abigen(env, &options, now); } + + // Same for struct ABI generator. + if options.run_struct_abigen { + return run_struct_abigen(env, &options, now); + } + // Same for the error map generator if options.run_errmapgen { return { @@ -370,6 +376,26 @@ fn run_abigen(env: &GlobalEnv, options: &Options, now: Instant) -> anyhow::Resul Ok(()) } +fn run_struct_abigen(env: &GlobalEnv, options: &Options, now: Instant) -> anyhow::Result<()> { + let mut generator = StructAbigen::new(env, &options.struct_abigen); + let checking_elapsed = now.elapsed(); + info!("generating ABI files"); + generator.gen()?; + + fs::write( + PathBuf::from(&options.struct_abigen.output_path).as_path(), + &serde_yaml::to_vec(&generator.into_result())?, + ) + .unwrap(); + let generating_elapsed = now.elapsed(); + info!( + "{:.3}s checking, {:.3}s generating", + checking_elapsed.as_secs_f64(), + (generating_elapsed - checking_elapsed).as_secs_f64() + ); + Ok(()) +} + fn run_errmapgen(env: &GlobalEnv, options: &Options, now: Instant) { let mut generator = ErrmapGen::new(env, &options.errmapgen); let checking_elapsed = now.elapsed(); From f50faf3f4d27873bd906d07dbbf9b5c79205a5de Mon Sep 17 00:00:00 2001 From: Runtian Zhou Date: Wed, 24 May 2023 21:51:18 -0700 Subject: [PATCH 3/3] fixup! fixup! [struct-api] Implement an extractor --- Cargo.lock | 25 ++++++++++--------------- language/move-prover/Cargo.toml | 1 + 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 22dd8e0105..316c66823a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1494,12 +1494,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31ad93652f40969dead8d4bf897a41e9462095152eb21c56e5830537e41179dd" -[[package]] -name = "dtoa" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d7ed2934d741c6b37e33e3832298e8850b53fd2d2bea03873375596c7cea4e" - [[package]] name = "duct" version = "0.13.5" @@ -2199,9 +2193,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.11.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "heck" @@ -2478,12 +2472,12 @@ checksum = "0cfe9645a18782869361d9c8732246be7b410ad4e919d3609ebabdac00ba12c3" [[package]] name = "indexmap" -version = "1.7.0" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown 0.11.2", + "hashbrown 0.12.3", ] [[package]] @@ -3437,6 +3431,7 @@ dependencies = [ "rand 0.8.4", "serde 1.0.145", "serde_json", + "serde_yaml", "shell-words", "simplelog", "tempfile", @@ -5450,12 +5445,12 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.8.17" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15654ed4ab61726bf918a39cb8d98a2e2995b002387807fa6ba58fdf7f59bb23" +checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" dependencies = [ - "dtoa", - "linked-hash-map", + "indexmap", + "ryu", "serde 1.0.145", "yaml-rust", ] diff --git a/language/move-prover/Cargo.toml b/language/move-prover/Cargo.toml index 875b9e1bc3..9adc63b439 100644 --- a/language/move-prover/Cargo.toml +++ b/language/move-prover/Cargo.toml @@ -37,6 +37,7 @@ pretty = "0.10.0" rand = "0.8.3" serde = { version = "1.0.124", features = ["derive"] } serde_json = "1.0.64" +serde_yaml = "0.8.26" simplelog = "0.9.0" tokio = { version = "1.18.2", features = ["full"] } toml = "0.5.8"