Repository: denoland/deno
Pull request: perf: do not clone swc `Program` when transpiling


Changed files:
filename: cli/cache/parsed_source.rs
patch: @@ -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
@@ -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.
@@ -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)?;

filename: cli/emit.rs
patch: @@ -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,
@@ -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)
   }
 

filename: runtime/shared.rs
patch: @@ -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