1#[allow(unused_imports)]
3pub use stylex_ast::ast::convertors::{
4 convert_atom_to_str_ref, convert_atom_to_string, convert_concat_to_tpl_expr,
5 convert_lit_to_number, convert_lit_to_string, convert_simple_tpl_to_str_expr,
6 convert_str_lit_to_atom, convert_str_lit_to_string, convert_string_to_prop_name,
7 convert_tpl_to_string_lit, convert_wtf8_to_atom, create_big_int_expr, create_bool_expr,
8 create_ident_expr, create_null_expr, create_number_expr, create_string_expr,
9 expand_shorthand_prop, extract_str_lit_ref, extract_tpl_cooked_value,
10};
11
12use anyhow::anyhow;
13use stylex_macros::{
15 as_expr_or_err, as_expr_or_opt_err, as_expr_or_panic, convert_expr_to_str_or_err, stylex_panic,
16 stylex_unimplemented, unwrap_or_panic,
17};
18use swc_core::ecma::ast::{
19 BinExpr, BinaryOp, Expr, Ident, KeyValueProp, Lit, PropName, Tpl, UnaryExpr, UnaryOp,
20};
21use swc_core::ecma::utils::ExprExt;
22
23use crate::shared::enums::data_structures::evaluate_result_value::EvaluateResultValue;
24use crate::shared::structures::functions::FunctionMap;
25use crate::shared::structures::state::EvaluationState;
26use crate::shared::structures::state_manager::StateManager;
27use crate::shared::utils::common::{
28 evaluate_bin_expr, get_expr_from_var_decl, get_var_decl_by_ident, wrap_key_in_quotes,
29};
30use crate::shared::utils::js::evaluate::{deopt, evaluate_cached};
31use stylex_constants::constants::messages::{
32 ILLEGAL_PROP_VALUE, VAR_DECL_INIT_REQUIRED, non_static_value,
33};
34use stylex_enums::misc::{BinaryExprType, VarDeclAction};
35use stylex_utils::swc::get_default_expr_ctx;
36
37pub fn expr_to_num(
38 expr_num: &Expr,
39 state: &mut EvaluationState,
40 traversal_state: &mut StateManager,
41 fns: &FunctionMap,
42) -> Result<f64, anyhow::Error> {
43 let result = match &expr_num {
44 Expr::Ident(ident) => ident_to_number(ident, state, traversal_state, &FunctionMap::default()),
45 Expr::Lit(lit) => return convert_lit_to_number(lit),
46 Expr::Unary(unary) => convert_unary_to_num(unary, state, traversal_state, fns),
47 Expr::Bin(lit) => {
48 let mut state = Box::new(EvaluationState::new());
49
50 match binary_expr_to_num(lit, &mut state, traversal_state, fns)
51 .unwrap_or_else(|error| stylex_panic!("{}", error))
52 {
53 BinaryExprType::Number(number) => number,
54 _ => stylex_panic!(
55 "Binary expression is not a number: {:?}",
56 expr_num.get_type(get_default_expr_ctx())
57 ),
58 }
59 },
60 _ => stylex_panic!(
61 "Expression in not a number: {:?}",
62 expr_num.get_type(get_default_expr_ctx())
63 ),
64 };
65
66 Result::Ok(result)
67}
68
69fn ident_to_string(ident: &Ident, state: &mut StateManager, functions: &FunctionMap) -> String {
70 let var_decl = get_var_decl_by_ident(ident, state, functions, VarDeclAction::Reduce);
71
72 match &var_decl {
73 Some(var_decl) => {
74 let var_decl_expr = get_expr_from_var_decl(var_decl);
75
76 match &var_decl_expr {
77 Expr::Lit(lit) => match convert_lit_to_string(lit) {
78 Some(s) => s,
79 None => stylex_panic!("{}", ILLEGAL_PROP_VALUE),
80 },
81 Expr::Ident(ident) => ident_to_string(ident, state, functions),
82 _ => stylex_panic!("{}", ILLEGAL_PROP_VALUE),
83 }
84 },
85 None => stylex_panic!("{}", ILLEGAL_PROP_VALUE),
86 }
87}
88
89#[inline]
90pub fn convert_ident_to_expr(
91 ident: &Ident,
92 state: &mut StateManager,
93 functions: &FunctionMap,
94) -> Expr {
95 match get_var_decl_by_ident(ident, state, functions, VarDeclAction::Reduce) {
96 Some(var_decl) => get_expr_from_var_decl(&var_decl).clone(),
97 _ => {
98 stylex_panic!("{}", ILLEGAL_PROP_VALUE)
99 },
100 }
101}
102
103pub fn convert_expr_to_str(
104 expr_string: &Expr,
105 state: &mut StateManager,
106 functions: &FunctionMap,
107) -> Option<String> {
108 match &expr_string {
109 Expr::Ident(ident) => Some(ident_to_string(ident, state, functions)),
110 Expr::Lit(lit) => convert_lit_to_string(lit),
111 _ => stylex_panic!(
112 "Expression in not a string, got {:?}",
113 expr_string.get_type(get_default_expr_ctx())
114 ),
115 }
116}
117
118pub fn convert_unary_to_num(
119 unary_expr: &UnaryExpr,
120 state: &mut EvaluationState,
121 traversal_state: &mut StateManager,
122 fns: &FunctionMap,
123) -> f64 {
124 let arg = unary_expr.arg.as_ref();
125 let op = unary_expr.op;
126
127 match &op {
128 UnaryOp::Minus => match expr_to_num(arg, state, traversal_state, fns) {
129 Ok(result) => -result,
130 Err(error) => stylex_panic!("{}", error),
131 },
132 UnaryOp::Plus => match expr_to_num(arg, state, traversal_state, fns) {
133 Ok(result) => result,
134 Err(error) => stylex_panic!("{}", error),
135 },
136 _ => stylex_panic!(
137 "Union operation '{:?}' is invalid",
138 Expr::from(unary_expr.clone()).get_type(get_default_expr_ctx())
139 ),
140 }
141}
142
143pub fn binary_expr_to_num(
144 binary_expr: &BinExpr,
145 state: &mut EvaluationState,
146 traversal_state: &mut StateManager,
147 fns: &FunctionMap,
148) -> Result<BinaryExprType, anyhow::Error> {
149 let op = binary_expr.op;
150 let Some(left) = evaluate_cached(&binary_expr.left, state, traversal_state, fns) else {
151 if !state.confident {
152 return Result::Err(anyhow::anyhow!("Left expression is not a number"));
153 }
154
155 stylex_panic!("Left expression is not a number")
156 };
157
158 let left_expr = as_expr_or_err!(left, "Left argument not expression");
159 let left_num = expr_to_num(left_expr, state, traversal_state, fns)?;
160
161 let Some(right) = evaluate_cached(&binary_expr.right, state, traversal_state, fns) else {
162 if !state.confident {
163 if op == BinaryOp::LogicalOr && left_num != 0.0 {
164 state.confident = true;
165
166 return Result::Ok(BinaryExprType::Number(left_num));
167 }
168
169 return Result::Err(anyhow::anyhow!("Right expression is not a number"));
170 }
171
172 stylex_panic!("Right expression is not a number")
173 };
174
175 let right_expr = as_expr_or_err!(right, "Right argument not expression");
176 let right_num = expr_to_num(right_expr, state, traversal_state, fns)?;
177
178 let result = match &op {
179 BinaryOp::Add => {
180 if let Some(value) =
181 evaluate_left_and_right_expression(state, traversal_state, fns, &left, &right)
182 {
183 return value;
184 }
185
186 left_num + right_num
187 },
188 BinaryOp::Sub => left_num - right_num,
189 BinaryOp::Mul => left_num * right_num,
190 BinaryOp::Div => left_num / right_num,
191 BinaryOp::Mod => left_num % right_num,
192 BinaryOp::Exp => left_num.powf(right_num),
193 BinaryOp::RShift => ((left_num as i32) >> right_num as i32) as f64,
194 BinaryOp::LShift => ((left_num as i32) << right_num as i32) as f64,
195 BinaryOp::BitAnd => ((left_num as i32) & right_num as i32) as f64,
196 BinaryOp::BitOr => ((left_num as i32) | right_num as i32) as f64,
197 BinaryOp::BitXor => ((left_num as i32) ^ right_num as i32) as f64,
198 BinaryOp::In => {
199 if right_num == 0.0 {
200 1.0
201 } else {
202 0.0
203 }
204 },
205 BinaryOp::InstanceOf => {
206 if right_num == 0.0 {
207 1.0
208 } else {
209 0.0
210 }
211 },
212 BinaryOp::EqEq => {
213 if left_num == right_num {
214 1.0
215 } else {
216 0.0
217 }
218 },
219 BinaryOp::NotEq => {
220 if left_num != right_num {
221 1.0
222 } else {
223 0.0
224 }
225 },
226 BinaryOp::EqEqEq => {
227 if left_num == right_num {
228 1.0
229 } else {
230 0.0
231 }
232 },
233 BinaryOp::NotEqEq => {
234 if left_num != right_num {
235 1.0
236 } else {
237 0.0
238 }
239 },
240 BinaryOp::Lt => {
241 if left_num < right_num {
242 1.0
243 } else {
244 0.0
245 }
246 },
247 BinaryOp::LtEq => {
248 if left_num <= right_num {
249 1.0
250 } else {
251 0.0
252 }
253 },
254 BinaryOp::Gt => {
255 if left_num > right_num {
256 1.0
257 } else {
258 0.0
259 }
260 },
261 BinaryOp::GtEq => {
262 if left_num >= right_num {
263 1.0
264 } else {
265 0.0
266 }
267 },
268 BinaryOp::LogicalOr => {
270 if let Some(value) =
271 evaluate_left_and_right_expression(state, traversal_state, fns, &left, &right)
272 {
273 return value;
274 }
275
276 if left_num != 0.0 { left_num } else { right_num }
277 },
278 BinaryOp::LogicalAnd => {
279 if let Some(value) =
280 evaluate_left_and_right_expression(state, traversal_state, fns, &left, &right)
281 {
282 return value;
283 }
284
285 if left_num != 0.0 { right_num } else { left_num }
286 },
287 BinaryOp::NullishCoalescing => {
288 if let Some(value) =
289 evaluate_left_and_right_expression(state, traversal_state, fns, &left, &right)
290 {
291 return value;
292 }
293
294 if left_num == 0.0 { right_num } else { left_num }
295 },
296 BinaryOp::ZeroFillRShift => ((left_num as i32) >> right_num as i32) as f64,
298 };
299
300 Result::Ok(BinaryExprType::Number(result))
301}
302
303pub fn binary_expr_to_string(
304 binary_expr: &BinExpr,
305 state: &mut EvaluationState,
306 traversal_state: &mut StateManager,
307 fns: &FunctionMap,
308) -> Result<BinaryExprType, anyhow::Error> {
309 let op = binary_expr.op;
310 let Some(left) = evaluate_cached(&binary_expr.left, state, traversal_state, fns) else {
311 if !state.confident {
312 return Result::Err(anyhow::anyhow!("Left expression is not a string"));
313 }
314
315 stylex_panic!("Left expression is not a string")
316 };
317
318 let left_expr = as_expr_or_err!(left, "Left argument not expression");
319 let left_str = convert_expr_to_str_or_err!(
320 left_expr,
321 traversal_state,
322 fns,
323 "Left expression is not a string"
324 );
325
326 let Some(right) = evaluate_cached(&binary_expr.right, state, traversal_state, fns) else {
327 if !state.confident {
328 if op == BinaryOp::LogicalOr {
329 state.confident = true;
330
331 return Result::Ok(BinaryExprType::String(left_str));
332 }
333
334 return Result::Err(anyhow::anyhow!("Right expression is not a string"));
335 }
336
337 stylex_panic!("Right expression is not a string")
338 };
339
340 let right_expr = as_expr_or_err!(right, "Right argument not expression");
341 let right_str = convert_expr_to_str_or_err!(
342 right_expr,
343 traversal_state,
344 fns,
345 "Right expression is not a string"
346 );
347
348 let result = match &op {
349 BinaryOp::Add => {
350 format!("{}{}", left_str, right_str)
351 },
352 _ => stylex_panic!(
353 "For string expressions, only addition is supported, got {:?}",
354 op
355 ),
356 };
357
358 Result::Ok(BinaryExprType::String(result))
359}
360
361fn evaluate_left_and_right_expression(
362 state: &mut EvaluationState,
363 traversal_state: &mut StateManager,
364 fns: &FunctionMap,
365 left: &EvaluateResultValue,
366 right: &EvaluateResultValue,
367) -> Option<Result<BinaryExprType, anyhow::Error>> {
368 let left_expr = as_expr_or_opt_err!(left, "Left argument not expression");
369 let right_expr = as_expr_or_opt_err!(right, "Right argument not expression");
370
371 let mut state_for_left = EvaluationState {
372 confident: true,
373 deopt_path: None,
374 ..state.clone()
375 };
376 let left_result = expr_to_num(left_expr, &mut state_for_left, traversal_state, fns);
377 let left_confident = state.confident;
378
379 let mut state_for_right = EvaluationState {
380 confident: true,
381 deopt_path: None,
382 ..state.clone()
383 };
384 let right_result = expr_to_num(right_expr, &mut state_for_right, traversal_state, fns);
385 let right_confident = state.confident;
386
387 if left_result.is_err() || right_result.is_err() {
388 let left_str = match left_expr {
389 Expr::Lit(Lit::Str(_)) => match left_expr.as_lit() {
390 Some(lit) => convert_lit_to_string(lit).unwrap_or_else(|| {
391 stylex_panic!(
392 "Left is not a string: {:?}",
393 left_expr.get_type(get_default_expr_ctx())
394 )
395 }),
396 None => stylex_panic!(
397 "Left is not a string: {:?}",
398 left_expr.get_type(get_default_expr_ctx())
399 ),
400 },
401 _ => String::default(),
402 };
403
404 let right_str = match right_expr {
405 Expr::Lit(Lit::Str(_)) => match right_expr.as_lit() {
406 Some(lit) => convert_lit_to_string(lit).unwrap_or_else(|| {
407 stylex_panic!(
408 "Right is not a string: {:?}",
409 left_expr.get_type(get_default_expr_ctx())
410 )
411 }),
412 None => stylex_panic!(
413 "Right is not a string: {:?}",
414 left_expr.get_type(get_default_expr_ctx())
415 ),
416 },
417 _ => String::default(),
418 };
419
420 if !left_str.is_empty() && !right_str.is_empty() {
421 return Some(Result::Ok(BinaryExprType::String(format!(
422 "{}{}",
423 left_str, right_str
424 ))));
425 }
426 }
427
428 if !left_confident {
429 let deopt_reason = state_for_left
430 .deopt_reason
431 .as_deref()
432 .unwrap_or("unknown error")
433 .to_string();
434
435 deopt(left_expr, state, &deopt_reason);
436
437 return Some(Result::Ok(BinaryExprType::Null));
438 }
439
440 if !right_confident {
441 let deopt_reason = state_for_right
442 .deopt_reason
443 .as_deref()
444 .unwrap_or("unknown error")
445 .to_string();
446
447 deopt(right_expr, state, &deopt_reason);
448
449 return Some(Result::Ok(BinaryExprType::Null));
450 }
451
452 None
453}
454
455pub fn ident_to_number(
456 ident: &Ident,
457 state: &mut EvaluationState,
458 traversal_state: &mut StateManager,
459 fns: &FunctionMap,
460) -> f64 {
461 let var_decl = get_var_decl_by_ident(ident, traversal_state, fns, VarDeclAction::Reduce);
462
463 match &var_decl {
464 Some(var_decl) => {
465 let var_decl_expr = get_expr_from_var_decl(var_decl);
466
467 match &var_decl_expr {
468 Expr::Bin(bin_expr) => {
469 match binary_expr_to_num(bin_expr, state, traversal_state, fns)
470 .unwrap_or_else(|error| stylex_panic!("{}", error))
471 {
472 BinaryExprType::Number(number) => number,
473 _ => stylex_panic!(
474 "Binary expression is not a number: {:?}",
475 var_decl_expr.get_type(get_default_expr_ctx())
476 ),
477 }
478 },
479 Expr::Unary(unary_expr) => convert_unary_to_num(unary_expr, state, traversal_state, fns),
480 Expr::Lit(lit) => {
481 convert_lit_to_number(lit).unwrap_or_else(|error| stylex_panic!("{}", error))
482 },
483 _ => stylex_panic!(
484 "Varable {:?} is not a number",
485 var_decl_expr.get_type(get_default_expr_ctx())
486 ),
487 }
488 },
489 None => {
490 stylex_panic!("Variable {} is not declared", ident.sym)
491 },
492 }
493}
494
495pub fn handle_tpl_to_expression(
496 tpl: &Tpl,
497 state: &mut StateManager,
498 functions: &FunctionMap,
499) -> Expr {
500 let mut tpl = tpl.clone();
502
503 for expr in tpl.exprs.iter_mut() {
505 if let Expr::Ident(ident) = expr.as_ref() {
507 let var_decl = get_var_decl_by_ident(ident, state, functions, VarDeclAction::Reduce);
509
510 if let Some(var_decl) = &var_decl {
512 *expr = match var_decl.init.clone() {
514 Some(init) => init,
515 None => stylex_panic!("{}", VAR_DECL_INIT_REQUIRED),
516 };
517 }
518 };
519 }
520
521 Expr::Tpl(tpl)
522}
523pub fn expr_tpl_to_string(
524 tpl: &Tpl,
525 state: &mut EvaluationState,
526 traversal_state: &mut StateManager,
527 fns: &FunctionMap,
528) -> String {
529 let mut tpl_str: String = String::new();
530
531 for (i, quasi) in tpl.quasis.iter().enumerate() {
532 tpl_str.push_str(quasi.raw.as_ref());
533
534 if i < tpl.exprs.len() {
535 match &tpl.exprs[i].as_ref() {
536 Expr::Ident(ident) => {
537 let ident = get_var_decl_by_ident(ident, traversal_state, fns, VarDeclAction::Reduce);
538
539 match ident {
540 Some(var_decl) => {
541 let var_decl_expr = get_expr_from_var_decl(&var_decl);
542
543 let value = match &var_decl_expr {
544 Expr::Lit(lit) => match convert_lit_to_string(lit) {
545 Some(s) => s,
546 None => stylex_panic!("{}", ILLEGAL_PROP_VALUE),
547 },
548 _ => stylex_panic!("{}", ILLEGAL_PROP_VALUE),
549 };
550
551 tpl_str.push_str(value.as_str());
552 },
553 None => stylex_panic!("{}", non_static_value("expr_tpl_to_string")),
554 }
555 },
556 Expr::Bin(bin) => tpl_str.push_str(
557 transform_bin_expr_to_number(bin, state, traversal_state, fns)
558 .to_string()
559 .as_str(),
560 ),
561 Expr::Lit(lit) => tpl_str.push_str(&match convert_lit_to_string(lit) {
562 Some(s) => s,
563 None => stylex_panic!("{}", ILLEGAL_PROP_VALUE),
564 }),
565 _ => stylex_unimplemented!(
566 "TPL expression: {:?}",
567 tpl.exprs[i].get_type(get_default_expr_ctx())
568 ),
569 }
570 }
571 }
572
573 tpl_str
574}
575
576pub fn transform_bin_expr_to_number(
577 bin: &BinExpr,
578 state: &mut EvaluationState,
579 traversal_state: &mut StateManager,
580 fns: &FunctionMap,
581) -> f64 {
582 let op = bin.op;
583 let Some(left) = evaluate_cached(&bin.left, state, traversal_state, fns) else {
584 stylex_panic!(
585 "Left expression is not a number: {:?}",
586 bin.left.get_type(get_default_expr_ctx())
587 )
588 };
589
590 let Some(right) = evaluate_cached(&bin.right, state, traversal_state, fns) else {
591 stylex_panic!(
592 "Left expression is not a number: {:?}",
593 bin.right.get_type(get_default_expr_ctx())
594 )
595 };
596
597 let left_expr = as_expr_or_panic!(left, "Left argument not expression");
598 let right_expr = as_expr_or_panic!(right, "Right argument not expression");
599
600 let left = unwrap_or_panic!(expr_to_num(left_expr, state, traversal_state, fns));
601 let right = unwrap_or_panic!(expr_to_num(right_expr, state, traversal_state, fns));
602
603 evaluate_bin_expr(op, left, right)
604}
605
606#[inline]
607pub fn convert_expr_to_bool(
608 expr: &Expr,
609 state: &mut StateManager,
610 functions: &FunctionMap,
611) -> bool {
612 match expr {
613 Expr::Lit(lit) => match lit {
614 Lit::Bool(b) => b.value,
615 Lit::Num(n) => n.value != 0.0,
616 Lit::Str(s) => !s.value.is_empty(),
617 Lit::Null(_) => false,
618 _ => stylex_unimplemented!(
619 "Conversion {:?} expression to boolean",
620 expr.get_type(get_default_expr_ctx())
621 ),
622 },
623 Expr::Ident(ident) => convert_expr_to_bool(
624 &convert_ident_to_expr(ident, state, functions),
625 state,
626 functions,
627 ),
628 Expr::Array(_) => true,
629 Expr::Object(_) => true,
630 Expr::Fn(_) | Expr::Class(_) => true,
631 Expr::Unary(unary) => match unary.op {
632 UnaryOp::Void => false,
633 UnaryOp::TypeOf => true,
634 UnaryOp::Bang => !convert_expr_to_bool(&unary.arg, state, functions),
635 UnaryOp::Minus => !convert_expr_to_bool(&unary.arg, state, functions),
636 UnaryOp::Plus => !convert_expr_to_bool(&unary.arg, state, functions),
637 UnaryOp::Tilde => !convert_expr_to_bool(&unary.arg, state, functions),
638 _ => stylex_unimplemented!(
639 "Conversion {:?} expression to boolean",
640 expr.get_type(get_default_expr_ctx())
641 ),
642 },
643 _ => {
644 stylex_unimplemented!(
645 "Conversion {:?} expression to boolean",
646 expr.get_type(get_default_expr_ctx())
647 )
648 },
649 }
650}
651
652#[inline]
653pub(crate) fn convert_key_value_to_str(key_value: &KeyValueProp) -> String {
654 let key = &key_value.key;
655 let mut should_wrap_in_quotes = false;
656
657 let key = match key {
658 PropName::Ident(ident) => ident.sym.to_string(),
659 PropName::Str(strng) => {
660 should_wrap_in_quotes = false;
661 convert_str_lit_to_string(strng)
662 },
663 PropName::Num(num) => {
664 should_wrap_in_quotes = false;
665 num.value.to_string()
666 },
667 PropName::BigInt(big_int) => {
668 should_wrap_in_quotes = false;
669 big_int.value.to_string()
670 },
671 PropName::Computed(computed) => match computed.expr.as_lit() {
672 Some(lit) => match convert_lit_to_string(lit) {
673 Some(s) => s,
674 None => stylex_panic!("Computed property key must be a string or number literal."),
675 },
676 None => stylex_unimplemented!("Computed key is not a literal"),
677 },
678 };
679
680 wrap_key_in_quotes(&key, should_wrap_in_quotes)
681}