refactor(plugin): Refactor transform executor (#5147)

This commit is contained in:
OJ Kwon 2022-07-07 23:02:42 -07:00 committed by GitHub
parent 07ffb76793
commit e8214babf5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 180 additions and 156 deletions

View File

@ -95,8 +95,10 @@ impl RustPlugins {
inner: self.comments.clone(),
},
|| {
let span = tracing::span!(tracing::Level::INFO, "serialize_program").entered();
let program = VersionedSerializable::new(n);
let mut serialized = PluginSerializedBytes::try_serialize(&program)?;
let mut serialized_program = PluginSerializedBytes::try_serialize(&program)?;
drop(span);
// Run plugin transformation against current program.
// We do not serialize / deserialize between each plugin execution but
@ -106,29 +108,6 @@ impl RustPlugins {
// transform.
if let Some(plugins) = &self.plugins {
for p in plugins {
let span = tracing::span!(
tracing::Level::INFO,
"serialize_context",
plugin_module = p.0.as_str()
);
let context_span_guard = span.enter();
let config_json = serde_json::to_string(&p.1)
.context("Failed to serialize plugin config as json")
.and_then(|value| {
PluginSerializedBytes::try_serialize(&VersionedSerializable::new(
value,
))
})?;
let context_json = serde_json::to_string(&self.plugin_context)
.context("Failed to serialize plugin context as json")
.and_then(|value| {
PluginSerializedBytes::try_serialize(&VersionedSerializable::new(
value,
))
})?;
let resolved_path = self
.resolver
.as_ref()
@ -140,31 +119,66 @@ impl RustPlugins {
} else {
anyhow::bail!("Failed to resolve plugin path: {:?}", resolved_path);
};
let mut transform_plugin_executor =
swc_plugin_runner::create_plugin_transform_executor(
&path,
&swc_plugin_runner::cache::PLUGIN_MODULE_CACHE,
&self.source_map,
)?;
let span = tracing::span!(
tracing::Level::INFO,
"serialize_context",
plugin_module = p.0.as_str()
);
let context_span_guard = span.enter();
let serialized_config_json = serde_json::to_string(&p.1)
.context("Failed to serialize plugin config as json")
.and_then(|value| {
PluginSerializedBytes::try_serialize(&VersionedSerializable::new(
value,
))
})?;
let serialized_context_json = serde_json::to_string(&self.plugin_context)
.context("Failed to serialize plugin context as json")
.and_then(|value| {
PluginSerializedBytes::try_serialize(&VersionedSerializable::new(
value,
))
})?;
drop(context_span_guard);
let span = tracing::span!(
tracing::Level::INFO,
"execute_plugin_runner",
plugin_module = p.0.as_str()
);
let transform_span_guard = span.enter();
serialized = swc_plugin_runner::apply_transform_plugin(
&p.0,
&path,
&swc_plugin_runner::cache::PLUGIN_MODULE_CACHE,
serialized,
config_json,
context_json,
should_enable_comments_proxy,
&self.source_map,
)?;
drop(transform_span_guard);
)
.entered();
serialized_program = transform_plugin_executor
.transform(
&serialized_program,
&serialized_config_json,
&serialized_context_json,
should_enable_comments_proxy,
)
.with_context(|| {
format!(
"failed to invoke `{}` as js transform plugin at {}",
&p.0,
path.display()
)
})?;
drop(span);
}
}
// Plugin transformation is done. Deserialize transformed bytes back
// into Program
serialized.deserialize().map(|v| v.into_inner())
serialized_program.deserialize().map(|v| v.into_inner())
},
)
}
@ -188,11 +202,11 @@ impl RustPlugins {
},
|| {
let program = VersionedSerializable::new(n);
let mut serialized = PluginSerializedBytes::try_serialize(&program)?;
let mut serialized_program = PluginSerializedBytes::try_serialize(&program)?;
if let Some(plugins) = &self.plugins {
for p in plugins {
let config_json = serde_json::to_string(&p.1)
let serialized_config_json = serde_json::to_string(&p.1)
.context("Failed to serialize plugin config as json")
.and_then(|value| {
PluginSerializedBytes::try_serialize(&VersionedSerializable::new(
@ -200,7 +214,7 @@ impl RustPlugins {
))
})?;
let context_json = serde_json::to_string(&self.plugin_context)
let serialized_context_json = serde_json::to_string(&self.plugin_context)
.context("Failed to serialize plugin context as json")
.and_then(|value| {
PluginSerializedBytes::try_serialize(&VersionedSerializable::new(
@ -208,20 +222,27 @@ impl RustPlugins {
))
})?;
serialized = swc_plugin_runner::apply_transform_plugin(
&p.0,
&PathBuf::from(&p.0),
&swc_plugin_runner::cache::PLUGIN_MODULE_CACHE,
serialized,
config_json,
context_json,
should_enable_comments_proxy,
&self.source_map,
)?;
let mut transform_plugin_executor =
swc_plugin_runner::create_plugin_transform_executor(
&PathBuf::from(&p.0),
&swc_plugin_runner::cache::PLUGIN_MODULE_CACHE,
&self.source_map,
)?;
serialized_program = transform_plugin_executor
.transform(
&serialized_program,
&serialized_config_json,
&serialized_context_json,
should_enable_comments_proxy,
)
.with_context(|| {
format!("failed to invoke `{}` as js transform plugin", &p.0)
})?;
}
}
serialized.deserialize().map(|v| v.into_inner())
serialized_program.deserialize().map(|v| v.into_inner())
},
)
}

