feat(plugin/runner): Update wasmer to v3 (#7197)

**Description:**

This PR reattempts https://github.com/swc-project/swc/pull/5456.

Most of the changes are for the breaking changes of wasmer@3, as well as enabling rkyv's strict mode (https://github.com/swc-project/swc/pull/6922). This could not be seperated since wasmer@3 enables strict mode by default without a way to turn it off.

There are a couple of changes worth noting:

- Disabling in-memory module lookup: https://github.com/swc-project/swc/pull/7197/files#diff-3bda5def6ce2b7553c3b3a5ad241c0bdb7021e67b7de1e594df4cd5a54d403b3R154-R159
- Disabling plugin_runner in bindings_wasm: https://github.com/swc-project/swc/pull/7197/files#diff-dc3ded556a1fd709a129acd588e5eda651b842c6acc3f5340d40088a1f927facR310-R312
- Skipping plugin compat test: https://github.com/swc-project/swc/pull/7197/files#diff-531197dfcefba05faca53f0cf442ecc2dc6b59d5ead01979f5ffb912aa36249aR64-R66
This commit is contained in:
OJ Kwon 2023-04-09 20:40:25 -07:00 committed by GitHub
parent e116fedd22
commit 56bdacc72d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 2241 additions and 1220 deletions

View File

@ -232,7 +232,7 @@ jobs:
os: ubuntu-latest
runner: ubuntu-latest
check: |
cargo hack check --feature-powerset --no-dev-deps --exclude-features debug --exclude-features plugin --exclude-features plugin_transform_schema_v1 --exclude-features plugin_transform_schema_vtest --exclude-features plugin-bytecheck
cargo hack check --feature-powerset --no-dev-deps --exclude-features debug --exclude-features plugin --exclude-features plugin_transform_schema_v1 --exclude-features plugin_transform_schema_vtest --exclude-features plugin-bytecheck --exclude-features plugin_transform_host_js --exclude-features plugin_transform_host_native
- crate: swc
os: windows-latest
runner: windows-latest

1428
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,20 @@ default = ["swc_v1"]
swc_v1 = []
swc_v2 = []
# This feature exists to allow cargo operations
plugin = ["swc_core/plugin_transform_host_js"]
# [TODO]: this is disabled due to signature mismatch between host_native and host_js,
# which causes build errors like
# .call(&mut self.store, ptr.0, ptr.1)
# | ^^^^ the trait `NativeWasmTypeInto` is not implemented for `u32`
# |
# = help: the following other types implement trait `NativeWasmTypeInto`:
# f32
# f64
# i32
# i64
# u128
plugin = [
#"swc_core/plugin_transform_host_js"
]
[dependencies]
anyhow = "1.0.66"

View File

@ -307,7 +307,9 @@ macro_rules! build_transform_sync {
let c = $crate::wasm::compiler();
#[cfg(feature = "plugin")]
//[TODO]: refer binding_core_wasm/Cargo.toml,
//disables via renaming feature to arbitary name
#[cfg(feature = "__plugin")]
{
if experimental_plugin_bytes_resolver.is_object() {
use $crate::wasm::js_sys::{Array, Object, Uint8Array};

View File

@ -46,6 +46,11 @@ plugin_transform_schema_vtest = [
"swc_plugin_runner/plugin_transform_schema_vtest",
]
plugin_transform_host_js = ["swc_plugin_runner/plugin_transform_host_js"]
plugin_transform_host_native = [
"swc_plugin_runner/plugin_transform_host_native",
]
[dependencies]
ahash = "0.7.4"
anyhow = "1"

View File

@ -22,14 +22,18 @@ rkyv-bytecheck-impl = ["__rkyv", "rkyv-latest"]
[dependencies]
bytecheck = { version = "0.6.9", optional = true }
once_cell = "1"
rkyv = { package = "rkyv", version = "=0.7.40", optional = true }
rkyv = { package = "rkyv", version = "=0.7.40", optional = true, features = [
"strict",
] }
# This is to avoid cargo version selection conflict between rkyv=0.7.40 and other versions, as it is strictly pinned
# cannot be merged.
rkyv-latest = { package = "rkyv-test", version = "=0.7.38-test.2", optional = true }
rustc-hash = "1.1.0"
serde = "1"
rkyv-latest = { package = "rkyv-test", version = "=0.7.38-test.2", optional = true, features = [
"strict",
] }
rustc-hash = "1.1.0"
serde = "1"
string_cache = "0.8.7"
triomphe = "0.1.8"
triomphe = "0.1.8"
[build-dependencies]
string_cache_codegen = "0.5.2"

View File

@ -60,7 +60,7 @@ new_debug_unreachable = "1.0.4"
num-bigint = "0.4"
once_cell = "1.10.0"
parking_lot = { version = "0.12.0", optional = true }
rkyv = { version = "=0.7.40", optional = true }
rkyv = { version = "=0.7.40", optional = true, features = ["strict"] }
# This is to avoid cargo version selection conflict between rkyv=0.7.40 and other versions, as it is strictly pinned
# cannot be merged.
rkyv-latest = { package = "rkyv-test", version = "=0.7.38-test.2", optional = true }

View File

@ -16,6 +16,17 @@ use rkyv_latest as rkyv;
use super::{snippet::Style, Applicability, CodeSuggestion, Level, Substitution, SubstitutionPart};
use crate::syntax_pos::{MultiSpan, Span};
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "diagnostic-serde",
derive(serde::Serialize, serde::Deserialize)
)]
#[cfg_attr(
any(feature = "rkyv-impl", feature = "rkyv-bytecheck-impl"),
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
pub struct Message(pub String, pub Style);
#[must_use]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(
@ -28,7 +39,7 @@ use crate::syntax_pos::{MultiSpan, Span};
)]
pub struct Diagnostic {
pub level: Level,
pub message: Vec<(String, Style)>,
pub message: Vec<Message>,
pub code: Option<DiagnosticId>,
pub span: MultiSpan,
pub children: Vec<SubDiagnostic>,
@ -61,7 +72,7 @@ pub enum DiagnosticId {
)]
pub struct SubDiagnostic {
pub level: Level,
pub message: Vec<(String, Style)>,
pub message: Vec<Message>,
pub span: MultiSpan,
pub render_span: Option<MultiSpan>,
}
@ -117,7 +128,7 @@ impl Diagnostic {
pub fn new_with_code(level: Level, code: Option<DiagnosticId>, message: &str) -> Self {
Diagnostic {
level,
message: vec![(message.to_owned(), Style::NoStyle)],
message: vec![Message(message.to_owned(), Style::NoStyle)],
code,
span: MultiSpan::new(),
children: vec![],
@ -184,18 +195,18 @@ impl Diagnostic {
expected_extra: &dyn fmt::Display,
found_extra: &dyn fmt::Display,
) -> &mut Self {
let mut msg: Vec<_> = vec![(format!("expected {} `", label), Style::NoStyle)];
let mut msg: Vec<_> = vec![Message(format!("expected {} `", label), Style::NoStyle)];
msg.extend(expected.0.iter().map(|x| match *x {
StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
StringPart::Normal(ref s) => Message(s.to_owned(), Style::NoStyle),
StringPart::Highlighted(ref s) => Message(s.to_owned(), Style::Highlight),
}));
msg.push((format!("`{}\n", expected_extra), Style::NoStyle));
msg.push((format!(" found {} `", label), Style::NoStyle));
msg.push(Message(format!("`{}\n", expected_extra), Style::NoStyle));
msg.push(Message(format!(" found {} `", label), Style::NoStyle));
msg.extend(found.0.iter().map(|x| match *x {
StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
StringPart::Normal(ref s) => Message(s.to_owned(), Style::NoStyle),
StringPart::Highlighted(ref s) => Message(s.to_owned(), Style::Highlight),
}));
msg.push((format!("`{}", found_extra), Style::NoStyle));
msg.push(Message(format!("`{}", found_extra), Style::NoStyle));
// For now, just attach these as notes
self.highlighted_note(msg);
@ -204,9 +215,9 @@ impl Diagnostic {
pub fn note_trait_signature(&mut self, name: String, signature: String) -> &mut Self {
self.highlighted_note(vec![
(format!("`{}` from trait: `", name), Style::NoStyle),
(signature, Style::Highlight),
("`".to_string(), Style::NoStyle),
Message(format!("`{}` from trait: `", name), Style::NoStyle),
Message(signature, Style::Highlight),
Message("`".to_string(), Style::NoStyle),
]);
self
}
@ -216,7 +227,7 @@ impl Diagnostic {
self
}
pub fn highlighted_note(&mut self, msg: Vec<(String, Style)>) -> &mut Self {
pub fn highlighted_note(&mut self, msg: Vec<Message>) -> &mut Self {
self.sub_with_highlights(Level::Note, msg, MultiSpan::new(), None);
self
}
@ -431,7 +442,7 @@ impl Diagnostic {
.collect::<String>()
}
pub fn styled_message(&self) -> &Vec<(String, Style)> {
pub fn styled_message(&self) -> &Vec<Message> {
&self.message
}
@ -454,7 +465,7 @@ impl Diagnostic {
) {
let sub = SubDiagnostic {
level,
message: vec![(message.to_owned(), Style::NoStyle)],
message: vec![Message(message.to_owned(), Style::NoStyle)],
span,
render_span,
};
@ -466,7 +477,7 @@ impl Diagnostic {
fn sub_with_highlights(
&mut self,
level: Level,
message: Vec<(String, Style)>,
message: Vec<Message>,
span: MultiSpan,
render_span: Option<MultiSpan>,
) {
@ -488,7 +499,7 @@ impl SubDiagnostic {
.collect::<String>()
}
pub fn styled_message(&self) -> &Vec<(String, Style)> {
pub fn styled_message(&self) -> &Vec<Message> {
&self.message
}
}

View File

@ -23,6 +23,7 @@ use unicode_width;
use self::Destination::*;
use super::{
diagnostic::Message,
snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, Style, StyledString},
styled_buffer::StyledBuffer,
CodeSuggestion, DiagnosticBuilder, DiagnosticId, Level, SourceMapperDyn, SubDiagnostic,
@ -842,7 +843,7 @@ impl EmitterWriter {
if spans_updated {
children.push(SubDiagnostic {
level: Level::Note,
message: vec![(
message: vec![Message(
"this error originates in a macro outside of the current crate (in Nightly \
builds, run with -Z external-macro-backtrace for more info)"
.to_string(),
@ -859,7 +860,7 @@ impl EmitterWriter {
fn msg_to_buffer(
&self,
buffer: &mut StyledBuffer,
msg: &[(String, Style)],
msg: &[Message],
padding: usize,
label: &str,
override_style: Option<Style>,
@ -912,7 +913,7 @@ impl EmitterWriter {
// see how it *looks* with
// very *weird* formats
// see?
for (text, style) in msg.iter() {
for Message(text, ref style) in msg.iter() {
let lines = text.split('\n').collect::<Vec<_>>();
if lines.len() > 1 {
for (i, line) in lines.iter().enumerate() {
@ -932,7 +933,7 @@ impl EmitterWriter {
fn emit_message_default(
&mut self,
msp: &MultiSpan,
msg: &[(String, Style)],
msg: &[Message],
code: &Option<DiagnosticId>,
level: Level,
max_line_num_len: usize,
@ -975,7 +976,7 @@ impl EmitterWriter {
if !level_str.is_empty() {
buffer.append(0, ": ", header_style);
}
for (text, _) in msg.iter() {
for Message(text, _) in msg.iter() {
buffer.append(0, text, header_style);
}
}
@ -1212,7 +1213,7 @@ impl EmitterWriter {
}
self.msg_to_buffer(
&mut buffer,
&[(suggestion.msg.to_owned(), Style::NoStyle)],
&[Message(suggestion.msg.to_owned(), Style::NoStyle)],
max_line_num_len,
"suggestion",
Some(Style::HeaderMsg),
@ -1332,7 +1333,7 @@ impl EmitterWriter {
fn emit_messages_default(
&mut self,
level: Level,
message: &[(String, Style)],
message: &[Message],
code: &Option<DiagnosticId>,
span: &MultiSpan,
children: &[SubDiagnostic],

View File

@ -488,8 +488,8 @@ impl SourceMap {
if lo.file.start_pos != hi.file.start_pos {
return Err(Box::new(SpanLinesError::DistinctSources(DistinctSources {
begin: (lo.file.name.clone(), lo.file.start_pos),
end: (hi.file.name.clone(), hi.file.start_pos),
begin: FilePos(lo.file.name.clone(), lo.file.start_pos),
end: FilePos(hi.file.name.clone(), hi.file.start_pos),
})));
}
assert!(hi.line >= lo.line);
@ -565,8 +565,8 @@ impl SourceMap {
if local_begin.sf.start_pos != local_end.sf.start_pos {
Err(Box::new(SpanSnippetError::DistinctSources(
DistinctSources {
begin: (local_begin.sf.name.clone(), local_begin.sf.start_pos),
end: (local_end.sf.name.clone(), local_end.sf.start_pos),
begin: FilePos(local_begin.sf.name.clone(), local_begin.sf.start_pos),
end: FilePos(local_end.sf.name.clone(), local_end.sf.start_pos),
},
)))
} else {

View File

@ -312,6 +312,17 @@ impl FileName {
}
}
#[derive(Clone, Debug, Default, Hash, PartialEq, Eq)]
#[cfg_attr(
feature = "diagnostic-serde",
derive(serde::Serialize, serde::Deserialize)
)]
#[cfg_attr(
any(feature = "rkyv-impl", feature = "rkyv-bytecheck-impl"),
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
pub struct PrimarySpanLabel(pub Span, pub String);
/// A collection of spans. Spans have two orthogonal attributes:
///
/// - they can be *primary spans*. In this case they are the locus of the error,
@ -329,7 +340,7 @@ impl FileName {
)]
pub struct MultiSpan {
primary_spans: Vec<Span>,
span_labels: Vec<(Span, String)>,
span_labels: Vec<PrimarySpanLabel>,
}
extern "C" {
@ -633,7 +644,7 @@ impl MultiSpan {
}
pub fn push_span_label(&mut self, span: Span, label: String) {
self.span_labels.push((span, label));
self.span_labels.push(PrimarySpanLabel(span, label));
}
/// Selects the first primary span (if any)
@ -689,7 +700,7 @@ impl MultiSpan {
let mut span_labels = self
.span_labels
.iter()
.map(|&(span, ref label)| SpanLabel {
.map(|&PrimarySpanLabel(span, ref label)| SpanLabel {
span,
is_primary: is_primary(span),
label: Some(label.clone()),
@ -1296,14 +1307,21 @@ pub enum SpanSnippetError {
SourceNotAvailable { filename: FileName },
}
#[derive(Clone, PartialEq, Eq, Debug)]
#[cfg_attr(
any(feature = "rkyv-impl", feature = "rkyv-bytecheck-impl"),
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
pub struct FilePos(pub FileName, pub BytePos);
#[derive(Clone, PartialEq, Eq, Debug)]
#[cfg_attr(
any(feature = "rkyv-impl", feature = "rkyv-bytecheck-impl"),
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
pub struct DistinctSources {
pub begin: (FileName, BytePos),
pub end: (FileName, BytePos),
pub begin: FilePos,
pub end: FilePos,
}
#[derive(Clone, PartialEq, Eq, Debug)]

View File

@ -295,15 +295,15 @@ __plugin_transform_host_bytecheck = [
# Internal flags to control plugin environment
__plugin_transform_env_native = [
"swc/plugin_transform_host_native",
"swc_plugin_runner/filesystem_cache",
"wasmer/default",
"wasmer-wasi/default",
"swc_plugin_runner/plugin_transform_host_native",
]
__plugin_transform_env_js = [
"swc/plugin_transform_host_js",
"swc_plugin_runner/memory_cache",
"wasmer/js-default",
"wasmer-wasi/js-default",
"swc_plugin_runner/plugin_transform_host_js",
]
# Do not use: testing purpose only
@ -337,9 +337,7 @@ __visit = ["__ecma", "swc_ecma_visit"]
[dependencies]
# 3rd party dependencies
once_cell = { optional = true, version = "1.13.0" }
wasmer = { optional = true, version = "2.3.0", default-features = false }
wasmer-wasi = { optional = true, version = "2.3.0", default-features = false }
once_cell = { optional = true, version = "1.13.0" }
# swc_* dependencies
binding_macros = { optional = true, version = "0.48.6", path = "../binding_macros" }
@ -350,6 +348,7 @@ swc_cached = { optional = true, version = "0.3.15", path =
swc_common = { optional = true, version = "0.30.5", path = "../swc_common" }
swc_css_ast = { optional = true, version = "0.136.6", path = "../swc_css_ast" }
swc_css_codegen = { optional = true, version = "0.146.6", path = "../swc_css_codegen" }
swc_css_compat = { optional = true, version = "0.22.7", path = "../swc_css_compat" }
swc_css_minifier = { optional = true, version = "0.111.6", path = "../swc_css_minifier" }
swc_css_modules = { optional = true, version = "0.24.1", path = "../swc_css_modules" }
swc_css_parser = { optional = true, version = "0.145.6", path = "../swc_css_parser" }
@ -383,8 +382,7 @@ swc_plugin_proxy = { optional = true, version = "0.31.5", path =
swc_trace_macro = { optional = true, version = "0.1.2", path = "../swc_trace_macro" }
testing = { optional = true, version = "0.32.5", path = "../testing" }
# TODO: eventually swc_plugin_runner needs to remove default features
swc_css_compat = { version = "0.22.7", path = "../swc_css_compat", optional = true }
swc_plugin_runner = { optional = true, version = "0.93.8", path = "../swc_plugin_runner", default-features = false }
swc_plugin_runner = { optional = true, version = "0.93.7", path = "../swc_plugin_runner", default-features = false }
[build-dependencies]
vergen = { version = "7.3.2", default-features = false, features = ["cargo"] }

View File

@ -24,7 +24,9 @@ swc_core = { path = "../../../../swc_core", features = [
"binding_macro_wasm",
"ecma_transforms",
"ecma_visit",
"plugin_transform_host_js",
# Disabled for now, until resolve type mismatches. Refer bindings/binding_core_wasm/Cargo.toml
# for the detail.
# "plugin_transform_host_js",
] }
tracing = { version = "0.1.37", features = ["max_level_off"] }
wasm-bindgen = { version = "0.2.82", features = ["enable-interning"] }

View File

@ -29,7 +29,7 @@ fn build_fixture_binary(dir: &Path, target: Option<&str>) -> Result<(), Error> {
}
#[test]
fn swc_core_napi_integartion_build() -> Result<(), Error> {
fn swc_core_napi_integration_build() -> Result<(), Error> {
build_fixture_binary(
&PathBuf::from(env::var("CARGO_MANIFEST_DIR")?)
.join("tests")
@ -40,7 +40,7 @@ fn swc_core_napi_integartion_build() -> Result<(), Error> {
}
#[test]
fn swc_core_wasm_integartion_build() -> Result<(), Error> {
fn swc_core_wasm_integration_build() -> Result<(), Error> {
build_fixture_binary(
&PathBuf::from(env::var("CARGO_MANIFEST_DIR")?)
.join("tests")

View File

@ -21,7 +21,7 @@ serde-impl = ["serde"]
[dependencies]
bytecheck = { version = "0.6.9", optional = true }
is-macro = "0.2.0"
rkyv = { version = "=0.7.40", optional = true }
rkyv = { version = "=0.7.40", optional = true, features = ["strict"] }
serde = { version = "1.0.127", features = ["derive"], optional = true }
string_enum = { version = "0.4.0", path = "../string_enum/" }

View File

@ -25,6 +25,14 @@ impl Take for TokenAndSpan {
}
}
#[derive(Debug, Clone, PartialEq, EqIgnoreSpan, Hash)]
#[cfg_attr(feature = "serde-impl", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
any(feature = "rkyv-impl", feature = "rkyv-bytecheck-impl"),
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
pub struct UrlKeyValue(pub Atom, pub Atom);
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Is, EqIgnoreSpan)]
#[cfg_attr(
feature = "rkyv",
@ -114,7 +122,7 @@ pub enum Token {
#[cfg_attr(feature = "rkyv", with(swc_atoms::EncodeJsWord))]
value: JsWord,
/// Name and value
raw: Box<(Atom, Atom)>,
raw: Box<UrlKeyValue>,
},
BadUrl {
raw: Atom,

View File

@ -2,7 +2,9 @@ use std::{cell::RefCell, char::REPLACEMENT_CHARACTER, rc::Rc};
use swc_atoms::{js_word, Atom, JsWord};
use swc_common::{input::Input, BytePos, Span};
use swc_css_ast::{matches_eq_ignore_ascii_case, DimensionToken, NumberType, Token, TokenAndSpan};
use swc_css_ast::{
matches_eq_ignore_ascii_case, DimensionToken, NumberType, Token, TokenAndSpan, UrlKeyValue,
};
use crate::{
error::{Error, ErrorKind},
@ -755,7 +757,7 @@ where
Some(')') => {
return Ok(Token::Url {
value: (&**out).into(),
raw: Box::new((name.1, (&**raw).into())),
raw: Box::new(UrlKeyValue(name.1, (&**raw).into())),
});
}
@ -766,7 +768,7 @@ where
return Ok(Token::Url {
value: (&**out).into(),
raw: Box::new((name.1, (&**raw).into())),
raw: Box::new(UrlKeyValue(name.1, (&**raw).into())),
});
}
@ -800,7 +802,7 @@ where
return Ok(Token::Url {
value: (&**out).into(),
raw: Box::new((name.1, (&**raw).into())),
raw: Box::new(UrlKeyValue(name.1, (&**raw).into())),
});
}
None => {
@ -810,7 +812,7 @@ where
return Ok(Token::Url {
value: (&**out).into(),
raw: Box::new((name.1, (&**raw).into())),
raw: Box::new(UrlKeyValue(name.1, (&**raw).into())),
});
}
_ => {}

View File

@ -2037,7 +2037,7 @@
16 | :unknown({!}) {}
`----
x Url { value: Atom('foo.png' type=inline), raw: ("url", "foo.png") }
x Url { value: Atom('foo.png' type=inline), raw: UrlKeyValue("url", "foo.png") }
,-[$DIR/tests/fixture/selector/pseudo-class/unknown/input.css:14:1]
14 | :unknown('string') {}
15 | :unknown(url(foo.png)) {}

View File

@ -2036,7 +2036,7 @@
16 | ::unknown({!}) {}
`----
x Url { value: Atom('foo.png' type=inline), raw: ("url", "foo.png") }
x Url { value: Atom('foo.png' type=inline), raw: UrlKeyValue("url", "foo.png") }
,-[$DIR/tests/fixture/selector/pseudo-element/unknown/input.css:14:1]
14 | ::unknown('string') {}
15 | ::unknown(url(foo.png)) {}

View File

@ -31,18 +31,22 @@ rkyv-bytecheck-impl = [
serde-impl = ["serde"]
[dependencies]
arbitrary = { version = "1", optional = true, features = ["derive"] }
bitflags = "1"
bytecheck = { version = "0.6.9", optional = true }
is-macro = "0.2.1"
arbitrary = { version = "1", optional = true, features = ["derive"] }
bitflags = "1"
bytecheck = { version = "0.6.9", optional = true }
is-macro = "0.2.1"
num-bigint = { version = "0.4", features = ["serde"] }
rkyv = { package = "rkyv", version = "=0.7.40", optional = true }
rkyv = { package = "rkyv", version = "=0.7.40", optional = true, features = [
"strict",
] }
# This is to avoid cargo version selection conflict between rkyv=0.7.40 and other versions, as it is strictly pinned
# cannot be merged.
rkyv-latest = { package = "rkyv-test", version = "=0.7.38-test.2", optional = true }
scoped-tls = "1.0.0"
serde = { version = "1.0.133", features = ["derive"], optional = true }
unicode-id = "0.3"
rkyv-latest = { package = "rkyv-test", version = "=0.7.38-test.2", optional = true, features = [
"strict",
] }
scoped-tls = "1.0.0"
serde = { version = "1.0.133", features = ["derive"], optional = true }
unicode-id = "0.3"
string_enum = { version = "0.4.0", path = "../string_enum" }
swc_atoms = { version = "0.4.43", path = "../swc_atoms" }

View File

@ -23,7 +23,7 @@ serde-impl = ["serde"]
[dependencies]
bytecheck = { version = "0.6.9", optional = true }
is-macro = "0.2.0"
rkyv = { version = "=0.7.40", optional = true }
rkyv = { version = "=0.7.40", optional = true, features = ["strict"] }
serde = { version = "1.0.127", features = ["derive"], optional = true }
string_enum = { version = "0.4.0", path = "../string_enum/" }

View File

@ -1,3 +1,5 @@
use std::alloc::{alloc as global_alloc, dealloc as global_dealloc, Layout};
/// Allocate bytes that won't be dropped by the allocator.
/// Return the pointer to the leaked allocation so the host can write to it.
///
@ -31,5 +33,46 @@ pub extern "C" fn __alloc(size: i32) -> *mut u8 {
pub extern "C" fn __free(ptr: *mut u8, size: i32) -> i32 {
let data = unsafe { Vec::from_raw_parts(ptr, size as usize, size as usize) };
std::mem::drop(data);
0
0 as _
}
/// Allocates memory area of specified size and returns its address.
/// Returns 0 if supplied size is too long.
/// [TODO]: This is for the experiment to alloc memory with specified layout instead of
/// manually creating vec![] and forget it. This is not used yet.
#[doc(hidden)]
#[cfg(all(target_arch = "wasm32", feature = "layout_alloc"))]
#[no_mangle]
#[inline(always)]
pub unsafe fn __alloc(size: usize) -> usize {
let layout = match Layout::from_size_align(size as usize, std::mem::align_of::<u8>()) {
Ok(layout) => layout,
// in this case a err may occur only in a case of too long allocated size,
// so just return 0
Err(_) => return 0 as _,
};
unsafe { global_alloc(layout) as _ }
}
/// Deallocates memory area for provided memory pointer and size.
/// Does nothing if supplied size is too long.
/// [TODO]: This is for the experiment to free memory with specified layout with explicit
/// dealloc instead of dropping it. This is not used yet.
#[doc(hidden)]
#[cfg(all(target_arch = "wasm32", feature = "layout_alloc"))]
#[no_mangle]
#[inline(always)]
pub unsafe fn __free(ptr: *mut u8, size: usize) {
let layout = match Layout::from_size_align(size as usize, std::mem::align_of::<u8>()) {
Ok(layout) => layout,
// in this case a err may occur only in a case of too long allocated size,
// so just done nothing
Err(_) => return 0 as _,
};
unsafe {
global_dealloc(ptr, layout);
}
0 as _
}

View File

@ -37,7 +37,9 @@ plugin-mode = ["__plugin_mode", "swc_common/plugin-base", "rkyv-impl"]
[dependencies]
rkyv = { package = "rkyv", version = "=0.7.40", optional = true }
rkyv = { package = "rkyv", version = "=0.7.40", optional = true, features = [
"strict",
] }
# This is to avoid cargo version selection conflict between rkyv=0.7.40 and other versions, as it is strictly pinned
# cannot be merged.
rkyv-latest = { package = "rkyv-test", version = "=0.7.38-test.2", optional = true }

View File

@ -12,9 +12,19 @@ version = "0.93.8"
bench = false
[features]
default = ["filesystem_cache"]
default = ["filesystem_cache", "plugin_transform_host_native"]
plugin_transform_host_js = [
"wasmer/js-default",
"wasmer-wasix/js-default",
"wasmer-compiler-cranelift/wasm",
]
plugin_transform_host_native = [
"wasmer/default",
"wasmer-wasix/default",
"wasmer-compiler-cranelift/default",
]
# Supports a cache allow to store compiled bytecode into filesystem location.
# This feature implies in-memory cache enabled always.
# This feature implies in-memory cache support, but not via `memory_cache` feature.
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.
@ -36,15 +46,15 @@ rkyv-bytecheck-impl = [
rkyv-impl = ["__rkyv", "swc_common/plugin-rt", "swc_plugin_proxy/plugin-rt"]
[dependencies]
anyhow = "1.0.42"
enumset = "1.0.12"
once_cell = "1.10.0"
parking_lot = "0.12.0"
serde = { version = "1.0.126", features = ["derive"] }
serde_json = "1.0.64"
tracing = "0.1.32"
wasmer = { version = "2.3.0", default-features = false }
wasmer-wasi = { version = "2.3.0", default-features = false }
anyhow = "1.0.42"
enumset = "1.0.12"
once_cell = "1.10.0"
parking_lot = "0.12.0"
serde = { version = "1.0.126", features = ["derive"] }
serde_json = "1.0.64"
tracing = "0.1.32"
wasmer = { version = "3.2.0-beta.2", default-features = false }
wasmer-wasix = { version = "0.2", default-features = false }
swc_common = { version = "0.30.5", path = "../swc_common", features = [
"concurrent",
@ -53,16 +63,12 @@ swc_css_ast = { version = "0.136.6", path = "../swc_css_ast", optional = true }
swc_ecma_ast = { version = "0.102.5", path = "../swc_ecma_ast", optional = true }
swc_plugin_proxy = { version = "0.31.5", path = "../swc_plugin_proxy" }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
wasmer-cache = { version = "2.3.0", optional = true }
wasmer-compiler-cranelift = { version = "2.3.0" }
wasmer-engine-universal = { version = "2.3.0" }
wasmer-cache = { version = "3.2.0-beta.2", optional = true }
wasmer-compiler-cranelift = { version = "3.2.0-beta.2", default-features = false }
[dev-dependencies]
criterion = "0.3"
wasmer = "2.3.0"
wasmer-wasi = "2.3.0"
criterion = "0.3"
swc_atoms = { version = "0.4.43", path = '../swc_atoms' }
swc_css_ast = { version = "0.136.6", path = "../swc_css_ast", features = [

View File

@ -35,7 +35,7 @@ compile_error!(
/// however it is not gauranteed to be compatible across wasmer's
/// internal changes.
/// https://github.com/wasmerio/wasmer/issues/2781
const MODULE_SERIALIZATION_VERSION: &str = "v4";
const MODULE_SERIALIZATION_VERSION: &str = "v5";
/// A shared instance to plugin's module bytecode cache.
pub static PLUGIN_MODULE_CACHE: Lazy<PluginModuleCache> = Lazy::new(Default::default);
@ -112,7 +112,7 @@ pub fn init_plugin_module_cache_once(filesystem_cache_root: &Option<String>) {
}
#[cfg(feature = "memory_cache")]
pub fn init_plugin_module_cache_once() {
pub fn init_plugin_module_cache_once(_unused_cache_root: &Option<String>) {
PLUGIN_MODULE_CACHE.inner.get_or_init(|| {
Mutex::new(CacheInner {
loaded_module_bytes: Default::default(),
@ -142,24 +142,30 @@ impl PluginModuleCache {
/// In actual transform, `plugins` is also being called per each transform.
#[cfg(feature = "filesystem_cache")]
#[tracing::instrument(level = "info", skip_all)]
pub fn load_module(&self, binary_path: &Path) -> Result<Module, Error> {
pub fn load_module(&self, wasmer_store: &Store, binary_path: &Path) -> Result<Module, Error> {
let binary_path = binary_path.to_path_buf();
let mut inner_cache = self.inner.get().expect("Cache should be available").lock();
// if constructed Module is available in-memory, directly return it.
// Note we do not invalidate in-memory cache currently: if wasm binary is
// replaced in-process lifecycle (i.e devserver) it won't be reflected.
/*
[TODO]: This is currently disabled, since on the latest wasmer@3 subsequent
plugin load via in memory module causes intermittent heap_get_oob when
host tries to allocate memory inside of the guest.
Current guess is memory instance is being corrupted by the previous run, but
until figure out root cause & fix will only use fs_cache directly.
let in_memory_module = inner_cache.loaded_module_bytes.get(&binary_path);
if let Some(module) = in_memory_module {
return Ok(module.clone());
}
}*/
let module_bytes =
std::fs::read(&binary_path).context("Cannot read plugin from specified path")?;
let module_bytes_hash = Hash::generate(&module_bytes);
let wasmer_store = new_store();
let load_cold_wasm_bytes = || {
let span = tracing::span!(
tracing::Level::INFO,
@ -169,7 +175,7 @@ impl PluginModuleCache {
let span_guard = span.enter();
let _lock = self.instantiation_lock.lock();
let ret =
Module::new(&wasmer_store, module_bytes).context("Cannot compile plugin binary");
Module::new(wasmer_store, module_bytes).context("Cannot compile plugin binary");
drop(span_guard);
ret
};
@ -177,7 +183,7 @@ impl PluginModuleCache {
// Try to load compiled bytes from filesystem cache if available.
// Otherwise, cold compile instead.
let module = if let Some(fs_cache) = &mut inner_cache.fs_cache {
let load_result = unsafe { fs_cache.load(&wasmer_store, module_bytes_hash) };
let load_result = unsafe { fs_cache.load(wasmer_store, module_bytes_hash) };
if let Ok(module) = load_result {
module
} else {
@ -198,7 +204,7 @@ impl PluginModuleCache {
#[cfg(feature = "memory_cache")]
#[tracing::instrument(level = "info", skip_all)]
pub fn load_module(&self, binary_path: &Path) -> Result<Module, Error> {
pub fn load_module(&self, wasmer_store: &Store, binary_path: &Path) -> Result<Module, Error> {
let binary_path = binary_path.to_path_buf();
let mut inner_cache = self.inner.get().expect("Cache should be available").lock();
@ -213,9 +219,7 @@ impl PluginModuleCache {
//TODO: In native runtime we have to reconstruct module using raw bytes in
// memory cache. requires https://github.com/wasmerio/wasmer/pull/2821
let wasmer_store = new_store();
let module = Module::new(&wasmer_store, in_memory_module_bytes)?;
let module = Module::new(wasmer_store, in_memory_module_bytes)?;
Ok(module)
}
@ -243,28 +247,3 @@ impl PluginModuleCache {
}
}
}
/// Creates an instnace of [Store].
///
/// This function exists because we need to disable simd.
#[cfg(not(target_arch = "wasm32"))]
#[allow(unused_mut)]
fn new_store() -> Store {
// Use empty enumset to disable simd.
let mut set = EnumSet::new();
#[cfg(target_arch = "x86_64")]
set.insert(CpuFeature::SSE2);
let target = Target::new(Triple::host(), set);
let config = wasmer_compiler_cranelift::Cranelift::default();
let engine = wasmer_engine_universal::Universal::new(config)
.target(target)
.engine();
let tunables = BaseTunables::for_target(engine.target());
Store::new_with_tunables(&engine, tunables)
}
#[cfg(target_arch = "wasm32")]
fn new_store() -> Store {
Store::default()
}

View File

@ -1,25 +1,22 @@
use wasmer::{LazyInit, Memory};
use wasmer::Memory;
/// An external environment state imported (declared in host, injected into
/// guest) fn can access. This'll allow host to read from updated state from
/// guest.
///
/// This is `base` environment exposes guest's memory space only. For other
/// This is `base` environment exposes nothing. For other
/// calls requires additional data to be set in the host, separate
/// hostenvironments are declared. Refer `CommentsHostEnvironment` for an
/// example.
///
/// ref: https://docs.wasmer.io/integrations/examples/host-functions#declaring-the-data
#[derive(wasmer::WasmerEnv, Clone)]
#[derive(Clone)]
pub struct BaseHostEnvironment {
#[wasmer(export)]
pub memory: wasmer::LazyInit<Memory>,
pub memory: Option<Memory>,
}
impl BaseHostEnvironment {
pub fn new() -> BaseHostEnvironment {
BaseHostEnvironment {
memory: LazyInit::new(),
}
BaseHostEnvironment { memory: None }
}
}

View File

@ -7,20 +7,18 @@ use swc_common::{
BytePos,
};
use swc_plugin_proxy::COMMENTS;
use wasmer::{LazyInit, Memory, NativeFunc};
use wasmer::{AsStoreMut, FunctionEnvMut, Memory, TypedFunction};
use crate::memory_interop::{allocate_return_values_into_guest, copy_bytes_into_host};
/// External environment state for imported (declared in host, injected into
/// guest) fn for comments proxy.
#[derive(wasmer::WasmerEnv, Clone)]
#[derive(Clone)]
pub struct CommentHostEnvironment {
#[wasmer(export)]
pub memory: wasmer::LazyInit<Memory>,
pub memory: Option<Memory>,
/// Attached imported fn `__alloc` to the hostenvironment to allow any other
/// imported fn can allocate guest's memory space from host runtime.
#[wasmer(export(name = "__alloc"))]
pub alloc_guest_memory: LazyInit<NativeFunc<u32, u32>>,
pub alloc_guest_memory: Option<TypedFunction<u32, u32>>,
/// A buffer to `Comment`, or `Vec<Comment>` plugin need to pass to the host
/// to perform mutable comment operations like `add_leading, or
/// add_leading_comments`. This is vec to serialized bytes, doesn't
@ -32,8 +30,8 @@ pub struct CommentHostEnvironment {
impl CommentHostEnvironment {
pub fn new(mutable_comment_buffer: &Arc<Mutex<Vec<u8>>>) -> CommentHostEnvironment {
CommentHostEnvironment {
memory: LazyInit::default(),
alloc_guest_memory: LazyInit::default(),
memory: None,
alloc_guest_memory: None,
mutable_comment_buffer: mutable_comment_buffer.clone(),
}
}
@ -42,11 +40,18 @@ impl CommentHostEnvironment {
/// Copy given serialized byte into host's comment buffer, subsequent proxy call
/// in the host can read it.
#[tracing::instrument(level = "info", skip_all)]
pub fn copy_comment_to_host_env(env: &CommentHostEnvironment, bytes_ptr: u32, bytes_ptr_len: u32) {
if let Some(memory) = env.memory_ref() {
(*env.mutable_comment_buffer.lock()) =
copy_bytes_into_host(memory, bytes_ptr, bytes_ptr_len);
}
pub fn copy_comment_to_host_env(
mut env: FunctionEnvMut<CommentHostEnvironment>,
bytes_ptr: i32,
bytes_ptr_len: i32,
) {
let memory = env
.data()
.memory
.as_ref()
.expect("Memory instance should be available, check initialization");
(*env.data_mut().mutable_comment_buffer.lock()) =
copy_bytes_into_host(&memory.view(&env), bytes_ptr, bytes_ptr_len);
}
/// Utility fn to unwrap necessary values for the comments fn operation when fn
@ -88,31 +93,42 @@ where
/// Utility fn to unwrap necessary values for the comments, as well as host
/// environment's state.
#[tracing::instrument(level = "info", skip_all)]
fn unwrap_comments_storage_with_env<F, R>(env: &CommentHostEnvironment, f: F, default: R) -> R
fn unwrap_comments_storage_with_env<F, R>(
env: &FunctionEnvMut<CommentHostEnvironment>,
f: F,
default: R,
) -> R
where
F: FnOnce(&SingleThreadedComments, &Memory, &NativeFunc<u32, u32>) -> R,
F: FnOnce(&SingleThreadedComments, &Memory, &TypedFunction<u32, u32>) -> R,
{
if let Some(memory) = env.memory_ref() {
if let Some(alloc_guest_memory) = env.alloc_guest_memory_ref() {
return unwrap_comments_storage_or_default(
|comments| f(comments, memory, alloc_guest_memory),
default,
);
}
}
default
let memory = env
.data()
.memory
.as_ref()
.expect("Memory instance should be available, check initialization");
let alloc_guest_memory = env
.data()
.alloc_guest_memory
.as_ref()
.expect("Alloc guest memory fn should be available, check initialization");
return unwrap_comments_storage_or_default(
|comments| f(comments, memory, alloc_guest_memory),
default,
);
}
/// Common logics for add_*_comment/comments.
#[tracing::instrument(level = "info", skip_all)]
fn add_comments_inner<F>(env: &CommentHostEnvironment, byte_pos: u32, f: F)
fn add_comments_inner<F>(mut env: FunctionEnvMut<CommentHostEnvironment>, byte_pos: u32, f: F)
where
F: FnOnce(&SingleThreadedComments, BytePos, PluginSerializedBytes),
{
unwrap_comments_storage(|comments| {
let byte_pos = BytePos(byte_pos);
// PluginCommentProxy in the guest should've copied buffer already
let comment_byte = &mut (*env.mutable_comment_buffer.lock());
let comment_byte = &mut (*env.data_mut().mutable_comment_buffer.lock());
let serialized = PluginSerializedBytes::from_slice(comment_byte);
f(comments, byte_pos, serialized);
@ -123,7 +139,7 @@ where
});
}
pub fn add_leading_comment_proxy(env: &CommentHostEnvironment, byte_pos: u32) {
pub fn add_leading_comment_proxy(env: FunctionEnvMut<CommentHostEnvironment>, byte_pos: u32) {
add_comments_inner(env, byte_pos, |comments, byte_pos, serialized| {
comments.add_leading(
byte_pos,
@ -135,7 +151,7 @@ pub fn add_leading_comment_proxy(env: &CommentHostEnvironment, byte_pos: u32) {
}
#[tracing::instrument(level = "info", skip_all)]
pub fn add_leading_comments_proxy(env: &CommentHostEnvironment, byte_pos: u32) {
pub fn add_leading_comments_proxy(env: FunctionEnvMut<CommentHostEnvironment>, byte_pos: u32) {
add_comments_inner(env, byte_pos, |comments, byte_pos, serialized| {
comments.add_leading_comments(
byte_pos,
@ -160,13 +176,22 @@ pub fn move_leading_comments_proxy(from_byte_pos: u32, to_byte_pos: u32) {
#[tracing::instrument(level = "info", skip_all)]
pub fn take_leading_comments_proxy(
env: &CommentHostEnvironment,
mut env: FunctionEnvMut<CommentHostEnvironment>,
byte_pos: u32,
allocated_ret_ptr: u32,
) -> i32 {
unwrap_comments_storage_with_env(
env,
|comments, memory, alloc_guest_memory| {
let memory = env.data().memory.clone();
let memory = memory
.as_ref()
.expect("Memory instance should be available, check initialization");
let alloc_guest_memory = env.data().alloc_guest_memory.clone();
let alloc_guest_memory = alloc_guest_memory
.as_ref()
.expect("Alloc guest memory fn should be available, check initialization");
return unwrap_comments_storage_or_default(
|comments| {
let leading_comments = comments.take_leading(BytePos(byte_pos));
if let Some(leading_comments) = leading_comments {
let serialized_leading_comments_vec_bytes =
@ -175,6 +200,7 @@ pub fn take_leading_comments_proxy(
allocate_return_values_into_guest(
memory,
&mut env.as_store_mut(),
alloc_guest_memory,
allocated_ret_ptr,
&serialized_leading_comments_vec_bytes,
@ -185,7 +211,7 @@ pub fn take_leading_comments_proxy(
}
},
0,
)
);
}
/// Ask to get leading_comments from currently scoped comments held by
@ -195,13 +221,22 @@ pub fn take_leading_comments_proxy(
/// Allocated results should be read through CommentsPtr.
#[tracing::instrument(level = "info", skip_all)]
pub fn get_leading_comments_proxy(
env: &CommentHostEnvironment,
mut env: FunctionEnvMut<CommentHostEnvironment>,
byte_pos: u32,
allocated_ret_ptr: u32,
) -> i32 {
unwrap_comments_storage_with_env(
env,
|comments, memory, alloc_guest_memory| {
let memory = env.data().memory.clone();
let memory = memory
.as_ref()
.expect("Memory instance should be available, check initialization");
let alloc_guest_memory = env.data().alloc_guest_memory.clone();
let alloc_guest_memory = alloc_guest_memory
.as_ref()
.expect("Alloc guest memory fn should be available, check initialization");
return unwrap_comments_storage_or_default(
|comments| {
let leading_comments = comments.get_leading(BytePos(byte_pos));
if let Some(leading_comments) = leading_comments {
let serialized_leading_comments_vec_bytes =
@ -210,6 +245,7 @@ pub fn get_leading_comments_proxy(
allocate_return_values_into_guest(
memory,
&mut env.as_store_mut(),
alloc_guest_memory,
allocated_ret_ptr,
&serialized_leading_comments_vec_bytes,
@ -220,11 +256,11 @@ pub fn get_leading_comments_proxy(
}
},
0,
)
);
}
#[tracing::instrument(level = "info", skip_all)]
pub fn add_trailing_comment_proxy(env: &CommentHostEnvironment, byte_pos: u32) {
pub fn add_trailing_comment_proxy(env: FunctionEnvMut<CommentHostEnvironment>, byte_pos: u32) {
add_comments_inner(env, byte_pos, |comments, byte_pos, serialized| {
comments.add_trailing(
byte_pos,
@ -236,7 +272,7 @@ pub fn add_trailing_comment_proxy(env: &CommentHostEnvironment, byte_pos: u32) {
}
#[tracing::instrument(level = "info", skip_all)]
pub fn add_trailing_comments_proxy(env: &CommentHostEnvironment, byte_pos: u32) {
pub fn add_trailing_comments_proxy(env: FunctionEnvMut<CommentHostEnvironment>, byte_pos: u32) {
add_comments_inner(env, byte_pos, |comments, byte_pos, serialized| {
comments.add_trailing_comments(
byte_pos,
@ -264,13 +300,22 @@ pub fn move_trailing_comments_proxy(from_byte_pos: u32, to_byte_pos: u32) {
#[tracing::instrument(level = "info", skip_all)]
pub fn take_trailing_comments_proxy(
env: &CommentHostEnvironment,
mut env: FunctionEnvMut<CommentHostEnvironment>,
byte_pos: u32,
allocated_ret_ptr: u32,
) -> i32 {
unwrap_comments_storage_with_env(
env,
|comments, memory, alloc_guest_memory| {
let memory = env.data().memory.clone();
let memory = memory
.as_ref()
.expect("Memory instance should be available, check initialization");
let alloc_guest_memory = env.data().alloc_guest_memory.clone();
let alloc_guest_memory = alloc_guest_memory
.as_ref()
.expect("Alloc guest memory fn should be available, check initialization");
return unwrap_comments_storage_or_default(
|comments| {
let trailing_comments = comments.take_trailing(BytePos(byte_pos));
if let Some(leading_comments) = trailing_comments {
let serialized_leading_comments_vec_bytes =
@ -279,6 +324,7 @@ pub fn take_trailing_comments_proxy(
allocate_return_values_into_guest(
memory,
&mut env.as_store_mut(),
alloc_guest_memory,
allocated_ret_ptr,
&serialized_leading_comments_vec_bytes,
@ -289,18 +335,27 @@ pub fn take_trailing_comments_proxy(
}
},
0,
)
);
}
#[tracing::instrument(level = "info", skip_all)]
pub fn get_trailing_comments_proxy(
env: &CommentHostEnvironment,
mut env: FunctionEnvMut<CommentHostEnvironment>,
byte_pos: u32,
allocated_ret_ptr: u32,
) -> i32 {
unwrap_comments_storage_with_env(
env,
|comments, memory, alloc_guest_memory| {
let memory = env.data().memory.clone();
let memory = memory
.as_ref()
.expect("Memory instance should be available, check initialization");
let alloc_guest_memory = env.data().alloc_guest_memory.clone();
let alloc_guest_memory = alloc_guest_memory
.as_ref()
.expect("Alloc guest memory fn should be available, check initialization");
return unwrap_comments_storage_or_default(
|comments| {
let trailing_comments = comments.get_trailing(BytePos(byte_pos));
if let Some(leading_comments) = trailing_comments {
let serialized_leading_comments_vec_bytes =
@ -309,6 +364,7 @@ pub fn get_trailing_comments_proxy(
allocate_return_values_into_guest(
memory,
&mut env.as_store_mut(),
alloc_guest_memory,
allocated_ret_ptr,
&serialized_leading_comments_vec_bytes,
@ -319,7 +375,7 @@ pub fn get_trailing_comments_proxy(
}
},
0,
)
);
}
#[tracing::instrument(level = "info", skip_all)]

View File

@ -1,23 +1,22 @@
use std::sync::Arc;
use parking_lot::Mutex;
use wasmer::{LazyInit, Memory};
use wasmer::{FunctionEnvMut, Memory};
use crate::memory_interop::copy_bytes_into_host;
/// External environment to read swc_core diagnostics from the host.
#[derive(wasmer::WasmerEnv, Clone)]
#[derive(Clone)]
pub struct DiagnosticContextHostEnvironment {
#[wasmer(export)]
pub memory: wasmer::LazyInit<Memory>,
pub memory: Option<Memory>,
/// A buffer to store diagnostics data strings.
pub core_diag_buffer: Arc<Mutex<Vec<u8>>>,
}
impl DiagnosticContextHostEnvironment {
pub fn new(core_diag_buffer: &Arc<Mutex<Vec<u8>>>) -> DiagnosticContextHostEnvironment {
pub fn new(core_diag_buffer: &Arc<Mutex<Vec<u8>>>) -> Self {
DiagnosticContextHostEnvironment {
memory: LazyInit::default(),
memory: None,
core_diag_buffer: core_diag_buffer.clone(),
}
}
@ -25,10 +24,16 @@ impl DiagnosticContextHostEnvironment {
#[tracing::instrument(level = "info", skip_all)]
pub fn set_plugin_core_pkg_diagnostics(
env: &DiagnosticContextHostEnvironment,
bytes_ptr: u32,
bytes_ptr_len: u32,
mut env: FunctionEnvMut<DiagnosticContextHostEnvironment>,
bytes_ptr: i32,
bytes_ptr_len: i32,
) {
let memory = env.memory_ref().expect("Memory should be initialized");
(*env.core_diag_buffer.lock()) = copy_bytes_into_host(memory, bytes_ptr, bytes_ptr_len);
let memory = env
.data()
.memory
.as_ref()
.expect("Memory instance should be available, check initialization");
(*env.data_mut().core_diag_buffer.lock()) =
copy_bytes_into_host(&memory.view(&env), bytes_ptr, bytes_ptr_len);
}

View File

@ -2,23 +2,33 @@ use swc_common::{
errors::{Diagnostic, HANDLER},
plugin::serialized::PluginSerializedBytes,
};
use wasmer::FunctionEnvMut;
use crate::{host_environment::BaseHostEnvironment, memory_interop::copy_bytes_into_host};
#[tracing::instrument(level = "info", skip_all)]
pub fn emit_diagnostics(env: &BaseHostEnvironment, bytes_ptr: u32, bytes_ptr_len: u32) {
if let Some(memory) = env.memory_ref() {
if HANDLER.is_set() {
HANDLER.with(|handler| {
let diagnostics_bytes = copy_bytes_into_host(memory, bytes_ptr, bytes_ptr_len);
let serialized = PluginSerializedBytes::from_slice(&diagnostics_bytes[..]);
let diagnostic = PluginSerializedBytes::deserialize::<Diagnostic>(&serialized)
.expect("Should able to be deserialized into diagnostic");
pub fn emit_diagnostics(
env: FunctionEnvMut<BaseHostEnvironment>,
bytes_ptr: i32,
bytes_ptr_len: i32,
) {
let memory = env
.data()
.memory
.as_ref()
.expect("Memory instance should be available, check initialization");
let mut builder =
swc_common::errors::DiagnosticBuilder::new_diagnostic(handler, diagnostic);
builder.emit();
})
}
if HANDLER.is_set() {
HANDLER.with(|handler| {
let diagnostics_bytes =
copy_bytes_into_host(&memory.view(&env), bytes_ptr, bytes_ptr_len);
let serialized = PluginSerializedBytes::from_slice(&diagnostics_bytes[..]);
let diagnostic = PluginSerializedBytes::deserialize::<Diagnostic>(&serialized)
.expect("Should able to be deserialized into diagnostic");
let mut builder =
swc_common::errors::DiagnosticBuilder::new_diagnostic(handler, diagnostic);
builder.emit();
})
}
}

View File

@ -1,6 +1,7 @@
use swc_common::{
hygiene::MutableMarkContext, plugin::serialized::PluginSerializedBytes, Mark, SyntaxContext,
};
use wasmer::{AsStoreMut, FunctionEnvMut};
use crate::{host_environment::BaseHostEnvironment, memory_interop::write_into_memory_view};
@ -30,39 +31,60 @@ pub fn mark_set_builtin_proxy(self_mark: u32, is_builtin: u32) {
/// with return value accordingly.
#[tracing::instrument(level = "info", skip_all)]
pub fn mark_is_descendant_of_proxy(
env: &BaseHostEnvironment,
mut env: FunctionEnvMut<BaseHostEnvironment>,
self_mark: u32,
ancestor: u32,
allocated_ptr: u32,
) {
let memory = env.data().memory.clone();
let memory = memory
.as_ref()
.expect("Memory instance should be available, check initialization");
let self_mark = Mark::from_u32(self_mark);
let ancestor = Mark::from_u32(ancestor);
let return_value = self_mark.is_descendant_of(ancestor);
if let Some(memory) = env.memory_ref() {
let context = MutableMarkContext(self_mark.as_u32(), 0, return_value as u32);
let serialized_bytes =
PluginSerializedBytes::try_serialize(&context).expect("Should be serializable");
let context = MutableMarkContext(self_mark.as_u32(), 0, return_value as u32);
let serialized_bytes =
PluginSerializedBytes::try_serialize(&context).expect("Should be serializable");
write_into_memory_view(memory, &serialized_bytes, |_| allocated_ptr);
}
write_into_memory_view(
memory,
&mut env.as_store_mut(),
&serialized_bytes,
|_, _| allocated_ptr,
);
}
#[tracing::instrument(level = "info", skip_all)]
pub fn mark_least_ancestor_proxy(env: &BaseHostEnvironment, a: u32, b: u32, allocated_ptr: u32) {
pub fn mark_least_ancestor_proxy(
mut env: FunctionEnvMut<BaseHostEnvironment>,
a: u32,
b: u32,
allocated_ptr: u32,
) {
let memory = env.data().memory.clone();
let memory = memory
.as_ref()
.expect("Memory instance should be available, check initialization");
let a = Mark::from_u32(a);
let b = Mark::from_u32(b);
let return_value = Mark::least_ancestor(a, b).as_u32();
if let Some(memory) = env.memory_ref() {
let context = MutableMarkContext(a.as_u32(), b.as_u32(), return_value);
let serialized_bytes =
PluginSerializedBytes::try_serialize(&context).expect("Should be serializable");
let context = MutableMarkContext(a.as_u32(), b.as_u32(), return_value);
let serialized_bytes =
PluginSerializedBytes::try_serialize(&context).expect("Should be serializable");
write_into_memory_view(memory, &serialized_bytes, |_| allocated_ptr);
}
write_into_memory_view(
memory,
&mut env.as_store_mut(),
&serialized_bytes,
|_, _| allocated_ptr,
);
}
#[tracing::instrument(level = "info", skip_all)]
@ -74,21 +96,29 @@ pub fn syntax_context_apply_mark_proxy(self_syntax_context: u32, mark: u32) -> u
#[tracing::instrument(level = "info", skip_all)]
pub fn syntax_context_remove_mark_proxy(
env: &BaseHostEnvironment,
mut env: FunctionEnvMut<BaseHostEnvironment>,
self_mark: u32,
allocated_ptr: u32,
) {
let memory = env.data().memory.clone();
let memory = memory
.as_ref()
.expect("Memory instance should be available, check initialization");
let mut self_mark = SyntaxContext::from_u32(self_mark);
let return_value = self_mark.remove_mark();
if let Some(memory) = env.memory_ref() {
let context = MutableMarkContext(self_mark.as_u32(), 0, return_value.as_u32());
let serialized_bytes =
PluginSerializedBytes::try_serialize(&context).expect("Should be serializable");
let context = MutableMarkContext(self_mark.as_u32(), 0, return_value.as_u32());
let serialized_bytes =
PluginSerializedBytes::try_serialize(&context).expect("Should be serializable");
write_into_memory_view(memory, &serialized_bytes, |_| allocated_ptr);
}
write_into_memory_view(
memory,
&mut env.as_store_mut(),
&serialized_bytes,
|_, _| allocated_ptr,
);
}
#[tracing::instrument(level = "info", skip_all)]

View File

@ -5,18 +5,16 @@ use swc_common::plugin::{
metadata::{TransformPluginMetadataContext, TransformPluginMetadataContextKind},
serialized::PluginSerializedBytes,
};
use wasmer::{LazyInit, Memory, NativeFunc};
use wasmer::{AsStoreMut, FunctionEnvMut, Memory, TypedFunction};
use crate::memory_interop::{allocate_return_values_into_guest, copy_bytes_into_host};
#[derive(wasmer::WasmerEnv, Clone)]
#[derive(Clone)]
pub struct MetadataContextHostEnvironment {
#[wasmer(export)]
pub memory: wasmer::LazyInit<Memory>,
pub memory: Option<Memory>,
/// Attached imported fn `__alloc` to the hostenvironment to allow any other
/// imported fn can allocate guest's memory space from host runtime.
#[wasmer(export(name = "__alloc"))]
pub alloc_guest_memory: LazyInit<NativeFunc<u32, u32>>,
pub alloc_guest_memory: Option<TypedFunction<u32, u32>>,
pub metadata_context: Arc<TransformPluginMetadataContext>,
pub transform_plugin_config: Option<serde_json::Value>,
/// A buffer to string key to the context plugin need to pass to the host.
@ -30,8 +28,8 @@ impl MetadataContextHostEnvironment {
mutable_context_key_buffer: &Arc<Mutex<Vec<u8>>>,
) -> Self {
MetadataContextHostEnvironment {
memory: LazyInit::default(),
alloc_guest_memory: LazyInit::default(),
memory: None,
alloc_guest_memory: None,
metadata_context: metadata_context.clone(),
transform_plugin_config: plugin_config.clone(),
mutable_context_key_buffer: mutable_context_key_buffer.clone(),
@ -43,135 +41,167 @@ impl MetadataContextHostEnvironment {
/// in the host can read it.
#[tracing::instrument(level = "info", skip_all)]
pub fn copy_context_key_to_host_env(
env: &MetadataContextHostEnvironment,
bytes_ptr: u32,
bytes_ptr_len: u32,
mut env: FunctionEnvMut<MetadataContextHostEnvironment>,
bytes_ptr: i32,
bytes_ptr_len: i32,
) {
if let Some(memory) = env.memory_ref() {
(*env.mutable_context_key_buffer.lock()) =
copy_bytes_into_host(memory, bytes_ptr, bytes_ptr_len);
}
let memory = env
.data()
.memory
.as_ref()
.expect("Memory instance should be available, check initialization");
(*env.data_mut().mutable_context_key_buffer.lock()) =
copy_bytes_into_host(&memory.view(&env), bytes_ptr, bytes_ptr_len);
}
#[tracing::instrument(level = "info", skip_all)]
pub fn get_transform_plugin_config(
env: &MetadataContextHostEnvironment,
mut env: FunctionEnvMut<MetadataContextHostEnvironment>,
allocated_ret_ptr: u32,
) -> i32 {
if let Some(memory) = env.memory_ref() {
if let Some(alloc_guest_memory) = env.alloc_guest_memory_ref() {
let config_value = &env.transform_plugin_config;
if let Some(config_value) = config_value {
// Lazy as possible as we can - only deserialize json value if transform plugin
// actually needs it.
let config = serde_json::to_string(config_value).ok();
if let Some(config) = config {
let serialized = PluginSerializedBytes::try_serialize(&config)
.expect("Should be serializable");
let memory = env.data().memory.clone();
let memory = memory
.as_ref()
.expect("Memory instance should be available, check initialization");
allocate_return_values_into_guest(
memory,
alloc_guest_memory,
allocated_ret_ptr,
&serialized,
);
let alloc_guest_memory = env.data().alloc_guest_memory.clone();
let alloc_guest_memory = alloc_guest_memory
.as_ref()
.expect("Alloc guest memory fn should be available, check initialization");
return 1;
}
}
let config_value = &env.data().transform_plugin_config;
if let Some(config_value) = config_value {
// Lazy as possible as we can - only deserialize json value if transform plugin
// actually needs it.
let config = serde_json::to_string(config_value).ok();
if let Some(config) = config {
let serialized =
PluginSerializedBytes::try_serialize(&config).expect("Should be serializable");
allocate_return_values_into_guest(
memory,
&mut env.as_store_mut(),
alloc_guest_memory,
allocated_ret_ptr,
&serialized,
);
return 1;
}
}
0
return 0;
}
#[tracing::instrument(level = "info", skip_all)]
pub fn get_transform_context(
env: &MetadataContextHostEnvironment,
mut env: FunctionEnvMut<MetadataContextHostEnvironment>,
key: u32,
allocated_ret_ptr: u32,
) -> i32 {
if let Some(memory) = env.memory_ref() {
if let Some(alloc_guest_memory) = env.alloc_guest_memory_ref() {
let value = env
.metadata_context
.get(&TransformPluginMetadataContextKind::from(key));
let memory = env.data().memory.clone();
let memory = memory
.as_ref()
.expect("Memory instance should be available, check initialization");
if let Some(value) = value {
let serialized =
PluginSerializedBytes::try_serialize(&value).expect("Should be serializable");
let alloc_guest_memory = env.data().alloc_guest_memory.clone();
let alloc_guest_memory = alloc_guest_memory
.as_ref()
.expect("Alloc guest memory fn should be available, check initialization");
allocate_return_values_into_guest(
memory,
alloc_guest_memory,
allocated_ret_ptr,
&serialized,
);
let value = env
.data()
.metadata_context
.get(&TransformPluginMetadataContextKind::from(key));
return 1;
}
}
if let Some(value) = value {
let serialized =
PluginSerializedBytes::try_serialize(&value).expect("Should be serializable");
allocate_return_values_into_guest(
memory,
&mut env.as_store_mut(),
alloc_guest_memory,
allocated_ret_ptr,
&serialized,
);
return 1;
}
0
return 0;
}
#[tracing::instrument(level = "info", skip_all)]
pub fn get_experimental_transform_context(
env: &MetadataContextHostEnvironment,
mut env: FunctionEnvMut<MetadataContextHostEnvironment>,
allocated_ret_ptr: u32,
) -> i32 {
if let Some(memory) = env.memory_ref() {
if let Some(alloc_guest_memory) = env.alloc_guest_memory_ref() {
let context_key_buffer = &*env.mutable_context_key_buffer.lock();
let key: String = PluginSerializedBytes::from_slice(&context_key_buffer[..])
.deserialize()
.expect("Should able to deserialize");
let memory = env.data().memory.clone();
let memory = memory
.as_ref()
.expect("Memory instance should be available, check initialization");
let value = env
.metadata_context
.experimental
.get(&key)
.map(|v| v.to_string());
let alloc_guest_memory = env.data().alloc_guest_memory.clone();
let alloc_guest_memory = alloc_guest_memory
.as_ref()
.expect("Alloc guest memory fn should be available, check initialization");
if let Some(value) = value {
let serialized =
PluginSerializedBytes::try_serialize(&value).expect("Should be serializable");
let context_key_buffer = env.data().mutable_context_key_buffer.lock().clone();
let key: String = PluginSerializedBytes::from_slice(&context_key_buffer[..])
.deserialize()
.expect("Should able to deserialize");
allocate_return_values_into_guest(
memory,
alloc_guest_memory,
allocated_ret_ptr,
&serialized,
);
let value = env
.data()
.metadata_context
.experimental
.get(&key)
.map(|v| v.to_string());
return 1;
}
}
if let Some(value) = value {
let serialized =
PluginSerializedBytes::try_serialize(&value).expect("Should be serializable");
allocate_return_values_into_guest(
memory,
&mut env.as_store_mut(),
alloc_guest_memory,
allocated_ret_ptr,
&serialized,
);
return 1;
}
0
return 0;
}
#[tracing::instrument(level = "info", skip_all)]
pub fn get_raw_experiemtal_transform_context(
env: &MetadataContextHostEnvironment,
mut env: FunctionEnvMut<MetadataContextHostEnvironment>,
allocated_ret_ptr: u32,
) -> i32 {
if let Some(memory) = env.memory_ref() {
let experimental_context = &env.metadata_context.experimental;
let serialized_experimental_context_bytes =
PluginSerializedBytes::try_serialize(experimental_context)
.expect("Should be serializable");
if let Some(alloc_guest_memory) = env.alloc_guest_memory_ref() {
allocate_return_values_into_guest(
memory,
alloc_guest_memory,
allocated_ret_ptr,
&serialized_experimental_context_bytes,
);
1
} else {
0
}
} else {
0
}
let memory = env.data().memory.clone();
let memory = memory
.as_ref()
.expect("Memory instance should be available, check initialization");
let alloc_guest_memory = env.data().alloc_guest_memory.clone();
let alloc_guest_memory = alloc_guest_memory
.as_ref()
.expect("Alloc guest memory fn should be available, check initialization");
let experimental_context = env.data().metadata_context.experimental.clone();
let serialized_experimental_context_bytes =
PluginSerializedBytes::try_serialize(&experimental_context)
.expect("Should be serializable");
allocate_return_values_into_guest(
memory,
&mut env.as_store_mut(),
alloc_guest_memory,
allocated_ret_ptr,
&serialized_experimental_context_bytes,
);
1
}

View File

@ -41,11 +41,7 @@
* actual vec into guest it'll write pointer to the vec into the struct.
*/
use std::sync::Arc;
use parking_lot::Mutex;
use swc_common::{plugin::metadata::TransformPluginMetadataContext, SourceMap};
use wasmer::{imports, Function, ImportObject, Module};
use wasmer::{imports, Function, FunctionEnv, Imports, Store};
use crate::{
host_environment::BaseHostEnvironment,
@ -57,247 +53,190 @@ use crate::{
has_trailing_comments_proxy, move_leading_comments_proxy, move_trailing_comments_proxy,
take_leading_comments_proxy, take_trailing_comments_proxy, CommentHostEnvironment,
},
diagnostics::{set_plugin_core_pkg_diagnostics, DiagnosticContextHostEnvironment},
metadata_context::get_raw_experiemtal_transform_context,
set_transform_result::{set_transform_result, TransformResultHostEnvironment},
source_map::span_to_source_proxy,
span::span_dummy_with_cmt_proxy,
},
};
mod comments;
mod diagnostics;
mod handler;
mod hygiene;
mod metadata_context;
mod set_transform_result;
mod source_map;
mod span;
pub(crate) mod comments;
pub(crate) mod diagnostics;
pub(crate) mod handler;
pub(crate) mod hygiene;
pub(crate) mod metadata_context;
pub(crate) mod set_transform_result;
pub(crate) mod source_map;
pub(crate) mod span;
use handler::*;
use hygiene::*;
use self::{
diagnostics::{set_plugin_core_pkg_diagnostics, DiagnosticContextHostEnvironment},
metadata_context::{
copy_context_key_to_host_env, get_experimental_transform_context, get_transform_context,
get_transform_plugin_config, MetadataContextHostEnvironment,
},
source_map::{
doctest_offset_line_proxy, lookup_byte_offset_proxy, lookup_char_pos_proxy,
merge_spans_proxy, span_to_filename_proxy, span_to_lines_proxy, span_to_source_proxy,
span_to_string_proxy, SourceMapHostEnvironment,
merge_spans_proxy, span_to_filename_proxy, span_to_lines_proxy, span_to_string_proxy,
SourceMapHostEnvironment,
},
};
/// Create an ImportObject includes functions to be imported from host to the
/// plugins.
pub(crate) fn build_import_object(
module: &Module,
source_map: Arc<SourceMap>,
metadata_context: Arc<TransformPluginMetadataContext>,
plugin_config: Option<serde_json::Value>,
transform_result: &Arc<Mutex<Vec<u8>>>,
core_diag_buffer: &Arc<Mutex<Vec<u8>>>,
) -> ImportObject {
let store = module.store();
wasmer_store: &mut Store,
metadata_env: &FunctionEnv<MetadataContextHostEnvironment>,
transform_env: &FunctionEnv<TransformResultHostEnvironment>,
base_env: &FunctionEnv<BaseHostEnvironment>,
comments_env: &FunctionEnv<CommentHostEnvironment>,
source_map_host_env: &FunctionEnv<SourceMapHostEnvironment>,
diagnostics_env: &FunctionEnv<DiagnosticContextHostEnvironment>,
) -> Imports {
// core_diagnostics
let set_transform_plugin_core_pkg_diagnostics_fn_decl = Function::new_native_with_env(
store,
DiagnosticContextHostEnvironment::new(core_diag_buffer),
let set_transform_plugin_core_pkg_diagnostics_fn_decl = Function::new_typed_with_env(
wasmer_store,
diagnostics_env,
set_plugin_core_pkg_diagnostics,
);
// metadata
let context_key_buffer = Arc::new(Mutex::new(vec![]));
let copy_context_key_to_host_env_fn_decl = Function::new_native_with_env(
store,
MetadataContextHostEnvironment::new(&metadata_context, &plugin_config, &context_key_buffer),
copy_context_key_to_host_env,
);
let get_transform_plugin_config_fn_decl = Function::new_native_with_env(
store,
MetadataContextHostEnvironment::new(&metadata_context, &plugin_config, &context_key_buffer),
get_transform_plugin_config,
);
let get_transform_context_fn_decl = Function::new_native_with_env(
store,
MetadataContextHostEnvironment::new(&metadata_context, &plugin_config, &context_key_buffer),
get_transform_context,
);
let get_experimental_transform_context_fn_decl = Function::new_native_with_env(
store,
MetadataContextHostEnvironment::new(&metadata_context, &plugin_config, &context_key_buffer),
let copy_context_key_to_host_env_fn_decl =
Function::new_typed_with_env(wasmer_store, metadata_env, copy_context_key_to_host_env);
let get_transform_plugin_config_fn_decl =
Function::new_typed_with_env(wasmer_store, metadata_env, get_transform_plugin_config);
let get_transform_context_fn_decl =
Function::new_typed_with_env(wasmer_store, metadata_env, get_transform_context);
let get_experimental_transform_context_fn_decl = Function::new_typed_with_env(
wasmer_store,
metadata_env,
get_experimental_transform_context,
);
let get_raw_experiemtal_transform_context_fn_decl = Function::new_native_with_env(
store,
MetadataContextHostEnvironment::new(&metadata_context, &plugin_config, &context_key_buffer),
let get_raw_experiemtal_transform_context_fn_decl = Function::new_typed_with_env(
wasmer_store,
metadata_env,
get_raw_experiemtal_transform_context,
);
// transform_result
let set_transform_result_fn_decl = Function::new_native_with_env(
store,
TransformResultHostEnvironment::new(transform_result),
set_transform_result,
);
let set_transform_result_fn_decl =
Function::new_typed_with_env(wasmer_store, transform_env, set_transform_result);
// handler
let emit_diagnostics_fn_decl =
Function::new_native_with_env(store, BaseHostEnvironment::new(), emit_diagnostics);
Function::new_typed_with_env(wasmer_store, base_env, emit_diagnostics);
// hygiene
let mark_fresh_fn_decl = Function::new_native(store, mark_fresh_proxy);
let mark_parent_fn_decl = Function::new_native(store, mark_parent_proxy);
let mark_is_builtin_fn_decl = Function::new_native(store, mark_is_builtin_proxy);
let mark_set_builtin_fn_decl = Function::new_native(store, mark_set_builtin_proxy);
let mark_is_descendant_of_fn_decl = Function::new_native_with_env(
store,
BaseHostEnvironment::new(),
mark_is_descendant_of_proxy,
);
let mark_fresh_fn_decl = Function::new_typed(wasmer_store, mark_fresh_proxy);
let mark_parent_fn_decl = Function::new_typed(wasmer_store, mark_parent_proxy);
let mark_is_builtin_fn_decl = Function::new_typed(wasmer_store, mark_is_builtin_proxy);
let mark_set_builtin_fn_decl = Function::new_typed(wasmer_store, mark_set_builtin_proxy);
let mark_is_descendant_of_fn_decl =
Function::new_typed_with_env(wasmer_store, base_env, mark_is_descendant_of_proxy);
let mark_least_ancestor_fn_decl =
Function::new_native_with_env(store, BaseHostEnvironment::new(), mark_least_ancestor_proxy);
Function::new_typed_with_env(wasmer_store, base_env, mark_least_ancestor_proxy);
let syntax_context_apply_mark_fn_decl =
Function::new_native(store, syntax_context_apply_mark_proxy);
let syntax_context_remove_mark_fn_decl = Function::new_native_with_env(
store,
BaseHostEnvironment::new(),
syntax_context_remove_mark_proxy,
);
let syntax_context_outer_fn_decl = Function::new_native(store, syntax_context_outer_proxy);
Function::new_typed(wasmer_store, syntax_context_apply_mark_proxy);
let syntax_context_remove_mark_fn_decl =
Function::new_typed_with_env(wasmer_store, base_env, syntax_context_remove_mark_proxy);
let syntax_context_outer_fn_decl =
Function::new_typed(wasmer_store, syntax_context_outer_proxy);
// Span
let span_dummy_with_cmt_fn_decl = Function::new_native(store, span_dummy_with_cmt_proxy);
let span_dummy_with_cmt_fn_decl = Function::new_typed(wasmer_store, span_dummy_with_cmt_proxy);
// comments
let comment_buffer = Arc::new(Mutex::new(vec![]));
let copy_comment_to_host_env_fn_decl =
Function::new_typed_with_env(wasmer_store, comments_env, copy_comment_to_host_env);
let copy_comment_to_host_env_fn_decl = Function::new_native_with_env(
store,
CommentHostEnvironment::new(&comment_buffer),
copy_comment_to_host_env,
);
let add_leading_comment_fn_decl =
Function::new_typed_with_env(wasmer_store, comments_env, add_leading_comment_proxy);
let add_leading_comment_fn_decl = Function::new_native_with_env(
store,
CommentHostEnvironment::new(&comment_buffer),
add_leading_comment_proxy,
);
let add_leading_comments_fn_decl =
Function::new_typed_with_env(wasmer_store, comments_env, add_leading_comments_proxy);
let add_leading_comments_fn_decl = Function::new_native_with_env(
store,
CommentHostEnvironment::new(&comment_buffer),
add_leading_comments_proxy,
);
let has_leading_comments_fn_decl =
Function::new_typed(wasmer_store, has_leading_comments_proxy);
let has_leading_comments_fn_decl = Function::new_native(store, has_leading_comments_proxy);
let move_leading_comments_fn_decl =
Function::new_typed(wasmer_store, move_leading_comments_proxy);
let move_leading_comments_fn_decl = Function::new_native(store, move_leading_comments_proxy);
let take_leading_comments_fn_decl = Function::new_native_with_env(
store,
let take_leading_comments_fn_decl = Function::new_typed_with_env(
wasmer_store,
// take_* doesn't need to share buffer to pass values from plugin to the host - do not
// clone buffer here.
CommentHostEnvironment::new(&Default::default()),
comments_env,
take_leading_comments_proxy,
);
let get_leading_comments_fn_decl = Function::new_native_with_env(
store,
let get_leading_comments_fn_decl = Function::new_typed_with_env(
wasmer_store,
// get_* doesn't need to share buffer to pass values from plugin to the host - do not clone
// buffer here.
CommentHostEnvironment::new(&Default::default()),
comments_env,
get_leading_comments_proxy,
);
let add_trailing_comment_fn_decl = Function::new_native_with_env(
store,
CommentHostEnvironment::new(&comment_buffer),
add_trailing_comment_proxy,
);
let add_trailing_comment_fn_decl =
Function::new_typed_with_env(wasmer_store, comments_env, add_trailing_comment_proxy);
let add_trailing_comments_fn_decl = Function::new_native_with_env(
store,
CommentHostEnvironment::new(&comment_buffer),
add_trailing_comments_proxy,
);
let add_trailing_comments_fn_decl =
Function::new_typed_with_env(wasmer_store, comments_env, add_trailing_comments_proxy);
let has_trailing_comments_fn_decl = Function::new_native(store, has_trailing_comments_proxy);
let has_trailing_comments_fn_decl =
Function::new_typed(wasmer_store, has_trailing_comments_proxy);
let move_trailing_comments_fn_decl = Function::new_native(store, move_trailing_comments_proxy);
let move_trailing_comments_fn_decl =
Function::new_typed(wasmer_store, move_trailing_comments_proxy);
let take_trailing_comments_fn_decl = Function::new_native_with_env(
store,
let take_trailing_comments_fn_decl = Function::new_typed_with_env(
wasmer_store,
// take_* doesn't need to share buffer to pass values from plugin to the host - do not
// clone buffer here.
CommentHostEnvironment::new(&Default::default()),
comments_env,
take_trailing_comments_proxy,
);
let get_trailing_comments_fn_decl = Function::new_native_with_env(
store,
let get_trailing_comments_fn_decl = Function::new_typed_with_env(
wasmer_store,
// get_* doesn't need to share buffer to pass values from plugin to the host - do not clone
// buffer here.
CommentHostEnvironment::new(&Default::default()),
comments_env,
get_trailing_comments_proxy,
);
let add_pure_comment_fn_decl = Function::new_native(store, add_pure_comment_proxy);
let add_pure_comment_fn_decl = Function::new_typed(wasmer_store, add_pure_comment_proxy);
// source_map
let source_map_buffer = Arc::new(Mutex::new(vec![]));
let source_map = Arc::new(Mutex::new(source_map));
let lookup_char_pos_source_map_fn_decl =
Function::new_typed_with_env(wasmer_store, source_map_host_env, lookup_char_pos_proxy);
let lookup_char_pos_source_map_fn_decl = Function::new_native_with_env(
store,
SourceMapHostEnvironment::new(&source_map, &source_map_buffer),
lookup_char_pos_proxy,
);
let doctest_offset_line_fn_decl =
Function::new_typed_with_env(wasmer_store, source_map_host_env, doctest_offset_line_proxy);
let doctest_offset_line_fn_decl = Function::new_native_with_env(
store,
SourceMapHostEnvironment::new(&source_map, &source_map_buffer),
doctest_offset_line_proxy,
);
let merge_spans_fn_decl =
Function::new_typed_with_env(wasmer_store, source_map_host_env, merge_spans_proxy);
let merge_spans_fn_decl = Function::new_native_with_env(
store,
SourceMapHostEnvironment::new(&source_map, &source_map_buffer),
merge_spans_proxy,
);
let span_to_string_fn_decl =
Function::new_typed_with_env(wasmer_store, source_map_host_env, span_to_string_proxy);
let span_to_string_fn_decl = Function::new_native_with_env(
store,
SourceMapHostEnvironment::new(&source_map, &source_map_buffer),
span_to_string_proxy,
);
let span_to_filename_fn_decl =
Function::new_typed_with_env(wasmer_store, source_map_host_env, span_to_filename_proxy);
let span_to_filename_fn_decl = Function::new_native_with_env(
store,
SourceMapHostEnvironment::new(&source_map, &source_map_buffer),
span_to_filename_proxy,
);
let span_to_source_fn_decl =
Function::new_typed_with_env(wasmer_store, source_map_host_env, span_to_source_proxy);
let span_to_source_fn_decl = Function::new_native_with_env(
store,
SourceMapHostEnvironment::new(&source_map, &source_map_buffer),
span_to_source_proxy,
);
let span_to_lines_fn_decl =
Function::new_typed_with_env(wasmer_store, source_map_host_env, span_to_lines_proxy);
let span_to_lines_fn_decl = Function::new_native_with_env(
store,
SourceMapHostEnvironment::new(&source_map, &source_map_buffer),
span_to_lines_proxy,
);
let lookup_byte_offset_fn_decl = Function::new_native_with_env(
store,
SourceMapHostEnvironment::new(&source_map, &source_map_buffer),
lookup_byte_offset_proxy,
);
let lookup_byte_offset_fn_decl =
Function::new_typed_with_env(wasmer_store, source_map_host_env, lookup_byte_offset_proxy);
imports! {
"env" => {

View File

@ -1,7 +1,7 @@
use std::sync::Arc;
use parking_lot::Mutex;
use wasmer::{LazyInit, Memory};
use wasmer::{FunctionEnvMut, Memory};
use crate::memory_interop::copy_bytes_into_host;
@ -10,17 +10,16 @@ use crate::memory_interop::copy_bytes_into_host;
///
/// When plugin performs its transform it'll fill in `transform_result` with
/// serialized result. Host will reconstruct AST from those value.
#[derive(wasmer::WasmerEnv, Clone)]
#[derive(Clone)]
pub struct TransformResultHostEnvironment {
#[wasmer(export)]
pub memory: wasmer::LazyInit<Memory>,
pub memory: Option<Memory>,
pub transform_result: Arc<Mutex<Vec<u8>>>,
}
impl TransformResultHostEnvironment {
pub fn new(transform_result: &Arc<Mutex<Vec<u8>>>) -> TransformResultHostEnvironment {
TransformResultHostEnvironment {
memory: LazyInit::default(),
memory: None,
transform_result: transform_result.clone(),
}
}
@ -32,11 +31,16 @@ impl TransformResultHostEnvironment {
/// this to set its result back to host.
#[tracing::instrument(level = "info", skip_all)]
pub fn set_transform_result(
env: &TransformResultHostEnvironment,
bytes_ptr: u32,
bytes_ptr_len: u32,
mut env: FunctionEnvMut<TransformResultHostEnvironment>,
bytes_ptr: i32,
bytes_ptr_len: i32,
) {
if let Some(memory) = env.memory_ref() {
(*env.transform_result.lock()) = copy_bytes_into_host(memory, bytes_ptr, bytes_ptr_len);
}
let memory = env
.data()
.memory
.as_ref()
.expect("Memory instance should be available, check initialization");
(*env.data_mut().transform_result.lock()) =
copy_bytes_into_host(&memory.view(&env), bytes_ptr, bytes_ptr_len);
}

View File

@ -6,20 +6,18 @@ use swc_common::{
source_map::{PartialFileLines, PartialLoc},
BytePos, SourceMap, SourceMapper, Span, SyntaxContext,
};
use wasmer::{LazyInit, Memory, NativeFunc};
use wasmer::{AsStoreMut, FunctionEnvMut, Memory, TypedFunction};
use crate::memory_interop::{allocate_return_values_into_guest, write_into_memory_view};
/// External environment state for imported (declared in host, injected into
/// guest) fn for source map proxy.
#[derive(wasmer::WasmerEnv, Clone)]
#[derive(Clone)]
pub struct SourceMapHostEnvironment {
#[wasmer(export)]
pub memory: wasmer::LazyInit<Memory>,
pub memory: Option<Memory>,
/// Attached imported fn `__alloc` to the hostenvironment to allow any other
/// imported fn can allocate guest's memory space from host runtime.
#[wasmer(export(name = "__alloc"))]
pub alloc_guest_memory: LazyInit<NativeFunc<u32, u32>>,
pub alloc_guest_memory: Option<TypedFunction<u32, u32>>,
pub source_map: Arc<Mutex<Arc<SourceMap>>>,
/// A buffer to non-determined size of return value from the host.
pub mutable_source_map_buffer: Arc<Mutex<Vec<u8>>>,
@ -31,8 +29,8 @@ impl SourceMapHostEnvironment {
mutable_source_map_buffer: &Arc<Mutex<Vec<u8>>>,
) -> SourceMapHostEnvironment {
SourceMapHostEnvironment {
memory: LazyInit::default(),
alloc_guest_memory: LazyInit::default(),
memory: None,
alloc_guest_memory: None,
source_map: source_map.clone(),
mutable_source_map_buffer: mutable_source_map_buffer.clone(),
}
@ -44,52 +42,54 @@ impl SourceMapHostEnvironment {
/// to avoid unnecessary data copying.
#[tracing::instrument(level = "info", skip_all)]
pub fn lookup_char_pos_proxy(
env: &SourceMapHostEnvironment,
mut env: FunctionEnvMut<SourceMapHostEnvironment>,
byte_pos: u32,
should_include_source_file: i32,
allocated_ret_ptr: u32,
) -> i32 {
if let Some(memory) = env.memory_ref() {
let original_loc = (env.source_map.lock()).lookup_char_pos(BytePos(byte_pos));
let ret = PartialLoc {
source_file: if should_include_source_file == 0 {
None
} else {
Some(original_loc.file)
},
line: original_loc.line,
col: original_loc.col.0,
col_display: original_loc.col_display,
};
let memory = env.data().memory.clone();
let memory = memory
.as_ref()
.expect("Memory instance should be available, check initialization");
let serialized_loc_bytes =
PluginSerializedBytes::try_serialize(&ret).expect("Should be serializable");
if let Some(alloc_guest_memory) = env.alloc_guest_memory_ref() {
allocate_return_values_into_guest(
memory,
alloc_guest_memory,
allocated_ret_ptr,
&serialized_loc_bytes,
);
1
let original_loc = (env.data().source_map.lock()).lookup_char_pos(BytePos(byte_pos));
let ret = PartialLoc {
source_file: if should_include_source_file == 0 {
None
} else {
0
}
Some(original_loc.file)
},
line: original_loc.line,
col: original_loc.col.0,
col_display: original_loc.col_display,
};
let serialized_loc_bytes =
PluginSerializedBytes::try_serialize(&ret).expect("Should be serializable");
if let Some(alloc_guest_memory) = env.data().alloc_guest_memory.clone().as_ref() {
allocate_return_values_into_guest(
memory,
&mut env.as_store_mut(),
alloc_guest_memory,
allocated_ret_ptr,
&serialized_loc_bytes,
);
1
} else {
0
}
}
#[tracing::instrument(level = "info", skip_all)]
pub fn doctest_offset_line_proxy(env: &SourceMapHostEnvironment, orig: u32) -> u32 {
(env.source_map.lock()).doctest_offset_line(orig as usize) as u32
pub fn doctest_offset_line_proxy(env: FunctionEnvMut<SourceMapHostEnvironment>, orig: u32) -> u32 {
(env.data().source_map.lock()).doctest_offset_line(orig as usize) as u32
}
#[allow(clippy::too_many_arguments)]
#[tracing::instrument(level = "info", skip_all)]
pub fn merge_spans_proxy(
env: &SourceMapHostEnvironment,
mut env: FunctionEnvMut<SourceMapHostEnvironment>,
lhs_lo: u32,
lhs_hi: u32,
lhs_ctxt: u32,
@ -98,28 +98,34 @@ pub fn merge_spans_proxy(
rhs_ctxt: u32,
allocated_ptr: u32,
) -> i32 {
if let Some(memory) = env.memory_ref() {
let sp_lhs = Span {
lo: BytePos(lhs_lo),
hi: BytePos(lhs_hi),
ctxt: SyntaxContext::from_u32(lhs_ctxt),
};
let memory = env.data().memory.clone();
let memory = memory
.as_ref()
.expect("Memory instance should be available, check initialization");
let sp_rhs = Span {
lo: BytePos(rhs_lo),
hi: BytePos(rhs_hi),
ctxt: SyntaxContext::from_u32(rhs_ctxt),
};
let sp_lhs = Span {
lo: BytePos(lhs_lo),
hi: BytePos(lhs_hi),
ctxt: SyntaxContext::from_u32(lhs_ctxt),
};
let ret = (env.source_map.lock()).merge_spans(sp_lhs, sp_rhs);
if let Some(span) = ret {
let serialized_bytes =
PluginSerializedBytes::try_serialize(&span).expect("Should be serializable");
write_into_memory_view(memory, &serialized_bytes, |_| allocated_ptr);
1
} else {
0
}
let sp_rhs = Span {
lo: BytePos(rhs_lo),
hi: BytePos(rhs_hi),
ctxt: SyntaxContext::from_u32(rhs_ctxt),
};
let ret = (env.data().source_map.lock()).merge_spans(sp_lhs, sp_rhs);
if let Some(span) = ret {
let serialized_bytes =
PluginSerializedBytes::try_serialize(&span).expect("Should be serializable");
write_into_memory_view(
memory,
&mut env.as_store_mut(),
&serialized_bytes,
|_, _| allocated_ptr,
);
1
} else {
0
}
@ -127,177 +133,192 @@ pub fn merge_spans_proxy(
#[tracing::instrument(level = "info", skip_all)]
pub fn span_to_lines_proxy(
env: &SourceMapHostEnvironment,
mut env: FunctionEnvMut<SourceMapHostEnvironment>,
span_lo: u32,
span_hi: u32,
span_ctxt: u32,
should_request_source_file: i32,
allocated_ret_ptr: u32,
) -> i32 {
if let Some(memory) = env.memory_ref() {
let span = Span {
lo: BytePos(span_lo),
hi: BytePos(span_hi),
ctxt: SyntaxContext::from_u32(span_ctxt),
};
let memory = env.data().memory.clone();
let memory = memory
.as_ref()
.expect("Memory instance should be available, check initialization");
let ret = (env.source_map.lock())
.span_to_lines(span)
.map(|lines| PartialFileLines {
file: if should_request_source_file == 0 {
None
} else {
Some(lines.file)
},
lines: lines.lines,
});
let alloc_guest_memory = env.data().alloc_guest_memory.clone();
let alloc_guest_memory = alloc_guest_memory
.as_ref()
.expect("Alloc guest memory fn should be available, check initialization");
let serialized_loc_bytes =
PluginSerializedBytes::try_serialize(&ret).expect("Should be serializable");
let span = Span {
lo: BytePos(span_lo),
hi: BytePos(span_hi),
ctxt: SyntaxContext::from_u32(span_ctxt),
};
if let Some(alloc_guest_memory) = env.alloc_guest_memory_ref() {
allocate_return_values_into_guest(
memory,
alloc_guest_memory,
allocated_ret_ptr,
&serialized_loc_bytes,
);
1
} else {
0
}
} else {
0
}
let ret = (env.data().source_map.lock())
.span_to_lines(span)
.map(|lines| PartialFileLines {
file: if should_request_source_file == 0 {
None
} else {
Some(lines.file)
},
lines: lines.lines,
});
let serialized_loc_bytes =
PluginSerializedBytes::try_serialize(&ret).expect("Should be serializable");
allocate_return_values_into_guest(
memory,
&mut env.as_store_mut(),
alloc_guest_memory,
allocated_ret_ptr,
&serialized_loc_bytes,
);
1
}
#[tracing::instrument(level = "info", skip_all)]
pub fn lookup_byte_offset_proxy(
env: &SourceMapHostEnvironment,
mut env: FunctionEnvMut<SourceMapHostEnvironment>,
byte_pos: u32,
allocated_ret_ptr: u32,
) -> i32 {
if let Some(memory) = env.memory_ref() {
let byte_pos = BytePos(byte_pos);
let ret = (env.source_map.lock()).lookup_byte_offset(byte_pos);
let memory = env.data().memory.clone();
let memory = memory
.as_ref()
.expect("Memory instance should be available, check initialization");
let serialized_loc_bytes =
PluginSerializedBytes::try_serialize(&ret).expect("Should be serializable");
let alloc_guest_memory = env.data().alloc_guest_memory.clone();
let alloc_guest_memory = alloc_guest_memory
.as_ref()
.expect("Alloc guest memory fn should be available, check initialization");
if let Some(alloc_guest_memory) = env.alloc_guest_memory_ref() {
allocate_return_values_into_guest(
memory,
alloc_guest_memory,
allocated_ret_ptr,
&serialized_loc_bytes,
);
1
} else {
0
}
} else {
0
}
let byte_pos = BytePos(byte_pos);
let ret = (env.data().source_map.lock()).lookup_byte_offset(byte_pos);
let serialized_loc_bytes =
PluginSerializedBytes::try_serialize(&ret).expect("Should be serializable");
allocate_return_values_into_guest(
memory,
&mut env.as_store_mut(),
alloc_guest_memory,
allocated_ret_ptr,
&serialized_loc_bytes,
);
1
}
#[tracing::instrument(level = "info", skip_all)]
pub fn span_to_string_proxy(
env: &SourceMapHostEnvironment,
mut env: FunctionEnvMut<SourceMapHostEnvironment>,
span_lo: u32,
span_hi: u32,
span_ctxt: u32,
allocated_ret_ptr: u32,
) -> i32 {
if let Some(memory) = env.memory_ref() {
let span = Span {
lo: BytePos(span_lo),
hi: BytePos(span_hi),
ctxt: SyntaxContext::from_u32(span_ctxt),
};
let ret = (env.source_map.lock()).span_to_string(span);
let serialized_loc_bytes =
PluginSerializedBytes::try_serialize(&ret).expect("Should be serializable");
let memory = env.data().memory.clone();
let memory = memory
.as_ref()
.expect("Memory instance should be available, check initialization");
if let Some(alloc_guest_memory) = env.alloc_guest_memory_ref() {
allocate_return_values_into_guest(
memory,
alloc_guest_memory,
allocated_ret_ptr,
&serialized_loc_bytes,
);
1
} else {
0
}
} else {
0
}
let alloc_guest_memory = env.data().alloc_guest_memory.clone();
let alloc_guest_memory = alloc_guest_memory
.as_ref()
.expect("Alloc guest memory fn should be available, check initialization");
let span = Span {
lo: BytePos(span_lo),
hi: BytePos(span_hi),
ctxt: SyntaxContext::from_u32(span_ctxt),
};
let ret = (env.data().source_map.lock()).span_to_string(span);
let serialized_loc_bytes =
PluginSerializedBytes::try_serialize(&ret).expect("Should be serializable");
allocate_return_values_into_guest(
memory,
&mut env.as_store_mut(),
alloc_guest_memory,
allocated_ret_ptr,
&serialized_loc_bytes,
);
1
}
#[tracing::instrument(level = "info", skip_all)]
pub fn span_to_filename_proxy(
env: &SourceMapHostEnvironment,
mut env: FunctionEnvMut<SourceMapHostEnvironment>,
span_lo: u32,
span_hi: u32,
span_ctxt: u32,
allocated_ret_ptr: u32,
) -> i32 {
if let Some(memory) = env.memory_ref() {
let span = Span {
lo: BytePos(span_lo),
hi: BytePos(span_hi),
ctxt: SyntaxContext::from_u32(span_ctxt),
};
let ret = (env.source_map.lock()).span_to_filename(span);
let serialized_loc_bytes =
PluginSerializedBytes::try_serialize(&ret).expect("Should be serializable");
let memory = env.data().memory.clone();
let memory = memory
.as_ref()
.expect("Memory instance should be available, check initialization");
if let Some(alloc_guest_memory) = env.alloc_guest_memory_ref() {
allocate_return_values_into_guest(
memory,
alloc_guest_memory,
allocated_ret_ptr,
&serialized_loc_bytes,
);
1
} else {
0
}
} else {
0
}
let alloc_guest_memory = env.data().alloc_guest_memory.clone();
let alloc_guest_memory = alloc_guest_memory
.as_ref()
.expect("Alloc guest memory fn should be available, check initialization");
let span = Span {
lo: BytePos(span_lo),
hi: BytePos(span_hi),
ctxt: SyntaxContext::from_u32(span_ctxt),
};
let ret = (env.data().source_map.lock()).span_to_filename(span);
let serialized_loc_bytes =
PluginSerializedBytes::try_serialize(&ret).expect("Should be serializable");
allocate_return_values_into_guest(
memory,
&mut env.as_store_mut(),
alloc_guest_memory,
allocated_ret_ptr,
&serialized_loc_bytes,
);
1
}
#[tracing::instrument(level = "info", skip_all)]
pub fn span_to_source_proxy(
env: &SourceMapHostEnvironment,
mut env: FunctionEnvMut<SourceMapHostEnvironment>,
span_lo: u32,
span_hi: u32,
span_ctxt: u32,
allocated_ret_ptr: u32,
) -> i32 {
if let Some(memory) = env.memory_ref() {
let span = Span {
lo: BytePos(span_lo),
hi: BytePos(span_hi),
ctxt: SyntaxContext::from_u32(span_ctxt),
};
let ret = (env.source_map.lock()).span_to_snippet(span);
let serialized_loc_bytes =
PluginSerializedBytes::try_serialize(&ret).expect("Should be serializable");
let memory = env.data().memory.clone();
let memory = memory
.as_ref()
.expect("Memory instance should be available, check initialization");
if let Some(alloc_guest_memory) = env.alloc_guest_memory_ref() {
allocate_return_values_into_guest(
memory,
alloc_guest_memory,
allocated_ret_ptr,
&serialized_loc_bytes,
);
1
} else {
0
}
} else {
0
}
let alloc_guest_memory = env.data().alloc_guest_memory.clone();
let alloc_guest_memory = alloc_guest_memory
.as_ref()
.expect("Alloc guest memory fn should be available, check initialization");
let span = Span {
lo: BytePos(span_lo),
hi: BytePos(span_hi),
ctxt: SyntaxContext::from_u32(span_ctxt),
};
let ret = (*env.data().source_map.lock()).span_to_snippet(span);
let serialized_loc_bytes =
PluginSerializedBytes::try_serialize(&ret).expect("Should be serializable");
allocate_return_values_into_guest(
memory,
&mut env.as_store_mut(),
alloc_guest_memory,
allocated_ret_ptr,
&serialized_loc_bytes,
);
1
}

View File

@ -1,71 +1,184 @@
use std::{env, sync::Arc};
use anyhow::{Context, Error};
use anyhow::Error;
use parking_lot::Mutex;
use swc_common::{plugin::metadata::TransformPluginMetadataContext, SourceMap};
use wasmer::{ChainableNamedResolver, Instance};
use wasmer_wasi::{is_wasi_module, WasiState};
use swc_common::{
plugin::{
diagnostics::PluginCorePkgDiagnostics, metadata::TransformPluginMetadataContext,
serialized::PluginSerializedBytes,
},
SourceMap,
};
use wasmer::{FunctionEnv, Instance, Store};
use wasmer_wasix::{default_fs_backing, is_wasi_module, WasiEnv, WasiFunctionEnv};
use crate::imported_fn::build_import_object;
use crate::{
host_environment::BaseHostEnvironment,
imported_fn::{
build_import_object, comments::CommentHostEnvironment,
diagnostics::DiagnosticContextHostEnvironment,
metadata_context::MetadataContextHostEnvironment,
set_transform_result::TransformResultHostEnvironment, source_map::SourceMapHostEnvironment,
},
};
#[tracing::instrument(level = "info", skip_all)]
pub fn load_plugin(
store: &mut Store,
plugin_path: &std::path::Path,
cache: &once_cell::sync::Lazy<crate::cache::PluginModuleCache>,
source_map: &Arc<SourceMap>,
metadata_context: &Arc<TransformPluginMetadataContext>,
plugin_config: Option<serde_json::Value>,
transform_result_buffer: &Arc<Mutex<Vec<u8>>>,
core_diag_buffer: &Arc<Mutex<Vec<u8>>>,
) -> Result<Instance, Error> {
let module = cache.load_module(plugin_path)?;
) -> Result<
(
Instance,
Arc<Mutex<Vec<u8>>>,
PluginCorePkgDiagnostics,
Option<WasiFunctionEnv>,
),
Error,
> {
let module = cache.load_module(store, plugin_path);
let import_object = build_import_object(
&module,
source_map.clone(),
metadata_context.clone(),
plugin_config,
transform_result_buffer,
core_diag_buffer,
);
match module {
Ok(module) => {
let context_key_buffer = Arc::new(Mutex::new(vec![]));
let metadata_env = FunctionEnv::new(
store,
MetadataContextHostEnvironment::new(
metadata_context,
&plugin_config,
&context_key_buffer,
),
);
// Plugin binary can be either wasm32-wasi or wasm32-unknown-unknown.
// Wasi specific env need to be initialized if given module targets wasm32-wasi.
// TODO: wasm host native runtime throws 'Memory should be set on `WasiEnv`
// first'
let instance = if is_wasi_module(&module) {
// Create the `WasiEnv`.
let mut wasi_env = WasiState::new(
plugin_path
.file_name()
.and_then(|f| f.to_str())
.expect("Plugin path missing file name"),
);
let transform_result: Arc<Mutex<Vec<u8>>> = Arc::new(Mutex::new(vec![]));
let transform_env = FunctionEnv::new(
store,
TransformResultHostEnvironment::new(&transform_result),
);
// Implicitly enable filesystem access for the wasi plugin to cwd.
//
// This allows wasi plugin can read arbitary data (i.e node_modules) or produce
// output for post process (i.e .lcov coverage data) directly.
//
// TODO: this is not finalized decision
// - should we support this?
// - can we limit to allowlisted input / output only?
// - should there be a top-level config from .swcrc to manually override this?
let wasi_env = if let Ok(cwd) = env::current_dir() {
wasi_env.map_dir("/cwd", cwd)?
} else {
&mut wasi_env
};
let base_env = FunctionEnv::new(store, BaseHostEnvironment::new());
let mut wasi_env = wasi_env.finalize()?;
let comment_buffer = Arc::new(Mutex::new(vec![]));
// Generate an `ImportObject` from wasi_env, overwrite into imported_object
let wasi_env_import_object = wasi_env.import_object(&module)?;
let chained_resolver = import_object.chain_front(wasi_env_import_object);
Instance::new(&module, &chained_resolver)
} else {
Instance::new(&module, &import_object)
};
let comments_env =
FunctionEnv::new(store, CommentHostEnvironment::new(&comment_buffer));
instance.context("Failed to create plugin instance")
let source_map_buffer = Arc::new(Mutex::new(vec![]));
let source_map = Arc::new(Mutex::new(source_map.clone()));
let source_map_host_env = FunctionEnv::new(
store,
SourceMapHostEnvironment::new(&source_map, &source_map_buffer),
);
let diagnostics_buffer: Arc<Mutex<Vec<u8>>> = Arc::new(Mutex::new(vec![]));
let diagnostics_env = FunctionEnv::new(
store,
DiagnosticContextHostEnvironment::new(&diagnostics_buffer),
);
let mut import_object = build_import_object(
store,
&metadata_env,
&transform_env,
&base_env,
&comments_env,
&source_map_host_env,
&diagnostics_env,
);
// Plugin binary can be either wasm32-wasi or wasm32-unknown-unknown.
// Wasi specific env need to be initialized if given module targets wasm32-wasi.
// TODO: wasm host native runtime throws 'Memory should be set on `WasiEnv`
// first'
let (instance, wasi_env) = if is_wasi_module(&module) {
let file_name = plugin_path
.file_name()
.and_then(|f| f.to_str())
.expect("Plugin path missing file name");
let builder = WasiEnv::builder(file_name);
// Implicitly enable filesystem access for the wasi plugin to cwd.
//
// This allows wasi plugin can read arbitary data (i.e node_modules) or produce
// output for post process (i.e .lcov coverage data) directly.
//
// TODO: this is not finalized decision
// - should we support this?
// - can we limit to allowlisted input / output only?
// - should there be a top-level config from .swcrc to manually override this?
let wasi_env_builder = if let Ok(cwd) = env::current_dir() {
builder
.fs(default_fs_backing())
.map_dirs(vec![("/cwd".to_string(), cwd)].drain(..))?
} else {
builder
};
//create the `WasiEnv`
let mut wasi_env = wasi_env_builder.finalize(store)?;
// Then, we get the import object related to our WASI,
// overwrite into imported_object
// and attach it to the Wasm instance.
let wasi_env_import_object = wasi_env.import_object(store, &module)?;
import_object.extend(&wasi_env_import_object);
let instance = Instance::new(store, &module, &import_object)?;
wasi_env.initialize(store, instance.clone())?;
(instance, Some(wasi_env))
} else {
(Instance::new(store, &module, &import_object)?, None)
};
// Attach the memory export
let memory = instance.exports.get_memory("memory")?;
import_object.define("env", "memory", memory.clone());
let alloc = instance.exports.get_typed_function(store, "__alloc")?;
// Unlike wasmer@2, have to manually `import` memory / necessary functions from
// the guest into env.
metadata_env.as_mut(store).memory = Some(memory.clone());
metadata_env.as_mut(store).alloc_guest_memory = Some(alloc.clone());
transform_env.as_mut(store).memory = Some(memory.clone());
base_env.as_mut(store).memory = Some(memory.clone());
comments_env.as_mut(store).memory = Some(memory.clone());
comments_env.as_mut(store).alloc_guest_memory = Some(alloc.clone());
source_map_host_env.as_mut(store).memory = Some(memory.clone());
source_map_host_env.as_mut(store).alloc_guest_memory = Some(alloc);
diagnostics_env.as_mut(store).memory = Some(memory.clone());
// As soon as instance is ready, host calls a fn to read plugin's swc_core pkg
// diagnostics as `handshake`. Once read those values will be available across
// whole plugin transform execution.
// IMPORTANT NOTE
// Note this is `handshake`, which we expect to success ALL TIME. Do not try to
// expand `PluginCorePkgDiagnostics` as it'll cause deserialization failure
// until we have forward-compat schema changes.
instance
.exports
.get_typed_function::<(), u32>(store, "__get_transform_plugin_core_pkg_diag")?
.call(store)?;
let diag_result: PluginCorePkgDiagnostics =
PluginSerializedBytes::from_slice(&(&(*diagnostics_buffer.lock()))[..])
.deserialize()?;
Ok((instance, transform_result, diag_result, wasi_env))
}
Err(err) => Err(err),
}
}

View File

@ -1,59 +1,40 @@
use swc_common::plugin::serialized::PluginSerializedBytes;
use swc_plugin_proxy::AllocatedBytesPtr;
use wasmer::{Array, Memory, NativeFunc, WasmPtr};
use wasmer::{Memory, MemoryView, StoreMut, TypedFunction, WasmPtr};
#[tracing::instrument(level = "info", skip_all)]
pub fn copy_bytes_into_host(memory: &Memory, bytes_ptr: u32, bytes_ptr_len: u32) -> Vec<u8> {
let ptr: WasmPtr<u8, Array> = WasmPtr::new(bytes_ptr as _);
pub fn copy_bytes_into_host(memory: &MemoryView, bytes_ptr: i32, bytes_ptr_len: i32) -> Vec<u8> {
let ptr: WasmPtr<u8> = WasmPtr::new(bytes_ptr as _);
let derefed_ptr = ptr.deref(memory);
let values = ptr.slice(memory, bytes_ptr_len as u32).expect("xxx");
// Deref & read through plugin's wasm memory space via returned ptr
let derefed_ptr = ptr
.deref(memory, 0, bytes_ptr_len)
.expect("Should able to deref from given ptr");
derefed_ptr
.iter()
.enumerate()
.take(bytes_ptr_len as usize)
.map(|(_size, cell)| cell.get())
.collect::<Vec<u8>>()
values
.read_to_vec()
.expect("Should able to read memory from given ptr")
}
/// Locate a view from given memory, write serialized bytes into.
#[tracing::instrument(level = "info", skip_all)]
pub fn write_into_memory_view<F>(
memory: &Memory,
store: &mut StoreMut,
serialized_bytes: &PluginSerializedBytes,
get_allocated_ptr: F,
) -> (u32, u32)
where
F: Fn(usize) -> u32,
F: Fn(&mut StoreMut, usize) -> u32,
{
let serialized_len = serialized_bytes.as_ptr().1;
let ptr_start: u32 = get_allocated_ptr(serialized_len);
let ptr_start_size: u32 = ptr_start;
let serialized_len_size: u32 = serialized_len.try_into().unwrap_or_else(|_| {
panic!(
"Should be able to convert the value {} to u32",
serialized_len
)
});
let ptr_start = get_allocated_ptr(store, serialized_len);
let ptr_start_size: u64 = ptr_start as u64;
// Note: it's important to get a view from memory _after_ alloc completes
let view = memory.view::<u8>();
// Get a subarray for current memoryview starting from ptr address we just
// allocated above, perform copying into specified ptr. Wasm's memory layout
// is linear and we have atomic guarantee by not having any thread access,
// so can safely get subarray from allocated ptr address.
//
// If we want safer operation instead, refer previous implementation
// https://github.com/swc-project/swc/blob/1ef8f3749b6454eb7d40a36a5f9366137fa97928/crates/swc_plugin_runner/src/lib.rs#L56-L61
unsafe {
view.subarray(ptr_start_size, ptr_start_size + serialized_len_size)
.copy_from(serialized_bytes.as_slice());
}
let view = memory.view(store);
// [TODO]: we need wasmer@3 compatible optimization like before
// https://github.com/swc-project/swc/blob/f73f96dd94639f8b7edcdb6290653e16bf848db6/crates/swc_plugin_runner/src/memory_interop.rs#L54
view.write(ptr_start_size, serialized_bytes.as_slice())
.expect("Should able to write into memory view");
(
ptr_start,
@ -70,34 +51,38 @@ where
#[tracing::instrument(level = "info", skip_all)]
pub fn allocate_return_values_into_guest(
memory: &Memory,
alloc_guest_memory: &NativeFunc<u32, u32>,
store: &mut StoreMut,
alloc_guest_memory: &TypedFunction<u32, u32>,
allocated_ret_ptr: u32,
serialized_bytes: &PluginSerializedBytes,
) {
let serialized_bytes_len: usize = serialized_bytes.as_ptr().1;
// In most cases our host-plugin trampoline works in a way that
// plugin pre-allocates
// memory before calling host imported fn. But in case of
// comments return value is Vec<Comments> which
// guest cannot predetermine size to allocate, instead
// let host allocate by calling guest's alloc via attached
// hostenvironment.
let guest_memory_ptr = alloc_guest_memory
.call(
store,
serialized_bytes_len
.try_into()
.expect("Should be able to convert size"),
)
.expect("Should able to allocate memory in the plugin");
let (allocated_ptr, allocated_ptr_len) =
write_into_memory_view(memory, serialized_bytes, |_| {
// In most cases our host-plugin trampoline works in a way that
// plugin pre-allocates
// memory before calling host imported fn. But in case of
// comments return value is Vec<Comments> which
// guest cannot predetermine size to allocate, instead
// let host allocate by calling guest's alloc via attached
// hostenvironment.
alloc_guest_memory
.call(
serialized_bytes_len
.try_into()
.expect("Should be able to convert size"),
)
.expect("Should able to allocate memory in the plugin")
});
write_into_memory_view(memory, store, serialized_bytes, |s, _| guest_memory_ptr);
let allocated_bytes = AllocatedBytesPtr(allocated_ptr, allocated_ptr_len);
// Retuning (allocated_ptr, len) into caller (plugin)
let comment_ptr_serialized =
PluginSerializedBytes::try_serialize(&allocated_bytes).expect("Should be serializable");
write_into_memory_view(memory, &comment_ptr_serialized, |_| allocated_ret_ptr);
write_into_memory_view(memory, store, &comment_ptr_serialized, |_, _| {
allocated_ret_ptr
});
}

View File

@ -13,22 +13,56 @@ use swc_common::{
plugin::{diagnostics::PluginCorePkgDiagnostics, metadata::TransformPluginMetadataContext},
SourceMap,
};
use wasmer::Instance;
use wasmer::{AsStoreMut, Instance, Store, TypedFunction};
use wasmer_wasix::WasiFunctionEnv;
#[cfg(feature = "__rkyv")]
use crate::memory_interop::write_into_memory_view;
/// Creates an instnace of [Store].
///
/// This function exists because we need to disable simd.
#[cfg(not(target_arch = "wasm32"))]
#[allow(unused_mut)]
fn new_store() -> Store {
// Use empty enumset to disable simd.
use enumset::EnumSet;
use wasmer::{BaseTunables, CompilerConfig, EngineBuilder, Target, Triple};
let mut set = EnumSet::new();
// [TODO]: Should we use is_x86_feature_detected! macro instead?
#[cfg(target_arch = "x86_64")]
set.insert(wasmer::CpuFeature::SSE2);
let target = Target::new(Triple::host(), set);
let config = wasmer_compiler_cranelift::Cranelift::default();
let mut engine = EngineBuilder::new(Box::new(config) as Box<dyn CompilerConfig>)
.set_target(Some(target))
.engine();
let tunables = BaseTunables::for_target(engine.target());
engine.set_tunables(tunables);
Store::new(engine)
}
#[cfg(target_arch = "wasm32")]
fn new_store() -> Store {
Store::default()
}
/// 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<(u32, u32, u32, u32), u32>,
exported_plugin_transform: TypedFunction<(u32, u32, u32, u32), u32>,
// `__free` function automatically exported via swc_plugin sdk to allow deallocation in guest
// memory space
exported_plugin_free: wasmer::NativeFunc<(u32, u32), u32>,
exported_plugin_free: TypedFunction<(u32, u32), u32>,
// `__alloc` function automatically exported via swc_plugin sdk to allow allocation in guest
// memory space
exported_plugin_alloc: wasmer::NativeFunc<u32, u32>,
exported_plugin_alloc: TypedFunction<u32, u32>,
wasi_env: Option<WasiFunctionEnv>,
instance: Instance,
store: Store,
// Reference to the pointers successfully allocated which'll be freed by Drop.
allocated_ptr_vec: Vec<(u32, u32)>,
transform_result: Arc<Mutex<Vec<u8>>>,
@ -49,54 +83,33 @@ impl TransformExecutor {
metadata_context: &Arc<TransformPluginMetadataContext>,
plugin_config: Option<serde_json::Value>,
) -> Result<TransformExecutor, Error> {
let transform_result = Arc::new(Mutex::new(vec![]));
let core_diag_buffer = Arc::new(Mutex::new(vec![]));
let mut store = new_store();
let instance = crate::load_plugin::load_plugin(
path,
cache,
source_map,
metadata_context,
plugin_config,
&transform_result,
&core_diag_buffer,
)?;
let (instance, transform_result, diagnostics_buffer, wasi_env) =
crate::load_plugin::load_plugin(
&mut store,
path,
cache,
source_map,
metadata_context,
plugin_config,
)?;
// As soon as instance is ready, host calls a fn to read plugin's swc_core pkg
// diagnostics as `handshake`. Once read those values will be available across
// whole plugin transform execution.
// IMPORTANT NOTE
// Note this is `handshake`, which we expect to success ALL TIME. Do not try to
// expand `PluginCorePkgDiagnostics` as it'll cause deserialization failure
// until we have forward-compat schema changes.
instance
.exports
.get_native_function::<(), i32>("__get_transform_plugin_core_pkg_diag")?
.call()?;
let diag_result: PluginCorePkgDiagnostics =
PluginSerializedBytes::from_slice(&(&(*core_diag_buffer.lock()))[..]).deserialize()?;
let tracker = TransformExecutor {
let executor = TransformExecutor {
exported_plugin_transform: instance
.exports
.get_native_function::<(u32, u32, u32, u32), u32>(
"__transform_plugin_process_impl",
)?,
exported_plugin_free: instance
.exports
.get_native_function::<(u32, u32), u32>("__free")?,
exported_plugin_alloc: instance
.exports
.get_native_function::<u32, u32>("__alloc")?,
.get_typed_function(&store, "__transform_plugin_process_impl")?,
exported_plugin_free: instance.exports.get_typed_function(&store, "__free")?,
exported_plugin_alloc: instance.exports.get_typed_function(&store, "__alloc")?,
instance,
store,
wasi_env,
allocated_ptr_vec: Vec::with_capacity(3),
transform_result,
plugin_core_diag: diag_result,
plugin_core_diag: diagnostics_buffer,
};
Ok(tracker)
Ok(executor)
}
/// Copy host's serialized bytes into guest (plugin)'s allocated memory.
@ -108,11 +121,22 @@ impl TransformExecutor {
) -> Result<(u32, u32), Error> {
let memory = self.instance.exports.get_memory("memory")?;
let ptr = write_into_memory_view(memory, serialized_bytes, |serialized_len| {
self.exported_plugin_alloc
.call(serialized_len.try_into().expect(""))
.expect("")
});
let ptr = write_into_memory_view(
memory,
&mut self.store.as_store_mut(),
serialized_bytes,
|s, serialized_len| {
self.exported_plugin_alloc
.call(s, serialized_len.try_into().expect("booo"))
.expect(
format!(
"Should able to allocate memory for the size of {}",
serialized_len
)
.as_str(),
)
},
);
self.allocated_ptr_vec.push(ptr);
Ok(ptr)
@ -154,7 +178,7 @@ impl TransformExecutor {
* current runtime.
*/
#[allow(unreachable_code)]
pub fn is_transform_schema_compatible(&self) -> Result<bool, Error> {
pub fn is_transform_schema_compatible(&mut self) -> Result<bool, Error> {
#[cfg(any(
feature = "plugin_transform_schema_v1",
feature = "plugin_transform_schema_vtest"
@ -191,6 +215,7 @@ impl TransformExecutor {
let guest_program_ptr = self.write_bytes_into_guest(program)?;
let result = self.exported_plugin_transform.call(
&mut self.store,
guest_program_ptr.0,
guest_program_ptr.1,
unresolved_mark.as_u32(),
@ -205,8 +230,12 @@ impl Drop for TransformExecutor {
fn drop(&mut self) {
for ptr in self.allocated_ptr_vec.iter() {
self.exported_plugin_free
.call(ptr.0, ptr.1)
.call(&mut self.store, ptr.0, ptr.1)
.expect("Failed to free memory allocated in the plugin");
}
if let Some(wasi_env) = &self.wasi_env {
wasi_env.cleanup(&mut self.store, None);
}
}
}

View File

@ -84,8 +84,6 @@ fn invoke(input: PathBuf) -> Result<(), Error> {
.into_iter()
.collect();
info!("Creating cache");
let mut plugin_transform_executor = swc_plugin_runner::create_plugin_transform_executor(
&path,
&PLUGIN_MODULE_CACHE,
@ -146,7 +144,7 @@ fn invoke(input: PathBuf) -> Result<(), Error> {
)),
Some(json!({ "pluginConfig": "testValue" })),
)
.expect("Should load plugin");
.expect("Should load first plugin");
serialized_program = plugin_transform_executor
.transform(&serialized_program, Mark::new(), false)
@ -164,7 +162,7 @@ fn invoke(input: PathBuf) -> Result<(), Error> {
)),
Some(json!({ "pluginConfig": "testValue" })),
)
.expect("Should load plugin");
.expect("Should load second plugin");
serialized_program = plugin_transform_executor
.transform(&serialized_program, Mark::new(), false)

View File

@ -2,7 +2,7 @@
[package]
edition = "2021"
name = "swc_noop_plugin"
name = "swc_noop_plugin"
publish = false
version = "0.1.0"
@ -11,4 +11,6 @@ crate-type = ["cdylib"]
[dependencies]
serde = "1"
swc_core = {path = "../../../../swc_core", features = ["ecma_plugin_transform"]}
swc_core = { path = "../../../../swc_core", features = [
"ecma_plugin_transform",
] }

View File

@ -4,6 +4,6 @@ use swc_core::{
};
#[plugin_transform]
pub fn process(program: Program, metadata: TransformPluginProgramMetadata) -> Program {
pub fn process(program: Program, _metadata: TransformPluginProgramMetadata) -> Program {
program
}

View File

@ -75,6 +75,7 @@ allow = [
"Zlib",
"MPL-2.0",
"0BSD",
"LicenseRef-ring", #ring
"Unicode-DFS-2016", #unicode-ident
]
# List of explictly disallowed licenses
@ -102,7 +103,7 @@ default = "deny"
confidence-threshold = 0.8
# Allow 1 or more licenses on a per-crate basis, so that particular licenses
# aren't accepted for every possible crate as with the normal allow list
exceptions = []
exceptions = [{ allow = ["BUSL-1.1"], name = "webc" }]
[[licenses.clarify]]
# The name of the crate the clarification applies to
@ -118,6 +119,11 @@ exceptions = []
# depending on the rest of your configuration
license-files = [{ path = "COPYRIGHT", hash = 972598577 }]
[[licenses.clarify]]
expression = "LicenseRef-ring"
license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }]
name = "ring"
# Some crates don't have (easily) machine readable licensing information,
# adding a clarification entry for it allows you to manually specify the
# licensing information

View File

@ -61,7 +61,9 @@ const inferBinaryName = () => {
);
};
describe("Published plugins", () => {
// [TODO]: It is expected to have a breaking changes to the plugin,
// disabling the test for now.
describe.skip("Published plugins", () => {
const packageName = platformPackagesMap[platform][arch];
if (!!packageName) {