mirror of
https://github.com/swc-project/swc.git
synced 2024-11-23 09:38:16 +03:00
[WIP] simplifier
This commit is contained in:
parent
015ccb07f4
commit
5a1c844549
@ -5,7 +5,10 @@
|
||||
#[macro_use]
|
||||
pub extern crate swc_atoms;
|
||||
pub extern crate swc_common;
|
||||
#[macro_use]
|
||||
pub extern crate swc_ecma_ast as ast;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
pub mod simplify;
|
||||
pub mod util;
|
||||
|
1
ecmascript/transforms/src/macros.rs
Normal file
1
ecmascript/transforms/src/macros.rs
Normal file
@ -0,0 +1 @@
|
||||
|
@ -7,6 +7,7 @@ use util::*;
|
||||
|
||||
impl Folder<ExprKind> for Simplify {
|
||||
fn fold(&mut self, expr: ExprKind) -> ExprKind {
|
||||
// fold children nodes.
|
||||
let expr = expr.fold_children(self);
|
||||
|
||||
match expr {
|
||||
@ -47,10 +48,10 @@ impl Folder<ExprKind> for Simplify {
|
||||
|
||||
fn fold_bin(left: Box<Expr>, op: BinaryOp, right: Box<Expr>) -> ExprKind {
|
||||
let (left, right) = match op {
|
||||
BinaryOp::Add => return fold_add(left, right),
|
||||
BinaryOp::LogicalAnd | BinaryOp::LogicalOr => match left.as_bool() {
|
||||
op!(bin "+") => return fold_add(left, right),
|
||||
op!("&&") | op!("||") => match left.as_bool() {
|
||||
(Pure, Known(val)) => {
|
||||
if op == BinaryOp::LogicalAnd {
|
||||
if op == op!("&&") {
|
||||
if val {
|
||||
// 1 && $right
|
||||
return right.node;
|
||||
@ -70,9 +71,9 @@ fn fold_bin(left: Box<Expr>, op: BinaryOp, right: Box<Expr>) -> ExprKind {
|
||||
}
|
||||
_ => (left, right),
|
||||
},
|
||||
BinaryOp::InstanceOf => (left, right),
|
||||
op!("instanceof") => (left, right),
|
||||
|
||||
BinaryOp::Sub | BinaryOp::Div | BinaryOp::Mod => {
|
||||
op!(bin "-") | op!("/") | op!("%") => {
|
||||
// Arithmetic operations
|
||||
|
||||
(left, right)
|
||||
@ -89,7 +90,7 @@ fn fold_add(left: Box<Expr>, right: Box<Expr>) -> ExprKind {
|
||||
|
||||
Binary {
|
||||
left,
|
||||
op: BinaryOp::Add,
|
||||
op: op!(bin "+"),
|
||||
right,
|
||||
}
|
||||
}
|
||||
@ -98,7 +99,7 @@ fn fold_unary(prefix: bool, op: UnaryOp, arg: Box<Expr>) -> ExprKind {
|
||||
let span = arg.span;
|
||||
|
||||
match op {
|
||||
UnaryOp::TypeOf => {
|
||||
op!("typeof") => {
|
||||
let val = match arg.node {
|
||||
Function(..) => "function",
|
||||
Lit(Lit::Str(..)) => "string",
|
||||
@ -107,7 +108,7 @@ fn fold_unary(prefix: bool, op: UnaryOp, arg: Box<Expr>) -> ExprKind {
|
||||
Lit(Lit::Null) | Object { .. } | Array { .. } => "object",
|
||||
Unary {
|
||||
prefix: true,
|
||||
op: UnaryOp::Void,
|
||||
op: op!("void"),
|
||||
..
|
||||
}
|
||||
| Ident(Ident {
|
||||
@ -122,7 +123,7 @@ fn fold_unary(prefix: bool, op: UnaryOp, arg: Box<Expr>) -> ExprKind {
|
||||
_ => {
|
||||
return Unary {
|
||||
prefix: true,
|
||||
op: UnaryOp::TypeOf,
|
||||
op: op!("typeof"),
|
||||
arg,
|
||||
}
|
||||
}
|
||||
@ -130,7 +131,7 @@ fn fold_unary(prefix: bool, op: UnaryOp, arg: Box<Expr>) -> ExprKind {
|
||||
|
||||
return Lit(Lit::Str(val.into()));
|
||||
}
|
||||
UnaryOp::Bang => match arg.as_bool() {
|
||||
op!("!") => match arg.as_bool() {
|
||||
(p, Known(val)) => {
|
||||
let new = Lit(Lit::Bool(!val));
|
||||
return if p.is_pure() {
|
||||
@ -149,8 +150,8 @@ fn fold_unary(prefix: bool, op: UnaryOp, arg: Box<Expr>) -> ExprKind {
|
||||
}
|
||||
}
|
||||
},
|
||||
UnaryOp::Plus => {}
|
||||
UnaryOp::Minus => match arg.node {
|
||||
op!(unary "+") => {}
|
||||
op!(unary "-") => match arg.node {
|
||||
Ident(Ident {
|
||||
sym: js_word!("Infinity"),
|
||||
..
|
||||
@ -173,4 +174,18 @@ fn fold_unary(prefix: bool, op: UnaryOp, arg: Box<Expr>) -> ExprKind {
|
||||
}
|
||||
|
||||
/// Try to fold arithmetic binary operators
|
||||
fn perform_arithmetic_op(op: BinaryOp, left: Box<Expr>, right: Box<Expr>) -> ExprKind {}
|
||||
fn perform_arithmetic_op(op: BinaryOp, left: Box<Expr>, right: Box<Expr>) -> ExprKind {
|
||||
let (lv, rv) = (left.as_number(), right.as_number());
|
||||
|
||||
if lv.is_unknown() && rv.is_unknown() {
|
||||
return Binary { left, op, right };
|
||||
}
|
||||
|
||||
Binary { left, op, right }
|
||||
}
|
||||
|
||||
/// https://tc39.github.io/ecma262/#sec-abstract-equality-comparison
|
||||
fn perform_abstract_eq_cmp(left: &Expr, right: &Expr) -> Value<bool> {}
|
||||
|
||||
/// https://tc39.github.io/ecma262/#sec-strict-equality-comparison
|
||||
fn perform_strict_eq_cmp(left: &Expr, right: &Expr) -> Value<bool> {}
|
||||
|
@ -81,6 +81,14 @@ impl Folder<StmtKind> for Simplify {
|
||||
ExprKind::Lit(Lit::Num(..))
|
||||
| ExprKind::Lit(Lit::Bool(..))
|
||||
| ExprKind::Lit(Lit::Regex(..)) => StmtKind::Empty,
|
||||
|
||||
//
|
||||
// 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.
|
||||
ExprKind::Function(..) => StmtKind::Empty,
|
||||
_ => StmtKind::Expr(box Expr { node, span }),
|
||||
},
|
||||
|
||||
@ -88,13 +96,58 @@ impl Folder<StmtKind> for Simplify {
|
||||
if stmts.len() == 0 {
|
||||
return StmtKind::Empty;
|
||||
} else if stmts.len() == 1 {
|
||||
// TODO: Check lexical variable
|
||||
// TODO: Check if lexical variable exists.
|
||||
return stmts.into_iter().next().unwrap().node;
|
||||
} else {
|
||||
StmtKind::Block(BlockStmt { span, stmts })
|
||||
}
|
||||
}
|
||||
|
||||
StmtKind::Try {
|
||||
block,
|
||||
handler,
|
||||
finalizer,
|
||||
} => {
|
||||
// Only leave the finally block if try block is empty
|
||||
if block.is_empty() {
|
||||
return finalizer.map(StmtKind::Block).unwrap_or(StmtKind::Empty);
|
||||
}
|
||||
|
||||
// If catch block and finally block is empty, remove try-catch is useless.
|
||||
if handler.is_empty() && finalizer.is_empty() {
|
||||
return StmtKind::Block(block);
|
||||
}
|
||||
|
||||
StmtKind::Try {
|
||||
block,
|
||||
handler,
|
||||
finalizer,
|
||||
}
|
||||
}
|
||||
|
||||
// Remove empty else block.
|
||||
// As we fold children before parent, unused expression
|
||||
// statements without side effects are converted to
|
||||
// StmtKind::Empty before here.
|
||||
StmtKind::If {
|
||||
test,
|
||||
consequent,
|
||||
alt,
|
||||
} => {
|
||||
if alt.is_empty() {
|
||||
return StmtKind::If {
|
||||
test,
|
||||
consequent,
|
||||
alt: None,
|
||||
};
|
||||
}
|
||||
StmtKind::If {
|
||||
test,
|
||||
consequent,
|
||||
alt,
|
||||
}
|
||||
}
|
||||
|
||||
_ => stmt,
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,59 @@
|
||||
pub use self::Purity::{MayBeImpure, Pure};
|
||||
pub use self::Value::{Known, Unknown};
|
||||
pub use self::value::Type::{self, Bool as BoolType, Null as NullType, Num as NumberType,
|
||||
Obj as ObjectType, Str as StringType, Symbol as SymbolType,
|
||||
Undefined as UndefinedType};
|
||||
pub use self::value::Value::{self, Known, Unknown};
|
||||
use ast::*;
|
||||
use std::borrow::Cow;
|
||||
use std::f64::{INFINITY, NAN};
|
||||
use std::num::FpCategory;
|
||||
use std::ops::{Add, Not};
|
||||
use std::ops::Add;
|
||||
|
||||
mod value;
|
||||
|
||||
pub type Bool = 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 StmtKind {
|
||||
fn is_empty(&self) -> bool {
|
||||
match *self {
|
||||
StmtKind::Empty => true,
|
||||
StmtKind::Block(ref b) => b.is_empty(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl IsEmpty for Stmt {
|
||||
fn is_empty(&self) -> bool {
|
||||
self.node.is_empty()
|
||||
}
|
||||
}
|
||||
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 + AsRef<Expr> {
|
||||
///
|
||||
@ -20,7 +69,7 @@ pub trait ExprExt: Sized + AsRef<Expr> {
|
||||
|
||||
ExprKind::Unary {
|
||||
prefix: true,
|
||||
op: UnaryOp::Bang,
|
||||
op: op!("!"),
|
||||
ref arg,
|
||||
} => {
|
||||
let (p, v) = arg.as_bool();
|
||||
@ -29,12 +78,12 @@ pub trait ExprExt: Sized + AsRef<Expr> {
|
||||
|
||||
ExprKind::Binary {
|
||||
ref left,
|
||||
op: op @ BinaryOp::LogicalAnd,
|
||||
op: op @ op!("&"),
|
||||
ref right,
|
||||
}
|
||||
| ExprKind::Binary {
|
||||
ref left,
|
||||
op: op @ BinaryOp::LogicalOr,
|
||||
op: op @ op!("|"),
|
||||
ref right,
|
||||
} => {
|
||||
// TODO: Ignore purity if value cannot be reached.
|
||||
@ -45,7 +94,7 @@ pub trait ExprExt: Sized + AsRef<Expr> {
|
||||
if lp + rp == Pure {
|
||||
return (Pure, lv.and(rv));
|
||||
}
|
||||
if op == BinaryOp::LogicalAnd {
|
||||
if op == op!("&") {
|
||||
lv.and(rv)
|
||||
} else {
|
||||
lv.or(rv)
|
||||
@ -60,7 +109,7 @@ pub trait ExprExt: Sized + AsRef<Expr> {
|
||||
|
||||
ExprKind::Unary {
|
||||
prefix: true,
|
||||
op: UnaryOp::Void,
|
||||
op: op!("void"),
|
||||
arg: _,
|
||||
} => Known(false),
|
||||
|
||||
@ -105,7 +154,7 @@ pub trait ExprExt: Sized + AsRef<Expr> {
|
||||
},
|
||||
ExprKind::Unary {
|
||||
prefix: true,
|
||||
op: UnaryOp::Minus,
|
||||
op: op!(unary "-"),
|
||||
arg:
|
||||
box Expr {
|
||||
span: _,
|
||||
@ -118,7 +167,7 @@ pub trait ExprExt: Sized + AsRef<Expr> {
|
||||
} => -INFINITY,
|
||||
ExprKind::Unary {
|
||||
prefix: true,
|
||||
op: UnaryOp::Bang,
|
||||
op: op!("!"),
|
||||
ref arg,
|
||||
} => match arg.as_bool() {
|
||||
(Pure, Known(v)) => {
|
||||
@ -132,14 +181,14 @@ pub trait ExprExt: Sized + AsRef<Expr> {
|
||||
},
|
||||
ExprKind::Unary {
|
||||
prefix: true,
|
||||
op: UnaryOp::Void,
|
||||
ref arg,
|
||||
op: op!("void"),
|
||||
arg: _,
|
||||
} => {
|
||||
if arg.may_have_side_effects() {
|
||||
return Unknown;
|
||||
} else {
|
||||
NAN
|
||||
}
|
||||
// if arg.may_have_side_effects() {
|
||||
return Unknown;
|
||||
// } else {
|
||||
// NAN
|
||||
// }
|
||||
}
|
||||
|
||||
ExprKind::Tpl(..) | ExprKind::Object { .. } | ExprKind::Array { .. } => {
|
||||
@ -160,7 +209,7 @@ pub trait ExprExt: Sized + AsRef<Expr> {
|
||||
match expr.node {
|
||||
ExprKind::Lit(ref l) => match *l {
|
||||
Lit::Str(ref s) => Some(Cow::Borrowed(s)),
|
||||
Lit::Num(n) => Some(format!("{}", n).into()),
|
||||
Lit::Num(ref n) => Some(format!("{}", n).into()),
|
||||
Lit::Bool(true) => Some(Cow::Borrowed("true")),
|
||||
Lit::Bool(false) => Some(Cow::Borrowed("false")),
|
||||
Lit::Null => Some(Cow::Borrowed("null")),
|
||||
@ -177,12 +226,12 @@ pub trait ExprExt: Sized + AsRef<Expr> {
|
||||
},
|
||||
ExprKind::Unary {
|
||||
prefix: true,
|
||||
op: UnaryOp::Void,
|
||||
op: op!("void"),
|
||||
..
|
||||
} => Some(Cow::Borrowed("undefined")),
|
||||
ExprKind::Unary {
|
||||
prefix: true,
|
||||
op: UnaryOp::Bang,
|
||||
op: op!("!"),
|
||||
ref arg,
|
||||
} => match arg.as_bool() {
|
||||
(Pure, Known(v)) => Some(Cow::Borrowed(if v { "false" } else { "true" })),
|
||||
@ -224,6 +273,78 @@ pub trait ExprExt: Sized + AsRef<Expr> {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_type(&self) -> Value<Type> {
|
||||
let expr = self.as_ref();
|
||||
|
||||
match expr.node {
|
||||
ExprKind::Assign { ref right, .. } => right.get_type(),
|
||||
ExprKind::Seq { ref exprs } => exprs
|
||||
.last()
|
||||
.expect("sequence expression should not be empty")
|
||||
.get_type(),
|
||||
ExprKind::Binary {
|
||||
ref left,
|
||||
op: op!("&&"),
|
||||
ref right,
|
||||
}
|
||||
| ExprKind::Binary {
|
||||
ref left,
|
||||
op: op!("||"),
|
||||
ref right,
|
||||
} => {}
|
||||
|
||||
|
||||
ExprKind::Assign {
|
||||
ref left,
|
||||
op: AssignOp::AddAssign,
|
||||
ref right,
|
||||
} => {
|
||||
if right.get_type() == Known(StringType) {
|
||||
return Known(StringType);
|
||||
}
|
||||
return Unknown;
|
||||
}
|
||||
|
||||
ExprKind::Ident(Ident { ref sym, .. }) => {
|
||||
return Known(match *sym {
|
||||
js_word!("undefined") => UndefinedType,
|
||||
js_word!("NaN") | js_word!("Infinity") => NumberType,
|
||||
_ => return Unknown,
|
||||
})
|
||||
}
|
||||
|
||||
ExprKind::Lit(Lit::Num(..))|
|
||||
ExprKind::Assign{op:op!("&="),..}|
|
||||
ExprKind::Assign{op:op!("^="),..}|
|
||||
ExprKind::Assign{op:op!("|="),..}|
|
||||
ExprKind::Assign{op:op!("<<="),..}|
|
||||
ExprKind::Assign{op:op!(">>="),..}|
|
||||
ExprKind::Assign{op:op!(">>>="),..}|
|
||||
ExprKind::Assign{op:op!("-="),..}|
|
||||
ExprKind::Assign{op:op!("*="),..}|
|
||||
ExprKind::Assign{op:op!("**="),..}|
|
||||
ExprKind::Assign{op:op!("/="),..}|
|
||||
ExprKind::Assign{op:op!("%="),..}
|
||||
// case BITNOT:
|
||||
// case BITOR:
|
||||
// case BITXOR:
|
||||
// case BITAND:
|
||||
// case LSH:
|
||||
// case RSH:
|
||||
// case URSH:
|
||||
// case SUB:
|
||||
// case MUL:
|
||||
// case MOD:
|
||||
// case DIV:
|
||||
// case EXPONENT:
|
||||
// case INC:
|
||||
// case DEC:
|
||||
// case POS:
|
||||
// case NEG:
|
||||
=> return Known(NumberType),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn num_from_str(s: &str) -> Value<f64> {
|
||||
@ -284,53 +405,3 @@ impl Add for Purity {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
Str,
|
||||
Obj,
|
||||
Undetermined,
|
||||
}
|
||||
|
||||
pub type Bool = Value<bool>;
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
63
ecmascript/transforms/src/util/value.rs
Normal file
63
ecmascript/transforms/src/util/value.rs
Normal file
@ -0,0 +1,63 @@
|
||||
use self::Value::{Known, Unknown};
|
||||
use std::ops::Not;
|
||||
|
||||
/// 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<T> Value<T> {
|
||||
pub fn is_unknown(&self) -> bool {
|
||||
match *self {
|
||||
Unknown => 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,
|
||||
}
|
||||
}
|
||||
}
|
302
ecmascript/transforms/tests/simplify.rs
Normal file
302
ecmascript/transforms/tests/simplify.rs
Normal file
@ -0,0 +1,302 @@
|
||||
extern crate swc_common;
|
||||
extern crate swc_ecma_ast as ast;
|
||||
extern crate swc_ecma_parser;
|
||||
extern crate swc_ecma_transforms as transforms;
|
||||
#[macro_use]
|
||||
extern crate testing;
|
||||
|
||||
use swc_common::fold::Folder;
|
||||
use swc_ecma_parser::lexer::Lexer;
|
||||
use swc_ecma_parser::parser::Parser;
|
||||
use testing::logger;
|
||||
use transforms::simplify::Simplify;
|
||||
|
||||
macro_rules! parse {
|
||||
($s:expr) => {{
|
||||
let l = logger();
|
||||
Parser::new_for_module(l.clone(), Lexer::new_from_str(l, $s))
|
||||
.parse_module()
|
||||
.unwrap_or_else(|err| panic!("failed to parse module: {:?}", err))
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! test {
|
||||
($l:expr, $r:expr) => {{
|
||||
assert_eq_ignore_span!(fold(parse!($l), Simplify), parse!($r))
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! test_expr {
|
||||
($l:expr, $r:expr) => {{
|
||||
test!(&format!("used(({}))", $l), &format!("used(({}))", $r));
|
||||
}};
|
||||
}
|
||||
|
||||
/// Should not modify expression.
|
||||
macro_rules! same_expr {
|
||||
($l:expr) => {{
|
||||
test_expr!(&format!("{}", $l), &format!("{}", $l));
|
||||
}};
|
||||
}
|
||||
|
||||
fn fold<T, F>(t: T, mut f: F) -> T
|
||||
where
|
||||
F: Folder<T>,
|
||||
{
|
||||
f.fold(t)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_simple() {
|
||||
test_expr!("3 + 6", "9");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cond_simple() {
|
||||
test_expr!("true ? 3 : 6", "3");
|
||||
test_expr!("false ? 3 : 6", "6");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cond_side_effect() {
|
||||
test!("new UnknownClass() ? 3 : 6", "new UnknownClass(), 3");
|
||||
test!("(void fn()) ? 3 : 6", "(void fn()), 6");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn oror_non_bool() {
|
||||
test_expr!("5 || 50", "5")
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// Tests ported from google closure compiler (which is licensed Apache 2.0)
|
||||
// See https://github.com/google/closure-compiler
|
||||
// --------------------------------------------------
|
||||
|
||||
#[test]
|
||||
fn undefined_cmp_1() {
|
||||
test_expr!("undefined == undefined", "true");
|
||||
test_expr!("undefined == null", "true");
|
||||
test_expr!("undefined == void 0", "true");
|
||||
|
||||
test_expr!("undefined == 0", "false");
|
||||
test_expr!("undefined == 1", "false");
|
||||
test_expr!("undefined == 'hi'", "false");
|
||||
test_expr!("undefined == true", "false");
|
||||
test_expr!("undefined == false", "false");
|
||||
|
||||
test_expr!("undefined === undefined", "true");
|
||||
test_expr!("undefined === null", "false");
|
||||
test_expr!("undefined === void 0", "true");
|
||||
|
||||
same_expr!("undefined == this");
|
||||
same_expr!("undefined == x");
|
||||
|
||||
test_expr!("undefined != undefined", "false");
|
||||
test_expr!("undefined != null", "false");
|
||||
test_expr!("undefined != void 0", "false");
|
||||
|
||||
test_expr!("undefined != 0", "true");
|
||||
test_expr!("undefined != 1", "true");
|
||||
test_expr!("undefined != 'hi'", "true");
|
||||
test_expr!("undefined != true", "true");
|
||||
test_expr!("undefined != false", "true");
|
||||
|
||||
test_expr!("undefined !== undefined", "false");
|
||||
test_expr!("undefined !== void 0", "false");
|
||||
test_expr!("undefined !== null", "true");
|
||||
|
||||
same_expr!("undefined != this");
|
||||
same_expr!("undefined != x");
|
||||
|
||||
test_expr!("undefined < undefined", "false");
|
||||
test_expr!("undefined > undefined", "false");
|
||||
test_expr!("undefined >= undefined", "false");
|
||||
test_expr!("undefined <= undefined", "false");
|
||||
|
||||
test_expr!("0 < undefined", "false");
|
||||
test_expr!("true > undefined", "false");
|
||||
test_expr!("'hi' >= undefined", "false");
|
||||
test_expr!("null <= undefined", "false");
|
||||
|
||||
test_expr!("undefined < 0", "false");
|
||||
test_expr!("undefined > true", "false");
|
||||
test_expr!("undefined >= 'hi'", "false");
|
||||
test_expr!("undefined <= null", "false");
|
||||
|
||||
test_expr!("null == undefined", "true");
|
||||
test_expr!("0 == undefined", "false");
|
||||
test_expr!("1 == undefined", "false");
|
||||
test_expr!("'hi' == undefined", "false");
|
||||
test_expr!("true == undefined", "false");
|
||||
test_expr!("false == undefined", "false");
|
||||
test_expr!("null === undefined", "false");
|
||||
test_expr!("void 0 === undefined", "true");
|
||||
|
||||
test_expr!("undefined == NaN", "false");
|
||||
test_expr!("NaN == undefined", "false");
|
||||
test_expr!("undefined == Infinity", "false");
|
||||
test_expr!("Infinity == undefined", "false");
|
||||
test_expr!("undefined == -Infinity", "false");
|
||||
test_expr!("-Infinity == undefined", "false");
|
||||
test_expr!("({}) == undefined", "false");
|
||||
test_expr!("undefined == ({})", "false");
|
||||
test_expr!("([]) == undefined", "false");
|
||||
test_expr!("undefined == ([])", "false");
|
||||
test_expr!("(/a/g) == undefined", "false");
|
||||
test_expr!("undefined == (/a/g)", "false");
|
||||
test_expr!("(function(){}) == undefined", "false");
|
||||
test_expr!("undefined == (function(){})", "false");
|
||||
|
||||
test_expr!("undefined != NaN", "true");
|
||||
test_expr!("NaN != undefined", "true");
|
||||
test_expr!("undefined != Infinity", "true");
|
||||
test_expr!("Infinity != undefined", "true");
|
||||
test_expr!("undefined != -Infinity", "true");
|
||||
test_expr!("-Infinity != undefined", "true");
|
||||
test_expr!("({}) != undefined", "true");
|
||||
test_expr!("undefined != ({})", "true");
|
||||
test_expr!("([]) != undefined", "true");
|
||||
test_expr!("undefined != ([])", "true");
|
||||
test_expr!("(/a/g) != undefined", "true");
|
||||
test_expr!("undefined != (/a/g)", "true");
|
||||
test_expr!("(function(){}) != undefined", "true");
|
||||
test_expr!("undefined != (function(){})", "true");
|
||||
|
||||
same_expr!("this == undefined");
|
||||
same_expr!("x == undefined");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn undefined_cmp_2() {
|
||||
test_expr!("\"123\" !== void 0", "true");
|
||||
test_expr!("\"123\" === void 0", "false");
|
||||
|
||||
test_expr!("void 0 !== \"123\"", "true");
|
||||
test_expr!("void 0 === \"123\"", "false");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn undefined_cmp_3() {
|
||||
test_expr!("\"123\" !== undefined", "true");
|
||||
test_expr!("\"123\" === undefined", "false");
|
||||
|
||||
test_expr!("undefined !== \"123\"", "true");
|
||||
test_expr!("undefined === \"123\"", "false");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn undefined_cmp_4() {
|
||||
test_expr!("1 !== void 0", "true");
|
||||
test_expr!("1 === void 0", "false");
|
||||
|
||||
test_expr!("null !== void 0", "true");
|
||||
test_expr!("null === void 0", "false");
|
||||
|
||||
test_expr!("undefined !== void 0", "false");
|
||||
test_expr!("undefined === void 0", "true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn null_cmp_1() {
|
||||
test_expr!("null == undefined", "true");
|
||||
test_expr!("null == null", "true");
|
||||
test_expr!("null == void 0", "true");
|
||||
|
||||
test_expr!("null == 0", "false");
|
||||
test_expr!("null == 1", "false");
|
||||
test_expr!("null == 'hi'", "false");
|
||||
test_expr!("null == true", "false");
|
||||
test_expr!("null == false", "false");
|
||||
|
||||
test_expr!("null === undefined", "false");
|
||||
test_expr!("null === null", "true");
|
||||
test_expr!("null === void 0", "false");
|
||||
same_expr!("null === x");
|
||||
|
||||
same_expr!("null == this");
|
||||
same_expr!("null == x");
|
||||
|
||||
test_expr!("null != undefined", "false");
|
||||
test_expr!("null != null", "false");
|
||||
test_expr!("null != void 0", "false");
|
||||
|
||||
test_expr!("null != 0", "true");
|
||||
test_expr!("null != 1", "true");
|
||||
test_expr!("null != 'hi'", "true");
|
||||
test_expr!("null != true", "true");
|
||||
test_expr!("null != false", "true");
|
||||
|
||||
test_expr!("null !== undefined", "true");
|
||||
test_expr!("null !== void 0", "true");
|
||||
test_expr!("null !== null", "false");
|
||||
|
||||
same_expr!("null != this");
|
||||
same_expr!("null != x");
|
||||
|
||||
test_expr!("null < null", "false");
|
||||
test_expr!("null > null", "false");
|
||||
test_expr!("null >= null", "true");
|
||||
test_expr!("null <= null", "true");
|
||||
|
||||
test_expr!("0 < null", "false");
|
||||
test_expr!("0 > null", "false");
|
||||
test_expr!("0 >= null", "true");
|
||||
test_expr!("true > null", "true");
|
||||
test_expr!("'hi' < null", "false");
|
||||
test_expr!("'hi' >= null", "false");
|
||||
test_expr!("null <= null", "true");
|
||||
|
||||
test_expr!("null < 0", "false");
|
||||
test_expr!("null > true", "false");
|
||||
test_expr!("null < 'hi'", "false");
|
||||
test_expr!("null >= 'hi'", "false");
|
||||
test_expr!("null <= null", "true");
|
||||
|
||||
test_expr!("null == null", "true");
|
||||
test_expr!("0 == null", "false");
|
||||
test_expr!("1 == null", "false");
|
||||
test_expr!("'hi' == null", "false");
|
||||
test_expr!("true == null", "false");
|
||||
test_expr!("false == null", "false");
|
||||
test_expr!("null === null", "true");
|
||||
test_expr!("void 0 === null", "false");
|
||||
|
||||
test_expr!("null == NaN", "false");
|
||||
test_expr!("NaN == null", "false");
|
||||
test_expr!("null == Infinity", "false");
|
||||
test_expr!("Infinity == null", "false");
|
||||
test_expr!("null == -Infinity", "false");
|
||||
test_expr!("-Infinity == null", "false");
|
||||
test_expr!("({}) == null", "false");
|
||||
test_expr!("null == ({})", "false");
|
||||
test_expr!("([]) == null", "false");
|
||||
test_expr!("null == ([])", "false");
|
||||
test_expr!("(/a/g) == null", "false");
|
||||
test_expr!("null == (/a/g)", "false");
|
||||
test_expr!("(function(){}) == null", "false");
|
||||
test_expr!("null == (function(){})", "false");
|
||||
|
||||
test_expr!("null != NaN", "true");
|
||||
test_expr!("NaN != null", "true");
|
||||
test_expr!("null != Infinity", "true");
|
||||
test_expr!("Infinity != null", "true");
|
||||
test_expr!("null != -Infinity", "true");
|
||||
test_expr!("-Infinity != null", "true");
|
||||
test_expr!("({}) != null", "true");
|
||||
test_expr!("null != ({})", "true");
|
||||
test_expr!("([]) != null", "true");
|
||||
test_expr!("null != ([])", "true");
|
||||
test_expr!("(/a/g) != null", "true");
|
||||
test_expr!("null != (/a/g)", "true");
|
||||
test_expr!("(function(){}) != null", "true");
|
||||
test_expr!("null != (function(){})", "true");
|
||||
|
||||
same_expr!("({a:f()}) == null");
|
||||
same_expr!("null == ({a:f()})");
|
||||
same_expr!("([f()]) == null");
|
||||
same_expr!("null == ([f()])");
|
||||
|
||||
same_expr!("this == null");
|
||||
same_expr!("x == null");
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
extern crate swc_common;
|
||||
extern crate swc_ecma_ast as ast;
|
||||
extern crate swc_ecma_parser;
|
||||
extern crate swc_ecma_transforms as transforms;
|
||||
#[macro_use]
|
||||
extern crate testing;
|
||||
|
||||
use swc_common::fold::Folder;
|
||||
use swc_ecma_parser::lexer::Lexer;
|
||||
use swc_ecma_parser::parser::Parser;
|
||||
use testing::logger;
|
||||
use transforms::simplify::Simplify;
|
||||
|
||||
macro_rules! parse {
|
||||
($s:expr) => {{
|
||||
let l = logger();
|
||||
Parser::new_for_module(l.clone(), Lexer::new_from_str(l, $s))
|
||||
.parse_module()
|
||||
.unwrap_or_else(|err| panic!("failed to parse module: {:?}", err))
|
||||
}};
|
||||
}
|
||||
|
||||
fn fold<T, F>(t: T, mut f: F) -> T
|
||||
where
|
||||
F: Folder<T>,
|
||||
{
|
||||
f.fold(t)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_simple() {
|
||||
assert_eq_ignore_span!(fold(parse!("use(3 + 6)"), Simplify), parse!("use(9)"))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cond_simple() {
|
||||
assert_eq_ignore_span!(
|
||||
fold(parse!("use(true ? 3 : 6)"), Simplify),
|
||||
parse!("use(3)")
|
||||
);
|
||||
assert_eq_ignore_span!(
|
||||
fold(parse!("use(false ? 3 : 6)"), Simplify),
|
||||
parse!("use(6)")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cond_side_effect() {
|
||||
assert_eq_ignore_span!(
|
||||
fold(parse!("new UnknownClass() ? 3 : 6"), Simplify),
|
||||
parse!("new UnknownClass(), 3")
|
||||
);
|
||||
assert_eq_ignore_span!(
|
||||
fold(parse!("(void fn()) ? 3 : 6"), Simplify),
|
||||
parse!("(void fn()), 6")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn oror_non_bool() {
|
||||
assert_eq_ignore_span!(fold(parse!("use(5 || 50)"), Simplify), parse!("use(5)"))
|
||||
}
|
Loading…
Reference in New Issue
Block a user