mirror of
https://github.com/swc-project/swc.git
synced 2024-11-28 02:29:04 +03:00
511 lines
14 KiB
Rust
511 lines
14 KiB
Rust
use std::{fs::read_to_string, path::PathBuf};
|
|
|
|
use rustc_hash::FxHashSet;
|
|
use sourcemap::SourceMap;
|
|
use swc_common::{comments::SingleThreadedComments, source_map::SourceMapGenConfig};
|
|
use swc_ecma_ast::EsVersion;
|
|
use swc_ecma_codegen::{self, text_writer::WriteJs, Emitter};
|
|
use swc_ecma_parser::{lexer::Lexer, Parser, Syntax};
|
|
use swc_ecma_testing::{exec_node_js, JsExecOptions};
|
|
|
|
static IGNORED_PASS_TESTS: &[&str] = &[
|
|
// Temporally ignored
|
|
"431ecef8c85d4d24.js",
|
|
"8386fbff927a9e0e.js",
|
|
"5654d4106d7025c2.js",
|
|
"369fd0a1e40030d8.js",
|
|
"3df03e7e138b7760.js",
|
|
"5333f04581124314.js",
|
|
"a157424306915066.js",
|
|
"ce5f3bc27d5ccaac.js",
|
|
// Stack size (Stupid parens)
|
|
"6b5e7e125097d439.js",
|
|
"714be6d28082eaa7.js",
|
|
"882910de7dd1aef9.js",
|
|
"dd3c63403db5c06e.js",
|
|
// Wrong tests (variable name or value is different)
|
|
"0339fa95c78c11bd.js",
|
|
"0426f15dac46e92d.js",
|
|
"0b4d61559ccce0f9.js",
|
|
"0f88c334715d2489.js",
|
|
"1093d98f5fc0758d.js",
|
|
"15d9592709b947a0.js",
|
|
"2179895ec5cc6276.js",
|
|
"247a3a57e8176ebd.js",
|
|
"441a92357939904a.js",
|
|
"47f974d6fc52e3e4.js",
|
|
"4e1a0da46ca45afe.js",
|
|
"5829d742ab805866.js",
|
|
"589dc8ad3b9aa28f.js",
|
|
"598a5cedba92154d.js",
|
|
"72d79750e81ef03d.js",
|
|
"7788d3c1e1247da9.js",
|
|
"7b72d7b43bedc895.js",
|
|
"7dab6e55461806c9.js",
|
|
"82c827ccaecbe22b.js",
|
|
"87a9b0d1d80812cc.js",
|
|
"8c80f7ee04352eba.js",
|
|
"96f5d93be9a54573.js",
|
|
"988e362ed9ddcac5.js",
|
|
"9bcae7c7f00b4e3c.js",
|
|
"a8a03a88237c4e8f.js",
|
|
"ad06370e34811a6a.js",
|
|
"b0fdc038ee292aba.js",
|
|
"b62c6dd890bef675.js",
|
|
"cb211fadccb029c7.js",
|
|
"ce968fcdf3a1987c.js",
|
|
"db3c01738aaf0b92.js",
|
|
"e1387fe892984e2b.js",
|
|
"e71c1d5f0b6b833c.js",
|
|
"e8ea384458526db0.js",
|
|
// We don't implement Annex B fully.
|
|
"1c1e2a43fe5515b6.js",
|
|
"3dabeca76119d501.js",
|
|
"52aeec7b8da212a2.js",
|
|
"59ae0289778b80cd.js",
|
|
"a4d62a651f69d815.js",
|
|
"c06df922631aeabc.js",
|
|
// swc_common issue - `\r` should be treated as a newline
|
|
"be2c3fff6426873e.js",
|
|
"db66e1e8f3f1faef.js",
|
|
"a7b8ce1d4c0f0bc2.js",
|
|
"6498dcc494193cb4.js",
|
|
"6a240463b40550d2.js",
|
|
// TODO: (maybe) fix span of `,`
|
|
"641ac9060a206183.js",
|
|
"e4cef19dab44335a.js",
|
|
"a6806d6fedbf6759.js",
|
|
"2dc0ded5a1bff643.js",
|
|
"547fa50af16beca7.js",
|
|
"547fa50af16beca7.js",
|
|
"8c8a7a2941fb6d64.js",
|
|
"9e98dbfde77e3dfe.js",
|
|
"d9eb39b11bc766f4.js",
|
|
"f9888fa1a1e366e7.js",
|
|
"78cf02220fb0937c.js",
|
|
// TODO(kdy1): Non-ascii char count
|
|
"58cb05d17f7ec010.js",
|
|
"4d2c7020de650d40.js",
|
|
"dafb7abe5b9b44f5.js",
|
|
// Our one is better
|
|
"1efde9ddd9d6e6ce.module.js",
|
|
"d010d377bcfd5565.js",
|
|
"ce0aaec02d5d4465.js",
|
|
"edd1f39f90576180.js",
|
|
"290fdc5a2f826ead.js",
|
|
"e71a91c61343cdb1.js",
|
|
"409f30dc7efe75d5.js",
|
|
"03608b6e222ae700.js",
|
|
"e54c1a2fc15cd4b8.js",
|
|
"e08e181172bad2b1.js",
|
|
"cc793d44a11617e7.js",
|
|
"54e70df597a4f9a3.js",
|
|
"efef19e06f58fdd9.js",
|
|
"e0fc2148b455a6be.js",
|
|
"10857a84ed2962f1.js",
|
|
"d7c7ff252e84e81d.js",
|
|
"0aa6aab640155051.js",
|
|
"c80d9415dde647cd.js",
|
|
"09e84f25af85b836.js",
|
|
"ce8c443eb361e1a2.js",
|
|
"affd557fd820e1f2.js",
|
|
"ec99a663d6f3983d.js",
|
|
"01fd8e8a0a42307b.js",
|
|
"e01c7172cf204b92.js",
|
|
"12d5bedf1812952a.js",
|
|
"df20c9b7a7d534cb.js",
|
|
"c767fa4d683aa3ce.js",
|
|
"bf8ffad512a5f568.js",
|
|
"c8513472857eae9c.js",
|
|
"b86b0122e80c330e.js",
|
|
"aa7e721756949024.js",
|
|
"a830df7cf2e74c9f.js",
|
|
"845631d1a33b3409.js",
|
|
"066b76285ce79182.js",
|
|
"fe2d3b945530c806.js",
|
|
"bd28a7d19ac0d50b.js",
|
|
"06c7efc128ce74a0.js",
|
|
"075c7204d0b0af60.js",
|
|
"0827a8316cca777a.js",
|
|
"b9a0cb6df76a73d2.js",
|
|
"bf210a4f0cf9e352.js",
|
|
"6edc155d463535cb.js",
|
|
"b8f8dfc41df97add.js",
|
|
"b549d045fc8e93bf.js",
|
|
"e42f306327c0f578.js",
|
|
"9a9cb616daadf90a.js",
|
|
"d2ae1c7b6e55143f.js",
|
|
"a445a478b4ce0c58.js",
|
|
"0d137e8a97ffe083.js",
|
|
"b7a6a807ae6db312.js",
|
|
"bb8b546cf9db5996.js",
|
|
"50ac15a08f7c812f.js",
|
|
"a2cb5a14559c6a50.js",
|
|
"bbff5671643cc2ea.js",
|
|
"c2f12d66ce17d5ab.js",
|
|
"13045bfdda0434e0.js",
|
|
"10d6486502949e74.js",
|
|
"119e9dce4feae643.js",
|
|
"1223609b0f7a2129.js",
|
|
"177fef3d002eb873.js",
|
|
"19ffea7e9e887e08.js",
|
|
"1c6c67fcd71f2d08.js",
|
|
"1cdce2d337e64b4f.js",
|
|
"1f039e0eeb1bc271.js",
|
|
"227118dffd2c9935.js",
|
|
"250ced8c8e83b389.js",
|
|
"a2798917405b080b.js",
|
|
"ad6bf12aa7eda975.js",
|
|
"24fa28a37061a18f.js",
|
|
"252bb992a448270e.js",
|
|
"285648c16156804f.js",
|
|
"2d10fed2af94fbd1.js",
|
|
"3097f73926c93640.js",
|
|
"30aee1020fc69090.js",
|
|
"312f85fecc352681.js",
|
|
"317532451c2ce8ff.js",
|
|
"32b635a9667a9fb1.js",
|
|
"36224cf8215ad8e4.js",
|
|
"37e4a6eca1ece7e5.js",
|
|
"38284ea2d9914d86.js",
|
|
"3b57183c81070eec.js",
|
|
"3bbd75d597d54fe6.js",
|
|
"3c1e2ada0ac2b8e3.js",
|
|
"3e1a6f702041b599.js",
|
|
"3e3a99768a4a1502.js",
|
|
"3e69c5cc1a7ac103.js",
|
|
"3eac36e29398cdc5.js",
|
|
"3ff52d86c77678bd.js",
|
|
"43023cd549deee77.js",
|
|
"44af28febe2288cc.js",
|
|
"478ede4cfe7906d5.js",
|
|
"4869454dd215468e.js",
|
|
"48b6f8ce65d3b3ee.js",
|
|
"4c71e11fbbc56349.js",
|
|
"4d833cbc56caaaf9.js",
|
|
"4e7c58761e24d77c.js",
|
|
"4e7c58761e24d77c.js",
|
|
"5641ad33abcd1752.js",
|
|
"587400d1c019785a.js",
|
|
"58ed6ffb30191684.js",
|
|
"5b8d2b991d2c1f5b.js",
|
|
"5f730961df66e8e8.js",
|
|
"597108fd45a6e79b.js",
|
|
"60dcd48a3f6af44f.js",
|
|
"62d7c1ee4e1626c4.js",
|
|
"665f4940c7cf30c9.js",
|
|
"64cc57f82a54b7fb.js",
|
|
"66d2dbcb692491ec.module.js",
|
|
"697b3d30c1d06918.js",
|
|
"698a8cfb0705c277.js",
|
|
"69bbdc7c34ed23cc.js",
|
|
"6a323491fe75918a.js",
|
|
"6b76b8761a049c19.js",
|
|
"70bf2c409480ae10.js",
|
|
"74c5ebda713c8bd7.js",
|
|
"75172741c27c7703.js",
|
|
"753a8b016a700975.js",
|
|
"77c661b2fbe3dd3a.js",
|
|
"784a059faa166072.js",
|
|
"7855fbf5ea10e622.js",
|
|
"7cd7c68a6131f816.js",
|
|
"7df2a606ecc6cd84.js",
|
|
"7dfb625b91c5c879.js",
|
|
"7fdf990c6f42edcd.module.js",
|
|
"80d2351a5ae68524.js",
|
|
"84250e15785d8a9e.js",
|
|
"85263ecacc7a4dc5.js",
|
|
"8628cd459b39ffe8.js",
|
|
"870a0b8d891753e9.js",
|
|
"8d14286a8cc6ee9d.js",
|
|
"8d67ad04bfc356c9.js",
|
|
"8ecaef2617d8c6a7.js",
|
|
"918e105a2ff6c64a.js",
|
|
"92fd8e24864fde0a.js",
|
|
"94b8a654a87039b9.js",
|
|
"94cb828d5dcfd136.js",
|
|
"98df58b0c40fac90.js",
|
|
"9949a2e1a6844836.module.js",
|
|
"99cdfc40e20af6f5.js",
|
|
"9a666205cafd530f.js",
|
|
"a454d2e2ab3484e6.js",
|
|
"a54cca69085ad35a.js",
|
|
"a86a29773d1168d3.js",
|
|
"b205355de22689d1.js",
|
|
"b93d116fd0409637.js",
|
|
"c85bc4de504befc7.js",
|
|
"c8689b6da6fd227a.js",
|
|
"cda499c521ff60c7.js",
|
|
"d4b898b45172a637.js",
|
|
"e2ac0bea41202dc9.js",
|
|
"f01d9f3c7b2b2717.js",
|
|
"f15772354efa5ecf.js",
|
|
"f17ec9517a3339d9.js",
|
|
"fa5b398eeef697a6.js",
|
|
"fa9eaf58f51d6926.js",
|
|
"faa4a026e1e86145.js",
|
|
"fada2c7bbfabe14a.js",
|
|
"fb8db7a71f3755fc.js",
|
|
"fbde237f11796df9.js",
|
|
"fd5ea844fcc07d3d.js",
|
|
"6c5f0dd83c417a5a.js",
|
|
"78eb22badc114b6f.js",
|
|
"7afd38d79e6795a8.js",
|
|
"80950061e291542b.js",
|
|
"8a0fc8ea31727188.module.js",
|
|
"af97a3752e579223.js",
|
|
"bbffb851469a3f0e.js",
|
|
"bc302492d441d561.js",
|
|
"be2fd5888f434cbd.js",
|
|
"f3260491590325af.js",
|
|
];
|
|
|
|
#[testing::fixture("../swc_ecma_parser/tests/test262-parser/pass/*.js")]
|
|
fn identity(entry: PathBuf) {
|
|
let file_name = entry
|
|
.file_name()
|
|
.unwrap()
|
|
.to_str()
|
|
.expect("to_str() failed")
|
|
.to_string();
|
|
|
|
let input = read_to_string(&entry).unwrap();
|
|
|
|
let ignore = IGNORED_PASS_TESTS.contains(&&*file_name);
|
|
|
|
if ignore {
|
|
return;
|
|
}
|
|
|
|
let is_module = file_name.contains("module");
|
|
|
|
let msg = format!(
|
|
"\n\n========== Running codegen test {}\nSource:\n{}\n",
|
|
file_name, input
|
|
);
|
|
let mut wr = vec![];
|
|
|
|
::testing::run_test(false, |cm, handler| {
|
|
let fm = cm.load_file(&entry).expect("failed to load file");
|
|
eprintln!(
|
|
"{}\nPos: {:?} ~ {:?} (L{})",
|
|
msg,
|
|
fm.start_pos,
|
|
fm.end_pos,
|
|
fm.count_lines()
|
|
);
|
|
let (expected_code, expected_map, visualizer_url_for_expected) =
|
|
match get_expected(&fm.src, is_module) {
|
|
Some(v) => v,
|
|
None => return Ok(()),
|
|
};
|
|
println!("Expected code:\n{}", expected_code);
|
|
let expected_tokens = print_source_map(&expected_map);
|
|
|
|
let comments = SingleThreadedComments::default();
|
|
let lexer = Lexer::new(
|
|
Syntax::default(),
|
|
Default::default(),
|
|
(&*fm).into(),
|
|
Some(&comments),
|
|
);
|
|
let mut parser: Parser<Lexer> = Parser::new_from(lexer);
|
|
let mut src_map = vec![];
|
|
|
|
{
|
|
let mut wr = Box::new(swc_ecma_codegen::text_writer::JsWriter::new(
|
|
cm.clone(),
|
|
"\n",
|
|
&mut wr,
|
|
Some(&mut src_map),
|
|
)) as Box<dyn WriteJs>;
|
|
|
|
wr = Box::new(swc_ecma_codegen::text_writer::omit_trailing_semi(wr));
|
|
|
|
let mut emitter = Emitter {
|
|
cfg: swc_ecma_codegen::Config {
|
|
minify: true,
|
|
target: EsVersion::Es5,
|
|
ascii_only: true,
|
|
..Default::default()
|
|
},
|
|
cm: cm.clone(),
|
|
wr,
|
|
comments: None,
|
|
};
|
|
|
|
// Parse source
|
|
if is_module {
|
|
emitter
|
|
.emit_module(
|
|
&parser
|
|
.parse_module()
|
|
.map_err(|e| e.into_diagnostic(handler).emit())?,
|
|
)
|
|
.unwrap();
|
|
} else {
|
|
emitter
|
|
.emit_script(
|
|
&parser
|
|
.parse_script()
|
|
.map_err(|e| e.into_diagnostic(handler).emit())?,
|
|
)
|
|
.unwrap();
|
|
}
|
|
}
|
|
|
|
let actual_code = String::from_utf8(wr).unwrap();
|
|
let actual_map = cm.build_source_map_with_config(&src_map, None, SourceMapConfigImpl);
|
|
|
|
let visualizer_url_for_actual = visualizer_url(&actual_code, &actual_map);
|
|
|
|
let actual_tokens = print_source_map(&actual_map);
|
|
|
|
let common_tokens = actual_tokens
|
|
.iter()
|
|
.filter(|a| expected_tokens.contains(&**a))
|
|
.map(|v| v.to_string())
|
|
.collect::<FxHashSet<_>>();
|
|
|
|
let actual_tokens_diff = actual_tokens
|
|
.iter()
|
|
.filter(|a| !common_tokens.contains(&**a))
|
|
.map(|v| v.to_string())
|
|
.collect::<Vec<_>>();
|
|
let expected_tokens_diff = expected_tokens
|
|
.iter()
|
|
.filter(|a| !common_tokens.contains(&**a))
|
|
.map(|v| v.to_string())
|
|
.collect::<Vec<_>>();
|
|
eprintln!("---- Actual -----");
|
|
for s in actual_tokens_diff {
|
|
eprintln!("{}", s);
|
|
}
|
|
eprintln!("---- Expected -----");
|
|
for s in expected_tokens_diff {
|
|
eprintln!("{}", s);
|
|
}
|
|
|
|
dbg!(&src_map);
|
|
|
|
if actual_code != expected_code {
|
|
// Generated code is different
|
|
// We can't ensure that identical sourcemap will mean identical code
|
|
eprintln!("Actual code:\n{}", actual_code);
|
|
eprintln!("Expected code:\n{}", expected_code);
|
|
return Ok(());
|
|
}
|
|
|
|
eprintln!(
|
|
"----- Visualizer -----\nExpected: {}\nActual: {}",
|
|
visualizer_url_for_expected, visualizer_url_for_actual
|
|
);
|
|
|
|
assert_eq_same_map(&expected_map, &actual_map);
|
|
Ok(())
|
|
})
|
|
.expect("failed to run test");
|
|
}
|
|
|
|
fn get_expected(code: &str, is_module: bool) -> Option<(String, SourceMap, String)> {
|
|
let output = exec_node_js(
|
|
include_str!("./srcmap.mjs"),
|
|
JsExecOptions {
|
|
cache: true,
|
|
module: true,
|
|
args: vec![
|
|
code.to_string(),
|
|
if is_module {
|
|
"module".into()
|
|
} else {
|
|
"script".into()
|
|
},
|
|
],
|
|
},
|
|
)
|
|
.ok()?;
|
|
|
|
let v = serde_json::from_str::<serde_json::Map<String, serde_json::Value>>(&output).unwrap();
|
|
|
|
let code = v.get("code").unwrap().as_str().unwrap();
|
|
let map = v.get("map").unwrap().as_str().unwrap();
|
|
|
|
let map = SourceMap::from_slice(map.as_bytes()).expect("invalid sourcemap");
|
|
|
|
let visualizer_url = visualizer_url(code, &map);
|
|
|
|
Some((code.to_string(), map, visualizer_url))
|
|
}
|
|
|
|
fn print_source_map(map: &SourceMap) -> Vec<String> {
|
|
let mut v = map
|
|
.tokens()
|
|
.map(|t| {
|
|
format!(
|
|
"Token: {}:{} => {}:{}",
|
|
t.get_src_line(),
|
|
t.get_src_col(),
|
|
t.get_dst_line(),
|
|
t.get_dst_col()
|
|
)
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
v.sort();
|
|
v
|
|
}
|
|
|
|
fn assert_eq_same_map(expected: &SourceMap, actual: &SourceMap) {
|
|
for expected_token in expected.tokens() {
|
|
let actual_token = actual
|
|
.lookup_token(expected_token.get_dst_line(), expected_token.get_dst_col())
|
|
.unwrap_or_else(|| panic!("token not found: {:?}", expected_token));
|
|
|
|
if expected_token.get_src_line() == 0 && expected_token.get_src_col() == 0 {
|
|
continue;
|
|
}
|
|
|
|
assert_eq!(
|
|
expected_token.get_src_line(),
|
|
actual_token.get_src_line(),
|
|
"line mismatch at {}:{}",
|
|
expected_token.get_dst_line(),
|
|
expected_token.get_dst_col()
|
|
);
|
|
assert_eq!(
|
|
expected_token.get_src_col(),
|
|
actual_token.get_src_col(),
|
|
"col mismatch at {}:{}",
|
|
expected_token.get_dst_line(),
|
|
expected_token.get_dst_col()
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Creates a url for https://evanw.github.io/source-map-visualization/
|
|
fn visualizer_url(code: &str, map: &SourceMap) -> String {
|
|
let map = {
|
|
let mut buf = vec![];
|
|
map.to_writer(&mut buf).unwrap();
|
|
String::from_utf8(buf).unwrap()
|
|
};
|
|
|
|
let code_len = format!("{}\0", code.len());
|
|
let map_len = format!("{}\0", map.len());
|
|
let hash = base64::encode(format!("{}{}{}{}", code_len, code, map_len, map));
|
|
|
|
format!("https://evanw.github.io/source-map-visualization/#{}", hash)
|
|
}
|
|
|
|
struct SourceMapConfigImpl;
|
|
|
|
impl SourceMapGenConfig for SourceMapConfigImpl {
|
|
fn file_name_to_source(&self, f: &swc_common::FileName) -> String {
|
|
f.to_string()
|
|
}
|
|
|
|
fn inline_sources_content(&self, _: &swc_common::FileName) -> bool {
|
|
true
|
|
}
|
|
}
|