mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 20:38:55 +00:00
perf(compile): code cache (#26528)
Adds a lazily created code cache to `deno compile` by default. The code cache is created on first run to a single file in the temp directory and is only written once. After it's been written, the code cache becomes read only on subsequent runs. Only the modules loaded during startup are cached (dynamic imports are not code cached). The code cache can be disabled by compiling with `--no-code-cache`.
This commit is contained in:
parent
3ba464dbc4
commit
dd4570ed85
@ -1939,6 +1939,7 @@ On the first invocation with deno will download the proper binary and cache it i
|
|||||||
])
|
])
|
||||||
.help_heading(COMPILE_HEADING),
|
.help_heading(COMPILE_HEADING),
|
||||||
)
|
)
|
||||||
|
.arg(no_code_cache_arg())
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("no-terminal")
|
Arg::new("no-terminal")
|
||||||
.long("no-terminal")
|
.long("no-terminal")
|
||||||
@ -4431,6 +4432,8 @@ fn compile_parse(
|
|||||||
};
|
};
|
||||||
ext_arg_parse(flags, matches);
|
ext_arg_parse(flags, matches);
|
||||||
|
|
||||||
|
flags.code_cache_enabled = !matches.get_flag("no-code-cache");
|
||||||
|
|
||||||
flags.subcommand = DenoSubcommand::Compile(CompileFlags {
|
flags.subcommand = DenoSubcommand::Compile(CompileFlags {
|
||||||
source_file,
|
source_file,
|
||||||
output,
|
output,
|
||||||
@ -10040,6 +10043,7 @@ mod tests {
|
|||||||
include: vec![]
|
include: vec![]
|
||||||
}),
|
}),
|
||||||
type_check_mode: TypeCheckMode::Local,
|
type_check_mode: TypeCheckMode::Local,
|
||||||
|
code_cache_enabled: true,
|
||||||
..Flags::default()
|
..Flags::default()
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -10048,7 +10052,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn compile_with_flags() {
|
fn compile_with_flags() {
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
let r = flags_from_vec(svec!["deno", "compile", "--import-map", "import_map.json", "--no-remote", "--config", "tsconfig.json", "--no-check", "--unsafely-ignore-certificate-errors", "--reload", "--lock", "lock.json", "--cert", "example.crt", "--cached-only", "--location", "https:foo", "--allow-read", "--allow-net", "--v8-flags=--help", "--seed", "1", "--no-terminal", "--icon", "favicon.ico", "--output", "colors", "--env=.example.env", "https://examples.deno.land/color-logging.ts", "foo", "bar", "-p", "8080"]);
|
let r = flags_from_vec(svec!["deno", "compile", "--import-map", "import_map.json", "--no-code-cache", "--no-remote", "--config", "tsconfig.json", "--no-check", "--unsafely-ignore-certificate-errors", "--reload", "--lock", "lock.json", "--cert", "example.crt", "--cached-only", "--location", "https:foo", "--allow-read", "--allow-net", "--v8-flags=--help", "--seed", "1", "--no-terminal", "--icon", "favicon.ico", "--output", "colors", "--env=.example.env", "https://examples.deno.land/color-logging.ts", "foo", "bar", "-p", "8080"]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
r.unwrap(),
|
r.unwrap(),
|
||||||
Flags {
|
Flags {
|
||||||
@ -10064,6 +10068,7 @@ mod tests {
|
|||||||
}),
|
}),
|
||||||
import_map_path: Some("import_map.json".to_string()),
|
import_map_path: Some("import_map.json".to_string()),
|
||||||
no_remote: true,
|
no_remote: true,
|
||||||
|
code_cache_enabled: false,
|
||||||
config_flag: ConfigFlag::Path("tsconfig.json".to_owned()),
|
config_flag: ConfigFlag::Path("tsconfig.json".to_owned()),
|
||||||
type_check_mode: TypeCheckMode::None,
|
type_check_mode: TypeCheckMode::None,
|
||||||
reload: true,
|
reload: true,
|
||||||
|
10
cli/cache/code_cache.rs
vendored
10
cli/cache/code_cache.rs
vendored
@ -1,10 +1,14 @@
|
|||||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use deno_ast::ModuleSpecifier;
|
use deno_ast::ModuleSpecifier;
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
use deno_runtime::code_cache;
|
use deno_runtime::code_cache;
|
||||||
use deno_runtime::deno_webstorage::rusqlite::params;
|
use deno_runtime::deno_webstorage::rusqlite::params;
|
||||||
|
|
||||||
|
use crate::worker::CliCodeCache;
|
||||||
|
|
||||||
use super::cache_db::CacheDB;
|
use super::cache_db::CacheDB;
|
||||||
use super::cache_db::CacheDBConfiguration;
|
use super::cache_db::CacheDBConfiguration;
|
||||||
use super::cache_db::CacheDBHash;
|
use super::cache_db::CacheDBHash;
|
||||||
@ -82,6 +86,12 @@ impl CodeCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CliCodeCache for CodeCache {
|
||||||
|
fn as_code_cache(self: Arc<Self>) -> Arc<dyn code_cache::CodeCache> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl code_cache::CodeCache for CodeCache {
|
impl code_cache::CodeCache for CodeCache {
|
||||||
fn get_sync(
|
fn get_sync(
|
||||||
&self,
|
&self,
|
||||||
|
@ -64,6 +64,7 @@ use crate::args::NpmInstallDepsProvider;
|
|||||||
use crate::args::PermissionFlags;
|
use crate::args::PermissionFlags;
|
||||||
use crate::args::UnstableConfig;
|
use crate::args::UnstableConfig;
|
||||||
use crate::cache::DenoDir;
|
use crate::cache::DenoDir;
|
||||||
|
use crate::cache::FastInsecureHasher;
|
||||||
use crate::emit::Emitter;
|
use crate::emit::Emitter;
|
||||||
use crate::file_fetcher::FileFetcher;
|
use crate::file_fetcher::FileFetcher;
|
||||||
use crate::http_util::HttpClientProvider;
|
use crate::http_util::HttpClientProvider;
|
||||||
@ -174,6 +175,7 @@ pub struct SerializedWorkspaceResolver {
|
|||||||
pub struct Metadata {
|
pub struct Metadata {
|
||||||
pub argv: Vec<String>,
|
pub argv: Vec<String>,
|
||||||
pub seed: Option<u64>,
|
pub seed: Option<u64>,
|
||||||
|
pub code_cache_key: Option<u64>,
|
||||||
pub permissions: PermissionFlags,
|
pub permissions: PermissionFlags,
|
||||||
pub location: Option<Url>,
|
pub location: Option<Url>,
|
||||||
pub v8_flags: Vec<String>,
|
pub v8_flags: Vec<String>,
|
||||||
@ -604,10 +606,21 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||||||
VfsBuilder::new(root_path.clone())?
|
VfsBuilder::new(root_path.clone())?
|
||||||
};
|
};
|
||||||
let mut remote_modules_store = RemoteModulesStoreBuilder::default();
|
let mut remote_modules_store = RemoteModulesStoreBuilder::default();
|
||||||
|
let mut code_cache_key_hasher = if cli_options.code_cache_enabled() {
|
||||||
|
Some(FastInsecureHasher::new_deno_versioned())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
for module in graph.modules() {
|
for module in graph.modules() {
|
||||||
if module.specifier().scheme() == "data" {
|
if module.specifier().scheme() == "data" {
|
||||||
continue; // don't store data urls as an entry as they're in the code
|
continue; // don't store data urls as an entry as they're in the code
|
||||||
}
|
}
|
||||||
|
if let Some(hasher) = &mut code_cache_key_hasher {
|
||||||
|
if let Some(source) = module.source() {
|
||||||
|
hasher.write(module.specifier().as_str().as_bytes());
|
||||||
|
hasher.write(source.as_bytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
let (maybe_source, media_type) = match module {
|
let (maybe_source, media_type) = match module {
|
||||||
deno_graph::Module::Js(m) => {
|
deno_graph::Module::Js(m) => {
|
||||||
let source = if m.media_type.is_emittable() {
|
let source = if m.media_type.is_emittable() {
|
||||||
@ -675,6 +688,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||||||
let metadata = Metadata {
|
let metadata = Metadata {
|
||||||
argv: compile_flags.args.clone(),
|
argv: compile_flags.args.clone(),
|
||||||
seed: cli_options.seed(),
|
seed: cli_options.seed(),
|
||||||
|
code_cache_key: code_cache_key_hasher.map(|h| h.finish()),
|
||||||
location: cli_options.location_flag().clone(),
|
location: cli_options.location_flag().clone(),
|
||||||
permissions: cli_options.permission_flags().clone(),
|
permissions: cli_options.permission_flags().clone(),
|
||||||
v8_flags: cli_options.v8_flags().clone(),
|
v8_flags: cli_options.v8_flags().clone(),
|
||||||
|
514
cli/standalone/code_cache.rs
Normal file
514
cli/standalone/code_cache.rs
Normal file
@ -0,0 +1,514 @@
|
|||||||
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::io::BufReader;
|
||||||
|
use std::io::BufWriter;
|
||||||
|
use std::io::Read;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use deno_ast::ModuleSpecifier;
|
||||||
|
use deno_core::anyhow::bail;
|
||||||
|
use deno_core::error::AnyError;
|
||||||
|
use deno_core::parking_lot::Mutex;
|
||||||
|
use deno_core::unsync::sync::AtomicFlag;
|
||||||
|
use deno_runtime::code_cache::CodeCache;
|
||||||
|
use deno_runtime::code_cache::CodeCacheType;
|
||||||
|
|
||||||
|
use crate::cache::FastInsecureHasher;
|
||||||
|
use crate::util::path::get_atomic_file_path;
|
||||||
|
use crate::worker::CliCodeCache;
|
||||||
|
|
||||||
|
enum CodeCacheStrategy {
|
||||||
|
FirstRun(FirstRunCodeCacheStrategy),
|
||||||
|
SubsequentRun(SubsequentRunCodeCacheStrategy),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct DenoCompileCodeCacheEntry {
|
||||||
|
pub source_hash: u64,
|
||||||
|
pub data: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DenoCompileCodeCache {
|
||||||
|
strategy: CodeCacheStrategy,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DenoCompileCodeCache {
|
||||||
|
pub fn new(file_path: PathBuf, cache_key: u64) -> Self {
|
||||||
|
// attempt to deserialize the cache data
|
||||||
|
match deserialize(&file_path, cache_key) {
|
||||||
|
Ok(data) => {
|
||||||
|
log::debug!("Loaded {} code cache entries", data.len());
|
||||||
|
Self {
|
||||||
|
strategy: CodeCacheStrategy::SubsequentRun(
|
||||||
|
SubsequentRunCodeCacheStrategy {
|
||||||
|
is_finished: AtomicFlag::lowered(),
|
||||||
|
data: Mutex::new(data),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log::debug!("Failed to deserialize code cache: {:#}", err);
|
||||||
|
Self {
|
||||||
|
strategy: CodeCacheStrategy::FirstRun(FirstRunCodeCacheStrategy {
|
||||||
|
cache_key,
|
||||||
|
file_path,
|
||||||
|
is_finished: AtomicFlag::lowered(),
|
||||||
|
data: Mutex::new(FirstRunCodeCacheData {
|
||||||
|
cache: HashMap::new(),
|
||||||
|
add_count: 0,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CodeCache for DenoCompileCodeCache {
|
||||||
|
fn get_sync(
|
||||||
|
&self,
|
||||||
|
specifier: &ModuleSpecifier,
|
||||||
|
code_cache_type: CodeCacheType,
|
||||||
|
source_hash: u64,
|
||||||
|
) -> Option<Vec<u8>> {
|
||||||
|
match &self.strategy {
|
||||||
|
CodeCacheStrategy::FirstRun(strategy) => {
|
||||||
|
if !strategy.is_finished.is_raised() {
|
||||||
|
// we keep track of how many times the cache is requested
|
||||||
|
// then serialize the cache when we get that number of
|
||||||
|
// "set" calls
|
||||||
|
strategy.data.lock().add_count += 1;
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
CodeCacheStrategy::SubsequentRun(strategy) => {
|
||||||
|
if strategy.is_finished.is_raised() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
strategy.take_from_cache(specifier, code_cache_type, source_hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_sync(
|
||||||
|
&self,
|
||||||
|
specifier: ModuleSpecifier,
|
||||||
|
code_cache_type: CodeCacheType,
|
||||||
|
source_hash: u64,
|
||||||
|
bytes: &[u8],
|
||||||
|
) {
|
||||||
|
match &self.strategy {
|
||||||
|
CodeCacheStrategy::FirstRun(strategy) => {
|
||||||
|
if strategy.is_finished.is_raised() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let data_to_serialize = {
|
||||||
|
let mut data = strategy.data.lock();
|
||||||
|
data.cache.insert(
|
||||||
|
(specifier.to_string(), code_cache_type),
|
||||||
|
DenoCompileCodeCacheEntry {
|
||||||
|
source_hash,
|
||||||
|
data: bytes.to_vec(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if data.add_count != 0 {
|
||||||
|
data.add_count -= 1;
|
||||||
|
}
|
||||||
|
if data.add_count == 0 {
|
||||||
|
// don't allow using the cache anymore
|
||||||
|
strategy.is_finished.raise();
|
||||||
|
if data.cache.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(std::mem::take(&mut data.cache))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if let Some(cache_data) = &data_to_serialize {
|
||||||
|
strategy.write_cache_data(cache_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CodeCacheStrategy::SubsequentRun(_) => {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CliCodeCache for DenoCompileCodeCache {
|
||||||
|
fn enabled(&self) -> bool {
|
||||||
|
match &self.strategy {
|
||||||
|
CodeCacheStrategy::FirstRun(strategy) => {
|
||||||
|
!strategy.is_finished.is_raised()
|
||||||
|
}
|
||||||
|
CodeCacheStrategy::SubsequentRun(strategy) => {
|
||||||
|
!strategy.is_finished.is_raised()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_code_cache(self: Arc<Self>) -> Arc<dyn CodeCache> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type CodeCacheKey = (String, CodeCacheType);
|
||||||
|
|
||||||
|
struct FirstRunCodeCacheData {
|
||||||
|
cache: HashMap<CodeCacheKey, DenoCompileCodeCacheEntry>,
|
||||||
|
add_count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FirstRunCodeCacheStrategy {
|
||||||
|
cache_key: u64,
|
||||||
|
file_path: PathBuf,
|
||||||
|
is_finished: AtomicFlag,
|
||||||
|
data: Mutex<FirstRunCodeCacheData>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FirstRunCodeCacheStrategy {
|
||||||
|
fn write_cache_data(
|
||||||
|
&self,
|
||||||
|
cache_data: &HashMap<CodeCacheKey, DenoCompileCodeCacheEntry>,
|
||||||
|
) {
|
||||||
|
let count = cache_data.len();
|
||||||
|
let temp_file = get_atomic_file_path(&self.file_path);
|
||||||
|
match serialize(&temp_file, self.cache_key, cache_data) {
|
||||||
|
Ok(()) => {
|
||||||
|
if let Err(err) = std::fs::rename(&temp_file, &self.file_path) {
|
||||||
|
log::debug!("Failed to rename code cache: {}", err);
|
||||||
|
} else {
|
||||||
|
log::debug!("Serialized {} code cache entries", count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
let _ = std::fs::remove_file(&temp_file);
|
||||||
|
log::debug!("Failed to serialize code cache: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SubsequentRunCodeCacheStrategy {
|
||||||
|
is_finished: AtomicFlag,
|
||||||
|
data: Mutex<HashMap<CodeCacheKey, DenoCompileCodeCacheEntry>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SubsequentRunCodeCacheStrategy {
|
||||||
|
fn take_from_cache(
|
||||||
|
&self,
|
||||||
|
specifier: &ModuleSpecifier,
|
||||||
|
code_cache_type: CodeCacheType,
|
||||||
|
source_hash: u64,
|
||||||
|
) -> Option<Vec<u8>> {
|
||||||
|
let mut data = self.data.lock();
|
||||||
|
// todo(dsherret): how to avoid the clone here?
|
||||||
|
let entry = data.remove(&(specifier.to_string(), code_cache_type))?;
|
||||||
|
if entry.source_hash != source_hash {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if data.is_empty() {
|
||||||
|
self.is_finished.raise();
|
||||||
|
}
|
||||||
|
Some(entry.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// File format:
|
||||||
|
/// - <header>
|
||||||
|
/// - <cache key>
|
||||||
|
/// - <u32: number of entries>
|
||||||
|
/// - <[entry length]> - u64 * number of entries
|
||||||
|
/// - <[entry]>
|
||||||
|
/// - <[u8]: entry data>
|
||||||
|
/// - <String: specifier>
|
||||||
|
/// - <u8>: code cache type
|
||||||
|
/// - <u32: specifier length>
|
||||||
|
/// - <u64: source hash>
|
||||||
|
/// - <u64: entry data hash>
|
||||||
|
fn serialize(
|
||||||
|
file_path: &Path,
|
||||||
|
cache_key: u64,
|
||||||
|
cache: &HashMap<CodeCacheKey, DenoCompileCodeCacheEntry>,
|
||||||
|
) -> Result<(), AnyError> {
|
||||||
|
let cache_file = std::fs::OpenOptions::new()
|
||||||
|
.create(true)
|
||||||
|
.truncate(true)
|
||||||
|
.write(true)
|
||||||
|
.open(file_path)?;
|
||||||
|
let mut writer = BufWriter::new(cache_file);
|
||||||
|
serialize_with_writer(&mut writer, cache_key, cache)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_with_writer<T: Write>(
|
||||||
|
writer: &mut BufWriter<T>,
|
||||||
|
cache_key: u64,
|
||||||
|
cache: &HashMap<CodeCacheKey, DenoCompileCodeCacheEntry>,
|
||||||
|
) -> Result<(), AnyError> {
|
||||||
|
// header
|
||||||
|
writer.write_all(&cache_key.to_le_bytes())?;
|
||||||
|
writer.write_all(&(cache.len() as u32).to_le_bytes())?;
|
||||||
|
// lengths of each entry
|
||||||
|
for ((specifier, _), entry) in cache {
|
||||||
|
let len: u64 =
|
||||||
|
entry.data.len() as u64 + specifier.len() as u64 + 1 + 4 + 8 + 8;
|
||||||
|
writer.write_all(&len.to_le_bytes())?;
|
||||||
|
}
|
||||||
|
// entries
|
||||||
|
for ((specifier, code_cache_type), entry) in cache {
|
||||||
|
writer.write_all(&entry.data)?;
|
||||||
|
writer.write_all(&[match code_cache_type {
|
||||||
|
CodeCacheType::EsModule => 0,
|
||||||
|
CodeCacheType::Script => 1,
|
||||||
|
}])?;
|
||||||
|
writer.write_all(specifier.as_bytes())?;
|
||||||
|
writer.write_all(&(specifier.len() as u32).to_le_bytes())?;
|
||||||
|
writer.write_all(&entry.source_hash.to_le_bytes())?;
|
||||||
|
let hash: u64 = FastInsecureHasher::new_without_deno_version()
|
||||||
|
.write(&entry.data)
|
||||||
|
.finish();
|
||||||
|
writer.write_all(&hash.to_le_bytes())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.flush()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize(
|
||||||
|
file_path: &Path,
|
||||||
|
expected_cache_key: u64,
|
||||||
|
) -> Result<HashMap<CodeCacheKey, DenoCompileCodeCacheEntry>, AnyError> {
|
||||||
|
let cache_file = std::fs::File::open(file_path)?;
|
||||||
|
let mut reader = BufReader::new(cache_file);
|
||||||
|
deserialize_with_reader(&mut reader, expected_cache_key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_with_reader<T: Read>(
|
||||||
|
reader: &mut BufReader<T>,
|
||||||
|
expected_cache_key: u64,
|
||||||
|
) -> Result<HashMap<CodeCacheKey, DenoCompileCodeCacheEntry>, AnyError> {
|
||||||
|
// it's very important to use this below so that a corrupt cache file
|
||||||
|
// doesn't cause a memory allocation error
|
||||||
|
fn new_vec_sized<T: Clone>(
|
||||||
|
capacity: usize,
|
||||||
|
default_value: T,
|
||||||
|
) -> Result<Vec<T>, AnyError> {
|
||||||
|
let mut vec = Vec::new();
|
||||||
|
vec.try_reserve(capacity)?;
|
||||||
|
vec.resize(capacity, default_value);
|
||||||
|
Ok(vec)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_subtract(a: usize, b: usize) -> Result<usize, AnyError> {
|
||||||
|
if a < b {
|
||||||
|
bail!("Integer underflow");
|
||||||
|
}
|
||||||
|
Ok(a - b)
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut header_bytes = vec![0; 8 + 4];
|
||||||
|
reader.read_exact(&mut header_bytes)?;
|
||||||
|
let actual_cache_key = u64::from_le_bytes(header_bytes[..8].try_into()?);
|
||||||
|
if actual_cache_key != expected_cache_key {
|
||||||
|
// cache bust
|
||||||
|
bail!("Cache key mismatch");
|
||||||
|
}
|
||||||
|
let len = u32::from_le_bytes(header_bytes[8..].try_into()?) as usize;
|
||||||
|
// read the lengths for each entry found in the file
|
||||||
|
let entry_len_bytes_capacity = len * 8;
|
||||||
|
let mut entry_len_bytes = new_vec_sized(entry_len_bytes_capacity, 0)?;
|
||||||
|
reader.read_exact(&mut entry_len_bytes)?;
|
||||||
|
let mut lengths = Vec::new();
|
||||||
|
lengths.try_reserve(len)?;
|
||||||
|
for i in 0..len {
|
||||||
|
let pos = i * 8;
|
||||||
|
lengths.push(
|
||||||
|
u64::from_le_bytes(entry_len_bytes[pos..pos + 8].try_into()?) as usize,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
map.try_reserve(len)?;
|
||||||
|
for len in lengths {
|
||||||
|
let mut buffer = new_vec_sized(len, 0)?;
|
||||||
|
reader.read_exact(&mut buffer)?;
|
||||||
|
let entry_data_hash_start_pos = try_subtract(buffer.len(), 8)?;
|
||||||
|
let expected_entry_data_hash =
|
||||||
|
u64::from_le_bytes(buffer[entry_data_hash_start_pos..].try_into()?);
|
||||||
|
let source_hash_start_pos = try_subtract(entry_data_hash_start_pos, 8)?;
|
||||||
|
let source_hash = u64::from_le_bytes(
|
||||||
|
buffer[source_hash_start_pos..entry_data_hash_start_pos].try_into()?,
|
||||||
|
);
|
||||||
|
let specifier_end_pos = try_subtract(source_hash_start_pos, 4)?;
|
||||||
|
let specifier_len = u32::from_le_bytes(
|
||||||
|
buffer[specifier_end_pos..source_hash_start_pos].try_into()?,
|
||||||
|
) as usize;
|
||||||
|
let specifier_start_pos = try_subtract(specifier_end_pos, specifier_len)?;
|
||||||
|
let specifier = String::from_utf8(
|
||||||
|
buffer[specifier_start_pos..specifier_end_pos].to_vec(),
|
||||||
|
)?;
|
||||||
|
let code_cache_type_pos = try_subtract(specifier_start_pos, 1)?;
|
||||||
|
let code_cache_type = match buffer[code_cache_type_pos] {
|
||||||
|
0 => CodeCacheType::EsModule,
|
||||||
|
1 => CodeCacheType::Script,
|
||||||
|
_ => bail!("Invalid code cache type"),
|
||||||
|
};
|
||||||
|
buffer.truncate(code_cache_type_pos);
|
||||||
|
let actual_entry_data_hash: u64 =
|
||||||
|
FastInsecureHasher::new_without_deno_version()
|
||||||
|
.write(&buffer)
|
||||||
|
.finish();
|
||||||
|
if expected_entry_data_hash != actual_entry_data_hash {
|
||||||
|
bail!("Hash mismatch.")
|
||||||
|
}
|
||||||
|
map.insert(
|
||||||
|
(specifier, code_cache_type),
|
||||||
|
DenoCompileCodeCacheEntry {
|
||||||
|
source_hash,
|
||||||
|
data: buffer,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(map)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use test_util::TempDir;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use std::fs::File;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_deserialize() {
|
||||||
|
let cache_key = 123456;
|
||||||
|
let cache = {
|
||||||
|
let mut cache = HashMap::new();
|
||||||
|
cache.insert(
|
||||||
|
("specifier1".to_string(), CodeCacheType::EsModule),
|
||||||
|
DenoCompileCodeCacheEntry {
|
||||||
|
source_hash: 1,
|
||||||
|
data: vec![1, 2, 3],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
cache.insert(
|
||||||
|
("specifier2".to_string(), CodeCacheType::EsModule),
|
||||||
|
DenoCompileCodeCacheEntry {
|
||||||
|
source_hash: 2,
|
||||||
|
data: vec![4, 5, 6],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
cache.insert(
|
||||||
|
("specifier2".to_string(), CodeCacheType::Script),
|
||||||
|
DenoCompileCodeCacheEntry {
|
||||||
|
source_hash: 2,
|
||||||
|
data: vec![6, 5, 1],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
cache
|
||||||
|
};
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
serialize_with_writer(&mut BufWriter::new(&mut buffer), cache_key, &cache)
|
||||||
|
.unwrap();
|
||||||
|
let deserialized =
|
||||||
|
deserialize_with_reader(&mut BufReader::new(&buffer[..]), cache_key)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(cache, deserialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_deserialize_empty() {
|
||||||
|
let cache_key = 1234;
|
||||||
|
let cache = HashMap::new();
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
serialize_with_writer(&mut BufWriter::new(&mut buffer), cache_key, &cache)
|
||||||
|
.unwrap();
|
||||||
|
let deserialized =
|
||||||
|
deserialize_with_reader(&mut BufReader::new(&buffer[..]), cache_key)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(cache, deserialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_deserialize_corrupt() {
|
||||||
|
let buffer = "corrupttestingtestingtesting".as_bytes().to_vec();
|
||||||
|
let err = deserialize_with_reader(&mut BufReader::new(&buffer[..]), 1234)
|
||||||
|
.unwrap_err();
|
||||||
|
assert_eq!(err.to_string(), "Cache key mismatch");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn code_cache() {
|
||||||
|
let temp_dir = TempDir::new();
|
||||||
|
let file_path = temp_dir.path().join("cache.bin").to_path_buf();
|
||||||
|
let url1 = ModuleSpecifier::parse("https://deno.land/example1.js").unwrap();
|
||||||
|
let url2 = ModuleSpecifier::parse("https://deno.land/example2.js").unwrap();
|
||||||
|
// first run
|
||||||
|
{
|
||||||
|
let code_cache = DenoCompileCodeCache::new(file_path.clone(), 1234);
|
||||||
|
assert!(code_cache
|
||||||
|
.get_sync(&url1, CodeCacheType::EsModule, 0)
|
||||||
|
.is_none());
|
||||||
|
assert!(code_cache
|
||||||
|
.get_sync(&url2, CodeCacheType::EsModule, 1)
|
||||||
|
.is_none());
|
||||||
|
assert!(code_cache.enabled());
|
||||||
|
code_cache.set_sync(url1.clone(), CodeCacheType::EsModule, 0, &[1, 2, 3]);
|
||||||
|
assert!(code_cache.enabled());
|
||||||
|
assert!(!file_path.exists());
|
||||||
|
code_cache.set_sync(url2.clone(), CodeCacheType::EsModule, 1, &[2, 1, 3]);
|
||||||
|
assert!(file_path.exists()); // now the new code cache exists
|
||||||
|
assert!(!code_cache.enabled()); // no longer enabled
|
||||||
|
}
|
||||||
|
// second run
|
||||||
|
{
|
||||||
|
let code_cache = DenoCompileCodeCache::new(file_path.clone(), 1234);
|
||||||
|
assert!(code_cache.enabled());
|
||||||
|
let result1 = code_cache
|
||||||
|
.get_sync(&url1, CodeCacheType::EsModule, 0)
|
||||||
|
.unwrap();
|
||||||
|
assert!(code_cache.enabled());
|
||||||
|
let result2 = code_cache
|
||||||
|
.get_sync(&url2, CodeCacheType::EsModule, 1)
|
||||||
|
.unwrap();
|
||||||
|
assert!(!code_cache.enabled()); // no longer enabled
|
||||||
|
assert_eq!(result1, vec![1, 2, 3]);
|
||||||
|
assert_eq!(result2, vec![2, 1, 3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// new cache key first run
|
||||||
|
{
|
||||||
|
let code_cache = DenoCompileCodeCache::new(file_path.clone(), 54321);
|
||||||
|
assert!(code_cache
|
||||||
|
.get_sync(&url1, CodeCacheType::EsModule, 0)
|
||||||
|
.is_none());
|
||||||
|
assert!(code_cache
|
||||||
|
.get_sync(&url2, CodeCacheType::EsModule, 1)
|
||||||
|
.is_none());
|
||||||
|
code_cache.set_sync(url1.clone(), CodeCacheType::EsModule, 0, &[2, 2, 3]);
|
||||||
|
code_cache.set_sync(url2.clone(), CodeCacheType::EsModule, 1, &[3, 2, 3]);
|
||||||
|
}
|
||||||
|
// new cache key second run
|
||||||
|
{
|
||||||
|
let code_cache = DenoCompileCodeCache::new(file_path.clone(), 54321);
|
||||||
|
let result1 = code_cache
|
||||||
|
.get_sync(&url1, CodeCacheType::EsModule, 0)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(result1, vec![2, 2, 3]);
|
||||||
|
assert!(code_cache
|
||||||
|
.get_sync(&url2, CodeCacheType::EsModule, 5) // different hash will cause none
|
||||||
|
.is_none());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
use binary::StandaloneData;
|
use binary::StandaloneData;
|
||||||
use binary::StandaloneModules;
|
use binary::StandaloneModules;
|
||||||
|
use code_cache::DenoCompileCodeCache;
|
||||||
use deno_ast::MediaType;
|
use deno_ast::MediaType;
|
||||||
use deno_cache_dir::npm::NpmCacheDir;
|
use deno_cache_dir::npm::NpmCacheDir;
|
||||||
use deno_config::workspace::MappedResolution;
|
use deno_config::workspace::MappedResolution;
|
||||||
@ -17,6 +18,7 @@ use deno_core::anyhow::Context;
|
|||||||
use deno_core::error::generic_error;
|
use deno_core::error::generic_error;
|
||||||
use deno_core::error::type_error;
|
use deno_core::error::type_error;
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
|
use deno_core::futures::future::LocalBoxFuture;
|
||||||
use deno_core::futures::FutureExt;
|
use deno_core::futures::FutureExt;
|
||||||
use deno_core::v8_set_flags;
|
use deno_core::v8_set_flags;
|
||||||
use deno_core::FastString;
|
use deno_core::FastString;
|
||||||
@ -27,6 +29,7 @@ use deno_core::ModuleSpecifier;
|
|||||||
use deno_core::ModuleType;
|
use deno_core::ModuleType;
|
||||||
use deno_core::RequestedModuleType;
|
use deno_core::RequestedModuleType;
|
||||||
use deno_core::ResolutionKind;
|
use deno_core::ResolutionKind;
|
||||||
|
use deno_core::SourceCodeCacheInfo;
|
||||||
use deno_npm::npm_rc::ResolvedNpmRc;
|
use deno_npm::npm_rc::ResolvedNpmRc;
|
||||||
use deno_package_json::PackageJsonDepValue;
|
use deno_package_json::PackageJsonDepValue;
|
||||||
use deno_resolver::npm::NpmReqResolverOptions;
|
use deno_resolver::npm::NpmReqResolverOptions;
|
||||||
@ -64,6 +67,7 @@ use crate::args::StorageKeyResolver;
|
|||||||
use crate::cache::Caches;
|
use crate::cache::Caches;
|
||||||
use crate::cache::DenoCacheEnvFsAdapter;
|
use crate::cache::DenoCacheEnvFsAdapter;
|
||||||
use crate::cache::DenoDirProvider;
|
use crate::cache::DenoDirProvider;
|
||||||
|
use crate::cache::FastInsecureHasher;
|
||||||
use crate::cache::NodeAnalysisCache;
|
use crate::cache::NodeAnalysisCache;
|
||||||
use crate::cache::RealDenoCacheEnv;
|
use crate::cache::RealDenoCacheEnv;
|
||||||
use crate::http_util::HttpClientProvider;
|
use crate::http_util::HttpClientProvider;
|
||||||
@ -86,12 +90,14 @@ use crate::resolver::NpmModuleLoader;
|
|||||||
use crate::util::progress_bar::ProgressBar;
|
use crate::util::progress_bar::ProgressBar;
|
||||||
use crate::util::progress_bar::ProgressBarStyle;
|
use crate::util::progress_bar::ProgressBarStyle;
|
||||||
use crate::util::v8::construct_v8_flags;
|
use crate::util::v8::construct_v8_flags;
|
||||||
|
use crate::worker::CliCodeCache;
|
||||||
use crate::worker::CliMainWorkerFactory;
|
use crate::worker::CliMainWorkerFactory;
|
||||||
use crate::worker::CliMainWorkerOptions;
|
use crate::worker::CliMainWorkerOptions;
|
||||||
use crate::worker::CreateModuleLoaderResult;
|
use crate::worker::CreateModuleLoaderResult;
|
||||||
use crate::worker::ModuleLoaderFactory;
|
use crate::worker::ModuleLoaderFactory;
|
||||||
|
|
||||||
pub mod binary;
|
pub mod binary;
|
||||||
|
mod code_cache;
|
||||||
mod file_system;
|
mod file_system;
|
||||||
mod serialization;
|
mod serialization;
|
||||||
mod virtual_fs;
|
mod virtual_fs;
|
||||||
@ -113,6 +119,35 @@ struct SharedModuleLoaderState {
|
|||||||
npm_req_resolver: Arc<CliNpmReqResolver>,
|
npm_req_resolver: Arc<CliNpmReqResolver>,
|
||||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||||
workspace_resolver: WorkspaceResolver,
|
workspace_resolver: WorkspaceResolver,
|
||||||
|
code_cache: Option<Arc<dyn CliCodeCache>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SharedModuleLoaderState {
|
||||||
|
fn get_code_cache(
|
||||||
|
&self,
|
||||||
|
specifier: &ModuleSpecifier,
|
||||||
|
source: &[u8],
|
||||||
|
) -> Option<SourceCodeCacheInfo> {
|
||||||
|
let Some(code_cache) = &self.code_cache else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
if !code_cache.enabled() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
// deno version is already included in the root cache key
|
||||||
|
let hash = FastInsecureHasher::new_without_deno_version()
|
||||||
|
.write_hashable(source)
|
||||||
|
.finish();
|
||||||
|
let data = code_cache.get_sync(
|
||||||
|
specifier,
|
||||||
|
deno_runtime::code_cache::CodeCacheType::EsModule,
|
||||||
|
hash,
|
||||||
|
);
|
||||||
|
Some(SourceCodeCacheInfo {
|
||||||
|
hash,
|
||||||
|
data: data.map(Cow::Owned),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -329,14 +364,19 @@ impl ModuleLoader for EmbeddedModuleLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if self.shared.node_resolver.in_npm_package(original_specifier) {
|
if self.shared.node_resolver.in_npm_package(original_specifier) {
|
||||||
let npm_module_loader = self.shared.npm_module_loader.clone();
|
let shared = self.shared.clone();
|
||||||
let original_specifier = original_specifier.clone();
|
let original_specifier = original_specifier.clone();
|
||||||
let maybe_referrer = maybe_referrer.cloned();
|
let maybe_referrer = maybe_referrer.cloned();
|
||||||
return deno_core::ModuleLoadResponse::Async(
|
return deno_core::ModuleLoadResponse::Async(
|
||||||
async move {
|
async move {
|
||||||
let code_source = npm_module_loader
|
let code_source = shared
|
||||||
|
.npm_module_loader
|
||||||
.load(&original_specifier, maybe_referrer.as_ref())
|
.load(&original_specifier, maybe_referrer.as_ref())
|
||||||
.await?;
|
.await?;
|
||||||
|
let code_cache_entry = shared.get_code_cache(
|
||||||
|
&code_source.found_url,
|
||||||
|
code_source.code.as_bytes(),
|
||||||
|
);
|
||||||
Ok(deno_core::ModuleSource::new_with_redirect(
|
Ok(deno_core::ModuleSource::new_with_redirect(
|
||||||
match code_source.media_type {
|
match code_source.media_type {
|
||||||
MediaType::Json => ModuleType::Json,
|
MediaType::Json => ModuleType::Json,
|
||||||
@ -345,7 +385,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
|
|||||||
code_source.code,
|
code_source.code,
|
||||||
&original_specifier,
|
&original_specifier,
|
||||||
&code_source.found_url,
|
&code_source.found_url,
|
||||||
None,
|
code_cache_entry,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
.boxed_local(),
|
.boxed_local(),
|
||||||
@ -398,25 +438,30 @@ impl ModuleLoader for EmbeddedModuleLoader {
|
|||||||
ModuleSourceCode::String(FastString::from_static(source))
|
ModuleSourceCode::String(FastString::from_static(source))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let code_cache_entry = shared
|
||||||
|
.get_code_cache(&module_specifier, module_source.as_bytes());
|
||||||
Ok(deno_core::ModuleSource::new_with_redirect(
|
Ok(deno_core::ModuleSource::new_with_redirect(
|
||||||
module_type,
|
module_type,
|
||||||
module_source,
|
module_source,
|
||||||
&original_specifier,
|
&original_specifier,
|
||||||
&module_specifier,
|
&module_specifier,
|
||||||
None,
|
code_cache_entry,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
.boxed_local(),
|
.boxed_local(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let module_source = module_source.into_for_v8();
|
let module_source = module_source.into_for_v8();
|
||||||
|
let code_cache_entry = self
|
||||||
|
.shared
|
||||||
|
.get_code_cache(module_specifier, module_source.as_bytes());
|
||||||
deno_core::ModuleLoadResponse::Sync(Ok(
|
deno_core::ModuleLoadResponse::Sync(Ok(
|
||||||
deno_core::ModuleSource::new_with_redirect(
|
deno_core::ModuleSource::new_with_redirect(
|
||||||
module_type,
|
module_type,
|
||||||
module_source,
|
module_source,
|
||||||
original_specifier,
|
original_specifier,
|
||||||
module_specifier,
|
module_specifier,
|
||||||
None,
|
code_cache_entry,
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -429,6 +474,23 @@ impl ModuleLoader for EmbeddedModuleLoader {
|
|||||||
))),
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn code_cache_ready(
|
||||||
|
&self,
|
||||||
|
specifier: ModuleSpecifier,
|
||||||
|
source_hash: u64,
|
||||||
|
code_cache_data: &[u8],
|
||||||
|
) -> LocalBoxFuture<'static, ()> {
|
||||||
|
if let Some(code_cache) = &self.shared.code_cache {
|
||||||
|
code_cache.set_sync(
|
||||||
|
specifier,
|
||||||
|
deno_runtime::code_cache::CodeCacheType::EsModule,
|
||||||
|
source_hash,
|
||||||
|
code_cache_data,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
std::future::ready(()).boxed_local()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NodeRequireLoader for EmbeddedModuleLoader {
|
impl NodeRequireLoader for EmbeddedModuleLoader {
|
||||||
@ -739,6 +801,19 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
|
|||||||
metadata.workspace_resolver.pkg_json_resolution,
|
metadata.workspace_resolver.pkg_json_resolution,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
let code_cache = match metadata.code_cache_key {
|
||||||
|
Some(code_cache_key) => Some(Arc::new(DenoCompileCodeCache::new(
|
||||||
|
root_path.with_file_name(format!(
|
||||||
|
"{}.cache",
|
||||||
|
root_path.file_name().unwrap().to_string_lossy()
|
||||||
|
)),
|
||||||
|
code_cache_key,
|
||||||
|
)) as Arc<dyn CliCodeCache>),
|
||||||
|
None => {
|
||||||
|
log::debug!("Code cache disabled.");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
let module_loader_factory = StandaloneModuleLoaderFactory {
|
let module_loader_factory = StandaloneModuleLoaderFactory {
|
||||||
shared: Arc::new(SharedModuleLoaderState {
|
shared: Arc::new(SharedModuleLoaderState {
|
||||||
cjs_tracker: cjs_tracker.clone(),
|
cjs_tracker: cjs_tracker.clone(),
|
||||||
@ -751,6 +826,7 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
|
|||||||
fs.clone(),
|
fs.clone(),
|
||||||
node_code_translator,
|
node_code_translator,
|
||||||
)),
|
)),
|
||||||
|
code_cache: code_cache.clone(),
|
||||||
npm_resolver: npm_resolver.clone(),
|
npm_resolver: npm_resolver.clone(),
|
||||||
workspace_resolver,
|
workspace_resolver,
|
||||||
npm_req_resolver,
|
npm_req_resolver,
|
||||||
@ -792,8 +868,7 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
|
|||||||
});
|
});
|
||||||
let worker_factory = CliMainWorkerFactory::new(
|
let worker_factory = CliMainWorkerFactory::new(
|
||||||
Arc::new(BlobStore::default()),
|
Arc::new(BlobStore::default()),
|
||||||
// Code cache is not supported for standalone binary yet.
|
code_cache,
|
||||||
None,
|
|
||||||
feature_checker,
|
feature_checker,
|
||||||
fs,
|
fs,
|
||||||
None,
|
None,
|
||||||
|
@ -83,6 +83,15 @@ pub trait HmrRunner: Send + Sync {
|
|||||||
async fn run(&mut self) -> Result<(), AnyError>;
|
async fn run(&mut self) -> Result<(), AnyError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait CliCodeCache: code_cache::CodeCache {
|
||||||
|
/// Gets if the code cache is still enabled.
|
||||||
|
fn enabled(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_code_cache(self: Arc<Self>) -> Arc<dyn code_cache::CodeCache>;
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
pub trait CoverageCollector: Send + Sync {
|
pub trait CoverageCollector: Send + Sync {
|
||||||
async fn start_collecting(&mut self) -> Result<(), AnyError>;
|
async fn start_collecting(&mut self) -> Result<(), AnyError>;
|
||||||
@ -127,7 +136,7 @@ pub struct CliMainWorkerOptions {
|
|||||||
struct SharedWorkerState {
|
struct SharedWorkerState {
|
||||||
blob_store: Arc<BlobStore>,
|
blob_store: Arc<BlobStore>,
|
||||||
broadcast_channel: InMemoryBroadcastChannel,
|
broadcast_channel: InMemoryBroadcastChannel,
|
||||||
code_cache: Option<Arc<dyn code_cache::CodeCache>>,
|
code_cache: Option<Arc<dyn CliCodeCache>>,
|
||||||
compiled_wasm_module_store: CompiledWasmModuleStore,
|
compiled_wasm_module_store: CompiledWasmModuleStore,
|
||||||
feature_checker: Arc<FeatureChecker>,
|
feature_checker: Arc<FeatureChecker>,
|
||||||
fs: Arc<dyn deno_fs::FileSystem>,
|
fs: Arc<dyn deno_fs::FileSystem>,
|
||||||
@ -393,7 +402,7 @@ impl CliMainWorkerFactory {
|
|||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
blob_store: Arc<BlobStore>,
|
blob_store: Arc<BlobStore>,
|
||||||
code_cache: Option<Arc<dyn code_cache::CodeCache>>,
|
code_cache: Option<Arc<dyn CliCodeCache>>,
|
||||||
feature_checker: Arc<FeatureChecker>,
|
feature_checker: Arc<FeatureChecker>,
|
||||||
fs: Arc<dyn deno_fs::FileSystem>,
|
fs: Arc<dyn deno_fs::FileSystem>,
|
||||||
maybe_file_watcher_communicator: Option<Arc<WatcherCommunicator>>,
|
maybe_file_watcher_communicator: Option<Arc<WatcherCommunicator>>,
|
||||||
@ -554,7 +563,7 @@ impl CliMainWorkerFactory {
|
|||||||
),
|
),
|
||||||
feature_checker,
|
feature_checker,
|
||||||
permissions,
|
permissions,
|
||||||
v8_code_cache: shared.code_cache.clone(),
|
v8_code_cache: shared.code_cache.clone().map(|c| c.as_code_cache()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let options = WorkerOptions {
|
let options = WorkerOptions {
|
||||||
|
@ -2,20 +2,12 @@
|
|||||||
|
|
||||||
use deno_core::ModuleSpecifier;
|
use deno_core::ModuleSpecifier;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum CodeCacheType {
|
pub enum CodeCacheType {
|
||||||
EsModule,
|
EsModule,
|
||||||
Script,
|
Script,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CodeCacheType {
|
|
||||||
pub fn as_str(&self) -> &str {
|
|
||||||
match self {
|
|
||||||
Self::EsModule => "esmodule",
|
|
||||||
Self::Script => "script",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait CodeCache: Send + Sync {
|
pub trait CodeCache: Send + Sync {
|
||||||
fn get_sync(
|
fn get_sync(
|
||||||
&self,
|
&self,
|
||||||
@ -23,6 +15,7 @@ pub trait CodeCache: Send + Sync {
|
|||||||
code_cache_type: CodeCacheType,
|
code_cache_type: CodeCacheType,
|
||||||
source_hash: u64,
|
source_hash: u64,
|
||||||
) -> Option<Vec<u8>>;
|
) -> Option<Vec<u8>>;
|
||||||
|
|
||||||
fn set_sync(
|
fn set_sync(
|
||||||
&self,
|
&self,
|
||||||
specifier: ModuleSpecifier,
|
specifier: ModuleSpecifier,
|
||||||
|
32
tests/specs/compile/code_cache/__test__.jsonc
Normal file
32
tests/specs/compile/code_cache/__test__.jsonc
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"tempDir": true,
|
||||||
|
"steps": [{
|
||||||
|
"if": "unix",
|
||||||
|
"args": "compile --output using_code_cache --log-level=debug main.ts",
|
||||||
|
"output": "[WILDCARD]"
|
||||||
|
}, {
|
||||||
|
"if": "unix",
|
||||||
|
"commandName": "./using_code_cache",
|
||||||
|
"args": [],
|
||||||
|
"output": "first_run.out"
|
||||||
|
}, {
|
||||||
|
"if": "unix",
|
||||||
|
"commandName": "./using_code_cache",
|
||||||
|
"args": [],
|
||||||
|
"output": "second_run.out"
|
||||||
|
}, {
|
||||||
|
"if": "windows",
|
||||||
|
"args": "compile --output using_code_cache.exe --log-level=debug main.ts",
|
||||||
|
"output": "[WILDCARD]"
|
||||||
|
}, {
|
||||||
|
"if": "windows",
|
||||||
|
"commandName": "./using_code_cache.exe",
|
||||||
|
"args": [],
|
||||||
|
"output": "first_run.out"
|
||||||
|
}, {
|
||||||
|
"if": "windows",
|
||||||
|
"commandName": "./using_code_cache.exe",
|
||||||
|
"args": [],
|
||||||
|
"output": "second_run.out"
|
||||||
|
}]
|
||||||
|
}
|
1
tests/specs/compile/code_cache/first_run.out
Normal file
1
tests/specs/compile/code_cache/first_run.out
Normal file
@ -0,0 +1 @@
|
|||||||
|
[WILDCARD]Serialized 9 code cache entries[WILDCARD]
|
3
tests/specs/compile/code_cache/main.ts
Normal file
3
tests/specs/compile/code_cache/main.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { join } from "jsr:@std/url@0.220/join";
|
||||||
|
|
||||||
|
console.log(join);
|
1
tests/specs/compile/code_cache/second_run.out
Normal file
1
tests/specs/compile/code_cache/second_run.out
Normal file
@ -0,0 +1 @@
|
|||||||
|
[WILDCARD]Loaded 9 code cache entries[WILDCARD][Function: join][WILDCARD]
|
22
tests/specs/compile/no_code_cache/__test__.jsonc
Normal file
22
tests/specs/compile/no_code_cache/__test__.jsonc
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"tempDir": true,
|
||||||
|
"steps": [{
|
||||||
|
"if": "unix",
|
||||||
|
"args": "compile --output no_code_cache --no-code-cache --log-level=debug main.ts",
|
||||||
|
"output": "[WILDCARD]"
|
||||||
|
}, {
|
||||||
|
"if": "unix",
|
||||||
|
"commandName": "./no_code_cache",
|
||||||
|
"args": [],
|
||||||
|
"output": "main.out"
|
||||||
|
}, {
|
||||||
|
"if": "windows",
|
||||||
|
"args": "compile --output no_code_cache.exe --no-code-cache --log-level=debug main.ts",
|
||||||
|
"output": "[WILDCARD]"
|
||||||
|
}, {
|
||||||
|
"if": "windows",
|
||||||
|
"commandName": "./no_code_cache.exe",
|
||||||
|
"args": [],
|
||||||
|
"output": "main.out"
|
||||||
|
}]
|
||||||
|
}
|
1
tests/specs/compile/no_code_cache/main.out
Normal file
1
tests/specs/compile/no_code_cache/main.out
Normal file
@ -0,0 +1 @@
|
|||||||
|
[WILDCARD]Code cache disabled.[WILDCARD]
|
3
tests/specs/compile/no_code_cache/main.ts
Normal file
3
tests/specs/compile/no_code_cache/main.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { join } from "jsr:@std/url@0.220/join";
|
||||||
|
|
||||||
|
console.log(join);
|
Loading…
Reference in New Issue
Block a user