diff options
author | David Sherret <dsherret@users.noreply.github.com> | 2022-07-12 18:58:39 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-07-12 18:58:39 -0400 |
commit | 0c87dd1e9898d7ac93e274d3611ee491a107d47a (patch) | |
tree | f626332706ccd12e0719f9b84d6b234d5483659b /cli/cache/check.rs | |
parent | 76107649804e674268becd693b7b2a954eecb3da (diff) |
perf: use emit from swc instead of tsc (#15118)
Diffstat (limited to 'cli/cache/check.rs')
-rw-r--r-- | cli/cache/check.rs | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/cli/cache/check.rs b/cli/cache/check.rs new file mode 100644 index 000000000..0ff86ef6c --- /dev/null +++ b/cli/cache/check.rs @@ -0,0 +1,215 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +use std::path::Path; + +use deno_ast::ModuleSpecifier; +use deno_core::anyhow::Context; +use deno_core::error::AnyError; +use deno_runtime::deno_webstorage::rusqlite::params; +use deno_runtime::deno_webstorage::rusqlite::Connection; + +use super::common::run_sqlite_pragma; + +/// The cache used to tell whether type checking should occur again. +/// +/// This simply stores a hash of the inputs of each successful type check +/// and only clears them out when changing CLI versions. +pub struct TypeCheckCache { + conn: Connection, +} + +impl TypeCheckCache { + pub fn new(db_file_path: &Path) -> Result<Self, AnyError> { + let conn = Connection::open(db_file_path).with_context(|| { + format!( + concat!( + "Error opening type checking cache at {} -- ", + "Perhaps it's corrupt. Maybe try deleting it." + ), + db_file_path.display() + ) + })?; + Self::from_connection(conn, crate::version::deno()) + } + + fn from_connection( + conn: Connection, + cli_version: String, + ) -> Result<Self, AnyError> { + run_sqlite_pragma(&conn)?; + create_tables(&conn, cli_version)?; + + Ok(Self { conn }) + } + + pub fn has_check_hash(&self, hash: u64) -> bool { + match self.hash_check_hash_result(hash) { + Ok(val) => val, + Err(err) => { + if cfg!(debug_assertions) { + panic!("Error retrieving hash: {}", err); + } else { + log::debug!("Error retrieving hash: {}", err); + // fail silently when not debugging + false + } + } + } + } + + fn hash_check_hash_result(&self, hash: u64) -> Result<bool, AnyError> { + let query = "SELECT * FROM checkcache WHERE check_hash=?1 LIMIT 1"; + let mut stmt = self.conn.prepare_cached(query)?; + Ok(stmt.exists(params![hash.to_string()])?) + } + + pub fn add_check_hash(&self, check_hash: u64) { + if let Err(err) = self.add_check_hash_result(check_hash) { + if cfg!(debug_assertions) { + panic!("Error saving check hash: {}", err); + } else { + log::debug!("Error saving check hash: {}", err); + } + } + } + + fn add_check_hash_result(&self, check_hash: u64) -> Result<(), AnyError> { + let sql = " + INSERT OR REPLACE INTO + checkcache (check_hash) + VALUES + (?1)"; + let mut stmt = self.conn.prepare_cached(sql)?; + stmt.execute(params![&check_hash.to_string(),])?; + Ok(()) + } + + pub fn get_tsbuildinfo(&self, specifier: &ModuleSpecifier) -> Option<String> { + let mut stmt = self + .conn + .prepare_cached("SELECT text FROM tsbuildinfo WHERE specifier=?1 LIMIT 1") + .ok()?; + let mut rows = stmt.query(params![specifier.to_string()]).ok()?; + let row = rows.next().ok().flatten()?; + + row.get(0).ok() + } + + pub fn set_tsbuildinfo(&self, specifier: &ModuleSpecifier, text: &str) { + if let Err(err) = self.set_tsbuildinfo_result(specifier, text) { + // should never error here, but if it ever does don't fail + if cfg!(debug_assertions) { + panic!("Error saving tsbuildinfo: {}", err); + } else { + log::debug!("Error saving tsbuildinfo: {}", err); + } + } + } + + fn set_tsbuildinfo_result( + &self, + specifier: &ModuleSpecifier, + text: &str, + ) -> Result<(), AnyError> { + let mut stmt = self.conn.prepare_cached( + "INSERT OR REPLACE INTO tsbuildinfo (specifier, text) VALUES (?1, ?2)", + )?; + stmt.execute(params![specifier.to_string(), text])?; + Ok(()) + } +} + +fn create_tables( + conn: &Connection, + cli_version: String, +) -> Result<(), AnyError> { + // INT doesn't store up to u64, so use TEXT + conn.execute( + "CREATE TABLE IF NOT EXISTS checkcache ( + check_hash TEXT PRIMARY KEY + )", + [], + )?; + conn.execute( + "CREATE TABLE IF NOT EXISTS tsbuildinfo ( + specifier TEXT PRIMARY KEY, + text TEXT NOT NULL + )", + [], + )?; + conn.execute( + "CREATE TABLE IF NOT EXISTS info ( + key TEXT PRIMARY KEY, + value TEXT NOT NULL + )", + [], + )?; + + // delete the cache when the CLI version changes + let data_cli_version: Option<String> = conn + .query_row( + "SELECT value FROM info WHERE key='CLI_VERSION' LIMIT 1", + [], + |row| row.get(0), + ) + .ok(); + if data_cli_version != Some(cli_version.to_string()) { + conn.execute("DELETE FROM checkcache", params![])?; + conn.execute("DELETE FROM tsbuildinfo", params![])?; + let mut stmt = conn + .prepare("INSERT OR REPLACE INTO info (key, value) VALUES (?1, ?2)")?; + stmt.execute(params!["CLI_VERSION", &cli_version])?; + } + + Ok(()) +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + pub fn check_cache_general_use() { + let conn = Connection::open_in_memory().unwrap(); + let cache = + TypeCheckCache::from_connection(conn, "1.0.0".to_string()).unwrap(); + + assert!(!cache.has_check_hash(1)); + cache.add_check_hash(1); + assert!(cache.has_check_hash(1)); + assert!(!cache.has_check_hash(2)); + + let specifier1 = ModuleSpecifier::parse("file:///test.json").unwrap(); + assert_eq!(cache.get_tsbuildinfo(&specifier1), None); + cache.set_tsbuildinfo(&specifier1, "test"); + assert_eq!(cache.get_tsbuildinfo(&specifier1), Some("test".to_string())); + + // try changing the cli version (should clear) + let conn = cache.conn; + let cache = + TypeCheckCache::from_connection(conn, "2.0.0".to_string()).unwrap(); + assert!(!cache.has_check_hash(1)); + cache.add_check_hash(1); + assert!(cache.has_check_hash(1)); + assert_eq!(cache.get_tsbuildinfo(&specifier1), None); + cache.set_tsbuildinfo(&specifier1, "test"); + assert_eq!(cache.get_tsbuildinfo(&specifier1), Some("test".to_string())); + + // recreating the cache should not remove the data because the CLI version and state hash is the same + let conn = cache.conn; + let cache = + TypeCheckCache::from_connection(conn, "2.0.0".to_string()).unwrap(); + assert!(cache.has_check_hash(1)); + assert!(!cache.has_check_hash(2)); + assert_eq!(cache.get_tsbuildinfo(&specifier1), Some("test".to_string())); + + // adding when already exists should not cause issue + cache.add_check_hash(1); + assert!(cache.has_check_hash(1)); + cache.set_tsbuildinfo(&specifier1, "other"); + assert_eq!( + cache.get_tsbuildinfo(&specifier1), + Some("other".to_string()) + ); + } +} |