Skip to content

Commit

Permalink
perf(check): faster source hashing (denoland#18534)
Browse files Browse the repository at this point in the history
  • Loading branch information
dsherret committed Apr 1, 2023
1 parent 23b9be7 commit ae1ba2a
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 48 deletions.
10 changes: 6 additions & 4 deletions cli/tools/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,12 @@ pub fn check(
// to make tsc build info work, we need to consistently hash modules, so that
// tsc can better determine if an emit is still valid or not, so we provide
// that data here.
let hash_data = vec![
options.ts_config.as_bytes(),
version::deno().as_bytes().to_owned(),
];
let hash_data = {
let mut hasher = FastInsecureHasher::new();
hasher.write(&options.ts_config.as_bytes());
hasher.write_str(version::deno());
hasher.finish()
};

let response = tsc::exec(tsc::Request {
config: options.ts_config,
Expand Down
2 changes: 1 addition & 1 deletion cli/tsc/99_main_compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,7 @@ delete Object.prototype.__proto__;
}
},
createHash(data) {
return ops.op_create_hash({ data }).hash;
return ops.op_create_hash(data);
},

// LanguageServiceHost
Expand Down
68 changes: 25 additions & 43 deletions cli/tsc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use crate::args::TsConfig;
use crate::args::TypeCheckMode;
use crate::cache::FastInsecureHasher;
use crate::node;
use crate::node::node_resolve_npm_reference;
use crate::node::NodeResolution;
Expand Down Expand Up @@ -241,15 +242,16 @@ fn get_lazily_loaded_asset(asset: &str) -> Option<&'static str> {

fn get_maybe_hash(
maybe_source: Option<&str>,
hash_data: &[Vec<u8>],
hash_data: u64,
) -> Option<String> {
if let Some(source) = maybe_source {
let mut data = vec![source.as_bytes().to_owned()];
data.extend_from_slice(hash_data);
Some(checksum::gen(&data))
} else {
None
}
maybe_source.map(|source| get_hash(source, hash_data))
}

fn get_hash(source: &str, hash_data: u64) -> String {
let mut hasher = FastInsecureHasher::new();
hasher.write_str(source);
hasher.write_u64(hash_data);
hasher.finish().to_string()
}

/// Hash the URL so it can be sent to `tsc` in a supportable way
Expand Down Expand Up @@ -303,7 +305,7 @@ pub struct Request {
/// Indicates to the tsc runtime if debug logging should occur.
pub debug: bool,
pub graph: Arc<ModuleGraph>,
pub hash_data: Vec<Vec<u8>>,
pub hash_data: u64,
pub maybe_npm_resolver: Option<NpmPackageResolver>,
pub maybe_tsbuildinfo: Option<String>,
/// A vector of strings that represent the root/entry point modules for the
Expand All @@ -324,7 +326,7 @@ pub struct Response {

#[derive(Debug, Default)]
struct State {
hash_data: Vec<Vec<u8>>,
hash_data: u64,
graph: Arc<ModuleGraph>,
maybe_tsbuildinfo: Option<String>,
maybe_response: Option<RespondArgs>,
Expand All @@ -337,7 +339,7 @@ struct State {
impl State {
pub fn new(
graph: Arc<ModuleGraph>,
hash_data: Vec<Vec<u8>>,
hash_data: u64,
maybe_npm_resolver: Option<NpmPackageResolver>,
maybe_tsbuildinfo: Option<String>,
root_map: HashMap<String, ModuleSpecifier>,
Expand All @@ -364,23 +366,10 @@ fn normalize_specifier(
resolve_url_or_path(specifier, current_dir).map_err(|err| err.into())
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct CreateHashArgs {
/// The string data to be used to generate the hash. This will be mixed with
/// other state data in Deno to derive the final hash.
data: String,
}

#[op]
fn op_create_hash(s: &mut OpState, args: Value) -> Result<Value, AnyError> {
fn op_create_hash(s: &mut OpState, text: &str) -> String {
let state = s.borrow_mut::<State>();
let v: CreateHashArgs = serde_json::from_value(args)
.context("Invalid request from JavaScript for \"op_create_hash\".")?;
let mut data = vec![v.data.as_bytes().to_owned()];
data.extend_from_slice(&state.hash_data);
let hash = checksum::gen(&data);
Ok(json!({ "hash": hash }))
get_hash(text, state.hash_data)
}

#[derive(Debug, Deserialize)]
Expand Down Expand Up @@ -455,7 +444,7 @@ fn op_load(state: &mut OpState, args: Value) -> Result<Value, AnyError> {
Some(Cow::Borrowed("declare const __: any;\nexport = __;\n"))
} else if let Some(name) = v.specifier.strip_prefix("asset:https:///") {
let maybe_source = get_lazily_loaded_asset(name);
hash = get_maybe_hash(maybe_source, &state.hash_data);
hash = get_maybe_hash(maybe_source, state.hash_data);
media_type = MediaType::from_str(&v.specifier);
maybe_source.map(Cow::Borrowed)
} else {
Expand Down Expand Up @@ -507,7 +496,7 @@ fn op_load(state: &mut OpState, args: Value) -> Result<Value, AnyError> {
media_type = MediaType::Unknown;
None
};
hash = get_maybe_hash(maybe_source.as_deref(), &state.hash_data);
hash = get_maybe_hash(maybe_source.as_deref(), state.hash_data);
maybe_source
};

Expand Down Expand Up @@ -902,12 +891,12 @@ mod tests {

async fn setup(
maybe_specifier: Option<ModuleSpecifier>,
maybe_hash_data: Option<Vec<Vec<u8>>>,
maybe_hash_data: Option<u64>,
maybe_tsbuildinfo: Option<String>,
) -> OpState {
let specifier = maybe_specifier
.unwrap_or_else(|| ModuleSpecifier::parse("file:https:///main.ts").unwrap());
let hash_data = maybe_hash_data.unwrap_or_else(|| vec![b"".to_vec()]);
let hash_data = maybe_hash_data.unwrap_or(0);
let fixtures = test_util::testdata_path().join("tsc2");
let mut loader = MockLoader { fixtures };
let mut graph = ModuleGraph::default();
Expand All @@ -933,7 +922,7 @@ mod tests {
async fn test_exec(
specifier: &ModuleSpecifier,
) -> Result<Response, AnyError> {
let hash_data = vec![b"something".to_vec()];
let hash_data = 123; // something random
let fixtures = test_util::testdata_path().join("tsc2");
let mut loader = MockLoader { fixtures };
let mut graph = ModuleGraph::default();
Expand Down Expand Up @@ -999,16 +988,9 @@ mod tests {

#[tokio::test]
async fn test_create_hash() {
let mut state = setup(None, Some(vec![b"something".to_vec()]), None).await;
let actual = op_create_hash::call(
&mut state,
json!({ "data": "some sort of content" }),
)
.expect("could not invoke op");
assert_eq!(
actual,
json!({"hash": "ae92df8f104748768838916857a1623b6a3c593110131b0a00f81ad9dac16511"})
);
let mut state = setup(None, Some(123), None).await;
let actual = op_create_hash::call(&mut state, "some sort of content");
assert_eq!(actual, "11905938177474799758");
}

#[test]
Expand Down Expand Up @@ -1050,12 +1032,12 @@ mod tests {
&mut state,
json!({ "specifier": "https://deno.land/x/mod.ts"}),
)
.expect("should have invoked op");
.unwrap();
assert_eq!(
actual,
json!({
"data": "console.log(\"hello deno\");\n",
"version": "149c777056afcc973d5fcbe11421b6d5ddc57b81786765302030d7fc893bf729",
"version": "7821807483407828376",
"scriptKind": 3,
})
);
Expand Down

0 comments on commit ae1ba2a

Please sign in to comment.