mod eval_array_expr;
mod eval_binary_expr;
mod eval_call_expr;
mod eval_cond_expr;
mod eval_lit_expr;
mod eval_member_expr;
mod eval_new_expr;
mod eval_prop_name;
mod eval_source;
mod eval_tpl_expr;
mod eval_unary_expr;
use bitflags::bitflags;
use num_bigint::BigInt;
use rspack_core::DependencyRange;
use swc_core::{atoms::Atom, common::Span, ecma::ast::Expr};
pub use self::{
eval_array_expr::eval_array_expression,
eval_binary_expr::eval_binary_expression,
eval_call_expr::eval_call_expression,
eval_cond_expr::eval_cond_expression,
eval_lit_expr::{eval_bigint, eval_bool, eval_lit_expr, eval_number, eval_str},
eval_member_expr::eval_member_expression,
eval_new_expr::eval_new_expression,
eval_prop_name::eval_prop_name,
eval_source::eval_source,
eval_tpl_expr::{TemplateStringKind, eval_tagged_tpl_expression, eval_tpl_expression},
eval_unary_expr::eval_unary_expression,
};
use crate::visitors::ExportedVariableInfo;
#[derive(Debug, Clone, PartialEq, Eq)]
enum Ty {
Unknown,
Undefined,
Null,
String,
Number,
Boolean,
RegExp,
Conditional,
Array,
Wrapped,
ConstArray,
BigInt,
Identifier,
TemplateString,
}
type Boolean = bool;
type Number = f64;
type Bigint = num_bigint::BigInt;
type String = std::string::String;
type Regexp = (String, String);
#[derive(Debug, Clone)]
pub struct BasicEvaluatedExpression<'a> {
owned_expression: Option<Box<Expr>>,
expression: Option<&'a Expr>,
ty: Ty,
range: Option<DependencyRange>,
falsy: bool,
truthy: bool,
side_effects: bool,
nullish: Option<bool>,
boolean: Option<Boolean>,
number: Option<Number>,
string: Option<String>,
bigint: Option<Bigint>,
regexp: Option<Regexp>,
array: Option<Vec<String>>,
identifier: Option<Atom>,
root_info: Option<ExportedVariableInfo>,
members: Option<Vec<Atom>>,
members_optionals: Option<Vec<bool>>,
member_ranges: Option<Vec<Span>>,
items: Option<Vec<BasicEvaluatedExpression<'a>>>,
quasis: Option<Vec<BasicEvaluatedExpression<'a>>>,
parts: Option<Vec<BasicEvaluatedExpression<'a>>>,
prefix: Option<Box<BasicEvaluatedExpression<'a>>>,
postfix: Option<Box<BasicEvaluatedExpression<'a>>>,
wrapped_inner_expressions: Option<Vec<BasicEvaluatedExpression<'a>>>,
template_string_kind: Option<TemplateStringKind>,
options: Option<Vec<BasicEvaluatedExpression<'a>>>,
}
impl Default for BasicEvaluatedExpression<'_> {
fn default() -> Self {
Self::new()
}
}
impl<'a> BasicEvaluatedExpression<'a> {
pub fn new() -> Self {
Self {
owned_expression: None,
expression: None,
ty: Ty::Unknown,
range: None,
falsy: false,
truthy: false,
side_effects: true,
nullish: None,
boolean: None,
number: None,
bigint: None,
array: None,
quasis: None,
parts: None,
identifier: None,
root_info: None,
members: None,
members_optionals: None,
member_ranges: None,
template_string_kind: None,
options: None,
string: None,
items: None,
regexp: None,
postfix: None,
prefix: None,
wrapped_inner_expressions: None,
}
}
pub fn with_range(start: u32, end: u32) -> Self {
let mut expr = BasicEvaluatedExpression::new();
expr.set_range(start, end);
expr
}
pub fn is_identifier(&self) -> bool {
matches!(self.ty, Ty::Identifier)
}
pub fn is_null(&self) -> bool {
matches!(self.ty, Ty::Null)
}
pub fn is_unknown(&self) -> bool {
matches!(self.ty, Ty::Unknown)
}
pub fn is_undefined(&self) -> bool {
matches!(self.ty, Ty::Undefined)
}
pub fn is_conditional(&self) -> bool {
matches!(self.ty, Ty::Conditional)
}
pub fn is_string(&self) -> bool {
matches!(self.ty, Ty::String)
}
pub fn is_bool(&self) -> bool {
matches!(self.ty, Ty::Boolean)
}
pub fn is_array(&self) -> bool {
matches!(self.ty, Ty::Array)
}
pub fn is_const_array(&self) -> bool {
matches!(self.ty, Ty::ConstArray)
}
pub fn is_wrapped(&self) -> bool {
matches!(self.ty, Ty::Wrapped)
}
pub fn is_number(&self) -> bool {
matches!(self.ty, Ty::Number)
}
pub fn is_bigint(&self) -> bool {
matches!(self.ty, Ty::BigInt)
}
pub fn is_template_string(&self) -> bool {
matches!(self.ty, Ty::TemplateString)
}
pub fn is_regexp(&self) -> bool {
matches!(self.ty, Ty::RegExp)
}
pub fn is_compile_time_value(&self) -> bool {
matches!(
self.ty,
Ty::Undefined
| Ty::Null
| Ty::String
| Ty::Number
| Ty::Boolean
| Ty::RegExp
| Ty::ConstArray
| Ty::BigInt
)
}
pub fn is_nullish(&self) -> Option<bool> {
self.nullish
}
pub fn is_primitive_type(&self) -> Option<bool> {
match self.ty {
Ty::Undefined
| Ty::Null
| Ty::String
| Ty::Number
| Ty::Boolean
| Ty::BigInt
| Ty::Wrapped
| Ty::TemplateString => Some(true),
Ty::RegExp | Ty::Array | Ty::ConstArray => Some(false),
_ => None,
}
}
pub fn as_number(&self) -> Option<f64> {
if self.is_bool() {
Some(if self.bool() { 1_f64 } else { 0_f64 })
} else if self.is_null() {
Some(0_f64)
} else if self.is_string() {
self.string().parse::<f64>().ok()
} else if self.is_number() {
Some(self.number())
} else {
None
}
}
pub fn as_int(&self) -> Option<i32> {
if self.is_bool() {
Some(if self.bool() { 1_i32 } else { 0_i32 })
} else if self.is_null() {
Some(0_i32)
} else if self.is_string() {
self.string().parse::<i32>().ok()
} else if self.is_number() {
Some(self.number() as i32)
} else {
None
}
}
pub fn as_string(&self) -> Option<std::string::String> {
if self.is_bool() {
Some(self.bool().to_string())
} else if self.is_null() {
Some("null".to_string())
} else if self.is_undefined() {
Some("undefined".to_string())
} else if self.is_string() {
Some(self.string().to_string())
} else if self.is_number() {
Some(self.number().to_string())
} else if self.is_array() {
let mut arr = Vec::new();
for item in self.items() {
if let Some(item) = item.as_string() {
arr.push(item)
} else {
return None;
}
}
Some(format!("[{}]", arr.join(", ")))
} else if self.is_template_string() {
let mut s = String::new();
for p in self.parts() {
if let Some(p) = p.as_string() {
s += &p;
} else {
return None;
}
}
Some(s)
} else {
None
}
}
pub fn as_bool(&self) -> Option<Boolean> {
if self.truthy {
Some(true)
} else if self.falsy || self.nullish == Some(true) || self.is_null() || self.is_undefined() {
Some(false)
} else if self.is_bool() {
self.boolean
} else if self.is_string() {
Some(!self.string().is_empty())
} else if self.is_number() {
Some(self.number() != 0.0)
} else {
None
}
}
pub fn as_nullish(&self) -> Option<bool> {
let nullish = self.is_nullish();
if nullish == Some(true) || self.is_null() || self.is_undefined() {
Some(true)
} else if nullish == Some(false)
|| self.is_bool()
|| self.is_string()
|| self.is_template_string()
{
Some(false)
} else {
None
}
}
pub fn compare_compile_time_value(&self, b: &BasicEvaluatedExpression) -> bool {
if self.ty != b.ty {
false
} else {
match self.ty {
Ty::Undefined => matches!(b.ty, Ty::Undefined),
Ty::Null => matches!(b.ty, Ty::Null),
Ty::String => {
b.string.as_ref().expect("should not empty")
== self.string.as_ref().expect("should not empty")
}
Ty::Number => {
b.number.as_ref().expect("should not empty")
== self.number.as_ref().expect("should not empty")
}
Ty::Boolean => {
b.boolean.as_ref().expect("should not empty")
== self.boolean.as_ref().expect("should not empty")
}
Ty::RegExp => false, Ty::BigInt => {
b.bigint.as_ref().expect("should not empty")
== self.bigint.as_ref().expect("should not empty")
}
_ => unreachable!("can only compare compile-time values"),
}
}
}
pub fn could_have_side_effects(&self) -> bool {
self.side_effects
}
pub fn set_side_effects(&mut self, side_effects: bool) {
self.side_effects = side_effects
}
pub fn set_null(&mut self) {
self.ty = Ty::Null;
self.side_effects = false
}
pub fn set_undefined(&mut self) {
self.ty = Ty::Undefined;
self.side_effects = false;
}
pub fn set_number(&mut self, number: Number) {
self.ty = Ty::Number;
self.number = Some(number);
self.side_effects = false;
}
pub fn set_bigint(&mut self, bigint: BigInt) {
self.ty = Ty::BigInt;
self.bigint = Some(bigint);
self.side_effects = false;
}
pub fn set_truthy(&mut self) {
self.falsy = false;
self.truthy = true;
self.nullish = Some(false);
}
pub fn set_falsy(&mut self) {
self.falsy = true;
self.truthy = false;
}
pub fn set_nullish(&mut self, nullish: bool) {
self.nullish = Some(nullish);
if nullish {
self.set_falsy()
}
}
pub fn set_items(&mut self, items: Vec<BasicEvaluatedExpression<'a>>) {
self.ty = Ty::Array;
self.side_effects = items.iter().any(|item| item.could_have_side_effects());
self.items = Some(items);
}
pub fn set_array(&mut self, array: Vec<String>) {
self.ty = Ty::ConstArray;
self.side_effects = false;
self.array = Some(array);
}
pub fn options(&self) -> &Vec<BasicEvaluatedExpression<'_>> {
self.options.as_ref().expect("options should not empty")
}
pub fn set_options(&mut self, options: Option<Vec<BasicEvaluatedExpression<'a>>>) {
self.ty = Ty::Conditional;
self.options = options;
self.side_effects = true;
}
pub fn add_options(&mut self, options: Vec<BasicEvaluatedExpression<'a>>) {
if let Some(old) = &mut self.options {
old.extend(options);
} else {
self.ty = Ty::Conditional;
self.options = Some(options);
self.side_effects = true;
}
}
pub fn set_identifier(
&mut self,
name: Atom,
root_info: ExportedVariableInfo,
members: Option<Vec<Atom>>,
members_optionals: Option<Vec<bool>>,
member_ranges: Option<Vec<Span>>,
) {
self.ty = Ty::Identifier;
self.identifier = Some(name);
self.root_info = Some(root_info);
self.members = members;
self.members_optionals = members_optionals;
self.member_ranges = member_ranges;
self.side_effects = true;
}
pub fn set_bool(&mut self, boolean: Boolean) {
self.ty = Ty::Boolean;
self.boolean = Some(boolean);
self.side_effects = false
}
pub fn set_range(&mut self, start: u32, end: u32) {
self.range = Some(DependencyRange::new(start, end))
}
pub fn set_template_string(
&mut self,
quasis: Vec<BasicEvaluatedExpression<'a>>,
parts: Vec<BasicEvaluatedExpression<'a>>,
kind: TemplateStringKind,
) {
self.ty = Ty::TemplateString;
self.quasis = Some(quasis);
self.side_effects = parts.iter().any(|p| p.side_effects);
self.parts = Some(parts);
self.template_string_kind = Some(kind);
}
pub fn set_string(&mut self, string: String) {
self.ty = Ty::String;
self.string = Some(string);
self.side_effects = false;
}
pub fn set_regexp(&mut self, regexp: String, flags: String) {
self.ty = Ty::RegExp;
self.regexp = Some((regexp, flags));
self.side_effects = false;
}
pub fn set_wrapped(
&mut self,
prefix: Option<BasicEvaluatedExpression<'a>>,
postfix: Option<BasicEvaluatedExpression<'a>>,
inner_expressions: Vec<BasicEvaluatedExpression<'a>>,
) {
self.ty = Ty::Wrapped;
self.prefix = prefix.map(Box::new);
self.postfix = postfix.map(Box::new);
self.wrapped_inner_expressions = Some(inner_expressions);
self.side_effects = true;
}
pub fn string(&self) -> &String {
self.string.as_ref().expect("make sure string exist")
}
pub fn identifier(&self) -> &Atom {
assert!(self.is_identifier());
self
.identifier
.as_ref()
.expect("make sure identifier exist")
}
pub fn root_info(&self) -> &ExportedVariableInfo {
assert!(self.is_identifier());
self.root_info.as_ref().expect("make sure identifier exist")
}
pub fn members(&self) -> Option<&Vec<Atom>> {
assert!(self.is_identifier());
self.members.as_ref()
}
pub fn members_optionals(&self) -> Option<&Vec<bool>> {
assert!(self.is_identifier());
self.members_optionals.as_ref()
}
pub fn member_ranges(&self) -> Option<&Vec<Span>> {
assert!(self.is_identifier());
self.member_ranges.as_ref()
}
pub fn regexp(&self) -> &Regexp {
self.regexp.as_ref().expect("make sure regexp exist")
}
pub fn bool(&self) -> Boolean {
self.boolean.expect("make sure bool exist")
}
pub fn range(&self) -> (u32, u32) {
let range = self.range.expect("range should not empty");
(range.start, range.end)
}
pub fn prefix(&self) -> Option<&BasicEvaluatedExpression<'a>> {
assert!(self.is_wrapped(), "prefix is only used in wrapped");
self.prefix.as_deref()
}
pub fn postfix(&self) -> Option<&BasicEvaluatedExpression<'a>> {
assert!(self.is_wrapped(), "postfix is only used in wrapped");
self.postfix.as_deref()
}
pub fn wrapped_inner_expressions(&self) -> Option<&[BasicEvaluatedExpression<'a>]> {
assert!(
self.is_wrapped(),
"wrapped_inner_expressions is only used in wrapped"
);
self.wrapped_inner_expressions.as_deref()
}
pub fn template_string_kind(&self) -> TemplateStringKind {
assert!(self.is_template_string());
self
.template_string_kind
.expect("make sure template string exist")
}
pub fn parts(&self) -> &Vec<BasicEvaluatedExpression<'a>> {
assert!(self.is_template_string());
self
.parts
.as_ref()
.expect("make sure template string exist")
}
pub fn quasis(&self) -> &Vec<BasicEvaluatedExpression<'a>> {
assert!(self.is_template_string(),);
self
.quasis
.as_ref()
.expect("quasis must exists for template string")
}
pub fn items(&self) -> &Vec<BasicEvaluatedExpression<'_>> {
assert!(self.is_array());
self.items.as_ref().expect("items must exists for array")
}
pub fn array(&self) -> &Vec<String> {
assert!(self.is_const_array());
self
.array
.as_ref()
.expect("array must exists for const array")
}
pub fn number(&self) -> Number {
assert!(self.is_number());
self.number.expect("number must exists in ty::number")
}
pub fn set_expression(&mut self, expression: Option<&'a Expr>) {
self.expression = expression;
}
pub fn with_expression(mut self, expression: Option<&'a Expr>) -> Self {
self.expression = expression;
self
}
pub fn expression(&self) -> Option<&'a Expr> {
self.expression
}
pub fn with_owned_expression<F>(expr: Expr, f: F) -> Option<BasicEvaluatedExpression<'static>>
where
F: FnOnce(&Expr) -> Option<BasicEvaluatedExpression<'_>>,
{
let expr = Box::new(expr);
let raw_ptr = Box::into_raw(expr);
let mut basic_evaluated_expression = f(unsafe { &*raw_ptr })?;
if basic_evaluated_expression.owned_expression.is_none() {
basic_evaluated_expression.owned_expression = Some(unsafe { Box::from_raw(raw_ptr) });
}
Some(basic_evaluated_expression)
}
}
pub fn evaluate_to_string<'a>(value: String, start: u32, end: u32) -> BasicEvaluatedExpression<'a> {
let mut eval = BasicEvaluatedExpression::with_range(start, end);
eval.set_string(value);
eval
}
pub fn evaluate_to_number<'a>(value: f64, start: u32, end: u32) -> BasicEvaluatedExpression<'a> {
let mut eval = BasicEvaluatedExpression::with_range(start, end);
eval.set_number(value);
eval
}
pub fn evaluate_to_boolean<'a>(value: bool, start: u32, end: u32) -> BasicEvaluatedExpression<'a> {
let mut eval = BasicEvaluatedExpression::with_range(start, end);
eval.set_bool(value);
eval
}
pub fn evaluate_to_null<'a>(start: u32, end: u32) -> BasicEvaluatedExpression<'a> {
let mut eval = BasicEvaluatedExpression::with_range(start, end);
eval.set_null();
eval
}
pub fn evaluate_to_undefined<'a>(start: u32, end: u32) -> BasicEvaluatedExpression<'a> {
let mut eval = BasicEvaluatedExpression::with_range(start, end);
eval.set_undefined();
eval
}
pub fn evaluate_to_identifier<'a>(
identifier: Atom,
root_info: Atom,
truthy: Option<bool>,
start: u32,
end: u32,
) -> BasicEvaluatedExpression<'a> {
let mut eval = BasicEvaluatedExpression::with_range(start, end);
eval.set_identifier(
identifier,
ExportedVariableInfo::Name(root_info),
None,
None,
None,
);
eval.set_side_effects(false);
match truthy {
Some(v) => {
if v {
eval.set_truthy();
} else {
eval.set_falsy();
}
}
None => eval.set_nullish(true),
};
eval
}
bitflags! {
struct RegExpFlag: u8 {
const FLAG_Y = 1 << 0;
const FLAG_M = 1 << 1;
const FLAG_I = 1 << 2;
const FLAG_G = 1 << 3;
}
}
pub fn is_valid_reg_exp_flags(flags: &str) -> bool {
if flags.is_empty() {
true
} else if flags.len() > 4 {
false
} else {
let mut remaining = RegExpFlag::empty();
for c in flags.as_bytes() {
match *c {
b'g' => {
if remaining.contains(RegExpFlag::FLAG_G) {
return false;
}
remaining.insert(RegExpFlag::FLAG_G);
}
b'i' => {
if remaining.contains(RegExpFlag::FLAG_I) {
return false;
}
remaining.insert(RegExpFlag::FLAG_I);
}
b'm' => {
if remaining.contains(RegExpFlag::FLAG_M) {
return false;
}
remaining.insert(RegExpFlag::FLAG_M);
}
b'y' => {
if remaining.contains(RegExpFlag::FLAG_Y) {
return false;
}
remaining.insert(RegExpFlag::FLAG_Y);
}
_ => return false,
}
}
true
}
}