summaryrefslogtreecommitdiff
path: root/cli/cache/check.rs
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2022-07-12 18:58:39 -0400
committerGitHub <noreply@github.com>2022-07-12 18:58:39 -0400
commit0c87dd1e9898d7ac93e274d3611ee491a107d47a (patch)
treef626332706ccd12e0719f9b84d6b234d5483659b /cli/cache/check.rs
parent76107649804e674268becd693b7b2a954eecb3da (diff)
perf: use emit from swc instead of tsc (#15118)
Diffstat (limited to 'cli/cache/check.rs')
-rw-r--r--cli/cache/check.rs215
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())
+ );
+ }
+}