Skip to main content

stylex_transform/shared/utils/js/
evaluate.rs

1use std::{borrow::Borrow, rc::Rc, sync::Arc};
2
3// Import error handling macros from shared utilities
4use crate::{expr_to_str_or_deopt, stylex_panic_with_context};
5use stylex_constants::constants::common::{MUTATING_ARRAY_METHODS, MUTATING_OBJECT_METHODS};
6
7use indexmap::IndexMap;
8use log::{debug, warn};
9use rustc_hash::{FxHashMap, FxHashSet};
10use stylex_macros::{
11  collect_confident, stylex_panic, stylex_unimplemented, stylex_unreachable, unwrap_or_panic,
12};
13use swc_core::{
14  atoms::Atom,
15  common::{EqIgnoreSpan, SyntaxContext},
16  ecma::{
17    ast::{
18      ArrayLit, AssignTarget, BlockStmtOrExpr, CallExpr, Callee, ComputedPropName, Expr,
19      ExprOrSpread, Ident, ImportSpecifier, KeyValueProp, Lit, MemberProp, ModuleExportName,
20      Number, ObjectLit, OptChainBase, Pat, Prop, PropName, PropOrSpread, SimpleAssignTarget,
21      TplElement, UnaryOp, VarDeclarator,
22    },
23    utils::{ExprExt, drop_span, ident::IdentLike, quote_ident},
24  },
25};
26
27use crate::shared::enums::data_structures::evaluate_result_value::EvaluateResultValue;
28use crate::shared::structures::evaluate_result::EvaluateResult;
29use crate::shared::structures::functions::{
30  CallbackType, FunctionConfig, FunctionConfigType, FunctionMap, FunctionType,
31};
32use crate::shared::structures::seen_value::SeenValue;
33use crate::shared::structures::state::EvaluationState;
34use crate::shared::structures::state_manager::{
35  SeenValueWithVarDeclCount, StateManager, add_import_expression,
36};
37use crate::shared::structures::theme_ref::ThemeRef;
38use crate::shared::structures::types::{FunctionMapIdentifiers, FunctionMapMemberExpression};
39use crate::shared::utils::ast::convertors::{
40  binary_expr_to_num, binary_expr_to_string, convert_atom_to_str_ref, convert_atom_to_string,
41  convert_expr_to_bool, convert_expr_to_str, convert_key_value_to_str, convert_lit_to_string,
42  create_big_int_expr, create_bool_expr, create_number_expr, create_string_expr,
43  expand_shorthand_prop, expr_to_num, extract_tpl_cooked_value,
44};
45use crate::shared::utils::common::{
46  char_code_at, deep_merge_props, get_hash_map_difference, get_hash_map_value_difference,
47  get_import_by_ident, get_key_values_from_object, get_var_decl_by_ident, get_var_decl_from,
48  normalize_expr, reduce_ident_count, reduce_member_expression_count, remove_duplicates,
49  sort_numbers_factory, stable_hash, sum_hash_map_values,
50};
51use crate::shared::utils::js::native_functions::{evaluate_filter, evaluate_join, evaluate_map};
52use stylex_ast::ast::factories::{
53  create_array_expression, create_expr_or_spread, create_ident_key_value_prop,
54  create_object_expression, create_object_lit, create_string_lit,
55};
56use stylex_constants::constants::common::{INVALID_METHODS, VALID_CALLEES};
57use stylex_constants::constants::evaluation_errors::{
58  IMPORT_PATH_RESOLUTION_ERROR, NON_CONSTANT, OBJECT_METHOD, PATH_WITHOUT_NODE,
59  UNEXPECTED_MEMBER_LOOKUP, unsupported_expression, unsupported_operator,
60};
61use stylex_constants::constants::messages::{
62  ARGUMENT_NOT_EXPRESSION, BUILT_IN_FUNCTION, EXPECTED_CSS_VAR, EXPRESSION_IS_NOT_A_STRING,
63  ILLEGAL_PROP_ARRAY_VALUE, INVALID_UTF8, KEY_VALUE_EXPECTED, MEMBER_NOT_RESOLVED,
64  MEMBER_OBJ_NOT_IDENT, OBJECT_KEY_MUST_BE_IDENT, PROPERTY_NOT_FOUND, SPREAD_MUST_BE_OBJECT,
65  SPREAD_NOT_SUPPORTED, THEME_IMPORT_KEY_AS_OBJECT_KEY, VALUE_MUST_BE_LITERAL,
66};
67use stylex_enums::core::TransformationCycle;
68use stylex_enums::import_path_resolution::{ImportPathResolution, ImportPathResolutionType};
69use stylex_enums::js::{ArrayJS, MathJS, ObjectJS, StringJS};
70use stylex_enums::misc::{BinaryExprType, VarDeclAction};
71use stylex_enums::value_with_default::ValueWithDefault;
72use stylex_structures::named_import_source::ImportSources;
73use stylex_structures::stylex_env::EnvEntry;
74use stylex_utils::swc::get_default_expr_ctx;
75
76use super::check_declaration::{DeclarationType, check_ident_declaration};
77
78/// Resolves an `EnvEntry` to an `EvaluateResultValue`.
79///
80/// - `Expr` → `EvaluateResultValue::Expr`
81/// - `Function` → returns the parent map so callers resolve the function at the call-expression site
82#[inline]
83fn resolve_env_entry_to_result(
84  entry: &EnvEntry,
85  parent_map: &IndexMap<String, EnvEntry>,
86) -> Option<EvaluateResultValue> {
87  match entry {
88    EnvEntry::Expr(expr) => Some(EvaluateResultValue::Expr(expr.clone())),
89    EnvEntry::Function(_) => Some(EvaluateResultValue::EnvObject(parent_map.clone())),
90  }
91}
92
93/// Converts `EvaluateResultValue::Vec` items into an `Expr::Array`.
94///
95/// Each item may itself be a nested `Vec` (converted to a sub-array) or a plain `Expr`.
96/// Only `Array`, `Object`, `Lit`, and `Ident` expressions are allowed as element values;
97/// any other variant panics with [`ILLEGAL_PROP_ARRAY_VALUE`].
98fn evaluate_result_vec_to_array_expr(items: &[Option<EvaluateResultValue>]) -> Expr {
99  let elems = items
100    .iter()
101    .map(|entry| {
102      let expr = entry
103        .as_ref()
104        .and_then(|entry| {
105          entry
106            .as_vec()
107            .map(|vec| evaluate_result_vec_to_array_expr(vec))
108            .or_else(|| entry.as_expr().cloned())
109        })
110        .unwrap_or_else(|| stylex_panic!("{}", ILLEGAL_PROP_ARRAY_VALUE));
111
112      let expr = match expr {
113        Expr::Array(array) => Expr::Array(array),
114        Expr::Object(obj) => Expr::Object(obj),
115        Expr::Lit(lit) => Expr::Lit(lit),
116        Expr::Ident(ident) => Expr::Ident(ident),
117        _ => stylex_panic!("{}", ILLEGAL_PROP_ARRAY_VALUE),
118      };
119
120      Some(create_expr_or_spread(expr))
121    })
122    .collect();
123
124  create_array_expression(elems)
125}
126
127/// Helper function to evaluate unary numeric operations (Plus, Minus, Tilde).
128/// This reduces code duplication for operations that convert an expression to a number,
129/// apply a transformation, and return the result as an expression.
130///
131/// # Arguments
132/// * `arg` - The expression argument to the unary operator
133/// * `state` - The evaluation state
134/// * `traversal_state` - The state manager for traversal context
135/// * `fns` - The function map for evaluating function calls
136/// * `transform` - A function to transform the numeric value
137///
138/// # Example
139/// ```ignore
140/// UnaryOp::Plus => evaluate_unary_numeric(&arg, state, traversal_state, fns, |v| v),
141/// UnaryOp::Minus => evaluate_unary_numeric(&arg, state, traversal_state, fns, |v| -v),
142/// ```
143#[inline]
144fn evaluate_unary_numeric(
145  arg: &Expr,
146  state: &mut EvaluationState,
147  traversal_state: &mut StateManager,
148  fns: &FunctionMap,
149  transform: impl FnOnce(f64) -> f64,
150) -> Option<EvaluateResultValue> {
151  let value = unwrap_or_panic!(expr_to_num(arg, state, traversal_state, fns));
152  Some(EvaluateResultValue::Expr(create_number_expr(transform(
153    value,
154  ))))
155}
156
157pub(crate) fn evaluate_obj_key(
158  prop_kv: &KeyValueProp,
159  state: &mut StateManager,
160  functions: &FunctionMap,
161) -> EvaluateResult {
162  let key_path = &prop_kv.key;
163
164  let key = match key_path {
165    PropName::Ident(ident) => create_string_expr(&ident.sym),
166    PropName::Computed(computed) => {
167      let computed_result = evaluate(&computed.expr, state, functions);
168      if computed_result.confident {
169        match computed_result.value {
170          Some(EvaluateResultValue::Expr(ref value)) => value.clone(),
171          _ => stylex_panic!("Expected an expression value from the evaluation result."),
172        }
173      } else {
174        return EvaluateResult {
175          confident: false,
176          deopt: computed_result.deopt,
177          reason: computed_result.reason,
178          value: None,
179          inline_styles: None,
180          fns: None,
181        };
182      }
183    },
184    PropName::Str(strng) => create_string_expr(&convert_atom_to_string(&strng.value)),
185    PropName::Num(num) => create_number_expr(num.value),
186    PropName::BigInt(big_int) => create_big_int_expr(big_int.clone()),
187  };
188
189  let key_expr = match convert_expr_to_str(&key, state, functions) {
190    Some(ref s) => create_string_expr(s),
191    None => {
192      return EvaluateResult {
193        confident: false,
194        deopt: Some(key),
195        reason: Some("Key is not a string".to_string()),
196        value: None,
197        inline_styles: None,
198        fns: None,
199      };
200    },
201  };
202
203  EvaluateResult {
204    confident: true,
205    deopt: None,
206    reason: None,
207    value: Some(EvaluateResultValue::Expr(key_expr)),
208    inline_styles: None,
209    fns: None,
210  }
211}
212
213pub fn evaluate(
214  path: &Expr,
215  traversal_state: &mut StateManager,
216  fns: &FunctionMap,
217) -> Box<EvaluateResult> {
218  let mut state = Box::new(EvaluationState {
219    confident: true,
220    deopt_path: None,
221    deopt_reason: None,
222    added_imports: FxHashSet::default(),
223    functions: fns.clone(),
224  });
225
226  let mut value = evaluate_cached(path, &mut state, traversal_state, fns);
227
228  if !state.confident {
229    value = None;
230  }
231
232  Box::new(EvaluateResult {
233    confident: state.confident,
234    value,
235    deopt: state.deopt_path,
236    reason: state.deopt_reason,
237    inline_styles: None,
238    fns: None,
239  })
240}
241
242pub(crate) fn deopt(
243  path: &Expr,
244  state: &mut EvaluationState,
245  reason: &str,
246) -> Option<EvaluateResultValue> {
247  if state.confident {
248    state.confident = false;
249    state.deopt_path = Some(path.clone());
250    state.deopt_reason = Some(reason.to_string());
251  }
252
253  None
254}
255
256fn _evaluate(
257  path: &mut Expr,
258  state: &mut EvaluationState,
259  traversal_state: &mut StateManager,
260  fns: &FunctionMap,
261) -> Option<EvaluateResultValue> {
262  if !state.confident {
263    return None;
264  }
265
266  let normalized_path = normalize_expr(path);
267
268  if is_mutation_expr(normalized_path) {
269    return deopt(path, state, NON_CONSTANT);
270  }
271
272  let result: Option<EvaluateResultValue> = match normalized_path {
273    Expr::Arrow(arrow) => {
274      let body = &arrow.body;
275      let params = &arrow.params;
276
277      let ident_params = params
278        .iter()
279        .filter_map(|param| {
280          if let Pat::Ident(ident) = param {
281            Some(ident.sym.clone())
282          } else {
283            None
284          }
285        })
286        .collect::<Vec<Atom>>();
287
288      match body.as_ref() {
289        BlockStmtOrExpr::Expr(body_expr) => {
290          if ident_params.len() == params.len() {
291            let arrow_closure_fabric =
292              |identifiers: FunctionMapIdentifiers,
293               ident_params: Vec<Atom>,
294               body_expr: Box<Expr>,
295               traversal_state: StateManager| {
296                move |cb_args: Vec<Option<EvaluateResultValue>>| {
297                  let mut identifiers = identifiers.clone();
298
299                  let mut member_expressions: FunctionMapMemberExpression = FxHashMap::default();
300
301                  ident_params.iter().enumerate().for_each(|(index, ident)| {
302                    if let Some(arg) = cb_args.get(index) {
303                      let expr = arg
304                        .as_ref()
305                        .and_then(|arg| arg.as_expr())
306                        .unwrap_or_else(|| stylex_panic!("{}", ARGUMENT_NOT_EXPRESSION));
307
308                      let cl = |arg: Expr| move || arg.clone();
309
310                      let result = (cl)(expr.clone());
311                      let function = FunctionConfig {
312                        fn_ptr: FunctionType::Mapper(Rc::new(result)),
313                        takes_path: false,
314                      };
315                      identifiers.insert(
316                        ident.clone(),
317                        Box::new(FunctionConfigType::Regular(function.clone())),
318                      );
319
320                      member_expressions.insert(
321                        ImportSources::Regular("entry".to_string()),
322                        Box::new(identifiers.clone()),
323                      );
324                    }
325                  });
326
327                  let mut local_state = traversal_state.clone();
328
329                  let result = evaluate(
330                    &body_expr,
331                    &mut local_state,
332                    &FunctionMap {
333                      identifiers,
334                      member_expressions,
335                      disable_imports: false,
336                    },
337                  );
338
339                  let value = result.value;
340
341                  match value {
342                    Some(res) => match res {
343                      EvaluateResultValue::Expr(expr) => expr.clone(),
344                      EvaluateResultValue::Vec(items) => evaluate_result_vec_to_array_expr(&items),
345                      _ => stylex_unimplemented!(
346                        "The evaluation result must resolve to a static expression."
347                      ),
348                    },
349                    None => *body_expr.clone(),
350                  }
351                }
352              };
353
354            let identifiers = state.functions.identifiers.clone();
355
356            let arrow_closure = Rc::new(arrow_closure_fabric(
357              identifiers,
358              ident_params,
359              Box::new(*body_expr.clone()),
360              traversal_state.clone(),
361            ));
362
363            return Some(EvaluateResultValue::Callback(arrow_closure));
364          }
365
366          None
367        },
368        BlockStmtOrExpr::BlockStmt(_) => None,
369      }
370    },
371    Expr::Ident(ident) => {
372      let atom_ident_id = &ident.sym;
373
374      if let Some(func) = state.functions.identifiers.get(atom_ident_id) {
375        match func.as_ref() {
376          FunctionConfigType::Regular(func) => match &func.fn_ptr {
377            FunctionType::Mapper(func) => {
378              return Some(EvaluateResultValue::Expr(func()));
379            },
380            FunctionType::DefaultMarker(func) => {
381              return Some(EvaluateResultValue::FunctionConfig(FunctionConfig {
382                fn_ptr: FunctionType::DefaultMarker(Arc::clone(func)),
383                takes_path: false,
384              }));
385            },
386            _ => {
387              return deopt(path, state, "Function not found");
388            },
389          },
390          FunctionConfigType::Map(func_map) => {
391            return Some(EvaluateResultValue::FunctionConfigMap(func_map.clone()));
392          },
393          FunctionConfigType::IndexMap(_func_map) => {
394            stylex_unimplemented!("IndexMap values are not supported in this context.");
395          },
396          FunctionConfigType::EnvObject(env_map) => {
397            return Some(EvaluateResultValue::EnvObject(env_map.clone()));
398          },
399        }
400      }
401
402      None
403    },
404    Expr::TsSatisfies(ts_satisfaies) => {
405      evaluate_cached(&ts_satisfaies.expr, state, traversal_state, fns)
406    },
407    Expr::TsConstAssertion(ts_const) => {
408      evaluate_cached(&ts_const.expr, state, traversal_state, fns)
409    },
410    Expr::TsAs(ts_as) => evaluate_cached(&ts_as.expr, state, traversal_state, fns),
411    Expr::TsNonNull(ts_non_null) => evaluate_cached(&ts_non_null.expr, state, traversal_state, fns),
412    Expr::TsTypeAssertion(ts_type) => evaluate_cached(&ts_type.expr, state, traversal_state, fns),
413    Expr::TsInstantiation(ts_instantiation) => {
414      evaluate_cached(&ts_instantiation.expr, state, traversal_state, fns)
415    },
416    Expr::Seq(sec) => {
417      let expr = match sec.exprs.last() {
418        Some(e) => e,
419        None => stylex_panic!("Sequence expression must contain at least one expression."),
420      };
421
422      evaluate_cached(expr, state, traversal_state, fns)
423    },
424    Expr::Lit(lit_path) => Some(EvaluateResultValue::Expr(Expr::Lit(lit_path.clone()))),
425    Expr::Tpl(tpl) => evaluate_quasis(
426      &Expr::Tpl(tpl.clone()),
427      &tpl.quasis,
428      false,
429      state,
430      traversal_state,
431      fns,
432    ),
433    Expr::TaggedTpl(_tagged_tpl) => {
434      stylex_panic_with_context!(
435        path,
436        traversal_state,
437        "Tagged template literals are not supported in static evaluation."
438      )
439      // TODO: Uncomment this for implementation of TaggedTpl
440      // evaluate_quasis(
441      //   &Expr::TaggedTpl(_tagged_tpl.clone()),
442      //   &_tagged_tpl.tpl.quasis,
443      //   false,
444      //   state,
445      // )
446    },
447    Expr::Cond(cond) => {
448      let test_result = evaluate_cached(&cond.test, state, traversal_state, fns);
449
450      if !state.confident {
451        return None;
452      }
453
454      let test_result = match match test_result {
455        Some(v) => v,
456        None => stylex_panic!(
457          "The test condition of a conditional expression must be a static expression."
458        ),
459      } {
460        EvaluateResultValue::Expr(ref expr) => convert_expr_to_bool(expr, traversal_state, fns),
461        _ => stylex_panic_with_context!(
462          path,
463          traversal_state,
464          "The test condition of a conditional expression must be a static expression."
465        ),
466      };
467
468      if !state.confident {
469        return None;
470      }
471
472      if test_result {
473        evaluate_cached(&cond.cons, state, traversal_state, fns)
474      } else {
475        evaluate_cached(&cond.alt, state, traversal_state, fns)
476      }
477    },
478    Expr::Paren(_) => stylex_panic_with_context!(
479      path,
480      traversal_state,
481      "Parenthesized expressions should be unwrapped before evaluation."
482    ),
483    Expr::Member(member) => {
484      let parent_is_call_expr = traversal_state
485        .all_call_expressions
486        .values()
487        .any(|call_expr| {
488          if let Callee::Expr(callee) = &call_expr.callee {
489            callee.eq_ignore_span(&Box::new(Expr::Member(member.clone())))
490          } else {
491            false
492          }
493        });
494
495      let evaluated_value = if parent_is_call_expr {
496        None
497      } else {
498        evaluate_cached(&member.obj, state, traversal_state, fns)
499      };
500      match evaluated_value {
501        Some(object) => {
502          if !state.confident {
503            return None;
504          }
505
506          let prop_path = &member.prop;
507
508          let property = match prop_path {
509            MemberProp::Ident(ident) => Some(EvaluateResultValue::Expr(Expr::from(ident.clone()))),
510            MemberProp::Computed(ComputedPropName { expr, .. }) => {
511              let result = evaluate_cached(expr, state, traversal_state, fns);
512
513              if !state.confident {
514                return None;
515              }
516
517              result
518            },
519            MemberProp::PrivateName(_) => {
520              return deopt(path, state, UNEXPECTED_MEMBER_LOOKUP);
521            },
522          };
523
524          match object {
525            EvaluateResultValue::Expr(expr) => match &expr {
526              Expr::Array(ArrayLit { elems, .. }) => {
527                let eval_res = match property {
528                  Some(p) => p,
529                  None => stylex_panic!("{}", PROPERTY_NOT_FOUND),
530                };
531
532                let expr = match eval_res {
533                  EvaluateResultValue::Expr(expr) => expr,
534                  _ => stylex_panic_with_context!(path, traversal_state, PROPERTY_NOT_FOUND),
535                };
536
537                let value = match expr {
538                  Expr::Lit(Lit::Num(Number { value, .. })) => value as usize,
539                  _ => stylex_panic_with_context!(path, traversal_state, MEMBER_NOT_RESOLVED),
540                };
541
542                let property = elems.get(value)?;
543
544                let Some(expr) = property.as_ref() else {
545                  stylex_panic_with_context!(path, traversal_state, MEMBER_NOT_RESOLVED)
546                };
547
548                let expr = expr.expr.clone();
549
550                Some(EvaluateResultValue::Expr(*expr))
551              },
552              Expr::Object(ObjectLit { props, .. }) => {
553                let eval_res = match property {
554                  Some(p) => p,
555                  None => stylex_panic!("{}", PROPERTY_NOT_FOUND),
556                };
557
558                let ident = match eval_res {
559                  EvaluateResultValue::Expr(ident) => ident,
560                  EvaluateResultValue::ThemeRef(theme) => {
561                    // NOTE: it's a very edge case, but it's possible to have a theme ref as a key
562                    // in an object, when theme import key is same as other variable name.
563                    // One of the reasons is code minification or obfuscation,
564                    // when theme import key is renamed to a shorter name.
565                    // Also it may be a result of a bug in the code.
566
567                    warn!(
568                      "A theme import key is being used as an object key. This might be caused by code minification or an internal error.\r\nFor additional details, please recompile using debug mode."
569                    );
570
571                    debug!("Evaluating member access on object:");
572                    debug!("Object expression: {:?}", expr);
573                    debug!("Theme reference: {:?}", theme);
574                    debug!("Original property: {:?}", prop_path);
575
576                    return deopt(path, state, THEME_IMPORT_KEY_AS_OBJECT_KEY);
577                  },
578                  _ => {
579                    debug!("Property not found for expression: {:?}", expr);
580                    debug!("Evaluation result: {:?}", eval_res);
581                    debug!("Original property: {:?}", prop_path);
582
583                    stylex_panic_with_context!(
584                      path,
585                      traversal_state,
586                      "Property not found. For additional details, please recompile using debug mode."
587                    );
588                  },
589                };
590
591                let ident = &mut ident.to_owned();
592                let normalized_ident = normalize_expr(ident);
593
594                let ident_string_name = match normalized_ident {
595                  Expr::Ident(ident) => ident.sym.to_string(),
596                  Expr::Lit(lit) => convert_lit_to_string(lit).unwrap_or_else(|| {
597                    stylex_panic_with_context!(
598                      path,
599                      traversal_state,
600                      "The property key must be convertible to a string."
601                    )
602                  }),
603                  _ => {
604                    stylex_panic_with_context!(
605                      path,
606                      traversal_state,
607                      "Computed member properties are not supported in static evaluation."
608                    )
609                  },
610                };
611
612                let property = props.iter().find(|prop| match prop {
613                  PropOrSpread::Spread(_) => stylex_panic_with_context!(
614                    path,
615                    traversal_state,
616                    "The spread operator (...) is not supported in this context. Declare each property explicitly."
617                  ),
618                  PropOrSpread::Prop(prop) => {
619                    let mut prop = prop.clone();
620
621                    expand_shorthand_prop(&mut prop);
622
623                    match prop.as_ref() {
624                      Prop::KeyValue(key_value) => {
625                        let key = convert_key_value_to_str(key_value);
626
627                        ident_string_name == key
628                      }
629                      _ => {
630                        stylex_panic_with_context!(
631                          path,
632                          traversal_state,
633                          "Computed property keys are not supported in static evaluation."
634                        );
635                      }
636                    }
637                  }
638                })?;
639
640                if let PropOrSpread::Prop(prop) = property {
641                  return Some(EvaluateResultValue::Expr(
642                    *match prop.as_key_value() {
643                      Some(kv) => kv,
644                      None => stylex_panic!("{}", KEY_VALUE_EXPECTED),
645                    }
646                    .value
647                    .clone(),
648                  ));
649                } else {
650                  stylex_panic_with_context!(path, traversal_state, MEMBER_NOT_RESOLVED);
651                }
652              },
653              Expr::Member(member_expr) => evaluate_cached(
654                &Expr::Member(member_expr.clone()),
655                state,
656                traversal_state,
657                fns,
658              ),
659              Expr::Lit(nested_lit) => {
660                evaluate_cached(&Expr::Lit(nested_lit.clone()), state, traversal_state, fns)
661              },
662              Expr::Ident(nested_ident) => evaluate_cached(
663                &Expr::Ident(nested_ident.clone()),
664                state,
665                traversal_state,
666                fns,
667              ),
668              _ => {
669                stylex_panic_with_context!(
670                  path,
671                  traversal_state,
672                  "This type of object member access is not yet supported in static evaluation."
673                );
674              },
675            },
676            EvaluateResultValue::FunctionConfigMap(fc_map) => {
677              let key = match property {
678                Some(property) => match property {
679                  EvaluateResultValue::Expr(expr) => match expr {
680                    Expr::Ident(ident) => Box::new(ident.clone()),
681                    _ => stylex_panic_with_context!(path, traversal_state, MEMBER_NOT_RESOLVED),
682                  },
683                  _ => stylex_panic_with_context!(
684                    path,
685                    traversal_state,
686                    "This function configuration property is not yet supported."
687                  ),
688                },
689                None => stylex_panic_with_context!(path, traversal_state, MEMBER_NOT_RESOLVED),
690              };
691
692              if let Some(fc) = fc_map.get(&key.sym) {
693                return Some(EvaluateResultValue::FunctionConfig(fc.clone()));
694              }
695
696              // Check if this is an "env" property access on a stylex import
697              if key.sym.as_ref() == "env" {
698                if traversal_state.options.env.is_empty() {
699                  stylex_panic_with_context!(
700                    path,
701                    traversal_state,
702                    "The stylex.env object is not configured. Check that the 'env' option is set in your StyleX configuration."
703                  );
704                }
705
706                return Some(EvaluateResultValue::EnvObject(
707                  traversal_state.options.env.clone(),
708                ));
709              }
710
711              stylex_panic_with_context!(
712                path,
713                traversal_state,
714                format!(
715                  "The property '{}' was not found in the function configuration.",
716                  key.sym
717                )
718                .as_str()
719              );
720            },
721            EvaluateResultValue::ThemeRef(mut theme_ref) => {
722              let key = match property {
723                Some(property) => match property {
724                  EvaluateResultValue::Expr(expr) => match expr {
725                    Expr::Ident(Ident { sym, .. }) => sym.to_string(),
726                    Expr::Lit(lit) => match convert_lit_to_string(&lit) {
727                      Some(s) => s,
728                      None => stylex_panic!("Property key must be a string value."),
729                    },
730                    _ => stylex_panic_with_context!(path, traversal_state, MEMBER_NOT_RESOLVED),
731                  },
732                  _ => stylex_panic_with_context!(
733                    path,
734                    traversal_state,
735                    "This theme reference property type is not yet supported."
736                  ),
737                },
738                None => {
739                  stylex_panic_with_context!(
740                    path,
741                    traversal_state,
742                    "The referenced property was not found on the theme object. Ensure it was declared in defineVars()."
743                  )
744                },
745              };
746
747              let value = theme_ref.get(&key, traversal_state);
748
749              return Some(EvaluateResultValue::Expr(create_string_expr(
750                match value.as_css_var() {
751                  Some(css_var) => css_var,
752                  None => stylex_panic!("{}", EXPECTED_CSS_VAR),
753                }
754                .as_str(),
755              )));
756            },
757            EvaluateResultValue::EnvObject(env_map) => {
758              let key = property
759                .as_ref()
760                .and_then(|prop| prop.as_string_key())
761                .unwrap_or_else(|| {
762                  stylex_panic_with_context!(
763                    path,
764                    traversal_state,
765                    "The referenced property was not found in the stylex.env configuration."
766                  )
767                });
768
769              match env_map.get(&key) {
770                Some(entry) => match resolve_env_entry_to_result(entry, &env_map) {
771                  Some(result) => return Some(result),
772                  None => stylex_panic_with_context!(
773                    path,
774                    traversal_state,
775                    "The stylex.env value could not be converted to a static expression."
776                  ),
777                },
778                None => {
779                  stylex_panic_with_context!(
780                    path,
781                    traversal_state,
782                    format!(
783                      "The property '{}' was not found in the stylex.env configuration.",
784                      key
785                    )
786                    .as_str()
787                  );
788                },
789              }
790            },
791            _ => stylex_panic_with_context!(
792              path,
793              traversal_state,
794              "This evaluation result type is not yet supported in static evaluation."
795            ),
796          }
797        },
798        _ => None,
799      }
800    },
801    Expr::Unary(unary) => {
802      if unary.op == UnaryOp::Void {
803        return None;
804      }
805
806      let argument = &unary.arg;
807
808      if unary.op == UnaryOp::TypeOf && (argument.is_fn_expr() || argument.is_class()) {
809        return Some(EvaluateResultValue::Expr(create_string_expr("function")));
810      }
811
812      let arg = evaluate_cached(argument, state, traversal_state, fns);
813
814      if !state.confident {
815        return None;
816      }
817
818      let arg = match match arg {
819        Some(v) => v,
820        None => stylex_panic!("The operand of a unary expression must be a static expression."),
821      } {
822        EvaluateResultValue::Expr(expr) => expr,
823        _ => {
824          stylex_panic_with_context!(
825            path,
826            traversal_state,
827            "The operand of a unary expression must be a static expression."
828          )
829        },
830      };
831
832      match unary.op {
833        UnaryOp::Bang => {
834          let value = convert_expr_to_bool(&arg, traversal_state, fns);
835
836          Some(EvaluateResultValue::Expr(create_bool_expr(!value)))
837        },
838        UnaryOp::Plus => evaluate_unary_numeric(&arg, state, traversal_state, fns, |v| v),
839        UnaryOp::Minus => evaluate_unary_numeric(&arg, state, traversal_state, fns, |v| -v),
840        UnaryOp::Tilde => {
841          evaluate_unary_numeric(&arg, state, traversal_state, fns, |v| (!(v as i64)) as f64)
842        },
843        UnaryOp::TypeOf => {
844          let arg_type = match &arg {
845            Expr::Lit(Lit::Str(_)) => "string",
846            Expr::Lit(Lit::Bool(_)) => "boolean",
847            Expr::Lit(Lit::Num(_)) => "number",
848            Expr::Lit(Lit::Null(_)) => "object",
849            Expr::Fn(_) => "function",
850            Expr::Class(_) => "function",
851            Expr::Ident(ident) if ident.sym == *"undefined" => "undefined",
852            Expr::Object(_) => "object",
853            Expr::Array(_) => "object",
854            _ => {
855              stylex_panic_with_context!(
856                path,
857                traversal_state,
858                "This unary operator is not supported in static evaluation."
859              )
860            },
861          };
862
863          Some(EvaluateResultValue::Expr(create_string_expr(arg_type)))
864        },
865        UnaryOp::Void => Some(EvaluateResultValue::Expr(Expr::Ident(quote_ident!(
866          SyntaxContext::empty(),
867          "undefined"
868        )))),
869        _ => deopt(
870          &Expr::from(unary.clone()),
871          state,
872          &unsupported_operator(unary.op.as_str()),
873        ),
874      }
875    },
876    Expr::Array(arr_path) => {
877      let mut arr: Vec<Option<EvaluateResultValue>> = Vec::with_capacity(arr_path.elems.len());
878
879      for elem in arr_path.elems.iter().flatten() {
880        let elem_value = evaluate(&elem.expr, traversal_state, &state.functions);
881        collect_confident!(elem_value, arr);
882      }
883
884      Some(EvaluateResultValue::Vec(arr))
885    },
886    Expr::Object(obj_path) => {
887      let mut props = vec![];
888
889      for prop in &obj_path.props {
890        match prop {
891          PropOrSpread::Spread(prop) => {
892            let spread_expression = evaluate_cached(&prop.expr, state, traversal_state, fns);
893
894            if !state.confident {
895              return deopt(path, state, OBJECT_METHOD);
896            }
897
898            let Some(new_props) = spread_expression.and_then(|s| s.into_object()) else {
899              stylex_panic_with_context!(path, traversal_state, SPREAD_MUST_BE_OBJECT);
900            };
901
902            let merged_object = deep_merge_props(props, new_props.props);
903
904            props = merged_object;
905
906            continue;
907          },
908          PropOrSpread::Prop(prop) => {
909            if prop.is_method() {
910              let deopt_reason = state
911                .deopt_reason
912                .as_deref()
913                .unwrap_or("unknown error")
914                .to_string();
915
916              return deopt(path, state, &deopt_reason);
917            }
918
919            let mut prop = prop.clone();
920
921            expand_shorthand_prop(&mut prop);
922
923            match prop.as_ref() {
924              Prop::KeyValue(path_key_value) => {
925                let key = match &path_key_value.key {
926                  PropName::Ident(ident) => Some(ident.sym.to_string()),
927                  PropName::Str(strng) => Some(convert_atom_to_string(&strng.value)),
928                  PropName::Num(num) => Some(num.value.to_string()),
929                  PropName::Computed(computed) => {
930                    let evaluated_result =
931                      evaluate(&computed.expr, traversal_state, &state.functions);
932
933                    if !evaluated_result.confident {
934                      if let Some(deopt_val) = evaluated_result.deopt {
935                        let deopt_reason = state
936                          .deopt_reason
937                          .as_deref()
938                          .unwrap_or(
939                            evaluated_result
940                              .reason
941                              .as_deref()
942                              .unwrap_or("unknown error"),
943                          )
944                          .to_string();
945
946                        deopt(&deopt_val, state, &deopt_reason);
947                      }
948
949                      return None;
950                    }
951
952                    if let Some(expr) = evaluated_result
953                      .value
954                      .as_ref()
955                      .and_then(|value| value.as_expr())
956                    {
957                      Some(expr_to_str_or_deopt!(
958                        expr,
959                        state,
960                        traversal_state,
961                        &state.functions,
962                        EXPRESSION_IS_NOT_A_STRING
963                      ))
964                    } else {
965                      stylex_panic_with_context!(
966                        path,
967                        traversal_state,
968                        "The property value must be a static expression."
969                      );
970                    }
971                  },
972                  PropName::BigInt(big_int) => Some(big_int.value.to_string()),
973                };
974
975                let eval_value = evaluate(&path_key_value.value, traversal_state, &state.functions);
976
977                if !eval_value.confident {
978                  if let Some(deopt_val) = eval_value.deopt {
979                    let base_reason = state
980                      .deopt_reason
981                      .as_deref()
982                      .unwrap_or(eval_value.reason.as_deref().unwrap_or("unknown error"))
983                      .to_string();
984
985                    let deopt_reason = if let Some(ref k) = key {
986                      format!("{} > {}", k, base_reason)
987                    } else {
988                      base_reason
989                    };
990
991                    deopt(&deopt_val, state, &deopt_reason);
992                  }
993
994                  return None;
995                }
996
997                let Some(value) = eval_value.value else {
998                  stylex_panic_with_context!(
999                    path,
1000                    traversal_state,
1001                    format!(
1002                      "Value of key '{}' must be present, but got {:?}",
1003                      key.clone().unwrap_or_else(|| "Unknown".to_string()),
1004                      path_key_value.value.get_type(get_default_expr_ctx())
1005                    )
1006                    .as_ref()
1007                  );
1008                };
1009
1010                let value = match value {
1011                  EvaluateResultValue::Expr(expr) => Some(expr),
1012                  EvaluateResultValue::Vec(items) => {
1013                    Some(evaluate_result_vec_to_array_expr(&items))
1014                  },
1015                  EvaluateResultValue::Callback(cb) => match path_key_value.value.as_ref() {
1016                    Expr::Call(call_expr) => {
1017                      let cb_args: Vec<Option<EvaluateResultValue>> = call_expr
1018                        .args
1019                        .iter()
1020                        .map(|arg| {
1021                          let eval_arg = evaluate_cached(&arg.expr, state, traversal_state, fns);
1022
1023                          if !state.confident {
1024                            return None;
1025                          }
1026
1027                          eval_arg
1028                        })
1029                        .collect();
1030
1031                      Some(cb(cb_args))
1032                    },
1033                    Expr::Arrow(arrow_func_expr) => Some(Expr::Arrow(arrow_func_expr.clone())),
1034                    _ => stylex_panic_with_context!(
1035                      path,
1036                      traversal_state,
1037                      "This callback type is not supported in static evaluation."
1038                    ),
1039                  },
1040                  EvaluateResultValue::ThemeRef(_) => None,
1041                  _ => stylex_panic_with_context!(
1042                    path,
1043                    traversal_state,
1044                    "The property value must be a static expression."
1045                  ),
1046                };
1047
1048                if let Some(value) = value {
1049                  props.push(create_ident_key_value_prop(
1050                    &match key {
1051                      Some(k) => k,
1052                      None => stylex_panic!("Property key must be present in the style object."),
1053                    },
1054                    value,
1055                  ));
1056                }
1057              },
1058              _ => stylex_panic_with_context!(
1059                path,
1060                traversal_state,
1061                "This evaluation result type is not yet supported in static evaluation."
1062              ),
1063            }
1064          },
1065        }
1066      }
1067
1068      return Some(EvaluateResultValue::Expr(Expr::Object(create_object_lit(
1069        remove_duplicates(props),
1070      ))));
1071    },
1072    Expr::Bin(bin) => unwrap_or_panic!(
1073      binary_expr_to_num(bin, state, traversal_state, fns)
1074        .or_else(|num_error| {
1075          binary_expr_to_string(bin, state, traversal_state, fns).or_else::<String, _>(
1076            |str_error| {
1077              debug!("Binary expression to string error: {}", str_error);
1078              debug!("Binary expression to number error: {}", num_error);
1079
1080              Ok(BinaryExprType::Null)
1081            },
1082          )
1083        })
1084        .map(|result| match result {
1085          BinaryExprType::Number(num) => Some(EvaluateResultValue::Expr(create_number_expr(num))),
1086          BinaryExprType::String(strng) => {
1087            Some(EvaluateResultValue::Expr(create_string_expr(&strng)))
1088          },
1089          BinaryExprType::Null => None,
1090        })
1091    ),
1092    Expr::Call(call) => {
1093      let mut context: Option<Vec<Option<EvaluateResultValue>>> = None;
1094      let mut func: Option<Box<FunctionConfig>> = None;
1095
1096      if let Callee::Expr(callee_expr) = &call.callee {
1097        if get_binding(callee_expr, traversal_state).is_none() && is_valid_callee(callee_expr) {
1098          // skip built-in function evaluation
1099        } else if let Expr::Ident(ident) = callee_expr.as_ref() {
1100          let ident_id = ident.to_id();
1101
1102          if state.functions.identifiers.contains_key(&ident_id.0) {
1103            match match state.functions.identifiers.get(&ident_id.0) {
1104              Some(v) => v,
1105              None => stylex_panic!(
1106                "Could not resolve the function identifier. Ensure the function is defined and in scope."
1107              ),
1108            }
1109            .as_ref()
1110            {
1111              FunctionConfigType::Map(_) => stylex_panic_with_context!(
1112                path,
1113                traversal_state,
1114                "Map-type function configurations are not yet supported in this context."
1115              ),
1116              FunctionConfigType::Regular(fc) => func = Some(Box::new(fc.clone())),
1117              FunctionConfigType::IndexMap(_) => {
1118                stylex_unimplemented!("IndexMap values are not supported in this context.")
1119              }
1120              FunctionConfigType::EnvObject(_) => {
1121                // EnvObject is not directly callable; access is done via member expressions
1122                return deopt(path, state, NON_CONSTANT);
1123              }
1124            }
1125          } else {
1126            let _maybe_function = evaluate_cached(callee_expr, state, traversal_state, fns);
1127
1128            if state.confident {
1129              match _maybe_function {
1130                Some(EvaluateResultValue::FunctionConfig(fc)) => func = Some(Box::new(fc.clone())),
1131                Some(EvaluateResultValue::Callback(cb)) => {
1132                  return Some(EvaluateResultValue::Callback(cb));
1133                },
1134                _ => {
1135                  return deopt(path, state, NON_CONSTANT);
1136                },
1137              }
1138            } else {
1139              return deopt(path, state, NON_CONSTANT);
1140            }
1141          }
1142        }
1143
1144        if let Expr::Member(member) = callee_expr.as_ref() {
1145          let object = &member.obj;
1146          let property = &member.prop;
1147
1148          if object.is_ident() {
1149            let obj_ident = match object.as_ident() {
1150              Some(ident) => ident,
1151              None => {
1152                stylex_panic!("{}", MEMBER_OBJ_NOT_IDENT)
1153              },
1154            };
1155
1156            if property.is_ident() {
1157              if is_mutating_object_method(property) {
1158                return deopt(path, state, NON_CONSTANT);
1159              }
1160
1161              if is_valid_callee(object) && !is_invalid_method(property) {
1162                let callee_name = get_callee_name(object);
1163                let method_name = get_method_name(property);
1164
1165                match callee_name {
1166                  "Math" => {
1167                    let first_arg = call.args.first().unwrap_or_else(|| {
1168                      stylex_panic!("Math.{} requires an argument", method_name)
1169                    });
1170
1171                    if first_arg.spread.is_some() {
1172                      stylex_panic_with_context!(path, traversal_state, SPREAD_NOT_SUPPORTED);
1173                    }
1174
1175                    match method_name {
1176                      "pow" => {
1177                        func = Some(Box::new(FunctionConfig {
1178                          fn_ptr: FunctionType::Callback(Box::new(CallbackType::Math(MathJS::Pow))),
1179                          takes_path: false,
1180                        }));
1181
1182                        let second_arg = match call.args.get(1) {
1183                          Some(arg) => arg,
1184                          None => stylex_panic!("Math.pow() requires a second numeric argument."),
1185                        };
1186
1187                        if second_arg.spread.is_some() {
1188                          stylex_panic_with_context!(
1189                            path,
1190                            traversal_state,
1191                            "The spread operator (...) is not supported in this context. Declare each property explicitly."
1192                          );
1193                        }
1194
1195                        let cached_first_arg =
1196                          evaluate_cached(&first_arg.expr, state, traversal_state, fns);
1197                        let cached_second_arg =
1198                          evaluate_cached(&second_arg.expr, state, traversal_state, fns);
1199
1200                        if let Some(cached_first_arg) = cached_first_arg
1201                          && let Some(cached_second_arg) = cached_second_arg
1202                        {
1203                          context = Some(vec![Some(EvaluateResultValue::Vec(vec![
1204                            Some(cached_first_arg),
1205                            Some(cached_second_arg),
1206                          ]))]);
1207                        }
1208                      },
1209                      "round" | "ceil" | "floor" => {
1210                        func = Some(Box::new(FunctionConfig {
1211                          fn_ptr: FunctionType::Callback(Box::new(CallbackType::Math(
1212                            match method_name {
1213                              "round" => MathJS::Round,
1214                              "ceil" => MathJS::Ceil,
1215                              "floor" => MathJS::Floor,
1216                              _ => stylex_unreachable!("Invalid method: {}", method_name),
1217                            },
1218                          ))),
1219                          takes_path: false,
1220                        }));
1221
1222                        let cached_first_arg =
1223                          evaluate_cached(&first_arg.expr, state, traversal_state, fns);
1224
1225                        if let Some(cached_first_arg) = cached_first_arg {
1226                          context = Some(vec![Some(EvaluateResultValue::Expr(
1227                            cached_first_arg
1228                              .as_expr()
1229                              .unwrap_or_else(|| stylex_panic!("{}", ARGUMENT_NOT_EXPRESSION))
1230                              .clone(),
1231                          ))]);
1232                        }
1233                      },
1234                      "min" | "max" => {
1235                        func = Some(Box::new(FunctionConfig {
1236                          fn_ptr: FunctionType::Callback(Box::new(CallbackType::Math(
1237                            match method_name {
1238                              "min" => MathJS::Min,
1239                              "max" => MathJS::Max,
1240                              _ => stylex_unreachable!("Invalid method: {}", method_name),
1241                            },
1242                          ))),
1243                          takes_path: false,
1244                        }));
1245
1246                        let cached_first_arg =
1247                          evaluate_cached(&first_arg.expr, state, traversal_state, fns);
1248
1249                        if let Some(cached_first_arg) = cached_first_arg {
1250                          let mut result = vec![Some(cached_first_arg)];
1251
1252                          result.extend(
1253                            call
1254                              .args
1255                              .iter()
1256                              .skip(1)
1257                              .map(|arg| evaluate_cached(&arg.expr, state, traversal_state, fns))
1258                              .collect::<Vec<Option<EvaluateResultValue>>>(),
1259                          );
1260
1261                          context = Some(vec![Some(EvaluateResultValue::Vec(
1262                            result.into_iter().collect(),
1263                          ))]);
1264                        }
1265                      },
1266                      "abs" => {
1267                        let cached_first_arg =
1268                          evaluate_cached(&first_arg.expr, state, traversal_state, fns);
1269
1270                        if let Some(cached_first_arg) = cached_first_arg {
1271                          func = Some(Box::new(FunctionConfig {
1272                            fn_ptr: FunctionType::Callback(Box::new(CallbackType::Math(
1273                              MathJS::Abs,
1274                            ))),
1275                            takes_path: false,
1276                          }));
1277
1278                          context = Some(vec![Some(EvaluateResultValue::Expr(
1279                            cached_first_arg
1280                              .as_expr()
1281                              .unwrap_or_else(|| stylex_panic!("{}", ARGUMENT_NOT_EXPRESSION))
1282                              .clone(),
1283                          ))]);
1284                        }
1285                      },
1286                      _ => {
1287                        stylex_panic!("{} - {}:{}", BUILT_IN_FUNCTION, callee_name, method_name)
1288                      },
1289                    }
1290                  },
1291                  "Object" => {
1292                    let args = &call.args;
1293
1294                    let arg = args.first().unwrap_or_else(|| {
1295                      stylex_panic!("Object.{} requires an argument", method_name)
1296                    });
1297
1298                    if arg.spread.is_some() {
1299                      stylex_panic_with_context!(path, traversal_state, SPREAD_NOT_SUPPORTED);
1300                    }
1301
1302                    let cached_arg = evaluate_cached(&arg.expr, state, traversal_state, fns);
1303
1304                    match method_name {
1305                      "fromEntries" => {
1306                        func = Some(Box::new(FunctionConfig {
1307                          fn_ptr: FunctionType::Callback(Box::new(CallbackType::Object(
1308                            ObjectJS::FromEntries,
1309                          ))),
1310                          takes_path: false,
1311                        }));
1312
1313                        let mut from_entries_result = IndexMap::new();
1314
1315                        match match cached_arg {
1316                          Some(v) => v,
1317                          None => stylex_panic!(
1318                            "Object.fromEntries() requires an array of [key, value] entries."
1319                          ),
1320                        } {
1321                          EvaluateResultValue::Expr(expr) => {
1322                            let array = expr.as_array().cloned().unwrap_or_else(|| {
1323                              stylex_panic!(
1324                                "Object.fromEntries() requires an array of [key, value] entries."
1325                              )
1326                            });
1327
1328                            let entries = array
1329                              .elems
1330                              .into_iter()
1331                              .flatten()
1332                              .collect::<Vec<ExprOrSpread>>();
1333
1334                            for entry in entries {
1335                              assert!(entry.spread.is_none(), "{}", SPREAD_NOT_SUPPORTED);
1336
1337                              let array = match entry.expr.as_array() {
1338                                Some(a) => a,
1339                                None => stylex_panic!(
1340                                  "Each entry in Object.fromEntries() must be a [key, value] array."
1341                                ),
1342                              };
1343
1344                              let elems =
1345                                array.elems.iter().flatten().collect::<Vec<&ExprOrSpread>>();
1346
1347                              let key =
1348                                elems
1349                                  .first()
1350                                  .and_then(|e| e.expr.as_lit())
1351                                  .unwrap_or_else(|| {
1352                                    stylex_panic!(
1353                                      "Object key must be a static literal (identifier or string)."
1354                                    )
1355                                  });
1356
1357                              let value = elems
1358                                .get(1)
1359                                .map(|e| e.expr.clone())
1360                                .unwrap_or_else(|| stylex_panic!("{}", VALUE_MUST_BE_LITERAL));
1361
1362                              from_entries_result.insert(key.clone(), value.clone());
1363                            }
1364                          },
1365                          EvaluateResultValue::Vec(vec) => {
1366                            for entry in vec {
1367                              let entry = entry
1368                                .and_then(|entry| entry.as_vec().cloned())
1369                                .unwrap_or_else(|| {
1370                                  stylex_panic!(
1371                                    "Expected an array element but found a hole (empty slot)."
1372                                  )
1373                                });
1374
1375                              let key = entry
1376                                .first()
1377                                .and_then(|item| item.clone())
1378                                .and_then(|item| item.as_expr().cloned())
1379                                .and_then(|expr| expr.as_lit().cloned())
1380                                .unwrap_or_else(|| {
1381                                  stylex_panic!(
1382                                    "Object key must be a static literal (identifier or string)."
1383                                  )
1384                                });
1385
1386                              let value = entry
1387                                .get(1)
1388                                .and_then(|item| item.clone())
1389                                .and_then(|item| item.as_expr().cloned())
1390                                .unwrap_or_else(|| stylex_panic!("{}", VALUE_MUST_BE_LITERAL));
1391
1392                              from_entries_result.insert(key.clone(), Box::new(value.clone()));
1393                            }
1394                          },
1395                          _ => {
1396                            stylex_panic!(
1397                              "Object.fromEntries() requires an array of [key, value] entries."
1398                            )
1399                          },
1400                        };
1401
1402                        context = Some(vec![Some(EvaluateResultValue::Entries(
1403                          from_entries_result,
1404                        ))]);
1405                      },
1406                      "keys" => {
1407                        func = Some(Box::new(FunctionConfig {
1408                          fn_ptr: FunctionType::Callback(Box::new(CallbackType::Object(
1409                            ObjectJS::Keys,
1410                          ))),
1411                          takes_path: false,
1412                        }));
1413
1414                        let object = normalize_js_object_method_args(cached_arg);
1415
1416                        let mut keys = vec![];
1417
1418                        if let Some(object) = object {
1419                          for prop in &object.props {
1420                            let expr = match prop.as_prop().cloned() {
1421                              Some(p) => p,
1422                              None => stylex_panic!("{}", SPREAD_NOT_SUPPORTED),
1423                            };
1424
1425                            let key_values = match expr.as_key_value() {
1426                              Some(kv) => kv,
1427                              None => stylex_panic!("Object.keys() requires an object argument."),
1428                            };
1429
1430                            let key = convert_key_value_to_str(key_values);
1431
1432                            keys.push(Some(create_expr_or_spread(create_string_expr(
1433                              key.as_str(),
1434                            ))));
1435                          }
1436                        }
1437
1438                        context = Some(vec![Some(EvaluateResultValue::Expr(
1439                          create_array_expression(keys),
1440                        ))]);
1441                      },
1442                      "values" => {
1443                        func = Some(Box::new(FunctionConfig {
1444                          fn_ptr: FunctionType::Callback(Box::new(CallbackType::Object(
1445                            ObjectJS::Values,
1446                          ))),
1447                          takes_path: false,
1448                        }));
1449
1450                        let object = normalize_js_object_method_args(cached_arg);
1451
1452                        let mut values = vec![];
1453
1454                        if let Some(object) = object {
1455                          for prop in &object.props {
1456                            let prop = match prop.as_prop().cloned() {
1457                              Some(p) => p,
1458                              None => stylex_panic!("{}", SPREAD_NOT_SUPPORTED),
1459                            };
1460
1461                            let key_values = match prop.as_key_value() {
1462                              Some(kv) => kv,
1463                              None => stylex_panic!("Object.values() requires an object argument."),
1464                            };
1465
1466                            values.push(Some(create_expr_or_spread(*key_values.value.clone())));
1467                          }
1468                        }
1469
1470                        context = Some(vec![Some(EvaluateResultValue::Expr(
1471                          create_array_expression(values),
1472                        ))]);
1473                      },
1474                      "entries" => {
1475                        func = Some(Box::new(FunctionConfig {
1476                          fn_ptr: FunctionType::Callback(Box::new(CallbackType::Object(
1477                            ObjectJS::Entries,
1478                          ))),
1479                          takes_path: false,
1480                        }));
1481
1482                        let object = normalize_js_object_method_args(cached_arg);
1483
1484                        let mut entries: IndexMap<Lit, Box<Expr>> = IndexMap::new();
1485
1486                        if let Some(object) = object {
1487                          for prop in &object.props {
1488                            let expr = match prop.as_prop().map(|prop| *prop.clone()) {
1489                              Some(p) => p,
1490                              None => stylex_panic!("{}", SPREAD_NOT_SUPPORTED),
1491                            };
1492
1493                            let key_values = match expr.as_key_value() {
1494                              Some(kv) => kv,
1495                              None => {
1496                                stylex_panic!("Object.entries() requires an object argument.")
1497                              },
1498                            };
1499
1500                            let value = key_values.value.clone();
1501
1502                            let key = convert_key_value_to_str(key_values);
1503
1504                            entries.insert(create_string_lit(key.as_str()), value);
1505                          }
1506                        }
1507
1508                        context = Some(vec![Some(EvaluateResultValue::Entries(entries))]);
1509                      },
1510                      _ => {
1511                        stylex_panic!("{} - {}:{}", BUILT_IN_FUNCTION, callee_name, method_name)
1512                      },
1513                    }
1514                  },
1515                  _ => stylex_panic!("{} - {}", BUILT_IN_FUNCTION, callee_name),
1516                }
1517              } else {
1518                let prop_ident = match property.as_ident() {
1519                  Some(ident) => ident,
1520                  None => stylex_panic!(
1521                    "Property key must be a static identifier, not a computed expression."
1522                  ),
1523                };
1524
1525                let obj_name = obj_ident.sym.to_string();
1526                let prop_id = prop_ident.sym.to_id();
1527
1528                if let Some(member_expr) = state
1529                  .functions
1530                  .member_expressions
1531                  .get(&ImportSources::Regular(obj_name))
1532                  && let Some(member_expr_fn) = member_expr.get(&prop_id.0)
1533                {
1534                  match member_expr_fn.as_ref() {
1535                    FunctionConfigType::Regular(fc) => {
1536                      func = Some(Box::new(fc.clone()));
1537                    },
1538                    FunctionConfigType::Map(_) => stylex_panic_with_context!(
1539                      path,
1540                      traversal_state,
1541                      "Map-type function configurations are not yet supported in this context."
1542                    ),
1543                    FunctionConfigType::IndexMap(_) => {
1544                      stylex_unimplemented!("IndexMap values are not supported in this context.")
1545                    },
1546                    FunctionConfigType::EnvObject(_) => {
1547                      // This shouldn't happen - env object isn't directly callable.
1548                      // But if it does, try to evaluate it as a member expression call.
1549                      return deopt(path, state, NON_CONSTANT);
1550                    },
1551                  }
1552                }
1553              }
1554            }
1555
1556            if let Some(prop_id) = is_id_prop(property) {
1557              let obj_name = obj_ident.sym.to_string();
1558
1559              if let Some(member_expr) = state
1560                .functions
1561                .member_expressions
1562                .get(&ImportSources::Regular(obj_name))
1563                && member_expr.contains_key(prop_id)
1564              {
1565                stylex_panic_with_context!(
1566                  path,
1567                  traversal_state,
1568                  "Unexpected expression encountered during static evaluation."
1569                );
1570
1571                // context = Some(member_expr.clone());
1572
1573                // TODO: uncomment this for implementation of member expressions
1574                // match member_expr.get(&prop_id).unwrap() {
1575                //   FunctionConfigType::Regular(fc) => {
1576                //     func = Some(Box::new(fc.clone()));
1577                //   }
1578                //   FunctionConfigType::Map(_) => unimplemented!("FunctionConfigType::Map"),
1579                // }
1580              }
1581            }
1582          }
1583
1584          if object.is_lit() {
1585            let obj_lit = match object.as_lit() {
1586              Some(lit) => lit,
1587              None => stylex_panic!("Expected a static object literal."),
1588            };
1589
1590            if property.is_ident()
1591              && let Lit::Bool(_) = obj_lit
1592            {
1593              stylex_panic_with_context!(
1594                path,
1595                traversal_state,
1596                "Boolean object methods are not supported in static evaluation."
1597              );
1598            }
1599          }
1600
1601          if func.is_none() {
1602            let parsed_obj = evaluate(object, traversal_state, &state.functions);
1603
1604            if parsed_obj.confident {
1605              if property.is_ident() {
1606                let prop_ident = match property.as_ident() {
1607                  Some(ident) => ident,
1608                  None => stylex_panic!(
1609                    "Property key must be a static identifier, not a computed expression."
1610                  ),
1611                };
1612                let prop_name = prop_ident.sym.to_string();
1613
1614                if is_mutating_array_method(property) {
1615                  return deopt(path, state, NON_CONSTANT);
1616                }
1617
1618                let value = match parsed_obj.value {
1619                  Some(v) => v,
1620                  None => {
1621                    stylex_panic_with_context!(
1622                      path,
1623                      traversal_state,
1624                      format!(
1625                        "Parsed object has no value when accessing property '.{}'",
1626                        prop_name
1627                      )
1628                      .as_str()
1629                    );
1630                  },
1631                };
1632
1633                match value.clone() {
1634                  EvaluateResultValue::Map(map) => {
1635                    let result_fn = map.get(&Expr::from(prop_ident.clone()));
1636
1637                    func = match result_fn {
1638                      Some(_) => stylex_panic_with_context!(
1639                        path,
1640                        traversal_state,
1641                        "Map evaluation results are not yet supported in this context."
1642                      ),
1643                      None => None,
1644                    };
1645                  },
1646                  EvaluateResultValue::Vec(expr) => {
1647                    func = Some(Box::new(FunctionConfig {
1648                      fn_ptr: FunctionType::Callback(Box::new(match prop_name.as_str() {
1649                        "map" => CallbackType::Array(ArrayJS::Map),
1650                        "filter" => CallbackType::Array(ArrayJS::Filter),
1651                        "join" => CallbackType::Array(ArrayJS::Join),
1652                        "entries" => CallbackType::Object(ObjectJS::Entries),
1653                        _ => stylex_panic_with_context!(
1654                          path,
1655                          traversal_state,
1656                          format!(
1657                            "The array method '{}' is not yet supported in static evaluation.",
1658                            prop_name
1659                          )
1660                          .as_str()
1661                        ),
1662                      })),
1663                      takes_path: false,
1664                    }));
1665
1666                    context = Some(expr.clone())
1667                  },
1668                  EvaluateResultValue::Expr(expr) => match expr {
1669                    Expr::Array(ArrayLit { elems, .. }) => {
1670                      func = Some(Box::new(FunctionConfig {
1671                        fn_ptr: FunctionType::Callback(Box::new(match prop_name.as_str() {
1672                          "map" => CallbackType::Array(ArrayJS::Map),
1673                          "filter" => CallbackType::Array(ArrayJS::Filter),
1674                          "entries" => CallbackType::Object(ObjectJS::Entries),
1675                          _ => stylex_panic_with_context!(
1676                            path,
1677                            traversal_state,
1678                            format!(
1679                              "The method '{}' is not yet supported in static evaluation.",
1680                              prop_name
1681                            )
1682                            .as_str()
1683                          ),
1684                        })),
1685                        takes_path: false,
1686                      }));
1687
1688                      let expr = elems
1689                        .iter()
1690                        .map(|elem| {
1691                          let elem = match elem.clone() {
1692                            Some(e) => e,
1693                            None => stylex_panic!(
1694                              "Array element must be present (no empty slots allowed)."
1695                            ),
1696                          };
1697                          Some(EvaluateResultValue::Expr(*elem.expr))
1698                        })
1699                        .collect::<Vec<Option<EvaluateResultValue>>>();
1700
1701                      context = Some(vec![Some(EvaluateResultValue::Vec(expr))]);
1702                    },
1703                    Expr::Lit(Lit::Str(_)) => {
1704                      func = Some(Box::new(FunctionConfig {
1705                        fn_ptr: FunctionType::Callback(Box::new(match prop_name.as_str() {
1706                          "concat" => CallbackType::String(StringJS::Concat),
1707                          "charCodeAt" => CallbackType::String(StringJS::CharCodeAt),
1708                          _ => stylex_panic_with_context!(
1709                            path,
1710                            traversal_state,
1711                            format!(
1712                              "The method '{}' is not yet supported in static evaluation.",
1713                              prop_name
1714                            )
1715                            .as_str()
1716                          ),
1717                        })),
1718                        takes_path: false,
1719                      }));
1720
1721                      context = Some(vec![Some(EvaluateResultValue::Expr(expr.clone()))]);
1722                    },
1723                    Expr::Object(object) => {
1724                      let key_values = get_key_values_from_object(&object);
1725
1726                      let key_value =
1727                        key_values
1728                          .into_iter()
1729                          .find(|key_value| match key_value.key.as_ident() {
1730                            Some(key_ident) => key_ident.sym == prop_name,
1731                            _ => false,
1732                          });
1733
1734                      let Some(key_value) = key_value else {
1735                        stylex_panic_with_context!(path, traversal_state, PROPERTY_NOT_FOUND);
1736                      };
1737
1738                      func = Some(Box::new(FunctionConfig {
1739                        fn_ptr: FunctionType::Callback(Box::new(CallbackType::Custom(
1740                          *key_value.value,
1741                        ))),
1742                        takes_path: false,
1743                      }));
1744
1745                      let args: Vec<Option<EvaluateResultValue>> = call
1746                        .args
1747                        .iter()
1748                        .map(|arg| {
1749                          let arg = evaluate_cached(&arg.expr, state, traversal_state, fns);
1750
1751                          if !state.confident {
1752                            return None;
1753                          }
1754
1755                          arg
1756                        })
1757                        .collect();
1758
1759                      context = Some(args);
1760                    },
1761                    Expr::Lit(Lit::Regex(_)) => {
1762                      // Regex methods like .test(), .exec(), etc. require runtime evaluation
1763                      // We can't statically evaluate them, so we deopt
1764                      return deopt(path, state, "Regex methods cannot be statically evaluated");
1765                    },
1766                    _ => {
1767                      stylex_panic_with_context!(
1768                        path,
1769                        traversal_state,
1770                        "This expression type is not yet supported in static evaluation."
1771                      )
1772                    },
1773                  },
1774                  EvaluateResultValue::FunctionConfig(fc) => match fc.fn_ptr {
1775                    FunctionType::StylexFnsFactory(sxfns) => {
1776                      let fc = sxfns(prop_name);
1777
1778                      func = Some(Box::new(FunctionConfig {
1779                        fn_ptr: FunctionType::StylexTypeFn(fc),
1780                        takes_path: false,
1781                      }));
1782
1783                      context = Some(vec![Some(value)]);
1784                    },
1785                    FunctionType::DefaultMarker(default_marker) => {
1786                      if let Some(expr_fn) = default_marker.get(&prop_name) {
1787                        func = Some(Box::new(FunctionConfig {
1788                          fn_ptr: FunctionType::StylexExprFn(*expr_fn),
1789                          takes_path: false,
1790                        }));
1791
1792                        context = Some(vec![Some(value)]);
1793                      };
1794                    },
1795                    _ => stylex_panic_with_context!(
1796                      path,
1797                      traversal_state,
1798                      "StyleX function factories are not supported in this context."
1799                    ),
1800                  },
1801                  EvaluateResultValue::EnvObject(env_map) => {
1802                    // Handle env function calls like `env.colorMix(...)` or `stylex.env.colorMix(...)`
1803                    if let Some(env_val) = env_map.get(&prop_name) {
1804                      if let Some(env_fn) = env_val.as_function() {
1805                        func = Some(Box::new(FunctionConfig {
1806                          fn_ptr: FunctionType::EnvFunction(env_fn.clone()),
1807                          takes_path: false,
1808                        }));
1809                      } else if let Some(result) = resolve_env_entry_to_result(env_val, &env_map) {
1810                        // It's a value, not a function - return it directly
1811                        return Some(result);
1812                      }
1813                    } else {
1814                      stylex_panic_with_context!(
1815                        path,
1816                        traversal_state,
1817                        format!(
1818                          "The property '{}' was not found in the stylex.env configuration.",
1819                          prop_name
1820                        )
1821                        .as_str()
1822                      );
1823                    }
1824                  },
1825                  _ => stylex_panic_with_context!(
1826                    path,
1827                    traversal_state,
1828                    "This evaluation result type is not yet supported in static evaluation."
1829                  ),
1830                }
1831              } else if let Some(prop_id) = is_id_prop(property) {
1832                let prop_id_owned = prop_id.to_string();
1833
1834                let value = match parsed_obj.value {
1835                  Some(v) => v,
1836                  None => {
1837                    stylex_panic_with_context!(
1838                      path,
1839                      traversal_state,
1840                      format!(
1841                        "Parsed object has no value when accessing computed property '{}'",
1842                        prop_id_owned
1843                      )
1844                      .as_str()
1845                    );
1846                  },
1847                };
1848                let map = match value.as_map() {
1849                  Some(m) => m,
1850                  None => {
1851                    stylex_panic_with_context!(
1852                      path,
1853                      traversal_state,
1854                      format!(
1855                        "Expected object map when accessing computed property '{}', got {:?}",
1856                        prop_id_owned, value
1857                      )
1858                      .as_str()
1859                    );
1860                  },
1861                };
1862
1863                let result_fn = map.get(&create_string_expr(&prop_id_owned));
1864
1865                func = match result_fn {
1866                  Some(_) => {
1867                    stylex_panic_with_context!(
1868                      path,
1869                      traversal_state,
1870                      "Unexpected function result during member expression evaluation."
1871                    )
1872                  },
1873                  None => None,
1874                };
1875              }
1876            }
1877          }
1878        }
1879      }
1880
1881      if let Some(func) = func {
1882        if func.takes_path {
1883          let args = call.args.iter().map(|arg| &*arg.expr).collect::<Vec<_>>();
1884
1885          match func.fn_ptr {
1886            FunctionType::ArrayArgs(func) => {
1887              let func_result = (func)(
1888                args.iter().map(|arg| (*arg).clone()).collect(),
1889                traversal_state,
1890                fns,
1891              );
1892              return Some(EvaluateResultValue::Expr(func_result));
1893            },
1894            FunctionType::StylexExprFn(func) => {
1895              let func_result = (func)(
1896                (**match args.first() {
1897                  Some(a) => a,
1898                  None => {
1899                    stylex_panic!("StyleX expression function requires at least one argument.")
1900                  },
1901                })
1902                .clone(),
1903                traversal_state,
1904              );
1905
1906              return Some(EvaluateResultValue::Expr(func_result));
1907            },
1908            FunctionType::StylexTypeFn(_) => {
1909              stylex_panic_with_context!(
1910                path,
1911                traversal_state,
1912                "StyleX function factories are not supported in this context."
1913              )
1914            },
1915            FunctionType::StylexFnsFactory(_) => {
1916              stylex_panic_with_context!(
1917                path,
1918                traversal_state,
1919                "StyleX function factories are not supported in this context."
1920              )
1921            },
1922            FunctionType::Callback(_) => {
1923              stylex_panic_with_context!(
1924                path,
1925                traversal_state,
1926                "Arrow function expressions are not supported in this context."
1927              )
1928            },
1929            FunctionType::Mapper(_) => stylex_panic_with_context!(
1930              path,
1931              traversal_state,
1932              "Mapper functions are not supported in static evaluation."
1933            ),
1934            FunctionType::DefaultMarker(_) => {
1935              stylex_panic_with_context!(
1936                path,
1937                traversal_state,
1938                "defaultMarker() cannot be called with arguments in this context."
1939              )
1940            },
1941            FunctionType::EnvFunction(_) => {
1942              stylex_panic_with_context!(
1943                path,
1944                traversal_state,
1945                "Env functions with path arguments are not yet supported."
1946              )
1947            },
1948          }
1949        } else {
1950          if !state.confident {
1951            return None;
1952          }
1953
1954          match func.fn_ptr {
1955            FunctionType::ArrayArgs(func) => {
1956              let args = evaluate_func_call_args(call, state, traversal_state, fns);
1957              let func_result = (func)(
1958                args
1959                  .into_iter()
1960                  .map(|arg| match arg.as_expr().cloned() {
1961                    Some(e) => e,
1962                    None => stylex_panic!("{}", ARGUMENT_NOT_EXPRESSION),
1963                  })
1964                  .collect(),
1965                traversal_state,
1966                fns,
1967              );
1968              return Some(EvaluateResultValue::Expr(func_result));
1969            },
1970            FunctionType::StylexExprFn(func) => {
1971              let args = evaluate_func_call_args(call, state, traversal_state, fns);
1972              let func_result = (func)(
1973                args
1974                  .first()
1975                  .and_then(|arg| arg.as_expr().cloned())
1976                  .unwrap_or_else(|| {
1977                    stylex_panic!("StyleX expression function requires an expression argument.")
1978                  }),
1979                traversal_state,
1980              );
1981              return Some(EvaluateResultValue::Expr(func_result));
1982            },
1983            FunctionType::StylexTypeFn(func) => {
1984              let args = evaluate_func_call_args(call, state, traversal_state, fns);
1985              let mut fn_args = IndexMap::default();
1986              let expr = args
1987                .first()
1988                .and_then(|expr| expr.as_expr())
1989                .unwrap_or_else(|| stylex_panic!("{}", ARGUMENT_NOT_EXPRESSION));
1990
1991              match expr {
1992                Expr::Object(obj) => {
1993                  for prop in &obj.props {
1994                    let prop = match prop.as_prop() {
1995                      Some(p) => p,
1996                      None => stylex_panic!("{}", SPREAD_NOT_SUPPORTED),
1997                    };
1998                    let key_value = match prop.as_key_value() {
1999                      Some(kv) => kv,
2000                      None => stylex_panic!("{}", KEY_VALUE_EXPECTED),
2001                    };
2002
2003                    let key = match key_value.key.as_ident() {
2004                      Some(ident) => ident.sym.to_string(),
2005                      None => stylex_panic!("{}", OBJECT_KEY_MUST_BE_IDENT),
2006                    };
2007
2008                    let value = match key_value.value.as_lit() {
2009                      Some(lit) => lit,
2010                      None => stylex_panic!("{}", VALUE_MUST_BE_LITERAL),
2011                    };
2012
2013                    fn_args.insert(
2014                      key,
2015                      ValueWithDefault::String(convert_lit_to_string(value).unwrap_or_default()),
2016                    );
2017                  }
2018                },
2019                Expr::Lit(lit) => {
2020                  fn_args.insert(
2021                    "default".to_string(),
2022                    ValueWithDefault::String(convert_lit_to_string(lit).unwrap_or_default()),
2023                  );
2024                },
2025                _ => {},
2026              }
2027
2028              let func_result = (func)(ValueWithDefault::Map(fn_args));
2029              return Some(EvaluateResultValue::Expr(func_result));
2030            },
2031            FunctionType::Callback(func) => {
2032              let context = match context {
2033                Some(c) => c,
2034                None => stylex_panic!("Object.entries() requires an object argument."),
2035              };
2036
2037              match func.as_ref() {
2038                CallbackType::Array(ArrayJS::Map) => {
2039                  let args = evaluate_func_call_args(call, state, traversal_state, fns);
2040
2041                  return evaluate_map(&args, &context);
2042                },
2043                CallbackType::Array(ArrayJS::Filter) => {
2044                  let args = evaluate_func_call_args(call, state, traversal_state, fns);
2045
2046                  return evaluate_filter(&args, &context);
2047                },
2048                CallbackType::Array(ArrayJS::Join) => {
2049                  let args = evaluate_func_call_args(call, state, traversal_state, fns);
2050
2051                  return evaluate_join(&args, &context, traversal_state, &state.functions);
2052                },
2053                CallbackType::Object(ObjectJS::Entries) => {
2054                  let Some(Some(eval_result)) = context.first() else {
2055                    stylex_panic_with_context!(
2056                      path,
2057                      traversal_state,
2058                      "Object.entries() requires an object argument."
2059                    )
2060                  };
2061
2062                  let EvaluateResultValue::Entries(entries) = eval_result else {
2063                    stylex_panic_with_context!(
2064                      path,
2065                      traversal_state,
2066                      "Object.entries() requires an object argument."
2067                    )
2068                  };
2069
2070                  let mut entry_elems: Vec<Option<ExprOrSpread>> = vec![];
2071
2072                  for (key, value) in entries {
2073                    let key_spread = create_expr_or_spread(Expr::from(key.clone()));
2074                    let value_spread = create_expr_or_spread(*value.clone());
2075
2076                    entry_elems.push(Some(create_expr_or_spread(create_array_expression(vec![
2077                      Some(key_spread),
2078                      Some(value_spread),
2079                    ]))));
2080                  }
2081
2082                  return Some(EvaluateResultValue::Expr(create_array_expression(
2083                    entry_elems,
2084                  )));
2085                },
2086                CallbackType::Object(ObjectJS::Keys) => {
2087                  let Some(Some(EvaluateResultValue::Expr(keys))) = context.first() else {
2088                    stylex_panic_with_context!(
2089                      path,
2090                      traversal_state,
2091                      "Object.keys() requires an argument."
2092                    )
2093                  };
2094
2095                  return Some(EvaluateResultValue::Expr(keys.clone()));
2096                },
2097                CallbackType::Object(ObjectJS::Values) => {
2098                  let Some(Some(EvaluateResultValue::Expr(values))) = context.first() else {
2099                    stylex_panic_with_context!(
2100                      path,
2101                      traversal_state,
2102                      "Object.keys() requires an argument."
2103                    )
2104                  };
2105
2106                  return Some(EvaluateResultValue::Expr(values.clone()));
2107                },
2108                CallbackType::Object(ObjectJS::FromEntries) => {
2109                  let Some(Some(EvaluateResultValue::Entries(entries))) = context.first() else {
2110                    stylex_panic_with_context!(
2111                      path,
2112                      traversal_state,
2113                      "Object.fromEntries() requires an array of [key, value] entries."
2114                    )
2115                  };
2116
2117                  let mut entry_elems = vec![];
2118
2119                  for (key, value) in entries {
2120                    let key_str = if let Lit::Str(lit_str) = key {
2121                      convert_atom_to_str_ref(&lit_str.value)
2122                    } else {
2123                      stylex_panic_with_context!(path, traversal_state, "Expected a string literal")
2124                    };
2125
2126                    entry_elems.push(create_ident_key_value_prop(key_str, *value.clone()));
2127                  }
2128
2129                  return Some(EvaluateResultValue::Expr(create_object_expression(
2130                    entry_elems,
2131                  )));
2132                },
2133                CallbackType::Math(MathJS::Pow) => {
2134                  let Some(Some(EvaluateResultValue::Vec(args))) = context.first() else {
2135                    stylex_panic_with_context!(
2136                      path,
2137                      traversal_state,
2138                      "Math.pow() requires an argument."
2139                    )
2140                  };
2141
2142                  let num_args = args
2143                    .iter()
2144                    .flatten()
2145                    .map(|arg| {
2146                      arg
2147                        .as_expr()
2148                        .map(|expr| {
2149                          unwrap_or_panic!(expr_to_num(expr, state, traversal_state, fns))
2150                        })
2151                        .unwrap_or_else(|| stylex_panic!("All arguments must be numeric values."))
2152                    })
2153                    .collect::<Vec<f64>>();
2154
2155                  let result = match (num_args.first(), num_args.get(1)) {
2156                    (Some(base), Some(exp)) => base.powf(*exp),
2157                    _ => stylex_panic!("Math.pow() requires two numeric arguments."),
2158                  };
2159
2160                  return Some(EvaluateResultValue::Expr(create_number_expr(result)));
2161                },
2162                CallbackType::Math(MathJS::Round | MathJS::Floor | MathJS::Ceil) => {
2163                  let Some(Some(EvaluateResultValue::Expr(expr))) = context.first() else {
2164                    stylex_panic_with_context!(
2165                      path,
2166                      traversal_state,
2167                      "Math.round()/Math.ceil()/Math.floor() requires one numeric argument."
2168                    )
2169                  };
2170
2171                  let num =
2172                    expr_to_num(expr, state, traversal_state, fns).unwrap_or_else(|error| {
2173                      stylex_panic_with_context!(path, traversal_state, error.to_string().as_str())
2174                    });
2175
2176                  let result = match func.as_ref() {
2177                    CallbackType::Math(MathJS::Round) => num.round(),
2178                    CallbackType::Math(MathJS::Ceil) => num.ceil(),
2179                    CallbackType::Math(MathJS::Floor) => num.floor(),
2180                    _ => stylex_unreachable!("Invalid function type"),
2181                  };
2182
2183                  return Some(EvaluateResultValue::Expr(create_number_expr(result)));
2184                },
2185                CallbackType::Math(MathJS::Min | MathJS::Max) => {
2186                  let Some(Some(EvaluateResultValue::Vec(args))) = context.first() else {
2187                    stylex_panic_with_context!(
2188                      path,
2189                      traversal_state,
2190                      "Math.min()/Math.max() requires at least one numeric argument."
2191                    )
2192                  };
2193
2194                  let num_args = args_to_numbers(args, state, traversal_state, fns);
2195
2196                  let result = match func.as_ref() {
2197                    CallbackType::Math(MathJS::Min) => {
2198                      num_args.iter().cloned().min_by(sort_numbers_factory())
2199                    }
2200                    CallbackType::Math(MathJS::Max) => {
2201                      num_args.iter().cloned().max_by(sort_numbers_factory())
2202                    }
2203                    _ => stylex_unreachable!("Invalid function type"),
2204                  }
2205                  .unwrap_or_else(|| stylex_panic!(
2206                    "Math.min()/Math.max() returned no result. Ensure numeric arguments are provided."
2207                  ));
2208
2209                  return Some(EvaluateResultValue::Expr(create_number_expr(result)));
2210                },
2211                CallbackType::Math(MathJS::Abs) => {
2212                  let Some(Some(EvaluateResultValue::Expr(expr))) = context.first() else {
2213                    stylex_panic_with_context!(
2214                      path,
2215                      traversal_state,
2216                      "Math.abs() requires one numeric argument."
2217                    )
2218                  };
2219
2220                  let num =
2221                    expr_to_num(expr, state, traversal_state, fns).unwrap_or_else(|error| {
2222                      stylex_panic_with_context!(path, traversal_state, error.to_string().as_str())
2223                    });
2224
2225                  return Some(EvaluateResultValue::Expr(create_number_expr(num.abs())));
2226                },
2227                CallbackType::String(StringJS::Concat) => {
2228                  let Some(Some(EvaluateResultValue::Expr(base_str))) = context.first() else {
2229                    stylex_panic_with_context!(
2230                      path,
2231                      traversal_state,
2232                      "String.concat() requires at least one argument."
2233                    )
2234                  };
2235
2236                  let args = evaluate_func_call_args(call, state, traversal_state, fns);
2237
2238                  let mut str_args_vec = Vec::new();
2239                  for arg in &args {
2240                    match arg.as_expr() {
2241                      Some(expr) => {
2242                        str_args_vec.push(expr_to_str_or_deopt!(
2243                          expr,
2244                          state,
2245                          traversal_state,
2246                          fns,
2247                          EXPRESSION_IS_NOT_A_STRING
2248                        ));
2249                      },
2250                      None => {
2251                        deopt(path, state, "All arguments must be a string");
2252                        return None;
2253                      },
2254                    }
2255                  }
2256                  let str_args = str_args_vec.join("");
2257
2258                  let base_str = expr_to_str_or_deopt!(
2259                    base_str,
2260                    state,
2261                    traversal_state,
2262                    fns,
2263                    EXPRESSION_IS_NOT_A_STRING
2264                  );
2265
2266                  return Some(EvaluateResultValue::Expr(create_string_expr(
2267                    format!("{}{}", base_str, str_args).as_str(),
2268                  )));
2269                },
2270                CallbackType::String(StringJS::CharCodeAt) => {
2271                  let Some(Some(EvaluateResultValue::Expr(base_str))) = context.first() else {
2272                    stylex_panic_with_context!(
2273                      path,
2274                      traversal_state,
2275                      "String.concat() requires at least one argument."
2276                    )
2277                  };
2278
2279                  let base_str = expr_to_str_or_deopt!(
2280                    base_str,
2281                    state,
2282                    traversal_state,
2283                    fns,
2284                    EXPRESSION_IS_NOT_A_STRING
2285                  );
2286
2287                  let args = evaluate_func_call_args(call, state, traversal_state, fns);
2288
2289                  let num_args = args
2290                    .iter()
2291                    .map(|arg| {
2292                      arg
2293                        .as_expr()
2294                        .map(|expr| {
2295                          unwrap_or_panic!(expr_to_num(expr, state, traversal_state, fns))
2296                        })
2297                        .unwrap_or_else(|| {
2298                          stylex_panic!("The first argument must be a numeric value.")
2299                        })
2300                    })
2301                    .collect::<Vec<f64>>();
2302
2303                  let char_index = num_args.first().unwrap_or_else(|| {
2304                    stylex_panic!("The first argument of String.charCodeAt() must be a number.")
2305                  });
2306
2307                  let char_code =
2308                    char_code_at(&base_str, *char_index as usize).unwrap_or_else(|| {
2309                      stylex_panic!("String.charCodeAt() returned no result for the given index.")
2310                    });
2311
2312                  return Some(EvaluateResultValue::Expr(create_number_expr(
2313                    char_code as f64,
2314                  )));
2315                },
2316                CallbackType::Custom(arrow_fn) => {
2317                  let args = evaluate_func_call_args(call, state, traversal_state, fns);
2318
2319                  let evaluation_result = evaluate_cached(arrow_fn, state, traversal_state, fns);
2320
2321                  let expr_result = match evaluation_result.as_ref() {
2322                    Some(EvaluateResultValue::Callback(cb)) => {
2323                      cb(args.into_iter().map(Some).collect())
2324                    },
2325                    _ => {
2326                      stylex_panic_with_context!(
2327                        path,
2328                        traversal_state,
2329                        "Could not resolve the arrow function reference."
2330                      )
2331                    },
2332                  };
2333
2334                  return Some(EvaluateResultValue::Expr(expr_result));
2335                },
2336              }
2337            },
2338            FunctionType::DefaultMarker(default_marker) => {
2339              return Some(EvaluateResultValue::FunctionConfig(FunctionConfig {
2340                fn_ptr: FunctionType::DefaultMarker(Arc::clone(&default_marker)),
2341                takes_path: false,
2342              }));
2343            },
2344            FunctionType::EnvFunction(env_fn) => {
2345              let args = evaluate_func_call_args(call, state, traversal_state, fns);
2346              let env_args: Vec<Expr> = args
2347                .iter()
2348                .map(|arg| {
2349                  match arg.as_expr() {
2350                    Some(e) => e,
2351                    None => stylex_panic!("{}", ARGUMENT_NOT_EXPRESSION),
2352                  }
2353                  .clone()
2354                })
2355                .collect();
2356              let result = env_fn.call(env_args);
2357              return Some(EvaluateResultValue::Expr(result));
2358            },
2359            _ => stylex_panic_with_context!(
2360              path,
2361              traversal_state,
2362              "Unsupported function type in static evaluation."
2363            ),
2364          }
2365        }
2366      }
2367
2368      return deopt(
2369        normalized_path,
2370        state,
2371        &unsupported_expression(&format!(
2372          "{:?}",
2373          normalized_path.get_type(get_default_expr_ctx())
2374        )),
2375      );
2376    },
2377    Expr::Await(await_expr) => evaluate_cached(&await_expr.arg, state, traversal_state, fns),
2378    Expr::OptChain(opt_chain) => {
2379      // Evaluate the base object/callee first
2380      let base_result = match opt_chain.base.as_ref() {
2381        OptChainBase::Member(member) => evaluate_cached(&member.obj, state, traversal_state, fns),
2382        OptChainBase::Call(call) => evaluate_cached(&call.callee, state, traversal_state, fns),
2383      };
2384
2385      // Check if we should short-circuit:
2386      // 1. Base is null literal
2387      // 2. Base is undefined identifier
2388      // 3. Base evaluation failed (returned None)
2389      let should_short_circuit = match &base_result {
2390        Some(EvaluateResultValue::Expr(base_expr)) => {
2391          matches!(base_expr, Expr::Lit(Lit::Null(_)))
2392            || (matches!(base_expr, Expr::Ident(ident) if ident.sym == *"undefined"))
2393        },
2394        None => true,
2395        // For other result types (Object, Array, FunctionConfig, etc.), don't short-circuit
2396        _ => false,
2397      };
2398
2399      if should_short_circuit {
2400        None
2401      } else {
2402        // Otherwise, evaluate the full optional chain expression
2403        match opt_chain.base.as_ref() {
2404          OptChainBase::Member(member) => {
2405            let member_expr = Expr::Member(member.clone());
2406            evaluate_cached(&member_expr, state, traversal_state, fns)
2407          },
2408          OptChainBase::Call(call) => {
2409            let call_expr = Expr::Call(call.clone().into());
2410            evaluate_cached(&call_expr, state, traversal_state, fns)
2411          },
2412        }
2413      }
2414    },
2415    _ => {
2416      warn!(
2417        "Unsupported type of expression: {:?}. If its not enough, please run in debug mode to see more details",
2418        normalized_path.get_type(get_default_expr_ctx())
2419      );
2420
2421      debug!("Unsupported type of expression: {:?}", normalized_path);
2422
2423      return deopt(
2424        normalized_path,
2425        state,
2426        &unsupported_expression(&format!(
2427          "{:?}",
2428          normalized_path.get_type(get_default_expr_ctx())
2429        )),
2430      );
2431    },
2432  };
2433
2434  if result.is_none() && normalized_path.is_ident() {
2435    let Some(ident) = normalized_path.as_ident() else {
2436      stylex_panic_with_context!(
2437        path,
2438        traversal_state,
2439        "Could not resolve the identifier. Ensure it is defined and in scope."
2440      )
2441    };
2442
2443    let binding = get_var_decl_by_ident(
2444      ident,
2445      traversal_state,
2446      &state.functions,
2447      if traversal_state.cycle == TransformationCycle::TransformExit {
2448        // NOTE: We don't want to reduce the binding count of stylex.props arguments
2449        VarDeclAction::None
2450      } else {
2451        VarDeclAction::Reduce
2452      },
2453    );
2454
2455    if let Some(init) = binding.and_then(|var_decl| var_decl.init.clone()) {
2456      return evaluate_cached(&init, state, traversal_state, fns);
2457    }
2458
2459    let name = ident.sym.to_string();
2460
2461    if name == "undefined" || name == "Infinity" || name == "NaN" {
2462      return Some(EvaluateResultValue::Expr(Expr::from(ident.clone())));
2463    }
2464
2465    if let Some(import_path) = get_import_by_ident(ident, traversal_state)
2466      && !state.functions.disable_imports
2467    {
2468      let (local_name, imported) = import_path
2469        .specifiers
2470        .iter()
2471        .find_map(|import| {
2472          let (local_name, imported) = match import {
2473            ImportSpecifier::Default(default) => (
2474              default.local.clone(),
2475              Some(ModuleExportName::Ident(default.local.clone())),
2476            ),
2477            ImportSpecifier::Named(named) => (named.local.clone(), named.imported.clone()),
2478            ImportSpecifier::Namespace(namespace) => (
2479              namespace.local.clone(),
2480              Some(ModuleExportName::Ident(namespace.local.clone())),
2481            ),
2482          };
2483
2484          if ident.sym == local_name.sym {
2485            Some((local_name, imported))
2486          } else {
2487            None
2488          }
2489        })
2490        .unwrap_or_else(|| {
2491          stylex_panic!("Could not resolve the import specifier. Ensure the import is correct.")
2492        });
2493
2494      let imported = imported
2495        .clone()
2496        .unwrap_or_else(|| ModuleExportName::Ident(local_name.clone()));
2497
2498      let abs_path = traversal_state.import_path_resolver(
2499        convert_atom_to_str_ref(&import_path.src.value),
2500        &mut FxHashMap::default(),
2501      );
2502
2503      let imported_name = match imported {
2504        ModuleExportName::Ident(ident) => ident.sym.to_string(),
2505        ModuleExportName::Str(strng) => convert_atom_to_string(&strng.value),
2506      };
2507
2508      let return_value = match abs_path {
2509        ImportPathResolution::Tuple(ImportPathResolutionType::ThemeNameRef, value) => {
2510          evaluate_theme_ref(&value, imported_name, traversal_state)
2511        },
2512        _ => return deopt(path, state, IMPORT_PATH_RESOLUTION_ERROR),
2513      };
2514
2515      if state.confident {
2516        let import_path_src = convert_atom_to_string(&import_path.src.value);
2517
2518        if !state.added_imports.contains(&import_path_src)
2519          && traversal_state.get_treeshake_compensation()
2520        {
2521          let prepend_import_module_item = add_import_expression(&import_path_src);
2522
2523          if !traversal_state
2524            .prepend_import_module_items
2525            .contains(&prepend_import_module_item)
2526          {
2527            traversal_state
2528              .prepend_import_module_items
2529              .push(prepend_import_module_item);
2530          }
2531
2532          state.added_imports.insert(import_path_src);
2533        }
2534
2535        return Some(EvaluateResultValue::ThemeRef(return_value));
2536      }
2537    }
2538
2539    return check_ident_declaration(
2540      ident,
2541      &[
2542        (
2543          DeclarationType::Class,
2544          &traversal_state.class_name_declarations,
2545        ),
2546        (
2547          DeclarationType::Function,
2548          &traversal_state.function_name_declarations,
2549        ),
2550      ],
2551      state,
2552      normalized_path,
2553    );
2554  }
2555
2556  if result.is_none() {
2557    return deopt(
2558      normalized_path,
2559      state,
2560      &unsupported_expression(&format!(
2561        "{:?}",
2562        normalized_path.get_type(get_default_expr_ctx())
2563      )),
2564    );
2565  }
2566
2567  result
2568}
2569
2570/// Normalizes different argument types into an ObjectLit for JavaScript object methods.
2571fn normalize_js_object_method_args(cached_arg: Option<EvaluateResultValue>) -> Option<ObjectLit> {
2572  cached_arg.and_then(|arg| match arg {
2573    EvaluateResultValue::Expr(expr) => expr.as_object().cloned().or_else(|| {
2574      if let Expr::Lit(Lit::Str(ref strng)) = expr {
2575        let keys = convert_atom_to_string(&strng.value)
2576          .chars()
2577          .enumerate()
2578          .map(|(i, c)| {
2579            create_ident_key_value_prop(&i.to_string(), create_string_expr(&c.to_string()))
2580          })
2581          .collect::<Vec<PropOrSpread>>();
2582
2583        Some(create_object_lit(keys))
2584      } else {
2585        None
2586      }
2587    }),
2588
2589    EvaluateResultValue::Vec(arr) => {
2590      let props = arr
2591        .iter()
2592        .enumerate()
2593        .filter_map(|(index, elem)| {
2594          elem.as_ref().map(|elem_value| {
2595            let expr = match elem_value {
2596              EvaluateResultValue::Expr(expr) => expr.clone(),
2597              EvaluateResultValue::Vec(vec) => normalize_js_object_method_nested_vector_arg(vec),
2598              _ => stylex_panic!("{}", ILLEGAL_PROP_ARRAY_VALUE),
2599            };
2600
2601            create_ident_key_value_prop(&index.to_string(), expr)
2602          })
2603        })
2604        .collect();
2605
2606      Some(create_object_lit(props))
2607    },
2608
2609    _ => None,
2610  })
2611}
2612
2613/// Helper function to convert a nested vector of EvaluateResultValues to an array expression
2614fn normalize_js_object_method_nested_vector_arg(vec: &[Option<EvaluateResultValue>]) -> Expr {
2615  let elems = vec
2616    .iter()
2617    .map(|entry| {
2618      let expr = entry
2619        .as_ref()
2620        .and_then(|entry| {
2621          entry
2622            .as_vec()
2623            .map(|nested_vec| {
2624              let nested_elems = nested_vec
2625                .iter()
2626                .flatten()
2627                .map(|item| {
2628                  let expr = match item.as_expr() {
2629                    Some(e) => e,
2630                    None => stylex_panic!("{}", ARGUMENT_NOT_EXPRESSION),
2631                  };
2632                  Some(create_expr_or_spread(expr.clone()))
2633                })
2634                .collect();
2635
2636              create_array_expression(nested_elems)
2637            })
2638            .or_else(|| entry.as_expr().cloned())
2639        })
2640        .unwrap_or_else(|| stylex_panic!("{}", ILLEGAL_PROP_ARRAY_VALUE));
2641
2642      Some(create_expr_or_spread(expr))
2643    })
2644    .collect();
2645
2646  create_array_expression(elems)
2647}
2648
2649fn evaluate_func_call_args(
2650  call: &CallExpr,
2651  state: &mut EvaluationState,
2652  traversal_state: &mut StateManager,
2653  fns: &FunctionMap,
2654) -> Vec<EvaluateResultValue> {
2655  call
2656    .args
2657    .iter()
2658    .filter_map(|arg| evaluate_cached(&arg.expr, state, traversal_state, fns))
2659    .collect()
2660}
2661
2662fn args_to_numbers(
2663  args: &[Option<EvaluateResultValue>],
2664  state: &mut EvaluationState,
2665  traversal_state: &mut StateManager,
2666  fns: &FunctionMap,
2667) -> Vec<f64> {
2668  args
2669    .iter()
2670    .flat_map(|arg| match arg {
2671      Some(arg) => match arg {
2672        EvaluateResultValue::Expr(expr) => {
2673          vec![
2674            expr_to_num(expr, state, traversal_state, fns).unwrap_or_else(|error| {
2675              stylex_panic_with_context!(expr, traversal_state, error.to_string().as_str())
2676            }),
2677          ]
2678        },
2679        EvaluateResultValue::Vec(vec) => args_to_numbers(vec, state, traversal_state, fns),
2680        _ => stylex_unreachable!("Math.min/max requires a number"),
2681      },
2682      None => vec![],
2683    })
2684    .collect::<Vec<f64>>()
2685}
2686
2687fn get_binding<'a>(callee: &'a Expr, state: &'a StateManager) -> Option<&'a VarDeclarator> {
2688  match callee {
2689    Expr::Ident(ident) => get_var_decl_from(state, ident),
2690    _ => None,
2691  }
2692}
2693
2694fn is_valid_callee(callee: &Expr) -> bool {
2695  if let Expr::Ident(ident) = callee {
2696    VALID_CALLEES.contains(ident.sym.as_ref())
2697  } else {
2698    false
2699  }
2700}
2701
2702fn get_callee_name(callee: &Expr) -> &str {
2703  match callee {
2704    Expr::Ident(ident) => &ident.sym,
2705    _ => stylex_panic!("The function being called must be a static identifier."),
2706  }
2707}
2708
2709fn is_invalid_method(prop: &MemberProp) -> bool {
2710  match prop {
2711    MemberProp::Ident(ident_prop) => INVALID_METHODS.contains(&*ident_prop.sym),
2712    _ => false,
2713  }
2714}
2715
2716/// Checks if a member property represents a mutating object method (Object.assign, etc.)
2717fn is_mutating_object_method(prop: &MemberProp) -> bool {
2718  if let MemberProp::Ident(ident_prop) = prop {
2719    MUTATING_OBJECT_METHODS.contains(&*ident_prop.sym)
2720  } else {
2721    false
2722  }
2723}
2724
2725/// Checks if a member property represents a mutating array method (push, pop, splice, etc.)
2726fn is_mutating_array_method(prop: &MemberProp) -> bool {
2727  if let MemberProp::Ident(ident_prop) = prop {
2728    MUTATING_ARRAY_METHODS.contains(&*ident_prop.sym)
2729  } else {
2730    false
2731  }
2732}
2733
2734/// Checks if an expression represents a mutation operation
2735/// Returns true if any of the following conditions are met:
2736/// - Assignment to a member expression (e.g., `a.x = 1` or `a[0] = 1`)
2737/// - Update expression on a member (e.g., `++a.x` or `a[0]++`)
2738/// - Delete operation on a member (e.g., `delete a.x`)
2739fn is_mutation_expr(expr: &Expr) -> bool {
2740  match expr {
2741    // Check for assignment to member: a.x = 1 or a[0] = 1
2742    Expr::Assign(assign)
2743      if matches!(
2744        &assign.left,
2745        AssignTarget::Simple(SimpleAssignTarget::Member(member)) if member.obj.is_ident()
2746      ) =>
2747    {
2748      true
2749    },
2750
2751    // Check for update on member: ++a.x or a[0]++
2752    Expr::Update(update) if matches!(&*update.arg, Expr::Member(member) if member.obj.is_ident()) => {
2753      true
2754    },
2755
2756    // Check for delete on member: delete a.x
2757    Expr::Unary(unary)
2758      if unary.op == UnaryOp::Delete
2759        && matches!(&*unary.arg, Expr::Member(member) if member.obj.is_ident()) =>
2760    {
2761      true
2762    },
2763
2764    _ => false,
2765  }
2766}
2767
2768fn get_method_name(prop: &MemberProp) -> &str {
2769  match prop {
2770    MemberProp::Ident(ident_prop) => &ident_prop.sym,
2771    _ => stylex_panic!("The method name in a call expression must be a static identifier."),
2772  }
2773}
2774
2775fn is_id_prop(prop: &MemberProp) -> Option<&Atom> {
2776  if let MemberProp::Computed(comp_prop) = prop
2777    && let Expr::Lit(Lit::Str(strng)) = comp_prop.expr.as_ref()
2778  {
2779    return Some(match strng.value.as_atom() {
2780      Some(a) => a,
2781      None => stylex_panic!("{}", INVALID_UTF8),
2782    });
2783  }
2784
2785  None
2786}
2787
2788pub(crate) fn evaluate_quasis(
2789  tpl_expr: &Expr,
2790  quasis: &[TplElement],
2791  raw: bool,
2792  state: &mut EvaluationState,
2793  traversal_state: &mut StateManager,
2794  fns: &FunctionMap,
2795) -> Option<EvaluateResultValue> {
2796  let mut strng = String::new();
2797
2798  let exprs = match tpl_expr {
2799    Expr::Tpl(tpl) => &tpl.exprs,
2800    Expr::TaggedTpl(tagged_tpl) => &tagged_tpl.tpl.exprs,
2801    _ => stylex_panic_with_context!(
2802      tpl_expr,
2803      traversal_state,
2804      "Expected a template literal expression but received a different expression type."
2805    ),
2806  };
2807
2808  for (i, elem) in quasis.iter().enumerate() {
2809    if !state.confident {
2810      return None;
2811    }
2812
2813    if raw {
2814      strng.push_str(&elem.raw);
2815    } else {
2816      strng.push_str(&extract_tpl_cooked_value(elem));
2817    }
2818
2819    if let Some(expr) = exprs.get(i)
2820      && let Some(evaluated_expr) = evaluate_cached(expr, state, traversal_state, fns)
2821      && let Some(lit_str) = evaluated_expr
2822        .as_expr()
2823        .and_then(|expr| expr.as_lit())
2824        .and_then(convert_lit_to_string)
2825    {
2826      strng.push_str(&lit_str);
2827    }
2828  }
2829
2830  if !state.confident {
2831    return None;
2832  }
2833
2834  Some(EvaluateResultValue::Expr(create_string_expr(&strng)))
2835}
2836
2837pub(crate) fn evaluate_cached(
2838  path: &Expr,
2839  state: &mut EvaluationState,
2840  traversal_state: &mut StateManager,
2841  fns: &FunctionMap,
2842) -> Option<EvaluateResultValue> {
2843  let mut cleaned_path = drop_span(path.clone());
2844
2845  let cleaned_path_hash = stable_hash(&cleaned_path);
2846
2847  let existing = traversal_state.seen.get(&cleaned_path_hash);
2848
2849  match existing {
2850    Some(evaluate_value) => {
2851      let evaluate_value: &SeenValueWithVarDeclCount = evaluate_value.borrow();
2852
2853      let evaluated_value = &evaluate_value.seen_value;
2854      let var_decl_count_value_diff = &evaluate_value.var_decl_count;
2855
2856      if evaluated_value.resolved {
2857        let resolved = evaluated_value.value.clone();
2858
2859        match path {
2860          Expr::Ident(ident) => reduce_ident_count(traversal_state, ident),
2861          Expr::Member(member) => reduce_member_expression_count(traversal_state, member),
2862          Expr::Object(_) => {
2863            if let Some(var_decl_count_value_diff) = var_decl_count_value_diff {
2864              traversal_state.var_decl_count_map = sum_hash_map_values(
2865                var_decl_count_value_diff,
2866                &traversal_state.var_decl_count_map,
2867              );
2868            }
2869          },
2870          _ => {},
2871        }
2872
2873        return resolved;
2874      }
2875
2876      deopt(path, state, PATH_WITHOUT_NODE)
2877    },
2878    None => {
2879      let should_save_var_decl_count = path.is_object();
2880
2881      let var_decl_count_map_orig =
2882        should_save_var_decl_count.then(|| traversal_state.var_decl_count_map.clone());
2883
2884      let val = _evaluate(&mut cleaned_path, state, traversal_state, fns);
2885
2886      let var_decl_count_value_diff = var_decl_count_map_orig.as_ref().map(|orig| {
2887        let var_decl_count_map_diff =
2888          get_hash_map_difference(&traversal_state.var_decl_count_map, orig);
2889
2890        get_hash_map_value_difference(&var_decl_count_map_diff, orig)
2891      });
2892
2893      let seen_value = if state.confident {
2894        SeenValue {
2895          value: val.clone(),
2896          resolved: true,
2897        }
2898      } else {
2899        SeenValue {
2900          value: None,
2901          resolved: false,
2902        }
2903      };
2904
2905      traversal_state
2906        .seen
2907        .entry(cleaned_path_hash)
2908        .or_insert_with(|| {
2909          Rc::new(SeenValueWithVarDeclCount {
2910            seen_value,
2911            var_decl_count: var_decl_count_value_diff,
2912          })
2913        });
2914
2915      val
2916    },
2917  }
2918}
2919
2920fn evaluate_theme_ref(file_name: &str, export_name: String, state: &StateManager) -> ThemeRef {
2921  ThemeRef::new(
2922    file_name.to_owned(),
2923    export_name,
2924    state.options.class_name_prefix.clone(),
2925  )
2926}