summaryrefslogtreecommitdiff
path: root/cli/cache/node.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/cache/node.rs')
-rw-r--r--cli/cache/node.rs312
1 files changed, 117 insertions, 195 deletions
diff --git a/cli/cache/node.rs b/cli/cache/node.rs
index 19ac45d6b..f42f132fd 100644
--- a/cli/cache/node.rs
+++ b/cli/cache/node.rs
@@ -1,40 +1,58 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-use std::path::Path;
-
use deno_ast::CjsAnalysis;
use deno_core::error::AnyError;
-use deno_core::parking_lot::Mutex;
use deno_core::serde_json;
use deno_runtime::deno_webstorage::rusqlite::params;
-use deno_runtime::deno_webstorage::rusqlite::Connection;
-use serde::Deserialize;
-use serde::Serialize;
-use std::path::PathBuf;
-use std::sync::Arc;
-use super::common::INITIAL_PRAGMAS;
+use super::cache_db::CacheDB;
+use super::cache_db::CacheDBConfiguration;
+use super::cache_db::CacheFailure;
use super::FastInsecureHasher;
-// todo(dsherret): use deno_ast::CjsAnalysisData directly when upgrading deno_ast
-// See https://github.com/denoland/deno_ast/pull/117
-#[derive(Serialize, Deserialize)]
-struct CjsAnalysisData {
- pub exports: Vec<String>,
- pub reexports: Vec<String>,
-}
+pub static NODE_ANALYSIS_CACHE_DB: CacheDBConfiguration =
+ CacheDBConfiguration {
+ table_initializer: concat!(
+ "CREATE TABLE IF NOT EXISTS cjsanalysiscache (
+ specifier TEXT PRIMARY KEY,
+ source_hash TEXT NOT NULL,
+ data TEXT NOT NULL
+ );",
+ "CREATE UNIQUE INDEX IF NOT EXISTS cjsanalysiscacheidx
+ ON cjsanalysiscache(specifier);",
+ "CREATE TABLE IF NOT EXISTS esmglobalscache (
+ specifier TEXT PRIMARY KEY,
+ source_hash TEXT NOT NULL,
+ data TEXT NOT NULL
+ );",
+ "CREATE UNIQUE INDEX IF NOT EXISTS esmglobalscacheidx
+ ON esmglobalscache(specifier);",
+ ),
+ on_version_change: concat!(
+ "DELETE FROM cjsanalysiscache;",
+ "DELETE FROM esmglobalscache;",
+ ),
+ preheat_queries: &[],
+ on_failure: CacheFailure::InMemory,
+ };
#[derive(Clone)]
pub struct NodeAnalysisCache {
- db_file_path: Option<PathBuf>,
- inner: Arc<Mutex<Option<Option<NodeAnalysisCacheInner>>>>,
+ inner: NodeAnalysisCacheInner,
}
impl NodeAnalysisCache {
- pub fn new(db_file_path: Option<PathBuf>) -> Self {
+ #[cfg(test)]
+ pub fn new_in_memory() -> Self {
+ Self::new(CacheDB::in_memory(
+ &NODE_ANALYSIS_CACHE_DB,
+ crate::version::deno(),
+ ))
+ }
+
+ pub fn new(db: CacheDB) -> Self {
Self {
- db_file_path,
- inner: Default::default(),
+ inner: NodeAnalysisCacheInner::new(db),
}
}
@@ -45,16 +63,31 @@ impl NodeAnalysisCache {
.to_string()
}
+ fn ensure_ok<T: Default>(res: Result<T, AnyError>) -> T {
+ match res {
+ Ok(x) => x,
+ Err(err) => {
+ // TODO(mmastrac): This behavior was inherited from before the refactoring but it probably makes sense to move it into the cache
+ // at some point.
+ // should never error here, but if it ever does don't fail
+ if cfg!(debug_assertions) {
+ panic!("Error using esm analysis: {err:#}");
+ } else {
+ log::debug!("Error using esm analysis: {:#}", err);
+ }
+ T::default()
+ }
+ }
+ }
+
pub fn get_cjs_analysis(
&self,
specifier: &str,
expected_source_hash: &str,
) -> Option<CjsAnalysis> {
- self
- .with_inner(|inner| {
- inner.get_cjs_analysis(specifier, expected_source_hash)
- })
- .flatten()
+ Self::ensure_ok(
+ self.inner.get_cjs_analysis(specifier, expected_source_hash),
+ )
}
pub fn set_cjs_analysis(
@@ -63,9 +96,11 @@ impl NodeAnalysisCache {
source_hash: &str,
cjs_analysis: &CjsAnalysis,
) {
- self.with_inner(|inner| {
- inner.set_cjs_analysis(specifier, source_hash, cjs_analysis)
- });
+ Self::ensure_ok(self.inner.set_cjs_analysis(
+ specifier,
+ source_hash,
+ cjs_analysis,
+ ));
}
pub fn get_esm_analysis(
@@ -73,11 +108,9 @@ impl NodeAnalysisCache {
specifier: &str,
expected_source_hash: &str,
) -> Option<Vec<String>> {
- self
- .with_inner(|inner| {
- inner.get_esm_analysis(specifier, expected_source_hash)
- })
- .flatten()
+ Self::ensure_ok(
+ self.inner.get_esm_analysis(specifier, expected_source_hash),
+ )
}
pub fn set_esm_analysis(
@@ -86,78 +119,22 @@ impl NodeAnalysisCache {
source_hash: &str,
top_level_decls: &Vec<String>,
) {
- self.with_inner(|inner| {
- inner.set_esm_analysis(specifier, source_hash, top_level_decls)
- });
- }
-
- fn with_inner<TResult>(
- &self,
- action: impl FnOnce(&NodeAnalysisCacheInner) -> Result<TResult, AnyError>,
- ) -> Option<TResult> {
- // lazily create the cache in order to not
- let mut maybe_created = self.inner.lock();
- let inner = match maybe_created.as_ref() {
- Some(maybe_inner) => maybe_inner.as_ref(),
- None => {
- let maybe_inner = match NodeAnalysisCacheInner::new(
- self.db_file_path.as_deref(),
- crate::version::deno().to_string(),
- ) {
- Ok(cache) => Some(cache),
- Err(err) => {
- // should never error here, but if it ever does don't fail
- if cfg!(debug_assertions) {
- panic!("Error creating node analysis cache: {err:#}");
- } else {
- log::debug!("Error creating node analysis cache: {:#}", err);
- None
- }
- }
- };
- *maybe_created = Some(maybe_inner);
- maybe_created.as_ref().and_then(|p| p.as_ref())
- }
- }?;
- match action(inner) {
- Ok(result) => Some(result),
- Err(err) => {
- // should never error here, but if it ever does don't fail
- if cfg!(debug_assertions) {
- panic!("Error using esm analysis: {err:#}");
- } else {
- log::debug!("Error using esm analysis: {:#}", err);
- }
- None
- }
- }
+ Self::ensure_ok(self.inner.set_esm_analysis(
+ specifier,
+ source_hash,
+ top_level_decls,
+ ))
}
}
+#[derive(Clone)]
struct NodeAnalysisCacheInner {
- conn: Connection,
+ conn: CacheDB,
}
impl NodeAnalysisCacheInner {
- pub fn new(
- db_file_path: Option<&Path>,
- version: String,
- ) -> Result<Self, AnyError> {
- log::debug!("Opening node analysis cache.");
- let conn = match db_file_path {
- Some(path) => Connection::open(path)?,
- None => Connection::open_in_memory()?,
- };
- Self::from_connection(conn, version)
- }
-
- fn from_connection(
- conn: Connection,
- version: String,
- ) -> Result<Self, AnyError> {
- initialize(&conn, &version)?;
-
- Ok(Self { conn })
+ pub fn new(conn: CacheDB) -> Self {
+ Self { conn }
}
pub fn get_cjs_analysis(
@@ -174,19 +151,15 @@ impl NodeAnalysisCacheInner {
specifier=?1
AND source_hash=?2
LIMIT 1";
- let mut stmt = self.conn.prepare_cached(query)?;
- let mut rows = stmt.query(params![specifier, &expected_source_hash])?;
- if let Some(row) = rows.next()? {
- let analysis_info: String = row.get(0)?;
- let analysis_info: CjsAnalysisData =
- serde_json::from_str(&analysis_info)?;
- Ok(Some(CjsAnalysis {
- exports: analysis_info.exports,
- reexports: analysis_info.reexports,
- }))
- } else {
- Ok(None)
- }
+ let res = self.conn.query_row(
+ query,
+ params![specifier, &expected_source_hash],
+ |row| {
+ let analysis_info: String = row.get(0)?;
+ Ok(serde_json::from_str(&analysis_info)?)
+ },
+ )?;
+ Ok(res)
}
pub fn set_cjs_analysis(
@@ -200,16 +173,14 @@ impl NodeAnalysisCacheInner {
cjsanalysiscache (specifier, source_hash, data)
VALUES
(?1, ?2, ?3)";
- let mut stmt = self.conn.prepare_cached(sql)?;
- stmt.execute(params![
- specifier,
- &source_hash.to_string(),
- &serde_json::to_string(&CjsAnalysisData {
- // temporary clones until upgrading deno_ast
- exports: cjs_analysis.exports.clone(),
- reexports: cjs_analysis.reexports.clone(),
- })?,
- ])?;
+ self.conn.execute(
+ sql,
+ params![
+ specifier,
+ &source_hash.to_string(),
+ &serde_json::to_string(&cjs_analysis)?,
+ ],
+ )?;
Ok(())
}
@@ -227,15 +198,16 @@ impl NodeAnalysisCacheInner {
specifier=?1
AND source_hash=?2
LIMIT 1";
- let mut stmt = self.conn.prepare_cached(query)?;
- let mut rows = stmt.query(params![specifier, &expected_source_hash])?;
- if let Some(row) = rows.next()? {
- let top_level_decls: String = row.get(0)?;
- let decls: Vec<String> = serde_json::from_str(&top_level_decls)?;
- Ok(Some(decls))
- } else {
- Ok(None)
- }
+ let res = self.conn.query_row(
+ query,
+ params![specifier, &expected_source_hash],
+ |row| {
+ let top_level_decls: String = row.get(0)?;
+ let decls: Vec<String> = serde_json::from_str(&top_level_decls)?;
+ Ok(decls)
+ },
+ )?;
+ Ok(res)
}
pub fn set_esm_analysis(
@@ -249,72 +221,26 @@ impl NodeAnalysisCacheInner {
esmglobalscache (specifier, source_hash, data)
VALUES
(?1, ?2, ?3)";
- let mut stmt = self.conn.prepare_cached(sql)?;
- stmt.execute(params![
- specifier,
- &source_hash,
- &serde_json::to_string(top_level_decls)?,
- ])?;
+ self.conn.execute(
+ sql,
+ params![
+ specifier,
+ &source_hash,
+ &serde_json::to_string(top_level_decls)?,
+ ],
+ )?;
Ok(())
}
}
-fn initialize(conn: &Connection, cli_version: &str) -> Result<(), AnyError> {
- // INT doesn't store up to u64, so use TEXT for source_hash
- let query = format!(
- "{INITIAL_PRAGMAS}
- CREATE TABLE IF NOT EXISTS cjsanalysiscache (
- specifier TEXT PRIMARY KEY,
- source_hash TEXT NOT NULL,
- data TEXT NOT NULL
- );
- CREATE UNIQUE INDEX IF NOT EXISTS cjsanalysiscacheidx
- ON cjsanalysiscache(specifier);
- CREATE TABLE IF NOT EXISTS esmglobalscache (
- specifier TEXT PRIMARY KEY,
- source_hash TEXT NOT NULL,
- data TEXT NOT NULL
- );
- CREATE UNIQUE INDEX IF NOT EXISTS esmglobalscacheidx
- ON esmglobalscache(specifier);
- CREATE TABLE IF NOT EXISTS info (
- key TEXT PRIMARY KEY,
- value TEXT NOT NULL
- );
- "
- );
-
- conn.execute_batch(&query)?;
-
- // 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.as_deref() != Some(cli_version) {
- conn.execute("DELETE FROM cjsanalysiscache", params![])?;
- conn.execute("DELETE FROM esmglobalscache", 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 node_analysis_cache_general_use() {
- let conn = Connection::open_in_memory().unwrap();
- let cache =
- NodeAnalysisCacheInner::from_connection(conn, "1.0.0".to_string())
- .unwrap();
+ let conn = CacheDB::in_memory(&NODE_ANALYSIS_CACHE_DB, "1.0.0");
+ let cache = NodeAnalysisCacheInner::new(conn);
assert!(cache.get_cjs_analysis("file.js", "2").unwrap().is_none());
let cjs_analysis = CjsAnalysis {
@@ -349,10 +275,8 @@ mod test {
.unwrap();
// recreating with same cli version should still have it
- let conn = cache.conn;
- let cache =
- NodeAnalysisCacheInner::from_connection(conn, "1.0.0".to_string())
- .unwrap();
+ let conn = cache.conn.recreate_with_version("1.0.0");
+ let cache = NodeAnalysisCacheInner::new(conn);
let actual_analysis =
cache.get_cjs_analysis("file.js", "2").unwrap().unwrap();
assert_eq!(actual_analysis.exports, cjs_analysis.exports);
@@ -362,10 +286,8 @@ mod test {
assert_eq!(actual_esm_analysis, esm_analysis);
// now changing the cli version should clear it
- let conn = cache.conn;
- let cache =
- NodeAnalysisCacheInner::from_connection(conn, "2.0.0".to_string())
- .unwrap();
+ let conn = cache.conn.recreate_with_version("2.0.0");
+ let cache = NodeAnalysisCacheInner::new(conn);
assert!(cache.get_cjs_analysis("file.js", "2").unwrap().is_none());
assert!(cache.get_esm_analysis("file.js", "2").unwrap().is_none());
}