mirror of
https://github.com/swc-project/swc.git
synced 2024-09-21 05:39:06 +03:00
fix(plugin): Make span_to_source
use dedicated function (#6853)
**Related issue:** - Closes https://github.com/swc-project/swc/issues/6852. - Closes https://github.com/swc-project/swc/issues/6404.
This commit is contained in:
parent
28be71dfaf
commit
99d48e305e
12
.vscode/settings.json
vendored
12
.vscode/settings.json
vendored
@ -23,10 +23,14 @@
|
||||
"crates/swc_html_parser/tests/html5lib-tests"
|
||||
],
|
||||
"eslint.enable": false,
|
||||
"cSpell.allowCompoundWords": true,
|
||||
"cSpell.caseSensitive": true,
|
||||
"rust-analyzer.checkOnSave.command": "clippy",
|
||||
"rust-analyzer.checkOnSave.features": [
|
||||
"rust-analyzer.check.command": "clippy",
|
||||
"rust-analyzer.check.features": [
|
||||
// We use this to make IDE faster
|
||||
"rust-analyzer",
|
||||
"rkyv-impl",
|
||||
"debug"
|
||||
],
|
||||
"rust-analyzer.cargo.features": [
|
||||
// We use this to make IDE faster
|
||||
"rust-analyzer",
|
||||
"rkyv-impl",
|
||||
|
@ -1267,6 +1267,10 @@ pub enum SpanLinesError {
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(
|
||||
any(feature = "rkyv-impl", feature = "rkyv-bytecheck-impl"),
|
||||
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
|
||||
)]
|
||||
pub enum SpanSnippetError {
|
||||
DummyBytePos,
|
||||
IllFormedSpan(Span),
|
||||
@ -1286,6 +1290,10 @@ pub struct DistinctSources {
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(
|
||||
any(feature = "rkyv-impl", feature = "rkyv-bytecheck-impl"),
|
||||
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
|
||||
)]
|
||||
pub struct MalformedSourceMapPositions {
|
||||
pub name: FileName,
|
||||
pub source_len: usize,
|
||||
|
@ -45,6 +45,12 @@ extern "C" {
|
||||
span_ctxt: u32,
|
||||
allocated_ret_ptr: u32,
|
||||
) -> u32;
|
||||
fn __span_to_source_proxy(
|
||||
span_lo: u32,
|
||||
span_hi: u32,
|
||||
span_ctxt: u32,
|
||||
allocated_ret_ptr: u32,
|
||||
) -> u32;
|
||||
fn __span_to_lines_proxy(
|
||||
span_lo: u32,
|
||||
span_hi: u32,
|
||||
@ -67,49 +73,6 @@ pub struct PluginSourceMapProxy {
|
||||
#[cfg(all(feature = "__rkyv", feature = "__plugin_mode", target_arch = "wasm32"))]
|
||||
#[swc_trace]
|
||||
impl PluginSourceMapProxy {
|
||||
/*
|
||||
fn sss<F, Ret>(&self, sp: Span, extract_source: F) -> Result<Ret, Box<SpanSnippetError>>
|
||||
where
|
||||
F: FnOnce(&str, usize, usize) -> Ret,
|
||||
{
|
||||
if sp.lo() > sp.hi() {
|
||||
return Err(SpanSnippetError::IllFormedSpan(sp));
|
||||
}
|
||||
if sp.lo.is_dummy() || sp.hi.is_dummy() {
|
||||
return Err(SpanSnippetError::DummyBytePos);
|
||||
}
|
||||
|
||||
let local_begin = self.lookup_byte_offset(sp.lo());
|
||||
let local_end = self.lookup_byte_offset(sp.hi());
|
||||
|
||||
if local_begin.sf.start_pos != local_end.sf.start_pos {
|
||||
Err(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),
|
||||
}))
|
||||
} else {
|
||||
let pos: BytePos = BytePos(0);
|
||||
let start_index = pos.to_usize();
|
||||
let end_index = local_end.pos.to_usize();
|
||||
let source_len = (local_begin.sf.end_pos - local_begin.sf.start_pos).to_usize();
|
||||
|
||||
if start_index > end_index || end_index > source_len {
|
||||
return Err(SpanSnippetError::MalformedForSourcemap(
|
||||
MalformedSourceMapPositions {
|
||||
name: local_begin.sf.name.clone(),
|
||||
source_len,
|
||||
begin_pos: local_begin.pos,
|
||||
end_pos: local_end.pos,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
let src = &local_begin.sf.src;
|
||||
Ok(extract_source(src, start_index, end_index))
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
pub fn span_to_source<F, Ret>(
|
||||
&self,
|
||||
sp: Span,
|
||||
@ -120,50 +83,14 @@ impl PluginSourceMapProxy {
|
||||
{
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
if sp.lo() > sp.hi() {
|
||||
return Err(Box::new(SpanSnippetError::IllFormedSpan(sp)));
|
||||
}
|
||||
if sp.lo.is_dummy() || sp.hi.is_dummy() {
|
||||
return Err(Box::new(SpanSnippetError::DummyBytePos));
|
||||
}
|
||||
|
||||
let local_begin: SourceFileAndBytePos =
|
||||
let src: Result<String, Box<SpanSnippetError>> =
|
||||
read_returned_result_from_host_fallible(|serialized_ptr| unsafe {
|
||||
__lookup_byte_offset_proxy(sp.lo().0, serialized_ptr)
|
||||
__span_to_source_proxy(sp.lo.0, sp.hi.0, sp.ctxt.as_u32(), serialized_ptr)
|
||||
})
|
||||
.expect("Should return begin offset");
|
||||
let local_end: SourceFileAndBytePos =
|
||||
read_returned_result_from_host_fallible(|serialized_ptr| unsafe {
|
||||
__lookup_byte_offset_proxy(sp.hi().0, serialized_ptr)
|
||||
})
|
||||
.expect("Should return end offset");
|
||||
.expect("Host should return source code");
|
||||
|
||||
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),
|
||||
},
|
||||
)))
|
||||
} else {
|
||||
let start_index = local_begin.pos.to_usize();
|
||||
let end_index = local_end.pos.to_usize();
|
||||
let source_len = (local_begin.sf.end_pos - local_begin.sf.start_pos).to_usize();
|
||||
|
||||
if start_index > end_index || end_index > source_len {
|
||||
return Err(Box::new(SpanSnippetError::MalformedForSourcemap(
|
||||
MalformedSourceMapPositions {
|
||||
name: local_begin.sf.name.clone(),
|
||||
source_len,
|
||||
begin_pos: local_begin.pos,
|
||||
end_pos: local_end.pos,
|
||||
},
|
||||
)));
|
||||
}
|
||||
|
||||
let src = &local_begin.sf.src;
|
||||
return Ok(extract_source(src, start_index, end_index));
|
||||
}
|
||||
let src = src?;
|
||||
return Ok(extract_source(&src, 0, src.len()));
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
|
@ -83,8 +83,8 @@ use self::{
|
||||
},
|
||||
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_string_proxy,
|
||||
SourceMapHostEnvironment,
|
||||
merge_spans_proxy, span_to_filename_proxy, span_to_lines_proxy, span_to_source_proxy,
|
||||
span_to_string_proxy, SourceMapHostEnvironment,
|
||||
},
|
||||
};
|
||||
|
||||
@ -281,6 +281,12 @@ pub(crate) fn build_import_object(
|
||||
span_to_filename_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_native_with_env(
|
||||
store,
|
||||
SourceMapHostEnvironment::new(&source_map, &source_map_buffer),
|
||||
@ -339,6 +345,7 @@ pub(crate) fn build_import_object(
|
||||
"__merge_spans_proxy" => merge_spans_fn_decl,
|
||||
"__span_to_string_proxy" => span_to_string_fn_decl,
|
||||
"__span_to_filename_proxy" => span_to_filename_fn_decl,
|
||||
"__span_to_source_proxy" => span_to_source_fn_decl,
|
||||
"__span_to_lines_proxy" => span_to_lines_fn_decl,
|
||||
"__lookup_byte_offset_proxy" => lookup_byte_offset_fn_decl
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use parking_lot::Mutex;
|
||||
use swc_common::{
|
||||
plugin::serialized::PluginSerializedBytes,
|
||||
source_map::{PartialFileLines, PartialLoc},
|
||||
BytePos, SourceMap, Span, SyntaxContext,
|
||||
BytePos, SourceMap, SourceMapper, Span, SyntaxContext,
|
||||
};
|
||||
use wasmer::{LazyInit, Memory, NativeFunc};
|
||||
|
||||
@ -267,3 +267,37 @@ pub fn span_to_filename_proxy(
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "info", skip_all)]
|
||||
pub fn span_to_source_proxy(
|
||||
env: &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");
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
1952
crates/swc_plugin_runner/tests/fixture/issue_6404/Cargo.lock
generated
Normal file
1952
crates/swc_plugin_runner/tests/fixture/issue_6404/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
17
crates/swc_plugin_runner/tests/fixture/issue_6404/Cargo.toml
Normal file
17
crates/swc_plugin_runner/tests/fixture/issue_6404/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[workspace]
|
||||
|
||||
[package]
|
||||
edition = "2021"
|
||||
name = "swc_issue_6404"
|
||||
publish = false
|
||||
version = "0.1.0"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
serde = "1"
|
||||
swc_core = { path = "../../../../swc_core", features = [
|
||||
"ecma_plugin_transform",
|
||||
"ecma_quote",
|
||||
] }
|
21
crates/swc_plugin_runner/tests/fixture/issue_6404/src/lib.rs
Normal file
21
crates/swc_plugin_runner/tests/fixture/issue_6404/src/lib.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use swc_core::{
|
||||
common::{BytePos, SourceMapper, Span, SyntaxContext},
|
||||
ecma::ast::*,
|
||||
plugin::{metadata::TransformPluginProgramMetadata, plugin_transform},
|
||||
};
|
||||
|
||||
#[plugin_transform]
|
||||
pub fn process_transform(program: Program, metadata: TransformPluginProgramMetadata) -> Program {
|
||||
for i in 1..50000 {
|
||||
let j: u32 = i;
|
||||
println!("i {} j {}", i, j);
|
||||
let res = metadata.source_map.span_to_snippet(Span::new(
|
||||
BytePos(j),
|
||||
BytePos(j + 1000),
|
||||
SyntaxContext::empty(),
|
||||
));
|
||||
let _ = dbg!(res);
|
||||
}
|
||||
|
||||
program
|
||||
}
|
146
crates/swc_plugin_runner/tests/issues.rs
Normal file
146
crates/swc_plugin_runner/tests/issues.rs
Normal file
@ -0,0 +1,146 @@
|
||||
#![cfg_attr(not(feature = "__rkyv"), allow(warnings))]
|
||||
|
||||
use std::{
|
||||
env, fs,
|
||||
path::{Path, PathBuf},
|
||||
process::{Command, Stdio},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, Error};
|
||||
use serde_json::json;
|
||||
#[cfg(feature = "__rkyv")]
|
||||
use swc_common::plugin::serialized::PluginSerializedBytes;
|
||||
use swc_common::{
|
||||
collections::AHashMap, plugin::metadata::TransformPluginMetadataContext, sync::Lazy, Mark,
|
||||
};
|
||||
use swc_ecma_ast::{CallExpr, Callee, EsVersion, Expr, Lit, MemberExpr, Program, Str};
|
||||
use swc_ecma_parser::{parse_file_as_program, EsConfig, Syntax};
|
||||
use swc_ecma_visit::Visit;
|
||||
use swc_plugin_runner::cache::PluginModuleCache;
|
||||
|
||||
/// Returns the path to the built plugin
|
||||
fn build_plugin(dir: &Path, crate_name: &str) -> Result<PathBuf, Error> {
|
||||
{
|
||||
let mut cmd = Command::new("cargo");
|
||||
cmd.current_dir(dir);
|
||||
cmd.args(["build", "--release", "--target=wasm32-wasi"])
|
||||
.stderr(Stdio::inherit());
|
||||
cmd.output()?;
|
||||
|
||||
if !cmd
|
||||
.status()
|
||||
.expect("Exit code should be available")
|
||||
.success()
|
||||
{
|
||||
return Err(anyhow!("Failed to build plugin"));
|
||||
}
|
||||
}
|
||||
|
||||
for entry in fs::read_dir(&dir.join("target").join("wasm32-wasi").join("release"))? {
|
||||
let entry = entry?;
|
||||
|
||||
let s = entry.file_name().to_string_lossy().into_owned();
|
||||
if s.eq_ignore_ascii_case(&format!("{}.wasm", crate_name)) {
|
||||
return Ok(entry.path());
|
||||
}
|
||||
}
|
||||
|
||||
Err(anyhow!("Could not find built plugin"))
|
||||
}
|
||||
|
||||
struct TestVisitor {
|
||||
pub plugin_transform_found: bool,
|
||||
}
|
||||
|
||||
impl Visit for TestVisitor {
|
||||
fn visit_call_expr(&mut self, call: &CallExpr) {
|
||||
if let Callee::Expr(expr) = &call.callee {
|
||||
if let Expr::Member(MemberExpr { obj, .. }) = &**expr {
|
||||
if let Expr::Ident(ident) = &**obj {
|
||||
if ident.sym == *"console" {
|
||||
let args = &*(call.args[0].expr);
|
||||
if let Expr::Lit(Lit::Str(Str { value, .. })) = args {
|
||||
self.plugin_transform_found = value == "changed_via_plugin";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "__rkyv")]
|
||||
#[test]
|
||||
fn issue_6404() -> Result<(), Error> {
|
||||
let plugin_path = build_plugin(
|
||||
&PathBuf::from(env::var("CARGO_MANIFEST_DIR")?)
|
||||
.join("tests")
|
||||
.join("fixture")
|
||||
.join("issue_6404"),
|
||||
"swc_issue_6404",
|
||||
)?;
|
||||
|
||||
dbg!("Built!");
|
||||
|
||||
// run single plugin
|
||||
testing::run_test(false, |cm, _handler| {
|
||||
let fm = cm
|
||||
.load_file("../swc_ecma_minifier/benches/full/typescript.js".as_ref())
|
||||
.unwrap();
|
||||
|
||||
let program = parse_file_as_program(
|
||||
&fm,
|
||||
Syntax::Es(EsConfig {
|
||||
..Default::default()
|
||||
}),
|
||||
EsVersion::latest(),
|
||||
None,
|
||||
&mut vec![],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let program = PluginSerializedBytes::try_serialize(&program).expect("Should serializable");
|
||||
let experimental_metadata: AHashMap<String, String> = [
|
||||
(
|
||||
"TestExperimental".to_string(),
|
||||
"ExperimentalValue".to_string(),
|
||||
),
|
||||
("OtherTest".to_string(), "OtherVal".to_string()),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
let cache: Lazy<PluginModuleCache> = Lazy::new(PluginModuleCache::new);
|
||||
let mut plugin_transform_executor = swc_plugin_runner::create_plugin_transform_executor(
|
||||
&plugin_path,
|
||||
&cache,
|
||||
&cm,
|
||||
&Arc::new(TransformPluginMetadataContext::new(
|
||||
None,
|
||||
"development".to_string(),
|
||||
Some(experimental_metadata),
|
||||
)),
|
||||
Some(json!({ "pluginConfig": "testValue" })),
|
||||
)
|
||||
.expect("Should load plugin");
|
||||
|
||||
assert!(!plugin_transform_executor
|
||||
.plugin_core_diag
|
||||
.pkg_version
|
||||
.is_empty());
|
||||
|
||||
let program_bytes = plugin_transform_executor
|
||||
.transform(&program, Mark::new(), false)
|
||||
.expect("Plugin should apply transform");
|
||||
|
||||
let _: Program = program_bytes
|
||||
.deserialize()
|
||||
.expect("Should able to deserialize");
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.expect("Should able to run single plugin transform");
|
||||
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in New Issue
Block a user