mirror of
https://github.com/swc-project/swc.git
synced 2024-12-23 21:54:36 +03:00
Compiler baseline
- Fix binary - Add base for transformers This will be last large squash.
This commit is contained in:
parent
f9be576e02
commit
e12dcf0452
@ -66,6 +66,19 @@ fn main() {
|
|||||||
"private",
|
"private",
|
||||||
"protected",
|
"protected",
|
||||||
"public",
|
"public",
|
||||||
|
//
|
||||||
|
"Object",
|
||||||
|
"length",
|
||||||
|
"Infinity",
|
||||||
|
"undefined",
|
||||||
|
"NaN",
|
||||||
|
"RegExp",
|
||||||
|
// helpers
|
||||||
|
"apply",
|
||||||
|
"call",
|
||||||
|
"concat",
|
||||||
|
"_extends",
|
||||||
|
"_toConsumableArray"
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
//! -----
|
//! -----
|
||||||
//!
|
//!
|
||||||
//! This module use [`::rustc_errors`][] internally.
|
//! This module use [`::rustc_errors`][] internally.
|
||||||
//!
|
|
||||||
|
|
||||||
pub use self::{diagnostic::*, diagnostic_builder::DiagnosticBuilder, handler::*};
|
pub use self::{diagnostic::*, diagnostic_builder::DiagnosticBuilder, handler::*};
|
||||||
|
#[doc(inline)]
|
||||||
pub use rustc_errors::{
|
pub use rustc_errors::{
|
||||||
ColorConfig,
|
ColorConfig,
|
||||||
Level::{self, *},
|
Level::{self, *},
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::{FileLoader, FilePathMapping, SourceMap};
|
||||||
use std::{
|
use std::{
|
||||||
io,
|
io,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
@ -34,9 +35,9 @@ function foo() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test() {
|
fn test() {
|
||||||
let cm = CodeMap::with_file_loader(box MyFileLoader, FilePathMapping::empty());
|
let cm = SourceMap::with_file_loader(box MyFileLoader, FilePathMapping::empty());
|
||||||
let file_map = cm
|
let file_map = cm
|
||||||
.load_file_and_lines("tmp.js".as_ref())
|
.load_file("tmp.js".into())
|
||||||
.expect("failed to load tmp.js");
|
.expect("failed to load tmp.js");
|
||||||
println!(
|
println!(
|
||||||
"File (start={},end={})",
|
"File (start={},end={})",
|
||||||
@ -60,7 +61,7 @@ fn test() {
|
|||||||
|
|
||||||
DiagnosticBuilder::new_with_code(
|
DiagnosticBuilder::new_with_code(
|
||||||
&handler,
|
&handler,
|
||||||
Warning,
|
super::Warning,
|
||||||
Some(DiagnosticId::Lint("WITH_STMT".into())),
|
Some(DiagnosticId::Lint("WITH_STMT".into())),
|
||||||
"Lint: With statement",
|
"Lint: With statement",
|
||||||
)
|
)
|
||||||
|
@ -22,7 +22,9 @@ pub use self::{
|
|||||||
pos::*,
|
pos::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use syntax::source_map::{FilePathMapping, SourceMap, SpanSnippetError};
|
pub use syntax::source_map::{
|
||||||
|
FileLines, FileLoader, FileName, FilePathMapping, SourceMap, SpanSnippetError,
|
||||||
|
};
|
||||||
mod ast_node;
|
mod ast_node;
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
mod fold;
|
mod fold;
|
||||||
|
@ -7,5 +7,6 @@ authors = ["강동윤 <kdy1@outlook.kr>"]
|
|||||||
swc_ecma_ast = { path = "./ast" }
|
swc_ecma_ast = { path = "./ast" }
|
||||||
swc_ecma_codegen = { path = "./codegen" }
|
swc_ecma_codegen = { path = "./codegen" }
|
||||||
swc_ecma_parser = { path = "./parser" }
|
swc_ecma_parser = { path = "./parser" }
|
||||||
|
swc_ecma_transforms = { path = "./transforms" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
@ -143,12 +143,12 @@ pub struct MemberExpr {
|
|||||||
pub prop: Box<Expr>,
|
pub prop: Box<Expr>,
|
||||||
pub computed: bool,
|
pub computed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[ast_node]
|
#[ast_node]
|
||||||
pub struct CondExpr {
|
pub struct CondExpr {
|
||||||
#[span(lo)]
|
pub span: Span,
|
||||||
pub test: Box<Expr>,
|
pub test: Box<Expr>,
|
||||||
pub cons: Box<Expr>,
|
pub cons: Box<Expr>,
|
||||||
#[span(hi)]
|
|
||||||
pub alt: Box<Expr>,
|
pub alt: Box<Expr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,4 +86,34 @@ impl Ident {
|
|||||||
pub fn new(sym: JsWord, span: Span) -> Self {
|
pub fn new(sym: JsWord, span: Span) -> Self {
|
||||||
Ident { span, sym }
|
Ident { span, sym }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_reserved_only_for_es3(&self) -> bool {
|
||||||
|
[
|
||||||
|
"abstract",
|
||||||
|
"boolean",
|
||||||
|
"byte",
|
||||||
|
"char",
|
||||||
|
"double",
|
||||||
|
"enum",
|
||||||
|
"final",
|
||||||
|
"float",
|
||||||
|
"goto",
|
||||||
|
"implements",
|
||||||
|
"int",
|
||||||
|
"interface",
|
||||||
|
"long",
|
||||||
|
"native",
|
||||||
|
"package",
|
||||||
|
"private",
|
||||||
|
"protected",
|
||||||
|
"public",
|
||||||
|
"short",
|
||||||
|
"static",
|
||||||
|
"synchronized",
|
||||||
|
"throws",
|
||||||
|
"transient",
|
||||||
|
"volatile",
|
||||||
|
]
|
||||||
|
.contains(&&*self.sym)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,9 @@ impl<'a> Emitter<'a> {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if ext_sp.lo() < BytePos(3) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
// include `//`
|
// include `//`
|
||||||
ext_sp = ext_sp.with_lo(ext_sp.lo() - BytePos(3));
|
ext_sp = ext_sp.with_lo(ext_sp.lo() - BytePos(3));
|
||||||
|
@ -1435,7 +1435,11 @@ fn get_text_of_node<T: Spanned>(
|
|||||||
node: &T,
|
node: &T,
|
||||||
_include_travia: bool,
|
_include_travia: bool,
|
||||||
) -> Option<String> {
|
) -> Option<String> {
|
||||||
Some(cm.span_to_snippet(node.span()).unwrap())
|
let s = cm.span_to_snippet(node.span()).unwrap();
|
||||||
|
if s == "" {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(s)
|
||||||
|
|
||||||
// let span = node.span();
|
// let span = node.span();
|
||||||
// let src = match file.src {
|
// let src = match file.src {
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
#![feature(specialization)]
|
#![feature(specialization)]
|
||||||
#![feature(never_type)]
|
#![feature(never_type)]
|
||||||
// #![feature(nll)]
|
// #![feature(nll)]
|
||||||
#![feature(proc_macro)]
|
|
||||||
#![feature(try_from)]
|
#![feature(try_from)]
|
||||||
#![feature(try_trait)]
|
#![feature(try_trait)]
|
||||||
#![deny(unreachable_patterns)]
|
#![deny(unreachable_patterns)]
|
||||||
|
@ -96,7 +96,12 @@ impl<'a, I: Input> Parser<'a, I> {
|
|||||||
expect!(':');
|
expect!(':');
|
||||||
let alt = self.parse_assignment_expr()?;
|
let alt = self.parse_assignment_expr()?;
|
||||||
|
|
||||||
Ok(box Expr::Cond(CondExpr { test, cons, alt }))
|
Ok(box Expr::Cond(CondExpr {
|
||||||
|
test,
|
||||||
|
cons,
|
||||||
|
alt,
|
||||||
|
span: span!(start),
|
||||||
|
}))
|
||||||
} else {
|
} else {
|
||||||
return Ok(test);
|
return Ok(test);
|
||||||
}
|
}
|
||||||
|
@ -187,3 +187,32 @@ fn new_new_no_paren() {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn array_lit() {
|
||||||
|
assert_eq_ignore_span!(
|
||||||
|
expr("[a,,,,, ...d,, e]"),
|
||||||
|
box Expr::Array(ArrayLit {
|
||||||
|
span,
|
||||||
|
elems: vec![
|
||||||
|
Some(ExprOrSpread {
|
||||||
|
spread: None,
|
||||||
|
expr: box Expr::Ident(Ident::new("a".into(), span))
|
||||||
|
}),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(ExprOrSpread {
|
||||||
|
spread: Some(span),
|
||||||
|
expr: box Expr::Ident(Ident::new("d".into(), span))
|
||||||
|
}),
|
||||||
|
None,
|
||||||
|
Some(ExprOrSpread {
|
||||||
|
spread: None,
|
||||||
|
expr: box Expr::Ident(Ident::new("e".into(), span))
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -244,7 +244,7 @@ macro_rules! span {
|
|||||||
start.0, end.0
|
start.0, end.0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
::swc_common::Span::new(start, end, Default::default())
|
::swc_common::Span::new(start, end, ::swc_common::SyntaxContext::empty())
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,5 +2,5 @@ error: Unterminated block comment
|
|||||||
--> $DIR/tests/test262-parser/fail/100c329e6dd70e5a.js:1:1
|
--> $DIR/tests/test262-parser/fail/100c329e6dd70e5a.js:1:1
|
||||||
|
|
|
|
||||||
1 | /*
|
1 | /*
|
||||||
| ^^^^^
|
| ^^^
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
error: Expected Comma
|
error: Unexpected token
|
||||||
--> $DIR/tests/test262-parser/fail/1a5b0dfa9fde985d.js:1:24
|
--> $DIR/tests/test262-parser/fail/1a5b0dfa9fde985d.js:1:25
|
||||||
|
|
|
|
||||||
1 | function* f() { [yield {a = 0}]; }
|
1 | function* f() { [yield {a = 0}]; }
|
||||||
| ^
|
| ^^^^^
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
error: Unterminated block comment
|
error: Unterminated block comment
|
||||||
--> $DIR/tests/test262-parser/fail/1c6ba8177a9624f0.js:1:1
|
--> $DIR/tests/test262-parser/fail/1c6ba8177a9624f0.js:1:1
|
||||||
|
|
|
|
||||||
1 | / /*
|
1 | /*
|
||||||
2 | |
|
| ^^^
|
||||||
| |_^
|
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
error: Unexpected token
|
error: Unexpected token
|
||||||
--> $DIR/tests/test262-parser/fail/3118eaa619345896.js:3:3
|
--> $DIR/tests/test262-parser/fail/3118eaa619345896.js:2:3
|
||||||
|
|
|
|
||||||
3 | */]
|
2 | */]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
error: LineBreak cannot follow 'throw'
|
error: LineBreak cannot follow 'throw'
|
||||||
--> $DIR/tests/test262-parser/fail/379c49fbf3259511.js:2:5
|
--> $DIR/tests/test262-parser/fail/379c49fbf3259511.js:1:15
|
||||||
|
|
|
|
||||||
2 | */ e
|
1 | throw /*
*/ e
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
error: Unexpected token
|
error: Unexpected token
|
||||||
--> $DIR/tests/test262-parser/fail/42cb3f2a38cb2930.js:3:3
|
--> $DIR/tests/test262-parser/fail/42cb3f2a38cb2930.js:2:3
|
||||||
|
|
|
|
||||||
3 | */]
|
2 | */]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
error: Unexpected token
|
error: Unexpected token
|
||||||
--> $DIR/tests/test262-parser/fail/62d72a3c3d14d150.js:3:1
|
--> $DIR/tests/test262-parser/fail/62d72a3c3d14d150.js:2:2
|
||||||
|
|
|
|
||||||
3 | ]
|
2 |
]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
error: Unexpected token
|
error: Unexpected token
|
||||||
--> $DIR/tests/test262-parser/fail/67419010fc81184a.js:3:3
|
--> $DIR/tests/test262-parser/fail/67419010fc81184a.js:2:4
|
||||||
|
|
|
|
||||||
3 | */]
|
2 |
*/]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
error: Unexpected token
|
error: Unexpected token
|
||||||
--> $DIR/tests/test262-parser/fail/85bb33accf520f1d.js:3:1
|
--> $DIR/tests/test262-parser/fail/85bb33accf520f1d.js:2:1
|
||||||
|
|
|
|
||||||
3 | ]
|
2 | ]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
error: Unexpected token
|
error: Unexpected token
|
||||||
--> $DIR/tests/test262-parser/fail/8f7c7b7f4d70f975.js:3:1
|
--> $DIR/tests/test262-parser/fail/8f7c7b7f4d70f975.js:2:1
|
||||||
|
|
|
|
||||||
3 | ]
|
2 | ]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
|
@ -2,5 +2,5 @@ error: Unterminated block comment
|
|||||||
--> $DIR/tests/test262-parser/fail/ac1ee2739ad30d66.js:1:1
|
--> $DIR/tests/test262-parser/fail/ac1ee2739ad30d66.js:1:1
|
||||||
|
|
|
|
||||||
1 | /*
|
1 | /*
|
||||||
| ^^^
|
| ^^
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
error: Unexpected token
|
error: Unexpected token
|
||||||
--> $DIR/tests/test262-parser/fail/b7ae8c17f892abf6.js:3:1
|
--> $DIR/tests/test262-parser/fail/b7ae8c17f892abf6.js:2:2
|
||||||
|
|
|
|
||||||
3 | ]
|
2 |
]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
error: Expected ';', '}' or <eof>
|
error: Expected ';', '}' or <eof>
|
||||||
--> $DIR/tests/test262-parser/fail/b94278c0e4bdb364.js:1:5
|
--> $DIR/tests/test262-parser/fail/b94278c0e4bdb364.js:1:3
|
||||||
|
|
|
|
||||||
1 | __
|
1 | __
|
||||||
| ^
|
| ^
|
||||||
|
|
|
|
||||||
note: This is the expression part of an expression statement
|
note: This is the expression part of an expression statement
|
||||||
--> $DIR/tests/test262-parser/fail/b94278c0e4bdb364.js:1:1
|
--> $DIR/tests/test262-parser/fail/b94278c0e4bdb364.js:1:1
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
error: Unexpected token
|
error: Unexpected token
|
||||||
--> $DIR/tests/test262-parser/fail/c49106fce7ac6b09.js:2:1
|
--> $DIR/tests/test262-parser/fail/c49106fce7ac6b09.js:1:2
|
||||||
|
|
|
|
||||||
2 | ]
|
1 |
]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
error: Unexpected token
|
error: Unexpected token
|
||||||
--> $DIR/tests/test262-parser/fail/c7023e867afea771.js:3:1
|
--> $DIR/tests/test262-parser/fail/c7023e867afea771.js:2:1
|
||||||
|
|
|
|
||||||
3 | ]
|
2 | ]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
|
@ -2,5 +2,5 @@ error: Unterminated block comment
|
|||||||
--> $DIR/tests/test262-parser/fail/d056300d8658429f.js:1:1
|
--> $DIR/tests/test262-parser/fail/d056300d8658429f.js:1:1
|
||||||
|
|
|
|
||||||
1 | /*
|
1 | /*
|
||||||
| ^^^^^
|
| ^^^
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
error: LineBreak cannot follow 'throw'
|
error: LineBreak cannot follow 'throw'
|
||||||
--> $DIR/tests/test262-parser/fail/f1ee13ed9e476d25.js:2:5
|
--> $DIR/tests/test262-parser/fail/f1ee13ed9e476d25.js:1:15
|
||||||
|
|
|
|
||||||
2 | */ e
|
1 | throw /*
*/ e
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ use std::{
|
|||||||
path::Path,
|
path::Path,
|
||||||
};
|
};
|
||||||
use swc_common::{FileName, Fold, FoldWith, Span};
|
use swc_common::{FileName, Fold, FoldWith, Span};
|
||||||
use swc_ecma_parser::{ast::*, FileMapInput, PResult, Parser, Session};
|
use swc_ecma_parser::{ast::*, PResult, Parser, Session, SourceFileInput};
|
||||||
use test::{test_main, Options, ShouldPanic::No, TestDesc, TestDescAndFn, TestFn, TestName};
|
use test::{test_main, Options, ShouldPanic::No, TestDesc, TestDescAndFn, TestFn, TestName};
|
||||||
use testing::NormalizedOutput;
|
use testing::NormalizedOutput;
|
||||||
|
|
||||||
@ -87,8 +87,12 @@ fn error_tests(tests: &mut Vec<TestDescAndFn>) -> Result<(), io::Error> {
|
|||||||
const IGNORED_ERROR_TESTS: &[&str] = &[
|
const IGNORED_ERROR_TESTS: &[&str] = &[
|
||||||
// Wrong tests
|
// Wrong tests
|
||||||
"0d5e450f1da8a92a.js",
|
"0d5e450f1da8a92a.js",
|
||||||
|
"346316bef54d805a.js",
|
||||||
|
"976b6247ca78ab51.js",
|
||||||
|
"ae0a7ac275bc9f5c.js",
|
||||||
"748656edbfb2d0bb.js",
|
"748656edbfb2d0bb.js",
|
||||||
"79f882da06f88c9f.js",
|
"79f882da06f88c9f.js",
|
||||||
|
"d28e80d99f819136.js",
|
||||||
"92b6af54adef3624.js",
|
"92b6af54adef3624.js",
|
||||||
"ef2d369cccc5386c.js",
|
"ef2d369cccc5386c.js",
|
||||||
// Temporarily ignore tests for using octal escape before use strict
|
// Temporarily ignore tests for using octal escape before use strict
|
||||||
@ -261,10 +265,10 @@ fn parse_module<'a>(file_name: &Path, s: &str) -> Result<Module, NormalizedOutpu
|
|||||||
|
|
||||||
fn with_parser<F, Ret>(file_name: &Path, src: &str, f: F) -> Result<Ret, NormalizedOutput>
|
fn with_parser<F, Ret>(file_name: &Path, src: &str, f: F) -> Result<Ret, NormalizedOutput>
|
||||||
where
|
where
|
||||||
F: for<'a> FnOnce(&mut Parser<'a, FileMapInput>) -> PResult<'a, Ret>,
|
F: for<'a> FnOnce(&mut Parser<'a, SourceFileInput>) -> PResult<'a, Ret>,
|
||||||
{
|
{
|
||||||
let output = ::testing::run_test(|logger, cm, handler| {
|
let output = ::testing::run_test(|logger, cm, handler| {
|
||||||
let fm = cm.new_filemap(FileName::Real(file_name.into()), src.into());
|
let fm = cm.new_source_file(FileName::Real(file_name.into()), src.into());
|
||||||
|
|
||||||
let res = f(&mut Parser::new(
|
let res = f(&mut Parser::new(
|
||||||
Session {
|
Session {
|
||||||
@ -291,87 +295,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn normalize<T>(t: T) -> T
|
|
||||||
where
|
|
||||||
Normalizer: Fold<T>,
|
|
||||||
{
|
|
||||||
let mut n = Normalizer;
|
|
||||||
n.fold(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Normalizer;
|
|
||||||
impl Fold<Span> for Normalizer {
|
|
||||||
fn fold(&mut self, _: Span) -> Span {
|
|
||||||
Span::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Fold<Str> for Normalizer {
|
|
||||||
fn fold(&mut self, s: Str) -> Str {
|
|
||||||
Str {
|
|
||||||
span: Default::default(),
|
|
||||||
has_escape: false,
|
|
||||||
..s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Fold<Expr> for Normalizer {
|
|
||||||
fn fold(&mut self, e: Expr) -> Expr {
|
|
||||||
let e = e.fold_children(self);
|
|
||||||
|
|
||||||
match e {
|
|
||||||
Expr::Paren(ParenExpr { expr, .. }) => *expr,
|
|
||||||
Expr::New(NewExpr {
|
|
||||||
callee,
|
|
||||||
args: None,
|
|
||||||
span,
|
|
||||||
}) => Expr::New(NewExpr {
|
|
||||||
span,
|
|
||||||
callee,
|
|
||||||
args: Some(vec![]),
|
|
||||||
}),
|
|
||||||
// Flatten comma expressions.
|
|
||||||
Expr::Seq(SeqExpr { mut exprs, span }) => {
|
|
||||||
let need_work = exprs.iter().any(|n| match **n {
|
|
||||||
Expr::Seq(..) => true,
|
|
||||||
_ => false,
|
|
||||||
});
|
|
||||||
|
|
||||||
if need_work {
|
|
||||||
exprs = exprs.into_iter().fold(vec![], |mut v, e| {
|
|
||||||
match *e {
|
|
||||||
Expr::Seq(SeqExpr { exprs, .. }) => v.extend(exprs),
|
|
||||||
_ => v.push(e),
|
|
||||||
}
|
|
||||||
v
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Expr::Seq(SeqExpr { exprs, span })
|
|
||||||
}
|
|
||||||
_ => e,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Fold<PropName> for Normalizer {
|
|
||||||
fn fold(&mut self, n: PropName) -> PropName {
|
|
||||||
let n = n.fold_children(self);
|
|
||||||
|
|
||||||
match n {
|
|
||||||
PropName::Ident(Ident { sym, .. }) => PropName::Str(Str {
|
|
||||||
span: Default::default(),
|
|
||||||
value: sym,
|
|
||||||
has_escape: false,
|
|
||||||
}),
|
|
||||||
PropName::Num(num) => PropName::Str(Str {
|
|
||||||
span: Default::default(),
|
|
||||||
value: num.to_string().into(),
|
|
||||||
has_escape: false,
|
|
||||||
}),
|
|
||||||
_ => n,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn identity() {
|
fn identity() {
|
||||||
let args: Vec<_> = env::args().collect();
|
let args: Vec<_> = env::args().collect();
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
pub extern crate swc_ecma_ast as ast;
|
pub extern crate swc_ecma_ast as ast;
|
||||||
|
pub extern crate swc_ecma_codegen as codegen;
|
||||||
pub extern crate swc_ecma_parser as parser;
|
pub extern crate swc_ecma_parser as parser;
|
||||||
|
pub extern crate swc_ecma_transforms as transforms;
|
||||||
|
17
ecmascript/transforms/Cargo.toml
Normal file
17
ecmascript/transforms/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
[package]
|
||||||
|
name = "swc_ecma_transforms"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["강동윤 <kdy1@outlook.kr>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
swc_atoms = { path = "../../atoms" }
|
||||||
|
swc_common = { path = "../../common" }
|
||||||
|
swc_ecma_ast = { path = "../ast" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
testing = { path = "../../testing" }
|
||||||
|
swc_ecma_codegen = { path = "../codegen" }
|
||||||
|
swc_ecma_parser = { path = "../parser" }
|
||||||
|
pretty_assertions = "0.4"
|
||||||
|
slog = "2"
|
||||||
|
sourcemap = "2.2"
|
57
ecmascript/transforms/README.md
Normal file
57
ecmascript/transforms/README.md
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# Babel packages to port
|
||||||
|
|
||||||
|
- [ ] babel-helper-bindify-decorators
|
||||||
|
- [ ] babel-helper-builder-binary-assignment-operator-visitor
|
||||||
|
- [ ] babel-helper-call-delegate
|
||||||
|
- [ ] babel-helper-define-map
|
||||||
|
- [ ] babel-helper-explode-assignable-expression
|
||||||
|
- [ ] babel-helper-explode-class
|
||||||
|
- [ ] babel-helper-fixtures
|
||||||
|
- [ ] babel-helper-function-name
|
||||||
|
- [ ] babel-helper-get-function-arity
|
||||||
|
- [ ] babel-helper-hoist-variables
|
||||||
|
- [ ] babel-helper-module-imports
|
||||||
|
- [ ] babel-helper-module-transforms
|
||||||
|
- [ ] babel-helper-optimise-call-expression
|
||||||
|
- [ ] babel-helper-plugin-test-runner
|
||||||
|
- [ ] babel-helper-regex
|
||||||
|
- [ ] babel-helper-remap-async-to-generator
|
||||||
|
- [ ] babel-helper-replace-supers
|
||||||
|
- [ ] babel-helper-simple-access
|
||||||
|
- [ ] babel-helper-wrap-function
|
||||||
|
- [ ] babel-helpers
|
||||||
|
- [ ] babel-plugin-check-constants
|
||||||
|
- [ ] babel-plugin-external-helpers
|
||||||
|
- [ ] babel-plugin-transform-arrow-functions
|
||||||
|
- [ ] babel-plugin-transform-async-to-generator
|
||||||
|
- [ ] babel-plugin-transform-block-scoped-functions
|
||||||
|
- [ ] babel-plugin-transform-block-scoping
|
||||||
|
- [ ] babel-plugin-transform-classes
|
||||||
|
- [ ] babel-plugin-transform-computed-properties
|
||||||
|
- [ ] babel-plugin-transform-destructuring
|
||||||
|
- [ ] babel-plugin-transform-dotall-regex
|
||||||
|
- [ ] babel-plugin-transform-duplicate-keys
|
||||||
|
- [ ] babel-plugin-transform-for-of
|
||||||
|
- [ ] babel-plugin-transform-function-name
|
||||||
|
- [ ] babel-plugin-transform-instanceof
|
||||||
|
- [ ] babel-plugin-transform-jscript
|
||||||
|
- [ ] babel-plugin-transform-member-expression-literals
|
||||||
|
- [ ] babel-plugin-transform-modules-amd
|
||||||
|
- [ ] babel-plugin-transform-modules-commonjs
|
||||||
|
- [ ] babel-plugin-transform-modules-systemjs
|
||||||
|
- [ ] babel-plugin-transform-modules-umd
|
||||||
|
- [ ] babel-plugin-transform-new-target
|
||||||
|
- [ ] babel-plugin-transform-object-assign
|
||||||
|
- [ ] babel-plugin-transform-object-set-prototype-of-to-assign
|
||||||
|
- [ ] babel-plugin-transform-object-super
|
||||||
|
- [ ] babel-plugin-transform-parameters
|
||||||
|
- [ ] babel-plugin-transform-property-literals
|
||||||
|
- [ ] babel-plugin-transform-property-mutators
|
||||||
|
- [ ] babel-plugin-transform-proto-to-assign
|
||||||
|
- [ ] babel-plugin-transform-regenerator
|
||||||
|
- [ ] babel-plugin-transform-reserved-words
|
||||||
|
- [ ] babel-plugin-transform-strict-mode
|
||||||
|
- [ ] babel-plugin-transform-template-literals
|
||||||
|
- [ ] babel-plugin-transform-typeof-symbol
|
||||||
|
- [ ] babel-plugin-transform-unicode-regex
|
||||||
|
- [ ] babel-template
|
70
ecmascript/transforms/src/compat/arrow.rs
Normal file
70
ecmascript/transforms/src/compat/arrow.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
use swc_common::{FoldWith, Folder};
|
||||||
|
use swc_ecma_ast::*;
|
||||||
|
|
||||||
|
///Compile ES2015 arrow functions to ES5
|
||||||
|
///
|
||||||
|
///# Example
|
||||||
|
///
|
||||||
|
///## In
|
||||||
|
/// ```js
|
||||||
|
/// var a = () => {};
|
||||||
|
/// var a = (b) => b;
|
||||||
|
///
|
||||||
|
/// const double = [1,2,3].map((num) => num * 2);
|
||||||
|
/// console.log(double); // [2,4,6]
|
||||||
|
///
|
||||||
|
/// var bob = {
|
||||||
|
/// _name: "Bob",
|
||||||
|
/// _friends: ["Sally", "Tom"],
|
||||||
|
/// printFriends() {
|
||||||
|
/// this._friends.forEach(f =>
|
||||||
|
/// console.log(this._name + " knows " + f));
|
||||||
|
/// }
|
||||||
|
/// };
|
||||||
|
/// console.log(bob.printFriends());
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
///## Out
|
||||||
|
///```js
|
||||||
|
/// var a = function () {};
|
||||||
|
/// var a = function (b) {
|
||||||
|
/// return b;
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// const double = [1, 2, 3].map(function (num) {
|
||||||
|
/// return num * 2;
|
||||||
|
/// });
|
||||||
|
/// console.log(double); // [2,4,6]
|
||||||
|
///
|
||||||
|
/// var bob = {
|
||||||
|
/// _name: "Bob",
|
||||||
|
/// _friends: ["Sally", "Tom"],
|
||||||
|
/// printFriends() {
|
||||||
|
/// var _this = this;
|
||||||
|
///
|
||||||
|
/// this._friends.forEach(function (f) {
|
||||||
|
/// return console.log(_this._name + " knows " + f);
|
||||||
|
/// });
|
||||||
|
/// }
|
||||||
|
/// };
|
||||||
|
/// console.log(bob.printFriends());
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct Arrow;
|
||||||
|
|
||||||
|
impl Folder<Expr> for Arrow {
|
||||||
|
fn fold(&mut self, e: Expr) -> Expr {
|
||||||
|
let e = e.fold_children(self);
|
||||||
|
|
||||||
|
match e {
|
||||||
|
Expr::Arrow(ArrowExpr { .. }) => unimplemented!("Arrow function"),
|
||||||
|
_ => e,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
}
|
30
ecmascript/transforms/src/compat/es3/member_expr_lits.rs
Normal file
30
ecmascript/transforms/src/compat/es3/member_expr_lits.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
use swc_common::{Fold, FoldWith};
|
||||||
|
use swc_ecma_ast::*;
|
||||||
|
|
||||||
|
/// babel: `transform-member-expression-literals`
|
||||||
|
///
|
||||||
|
/// # Input
|
||||||
|
/// ```js
|
||||||
|
/// obj["foo"] = "isValid";
|
||||||
|
///
|
||||||
|
/// obj.const = "isKeyword";
|
||||||
|
/// obj["var"] = "isKeyword";
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Output
|
||||||
|
/// ```js
|
||||||
|
/// obj.foo = "isValid";
|
||||||
|
///
|
||||||
|
/// obj["const"] = "isKeyword";
|
||||||
|
/// obj["var"] = "isKeyword";
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct MemberExprLit;
|
||||||
|
|
||||||
|
impl Fold<MemberExpr> for MemberExprLit {
|
||||||
|
fn fold(&mut self, e: MemberExpr) -> MemberExpr {
|
||||||
|
let e = e.fold_children(self);
|
||||||
|
|
||||||
|
e
|
||||||
|
}
|
||||||
|
}
|
7
ecmascript/transforms/src/compat/es3/mod.rs
Normal file
7
ecmascript/transforms/src/compat/es3/mod.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
pub use self::{
|
||||||
|
member_expr_lits::MemberExprLit, prop_lits::PropertyLiteral, reserved_word::ReservedWord,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod member_expr_lits;
|
||||||
|
mod prop_lits;
|
||||||
|
mod reserved_word;
|
82
ecmascript/transforms/src/compat/es3/prop_lits.rs
Normal file
82
ecmascript/transforms/src/compat/es3/prop_lits.rs
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
use swc_common::{Fold, FoldWith};
|
||||||
|
use swc_ecma_ast::*;
|
||||||
|
|
||||||
|
/// babel: `transform-property-literals`
|
||||||
|
///
|
||||||
|
/// # Input
|
||||||
|
/// ```js
|
||||||
|
/// var foo = {
|
||||||
|
/// // changed
|
||||||
|
/// "bar": function () {},
|
||||||
|
/// "1": function () {},
|
||||||
|
///
|
||||||
|
/// // not changed
|
||||||
|
/// "default": 1,
|
||||||
|
/// [a]: 2,
|
||||||
|
/// foo: 1
|
||||||
|
/// };
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Output
|
||||||
|
/// ```js
|
||||||
|
/// var foo = {
|
||||||
|
/// bar: function () {},
|
||||||
|
/// 1: function () {},
|
||||||
|
///
|
||||||
|
/// "default": 1,
|
||||||
|
/// [a]: 2,
|
||||||
|
/// foo: 1
|
||||||
|
/// };
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct PropertyLiteral;
|
||||||
|
|
||||||
|
impl Fold<PropName> for PropertyLiteral {
|
||||||
|
fn fold(&mut self, n: PropName) -> PropName {
|
||||||
|
let n = n.fold_children(self);
|
||||||
|
|
||||||
|
match n {
|
||||||
|
PropName::Ident(ident) => {
|
||||||
|
if ident.is_reserved_only_for_es3() {
|
||||||
|
return PropName::Str(Str {
|
||||||
|
value: ident.sym,
|
||||||
|
span: ident.span,
|
||||||
|
has_escape: false,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
PropName::Ident(ident)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => n,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
test!(
|
||||||
|
PropertyLiteral,
|
||||||
|
babel_basic,
|
||||||
|
r#"var foo = {
|
||||||
|
// changed
|
||||||
|
"bar": function () {},
|
||||||
|
"1": function () {},
|
||||||
|
|
||||||
|
// not changed
|
||||||
|
"default": 1,
|
||||||
|
[a]: 2,
|
||||||
|
foo: 1
|
||||||
|
};"#,
|
||||||
|
r#"var foo = {
|
||||||
|
bar: function () {},
|
||||||
|
1: function () {},
|
||||||
|
|
||||||
|
"default": 1,
|
||||||
|
[a]: 2,
|
||||||
|
foo: 1
|
||||||
|
};"#
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
30
ecmascript/transforms/src/compat/es3/reserved_word.rs
Normal file
30
ecmascript/transforms/src/compat/es3/reserved_word.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
use swc_common::{Fold, FoldWith};
|
||||||
|
use swc_ecma_ast::*;
|
||||||
|
|
||||||
|
/// babel: `@babel/plugin-transform-reserved-words`
|
||||||
|
///
|
||||||
|
/// Some words were reserved in ES3 as potential future keywords but were not
|
||||||
|
/// reserved in ES5 and later. This plugin, to be used when targeting ES3
|
||||||
|
/// environments, renames variables from that set of words.
|
||||||
|
///
|
||||||
|
/// # Input
|
||||||
|
/// ```js
|
||||||
|
/// var abstract = 1;
|
||||||
|
/// var x = abstract + 1;
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Output
|
||||||
|
/// ```js
|
||||||
|
/// var _abstract = 1;
|
||||||
|
/// var x = _abstract + 1;
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct ReservedWord;
|
||||||
|
|
||||||
|
impl Fold<Expr> for ReservedWord {
|
||||||
|
fn fold(&mut self, e: Expr) -> Expr {
|
||||||
|
let e = e.fold_children(self);
|
||||||
|
|
||||||
|
e
|
||||||
|
}
|
||||||
|
}
|
109
ecmascript/transforms/src/compat/exponentation.rs
Normal file
109
ecmascript/transforms/src/compat/exponentation.rs
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
use swc_common::{FoldWith, Folder};
|
||||||
|
use swc_ecma_ast::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct Exponentation;
|
||||||
|
|
||||||
|
impl Folder<Expr> for Exponentation {
|
||||||
|
fn fold(&mut self, e: Expr) -> Expr {
|
||||||
|
let e = e.fold_children(self);
|
||||||
|
|
||||||
|
match e {
|
||||||
|
Expr::Bin(BinExpr {
|
||||||
|
left,
|
||||||
|
op: op!("**"),
|
||||||
|
right,
|
||||||
|
span,
|
||||||
|
}) => Expr::Call(CallExpr {
|
||||||
|
span,
|
||||||
|
callee: ExprOrSuper::Expr(box Expr::Member(MemberExpr {
|
||||||
|
span,
|
||||||
|
obj: ExprOrSuper::Expr(
|
||||||
|
box Ident {
|
||||||
|
span,
|
||||||
|
sym: "Math".into(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
|
prop: box Ident {
|
||||||
|
span,
|
||||||
|
sym: "pow".into(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
computed: false,
|
||||||
|
})),
|
||||||
|
|
||||||
|
args: vec![
|
||||||
|
ExprOrSpread {
|
||||||
|
expr: left,
|
||||||
|
spread: None,
|
||||||
|
},
|
||||||
|
ExprOrSpread {
|
||||||
|
expr: right,
|
||||||
|
spread: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
_ => e,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
test!(Exponentation, babel_binary, "2 ** 2", "Math.pow(2, 2)");
|
||||||
|
|
||||||
|
test_exec!(
|
||||||
|
Exponentation,
|
||||||
|
babel_comprehensive,
|
||||||
|
r#"assert.equal(8, 2 ** 3);
|
||||||
|
assert.equal(24, 3 * 2 ** 3);
|
||||||
|
var x = 2;
|
||||||
|
assert.equal(8, 2 ** ++x);
|
||||||
|
assert.equal(1, 2 ** -1 * 2);
|
||||||
|
|
||||||
|
var calls = 0;
|
||||||
|
var q = {q: 3};
|
||||||
|
var o = {
|
||||||
|
get p() {
|
||||||
|
calls++;
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
o.p.q **= 2;
|
||||||
|
assert.equal(1, calls);
|
||||||
|
assert.equal(9, o.p.q);
|
||||||
|
|
||||||
|
assert.equal(512, 2 ** (3 ** 2));
|
||||||
|
assert.equal(512, 2 ** 3 ** 2);"#
|
||||||
|
);
|
||||||
|
|
||||||
|
test_exec!(
|
||||||
|
Exponentation,
|
||||||
|
babel_memoize_object,
|
||||||
|
r#"var counters = 0;
|
||||||
|
Object.defineProperty(global, "reader", {
|
||||||
|
get: function () {
|
||||||
|
counters += 1;
|
||||||
|
return { x: 2 };
|
||||||
|
},
|
||||||
|
configurable: true
|
||||||
|
});
|
||||||
|
reader.x **= 2;
|
||||||
|
assert.ok(counters === 1);"#
|
||||||
|
);
|
||||||
|
|
||||||
|
// test!(
|
||||||
|
// Exponentation,
|
||||||
|
// babel_4403,
|
||||||
|
// "var a, b;
|
||||||
|
// a[`${b++}`] **= 1;",
|
||||||
|
// "var _ref;
|
||||||
|
|
||||||
|
// var a, b;
|
||||||
|
// _ref = `${b++}`, a[_ref] = Math.pow(a[_ref], 1);"
|
||||||
|
// );
|
||||||
|
}
|
16
ecmascript/transforms/src/compat/helpers.rs
Normal file
16
ecmascript/transforms/src/compat/helpers.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct Helpers {
|
||||||
|
/// `_extends({}, b)`
|
||||||
|
pub(crate) extends: AtomicBool,
|
||||||
|
pub(crate) to_consumable_array: AtomicBool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Helpers {
|
||||||
|
pub fn merge(mut self, other: &Self) -> Self {
|
||||||
|
*self.extends.get_mut() |= other.extends.load(Ordering::SeqCst);
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
13
ecmascript/transforms/src/compat/mod.rs
Normal file
13
ecmascript/transforms/src/compat/mod.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
pub use self::{
|
||||||
|
arrow::Arrow, es3::*, exponentation::Exponentation, shorthand::Shorthand,
|
||||||
|
spread::SpreadElement, sticky_regex::StickyRegex, template_literal::TemplateLiteral,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod arrow;
|
||||||
|
mod es3;
|
||||||
|
mod exponentation;
|
||||||
|
mod helpers;
|
||||||
|
mod shorthand;
|
||||||
|
mod spread;
|
||||||
|
mod sticky_regex;
|
||||||
|
mod template_literal;
|
125
ecmascript/transforms/src/compat/shorthand.rs
Normal file
125
ecmascript/transforms/src/compat/shorthand.rs
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
use swc_common::{FoldWith, Folder};
|
||||||
|
use swc_ecma_ast::*;
|
||||||
|
|
||||||
|
///Compile ES2015 shorthand properties to ES5
|
||||||
|
///
|
||||||
|
///# Example
|
||||||
|
///
|
||||||
|
///## In
|
||||||
|
///
|
||||||
|
/// ```js
|
||||||
|
/// var o = { a, b, c };
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
///## Out
|
||||||
|
///
|
||||||
|
/// ```js
|
||||||
|
/// var o = { a: a, b: b, c: c };
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
///## In
|
||||||
|
///
|
||||||
|
/// ```js
|
||||||
|
/// var cat = {
|
||||||
|
/// getName() {
|
||||||
|
/// return name;
|
||||||
|
/// }
|
||||||
|
/// };
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
///## Out
|
||||||
|
///```js
|
||||||
|
/// var cat = {
|
||||||
|
/// getName: function () {
|
||||||
|
/// return name;
|
||||||
|
/// }
|
||||||
|
/// };
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct Shorthand;
|
||||||
|
|
||||||
|
impl Folder<Prop> for Shorthand {
|
||||||
|
fn fold(&mut self, prop: Prop) -> Prop {
|
||||||
|
let prop = prop.fold_children(self);
|
||||||
|
|
||||||
|
match prop {
|
||||||
|
Prop::Shorthand(i) => Prop::KeyValue(KeyValueProp {
|
||||||
|
key: PropName::Ident(i.clone()),
|
||||||
|
value: box i.into(),
|
||||||
|
}),
|
||||||
|
Prop::Method(MethodProp { key, function }) => Prop::KeyValue(KeyValueProp {
|
||||||
|
key,
|
||||||
|
value: box Expr::Fn(FnExpr {
|
||||||
|
ident: None,
|
||||||
|
function,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
_ => prop,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
test!(
|
||||||
|
Shorthand,
|
||||||
|
babel_method_plain,
|
||||||
|
"var obj = {
|
||||||
|
method() {
|
||||||
|
return 5 + 5;
|
||||||
|
}
|
||||||
|
};",
|
||||||
|
"var obj = {
|
||||||
|
method: function () {
|
||||||
|
return 5 + 5;
|
||||||
|
}
|
||||||
|
};"
|
||||||
|
);
|
||||||
|
|
||||||
|
test!(
|
||||||
|
Shorthand,
|
||||||
|
babel_comments,
|
||||||
|
"var A = 'a';
|
||||||
|
var o = {
|
||||||
|
A // comment
|
||||||
|
};",
|
||||||
|
"var A = 'a';
|
||||||
|
var o = {
|
||||||
|
A: A // comment
|
||||||
|
|
||||||
|
};"
|
||||||
|
);
|
||||||
|
|
||||||
|
test!(
|
||||||
|
Shorthand,
|
||||||
|
babel_mixed,
|
||||||
|
"var coords = { x, y, foo: 'bar' };",
|
||||||
|
"var coords = {
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
foo: 'bar'
|
||||||
|
};"
|
||||||
|
);
|
||||||
|
|
||||||
|
test!(
|
||||||
|
Shorthand,
|
||||||
|
babel_multiple,
|
||||||
|
"var coords = { x, y };",
|
||||||
|
"var coords = {
|
||||||
|
x: x,
|
||||||
|
y: y
|
||||||
|
};"
|
||||||
|
);
|
||||||
|
|
||||||
|
test!(
|
||||||
|
Shorthand,
|
||||||
|
babel_single,
|
||||||
|
"var coords = { x };",
|
||||||
|
"var coords = {
|
||||||
|
x: x
|
||||||
|
};"
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
202
ecmascript/transforms/src/compat/spread.rs
Normal file
202
ecmascript/transforms/src/compat/spread.rs
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
use super::helpers::Helpers;
|
||||||
|
use std::{
|
||||||
|
mem,
|
||||||
|
sync::{atomic::Ordering, Arc},
|
||||||
|
};
|
||||||
|
use swc_common::{Fold, FoldWith, Span};
|
||||||
|
use swc_ecma_ast::*;
|
||||||
|
use util::ExprFactory;
|
||||||
|
|
||||||
|
/// es2015 - `SpreadElement`
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct SpreadElement {
|
||||||
|
helpers: Arc<Helpers>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Fold<Expr> for SpreadElement {
|
||||||
|
fn fold(&mut self, e: Expr) -> Expr {
|
||||||
|
let e = e.fold_children(self);
|
||||||
|
|
||||||
|
match e {
|
||||||
|
Expr::Array(ArrayLit { .. }) => unimplemented!(),
|
||||||
|
Expr::Call(CallExpr {
|
||||||
|
callee: ExprOrSuper::Expr(callee),
|
||||||
|
args,
|
||||||
|
span,
|
||||||
|
}) => {
|
||||||
|
let has_spread = args
|
||||||
|
.iter()
|
||||||
|
.any(|ExprOrSpread { spread, .. }| spread.is_some());
|
||||||
|
if !has_spread {
|
||||||
|
return Expr::Call(CallExpr {
|
||||||
|
callee: ExprOrSuper::Expr(callee),
|
||||||
|
args,
|
||||||
|
span,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let args_array = concat_args(&self.helpers, span, args);
|
||||||
|
//
|
||||||
|
// f.apply(undefined, args)
|
||||||
|
//
|
||||||
|
callee.apply(span, expr!(span, undefined), vec![args_array.as_arg()])
|
||||||
|
}
|
||||||
|
Expr::New(NewExpr {
|
||||||
|
callee,
|
||||||
|
args: Some(args),
|
||||||
|
span,
|
||||||
|
}) => {
|
||||||
|
let has_spread = args
|
||||||
|
.iter()
|
||||||
|
.any(|ExprOrSpread { spread, .. }| spread.is_some());
|
||||||
|
if !has_spread {
|
||||||
|
return Expr::New(NewExpr {
|
||||||
|
callee,
|
||||||
|
args: Some(args),
|
||||||
|
span,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let args = concat_args(
|
||||||
|
&self.helpers,
|
||||||
|
span,
|
||||||
|
vec![expr!(span, null).as_arg()]
|
||||||
|
.into_iter()
|
||||||
|
.chain(args)
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
|
||||||
|
//
|
||||||
|
// f.apply(undefined, args)
|
||||||
|
//
|
||||||
|
|
||||||
|
Expr::New(NewExpr {
|
||||||
|
span,
|
||||||
|
callee: box member_expr!(span, Function.prototype.bind)
|
||||||
|
.apply(span, callee, vec![args.as_arg()])
|
||||||
|
.wrap_with_paren(),
|
||||||
|
args: Some(vec![]),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => e,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn concat_args(helpers: &Helpers, span: Span, args: Vec<ExprOrSpread>) -> Expr {
|
||||||
|
//
|
||||||
|
// []
|
||||||
|
//
|
||||||
|
let mut first_arr = None;
|
||||||
|
|
||||||
|
let mut tmp_arr = vec![];
|
||||||
|
let mut buf = vec![];
|
||||||
|
|
||||||
|
macro_rules! make_arr {
|
||||||
|
() => {
|
||||||
|
let elems = mem::replace(&mut tmp_arr, vec![]);
|
||||||
|
match first_arr {
|
||||||
|
Some(_) => {
|
||||||
|
if !elems.is_empty() {
|
||||||
|
buf.push(Expr::Array(ArrayLit { span, elems }).as_arg());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
first_arr = Some(Expr::Array(ArrayLit { span, elems }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
for arg in args {
|
||||||
|
let ExprOrSpread { expr, spread } = arg;
|
||||||
|
|
||||||
|
match spread {
|
||||||
|
// ...b -> toConsumableArray(b)
|
||||||
|
Some(span) => {
|
||||||
|
//
|
||||||
|
make_arr!();
|
||||||
|
|
||||||
|
helpers.to_consumable_array.store(true, Ordering::SeqCst);
|
||||||
|
|
||||||
|
buf.push(
|
||||||
|
Expr::Call(CallExpr {
|
||||||
|
span,
|
||||||
|
callee: ExprOrSuper::Expr(
|
||||||
|
box Ident::new(js_word!("_toConsumableArray"), span).into(),
|
||||||
|
),
|
||||||
|
args: vec![ExprOrSpread { expr, spread: None }],
|
||||||
|
})
|
||||||
|
.as_arg(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
None => tmp_arr.push(Some(ExprOrSpread { expr, spread: None })),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
make_arr!();
|
||||||
|
|
||||||
|
Expr::Call(CallExpr {
|
||||||
|
// TODO
|
||||||
|
span,
|
||||||
|
|
||||||
|
callee: ExprOrSuper::Expr(box Expr::Member(MemberExpr {
|
||||||
|
// TODO: Mark
|
||||||
|
span,
|
||||||
|
prop: box Expr::Ident(Ident::new(js_word!("concat"), span)),
|
||||||
|
obj: ExprOrSuper::Expr(box first_arr.take().unwrap_or_else(|| {
|
||||||
|
// No arg
|
||||||
|
|
||||||
|
// assert!(args.is_empty());
|
||||||
|
|
||||||
|
Expr::Array(ArrayLit {
|
||||||
|
span,
|
||||||
|
elems: vec![],
|
||||||
|
})
|
||||||
|
})),
|
||||||
|
computed: false,
|
||||||
|
})),
|
||||||
|
|
||||||
|
args: buf,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
test!(
|
||||||
|
SpreadElement::default(),
|
||||||
|
call,
|
||||||
|
"ca(a, b, c, ...d, e)",
|
||||||
|
"ca.apply(undefined, [a, b, c].concat(_toConsumableArray(d), [e]));"
|
||||||
|
);
|
||||||
|
|
||||||
|
test!(
|
||||||
|
SpreadElement::default(),
|
||||||
|
call_multi_spread,
|
||||||
|
"ca(a, b, ...d, e, f, ...h)",
|
||||||
|
"ca.apply(undefined, [a, b].concat(_toConsumableArray(d), [e, f], _toConsumableArray(h)));"
|
||||||
|
);
|
||||||
|
|
||||||
|
test!(
|
||||||
|
SpreadElement::default(),
|
||||||
|
call_noop,
|
||||||
|
"ca(a, b, c, d, e)",
|
||||||
|
"ca(a, b, c, d, e);"
|
||||||
|
);
|
||||||
|
|
||||||
|
test!(
|
||||||
|
SpreadElement::default(),
|
||||||
|
new,
|
||||||
|
"new C(a, b, c, ...d, e)",
|
||||||
|
"new (Function.prototype.bind.apply(C, [null, a, b, c].concat(_toConsumableArray(d), \
|
||||||
|
[e])))();"
|
||||||
|
);
|
||||||
|
|
||||||
|
test!(
|
||||||
|
SpreadElement::default(),
|
||||||
|
new_noop,
|
||||||
|
"new C(a, b, c, c, d, e)",
|
||||||
|
"new C(a, b, c, c, d, e);"
|
||||||
|
);
|
||||||
|
}
|
80
ecmascript/transforms/src/compat/sticky_regex.rs
Normal file
80
ecmascript/transforms/src/compat/sticky_regex.rs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
use swc_common::{FoldWith, Folder, DUMMY_SP};
|
||||||
|
use swc_ecma_ast::*;
|
||||||
|
|
||||||
|
/// Compile ES2015 sticky regex to an ES5 RegExp constructor
|
||||||
|
///
|
||||||
|
///# Example
|
||||||
|
///## In
|
||||||
|
///
|
||||||
|
/// ```js
|
||||||
|
/// /o+/y;
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
///## Out
|
||||||
|
///
|
||||||
|
/// ```js
|
||||||
|
/// new RegExp("o+", "y")
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct StickyRegex;
|
||||||
|
|
||||||
|
impl Folder<Expr> for StickyRegex {
|
||||||
|
fn fold(&mut self, e: Expr) -> Expr {
|
||||||
|
let e = e.fold_children(self);
|
||||||
|
|
||||||
|
match e {
|
||||||
|
Expr::Lit(Lit::Regex(Regex { exp, flags, span })) => {
|
||||||
|
if flags
|
||||||
|
.as_ref()
|
||||||
|
.map(|s| s.value.contains("y"))
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
let str_lit = |s: Str| box Expr::Lit(Lit::Str(s));
|
||||||
|
|
||||||
|
return Expr::New(NewExpr {
|
||||||
|
callee: box Ident {
|
||||||
|
span: DUMMY_SP,
|
||||||
|
sym: js_word!("RegExp"),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
args: Some(
|
||||||
|
vec![ExprOrSpread {
|
||||||
|
expr: str_lit(exp),
|
||||||
|
spread: None,
|
||||||
|
}]
|
||||||
|
.into_iter()
|
||||||
|
.chain(flags.map(|flags| ExprOrSpread {
|
||||||
|
expr: str_lit(flags),
|
||||||
|
spread: None,
|
||||||
|
}))
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
span,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return Expr::Lit(Lit::Regex(Regex { exp, flags, span }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => e,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
test!(
|
||||||
|
StickyRegex,
|
||||||
|
babel_basic,
|
||||||
|
"var re = /o+/y;",
|
||||||
|
"var re = new RegExp('o+', 'y');"
|
||||||
|
);
|
||||||
|
|
||||||
|
test!(
|
||||||
|
StickyRegex,
|
||||||
|
babel_ignore_non_sticky,
|
||||||
|
"var re = /o+/;",
|
||||||
|
"var re = /o+/;"
|
||||||
|
);
|
||||||
|
}
|
19
ecmascript/transforms/src/compat/template_literal.rs
Normal file
19
ecmascript/transforms/src/compat/template_literal.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
use swc_common::{Fold, FoldWith};
|
||||||
|
use swc_ecma_ast::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct TemplateLiteral;
|
||||||
|
|
||||||
|
impl Fold<Expr> for TemplateLiteral {
|
||||||
|
fn fold(&mut self, e: Expr) -> Expr {
|
||||||
|
let e = e.fold_children(self);
|
||||||
|
|
||||||
|
match e {
|
||||||
|
Expr::Tpl(TplLit { .. }) => {
|
||||||
|
// TODO
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
_ => e,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
ecmascript/transforms/src/lib.rs
Normal file
32
ecmascript/transforms/src/lib.rs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#![feature(box_patterns)]
|
||||||
|
#![feature(box_syntax)]
|
||||||
|
#![feature(try_trait)]
|
||||||
|
#![feature(specialization)]
|
||||||
|
#![feature(trace_macros)]
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[macro_use]
|
||||||
|
extern crate slog;
|
||||||
|
#[macro_use]
|
||||||
|
pub extern crate swc_atoms;
|
||||||
|
pub extern crate swc_common;
|
||||||
|
#[macro_use]
|
||||||
|
pub extern crate swc_ecma_ast;
|
||||||
|
#[cfg(test)]
|
||||||
|
extern crate sourcemap;
|
||||||
|
#[cfg(test)]
|
||||||
|
pub extern crate swc_ecma_codegen;
|
||||||
|
#[cfg(test)]
|
||||||
|
pub extern crate swc_ecma_parser;
|
||||||
|
#[cfg(test)]
|
||||||
|
#[macro_use]
|
||||||
|
extern crate testing;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod macros;
|
||||||
|
#[macro_use]
|
||||||
|
mod quote;
|
||||||
|
pub mod compat;
|
||||||
|
pub mod scope;
|
||||||
|
pub mod simplify;
|
||||||
|
pub mod util;
|
131
ecmascript/transforms/src/macros.rs
Normal file
131
ecmascript/transforms/src/macros.rs
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
#![cfg(test)]
|
||||||
|
use sourcemap::SourceMapBuilder;
|
||||||
|
use std::{
|
||||||
|
io::{self, Write},
|
||||||
|
rc::Rc,
|
||||||
|
sync::{Arc, RwLock},
|
||||||
|
};
|
||||||
|
use swc_common::{BytePos, FileName, Fold};
|
||||||
|
use swc_ecma_ast::*;
|
||||||
|
use swc_ecma_codegen::Emitter;
|
||||||
|
use swc_ecma_parser::{Parser, Session, SourceFileInput};
|
||||||
|
|
||||||
|
pub fn fold<F>(module: ::swc_ecma_ast::Module, f: &mut F) -> ::swc_ecma_ast::Module
|
||||||
|
where
|
||||||
|
F: ::swc_common::Folder<::swc_ecma_ast::Module>,
|
||||||
|
{
|
||||||
|
let module = f.fold(module);
|
||||||
|
// normalize (remove span)
|
||||||
|
::testing::drop_span(module)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MyHandlers;
|
||||||
|
|
||||||
|
impl swc_ecma_codegen::Handlers for MyHandlers {}
|
||||||
|
|
||||||
|
pub(crate) struct Noop;
|
||||||
|
|
||||||
|
pub fn apply_transform<T: Fold<Module>>(
|
||||||
|
mut tr: T,
|
||||||
|
name: &'static str,
|
||||||
|
src: &'static str,
|
||||||
|
) -> String {
|
||||||
|
let out = ::testing::run_test(|logger, cm, handler| {
|
||||||
|
let fm = cm.new_source_file(FileName::Real(name.into()), src.into());
|
||||||
|
|
||||||
|
let module = {
|
||||||
|
let handler = ::swc_common::errors::Handler::with_tty_emitter(
|
||||||
|
::swc_common::errors::ColorConfig::Auto,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
Some(cm.clone()),
|
||||||
|
);
|
||||||
|
let logger = ::testing::logger().new(o!("src" => src));
|
||||||
|
|
||||||
|
let sess = Session {
|
||||||
|
handler: &handler,
|
||||||
|
logger: &logger,
|
||||||
|
cfg: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let module = {
|
||||||
|
let mut p = Parser::new(sess, SourceFileInput::from(&*fm));
|
||||||
|
p.parse_module().unwrap_or_else(|err| {
|
||||||
|
err.emit();
|
||||||
|
panic!("failed to parse")
|
||||||
|
})
|
||||||
|
};
|
||||||
|
println!("parsed {} as a module", src);
|
||||||
|
|
||||||
|
module
|
||||||
|
};
|
||||||
|
|
||||||
|
let module = fold(module, &mut tr);
|
||||||
|
|
||||||
|
let handlers = box MyHandlers;
|
||||||
|
|
||||||
|
let mut wr = Buf(Arc::new(RwLock::new(vec![])));
|
||||||
|
{
|
||||||
|
let mut emitter = Emitter {
|
||||||
|
cfg: swc_ecma_codegen::config::Config::default(),
|
||||||
|
cm,
|
||||||
|
file: fm.clone(),
|
||||||
|
enable_comments: true,
|
||||||
|
srcmap: SourceMapBuilder::new(Some(&src)),
|
||||||
|
wr: box swc_ecma_codegen::text_writer::WriterWrapper::new("\n", &mut wr),
|
||||||
|
handlers,
|
||||||
|
pos_of_leading_comments: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
emitter.emit_module(&module).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let r = wr.0.read().unwrap();
|
||||||
|
let s = String::from_utf8_lossy(&*r);
|
||||||
|
s.to_string()
|
||||||
|
});
|
||||||
|
|
||||||
|
out.result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test transformation.
|
||||||
|
#[cfg(test)]
|
||||||
|
macro_rules! test {
|
||||||
|
($tr:expr, $test_name:ident, $input:expr, $expected:expr) => {
|
||||||
|
#[test]
|
||||||
|
fn $test_name() {
|
||||||
|
let actual = $crate::macros::apply_transform($tr, stringify!($test_name), $input);
|
||||||
|
let expected = $crate::macros::apply_transform(
|
||||||
|
$crate::macros::Noop,
|
||||||
|
stringify!($test_name),
|
||||||
|
$expected,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq_ignore_span!(actual, expected);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test transformation.
|
||||||
|
#[cfg(test)]
|
||||||
|
macro_rules! test_exec {
|
||||||
|
($tr:expr, $test_name:ident, $input:expr) => {
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn $test_name() {
|
||||||
|
let _transformed = $crate::macros::apply_transform($tr, stringify!($test_name), $input);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct Buf(Arc<RwLock<Vec<u8>>>);
|
||||||
|
impl Write for Buf {
|
||||||
|
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
|
||||||
|
self.0.write().unwrap().write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
self.0.write().unwrap().flush()
|
||||||
|
}
|
||||||
|
}
|
79
ecmascript/transforms/src/quote.rs
Normal file
79
ecmascript/transforms/src/quote.rs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
#[macro_export]
|
||||||
|
macro_rules! quote_ident {
|
||||||
|
($span:expr, $s:expr) => {{
|
||||||
|
::swc_ecma_ast::Ident::new($s.into(), $span)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! expr {
|
||||||
|
($span:expr, null) => {{
|
||||||
|
use swc_ecma_ast::*;
|
||||||
|
Expr::Lit(Lit::Null(Null { span: $span }))
|
||||||
|
}};
|
||||||
|
|
||||||
|
($span:expr, undefined) => {{
|
||||||
|
box Expr::Ident(Ident::new(js_word!("undefined"), $span))
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns Box<Expr>
|
||||||
|
macro_rules! member_expr {
|
||||||
|
($span:expr, $first:ident) => {{
|
||||||
|
use swc_ecma_ast::*;
|
||||||
|
box Expr::Ident(Ident::new(stringify!($first).into(), $span))
|
||||||
|
}};
|
||||||
|
|
||||||
|
($span:expr, $first:ident . $($rest:tt)+) => {{
|
||||||
|
let obj = member_expr!($span, $first);
|
||||||
|
|
||||||
|
member_expr!(@EXT, $span, obj, $($rest)* )
|
||||||
|
}};
|
||||||
|
|
||||||
|
(@EXT, $span:expr, $obj:expr, $first:ident . $($rest:tt)* ) => {{
|
||||||
|
let prop = member_expr!($span, $first);
|
||||||
|
|
||||||
|
member_expr!(@EXT, $span, box Expr::Member(MemberExpr{
|
||||||
|
span: $span,
|
||||||
|
obj: ExprOrSuper::Expr($obj),
|
||||||
|
computed: false,
|
||||||
|
prop,
|
||||||
|
}), $($rest)*)
|
||||||
|
}};
|
||||||
|
|
||||||
|
(@EXT, $span:expr, $obj:expr, $first:ident) => {{
|
||||||
|
use swc_ecma_ast::*;
|
||||||
|
let prop = member_expr!($span, $first);
|
||||||
|
|
||||||
|
box Expr::Member(MemberExpr{
|
||||||
|
span: $span,
|
||||||
|
obj: ExprOrSuper::Expr($obj),
|
||||||
|
computed: false,
|
||||||
|
prop,
|
||||||
|
})
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use swc_common::DUMMY_SP as span;
|
||||||
|
use swc_ecma_ast::*;
|
||||||
|
#[test]
|
||||||
|
fn quote_member_expr() {
|
||||||
|
assert_eq_ignore_span!(
|
||||||
|
member_expr!(span, Function.prototype.bind),
|
||||||
|
box Expr::Member(MemberExpr {
|
||||||
|
span,
|
||||||
|
obj: ExprOrSuper::Expr(box Expr::Member(MemberExpr {
|
||||||
|
span,
|
||||||
|
obj: ExprOrSuper::Expr(member_expr!(span, Function)),
|
||||||
|
computed: false,
|
||||||
|
prop: member_expr!(span, prototype),
|
||||||
|
})),
|
||||||
|
computed: false,
|
||||||
|
prop: member_expr!(span, bind),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
50
ecmascript/transforms/src/scope.rs
Normal file
50
ecmascript/transforms/src/scope.rs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
use swc_common::{FoldWith, Folder};
|
||||||
|
use swc_ecma_ast::*;
|
||||||
|
|
||||||
|
pub trait FoldScope<T> {
|
||||||
|
/// `scope`: Scope which contains `node`.
|
||||||
|
fn fold_scope(&mut self, scope: &mut Scope, node: T) -> T;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ScopeFolder<'a, F> {
|
||||||
|
pub folder: F,
|
||||||
|
pub cur_scope: &'a mut Scope<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, F, T> Folder<T> for ScopeFolder<'a, F>
|
||||||
|
where
|
||||||
|
F: FoldScope<T>,
|
||||||
|
T: FoldWith<F> + FoldWith<ScopeFolder<'a, F>>,
|
||||||
|
{
|
||||||
|
default fn fold(&mut self, node: T) -> T {
|
||||||
|
self.folder.fold_scope(self.cur_scope, node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct Scope<'a> {
|
||||||
|
pub parent: Option<&'a Scope<'a>>,
|
||||||
|
pub kind: ScopeKind,
|
||||||
|
pub children: Vec<Scope<'a>>,
|
||||||
|
pub bindings: Vec<&'a str>,
|
||||||
|
pub refs: Vec<&'a str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Scope<'a> {
|
||||||
|
pub fn from_fn(parent: Option<&'a Scope<'a>>, f: &'a Function) -> Self {
|
||||||
|
Scope {
|
||||||
|
parent,
|
||||||
|
kind: ScopeKind::Fn,
|
||||||
|
children: vec![],
|
||||||
|
bindings: vec![],
|
||||||
|
refs: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum ScopeKind {
|
||||||
|
Fn,
|
||||||
|
Block,
|
||||||
|
}
|
710
ecmascript/transforms/src/simplify/expr.rs
Normal file
710
ecmascript/transforms/src/simplify/expr.rs
Normal file
@ -0,0 +1,710 @@
|
|||||||
|
use super::Simplify;
|
||||||
|
use std::iter;
|
||||||
|
use swc_common::{FoldWith, Folder, Span, Spanned, DUMMY_SP};
|
||||||
|
use swc_ecma_ast::{Ident, Lit, *};
|
||||||
|
use util::*;
|
||||||
|
|
||||||
|
impl Folder<Expr> for Simplify {
|
||||||
|
fn fold(&mut self, expr: Expr) -> Expr {
|
||||||
|
// fold children nodes.
|
||||||
|
let expr = expr.fold_children(self);
|
||||||
|
|
||||||
|
match expr {
|
||||||
|
// Do nothing.
|
||||||
|
Expr::Lit(_) | Expr::This(..) => expr,
|
||||||
|
|
||||||
|
// Remove parenthesis. This may break ast, but it will be fixed up later.
|
||||||
|
Expr::Paren(ParenExpr { expr, .. }) => *expr,
|
||||||
|
|
||||||
|
Expr::Unary(expr) => fold_unary(expr),
|
||||||
|
Expr::Bin(expr) => fold_bin(expr),
|
||||||
|
|
||||||
|
Expr::Member(e) => fold_member_expr(e),
|
||||||
|
|
||||||
|
Expr::Cond(CondExpr {
|
||||||
|
span,
|
||||||
|
test,
|
||||||
|
cons,
|
||||||
|
alt,
|
||||||
|
}) => match test.as_bool() {
|
||||||
|
(p, Known(val)) => {
|
||||||
|
let expr_value = if val { cons } else { alt };
|
||||||
|
if p.is_pure() {
|
||||||
|
*expr_value
|
||||||
|
} else {
|
||||||
|
Expr::Seq(SeqExpr {
|
||||||
|
span,
|
||||||
|
exprs: vec![test, expr_value],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Expr::Cond(CondExpr {
|
||||||
|
span,
|
||||||
|
test,
|
||||||
|
cons,
|
||||||
|
alt,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
|
||||||
|
// Simplify sequence expression.
|
||||||
|
Expr::Seq(SeqExpr { span, exprs }) => {
|
||||||
|
if exprs.len() == 1 {
|
||||||
|
//TODO: Respan
|
||||||
|
*exprs.into_iter().next().unwrap()
|
||||||
|
} else {
|
||||||
|
assert!(!exprs.is_empty(), "sequence expression should not be empty");
|
||||||
|
//TODO: remove unused
|
||||||
|
return Expr::Seq(SeqExpr { span, exprs });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// be conservative.
|
||||||
|
_ => expr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fold_member_expr(e: MemberExpr) -> Expr {
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
enum KnownOp {
|
||||||
|
/// Length
|
||||||
|
Len,
|
||||||
|
Index(u32),
|
||||||
|
}
|
||||||
|
let op = match *e.prop {
|
||||||
|
Expr::Ident(Ident {
|
||||||
|
sym: js_word!("length"),
|
||||||
|
..
|
||||||
|
}) => KnownOp::Len,
|
||||||
|
// Lit(Lit::Num(Number(f)))=>{
|
||||||
|
// if f==0{
|
||||||
|
|
||||||
|
// }else{
|
||||||
|
|
||||||
|
// }
|
||||||
|
// // TODO: Report error
|
||||||
|
// KnownOp::Index(f)},
|
||||||
|
_ => return Expr::Member(e),
|
||||||
|
};
|
||||||
|
|
||||||
|
let o = match e.obj {
|
||||||
|
ExprOrSuper::Super(_) => return Expr::Member(e),
|
||||||
|
ExprOrSuper::Expr(box o) => o,
|
||||||
|
};
|
||||||
|
|
||||||
|
match o {
|
||||||
|
Expr::Lit(Lit::Str(Str {
|
||||||
|
ref value, span, ..
|
||||||
|
}))
|
||||||
|
if op == KnownOp::Len =>
|
||||||
|
{
|
||||||
|
Expr::Lit(Lit::Num(Number {
|
||||||
|
value: value.len() as _,
|
||||||
|
span,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::Array(ArrayLit { ref elems, span })
|
||||||
|
if op == KnownOp::Len && !o.may_have_side_effects() =>
|
||||||
|
{
|
||||||
|
Expr::Lit(Lit::Num(Number {
|
||||||
|
value: elems.len() as _,
|
||||||
|
span,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Expr::Member(MemberExpr {
|
||||||
|
obj: ExprOrSuper::Expr(box o),
|
||||||
|
..e
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fold_bin(
|
||||||
|
BinExpr {
|
||||||
|
left,
|
||||||
|
op,
|
||||||
|
right,
|
||||||
|
span,
|
||||||
|
}: BinExpr,
|
||||||
|
) -> Expr {
|
||||||
|
macro_rules! try_replace {
|
||||||
|
($v:expr) => {{
|
||||||
|
match $v {
|
||||||
|
Known(v) => {
|
||||||
|
// TODO: Optimize
|
||||||
|
|
||||||
|
return make_bool_expr(span, v, { iter::once(left).chain(iter::once(right)) });
|
||||||
|
}
|
||||||
|
_ => (left, right),
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
(number, $v:expr) => {{
|
||||||
|
match $v {
|
||||||
|
Known(v) => {
|
||||||
|
return preserve_effects(
|
||||||
|
span,
|
||||||
|
Expr::Lit(Lit::Num(Number {
|
||||||
|
value: v,
|
||||||
|
span: DUMMY_SP,
|
||||||
|
})),
|
||||||
|
{ iter::once(left).chain(iter::once(right)) },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => (left, right),
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
let (left, right) = match op {
|
||||||
|
op!(bin, "+") => {
|
||||||
|
// It's string concatenation if either left or right is string.
|
||||||
|
|
||||||
|
let mut bin = Expr::Bin(BinExpr {
|
||||||
|
span,
|
||||||
|
left,
|
||||||
|
op: op!(bin, "+"),
|
||||||
|
right,
|
||||||
|
});
|
||||||
|
|
||||||
|
match bin.get_type() {
|
||||||
|
Known(BoolType) | Known(NullType) | Known(NumberType) | Known(UndefinedType) => {
|
||||||
|
bin = match bin {
|
||||||
|
Expr::Bin(BinExpr {
|
||||||
|
left,
|
||||||
|
op: _,
|
||||||
|
right,
|
||||||
|
span,
|
||||||
|
}) => match perform_arithmetic_op(op!(bin, "+"), &left, &right) {
|
||||||
|
Known(v) => {
|
||||||
|
return preserve_effects(
|
||||||
|
span,
|
||||||
|
Expr::Lit(Lit::Num(Number { value: v, span })),
|
||||||
|
{ iter::once(left).chain(iter::once(right)) },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => Expr::Bin(BinExpr {
|
||||||
|
span,
|
||||||
|
left,
|
||||||
|
op: op!(bin, "+"),
|
||||||
|
right,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Known(StringType) => {}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: try string concat
|
||||||
|
return bin;
|
||||||
|
}
|
||||||
|
|
||||||
|
op!("&&") | op!("||") => match left.as_bool() {
|
||||||
|
(_, Known(val)) => {
|
||||||
|
let node = if op == op!("&&") {
|
||||||
|
if val {
|
||||||
|
// 1 && $right
|
||||||
|
right
|
||||||
|
} else {
|
||||||
|
// 0 && $right
|
||||||
|
return *left;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if val {
|
||||||
|
// 1 || $right
|
||||||
|
return *left;
|
||||||
|
} else {
|
||||||
|
// 0 || $right
|
||||||
|
right
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return if !left.may_have_side_effects() {
|
||||||
|
*node
|
||||||
|
} else {
|
||||||
|
Expr::Seq(SeqExpr {
|
||||||
|
span,
|
||||||
|
exprs: vec![left, node],
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
_ => (left, right),
|
||||||
|
},
|
||||||
|
op!("instanceof") => {
|
||||||
|
fn is_non_obj(e: &Expr) -> bool {
|
||||||
|
match *e {
|
||||||
|
// Non-object types are never instances.
|
||||||
|
Expr::Lit(Lit::Str { .. })
|
||||||
|
| Expr::Lit(Lit::Num(..))
|
||||||
|
| Expr::Lit(Lit::Null(..))
|
||||||
|
| Expr::Lit(Lit::Bool(..))
|
||||||
|
| Expr::Ident(Ident {
|
||||||
|
sym: js_word!("undefined"),
|
||||||
|
..
|
||||||
|
})
|
||||||
|
| Expr::Ident(Ident {
|
||||||
|
sym: js_word!("Infinity"),
|
||||||
|
..
|
||||||
|
})
|
||||||
|
| Expr::Ident(Ident {
|
||||||
|
sym: js_word!("NaN"),
|
||||||
|
..
|
||||||
|
}) => true,
|
||||||
|
|
||||||
|
Expr::Unary(UnaryExpr {
|
||||||
|
op: op!("!"),
|
||||||
|
ref arg,
|
||||||
|
..
|
||||||
|
})
|
||||||
|
| Expr::Unary(UnaryExpr {
|
||||||
|
op: op!(unary, "-"),
|
||||||
|
ref arg,
|
||||||
|
..
|
||||||
|
})
|
||||||
|
| Expr::Unary(UnaryExpr {
|
||||||
|
op: op!("void"),
|
||||||
|
ref arg,
|
||||||
|
..
|
||||||
|
}) => is_non_obj(&arg),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-object types are never instances.
|
||||||
|
if is_non_obj(&left) {
|
||||||
|
return make_bool_expr(span, false, iter::once(right));
|
||||||
|
}
|
||||||
|
|
||||||
|
if right.is_ident_ref_to(js_word!("Object")) {
|
||||||
|
return make_bool_expr(span, true, iter::once(left));
|
||||||
|
}
|
||||||
|
|
||||||
|
(left, right)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arithmetic operations
|
||||||
|
op!(bin, "-") | op!("/") | op!("%") => {
|
||||||
|
try_replace!(number, perform_arithmetic_op(op, &left, &right))
|
||||||
|
}
|
||||||
|
|
||||||
|
// These needs one more check.
|
||||||
|
//
|
||||||
|
// (a * 1) * 2 --> a * (1 * 2) --> a * 2
|
||||||
|
op!("*") | op!("&") | op!("|") | op!("^") => {
|
||||||
|
let (left, right) = try_replace!(number, perform_arithmetic_op(op, &left, &right));
|
||||||
|
// TODO: Try left.rhs * right
|
||||||
|
(left, right)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparisons
|
||||||
|
op!("<") => try_replace!(perform_abstract_rel_cmp(span, &left, &right, false)),
|
||||||
|
op!(">") => try_replace!(perform_abstract_rel_cmp(span, &right, &left, false)),
|
||||||
|
op!("<=") => try_replace!(!perform_abstract_rel_cmp(span, &right, &left, true)),
|
||||||
|
op!(">=") => try_replace!(!perform_abstract_rel_cmp(span, &left, &right, true)),
|
||||||
|
|
||||||
|
op!("==") => try_replace!(perform_abstract_eq_cmp(span, &left, &right)),
|
||||||
|
op!("!=") => try_replace!(!perform_abstract_eq_cmp(span, &left, &right)),
|
||||||
|
op!("===") => try_replace!(perform_strict_eq_cmp(span, &left, &right)),
|
||||||
|
op!("!==") => try_replace!(!perform_strict_eq_cmp(span, &left, &right)),
|
||||||
|
_ => (left, right),
|
||||||
|
};
|
||||||
|
|
||||||
|
Expr::Bin(BinExpr {
|
||||||
|
left,
|
||||||
|
op,
|
||||||
|
right,
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fold_unary(UnaryExpr { span, op, arg }: UnaryExpr) -> Expr {
|
||||||
|
let may_have_side_effects = arg.may_have_side_effects();
|
||||||
|
|
||||||
|
match op {
|
||||||
|
op!("typeof") if !may_have_side_effects => {
|
||||||
|
let val = match *arg {
|
||||||
|
Expr::Fn(..) => "function",
|
||||||
|
Expr::Lit(Lit::Str { .. }) => "string",
|
||||||
|
Expr::Lit(Lit::Num(..)) => "number",
|
||||||
|
Expr::Lit(Lit::Bool(..)) => "boolean",
|
||||||
|
Expr::Lit(Lit::Null(..)) | Expr::Object { .. } | Expr::Array { .. } => "object",
|
||||||
|
Expr::Unary(UnaryExpr {
|
||||||
|
op: op!("void"), ..
|
||||||
|
})
|
||||||
|
| Expr::Ident(Ident {
|
||||||
|
sym: js_word!("undefined"),
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
// We can assume `undefined` is `undefined`,
|
||||||
|
// because overriding `undefined` is always hard error in swc.
|
||||||
|
"undefined"
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
return Expr::Unary(UnaryExpr {
|
||||||
|
op: op!("typeof"),
|
||||||
|
arg,
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return Expr::Lit(Lit::Str(Str {
|
||||||
|
span,
|
||||||
|
value: val.into(),
|
||||||
|
has_escape: false,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
op!("!") => match arg.as_bool() {
|
||||||
|
(_, Known(val)) => return make_bool_expr(span, !val, iter::once(arg)),
|
||||||
|
_ => return Expr::Unary(UnaryExpr { op, arg, span }),
|
||||||
|
},
|
||||||
|
op!(unary, "+") => match arg.as_number() {
|
||||||
|
Known(v) => {
|
||||||
|
return preserve_effects(
|
||||||
|
span,
|
||||||
|
Expr::Lit(Lit::Num(Number { value: v, span })),
|
||||||
|
iter::once(arg),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => return Expr::Unary(UnaryExpr { op, arg, span }),
|
||||||
|
},
|
||||||
|
op!(unary, "-") => match *arg {
|
||||||
|
Expr::Ident(Ident {
|
||||||
|
sym: js_word!("Infinity"),
|
||||||
|
span,
|
||||||
|
}) => return Expr::Unary(UnaryExpr { op, arg, span }),
|
||||||
|
// "-NaN" is "NaN"
|
||||||
|
Expr::Ident(Ident {
|
||||||
|
sym: js_word!("NaN"),
|
||||||
|
..
|
||||||
|
}) => return *arg,
|
||||||
|
Expr::Lit(Lit::Num(Number { value: f, .. })) => {
|
||||||
|
return Expr::Lit(Lit::Num(Number { value: -f, span }))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
|
||||||
|
// TODO: Report that user is something bad (negating non-number value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
op!("void") if !may_have_side_effects => {
|
||||||
|
return Expr::Unary(UnaryExpr {
|
||||||
|
op: op!("void"),
|
||||||
|
arg: box Expr::Lit(Lit::Num(Number {
|
||||||
|
value: 0.0,
|
||||||
|
span: arg.span(),
|
||||||
|
})),
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::Unary(UnaryExpr { op, arg, span })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try to fold arithmetic binary operators
|
||||||
|
fn perform_arithmetic_op(op: BinaryOp, left: &Expr, right: &Expr) -> Value<f64> {
|
||||||
|
let (lv, rv) = (left.as_number(), right.as_number());
|
||||||
|
|
||||||
|
if (lv.is_unknown() && rv.is_unknown())
|
||||||
|
|| !left.get_type().casted_to_number_on_add()
|
||||||
|
|| !right.get_type().casted_to_number_on_add()
|
||||||
|
{
|
||||||
|
return Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
match op {
|
||||||
|
op!(bin, "+") => {
|
||||||
|
if let (Known(lv), Known(rv)) = (lv, rv) {
|
||||||
|
return Known(lv + rv);
|
||||||
|
}
|
||||||
|
if lv == Known(0.0) {
|
||||||
|
return rv;
|
||||||
|
} else if rv == Known(0.0) {
|
||||||
|
return lv;
|
||||||
|
}
|
||||||
|
return Unknown;
|
||||||
|
}
|
||||||
|
op!(bin, "-") => {
|
||||||
|
if let (Known(lv), Known(rv)) = (lv, rv) {
|
||||||
|
return Known(lv - rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0 - x => -x
|
||||||
|
if lv == Known(0.0) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// x - 0 => x
|
||||||
|
if rv == Known(0.0) {
|
||||||
|
return lv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Unknown;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This actually performs `<`.
|
||||||
|
///
|
||||||
|
/// https://tc39.github.io/ecma262/#sec-abstract-relational-comparison
|
||||||
|
fn perform_abstract_rel_cmp(
|
||||||
|
_span: Span,
|
||||||
|
left: &Expr,
|
||||||
|
right: &Expr,
|
||||||
|
will_negate: bool,
|
||||||
|
) -> Value<bool> {
|
||||||
|
match (left, right) {
|
||||||
|
// Special case: `x < x` is always false.
|
||||||
|
(&Expr::Ident(Ident { sym: ref li, .. }, ..), &Expr::Ident(Ident { sym: ref ri, .. }))
|
||||||
|
if !will_negate && li == ri =>
|
||||||
|
{
|
||||||
|
return Known(false)
|
||||||
|
}
|
||||||
|
// Special case: `typeof a < typeof a` is always false.
|
||||||
|
(
|
||||||
|
&Expr::Unary(UnaryExpr {
|
||||||
|
op: op!("typeof"),
|
||||||
|
arg: box Expr::Ident(Ident { sym: ref li, .. }),
|
||||||
|
..
|
||||||
|
}),
|
||||||
|
&Expr::Unary(UnaryExpr {
|
||||||
|
op: op!("typeof"),
|
||||||
|
arg: box Expr::Ident(Ident { sym: ref ri, .. }),
|
||||||
|
..
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
if li == ri =>
|
||||||
|
{
|
||||||
|
return Known(false)
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to evaluate based on the general type.
|
||||||
|
let (lt, rt) = (left.get_type(), right.get_type());
|
||||||
|
|
||||||
|
match (lt, rt) {
|
||||||
|
(Known(StringType), Known(StringType)) => {
|
||||||
|
match (left.as_string(), right.as_string()) {
|
||||||
|
(Known(lv), Known(rv)) => {
|
||||||
|
// In JS, browsers parse \v differently. So do not compare strings if one
|
||||||
|
// contains \v.
|
||||||
|
if lv.contains('\u{000B}') || rv.contains('\u{000B}') {
|
||||||
|
return Unknown;
|
||||||
|
} else {
|
||||||
|
return Known(lv < rv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then, try to evaluate based on the value of the node. Try comparing as
|
||||||
|
// numbers.
|
||||||
|
let (lv, rv) = (left.as_number()?, right.as_number()?);
|
||||||
|
if lv.is_nan() || rv.is_nan() {
|
||||||
|
return Known(will_negate);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Known(lv < rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://tc39.github.io/ecma262/#sec-abstract-equality-comparison
|
||||||
|
fn perform_abstract_eq_cmp(span: Span, left: &Expr, right: &Expr) -> Value<bool> {
|
||||||
|
let (lt, rt) = (left.get_type()?, right.get_type()?);
|
||||||
|
|
||||||
|
if lt == rt {
|
||||||
|
return perform_strict_eq_cmp(span, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
match (lt, rt) {
|
||||||
|
(NullType, UndefinedType) | (UndefinedType, NullType) => return Known(true),
|
||||||
|
(NumberType, StringType) | (_, BoolType) => {
|
||||||
|
let rv = right.as_number()?;
|
||||||
|
return perform_abstract_eq_cmp(
|
||||||
|
span,
|
||||||
|
left,
|
||||||
|
&Expr::Lit(Lit::Num(Number { value: rv, span })),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
(StringType, NumberType) | (BoolType, _) => {
|
||||||
|
let lv = left.as_number()?;
|
||||||
|
return perform_abstract_eq_cmp(
|
||||||
|
span,
|
||||||
|
&Expr::Lit(Lit::Num(Number { value: lv, span })),
|
||||||
|
right,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
(StringType, ObjectType)
|
||||||
|
| (NumberType, ObjectType)
|
||||||
|
| (ObjectType, StringType)
|
||||||
|
| (ObjectType, NumberType) => return Unknown,
|
||||||
|
|
||||||
|
_ => return Known(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://tc39.github.io/ecma262/#sec-strict-equality-comparison
|
||||||
|
fn perform_strict_eq_cmp(_span: Span, left: &Expr, right: &Expr) -> Value<bool> {
|
||||||
|
// Any strict equality comparison against NaN returns false.
|
||||||
|
if left.is_nan() || right.is_nan() {
|
||||||
|
return Known(false);
|
||||||
|
}
|
||||||
|
match (left, right) {
|
||||||
|
// Special case, typeof a == typeof a is always true.
|
||||||
|
(
|
||||||
|
&Expr::Unary(UnaryExpr {
|
||||||
|
op: op!("typeof"),
|
||||||
|
arg: box Expr::Ident(Ident { sym: ref li, .. }),
|
||||||
|
..
|
||||||
|
}),
|
||||||
|
&Expr::Unary(UnaryExpr {
|
||||||
|
op: op!("typeof"),
|
||||||
|
arg: box Expr::Ident(Ident { sym: ref ri, .. }),
|
||||||
|
..
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
if li == ri =>
|
||||||
|
{
|
||||||
|
return Known(true)
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (lt, rt) = (left.get_type()?, right.get_type()?);
|
||||||
|
// Strict equality can only be true for values of the same type.
|
||||||
|
if lt != rt {
|
||||||
|
return Known(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
match lt {
|
||||||
|
UndefinedType | NullType => return Known(true),
|
||||||
|
NumberType => {
|
||||||
|
return Known(left.as_number()? == right.as_number()?);
|
||||||
|
}
|
||||||
|
StringType => {
|
||||||
|
let (lv, rv) = (left.as_string()?, right.as_string()?);
|
||||||
|
// In JS, browsers parse \v differently. So do not consider strings
|
||||||
|
// equal if one contains \v.
|
||||||
|
if lv.contains('\u{000B}') || rv.contains('\u{000B}') {
|
||||||
|
return Unknown;
|
||||||
|
}
|
||||||
|
return Known(lv == rv);
|
||||||
|
}
|
||||||
|
BoolType => {
|
||||||
|
let (lv, rv) = (left.as_pure_bool(), right.as_pure_bool());
|
||||||
|
|
||||||
|
// lv && rv || !lv && !rv
|
||||||
|
|
||||||
|
return lv.and(rv).or((!lv).and(!rv));
|
||||||
|
}
|
||||||
|
ObjectType | SymbolType => return Unknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// make a new boolean expression preserving side effects, if any.
|
||||||
|
fn make_bool_expr<I>(span: Span, value: bool, orig: I) -> Expr
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = Box<Expr>>,
|
||||||
|
{
|
||||||
|
preserve_effects(span, Expr::Lit(Lit::Bool(Bool { value, span })), orig)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// make a new expression which evaluates `val` preserving side effects, if any.
|
||||||
|
fn preserve_effects<I>(span: Span, val: Expr, exprs: I) -> Expr
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = Box<Expr>>,
|
||||||
|
{
|
||||||
|
/// Add side effects of `expr` to `v`
|
||||||
|
/// preserving order and conditions. (think a() ? yield b() : c())
|
||||||
|
fn add_effects(v: &mut Vec<Box<Expr>>, box expr: Box<Expr>) {
|
||||||
|
match expr {
|
||||||
|
Expr::Lit(..) | Expr::This(..) | Expr::Fn(..) | Expr::Arrow(..) | Expr::Ident(..) => {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// In most case, we can do nothing for this.
|
||||||
|
Expr::Update(_) | Expr::Assign(_) | Expr::Yield(_) | Expr::Await(_) => v.push(box expr),
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
Expr::MetaProp(_) => v.push(box expr),
|
||||||
|
|
||||||
|
Expr::Call(_) => v.push(box expr),
|
||||||
|
Expr::New(_) => v.push(box expr),
|
||||||
|
Expr::Member(_) => v.push(box expr),
|
||||||
|
|
||||||
|
// We are at here because we could not determine value of test.
|
||||||
|
//TODO: Drop values if it does not have side effects.
|
||||||
|
Expr::Cond(_) => v.push(box expr),
|
||||||
|
|
||||||
|
Expr::Unary(UnaryExpr { arg, .. }) => add_effects(v, arg),
|
||||||
|
Expr::Bin(BinExpr { left, right, .. }) => {
|
||||||
|
add_effects(v, left);
|
||||||
|
add_effects(v, right);
|
||||||
|
}
|
||||||
|
Expr::Seq(SeqExpr { exprs, .. }) => exprs.into_iter().for_each(|e| add_effects(v, e)),
|
||||||
|
|
||||||
|
Expr::Paren(e) => add_effects(v, e.expr),
|
||||||
|
|
||||||
|
Expr::Object(ObjectLit { props, .. }) => {
|
||||||
|
props.into_iter().for_each(|node| match node {
|
||||||
|
Prop::Shorthand(..) => return,
|
||||||
|
Prop::KeyValue(KeyValueProp { key, value }) => {
|
||||||
|
match key {
|
||||||
|
PropName::Computed(e) => add_effects(v, e),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
add_effects(v, value)
|
||||||
|
}
|
||||||
|
Prop::Getter(GetterProp { key, .. })
|
||||||
|
| Prop::Setter(SetterProp { key, .. })
|
||||||
|
| Prop::Method(MethodProp { key, .. }) => match key {
|
||||||
|
PropName::Computed(e) => add_effects(v, e),
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
Prop::Assign(..) => {
|
||||||
|
unreachable!("assign property in object literal is not a valid syntax")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::Array(ArrayLit { elems, .. }) => {
|
||||||
|
elems.into_iter().filter_map(|e| e).fold(v, |v, e| {
|
||||||
|
add_effects(v, e.expr);
|
||||||
|
|
||||||
|
v
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::Tpl { .. } => unimplemented!("add_effects for template literal"),
|
||||||
|
Expr::Class(ClassExpr { .. }) => unimplemented!("add_effects for class expression"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut exprs = exprs.into_iter().fold(vec![], |mut v, e| {
|
||||||
|
add_effects(&mut v, e);
|
||||||
|
v
|
||||||
|
});
|
||||||
|
|
||||||
|
if exprs.is_empty() {
|
||||||
|
return val;
|
||||||
|
} else {
|
||||||
|
exprs.push(box val);
|
||||||
|
|
||||||
|
Expr::Seq(SeqExpr { exprs, span })
|
||||||
|
}
|
||||||
|
}
|
198
ecmascript/transforms/src/simplify/mod.rs
Normal file
198
ecmascript/transforms/src/simplify/mod.rs
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
//! Ported from closure compiler.
|
||||||
|
use swc_common::{FoldWith, Folder, DUMMY_SP};
|
||||||
|
use swc_ecma_ast::*;
|
||||||
|
use util::*;
|
||||||
|
|
||||||
|
mod expr;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct Simplify;
|
||||||
|
|
||||||
|
impl<T: StmtLike> Folder<Vec<T>> for Simplify
|
||||||
|
where
|
||||||
|
Self: Folder<T>,
|
||||||
|
{
|
||||||
|
fn fold(&mut self, stmts: Vec<T>) -> Vec<T> {
|
||||||
|
let mut buf = Vec::with_capacity(stmts.len());
|
||||||
|
|
||||||
|
for stmt_like in stmts {
|
||||||
|
let stmt_like = self.fold(stmt_like);
|
||||||
|
let stmt_like = match stmt_like.try_into_stmt() {
|
||||||
|
Ok(stmt) => {
|
||||||
|
let stmt = match stmt {
|
||||||
|
// Remove empty statements.
|
||||||
|
Stmt::Empty(..) => continue,
|
||||||
|
|
||||||
|
// Control flow
|
||||||
|
Stmt::Throw(..)
|
||||||
|
| Stmt::Return { .. }
|
||||||
|
| Stmt::Continue { .. }
|
||||||
|
| Stmt::Break { .. } => {
|
||||||
|
let stmt_like = T::from_stmt(stmt);
|
||||||
|
buf.push(stmt_like);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
// Optimize if statement.
|
||||||
|
Stmt::If(IfStmt {
|
||||||
|
test,
|
||||||
|
cons,
|
||||||
|
alt,
|
||||||
|
span,
|
||||||
|
}) => {
|
||||||
|
// check if
|
||||||
|
let node = match test.as_bool() {
|
||||||
|
(Pure, Known(val)) => {
|
||||||
|
if val {
|
||||||
|
*cons
|
||||||
|
} else {
|
||||||
|
alt.map(|e| *e).unwrap_or(Stmt::Empty(EmptyStmt { span }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: Impure
|
||||||
|
_ => Stmt::If(IfStmt {
|
||||||
|
test,
|
||||||
|
cons,
|
||||||
|
alt,
|
||||||
|
span,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
node
|
||||||
|
}
|
||||||
|
_ => stmt,
|
||||||
|
};
|
||||||
|
|
||||||
|
T::from_stmt(stmt)
|
||||||
|
}
|
||||||
|
Err(stmt_like) => stmt_like,
|
||||||
|
};
|
||||||
|
|
||||||
|
buf.push(stmt_like);
|
||||||
|
}
|
||||||
|
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Folder<Stmt> for Simplify {
|
||||||
|
fn fold(&mut self, stmt: Stmt) -> Stmt {
|
||||||
|
let stmt = stmt.fold_children(self);
|
||||||
|
|
||||||
|
match stmt {
|
||||||
|
// `1;` -> `;`
|
||||||
|
Stmt::Expr(box node) => match node {
|
||||||
|
Expr::Lit(Lit::Num(..)) | Expr::Lit(Lit::Bool(..)) | Expr::Lit(Lit::Regex(..)) => {
|
||||||
|
Stmt::Empty(EmptyStmt { span: DUMMY_SP })
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Function expressions are useless if they are not used.
|
||||||
|
//
|
||||||
|
// As function expressions cannot start with 'function',
|
||||||
|
// this will be reached only if other things
|
||||||
|
// are removed while folding chilren.
|
||||||
|
Expr::Fn(FnExpr {
|
||||||
|
function: Function { span, .. },
|
||||||
|
..
|
||||||
|
}) => Stmt::Empty(EmptyStmt { span }),
|
||||||
|
_ => Stmt::Expr(box node),
|
||||||
|
},
|
||||||
|
|
||||||
|
Stmt::Block(BlockStmt { span, stmts }) => {
|
||||||
|
if stmts.len() == 0 {
|
||||||
|
return Stmt::Empty(EmptyStmt { span });
|
||||||
|
} else if stmts.len() == 1 {
|
||||||
|
// TODO: Check if lexical variable exists.
|
||||||
|
return stmts.into_iter().next().unwrap();
|
||||||
|
} else {
|
||||||
|
Stmt::Block(BlockStmt { span, stmts })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Stmt::Try(TryStmt {
|
||||||
|
span,
|
||||||
|
block,
|
||||||
|
handler,
|
||||||
|
finalizer,
|
||||||
|
}) => {
|
||||||
|
// Only leave the finally block if try block is empty
|
||||||
|
if block.is_empty() {
|
||||||
|
return finalizer
|
||||||
|
.map(Stmt::Block)
|
||||||
|
.unwrap_or(Stmt::Empty(EmptyStmt { span }));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If catch block and finally block is empty, remove try-catch is useless.
|
||||||
|
if handler.is_empty() && finalizer.is_empty() {
|
||||||
|
return Stmt::Block(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
Stmt::Try(TryStmt {
|
||||||
|
span,
|
||||||
|
block,
|
||||||
|
handler,
|
||||||
|
finalizer,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove empty else block.
|
||||||
|
// As we fold children before parent, unused expression
|
||||||
|
// statements without side effects are converted to
|
||||||
|
// Stmt::Empty before here.
|
||||||
|
Stmt::If(IfStmt {
|
||||||
|
span,
|
||||||
|
test,
|
||||||
|
cons,
|
||||||
|
alt,
|
||||||
|
}) => {
|
||||||
|
if alt.is_empty() {
|
||||||
|
return Stmt::If(IfStmt {
|
||||||
|
span,
|
||||||
|
test,
|
||||||
|
cons,
|
||||||
|
alt: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Stmt::If(IfStmt {
|
||||||
|
span,
|
||||||
|
test,
|
||||||
|
cons,
|
||||||
|
alt,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => stmt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait StmtLike: Sized {
|
||||||
|
fn try_into_stmt(self) -> Result<Stmt, Self>;
|
||||||
|
fn from_stmt(stmt: Stmt) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StmtLike for Stmt {
|
||||||
|
fn try_into_stmt(self) -> Result<Stmt, Self> {
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
fn from_stmt(stmt: Stmt) -> Self {
|
||||||
|
stmt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StmtLike for ModuleItem {
|
||||||
|
fn try_into_stmt(self) -> Result<Stmt, Self> {
|
||||||
|
match self {
|
||||||
|
ModuleItem::Stmt(stmt) => Ok(stmt),
|
||||||
|
_ => Err(self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn from_stmt(stmt: Stmt) -> Self {
|
||||||
|
ModuleItem::Stmt(stmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// impl Folder<Stmt> for Simplify {
|
||||||
|
// fn fold(&mut self, stmt: Stmt) -> Stmt {
|
||||||
|
// stmt.fold_children(&mut FoldConst)
|
||||||
|
// }
|
||||||
|
// }
|
42
ecmascript/transforms/src/util/factory.rs
Normal file
42
ecmascript/transforms/src/util/factory.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
use swc_common::{Span, Spanned};
|
||||||
|
use swc_ecma_ast::*;
|
||||||
|
|
||||||
|
pub(crate) trait ExprFactory: Into<Expr> {
|
||||||
|
fn as_arg(self) -> ExprOrSpread {
|
||||||
|
ExprOrSpread {
|
||||||
|
expr: box self.into(),
|
||||||
|
spread: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply(self, span: Span, this: Box<Expr>, args: Vec<ExprOrSpread>) -> Expr {
|
||||||
|
let apply = Expr::Member(MemberExpr {
|
||||||
|
// TODO
|
||||||
|
span,
|
||||||
|
|
||||||
|
obj: ExprOrSuper::Expr(box self.into()),
|
||||||
|
prop: box Expr::Ident(Ident::new(js_word!("apply"), span)),
|
||||||
|
computed: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
Expr::Call(CallExpr {
|
||||||
|
span,
|
||||||
|
callee: ExprOrSuper::Expr(box apply),
|
||||||
|
args: vec![ExprOrSpread {
|
||||||
|
expr: this,
|
||||||
|
spread: None,
|
||||||
|
}]
|
||||||
|
.into_iter()
|
||||||
|
.chain(args)
|
||||||
|
.collect(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrap_with_paren(self) -> Expr {
|
||||||
|
let expr = box self.into();
|
||||||
|
let span = expr.span();
|
||||||
|
Expr::Paren(ParenExpr { expr, span })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Into<Expr>> ExprFactory for T {}
|
595
ecmascript/transforms/src/util/mod.rs
Normal file
595
ecmascript/transforms/src/util/mod.rs
Normal file
@ -0,0 +1,595 @@
|
|||||||
|
pub(crate) use self::{
|
||||||
|
factory::ExprFactory,
|
||||||
|
value::{
|
||||||
|
Type::{
|
||||||
|
self, Bool as BoolType, Null as NullType, Num as NumberType, Obj as ObjectType,
|
||||||
|
Str as StringType, Symbol as SymbolType, Undefined as UndefinedType,
|
||||||
|
},
|
||||||
|
Value::{self, Known, Unknown},
|
||||||
|
},
|
||||||
|
Purity::{MayBeImpure, Pure},
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
borrow::Cow,
|
||||||
|
f64::{INFINITY, NAN},
|
||||||
|
num::FpCategory,
|
||||||
|
ops::Add,
|
||||||
|
};
|
||||||
|
use swc_atoms::JsWord;
|
||||||
|
use swc_ecma_ast::*;
|
||||||
|
|
||||||
|
mod factory;
|
||||||
|
mod value;
|
||||||
|
|
||||||
|
pub type BoolValue = Value<bool>;
|
||||||
|
|
||||||
|
pub trait IsEmpty {
|
||||||
|
fn is_empty(&self) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IsEmpty for BlockStmt {
|
||||||
|
fn is_empty(&self) -> bool {
|
||||||
|
self.stmts.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl IsEmpty for CatchClause {
|
||||||
|
fn is_empty(&self) -> bool {
|
||||||
|
self.body.stmts.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl IsEmpty for Stmt {
|
||||||
|
fn is_empty(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Stmt::Empty(_) => true,
|
||||||
|
Stmt::Block(ref b) => b.is_empty(),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: IsEmpty> IsEmpty for Option<T> {
|
||||||
|
fn is_empty(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Some(ref node) => node.is_empty(),
|
||||||
|
None => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: IsEmpty> IsEmpty for Box<T> {
|
||||||
|
fn is_empty(&self) -> bool {
|
||||||
|
<T as IsEmpty>::is_empty(&*self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ExprExt: Sized {
|
||||||
|
fn as_expr_kind(&self) -> &Expr;
|
||||||
|
|
||||||
|
/// Checks if `self` is `NaN`.
|
||||||
|
fn is_nan(&self) -> bool {
|
||||||
|
self.is_ident_ref_to(js_word!("NaN"))
|
||||||
|
}
|
||||||
|
/// Is `self` an IdentifierReference to `id`?
|
||||||
|
fn is_ident_ref_to(&self, id: JsWord) -> bool {
|
||||||
|
match *self.as_expr_kind() {
|
||||||
|
Expr::Ident(Ident { ref sym, .. }) if *sym == id => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn as_pure_bool(&self) -> BoolValue {
|
||||||
|
match self.as_bool() {
|
||||||
|
(Pure, Known(b)) => Known(b),
|
||||||
|
_ => Unknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// This method emulates the `Boolean()` JavaScript cast function.
|
||||||
|
///Note: unlike getPureBooleanValue this function does not return `None`
|
||||||
|
///for expressions with side-effects.
|
||||||
|
fn as_bool(&self) -> (Purity, BoolValue) {
|
||||||
|
let expr = self.as_expr_kind();
|
||||||
|
let val = match *expr {
|
||||||
|
Expr::Paren(ref e) => return e.expr.as_bool(),
|
||||||
|
Expr::Seq(SeqExpr { ref exprs, .. }) => return exprs.last().unwrap().as_bool(),
|
||||||
|
Expr::Assign(AssignExpr { ref right, .. }) => return right.as_bool(),
|
||||||
|
|
||||||
|
Expr::Unary(UnaryExpr {
|
||||||
|
op: op!("!"),
|
||||||
|
ref arg,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
let (p, v) = arg.as_bool();
|
||||||
|
return (p, !v);
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::Bin(BinExpr {
|
||||||
|
ref left,
|
||||||
|
op: op @ op!("&"),
|
||||||
|
ref right,
|
||||||
|
..
|
||||||
|
})
|
||||||
|
| Expr::Bin(BinExpr {
|
||||||
|
ref left,
|
||||||
|
op: op @ op!("|"),
|
||||||
|
ref right,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
// TODO: Ignore purity if value cannot be reached.
|
||||||
|
|
||||||
|
let (lp, lv) = left.as_bool();
|
||||||
|
let (rp, rv) = right.as_bool();
|
||||||
|
|
||||||
|
if lp + rp == Pure {
|
||||||
|
return (Pure, lv.and(rv));
|
||||||
|
}
|
||||||
|
if op == op!("&") {
|
||||||
|
lv.and(rv)
|
||||||
|
} else {
|
||||||
|
lv.or(rv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::Fn(..) | Expr::Class(..) | Expr::New(..) | Expr::Array(..) | Expr::Object(..) => {
|
||||||
|
Known(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::Unary(UnaryExpr {
|
||||||
|
op: op!("void"),
|
||||||
|
arg: _,
|
||||||
|
..
|
||||||
|
}) => Known(false),
|
||||||
|
|
||||||
|
Expr::Lit(ref lit) => {
|
||||||
|
return (
|
||||||
|
Pure,
|
||||||
|
Known(match *lit {
|
||||||
|
Lit::Num(Number { value: n, .. }) => match n.classify() {
|
||||||
|
FpCategory::Nan | FpCategory::Zero => false,
|
||||||
|
_ => true,
|
||||||
|
},
|
||||||
|
Lit::Bool(b) => b.value,
|
||||||
|
Lit::Str(Str { ref value, .. }) => !value.is_empty(),
|
||||||
|
Lit::Null(..) => false,
|
||||||
|
Lit::Regex(..) => true,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO?
|
||||||
|
_ => Unknown,
|
||||||
|
};
|
||||||
|
|
||||||
|
(MayBeImpure, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Emulates javascript Number() cast function.
|
||||||
|
fn as_number(&self) -> Value<f64> {
|
||||||
|
let expr = self.as_expr_kind();
|
||||||
|
let v = match *expr {
|
||||||
|
Expr::Lit(ref l) => match *l {
|
||||||
|
Lit::Bool(Bool { value: true, .. }) => 1.0,
|
||||||
|
Lit::Bool(Bool { value: false, .. }) | Lit::Null(..) => 0.0,
|
||||||
|
Lit::Num(Number { value: n, .. }) => n,
|
||||||
|
Lit::Str(Str { ref value, .. }) => return num_from_str(value),
|
||||||
|
_ => return Unknown,
|
||||||
|
},
|
||||||
|
Expr::Ident(Ident { ref sym, .. }) => match &**sym {
|
||||||
|
"undefined" | "NaN" => NAN,
|
||||||
|
"Infinity" => INFINITY,
|
||||||
|
_ => return Unknown,
|
||||||
|
},
|
||||||
|
Expr::Unary(UnaryExpr {
|
||||||
|
op: op!(unary, "-"),
|
||||||
|
arg:
|
||||||
|
box Expr::Ident(Ident {
|
||||||
|
sym: js_word!("Infinity"),
|
||||||
|
..
|
||||||
|
}),
|
||||||
|
span: _,
|
||||||
|
}) => -INFINITY,
|
||||||
|
Expr::Unary(UnaryExpr {
|
||||||
|
op: op!("!"),
|
||||||
|
ref arg,
|
||||||
|
span: _,
|
||||||
|
}) => match arg.as_bool() {
|
||||||
|
(Pure, Known(v)) => {
|
||||||
|
if v {
|
||||||
|
0.0
|
||||||
|
} else {
|
||||||
|
1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => return Unknown,
|
||||||
|
},
|
||||||
|
Expr::Unary(UnaryExpr {
|
||||||
|
op: op!("void"),
|
||||||
|
ref arg,
|
||||||
|
span: _,
|
||||||
|
}) => {
|
||||||
|
if arg.may_have_side_effects() {
|
||||||
|
return Unknown;
|
||||||
|
} else {
|
||||||
|
NAN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::Tpl(..) | Expr::Object(ObjectLit { .. }) | Expr::Array(ArrayLit { .. }) => {
|
||||||
|
return num_from_str(&*self.as_string()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => return Unknown,
|
||||||
|
};
|
||||||
|
|
||||||
|
Known(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_string(&self) -> Value<Cow<str>> {
|
||||||
|
let expr = self.as_expr_kind();
|
||||||
|
match *expr {
|
||||||
|
Expr::Lit(ref l) => match *l {
|
||||||
|
Lit::Str(Str { ref value, .. }) => Known(Cow::Borrowed(value)),
|
||||||
|
Lit::Num(ref n) => Known(format!("{}", n).into()),
|
||||||
|
Lit::Bool(Bool { value: true, .. }) => Known(Cow::Borrowed("true")),
|
||||||
|
Lit::Bool(Bool { value: false, .. }) => Known(Cow::Borrowed("false")),
|
||||||
|
Lit::Null(..) => Known(Cow::Borrowed("null")),
|
||||||
|
_ => Unknown,
|
||||||
|
},
|
||||||
|
Expr::Tpl(_) => {
|
||||||
|
// TODO:
|
||||||
|
// Only convert a template literal if all its expressions can be converted.
|
||||||
|
unimplemented!("TplLit.as_string()")
|
||||||
|
}
|
||||||
|
Expr::Ident(Ident { ref sym, .. }) => match &**sym {
|
||||||
|
"undefined" | "Infinity" | "NaN" => Known(Cow::Borrowed(&**sym)),
|
||||||
|
_ => Unknown,
|
||||||
|
},
|
||||||
|
Expr::Unary(UnaryExpr {
|
||||||
|
op: op!("void"), ..
|
||||||
|
}) => Known(Cow::Borrowed("undefined")),
|
||||||
|
Expr::Unary(UnaryExpr {
|
||||||
|
op: op!("!"),
|
||||||
|
ref arg,
|
||||||
|
..
|
||||||
|
}) => Known(Cow::Borrowed(if arg.as_pure_bool()? {
|
||||||
|
"false"
|
||||||
|
} else {
|
||||||
|
"true"
|
||||||
|
})),
|
||||||
|
Expr::Array(ArrayLit { ref elems, .. }) => {
|
||||||
|
let mut first = true;
|
||||||
|
let mut buf = String::new();
|
||||||
|
// null, undefined is "" in array literl.
|
||||||
|
for elem in elems {
|
||||||
|
let e = match *elem {
|
||||||
|
Some(ref elem) => match *elem {
|
||||||
|
ExprOrSpread { ref expr, .. } => match **expr {
|
||||||
|
Expr::Lit(Lit::Null(..))
|
||||||
|
| Expr::Ident(Ident {
|
||||||
|
sym: js_word!("undefined"),
|
||||||
|
..
|
||||||
|
}) => Cow::Borrowed(""),
|
||||||
|
_ => expr.as_string()?,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
None => Cow::Borrowed(""),
|
||||||
|
};
|
||||||
|
buf.push_str(&e);
|
||||||
|
|
||||||
|
if first {
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
buf.push(',');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Known(buf.into())
|
||||||
|
}
|
||||||
|
Expr::Object(ObjectLit { .. }) => Known(Cow::Borrowed("[object Object]")),
|
||||||
|
_ => Unknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_type(&self) -> Value<Type> {
|
||||||
|
let expr = self.as_expr_kind();
|
||||||
|
|
||||||
|
match *expr {
|
||||||
|
Expr::Assign(AssignExpr {
|
||||||
|
ref right,
|
||||||
|
op: op!("="),
|
||||||
|
..
|
||||||
|
}) => right.get_type(),
|
||||||
|
Expr::Seq(SeqExpr { ref exprs, .. }) => exprs
|
||||||
|
.last()
|
||||||
|
.expect("sequence expression should not be empty")
|
||||||
|
.get_type(),
|
||||||
|
Expr::Bin(BinExpr {
|
||||||
|
ref left,
|
||||||
|
op: op!("&&"),
|
||||||
|
ref right,
|
||||||
|
..
|
||||||
|
})
|
||||||
|
| Expr::Bin(BinExpr {
|
||||||
|
ref left,
|
||||||
|
op: op!("||"),
|
||||||
|
ref right,
|
||||||
|
..
|
||||||
|
})
|
||||||
|
| Expr::Cond(CondExpr {
|
||||||
|
cons: ref left,
|
||||||
|
alt: ref right,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
let (lt, rt) = (left.get_type(), right.get_type());
|
||||||
|
|
||||||
|
if lt == rt {
|
||||||
|
return lt;
|
||||||
|
}
|
||||||
|
return Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::Bin(BinExpr {
|
||||||
|
ref left,
|
||||||
|
op: op!(bin, "+"),
|
||||||
|
ref right,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
let rt = right.get_type();
|
||||||
|
if rt == Known(StringType) {
|
||||||
|
return Known(StringType);
|
||||||
|
}
|
||||||
|
|
||||||
|
let lt = left.get_type();
|
||||||
|
if lt == Known(StringType) {
|
||||||
|
return Known(StringType);
|
||||||
|
}
|
||||||
|
|
||||||
|
match (lt, rt) {
|
||||||
|
// There are some pretty weird cases for object types:
|
||||||
|
// {} + [] === "0"
|
||||||
|
// [] + {} ==== "[object Object]"
|
||||||
|
(Known(ObjectType), _) | (_, Known(ObjectType)) => return Unknown,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ADD used with compilations of null, undefined, boolean and number always
|
||||||
|
// result in numbers.
|
||||||
|
if lt.casted_to_number_on_add() && rt.casted_to_number_on_add() {
|
||||||
|
return Known(NumberType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// There are some pretty weird cases for object types:
|
||||||
|
// {} + [] === "0"
|
||||||
|
// [] + {} ==== "[object Object]"
|
||||||
|
|
||||||
|
return Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::Assign(AssignExpr {
|
||||||
|
op: op!("+="),
|
||||||
|
ref right,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
if right.get_type() == Known(StringType) {
|
||||||
|
return Known(StringType);
|
||||||
|
}
|
||||||
|
return Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::Ident(Ident { ref sym, .. }) => {
|
||||||
|
return Known(match *sym {
|
||||||
|
js_word!("undefined") => UndefinedType,
|
||||||
|
js_word!("NaN") | js_word!("Infinity") => NumberType,
|
||||||
|
_ => return Unknown,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::Lit(Lit::Num(..))
|
||||||
|
| Expr::Assign(AssignExpr { op: op!("&="), .. })
|
||||||
|
| Expr::Assign(AssignExpr { op: op!("^="), .. })
|
||||||
|
| Expr::Assign(AssignExpr { op: op!("|="), .. })
|
||||||
|
| Expr::Assign(AssignExpr { op: op!("<<="), .. })
|
||||||
|
| Expr::Assign(AssignExpr { op: op!(">>="), .. })
|
||||||
|
| Expr::Assign(AssignExpr {
|
||||||
|
op: op!(">>>="), ..
|
||||||
|
})
|
||||||
|
| Expr::Assign(AssignExpr { op: op!("-="), .. })
|
||||||
|
| Expr::Assign(AssignExpr { op: op!("*="), .. })
|
||||||
|
| Expr::Assign(AssignExpr { op: op!("**="), .. })
|
||||||
|
| Expr::Assign(AssignExpr { op: op!("/="), .. })
|
||||||
|
| Expr::Assign(AssignExpr { op: op!("%="), .. })
|
||||||
|
| Expr::Unary(UnaryExpr { op: op!("~"), .. })
|
||||||
|
| Expr::Bin(BinExpr { op: op!("|"), .. })
|
||||||
|
| Expr::Bin(BinExpr { op: op!("^"), .. })
|
||||||
|
| Expr::Bin(BinExpr { op: op!("&"), .. })
|
||||||
|
| Expr::Bin(BinExpr { op: op!("<<"), .. })
|
||||||
|
| Expr::Bin(BinExpr { op: op!(">>"), .. })
|
||||||
|
| Expr::Bin(BinExpr { op: op!(">>>"), .. })
|
||||||
|
| Expr::Bin(BinExpr {
|
||||||
|
op: op!(bin, "-"), ..
|
||||||
|
})
|
||||||
|
| Expr::Bin(BinExpr { op: op!("*"), .. })
|
||||||
|
| Expr::Bin(BinExpr { op: op!("%"), .. })
|
||||||
|
| Expr::Bin(BinExpr { op: op!("/"), .. })
|
||||||
|
| Expr::Bin(BinExpr { op: op!("**"), .. })
|
||||||
|
| Expr::Update(UpdateExpr { op: op!("++"), .. })
|
||||||
|
| Expr::Update(UpdateExpr { op: op!("--"), .. })
|
||||||
|
| Expr::Unary(UnaryExpr {
|
||||||
|
op: op!(unary, "+"),
|
||||||
|
..
|
||||||
|
})
|
||||||
|
| Expr::Unary(UnaryExpr {
|
||||||
|
op: op!(unary, "-"),
|
||||||
|
..
|
||||||
|
}) => return Known(NumberType),
|
||||||
|
|
||||||
|
// Primitives
|
||||||
|
Expr::Lit(Lit::Bool(..))
|
||||||
|
| Expr::Bin(BinExpr { op: op!("=="), .. })
|
||||||
|
| Expr::Bin(BinExpr { op: op!("!="), .. })
|
||||||
|
| Expr::Bin(BinExpr { op: op!("==="), .. })
|
||||||
|
| Expr::Bin(BinExpr { op: op!("!=="), .. })
|
||||||
|
| Expr::Bin(BinExpr { op: op!("<"), .. })
|
||||||
|
| Expr::Bin(BinExpr { op: op!("<="), .. })
|
||||||
|
| Expr::Bin(BinExpr { op: op!(">"), .. })
|
||||||
|
| Expr::Bin(BinExpr { op: op!(">="), .. })
|
||||||
|
| Expr::Bin(BinExpr { op: op!("in"), .. })
|
||||||
|
| Expr::Bin(BinExpr {
|
||||||
|
op: op!("instanceof"),
|
||||||
|
..
|
||||||
|
})
|
||||||
|
| Expr::Unary(UnaryExpr { op: op!("!"), .. })
|
||||||
|
| Expr::Unary(UnaryExpr {
|
||||||
|
op: op!("delete"), ..
|
||||||
|
}) => return Known(BoolType),
|
||||||
|
|
||||||
|
Expr::Unary(UnaryExpr {
|
||||||
|
op: op!("typeof"), ..
|
||||||
|
})
|
||||||
|
| Expr::Lit(Lit::Str { .. }) => return Known(StringType),
|
||||||
|
|
||||||
|
Expr::Lit(Lit::Null(..)) => return Known(NullType),
|
||||||
|
|
||||||
|
Expr::Unary(UnaryExpr {
|
||||||
|
op: op!("void"), ..
|
||||||
|
}) => return Known(UndefinedType),
|
||||||
|
|
||||||
|
Expr::Fn(..)
|
||||||
|
| Expr::New(NewExpr { .. })
|
||||||
|
| Expr::Array(ArrayLit { .. })
|
||||||
|
| Expr::Object(ObjectLit { .. })
|
||||||
|
| Expr::Lit(Lit::Regex(..)) => return Known(ObjectType),
|
||||||
|
|
||||||
|
_ => Unknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn may_have_side_effects(&self) -> bool {
|
||||||
|
match *self.as_expr_kind() {
|
||||||
|
Expr::Lit(..) | Expr::Ident(..) | Expr::This(..) => false,
|
||||||
|
Expr::Paren(ref e) => e.expr.may_have_side_effects(),
|
||||||
|
// Function expression does not have any side effect if it's not used.
|
||||||
|
Expr::Fn(..) | Expr::Arrow(ArrowExpr { .. }) => false,
|
||||||
|
// TODO
|
||||||
|
Expr::Class(..) => true,
|
||||||
|
Expr::Array(ArrayLit { ref elems, .. }) => elems
|
||||||
|
.iter()
|
||||||
|
.filter_map(|e| e.as_ref())
|
||||||
|
.any(|e| e.expr.may_have_side_effects()),
|
||||||
|
Expr::Unary(UnaryExpr { ref arg, .. }) => arg.may_have_side_effects(),
|
||||||
|
Expr::Bin(BinExpr {
|
||||||
|
ref left,
|
||||||
|
ref right,
|
||||||
|
..
|
||||||
|
}) => left.may_have_side_effects() || right.may_have_side_effects(),
|
||||||
|
|
||||||
|
//TODO
|
||||||
|
Expr::Tpl(_) => true,
|
||||||
|
Expr::MetaProp(_) => true,
|
||||||
|
|
||||||
|
Expr::Await(_)
|
||||||
|
| Expr::Yield(_)
|
||||||
|
| Expr::Member(_)
|
||||||
|
| Expr::Update(_)
|
||||||
|
| Expr::Assign(_) => true,
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
Expr::New(_) => true,
|
||||||
|
// TODO
|
||||||
|
Expr::Call(_) => true,
|
||||||
|
|
||||||
|
Expr::Seq(SeqExpr { ref exprs, .. }) => exprs.iter().any(|e| e.may_have_side_effects()),
|
||||||
|
|
||||||
|
Expr::Cond(CondExpr {
|
||||||
|
ref test,
|
||||||
|
ref cons,
|
||||||
|
ref alt,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
test.may_have_side_effects()
|
||||||
|
|| cons.may_have_side_effects()
|
||||||
|
|| alt.may_have_side_effects()
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::Object(ObjectLit { ref props, .. }) => props.iter().any(|node| match *node {
|
||||||
|
Prop::Shorthand(..) => false,
|
||||||
|
Prop::KeyValue(KeyValueProp { ref key, ref value }) => {
|
||||||
|
let k = match *key {
|
||||||
|
PropName::Computed(ref e) => e.may_have_side_effects(),
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
k || value.may_have_side_effects()
|
||||||
|
}
|
||||||
|
_ => true,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn num_from_str(s: &str) -> Value<f64> {
|
||||||
|
if s.contains('\u{000b}') {
|
||||||
|
return Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Check if this is correct
|
||||||
|
let s = s.trim();
|
||||||
|
|
||||||
|
if s.is_empty() {
|
||||||
|
return Known(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.starts_with("0x") || s.starts_with("0X") {
|
||||||
|
return match s[2..4].parse() {
|
||||||
|
Ok(n) => Known(n),
|
||||||
|
Err(_) => Known(NAN),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s.starts_with('-') || s.starts_with('+'))
|
||||||
|
&& (s[1..].starts_with("0x") || s[1..].starts_with("0X"))
|
||||||
|
{
|
||||||
|
// hex numbers with explicit signs vary between browsers.
|
||||||
|
return Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Firefox and IE treat the "Infinity" differently. Firefox is case
|
||||||
|
// insensitive, but IE treats "infinity" as NaN. So leave it alone.
|
||||||
|
match s {
|
||||||
|
"infinity" | "+infinity" | "-infinity" => return Unknown,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Known(s.parse().ok().unwrap_or(NAN))
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExprExt for Box<Expr> {
|
||||||
|
fn as_expr_kind(&self) -> &Expr {
|
||||||
|
&self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExprExt for Expr {
|
||||||
|
fn as_expr_kind(&self) -> &Expr {
|
||||||
|
&self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub enum Purity {
|
||||||
|
MayBeImpure,
|
||||||
|
Pure,
|
||||||
|
}
|
||||||
|
impl Purity {
|
||||||
|
pub fn is_pure(self) -> bool {
|
||||||
|
self == Pure
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add for Purity {
|
||||||
|
type Output = Self;
|
||||||
|
fn add(self, rhs: Self) -> Self {
|
||||||
|
match (self, rhs) {
|
||||||
|
(Pure, Pure) => Pure,
|
||||||
|
_ => MayBeImpure,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
97
ecmascript/transforms/src/util/value.rs
Normal file
97
ecmascript/transforms/src/util/value.rs
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
use self::Value::{Known, Unknown};
|
||||||
|
use std::ops::{Not, Try};
|
||||||
|
|
||||||
|
/// Runtime value.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub enum Value<T> {
|
||||||
|
Known(T),
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub enum Type {
|
||||||
|
Undefined,
|
||||||
|
Null,
|
||||||
|
Bool,
|
||||||
|
Str,
|
||||||
|
Symbol,
|
||||||
|
Num,
|
||||||
|
Obj,
|
||||||
|
}
|
||||||
|
impl Value<Type> {
|
||||||
|
pub fn casted_to_number_on_add(self) -> bool {
|
||||||
|
match self {
|
||||||
|
Known(Type::Bool) | Known(Type::Null) | Known(Type::Num) | Known(Type::Undefined) => {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UnknownError;
|
||||||
|
impl<T> Try for Value<T> {
|
||||||
|
type Ok = T;
|
||||||
|
type Error = UnknownError;
|
||||||
|
fn from_ok(t: T) -> Self {
|
||||||
|
Known(t)
|
||||||
|
}
|
||||||
|
fn from_error(_: UnknownError) -> Self {
|
||||||
|
Unknown
|
||||||
|
}
|
||||||
|
fn into_result(self) -> Result<T, UnknownError> {
|
||||||
|
match self {
|
||||||
|
Known(t) => Ok(t),
|
||||||
|
Unknown => Err(UnknownError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Value<T> {
|
||||||
|
pub fn is_unknown(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Unknown => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn is_known(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Known(..) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Value<bool> {
|
||||||
|
pub fn and(self, other: Self) -> Self {
|
||||||
|
match self {
|
||||||
|
Known(true) => other,
|
||||||
|
Known(false) => Known(false),
|
||||||
|
Unknown => match other {
|
||||||
|
Known(false) => Known(false),
|
||||||
|
_ => Unknown,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn or(self, other: Self) -> Self {
|
||||||
|
match self {
|
||||||
|
Known(true) => Known(true),
|
||||||
|
Known(false) => other,
|
||||||
|
Unknown => match other {
|
||||||
|
Known(true) => Known(true),
|
||||||
|
_ => Unknown,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Not for Value<bool> {
|
||||||
|
type Output = Self;
|
||||||
|
fn not(self) -> Self {
|
||||||
|
match self {
|
||||||
|
Value::Known(b) => Value::Known(!b),
|
||||||
|
Value::Unknown => Value::Unknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1484
ecmascript/transforms/tests/simplify_expr.rs
Normal file
1484
ecmascript/transforms/tests/simplify_expr.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -16,7 +16,7 @@ use std::{
|
|||||||
rc::Rc,
|
rc::Rc,
|
||||||
};
|
};
|
||||||
use swc::Compiler;
|
use swc::Compiler;
|
||||||
use swc_common::errors::{CodeMap, FilePathMapping, Handler};
|
use swc_common::{errors::Handler, FilePathMapping, SourceMap};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
run().unwrap()
|
run().unwrap()
|
||||||
@ -62,7 +62,7 @@ fn run() -> Result<(), Box<Error>> {
|
|||||||
.build()
|
.build()
|
||||||
.expect("failed to create rayon::ThreadPool?");
|
.expect("failed to create rayon::ThreadPool?");
|
||||||
|
|
||||||
let cm = Rc::new(CodeMap::new(FilePathMapping::empty()));
|
let cm = Rc::new(SourceMap::new(FilePathMapping::empty()));
|
||||||
|
|
||||||
let handler = Handler::with_tty_emitter(
|
let handler = Handler::with_tty_emitter(
|
||||||
::swc_common::errors::ColorConfig::Always,
|
::swc_common::errors::ColorConfig::Always,
|
||||||
|
@ -19,7 +19,7 @@ use swc_ecmascript::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub struct Compiler {
|
pub struct Compiler {
|
||||||
codemap: Lrc<CodeMap>,
|
codemap: Lrc<SourceMap>,
|
||||||
threads: rayon::ThreadPool,
|
threads: rayon::ThreadPool,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
handler: Handler,
|
handler: Handler,
|
||||||
@ -28,7 +28,7 @@ pub struct Compiler {
|
|||||||
impl Compiler {
|
impl Compiler {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
codemap: Lrc<SourceMapperDyn>,
|
codemap: Lrc<SourceMap>,
|
||||||
handler: Handler,
|
handler: Handler,
|
||||||
threads: rayon::ThreadPool,
|
threads: rayon::ThreadPool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@ -42,10 +42,7 @@ impl Compiler {
|
|||||||
|
|
||||||
/// TODO
|
/// TODO
|
||||||
pub fn parse_js(&self, path: &Path) -> PResult<Module> {
|
pub fn parse_js(&self, path: &Path) -> PResult<Module> {
|
||||||
let fm = self
|
let fm = self.codemap.load_file(path).expect("failed to load file");
|
||||||
.codemap
|
|
||||||
.load_file_and_lines(path)
|
|
||||||
.expect("failed to load file");
|
|
||||||
|
|
||||||
Parser::new(
|
Parser::new(
|
||||||
ParseSess {
|
ParseSess {
|
||||||
|
Loading…
Reference in New Issue
Block a user