- Fix block_scoping (Fix #682)
 - Handle new.target in susbscript position (Fix #675)
This commit is contained in:
강동윤 2020-02-20 13:06:28 +09:00 committed by GitHub
parent 15bef97278
commit e3ca8e5d13
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 167 additions and 74 deletions

View File

@ -413,10 +413,12 @@ impl<'a, I: Tokens> Parser<'a, I> {
if eat!('.') { if eat!('.') {
let start_of_target = cur_pos!(); let start_of_target = cur_pos!();
if eat!("target") { if eat!("target") {
return Ok(Box::new(Expr::MetaProp(MetaPropExpr { let expr = Box::new(Expr::MetaProp(MetaPropExpr {
meta: Ident::new(js_word!("new"), span_of_new), meta: Ident::new(js_word!("new"), span_of_new),
prop: Ident::new(js_word!("target"), span!(start_of_target)), prop: Ident::new(js_word!("target"), span!(start_of_target)),
}))); }));
return self.parse_subscripts(ExprOrSuper::Expr(expr), true);
} }
unexpected!() unexpected!()

View File

@ -418,6 +418,11 @@ fn issue_380() {
); );
} }
#[test]
fn issue_675() {
expr("Object.setPrototypeOf(this, new.target.prototype)");
}
#[bench] #[bench]
fn bench_new_expr_ts(b: &mut Bencher) { fn bench_new_expr_ts(b: &mut Bencher) {
bench_parser( bench_parser(

View File

@ -21,7 +21,11 @@ use swc_ecma_utils::{
/// } /// }
/// ``` /// ```
pub fn block_scoping() -> impl Pass { pub fn block_scoping() -> impl Pass {
BlockScoping::default() BlockScoping {
scope: Default::default(),
vars: vec![],
var_decl_kind: VarDeclKind::Var,
}
} }
type ScopeStack = SmallVec<[ScopeKind; 8]>; type ScopeStack = SmallVec<[ScopeKind; 8]>;
@ -39,10 +43,10 @@ enum ScopeKind {
Block, Block,
} }
#[derive(Default)]
struct BlockScoping { struct BlockScoping {
scope: ScopeStack, scope: ScopeStack,
vars: Vec<VarDeclarator>, vars: Vec<VarDeclarator>,
var_decl_kind: VarDeclKind,
} }
noop_fold_type!(BlockScoping); noop_fold_type!(BlockScoping);
@ -99,6 +103,14 @@ impl BlockScoping {
fn handle_vars(&mut self, body: Box<Stmt>) -> Box<Stmt> { fn handle_vars(&mut self, body: Box<Stmt>) -> Box<Stmt> {
body.map(|body| { body.map(|body| {
{
let mut v = FunctionFinder { found: false };
body.visit_with(&mut v);
if !v.found {
return body;
}
}
// //
if let Some(ScopeKind::ForLetLoop { args, used, .. }) = self.scope.pop() { if let Some(ScopeKind::ForLetLoop { args, used, .. }) = self.scope.pop() {
if used.is_empty() { if used.is_empty() {
@ -330,8 +342,12 @@ impl Fold<SetterProp> for BlockScoping {
impl Fold<VarDecl> for BlockScoping { impl Fold<VarDecl> for BlockScoping {
fn fold(&mut self, var: VarDecl) -> VarDecl { fn fold(&mut self, var: VarDecl) -> VarDecl {
let old = self.var_decl_kind;
self.var_decl_kind = var.kind;
let var = var.fold_children(self); let var = var.fold_children(self);
self.var_decl_kind = old;
VarDecl { VarDecl {
kind: VarDeclKind::Var, kind: VarDeclKind::Var,
..var ..var
@ -344,7 +360,11 @@ impl Fold<VarDeclarator> for BlockScoping {
let var = var.fold_children(self); let var = var.fold_children(self);
let init = if self.in_loop_body() && var.init.is_none() { let init = if self.in_loop_body() && var.init.is_none() {
Some(undefined(var.span())) if self.var_decl_kind == VarDeclKind::Var {
None
} else {
Some(undefined(var.span()))
}
} else { } else {
var.init var.init
}; };
@ -419,6 +439,8 @@ struct InfectionFinder<'a> {
found: bool, found: bool,
} }
noop_visit_type!(InfectionFinder<'_>);
impl Visit<VarDeclarator> for InfectionFinder<'_> { impl Visit<VarDeclarator> for InfectionFinder<'_> {
fn visit(&mut self, node: &VarDeclarator) { fn visit(&mut self, node: &VarDeclarator) {
let old = self.found; let old = self.found;
@ -480,6 +502,19 @@ impl Visit<Ident> for InfectionFinder<'_> {
} }
} }
#[derive(Debug)]
struct FunctionFinder {
found: bool,
}
noop_visit_type!(FunctionFinder);
impl Visit<Function> for FunctionFinder {
fn visit(&mut self, node: &Function) {
self.found = true
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::block_scoping; use super::block_scoping;
@ -591,4 +626,52 @@ expect(functions[0]()).toBe(1);
expect(functions[1]()).toBe(3); expect(functions[1]()).toBe(3);
" "
); );
test!(
::swc_ecma_parser::Syntax::default(),
|_| block_scoping(),
issue_662,
"function foo(parts) {
let match = null;
for (let i = 1; i >= 0; i--) {
for (let j = 0; j >= 0; j--) {
match = parts[i][j];
if (match) {
break;
}
}
if (match) {
break;
}
}
return match;
}
foo();",
"function foo(parts) {
var match = null;
for (var i = 1; i >= 0; i--) {
for (var j = 0; j >= 0; j--) {
match = parts[i][j];
if (match) {
break;
}
}
if (match) {
break;
}
}
return match;
}
foo();"
);
} }

View File

@ -206,80 +206,83 @@ impl Compiler {
opts: &Options, opts: &Options,
fm: &SourceFile, fm: &SourceFile,
) -> Result<BuiltConfig<impl Pass>, Error> { ) -> Result<BuiltConfig<impl Pass>, Error> {
let Options { self.run(|| {
ref root, let Options {
root_mode, ref root,
swcrc, root_mode,
config_file, swcrc,
is_module, config_file,
.. is_module,
} = opts; ..
let root = root } = opts;
.clone() let root = root
.unwrap_or_else(|| ::std::env::current_dir().unwrap()); .clone()
.unwrap_or_else(|| ::std::env::current_dir().unwrap());
let config_file = match config_file { let config_file = match config_file {
Some(ConfigFile::Str(ref s)) => { Some(ConfigFile::Str(ref s)) => {
let path = Path::new(s); let path = Path::new(s);
let r = File::open(&path).map_err(|err| Error::FailedToReadConfigFile { err })?; let r =
let config: Rc = serde_json::from_reader(r) File::open(&path).map_err(|err| Error::FailedToReadConfigFile { err })?;
.map_err(|err| Error::FailedToParseConfigFile { err })?; let config: Rc = serde_json::from_reader(r)
Some(config) .map_err(|err| Error::FailedToParseConfigFile { err })?;
} Some(config)
_ => None,
};
match fm.name {
FileName::Real(ref path) => {
if *swcrc {
let mut parent = path.parent();
while let Some(dir) = parent {
let swcrc = dir.join(".swcrc");
if swcrc.exists() {
let r = File::open(&swcrc)
.map_err(|err| Error::FailedToReadConfigFile { err })?;
let mut config = serde_json::from_reader(r)
.map_err(|err| Error::FailedToParseConfigFile { err })
.and_then(|rc: Rc| rc.into_config(Some(path)))?;
if let Some(config_file) = config_file {
config.merge(&config_file.into_config(Some(path))?)
}
let built =
opts.build(&self.cm, &self.handler, *is_module, Some(config));
return Ok(built);
}
if dir == root && *root_mode == RootMode::Root {
break;
}
parent = dir.parent();
}
} }
_ => None,
};
let config_file = config_file.unwrap_or_else(|| Rc::default()); match fm.name {
let built = opts.build( FileName::Real(ref path) => {
&self.cm, if *swcrc {
&self.handler, let mut parent = path.parent();
*is_module, while let Some(dir) = parent {
Some(config_file.into_config(Some(path))?), let swcrc = dir.join(".swcrc");
);
return Ok(built); if swcrc.exists() {
let r = File::open(&swcrc)
.map_err(|err| Error::FailedToReadConfigFile { err })?;
let mut config = serde_json::from_reader(r)
.map_err(|err| Error::FailedToParseConfigFile { err })
.and_then(|rc: Rc| rc.into_config(Some(path)))?;
if let Some(config_file) = config_file {
config.merge(&config_file.into_config(Some(path))?)
}
let built =
opts.build(&self.cm, &self.handler, *is_module, Some(config));
return Ok(built);
}
if dir == root && *root_mode == RootMode::Root {
break;
}
parent = dir.parent();
}
}
let config_file = config_file.unwrap_or_else(|| Rc::default());
let built = opts.build(
&self.cm,
&self.handler,
*is_module,
Some(config_file.into_config(Some(path))?),
);
return Ok(built);
}
_ => {}
} }
_ => {}
}
let built = opts.build( let built = opts.build(
&self.cm, &self.cm,
&self.handler, &self.handler,
*is_module, *is_module,
match config_file { match config_file {
Some(config_file) => Some(config_file.into_config(None)?), Some(config_file) => Some(config_file.into_config(None)?),
None => Some(Rc::default().into_config(None)?), None => Some(Rc::default().into_config(None)?),
}, },
); );
Ok(built) Ok(built)
})
} }
pub fn process_js_file( pub fn process_js_file(