From f89782ce539de06e11829c141d183c97aecc4f56 Mon Sep 17 00:00:00 2001 From: OJ Kwon Date: Fri, 8 Jul 2022 11:33:07 -0700 Subject: [PATCH 1/3] feat(swc/plugin_macro): export plugin schema version interface --- crates/swc/src/plugin.rs | 4 +++ crates/swc_common/src/plugin.rs | 17 ++++++++++++ crates/swc_plugin/src/lib.rs | 5 +++- crates/swc_plugin_macro/src/lib.rs | 7 +++++ .../src/transform_executor.rs | 27 ++++++++++++++++--- .../swc_internal_plugin/Cargo.lock | 4 +-- 6 files changed, 57 insertions(+), 7 deletions(-) diff --git a/crates/swc/src/plugin.rs b/crates/swc/src/plugin.rs index eef298f26aa1..0ac52ae0f262 100644 --- a/crates/swc/src/plugin.rs +++ b/crates/swc/src/plugin.rs @@ -127,6 +127,10 @@ impl RustPlugins { &self.source_map, )?; + if !transform_plugin_executor.is_transform_schema_compatible()? { + anyhow::bail!("Cannot execute incompatible plugin {}", &p.0); + } + let span = tracing::span!( tracing::Level::INFO, "serialize_context", diff --git a/crates/swc_common/src/plugin.rs b/crates/swc_common/src/plugin.rs index edc978abc4b0..26d71fff3494 100644 --- a/crates/swc_common/src/plugin.rs +++ b/crates/swc_common/src/plugin.rs @@ -12,6 +12,23 @@ use rkyv::{with::AsBox, Archive, Deserialize, Serialize}; use crate::{syntax_pos::Mark, SyntaxContext}; +/** + * Compile-time version constant for the AST struct schema's version. + * + * NOTE: this is for PARTIAL compatibility only, supporting if AST struct + * adds new properties without changing / removing existing properties. + * + * - When adding a new properties to the AST struct: + * 1. Create a new feature flag in cargo.toml + * 2. Create a new schema version with new feature flag. + * 3. Create a new AST struct with compile time feature flag with newly + * added properties. Previous struct should remain with existing feature + * flag, or add previous latest feature flag. + * + * - When removing, or changing existing properties in the AST struct: TBD + */ +pub const PLUGIN_TRANSFORM_AST_SCHEMA_VERSION: u32 = 1; + #[derive(Debug, Clone, PartialEq, Eq)] #[non_exhaustive] #[cfg_attr( diff --git a/crates/swc_plugin/src/lib.rs b/crates/swc_plugin/src/lib.rs index 2f32ccd85322..46eaacb00dae 100644 --- a/crates/swc_plugin/src/lib.rs +++ b/crates/swc_plugin/src/lib.rs @@ -3,7 +3,10 @@ // Reexports pub use swc_common::{ chain, - plugin::{deserialize_from_ptr, PluginError, PluginSerializedBytes, VersionedSerializable}, + plugin::{ + deserialize_from_ptr, PluginError, PluginSerializedBytes, VersionedSerializable, + PLUGIN_TRANSFORM_AST_SCHEMA_VERSION, + }, }; pub mod comments { diff --git a/crates/swc_plugin_macro/src/lib.rs b/crates/swc_plugin_macro/src/lib.rs index 5a5b9281fc94..50258127e977 100644 --- a/crates/swc_plugin_macro/src/lib.rs +++ b/crates/swc_plugin_macro/src/lib.rs @@ -21,6 +21,8 @@ fn handle_func(func: ItemFn) -> TokenStream { let ident = func.sig.ident.clone(); let transform_process_impl_ident = Ident::new("__transform_plugin_process_impl", Span::call_site()); + let transform_schema_version_ident = + Ident::new("__get_transform_plugin_schema_version", Span::call_site()); let ret = quote! { #func @@ -55,6 +57,11 @@ fn handle_func(func: ItemFn) -> TokenStream { 1 } + #[no_mangle] + pub fn #transform_schema_version_ident() -> u32 { + swc_plugin::PLUGIN_TRANSFORM_AST_SCHEMA_VERSION + } + // Macro to allow compose plugin's transform function without manual pointer operation. // Internally it wraps pointer operation also bubbles up error in forms of PluginError. // There are some cases error won't be wrapped up however - for example, we expect diff --git a/crates/swc_plugin_runner/src/transform_executor.rs b/crates/swc_plugin_runner/src/transform_executor.rs index 3022b21a828c..f32b1fa6a448 100644 --- a/crates/swc_plugin_runner/src/transform_executor.rs +++ b/crates/swc_plugin_runner/src/transform_executor.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use anyhow::{anyhow, Error}; use parking_lot::Mutex; use swc_common::{ - plugin::{PluginError, PluginSerializedBytes}, + plugin::{PluginError, PluginSerializedBytes, PLUGIN_TRANSFORM_AST_SCHEMA_VERSION}, SourceMap, }; use wasmer::Instance; @@ -14,6 +14,8 @@ use crate::memory_interop::write_into_memory_view; pub struct TransformExecutor { // Main transform interface plugin exports exported_plugin_transform: wasmer::NativeFunc<(i32, i32, i32, i32, i32, i32, i32), i32>, + // Schema version interface exports + exported_plugin_transform_schema_version: wasmer::NativeFunc<(), u32>, // `__free` function automatically exported via swc_plugin sdk to allow deallocation in guest // memory space exported_plugin_free: wasmer::NativeFunc<(i32, i32), i32>, @@ -42,6 +44,9 @@ impl TransformExecutor { .get_native_function::<(i32, i32, i32, i32, i32, i32, i32), i32>( "__transform_plugin_process_impl", )?, + exported_plugin_transform_schema_version: instance + .exports + .get_native_function::<(), u32>("__get_transform_plugin_schema_version")?, exported_plugin_free: instance .exports .get_native_function::<(i32, i32), i32>("__free")?, @@ -103,14 +108,28 @@ impl TransformExecutor { } /** - * Check compile-time versions of AST schema between the plugin and + * Check compile-time version 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"); + pub fn is_transform_schema_compatible(&self) -> Result { + let plugin_schema_version = self.exported_plugin_transform_schema_version.call(); + + match plugin_schema_version { + Ok(plugin_schema_version) => { + let host_schema_version = PLUGIN_TRANSFORM_AST_SCHEMA_VERSION; + + // TODO: this is incomplete + if plugin_schema_version == host_schema_version { + Ok(true) + } else { + Ok(false) + } + } + Err(e) => Err(anyhow!("Failed to call plugin's schema version: {}", e)), + } } #[tracing::instrument(level = "info", skip_all)] diff --git a/tests/rust-plugins/swc_internal_plugin/Cargo.lock b/tests/rust-plugins/swc_internal_plugin/Cargo.lock index aacb83ede756..25f2b5ce57e4 100644 --- a/tests/rust-plugins/swc_internal_plugin/Cargo.lock +++ b/tests/rust-plugins/swc_internal_plugin/Cargo.lock @@ -848,7 +848,7 @@ dependencies = [ [[package]] name = "swc_plugin" -version = "0.74.0" +version = "0.75.0" dependencies = [ "swc_atoms", "swc_common", @@ -868,7 +868,7 @@ dependencies = [ [[package]] name = "swc_plugin_proxy" -version = "0.8.0" +version = "0.9.0" dependencies = [ "better_scoped_tls", "bytecheck", From 90cc67abef43e6fc7ccc2dd99a73511bdb3aa0b3 Mon Sep 17 00:00:00 2001 From: OJ Kwon Date: Fri, 8 Jul 2022 13:11:59 -0700 Subject: [PATCH 2/3] test(swc/plugin_runner): fail hard if plugin build fails --- crates/swc_plugin_runner/tests/integration.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/swc_plugin_runner/tests/integration.rs b/crates/swc_plugin_runner/tests/integration.rs index 77430528ef30..af83c722568d 100644 --- a/crates/swc_plugin_runner/tests/integration.rs +++ b/crates/swc_plugin_runner/tests/integration.rs @@ -24,6 +24,14 @@ fn build_plugin(dir: &Path) -> Result { cmd.args(["build", "--target=wasm32-wasi"]) .stderr(Stdio::inherit()); cmd.output()?; + + if !cmd + .status() + .expect("Exit code should be available") + .success() + { + return Err(anyhow!("Failed to build plugin")); + } } for entry in fs::read_dir(&dir.join("target").join("wasm32-wasi").join("debug"))? { From 936dcdde0d5570ed3b2a2bbb36f206d571d22023 Mon Sep 17 00:00:00 2001 From: OJ Kwon Date: Fri, 8 Jul 2022 13:18:00 -0700 Subject: [PATCH 3/3] build(cargo): features for the schema version --- .github/workflows/cargo.yml | 7 ++++++- crates/binding_core_node/Cargo.toml | 3 +++ crates/binding_core_wasm/Cargo.toml | 1 + crates/swc/Cargo.toml | 4 ++++ crates/swc_cli/Cargo.toml | 1 + crates/swc_common/Cargo.toml | 2 ++ crates/swc_common/src/plugin.rs | 5 +++++ crates/swc_plugin/Cargo.toml | 1 + crates/swc_plugin_runner/Cargo.toml | 5 ++++- 9 files changed, 27 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cargo.yml b/.github/workflows/cargo.yml index 038daccc1bf4..38fa86806f6f 100644 --- a/.github/workflows/cargo.yml +++ b/.github/workflows/cargo.yml @@ -136,7 +136,7 @@ jobs: - crate: swc_common os: ubuntu-latest check: | - cargo hack check --feature-powerset --no-dev-deps + cargo hack check --feature-powerset --no-dev-deps --exclude-features plugin-transform-schema-vtest - crate: swc_common os: windows-latest - crate: swc_config @@ -393,6 +393,11 @@ jobs: run: | cargo test --color always -p ${{ matrix.settings.crate }} + - name: Run cargo test (plugin) + if: matrix.settings.crate == 'swc_plugin_runner' + run: | + cargo test --color always -p swc_plugin_runner --features plugin-transform-schema-v1 + - name: Run cargo test (all features) if: matrix.settings.crate == 'swc_ecma_parser' || matrix.settings.crate == 'swc_ecma_loader' || matrix.settings.crate == 'swc_ecma_transforms' run: | diff --git a/crates/binding_core_node/Cargo.toml b/crates/binding_core_node/Cargo.toml index 8bc89402fbda..fcaa556cc64c 100644 --- a/crates/binding_core_node/Cargo.toml +++ b/crates/binding_core_node/Cargo.toml @@ -16,7 +16,10 @@ crate-type = ["cdylib"] default = ["swc_v1", "plugin"] plugin = [ "swc/plugin", + "swc/plugin-transform-schema-v1", + "swc_common/plugin-transform-schema-v1", "swc_plugin_runner/default", + "swc_plugin_runner/plugin-transform-schema-v1", "wasmer/default", "wasmer-wasi/default", ] diff --git a/crates/binding_core_wasm/Cargo.toml b/crates/binding_core_wasm/Cargo.toml index 2643c8c83f24..7a2b1a63dbac 100644 --- a/crates/binding_core_wasm/Cargo.toml +++ b/crates/binding_core_wasm/Cargo.toml @@ -20,6 +20,7 @@ swc_v2 = [] plugin = [ "swc/plugin", "swc_plugin_runner/memory_cache", + "swc_plugin_runner/plugin-transform-schema-v1", "wasmer", "wasmer-wasi", "wasmer/js-default", diff --git a/crates/swc/Cargo.toml b/crates/swc/Cargo.toml index 5ff84502fbec..88160434b380 100644 --- a/crates/swc/Cargo.toml +++ b/crates/swc/Cargo.toml @@ -22,6 +22,10 @@ default = ["es3"] es3 = [] node = ["napi", "napi-derive"] plugin = ["swc_plugin_runner", "swc_plugin_proxy/plugin-rt"] +plugin-transform-schema-v1 = [ + "swc_common/plugin-transform-schema-v1", + "swc_plugin_runner/plugin-transform-schema-v1" +] [dependencies] ahash = "0.7.4" diff --git a/crates/swc_cli/Cargo.toml b/crates/swc_cli/Cargo.toml index 8c5062388b40..f841a495763c 100644 --- a/crates/swc_cli/Cargo.toml +++ b/crates/swc_cli/Cargo.toml @@ -18,6 +18,7 @@ default = [] plugin = [ "swc/plugin", "swc_plugin_runner/filesystem_cache", + "swc_plugin_runner/plugin-transform-schema-v1", "wasmer/default", "wasmer-wasi/default", ] diff --git a/crates/swc_common/Cargo.toml b/crates/swc_common/Cargo.toml index 04bf9a01ac08..e1bbd3c289a4 100644 --- a/crates/swc_common/Cargo.toml +++ b/crates/swc_common/Cargo.toml @@ -26,6 +26,8 @@ plugin-mode = ["plugin-base"] plugin-rt = ["plugin-base"] rkyv-impl = ["rkyv", "bytecheck"] tty-emitter = ["atty", "termcolor"] +plugin-transform-schema-v1 = [] +plugin-transform-schema-vtest = [] [dependencies] ahash = "0.7.4" diff --git a/crates/swc_common/src/plugin.rs b/crates/swc_common/src/plugin.rs index 26d71fff3494..ff8720c98e3d 100644 --- a/crates/swc_common/src/plugin.rs +++ b/crates/swc_common/src/plugin.rs @@ -27,8 +27,13 @@ use crate::{syntax_pos::Mark, SyntaxContext}; * * - When removing, or changing existing properties in the AST struct: TBD */ +#[cfg(feature = "plugin-transform-schema-v1")] pub const PLUGIN_TRANSFORM_AST_SCHEMA_VERSION: u32 = 1; +// Reserved for the testing purpose. +#[cfg(feature = "plugin-transform-schema-vtest")] +pub const PLUGIN_TRANSFORM_AST_SCHEMA_VERSION: u32 = u32::MAX - 1; + #[derive(Debug, Clone, PartialEq, Eq)] #[non_exhaustive] #[cfg_attr( diff --git a/crates/swc_plugin/Cargo.toml b/crates/swc_plugin/Cargo.toml index fe4bfcdc110a..57384de9fab1 100644 --- a/crates/swc_plugin/Cargo.toml +++ b/crates/swc_plugin/Cargo.toml @@ -22,6 +22,7 @@ quote = ["swc_ecma_quote"] swc_atoms = { version = "0.2.0", path = "../swc_atoms" } swc_common = { version = "0.22.0", path = "../swc_common", features = [ "plugin-mode", + "plugin-transform-schema-v1" ] } swc_ecma_quote = { version = "0.24.0", path = "../swc_ecma_quote", optional = true } swc_ecmascript = { version = "0.178.0", path = "../swc_ecmascript", features = ["utils", "visit", "rkyv-impl"] } diff --git a/crates/swc_plugin_runner/Cargo.toml b/crates/swc_plugin_runner/Cargo.toml index 7e8a6d2b5858..759fe02f32ec 100644 --- a/crates/swc_plugin_runner/Cargo.toml +++ b/crates/swc_plugin_runner/Cargo.toml @@ -19,6 +19,9 @@ filesystem_cache = ["wasmer-cache"] # Supports a cache allow to store wasm module in-memory. This avoids recompilation # to the same module in a single procress lifecycle. memory_cache = [] +plugin-transform-schema-v1 = [ + "swc_common/plugin-transform-schema-v1" +] [dependencies] anyhow = "1.0.42" @@ -28,7 +31,7 @@ serde = { version = "1.0.126", features = ["derive"] } serde_json = "1.0.64" swc_common = { version = "0.22.0", path = "../swc_common", features = [ "plugin-rt", - "concurrent", + "concurrent" ] } swc_ecma_ast = { version = "0.83.0", path = "../swc_ecma_ast", features = [ "rkyv-impl",