From 8011eced141e7bf0e1eac334daf326bd49504748 Mon Sep 17 00:00:00 2001 From: Kitson Kelly Date: Wed, 30 Dec 2020 15:17:17 +1100 Subject: [PATCH] feat(lsp): add cache command (#8911) --- cli/file_fetcher.rs | 14 +++--- cli/lsp/diagnostics.rs | 4 ++ cli/lsp/language_server.rs | 97 ++++++++++++++++++++++++++----------- cli/lsp/sources.rs | 20 ++++++++ cli/main.rs | 8 +-- cli/module_graph.rs | 87 +++++++++++++++------------------ cli/ops/runtime_compiler.rs | 11 +++-- cli/program_state.rs | 4 +- cli/specifier_handler.rs | 7 +-- cli/tsc.rs | 24 ++++----- 10 files changed, 165 insertions(+), 111 deletions(-) diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs index 86c0ac9663..52e9cc8007 100644 --- a/cli/file_fetcher.rs +++ b/cli/file_fetcher.rs @@ -364,25 +364,25 @@ impl FileFetcher { specifier: &ModuleSpecifier, permissions: &Permissions, redirect_limit: i64, - ) -> Pin>>> { + ) -> Pin> + Send>> { debug!("FileFetcher::fetch_remote() - specifier: {}", specifier); if redirect_limit < 0 { return futures::future::err(custom_error("Http", "Too many redirects.")) - .boxed_local(); + .boxed(); } if let Err(err) = permissions.check_specifier(specifier) { - return futures::future::err(err).boxed_local(); + return futures::future::err(err).boxed(); } if self.cache_setting.should_use(specifier) { match self.fetch_cached(specifier, redirect_limit) { Ok(Some(file)) => { - return futures::future::ok(file).boxed_local(); + return futures::future::ok(file).boxed(); } Ok(None) => {} Err(err) => { - return futures::future::err(err).boxed_local(); + return futures::future::err(err).boxed(); } } } @@ -395,7 +395,7 @@ impl FileFetcher { specifier ), )) - .boxed_local(); + .boxed(); } info!("{} {}", colors::green("Download"), specifier); @@ -436,7 +436,7 @@ impl FileFetcher { } } } - .boxed_local() + .boxed() } /// Fetch a source file and asynchronously return it. diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs index ac938d0630..e7be61cdc6 100644 --- a/cli/lsp/diagnostics.rs +++ b/cli/lsp/diagnostics.rs @@ -60,6 +60,10 @@ impl DiagnosticCollection { self.versions.get(file_id).cloned() } + pub fn invalidate(&mut self, file_id: &FileId) { + self.versions.remove(file_id); + } + pub fn take_changes(&mut self) -> Option> { if self.changes.is_empty() { return None; diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index e70c0198dc..08ece2b629 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -9,9 +9,8 @@ use deno_core::serde_json::json; use deno_core::serde_json::Value; use deno_core::ModuleSpecifier; use dprint_plugin_typescript as dprint; -use lspower::jsonrpc::Error as LSPError; -use lspower::jsonrpc::ErrorCode as LSPErrorCode; -use lspower::jsonrpc::Result as LSPResult; +use lspower::jsonrpc::Error as LspError; +use lspower::jsonrpc::Result as LspResult; use lspower::lsp_types::*; use lspower::Client; use std::collections::HashMap; @@ -33,6 +32,7 @@ use super::diagnostics; use super::diagnostics::DiagnosticCollection; use super::diagnostics::DiagnosticSource; use super::memory_cache::MemoryCache; +use super::sources; use super::sources::Sources; use super::text; use super::text::apply_content_changes; @@ -361,7 +361,7 @@ impl lspower::LanguageServer for LanguageServer { async fn initialize( &self, params: InitializeParams, - ) -> LSPResult { + ) -> LspResult { info!("Starting Deno language server..."); let capabilities = capabilities::server_capabilities(¶ms.capabilities); @@ -439,7 +439,7 @@ impl lspower::LanguageServer for LanguageServer { info!("Server ready."); } - async fn shutdown(&self) -> LSPResult<()> { + async fn shutdown(&self) -> LspResult<()> { Ok(()) } @@ -586,7 +586,7 @@ impl lspower::LanguageServer for LanguageServer { async fn formatting( &self, params: DocumentFormattingParams, - ) -> LSPResult>> { + ) -> LspResult>> { let specifier = utils::normalize_url(params.text_document.uri.clone()); let file_text = { let file_cache = self.file_cache.read().unwrap(); @@ -631,7 +631,7 @@ impl lspower::LanguageServer for LanguageServer { } } - async fn hover(&self, params: HoverParams) -> LSPResult> { + async fn hover(&self, params: HoverParams) -> LspResult> { if !self.enabled() { return Ok(None); } @@ -662,7 +662,7 @@ impl lspower::LanguageServer for LanguageServer { async fn document_highlight( &self, params: DocumentHighlightParams, - ) -> LSPResult>> { + ) -> LspResult>> { if !self.enabled() { return Ok(None); } @@ -702,7 +702,7 @@ impl lspower::LanguageServer for LanguageServer { async fn references( &self, params: ReferenceParams, - ) -> LSPResult>> { + ) -> LspResult>> { if !self.enabled() { return Ok(None); } @@ -743,7 +743,7 @@ impl lspower::LanguageServer for LanguageServer { async fn goto_definition( &self, params: GotoDefinitionParams, - ) -> LSPResult> { + ) -> LspResult> { if !self.enabled() { return Ok(None); } @@ -779,7 +779,7 @@ impl lspower::LanguageServer for LanguageServer { async fn completion( &self, params: CompletionParams, - ) -> LSPResult> { + ) -> LspResult> { if !self.enabled() { return Ok(None); } @@ -812,7 +812,7 @@ impl lspower::LanguageServer for LanguageServer { async fn rename( &self, params: RenameParams, - ) -> LSPResult> { + ) -> LspResult> { if !self.enabled() { return Ok(None); } @@ -827,7 +827,7 @@ impl lspower::LanguageServer for LanguageServer { .await .map_err(|err| { error!("Failed to get line_index {:#?}", err); - LSPError::internal_error() + LspError::internal_error() })?; let req = tsc::RequestMethod::FindRenameLocations(( @@ -844,7 +844,7 @@ impl lspower::LanguageServer for LanguageServer { .await .map_err(|err| { error!("Failed to request to tsserver {:#?}", err); - LSPError::invalid_request() + LspError::invalid_request() })?; let maybe_locations = serde_json::from_value::< @@ -855,7 +855,7 @@ impl lspower::LanguageServer for LanguageServer { "Failed to deserialize tsserver response to Vec {:#?}", err ); - LSPError::internal_error() + LspError::internal_error() })?; match maybe_locations { @@ -873,7 +873,7 @@ impl lspower::LanguageServer for LanguageServer { "Failed to convert tsc::RenameLocations to WorkspaceEdit {:#?}", err ); - LSPError::internal_error() + LspError::internal_error() })?; Ok(Some(workpace_edits)) } @@ -885,8 +885,18 @@ impl lspower::LanguageServer for LanguageServer { &self, method: &str, params: Option, - ) -> LSPResult> { + ) -> LspResult> { match method { + "deno/cache" => match params.map(serde_json::from_value) { + Some(Ok(params)) => Ok(Some( + serde_json::to_value(self.cache(params).await?).map_err(|err| { + error!("Failed to serialize cache response: {:#?}", err); + LspError::internal_error() + })?, + )), + Some(Err(err)) => Err(LspError::invalid_params(err.to_string())), + None => Err(LspError::invalid_params("Missing parameters")), + }, "deno/virtualTextDocument" => match params.map(serde_json::from_value) { Some(Ok(params)) => Ok(Some( serde_json::to_value(self.virtual_text_document(params).await?) @@ -895,25 +905,60 @@ impl lspower::LanguageServer for LanguageServer { "Failed to serialize virtual_text_document response: {:#?}", err ); - LSPError::internal_error() + LspError::internal_error() })?, )), - Some(Err(err)) => Err(LSPError::invalid_params(err.to_string())), - None => Err(LSPError::invalid_params("Missing parameters")), + Some(Err(err)) => Err(LspError::invalid_params(err.to_string())), + None => Err(LspError::invalid_params("Missing parameters")), }, _ => { error!("Got a {} request, but no handler is defined", method); - Err(LSPError::method_not_found()) + Err(LspError::method_not_found()) } } } } +#[derive(Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CacheParams { + pub text_document: TextDocumentIdentifier, +} + +#[derive(Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct VirtualTextDocumentParams { + pub text_document: TextDocumentIdentifier, +} + impl LanguageServer { + async fn cache(&self, params: CacheParams) -> LspResult { + let specifier = utils::normalize_url(params.text_document.uri); + let maybe_import_map = self.maybe_import_map.read().unwrap().clone(); + sources::cache(specifier.clone(), maybe_import_map) + .await + .map_err(|err| { + error!("{}", err); + LspError::internal_error() + })?; + { + let file_cache = self.file_cache.read().unwrap(); + if let Some(file_id) = file_cache.lookup(&specifier) { + let mut diagnostics_collection = self.diagnostics.write().unwrap(); + diagnostics_collection.invalidate(&file_id); + } + } + self.prepare_diagnostics().await.map_err(|err| { + error!("{}", err); + LspError::internal_error() + })?; + Ok(true) + } + async fn virtual_text_document( &self, params: VirtualTextDocumentParams, - ) -> LSPResult> { + ) -> LspResult> { let specifier = utils::normalize_url(params.text_document.uri); let url = specifier.as_url(); let contents = if url.as_str() == "deno:/status.md" { @@ -933,7 +978,7 @@ impl LanguageServer { if let Some(text) = tsc::get_asset(&specifier, &self.ts_server, &state_snapshot) .await - .map_err(|_| LSPError::new(LSPErrorCode::InternalError))? + .map_err(|_| LspError::internal_error())? { Some(text) } else { @@ -1009,12 +1054,6 @@ impl DocumentData { } } -#[derive(Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct VirtualTextDocumentParams { - pub text_document: TextDocumentIdentifier, -} - #[cfg(test)] mod tests { use super::*; diff --git a/cli/lsp/sources.rs b/cli/lsp/sources.rs index c6ab87f218..5ef16a876f 100644 --- a/cli/lsp/sources.rs +++ b/cli/lsp/sources.rs @@ -10,16 +10,36 @@ use crate::http_cache; use crate::http_cache::HttpCache; use crate::import_map::ImportMap; use crate::media_type::MediaType; +use crate::module_graph::GraphBuilder; +use crate::program_state::ProgramState; +use crate::specifier_handler::FetchHandler; use crate::text_encoding; +use crate::Permissions; +use deno_core::error::AnyError; use deno_core::serde_json; use deno_core::ModuleSpecifier; use std::collections::HashMap; use std::fs; use std::path::Path; use std::path::PathBuf; +use std::sync::Arc; +use std::sync::Mutex; use std::time::SystemTime; +pub async fn cache( + specifier: ModuleSpecifier, + maybe_import_map: Option, +) -> Result<(), AnyError> { + let program_state = Arc::new(ProgramState::new(Default::default())?); + let handler = Arc::new(Mutex::new(FetchHandler::new( + &program_state, + Permissions::allow_all(), + )?)); + let mut builder = GraphBuilder::new(handler, maybe_import_map, None); + builder.add(&specifier, false).await +} + #[derive(Debug, Clone, Default)] struct Metadata { dependencies: Option>, diff --git a/cli/main.rs b/cli/main.rs index 578ea6846b..ac0d2f5915 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -76,7 +76,6 @@ use deno_runtime::worker::MainWorker; use deno_runtime::worker::WorkerOptions; use log::Level; use log::LevelFilter; -use std::cell::RefCell; use std::env; use std::io::Read; use std::io::Write; @@ -85,6 +84,7 @@ use std::path::PathBuf; use std::pin::Pin; use std::rc::Rc; use std::sync::Arc; +use std::sync::Mutex; fn create_web_worker_callback( program_state: Arc, @@ -349,7 +349,7 @@ async fn info_command( let program_state = ProgramState::new(flags)?; if let Some(specifier) = maybe_specifier { let specifier = ModuleSpecifier::resolve_url_or_path(&specifier)?; - let handler = Rc::new(RefCell::new(specifier_handler::FetchHandler::new( + let handler = Arc::new(Mutex::new(specifier_handler::FetchHandler::new( &program_state, // info accesses dynamically imported modules just for their information // so we allow access to all of them. @@ -497,7 +497,7 @@ async fn create_module_graph_and_maybe_check( program_state: Arc, debug: bool, ) -> Result { - let handler = Rc::new(RefCell::new(FetchHandler::new( + let handler = Arc::new(Mutex::new(FetchHandler::new( &program_state, // when bundling, dynamic imports are only access for their type safety, // therefore we will allow the graph to access any module. @@ -850,7 +850,7 @@ async fn run_with_watch(flags: Flags, script: String) -> Result<(), AnyError> { async move { let main_module = ModuleSpecifier::resolve_url_or_path(&script1)?; let program_state = ProgramState::new(flags)?; - let handler = Rc::new(RefCell::new(FetchHandler::new( + let handler = Arc::new(Mutex::new(FetchHandler::new( &program_state, Permissions::allow_all(), )?)); diff --git a/cli/module_graph.rs b/cli/module_graph.rs index f5e08882ed..be0f428b4e 100644 --- a/cli/module_graph.rs +++ b/cli/module_graph.rs @@ -43,7 +43,6 @@ use deno_core::ModuleResolutionError; use deno_core::ModuleSource; use deno_core::ModuleSpecifier; use regex::Regex; -use std::cell::RefCell; use std::collections::HashSet; use std::collections::{BTreeSet, HashMap}; use std::error::Error; @@ -239,8 +238,7 @@ pub struct Module { is_parsed: bool, maybe_emit: Option, maybe_emit_path: Option<(PathBuf, Option)>, - maybe_import_map: Option>>, - maybe_parsed_module: Option, + maybe_import_map: Option>>, maybe_types: Option<(String, ModuleSpecifier)>, maybe_version: Option, media_type: MediaType, @@ -258,7 +256,6 @@ impl Default for Module { maybe_emit: None, maybe_emit_path: None, maybe_import_map: None, - maybe_parsed_module: None, maybe_types: None, maybe_version: None, media_type: MediaType::Unknown, @@ -273,7 +270,7 @@ impl Module { pub fn new( cached_module: CachedModule, is_root: bool, - maybe_import_map: Option>>, + maybe_import_map: Option>>, ) -> Self { // If this is a local root file, and its media type is unknown, set the // media type to JavaScript. This allows easier ability to create "shell" @@ -330,7 +327,7 @@ impl Module { /// Parse a module, populating the structure with data retrieved from the /// source of the module. - pub fn parse(&mut self) -> Result<(), AnyError> { + pub fn parse(&mut self) -> Result { let parsed_module = parse(self.specifier.as_str(), &self.source, &self.media_type)?; @@ -430,9 +427,7 @@ impl Module { dep.maybe_type = maybe_type; } } - - self.maybe_parsed_module = Some(parsed_module); - Ok(()) + Ok(parsed_module) } fn resolve_import( @@ -443,7 +438,8 @@ impl Module { let maybe_resolve = if let Some(import_map) = self.maybe_import_map.clone() { import_map - .borrow() + .lock() + .unwrap() .resolve(specifier, self.specifier.as_str())? } else { None @@ -650,7 +646,7 @@ pub struct TranspileOptions { #[derive(Debug, Clone)] enum ModuleSlot { /// The module fetch resulted in a non-recoverable error. - Err(Rc), + Err(Arc), /// The the fetch resulted in a module. Module(Box), /// Used to denote a module that isn't part of the graph. @@ -666,7 +662,7 @@ enum ModuleSlot { pub struct Graph { /// A reference to the specifier handler that will retrieve and cache modules /// for the graph. - handler: Rc>, + handler: Arc>, /// Optional TypeScript build info that will be passed to `tsc` if `tsc` is /// invoked. maybe_tsbuildinfo: Option, @@ -734,7 +730,7 @@ impl Graph { /// `SpecifierHandler` trait. /// pub fn new( - handler: Rc>, + handler: Arc>, maybe_lockfile: Option>>, ) -> Self { Graph { @@ -844,7 +840,7 @@ impl Graph { let maybe_tsbuildinfo = self.maybe_tsbuildinfo.clone(); let hash_data = vec![config.as_bytes(), version::deno().as_bytes().to_owned()]; - let graph = Rc::new(RefCell::new(self)); + let graph = Arc::new(Mutex::new(self)); let response = tsc::exec( js::compiler_isolate_init(), @@ -858,7 +854,7 @@ impl Graph { }, )?; - let mut graph = graph.borrow_mut(); + let mut graph = graph.lock().unwrap(); graph.maybe_tsbuildinfo = response.maybe_tsbuildinfo; // Only process changes to the graph if there are no diagnostics and there // were files emitted. @@ -964,7 +960,7 @@ impl Graph { let root_names = self.get_root_names(!config.get_check_js()); let hash_data = vec![config.as_bytes(), version::deno().as_bytes().to_owned()]; - let graph = Rc::new(RefCell::new(self)); + let graph = Arc::new(Mutex::new(self)); let response = tsc::exec( js::compiler_isolate_init(), @@ -979,7 +975,7 @@ impl Graph { )?; let mut emitted_files = HashMap::new(); - let graph = graph.borrow(); + let graph = graph.lock().unwrap(); match options.bundle_type { BundleType::Esm => { assert!( @@ -1081,7 +1077,7 @@ impl Graph { /// Update the handler with any modules that are marked as _dirty_ and update /// any build info if present. fn flush(&mut self) -> Result<(), AnyError> { - let mut handler = self.handler.borrow_mut(); + let mut handler = self.handler.lock().unwrap(); for (_, module_slot) in self.modules.iter_mut() { if let ModuleSlot::Module(module) = module_slot { if module.is_dirty { @@ -1595,10 +1591,7 @@ impl Graph { if !options.reload && module.is_emit_valid(&config) { continue; } - if module.maybe_parsed_module.is_none() { - module.parse()?; - } - let parsed_module = module.maybe_parsed_module.clone().unwrap(); + let parsed_module = module.parse()?; let emit = parsed_module.transpile(&emit_options)?; emit_count += 1; module.maybe_emit = Some(Emit::Cli(emit)); @@ -1647,18 +1640,18 @@ impl swc_bundler::Resolve for Graph { /// A structure for building a dependency graph of modules. pub struct GraphBuilder { graph: Graph, - maybe_import_map: Option>>, + maybe_import_map: Option>>, pending: FuturesUnordered, } impl GraphBuilder { pub fn new( - handler: Rc>, + handler: Arc>, maybe_import_map: Option, maybe_lockfile: Option>>, ) -> Self { let internal_import_map = if let Some(import_map) = maybe_import_map { - Some(Rc::new(RefCell::new(import_map))) + Some(Arc::new(Mutex::new(import_map))) } else { None }; @@ -1685,7 +1678,7 @@ impl GraphBuilder { self .graph .modules - .insert(specifier, ModuleSlot::Err(Rc::new(err))); + .insert(specifier, ModuleSlot::Err(Arc::new(err))); } Some(Ok(cached_module)) => { let is_root = &cached_module.specifier == specifier; @@ -1702,7 +1695,7 @@ impl GraphBuilder { self.graph.roots.push(specifier.clone()); self.graph.roots_dynamic = self.graph.roots_dynamic && is_dynamic; if self.graph.maybe_tsbuildinfo.is_none() { - let handler = self.graph.handler.borrow(); + let handler = self.graph.handler.lock().unwrap(); self.graph.maybe_tsbuildinfo = handler.get_tsbuildinfo(specifier)?; } } @@ -1723,11 +1716,9 @@ impl GraphBuilder { .graph .modules .insert(specifier.clone(), ModuleSlot::Pending); - let future = self.graph.handler.borrow_mut().fetch( - specifier.clone(), - maybe_referrer.clone(), - is_dynamic, - ); + let mut handler = self.graph.handler.lock().unwrap(); + let future = + handler.fetch(specifier.clone(), maybe_referrer.clone(), is_dynamic); self.pending.push(future); } } @@ -1763,7 +1754,7 @@ impl GraphBuilder { let has_types = module.maybe_types.is_some(); module.parse()?; if self.maybe_import_map.is_none() { - let mut handler = self.graph.handler.borrow_mut(); + let mut handler = self.graph.handler.lock().unwrap(); handler.set_deps(&specifier, module.dependencies.clone())?; if !has_types { if let Some((types, _)) = module.maybe_types.clone() { @@ -1934,10 +1925,10 @@ pub mod tests { async fn setup( specifier: ModuleSpecifier, - ) -> (Graph, Rc>) { + ) -> (Graph, Arc>) { let c = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); let fixtures = c.join("tests/module_graph"); - let handler = Rc::new(RefCell::new(MockSpecifierHandler { + let handler = Arc::new(Mutex::new(MockSpecifierHandler { fixtures, ..MockSpecifierHandler::default() })); @@ -1958,7 +1949,7 @@ pub mod tests { .iter() .map(|(k, v)| (k.to_string(), v.to_string())) .collect(); - let handler = Rc::new(RefCell::new(MemoryHandler::new(sources))); + let handler = Arc::new(Mutex::new(MemoryHandler::new(sources))); let mut builder = GraphBuilder::new(handler.clone(), None, None); builder .add(&specifier, false) @@ -2064,7 +2055,7 @@ pub mod tests { for (specifier, expected_str) in tests { let specifier = ModuleSpecifier::resolve_url_or_path(specifier).unwrap(); - let handler = Rc::new(RefCell::new(MockSpecifierHandler { + let handler = Arc::new(Mutex::new(MockSpecifierHandler { fixtures: fixtures.clone(), ..MockSpecifierHandler::default() })); @@ -2103,7 +2094,7 @@ pub mod tests { assert!(result_info.maybe_ignored_options.is_none()); assert_eq!(result_info.stats.0.len(), 12); assert!(result_info.diagnostics.is_empty()); - let h = handler.borrow(); + let h = handler.lock().unwrap(); assert_eq!(h.cache_calls.len(), 2); assert_eq!(h.tsbuildinfo_calls.len(), 1); } @@ -2144,7 +2135,7 @@ pub mod tests { assert!(result_info.maybe_ignored_options.is_none()); assert_eq!(result_info.stats.0.len(), 12); assert!(!result_info.diagnostics.is_empty()); - let h = handler.borrow(); + let h = handler.lock().unwrap(); // we shouldn't cache any files or write out tsbuildinfo if there are // diagnostic errors assert_eq!(h.cache_calls.len(), 0); @@ -2169,7 +2160,7 @@ pub mod tests { assert!(result_info.maybe_ignored_options.is_none()); assert_eq!(result_info.stats.0.len(), 12); assert!(result_info.diagnostics.is_empty()); - let h = handler.borrow(); + let h = handler.lock().unwrap(); assert_eq!(h.cache_calls.len(), 0); assert_eq!(h.tsbuildinfo_calls.len(), 1); } @@ -2190,7 +2181,7 @@ pub mod tests { .expect("should have checked"); assert!(result_info.maybe_ignored_options.is_none()); assert!(result_info.diagnostics.is_empty()); - let h = handler.borrow(); + let h = handler.lock().unwrap(); assert_eq!(h.cache_calls.len(), 1); assert_eq!(h.tsbuildinfo_calls.len(), 1); } @@ -2231,7 +2222,7 @@ pub mod tests { .expect("should have checked"); assert!(result_info.maybe_ignored_options.is_none()); assert!(result_info.diagnostics.is_empty()); - let h = handler.borrow(); + let h = handler.lock().unwrap(); assert_eq!(h.version_calls.len(), 2); let ver0 = h.version_calls[0].1.clone(); let ver1 = h.version_calls[1].1.clone(); @@ -2251,7 +2242,7 @@ pub mod tests { .expect("should have checked"); assert!(result_info.maybe_ignored_options.is_none()); assert!(result_info.diagnostics.is_empty()); - let h = handler.borrow(); + let h = handler.lock().unwrap(); assert_eq!(h.version_calls.len(), 2); assert!(h.version_calls[0].1 == ver0 || h.version_calls[0].1 == ver1); assert!(h.version_calls[1].1 == ver0 || h.version_calls[1].1 == ver1); @@ -2403,7 +2394,7 @@ pub mod tests { .expect("could not resolve module"); let c = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); let fixtures = c.join("tests/module_graph"); - let handler = Rc::new(RefCell::new(MockSpecifierHandler { + let handler = Arc::new(Mutex::new(MockSpecifierHandler { fixtures, ..MockSpecifierHandler::default() })); @@ -2430,7 +2421,7 @@ pub mod tests { let result_info = graph.transpile(TranspileOptions::default()).unwrap(); assert_eq!(result_info.stats.0.len(), 3); assert_eq!(result_info.maybe_ignored_options, None); - let h = handler.borrow(); + let h = handler.lock().unwrap(); assert_eq!(h.cache_calls.len(), 2); match &h.cache_calls[0].1 { Emit::Cli((code, maybe_map)) => { @@ -2492,7 +2483,7 @@ pub mod tests { vec!["target".to_string()], "the 'target' options should have been ignored" ); - let h = handler.borrow(); + let h = handler.lock().unwrap(); assert_eq!(h.cache_calls.len(), 1, "only one file should be emitted"); // FIXME(bartlomieju): had to add space in `
`, probably a quirk in swc_ecma_codegen match &h.cache_calls[0].1 { @@ -2521,7 +2512,7 @@ pub mod tests { ) .expect("could not parse import map"), ); - let handler = Rc::new(RefCell::new(MockSpecifierHandler { + let handler = Arc::new(Mutex::new(MockSpecifierHandler { fixtures, ..Default::default() })); @@ -2541,7 +2532,7 @@ pub mod tests { let lockfile = Lockfile::new(lockfile_path, false).expect("could not load lockfile"); let maybe_lockfile = Some(Arc::new(Mutex::new(lockfile))); - let handler = Rc::new(RefCell::new(MockSpecifierHandler { + let handler = Arc::new(Mutex::new(MockSpecifierHandler { fixtures, ..MockSpecifierHandler::default() })); diff --git a/cli/ops/runtime_compiler.rs b/cli/ops/runtime_compiler.rs index ec9806e60c..bb3e47226a 100644 --- a/cli/ops/runtime_compiler.rs +++ b/cli/ops/runtime_compiler.rs @@ -11,8 +11,6 @@ use crate::specifier_handler::FetchHandler; use crate::specifier_handler::MemoryHandler; use crate::specifier_handler::SpecifierHandler; use crate::tsc_config; -use deno_runtime::permissions::Permissions; -use std::sync::Arc; use deno_core::error::AnyError; use deno_core::error::Context; @@ -23,10 +21,13 @@ use deno_core::serde_json::Value; use deno_core::BufVec; use deno_core::ModuleSpecifier; use deno_core::OpState; +use deno_runtime::permissions::Permissions; use serde::Deserialize; use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; +use std::sync::Arc; +use std::sync::Mutex; pub fn init(rt: &mut deno_core::JsRuntime) { super::reg_json_async(rt, "op_compile", op_compile); @@ -58,11 +59,11 @@ async fn op_compile( let state = state.borrow(); state.borrow::().clone() }; - let handler: Rc> = + let handler: Arc> = if let Some(sources) = args.sources { - Rc::new(RefCell::new(MemoryHandler::new(sources))) + Arc::new(Mutex::new(MemoryHandler::new(sources))) } else { - Rc::new(RefCell::new(FetchHandler::new( + Arc::new(Mutex::new(FetchHandler::new( &program_state, runtime_permissions, )?)) diff --git a/cli/program_state.rs b/cli/program_state.rs index afae8c1255..e55386fe58 100644 --- a/cli/program_state.rs +++ b/cli/program_state.rs @@ -23,10 +23,8 @@ use deno_core::error::AnyError; use deno_core::url::Url; use deno_core::ModuleSource; use deno_core::ModuleSpecifier; -use std::cell::RefCell; use std::collections::HashMap; use std::env; -use std::rc::Rc; use std::sync::Arc; use std::sync::Mutex; @@ -144,7 +142,7 @@ impl ProgramState { runtime_permissions.check_specifier(&specifier)?; } let handler = - Rc::new(RefCell::new(FetchHandler::new(self, runtime_permissions)?)); + Arc::new(Mutex::new(FetchHandler::new(self, runtime_permissions)?)); let mut builder = GraphBuilder::new(handler, maybe_import_map, self.lockfile.clone()); builder.add(&specifier, is_dynamic).await?; diff --git a/cli/specifier_handler.rs b/cli/specifier_handler.rs index 7523164675..25ee915f93 100644 --- a/cli/specifier_handler.rs +++ b/cli/specifier_handler.rs @@ -28,7 +28,8 @@ pub type DependencyMap = HashMap; pub type FetchFuture = Pin< Box< (dyn Future> - + 'static), + + 'static + + Send), >, >; @@ -129,7 +130,7 @@ impl Dependency { } } -pub trait SpecifierHandler { +pub trait SpecifierHandler: Sync + Send { /// Instructs the handler to fetch a specifier or retrieve its value from the /// cache. fn fetch( @@ -361,7 +362,7 @@ impl SpecifierHandler for FetchHandler { specifier: source_file.specifier, }) } - .boxed_local() + .boxed() } fn get_tsbuildinfo( diff --git a/cli/tsc.rs b/cli/tsc.rs index d6de4e1228..c21c52a563 100644 --- a/cli/tsc.rs +++ b/cli/tsc.rs @@ -20,10 +20,10 @@ use deno_core::OpFn; use deno_core::RuntimeOptions; use deno_core::Snapshot; use serde::Deserialize; -use std::cell::RefCell; use std::collections::HashMap; use std::path::PathBuf; -use std::rc::Rc; +use std::sync::Arc; +use std::sync::Mutex; /// Provide static assets that are not preloaded in the compiler snapshot. pub fn get_asset(asset: &str) -> Option<&'static str> { @@ -115,7 +115,7 @@ pub struct Request { pub config: TsConfig, /// Indicates to the tsc runtime if debug logging should occur. pub debug: bool, - pub graph: Rc>, + pub graph: Arc>, pub hash_data: Vec>, pub maybe_tsbuildinfo: Option, /// A vector of strings that represent the root/entry point modules for the @@ -138,7 +138,7 @@ pub struct Response { struct State { hash_data: Vec>, emitted_files: Vec, - graph: Rc>, + graph: Arc>, maybe_tsbuildinfo: Option, maybe_response: Option, root_map: HashMap, @@ -146,7 +146,7 @@ struct State { impl State { pub fn new( - graph: Rc>, + graph: Arc>, hash_data: Vec>, maybe_tsbuildinfo: Option, root_map: HashMap, @@ -260,7 +260,7 @@ fn load(state: &mut State, args: Value) -> Result { media_type = MediaType::from(&v.specifier); maybe_source } else { - let graph = state.graph.borrow(); + let graph = state.graph.lock().unwrap(); let specifier = if let Some(remapped_specifier) = state.root_map.get(&v.specifier) { remapped_specifier.clone() @@ -310,7 +310,7 @@ fn resolve(state: &mut State, args: Value) -> Result { MediaType::from(specifier).as_ts_extension().to_string(), )); } else { - let graph = state.graph.borrow(); + let graph = state.graph.lock().unwrap(); match graph.resolve(specifier, &referrer, true) { Ok(resolved_specifier) => { let media_type = if let Some(media_type) = @@ -446,9 +446,9 @@ mod tests { use crate::module_graph::tests::MockSpecifierHandler; use crate::module_graph::GraphBuilder; use crate::tsc_config::TsConfig; - use std::cell::RefCell; use std::env; use std::path::PathBuf; + use std::sync::Mutex; async fn setup( maybe_specifier: Option, @@ -461,7 +461,7 @@ mod tests { let hash_data = maybe_hash_data.unwrap_or_else(|| vec![b"".to_vec()]); let c = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); let fixtures = c.join("tests/tsc2"); - let handler = Rc::new(RefCell::new(MockSpecifierHandler { + let handler = Arc::new(Mutex::new(MockSpecifierHandler { fixtures, ..MockSpecifierHandler::default() })); @@ -470,7 +470,7 @@ mod tests { .add(&specifier, false) .await .expect("module not inserted"); - let graph = Rc::new(RefCell::new(builder.get_graph())); + let graph = Arc::new(Mutex::new(builder.get_graph())); State::new(graph, hash_data, maybe_tsbuildinfo, HashMap::new()) } @@ -480,13 +480,13 @@ mod tests { let hash_data = vec![b"something".to_vec()]; let c = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); let fixtures = c.join("tests/tsc2"); - let handler = Rc::new(RefCell::new(MockSpecifierHandler { + let handler = Arc::new(Mutex::new(MockSpecifierHandler { fixtures, ..Default::default() })); let mut builder = GraphBuilder::new(handler.clone(), None, None); builder.add(&specifier, false).await?; - let graph = Rc::new(RefCell::new(builder.get_graph())); + let graph = Arc::new(Mutex::new(builder.get_graph())); let config = TsConfig::new(json!({ "allowJs": true, "checkJs": false,