View File

@ -228,7 +228,7 @@ impl VisitMut for TransformVisitor {
/// as returning ptr back to host.
///
/// It is possible to opt out from macro by writing transform fn manually via
/// `__plugin_process_impl(
/// `__transform_plugin_process_impl(
/// ast_ptr: *const u8,
/// ast_ptr_len: i32,
/// config_str_ptr: *const u8,

View File

@ -19,7 +19,8 @@ pub fn plugin_transform(
fn handle_func(func: ItemFn) -> TokenStream {
let ident = func.sig.ident.clone();
let process_impl_ident = Ident::new("__plugin_process_impl", Span::call_site());
let transform_process_impl_ident =
Ident::new("__transform_plugin_process_impl", Span::call_site());
let ret = quote! {
#func
@ -59,7 +60,7 @@ fn handle_func(func: ItemFn) -> TokenStream {
// There are some cases error won't be wrapped up however - for example, we expect
// serialization of PluginError itself should succeed.
#[no_mangle]
pub fn #process_impl_ident(ast_ptr: *const u8, ast_ptr_len: i32, config_str_ptr: *const u8, config_str_ptr_len: i32, context_str_ptr: *const u8, context_str_ptr_len: i32, should_enable_comments_proxy: i32) -> i32 {
pub fn #transform_process_impl_ident(ast_ptr: *const u8, ast_ptr_len: i32, config_str_ptr: *const u8, config_str_ptr_len: i32, context_str_ptr: *const u8, context_str_ptr_len: i32, should_enable_comments_proxy: i32) -> i32 {
// Reconstruct `Program` & config string from serialized program
// Host (SWC) should allocate memory, copy bytes and pass ptr to plugin.
let program = unsafe { swc_plugin::deserialize_from_ptr(ast_ptr, ast_ptr_len).map(|v| v.into_inner()) };

View File

@ -5,8 +5,8 @@
/// specifically `SingleThreadedComments`. It doesn't support thread-safety
/// which does not allow to be passed into wasmerEnv (HostEnvironment). A scoped
/// tls holds inner comments as global, per each transform execution. Refer
/// `swc::RustPlugins::apply_transform_plugin` for the responsibility to manage
/// actual data.
/// `swc_plugin::transform_execytor::TransformExecutor::transform` for the
/// responsibility to manage actual data.
///
/// Should never attempt to use this other than plugin_runner.
// TODO: This storage does not support mutable yet

View File

@ -62,24 +62,32 @@ fn bench_transform(b: &mut Bencher, plugin_dir: &Path) {
let program = VersionedSerializable::new(program);
let program_ser = PluginSerializedBytes::try_serialize(&program).unwrap();
let res = swc_plugin_runner::apply_transform_plugin(
"test",
let mut transform_plugin_executor = swc_plugin_runner::create_plugin_transform_executor(
&plugin_dir
.join("target")
.join("wasm32-unknown-unknown")
.join("release")
.join("swc_internal_plugin.wasm"),
&cache,
program_ser,
PluginSerializedBytes::try_serialize(&VersionedSerializable::new(String::from("{}")))
.unwrap(),
PluginSerializedBytes::try_serialize(&VersionedSerializable::new(String::from("{}")))
.unwrap(),
true,
&swc_plugin_runner::cache::PLUGIN_MODULE_CACHE,
&cm,
)
.unwrap();
let res = transform_plugin_executor
.transform(
&program_ser,
&PluginSerializedBytes::try_serialize(&VersionedSerializable::new(String::from(
"{}",
)))
.unwrap(),
&PluginSerializedBytes::try_serialize(&VersionedSerializable::new(String::from(
"{}",
)))
.unwrap(),
true,
)
.unwrap();
let _ = black_box(res);
})
}

View File

@ -1,8 +1,8 @@
use std::{path::Path, sync::Arc};
use anyhow::{Context, Error};
use anyhow::Error;
use once_cell::sync::Lazy;
use swc_common::{plugin::PluginSerializedBytes, SourceMap};
use swc_common::SourceMap;
use transform_executor::TransformExecutor;
pub mod cache;
@ -12,33 +12,18 @@ mod load_plugin;
mod memory_interop;
mod transform_executor;
// entrypoint fn swc calls to perform its transform via plugin.
#[allow(clippy::too_many_arguments)]
pub fn apply_transform_plugin(
plugin_name: &str,
/**
* Attempt to create a executor to run plugin binaries.
* Internally this will try to load binary from given cache which can fail,
* returns error in that case.
*
* Note you CANNOT reuse executor once trasform has been executed: executor
* is stateful.
*/
pub fn create_plugin_transform_executor(
path: &Path,
cache: &Lazy<cache::PluginModuleCache>,
program: PluginSerializedBytes,
config_json: PluginSerializedBytes,
context_json: PluginSerializedBytes,
should_enable_comments_proxy: bool,
source_map: &Arc<SourceMap>,
) -> Result<PluginSerializedBytes, Error> {
(|| -> Result<_, Error> {
let mut transform_tracker = TransformExecutor::new(path, cache, source_map)?;
let should_enable_comments_proxy = if should_enable_comments_proxy { 1 } else { 0 };
transform_tracker.transform(
&program,
&config_json,
&context_json,
should_enable_comments_proxy,
)
})()
.with_context(|| {
format!(
"failed to invoke `{}` as js transform plugin at {}",
plugin_name,
path.display()
)
})
) -> Result<TransformExecutor, Error> {
TransformExecutor::new(path, cache, source_map)
}

View File

@ -40,7 +40,7 @@ impl TransformExecutor {
exported_plugin_transform: instance
.exports
.get_native_function::<(i32, i32, i32, i32, i32, i32, i32), i32>(
"__plugin_process_impl",
"__transform_plugin_process_impl",
)?,
exported_plugin_free: instance
.exports
@ -102,14 +102,26 @@ impl TransformExecutor {
}
}
/**
* Check compile-time versions of AST schema between the plugin and
* the host. Returns true if it's compatible, false otherwise.
*
* Host should appropriately handle if plugin is not compatible to the
* current runtime.
*/
pub fn is_transform_schema_compatible(&self) -> bool {
todo!("Not supported yet");
}
#[tracing::instrument(level = "info", skip_all)]
pub fn transform(
&mut self,
program: &PluginSerializedBytes,
config: &PluginSerializedBytes,
context: &PluginSerializedBytes,
should_enable_comments_proxy: i32,
should_enable_comments_proxy: bool,
) -> Result<PluginSerializedBytes, Error> {
let should_enable_comments_proxy = if should_enable_comments_proxy { 1 } else { 0 };
let guest_program_ptr = self.write_bytes_into_guest(program)?;
let config_str_ptr = self.write_bytes_into_guest(config)?;
let context_str_ptr = self.write_bytes_into_guest(context)?;

View File

@ -96,18 +96,13 @@ fn internal() -> Result<(), Error> {
.expect("Should serializable");
let cache: Lazy<PluginModuleCache> = Lazy::new(PluginModuleCache::new);
let mut plugin_transform_executor =
swc_plugin_runner::create_plugin_transform_executor(&path, &cache, &cm)
.expect("Should load plugin");
let program_bytes = swc_plugin_runner::apply_transform_plugin(
"internal-test",
&path,
&cache,
program,
config,
context,
false,
&cm,
)
.expect("Plugin should apply transform");
let program_bytes = plugin_transform_executor
.transform(&program, &config, &context, false)
.expect("Plugin should apply transform");
let program: Program = program_bytes
.deserialize()
@ -153,17 +148,13 @@ fn internal() -> Result<(), Error> {
let cache: Lazy<PluginModuleCache> = Lazy::new(PluginModuleCache::new);
let _res = HANDLER.set(&handler, || {
swc_plugin_runner::apply_transform_plugin(
"internal-test",
&path,
&cache,
program,
config,
context,
false,
&cm,
)
.expect("Plugin should apply transform")
let mut plugin_transform_executor =
swc_plugin_runner::create_plugin_transform_executor(&path, &cache, &cm)
.expect("Should load plugin");
plugin_transform_executor
.transform(&program, &config, &context, false)
.expect("Plugin should apply transform")
});
Ok(())
@ -190,38 +181,44 @@ fn internal() -> Result<(), Error> {
.expect("Should serializable");
let cache: Lazy<PluginModuleCache> = Lazy::new(PluginModuleCache::new);
serialized_program = swc_plugin_runner::apply_transform_plugin(
"internal-test",
&path,
&cache,
serialized_program,
PluginSerializedBytes::try_serialize(&VersionedSerializable::new("{}".to_string()))
let mut plugin_transform_executor =
swc_plugin_runner::create_plugin_transform_executor(&path, &cache, &cm)
.expect("Should load plugin");
serialized_program = plugin_transform_executor
.transform(
&serialized_program,
&PluginSerializedBytes::try_serialize(&VersionedSerializable::new(
"{}".to_string(),
))
.expect("Should serializable"),
PluginSerializedBytes::try_serialize(&VersionedSerializable::new(
"{sourceFileName: 'multiple_plugin_test'}".to_string(),
))
.expect("Should serializable"),
false,
&cm,
)
.expect("Plugin should apply transform");
&PluginSerializedBytes::try_serialize(&VersionedSerializable::new(
"{sourceFileName: 'multiple_plugin_test'}".to_string(),
))
.expect("Should serializable"),
false,
)
.expect("Plugin should apply transform");
// TODO: we'll need to apply 2 different plugins
serialized_program = swc_plugin_runner::apply_transform_plugin(
"internal-test",
&path,
&cache,
serialized_program,
PluginSerializedBytes::try_serialize(&VersionedSerializable::new("{}".to_string()))
let mut plugin_transform_executor =
swc_plugin_runner::create_plugin_transform_executor(&path, &cache, &cm)
.expect("Should load plugin");
serialized_program = plugin_transform_executor
.transform(
&serialized_program,
&PluginSerializedBytes::try_serialize(&VersionedSerializable::new(
"{}".to_string(),
))
.expect("Should serializable"),
PluginSerializedBytes::try_serialize(&VersionedSerializable::new(
"{sourceFileName: 'multiple_plugin_test2'}".to_string(),
))
.expect("Should serializable"),
false,
&cm,
)
.expect("Plugin should apply transform");
&PluginSerializedBytes::try_serialize(&VersionedSerializable::new(
"{sourceFileName: 'multiple_plugin_test2'}".to_string(),
))
.expect("Should serializable"),
false,
)
.expect("Plugin should apply transform");
let program: Program = serialized_program
.deserialize()

View File

@ -722,7 +722,7 @@ dependencies = [
[[package]]
name = "swc_common"
version = "0.21.0"
version = "0.22.0"
dependencies = [
"ahash",
"anyhow",
@ -750,7 +750,7 @@ dependencies = [
[[package]]
name = "swc_ecma_ast"
version = "0.82.0"
version = "0.83.0"
dependencies = [
"bitflags",
"bytecheck",
@ -767,7 +767,7 @@ dependencies = [
[[package]]
name = "swc_ecma_parser"
version = "0.109.1"
version = "0.110.0"
dependencies = [
"either",
"enum_kind",
@ -784,7 +784,7 @@ dependencies = [
[[package]]
name = "swc_ecma_utils"
version = "0.91.0"
version = "0.92.0"
dependencies = [
"indexmap",
"once_cell",
@ -798,7 +798,7 @@ dependencies = [
[[package]]
name = "swc_ecma_visit"
version = "0.68.0"
version = "0.69.0"
dependencies = [
"num-bigint",
"swc_atoms",
@ -810,7 +810,7 @@ dependencies = [
[[package]]
name = "swc_ecmascript"
version = "0.176.0"
version = "0.178.0"
dependencies = [
"swc_ecma_ast",
"swc_ecma_parser",
@ -848,7 +848,7 @@ dependencies = [
[[package]]
name = "swc_plugin"
version = "0.72.0"
version = "0.74.0"
dependencies = [
"swc_atoms",
"swc_common",
@ -868,7 +868,7 @@ dependencies = [
[[package]]
name = "swc_plugin_proxy"
version = "0.7.0"
version = "0.8.0"
dependencies = [
"better_scoped_tls",
"bytecheck",
@ -887,7 +887,7 @@ dependencies = [
[[package]]
name = "swc_visit_macros"
version = "0.3.2"
version = "0.3.5"
dependencies = [
"Inflector",
"pmutil",

View File

@ -29,7 +29,7 @@ impl VisitMut for ConsoleOutputReplacer {
/// as returning ptr back to host.
///
/// It is possible to opt out from macro by writing transform fn manually via
/// `__plugin_process_impl(
/// `__transform_plugin_process_impl(
/// ast_ptr: *const u8,
/// ast_ptr_len: i32,
/// config_str_ptr: *const u8,