1use std::{borrow::Borrow, rc::Rc, sync::Arc};
2
3use 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#[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
93fn 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#[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 },
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 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 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 } 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 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 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 }
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 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 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 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 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 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 _ => false,
2397 };
2398
2399 if should_short_circuit {
2400 None
2401 } else {
2402 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 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
2570fn 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
2613fn 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
2716fn 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
2725fn 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
2734fn is_mutation_expr(expr: &Expr) -> bool {
2740 match expr {
2741 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 Expr::Update(update) if matches!(&*update.arg, Expr::Member(member) if member.obj.is_ident()) => {
2753 true
2754 },
2755
2756 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}