From d11bddd0692b111f3d6a28d76e7c639173583b02 Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Thu, 11 Jul 2024 13:23:34 +1000 Subject: [PATCH 01/29] get and insert ty_modules --- sway-core/src/query_engine/mod.rs | 21 +++++++++++++- sway-core/src/semantic_analysis/module.rs | 34 +++++++++++++++++------ 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/sway-core/src/query_engine/mod.rs b/sway-core/src/query_engine/mod.rs index 8f7e248291f..bace080fe7f 100644 --- a/sway-core/src/query_engine/mod.rs +++ b/sway-core/src/query_engine/mod.rs @@ -5,7 +5,7 @@ use sway_error::warning::CompileWarning; use sway_types::IdentUnique; use crate::decl_engine::{DeclId, DeclRef}; -use crate::language::ty::{TyFunctionDecl, TyFunctionSig}; +use crate::language::ty::{TyFunctionDecl, TyFunctionSig, TyModule}; use crate::{Engines, Programs}; pub type ModulePath = Arc; @@ -46,6 +46,13 @@ pub struct ProgramsCacheEntry { pub type ProgramsCacheMap = HashMap, ProgramsCacheEntry>; +#[derive(Clone, Debug)] +pub struct TyModuleCacheEntry { + pub path: Arc, + pub module: TyModule, +} +pub type TyModuleCacheMap = HashMap, TyModuleCacheEntry>; + #[derive(Clone, Debug)] pub struct FunctionCacheEntry { pub fn_decl: DeclRef>, @@ -59,6 +66,8 @@ pub struct QueryEngine { parse_module_cache: Arc>, programs_cache: Arc>, function_cache: Arc>, + + ty_module_cache: Arc>, } impl QueryEngine { @@ -85,6 +94,16 @@ impl QueryEngine { cache.insert(entry.path.clone(), entry); } + pub fn get_ty_module_cache_entry(&self, path: &PathBuf) -> Option { + let cache = self.ty_module_cache.read(); + cache.get(path).cloned() + } + + pub fn insert_ty_module_cache_entry(&self, entry: TyModuleCacheEntry) { + let mut cache = self.ty_module_cache.write(); + cache.insert(entry.path.clone(), entry); + } + pub fn get_function( &self, engines: &Engines, diff --git a/sway-core/src/semantic_analysis/module.rs b/sway-core/src/semantic_analysis/module.rs index 2edc16ae3b7..d4cc7585d5e 100644 --- a/sway-core/src/semantic_analysis/module.rs +++ b/sway-core/src/semantic_analysis/module.rs @@ -1,9 +1,10 @@ use std::{ collections::{HashMap, HashSet}, fmt::Display, - fs, + fs, sync::Arc, }; +use fuel_vm::fuel_crypto::coins_bip32::path; use graph_cycles::Cycles; use sway_error::{ error::CompileError, @@ -12,15 +13,11 @@ use sway_error::{ use sway_types::{BaseIdent, Named}; use crate::{ - decl_engine::{DeclEngineGet, DeclId}, - engine_threading::DebugWithEngines, - language::{ + decl_engine::{DeclEngineGet, DeclId}, engine_threading::DebugWithEngines, language::{ parsed::*, ty::{self, TyAstNodeContent, TyDecl}, CallPath, ModName, - }, - semantic_analysis::*, - Engines, TypeInfo, + }, query_engine::TyModuleCacheEntry, semantic_analysis::*, Engines, TypeInfo }; use super::{ @@ -264,6 +261,14 @@ impl ty::TyModule { kind: TreeType, parsed: &ParseModule, ) -> Result { + // Check if the module is already in the cache + if let Some(source_id) = parsed.span.source_id() { + let path = engines.se().get_path(&source_id); + let entry = engines.qe().get_ty_module_cache_entry(&path).unwrap(); + // Return the cached module + return Ok(entry.module); + } + let ParseModule { submodules, tree, @@ -374,13 +379,24 @@ impl ty::TyModule { } } - Ok(Self { + let ty_module = Self { span: span.clone(), submodules, namespace: ctx.namespace.clone(), all_nodes, attributes: attributes.clone(), - }) + }; + + // Cache the ty module + if let Some(source_id) = span.source_id() { + let path = engines.se().get_path(&source_id); + engines.qe().insert_ty_module_cache_entry(TyModuleCacheEntry { + path: Arc::new(path), + module: ty_module.clone(), + }); + } + + Ok(ty_module) } // Filter and gather impl items From 2413bfe1598aa2882c63052706d44788bf7ac95e Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Thu, 11 Jul 2024 13:38:53 +1000 Subject: [PATCH 02/29] add fusd test --- sway-core/src/semantic_analysis/module.rs | 11 +++++++---- sway-lsp/tests/lib.rs | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/sway-core/src/semantic_analysis/module.rs b/sway-core/src/semantic_analysis/module.rs index d4cc7585d5e..6253638bbe9 100644 --- a/sway-core/src/semantic_analysis/module.rs +++ b/sway-core/src/semantic_analysis/module.rs @@ -17,7 +17,7 @@ use crate::{ parsed::*, ty::{self, TyAstNodeContent, TyDecl}, CallPath, ModName, - }, query_engine::TyModuleCacheEntry, semantic_analysis::*, Engines, TypeInfo + }, query_engine::TyModuleCacheEntry, semantic_analysis::*, BuildConfig, Engines, TypeInfo }; use super::{ @@ -264,9 +264,12 @@ impl ty::TyModule { // Check if the module is already in the cache if let Some(source_id) = parsed.span.source_id() { let path = engines.se().get_path(&source_id); - let entry = engines.qe().get_ty_module_cache_entry(&path).unwrap(); - // Return the cached module - return Ok(entry.module); + eprintln!("Checking cache for module {}", path.display()); + if let Some(entry) = engines.qe().get_ty_module_cache_entry(&path) { + eprintln!("Cache hit for module {}", path.display()); + // Return the cached module + return Ok(entry.module); + } } let ParseModule { diff --git a/sway-lsp/tests/lib.rs b/sway-lsp/tests/lib.rs index e0baeb3984e..76bf8b11f27 100644 --- a/sway-lsp/tests/lib.rs +++ b/sway-lsp/tests/lib.rs @@ -162,6 +162,22 @@ fn did_change() { }); } +#[test] +fn did_open_fluid_libraries() { + run_async!({ + let now = std::time::Instant::now(); + let (mut service, _) = LspService::build(ServerState::new) + .custom_method("sway/metrics", ServerState::metrics) + .finish(); + let uri = init_and_open(&mut service, PathBuf::from("/Users/josh/Documents/rust/fuel/user_projects/fluid-protocol/libraries").join("src/interface.sw")).await; + + let _ = lsp::did_change_request(&mut service, &uri, 1, None).await; + service.inner().wait_for_parsing().await; + eprintln!("Elapsed time: {:?}", now.elapsed()); + shutdown_and_exit(&mut service).await; + }); +} + #[test] fn did_cache_test() { run_async!({ From ff172574b5ea5a5d10adae3dd693e4af6b6ae468 Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Mon, 15 Jul 2024 13:33:39 +1000 Subject: [PATCH 03/29] check if ty modele cache is up to date --- sway-core/src/lib.rs | 74 ++++++++++- sway-core/src/query_engine/mod.rs | 11 +- sway-core/src/semantic_analysis/module.rs | 148 ++++++++++++++++----- sway-core/src/semantic_analysis/program.rs | 11 +- sway-lsp/tests/lib.rs | 8 +- 5 files changed, 207 insertions(+), 45 deletions(-) diff --git a/sway-core/src/lib.rs b/sway-core/src/lib.rs index bdc7e683ceb..4a813b225aa 100644 --- a/sway-core/src/lib.rs +++ b/sway-core/src/lib.rs @@ -33,7 +33,7 @@ use control_flow_analysis::ControlFlowGraph; pub use debug_generation::write_dwarf; use indexmap::IndexMap; use metadata::MetadataManager; -use query_engine::{ModuleCacheKey, ModulePath, ProgramsCacheEntry}; +use query_engine::{ModuleCacheKey, ProgramsCacheEntry, TyModuleCacheEntry}; use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; use std::path::{Path, PathBuf}; @@ -236,7 +236,7 @@ fn parse_in_memory( pub struct Submodule { name: Ident, - path: ModulePath, + path: Arc, lexed: lexed::LexedSubmodule, parsed: parsed::ParseSubmodule, } @@ -412,13 +412,24 @@ fn parse_module_tree( .and_then(|lsp| lsp.file_versions.get(path.as_ref()).copied()) .unwrap_or(None); let cache_entry = ModuleCacheEntry { - path, + path: path.clone(), modified_time, hash, dependencies, include_tests, version, }; + + let split_points = ["sway-lib-core", "sway-lib-std", "libraries"]; + let relevant_path = path + .iter() + .skip_while(|&comp| !split_points.contains(&comp.to_str().unwrap())) + .collect::(); + eprintln!( + "๐Ÿ€„ ๐Ÿ—‚๏ธ Inserted cache entry for parse module {:?}", + relevant_path + ); + query_engine.insert_parse_module_cache_entry(cache_entry); Ok(ParsedModuleTree { @@ -428,16 +439,58 @@ fn parse_module_tree( }) } -fn is_parse_module_cache_up_to_date( +pub(crate) fn is_ty_module_cache_up_to_date( + path: &Arc, + build_config: Option<&BuildConfig>, + entry: &TyModuleCacheEntry, +) -> bool { + let split_points = ["sway-lib-core", "sway-lib-std", "libraries"]; + let relevant_path = path + .iter() + .skip_while(|&comp| !split_points.contains(&comp.to_str().unwrap())) + .collect::(); + + let cache_up_to_date = build_config + .as_ref() + .and_then(|x| x.lsp_mode.as_ref()) + .and_then(|lsp| lsp.file_versions.get(path.as_ref())) + .map_or_else( + || false, + |version| !version.map_or(false, |v| v > entry.version.unwrap_or(0)), + ); + + let res = if cache_up_to_date { + entry + .dependencies + .iter() + .all(|path| is_ty_module_cache_up_to_date(path, build_config, entry)) + } else { + false + }; + + eprintln!( + "๐Ÿ“Ÿ ๐Ÿ‘“ Checking cache for TY module {:?} | is up to date? {}", + relevant_path, res + ); + res +} + +pub(crate) fn is_parse_module_cache_up_to_date( engines: &Engines, path: &Arc, include_tests: bool, build_config: Option<&BuildConfig>, ) -> bool { + let split_points = ["sway-lib-core", "sway-lib-std", "libraries"]; + let relevant_path = path + .iter() + .skip_while(|&comp| !split_points.contains(&comp.to_str().unwrap())) + .collect::(); + let query_engine = engines.qe(); let key = ModuleCacheKey::new(path.clone(), include_tests); let entry = query_engine.get_parse_module_cache_entry(&key); - match entry { + let res = match entry { Some(entry) => { // Let's check if we can re-use the dependency information // we got from the cache. @@ -480,7 +533,14 @@ fn is_parse_module_cache_up_to_date( } } None => false, - } + }; + + eprintln!( + "๐Ÿ€„ ๐Ÿ‘“ Checking cache for parse module {:?} | is up to date? {}", + relevant_path, res + ); + + res } fn module_path( @@ -696,6 +756,7 @@ pub fn compile_to_ast( package_name: &str, retrigger_compilation: Option>, ) -> Result { + eprintln!("๐Ÿ‘จโ€๐Ÿ’ป compile_to_ast ๐Ÿ‘จโ€๐Ÿ’ป"); check_should_abort(handler, retrigger_compilation.clone())?; let query_engine = engines.qe(); let mut metrics = PerformanceData::default(); @@ -739,6 +800,7 @@ pub fn compile_to_ast( parsed_program.exclude_tests(engines); } + eprintln!("๐Ÿ‘ฉโ€๐Ÿ’ป parsed to typed AST ๐Ÿ‘ฉโ€๐Ÿ’ป"); // Type check (+ other static analysis) the CST to a typed AST. let typed_res = time_expr!( "parse the concrete syntax tree (CST) to a typed AST", diff --git a/sway-core/src/query_engine/mod.rs b/sway-core/src/query_engine/mod.rs index bace080fe7f..70326a75555 100644 --- a/sway-core/src/query_engine/mod.rs +++ b/sway-core/src/query_engine/mod.rs @@ -8,8 +8,6 @@ use crate::decl_engine::{DeclId, DeclRef}; use crate::language::ty::{TyFunctionDecl, TyFunctionSig, TyModule}; use crate::{Engines, Programs}; -pub type ModulePath = Arc; - #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct ModuleCacheKey { pub path: Arc, @@ -27,12 +25,12 @@ impl ModuleCacheKey { #[derive(Clone, Debug)] pub struct ModuleCacheEntry { - pub path: ModulePath, - pub modified_time: Option, + pub path: Arc, pub hash: u64, - pub dependencies: Vec, pub include_tests: bool, + pub dependencies: Vec>, pub version: Option, + pub modified_time: Option, } pub type ModuleCacheMap = HashMap; @@ -50,7 +48,10 @@ pub type ProgramsCacheMap = HashMap, ProgramsCacheEntry>; pub struct TyModuleCacheEntry { pub path: Arc, pub module: TyModule, + pub dependencies: Vec>, + pub version: Option, } + pub type TyModuleCacheMap = HashMap, TyModuleCacheEntry>; #[derive(Clone, Debug)] diff --git a/sway-core/src/semantic_analysis/module.rs b/sway-core/src/semantic_analysis/module.rs index 6253638bbe9..da8fc34c202 100644 --- a/sway-core/src/semantic_analysis/module.rs +++ b/sway-core/src/semantic_analysis/module.rs @@ -1,23 +1,30 @@ use std::{ collections::{HashMap, HashSet}, fmt::Display, - fs, sync::Arc, + fs, + path::PathBuf, + sync::Arc, }; -use fuel_vm::fuel_crypto::coins_bip32::path; use graph_cycles::Cycles; use sway_error::{ error::CompileError, handler::{ErrorEmitted, Handler}, }; -use sway_types::{BaseIdent, Named}; +use sway_types::{BaseIdent, Named, SourceId, Spanned}; use crate::{ - decl_engine::{DeclEngineGet, DeclId}, engine_threading::DebugWithEngines, language::{ + decl_engine::{DeclEngineGet, DeclId}, + engine_threading::DebugWithEngines, + is_parse_module_cache_up_to_date, is_ty_module_cache_up_to_date, + language::{ parsed::*, ty::{self, TyAstNodeContent, TyDecl}, CallPath, ModName, - }, query_engine::TyModuleCacheEntry, semantic_analysis::*, BuildConfig, Engines, TypeInfo + }, + query_engine::TyModuleCacheEntry, + semantic_analysis::*, + BuildConfig, Engines, TypeInfo, }; use super::{ @@ -251,6 +258,43 @@ impl ty::TyModule { Ok(()) } + fn check_cache( + source_id: Option<&SourceId>, + engines: &Engines, + build_config: Option<&BuildConfig>, + ) -> Option { + // Check if the module is already in the cache + if let Some(source_id) = source_id { + let path = engines.se().get_path(&source_id); + + let split_points = ["sway-lib-core", "sway-lib-std", "libraries"]; + let relevant_path = path + .iter() + .skip_while(|&comp| !split_points.contains(&comp.to_str().unwrap())) + .collect::(); + + eprintln!("๐Ÿฅธ Checking cache for TY module {:?}", relevant_path); + if let Some(entry) = engines.qe().get_ty_module_cache_entry(&path) { + // We now need to check if the module is up to date, if not, we need to recompute it so + // we will return None, otherwise we will return the cached module. + + // For now we will duplicate the logic of the parsed cache entry check but + // we should ideally have a way of sharing this logic. + + // Let's check if we can re-use the dependency information + // we got from the cache. + if is_ty_module_cache_up_to_date(&path.into(), build_config, &entry) { + eprintln!("โœ… Cache hit for module {:?}", relevant_path); + // Return the cached module + return Some(entry.module); + } else { + eprintln!("๐Ÿ”„ Cache unable to be used for module {:?}", relevant_path); + } + } + } + None + } + /// Type-check the given parsed module to produce a typed module. /// /// Recursively type-checks submodules first. @@ -260,18 +304,8 @@ impl ty::TyModule { engines: &Engines, kind: TreeType, parsed: &ParseModule, + build_config: Option<&BuildConfig>, ) -> Result { - // Check if the module is already in the cache - if let Some(source_id) = parsed.span.source_id() { - let path = engines.se().get_path(&source_id); - eprintln!("Checking cache for module {}", path.display()); - if let Some(entry) = engines.qe().get_ty_module_cache_entry(&path) { - eprintln!("Cache hit for module {}", path.display()); - // Return the cached module - return Ok(entry.module); - } - } - let ParseModule { submodules, tree, @@ -289,20 +323,42 @@ impl ty::TyModule { .iter() .find(|(submod_name, _submodule)| eval_mod_name == submod_name) .unwrap(); - Ok(( - name.clone(), - ty::TySubmodule::type_check( - handler, - ctx.by_ref(), - engines, + + // Check if the submodule cache is up to date + if let Some(module) = ty::TyModule::check_cache( + submodule.module.span.source_id(), + engines, + build_config, + ) { + let submodule = ty::TySubmodule { + module, + mod_name_span: submodule.mod_name_span.clone(), + }; + Ok((name.clone(), submodule)) + } else { + Ok(( name.clone(), - kind, - submodule, - )?, - )) + ty::TySubmodule::type_check( + handler, + ctx.by_ref(), + engines, + name.clone(), + kind, + submodule, + build_config, + )?, + )) + } }) .collect::, _>>(); + // Check if the root module cache is up to date + if let Some(module) = + ty::TyModule::check_cache(parsed.span.source_id(), engines, build_config) + { + return Ok(module); + } + // TODO: Ordering should be solved across all modules prior to the beginning of type-check. let ordered_nodes = node_dependencies::order_ast_nodes_by_dependency( handler, @@ -382,6 +438,16 @@ impl ty::TyModule { } } + let dependencies = submodules + .iter() + .filter_map(|(ident, _)| { + ident + .span() + .source_id() + .map(|path| Arc::new(engines.se().get_path(path))) + }) + .collect::>(); + let ty_module = Self { span: span.clone(), submodules, @@ -390,13 +456,29 @@ impl ty::TyModule { attributes: attributes.clone(), }; - // Cache the ty module + // Cache the ty module if let Some(source_id) = span.source_id() { let path = engines.se().get_path(&source_id); - engines.qe().insert_ty_module_cache_entry(TyModuleCacheEntry { - path: Arc::new(path), - module: ty_module.clone(), - }); + let split_points = ["sway-lib-core", "sway-lib-std", "libraries"]; + let relevant_path = path + .iter() + .skip_while(|&comp| !split_points.contains(&comp.to_str().unwrap())) + .collect::(); + eprintln!("๐Ÿ’พ Inserting cache entry for TY module {:?}", relevant_path); + + let version = build_config + .and_then(|config| config.lsp_mode.as_ref()) + .and_then(|lsp| lsp.file_versions.get(&path).copied()) + .flatten(); + + engines + .qe() + .insert_ty_module_cache_entry(TyModuleCacheEntry { + path: Arc::new(path), + module: ty_module.clone(), + dependencies, + version, + }); } Ok(ty_module) @@ -614,6 +696,7 @@ impl ty::TySubmodule { mod_name: ModName, kind: TreeType, submodule: &ParseSubmodule, + build_config: Option<&BuildConfig>, ) -> Result { let ParseSubmodule { module, @@ -621,7 +704,8 @@ impl ty::TySubmodule { visibility, } = submodule; parent_ctx.enter_submodule(mod_name, *visibility, module.span.clone(), |submod_ctx| { - let module_res = ty::TyModule::type_check(handler, submod_ctx, engines, kind, module); + let module_res = + ty::TyModule::type_check(handler, submod_ctx, engines, kind, module, build_config); module_res.map(|module| ty::TySubmodule { module, mod_name_span: mod_name_span.clone(), diff --git a/sway-core/src/semantic_analysis/program.rs b/sway-core/src/semantic_analysis/program.rs index 71f73f359d7..65f5d762552 100644 --- a/sway-core/src/semantic_analysis/program.rs +++ b/sway-core/src/semantic_analysis/program.rs @@ -60,7 +60,16 @@ impl TyProgram { let ParseProgram { root, kind } = parsed; - let root = ty::TyModule::type_check(handler, ctx.by_ref(), engines, parsed.kind, root)?; + let now = std::time::Instant::now(); + let root = ty::TyModule::type_check( + handler, + ctx.by_ref(), + engines, + parsed.kind, + root, + build_config, + )?; + eprintln!("โฑ๏ธ Type-checking module took {:?}", now.elapsed()); let (kind, declarations, configurables) = Self::validate_root( handler, diff --git a/sway-lsp/tests/lib.rs b/sway-lsp/tests/lib.rs index 76bf8b11f27..60444f90977 100644 --- a/sway-lsp/tests/lib.rs +++ b/sway-lsp/tests/lib.rs @@ -169,8 +169,14 @@ fn did_open_fluid_libraries() { let (mut service, _) = LspService::build(ServerState::new) .custom_method("sway/metrics", ServerState::metrics) .finish(); - let uri = init_and_open(&mut service, PathBuf::from("/Users/josh/Documents/rust/fuel/user_projects/fluid-protocol/libraries").join("src/interface.sw")).await; + let uri = init_and_open( + &mut service, + PathBuf::from("/Users/josh/Documents/rust/fuel/user_projects/fluid-protocol/libraries") + .join("src/interface.sw"), + ) + .await; + eprintln!("\n ๐Ÿช†๐Ÿช†๐Ÿช†๐Ÿช† Initial compilation complete, starting recompilation ๐Ÿช†๐Ÿช†๐Ÿช†๐Ÿช† \n"); let _ = lsp::did_change_request(&mut service, &uri, 1, None).await; service.inner().wait_for_parsing().await; eprintln!("Elapsed time: {:?}", now.elapsed()); From 5d3c6e7ca74cbcbb037f8358ab515bd4990ae828 Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Mon, 15 Jul 2024 13:58:15 +1000 Subject: [PATCH 04/29] add clear module fns for GC --- sway-core/src/decl_engine/engine.rs | 49 +++++++++++++++++++++- sway-core/src/decl_engine/parsed_engine.rs | 43 ++++++++++++++++++- sway-core/src/engine_threading.rs | 8 ++++ sway-core/src/type_system/engine.rs | 22 ++++++---- sway-lsp/src/core/session.rs | 8 ++++ sway-lsp/src/server_state.rs | 3 +- 6 files changed, 122 insertions(+), 11 deletions(-) diff --git a/sway-core/src/decl_engine/engine.rs b/sway-core/src/decl_engine/engine.rs index 8f992f119bd..e1f8843385e 100644 --- a/sway-core/src/decl_engine/engine.rs +++ b/sway-core/src/decl_engine/engine.rs @@ -5,7 +5,7 @@ use std::{ sync::Arc, }; -use sway_types::{Named, ProgramId, Spanned}; +use sway_types::{Named, ProgramId, SourceId, Spanned}; use crate::{ concurrent_slab::ConcurrentSlab, @@ -375,6 +375,53 @@ decl_engine_clear_program!( type_alias_slab, ty::TyTypeAliasDecl; ); +macro_rules! decl_engine_clear_module { + ($($slab:ident, $decl:ty);* $(;)?) => { + impl DeclEngine { + pub fn clear_module(&mut self, source_id: &SourceId) { + self.parents.write().retain(|key, _| { + match key { + AssociatedItemDeclId::TraitFn(decl_id) => { + self.get_trait_fn(decl_id).span().source_id().map_or(true, |src_id| src_id != source_id) + }, + AssociatedItemDeclId::Function(decl_id) => { + self.get_function(decl_id).span().source_id().map_or(true, |src_id| src_id != source_id) + }, + AssociatedItemDeclId::Type(decl_id) => { + self.get_type(decl_id).span().source_id().map_or(true, |src_id| src_id != source_id) + }, + AssociatedItemDeclId::Constant(decl_id) => { + self.get_constant(decl_id).span().source_id().map_or(true, |src_id| src_id != source_id) + }, + } + }); + + $( + self.$slab.retain(|_k, ty| match ty.span().source_id() { + Some(src_id) => src_id != source_id, + None => true, + }); + )* + } + } + }; +} + +decl_engine_clear_module!( + function_slab, ty::TyFunctionDecl; + trait_slab, ty::TyTraitDecl; + trait_fn_slab, ty::TyTraitFn; + trait_type_slab, ty::TyTraitType; + impl_self_or_trait_slab, ty::TyImplTrait; + struct_slab, ty::TyStructDecl; + storage_slab, ty::TyStorageDecl; + abi_slab, ty::TyAbiDecl; + constant_slab, ty::TyConstantDecl; + configurable_slab, ty::TyConfigurableDecl; + enum_slab, ty::TyEnumDecl; + type_alias_slab, ty::TyTypeAliasDecl; +); + impl DeclEngine { /// Given a [DeclRef] `index`, finds all the parents of `index` and all the /// recursive parents of those parents, and so on. Does not perform diff --git a/sway-core/src/decl_engine/parsed_engine.rs b/sway-core/src/decl_engine/parsed_engine.rs index 8b8f84107eb..a3e2ffdf230 100644 --- a/sway-core/src/decl_engine/parsed_engine.rs +++ b/sway-core/src/decl_engine/parsed_engine.rs @@ -9,7 +9,7 @@ use crate::{ }; use std::sync::Arc; -use sway_types::{ProgramId, Spanned}; +use sway_types::{ProgramId, SourceId, Spanned}; use super::parsed_id::ParsedDeclId; @@ -167,6 +167,47 @@ decl_engine_clear_program!( .span()), ); + +macro_rules! decl_engine_clear_module { + ($(($slab:ident, $getter:expr)),* $(,)?) => { + impl ParsedDeclEngine { + pub fn clear_module(&mut self, program_id: &SourceId) { + $( + self.$slab.retain(|_k, item| { + #[allow(clippy::redundant_closure_call)] + let span = $getter(item); + match span.source_id() { + Some(src_id) => src_id != program_id, + None => true, + } + }); + )* + } + } + }; +} + +decl_engine_clear_module!( + (variable_slab, |item: &VariableDeclaration| item.name.span()), + (function_slab, |item: &FunctionDeclaration| item.name.span()), + (trait_slab, |item: &TraitDeclaration| item.name.span()), + (trait_fn_slab, |item: &TraitFn| item.name.span()), + (trait_type_slab, |item: &TraitTypeDeclaration| item + .name + .span()), + (impl_self_or_trait_slab, |item: &ImplSelfOrTrait| item + .block_span + .clone()), + (struct_slab, |item: &StructDeclaration| item.name.span()), + (storage_slab, |item: &StorageDeclaration| item.span.clone()), + (abi_slab, |item: &AbiDeclaration| item.name.span()), + (constant_slab, |item: &ConstantDeclaration| item.name.span()), + (enum_slab, |item: &EnumDeclaration| item.name.span()), + (type_alias_slab, |item: &TypeAliasDeclaration| item + .name + .span()), +); + impl ParsedDeclEngine { /// Friendly helper method for calling the `get` method from the /// implementation of [ParsedDeclEngineGet] for [ParsedDeclEngine] diff --git a/sway-core/src/engine_threading.rs b/sway-core/src/engine_threading.rs index c698d9700f4..e0e151249eb 100644 --- a/sway-core/src/engine_threading.rs +++ b/sway-core/src/engine_threading.rs @@ -48,6 +48,14 @@ impl Engines { self.parsed_decl_engine.clear_program(program_id); } + /// Removes all data associated with `source_id` from the declaration and type engines. + /// It is intended to be used during garbage collection to remove any data that is no longer needed. + pub fn clear_module(&mut self, source_id: &sway_types::SourceId) { + self.type_engine.clear_module(source_id); + self.decl_engine.clear_module(source_id); + self.parsed_decl_engine.clear_module(source_id); + } + /// Helps out some `thing: T` by adding `self` as context. pub fn help_out(&self, thing: T) -> WithEngines<'_, T> { WithEngines { diff --git a/sway-core/src/type_system/engine.rs b/sway-core/src/type_system/engine.rs index 38871fd4108..9e2085bda39 100644 --- a/sway-core/src/type_system/engine.rs +++ b/sway-core/src/type_system/engine.rs @@ -67,16 +67,22 @@ impl TypeEngine { } } + fn clear_items(&mut self, keep: F) + where + F: Fn(&SourceId) -> bool, + { + self.slab.retain(|_, tsi| tsi.source_id.as_ref().map_or(true, &keep)); + self.id_map.write().retain(|tsi, _| tsi.source_id.as_ref().map_or(true, &keep)); + } + /// Removes all data associated with `program_id` from the type engine. pub fn clear_program(&mut self, program_id: &ProgramId) { - self.slab.retain(|_, tsi| match tsi.source_id { - Some(source_id) => &source_id.program_id() != program_id, - None => true, - }); - self.id_map.write().retain(|tsi, _| match tsi.source_id { - Some(source_id) => &source_id.program_id() != program_id, - None => true, - }); + self.clear_items(|id| id.program_id() != *program_id); + } + + /// Removes all data associated with `source_id` from the type engine. + pub fn clear_module(&mut self, source_id: &SourceId) { + self.clear_items(|id| id != source_id); } pub fn replace(&self, id: TypeId, new_value: TypeSourceInfo) { diff --git a/sway-lsp/src/core/session.rs b/sway-lsp/src/core/session.rs index 6dab2952254..0b622b75899 100644 --- a/sway-lsp/src/core/session.rs +++ b/sway-lsp/src/core/session.rs @@ -134,6 +134,14 @@ impl Session { Ok(()) } + pub fn garbage_collect_module(&self, engines: &mut Engines, uri: &Url) -> Result<(), LanguageServerError> { + let path = uri.to_file_path().unwrap(); + eprintln!("๐Ÿ—‘๏ธ Garbage collecting module {:?}", path); + let source_id = { engines.se().get_source_id(&path) }; + engines.clear_module(&source_id); + Ok(()) + } + pub fn token_ranges(&self, url: &Url, position: Position) -> Option> { let _p = tracing::trace_span!("token_ranges").entered(); let mut token_ranges: Vec<_> = self diff --git a/sway-lsp/src/server_state.rs b/sway-lsp/src/server_state.rs index 3f7760b4544..cc7e5d98f03 100644 --- a/sway-lsp/src/server_state.rs +++ b/sway-lsp/src/server_state.rs @@ -142,7 +142,8 @@ impl ServerState { { // Call this on the engines clone so we don't clear types that are still in use // and might be needed in the case cancel compilation was triggered. - if let Err(err) = session.garbage_collect(&mut engines_clone) { + // if let Err(err) = session.garbage_collect_program(&mut engines_clone) { + if let Err(err) = session.garbage_collect_module(&mut engines_clone, &uri) { tracing::error!( "Unable to perform garbage collection: {}", err.to_string() From 74628ddec2554946c8378e44c4122a786d4754a7 Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Mon, 15 Jul 2024 17:07:33 +1000 Subject: [PATCH 05/29] fix stack overflow --- sway-core/src/lib.rs | 70 +++++++++++++---------- sway-core/src/semantic_analysis/module.rs | 57 ++++++++++++++---- sway-lsp/src/core/session.rs | 11 +++- sway-lsp/src/server_state.rs | 4 +- 4 files changed, 97 insertions(+), 45 deletions(-) diff --git a/sway-core/src/lib.rs b/sway-core/src/lib.rs index 4a813b225aa..1b3afd8fbf2 100644 --- a/sway-core/src/lib.rs +++ b/sway-core/src/lib.rs @@ -408,6 +408,7 @@ fn parse_module_tree( .ok() .and_then(|m| m.modified().ok()); let dependencies = submodules.into_iter().map(|s| s.path).collect::>(); + eprintln!("๐Ÿ” path {:?} | dependencies {:?}", path, dependencies); let version = lsp_mode .and_then(|lsp| lsp.file_versions.get(path.as_ref()).copied()) .unwrap_or(None); @@ -440,39 +441,45 @@ fn parse_module_tree( } pub(crate) fn is_ty_module_cache_up_to_date( + engines: &Engines, path: &Arc, build_config: Option<&BuildConfig>, - entry: &TyModuleCacheEntry, + //entry: &TyModuleCacheEntry, ) -> bool { - let split_points = ["sway-lib-core", "sway-lib-std", "libraries"]; - let relevant_path = path - .iter() - .skip_while(|&comp| !split_points.contains(&comp.to_str().unwrap())) - .collect::(); - - let cache_up_to_date = build_config - .as_ref() - .and_then(|x| x.lsp_mode.as_ref()) - .and_then(|lsp| lsp.file_versions.get(path.as_ref())) - .map_or_else( - || false, - |version| !version.map_or(false, |v| v > entry.version.unwrap_or(0)), - ); - - let res = if cache_up_to_date { - entry - .dependencies - .iter() - .all(|path| is_ty_module_cache_up_to_date(path, build_config, entry)) - } else { - false - }; - - eprintln!( - "๐Ÿ“Ÿ ๐Ÿ‘“ Checking cache for TY module {:?} | is up to date? {}", - relevant_path, res - ); - res + let query_engine = engines.qe(); + let entry = query_engine.get_ty_module_cache_entry(&path); + match entry { + Some(entry) => { + // return false; + let cache_up_to_date = build_config + .as_ref() + .and_then(|x| x.lsp_mode.as_ref()) + .and_then(|lsp| lsp.file_versions.get(path.as_ref())) + .map_or_else( + || false, + |version| !version.map_or(false, |v| v > entry.version.unwrap_or(0)), + ); + // Only putting this here to confirm we don't get a stack overflow if we return early. + // return cache_up_to_date; + if cache_up_to_date { + // This is causing a stack overflow, why? + eprintln!("num dependencies for path {:?}: {}", path, entry.dependencies.len()); + entry + .dependencies + .iter() + .all(|path| { + eprint!("checking dep path {:?} ", path); + is_ty_module_cache_up_to_date(engines, path, build_config) + }) + } else { + false + } + } + None => { + false + } + } + } pub(crate) fn is_parse_module_cache_up_to_date( @@ -525,7 +532,10 @@ pub(crate) fn is_parse_module_cache_up_to_date( // Look at the dependencies recursively to make sure they have not been // modified either. if cache_up_to_date { + //eprintln!("num dependencies for path {:?}: {}", path, entry.dependencies.len()); + entry.dependencies.iter().all(|path| { +// eprint!("checking dep path {:?} ", path); is_parse_module_cache_up_to_date(engines, path, include_tests, build_config) }) } else { diff --git a/sway-core/src/semantic_analysis/module.rs b/sway-core/src/semantic_analysis/module.rs index da8fc34c202..039a967782c 100644 --- a/sway-core/src/semantic_analysis/module.rs +++ b/sway-core/src/semantic_analysis/module.rs @@ -22,7 +22,7 @@ use crate::{ ty::{self, TyAstNodeContent, TyDecl}, CallPath, ModName, }, - query_engine::TyModuleCacheEntry, + query_engine::{ModuleCacheKey, TyModuleCacheEntry}, semantic_analysis::*, BuildConfig, Engines, TypeInfo, }; @@ -272,8 +272,8 @@ impl ty::TyModule { .iter() .skip_while(|&comp| !split_points.contains(&comp.to_str().unwrap())) .collect::(); - eprintln!("๐Ÿฅธ Checking cache for TY module {:?}", relevant_path); + if let Some(entry) = engines.qe().get_ty_module_cache_entry(&path) { // We now need to check if the module is up to date, if not, we need to recompute it so // we will return None, otherwise we will return the cached module. @@ -283,11 +283,22 @@ impl ty::TyModule { // Let's check if we can re-use the dependency information // we got from the cache. - if is_ty_module_cache_up_to_date(&path.into(), build_config, &entry) { + + if is_ty_module_cache_up_to_date(engines, &path.into(), build_config) { + eprintln!( + "๐Ÿ“Ÿ ๐Ÿ‘“ Checking cache for TY module {:?} | is up to date? {}", + relevant_path, true + ); + eprintln!("โœ… Cache hit for module {:?}", relevant_path); // Return the cached module return Some(entry.module); } else { + eprintln!( + "๐Ÿ“Ÿ ๐Ÿ‘“ Checking cache for TY module {:?} | is up to date? {}", + relevant_path, false + ); + eprintln!("๐Ÿ”„ Cache unable to be used for module {:?}", relevant_path); } } @@ -315,6 +326,7 @@ impl ty::TyModule { .. } = parsed; + // Type-check submodules first in order of evaluation previously computed by the dependency graph. let submodules_res = module_eval_order .iter() @@ -438,15 +450,15 @@ impl ty::TyModule { } } - let dependencies = submodules - .iter() - .filter_map(|(ident, _)| { - ident - .span() - .source_id() - .map(|path| Arc::new(engines.se().get_path(path))) - }) - .collect::>(); + // let dependencies = submodules + // .iter() + // .filter_map(|(ident, _)| { + // ident + // .span() + // .source_id() + // .map(|src_id| Arc::new(engines.se().get_path(src_id))) + // }) + // .collect::>(); let ty_module = Self { span: span.clone(), @@ -458,12 +470,33 @@ impl ty::TyModule { // Cache the ty module if let Some(source_id) = span.source_id() { + let path = engines.se().get_path(&source_id); let split_points = ["sway-lib-core", "sway-lib-std", "libraries"]; let relevant_path = path .iter() .skip_while(|&comp| !split_points.contains(&comp.to_str().unwrap())) .collect::(); + + // let module_eval_order = module_eval_order.iter().map(|ident| { + // let path = engines.se().get_path(ident.span().source_id().unwrap()); + // }).collect::>(); + + // let module_eval_order = module_eval_order.iter() + // .filter_map(|ident| { + // ident.span().source_id() + // .map(|source_id| engines.se().get_path(source_id)) + // }) + // .collect::>(); + + let dependencies = engines.qe().get_parse_module_cache_entry(&ModuleCacheKey { + path: path.clone().into(), + include_tests: true, // TODO: pass this in + }).unwrap().dependencies; + + //eprintln!("๐Ÿค ๐Ÿค ๐Ÿค ๐Ÿค ๐Ÿค ๐Ÿค ๐Ÿค ๐Ÿค path {:?} | module_eval_order {:?}", relevant_path, module_eval_order); + eprintln!("๐Ÿค path {:?} | dependencies {:?}", relevant_path, dependencies); + eprintln!("๐Ÿ’พ Inserting cache entry for TY module {:?}", relevant_path); let version = build_config diff --git a/sway-lsp/src/core/session.rs b/sway-lsp/src/core/session.rs index 0b622b75899..0236e278051 100644 --- a/sway-lsp/src/core/session.rs +++ b/sway-lsp/src/core/session.rs @@ -124,7 +124,10 @@ impl Session { } /// Clean up memory in the [TypeEngine] and [DeclEngine] for the user's workspace. - pub fn garbage_collect(&self, engines: &mut Engines) -> Result<(), LanguageServerError> { + pub fn garbage_collect_program( + &self, + engines: &mut Engines, + ) -> Result<(), LanguageServerError> { let _p = tracing::trace_span!("garbage_collect").entered(); let path = self.sync.temp_dir()?; let program_id = { engines.se().get_program_id(&path) }; @@ -134,7 +137,11 @@ impl Session { Ok(()) } - pub fn garbage_collect_module(&self, engines: &mut Engines, uri: &Url) -> Result<(), LanguageServerError> { + pub fn garbage_collect_module( + &self, + engines: &mut Engines, + uri: &Url, + ) -> Result<(), LanguageServerError> { let path = uri.to_file_path().unwrap(); eprintln!("๐Ÿ—‘๏ธ Garbage collecting module {:?}", path); let source_id = { engines.se().get_source_id(&path) }; diff --git a/sway-lsp/src/server_state.rs b/sway-lsp/src/server_state.rs index cc7e5d98f03..a03594721de 100644 --- a/sway-lsp/src/server_state.rs +++ b/sway-lsp/src/server_state.rs @@ -143,7 +143,9 @@ impl ServerState { // Call this on the engines clone so we don't clear types that are still in use // and might be needed in the case cancel compilation was triggered. // if let Err(err) = session.garbage_collect_program(&mut engines_clone) { - if let Err(err) = session.garbage_collect_module(&mut engines_clone, &uri) { + if let Err(err) = + session.garbage_collect_module(&mut engines_clone, &uri) + { tracing::error!( "Unable to perform garbage collection: {}", err.to_string() From 8a98547fa2a30a55a48e575f8f1d0095adf2df55 Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Mon, 15 Jul 2024 17:13:57 +1000 Subject: [PATCH 06/29] clean up --- sway-core/src/lib.rs | 6 ------ sway-core/src/semantic_analysis/module.rs | 23 +---------------------- 2 files changed, 1 insertion(+), 28 deletions(-) diff --git a/sway-core/src/lib.rs b/sway-core/src/lib.rs index 1b3afd8fbf2..2f633a46da2 100644 --- a/sway-core/src/lib.rs +++ b/sway-core/src/lib.rs @@ -444,13 +444,11 @@ pub(crate) fn is_ty_module_cache_up_to_date( engines: &Engines, path: &Arc, build_config: Option<&BuildConfig>, - //entry: &TyModuleCacheEntry, ) -> bool { let query_engine = engines.qe(); let entry = query_engine.get_ty_module_cache_entry(&path); match entry { Some(entry) => { - // return false; let cache_up_to_date = build_config .as_ref() .and_then(|x| x.lsp_mode.as_ref()) @@ -459,11 +457,7 @@ pub(crate) fn is_ty_module_cache_up_to_date( || false, |version| !version.map_or(false, |v| v > entry.version.unwrap_or(0)), ); - // Only putting this here to confirm we don't get a stack overflow if we return early. - // return cache_up_to_date; if cache_up_to_date { - // This is causing a stack overflow, why? - eprintln!("num dependencies for path {:?}: {}", path, entry.dependencies.len()); entry .dependencies .iter() diff --git a/sway-core/src/semantic_analysis/module.rs b/sway-core/src/semantic_analysis/module.rs index 039a967782c..802f26d1d2f 100644 --- a/sway-core/src/semantic_analysis/module.rs +++ b/sway-core/src/semantic_analysis/module.rs @@ -450,16 +450,6 @@ impl ty::TyModule { } } - // let dependencies = submodules - // .iter() - // .filter_map(|(ident, _)| { - // ident - // .span() - // .source_id() - // .map(|src_id| Arc::new(engines.se().get_path(src_id))) - // }) - // .collect::>(); - let ty_module = Self { span: span.clone(), submodules, @@ -470,7 +460,6 @@ impl ty::TyModule { // Cache the ty module if let Some(source_id) = span.source_id() { - let path = engines.se().get_path(&source_id); let split_points = ["sway-lib-core", "sway-lib-std", "libraries"]; let relevant_path = path @@ -478,17 +467,7 @@ impl ty::TyModule { .skip_while(|&comp| !split_points.contains(&comp.to_str().unwrap())) .collect::(); - // let module_eval_order = module_eval_order.iter().map(|ident| { - // let path = engines.se().get_path(ident.span().source_id().unwrap()); - // }).collect::>(); - - // let module_eval_order = module_eval_order.iter() - // .filter_map(|ident| { - // ident.span().source_id() - // .map(|source_id| engines.se().get_path(source_id)) - // }) - // .collect::>(); - + // how about instead of saving this here we just check in the is_ty_module_cache_up_to_date function let dependencies = engines.qe().get_parse_module_cache_entry(&ModuleCacheKey { path: path.clone().into(), include_tests: true, // TODO: pass this in From 3b15f96872395cd77c87ffd512c46b47902f2511 Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Tue, 16 Jul 2024 15:07:23 +1000 Subject: [PATCH 07/29] rework so we only need one ModuleCacheEntry for both parsed and typed --- sway-core/src/decl_engine/parsed_engine.rs | 1 - sway-core/src/lib.rs | 85 ++++++----- sway-core/src/query_engine/mod.rs | 155 +++++++++++++++------ sway-core/src/semantic_analysis/module.rs | 78 ++++++----- sway-core/src/type_system/engine.rs | 7 +- 5 files changed, 207 insertions(+), 119 deletions(-) diff --git a/sway-core/src/decl_engine/parsed_engine.rs b/sway-core/src/decl_engine/parsed_engine.rs index a3e2ffdf230..642f0c9ee6e 100644 --- a/sway-core/src/decl_engine/parsed_engine.rs +++ b/sway-core/src/decl_engine/parsed_engine.rs @@ -167,7 +167,6 @@ decl_engine_clear_program!( .span()), ); - macro_rules! decl_engine_clear_module { ($(($slab:ident, $getter:expr)),* $(,)?) => { impl ParsedDeclEngine { diff --git a/sway-core/src/lib.rs b/sway-core/src/lib.rs index 2f633a46da2..24df5a46d75 100644 --- a/sway-core/src/lib.rs +++ b/sway-core/src/lib.rs @@ -31,9 +31,10 @@ pub use asm_generation::{CompiledBytecode, FinalizedEntry}; pub use build_config::{BuildConfig, BuildTarget, LspConfig, OptLevel, PrintAsm, PrintIr}; use control_flow_analysis::ControlFlowGraph; pub use debug_generation::write_dwarf; +use fuel_vm::fuel_merkle::common; use indexmap::IndexMap; use metadata::MetadataManager; -use query_engine::{ModuleCacheKey, ProgramsCacheEntry, TyModuleCacheEntry}; +use query_engine::{ModuleCacheKey, ModuleCommonInfo, ParsedModuleInfo, ProgramsCacheEntry}; use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; use std::path::{Path, PathBuf}; @@ -412,14 +413,26 @@ fn parse_module_tree( let version = lsp_mode .and_then(|lsp| lsp.file_versions.get(path.as_ref()).copied()) .unwrap_or(None); - let cache_entry = ModuleCacheEntry { + // let cache_entry = ModuleCacheEntry { + // path: path.clone(), + // modified_time, + // hash, + // dependencies, + // include_tests, + // version, + // }; + + let common_info = ModuleCommonInfo { path: path.clone(), - modified_time, - hash, - dependencies, include_tests, + dependencies, + hash, + }; + let parsed_info = ParsedModuleInfo { + modified_time, version, }; + let cache_entry = ModuleCacheEntry::new(common_info, parsed_info); let split_points = ["sway-lib-core", "sway-lib-std", "libraries"]; let relevant_path = path @@ -431,7 +444,7 @@ fn parse_module_tree( relevant_path ); - query_engine.insert_parse_module_cache_entry(cache_entry); + query_engine.update_or_insert_parsed_module_cache_entry(cache_entry); Ok(ParsedModuleTree { tree_type: kind, @@ -440,40 +453,40 @@ fn parse_module_tree( }) } +// TODO: This function can probably be written better and more concisely. pub(crate) fn is_ty_module_cache_up_to_date( engines: &Engines, path: &Arc, + include_tests: bool, build_config: Option<&BuildConfig>, ) -> bool { let query_engine = engines.qe(); - let entry = query_engine.get_ty_module_cache_entry(&path); + let key = ModuleCacheKey::new(path.clone(), include_tests); + let entry = query_engine.get_module_cache_entry(&key); match entry { - Some(entry) => { - let cache_up_to_date = build_config - .as_ref() - .and_then(|x| x.lsp_mode.as_ref()) - .and_then(|lsp| lsp.file_versions.get(path.as_ref())) - .map_or_else( - || false, - |version| !version.map_or(false, |v| v > entry.version.unwrap_or(0)), - ); - if cache_up_to_date { - entry - .dependencies - .iter() - .all(|path| { + Some(entry) => match entry.typed { + Some(typed) => { + let cache_up_to_date = build_config + .as_ref() + .and_then(|x| x.lsp_mode.as_ref()) + .and_then(|lsp| lsp.file_versions.get(path.as_ref())) + .map_or_else( + || false, + |version| !version.map_or(false, |v| v > typed.version.unwrap_or(0)), + ); + if cache_up_to_date { + entry.common.dependencies.iter().all(|path| { eprint!("checking dep path {:?} ", path); - is_ty_module_cache_up_to_date(engines, path, build_config) - }) - } else { - false + is_ty_module_cache_up_to_date(engines, path, include_tests, build_config) + }) + } else { + false + } } - } - None => { - false - } + None => false, + }, + None => false, } - } pub(crate) fn is_parse_module_cache_up_to_date( @@ -490,7 +503,7 @@ pub(crate) fn is_parse_module_cache_up_to_date( let query_engine = engines.qe(); let key = ModuleCacheKey::new(path.clone(), include_tests); - let entry = query_engine.get_parse_module_cache_entry(&key); + let entry = query_engine.get_module_cache_entry(&key); let res = match entry { Some(entry) => { // Let's check if we can re-use the dependency information @@ -509,17 +522,17 @@ pub(crate) fn is_parse_module_cache_up_to_date( let modified_time = std::fs::metadata(path.as_path()) .ok() .and_then(|m| m.modified().ok()); - entry.modified_time == modified_time || { + entry.parsed.modified_time == modified_time || { let src = std::fs::read_to_string(path.as_path()).unwrap(); let mut hasher = DefaultHasher::new(); src.hash(&mut hasher); let hash = hasher.finish(); - hash == entry.hash + hash == entry.common.hash } }, |version| { // The cache is invalid if the lsp version is greater than the last compilation - !version.map_or(false, |v| v > entry.version.unwrap_or(0)) + !version.map_or(false, |v| v > entry.parsed.version.unwrap_or(0)) }, ); @@ -528,8 +541,8 @@ pub(crate) fn is_parse_module_cache_up_to_date( if cache_up_to_date { //eprintln!("num dependencies for path {:?}: {}", path, entry.dependencies.len()); - entry.dependencies.iter().all(|path| { -// eprint!("checking dep path {:?} ", path); + entry.common.dependencies.iter().all(|path| { + // eprint!("checking dep path {:?} ", path); is_parse_module_cache_up_to_date(engines, path, include_tests, build_config) }) } else { diff --git a/sway-core/src/query_engine/mod.rs b/sway-core/src/query_engine/mod.rs index 70326a75555..79e5aa04bb0 100644 --- a/sway-core/src/query_engine/mod.rs +++ b/sway-core/src/query_engine/mod.rs @@ -1,12 +1,19 @@ use parking_lot::RwLock; -use std::{collections::HashMap, path::PathBuf, sync::Arc, time::SystemTime}; -use sway_error::error::CompileError; -use sway_error::warning::CompileWarning; +use std::{ + collections::HashMap, + ops::{Deref, DerefMut}, + path::PathBuf, + sync::Arc, + time::SystemTime, +}; +use sway_error::{error::CompileError, warning::CompileWarning}; use sway_types::IdentUnique; -use crate::decl_engine::{DeclId, DeclRef}; -use crate::language::ty::{TyFunctionDecl, TyFunctionSig, TyModule}; -use crate::{Engines, Programs}; +use crate::{ + decl_engine::{DeclId, DeclRef}, + language::ty::{TyFunctionDecl, TyFunctionSig, TyModule}, + {Engines, Programs}, +}; #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct ModuleCacheKey { @@ -24,65 +31,139 @@ impl ModuleCacheKey { } #[derive(Clone, Debug)] -pub struct ModuleCacheEntry { +pub struct ModuleCommonInfo { pub path: Arc, pub hash: u64, pub include_tests: bool, pub dependencies: Vec>, - pub version: Option, +} + +#[derive(Clone, Debug)] +pub struct ParsedModuleInfo { pub modified_time: Option, + pub version: Option, } -pub type ModuleCacheMap = HashMap; +#[derive(Clone, Debug)] +pub struct TypedModuleInfo { + pub module: TyModule, + pub modified_time: Option, + pub version: Option, +} #[derive(Clone, Debug)] -pub struct ProgramsCacheEntry { - pub path: Arc, - pub programs: Programs, - pub handler_data: (Vec, Vec), +pub struct ModuleCacheEntry { + pub common: ModuleCommonInfo, + pub parsed: ParsedModuleInfo, + pub typed: Option, +} + +impl ModuleCacheEntry { + pub fn new(common: ModuleCommonInfo, parsed: ParsedModuleInfo) -> Self { + Self { + common, + parsed, + typed: None, + } + } + + pub fn is_typed(&self) -> bool { + self.typed.is_some() + } + + pub fn set_typed(&mut self, typed: TypedModuleInfo) { + self.typed = Some(typed); + } + + pub fn update_common(&mut self, new_common: ModuleCommonInfo) { + self.common = new_common; + } + + pub fn update_parsed(&mut self, new_parsed: ParsedModuleInfo) { + self.parsed = new_parsed; + } + + pub fn update_parsed_and_common( + &mut self, + new_common: ModuleCommonInfo, + new_parsed: ParsedModuleInfo, + ) { + self.common = new_common; + self.parsed = new_parsed; + } +} + +#[derive(Debug, Default, Clone)] +struct ModuleCacheMap(HashMap); + +impl Deref for ModuleCacheMap { + type Target = HashMap; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for ModuleCacheMap { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl ModuleCacheMap { + pub fn update_entry( + &mut self, + key: &ModuleCacheKey, + new_common: ModuleCommonInfo, + new_parsed: ParsedModuleInfo, + ) { + if let Some(entry) = self.get_mut(key) { + entry.update_parsed_and_common(new_common, new_parsed); + } else { + self.insert(key.clone(), ModuleCacheEntry::new(new_common, new_parsed)); + } + } } pub type ProgramsCacheMap = HashMap, ProgramsCacheEntry>; +pub type FunctionsCacheMap = HashMap<(IdentUnique, String), FunctionCacheEntry>; #[derive(Clone, Debug)] -pub struct TyModuleCacheEntry { +pub struct ProgramsCacheEntry { pub path: Arc, - pub module: TyModule, - pub dependencies: Vec>, - pub version: Option, + pub programs: Programs, + pub handler_data: (Vec, Vec), } -pub type TyModuleCacheMap = HashMap, TyModuleCacheEntry>; - #[derive(Clone, Debug)] pub struct FunctionCacheEntry { pub fn_decl: DeclRef>, } -pub type FunctionsCacheMap = HashMap<(IdentUnique, String), FunctionCacheEntry>; - #[derive(Debug, Default, Clone)] pub struct QueryEngine { // We want the below types wrapped in Arcs to optimize cloning from LSP. - parse_module_cache: Arc>, + module_cache: Arc>, programs_cache: Arc>, function_cache: Arc>, - - ty_module_cache: Arc>, } impl QueryEngine { - pub fn get_parse_module_cache_entry(&self, path: &ModuleCacheKey) -> Option { - let cache = self.parse_module_cache.read(); - cache.get(path).cloned() + pub fn get_module_cache_entry(&self, key: &ModuleCacheKey) -> Option { + let cache = self.module_cache.read(); + cache.get(key).cloned() } - pub fn insert_parse_module_cache_entry(&self, entry: ModuleCacheEntry) { - let path = entry.path.clone(); - let include_tests = entry.include_tests; + pub fn update_or_insert_parsed_module_cache_entry(&self, entry: ModuleCacheEntry) { + let path = entry.common.path.clone(); + let include_tests = entry.common.include_tests; let key = ModuleCacheKey::new(path, include_tests); - let mut cache = self.parse_module_cache.write(); - cache.insert(key, entry); + let mut cache = self.module_cache.write(); + cache.update_entry(&key, entry.common, entry.parsed); + } + + pub fn update_typed_module_cache_entry(&self, key: &ModuleCacheKey, entry: TypedModuleInfo) { + let mut cache = self.module_cache.write(); + cache.get_mut(key).unwrap().set_typed(entry); } pub fn get_programs_cache_entry(&self, path: &Arc) -> Option { @@ -95,16 +176,6 @@ impl QueryEngine { cache.insert(entry.path.clone(), entry); } - pub fn get_ty_module_cache_entry(&self, path: &PathBuf) -> Option { - let cache = self.ty_module_cache.read(); - cache.get(path).cloned() - } - - pub fn insert_ty_module_cache_entry(&self, entry: TyModuleCacheEntry) { - let mut cache = self.ty_module_cache.write(); - cache.insert(entry.path.clone(), entry); - } - pub fn get_function( &self, engines: &Engines, diff --git a/sway-core/src/semantic_analysis/module.rs b/sway-core/src/semantic_analysis/module.rs index 802f26d1d2f..26131b08eef 100644 --- a/sway-core/src/semantic_analysis/module.rs +++ b/sway-core/src/semantic_analysis/module.rs @@ -22,7 +22,7 @@ use crate::{ ty::{self, TyAstNodeContent, TyDecl}, CallPath, ModName, }, - query_engine::{ModuleCacheKey, TyModuleCacheEntry}, + query_engine::{ModuleCacheKey, TypedModuleInfo}, semantic_analysis::*, BuildConfig, Engines, TypeInfo, }; @@ -266,6 +266,7 @@ impl ty::TyModule { // Check if the module is already in the cache if let Some(source_id) = source_id { let path = engines.se().get_path(&source_id); + let include_tests = build_config.map_or(false, |x| x.include_tests); let split_points = ["sway-lib-core", "sway-lib-std", "libraries"]; let relevant_path = path @@ -273,8 +274,9 @@ impl ty::TyModule { .skip_while(|&comp| !split_points.contains(&comp.to_str().unwrap())) .collect::(); eprintln!("๐Ÿฅธ Checking cache for TY module {:?}", relevant_path); - - if let Some(entry) = engines.qe().get_ty_module_cache_entry(&path) { + + let key: ModuleCacheKey = ModuleCacheKey::new(path.clone().into(), include_tests); + if let Some(entry) = engines.qe().get_module_cache_entry(&key) { // We now need to check if the module is up to date, if not, we need to recompute it so // we will return None, otherwise we will return the cached module. @@ -283,23 +285,29 @@ impl ty::TyModule { // Let's check if we can re-use the dependency information // we got from the cache. - - if is_ty_module_cache_up_to_date(engines, &path.into(), build_config) { - eprintln!( - "๐Ÿ“Ÿ ๐Ÿ‘“ Checking cache for TY module {:?} | is up to date? {}", - relevant_path, true - ); - - eprintln!("โœ… Cache hit for module {:?}", relevant_path); - // Return the cached module - return Some(entry.module); - } else { - eprintln!( - "๐Ÿ“Ÿ ๐Ÿ‘“ Checking cache for TY module {:?} | is up to date? {}", - relevant_path, false - ); - - eprintln!("๐Ÿ”„ Cache unable to be used for module {:?}", relevant_path); + if let Some(typed) = entry.typed { + if is_ty_module_cache_up_to_date( + engines, + &path.into(), + include_tests, + build_config, + ) { + eprintln!( + "๐Ÿ“Ÿ ๐Ÿ‘“ Checking cache for TY module {:?} | is up to date? {}", + relevant_path, true + ); + + eprintln!("โœ… Cache hit for module {:?}", relevant_path); + // Return the cached module + return Some(typed.module); + } else { + eprintln!( + "๐Ÿ“Ÿ ๐Ÿ‘“ Checking cache for TY module {:?} | is up to date? {}", + relevant_path, false + ); + + eprintln!("๐Ÿ”„ Cache unable to be used for module {:?}", relevant_path); + } } } } @@ -326,7 +334,6 @@ impl ty::TyModule { .. } = parsed; - // Type-check submodules first in order of evaluation previously computed by the dependency graph. let submodules_res = module_eval_order .iter() @@ -467,30 +474,25 @@ impl ty::TyModule { .skip_while(|&comp| !split_points.contains(&comp.to_str().unwrap())) .collect::(); - // how about instead of saving this here we just check in the is_ty_module_cache_up_to_date function - let dependencies = engines.qe().get_parse_module_cache_entry(&ModuleCacheKey { - path: path.clone().into(), - include_tests: true, // TODO: pass this in - }).unwrap().dependencies; - - //eprintln!("๐Ÿค ๐Ÿค ๐Ÿค ๐Ÿค ๐Ÿค ๐Ÿค ๐Ÿค ๐Ÿค path {:?} | module_eval_order {:?}", relevant_path, module_eval_order); - eprintln!("๐Ÿค path {:?} | dependencies {:?}", relevant_path, dependencies); - - eprintln!("๐Ÿ’พ Inserting cache entry for TY module {:?}", relevant_path); - + let modified_time = std::fs::metadata(path.as_path()) + .ok() + .and_then(|m| m.modified().ok()); let version = build_config .and_then(|config| config.lsp_mode.as_ref()) .and_then(|lsp| lsp.file_versions.get(&path).copied()) .flatten(); - engines - .qe() - .insert_ty_module_cache_entry(TyModuleCacheEntry { - path: Arc::new(path), + eprintln!("๐Ÿ’พ Inserting cache entry for TY module {:?}", relevant_path); + let include_tests = build_config.map_or(false, |x| x.include_tests); + let key = ModuleCacheKey::new(path.clone().into(), include_tests); + engines.qe().update_typed_module_cache_entry( + &key, + TypedModuleInfo { module: ty_module.clone(), - dependencies, + modified_time, version, - }); + }, + ); } Ok(ty_module) diff --git a/sway-core/src/type_system/engine.rs b/sway-core/src/type_system/engine.rs index 9e2085bda39..2b91f499dc6 100644 --- a/sway-core/src/type_system/engine.rs +++ b/sway-core/src/type_system/engine.rs @@ -71,8 +71,11 @@ impl TypeEngine { where F: Fn(&SourceId) -> bool, { - self.slab.retain(|_, tsi| tsi.source_id.as_ref().map_or(true, &keep)); - self.id_map.write().retain(|tsi, _| tsi.source_id.as_ref().map_or(true, &keep)); + self.slab + .retain(|_, tsi| tsi.source_id.as_ref().map_or(true, &keep)); + self.id_map + .write() + .retain(|tsi, _| tsi.source_id.as_ref().map_or(true, &keep)); } /// Removes all data associated with `program_id` from the type engine. From 79fdfc34f80d9dd699df601e3121c13889bfcd0c Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Tue, 16 Jul 2024 16:25:59 +1000 Subject: [PATCH 08/29] wip --- sway-core/src/lib.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/sway-core/src/lib.rs b/sway-core/src/lib.rs index 24df5a46d75..73b248155cf 100644 --- a/sway-core/src/lib.rs +++ b/sway-core/src/lib.rs @@ -413,14 +413,6 @@ fn parse_module_tree( let version = lsp_mode .and_then(|lsp| lsp.file_versions.get(path.as_ref()).copied()) .unwrap_or(None); - // let cache_entry = ModuleCacheEntry { - // path: path.clone(), - // modified_time, - // hash, - // dependencies, - // include_tests, - // version, - // }; let common_info = ModuleCommonInfo { path: path.clone(), From 33dec2cef16a2d5445034ad2fb790c4afd8ea969 Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Fri, 19 Jul 2024 10:35:59 +1000 Subject: [PATCH 09/29] more debugging start of friday --- forc-pkg/src/manifest/mod.rs | 4 +-- forc-pkg/src/pkg.rs | 1 + sway-core/src/lib.rs | 38 ++++++++++++++--------- sway-core/src/semantic_analysis/module.rs | 22 ++++++------- sway-lsp/src/core/session.rs | 7 ++++- sway-lsp/src/server_state.rs | 9 ++++++ sway-lsp/tests/lib.rs | 26 ++++++++++++---- 7 files changed, 73 insertions(+), 34 deletions(-) diff --git a/forc-pkg/src/manifest/mod.rs b/forc-pkg/src/manifest/mod.rs index 3f7bc21b1bd..ca35a46e9da 100644 --- a/forc-pkg/src/manifest/mod.rs +++ b/forc-pkg/src/manifest/mod.rs @@ -566,7 +566,7 @@ impl PackageManifest { }) .map_err(|e| anyhow!("failed to parse manifest: {}.", e))?; for warning in warnings { - println_warning(&warning); + //println_warning(&warning); } manifest.implicitly_include_std_if_missing(); manifest.implicitly_include_default_build_profiles_if_missing(); @@ -955,7 +955,7 @@ impl WorkspaceManifest { }) .map_err(|e| anyhow!("failed to parse manifest: {}.", e))?; for warning in warnings { - println_warning(&warning); + //println_warning(&warning); } Ok(manifest) } diff --git a/forc-pkg/src/pkg.rs b/forc-pkg/src/pkg.rs index 22b4dc6ed5c..508fa739302 100644 --- a/forc-pkg/src/pkg.rs +++ b/forc-pkg/src/pkg.rs @@ -2711,6 +2711,7 @@ pub fn check( .as_ref() .is_some_and(|b| b.load(std::sync::atomic::Ordering::SeqCst)) { + eprintln!("๐Ÿช“ ๐Ÿช“ compilation was cancelled 2716"); bail!("compilation was retriggered") } diff --git a/sway-core/src/lib.rs b/sway-core/src/lib.rs index 73b248155cf..a2381c68e15 100644 --- a/sway-core/src/lib.rs +++ b/sway-core/src/lib.rs @@ -409,7 +409,7 @@ fn parse_module_tree( .ok() .and_then(|m| m.modified().ok()); let dependencies = submodules.into_iter().map(|s| s.path).collect::>(); - eprintln!("๐Ÿ” path {:?} | dependencies {:?}", path, dependencies); + //eprintln!("๐Ÿ” path {:?} | dependencies {:?}", path, dependencies); let version = lsp_mode .and_then(|lsp| lsp.file_versions.get(path.as_ref()).copied()) .unwrap_or(None); @@ -468,7 +468,7 @@ pub(crate) fn is_ty_module_cache_up_to_date( ); if cache_up_to_date { entry.common.dependencies.iter().all(|path| { - eprint!("checking dep path {:?} ", path); + //eprint!("checking dep path {:?} ", path); is_ty_module_cache_up_to_date(engines, path, include_tests, build_config) }) } else { @@ -544,10 +544,18 @@ pub(crate) fn is_parse_module_cache_up_to_date( None => false, }; - eprintln!( - "๐Ÿ€„ ๐Ÿ‘“ Checking cache for parse module {:?} | is up to date? {}", - relevant_path, res - ); + if res { + eprintln!( + "๐Ÿ€„ ๐Ÿ‘“ Checking cache for parse module {:?} | is up to date? true ๐ŸŸฉ", + relevant_path + ); + } else { + eprintln!( + "๐Ÿ€„ ๐Ÿ‘“ Checking cache for parse module {:?} | is up to date? FALSE ๐ŸŸฅ", + relevant_path + ); + } + res } @@ -618,7 +626,7 @@ pub fn parsed_to_ast( package_name, build_config, ); - check_should_abort(handler, retrigger_compilation.clone())?; + check_should_abort(handler, retrigger_compilation.clone(), 629)?; // Only clear the parsed AST nodes if we are running a regular compilation pipeline. // LSP needs these to build its token map, and they are cleared by `clear_program` as @@ -679,7 +687,7 @@ pub fn parsed_to_ast( None => (None, None), }; - check_should_abort(handler, retrigger_compilation.clone())?; + check_should_abort(handler, retrigger_compilation.clone(), 690)?; // Perform control flow analysis and extend with any errors. let _ = perform_control_flow_analysis( @@ -765,8 +773,8 @@ pub fn compile_to_ast( package_name: &str, retrigger_compilation: Option>, ) -> Result { - eprintln!("๐Ÿ‘จโ€๐Ÿ’ป compile_to_ast ๐Ÿ‘จโ€๐Ÿ’ป"); - check_should_abort(handler, retrigger_compilation.clone())?; + eprintln!("๐Ÿ”จ๐Ÿ”จ --- compile_to_ast --- ๐Ÿ”จ๐Ÿ”จ"); + check_should_abort(handler, retrigger_compilation.clone(), 777)?; let query_engine = engines.qe(); let mut metrics = PerformanceData::default(); if let Some(config) = build_config { @@ -794,7 +802,7 @@ pub fn compile_to_ast( metrics ); - check_should_abort(handler, retrigger_compilation.clone())?; + check_should_abort(handler, retrigger_compilation.clone(), 805)?; let (lexed_program, mut parsed_program) = match parse_program_opt { Ok(modules) => modules, @@ -809,7 +817,7 @@ pub fn compile_to_ast( parsed_program.exclude_tests(engines); } - eprintln!("๐Ÿ‘ฉโ€๐Ÿ’ป parsed to typed AST ๐Ÿ‘ฉโ€๐Ÿ’ป"); + eprintln!("๐Ÿ”จ๐Ÿ”จ --- parsed to typed AST ๐Ÿ”จ๐Ÿ”จ ---"); // Type check (+ other static analysis) the CST to a typed AST. let typed_res = time_expr!( "parse the concrete syntax tree (CST) to a typed AST", @@ -827,7 +835,7 @@ pub fn compile_to_ast( metrics ); - check_should_abort(handler, retrigger_compilation.clone())?; + check_should_abort(handler, retrigger_compilation.clone(), 838)?; handler.dedup(); @@ -843,7 +851,7 @@ pub fn compile_to_ast( query_engine.insert_programs_cache_entry(cache_entry); } - check_should_abort(handler, retrigger_compilation.clone())?; + check_should_abort(handler, retrigger_compilation.clone(), 854)?; Ok(programs) } @@ -1150,9 +1158,11 @@ fn module_return_path_analysis( fn check_should_abort( handler: &Handler, retrigger_compilation: Option>, + line_num: u32, ) -> Result<(), ErrorEmitted> { if let Some(ref retrigger_compilation) = retrigger_compilation { if retrigger_compilation.load(Ordering::SeqCst) { + eprintln!("๐Ÿช“ ๐Ÿช“ compilation was cancelled at line {} ๐Ÿช“ ๐Ÿช“", line_num); return Err(handler.cancel()); } } diff --git a/sway-core/src/semantic_analysis/module.rs b/sway-core/src/semantic_analysis/module.rs index 26131b08eef..7a22fc2e886 100644 --- a/sway-core/src/semantic_analysis/module.rs +++ b/sway-core/src/semantic_analysis/module.rs @@ -293,8 +293,8 @@ impl ty::TyModule { build_config, ) { eprintln!( - "๐Ÿ“Ÿ ๐Ÿ‘“ Checking cache for TY module {:?} | is up to date? {}", - relevant_path, true + "๐Ÿ“Ÿ ๐Ÿ‘“ Checking cache for TY module {:?} | is up to date? true ๐ŸŸฉ", + relevant_path ); eprintln!("โœ… Cache hit for module {:?}", relevant_path); @@ -302,11 +302,11 @@ impl ty::TyModule { return Some(typed.module); } else { eprintln!( - "๐Ÿ“Ÿ ๐Ÿ‘“ Checking cache for TY module {:?} | is up to date? {}", - relevant_path, false + "๐Ÿ“Ÿ ๐Ÿ‘“ Checking cache for TY module {:?} | is up to date? false ๐ŸŸฅ", + relevant_path ); - eprintln!("๐Ÿ”„ Cache unable to be used for module {:?}", relevant_path); + eprintln!("๐Ÿ”„ ๐ŸŸจ ๐ŸŸจ ๐ŸŸจ Cache unable to be used for module {:?}", relevant_path); } } } @@ -371,12 +371,12 @@ impl ty::TyModule { }) .collect::, _>>(); - // Check if the root module cache is up to date - if let Some(module) = - ty::TyModule::check_cache(parsed.span.source_id(), engines, build_config) - { - return Ok(module); - } + // // Check if the root module cache is up to date + // if let Some(module) = + // ty::TyModule::check_cache(parsed.span.source_id(), engines, build_config) + // { + // return Ok(module); + // } // TODO: Ordering should be solved across all modules prior to the beginning of type-check. let ordered_nodes = node_dependencies::order_ast_nodes_by_dependency( diff --git a/sway-lsp/src/core/session.rs b/sway-lsp/src/core/session.rs index 0236e278051..2fcdbe8643d 100644 --- a/sway-lsp/src/core/session.rs +++ b/sway-lsp/src/core/session.rs @@ -143,7 +143,7 @@ impl Session { uri: &Url, ) -> Result<(), LanguageServerError> { let path = uri.to_file_path().unwrap(); - eprintln!("๐Ÿ—‘๏ธ Garbage collecting module {:?}", path); + eprintln!("๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ Garbage collecting module {:?} ๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ", path); let source_id = { engines.se().get_source_id(&path) }; engines.clear_module(&source_id); Ok(()) @@ -338,6 +338,8 @@ pub fn traverse( let path = engines.se().get_path(source_id); let program_id = program_id_from_path(&path, engines)?; session.metrics.insert(program_id, metrics); + + eprintln!("โคต๏ธ Traversing: {:?}", path); } // Get a reference to the typed program AST. @@ -412,6 +414,7 @@ pub fn parse_project( if results.last().is_none() { return Err(LanguageServerError::ProgramsIsNone); } + eprintln!("โคต๏ธ Traversing the ASTS"); let diagnostics = traverse(results, engines, session.clone())?; if let Some(config) = &lsp_mode { // Only write the diagnostics results on didSave or didOpen. @@ -435,6 +438,7 @@ fn parse_ast_to_tokens( ctx: &ParseContext, f: impl Fn(&AstNode, &ParseContext) + Sync, ) { + eprintln!("โคต๏ธ Parsing the AST"); let nodes = parse_program .root .tree @@ -456,6 +460,7 @@ fn parse_ast_to_typed_tokens( ctx: &ParseContext, f: impl Fn(&ty::TyAstNode, &ParseContext) + Sync, ) { + eprintln!("โคต๏ธ Parsing the typed AST"); let nodes = typed_program .root .all_nodes diff --git a/sway-lsp/src/server_state.rs b/sway-lsp/src/server_state.rs index a03594721de..fbb1d9cad55 100644 --- a/sway-lsp/src/server_state.rs +++ b/sway-lsp/src/server_state.rs @@ -134,6 +134,7 @@ impl ServerState { let uri = ctx.uri.as_ref().unwrap().clone(); let session = ctx.session.as_ref().unwrap().clone(); let mut engines_clone = session.engines.read().clone(); + eprintln!("\n ----------------- NEW COMPILATION TASK: triggered by {:?} -----------------", uri.path()); if let Some(version) = ctx.version { // Perform garbage collection at configured intervals if enabled to manage memory usage. @@ -151,6 +152,8 @@ impl ServerState { err.to_string() ); } + } else { + eprintln!("No Garbabe collection applied"); } } @@ -161,6 +164,7 @@ impl ServerState { // Set the is_compiling flag to true so that the wait_for_parsing function knows that we are compiling is_compiling.store(true, Ordering::SeqCst); + eprintln!("โš™๏ธ โš™๏ธ โš™๏ธ โš™๏ธ session::parse_project โš™๏ธ โš™๏ธ โš™๏ธ โš™๏ธ"); match session::parse_project( &uri, &engines_clone, @@ -174,14 +178,17 @@ impl ServerState { // Find the module id from the path match session::program_id_from_path(&path, &engines_clone) { Ok(program_id) => { + eprintln!("๐Ÿ‘จโ€๐Ÿ’ป โœ… Compliation returned successfully ๐Ÿ‘จโ€๐Ÿ’ป"); // Use the module id to get the metrics for the module if let Some(metrics) = session.metrics.get(&program_id) { // It's very important to check if the workspace AST was reused to determine if we need to overwrite the engines. // Because the engines_clone has garbage collection applied. If the workspace AST was reused, we need to keep the old engines // as the engines_clone might have cleared some types that are still in use. + eprintln!("๐Ÿ‘จโ€๐Ÿ’ป metrics.reused_programs {} ๐Ÿ‘จโ€๐Ÿ’ป", metrics.reused_programs); if metrics.reused_programs == 0 { // The compiler did not reuse the workspace AST. // We need to overwrite the old engines with the engines clone. + eprintln!("๐Ÿ‘จโ€๐Ÿ’ป โ†ช Swapping engines ๐Ÿ‘จโ€๐Ÿ’ป โ†ช"); mem::swap( &mut *session.engines.write(), &mut engines_clone, @@ -192,6 +199,7 @@ impl ServerState { LastCompilationState::Success; } Err(err) => { + eprintln!("๐Ÿ‘จโ€๐Ÿ’ป โŒ Compliation failed ๐Ÿ‘จโ€๐Ÿ’ป"); tracing::error!("{}", err.to_string()); *last_compilation_state.write() = LastCompilationState::Failed; @@ -199,6 +207,7 @@ impl ServerState { } } Err(_err) => { + eprintln!("๐Ÿ‘จโ€๐Ÿ’ป โŒ Compliation failed ๐Ÿ‘จโ€๐Ÿ’ป"); *last_compilation_state.write() = LastCompilationState::Failed; } } diff --git a/sway-lsp/tests/lib.rs b/sway-lsp/tests/lib.rs index 60444f90977..4b4f8bc5a33 100644 --- a/sway-lsp/tests/lib.rs +++ b/sway-lsp/tests/lib.rs @@ -233,15 +233,29 @@ fn did_change_stress_test() { #[test] fn did_change_stress_test_random_wait() { run_async!({ - let test_duration = tokio::time::Duration::from_secs(5 * 60); // 5 minutes timeout + let test_duration = tokio::time::Duration::from_secs(250 * 60); // 5 minutes timeout let test_future = async { setup_panic_hook(); let (mut service, _) = LspService::new(ServerState::new); - let example_dir = sway_workspace_dir() - .join(e2e_language_dir()) - .join("generics_in_contract"); - let uri = init_and_open(&mut service, example_dir.join("src/main.sw")).await; - let times = 60; + // let example_dir = sway_workspace_dir() + // .join(e2e_language_dir()) + // .join("generics_in_contract"); +// let uri = init_and_open(&mut service, example_dir.join("src/main.sw")).await; + + let uri = init_and_open( + &mut service, + PathBuf::from("/Users/josh/Documents/rust/fuel/user_projects/fluid-protocol/libraries") + .join("src/fpt_staking_interface.sw"), + ) + .await; + + // 1. randomise the file that is changed out of all the files in the project. + // 2. change the file + // 3. wait for the file to be parsed + // 4. try and do a hover or goto def for a random type + // 5. repeat. + + let times = 6000; for version in 0..times { //eprintln!("version: {}", version); let _ = lsp::did_change_request(&mut service, &uri, version + 1, None).await; From 439ec8e156af9b52378c56d4e84d22ebf09b5bf5 Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Fri, 19 Jul 2024 11:28:50 +1000 Subject: [PATCH 10/29] check if root module is up to date --- sway-core/src/semantic_analysis/module.rs | 15 +++++++++------ .../src/semantic_analysis/node_dependencies.rs | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/sway-core/src/semantic_analysis/module.rs b/sway-core/src/semantic_analysis/module.rs index 7a22fc2e886..a9a76539bb7 100644 --- a/sway-core/src/semantic_analysis/module.rs +++ b/sway-core/src/semantic_analysis/module.rs @@ -334,6 +334,14 @@ impl ty::TyModule { .. } = parsed; + // Check if the root module cache is up to date + eprintln!("Root Module: {:?}", parsed.span.source_id().map(|x| engines.se().get_path(x))); + if let Some(module) = + ty::TyModule::check_cache(parsed.span.source_id(), engines, build_config) + { + return Ok(module); + } + // Type-check submodules first in order of evaluation previously computed by the dependency graph. let submodules_res = module_eval_order .iter() @@ -371,12 +379,7 @@ impl ty::TyModule { }) .collect::, _>>(); - // // Check if the root module cache is up to date - // if let Some(module) = - // ty::TyModule::check_cache(parsed.span.source_id(), engines, build_config) - // { - // return Ok(module); - // } + // TODO: Ordering should be solved across all modules prior to the beginning of type-check. let ordered_nodes = node_dependencies::order_ast_nodes_by_dependency( diff --git a/sway-core/src/semantic_analysis/node_dependencies.rs b/sway-core/src/semantic_analysis/node_dependencies.rs index 45d2858a1b8..9edf1cde31f 100644 --- a/sway-core/src/semantic_analysis/node_dependencies.rs +++ b/sway-core/src/semantic_analysis/node_dependencies.rs @@ -42,7 +42,7 @@ pub(crate) fn order_ast_nodes_by_dependency( Ok(()) })?; - // Reorder the parsed AstNodes based on dependency. Includes first, then uses, then + // Reorder the parsed AstNodes based on dependency. Includes first, then uses, then // reordered declarations, then anything else. To keep the list stable and simple we can // use a basic insertion sort. Ok(nodes From 1fe9845935df1578c8ec06afea46aaf1a43a4268 Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Fri, 19 Jul 2024 12:14:57 +1000 Subject: [PATCH 11/29] add debug timings --- forc-pkg/src/pkg.rs | 2 ++ sway-lsp/src/core/session.rs | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/forc-pkg/src/pkg.rs b/forc-pkg/src/pkg.rs index 508fa739302..057ef1125c5 100644 --- a/forc-pkg/src/pkg.rs +++ b/forc-pkg/src/pkg.rs @@ -2655,6 +2655,7 @@ pub fn check( let mut results = vec![]; for (idx, &node) in plan.compilation_order.iter().enumerate() { + let now = std::time::Instant::now(); let pkg = &plan.graph[node]; let manifest = &plan.manifest_map()[&pkg.id()]; @@ -2747,6 +2748,7 @@ pub fn check( return Ok(results); } results.push((programs_res.ok(), handler)); + eprintln!("โฑ๏ธ Compiling package {:?} took {:?}", pkg.name, now.elapsed()); } if results.is_empty() { diff --git a/sway-lsp/src/core/session.rs b/sway-lsp/src/core/session.rs index 2fcdbe8643d..8ff0d293f03 100644 --- a/sway-lsp/src/core/session.rs +++ b/sway-lsp/src/core/session.rs @@ -26,6 +26,7 @@ use pkg::{ BuildPlan, }; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; +use swayfmt::parse; use std::{ path::PathBuf, sync::{atomic::AtomicBool, Arc}, @@ -279,6 +280,9 @@ pub fn compile( experimental: sway_core::ExperimentalFlags, ) -> Result, Handler)>, LanguageServerError> { let _p = tracing::trace_span!("compile").entered(); + let build_plan_now = std::time::Instant::now(); + let build_plan = build_plan(uri)?; + eprintln!("โฑ๏ธ Build Plan took {:?}", build_plan_now.elapsed()); let tests_enabled = true; pkg::check( build_plan, @@ -404,6 +408,7 @@ pub fn parse_project( let build_plan = session .build_plan_cache .get_or_update(&session.sync.manifest_path(), || build_plan(uri))?; + let parse_now = std::time::Instant::now(); let results = compile( &build_plan, engines, @@ -411,11 +416,14 @@ pub fn parse_project( lsp_mode.clone(), experimental, )?; + eprintln!("โฑ๏ธ Total Compilation took {:?}", parse_now.elapsed()); if results.last().is_none() { return Err(LanguageServerError::ProgramsIsNone); } eprintln!("โคต๏ธ Traversing the ASTS"); + let traverse_now = std::time::Instant::now(); let diagnostics = traverse(results, engines, session.clone())?; + eprintln!("โฑ๏ธ Traversing the ASTS took {:?}", traverse_now.elapsed()); if let Some(config) = &lsp_mode { // Only write the diagnostics results on didSave or didOpen. if !config.optimized_build { @@ -425,10 +433,13 @@ pub fn parse_project( } } } + let runnables_now = std::time::Instant::now(); if let Some(typed) = &session.compiled_program.read().typed { session.runnables.clear(); create_runnables(&session.runnables, typed, engines.de(), engines.se()); } + eprintln!("โฑ๏ธ creating runnables took: {:?}", runnables_now.elapsed()); + eprintln!("โฑ๏ธ TOTAL COMPILATION AND TRAVERSAL TIME: {:?}", parse_now.elapsed()); Ok(()) } From 227804b3ef0e19ff2cd00e60da98827a773f732f Mon Sep 17 00:00:00 2001 From: Joshua Batty Date: Mon, 22 Jul 2024 12:10:22 +1000 Subject: [PATCH 12/29] more timings --- forc-pkg/src/pkg.rs | 6 ++++++ sway-core/Cargo.toml | 1 + sway-core/src/lib.rs | 11 ++++++++++- sway-lsp/src/core/session.rs | 2 ++ sway-lsp/tests/lib.rs | 2 +- 5 files changed, 20 insertions(+), 2 deletions(-) diff --git a/forc-pkg/src/pkg.rs b/forc-pkg/src/pkg.rs index 057ef1125c5..5e446567e47 100644 --- a/forc-pkg/src/pkg.rs +++ b/forc-pkg/src/pkg.rs @@ -2671,6 +2671,7 @@ pub fn check( let contract_id_value = (idx == plan.compilation_order.len() - 1).then(|| DUMMY_CONTRACT_ID.to_string()); + let dep_now = std::time::Instant::now(); let mut dep_namespace = dependency_namespace( &lib_namespace_map, &compiled_contract_deps, @@ -2681,12 +2682,14 @@ pub fn check( experimental, ) .expect("failed to create dependency namespace"); + eprintln!("โฑ๏ธ Dependency namespace took {:?}", dep_now.elapsed()); let profile = BuildProfile { terse: terse_mode, ..BuildProfile::debug() }; + let build_config_now = std::time::Instant::now(); let build_config = sway_build_config( manifest.dir(), &manifest.entry_path(), @@ -2695,9 +2698,11 @@ pub fn check( )? .with_include_tests(include_tests) .with_lsp_mode(lsp_mode.clone()); + eprintln!("โฑ๏ธ Build config took {:?}", build_config_now.elapsed()); let input = manifest.entry_string()?; let handler = Handler::default(); + let compile_to_ast_now = std::time::Instant::now(); let programs_res = sway_core::compile_to_ast( &handler, engines, @@ -2707,6 +2712,7 @@ pub fn check( &pkg.name, retrigger_compilation.clone(), ); + eprintln!("โฑ๏ธ Compile to AST took {:?}", compile_to_ast_now.elapsed()); if retrigger_compilation .as_ref() diff --git a/sway-core/Cargo.toml b/sway-core/Cargo.toml index 9c0122123c2..a903dca0b7b 100644 --- a/sway-core/Cargo.toml +++ b/sway-core/Cargo.toml @@ -34,6 +34,7 @@ parking_lot = "0.12" pest = "2.1.3" pest_derive = "2.1" petgraph = "0.6" +rayon = "1.5" rustc-hash = "1.1.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.91" diff --git a/sway-core/src/lib.rs b/sway-core/src/lib.rs index a2381c68e15..9be80676d8b 100644 --- a/sway-core/src/lib.rs +++ b/sway-core/src/lib.rs @@ -31,10 +31,12 @@ pub use asm_generation::{CompiledBytecode, FinalizedEntry}; pub use build_config::{BuildConfig, BuildTarget, LspConfig, OptLevel, PrintAsm, PrintIr}; use control_flow_analysis::ControlFlowGraph; pub use debug_generation::write_dwarf; +use decl_engine::parsed_engine; use fuel_vm::fuel_merkle::common; use indexmap::IndexMap; use metadata::MetadataManager; use query_engine::{ModuleCacheKey, ModuleCommonInfo, ParsedModuleInfo, ProgramsCacheEntry}; +use rayon::iter::{ParallelBridge, ParallelIterator}; use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; use std::path::{Path, PathBuf}; @@ -348,6 +350,7 @@ fn parse_module_tree( ) -> Result { let query_engine = engines.qe(); + let lexed_now = std::time::Instant::now(); // Parse this module first. let module_dir = path.parent().expect("module file has no parent directory"); let source_id = engines.se().get_source_id(&path.clone()); @@ -366,7 +369,9 @@ fn parse_module_tree( experimental, lsp_mode, ); + eprintln!("โฑ๏ธ Lexed module took {:?}", lexed_now.elapsed()); + let parsed_now = std::time::Instant::now(); // Convert from the raw parsed module to the `ParseTree` ready for type-check. let (kind, tree) = to_parsed_lang::convert_parse_tree( &mut to_parsed_lang::Context::new(build_target, experimental), @@ -376,6 +381,7 @@ fn parse_module_tree( )?; let module_kind_span = module.value.kind.span(); let attributes = module_attrs_to_map(handler, &module.attribute_list)?; + eprintln!("โฑ๏ธ Parsed module took {:?}", parsed_now.elapsed()); let lexed_submodules = submodules .iter() @@ -555,7 +561,6 @@ pub(crate) fn is_parse_module_cache_up_to_date( relevant_path ); } - res } @@ -775,12 +780,16 @@ pub fn compile_to_ast( ) -> Result { eprintln!("๐Ÿ”จ๐Ÿ”จ --- compile_to_ast --- ๐Ÿ”จ๐Ÿ”จ"); check_should_abort(handler, retrigger_compilation.clone(), 777)?; + let query_engine = engines.qe(); let mut metrics = PerformanceData::default(); if let Some(config) = build_config { let path = config.canonical_root_module(); let include_tests = config.include_tests; + //eprintln!(" ๐Ÿ“‚ {}", path.display()); + //eprintln!("{:?}", config.lsp_mode.as_ref().unwrap().file_versions); + // Check if we can re-use the data in the cache. if is_parse_module_cache_up_to_date(engines, &path, include_tests, build_config) { let mut entry = query_engine.get_programs_cache_entry(&path).unwrap(); diff --git a/sway-lsp/src/core/session.rs b/sway-lsp/src/core/session.rs index 8ff0d293f03..ab58688acee 100644 --- a/sway-lsp/src/core/session.rs +++ b/sway-lsp/src/core/session.rs @@ -310,6 +310,7 @@ pub fn traverse( let mut diagnostics: CompileResults = (Vec::default(), Vec::default()); let results_len = results.len(); for (i, (value, handler)) in results.into_iter().enumerate() { + let parse_now = std::time::Instant::now(); // We can convert these destructured elements to a Vec later on. let current_diagnostics = handler.consume(); diagnostics = current_diagnostics; @@ -391,6 +392,7 @@ pub fn traverse( dependency::collect_typed_declaration(node, ctx); }); } + eprintln!("โฑ๏ธ Traversal took {:?}", parse_now.elapsed()); } Ok(Some(diagnostics)) } diff --git a/sway-lsp/tests/lib.rs b/sway-lsp/tests/lib.rs index 4b4f8bc5a33..117bd87a895 100644 --- a/sway-lsp/tests/lib.rs +++ b/sway-lsp/tests/lib.rs @@ -244,7 +244,7 @@ fn did_change_stress_test_random_wait() { let uri = init_and_open( &mut service, - PathBuf::from("/Users/josh/Documents/rust/fuel/user_projects/fluid-protocol/libraries") + PathBuf::from("/Users/joshuabatty/Documents/rust/fuel/user_projects/fluid-protocol/libraries") .join("src/fpt_staking_interface.sw"), ) .await; From 2b90475408d55502ea64c8bb6f744dfb36897883 Mon Sep 17 00:00:00 2001 From: Joshua Batty Date: Mon, 22 Jul 2024 13:35:52 +1000 Subject: [PATCH 13/29] clean up --- sway-core/src/semantic_analysis/module.rs | 28 +++++++++-------------- sway-lsp/src/handlers/notification.rs | 1 + sway-lsp/tests/lib.rs | 2 +- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/sway-core/src/semantic_analysis/module.rs b/sway-core/src/semantic_analysis/module.rs index a9a76539bb7..eb3278e37ed 100644 --- a/sway-core/src/semantic_analysis/module.rs +++ b/sway-core/src/semantic_analysis/module.rs @@ -273,7 +273,6 @@ impl ty::TyModule { .iter() .skip_while(|&comp| !split_points.contains(&comp.to_str().unwrap())) .collect::(); - eprintln!("๐Ÿฅธ Checking cache for TY module {:?}", relevant_path); let key: ModuleCacheKey = ModuleCacheKey::new(path.clone().into(), include_tests); if let Some(entry) = engines.qe().get_module_cache_entry(&key) { @@ -286,29 +285,24 @@ impl ty::TyModule { // Let's check if we can re-use the dependency information // we got from the cache. if let Some(typed) = entry.typed { - if is_ty_module_cache_up_to_date( + let is_up_to_date = is_ty_module_cache_up_to_date( engines, &path.into(), include_tests, build_config, - ) { - eprintln!( - "๐Ÿ“Ÿ ๐Ÿ‘“ Checking cache for TY module {:?} | is up to date? true ๐ŸŸฉ", - relevant_path - ); - - eprintln!("โœ… Cache hit for module {:?}", relevant_path); - // Return the cached module - return Some(typed.module); + ); + let status = if is_up_to_date { + "โœ… Cache hit" } else { - eprintln!( - "๐Ÿ“Ÿ ๐Ÿ‘“ Checking cache for TY module {:?} | is up to date? false ๐ŸŸฅ", - relevant_path - ); - - eprintln!("๐Ÿ”„ ๐ŸŸจ ๐ŸŸจ ๐ŸŸจ Cache unable to be used for module {:?}", relevant_path); + "๐Ÿ”„ Cache miss" + }; + eprintln!("{} for module {:?} (up to date: {})", status, relevant_path, is_up_to_date); + if is_up_to_date { + return Some(typed.module); } } + } else { + eprintln!("โŒ No cache entry for module {:?}", relevant_path); } } None diff --git a/sway-lsp/src/handlers/notification.rs b/sway-lsp/src/handlers/notification.rs index 928262efb51..7961bd6dea6 100644 --- a/sway-lsp/src/handlers/notification.rs +++ b/sway-lsp/src/handlers/notification.rs @@ -106,6 +106,7 @@ pub async fn handle_did_change_text_document( &uri, Some(params.text_document.version as u64), ); + eprintln!("File versions: {:#?}", file_versions); send_new_compilation_request( state, session.clone(), diff --git a/sway-lsp/tests/lib.rs b/sway-lsp/tests/lib.rs index 117bd87a895..4cdfd3224a7 100644 --- a/sway-lsp/tests/lib.rs +++ b/sway-lsp/tests/lib.rs @@ -171,7 +171,7 @@ fn did_open_fluid_libraries() { .finish(); let uri = init_and_open( &mut service, - PathBuf::from("/Users/josh/Documents/rust/fuel/user_projects/fluid-protocol/libraries") + PathBuf::from("/Users/joshuabatty/Documents/rust/fuel/user_projects/fluid-protocol/libraries") .join("src/interface.sw"), ) .await; From e5bfd87c7de8dbbb3df1e3e96b5cd81c7d85ce40 Mon Sep 17 00:00:00 2001 From: Joshua Batty Date: Tue, 23 Jul 2024 11:08:58 +1000 Subject: [PATCH 14/29] wip witching to mac studio --- sway-lsp/src/handlers/notification.rs | 2 +- sway-lsp/src/server_state.rs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/sway-lsp/src/handlers/notification.rs b/sway-lsp/src/handlers/notification.rs index 7961bd6dea6..ac7a8e23b29 100644 --- a/sway-lsp/src/handlers/notification.rs +++ b/sway-lsp/src/handlers/notification.rs @@ -106,7 +106,7 @@ pub async fn handle_did_change_text_document( &uri, Some(params.text_document.version as u64), ); - eprintln!("File versions: {:#?}", file_versions); + //eprintln!("File versions: {:#?}", file_versions); send_new_compilation_request( state, session.clone(), diff --git a/sway-lsp/src/server_state.rs b/sway-lsp/src/server_state.rs index fbb1d9cad55..4c96075640b 100644 --- a/sway-lsp/src/server_state.rs +++ b/sway-lsp/src/server_state.rs @@ -381,6 +381,9 @@ impl ServerState { let session = self.sessions.get(&manifest_dir).unwrap_or({ // If no session can be found, then we need to call init and insert a new session into the map + eprintln!("๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ Initializing new session for manifest_dir = {:?}", manifest_dir); + + self.init_session(uri).await?; self.sessions .get(&manifest_dir) From bb3c0fcd3b3c5d070a9cfec36966e5c1e823de55 Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Tue, 23 Jul 2024 11:59:30 +1000 Subject: [PATCH 15/29] fix session cache bug --- forc-pkg/src/pkg.rs | 6 +- sway-core/src/semantic_analysis/module.rs | 12 ++- sway-lsp/src/core/session.rs | 21 ++-- sway-lsp/src/server_state.rs | 124 ++++++++++++++++++---- sway-lsp/tests/lib.rs | 55 ++++++++-- 5 files changed, 172 insertions(+), 46 deletions(-) diff --git a/forc-pkg/src/pkg.rs b/forc-pkg/src/pkg.rs index 5e446567e47..65e44e67dc5 100644 --- a/forc-pkg/src/pkg.rs +++ b/forc-pkg/src/pkg.rs @@ -2754,7 +2754,11 @@ pub fn check( return Ok(results); } results.push((programs_res.ok(), handler)); - eprintln!("โฑ๏ธ Compiling package {:?} took {:?}", pkg.name, now.elapsed()); + eprintln!( + "โฑ๏ธ Compiling package {:?} took {:?}", + pkg.name, + now.elapsed() + ); } if results.is_empty() { diff --git a/sway-core/src/semantic_analysis/module.rs b/sway-core/src/semantic_analysis/module.rs index eb3278e37ed..a6d71b519db 100644 --- a/sway-core/src/semantic_analysis/module.rs +++ b/sway-core/src/semantic_analysis/module.rs @@ -296,7 +296,10 @@ impl ty::TyModule { } else { "๐Ÿ”„ Cache miss" }; - eprintln!("{} for module {:?} (up to date: {})", status, relevant_path, is_up_to_date); + eprintln!( + "{} for module {:?} (up to date: {})", + status, relevant_path, is_up_to_date + ); if is_up_to_date { return Some(typed.module); } @@ -329,7 +332,10 @@ impl ty::TyModule { } = parsed; // Check if the root module cache is up to date - eprintln!("Root Module: {:?}", parsed.span.source_id().map(|x| engines.se().get_path(x))); + eprintln!( + "Root Module: {:?}", + parsed.span.source_id().map(|x| engines.se().get_path(x)) + ); if let Some(module) = ty::TyModule::check_cache(parsed.span.source_id(), engines, build_config) { @@ -373,8 +379,6 @@ impl ty::TyModule { }) .collect::, _>>(); - - // TODO: Ordering should be solved across all modules prior to the beginning of type-check. let ordered_nodes = node_dependencies::order_ast_nodes_by_dependency( handler, diff --git a/sway-lsp/src/core/session.rs b/sway-lsp/src/core/session.rs index ab58688acee..a25449bc43a 100644 --- a/sway-lsp/src/core/session.rs +++ b/sway-lsp/src/core/session.rs @@ -26,7 +26,6 @@ use pkg::{ BuildPlan, }; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; -use swayfmt::parse; use std::{ path::PathBuf, sync::{atomic::AtomicBool, Arc}, @@ -44,6 +43,7 @@ use sway_core::{ use sway_error::{error::CompileError, handler::Handler, warning::CompileWarning}; use sway_types::{ProgramId, SourceEngine, Spanned}; use sway_utils::{helpers::get_sway_files, PerformanceData}; +use swayfmt::parse; pub type RunnableMap = DashMap>>; pub type ProjectDirectory = PathBuf; @@ -144,7 +144,10 @@ impl Session { uri: &Url, ) -> Result<(), LanguageServerError> { let path = uri.to_file_path().unwrap(); - eprintln!("๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ Garbage collecting module {:?} ๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ", path); + eprintln!( + "๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ Garbage collecting module {:?} ๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ", + path + ); let source_id = { engines.se().get_source_id(&path) }; engines.clear_module(&source_id); Ok(()) @@ -280,16 +283,12 @@ pub fn compile( experimental: sway_core::ExperimentalFlags, ) -> Result, Handler)>, LanguageServerError> { let _p = tracing::trace_span!("compile").entered(); - let build_plan_now = std::time::Instant::now(); - let build_plan = build_plan(uri)?; - eprintln!("โฑ๏ธ Build Plan took {:?}", build_plan_now.elapsed()); - let tests_enabled = true; pkg::check( build_plan, BuildTarget::default(), true, lsp_mode, - tests_enabled, + true, engines, retrigger_compilation, experimental, @@ -407,9 +406,12 @@ pub fn parse_project( experimental: sway_core::ExperimentalFlags, ) -> Result<(), LanguageServerError> { let _p = tracing::trace_span!("parse_project").entered(); + let build_plan_now = std::time::Instant::now(); let build_plan = session .build_plan_cache .get_or_update(&session.sync.manifest_path(), || build_plan(uri))?; + eprintln!("โฑ๏ธ Build Plan took {:?}", build_plan_now.elapsed()); + let parse_now = std::time::Instant::now(); let results = compile( &build_plan, @@ -441,7 +443,10 @@ pub fn parse_project( create_runnables(&session.runnables, typed, engines.de(), engines.se()); } eprintln!("โฑ๏ธ creating runnables took: {:?}", runnables_now.elapsed()); - eprintln!("โฑ๏ธ TOTAL COMPILATION AND TRAVERSAL TIME: {:?}", parse_now.elapsed()); + eprintln!( + "โฑ๏ธ TOTAL COMPILATION AND TRAVERSAL TIME: {:?}", + parse_now.elapsed() + ); Ok(()) } diff --git a/sway-lsp/src/server_state.rs b/sway-lsp/src/server_state.rs index 4c96075640b..4dffa910458 100644 --- a/sway-lsp/src/server_state.rs +++ b/sway-lsp/src/server_state.rs @@ -41,7 +41,7 @@ pub struct ServerState { /// A Least Recently Used (LRU) cache of [Session]s, each representing a project opened in the user's workspace. /// This cache limits memory usage by maintaining a fixed number of active sessions, automatically /// evicting the least recently used sessions when the capacity is reached. - pub(crate) sessions: LruSessionCache, + pub sessions: LruSessionCache, pub documents: Documents, // Compilation thread related fields pub(crate) retrigger_compilation: Arc, @@ -184,7 +184,10 @@ impl ServerState { // It's very important to check if the workspace AST was reused to determine if we need to overwrite the engines. // Because the engines_clone has garbage collection applied. If the workspace AST was reused, we need to keep the old engines // as the engines_clone might have cleared some types that are still in use. - eprintln!("๐Ÿ‘จโ€๐Ÿ’ป metrics.reused_programs {} ๐Ÿ‘จโ€๐Ÿ’ป", metrics.reused_programs); + eprintln!( + "๐Ÿ‘จโ€๐Ÿ’ป metrics.reused_programs {} ๐Ÿ‘จโ€๐Ÿ’ป", + metrics.reused_programs + ); if metrics.reused_programs == 0 { // The compiler did not reuse the workspace AST. // We need to overwrite the old engines with the engines clone. @@ -355,7 +358,7 @@ impl ServerState { /// Constructs and returns a tuple of `(Url, Arc)` from a given workspace URI. /// The returned URL represents the temp directory workspace. - pub(crate) async fn uri_and_session_from_workspace( + pub async fn uri_and_session_from_workspace( &self, workspace_uri: &Url, ) -> Result<(Url, Arc), LanguageServerError> { @@ -379,23 +382,23 @@ impl ServerState { .ok_or(DirectoryError::ManifestDirNotFound)? .to_path_buf(); - let session = self.sessions.get(&manifest_dir).unwrap_or({ - // If no session can be found, then we need to call init and insert a new session into the map - eprintln!("๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ Initializing new session for manifest_dir = {:?}", manifest_dir); + // If the session is already in the cache, return it + if let Some(session) = self.sessions.get(&manifest_dir) { + return Ok(session); + } + // If no session can be found, then we need to call init and insert a new session into the map + let session = Arc::new(Session::new()); + session.init(uri, &self.documents).await?; + self.sessions.insert(manifest_dir.clone(), session.clone()); - self.init_session(uri).await?; - self.sessions - .get(&manifest_dir) - .expect("no session found even though it was just inserted into the map") - }); Ok(session) } } /// A Least Recently Used (LRU) cache for storing and managing `Session` objects. /// This cache helps limit memory usage by maintaining a fixed number of active sessions. -pub(crate) struct LruSessionCache { +pub struct LruSessionCache { /// Stores the actual `Session` objects, keyed by their file paths. sessions: Arc>>, /// Keeps track of the order in which sessions were accessed, with most recent at the front. @@ -406,7 +409,7 @@ pub(crate) struct LruSessionCache { impl LruSessionCache { /// Creates a new `LruSessionCache` with the specified capacity. - pub(crate) fn new(capacity: usize) -> Self { + pub fn new(capacity: usize) -> Self { LruSessionCache { sessions: Arc::new(DashMap::new()), usage_order: Arc::new(Mutex::new(VecDeque::with_capacity(capacity))), @@ -414,12 +417,12 @@ impl LruSessionCache { } } - pub(crate) fn iter(&self) -> impl Iterator>> { + pub fn iter(&self) -> impl Iterator>> { self.sessions.iter() } /// Retrieves a session from the cache and updates its position to the front of the usage order. - pub(crate) fn get(&self, path: &PathBuf) -> Option> { + pub fn get(&self, path: &PathBuf) -> Option> { if let Some(session) = self.sessions.try_get(path).try_unwrap() { if self.sessions.len() >= self.capacity { self.move_to_front(path); @@ -433,16 +436,13 @@ impl LruSessionCache { /// Inserts or updates a session in the cache. /// If at capacity and inserting a new session, evicts the least recently used one. /// For existing sessions, updates their position in the usage order if at capacity. - pub(crate) fn insert(&self, path: PathBuf, session: Arc) { - if self.sessions.get(&path).is_some() { - tracing::trace!("Updating existing session for path: {:?}", path); - // Session already exists, just update its position in the usage order if at capacity - if self.sessions.len() >= self.capacity { - self.move_to_front(&path); - } + pub fn insert(&self, path: PathBuf, session: Arc) { + if let Some(mut entry) = self.sessions.get_mut(&path) { + // Session already exists, update it + *entry = session; + self.move_to_front(&path); } else { // New session - tracing::trace!("Inserting new session for path: {:?}", path); if self.sessions.len() >= self.capacity { self.evict_least_used(); } @@ -474,3 +474,81 @@ impl LruSessionCache { } } } + +#[cfg(test)] +mod tests { + use super::*; + use std::path::PathBuf; + use std::sync::Arc; + + #[test] + fn test_lru_session_cache_insertion_and_retrieval() { + let cache = LruSessionCache::new(2); + let path1 = PathBuf::from("/path/1"); + let path2 = PathBuf::from("/path/2"); + let session1 = Arc::new(Session::new()); + let session2 = Arc::new(Session::new()); + + cache.insert(path1.clone(), session1.clone()); + cache.insert(path2.clone(), session2.clone()); + + assert!(Arc::ptr_eq(&cache.get(&path1).unwrap(), &session1)); + assert!(Arc::ptr_eq(&cache.get(&path2).unwrap(), &session2)); + } + + #[test] + fn test_lru_session_cache_capacity() { + let cache = LruSessionCache::new(2); + let path1 = PathBuf::from("/path/1"); + let path2 = PathBuf::from("/path/2"); + let path3 = PathBuf::from("/path/3"); + let session1 = Arc::new(Session::new()); + let session2 = Arc::new(Session::new()); + let session3 = Arc::new(Session::new()); + + cache.insert(path1.clone(), session1); + cache.insert(path2.clone(), session2); + cache.insert(path3.clone(), session3); + + assert!(cache.get(&path1).is_none()); + assert!(cache.get(&path2).is_some()); + assert!(cache.get(&path3).is_some()); + } + + #[test] + fn test_lru_session_cache_update_order() { + let cache = LruSessionCache::new(2); + let path1 = PathBuf::from("/path/1"); + let path2 = PathBuf::from("/path/2"); + let path3 = PathBuf::from("/path/3"); + let session1 = Arc::new(Session::new()); + let session2 = Arc::new(Session::new()); + let session3 = Arc::new(Session::new()); + + cache.insert(path1.clone(), session1.clone()); + cache.insert(path2.clone(), session2.clone()); + + // Access path1 to move it to the front + cache.get(&path1); + + // Insert path3, which should evict path2 + cache.insert(path3.clone(), session3); + + assert!(cache.get(&path1).is_some()); + assert!(cache.get(&path2).is_none()); + assert!(cache.get(&path3).is_some()); + } + + #[test] + fn test_lru_session_cache_overwrite() { + let cache = LruSessionCache::new(2); + let path1 = PathBuf::from("/path/1"); + let session1 = Arc::new(Session::new()); + let session1_new = Arc::new(Session::new()); + + cache.insert(path1.clone(), session1); + cache.insert(path1.clone(), session1_new.clone()); + + assert!(Arc::ptr_eq(&cache.get(&path1).unwrap(), &session1_new)); + } +} diff --git a/sway-lsp/tests/lib.rs b/sway-lsp/tests/lib.rs index 4cdfd3224a7..f812e219e39 100644 --- a/sway-lsp/tests/lib.rs +++ b/sway-lsp/tests/lib.rs @@ -171,7 +171,7 @@ fn did_open_fluid_libraries() { .finish(); let uri = init_and_open( &mut service, - PathBuf::from("/Users/joshuabatty/Documents/rust/fuel/user_projects/fluid-protocol/libraries") + PathBuf::from("/Users/josh/Documents/rust/fuel/user_projects/fluid-protocol/libraries") .join("src/interface.sw"), ) .await; @@ -239,21 +239,23 @@ fn did_change_stress_test_random_wait() { let (mut service, _) = LspService::new(ServerState::new); // let example_dir = sway_workspace_dir() // .join(e2e_language_dir()) - // .join("generics_in_contract"); -// let uri = init_and_open(&mut service, example_dir.join("src/main.sw")).await; + // .join("generics_in_contract"); + // let uri = init_and_open(&mut service, example_dir.join("src/main.sw")).await; let uri = init_and_open( &mut service, - PathBuf::from("/Users/joshuabatty/Documents/rust/fuel/user_projects/fluid-protocol/libraries") - .join("src/fpt_staking_interface.sw"), + PathBuf::from( + "/Users/josh/Documents/rust/fuel/user_projects/fluid-protocol/libraries", + ) + .join("src/fpt_staking_interface.sw"), ) .await; - // 1. randomise the file that is changed out of all the files in the project. - // 2. change the file - // 3. wait for the file to be parsed - // 4. try and do a hover or goto def for a random type - // 5. repeat. + // 1. randomise the file that is changed out of all the files in the project. + // 2. change the file + // 3. wait for the file to be parsed + // 4. try and do a hover or goto def for a random type + // 5. repeat. let times = 6000; for version in 0..times { @@ -2195,3 +2197,36 @@ async fn write_all_example_asts() { } let _ = server.shutdown_server(); } + +#[test] +fn test_url_to_session_existing_session() { + use std::sync::Arc; + run_async!({ + let (mut service, _) = LspService::new(ServerState::new); + let uri = init_and_open(&mut service, doc_comments_dir().join("src/main.sw")).await; + + // First call to uri_and_session_from_workspace + let (first_uri, first_session) = service + .inner() + .uri_and_session_from_workspace(&uri) + .await + .unwrap(); + + // Second call to uri_and_session_from_workspace + let (second_uri, second_session) = service + .inner() + .uri_and_session_from_workspace(&uri) + .await + .unwrap(); + + // Assert that the URIs are the same + assert_eq!(first_uri, second_uri, "URIs should be identical"); + + // Assert that the sessions are the same (they should point to the same Arc) + assert!( + Arc::ptr_eq(&first_session, &second_session), + "Sessions should be identical" + ); + shutdown_and_exit(&mut service).await; + }); +} From b00d702deabd4afda7ad9c6d0165a3d3fa5dd2a7 Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Tue, 23 Jul 2024 13:02:51 +1000 Subject: [PATCH 16/29] dont clone module cache entry --- sway-core/src/language/ty/module.rs | 2 +- sway-core/src/language/ty/program.rs | 4 +-- sway-core/src/lib.rs | 38 +++++++++++++++++++--- sway-core/src/query_engine/mod.rs | 31 +++++++++++++----- sway-core/src/semantic_analysis/module.rs | 15 +++++---- sway-core/src/semantic_analysis/program.rs | 2 +- 6 files changed, 67 insertions(+), 25 deletions(-) diff --git a/sway-core/src/language/ty/module.rs b/sway-core/src/language/ty/module.rs index 21bc45eb6cf..85234c5825b 100644 --- a/sway-core/src/language/ty/module.rs +++ b/sway-core/src/language/ty/module.rs @@ -23,7 +23,7 @@ pub struct TyModule { #[derive(Clone, Debug)] pub struct TySubmodule { - pub module: TyModule, + pub module: Arc, pub mod_name_span: Span, } diff --git a/sway-core/src/language/ty/program.rs b/sway-core/src/language/ty/program.rs index 7d585533f18..43f85d9d6d3 100644 --- a/sway-core/src/language/ty/program.rs +++ b/sway-core/src/language/ty/program.rs @@ -502,7 +502,7 @@ impl CollectTypesMetadata for TyProgram { for module in std::iter::once(&self.root).chain( self.root .submodules_recursive() - .map(|(_, submod)| &submod.module), + .map(|(_, submod)| &*submod.module), ) { for node in module.all_nodes.iter() { let is_generic_function = node.is_generic_function(decl_engine); @@ -531,7 +531,7 @@ impl CollectTypesMetadata for TyProgram { for module in std::iter::once(&self.root).chain( self.root .submodules_recursive() - .map(|(_, submod)| &submod.module), + .map(|(_, submod)| &*submod.module), ) { for node in module.all_nodes.iter() { if node.is_test_function(decl_engine) { diff --git a/sway-core/src/lib.rs b/sway-core/src/lib.rs index 9be80676d8b..9524a2417d8 100644 --- a/sway-core/src/lib.rs +++ b/sway-core/src/lib.rs @@ -460,9 +460,10 @@ pub(crate) fn is_ty_module_cache_up_to_date( ) -> bool { let query_engine = engines.qe(); let key = ModuleCacheKey::new(path.clone(), include_tests); - let entry = query_engine.get_module_cache_entry(&key); + let cache = query_engine.module_cache.read(); + let entry = cache.get(&key); match entry { - Some(entry) => match entry.typed { + Some(entry) => match &entry.typed { Some(typed) => { let cache_up_to_date = build_config .as_ref() @@ -493,38 +494,61 @@ pub(crate) fn is_parse_module_cache_up_to_date( include_tests: bool, build_config: Option<&BuildConfig>, ) -> bool { + let n1 = std::time::Instant::now(); let split_points = ["sway-lib-core", "sway-lib-std", "libraries"]; + eprintln!("โฑ๏ธ split_points took: {:?}", n1.elapsed()); + + let n2 = std::time::Instant::now(); let relevant_path = path .iter() .skip_while(|&comp| !split_points.contains(&comp.to_str().unwrap())) .collect::(); + eprintln!("โฑ๏ธ relevant_path took: {:?}", n2.elapsed()); + let n3 = std::time::Instant::now(); let query_engine = engines.qe(); + eprintln!("โฑ๏ธ query_engine took: {:?}", n3.elapsed()); + + let n4 = std::time::Instant::now(); let key = ModuleCacheKey::new(path.clone(), include_tests); - let entry = query_engine.get_module_cache_entry(&key); + eprintln!("โฑ๏ธ key took: {:?}", n4.elapsed()); + + let n5 = std::time::Instant::now(); + // let entry = query_engine.get_module_cache_entry(&key); + let cache = query_engine.module_cache.read(); + let entry = cache.get(&key); + eprintln!("โฑ๏ธ entry took: {:?}", n5.elapsed()); + let res = match entry { Some(entry) => { // Let's check if we can re-use the dependency information // we got from the cache. + let n6 = std::time::Instant::now(); let cache_up_to_date = build_config .as_ref() .and_then(|x| x.lsp_mode.as_ref()) .and_then(|lsp| { + eprintln!("โฑ๏ธ cache_up_to_date lsp_mode: {:?}", n6.elapsed()); // First try to get the file version from lsp if it exists lsp.file_versions.get(path.as_ref()) }) .map_or_else( || { + let n7 = std::time::Instant::now(); // Otherwise we can safely read the file from disk here, as the LSP has not modified it, or we are not in LSP mode. // Check if the file has been modified or if its hash is the same as the last compilation let modified_time = std::fs::metadata(path.as_path()) .ok() .and_then(|m| m.modified().ok()); + eprintln!("โฑ๏ธ cache_up_to_date modified_time: {:?}", n7.elapsed()); + + let n8 = std::time::Instant::now(); entry.parsed.modified_time == modified_time || { let src = std::fs::read_to_string(path.as_path()).unwrap(); let mut hasher = DefaultHasher::new(); src.hash(&mut hasher); let hash = hasher.finish(); + eprintln!("โฑ๏ธ cache_up_to_date hash: {:?}", n8.elapsed()); hash == entry.common.hash } }, @@ -533,16 +557,20 @@ pub(crate) fn is_parse_module_cache_up_to_date( !version.map_or(false, |v| v > entry.parsed.version.unwrap_or(0)) }, ); + eprintln!("โฑ๏ธ cache_up_to_date took: {:?}", n6.elapsed()); // Look at the dependencies recursively to make sure they have not been // modified either. if cache_up_to_date { + let n9 = std::time::Instant::now(); //eprintln!("num dependencies for path {:?}: {}", path, entry.dependencies.len()); - entry.common.dependencies.iter().all(|path| { + let res = entry.common.dependencies.iter().all(|path| { // eprint!("checking dep path {:?} ", path); is_parse_module_cache_up_to_date(engines, path, include_tests, build_config) - }) + }); + eprintln!("โฑ๏ธ is_parse_module_cache_up_to_date: {:?}", n9.elapsed()); + res } else { false } diff --git a/sway-core/src/query_engine/mod.rs b/sway-core/src/query_engine/mod.rs index 79e5aa04bb0..7194f9f5728 100644 --- a/sway-core/src/query_engine/mod.rs +++ b/sway-core/src/query_engine/mod.rs @@ -1,4 +1,4 @@ -use parking_lot::RwLock; +use parking_lot::{RwLock, RwLockReadGuard}; use std::{ collections::HashMap, ops::{Deref, DerefMut}, @@ -46,7 +46,7 @@ pub struct ParsedModuleInfo { #[derive(Clone, Debug)] pub struct TypedModuleInfo { - pub module: TyModule, + pub module: Arc, pub modified_time: Option, pub version: Option, } @@ -94,7 +94,7 @@ impl ModuleCacheEntry { } #[derive(Debug, Default, Clone)] -struct ModuleCacheMap(HashMap); +pub struct ModuleCacheMap(HashMap); impl Deref for ModuleCacheMap { type Target = HashMap; @@ -142,16 +142,26 @@ pub struct FunctionCacheEntry { #[derive(Debug, Default, Clone)] pub struct QueryEngine { // We want the below types wrapped in Arcs to optimize cloning from LSP. - module_cache: Arc>, + pub module_cache: Arc>, programs_cache: Arc>, function_cache: Arc>, } impl QueryEngine { - pub fn get_module_cache_entry(&self, key: &ModuleCacheKey) -> Option { - let cache = self.module_cache.read(); - cache.get(key).cloned() - } + // pub fn get_module_cache_entry(&self, key: &ModuleCacheKey) -> Option<(RwLockReadGuard<'_, ModuleCacheMap>, &ModuleCacheEntry)> { + // let cache = self.module_cache.read(); + // cache.get(key).map(|entry| (cache, entry)) + // } + + // pub fn get_module_cache_entry(&self, key: &ModuleCacheKey) -> Option<&ModuleCacheEntry> { + // let cache = self.module_cache.read(); + // cache.get(key) + // } + + // pub fn get_module_cache_entry(&self, key: &ModuleCacheKey) -> Option { + // let cache = self.module_cache.read(); + // cache.get(key).cloned() + // } pub fn update_or_insert_parsed_module_cache_entry(&self, entry: ModuleCacheEntry) { let path = entry.common.path.clone(); @@ -167,8 +177,11 @@ impl QueryEngine { } pub fn get_programs_cache_entry(&self, path: &Arc) -> Option { + let now = std::time::Instant::now(); let cache = self.programs_cache.read(); - cache.get(path).cloned() + let res = cache.get(path).cloned(); + eprintln!("โฑ๏ธ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!get_programs_cache_entry took: {:?}", now.elapsed()); + res } pub fn insert_programs_cache_entry(&self, entry: ProgramsCacheEntry) { diff --git a/sway-core/src/semantic_analysis/module.rs b/sway-core/src/semantic_analysis/module.rs index a6d71b519db..c29f1e9266f 100644 --- a/sway-core/src/semantic_analysis/module.rs +++ b/sway-core/src/semantic_analysis/module.rs @@ -262,7 +262,7 @@ impl ty::TyModule { source_id: Option<&SourceId>, engines: &Engines, build_config: Option<&BuildConfig>, - ) -> Option { + ) -> Option> { // Check if the module is already in the cache if let Some(source_id) = source_id { let path = engines.se().get_path(&source_id); @@ -275,7 +275,8 @@ impl ty::TyModule { .collect::(); let key: ModuleCacheKey = ModuleCacheKey::new(path.clone().into(), include_tests); - if let Some(entry) = engines.qe().get_module_cache_entry(&key) { + let cache = engines.qe().module_cache.read(); + if let Some(entry) = cache.get(&key) { // We now need to check if the module is up to date, if not, we need to recompute it so // we will return None, otherwise we will return the cached module. @@ -284,7 +285,7 @@ impl ty::TyModule { // Let's check if we can re-use the dependency information // we got from the cache. - if let Some(typed) = entry.typed { + if let Some(typed) = &entry.typed { let is_up_to_date = is_ty_module_cache_up_to_date( engines, &path.into(), @@ -301,7 +302,7 @@ impl ty::TyModule { status, relevant_path, is_up_to_date ); if is_up_to_date { - return Some(typed.module); + return Some(typed.module.clone()); } } } else { @@ -321,7 +322,7 @@ impl ty::TyModule { kind: TreeType, parsed: &ParseModule, build_config: Option<&BuildConfig>, - ) -> Result { + ) -> Result, ErrorEmitted> { let ParseModule { submodules, tree, @@ -458,13 +459,13 @@ impl ty::TyModule { } } - let ty_module = Self { + let ty_module = Arc::new(Self { span: span.clone(), submodules, namespace: ctx.namespace.clone(), all_nodes, attributes: attributes.clone(), - }; + }); // Cache the ty module if let Some(source_id) = span.source_id() { diff --git a/sway-core/src/semantic_analysis/program.rs b/sway-core/src/semantic_analysis/program.rs index 65f5d762552..bfe41511a4f 100644 --- a/sway-core/src/semantic_analysis/program.rs +++ b/sway-core/src/semantic_analysis/program.rs @@ -82,7 +82,7 @@ impl TyProgram { let program = TyProgram { kind, - root, + root: (*root).clone(), declarations, configurables, storage_slots: vec![], From c6f523caa6ac635dce2f79ca95fe9e14b51bb3df Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Tue, 23 Jul 2024 13:43:29 +1000 Subject: [PATCH 17/29] clean up --- sway-core/src/lib.rs | 36 +++++-------------------------- sway-core/src/query_engine/mod.rs | 20 +---------------- 2 files changed, 6 insertions(+), 50 deletions(-) diff --git a/sway-core/src/lib.rs b/sway-core/src/lib.rs index 9524a2417d8..a6b85af42b2 100644 --- a/sway-core/src/lib.rs +++ b/sway-core/src/lib.rs @@ -494,61 +494,38 @@ pub(crate) fn is_parse_module_cache_up_to_date( include_tests: bool, build_config: Option<&BuildConfig>, ) -> bool { - let n1 = std::time::Instant::now(); let split_points = ["sway-lib-core", "sway-lib-std", "libraries"]; - eprintln!("โฑ๏ธ split_points took: {:?}", n1.elapsed()); - - let n2 = std::time::Instant::now(); let relevant_path = path .iter() .skip_while(|&comp| !split_points.contains(&comp.to_str().unwrap())) .collect::(); - eprintln!("โฑ๏ธ relevant_path took: {:?}", n2.elapsed()); - - let n3 = std::time::Instant::now(); - let query_engine = engines.qe(); - eprintln!("โฑ๏ธ query_engine took: {:?}", n3.elapsed()); - - let n4 = std::time::Instant::now(); let key = ModuleCacheKey::new(path.clone(), include_tests); - eprintln!("โฑ๏ธ key took: {:?}", n4.elapsed()); - - let n5 = std::time::Instant::now(); - // let entry = query_engine.get_module_cache_entry(&key); - let cache = query_engine.module_cache.read(); + let cache = engines.qe().module_cache.read(); let entry = cache.get(&key); - eprintln!("โฑ๏ธ entry took: {:?}", n5.elapsed()); let res = match entry { Some(entry) => { // Let's check if we can re-use the dependency information // we got from the cache. - let n6 = std::time::Instant::now(); let cache_up_to_date = build_config .as_ref() .and_then(|x| x.lsp_mode.as_ref()) .and_then(|lsp| { - eprintln!("โฑ๏ธ cache_up_to_date lsp_mode: {:?}", n6.elapsed()); // First try to get the file version from lsp if it exists lsp.file_versions.get(path.as_ref()) }) .map_or_else( || { - let n7 = std::time::Instant::now(); // Otherwise we can safely read the file from disk here, as the LSP has not modified it, or we are not in LSP mode. // Check if the file has been modified or if its hash is the same as the last compilation let modified_time = std::fs::metadata(path.as_path()) .ok() .and_then(|m| m.modified().ok()); - eprintln!("โฑ๏ธ cache_up_to_date modified_time: {:?}", n7.elapsed()); - - let n8 = std::time::Instant::now(); entry.parsed.modified_time == modified_time || { let src = std::fs::read_to_string(path.as_path()).unwrap(); let mut hasher = DefaultHasher::new(); src.hash(&mut hasher); let hash = hasher.finish(); - eprintln!("โฑ๏ธ cache_up_to_date hash: {:?}", n8.elapsed()); hash == entry.common.hash } }, @@ -557,20 +534,15 @@ pub(crate) fn is_parse_module_cache_up_to_date( !version.map_or(false, |v| v > entry.parsed.version.unwrap_or(0)) }, ); - eprintln!("โฑ๏ธ cache_up_to_date took: {:?}", n6.elapsed()); // Look at the dependencies recursively to make sure they have not been // modified either. if cache_up_to_date { - let n9 = std::time::Instant::now(); //eprintln!("num dependencies for path {:?}: {}", path, entry.dependencies.len()); - - let res = entry.common.dependencies.iter().all(|path| { + entry.common.dependencies.iter().all(|path| { // eprint!("checking dep path {:?} ", path); is_parse_module_cache_up_to_date(engines, path, include_tests, build_config) - }); - eprintln!("โฑ๏ธ is_parse_module_cache_up_to_date: {:?}", n9.elapsed()); - res + }) } else { false } @@ -830,6 +802,7 @@ pub fn compile_to_ast( }; } + let parse_now = std::time::Instant::now(); // Parse the program to a concrete syntax tree (CST). let parse_program_opt = time_expr!( "parse the program to a concrete syntax tree (CST)", @@ -838,6 +811,7 @@ pub fn compile_to_ast( build_config, metrics ); + eprintln!("โฑ๏ธ compile_to_ast took {:?}", parse_now.elapsed()); check_should_abort(handler, retrigger_compilation.clone(), 805)?; diff --git a/sway-core/src/query_engine/mod.rs b/sway-core/src/query_engine/mod.rs index 7194f9f5728..d4fd5a6df69 100644 --- a/sway-core/src/query_engine/mod.rs +++ b/sway-core/src/query_engine/mod.rs @@ -148,21 +148,6 @@ pub struct QueryEngine { } impl QueryEngine { - // pub fn get_module_cache_entry(&self, key: &ModuleCacheKey) -> Option<(RwLockReadGuard<'_, ModuleCacheMap>, &ModuleCacheEntry)> { - // let cache = self.module_cache.read(); - // cache.get(key).map(|entry| (cache, entry)) - // } - - // pub fn get_module_cache_entry(&self, key: &ModuleCacheKey) -> Option<&ModuleCacheEntry> { - // let cache = self.module_cache.read(); - // cache.get(key) - // } - - // pub fn get_module_cache_entry(&self, key: &ModuleCacheKey) -> Option { - // let cache = self.module_cache.read(); - // cache.get(key).cloned() - // } - pub fn update_or_insert_parsed_module_cache_entry(&self, entry: ModuleCacheEntry) { let path = entry.common.path.clone(); let include_tests = entry.common.include_tests; @@ -177,11 +162,8 @@ impl QueryEngine { } pub fn get_programs_cache_entry(&self, path: &Arc) -> Option { - let now = std::time::Instant::now(); let cache = self.programs_cache.read(); - let res = cache.get(path).cloned(); - eprintln!("โฑ๏ธ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!get_programs_cache_entry took: {:?}", now.elapsed()); - res + cache.get(path).cloned() } pub fn insert_programs_cache_entry(&self, entry: ProgramsCacheEntry) { From 1c0eb5ce9f819373855e7f1925e027e69d281cec Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Wed, 24 Jul 2024 12:01:11 +1000 Subject: [PATCH 18/29] update is_ty_module_cache_up_to_date function --- sway-core/src/lib.rs | 49 +++++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/sway-core/src/lib.rs b/sway-core/src/lib.rs index a6b85af42b2..c0c0258287c 100644 --- a/sway-core/src/lib.rs +++ b/sway-core/src/lib.rs @@ -451,41 +451,34 @@ fn parse_module_tree( }) } -// TODO: This function can probably be written better and more concisely. +/// Checks if the typed module cache for a given path is up to date. +/// +/// Returns `true` if the cache is up to date, `false` otherwise. pub(crate) fn is_ty_module_cache_up_to_date( engines: &Engines, path: &Arc, include_tests: bool, build_config: Option<&BuildConfig>, ) -> bool { - let query_engine = engines.qe(); + let cache = engines.qe().module_cache.read(); let key = ModuleCacheKey::new(path.clone(), include_tests); - let cache = query_engine.module_cache.read(); - let entry = cache.get(&key); - match entry { - Some(entry) => match &entry.typed { - Some(typed) => { - let cache_up_to_date = build_config - .as_ref() - .and_then(|x| x.lsp_mode.as_ref()) - .and_then(|lsp| lsp.file_versions.get(path.as_ref())) - .map_or_else( - || false, - |version| !version.map_or(false, |v| v > typed.version.unwrap_or(0)), - ); - if cache_up_to_date { - entry.common.dependencies.iter().all(|path| { - //eprint!("checking dep path {:?} ", path); - is_ty_module_cache_up_to_date(engines, path, include_tests, build_config) - }) - } else { - false - } - } - None => false, - }, - None => false, - } + + cache.get(&key).map_or(false, |entry| { + entry.typed.as_ref().map_or(false, |typed| { + // Check if the cache is up to date based on file versions + let cache_up_to_date = build_config + .and_then(|x| x.lsp_mode.as_ref()) + .and_then(|lsp| lsp.file_versions.get(path.as_ref())) + .map_or(true, |version| { + version.map_or(true, |v| v <= typed.version.unwrap_or(0)) + }); + + // If the cache is up to date, recursively check all dependencies + cache_up_to_date && entry.common.dependencies.iter().all(|dep_path| + is_ty_module_cache_up_to_date(engines, dep_path, include_tests, build_config) + ) + }) + }) } pub(crate) fn is_parse_module_cache_up_to_date( From f0352bd6491413f41111da059196a4c8fbf0effe Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Wed, 24 Jul 2024 12:41:46 +1000 Subject: [PATCH 19/29] clean up and add comments --- sway-core/src/lib.rs | 119 ++++++++---------- sway-core/src/semantic_analysis/module.rs | 146 ++++++++++++---------- 2 files changed, 131 insertions(+), 134 deletions(-) diff --git a/sway-core/src/lib.rs b/sway-core/src/lib.rs index c0c0258287c..01d12f231b0 100644 --- a/sway-core/src/lib.rs +++ b/sway-core/src/lib.rs @@ -453,7 +453,11 @@ fn parse_module_tree( /// Checks if the typed module cache for a given path is up to date. /// -/// Returns `true` if the cache is up to date, `false` otherwise. +/// This function determines whether the cached typed representation of a module +/// is still valid based on file versions and dependencies. +/// +/// Note: This functionality is currently only supported when the compiler is +/// initiated from the language server. pub(crate) fn is_ty_module_cache_up_to_date( engines: &Engines, path: &Arc, @@ -462,7 +466,6 @@ pub(crate) fn is_ty_module_cache_up_to_date( ) -> bool { let cache = engines.qe().module_cache.read(); let key = ModuleCacheKey::new(path.clone(), include_tests); - cache.get(&key).map_or(false, |entry| { entry.typed.as_ref().map_or(false, |typed| { // Check if the cache is up to date based on file versions @@ -474,13 +477,18 @@ pub(crate) fn is_ty_module_cache_up_to_date( }); // If the cache is up to date, recursively check all dependencies - cache_up_to_date && entry.common.dependencies.iter().all(|dep_path| - is_ty_module_cache_up_to_date(engines, dep_path, include_tests, build_config) - ) + cache_up_to_date + && entry.common.dependencies.iter().all(|dep_path| { + is_ty_module_cache_up_to_date(engines, dep_path, include_tests, build_config) + }) }) }) } +/// Checks if the parsed module cache for a given path is up to date. +/// +/// This function determines whether the cached parsed representation of a module +/// is still valid based on file versions, modification times, or content hashes. pub(crate) fn is_parse_module_cache_up_to_date( engines: &Engines, path: &Arc, @@ -492,69 +500,50 @@ pub(crate) fn is_parse_module_cache_up_to_date( .iter() .skip_while(|&comp| !split_points.contains(&comp.to_str().unwrap())) .collect::(); - let key = ModuleCacheKey::new(path.clone(), include_tests); + let cache = engines.qe().module_cache.read(); - let entry = cache.get(&key); - - let res = match entry { - Some(entry) => { - // Let's check if we can re-use the dependency information - // we got from the cache. - let cache_up_to_date = build_config - .as_ref() - .and_then(|x| x.lsp_mode.as_ref()) - .and_then(|lsp| { - // First try to get the file version from lsp if it exists - lsp.file_versions.get(path.as_ref()) - }) - .map_or_else( - || { - // Otherwise we can safely read the file from disk here, as the LSP has not modified it, or we are not in LSP mode. - // Check if the file has been modified or if its hash is the same as the last compilation - let modified_time = std::fs::metadata(path.as_path()) - .ok() - .and_then(|m| m.modified().ok()); - entry.parsed.modified_time == modified_time || { - let src = std::fs::read_to_string(path.as_path()).unwrap(); - let mut hasher = DefaultHasher::new(); - src.hash(&mut hasher); - let hash = hasher.finish(); - hash == entry.common.hash - } - }, - |version| { - // The cache is invalid if the lsp version is greater than the last compilation - !version.map_or(false, |v| v > entry.parsed.version.unwrap_or(0)) - }, - ); - - // Look at the dependencies recursively to make sure they have not been - // modified either. - if cache_up_to_date { - //eprintln!("num dependencies for path {:?}: {}", path, entry.dependencies.len()); - entry.common.dependencies.iter().all(|path| { - // eprint!("checking dep path {:?} ", path); - is_parse_module_cache_up_to_date(engines, path, include_tests, build_config) - }) - } else { - false - } - } - None => false, - }; + let key = ModuleCacheKey::new(path.clone(), include_tests); - if res { - eprintln!( - "๐Ÿ€„ ๐Ÿ‘“ Checking cache for parse module {:?} | is up to date? true ๐ŸŸฉ", - relevant_path - ); - } else { - eprintln!( - "๐Ÿ€„ ๐Ÿ‘“ Checking cache for parse module {:?} | is up to date? FALSE ๐ŸŸฅ", - relevant_path - ); - } + let res = cache.get(&key).map_or(false, |entry| { + // Determine if the cached dependency information is still valid + let cache_up_to_date = build_config + .and_then(|x| x.lsp_mode.as_ref()) + .and_then(|lsp| lsp.file_versions.get(path.as_ref())) + .map_or_else( + || { + // If LSP mode is not active or file version is unavailable, fall back to filesystem checks. + let modified_time = std::fs::metadata(path.as_path()) + .ok() + .and_then(|m| m.modified().ok()); + // Check if modification time matches, or if not, compare file content hash + entry.parsed.modified_time == modified_time || { + let src = std::fs::read_to_string(path.as_path()).unwrap(); + let mut hasher = DefaultHasher::new(); + src.hash(&mut hasher); + hasher.finish() == entry.common.hash + } + }, + |version| { + // In LSP mode, cache is valid if the current version is not greater + // than the version at last compilation. + !version.map_or(false, |v| v > entry.parsed.version.unwrap_or(0)) + }, + ); + + // Checks if the typed module cache for a given path is up to date// If the cache is up to date, recursively check all dependencies to make sure they have not been + // modified either. + cache_up_to_date + && entry.common.dependencies.iter().all(|dep_path| { + is_parse_module_cache_up_to_date(engines, dep_path, include_tests, build_config) + }) + }); + eprintln!( + "๐Ÿ€„ ๐Ÿ‘“ Checking cache for parse module {:?} | is up to date? {} {}", + relevant_path, + if res { "true ๐ŸŸฉ" } else { "FALSE ๐ŸŸฅ" }, + if res { " " } else { "" } + ); res } diff --git a/sway-core/src/semantic_analysis/module.rs b/sway-core/src/semantic_analysis/module.rs index c29f1e9266f..002c03bd574 100644 --- a/sway-core/src/semantic_analysis/module.rs +++ b/sway-core/src/semantic_analysis/module.rs @@ -11,12 +11,12 @@ use sway_error::{ error::CompileError, handler::{ErrorEmitted, Handler}, }; -use sway_types::{BaseIdent, Named, SourceId, Spanned}; +use sway_types::{BaseIdent, Named, SourceId}; use crate::{ decl_engine::{DeclEngineGet, DeclId}, engine_threading::DebugWithEngines, - is_parse_module_cache_up_to_date, is_ty_module_cache_up_to_date, + is_ty_module_cache_up_to_date, language::{ parsed::*, ty::{self, TyAstNodeContent, TyDecl}, @@ -258,58 +258,60 @@ impl ty::TyModule { Ok(()) } - fn check_cache( + /// Retrieves a cached typed module if it's up to date. + /// + /// This function checks the cache for a typed module corresponding to the given source ID. + /// If found and up to date, it returns the cached module. Otherwise, it returns None. + fn get_cached_ty_module_if_up_to_date( source_id: Option<&SourceId>, engines: &Engines, build_config: Option<&BuildConfig>, ) -> Option> { - // Check if the module is already in the cache - if let Some(source_id) = source_id { - let path = engines.se().get_path(&source_id); - let include_tests = build_config.map_or(false, |x| x.include_tests); + let Some(source_id) = source_id else { + return None; + }; - let split_points = ["sway-lib-core", "sway-lib-std", "libraries"]; - let relevant_path = path - .iter() - .skip_while(|&comp| !split_points.contains(&comp.to_str().unwrap())) - .collect::(); + // Create a cache key and get the module cache + let path = engines.se().get_path(&source_id); + let include_tests = build_config.map_or(false, |x| x.include_tests); + let key = ModuleCacheKey::new(path.clone().into(), include_tests); + let cache = engines.qe().module_cache.read(); - let key: ModuleCacheKey = ModuleCacheKey::new(path.clone().into(), include_tests); - let cache = engines.qe().module_cache.read(); - if let Some(entry) = cache.get(&key) { - // We now need to check if the module is up to date, if not, we need to recompute it so - // we will return None, otherwise we will return the cached module. + let split_points = ["sway-lib-core", "sway-lib-std", "libraries"]; + let relevant_path = path + .iter() + .skip_while(|&comp| !split_points.contains(&comp.to_str().unwrap())) + .collect::(); - // For now we will duplicate the logic of the parsed cache entry check but - // we should ideally have a way of sharing this logic. + cache.get(&key).and_then(|entry| { + entry.typed.as_ref().and_then(|typed| { + // Check if the cached module is up to date + let is_up_to_date = is_ty_module_cache_up_to_date( + engines, + &path.into(), + include_tests, + build_config, + ); - // Let's check if we can re-use the dependency information - // we got from the cache. - if let Some(typed) = &entry.typed { - let is_up_to_date = is_ty_module_cache_up_to_date( - engines, - &path.into(), - include_tests, - build_config, - ); - let status = if is_up_to_date { - "โœ… Cache hit" - } else { - "๐Ÿ”„ Cache miss" - }; - eprintln!( - "{} for module {:?} (up to date: {})", - status, relevant_path, is_up_to_date - ); - if is_up_to_date { - return Some(typed.module.clone()); - } + // Log the cache status + let status = if is_up_to_date { + "โœ… Cache hit" + } else { + "๐Ÿ”„ Cache miss" + }; + eprintln!( + "{} for module {:?} (up to date: {})", + status, relevant_path, is_up_to_date + ); + + // Return the cached module if it's up to date, otherwise None + if is_up_to_date { + Some(typed.module.clone()) + } else { + None } - } else { - eprintln!("โŒ No cache entry for module {:?}", relevant_path); - } - } - None + }) + }) } /// Type-check the given parsed module to produce a typed module. @@ -337,48 +339,54 @@ impl ty::TyModule { "Root Module: {:?}", parsed.span.source_id().map(|x| engines.se().get_path(x)) ); - if let Some(module) = - ty::TyModule::check_cache(parsed.span.source_id(), engines, build_config) - { + + // Try to get the cached root module + if let Some(module) = ty::TyModule::get_cached_ty_module_if_up_to_date( + parsed.span.source_id(), + engines, + build_config, + ) { return Ok(module); } // Type-check submodules first in order of evaluation previously computed by the dependency graph. - let submodules_res = module_eval_order + let submodules_res: Result, _> = module_eval_order .iter() .map(|eval_mod_name| { let (name, submodule) = submodules .iter() - .find(|(submod_name, _submodule)| eval_mod_name == submod_name) + .find(|(submod_name, _)| eval_mod_name == submod_name) .unwrap(); - // Check if the submodule cache is up to date - if let Some(module) = ty::TyModule::check_cache( + // Try to get the cached submodule + if let Some(cached_module) = ty::TyModule::get_cached_ty_module_if_up_to_date( submodule.module.span.source_id(), engines, build_config, ) { - let submodule = ty::TySubmodule { - module, - mod_name_span: submodule.mod_name_span.clone(), - }; - Ok((name.clone(), submodule)) - } else { - Ok(( + // If cached, create TySubmodule from cached module + Ok::<(BaseIdent, ty::TySubmodule), ErrorEmitted>(( name.clone(), - ty::TySubmodule::type_check( - handler, - ctx.by_ref(), - engines, - name.clone(), - kind, - submodule, - build_config, - )?, + ty::TySubmodule { + module: cached_module, + mod_name_span: submodule.mod_name_span.clone(), + }, )) + } else { + // If not cached, type-check the submodule + let type_checked_submodule = ty::TySubmodule::type_check( + handler, + ctx.by_ref(), + engines, + name.clone(), + kind, + submodule, + build_config, + )?; + Ok((name.clone(), type_checked_submodule)) } }) - .collect::, _>>(); + .collect(); // TODO: Ordering should be solved across all modules prior to the beginning of type-check. let ordered_nodes = node_dependencies::order_ast_nodes_by_dependency( From 9be7ce1841be20d8ba3d73d26474bb7686499829 Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Wed, 24 Jul 2024 12:51:06 +1000 Subject: [PATCH 20/29] clippy --- sway-core/Cargo.toml | 1 - sway-core/src/lib.rs | 3 --- sway-core/src/query_engine/mod.rs | 3 +-- sway-core/src/semantic_analysis/module.rs | 12 +++--------- sway-lsp/src/core/session.rs | 1 - 5 files changed, 4 insertions(+), 16 deletions(-) diff --git a/sway-core/Cargo.toml b/sway-core/Cargo.toml index a903dca0b7b..9c0122123c2 100644 --- a/sway-core/Cargo.toml +++ b/sway-core/Cargo.toml @@ -34,7 +34,6 @@ parking_lot = "0.12" pest = "2.1.3" pest_derive = "2.1" petgraph = "0.6" -rayon = "1.5" rustc-hash = "1.1.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.91" diff --git a/sway-core/src/lib.rs b/sway-core/src/lib.rs index 01d12f231b0..52e261b028a 100644 --- a/sway-core/src/lib.rs +++ b/sway-core/src/lib.rs @@ -31,12 +31,9 @@ pub use asm_generation::{CompiledBytecode, FinalizedEntry}; pub use build_config::{BuildConfig, BuildTarget, LspConfig, OptLevel, PrintAsm, PrintIr}; use control_flow_analysis::ControlFlowGraph; pub use debug_generation::write_dwarf; -use decl_engine::parsed_engine; -use fuel_vm::fuel_merkle::common; use indexmap::IndexMap; use metadata::MetadataManager; use query_engine::{ModuleCacheKey, ModuleCommonInfo, ParsedModuleInfo, ProgramsCacheEntry}; -use rayon::iter::{ParallelBridge, ParallelIterator}; use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; use std::path::{Path, PathBuf}; diff --git a/sway-core/src/query_engine/mod.rs b/sway-core/src/query_engine/mod.rs index d4fd5a6df69..b25a64dffef 100644 --- a/sway-core/src/query_engine/mod.rs +++ b/sway-core/src/query_engine/mod.rs @@ -1,4 +1,4 @@ -use parking_lot::{RwLock, RwLockReadGuard}; +use parking_lot::RwLock; use std::{ collections::HashMap, ops::{Deref, DerefMut}, @@ -47,7 +47,6 @@ pub struct ParsedModuleInfo { #[derive(Clone, Debug)] pub struct TypedModuleInfo { pub module: Arc, - pub modified_time: Option, pub version: Option, } diff --git a/sway-core/src/semantic_analysis/module.rs b/sway-core/src/semantic_analysis/module.rs index 002c03bd574..3874c234331 100644 --- a/sway-core/src/semantic_analysis/module.rs +++ b/sway-core/src/semantic_analysis/module.rs @@ -267,12 +267,10 @@ impl ty::TyModule { engines: &Engines, build_config: Option<&BuildConfig>, ) -> Option> { - let Some(source_id) = source_id else { - return None; - }; + let source_id = source_id?; // Create a cache key and get the module cache - let path = engines.se().get_path(&source_id); + let path = engines.se().get_path(source_id); let include_tests = build_config.map_or(false, |x| x.include_tests); let key = ModuleCacheKey::new(path.clone().into(), include_tests); let cache = engines.qe().module_cache.read(); @@ -477,16 +475,13 @@ impl ty::TyModule { // Cache the ty module if let Some(source_id) = span.source_id() { - let path = engines.se().get_path(&source_id); + let path = engines.se().get_path(source_id); let split_points = ["sway-lib-core", "sway-lib-std", "libraries"]; let relevant_path = path .iter() .skip_while(|&comp| !split_points.contains(&comp.to_str().unwrap())) .collect::(); - let modified_time = std::fs::metadata(path.as_path()) - .ok() - .and_then(|m| m.modified().ok()); let version = build_config .and_then(|config| config.lsp_mode.as_ref()) .and_then(|lsp| lsp.file_versions.get(&path).copied()) @@ -499,7 +494,6 @@ impl ty::TyModule { &key, TypedModuleInfo { module: ty_module.clone(), - modified_time, version, }, ); diff --git a/sway-lsp/src/core/session.rs b/sway-lsp/src/core/session.rs index a25449bc43a..25963211948 100644 --- a/sway-lsp/src/core/session.rs +++ b/sway-lsp/src/core/session.rs @@ -43,7 +43,6 @@ use sway_core::{ use sway_error::{error::CompileError, handler::Handler, warning::CompileWarning}; use sway_types::{ProgramId, SourceEngine, Spanned}; use sway_utils::{helpers::get_sway_files, PerformanceData}; -use swayfmt::parse; pub type RunnableMap = DashMap>>; pub type ProjectDirectory = PathBuf; From 335bd945c765bc357abb885db79427b27092b30d Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Thu, 25 Jul 2024 12:09:56 +1000 Subject: [PATCH 21/29] lots and lots of dbg prints wip prob safe to remove --- forc-pkg/src/manifest/mod.rs | 22 ++++++++ forc-pkg/src/pkg.rs | 10 +++- sway-core/src/lib.rs | 42 ++++++++++++--- sway-core/src/semantic_analysis/module.rs | 4 +- sway-error/src/handler.rs | 1 + sway-lsp/src/capabilities/diagnostic.rs | 4 +- sway-lsp/src/core/session.rs | 16 +++++- sway-lsp/tests/integration/lsp.rs | 1 + sway-lsp/tests/lib.rs | 62 ++++++++++++++++++++++- sway-lsp/tests/utils/src/lib.rs | 2 +- sway-parse/src/lib.rs | 2 + sway-parse/src/module.rs | 10 +++- sway-parse/src/parser.rs | 5 ++ 13 files changed, 163 insertions(+), 18 deletions(-) diff --git a/forc-pkg/src/manifest/mod.rs b/forc-pkg/src/manifest/mod.rs index ca35a46e9da..87c31b6903e 100644 --- a/forc-pkg/src/manifest/mod.rs +++ b/forc-pkg/src/manifest/mod.rs @@ -357,11 +357,33 @@ impl PackageManifestFile { /// Produces the string of the entry point file. pub fn entry_string(&self) -> Result> { + dbg!(); let entry_path = self.entry_path(); + dbg!(); let entry_string = std::fs::read_to_string(entry_path)?; + dbg!(); + if entry_string.is_empty() { + eprintln!("entry_string empty! | entry_path: {:?}", self.entry_path()); + } else { + eprintln!("entry_string not empty! | entry_path: {:?}", self.entry_path()); + } Ok(Arc::from(entry_string)) } + // we could try to read the file 3 times, but it feels like a hack + // pub fn entry_string(&self) -> Result> { + // for attempt in 1..=3 { + // let entry_path = self.entry_path(); + // let entry_string = std::fs::read_to_string(&entry_path)?; + // if !entry_string.is_empty() { + // return Ok(Arc::from(entry_string)); + // } + // eprintln!("Attempt {}: File empty, retrying...", attempt); + // std::thread::sleep(std::time::Duration::from_millis(100)); + // } + // Err(anyhow::anyhow!("File remained empty after multiple attempts")) + // } + /// Parse and return the associated project's program type. pub fn program_type(&self) -> Result { let entry_string = self.entry_string()?; diff --git a/forc-pkg/src/pkg.rs b/forc-pkg/src/pkg.rs index 65e44e67dc5..cd058613f98 100644 --- a/forc-pkg/src/pkg.rs +++ b/forc-pkg/src/pkg.rs @@ -2700,7 +2700,9 @@ pub fn check( .with_lsp_mode(lsp_mode.clone()); eprintln!("โฑ๏ธ Build config took {:?}", build_config_now.elapsed()); + eprintln!("Forc pacakge loading input string"); let input = manifest.entry_string()?; + eprintln!("Forc package input string loaded | {}", input.clone()); let handler = Handler::default(); let compile_to_ast_now = std::time::Instant::now(); let programs_res = sway_core::compile_to_ast( @@ -2712,6 +2714,7 @@ pub fn check( &pkg.name, retrigger_compilation.clone(), ); + eprintln!("programs res: {:?}", programs_res.is_ok()); eprintln!("โฑ๏ธ Compile to AST took {:?}", compile_to_ast_now.elapsed()); if retrigger_compilation @@ -2723,8 +2726,13 @@ pub fn check( } let programs = match programs_res.as_ref() { - Ok(programs) => programs, + Ok(programs) => { + dbg!(); + programs + }, _ => { + eprintln!("ERROR PARSING MODULE | {:?}", programs_res.clone().ok()); + eprintln!("Returning results with handler | {:?}", handler); results.push((programs_res.ok(), handler)); return Ok(results); } diff --git a/sway-core/src/lib.rs b/sway-core/src/lib.rs index 52e261b028a..0653a30776b 100644 --- a/sway-core/src/lib.rs +++ b/sway-core/src/lib.rs @@ -90,6 +90,7 @@ pub fn parse( engines: &Engines, config: Option<&BuildConfig>, ) -> Result<(lexed::LexedProgram, parsed::ParseProgram), ErrorEmitted> { + dbg!(); match config { None => parse_in_memory( handler, @@ -257,13 +258,15 @@ fn parse_submodules( experimental: ExperimentalFlags, lsp_mode: Option<&LspConfig>, ) -> Submodules { + dbg!(); // Assume the happy path, so there'll be as many submodules as dependencies, but no more. let mut submods = Vec::with_capacity(module.submodules().count()); - + dbg!(); module.submodules().for_each(|submod| { // Read the source code from the dependency. // If we cannot, record as an error, but continue with other files. let submod_path = Arc::new(module_path(module_dir, module_name, submod)); + dbg!(); let submod_str: Arc = match std::fs::read_to_string(&*submod_path) { Ok(s) => Arc::from(s), Err(e) => { @@ -275,7 +278,7 @@ fn parse_submodules( return; } }; - + dbg!(); if let Ok(ParsedModuleTree { tree_type: kind, lexed_module, @@ -318,7 +321,7 @@ fn parse_submodules( submods.push(submodule); } }); - + dbg!(); submods } @@ -345,14 +348,23 @@ fn parse_module_tree( experimental: ExperimentalFlags, lsp_mode: Option<&LspConfig>, ) -> Result { + dbg!(); let query_engine = engines.qe(); let lexed_now = std::time::Instant::now(); // Parse this module first. let module_dir = path.parent().expect("module file has no parent directory"); let source_id = engines.se().get_source_id(&path.clone()); - let module = sway_parse::parse_file(handler, src.clone(), Some(source_id))?; - + dbg!(); + let module = match sway_parse::parse_file(handler, src.clone(), Some(source_id)) { + Ok(module) => module, + Err(e) => { + eprintln!("ERROR PARSING MODULE | {:?} | src_file: {}", e, src.clone()); + return Err(e); + } + }; + //let module = sway_parse::parse_file(handler, src.clone(), Some(source_id))?; + dbg!(); // Parse all submodules before converting to the `ParseTree`. // This always recovers on parse errors for the file itself by skipping that file. let submodules = parse_submodules( @@ -366,6 +378,7 @@ fn parse_module_tree( experimental, lsp_mode, ); + dbg!(); eprintln!("โฑ๏ธ Lexed module took {:?}", lexed_now.elapsed()); let parsed_now = std::time::Instant::now(); @@ -376,8 +389,11 @@ fn parse_module_tree( engines, module.value.clone(), )?; + dbg!(); let module_kind_span = module.value.kind.span(); + dbg!(); let attributes = module_attrs_to_map(handler, &module.attribute_list)?; + dbg!(); eprintln!("โฑ๏ธ Parsed module took {:?}", parsed_now.elapsed()); let lexed_submodules = submodules @@ -429,7 +445,7 @@ fn parse_module_tree( }; let cache_entry = ModuleCacheEntry::new(common_info, parsed_info); - let split_points = ["sway-lib-core", "sway-lib-std", "libraries"]; + let split_points = ["sway-lib-core", "sway-lib-std", "libraries", "multi-trove-getter-contract"]; let relevant_path = path .iter() .skip_while(|&comp| !split_points.contains(&comp.to_str().unwrap())) @@ -492,7 +508,7 @@ pub(crate) fn is_parse_module_cache_up_to_date( include_tests: bool, build_config: Option<&BuildConfig>, ) -> bool { - let split_points = ["sway-lib-core", "sway-lib-std", "libraries"]; + let split_points = ["sway-lib-core", "sway-lib-std", "libraries", "multi-trove-getter-contract"]; let relevant_path = path .iter() .skip_while(|&comp| !split_points.contains(&comp.to_str().unwrap())) @@ -777,10 +793,14 @@ pub fn compile_to_ast( let (warnings, errors) = entry.handler_data; let new_handler = Handler::from_parts(warnings, errors); handler.append(new_handler); + eprintln!("programs cache valid, returning"); return Ok(entry.programs); }; } + eprintln!("programs cache invalid, continuing to parse"); + let input_clone = input.clone(); + let parse_now = std::time::Instant::now(); // Parse the program to a concrete syntax tree (CST). let parse_program_opt = time_expr!( @@ -795,8 +815,14 @@ pub fn compile_to_ast( check_should_abort(handler, retrigger_compilation.clone(), 805)?; let (lexed_program, mut parsed_program) = match parse_program_opt { - Ok(modules) => modules, + Ok(modules) => { + dbg!(); + modules + }, Err(e) => { + dbg!(); + // Input string is completely empty. how? + eprintln!("ERROR PARSING PROGRAM | {:?} | src_file: {}", e, input_clone); handler.dedup(); return Err(e); } diff --git a/sway-core/src/semantic_analysis/module.rs b/sway-core/src/semantic_analysis/module.rs index 3874c234331..5feacaa7f76 100644 --- a/sway-core/src/semantic_analysis/module.rs +++ b/sway-core/src/semantic_analysis/module.rs @@ -275,7 +275,7 @@ impl ty::TyModule { let key = ModuleCacheKey::new(path.clone().into(), include_tests); let cache = engines.qe().module_cache.read(); - let split_points = ["sway-lib-core", "sway-lib-std", "libraries"]; + let split_points = ["sway-lib-core", "sway-lib-std", "libraries", "multi-trove-getter-contract"]; let relevant_path = path .iter() .skip_while(|&comp| !split_points.contains(&comp.to_str().unwrap())) @@ -476,7 +476,7 @@ impl ty::TyModule { // Cache the ty module if let Some(source_id) = span.source_id() { let path = engines.se().get_path(source_id); - let split_points = ["sway-lib-core", "sway-lib-std", "libraries"]; + let split_points = ["sway-lib-core", "sway-lib-std", "libraries", "multi-trove-getter-contract"]; let relevant_path = path .iter() .skip_while(|&comp| !split_points.contains(&comp.to_str().unwrap())) diff --git a/sway-error/src/handler.rs b/sway-error/src/handler.rs index 24d2222da1b..dc933fbe91c 100644 --- a/sway-error/src/handler.rs +++ b/sway-error/src/handler.rs @@ -35,6 +35,7 @@ impl Handler { // Compilation should be cancelled. pub fn cancel(&self) -> ErrorEmitted { + eprintln!("CANCELLED"); ErrorEmitted { _priv: () } } diff --git a/sway-lsp/src/capabilities/diagnostic.rs b/sway-lsp/src/capabilities/diagnostic.rs index d5121d21914..71a860b7a71 100644 --- a/sway-lsp/src/capabilities/diagnostic.rs +++ b/sway-lsp/src/capabilities/diagnostic.rs @@ -42,6 +42,7 @@ pub fn get_diagnostics( errors: &[CompileError], source_engine: &SourceEngine, ) -> DiagnosticMap { + dbg!(); let mut diagnostics = DiagnosticMap::new(); for warning in warnings { let diagnostic = get_warning_diagnostic(warning); @@ -54,6 +55,7 @@ pub fn get_diagnostics( .push(diagnostic); } } + dbg!(); for error in errors { let diagnostic = get_error_diagnostic(error); if let Some(source_id) = error.span().source_id() { @@ -61,7 +63,7 @@ pub fn get_diagnostics( diagnostics.entry(path).or_default().errors.push(diagnostic); } } - + dbg!(); diagnostics } diff --git a/sway-lsp/src/core/session.rs b/sway-lsp/src/core/session.rs index 25963211948..1b09e7ce165 100644 --- a/sway-lsp/src/core/session.rs +++ b/sway-lsp/src/core/session.rs @@ -314,6 +314,8 @@ pub fn traverse( diagnostics = current_diagnostics; if value.is_none() { + eprintln!("Unable to traverse module, value is None"); + // Should this be an error? continue; } let Programs { @@ -428,17 +430,24 @@ pub fn parse_project( let diagnostics = traverse(results, engines, session.clone())?; eprintln!("โฑ๏ธ Traversing the ASTS took {:?}", traverse_now.elapsed()); if let Some(config) = &lsp_mode { + dbg!(); // Only write the diagnostics results on didSave or didOpen. if !config.optimized_build { + dbg!(); if let Some((errors, warnings)) = &diagnostics { + dbg!(); *session.diagnostics.write() = capabilities::diagnostic::get_diagnostics(warnings, errors, engines.se()); } } } + dbg!(); let runnables_now = std::time::Instant::now(); if let Some(typed) = &session.compiled_program.read().typed { + dbg!(); session.runnables.clear(); + dbg!(); + // This is where it's crashing OOB into the concurrent slab create_runnables(&session.runnables, typed, engines.de(), engines.se()); } eprintln!("โฑ๏ธ creating runnables took: {:?}", runnables_now.elapsed()); @@ -499,8 +508,10 @@ fn create_runnables( decl_engine: &DeclEngine, source_engine: &SourceEngine, ) { + dbg!(); let _p = tracing::trace_span!("create_runnables").entered(); // Insert runnable test functions. + for (decl, _) in typed_program.test_fns(decl_engine) { // Get the span of the first attribute if it exists, otherwise use the span of the function name. let span = decl @@ -508,6 +519,7 @@ fn create_runnables( .first() .map_or_else(|| decl.name.span(), |(_, attr)| attr.span.clone()); if let Some(source_id) = span.source_id() { + dbg!(); let path = source_engine.get_path(source_id); let runnable = Box::new(RunnableTestFn { range: token::get_range_from_span(&span.clone()), @@ -517,15 +529,17 @@ fn create_runnables( runnables.entry(path).or_default().push(runnable); } } - + dbg!(); // Insert runnable main function if the program is a script. if let ty::TyProgramKind::Script { entry_function: ref main_function, .. } = typed_program.kind { + dbg!(); let main_function = decl_engine.get_function(main_function); let span = main_function.name.span(); + dbg!(); if let Some(source_id) = span.source_id() { let path = source_engine.get_path(source_id); let runnable = Box::new(RunnableMainFn { diff --git a/sway-lsp/tests/integration/lsp.rs b/sway-lsp/tests/integration/lsp.rs index e0d063871be..576477081f9 100644 --- a/sway-lsp/tests/integration/lsp.rs +++ b/sway-lsp/tests/integration/lsp.rs @@ -43,6 +43,7 @@ pub(crate) async fn initialize_request(service: &mut LspService) -> 1.into(), json!({ "capabilities": sway_lsp::server_capabilities() }), ); + eprintln!("initialize response: {:?}", response); assert_json_eq!(expected, response.ok().unwrap()); initialize } diff --git a/sway-lsp/tests/lib.rs b/sway-lsp/tests/lib.rs index f812e219e39..fc415bb67af 100644 --- a/sway-lsp/tests/lib.rs +++ b/sway-lsp/tests/lib.rs @@ -242,12 +242,21 @@ fn did_change_stress_test_random_wait() { // .join("generics_in_contract"); // let uri = init_and_open(&mut service, example_dir.join("src/main.sw")).await; + // let uri = init_and_open( + // &mut service, + // PathBuf::from( + // "/Users/josh/Documents/rust/fuel/user_projects/fluid-protocol/libraries", + // ) + // .join("src/fpt_staking_interface.sw"), + // ) + // .await; + let uri = init_and_open( &mut service, PathBuf::from( - "/Users/josh/Documents/rust/fuel/user_projects/fluid-protocol/libraries", + "/Users/josh/Documents/rust/fuel/user_projects/fluid-protocol/contracts/multi-trove-getter-contract", ) - .join("src/fpt_staking_interface.sw"), + .join("src/main.sw"), ) .await; @@ -290,6 +299,55 @@ fn did_change_stress_test_random_wait() { }); } + +#[test] +fn did_change_stress_test_enter_uzi() { + run_async!({ + let test_duration = tokio::time::Duration::from_secs(250 * 60); // 5 minutes timeout + let test_future = async { + setup_panic_hook(); + let (mut service, _) = LspService::new(ServerState::new); + let uri = init_and_open( + &mut service, + PathBuf::from( + "/Users/josh/Documents/rust/fuel/user_projects/fluid-protocol/contracts/multi-trove-getter-contract", + ) + .join("src/main.sw"), + ) + .await; + + let times = 6000; + for version in 0..times { + //eprintln!("version: {}", version); + let _ = lsp::did_change_request(&mut service, &uri, version + 1, None).await; + if version == 0 { + service.inner().wait_for_parsing().await; + } + // wait for a random amount of time between 1s + tokio::time::sleep(tokio::time::Duration::from_millis(2)).await; + + // there is a 10% chance that a longer 100-800ms wait will be added + if rand::random::() % 100 < 2 { + tokio::time::sleep(tokio::time::Duration::from_millis( + rand::random::() % 100, + )) + .await; + } + } + shutdown_and_exit(&mut service).await; + }; + if tokio::time::timeout(test_duration, test_future) + .await + .is_err() + { + panic!( + "did_change_stress_test_random_wait did not complete within the timeout period." + ); + } + }); +} + + fn garbage_collection_runner(path: PathBuf) { run_async!({ setup_panic_hook(); diff --git a/sway-lsp/tests/utils/src/lib.rs b/sway-lsp/tests/utils/src/lib.rs index 3124481ca06..e60363e6aa4 100644 --- a/sway-lsp/tests/utils/src/lib.rs +++ b/sway-lsp/tests/utils/src/lib.rs @@ -142,7 +142,7 @@ pub async fn random_delay() { /// Sets up the environment and a custom panic hook to print panic information and exit the program. pub fn setup_panic_hook() { // Enable backtrace to get more information about panic - std::env::set_var("RUST_BACKTRACE", "1"); + std::env::set_var("RUST_BACKTRACE", "FULL"); // Take the default panic hook let default_panic = std::panic::take_hook(); diff --git a/sway-parse/src/lib.rs b/sway-parse/src/lib.rs index 2022c954d7a..191e1c2c0ae 100644 --- a/sway-parse/src/lib.rs +++ b/sway-parse/src/lib.rs @@ -42,7 +42,9 @@ pub fn parse_file( src: Arc, source_id: Option, ) -> Result, ErrorEmitted> { + dbg!(); let ts = lex(handler, &src, 0, src.len(), source_id)?; + dbg!(); let (m, _) = Parser::new(handler, &ts).parse_to_end()?; Ok(m) } diff --git a/sway-parse/src/module.rs b/sway-parse/src/module.rs index edd33c8bb9c..047ad4c4af0 100644 --- a/sway-parse/src/module.rs +++ b/sway-parse/src/module.rs @@ -21,6 +21,7 @@ impl Parse for ModuleKind { } else if let Some(library_token) = parser.take() { Ok(Self::Library { library_token }) } else { + eprintln!("โ”โ‰๏ธ Expected Module Kind, how the hell did we get here?"); Err(parser.emit_error(ParseErrorKind::ExpectedModuleKind)) } } @@ -28,10 +29,13 @@ impl Parse for ModuleKind { impl ParseToEnd for Annotated { fn parse_to_end<'a, 'e>(mut parser: Parser<'a, '_>) -> ParseResult<(Self, ParserConsumed<'a>)> { + dbg!(); // Parse the attribute list. let mut attribute_list = Vec::new(); while let Some(DocComment { .. }) = parser.peek() { + dbg!(); let doc_comment = parser.parse::()?; + dbg!(); // TODO: Use a Literal instead of an Ident when Attribute args // start supporting them and remove `Ident::new_no_trim`. let name = Ident::new_no_trim(doc_comment.content_span.clone()); @@ -55,14 +59,16 @@ impl ParseToEnd for Annotated { ), }), DocStyle::Outer => { + dbg!(); parser.emit_error(ParseErrorKind::ExpectedModuleDocComment); } } } + dbg!(); let (kind, semicolon_token) = parser.parse()?; - + dbg!(); let (items, consumed) = parser.parse_to_end()?; - + dbg!(); let module = Annotated { attribute_list, value: Module { diff --git a/sway-parse/src/parser.rs b/sway-parse/src/parser.rs index ad9d3e7ffc2..3aa77a1e465 100644 --- a/sway-parse/src/parser.rs +++ b/sway-parse/src/parser.rs @@ -209,6 +209,8 @@ impl<'a, 'e> Parser<'a, 'e> { /// Parses a `T` in its canonical way. pub fn parse(&mut self) -> ParseResult { + //eprintln!("parse Type of T: {}", std::any::type_name::()); + T::parse(self) } @@ -220,6 +222,9 @@ impl<'a, 'e> Parser<'a, 'e> { } pub fn parse_to_end(self) -> ParseResult<(T, ParserConsumed<'a>)> { + // eprintln! type of T + // eprintln!("parse_to_end Type of T: {}", std::any::type_name::()); + T::parse_to_end(self) } From 6bf8e0406a3c14cb2877d7d6a9b3d894b525b3f3 Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Thu, 1 Aug 2024 12:45:03 +1000 Subject: [PATCH 22/29] CowCache works --- forc-pkg/src/manifest/mod.rs | 7 +- forc-pkg/src/pkg.rs | 4 +- sway-core/src/lib.rs | 31 ++--- sway-core/src/query_engine/mod.rs | 124 ++++++++++++++++++-- sway-core/src/semantic_analysis/module.rs | 8 +- sway-lsp/src/capabilities/diagnostic.rs | 3 - sway-lsp/src/capabilities/hover/mod.rs | 2 + sway-lsp/src/core/session.rs | 12 +- sway-lsp/src/server_state.rs | 8 +- sway-lsp/tests/integration/lsp.rs | 47 ++++++++ sway-lsp/tests/lib.rs | 133 ++++++++++++++-------- sway-parse/src/lib.rs | 2 - sway-parse/src/module.rs | 7 -- 13 files changed, 277 insertions(+), 111 deletions(-) diff --git a/forc-pkg/src/manifest/mod.rs b/forc-pkg/src/manifest/mod.rs index 87c31b6903e..dba7dcdac0e 100644 --- a/forc-pkg/src/manifest/mod.rs +++ b/forc-pkg/src/manifest/mod.rs @@ -357,15 +357,12 @@ impl PackageManifestFile { /// Produces the string of the entry point file. pub fn entry_string(&self) -> Result> { - dbg!(); let entry_path = self.entry_path(); - dbg!(); let entry_string = std::fs::read_to_string(entry_path)?; - dbg!(); if entry_string.is_empty() { - eprintln!("entry_string empty! | entry_path: {:?}", self.entry_path()); + eprintln!("๐ŸŸฅ entry_string empty! | entry_path: {:?}", self.entry_path()); } else { - eprintln!("entry_string not empty! | entry_path: {:?}", self.entry_path()); + eprintln!("๐ŸŸฉ entry_string not empty! | entry_path: {:?}", self.entry_path()); } Ok(Arc::from(entry_string)) } diff --git a/forc-pkg/src/pkg.rs b/forc-pkg/src/pkg.rs index cd058613f98..31dd4a8b599 100644 --- a/forc-pkg/src/pkg.rs +++ b/forc-pkg/src/pkg.rs @@ -2702,7 +2702,8 @@ pub fn check( eprintln!("Forc pacakge loading input string"); let input = manifest.entry_string()?; - eprintln!("Forc package input string loaded | {}", input.clone()); + eprintln!("Forc package input string loaded"); + // eprintln!("Forc package input string loaded | {}", input.clone()); let handler = Handler::default(); let compile_to_ast_now = std::time::Instant::now(); let programs_res = sway_core::compile_to_ast( @@ -2727,7 +2728,6 @@ pub fn check( let programs = match programs_res.as_ref() { Ok(programs) => { - dbg!(); programs }, _ => { diff --git a/sway-core/src/lib.rs b/sway-core/src/lib.rs index 0653a30776b..ed1ee7c727a 100644 --- a/sway-core/src/lib.rs +++ b/sway-core/src/lib.rs @@ -90,7 +90,6 @@ pub fn parse( engines: &Engines, config: Option<&BuildConfig>, ) -> Result<(lexed::LexedProgram, parsed::ParseProgram), ErrorEmitted> { - dbg!(); match config { None => parse_in_memory( handler, @@ -258,15 +257,12 @@ fn parse_submodules( experimental: ExperimentalFlags, lsp_mode: Option<&LspConfig>, ) -> Submodules { - dbg!(); // Assume the happy path, so there'll be as many submodules as dependencies, but no more. let mut submods = Vec::with_capacity(module.submodules().count()); - dbg!(); module.submodules().for_each(|submod| { // Read the source code from the dependency. // If we cannot, record as an error, but continue with other files. let submod_path = Arc::new(module_path(module_dir, module_name, submod)); - dbg!(); let submod_str: Arc = match std::fs::read_to_string(&*submod_path) { Ok(s) => Arc::from(s), Err(e) => { @@ -278,7 +274,6 @@ fn parse_submodules( return; } }; - dbg!(); if let Ok(ParsedModuleTree { tree_type: kind, lexed_module, @@ -321,7 +316,6 @@ fn parse_submodules( submods.push(submodule); } }); - dbg!(); submods } @@ -348,14 +342,12 @@ fn parse_module_tree( experimental: ExperimentalFlags, lsp_mode: Option<&LspConfig>, ) -> Result { - dbg!(); let query_engine = engines.qe(); let lexed_now = std::time::Instant::now(); // Parse this module first. let module_dir = path.parent().expect("module file has no parent directory"); let source_id = engines.se().get_source_id(&path.clone()); - dbg!(); let module = match sway_parse::parse_file(handler, src.clone(), Some(source_id)) { Ok(module) => module, Err(e) => { @@ -364,7 +356,6 @@ fn parse_module_tree( } }; //let module = sway_parse::parse_file(handler, src.clone(), Some(source_id))?; - dbg!(); // Parse all submodules before converting to the `ParseTree`. // This always recovers on parse errors for the file itself by skipping that file. let submodules = parse_submodules( @@ -378,8 +369,7 @@ fn parse_module_tree( experimental, lsp_mode, ); - dbg!(); - eprintln!("โฑ๏ธ Lexed module took {:?}", lexed_now.elapsed()); + //eprintln!("โฑ๏ธ Lexed module took {:?}", lexed_now.elapsed()); let parsed_now = std::time::Instant::now(); // Convert from the raw parsed module to the `ParseTree` ready for type-check. @@ -389,12 +379,9 @@ fn parse_module_tree( engines, module.value.clone(), )?; - dbg!(); let module_kind_span = module.value.kind.span(); - dbg!(); let attributes = module_attrs_to_map(handler, &module.attribute_list)?; - dbg!(); - eprintln!("โฑ๏ธ Parsed module took {:?}", parsed_now.elapsed()); + //eprintln!("โฑ๏ธ Parsed module took {:?}", parsed_now.elapsed()); let lexed_submodules = submodules .iter() @@ -486,7 +473,7 @@ pub(crate) fn is_ty_module_cache_up_to_date( .and_then(|x| x.lsp_mode.as_ref()) .and_then(|lsp| lsp.file_versions.get(path.as_ref())) .map_or(true, |version| { - version.map_or(true, |v| v <= typed.version.unwrap_or(0)) + version.map_or(true, |v| typed.version.map_or(false, |tv| v <= tv)) }); // If the cache is up to date, recursively check all dependencies @@ -537,9 +524,13 @@ pub(crate) fn is_parse_module_cache_up_to_date( } }, |version| { - // In LSP mode, cache is valid if the current version is not greater - // than the version at last compilation. - !version.map_or(false, |v| v > entry.parsed.version.unwrap_or(0)) + // Determine if the parse cache is up-to-date in LSP mode: + // - If there's no LSP file version (version is None), consider the cache up-to-date. + // - If there is an LSP file version: + // - If there's no cached version (entry.parsed.version is None), the cache is outdated. + // - If there's a cached version, compare them: cache is up-to-date if the LSP file version + // is not greater than the cached version. + version.map_or(true, |v| entry.parsed.version.map_or(false, |ev| v <= ev)) }, ); @@ -816,11 +807,9 @@ pub fn compile_to_ast( let (lexed_program, mut parsed_program) = match parse_program_opt { Ok(modules) => { - dbg!(); modules }, Err(e) => { - dbg!(); // Input string is completely empty. how? eprintln!("ERROR PARSING PROGRAM | {:?} | src_file: {}", e, input_clone); handler.dedup(); diff --git a/sway-core/src/query_engine/mod.rs b/sway-core/src/query_engine/mod.rs index b25a64dffef..a45bb09daf1 100644 --- a/sway-core/src/query_engine/mod.rs +++ b/sway-core/src/query_engine/mod.rs @@ -1,10 +1,6 @@ -use parking_lot::RwLock; +use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use std::{ - collections::HashMap, - ops::{Deref, DerefMut}, - path::PathBuf, - sync::Arc, - time::SystemTime, + collections::HashMap, ops::{Deref, DerefMut}, path::PathBuf, sync::Arc, time::SystemTime }; use sway_error::{error::CompileError, warning::CompileWarning}; use sway_types::IdentUnique; @@ -138,14 +134,27 @@ pub struct FunctionCacheEntry { pub fn_decl: DeclRef>, } -#[derive(Debug, Default, Clone)] +#[derive(Debug, Default)] pub struct QueryEngine { // We want the below types wrapped in Arcs to optimize cloning from LSP. - pub module_cache: Arc>, + //pub module_cache: Arc>, + // pub module_cache: RwLock, + pub module_cache: CowCache, programs_cache: Arc>, function_cache: Arc>, } +impl Clone for QueryEngine { + fn clone(&self) -> Self { + Self { + // module_cache: RwLock::new(self.module_cache.read().clone()), + module_cache: CowCache::new(self.module_cache.read().clone()), + programs_cache: self.programs_cache.clone(), + function_cache: self.function_cache.clone(), + } + } +} + impl QueryEngine { pub fn update_or_insert_parsed_module_cache_entry(&self, entry: ModuleCacheEntry) { let path = entry.common.path.clone(); @@ -196,3 +205,102 @@ impl QueryEngine { ); } } + + +/// Thread-safe, copy-on-write cache optimized for LSP operations. +/// +/// Addresses key LSP challenges: +/// 1. Concurrent read access to shared data +/// 2. Local modifications for cancellable operations (e.g., compilation) +/// 3. Prevents incomplete results from affecting shared state +/// 4. Maintains consistency via explicit commit step +/// +/// Uses `Arc>` for shared state and `RwLock>` for local changes. +/// Suitable for interactive sessions with frequent file changes. +#[derive(Debug, Default)] +pub struct CowCache { + inner: Arc>, + local: RwLock>, +} + +impl CowCache { + /// Creates a new `CowCache` with the given initial value. + /// + /// The value is wrapped in an `Arc>` to allow shared access across threads. + pub fn new(value: T) -> Self { + Self { + inner: Arc::new(RwLock::new(value)), + local: RwLock::new(None), + } + } + + /// Provides read access to the cached value. + /// + /// If a local modification exists, it returns a reference to the local copy. + /// Otherwise, it returns a reference to the shared state. + /// + /// This method is optimized for concurrent read access in LSP operations. + pub fn read(&self) -> impl Deref + '_ { + if self.local.read().is_some() { + ReadGuard::Local(self.local.read()) + } else { + ReadGuard::Shared(self.inner.read()) + } + } + + /// Provides write access to a local copy of the cached value. + /// + /// In LSP, this is used for operations like compilation tasks that may be cancelled. + /// It allows modifications without affecting the shared state until explicitly committed. + pub fn write(&self) -> impl DerefMut + '_ { + let mut local = self.local.write(); + if local.is_none() { + *local = Some(self.inner.read().clone()); + } + WriteGuard(local) + } + + /// Commits local modifications to the shared state. + /// + /// Called after successful completion of a compilation task. + /// If a task is cancelled, not calling this method effectively discards local changes. + pub fn commit(&self) { + if let Some(local) = self.local.write().take() { + *self.inner.write() = local; + } + } +} + +/// A guard type that provides read access to either the local or shared state. +enum ReadGuard<'a, T: Clone> { + Local(RwLockReadGuard<'a, Option>), + Shared(RwLockReadGuard<'a, T>), +} + +impl<'a, T: Clone> Deref for ReadGuard<'a, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + match self { + ReadGuard::Local(r) => r.as_ref().unwrap(), + ReadGuard::Shared(guard) => guard.deref(), + } + } +} + +/// A guard type that provides write access to the local state. +struct WriteGuard<'a, T: Clone>(RwLockWriteGuard<'a, Option>); + +impl<'a, T: Clone> Deref for WriteGuard<'a, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.0.as_ref().unwrap() + } +} + +impl<'a, T: Clone> DerefMut for WriteGuard<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.0.as_mut().unwrap() + } +} diff --git a/sway-core/src/semantic_analysis/module.rs b/sway-core/src/semantic_analysis/module.rs index 5feacaa7f76..508db5b2a8a 100644 --- a/sway-core/src/semantic_analysis/module.rs +++ b/sway-core/src/semantic_analysis/module.rs @@ -333,10 +333,10 @@ impl ty::TyModule { } = parsed; // Check if the root module cache is up to date - eprintln!( - "Root Module: {:?}", - parsed.span.source_id().map(|x| engines.se().get_path(x)) - ); + // eprintln!( + // "Root Module: {:?}", + // parsed.span.source_id().map(|x| engines.se().get_path(x)) + // ); // Try to get the cached root module if let Some(module) = ty::TyModule::get_cached_ty_module_if_up_to_date( diff --git a/sway-lsp/src/capabilities/diagnostic.rs b/sway-lsp/src/capabilities/diagnostic.rs index 71a860b7a71..5bdeee39fe8 100644 --- a/sway-lsp/src/capabilities/diagnostic.rs +++ b/sway-lsp/src/capabilities/diagnostic.rs @@ -42,7 +42,6 @@ pub fn get_diagnostics( errors: &[CompileError], source_engine: &SourceEngine, ) -> DiagnosticMap { - dbg!(); let mut diagnostics = DiagnosticMap::new(); for warning in warnings { let diagnostic = get_warning_diagnostic(warning); @@ -55,7 +54,6 @@ pub fn get_diagnostics( .push(diagnostic); } } - dbg!(); for error in errors { let diagnostic = get_error_diagnostic(error); if let Some(source_id) = error.span().source_id() { @@ -63,7 +61,6 @@ pub fn get_diagnostics( diagnostics.entry(path).or_default().errors.push(diagnostic); } } - dbg!(); diagnostics } diff --git a/sway-lsp/src/capabilities/hover/mod.rs b/sway-lsp/src/capabilities/hover/mod.rs index 97ef886daf7..a6af0031cf1 100644 --- a/sway-lsp/src/capabilities/hover/mod.rs +++ b/sway-lsp/src/capabilities/hover/mod.rs @@ -27,7 +27,9 @@ pub fn hover_data( position: Position, client_config: LspClient, ) -> Option { + eprintln!("Hover request at position {:?}", position); let t = session.token_map().token_at_position(url, position)?; + eprintln!("Token at position {:?}", t.key()); let (ident, token) = t.pair(); let range = ident.range; diff --git a/sway-lsp/src/core/session.rs b/sway-lsp/src/core/session.rs index 1b09e7ce165..f86cc1429f9 100644 --- a/sway-lsp/src/core/session.rs +++ b/sway-lsp/src/core/session.rs @@ -430,25 +430,22 @@ pub fn parse_project( let diagnostics = traverse(results, engines, session.clone())?; eprintln!("โฑ๏ธ Traversing the ASTS took {:?}", traverse_now.elapsed()); if let Some(config) = &lsp_mode { - dbg!(); // Only write the diagnostics results on didSave or didOpen. if !config.optimized_build { - dbg!(); if let Some((errors, warnings)) = &diagnostics { - dbg!(); *session.diagnostics.write() = capabilities::diagnostic::get_diagnostics(warnings, errors, engines.se()); } } } - dbg!(); + let runnables_now = std::time::Instant::now(); if let Some(typed) = &session.compiled_program.read().typed { - dbg!(); session.runnables.clear(); dbg!(); // This is where it's crashing OOB into the concurrent slab create_runnables(&session.runnables, typed, engines.de(), engines.se()); + dbg!(); } eprintln!("โฑ๏ธ creating runnables took: {:?}", runnables_now.elapsed()); eprintln!( @@ -508,7 +505,6 @@ fn create_runnables( decl_engine: &DeclEngine, source_engine: &SourceEngine, ) { - dbg!(); let _p = tracing::trace_span!("create_runnables").entered(); // Insert runnable test functions. @@ -519,7 +515,6 @@ fn create_runnables( .first() .map_or_else(|| decl.name.span(), |(_, attr)| attr.span.clone()); if let Some(source_id) = span.source_id() { - dbg!(); let path = source_engine.get_path(source_id); let runnable = Box::new(RunnableTestFn { range: token::get_range_from_span(&span.clone()), @@ -529,17 +524,14 @@ fn create_runnables( runnables.entry(path).or_default().push(runnable); } } - dbg!(); // Insert runnable main function if the program is a script. if let ty::TyProgramKind::Script { entry_function: ref main_function, .. } = typed_program.kind { - dbg!(); let main_function = decl_engine.get_function(main_function); let span = main_function.name.span(); - dbg!(); if let Some(source_id) = span.source_id() { let path = source_engine.get_path(source_id); let runnable = Box::new(RunnableMainFn { diff --git a/sway-lsp/src/server_state.rs b/sway-lsp/src/server_state.rs index 4dffa910458..22508a6350d 100644 --- a/sway-lsp/src/server_state.rs +++ b/sway-lsp/src/server_state.rs @@ -134,7 +134,7 @@ impl ServerState { let uri = ctx.uri.as_ref().unwrap().clone(); let session = ctx.session.as_ref().unwrap().clone(); let mut engines_clone = session.engines.read().clone(); - eprintln!("\n ----------------- NEW COMPILATION TASK: triggered by {:?} -----------------", uri.path()); + eprintln!("\n ----------------- NEW COMPILATION TASK: version {:?} triggered by {:?} -----------------", ctx.version, uri.path()); if let Some(version) = ctx.version { // Perform garbage collection at configured intervals if enabled to manage memory usage. @@ -156,12 +156,13 @@ impl ServerState { eprintln!("No Garbabe collection applied"); } } - + let lsp_mode = Some(LspConfig { optimized_build: ctx.optimized_build, file_versions: ctx.file_versions, }); + // Set the is_compiling flag to true so that the wait_for_parsing function knows that we are compiling is_compiling.store(true, Ordering::SeqCst); eprintln!("โš™๏ธ โš™๏ธ โš™๏ธ โš™๏ธ session::parse_project โš™๏ธ โš™๏ธ โš™๏ธ โš™๏ธ"); @@ -178,7 +179,7 @@ impl ServerState { // Find the module id from the path match session::program_id_from_path(&path, &engines_clone) { Ok(program_id) => { - eprintln!("๐Ÿ‘จโ€๐Ÿ’ป โœ… Compliation returned successfully ๐Ÿ‘จโ€๐Ÿ’ป"); + eprintln!("๐Ÿ‘จโ€๐Ÿ’ป โœ… Compliation returned successfully for version {:?} ๐Ÿ‘จโ€๐Ÿ’ป", ctx.version); // Use the module id to get the metrics for the module if let Some(metrics) = session.metrics.get(&program_id) { // It's very important to check if the workspace AST was reused to determine if we need to overwrite the engines. @@ -189,6 +190,7 @@ impl ServerState { metrics.reused_programs ); if metrics.reused_programs == 0 { + engines_clone.qe().module_cache.commit(); // The compiler did not reuse the workspace AST. // We need to overwrite the old engines with the engines clone. eprintln!("๐Ÿ‘จโ€๐Ÿ’ป โ†ช Swapping engines ๐Ÿ‘จโ€๐Ÿ’ป โ†ช"); diff --git a/sway-lsp/tests/integration/lsp.rs b/sway-lsp/tests/integration/lsp.rs index 576477081f9..ee07d0cb530 100644 --- a/sway-lsp/tests/integration/lsp.rs +++ b/sway-lsp/tests/integration/lsp.rs @@ -124,6 +124,53 @@ pub(crate) async fn did_change_request( did_change } +/// Simulates a keypress at the current cursor position +/// 66% chance of enter keypress +/// 33% chance of backspace keypress +pub fn simulate_keypress( + uri: &Url, + version: i32, + cursor_line: &mut u32, +) -> DidChangeTextDocumentParams { + if rand::random::() % 3 < 2 { + // enter keypress at current cursor line + *cursor_line += 1; // Move cursor down + eprintln!("Enter keypress"); + create_did_change_params( + uri, + version, + Position { + line: *cursor_line - 1, + character: 0, + }, + Position { + line: *cursor_line - 1, + character: 0, + }, + 0, + ) + } else { + eprintln!("Backspace keypress"); + // backspace keypress at current cursor line + if *cursor_line > 1 { + *cursor_line -= 1; // Move cursor up, but not above line 1 + } + create_did_change_params( + uri, + version, + Position { + line: *cursor_line, + character: 0, + }, + Position { + line: *cursor_line + 1, + character: 0, + }, + 1, + ) + } +} + pub(crate) async fn show_ast_request( server: &ServerState, uri: &Url, diff --git a/sway-lsp/tests/lib.rs b/sway-lsp/tests/lib.rs index fc415bb67af..6ba7c73d4c1 100644 --- a/sway-lsp/tests/lib.rs +++ b/sway-lsp/tests/lib.rs @@ -2,6 +2,7 @@ pub mod integration; use crate::integration::{code_actions, lsp}; use lsp_types::*; +use rand::Rng; use std::{fs, path::PathBuf}; use sway_lsp::{ config::LspClient, @@ -242,37 +243,66 @@ fn did_change_stress_test_random_wait() { // .join("generics_in_contract"); // let uri = init_and_open(&mut service, example_dir.join("src/main.sw")).await; - // let uri = init_and_open( - // &mut service, - // PathBuf::from( - // "/Users/josh/Documents/rust/fuel/user_projects/fluid-protocol/libraries", - // ) - // .join("src/fpt_staking_interface.sw"), - // ) - // .await; - let uri = init_and_open( &mut service, PathBuf::from( - "/Users/josh/Documents/rust/fuel/user_projects/fluid-protocol/contracts/multi-trove-getter-contract", + "/Users/josh/Documents/rust/fuel/user_projects/fluid-protocol/libraries", ) - .join("src/main.sw"), + .join("src/fpt_staking_interface.sw"), ) .await; + let mut uris = vec![]; + uris.push(Url::from_file_path(PathBuf::from( + "/Users/josh/Documents/rust/fuel/user_projects/fluid-protocol/libraries/src/token_interface.sw", + )).unwrap()); + uris.push(Url::from_file_path(PathBuf::from( + "/Users/josh/Documents/rust/fuel/user_projects/fluid-protocol/libraries/src/active_pool_interface.sw", + )).unwrap()); + uris.push(Url::from_file_path(PathBuf::from( + "/Users/josh/Documents/rust/fuel/user_projects/fluid-protocol/libraries/src/trove_manager_interface.sw", + )).unwrap()); + uris.push(Url::from_file_path(PathBuf::from( + "/Users/josh/Documents/rust/fuel/user_projects/fluid-protocol/libraries/src/fpt_staking_interface.sw", + )).unwrap()); + + // Initialize cursor position + let mut cursor_line = 29; + + // let uri = init_and_open( + // &mut service, + // PathBuf::from( + // "/Users/josh/Documents/rust/fuel/user_projects/fluid-protocol/contracts/multi-trove-getter-contract", + // ) + // .join("src/main.sw"), + // ) + // .await; + // 1. randomise the file that is changed out of all the files in the project. // 2. change the file // 3. wait for the file to be parsed // 4. try and do a hover or goto def for a random type // 5. repeat. - let times = 6000; + + let times = 600000; for version in 0..times { - //eprintln!("version: {}", version); - let _ = lsp::did_change_request(&mut service, &uri, version + 1, None).await; + let uri = if version != 0 { + cursor_line = 2; + // random number between 0 and the number of uris + let rand = rand::thread_rng().gen_range(0..uris.len()); + uris[rand].clone() + } else { + uri.clone() + }; + + eprintln!("version: {}", version); + let params = lsp::simulate_keypress(&uri, version, &mut cursor_line); + let _ = lsp::did_change_request(&mut service, &uri, version, Some(params)).await; if version == 0 { service.inner().wait_for_parsing().await; } + // wait for a random amount of time between 1-30ms tokio::time::sleep(tokio::time::Duration::from_millis( rand::random::() % 30 + 1, @@ -285,6 +315,42 @@ fn did_change_stress_test_random_wait() { )) .await; } + + // if rand::random::() % 10 < 1 { + // tokio::time::sleep(tokio::time::Duration::from_millis( + // rand::random::() % 2000, + // )) + // .await; + + // match service.inner() + // .uri_and_session_from_workspace(&uri) + // .await + // { + // Ok((uri, _)) => { + // // Here we will do a hover request to simulate the user hovering over a type + // let hover = HoverDocumentation { + // req_uri: &uri, + // req_line: cursor_line + 2, + // req_char: 29, + // documentation: vec!["```sway\npub struct ReadStorage\n```\n---"], + // }; + // service.inner().wait_for_parsing().await; + // eprintln!("๐Ÿ“ž Hover request ๐Ÿ“ž | cursor_line: {}", cursor_line); + // if let Ok(text_document) = service.inner().documents.get_text_document(&uri) { + // println!("Document content:"); + // for (index, line) in text_document.get_text().lines().enumerate() { + // println!("{:4}: {}", index + 1, line); + // } + // } else { + // eprintln!("Failed to get text document for URI: {}", uri); + // } + // let _ = lsp::hover_request(&mut service.inner(), &hover).await; + // } + // Err(err) => { + // tracing::error!("{}", err.to_string()); + // } + // } + // } } shutdown_and_exit(&mut service).await; }; @@ -347,7 +413,6 @@ fn did_change_stress_test_enter_uzi() { }); } - fn garbage_collection_runner(path: PathBuf) { run_async!({ setup_panic_hook(); @@ -361,43 +426,19 @@ fn garbage_collection_runner(path: PathBuf) { .gc_frequency = 1; let uri = init_and_open(&mut service, path).await; let times = 60; + + // Initialize cursor position + let mut cursor_line = 20; + for version in 1..times { //eprintln!("version: {}", version); - let params = if rand::random::() % 3 < 1 { - // enter keypress at line 20 - lsp::create_did_change_params( - &uri, - version, - Position { - line: 20, - character: 0, - }, - Position { - line: 20, - character: 0, - }, - 0, - ) - } else { - // backspace keypress at line 21 - lsp::create_did_change_params( - &uri, - version, - Position { - line: 20, - character: 0, - }, - Position { - line: 21, - character: 0, - }, - 1, - ) - }; + let params = lsp::simulate_keypress(&uri, version, &mut cursor_line); let _ = lsp::did_change_request(&mut service, &uri, version, Some(params)).await; if version == 0 { service.inner().wait_for_parsing().await; } + // Print current cursor position + eprintln!("Cursor position: line {}", cursor_line); // wait for a random amount of time to simulate typing random_delay().await; } diff --git a/sway-parse/src/lib.rs b/sway-parse/src/lib.rs index 191e1c2c0ae..2022c954d7a 100644 --- a/sway-parse/src/lib.rs +++ b/sway-parse/src/lib.rs @@ -42,9 +42,7 @@ pub fn parse_file( src: Arc, source_id: Option, ) -> Result, ErrorEmitted> { - dbg!(); let ts = lex(handler, &src, 0, src.len(), source_id)?; - dbg!(); let (m, _) = Parser::new(handler, &ts).parse_to_end()?; Ok(m) } diff --git a/sway-parse/src/module.rs b/sway-parse/src/module.rs index 047ad4c4af0..ae8449ecbc9 100644 --- a/sway-parse/src/module.rs +++ b/sway-parse/src/module.rs @@ -29,13 +29,10 @@ impl Parse for ModuleKind { impl ParseToEnd for Annotated { fn parse_to_end<'a, 'e>(mut parser: Parser<'a, '_>) -> ParseResult<(Self, ParserConsumed<'a>)> { - dbg!(); // Parse the attribute list. let mut attribute_list = Vec::new(); while let Some(DocComment { .. }) = parser.peek() { - dbg!(); let doc_comment = parser.parse::()?; - dbg!(); // TODO: Use a Literal instead of an Ident when Attribute args // start supporting them and remove `Ident::new_no_trim`. let name = Ident::new_no_trim(doc_comment.content_span.clone()); @@ -59,16 +56,12 @@ impl ParseToEnd for Annotated { ), }), DocStyle::Outer => { - dbg!(); parser.emit_error(ParseErrorKind::ExpectedModuleDocComment); } } } - dbg!(); let (kind, semicolon_token) = parser.parse()?; - dbg!(); let (items, consumed) = parser.parse_to_end()?; - dbg!(); let module = Annotated { attribute_list, value: Module { From c51418f29fb1633c939939ef65503259be1228c7 Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Thu, 1 Aug 2024 13:01:18 +1000 Subject: [PATCH 23/29] remove all dbg prints and timings --- forc-pkg/src/manifest/mod.rs | 19 ------ forc-pkg/src/pkg.rs | 19 ------ sway-core/src/lib.rs | 69 +++------------------- sway-core/src/semantic_analysis/module.rs | 35 +---------- sway-core/src/semantic_analysis/program.rs | 2 - sway-error/src/handler.rs | 1 - sway-lsp/src/capabilities/hover/mod.rs | 2 - sway-lsp/src/core/session.rs | 31 +--------- sway-lsp/src/handlers/notification.rs | 1 - sway-lsp/src/server_state.rs | 15 +---- sway-lsp/tests/integration/lsp.rs | 7 +-- sway-lsp/tests/lib.rs | 1 - sway-parse/src/module.rs | 1 - sway-parse/src/parser.rs | 5 -- 14 files changed, 17 insertions(+), 191 deletions(-) diff --git a/forc-pkg/src/manifest/mod.rs b/forc-pkg/src/manifest/mod.rs index dba7dcdac0e..ca35a46e9da 100644 --- a/forc-pkg/src/manifest/mod.rs +++ b/forc-pkg/src/manifest/mod.rs @@ -359,28 +359,9 @@ impl PackageManifestFile { pub fn entry_string(&self) -> Result> { let entry_path = self.entry_path(); let entry_string = std::fs::read_to_string(entry_path)?; - if entry_string.is_empty() { - eprintln!("๐ŸŸฅ entry_string empty! | entry_path: {:?}", self.entry_path()); - } else { - eprintln!("๐ŸŸฉ entry_string not empty! | entry_path: {:?}", self.entry_path()); - } Ok(Arc::from(entry_string)) } - // we could try to read the file 3 times, but it feels like a hack - // pub fn entry_string(&self) -> Result> { - // for attempt in 1..=3 { - // let entry_path = self.entry_path(); - // let entry_string = std::fs::read_to_string(&entry_path)?; - // if !entry_string.is_empty() { - // return Ok(Arc::from(entry_string)); - // } - // eprintln!("Attempt {}: File empty, retrying...", attempt); - // std::thread::sleep(std::time::Duration::from_millis(100)); - // } - // Err(anyhow::anyhow!("File remained empty after multiple attempts")) - // } - /// Parse and return the associated project's program type. pub fn program_type(&self) -> Result { let entry_string = self.entry_string()?; diff --git a/forc-pkg/src/pkg.rs b/forc-pkg/src/pkg.rs index 31dd4a8b599..584b760ca61 100644 --- a/forc-pkg/src/pkg.rs +++ b/forc-pkg/src/pkg.rs @@ -2655,7 +2655,6 @@ pub fn check( let mut results = vec![]; for (idx, &node) in plan.compilation_order.iter().enumerate() { - let now = std::time::Instant::now(); let pkg = &plan.graph[node]; let manifest = &plan.manifest_map()[&pkg.id()]; @@ -2671,7 +2670,6 @@ pub fn check( let contract_id_value = (idx == plan.compilation_order.len() - 1).then(|| DUMMY_CONTRACT_ID.to_string()); - let dep_now = std::time::Instant::now(); let mut dep_namespace = dependency_namespace( &lib_namespace_map, &compiled_contract_deps, @@ -2682,14 +2680,12 @@ pub fn check( experimental, ) .expect("failed to create dependency namespace"); - eprintln!("โฑ๏ธ Dependency namespace took {:?}", dep_now.elapsed()); let profile = BuildProfile { terse: terse_mode, ..BuildProfile::debug() }; - let build_config_now = std::time::Instant::now(); let build_config = sway_build_config( manifest.dir(), &manifest.entry_path(), @@ -2698,14 +2694,9 @@ pub fn check( )? .with_include_tests(include_tests) .with_lsp_mode(lsp_mode.clone()); - eprintln!("โฑ๏ธ Build config took {:?}", build_config_now.elapsed()); - eprintln!("Forc pacakge loading input string"); let input = manifest.entry_string()?; - eprintln!("Forc package input string loaded"); - // eprintln!("Forc package input string loaded | {}", input.clone()); let handler = Handler::default(); - let compile_to_ast_now = std::time::Instant::now(); let programs_res = sway_core::compile_to_ast( &handler, engines, @@ -2715,14 +2706,11 @@ pub fn check( &pkg.name, retrigger_compilation.clone(), ); - eprintln!("programs res: {:?}", programs_res.is_ok()); - eprintln!("โฑ๏ธ Compile to AST took {:?}", compile_to_ast_now.elapsed()); if retrigger_compilation .as_ref() .is_some_and(|b| b.load(std::sync::atomic::Ordering::SeqCst)) { - eprintln!("๐Ÿช“ ๐Ÿช“ compilation was cancelled 2716"); bail!("compilation was retriggered") } @@ -2731,8 +2719,6 @@ pub fn check( programs }, _ => { - eprintln!("ERROR PARSING MODULE | {:?}", programs_res.clone().ok()); - eprintln!("Returning results with handler | {:?}", handler); results.push((programs_res.ok(), handler)); return Ok(results); } @@ -2762,11 +2748,6 @@ pub fn check( return Ok(results); } results.push((programs_res.ok(), handler)); - eprintln!( - "โฑ๏ธ Compiling package {:?} took {:?}", - pkg.name, - now.elapsed() - ); } if results.is_empty() { diff --git a/sway-core/src/lib.rs b/sway-core/src/lib.rs index ed1ee7c727a..8283795ae8c 100644 --- a/sway-core/src/lib.rs +++ b/sway-core/src/lib.rs @@ -344,18 +344,13 @@ fn parse_module_tree( ) -> Result { let query_engine = engines.qe(); - let lexed_now = std::time::Instant::now(); // Parse this module first. let module_dir = path.parent().expect("module file has no parent directory"); let source_id = engines.se().get_source_id(&path.clone()); let module = match sway_parse::parse_file(handler, src.clone(), Some(source_id)) { Ok(module) => module, - Err(e) => { - eprintln!("ERROR PARSING MODULE | {:?} | src_file: {}", e, src.clone()); - return Err(e); - } + Err(e) => return Err(e), }; - //let module = sway_parse::parse_file(handler, src.clone(), Some(source_id))?; // Parse all submodules before converting to the `ParseTree`. // This always recovers on parse errors for the file itself by skipping that file. let submodules = parse_submodules( @@ -369,9 +364,7 @@ fn parse_module_tree( experimental, lsp_mode, ); - //eprintln!("โฑ๏ธ Lexed module took {:?}", lexed_now.elapsed()); - let parsed_now = std::time::Instant::now(); // Convert from the raw parsed module to the `ParseTree` ready for type-check. let (kind, tree) = to_parsed_lang::convert_parse_tree( &mut to_parsed_lang::Context::new(build_target, experimental), @@ -381,7 +374,6 @@ fn parse_module_tree( )?; let module_kind_span = module.value.kind.span(); let attributes = module_attrs_to_map(handler, &module.attribute_list)?; - //eprintln!("โฑ๏ธ Parsed module took {:?}", parsed_now.elapsed()); let lexed_submodules = submodules .iter() @@ -415,7 +407,6 @@ fn parse_module_tree( .ok() .and_then(|m| m.modified().ok()); let dependencies = submodules.into_iter().map(|s| s.path).collect::>(); - //eprintln!("๐Ÿ” path {:?} | dependencies {:?}", path, dependencies); let version = lsp_mode .and_then(|lsp| lsp.file_versions.get(path.as_ref()).copied()) .unwrap_or(None); @@ -431,17 +422,6 @@ fn parse_module_tree( version, }; let cache_entry = ModuleCacheEntry::new(common_info, parsed_info); - - let split_points = ["sway-lib-core", "sway-lib-std", "libraries", "multi-trove-getter-contract"]; - let relevant_path = path - .iter() - .skip_while(|&comp| !split_points.contains(&comp.to_str().unwrap())) - .collect::(); - eprintln!( - "๐Ÿ€„ ๐Ÿ—‚๏ธ Inserted cache entry for parse module {:?}", - relevant_path - ); - query_engine.update_or_insert_parsed_module_cache_entry(cache_entry); Ok(ParsedModuleTree { @@ -495,16 +475,9 @@ pub(crate) fn is_parse_module_cache_up_to_date( include_tests: bool, build_config: Option<&BuildConfig>, ) -> bool { - let split_points = ["sway-lib-core", "sway-lib-std", "libraries", "multi-trove-getter-contract"]; - let relevant_path = path - .iter() - .skip_while(|&comp| !split_points.contains(&comp.to_str().unwrap())) - .collect::(); - let cache = engines.qe().module_cache.read(); let key = ModuleCacheKey::new(path.clone(), include_tests); - - let res = cache.get(&key).map_or(false, |entry| { + cache.get(&key).map_or(false, |entry| { // Determine if the cached dependency information is still valid let cache_up_to_date = build_config .and_then(|x| x.lsp_mode.as_ref()) @@ -540,15 +513,7 @@ pub(crate) fn is_parse_module_cache_up_to_date( && entry.common.dependencies.iter().all(|dep_path| { is_parse_module_cache_up_to_date(engines, dep_path, include_tests, build_config) }) - }); - - eprintln!( - "๐Ÿ€„ ๐Ÿ‘“ Checking cache for parse module {:?} | is up to date? {} {}", - relevant_path, - if res { "true ๐ŸŸฉ" } else { "FALSE ๐ŸŸฅ" }, - if res { " " } else { "" } - ); - res + }) } fn module_path( @@ -617,7 +582,7 @@ pub fn parsed_to_ast( package_name, build_config, ); - check_should_abort(handler, retrigger_compilation.clone(), 629)?; + check_should_abort(handler, retrigger_compilation.clone())?; // Only clear the parsed AST nodes if we are running a regular compilation pipeline. // LSP needs these to build its token map, and they are cleared by `clear_program` as @@ -678,7 +643,7 @@ pub fn parsed_to_ast( None => (None, None), }; - check_should_abort(handler, retrigger_compilation.clone(), 690)?; + check_should_abort(handler, retrigger_compilation.clone())?; // Perform control flow analysis and extend with any errors. let _ = perform_control_flow_analysis( @@ -764,18 +729,13 @@ pub fn compile_to_ast( package_name: &str, retrigger_compilation: Option>, ) -> Result { - eprintln!("๐Ÿ”จ๐Ÿ”จ --- compile_to_ast --- ๐Ÿ”จ๐Ÿ”จ"); - check_should_abort(handler, retrigger_compilation.clone(), 777)?; + check_should_abort(handler, retrigger_compilation.clone())?; let query_engine = engines.qe(); let mut metrics = PerformanceData::default(); if let Some(config) = build_config { let path = config.canonical_root_module(); let include_tests = config.include_tests; - - //eprintln!(" ๐Ÿ“‚ {}", path.display()); - //eprintln!("{:?}", config.lsp_mode.as_ref().unwrap().file_versions); - // Check if we can re-use the data in the cache. if is_parse_module_cache_up_to_date(engines, &path, include_tests, build_config) { let mut entry = query_engine.get_programs_cache_entry(&path).unwrap(); @@ -784,15 +744,10 @@ pub fn compile_to_ast( let (warnings, errors) = entry.handler_data; let new_handler = Handler::from_parts(warnings, errors); handler.append(new_handler); - eprintln!("programs cache valid, returning"); return Ok(entry.programs); }; } - eprintln!("programs cache invalid, continuing to parse"); - let input_clone = input.clone(); - - let parse_now = std::time::Instant::now(); // Parse the program to a concrete syntax tree (CST). let parse_program_opt = time_expr!( "parse the program to a concrete syntax tree (CST)", @@ -801,17 +756,14 @@ pub fn compile_to_ast( build_config, metrics ); - eprintln!("โฑ๏ธ compile_to_ast took {:?}", parse_now.elapsed()); - check_should_abort(handler, retrigger_compilation.clone(), 805)?; + check_should_abort(handler, retrigger_compilation.clone())?; let (lexed_program, mut parsed_program) = match parse_program_opt { Ok(modules) => { modules }, Err(e) => { - // Input string is completely empty. how? - eprintln!("ERROR PARSING PROGRAM | {:?} | src_file: {}", e, input_clone); handler.dedup(); return Err(e); } @@ -822,7 +774,6 @@ pub fn compile_to_ast( parsed_program.exclude_tests(engines); } - eprintln!("๐Ÿ”จ๐Ÿ”จ --- parsed to typed AST ๐Ÿ”จ๐Ÿ”จ ---"); // Type check (+ other static analysis) the CST to a typed AST. let typed_res = time_expr!( "parse the concrete syntax tree (CST) to a typed AST", @@ -840,7 +791,7 @@ pub fn compile_to_ast( metrics ); - check_should_abort(handler, retrigger_compilation.clone(), 838)?; + check_should_abort(handler, retrigger_compilation.clone())?; handler.dedup(); @@ -856,7 +807,7 @@ pub fn compile_to_ast( query_engine.insert_programs_cache_entry(cache_entry); } - check_should_abort(handler, retrigger_compilation.clone(), 854)?; + check_should_abort(handler, retrigger_compilation.clone())?; Ok(programs) } @@ -1163,11 +1114,9 @@ fn module_return_path_analysis( fn check_should_abort( handler: &Handler, retrigger_compilation: Option>, - line_num: u32, ) -> Result<(), ErrorEmitted> { if let Some(ref retrigger_compilation) = retrigger_compilation { if retrigger_compilation.load(Ordering::SeqCst) { - eprintln!("๐Ÿช“ ๐Ÿช“ compilation was cancelled at line {} ๐Ÿช“ ๐Ÿช“", line_num); return Err(handler.cancel()); } } diff --git a/sway-core/src/semantic_analysis/module.rs b/sway-core/src/semantic_analysis/module.rs index 508db5b2a8a..6ff57764ac0 100644 --- a/sway-core/src/semantic_analysis/module.rs +++ b/sway-core/src/semantic_analysis/module.rs @@ -2,7 +2,6 @@ use std::{ collections::{HashMap, HashSet}, fmt::Display, fs, - path::PathBuf, sync::Arc, }; @@ -274,13 +273,6 @@ impl ty::TyModule { let include_tests = build_config.map_or(false, |x| x.include_tests); let key = ModuleCacheKey::new(path.clone().into(), include_tests); let cache = engines.qe().module_cache.read(); - - let split_points = ["sway-lib-core", "sway-lib-std", "libraries", "multi-trove-getter-contract"]; - let relevant_path = path - .iter() - .skip_while(|&comp| !split_points.contains(&comp.to_str().unwrap())) - .collect::(); - cache.get(&key).and_then(|entry| { entry.typed.as_ref().and_then(|typed| { // Check if the cached module is up to date @@ -291,17 +283,6 @@ impl ty::TyModule { build_config, ); - // Log the cache status - let status = if is_up_to_date { - "โœ… Cache hit" - } else { - "๐Ÿ”„ Cache miss" - }; - eprintln!( - "{} for module {:?} (up to date: {})", - status, relevant_path, is_up_to_date - ); - // Return the cached module if it's up to date, otherwise None if is_up_to_date { Some(typed.module.clone()) @@ -332,13 +313,7 @@ impl ty::TyModule { .. } = parsed; - // Check if the root module cache is up to date - // eprintln!( - // "Root Module: {:?}", - // parsed.span.source_id().map(|x| engines.se().get_path(x)) - // ); - - // Try to get the cached root module + // Try to get the cached root module if it's up to date if let Some(module) = ty::TyModule::get_cached_ty_module_if_up_to_date( parsed.span.source_id(), engines, @@ -394,7 +369,6 @@ impl ty::TyModule { )?; let mut all_nodes = Self::type_check_nodes(handler, ctx.by_ref(), &ordered_nodes)?; - let submodules = submodules_res?; let fallback_fn = collect_fallback_fn(&all_nodes, engines, handler)?; @@ -476,18 +450,11 @@ impl ty::TyModule { // Cache the ty module if let Some(source_id) = span.source_id() { let path = engines.se().get_path(source_id); - let split_points = ["sway-lib-core", "sway-lib-std", "libraries", "multi-trove-getter-contract"]; - let relevant_path = path - .iter() - .skip_while(|&comp| !split_points.contains(&comp.to_str().unwrap())) - .collect::(); - let version = build_config .and_then(|config| config.lsp_mode.as_ref()) .and_then(|lsp| lsp.file_versions.get(&path).copied()) .flatten(); - eprintln!("๐Ÿ’พ Inserting cache entry for TY module {:?}", relevant_path); let include_tests = build_config.map_or(false, |x| x.include_tests); let key = ModuleCacheKey::new(path.clone().into(), include_tests); engines.qe().update_typed_module_cache_entry( diff --git a/sway-core/src/semantic_analysis/program.rs b/sway-core/src/semantic_analysis/program.rs index bfe41511a4f..dd7e016c61c 100644 --- a/sway-core/src/semantic_analysis/program.rs +++ b/sway-core/src/semantic_analysis/program.rs @@ -60,7 +60,6 @@ impl TyProgram { let ParseProgram { root, kind } = parsed; - let now = std::time::Instant::now(); let root = ty::TyModule::type_check( handler, ctx.by_ref(), @@ -69,7 +68,6 @@ impl TyProgram { root, build_config, )?; - eprintln!("โฑ๏ธ Type-checking module took {:?}", now.elapsed()); let (kind, declarations, configurables) = Self::validate_root( handler, diff --git a/sway-error/src/handler.rs b/sway-error/src/handler.rs index dc933fbe91c..24d2222da1b 100644 --- a/sway-error/src/handler.rs +++ b/sway-error/src/handler.rs @@ -35,7 +35,6 @@ impl Handler { // Compilation should be cancelled. pub fn cancel(&self) -> ErrorEmitted { - eprintln!("CANCELLED"); ErrorEmitted { _priv: () } } diff --git a/sway-lsp/src/capabilities/hover/mod.rs b/sway-lsp/src/capabilities/hover/mod.rs index a6af0031cf1..97ef886daf7 100644 --- a/sway-lsp/src/capabilities/hover/mod.rs +++ b/sway-lsp/src/capabilities/hover/mod.rs @@ -27,9 +27,7 @@ pub fn hover_data( position: Position, client_config: LspClient, ) -> Option { - eprintln!("Hover request at position {:?}", position); let t = session.token_map().token_at_position(url, position)?; - eprintln!("Token at position {:?}", t.key()); let (ident, token) = t.pair(); let range = ident.range; diff --git a/sway-lsp/src/core/session.rs b/sway-lsp/src/core/session.rs index f86cc1429f9..6f0af04746a 100644 --- a/sway-lsp/src/core/session.rs +++ b/sway-lsp/src/core/session.rs @@ -143,10 +143,6 @@ impl Session { uri: &Url, ) -> Result<(), LanguageServerError> { let path = uri.to_file_path().unwrap(); - eprintln!( - "๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ Garbage collecting module {:?} ๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ ๐Ÿ—‘๏ธ", - path - ); let source_id = { engines.se().get_source_id(&path) }; engines.clear_module(&source_id); Ok(()) @@ -308,14 +304,11 @@ pub fn traverse( let mut diagnostics: CompileResults = (Vec::default(), Vec::default()); let results_len = results.len(); for (i, (value, handler)) in results.into_iter().enumerate() { - let parse_now = std::time::Instant::now(); // We can convert these destructured elements to a Vec later on. let current_diagnostics = handler.consume(); diagnostics = current_diagnostics; if value.is_none() { - eprintln!("Unable to traverse module, value is None"); - // Should this be an error? continue; } let Programs { @@ -343,8 +336,6 @@ pub fn traverse( let path = engines.se().get_path(source_id); let program_id = program_id_from_path(&path, engines)?; session.metrics.insert(program_id, metrics); - - eprintln!("โคต๏ธ Traversing: {:?}", path); } // Get a reference to the typed program AST. @@ -392,7 +383,6 @@ pub fn traverse( dependency::collect_typed_declaration(node, ctx); }); } - eprintln!("โฑ๏ธ Traversal took {:?}", parse_now.elapsed()); } Ok(Some(diagnostics)) } @@ -407,13 +397,10 @@ pub fn parse_project( experimental: sway_core::ExperimentalFlags, ) -> Result<(), LanguageServerError> { let _p = tracing::trace_span!("parse_project").entered(); - let build_plan_now = std::time::Instant::now(); let build_plan = session .build_plan_cache .get_or_update(&session.sync.manifest_path(), || build_plan(uri))?; - eprintln!("โฑ๏ธ Build Plan took {:?}", build_plan_now.elapsed()); - - let parse_now = std::time::Instant::now(); + let results = compile( &build_plan, engines, @@ -421,14 +408,11 @@ pub fn parse_project( lsp_mode.clone(), experimental, )?; - eprintln!("โฑ๏ธ Total Compilation took {:?}", parse_now.elapsed()); if results.last().is_none() { return Err(LanguageServerError::ProgramsIsNone); } - eprintln!("โคต๏ธ Traversing the ASTS"); - let traverse_now = std::time::Instant::now(); + let diagnostics = traverse(results, engines, session.clone())?; - eprintln!("โฑ๏ธ Traversing the ASTS took {:?}", traverse_now.elapsed()); if let Some(config) = &lsp_mode { // Only write the diagnostics results on didSave or didOpen. if !config.optimized_build { @@ -439,19 +423,10 @@ pub fn parse_project( } } - let runnables_now = std::time::Instant::now(); if let Some(typed) = &session.compiled_program.read().typed { session.runnables.clear(); - dbg!(); - // This is where it's crashing OOB into the concurrent slab create_runnables(&session.runnables, typed, engines.de(), engines.se()); - dbg!(); } - eprintln!("โฑ๏ธ creating runnables took: {:?}", runnables_now.elapsed()); - eprintln!( - "โฑ๏ธ TOTAL COMPILATION AND TRAVERSAL TIME: {:?}", - parse_now.elapsed() - ); Ok(()) } @@ -461,7 +436,6 @@ fn parse_ast_to_tokens( ctx: &ParseContext, f: impl Fn(&AstNode, &ParseContext) + Sync, ) { - eprintln!("โคต๏ธ Parsing the AST"); let nodes = parse_program .root .tree @@ -483,7 +457,6 @@ fn parse_ast_to_typed_tokens( ctx: &ParseContext, f: impl Fn(&ty::TyAstNode, &ParseContext) + Sync, ) { - eprintln!("โคต๏ธ Parsing the typed AST"); let nodes = typed_program .root .all_nodes diff --git a/sway-lsp/src/handlers/notification.rs b/sway-lsp/src/handlers/notification.rs index ac7a8e23b29..928262efb51 100644 --- a/sway-lsp/src/handlers/notification.rs +++ b/sway-lsp/src/handlers/notification.rs @@ -106,7 +106,6 @@ pub async fn handle_did_change_text_document( &uri, Some(params.text_document.version as u64), ); - //eprintln!("File versions: {:#?}", file_versions); send_new_compilation_request( state, session.clone(), diff --git a/sway-lsp/src/server_state.rs b/sway-lsp/src/server_state.rs index 22508a6350d..83e1c76721a 100644 --- a/sway-lsp/src/server_state.rs +++ b/sway-lsp/src/server_state.rs @@ -134,7 +134,6 @@ impl ServerState { let uri = ctx.uri.as_ref().unwrap().clone(); let session = ctx.session.as_ref().unwrap().clone(); let mut engines_clone = session.engines.read().clone(); - eprintln!("\n ----------------- NEW COMPILATION TASK: version {:?} triggered by {:?} -----------------", ctx.version, uri.path()); if let Some(version) = ctx.version { // Perform garbage collection at configured intervals if enabled to manage memory usage. @@ -152,8 +151,6 @@ impl ServerState { err.to_string() ); } - } else { - eprintln!("No Garbabe collection applied"); } } @@ -165,7 +162,6 @@ impl ServerState { // Set the is_compiling flag to true so that the wait_for_parsing function knows that we are compiling is_compiling.store(true, Ordering::SeqCst); - eprintln!("โš™๏ธ โš™๏ธ โš™๏ธ โš™๏ธ session::parse_project โš™๏ธ โš™๏ธ โš™๏ธ โš™๏ธ"); match session::parse_project( &uri, &engines_clone, @@ -179,21 +175,18 @@ impl ServerState { // Find the module id from the path match session::program_id_from_path(&path, &engines_clone) { Ok(program_id) => { - eprintln!("๐Ÿ‘จโ€๐Ÿ’ป โœ… Compliation returned successfully for version {:?} ๐Ÿ‘จโ€๐Ÿ’ป", ctx.version); // Use the module id to get the metrics for the module if let Some(metrics) = session.metrics.get(&program_id) { // It's very important to check if the workspace AST was reused to determine if we need to overwrite the engines. // Because the engines_clone has garbage collection applied. If the workspace AST was reused, we need to keep the old engines // as the engines_clone might have cleared some types that are still in use. - eprintln!( - "๐Ÿ‘จโ€๐Ÿ’ป metrics.reused_programs {} ๐Ÿ‘จโ€๐Ÿ’ป", - metrics.reused_programs - ); if metrics.reused_programs == 0 { + // Commit local changes in the module cache to the shared state. + // This ensures that any modifications made during compilation are preserved + // before we swap the engines. engines_clone.qe().module_cache.commit(); // The compiler did not reuse the workspace AST. // We need to overwrite the old engines with the engines clone. - eprintln!("๐Ÿ‘จโ€๐Ÿ’ป โ†ช Swapping engines ๐Ÿ‘จโ€๐Ÿ’ป โ†ช"); mem::swap( &mut *session.engines.write(), &mut engines_clone, @@ -204,7 +197,6 @@ impl ServerState { LastCompilationState::Success; } Err(err) => { - eprintln!("๐Ÿ‘จโ€๐Ÿ’ป โŒ Compliation failed ๐Ÿ‘จโ€๐Ÿ’ป"); tracing::error!("{}", err.to_string()); *last_compilation_state.write() = LastCompilationState::Failed; @@ -212,7 +204,6 @@ impl ServerState { } } Err(_err) => { - eprintln!("๐Ÿ‘จโ€๐Ÿ’ป โŒ Compliation failed ๐Ÿ‘จโ€๐Ÿ’ป"); *last_compilation_state.write() = LastCompilationState::Failed; } } diff --git a/sway-lsp/tests/integration/lsp.rs b/sway-lsp/tests/integration/lsp.rs index ee07d0cb530..e6ce422616f 100644 --- a/sway-lsp/tests/integration/lsp.rs +++ b/sway-lsp/tests/integration/lsp.rs @@ -43,7 +43,6 @@ pub(crate) async fn initialize_request(service: &mut LspService) -> 1.into(), json!({ "capabilities": sway_lsp::server_capabilities() }), ); - eprintln!("initialize response: {:?}", response); assert_json_eq!(expected, response.ok().unwrap()); initialize } @@ -134,8 +133,7 @@ pub fn simulate_keypress( ) -> DidChangeTextDocumentParams { if rand::random::() % 3 < 2 { // enter keypress at current cursor line - *cursor_line += 1; // Move cursor down - eprintln!("Enter keypress"); + *cursor_line += 1; create_did_change_params( uri, version, @@ -150,10 +148,9 @@ pub fn simulate_keypress( 0, ) } else { - eprintln!("Backspace keypress"); // backspace keypress at current cursor line if *cursor_line > 1 { - *cursor_line -= 1; // Move cursor up, but not above line 1 + *cursor_line -= 1; } create_did_change_params( uri, diff --git a/sway-lsp/tests/lib.rs b/sway-lsp/tests/lib.rs index 6ba7c73d4c1..09fbe345721 100644 --- a/sway-lsp/tests/lib.rs +++ b/sway-lsp/tests/lib.rs @@ -296,7 +296,6 @@ fn did_change_stress_test_random_wait() { uri.clone() }; - eprintln!("version: {}", version); let params = lsp::simulate_keypress(&uri, version, &mut cursor_line); let _ = lsp::did_change_request(&mut service, &uri, version, Some(params)).await; if version == 0 { diff --git a/sway-parse/src/module.rs b/sway-parse/src/module.rs index ae8449ecbc9..7936012e96a 100644 --- a/sway-parse/src/module.rs +++ b/sway-parse/src/module.rs @@ -21,7 +21,6 @@ impl Parse for ModuleKind { } else if let Some(library_token) = parser.take() { Ok(Self::Library { library_token }) } else { - eprintln!("โ”โ‰๏ธ Expected Module Kind, how the hell did we get here?"); Err(parser.emit_error(ParseErrorKind::ExpectedModuleKind)) } } diff --git a/sway-parse/src/parser.rs b/sway-parse/src/parser.rs index 3aa77a1e465..ad9d3e7ffc2 100644 --- a/sway-parse/src/parser.rs +++ b/sway-parse/src/parser.rs @@ -209,8 +209,6 @@ impl<'a, 'e> Parser<'a, 'e> { /// Parses a `T` in its canonical way. pub fn parse(&mut self) -> ParseResult { - //eprintln!("parse Type of T: {}", std::any::type_name::()); - T::parse(self) } @@ -222,9 +220,6 @@ impl<'a, 'e> Parser<'a, 'e> { } pub fn parse_to_end(self) -> ParseResult<(T, ParserConsumed<'a>)> { - // eprintln! type of T - // eprintln!("parse_to_end Type of T: {}", std::any::type_name::()); - T::parse_to_end(self) } From 6ee0dd2229b82094494cd1c98852a61b25a1bca3 Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Thu, 1 Aug 2024 13:14:49 +1000 Subject: [PATCH 24/29] clean up --- forc-pkg/src/manifest/mod.rs | 4 ++-- forc-pkg/src/pkg.rs | 4 +--- sway-core/src/lib.rs | 10 +++------- sway-core/src/query_engine/mod.rs | 14 +++++++------- sway-core/src/semantic_analysis/module.rs | 4 ++-- sway-lsp/src/core/session.rs | 5 +++-- sway-lsp/src/server_state.rs | 12 +----------- sway-lsp/tests/lib.rs | 6 ++---- sway-lsp/tests/utils/src/lib.rs | 2 +- 9 files changed, 22 insertions(+), 39 deletions(-) diff --git a/forc-pkg/src/manifest/mod.rs b/forc-pkg/src/manifest/mod.rs index ca35a46e9da..3f7bc21b1bd 100644 --- a/forc-pkg/src/manifest/mod.rs +++ b/forc-pkg/src/manifest/mod.rs @@ -566,7 +566,7 @@ impl PackageManifest { }) .map_err(|e| anyhow!("failed to parse manifest: {}.", e))?; for warning in warnings { - //println_warning(&warning); + println_warning(&warning); } manifest.implicitly_include_std_if_missing(); manifest.implicitly_include_default_build_profiles_if_missing(); @@ -955,7 +955,7 @@ impl WorkspaceManifest { }) .map_err(|e| anyhow!("failed to parse manifest: {}.", e))?; for warning in warnings { - //println_warning(&warning); + println_warning(&warning); } Ok(manifest) } diff --git a/forc-pkg/src/pkg.rs b/forc-pkg/src/pkg.rs index 584b760ca61..22b4dc6ed5c 100644 --- a/forc-pkg/src/pkg.rs +++ b/forc-pkg/src/pkg.rs @@ -2715,9 +2715,7 @@ pub fn check( } let programs = match programs_res.as_ref() { - Ok(programs) => { - programs - }, + Ok(programs) => programs, _ => { results.push((programs_res.ok(), handler)); return Ok(results); diff --git a/sway-core/src/lib.rs b/sway-core/src/lib.rs index 8283795ae8c..0046e2c9624 100644 --- a/sway-core/src/lib.rs +++ b/sway-core/src/lib.rs @@ -347,10 +347,8 @@ fn parse_module_tree( // Parse this module first. let module_dir = path.parent().expect("module file has no parent directory"); let source_id = engines.se().get_source_id(&path.clone()); - let module = match sway_parse::parse_file(handler, src.clone(), Some(source_id)) { - Ok(module) => module, - Err(e) => return Err(e), - }; + let module = sway_parse::parse_file(handler, src.clone(), Some(source_id))?; + // Parse all submodules before converting to the `ParseTree`. // This always recovers on parse errors for the file itself by skipping that file. let submodules = parse_submodules( @@ -760,9 +758,7 @@ pub fn compile_to_ast( check_should_abort(handler, retrigger_compilation.clone())?; let (lexed_program, mut parsed_program) = match parse_program_opt { - Ok(modules) => { - modules - }, + Ok(modules) => modules, Err(e) => { handler.dedup(); return Err(e); diff --git a/sway-core/src/query_engine/mod.rs b/sway-core/src/query_engine/mod.rs index a45bb09daf1..c05efb384f2 100644 --- a/sway-core/src/query_engine/mod.rs +++ b/sway-core/src/query_engine/mod.rs @@ -1,6 +1,10 @@ use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use std::{ - collections::HashMap, ops::{Deref, DerefMut}, path::PathBuf, sync::Arc, time::SystemTime + collections::HashMap, + ops::{Deref, DerefMut}, + path::PathBuf, + sync::Arc, + time::SystemTime, }; use sway_error::{error::CompileError, warning::CompileWarning}; use sway_types::IdentUnique; @@ -137,20 +141,17 @@ pub struct FunctionCacheEntry { #[derive(Debug, Default)] pub struct QueryEngine { // We want the below types wrapped in Arcs to optimize cloning from LSP. - //pub module_cache: Arc>, - // pub module_cache: RwLock, - pub module_cache: CowCache, programs_cache: Arc>, function_cache: Arc>, + pub module_cache: CowCache, } impl Clone for QueryEngine { fn clone(&self) -> Self { Self { - // module_cache: RwLock::new(self.module_cache.read().clone()), - module_cache: CowCache::new(self.module_cache.read().clone()), programs_cache: self.programs_cache.clone(), function_cache: self.function_cache.clone(), + module_cache: CowCache::new(self.module_cache.read().clone()), } } } @@ -206,7 +207,6 @@ impl QueryEngine { } } - /// Thread-safe, copy-on-write cache optimized for LSP operations. /// /// Addresses key LSP challenges: diff --git a/sway-core/src/semantic_analysis/module.rs b/sway-core/src/semantic_analysis/module.rs index 6ff57764ac0..d9fdaf6551e 100644 --- a/sway-core/src/semantic_analysis/module.rs +++ b/sway-core/src/semantic_analysis/module.rs @@ -323,7 +323,7 @@ impl ty::TyModule { } // Type-check submodules first in order of evaluation previously computed by the dependency graph. - let submodules_res: Result, _> = module_eval_order + let submodules_res = module_eval_order .iter() .map(|eval_mod_name| { let (name, submodule) = submodules @@ -359,7 +359,7 @@ impl ty::TyModule { Ok((name.clone(), type_checked_submodule)) } }) - .collect(); + .collect::, _>>(); // TODO: Ordering should be solved across all modules prior to the beginning of type-check. let ordered_nodes = node_dependencies::order_ast_nodes_by_dependency( diff --git a/sway-lsp/src/core/session.rs b/sway-lsp/src/core/session.rs index 6f0af04746a..edcda82ad8c 100644 --- a/sway-lsp/src/core/session.rs +++ b/sway-lsp/src/core/session.rs @@ -137,6 +137,7 @@ impl Session { Ok(()) } + /// Clean up memory in the [TypeEngine] and [DeclEngine] for the modified file. pub fn garbage_collect_module( &self, engines: &mut Engines, @@ -400,7 +401,7 @@ pub fn parse_project( let build_plan = session .build_plan_cache .get_or_update(&session.sync.manifest_path(), || build_plan(uri))?; - + let results = compile( &build_plan, engines, @@ -480,7 +481,7 @@ fn create_runnables( ) { let _p = tracing::trace_span!("create_runnables").entered(); // Insert runnable test functions. - + for (decl, _) in typed_program.test_fns(decl_engine) { // Get the span of the first attribute if it exists, otherwise use the span of the function name. let span = decl diff --git a/sway-lsp/src/server_state.rs b/sway-lsp/src/server_state.rs index 83e1c76721a..b50f546cf97 100644 --- a/sway-lsp/src/server_state.rs +++ b/sway-lsp/src/server_state.rs @@ -142,7 +142,6 @@ impl ServerState { { // Call this on the engines clone so we don't clear types that are still in use // and might be needed in the case cancel compilation was triggered. - // if let Err(err) = session.garbage_collect_program(&mut engines_clone) { if let Err(err) = session.garbage_collect_module(&mut engines_clone, &uri) { @@ -153,13 +152,11 @@ impl ServerState { } } } - + let lsp_mode = Some(LspConfig { optimized_build: ctx.optimized_build, file_versions: ctx.file_versions, }); - - // Set the is_compiling flag to true so that the wait_for_parsing function knows that we are compiling is_compiling.store(true, Ordering::SeqCst); match session::parse_project( @@ -342,13 +339,6 @@ impl ServerState { diagnostics_to_publish } - async fn init_session(&self, uri: &Url) -> Result<(), LanguageServerError> { - let session = Arc::new(Session::new()); - let project_name = session.init(uri, &self.documents).await?; - self.sessions.insert(project_name, session); - Ok(()) - } - /// Constructs and returns a tuple of `(Url, Arc)` from a given workspace URI. /// The returned URL represents the temp directory workspace. pub async fn uri_and_session_from_workspace( diff --git a/sway-lsp/tests/lib.rs b/sway-lsp/tests/lib.rs index 09fbe345721..20a57ae15e2 100644 --- a/sway-lsp/tests/lib.rs +++ b/sway-lsp/tests/lib.rs @@ -284,7 +284,6 @@ fn did_change_stress_test_random_wait() { // 4. try and do a hover or goto def for a random type // 5. repeat. - let times = 600000; for version in 0..times { let uri = if version != 0 { @@ -295,7 +294,7 @@ fn did_change_stress_test_random_wait() { } else { uri.clone() }; - + let params = lsp::simulate_keypress(&uri, version, &mut cursor_line); let _ = lsp::did_change_request(&mut service, &uri, version, Some(params)).await; if version == 0 { @@ -364,7 +363,6 @@ fn did_change_stress_test_random_wait() { }); } - #[test] fn did_change_stress_test_enter_uzi() { run_async!({ @@ -425,7 +423,7 @@ fn garbage_collection_runner(path: PathBuf) { .gc_frequency = 1; let uri = init_and_open(&mut service, path).await; let times = 60; - + // Initialize cursor position let mut cursor_line = 20; diff --git a/sway-lsp/tests/utils/src/lib.rs b/sway-lsp/tests/utils/src/lib.rs index e60363e6aa4..3124481ca06 100644 --- a/sway-lsp/tests/utils/src/lib.rs +++ b/sway-lsp/tests/utils/src/lib.rs @@ -142,7 +142,7 @@ pub async fn random_delay() { /// Sets up the environment and a custom panic hook to print panic information and exit the program. pub fn setup_panic_hook() { // Enable backtrace to get more information about panic - std::env::set_var("RUST_BACKTRACE", "FULL"); + std::env::set_var("RUST_BACKTRACE", "1"); // Take the default panic hook let default_panic = std::panic::take_hook(); From f5b63ffb046c4b4902c68255eda58ea7529daaa2 Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Thu, 1 Aug 2024 13:18:05 +1000 Subject: [PATCH 25/29] revert pkg update --- forc-pkg/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forc-pkg/Cargo.toml b/forc-pkg/Cargo.toml index 9460f4854f4..f722cff085b 100644 --- a/forc-pkg/Cargo.toml +++ b/forc-pkg/Cargo.toml @@ -21,7 +21,7 @@ git2 = { version = "0.19", features = [ "vendored-libgit2", "vendored-openssl", ] } -gix-url = { version = "0.27", features = ["serde"] } +gix-url = { version = "0.16.0", features = ["serde1"] } hex = "0.4.3" ipfs-api-backend-hyper = { version = "0.6", features = ["with-builder"] } petgraph = { version = "0.6", features = ["serde-1"] } From 2ffd22eb87acf593affe7b803f94ae6fa995d729 Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Thu, 1 Aug 2024 13:18:14 +1000 Subject: [PATCH 26/29] add Cargo.lock --- Cargo.lock | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d2d148f2849..6d292b8561f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1862,12 +1862,6 @@ dependencies = [ "regex", ] -[[package]] -name = "faster-hex" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2a2b11eda1d40935b26cf18f6833c526845ae8c41e58d09af6adeb6f0269183" - [[package]] name = "fastrand" version = "2.1.0" @@ -2982,49 +2976,39 @@ dependencies = [ [[package]] name = "gix-features" -version = "0.38.2" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac7045ac9fe5f9c727f38799d002a7ed3583cd777e3322a7c4b43e3cf437dc69" +checksum = "0b76f9a80f6dd7be66442ae86e1f534effad9546676a392acc95e269d0c21c22" dependencies = [ "gix-hash", - "gix-trace", "libc", ] [[package]] name = "gix-hash" -version = "0.14.2" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93d7df7366121b5018f947a04d37f034717e113dcf9ccd85c34b58e57a74d5e" +checksum = "2a258595457bc192d1f1c59d0d168a1e34e2be9b97a614e14995416185de41a7" dependencies = [ - "faster-hex", + "hex", "thiserror", ] [[package]] name = "gix-path" -version = "0.10.9" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d23d5bbda31344d8abc8de7c075b3cf26e5873feba7c4a15d916bce67382bd9" +checksum = "32370dce200bb951df013e03dff35b4233fc7a89458642b047629b91734a7e19" dependencies = [ "bstr", - "gix-trace", - "home", - "once_cell", "thiserror", ] -[[package]] -name = "gix-trace" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f924267408915fddcd558e3f37295cc7d6a3e50f8bd8b606cee0808c3915157e" - [[package]] name = "gix-url" -version = "0.27.4" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2eb9b35bba92ea8f0b5ab406fad3cf6b87f7929aa677ff10aa042c6da621156" +checksum = "b6a22b4b32ad14d68f7b7fb6458fa58d44b01797d94c1b8f4db2d9c7b3c366b5" dependencies = [ "bstr", "gix-features", From d4bff74ad5a674a29dbd681d0d3a0019eef5a2e0 Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Thu, 1 Aug 2024 13:19:49 +1000 Subject: [PATCH 27/29] rebase master From f70f8ea3844d31eb1df76c278507ee25fa36076b Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Thu, 1 Aug 2024 13:31:03 +1000 Subject: [PATCH 28/29] add Cargo.lock --- Cargo.lock | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6d292b8561f..d2d148f2849 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1862,6 +1862,12 @@ dependencies = [ "regex", ] +[[package]] +name = "faster-hex" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2a2b11eda1d40935b26cf18f6833c526845ae8c41e58d09af6adeb6f0269183" + [[package]] name = "fastrand" version = "2.1.0" @@ -2976,39 +2982,49 @@ dependencies = [ [[package]] name = "gix-features" -version = "0.28.1" +version = "0.38.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b76f9a80f6dd7be66442ae86e1f534effad9546676a392acc95e269d0c21c22" +checksum = "ac7045ac9fe5f9c727f38799d002a7ed3583cd777e3322a7c4b43e3cf437dc69" dependencies = [ "gix-hash", + "gix-trace", "libc", ] [[package]] name = "gix-hash" -version = "0.10.4" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a258595457bc192d1f1c59d0d168a1e34e2be9b97a614e14995416185de41a7" +checksum = "f93d7df7366121b5018f947a04d37f034717e113dcf9ccd85c34b58e57a74d5e" dependencies = [ - "hex", + "faster-hex", "thiserror", ] [[package]] name = "gix-path" -version = "0.7.3" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32370dce200bb951df013e03dff35b4233fc7a89458642b047629b91734a7e19" +checksum = "8d23d5bbda31344d8abc8de7c075b3cf26e5873feba7c4a15d916bce67382bd9" dependencies = [ "bstr", + "gix-trace", + "home", + "once_cell", "thiserror", ] +[[package]] +name = "gix-trace" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f924267408915fddcd558e3f37295cc7d6a3e50f8bd8b606cee0808c3915157e" + [[package]] name = "gix-url" -version = "0.16.0" +version = "0.27.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6a22b4b32ad14d68f7b7fb6458fa58d44b01797d94c1b8f4db2d9c7b3c366b5" +checksum = "e2eb9b35bba92ea8f0b5ab406fad3cf6b87f7929aa677ff10aa042c6da621156" dependencies = [ "bstr", "gix-features", From bc06646fdff82879b4e4964514109f712d67fc39 Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Thu, 1 Aug 2024 13:31:12 +1000 Subject: [PATCH 29/29] clean up lsp tests --- forc-pkg/Cargo.toml | 2 +- sway-lsp/tests/lib.rs | 169 ++---------------------------------------- 2 files changed, 7 insertions(+), 164 deletions(-) diff --git a/forc-pkg/Cargo.toml b/forc-pkg/Cargo.toml index f722cff085b..9460f4854f4 100644 --- a/forc-pkg/Cargo.toml +++ b/forc-pkg/Cargo.toml @@ -21,7 +21,7 @@ git2 = { version = "0.19", features = [ "vendored-libgit2", "vendored-openssl", ] } -gix-url = { version = "0.16.0", features = ["serde1"] } +gix-url = { version = "0.27", features = ["serde"] } hex = "0.4.3" ipfs-api-backend-hyper = { version = "0.6", features = ["with-builder"] } petgraph = { version = "0.6", features = ["serde-1"] } diff --git a/sway-lsp/tests/lib.rs b/sway-lsp/tests/lib.rs index 20a57ae15e2..5c0d38a7f43 100644 --- a/sway-lsp/tests/lib.rs +++ b/sway-lsp/tests/lib.rs @@ -2,7 +2,6 @@ pub mod integration; use crate::integration::{code_actions, lsp}; use lsp_types::*; -use rand::Rng; use std::{fs, path::PathBuf}; use sway_lsp::{ config::LspClient, @@ -163,28 +162,6 @@ fn did_change() { }); } -#[test] -fn did_open_fluid_libraries() { - run_async!({ - let now = std::time::Instant::now(); - let (mut service, _) = LspService::build(ServerState::new) - .custom_method("sway/metrics", ServerState::metrics) - .finish(); - let uri = init_and_open( - &mut service, - PathBuf::from("/Users/josh/Documents/rust/fuel/user_projects/fluid-protocol/libraries") - .join("src/interface.sw"), - ) - .await; - - eprintln!("\n ๐Ÿช†๐Ÿช†๐Ÿช†๐Ÿช† Initial compilation complete, starting recompilation ๐Ÿช†๐Ÿช†๐Ÿช†๐Ÿช† \n"); - let _ = lsp::did_change_request(&mut service, &uri, 1, None).await; - service.inner().wait_for_parsing().await; - eprintln!("Elapsed time: {:?}", now.elapsed()); - shutdown_and_exit(&mut service).await; - }); -} - #[test] fn did_cache_test() { run_async!({ @@ -234,73 +211,24 @@ fn did_change_stress_test() { #[test] fn did_change_stress_test_random_wait() { run_async!({ - let test_duration = tokio::time::Duration::from_secs(250 * 60); // 5 minutes timeout + let test_duration = tokio::time::Duration::from_secs(5 * 60); // 5 minutes timeout let test_future = async { setup_panic_hook(); let (mut service, _) = LspService::new(ServerState::new); - // let example_dir = sway_workspace_dir() - // .join(e2e_language_dir()) - // .join("generics_in_contract"); - // let uri = init_and_open(&mut service, example_dir.join("src/main.sw")).await; - - let uri = init_and_open( - &mut service, - PathBuf::from( - "/Users/josh/Documents/rust/fuel/user_projects/fluid-protocol/libraries", - ) - .join("src/fpt_staking_interface.sw"), - ) - .await; - - let mut uris = vec![]; - uris.push(Url::from_file_path(PathBuf::from( - "/Users/josh/Documents/rust/fuel/user_projects/fluid-protocol/libraries/src/token_interface.sw", - )).unwrap()); - uris.push(Url::from_file_path(PathBuf::from( - "/Users/josh/Documents/rust/fuel/user_projects/fluid-protocol/libraries/src/active_pool_interface.sw", - )).unwrap()); - uris.push(Url::from_file_path(PathBuf::from( - "/Users/josh/Documents/rust/fuel/user_projects/fluid-protocol/libraries/src/trove_manager_interface.sw", - )).unwrap()); - uris.push(Url::from_file_path(PathBuf::from( - "/Users/josh/Documents/rust/fuel/user_projects/fluid-protocol/libraries/src/fpt_staking_interface.sw", - )).unwrap()); + let example_dir = sway_workspace_dir() + .join(e2e_language_dir()) + .join("generics_in_contract"); + let uri = init_and_open(&mut service, example_dir.join("src/main.sw")).await; + let times = 60; // Initialize cursor position let mut cursor_line = 29; - - // let uri = init_and_open( - // &mut service, - // PathBuf::from( - // "/Users/josh/Documents/rust/fuel/user_projects/fluid-protocol/contracts/multi-trove-getter-contract", - // ) - // .join("src/main.sw"), - // ) - // .await; - - // 1. randomise the file that is changed out of all the files in the project. - // 2. change the file - // 3. wait for the file to be parsed - // 4. try and do a hover or goto def for a random type - // 5. repeat. - - let times = 600000; for version in 0..times { - let uri = if version != 0 { - cursor_line = 2; - // random number between 0 and the number of uris - let rand = rand::thread_rng().gen_range(0..uris.len()); - uris[rand].clone() - } else { - uri.clone() - }; - let params = lsp::simulate_keypress(&uri, version, &mut cursor_line); let _ = lsp::did_change_request(&mut service, &uri, version, Some(params)).await; if version == 0 { service.inner().wait_for_parsing().await; } - // wait for a random amount of time between 1-30ms tokio::time::sleep(tokio::time::Duration::from_millis( rand::random::() % 30 + 1, @@ -313,89 +241,6 @@ fn did_change_stress_test_random_wait() { )) .await; } - - // if rand::random::() % 10 < 1 { - // tokio::time::sleep(tokio::time::Duration::from_millis( - // rand::random::() % 2000, - // )) - // .await; - - // match service.inner() - // .uri_and_session_from_workspace(&uri) - // .await - // { - // Ok((uri, _)) => { - // // Here we will do a hover request to simulate the user hovering over a type - // let hover = HoverDocumentation { - // req_uri: &uri, - // req_line: cursor_line + 2, - // req_char: 29, - // documentation: vec!["```sway\npub struct ReadStorage\n```\n---"], - // }; - // service.inner().wait_for_parsing().await; - // eprintln!("๐Ÿ“ž Hover request ๐Ÿ“ž | cursor_line: {}", cursor_line); - // if let Ok(text_document) = service.inner().documents.get_text_document(&uri) { - // println!("Document content:"); - // for (index, line) in text_document.get_text().lines().enumerate() { - // println!("{:4}: {}", index + 1, line); - // } - // } else { - // eprintln!("Failed to get text document for URI: {}", uri); - // } - // let _ = lsp::hover_request(&mut service.inner(), &hover).await; - // } - // Err(err) => { - // tracing::error!("{}", err.to_string()); - // } - // } - // } - } - shutdown_and_exit(&mut service).await; - }; - if tokio::time::timeout(test_duration, test_future) - .await - .is_err() - { - panic!( - "did_change_stress_test_random_wait did not complete within the timeout period." - ); - } - }); -} - -#[test] -fn did_change_stress_test_enter_uzi() { - run_async!({ - let test_duration = tokio::time::Duration::from_secs(250 * 60); // 5 minutes timeout - let test_future = async { - setup_panic_hook(); - let (mut service, _) = LspService::new(ServerState::new); - let uri = init_and_open( - &mut service, - PathBuf::from( - "/Users/josh/Documents/rust/fuel/user_projects/fluid-protocol/contracts/multi-trove-getter-contract", - ) - .join("src/main.sw"), - ) - .await; - - let times = 6000; - for version in 0..times { - //eprintln!("version: {}", version); - let _ = lsp::did_change_request(&mut service, &uri, version + 1, None).await; - if version == 0 { - service.inner().wait_for_parsing().await; - } - // wait for a random amount of time between 1s - tokio::time::sleep(tokio::time::Duration::from_millis(2)).await; - - // there is a 10% chance that a longer 100-800ms wait will be added - if rand::random::() % 100 < 2 { - tokio::time::sleep(tokio::time::Duration::from_millis( - rand::random::() % 100, - )) - .await; - } } shutdown_and_exit(&mut service).await; }; @@ -434,8 +279,6 @@ fn garbage_collection_runner(path: PathBuf) { if version == 0 { service.inner().wait_for_parsing().await; } - // Print current cursor position - eprintln!("Cursor position: line {}", cursor_line); // wait for a random amount of time to simulate typing random_delay().await; }