summaryrefslogtreecommitdiff
path: root/cli/cache/parsed_source.rs
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2023-10-25 18:13:22 -0400
committerGitHub <noreply@github.com>2023-10-25 18:13:22 -0400
commit842e29057d6e545c6b498c584a5366fff34f6aa7 (patch)
treeaba1bb9d767945ba72be5f11c5c87027f65c5678 /cli/cache/parsed_source.rs
parent79a9f2a77c1c517282a0e3ac77f8a1252b6c50b9 (diff)
refactor: break out ModuleInfoCache from ParsedSourceCache (#20977)
As title. This will help use the two independently from the other, which will help in an upcoming deno doc PR where I need to parse the source files with scope analysis.
Diffstat (limited to 'cli/cache/parsed_source.rs')
-rw-r--r--cli/cache/parsed_source.rs333
1 files changed, 21 insertions, 312 deletions
diff --git a/cli/cache/parsed_source.rs b/cli/cache/parsed_source.rs
index 68503e6aa..8ca3d80dd 100644
--- a/cli/cache/parsed_source.rs
+++ b/cli/cache/parsed_source.rs
@@ -6,94 +6,16 @@ use std::sync::Arc;
use deno_ast::MediaType;
use deno_ast::ModuleSpecifier;
use deno_ast::ParsedSource;
-use deno_core::error::AnyError;
use deno_core::parking_lot::Mutex;
-use deno_core::serde_json;
use deno_graph::CapturingModuleParser;
-use deno_graph::DefaultModuleAnalyzer;
-use deno_graph::ModuleInfo;
use deno_graph::ModuleParser;
-use deno_runtime::deno_webstorage::rusqlite::params;
-use super::cache_db::CacheDB;
-use super::cache_db::CacheDBConfiguration;
-use super::cache_db::CacheFailure;
-use super::FastInsecureHasher;
-
-const SELECT_MODULE_INFO: &str = "
-SELECT
- module_info
-FROM
- moduleinfocache
-WHERE
- specifier=?1
- AND media_type=?2
- AND source_hash=?3
-LIMIT 1";
-
-pub static PARSED_SOURCE_CACHE_DB: CacheDBConfiguration =
- CacheDBConfiguration {
- table_initializer: "CREATE TABLE IF NOT EXISTS moduleinfocache (
- specifier TEXT PRIMARY KEY,
- media_type TEXT NOT NULL,
- source_hash TEXT NOT NULL,
- module_info TEXT NOT NULL
- );",
- on_version_change: "DELETE FROM moduleinfocache;",
- preheat_queries: &[SELECT_MODULE_INFO],
- on_failure: CacheFailure::InMemory,
- };
-
-#[derive(Clone, Default)]
-struct ParsedSourceCacheSources(
- Arc<Mutex<HashMap<ModuleSpecifier, ParsedSource>>>,
-);
-
-/// It's ok that this is racy since in non-LSP situations
-/// this will only ever store one form of a parsed source
-/// and in LSP settings the concurrency will be enforced
-/// at a higher level to ensure this will have the latest
-/// parsed source.
-impl deno_graph::ParsedSourceStore for ParsedSourceCacheSources {
- fn set_parsed_source(
- &self,
- specifier: deno_graph::ModuleSpecifier,
- parsed_source: ParsedSource,
- ) -> Option<ParsedSource> {
- self.0.lock().insert(specifier, parsed_source)
- }
-
- fn get_parsed_source(
- &self,
- specifier: &deno_graph::ModuleSpecifier,
- ) -> Option<ParsedSource> {
- self.0.lock().get(specifier).cloned()
- }
-}
-
-/// A cache of `ParsedSource`s, which may be used with `deno_graph`
-/// for cached dependency analysis.
+#[derive(Default)]
pub struct ParsedSourceCache {
- db: CacheDB,
- sources: ParsedSourceCacheSources,
+ sources: Mutex<HashMap<ModuleSpecifier, ParsedSource>>,
}
impl ParsedSourceCache {
- #[cfg(test)]
- pub fn new_in_memory() -> Self {
- Self {
- db: CacheDB::in_memory(&PARSED_SOURCE_CACHE_DB, crate::version::deno()),
- sources: Default::default(),
- }
- }
-
- pub fn new(db: CacheDB) -> Self {
- Self {
- db,
- sources: Default::default(),
- }
- }
-
pub fn get_parsed_source_from_esm_module(
&self,
module: &deno_graph::EsmModule,
@@ -120,251 +42,38 @@ impl ParsedSourceCache {
/// Frees the parsed source from memory.
pub fn free(&self, specifier: &ModuleSpecifier) {
- self.sources.0.lock().remove(specifier);
- }
-
- pub fn as_analyzer(&self) -> Box<dyn deno_graph::ModuleAnalyzer> {
- Box::new(ParsedSourceCacheModuleAnalyzer::new(
- self.db.clone(),
- self.sources.clone(),
- ))
+ self.sources.lock().remove(specifier);
}
/// Creates a parser that will reuse a ParsedSource from the store
/// if it exists, or else parse.
pub fn as_capturing_parser(&self) -> CapturingModuleParser {
- CapturingModuleParser::new(None, &self.sources)
+ CapturingModuleParser::new(None, self)
}
- pub fn cache_module_info(
- &self,
- specifier: &ModuleSpecifier,
- media_type: MediaType,
- source: &str,
- module_info: &ModuleInfo,
- ) -> Result<(), AnyError> {
- let source_hash = compute_source_hash(source.as_bytes());
- ParsedSourceCacheModuleAnalyzer::new(self.db.clone(), self.sources.clone())
- .set_module_info(specifier, media_type, &source_hash, module_info)
+ pub fn as_store(self: &Arc<Self>) -> Arc<dyn deno_graph::ParsedSourceStore> {
+ self.clone()
}
}
-struct ParsedSourceCacheModuleAnalyzer {
- conn: CacheDB,
- sources: ParsedSourceCacheSources,
-}
-
-impl ParsedSourceCacheModuleAnalyzer {
- pub fn new(conn: CacheDB, sources: ParsedSourceCacheSources) -> Self {
- Self { conn, sources }
- }
-
- pub fn get_module_info(
- &self,
- specifier: &ModuleSpecifier,
- media_type: MediaType,
- expected_source_hash: &str,
- ) -> Result<Option<ModuleInfo>, AnyError> {
- let query = SELECT_MODULE_INFO;
- let res = self.conn.query_row(
- query,
- params![
- &specifier.as_str(),
- serialize_media_type(media_type),
- &expected_source_hash,
- ],
- |row| {
- let module_info: String = row.get(0)?;
- let module_info = serde_json::from_str(&module_info)?;
- Ok(module_info)
- },
- )?;
- Ok(res)
- }
-
- pub fn set_module_info(
+/// It's ok that this is racy since in non-LSP situations
+/// this will only ever store one form of a parsed source
+/// and in LSP settings the concurrency will be enforced
+/// at a higher level to ensure this will have the latest
+/// parsed source.
+impl deno_graph::ParsedSourceStore for ParsedSourceCache {
+ fn set_parsed_source(
&self,
- specifier: &ModuleSpecifier,
- media_type: MediaType,
- source_hash: &str,
- module_info: &ModuleInfo,
- ) -> Result<(), AnyError> {
- let sql = "
- INSERT OR REPLACE INTO
- moduleinfocache (specifier, media_type, source_hash, module_info)
- VALUES
- (?1, ?2, ?3, ?4)";
- self.conn.execute(
- sql,
- params![
- specifier.as_str(),
- serialize_media_type(media_type),
- &source_hash,
- &serde_json::to_string(&module_info)?,
- ],
- )?;
- Ok(())
- }
-}
-
-// todo(dsherret): change this to be stored as an integer next time
-// the cache version is bumped
-fn serialize_media_type(media_type: MediaType) -> &'static str {
- use MediaType::*;
- match media_type {
- JavaScript => "1",
- Jsx => "2",
- Mjs => "3",
- Cjs => "4",
- TypeScript => "5",
- Mts => "6",
- Cts => "7",
- Dts => "8",
- Dmts => "9",
- Dcts => "10",
- Tsx => "11",
- Json => "12",
- Wasm => "13",
- TsBuildInfo => "14",
- SourceMap => "15",
- Unknown => "16",
+ specifier: deno_graph::ModuleSpecifier,
+ parsed_source: ParsedSource,
+ ) -> Option<ParsedSource> {
+ self.sources.lock().insert(specifier, parsed_source)
}
-}
-impl deno_graph::ModuleAnalyzer for ParsedSourceCacheModuleAnalyzer {
- fn analyze(
+ fn get_parsed_source(
&self,
- specifier: &ModuleSpecifier,
- source: Arc<str>,
- media_type: MediaType,
- ) -> Result<ModuleInfo, deno_ast::Diagnostic> {
- // attempt to load from the cache
- let source_hash = compute_source_hash(source.as_bytes());
- match self.get_module_info(specifier, media_type, &source_hash) {
- Ok(Some(info)) => return Ok(info),
- Ok(None) => {}
- Err(err) => {
- log::debug!(
- "Error loading module cache info for {}. {:#}",
- specifier,
- err
- );
- }
- }
-
- // otherwise, get the module info from the parsed source cache
- let parser = CapturingModuleParser::new(None, &self.sources);
- let analyzer = DefaultModuleAnalyzer::new(&parser);
-
- let module_info = analyzer.analyze(specifier, source, media_type)?;
-
- // then attempt to cache it
- if let Err(err) =
- self.set_module_info(specifier, media_type, &source_hash, &module_info)
- {
- log::debug!(
- "Error saving module cache info for {}. {:#}",
- specifier,
- err
- );
- }
-
- Ok(module_info)
- }
-}
-
-fn compute_source_hash(bytes: &[u8]) -> String {
- FastInsecureHasher::hash(bytes).to_string()
-}
-
-#[cfg(test)]
-mod test {
- use deno_graph::PositionRange;
- use deno_graph::SpecifierWithRange;
-
- use super::*;
-
- #[test]
- pub fn parsed_source_cache_module_analyzer_general_use() {
- let conn = CacheDB::in_memory(&PARSED_SOURCE_CACHE_DB, "1.0.0");
- let cache = ParsedSourceCacheModuleAnalyzer::new(conn, Default::default());
- let specifier1 =
- ModuleSpecifier::parse("https://localhost/mod.ts").unwrap();
- let specifier2 =
- ModuleSpecifier::parse("https://localhost/mod2.ts").unwrap();
- assert_eq!(
- cache
- .get_module_info(&specifier1, MediaType::JavaScript, "1")
- .unwrap(),
- None
- );
-
- let mut module_info = ModuleInfo::default();
- module_info.jsdoc_imports.push(SpecifierWithRange {
- range: PositionRange {
- start: deno_graph::Position {
- line: 0,
- character: 3,
- },
- end: deno_graph::Position {
- line: 1,
- character: 2,
- },
- },
- text: "test".to_string(),
- });
- cache
- .set_module_info(&specifier1, MediaType::JavaScript, "1", &module_info)
- .unwrap();
- assert_eq!(
- cache
- .get_module_info(&specifier1, MediaType::JavaScript, "1")
- .unwrap(),
- Some(module_info.clone())
- );
- assert_eq!(
- cache
- .get_module_info(&specifier2, MediaType::JavaScript, "1")
- .unwrap(),
- None,
- );
- // different media type
- assert_eq!(
- cache
- .get_module_info(&specifier1, MediaType::TypeScript, "1")
- .unwrap(),
- None,
- );
- // different source hash
- assert_eq!(
- cache
- .get_module_info(&specifier1, MediaType::JavaScript, "2")
- .unwrap(),
- None,
- );
-
- // try recreating with the same version
- let conn = cache.conn.recreate_with_version("1.0.0");
- let cache = ParsedSourceCacheModuleAnalyzer::new(conn, Default::default());
-
- // should get it
- assert_eq!(
- cache
- .get_module_info(&specifier1, MediaType::JavaScript, "1")
- .unwrap(),
- Some(module_info)
- );
-
- // try recreating with a different version
- let conn = cache.conn.recreate_with_version("1.0.1");
- let cache = ParsedSourceCacheModuleAnalyzer::new(conn, Default::default());
-
- // should no longer exist
- assert_eq!(
- cache
- .get_module_info(&specifier1, MediaType::JavaScript, "1")
- .unwrap(),
- None,
- );
+ specifier: &deno_graph::ModuleSpecifier,
+ ) -> Option<ParsedSource> {
+ self.sources.lock().get(specifier).cloned()
}
}