From 81082ed5d64f7c591200ceb9e2ee3bffacd5030c Mon Sep 17 00:00:00 2001 From: vladimirovmm Date: Thu, 2 Sep 2021 18:54:21 +0500 Subject: [PATCH 1/2] updated MetaData --- Cargo.lock | 8 +- dove/src/tx/fn_call.rs | 20 +- dove/src/tx/resolver.rs | 12 +- lang/src/compiler/metadata.rs | 466 +++++++++++++++++++++++++++------- 4 files changed, 393 insertions(+), 113 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a3cc61c3..0f186124 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2594,9 +2594,9 @@ dependencies = [ [[package]] name = "include_dir" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31a924bd335356c7622dff9ee33d06920afcf7f762a1a991236645e08c8a484b" +checksum = "24b56e147e6187d61e9d0f039f10e070d0c0a887e24fe0bb9ca3f29bfde62cab" dependencies = [ "glob", "include_dir_impl", @@ -2605,9 +2605,9 @@ dependencies = [ [[package]] name = "include_dir_impl" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afae3917f781921d7c7813992ccadff7e816f7e6ecb4b70a9ec3e740d51da3d6" +checksum = "0a0c890c85da4bab7bce4204c707396bbd3c6c8a681716a51c8814cfc2b682df" dependencies = [ "anyhow", "proc-macro-hack", diff --git a/dove/src/tx/fn_call.rs b/dove/src/tx/fn_call.rs index fdfe72df..6ce3903d 100644 --- a/dove/src/tx/fn_call.rs +++ b/dove/src/tx/fn_call.rs @@ -58,8 +58,12 @@ pub(crate) fn make_script_call( let (path, meta) = select_function(scripts, addr, &type_tag, &args, &cfg)?; - let (signers, args) = - prepare_function_signature(&meta.parameters, &args, !cfg.deny_signers_definition, addr)?; + let (signers, args) = prepare_function_signature( + &meta.value.parameters, + &args, + !cfg.deny_signers_definition, + addr, + )?; let (signers, mut tx) = match signers { Signers::Explicit(signers) => ( @@ -141,8 +145,12 @@ pub(crate) fn make_function_call( let addr = ctx.account_address()?; let (_, meta) = select_function(functions, addr, &type_tag, &args, &cfg)?; - let (signers, args) = - prepare_function_signature(&meta.parameters, &args, !cfg.deny_signers_definition, addr)?; + let (signers, args) = prepare_function_signature( + &meta.value.parameters, + &args, + !cfg.deny_signers_definition, + addr, + )?; let tx_name = format!("{}_{}", module, func); let (signers, tx) = match signers { @@ -191,10 +199,10 @@ fn select_function( } else if func.len() > 1 { let mut func = func .into_iter() - .filter(|(_, f)| f.type_parameters.len() == type_tag.len()) + .filter(|(_, f)| f.value.type_parameters.len() == type_tag.len()) .filter(|(_, f)| { prepare_function_signature( - &f.parameters, + &f.value.parameters, args, !cfg.deny_signers_definition, addr, diff --git a/dove/src/tx/resolver.rs b/dove/src/tx/resolver.rs index e11b0024..3eb421bf 100644 --- a/dove/src/tx/resolver.rs +++ b/dove/src/tx/resolver.rs @@ -5,7 +5,7 @@ use anyhow::Error; use lang::compiler::file::find_move_files; use std::fs; use regex::Regex; -use lang::compiler::metadata::{module_meta, FuncMeta, script_meta}; +use lang::compiler::metadata::{module_meta, script_meta, FuncMeta}; use std::path::PathBuf; pub(crate) fn find_module_function( @@ -41,12 +41,12 @@ pub(crate) fn find_module_function( }) .flat_map(|(p, m)| { m.into_iter() - .filter(|m| m.address == *address && &m.name == m_name) - .flat_map(|m| m.funs) - .filter(|f| &f.name == f_name) + .filter(|m| m.value.address == *address && &m.value.name == m_name) + .flat_map(|m| m.value.funs) + .filter(|f| &f.value.name == f_name) .filter(|f| { if script_only { - f.visibility.is_script() + f.value.visibility.is_script() } else { false } @@ -80,7 +80,7 @@ pub(crate) fn find_script( }) .flat_map(|(p, m)| { m.into_iter() - .filter(|m| &m.name == name) + .filter(|m| &m.value.name == name) .map(|m| (p.to_owned(), m)) .collect::>() }) diff --git a/lang/src/compiler/metadata.rs b/lang/src/compiler/metadata.rs index a16f503c..85febd06 100644 --- a/lang/src/compiler/metadata.rs +++ b/lang/src/compiler/metadata.rs @@ -2,24 +2,75 @@ use std::collections::HashMap; use anyhow::Error; use move_core_types::account_address::AccountAddress; -use move_lang::{leak_str, parse_file}; +use move_lang::{leak_str, parse_file, MatchedFileCommentMap}; use move_lang::errors::{FilesSourceText, output_errors}; use move_lang::parser::ast::{ Definition, Script, Type, Type_, NameAccessChain_, LeadingNameAccess_, ModuleDefinition, - ModuleMember, Visibility as AstVisibility, + ModuleMember, Visibility as AstVisibility, StructFields, }; use crate::compiler::dialects::Dialect; use crate::compiler::preprocessor::BuilderPreprocessor; use codespan_reporting::term::termcolor::{StandardStream, ColorChoice}; use move_core_types::identifier::Identifier; +use move_ir_types::location::Spanned; +use move_lang::shared::Identifier as Iden; #[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] -pub struct FuncMeta { +pub enum Unit { + Module(ModuleMeta), + Script(FuncMeta), +} + +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] +pub struct FuncMeta_ { pub name: Identifier, pub visibility: Visibility, pub type_parameters: Vec, pub parameters: Vec<(String, String)>, + pub doc: Option, +} + +pub type FuncMeta = Spanned; + +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] +pub struct StructMeta_ { + /// struct Name { ... } + pub name: Identifier, + /// struct Name has ability1, ability2 { ... } + pub abilities: Vec, + /// struct Name { ... } + pub type_parameters: Vec<(String, Vec)>, + /// struct Example { + /// /// doc for field + /// field1: type, + /// field2: u8, + /// field3: u64, + /// ... + /// } + pub fields: Vec, + /// /// Doc text + /// struct Example { + /// ... + /// } + pub doc: Option, +} + +pub type StructMeta = Spanned; + +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] +pub struct StructMetaField_ { + pub name: Identifier, + pub type_field: String, + pub doc: Option, +} + +pub type StructMetaField = Spanned; + +fn processing_docs(docs: &mut MatchedFileCommentMap) { + docs.iter_mut() + .for_each(|(_, doc)| *doc = doc.trim().to_string()); + docs.retain(|_, doc| !doc.is_empty()); } pub fn script_meta( @@ -27,11 +78,14 @@ pub fn script_meta( dialect: &dyn Dialect, sender: &str, ) -> Result, Error> { - Ok(parse(script_path, dialect, sender)? + let (def, mut docs) = parse(script_path, dialect, sender)?; + processing_docs(&mut docs); + + Ok(def .into_iter() .filter_map(|def| { if let Definition::Script(script) = def { - make_script_meta(script).ok() + make_script_meta(script, &docs).ok() } else { None } @@ -54,23 +108,29 @@ impl Visibility { } #[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] -pub struct ModuleMeta { +pub struct ModuleMeta_ { pub address: AccountAddress, pub name: Identifier, pub funs: Vec, + pub structs: Vec, + pub doc: Option, } +pub type ModuleMeta = Spanned; + pub fn module_meta( module_path: &str, dialect: &dyn Dialect, sender: &str, ) -> Result, Error> { let mut modules = Vec::new(); + let (defs, mut docs) = parse(module_path, dialect, sender)?; + processing_docs(&mut docs); - for def in parse(module_path, dialect, sender)? { + for def in defs { match def { Definition::Module(module) => { - modules.push(parse_module_definition(module, None)?); + modules.push(parse_module_definition(module, &docs, None)?); } Definition::Address(def) => { let addr = match def.addr.value { @@ -82,7 +142,7 @@ pub fn module_meta( .map(|addr| AccountAddress::new(addr.value.into_bytes())), }; for def in def.modules { - modules.push(parse_module_definition(def, addr)?); + modules.push(parse_module_definition(def, &docs, addr)?); } } Definition::Script(_) => { @@ -90,19 +150,19 @@ pub fn module_meta( } } } + Ok(modules) } -fn parse( - script_path: &str, - dialect: &dyn Dialect, - sender: &str, -) -> Result, Error> { +type ParseFile = (Vec, MatchedFileCommentMap); + +fn parse(script_path: &str, dialect: &dyn Dialect, sender: &str) -> Result { let mut preprocessor = BuilderPreprocessor::new(dialect, sender); let mut files: FilesSourceText = HashMap::new(); - let (defs, _, errors) = parse_file(&mut files, leak_str(script_path), &mut preprocessor)?; + let (defs, doc, errors) = parse_file(&mut files, leak_str(script_path), &mut preprocessor)?; + if errors.is_empty() { - Ok(defs) + Ok((defs, doc)) } else { let errors = preprocessor.transform(errors); let mut writer = StandardStream::stderr(ColorChoice::Auto); @@ -111,8 +171,9 @@ fn parse( } } -fn make_script_meta(script: Script) -> Result { +fn make_script_meta(script: Script, docs: &MatchedFileCommentMap) -> Result { let func = script.function; + let type_parameters = func .signature .type_parameters @@ -125,12 +186,16 @@ fn make_script_meta(script: Script) -> Result { .into_iter() .map(|(var, tp)| (var.0.value, extract_type_name(tp))) .collect(); - Ok(FuncMeta { - name: Identifier::new(func.name.0.value)?, - visibility: Visibility::Script, - type_parameters, - parameters, - }) + Ok(Spanned::new( + func.loc, + FuncMeta_ { + name: Identifier::new(func.name.0.value)?, + visibility: Visibility::Script, + type_parameters, + parameters, + doc: docs.get(&func.loc.span.start()).cloned(), + }, + )) } fn extract_type_name(tp: Type) -> String { @@ -193,6 +258,7 @@ fn extract_type_name(tp: Type) -> String { fn parse_module_definition( module: ModuleDefinition, + docs: &MatchedFileCommentMap, adds: Option, ) -> Result { let ModuleDefinition { @@ -211,20 +277,20 @@ fn parse_module_definition( .ok_or_else(|| anyhow!("Failed to parse module definition. The module {} does not contain an address definition.", name.0.value))?; let funs = members - .into_iter() + .iter() .filter_map(|member| match member { ModuleMember::Function(func) => { let type_parameters = func .signature .type_parameters - .into_iter() - .map(|tp| tp.0.value) + .iter() + .map(|tp| tp.0.value.to_owned()) .collect(); let parameters = func .signature .parameters - .into_iter() - .map(|(var, tp)| (var.0.value, extract_type_name(tp))) + .iter() + .map(|(var, tp)| (var.0.value.to_owned(), extract_type_name(tp.to_owned()))) .collect(); let visibility = match func.visibility { @@ -234,63 +300,174 @@ fn parse_module_definition( AstVisibility::Internal => Visibility::Internal, }; - Some(FuncMeta { - name: Identifier::new(func.name.0.value).expect("Valid identifier"), - visibility, - type_parameters, - parameters, - }) + Some(Spanned::new( + func.loc, + FuncMeta_ { + name: Identifier::new(func.name.0.value.to_owned()) + .expect("Valid identifier"), + visibility, + type_parameters, + parameters, + doc: docs.get(&func.loc.span.start()).cloned(), + }, + )) } _ => None, }) .collect(); - Ok(ModuleMeta { - address, - name: Identifier::new(name.0.value)?, - funs, - }) + let structs = members + .into_iter() + .filter_map(|member| match member { + ModuleMember::Struct(struc) => { + let abilities = struc + .abilities + .iter() + .map(|ab| ab.value.to_string()) + .collect(); + let fields = match struc.fields { + StructFields::Defined(fields) => fields + .iter() + .map(|(name, tp)| { + Spanned::new( + name.loc(), + StructMetaField_ { + name: Identifier::new(name.to_string()) + .expect("Valid identifier"), + type_field: extract_type_name(tp.clone()), + doc: docs.get(&name.loc().span.start()).cloned(), + }, + ) + }) + .collect(), + _ => Vec::new(), + }; + let type_parameters = struc + .type_parameters + .iter() + .map(|(name, ab)| { + let ab = ab.iter().map(|ab| ab.to_string()).collect(); + (name.to_string(), ab) + }) + .collect(); + Some(Spanned::new( + struc.loc, + StructMeta_ { + name: Identifier::new(struc.name.0.value).expect("Valid identifier"), + abilities, + type_parameters, + fields, + doc: docs.get(&struc.loc.span.start()).cloned(), + }, + )) + } + _ => None, + }) + .collect(); + + Ok(Spanned::new( + module.loc, + ModuleMeta_ { + address, + name: Identifier::new(name.0.value)?, + funs, + structs, + doc: docs.get(&module.loc.span.start()).cloned(), + }, + )) } #[cfg(test)] mod metadata_tests { - use crate::compiler::metadata::{module_meta, ModuleMeta, FuncMeta, Visibility, script_meta}; + use crate::compiler::metadata::{ + module_meta, ModuleMeta_, FuncMeta_, Visibility, script_meta, StructMeta_, + StructMetaField_, StructMetaField, + }; use crate::compiler::dialects::DialectName; use tempfile::NamedTempFile; use std::io::Write; use move_core_types::language_storage::CORE_CODE_ADDRESS; use move_core_types::identifier::Identifier; use move_core_types::account_address::AccountAddress; + use move_ir_types::location::{Spanned, Loc}; + use codespan::Span; - #[test] - fn test_module_meta() { - let source = r" -address 0x1 { -module Empty {} + fn spanned_wrap(value: T) -> Spanned { + Spanned::new(Loc::new("none", Span::new(0, 0)), value) + } + fn create_field(name: &str, tp: &str, doc: Option<&str>) -> StructMetaField { + spanned_wrap(StructMetaField_ { + name: Identifier::new(name).unwrap(), + type_field: tp.to_string(), + doc: doc.map(|d| d.to_string()), + }) + } -module FuncsVisability { - fun f1() {} + trait ToSpannded { + fn to_spanned(self) -> Spanned + where + Self: Sized, + { + spanned_wrap(self) + } + } - public fun f2() {} + impl ToSpannded for FuncMeta_ {} + impl ToSpannded for ModuleMeta_ {} + impl ToSpannded for StructMeta_ {} - public(script) fun f3() {} + #[test] + fn test_module_meta() { + let source = r" + address 0x1 { + module Empty {} + + /// doc for module + module StructsModule{ + struct Empty {} + /// doc for stucture + struct Example { + /// doc for field + field1: u8, + field2: u64, + field3: address, + field4: bool, + field5: Empty + } + struct Example2 has copy, drop { + field1: bool, + field2: T + } + } + module FuncsVisability { - public(friend) fun f4() {} - native fun f5(); - native public fun f6(); -} -} + struct MyStruct { + field1: bool, + } + /// doc for function + fun f1() {} + /* + not doc type comment + */ + public fun f2() {} + // not doc type comment + public(script) fun f3() {} + + public(friend) fun f4() {} + native fun f5(); + native public fun f6(); + } + } -module 0x2::FuncsTp { - public(script) fun f1() {} - public(script) fun f2() {} -} + module 0x2::FuncsTp { + public(script) fun f1() {} + public(script) fun f2() {} + } -module 0x3::FuncsArgs { - public(script) fun f1() {} - public(script) fun f2(_d: signer, d: u8) {} -} - "; + module 0x3::FuncsArgs { + public(script) fun f1() {} + public(script) fun f2(_d: signer, d: u8) {} + }"; let mut module = NamedTempFile::new().unwrap(); module.write_all(source.as_bytes()).unwrap(); @@ -306,82 +483,161 @@ module 0x3::FuncsArgs { assert_eq!( defs, vec![ - ModuleMeta { + ModuleMeta_ { address: CORE_CODE_ADDRESS, name: Identifier::new("Empty").unwrap(), funs: vec![], - }, - ModuleMeta { + structs: vec![], + doc: None + } + .to_spanned(), + ModuleMeta_ { + address: CORE_CODE_ADDRESS, + name: Identifier::new("StructsModule").unwrap(), + funs: vec![], + structs: vec![ + StructMeta_ { + name: Identifier::new("Empty").unwrap(), + abilities: vec![], + type_parameters: vec![], + fields: vec![], + doc: None + } + .to_spanned(), + StructMeta_ { + name: Identifier::new("Example").unwrap(), + abilities: vec![], + type_parameters: vec![], + fields: vec![ + create_field("field1", "u8", Some("doc for field")), + create_field("field2", "u64", None), + create_field("field3", "address", None), + create_field("field4", "bool", None), + create_field("field5", "Empty", None) + ], + doc: Some("doc for stucture".to_string()) + } + .to_spanned(), + StructMeta_ { + name: Identifier::new("Example2").unwrap(), + abilities: vec!["copy".to_string(), "drop".to_string()], + type_parameters: vec![( + "T".to_string(), + vec!["copy".to_string(), "drop".to_string()] + )], + fields: vec![ + create_field("field1", "bool", None), + create_field("field2", "T", None), + ], + doc: None + } + .to_spanned() + ], + doc: Some("doc for module".to_string()) + } + .to_spanned(), + ModuleMeta_ { address: CORE_CODE_ADDRESS, name: Identifier::new("FuncsVisability").unwrap(), funs: vec![ - FuncMeta { + FuncMeta_ { name: Identifier::new("f1").unwrap(), visibility: Visibility::Internal, type_parameters: vec![], parameters: vec![], - }, - FuncMeta { + doc: Some("doc for function".to_string()) + } + .to_spanned(), + FuncMeta_ { name: Identifier::new("f2").unwrap(), visibility: Visibility::Public, type_parameters: vec![], parameters: vec![], - }, - FuncMeta { + doc: None + } + .to_spanned(), + FuncMeta_ { name: Identifier::new("f3").unwrap(), visibility: Visibility::Script, type_parameters: vec![], parameters: vec![], - }, - FuncMeta { + doc: None + } + .to_spanned(), + FuncMeta_ { name: Identifier::new("f4").unwrap(), visibility: Visibility::Friend, type_parameters: vec![], parameters: vec![], - }, - FuncMeta { + doc: None + } + .to_spanned(), + FuncMeta_ { name: Identifier::new("f5").unwrap(), visibility: Visibility::Internal, type_parameters: vec![], parameters: vec![], - }, - FuncMeta { + doc: None + } + .to_spanned(), + FuncMeta_ { name: Identifier::new("f6").unwrap(), visibility: Visibility::Public, type_parameters: vec![], parameters: vec![], - }, + doc: None + } + .to_spanned(), ], - }, - ModuleMeta { + structs: vec![StructMeta_ { + name: Identifier::new("MyStruct").unwrap(), + abilities: vec![], + type_parameters: vec![], + fields: vec![create_field("field1", "bool", None)], + doc: None + } + .to_spanned()], + doc: None + } + .to_spanned(), + ModuleMeta_ { address: AccountAddress::from_hex_literal("0x2").unwrap(), name: Identifier::new("FuncsTp").unwrap(), funs: vec![ - FuncMeta { + FuncMeta_ { name: Identifier::new("f1").unwrap(), visibility: Visibility::Script, type_parameters: vec!["T".to_string(), "D".to_string()], parameters: vec![], - }, - FuncMeta { + doc: None + } + .to_spanned(), + FuncMeta_ { name: Identifier::new("f2").unwrap(), visibility: Visibility::Script, type_parameters: vec![], parameters: vec![], - }, + doc: None + } + .to_spanned(), ], - }, - ModuleMeta { + structs: vec![], + doc: None + } + .to_spanned(), + ModuleMeta_ { address: AccountAddress::from_hex_literal("0x3").unwrap(), name: Identifier::new("FuncsArgs").unwrap(), funs: vec![ - FuncMeta { + FuncMeta_ { name: Identifier::new("f1").unwrap(), visibility: Visibility::Script, type_parameters: vec![], parameters: vec![], - }, - FuncMeta { + doc: None + } + .to_spanned(), + FuncMeta_ { name: Identifier::new("f2").unwrap(), visibility: Visibility::Script, type_parameters: vec![], @@ -389,9 +645,14 @@ module 0x3::FuncsArgs { ("_d".to_string(), "signer".to_string(),), ("d".to_string(), "u8".to_string(),), ], - }, + doc: None + } + .to_spanned(), ], - }, + structs: vec![], + doc: None + } + .to_spanned(), ] ); } @@ -400,14 +661,19 @@ module 0x3::FuncsArgs { fn test_script_meta() { let source = r" script { + /// doc for function fun main() { } } script { + // not doc type comment fun main_1(_d: signer) { } } script { + /* + not doc type comment + */ fun main_2(_d: signer) { } } @@ -427,24 +693,30 @@ module 0x3::FuncsArgs { assert_eq!( defs, vec![ - FuncMeta { + FuncMeta_ { name: Identifier::new("main").unwrap(), visibility: Visibility::Script, type_parameters: vec![], parameters: vec![], - }, - FuncMeta { + doc: Some("doc for function".to_string()) + } + .to_spanned(), + FuncMeta_ { name: Identifier::new("main_1").unwrap(), visibility: Visibility::Script, type_parameters: vec![], parameters: vec![("_d".to_string(), "signer".to_string(),)], - }, - FuncMeta { + doc: None + } + .to_spanned(), + FuncMeta_ { name: Identifier::new("main_2").unwrap(), visibility: Visibility::Script, type_parameters: vec!["T".to_string()], parameters: vec![("_d".to_string(), "signer".to_string(),)], - }, + doc: None + } + .to_spanned(), ] ); } From c24a3076c0cb807982e863f7c72dcb6e1c48bca8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=92=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=B8=D0=BC=D0=B8=D1=80=D0=BE=D0=B2?= <32911557+vladimirovmm@users.noreply.github.com> Date: Wed, 8 Sep 2021 23:48:01 +0500 Subject: [PATCH 2/2] save project map + .system folder (#144) --- dove/src/context.rs | 9 +- dove/src/lib.rs | 2 + dove/src/manifest.rs | 22 ++- dove/src/metadata.rs | 148 ++++++++++++++++++ dove/src/tx/resolver.rs | 10 +- lang/src/compiler/file.rs | 18 +-- .../compiler/{metadata.rs => metadata/mod.rs} | 122 ++++++++------- lang/src/compiler/metadata/spanned.rs | 130 +++++++++++++++ 8 files changed, 387 insertions(+), 74 deletions(-) create mode 100644 dove/src/metadata.rs rename lang/src/compiler/{metadata.rs => metadata/mod.rs} (90%) create mode 100644 lang/src/compiler/metadata/spanned.rs diff --git a/dove/src/context.rs b/dove/src/context.rs index 4184f321..f75a2f72 100644 --- a/dove/src/context.rs +++ b/dove/src/context.rs @@ -10,6 +10,7 @@ use crate::index::Index; use crate::manifest::{default_dialect, DoveToml, MANIFEST, read_manifest}; use diem_crypto::hash::CryptoHash; use crate::index::interface::{InterfaceBuilder, Interface}; +use crate::metadata::MapDependencies; /// Project context. pub struct Context { @@ -63,6 +64,7 @@ impl Context { new_index.store(&index_path)?; new_index.remove_unused(old_index.diff(&new_index))?; new_index.remove_unnecessary_elements_in_dependencies(); + MapDependencies::create_and_save(self)?; new_index }; let builder = InterfaceBuilder::new(self, &index); @@ -102,18 +104,19 @@ impl Context { /// Returns interface files dir. pub fn interface_files_dir(&self) -> PathBuf { - self.path_for(&self.manifest.layout.artifacts) + self.path_for(&self.manifest.layout.system_folder) .join("interface_files_dir") } /// Returns directory for dependency bytecode. pub fn deps_mv_dir(&self) -> PathBuf { - self.path_for(&self.manifest.layout.artifacts).join("depmv") + self.path_for(&self.manifest.layout.system_folder) + .join("depmv") } /// Interface files lock. pub fn interface_files_lock(&self) -> PathBuf { - self.path_for(&self.manifest.layout.artifacts) + self.path_for(&self.manifest.layout.system_folder) .join("interface.lock") } } diff --git a/dove/src/lib.rs b/dove/src/lib.rs index 252242dc..0667d69b 100644 --- a/dove/src/lib.rs +++ b/dove/src/lib.rs @@ -36,6 +36,8 @@ pub mod executor; pub mod index; /// Dove configuration. pub mod manifest; +/// Metadata from project +pub mod metadata; /// StdOut stream pub mod stdout; #[doc(hidden)] diff --git a/dove/src/manifest.rs b/dove/src/manifest.rs index 47b0ec04..7e0a13e9 100644 --- a/dove/src/manifest.rs +++ b/dove/src/manifest.rs @@ -129,7 +129,7 @@ fn artifacts() -> String { } fn index() -> String { - format!("{}{}{}", artifacts(), MS, ".DoveIndex.toml") + format!("{}{}{}", system_folder(), MS, ".DoveIndex.toml") } fn storage_dir() -> String { @@ -144,6 +144,14 @@ fn code_code_address() -> String { "0x1".to_string() } +fn system_folder() -> String { + format!("{}{}{}", artifacts(), MS, ".system") +} + +fn project_map() -> String { + format!("{}{}{}", system_folder(), MS, "project.map") +} + /// Project layout. #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)] pub struct Layout { @@ -203,6 +211,14 @@ pub struct Layout { /// Path to pover settings #[serde(default = "prover_toml")] pub prover_toml: String, + + /// Path t + #[serde(default = "system_folder")] + pub system_folder: String, + + /// Path to project map + #[serde(default = "project_map")] + pub project_map: String, } impl Layout { @@ -225,6 +241,8 @@ impl Layout { storage_dir: ctx.str_path_for(&self.storage_dir)?, exe_build_dir: ctx.str_path_for(&self.exe_build_dir)?, prover_toml: ctx.str_path_for(&self.prover_toml)?, + system_folder: ctx.str_path_for(&self.system_folder)?, + project_map: ctx.str_path_for(&self.project_map)?, }) } } @@ -248,6 +266,8 @@ impl Default for Layout { storage_dir: storage_dir(), exe_build_dir: exe_build_dir(), prover_toml: prover_toml(), + system_folder: system_folder(), + project_map: project_map(), } } } diff --git a/dove/src/metadata.rs b/dove/src/metadata.rs new file mode 100644 index 00000000..2be69ed2 --- /dev/null +++ b/dove/src/metadata.rs @@ -0,0 +1,148 @@ +use std::fs; +use std::fs::{create_dir_all, read_to_string}; +use anyhow::Error; +use serde::{Deserialize, Serialize}; +use lang::compiler::metadata::{Unit, parse, FuncMeta, ModuleMeta}; +use lang::compiler::file::is_move_file; +use crate::context::Context; +use std::path::PathBuf; +use crate::manifest::DoveToml; + +/// All modules and scripts from dependencies +pub(crate) fn get_all_dependencies(ctx: &Context) -> Result, Error> { + let external_folder = ctx.path_for(&ctx.manifest.layout.deps); + if !external_folder.exists() { + return Ok(Vec::new()); + } + + let sender = ctx.account_address_str()?; + let dialect = ctx.dialect.as_ref(); + + let externals = external_folder + .read_dir()? + .filter_map(|path| path.ok()) + .map(|path| path.path()) + .filter(|path| { + path.is_dir() + && path + .file_name() + .and_then(|name| name.to_str()) + .unwrap_or_default() + .starts_with("git_") + }) + .map(test_folder) + .map(|(project_path, test_path)| { + move_files_without_tests(project_path, test_path.as_ref()) + }) + .flatten() + .collect::>(); + + let meta = externals + .iter() + .filter_map(|path| parse(&path.to_string_lossy(), dialect, &sender).ok()) + .flatten() + .collect(); + + Ok(meta) +} + +fn test_folder(path: PathBuf) -> (PathBuf, Option) { + let dove_path = path.join("Dove.toml"); + if !dove_path.exists() { + return (path, None); + } + + let test = read_to_string(dove_path) + .ok() + .and_then(|str| toml::from_str::(&str).ok()) + .map(|dovetoml| path.join(dovetoml.layout.tests_dir)); + + (path, test) +} +fn move_files_without_tests( + project_folder: PathBuf, + test_folder: Option<&PathBuf>, +) -> Vec { + project_folder + .read_dir() + .ok() + .map(|read| { + read.into_iter() + .filter_map(|path| path.ok()) + .map(|path| path.path()) + .filter(|path| { + !test_folder + .map(|test| path.starts_with(test)) + .unwrap_or(false) + }) + .filter_map(|path| { + if path.is_dir() { + Some(move_files_without_tests(path, test_folder)) + } else if path.is_file() && is_move_file(&path) { + Some(vec![path]) + } else { + None + } + }) + .flatten() + .collect::>() + }) + .unwrap_or_default() +} + +/// All scripts and modules from dependencies +#[derive(Debug, Serialize, Deserialize)] +pub struct MapDependencies { + /// All scripts from dependencies + pub scripts: Vec, + /// All modules from dependencies + pub modules: Vec, +} +impl MapDependencies { + /// Create a dependencies map and save it to disk + pub fn create_and_save(ctx: &Context) -> Result<(), Error> { + let fpath = ctx.path_for(&ctx.manifest.layout.project_map); + if !fpath.exists() { + let parent = fpath.parent().map_or_else( + || anyhow::bail!("The path to the dependencies map is set incorrectly"), + Ok, + )?; + if !parent.exists() { + create_dir_all(parent)?; + } + } + + let map = Self::create(ctx)?; + let bmap = bcs::to_bytes(&map)?; + fs::write(&fpath, bmap).map_err(|err| anyhow!("{}", err.to_string())) + } + + /// Get all scripts and modules from dependencies + pub fn create(ctx: &Context) -> Result { + let deps = get_all_dependencies(ctx)?; + + let (scripts, modules) = deps.into_iter().fold( + (Vec::new(), Vec::new()), + |(mut scripts, mut modules), unit| { + match unit { + Unit::Module(module) => modules.push(module), + Unit::Script(script) => scripts.push(script), + } + (scripts, modules) + }, + ); + + Ok(MapDependencies { scripts, modules }) + } + + /// Download a dependencies map from disk + pub fn load(ctx: &Context) -> Result { + let fpath = ctx.path_for(&ctx.manifest.layout.project_map); + if !fpath.exists() { + anyhow::bail!("The project map file was not found. Build a project."); + } + + let bmap = fs::read(&fpath)?; + bcs::from_bytes::(&bmap).map_err(|err| anyhow!("{:?}", err)) + } +} diff --git a/dove/src/tx/resolver.rs b/dove/src/tx/resolver.rs index 3eb421bf..4fdbd3ef 100644 --- a/dove/src/tx/resolver.rs +++ b/dove/src/tx/resolver.rs @@ -1,12 +1,12 @@ +use std::fs; +use std::path::PathBuf; +use anyhow::Error; +use regex::Regex; use move_core_types::account_address::AccountAddress; use move_core_types::identifier::Identifier; -use crate::context::Context; -use anyhow::Error; use lang::compiler::file::find_move_files; -use std::fs; -use regex::Regex; use lang::compiler::metadata::{module_meta, script_meta, FuncMeta}; -use std::path::PathBuf; +use crate::context::Context; pub(crate) fn find_module_function( ctx: &Context, diff --git a/lang/src/compiler/file.rs b/lang/src/compiler/file.rs index eb9ef55d..2394a728 100644 --- a/lang/src/compiler/file.rs +++ b/lang/src/compiler/file.rs @@ -34,7 +34,7 @@ impl<'a, P: AsRef> Iterator for Files<'a, P> { loop { if let Some(entry) = self.next_entry() { if let Ok(entry) = entry { - if is_move_file(&entry) { + if is_move_file(entry.path()) { return Some(entry.into_path()); } } @@ -44,19 +44,15 @@ impl<'a, P: AsRef> Iterator for Files<'a, P> { } } } - -fn is_move_file(entry: &DirEntry) -> bool { - entry.file_type().is_file() +/// Check whether the file is a move +pub fn is_move_file(entry: &Path) -> bool { + entry.is_file() && !entry .file_name() - .to_str() + .and_then(|name| name.to_str()) .map(|name| name.starts_with('.')) .unwrap_or(true) - && entry - .path() - .extension() - .map(|ext| ext.eq("move")) - .unwrap_or(false) + && entry.extension().map(|ext| ext.eq("move")).unwrap_or(false) } pub fn find_move_files

(paths: &[P]) -> Files

@@ -78,7 +74,7 @@ where .into_iter() .filter_map(|entry| match entry { Ok(entry) => { - if is_move_file(&entry) { + if is_move_file(entry.path()) { let path = entry.into_path(); if filter(&path) { Some(Ok(path)) diff --git a/lang/src/compiler/metadata.rs b/lang/src/compiler/metadata/mod.rs similarity index 90% rename from lang/src/compiler/metadata.rs rename to lang/src/compiler/metadata/mod.rs index 85febd06..a316a163 100644 --- a/lang/src/compiler/metadata.rs +++ b/lang/src/compiler/metadata/mod.rs @@ -1,5 +1,5 @@ use std::collections::HashMap; - +use serde::{Deserialize, Serialize}; use anyhow::Error; use move_core_types::account_address::AccountAddress; use move_lang::{leak_str, parse_file, MatchedFileCommentMap}; @@ -13,16 +13,33 @@ use crate::compiler::dialects::Dialect; use crate::compiler::preprocessor::BuilderPreprocessor; use codespan_reporting::term::termcolor::{StandardStream, ColorChoice}; use move_core_types::identifier::Identifier; -use move_ir_types::location::Spanned; use move_lang::shared::Identifier as Iden; -#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] +pub mod spanned; +use spanned::Spanned; + +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize)] pub enum Unit { Module(ModuleMeta), Script(FuncMeta), } - -#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] +impl Unit { + /// Converts from Unit to Option. + pub fn module(&self) -> Option<&ModuleMeta> { + match self { + Unit::Module(module) => Some(module), + _ => None, + } + } + /// Converts from Unit to Option. + pub fn script(&self) -> Option<&FuncMeta> { + match self { + Unit::Script(script) => Some(script), + _ => None, + } + } +} +#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize)] pub struct FuncMeta_ { pub name: Identifier, pub visibility: Visibility, @@ -33,7 +50,7 @@ pub struct FuncMeta_ { pub type FuncMeta = Spanned; -#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] +#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize)] pub struct StructMeta_ { /// struct Name { ... } pub name: Identifier, @@ -58,7 +75,7 @@ pub struct StructMeta_ { pub type StructMeta = Spanned; -#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] +#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize)] pub struct StructMetaField_ { pub name: Identifier, pub type_field: String, @@ -78,22 +95,15 @@ pub fn script_meta( dialect: &dyn Dialect, sender: &str, ) -> Result, Error> { - let (def, mut docs) = parse(script_path, dialect, sender)?; - processing_docs(&mut docs); - - Ok(def - .into_iter() - .filter_map(|def| { - if let Definition::Script(script) = def { - make_script_meta(script, &docs).ok() - } else { - None - } - }) - .collect::>()) + parse(script_path, dialect, sender).map(|list| { + list.iter() + .filter_map(|unit| unit.script()) + .cloned() + .collect::>() + }) } -#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] +#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize)] pub enum Visibility { Public, Script, @@ -107,7 +117,7 @@ impl Visibility { } } -#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] +#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize)] pub struct ModuleMeta_ { pub address: AccountAddress, pub name: Identifier, @@ -123,14 +133,35 @@ pub fn module_meta( dialect: &dyn Dialect, sender: &str, ) -> Result, Error> { - let mut modules = Vec::new(); - let (defs, mut docs) = parse(module_path, dialect, sender)?; + parse(module_path, dialect, sender).map(|list| { + list.iter() + .filter_map(|unit| unit.module()) + .cloned() + .collect() + }) +} + +/// Get metadata from move file +pub fn parse(script_path: &str, dialect: &dyn Dialect, sender: &str) -> Result, Error> { + let mut preprocessor = BuilderPreprocessor::new(dialect, sender); + let mut files: FilesSourceText = HashMap::new(); + let (defs, mut docs, errors) = + parse_file(&mut files, leak_str(script_path), &mut preprocessor)?; + + if !errors.is_empty() { + let errors = preprocessor.transform(errors); + let mut writer = StandardStream::stderr(ColorChoice::Auto); + output_errors(&mut writer, files, errors); + anyhow::bail!("Could not compile scripts '{}'.", script_path); + } + + let mut list_unit = Vec::new(); processing_docs(&mut docs); for def in defs { match def { Definition::Module(module) => { - modules.push(parse_module_definition(module, &docs, None)?); + list_unit.push(Unit::Module(parse_module_definition(module, &docs, None)?)); } Definition::Address(def) => { let addr = match def.addr.value { @@ -142,33 +173,16 @@ pub fn module_meta( .map(|addr| AccountAddress::new(addr.value.into_bytes())), }; for def in def.modules { - modules.push(parse_module_definition(def, &docs, addr)?); + list_unit.push(Unit::Module(parse_module_definition(def, &docs, addr)?)); } } - Definition::Script(_) => { - // no-op + Definition::Script(script) => { + list_unit.push(Unit::Script(make_script_meta(script, &docs)?)) } } } - Ok(modules) -} - -type ParseFile = (Vec, MatchedFileCommentMap); - -fn parse(script_path: &str, dialect: &dyn Dialect, sender: &str) -> Result { - let mut preprocessor = BuilderPreprocessor::new(dialect, sender); - let mut files: FilesSourceText = HashMap::new(); - let (defs, doc, errors) = parse_file(&mut files, leak_str(script_path), &mut preprocessor)?; - - if errors.is_empty() { - Ok((defs, doc)) - } else { - let errors = preprocessor.transform(errors); - let mut writer = StandardStream::stderr(ColorChoice::Auto); - output_errors(&mut writer, files, errors); - Err(anyhow!("Could not compile scripts '{}'.", script_path)) - } + Ok(list_unit) } fn make_script_meta(script: Script, docs: &MatchedFileCommentMap) -> Result { @@ -187,7 +201,7 @@ fn make_script_meta(script: Script, docs: &MatchedFileCommentMap) -> Result(value: T) -> Spanned { - Spanned::new(Loc::new("none", Span::new(0, 0)), value) + Spanned::new(Loc::new("none".to_string(), Span::new(0, 0)), value) } fn create_field(name: &str, tp: &str, doc: Option<&str>) -> StructMetaField { spanned_wrap(StructMetaField_ { diff --git a/lang/src/compiler/metadata/spanned.rs b/lang/src/compiler/metadata/spanned.rs new file mode 100644 index 00000000..447098ef --- /dev/null +++ b/lang/src/compiler/metadata/spanned.rs @@ -0,0 +1,130 @@ +use serde::{Deserialize, Serialize}; +use std::{ + cmp::Ordering, + fmt, + hash::{Hash, Hasher}, +}; +use codespan::Span; +use move_ir_types::location::Loc as LocDiem; + +//************************************************************************************************** +// Spanned +//************************************************************************************************** + +#[derive(Clone, Serialize, Deserialize)] +pub struct Spanned { + pub loc: Loc, + pub value: T, +} + +impl Spanned { + pub fn new(loc: Loc, value: T) -> Spanned { + Spanned { loc, value } + } + + const NO_LOC_FILE: &'static str = ""; + pub fn unsafe_no_loc(value: T) -> Spanned { + Spanned { + value, + loc: Loc::new(Self::NO_LOC_FILE.to_string(), Span::default()), + } + } +} + +impl PartialEq for Spanned { + fn eq(&self, other: &Spanned) -> bool { + self.value == other.value + } +} + +impl Eq for Spanned {} + +impl Hash for Spanned { + fn hash(&self, state: &mut H) { + self.value.hash(state); + } +} + +impl PartialOrd for Spanned { + fn partial_cmp(&self, other: &Spanned) -> Option { + self.value.partial_cmp(&other.value) + } +} + +impl Ord for Spanned { + fn cmp(&self, other: &Spanned) -> Ordering { + self.value.cmp(&other.value) + } +} + +impl fmt::Display for Spanned { + fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", &self.value) + } +} + +impl fmt::Debug for Spanned { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", &self.value) + } +} + +impl From for Loc { + fn from(locdiem: LocDiem) -> Loc { + Loc { + span: locdiem.span, + file: locdiem.file.to_string(), + } + } +} + +/// Function used to have nearly tuple-like syntax for creating a Spanned +pub const fn sp(loc: Loc, value: T) -> Spanned { + Spanned { loc, value } +} + +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] +pub struct Loc { + pub file: String, + pub span: Span, +} +impl Loc { + pub fn new(file: String, span: Span) -> Loc { + Loc { file, span } + } + + pub fn file(self) -> String { + self.file + } + + pub fn span(self) -> Span { + self.span + } +} + +impl PartialOrd for Loc { + fn partial_cmp(&self, other: &Loc) -> Option { + let file_ord = self.file.partial_cmp(&other.file)?; + if file_ord != Ordering::Equal { + return Some(file_ord); + } + + let start_ord = self.span.start().partial_cmp(&other.span.start())?; + if start_ord != Ordering::Equal { + return Some(start_ord); + } + + self.span.end().partial_cmp(&other.span.end()) + } +} + +impl Ord for Loc { + fn cmp(&self, other: &Loc) -> Ordering { + self.file.cmp(&other.file).then_with(|| { + self.span + .start() + .cmp(&other.span.start()) + .then_with(|| self.span.end().cmp(&other.span.end())) + }) + } +}