Skip to content

Commit

Permalink
perf: do not clone swc Program when transpiling (denoland#23365)
Browse files Browse the repository at this point in the history
  • Loading branch information
dsherret authored and littledivy committed Apr 19, 2024
1 parent 2e8c441 commit 7f6ca12
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 37 deletions.
51 changes: 34 additions & 17 deletions cli/cache/parsed_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ use deno_ast::ModuleSpecifier;
use deno_ast::ParsedSource;
use deno_core::parking_lot::Mutex;
use deno_graph::CapturingModuleParser;
use deno_graph::DefaultModuleParser;
use deno_graph::ModuleParser;
use deno_graph::ParseOptions;
use deno_graph::ParsedSourceStore;

/// Lazily parses JS/TS sources from a `deno_graph::ModuleGraph` given
/// a `ParsedSourceCache`. Note that deno_graph doesn't necessarily cause
Expand Down Expand Up @@ -54,30 +56,38 @@ impl ParsedSourceCache {
&self,
module: &deno_graph::JsModule,
) -> Result<ParsedSource, deno_ast::ParseDiagnostic> {
self.get_or_parse_module(
&module.specifier,
module.source.clone(),
module.media_type,
)
let parser = self.as_capturing_parser();
// this will conditionally parse because it's using a CapturingModuleParser
parser.parse_module(ParseOptions {
specifier: &module.specifier,
source: module.source.clone(),
media_type: module.media_type,
// don't bother enabling because this method is currently only used for vendoring
scope_analysis: false,
})
}

/// Gets the matching `ParsedSource` from the cache
/// or parses a new one and stores that in the cache.
pub fn get_or_parse_module(
pub fn remove_or_parse_module(
&self,
specifier: &deno_graph::ModuleSpecifier,
specifier: &ModuleSpecifier,
source: Arc<str>,
media_type: MediaType,
) -> deno_core::anyhow::Result<ParsedSource, deno_ast::ParseDiagnostic> {
let parser = self.as_capturing_parser();
// this will conditionally parse because it's using a CapturingModuleParser
parser.parse_module(ParseOptions {
) -> Result<ParsedSource, deno_ast::ParseDiagnostic> {
if let Some(parsed_source) = self.remove_parsed_source(specifier) {
if parsed_source.media_type() == media_type
&& parsed_source.text_info().text_str() == source.as_ref()
{
return Ok(parsed_source);
}
}
let options = ParseOptions {
specifier,
source,
media_type,
// don't bother enabling because this method is currently only used for emitting
scope_analysis: false,
})
};
DefaultModuleParser.parse_module(options)
}

/// Frees the parsed source from memory.
Expand All @@ -100,22 +110,29 @@ impl ParsedSourceCache {
impl deno_graph::ParsedSourceStore for ParsedSourceCache {
fn set_parsed_source(
&self,
specifier: deno_graph::ModuleSpecifier,
specifier: ModuleSpecifier,
parsed_source: ParsedSource,
) -> Option<ParsedSource> {
self.sources.lock().insert(specifier, parsed_source)
}

fn get_parsed_source(
&self,
specifier: &deno_graph::ModuleSpecifier,
specifier: &ModuleSpecifier,
) -> Option<ParsedSource> {
self.sources.lock().get(specifier).cloned()
}

fn remove_parsed_source(
&self,
specifier: &ModuleSpecifier,
) -> Option<ParsedSource> {
self.sources.lock().remove(specifier)
}

fn get_scope_analysis_parsed_source(
&self,
specifier: &deno_graph::ModuleSpecifier,
specifier: &ModuleSpecifier,
) -> Option<ParsedSource> {
let mut sources = self.sources.lock();
let parsed_source = sources.get(specifier)?;
Expand Down
24 changes: 17 additions & 7 deletions cli/emit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,24 @@ impl Emitter {
{
Ok(emit_code.into())
} else {
// this will use a cached version if it exists
let parsed_source = self.parsed_source_cache.get_or_parse_module(
// nothing else needs the parsed source at this point, so remove from
// the cache in order to not transpile owned
let parsed_source = self.parsed_source_cache.remove_or_parse_module(
specifier,
source.clone(),
media_type,
)?;
let transpiled_source =
parsed_source.transpile(&self.transpile_options, &self.emit_options)?;
let transpiled_source = match parsed_source
.transpile_owned(&self.transpile_options, &self.emit_options)
{
Ok(result) => result?,
Err(parsed_source) => {
// transpile_owned is more efficient and should be preferred
debug_assert!(false, "Transpile owned failed.");
parsed_source
.transpile(&self.transpile_options, &self.emit_options)?
}
};
debug_assert!(transpiled_source.source_map.is_none());
self.emit_cache.set_emit_code(
specifier,
Expand All @@ -124,11 +134,11 @@ impl Emitter {
let source_arc: Arc<str> = source_code.into();
let parsed_source = self
.parsed_source_cache
.get_or_parse_module(specifier, source_arc, media_type)?;
.remove_or_parse_module(specifier, source_arc, media_type)?;
let mut options = self.emit_options;
options.source_map = SourceMapOption::None;
let transpiled_source =
parsed_source.transpile(&self.transpile_options, &options)?;
let transpiled_source = parsed_source
.transpile_owned_with_fallback(&self.transpile_options, &options)?;
Ok(transpiled_source.text)
}

Expand Down
28 changes: 15 additions & 13 deletions runtime/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,20 +95,22 @@ pub fn maybe_transpile_source(
scope_analysis: false,
maybe_syntax: None,
})?;
let transpiled_source = parsed.transpile(
&deno_ast::TranspileOptions {
imports_not_used_as_values: deno_ast::ImportsNotUsedAsValues::Remove,
..Default::default()
},
&deno_ast::EmitOptions {
source_map: if cfg!(debug_assertions) {
SourceMapOption::Separate
} else {
SourceMapOption::None
let transpiled_source = parsed
.transpile_owned(
&deno_ast::TranspileOptions {
imports_not_used_as_values: deno_ast::ImportsNotUsedAsValues::Remove,
..Default::default()
},
..Default::default()
},
)?;
&deno_ast::EmitOptions {
source_map: if cfg!(debug_assertions) {
SourceMapOption::Separate
} else {
SourceMapOption::None
},
..Default::default()
},
)
.unwrap()?;

let maybe_source_map: Option<SourceMapData> = transpiled_source
.source_map
Expand Down

0 comments on commit 7f6ca12

Please sign in to comment.