Shebang & small improvement for object rest spread (#184)

swc_ecma_ast:
 - handle shebang

swc_ecma_codegen:
 - handle shebang

swc_ecma_parser:
 - handle shebang

swc_ecma_transforms:
 - fix arrow expression used with object rest spread
This commit is contained in:
강동윤 2019-02-13 11:08:21 +09:00 committed by GitHub
parent f797019b79
commit 2d062432a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 184 additions and 64 deletions

View File

@ -33,7 +33,7 @@ pub use self::{
JSXOpeningFragment, JSXSpreadChild, JSXText,
},
lit::{Bool, Lit, Null, Number, Regex, RegexFlags, Str},
module::{Module, ModuleItem},
module::{Module, ModuleItem, Script},
module_decl::{
ExportAll, ExportDefaultDecl, ExportSpecifier, ImportDecl, ImportDefault, ImportSpecific,
ImportSpecifier, ImportStarAs, ModuleDecl, NamedExport, NamedExportSpecifier,

View File

@ -1,10 +1,19 @@
use crate::{module_decl::ModuleDecl, stmt::Stmt};
use swc_atoms::JsWord;
use swc_common::{ast_node, Span};
#[ast_node]
pub struct Module {
pub span: Span,
pub body: Vec<ModuleItem>,
pub shebang: Option<JsWord>,
}
#[ast_node]
pub struct Script {
pub span: Span,
pub body: Vec<Stmt>,
pub shebang: Option<JsWord>,
}
#[ast_node]

View File

@ -72,7 +72,7 @@ pub struct Emitter<'a> {
}
impl<'a> Emitter<'a> {
pub fn emit_script(&mut self, stmts: &[Stmt]) -> Result {
pub fn emit_stmts(&mut self, stmts: &[Stmt]) -> Result {
let span = if stmts.is_empty() {
DUMMY_SP
} else {
@ -91,6 +91,21 @@ impl<'a> Emitter<'a> {
#[emitter]
pub fn emit_module(&mut self, node: &Module) -> Result {
if let Some(ref shebang) = node.shebang {
punct!("#!");
self.wr.write_lit(DUMMY_SP, &*shebang)?;
}
for stmt in &node.body {
emit!(stmt);
}
}
#[emitter]
pub fn emit_script(&mut self, node: &Script) -> Result {
if let Some(ref shebang) = node.shebang {
punct!("#!");
self.wr.write_lit(DUMMY_SP, &*shebang)?;
}
for stmt in &node.body {
emit!(stmt);
}
@ -1462,14 +1477,6 @@ impl<'a> Emitter<'a> {
/// Statements
impl<'a> Emitter<'a> {
pub fn emit_stmts(&mut self, stmts: &[Stmt]) -> Result {
for stmt in stmts {
self.emit_stmt(stmt)?;
}
Ok(())
}
#[emitter]
pub fn emit_stmt(&mut self, node: &Stmt) -> Result {
match *node {

View File

@ -758,6 +758,16 @@ impl<'a, I: Input> Lexer<'a, I> {
))
}
fn read_shebang(&mut self) -> LexResult<Option<JsWord>> {
if self.input.cur() != Some('#') || self.input.peek() != Some('!') {
return Ok(None);
}
self.input.bump();
self.input.bump();
let s = self.input.uncons_while(|c| !c.is_line_break());
Ok(Some(s.into()))
}
fn read_tmpl_token(&mut self, start_of_tpl: BytePos) -> LexResult<Token> {
let start = self.cur_pos();

View File

@ -99,43 +99,41 @@ impl<'a, I: Input> Lexer<'a, I> {
impl<'a, I: Input> Iterator for Lexer<'a, I> {
type Item = TokenAndSpan;
fn next(&mut self) -> Option<Self::Item> {
self.state.had_line_break = self.state.is_first;
self.state.is_first = false;
// skip spaces before getting next character, if we are allowed to.
if self.state.can_skip_space() {
let start = self.cur_pos();
match self.skip_space() {
Err(err) => {
return Some(Token::Error(err)).map(|token| {
// Attatch span to token.
TokenAndSpan {
token,
had_line_break: self.had_line_break_before_last(),
span: self.span(start),
}
});
}
_ => {}
}
};
let c = match self.input.cur() {
Some(c) => c,
None => return None,
};
// println!(
// "\tContext: ({:?}) {:?}",
// self.input.cur().unwrap(),
// self.state.context.0
// );
let start = self.cur_pos();
self.state.start = start;
let res = (|| -> Result<Option<_>, _> {
if self.state.is_first {
if let Some(shebang) = self.read_shebang()? {
return Ok(Some(Token::Shebang(shebang)));
}
}
self.state.had_line_break = self.state.is_first;
self.state.is_first = false;
// skip spaces before getting next character, if we are allowed to.
if self.state.can_skip_space() {
match self.skip_space() {
Err(err) => {
return Err(err);
}
_ => {}
}
};
let c = match self.input.cur() {
Some(c) => c,
None => return Ok(None),
};
// println!(
// "\tContext: ({:?}) {:?}",
// self.input.cur().unwrap(),
// self.state.context.0
// );
self.state.start = start;
if self.syntax.typescript() && self.ctx.in_type {
if c == '<' {
self.input.bump();

View File

@ -896,6 +896,14 @@ fn max_integer() {
lex_tokens(::Syntax::default(), "1.7976931348623157e+308");
}
#[test]
fn shebang() {
assert_eq!(
lex_tokens(::Syntax::default(), "#!/usr/bin/env node",),
vec![Token::Shebang("/usr/bin/env node".into())]
);
}
#[bench]
fn lex_colors_js(b: &mut Bencher) {
b.bytes = include_str!("../../colors.js").len() as _;

View File

@ -58,17 +58,28 @@ impl<'a, I: Input> Parser<'a, I> {
self.input.take_comments()
}
pub fn parse_script(&mut self) -> PResult<'a, (Vec<Stmt>)> {
pub fn parse_script(&mut self) -> PResult<'a, Script> {
let start = cur_pos!();
let shebang = self.parse_shebang()?;
let ctx = Context {
module: false,
..self.ctx()
};
self.set_ctx(ctx);
self.parse_block_body(true, true, None)
self.parse_block_body(true, true, None).map(|body| Script {
span: span!(start),
body,
shebang,
})
}
pub fn parse_module(&mut self) -> PResult<'a, Module> {
let start = cur_pos!();
let shebang = self.parse_shebang()?;
//TODO: parse() -> PResult<'a, Program>
let ctx = Context {
module: true,
@ -78,13 +89,23 @@ impl<'a, I: Input> Parser<'a, I> {
// Module code is always in strict mode
self.set_ctx(ctx);
let start = cur_pos!();
self.parse_block_body(true, true, None).map(|body| Module {
span: span!(start),
body,
shebang,
})
}
fn parse_shebang(&mut self) -> PResult<'a, Option<JsWord>> {
match *cur!(false)? {
Token::Shebang(..) => match bump!() {
Token::Shebang(v) => Ok(Some(v)),
_ => unreachable!(),
},
_ => Ok(None),
}
}
fn ctx(&self) -> Context {
self.input.get_ctx()
}

View File

@ -1061,4 +1061,21 @@ export default App"#;
);
}
#[test]
fn shebang() {
let src = "#!/usr/bin/env node";
test_parser(
src,
Syntax::Es(EsConfig {
..Default::default()
}),
|p| {
p.parse_module().map_err(|mut e| {
e.emit();
()
})
},
);
}
}

View File

@ -131,6 +131,7 @@ pub(crate) enum Token {
JSXTagStart,
JSXTagEnd,
Shebang(JsWord),
Error(#[cfg_attr(feature = "fold", fold(ignore))] Error),
}

View File

@ -262,7 +262,7 @@ fn identity_tests(tests: &mut Vec<TestDescAndFn>) -> Result<(), io::Error> {
Ok(())
}
fn parse_script(file_name: &Path) -> Result<Vec<Stmt>, NormalizedOutput> {
fn parse_script(file_name: &Path) -> Result<Script, NormalizedOutput> {
with_parser(file_name, |p| p.parse_script())
}
fn parse_module<'a>(file_name: &Path) -> Result<Module, NormalizedOutput> {

View File

@ -72,8 +72,7 @@ macro_rules! impl_for_for_stmt {
left
}
VarDeclOrPat::Pat(pat) => {
let var_ident =
quote_ident!(DUMMY_SP.apply_mark(Mark::fresh(Mark::root())), "_ref");
let var_ident = private_ident!("_ref");
let index = self.vars.len();
let pat =
self.fold_rest(pat, box Expr::Ident(var_ident.clone()), false, true);
@ -161,7 +160,10 @@ impl Fold<Vec<VarDeclarator>> for RestFolder {
let var_ident = match decl.init {
Some(box Expr::Ident(ref ident)) => ident.clone(),
_ => quote_ident!(DUMMY_SP.apply_mark(Mark::fresh(Mark::root())), "_ref"),
_ => match decl.name {
Pat::Ident(ref i) => i.clone(),
_ => private_ident!("_ref"),
},
};
let has_init = decl.init.is_some();
@ -410,7 +412,10 @@ impl Fold<ArrowExpr> for RestFolder {
f.params,
match f.body {
BlockStmtOrExpr::BlockStmt(block) => block.stmts,
BlockStmtOrExpr::Expr(expr) => vec![Stmt::Expr(expr)],
BlockStmtOrExpr::Expr(expr) => vec![Stmt::Return(ReturnStmt {
span: DUMMY_SP,
arg: Some(expr),
})],
},
);
ArrowExpr {
@ -480,8 +485,19 @@ impl Fold<CatchClause> for RestFolder {
}
impl RestFolder {
#[inline(always)]
fn insert_var_if_not_empty(&mut self, idx: usize, decl: VarDeclarator) {
match decl.init {
Some(box Expr::Ident(ref i1)) => match decl.name {
Pat::Ident(ref i2) => {
if *i1 == *i2 {
return;
}
}
_ => {}
},
_ => {}
}
match decl.name {
Pat::Object(ObjectPat { ref props, .. }) => {
if props.len() == 0 {
@ -493,8 +509,19 @@ impl RestFolder {
self.vars.insert(idx, decl)
}
#[inline(always)]
fn push_var_if_not_empty(&mut self, decl: VarDeclarator) {
match decl.init {
Some(box Expr::Ident(ref i1)) => match decl.name {
Pat::Ident(ref i2) => {
if *i1 == *i2 {
return;
}
}
_ => {}
},
_ => {}
}
match decl.name {
Pat::Object(ObjectPat { ref props, .. }) => {
if props.len() == 0 {

View File

@ -7,6 +7,21 @@ fn tr() -> impl Fold<Module> {
object_rest_spread()
}
test!(
::swc_ecma_parser::Syntax::default(),
|_| tr(),
issue_181,
r#"
const fn = ({ a, ...otherProps }) => otherProps;
"#,
r#"
const fn = (_param)=>{
var { a } = _param, otherProps = _objectWithoutProperties(_param, ['a']);
return otherProps;
};
"#
);
test!(
::swc_ecma_parser::Syntax::default(),
|_| tr(),

View File

@ -43,7 +43,7 @@ macro_rules! add_to {
None,
)
.parse_script()
.map(|stmts| stmts.fold_with(&mut DropSpan))
.map(|script| script.body.fold_with(&mut DropSpan))
.map_err(|mut e| {
e.emit();
()

View File

@ -57,6 +57,7 @@ where
Ok(Module {
span: DUMMY_SP,
body: op(tester)?.into_iter().map(ModuleItem::Stmt).collect(),
shebang: None,
})
},
expected,

View File

@ -467,7 +467,6 @@ impl Fold<Module> for Amd {
// ====================
Module {
span: module.span,
body: vec![ModuleItem::Stmt(Stmt::Expr(box Expr::Call(CallExpr {
span: DUMMY_SP,
callee: quote_ident!("define").as_callee(),
@ -500,6 +499,7 @@ impl Fold<Module> for Amd {
.collect(),
type_args: Default::default(),
})))],
..module
}
}
}

View File

@ -442,12 +442,16 @@ macro_rules! var_noop {
fn visit(&mut self, _: &$T) {}
}
};
($T:path, $($rest:tt)*) => {
var_noop!($T);
var_noop!($($rest)*);
};
}
var_noop!(Expr);
var_noop!(ArrowExpr);
var_noop!(Function);
var_noop!(Constructor);
var_noop!(Expr, ArrowExpr, Function, Constructor);
var_noop!(TsType, TsTypeAnn, TsTypeParam);
/// Private `_exports` ident.
pub(super) struct Exports(pub Ident);

View File

@ -71,10 +71,12 @@ impl<'a> Tester<'a> {
pub fn parse_stmts(&mut self, file_name: &str, src: &str) -> Result<Vec<Stmt>, ()> {
let stmts = self.with_parser(file_name, Syntax::default(), src, |p| {
p.parse_script().map_err(|mut e| {
e.emit();
()
})
p.parse_script()
.map_err(|mut e| {
e.emit();
()
})
.map(|script| script.body)
})?;
Ok(stmts)