feat(es/plugin): Pass experimental metadata from host (#5261)

This commit is contained in:
OJ Kwon 2022-07-20 22:34:48 -07:00 committed by GitHub
parent 0dc240fbd9
commit 43eeeb35a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 145 additions and 44 deletions

View File

@ -96,6 +96,7 @@ impl RustPlugins {
use anyhow::Context;
use swc_common::{
collections::AHashMap,
plugin::{PluginSerializedBytes, VersionedSerializable},
FileName,
};
@ -177,11 +178,20 @@ impl RustPlugins {
)
.entered();
// Forward host side experimental metadata into plugin.
// This is currently not being used, reserved to enable proper serialization
// transform.
let experimental_metadata: AHashMap<String, String> = AHashMap::default();
let experimental_metadata_reserved = PluginSerializedBytes::try_serialize(
&VersionedSerializable::new(experimental_metadata),
)?;
serialized_program = transform_plugin_executor
.transform(
&serialized_program,
&serialized_config_json,
&serialized_context_json,
&experimental_metadata_reserved,
self.unresolved_mark,
should_enable_comments_proxy,
)
@ -209,6 +219,7 @@ impl RustPlugins {
use anyhow::Context;
use swc_common::{
collections::AHashMap,
plugin::{PluginSerializedBytes, VersionedSerializable},
FileName,
};
@ -249,11 +260,20 @@ impl RustPlugins {
&self.source_map,
)?;
// Forward host side experimental metadata into plugin.
// This is currently not being used, reserved to enable proper serialization
// transform.
let experimental_metadata: AHashMap<String, String> = AHashMap::default();
let experimental_metadata_reserved = PluginSerializedBytes::try_serialize(
&VersionedSerializable::new(experimental_metadata),
)?;
serialized_program = transform_plugin_executor
.transform(
&serialized_program,
&serialized_config_json,
&serialized_context_json,
&experimental_metadata_reserved,
self.unresolved_mark,
should_enable_comments_proxy,
)

View File

@ -12,10 +12,11 @@ pub enum Program {
Module(Module),
#[tag("Script")]
Script(Script),
/// Reserved type for the testing purpose only. Prod codes does not utilize
/// this at all and user should not try to attempt to use this as well.
#[tag("ReservedUnused")]
ReservedUnused(ReservedUnused),
// Reserved type for the testing purpose only. Prod codes does not utilize
// this at all and user should not try to attempt to use this as well.
// TODO: reenable once experimental_metadata breaking change is merged
// #[tag("ReservedUnused")]
// ReservedUnused(ReservedUnused),
}
#[ast_node("ReservedUnused")]

View File

@ -86,7 +86,8 @@ where
match *node {
Program::Module(ref m) => emit!(m),
Program::Script(ref s) => emit!(s),
_ => unreachable!(),
// TODO: reenable once experimental_metadata breaking change is merged
// _ => unreachable!(),
}
}

View File

@ -69,7 +69,8 @@ fn pass(input: PathBuf) {
Program::Script(s) => {
rules.lint_script(s);
}
_ => unreachable!(),
// TODO: reenable once experimental_metadata breaking change is merged
//_ => unreachable!(),
});
if handler.has_errors() {

View File

@ -1009,7 +1009,8 @@ define!({
pub enum Program {
Module(Module),
Script(Script),
ReservedUnused(ReservedUnused),
// TODO: reenable once experimental_metadata breaking change is merged
// ReservedUnused(ReservedUnused),
}
pub struct Module {
pub span: Span,

View File

@ -18,7 +18,8 @@ impl Babelify for Program {
let program = match self {
Program::Module(module) => module.babelify(ctx),
Program::Script(script) => script.babelify(ctx),
_ => unreachable!(),
// TODO: reenable once experimental_metadata breaking change is merged
// _ => unreachable!(),
};
File {

View File

@ -67,7 +67,12 @@ 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 #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, unresolved_mark: u32, 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,
experimental_metadata_ptr: *const u8, experimental_metadata_ptr_len: i32,
unresolved_mark: u32, 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()) };
@ -93,6 +98,13 @@ fn handle_func(func: ItemFn) -> TokenStream {
}
let context: String = context.expect("Should be a string");
let experimental_metadata = unsafe { swc_plugin::deserialize_from_ptr(experimental_metadata_ptr, experimental_metadata_ptr_len).map(|v| v.into_inner()) };
if experimental_metadata.is_err() {
let err = swc_plugin::PluginError::Deserialize("Failed to deserialize experimental_metadata received from host".to_string());
return construct_error_ptr(err);
}
let experimental_metadata: swc_plugin::collections::AHashMap<String, String> = experimental_metadata.expect("Should be a hashmap");
// Create a handler wired with plugin's diagnostic emitter, set it for global context.
let handler = swc_plugin::errors::Handler::with_emitter(
true,
@ -116,7 +128,7 @@ fn handle_func(func: ItemFn) -> TokenStream {
plugin_config: config,
unresolved_mark: swc_plugin::syntax_pos::Mark::from_u32(unresolved_mark),
transform_context: context,
experimental: swc_plugin::collections::AHashMap::default(),
experimental: experimental_metadata,
};
// Take original plugin fn ident, then call it with interop'ed args

View File

@ -10,6 +10,7 @@ use std::{
use criterion::{black_box, criterion_group, criterion_main, Bencher, Criterion};
use once_cell::sync::Lazy;
use swc_common::{
collections::AHashMap,
plugin::{PluginSerializedBytes, VersionedSerializable},
FileName, FilePathMapping, Mark, SourceMap,
};
@ -73,6 +74,12 @@ fn bench_transform(b: &mut Bencher, plugin_dir: &Path) {
)
.unwrap();
let experimental_metadata: AHashMap<String, String> = AHashMap::default();
let experimental_metadata = PluginSerializedBytes::try_serialize(
&VersionedSerializable::new(experimental_metadata),
)
.expect("Should be a hashmap");
let res = transform_plugin_executor
.transform(
&program_ser,
@ -84,6 +91,7 @@ fn bench_transform(b: &mut Bencher, plugin_dir: &Path) {
"{}",
)))
.unwrap(),
&experimental_metadata,
Mark::new(),
true,
)

View File

@ -13,7 +13,8 @@ use crate::memory_interop::write_into_memory_view;
/// A struct encapsule executing a plugin's transform interop to its teardown
pub struct TransformExecutor {
// Main transform interface plugin exports
exported_plugin_transform: wasmer::NativeFunc<(i32, i32, i32, i32, i32, i32, u32, i32), i32>,
exported_plugin_transform:
wasmer::NativeFunc<(i32, i32, i32, i32, i32, i32, i32, i32, u32, 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
@ -39,11 +40,20 @@ impl TransformExecutor {
crate::load_plugin::load_plugin(path, cache, source_map)?;
let tracker = TransformExecutor {
exported_plugin_transform: instance
.exports
.get_native_function::<(i32, i32, i32, i32, i32, i32, u32, i32), i32>(
"__transform_plugin_process_impl",
)?,
exported_plugin_transform: instance.exports.get_native_function::<(
i32,
i32,
i32,
i32,
i32,
i32,
i32,
i32,
u32,
i32,
), i32>(
"__transform_plugin_process_impl"
)?,
exported_plugin_transform_schema_version: instance
.exports
.get_native_function::<(), u32>("__get_transform_plugin_schema_version")?,
@ -138,6 +148,7 @@ impl TransformExecutor {
program: &PluginSerializedBytes,
config: &PluginSerializedBytes,
context: &PluginSerializedBytes,
experimental_metadata: &PluginSerializedBytes,
unresolved_mark: swc_common::Mark,
should_enable_comments_proxy: bool,
) -> Result<PluginSerializedBytes, Error> {
@ -145,6 +156,7 @@ impl TransformExecutor {
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)?;
let experimental_metadata_ptr = self.write_bytes_into_guest(experimental_metadata)?;
let result = self.exported_plugin_transform.call(
guest_program_ptr.0,
@ -153,6 +165,8 @@ impl TransformExecutor {
config_str_ptr.1,
context_str_ptr.0,
context_str_ptr.1,
experimental_metadata_ptr.0,
experimental_metadata_ptr.1,
unresolved_mark.as_u32(),
should_enable_comments_proxy,
)?;

View File

@ -717,7 +717,7 @@ dependencies = [
[[package]]
name = "swc_common"
version = "0.24.1"
version = "0.24.2"
dependencies = [
"ahash",
"anyhow",
@ -745,7 +745,7 @@ dependencies = [
[[package]]
name = "swc_ecma_ast"
version = "0.85.1"
version = "0.87.0"
dependencies = [
"bitflags",
"bytecheck",
@ -762,7 +762,7 @@ dependencies = [
[[package]]
name = "swc_ecma_parser"
version = "0.112.2"
version = "0.114.0"
dependencies = [
"either",
"enum_kind",
@ -779,7 +779,7 @@ dependencies = [
[[package]]
name = "swc_ecma_utils"
version = "0.94.0"
version = "0.96.0"
dependencies = [
"indexmap",
"once_cell",
@ -793,7 +793,7 @@ dependencies = [
[[package]]
name = "swc_ecma_visit"
version = "0.71.1"
version = "0.73.0"
dependencies = [
"num-bigint",
"serde",
@ -806,7 +806,7 @@ dependencies = [
[[package]]
name = "swc_ecmascript"
version = "0.181.1"
version = "0.184.0"
dependencies = [
"swc_ecma_ast",
"swc_ecma_parser",
@ -844,7 +844,7 @@ dependencies = [
[[package]]
name = "swc_plugin"
version = "0.80.2"
version = "0.85.0"
dependencies = [
"swc_atoms",
"swc_common",
@ -855,7 +855,7 @@ dependencies = [
[[package]]
name = "swc_plugin_macro"
version = "0.6.0"
version = "0.7.0"
dependencies = [
"proc-macro2",
"quote",
@ -864,7 +864,7 @@ dependencies = [
[[package]]
name = "swc_plugin_proxy"
version = "0.12.1"
version = "0.15.0"
dependencies = [
"better_scoped_tls",
"bytecheck",
@ -875,7 +875,7 @@ dependencies = [
[[package]]
name = "swc_visit"
version = "0.5.0"
version = "0.5.1"
dependencies = [
"either",
"swc_visit_macros",
@ -883,7 +883,7 @@ dependencies = [
[[package]]
name = "swc_visit_macros"
version = "0.5.1"
version = "0.5.2"
dependencies = [
"Inflector",
"pmutil",

View File

@ -45,12 +45,15 @@ impl VisitMut for ConsoleOutputReplacer {
/// important steps manually need to be performed like sending transformed
/// results back to host. Refer swc_plugin_macro how does it work internally.
#[plugin_transform]
pub fn process(program: Program, _metadata: TransformPluginProgramMetadata) -> Program {
pub fn process(program: Program, metadata: TransformPluginProgramMetadata) -> Program {
HANDLER.with(|handler| {
handler
.struct_span_err(DUMMY_SP, "Test diagnostics from plugin")
.emit();
});
// Arbitaray call for now
metadata.experimental.is_empty();
program.fold_with(&mut as_folder(ConsoleOutputReplacer))
}

View File

@ -6,6 +6,7 @@ use std::{
use anyhow::{anyhow, Error};
use swc_common::{
collections::AHashMap,
errors::HANDLER,
plugin::{PluginSerializedBytes, VersionedSerializable},
sync::Lazy,
@ -100,6 +101,11 @@ fn internal() -> Result<(), Error> {
"{sourceFileName: 'single_plugin_test'}".to_string(),
))
.expect("Should serializable");
let experimental_metadata: AHashMap<String, String> = AHashMap::default();
let experimental_metadata = PluginSerializedBytes::try_serialize(
&VersionedSerializable::new(experimental_metadata),
)
.expect("Should be a hashmap");
let cache: Lazy<PluginModuleCache> = Lazy::new(PluginModuleCache::new);
let mut plugin_transform_executor =
@ -107,7 +113,14 @@ fn internal() -> Result<(), Error> {
.expect("Should load plugin");
let program_bytes = plugin_transform_executor
.transform(&program, &config, &context, Mark::new(), false)
.transform(
&program,
&config,
&context,
&experimental_metadata,
Mark::new(),
false,
)
.expect("Plugin should apply transform");
let program: Program = program_bytes
@ -150,6 +163,11 @@ fn internal() -> Result<(), Error> {
"{sourceFileName: 'single_plugin_handler_test'}".to_string(),
))
.expect("Should serializable");
let experimental_metadata: AHashMap<String, String> = AHashMap::default();
let experimental_metadata = PluginSerializedBytes::try_serialize(
&VersionedSerializable::new(experimental_metadata),
)
.expect("Should be a hashmap");
let cache: Lazy<PluginModuleCache> = Lazy::new(PluginModuleCache::new);
@ -159,7 +177,14 @@ fn internal() -> Result<(), Error> {
.expect("Should load plugin");
plugin_transform_executor
.transform(&program, &config, &context, Mark::new(), false)
.transform(
&program,
&config,
&context,
&experimental_metadata,
Mark::new(),
false,
)
.expect("Plugin should apply transform")
});
@ -191,6 +216,12 @@ fn internal() -> Result<(), Error> {
swc_plugin_runner::create_plugin_transform_executor(&path, &cache, &cm)
.expect("Should load plugin");
let experimental_metadata: AHashMap<String, String> = AHashMap::default();
let experimental_metadata = PluginSerializedBytes::try_serialize(
&VersionedSerializable::new(experimental_metadata),
)
.expect("Should be a hashmap");
serialized_program = plugin_transform_executor
.transform(
&serialized_program,
@ -202,6 +233,7 @@ fn internal() -> Result<(), Error> {
"{sourceFileName: 'multiple_plugin_test'}".to_string(),
))
.expect("Should serializable"),
&experimental_metadata,
Mark::new(),
false,
)
@ -223,6 +255,7 @@ fn internal() -> Result<(), Error> {
"{sourceFileName: 'multiple_plugin_test2'}".to_string(),
))
.expect("Should serializable"),
&experimental_metadata,
Mark::new(),
false,
)

View File

@ -725,7 +725,7 @@ dependencies = [
[[package]]
name = "swc_common"
version = "0.24.1"
version = "0.24.2"
dependencies = [
"ahash",
"anyhow",
@ -753,7 +753,7 @@ dependencies = [
[[package]]
name = "swc_ecma_ast"
version = "0.86.0"
version = "0.87.0"
dependencies = [
"bitflags",
"bytecheck",
@ -770,7 +770,7 @@ dependencies = [
[[package]]
name = "swc_ecma_parser"
version = "0.113.0"
version = "0.114.0"
dependencies = [
"either",
"enum_kind",
@ -787,7 +787,7 @@ dependencies = [
[[package]]
name = "swc_ecma_utils"
version = "0.95.0"
version = "0.96.0"
dependencies = [
"indexmap",
"once_cell",
@ -801,7 +801,7 @@ dependencies = [
[[package]]
name = "swc_ecma_visit"
version = "0.72.0"
version = "0.73.0"
dependencies = [
"num-bigint",
"serde",
@ -814,7 +814,7 @@ dependencies = [
[[package]]
name = "swc_ecmascript"
version = "0.183.0"
version = "0.184.0"
dependencies = [
"swc_ecma_ast",
"swc_ecma_parser",
@ -844,7 +844,7 @@ dependencies = [
[[package]]
name = "swc_plugin"
version = "0.83.0"
version = "0.85.0"
dependencies = [
"swc_atoms",
"swc_common",
@ -855,7 +855,7 @@ dependencies = [
[[package]]
name = "swc_plugin_macro"
version = "0.6.1"
version = "0.7.0"
dependencies = [
"proc-macro2",
"quote",
@ -864,7 +864,7 @@ dependencies = [
[[package]]
name = "swc_plugin_proxy"
version = "0.14.0"
version = "0.15.0"
dependencies = [
"better_scoped_tls",
"bytecheck",
@ -875,7 +875,7 @@ dependencies = [
[[package]]
name = "swc_visit"
version = "0.5.0"
version = "0.5.1"
dependencies = [
"either",
"swc_visit_macros",

View File

@ -25,12 +25,13 @@ pub fn process(program: Program, _metadata: TransformPluginProgramMetadata) -> P
// Ensure this plugin uses vtest AST struct schema, by compile-time validating
// it does have new enum for the testing purpose.
match &program {
Program::Script(..) => {}
Program::Module(..) => {}
/* TODO: reenable once experimental_metadata breaking change is merged
Program::ReservedUnused(_reserved_unused) => {
println!("{:#?}", _reserved_unused);
panic!("ReservedUnused is not supported");
}
}*/
Program::Script(..) => {}
Program::Module(..) => {}
}
program.fold_with(&mut as_folder(ConsoleOutputReplacer))
}

View File

@ -72,7 +72,12 @@ describe("Plugins", () => {
{
host: "plugin_transform_schema_vtest",
plugin: [
"plugin_transform_schema_v1",
// TODO: reenable once new packages are published
// Note: this test runs against latest-published version of the plugin,
// an actual e2e test verifies a breaking change.
// We don't have a automatic way to resolve this when we attempt an
// actual breaking change - manually workaround for now.
// "plugin_transform_schema_v1",
"plugin_transform_schema_vtest",
],
},