1use react_compiler_diagnostics::CompilerError;
11use react_compiler_hir::{
12 EvaluationOrder, FunctionId, InstructionValue, ParamPattern, Place, PrunedReactiveScopeBlock,
13 ReactiveBlock, ReactiveFunction, ReactiveInstruction, ReactiveScopeBlock, ReactiveStatement,
14 ReactiveTerminal, ReactiveTerminalStatement, ReactiveValue, environment::Environment,
15};
16
17pub trait ReactiveFunctionVisitor {
28 type State;
29
30 fn env(&self) -> &Environment;
34
35 fn visit_id(&self, _id: EvaluationOrder, _state: &mut Self::State) {}
36
37 fn visit_place(&self, _id: EvaluationOrder, _place: &Place, _state: &mut Self::State) {}
38
39 fn visit_lvalue(&self, _id: EvaluationOrder, _lvalue: &Place, _state: &mut Self::State) {}
40
41 fn visit_param(&self, _place: &Place, _state: &mut Self::State) {}
42
43 fn visit_hir_function(&self, func_id: FunctionId, state: &mut Self::State) {
47 let inner_func = &self.env().functions[func_id.0 as usize];
48 for param in &inner_func.params {
49 let place = match param {
50 ParamPattern::Place(p) => p,
51 ParamPattern::Spread(s) => &s.place,
52 };
53 self.visit_param(place, state);
54 }
55 let block_ids: Vec<_> = inner_func.body.blocks.keys().copied().collect();
56 for block_id in block_ids {
57 let inner_func = &self.env().functions[func_id.0 as usize];
58 let block = &inner_func.body.blocks[&block_id];
59 let instr_ids: Vec<_> = block.instructions.clone();
60 let terminal_operands: Vec<Place> =
61 react_compiler_hir::visitors::each_terminal_operand(&block.terminal);
62 let terminal_id = block.terminal.evaluation_order();
63
64 for instr_id in &instr_ids {
65 let inner_func = &self.env().functions[func_id.0 as usize];
66 let instr = &inner_func.instructions[instr_id.0 as usize];
67 let reactive_instr = ReactiveInstruction {
69 id: instr.id,
70 lvalue: Some(instr.lvalue.clone()),
71 value: ReactiveValue::Instruction(instr.value.clone()),
72 effects: None,
73 loc: instr.loc,
74 };
75 self.visit_instruction(&reactive_instr, state);
76 match &instr.value {
78 InstructionValue::FunctionExpression { lowered_func, .. }
79 | InstructionValue::ObjectMethod { lowered_func, .. } => {
80 self.visit_hir_function(lowered_func.func, state);
81 }
82 _ => {}
83 }
84 }
85 for operand in &terminal_operands {
86 self.visit_place(terminal_id, operand, state);
87 }
88 }
89 }
90
91 fn visit_value(&self, id: EvaluationOrder, value: &ReactiveValue, state: &mut Self::State) {
92 self.traverse_value(id, value, state);
93 }
94
95 fn traverse_value(&self, id: EvaluationOrder, value: &ReactiveValue, state: &mut Self::State) {
96 match value {
97 ReactiveValue::OptionalExpression { value: inner, .. } => {
98 self.visit_value(id, inner, state);
99 }
100 ReactiveValue::LogicalExpression { left, right, .. } => {
101 self.visit_value(id, left, state);
102 self.visit_value(id, right, state);
103 }
104 ReactiveValue::ConditionalExpression {
105 test,
106 consequent,
107 alternate,
108 ..
109 } => {
110 self.visit_value(id, test, state);
111 self.visit_value(id, consequent, state);
112 self.visit_value(id, alternate, state);
113 }
114 ReactiveValue::SequenceExpression {
115 instructions,
116 id: seq_id,
117 value: inner,
118 ..
119 } => {
120 for instr in instructions {
121 self.visit_instruction(instr, state);
122 }
123 self.visit_value(*seq_id, inner, state);
124 }
125 ReactiveValue::Instruction(instr_value) => {
126 let operands = react_compiler_hir::visitors::each_instruction_value_operand(
127 instr_value,
128 self.env(),
129 );
130 for place in &operands {
131 self.visit_place(id, place, state);
132 }
133 }
134 }
135 }
136
137 fn visit_instruction(&self, instruction: &ReactiveInstruction, state: &mut Self::State) {
138 self.traverse_instruction(instruction, state);
139 }
140
141 fn traverse_instruction(&self, instruction: &ReactiveInstruction, state: &mut Self::State) {
142 self.visit_id(instruction.id, state);
143 if let Some(lvalue) = &instruction.lvalue {
145 self.visit_lvalue(instruction.id, lvalue, state);
146 }
147 if let ReactiveValue::Instruction(iv) = &instruction.value {
149 for place in react_compiler_hir::visitors::each_instruction_value_lvalue(iv) {
150 self.visit_lvalue(instruction.id, &place, state);
151 }
152 }
153 self.visit_value(instruction.id, &instruction.value, state);
154 }
155
156 fn visit_terminal(&self, stmt: &ReactiveTerminalStatement, state: &mut Self::State) {
157 self.traverse_terminal(stmt, state);
158 }
159
160 fn traverse_terminal(&self, stmt: &ReactiveTerminalStatement, state: &mut Self::State) {
161 let terminal = &stmt.terminal;
162 let id = terminal_id(terminal);
163 self.visit_id(id, state);
164 match terminal {
165 ReactiveTerminal::Break { .. } | ReactiveTerminal::Continue { .. } => {}
166 ReactiveTerminal::Return { value, id, .. } => {
167 self.visit_place(*id, value, state);
168 }
169 ReactiveTerminal::Throw { value, id, .. } => {
170 self.visit_place(*id, value, state);
171 }
172 ReactiveTerminal::For {
173 init,
174 test,
175 update,
176 loop_block,
177 id,
178 ..
179 } => {
180 self.visit_value(*id, init, state);
181 self.visit_value(*id, test, state);
182 self.visit_block(loop_block, state);
183 if let Some(update) = update {
184 self.visit_value(*id, update, state);
185 }
186 }
187 ReactiveTerminal::ForOf {
188 init,
189 test,
190 loop_block,
191 id,
192 ..
193 } => {
194 self.visit_value(*id, init, state);
195 self.visit_value(*id, test, state);
196 self.visit_block(loop_block, state);
197 }
198 ReactiveTerminal::ForIn {
199 init,
200 loop_block,
201 id,
202 ..
203 } => {
204 self.visit_value(*id, init, state);
205 self.visit_block(loop_block, state);
206 }
207 ReactiveTerminal::DoWhile {
208 loop_block,
209 test,
210 id,
211 ..
212 } => {
213 self.visit_block(loop_block, state);
214 self.visit_value(*id, test, state);
215 }
216 ReactiveTerminal::While {
217 test,
218 loop_block,
219 id,
220 ..
221 } => {
222 self.visit_value(*id, test, state);
223 self.visit_block(loop_block, state);
224 }
225 ReactiveTerminal::If {
226 test,
227 consequent,
228 alternate,
229 id,
230 ..
231 } => {
232 self.visit_place(*id, test, state);
233 self.visit_block(consequent, state);
234 if let Some(alt) = alternate {
235 self.visit_block(alt, state);
236 }
237 }
238 ReactiveTerminal::Switch {
239 test, cases, id, ..
240 } => {
241 self.visit_place(*id, test, state);
242 for case in cases {
243 if let Some(t) = &case.test {
244 self.visit_place(*id, t, state);
245 }
246 if let Some(block) = &case.block {
247 self.visit_block(block, state);
248 }
249 }
250 }
251 ReactiveTerminal::Label { block, .. } => {
252 self.visit_block(block, state);
253 }
254 ReactiveTerminal::Try {
255 block,
256 handler_binding,
257 handler,
258 id,
259 ..
260 } => {
261 self.visit_block(block, state);
262 if let Some(binding) = handler_binding {
263 self.visit_place(*id, binding, state);
264 }
265 self.visit_block(handler, state);
266 }
267 }
268 }
269
270 fn visit_scope(&self, scope: &ReactiveScopeBlock, state: &mut Self::State) {
271 self.traverse_scope(scope, state);
272 }
273
274 fn traverse_scope(&self, scope: &ReactiveScopeBlock, state: &mut Self::State) {
275 self.visit_block(&scope.instructions, state);
276 }
277
278 fn visit_pruned_scope(&self, scope: &PrunedReactiveScopeBlock, state: &mut Self::State) {
279 self.traverse_pruned_scope(scope, state);
280 }
281
282 fn traverse_pruned_scope(&self, scope: &PrunedReactiveScopeBlock, state: &mut Self::State) {
283 self.visit_block(&scope.instructions, state);
284 }
285
286 fn visit_block(&self, block: &ReactiveBlock, state: &mut Self::State) {
287 self.traverse_block(block, state);
288 }
289
290 fn traverse_block(&self, block: &ReactiveBlock, state: &mut Self::State) {
291 for stmt in block {
292 match stmt {
293 ReactiveStatement::Instruction(instr) => {
294 self.visit_instruction(instr, state);
295 }
296 ReactiveStatement::Scope(scope) => {
297 self.visit_scope(scope, state);
298 }
299 ReactiveStatement::PrunedScope(scope) => {
300 self.visit_pruned_scope(scope, state);
301 }
302 ReactiveStatement::Terminal(terminal) => {
303 self.visit_terminal(terminal, state);
304 }
305 }
306 }
307 }
308}
309
310pub fn visit_reactive_function<V: ReactiveFunctionVisitor>(
313 func: &ReactiveFunction,
314 visitor: &V,
315 state: &mut V::State,
316) {
317 visitor.visit_block(&func.body, state);
318}
319
320pub enum Transformed<T> {
327 Keep,
328 Remove,
329 Replace(T),
330 ReplaceMany(Vec<T>),
331}
332
333#[allow(dead_code)]
336pub enum TransformedValue {
337 Keep,
338 Replace(ReactiveValue),
339}
340
341pub trait ReactiveFunctionTransform {
353 type State;
354
355 fn env(&self) -> &Environment;
359
360 fn visit_id(
361 &mut self,
362 _id: EvaluationOrder,
363 _state: &mut Self::State,
364 ) -> Result<(), CompilerError> {
365 Ok(())
366 }
367
368 fn visit_place(
369 &mut self,
370 _id: EvaluationOrder,
371 _place: &Place,
372 _state: &mut Self::State,
373 ) -> Result<(), CompilerError> {
374 Ok(())
375 }
376
377 fn visit_lvalue(
378 &mut self,
379 _id: EvaluationOrder,
380 _lvalue: &Place,
381 _state: &mut Self::State,
382 ) -> Result<(), CompilerError> {
383 Ok(())
384 }
385
386 fn visit_value(
387 &mut self,
388 id: EvaluationOrder,
389 value: &mut ReactiveValue,
390 state: &mut Self::State,
391 ) -> Result<(), CompilerError> {
392 self.traverse_value(id, value, state)
393 }
394
395 fn traverse_value(
396 &mut self,
397 id: EvaluationOrder,
398 value: &mut ReactiveValue,
399 state: &mut Self::State,
400 ) -> Result<(), CompilerError> {
401 match value {
402 ReactiveValue::OptionalExpression { value: inner, .. } => {
403 let next = self.transform_value(id, inner, state)?;
404 if let TransformedValue::Replace(new_value) = next {
405 **inner = new_value;
406 }
407 }
408 ReactiveValue::LogicalExpression { left, right, .. } => {
409 let next_left = self.transform_value(id, left, state)?;
410 if let TransformedValue::Replace(new_value) = next_left {
411 **left = new_value;
412 }
413 let next_right = self.transform_value(id, right, state)?;
414 if let TransformedValue::Replace(new_value) = next_right {
415 **right = new_value;
416 }
417 }
418 ReactiveValue::ConditionalExpression {
419 test,
420 consequent,
421 alternate,
422 ..
423 } => {
424 let next_test = self.transform_value(id, test, state)?;
425 if let TransformedValue::Replace(new_value) = next_test {
426 **test = new_value;
427 }
428 let next_cons = self.transform_value(id, consequent, state)?;
429 if let TransformedValue::Replace(new_value) = next_cons {
430 **consequent = new_value;
431 }
432 let next_alt = self.transform_value(id, alternate, state)?;
433 if let TransformedValue::Replace(new_value) = next_alt {
434 **alternate = new_value;
435 }
436 }
437 ReactiveValue::SequenceExpression {
438 instructions,
439 id: seq_id,
440 value: inner,
441 ..
442 } => {
443 let seq_id = *seq_id;
444 for instr in instructions.iter_mut() {
445 self.visit_instruction(instr, state)?;
446 }
447 let next = self.transform_value(seq_id, inner, state)?;
448 if let TransformedValue::Replace(new_value) = next {
449 **inner = new_value;
450 }
451 }
452 ReactiveValue::Instruction(instr_value) => {
453 let operands = react_compiler_hir::visitors::each_instruction_value_operand(
456 instr_value,
457 self.env(),
458 );
459 for place in &operands {
460 self.visit_place(id, place, state)?;
461 }
462 }
463 }
464 Ok(())
465 }
466
467 fn visit_instruction(
468 &mut self,
469 instruction: &mut ReactiveInstruction,
470 state: &mut Self::State,
471 ) -> Result<(), CompilerError> {
472 self.traverse_instruction(instruction, state)
473 }
474
475 fn transform_value(
476 &mut self,
477 id: EvaluationOrder,
478 value: &mut ReactiveValue,
479 state: &mut Self::State,
480 ) -> Result<TransformedValue, CompilerError> {
481 self.visit_value(id, value, state)?;
482 Ok(TransformedValue::Keep)
483 }
484
485 fn traverse_instruction(
486 &mut self,
487 instruction: &mut ReactiveInstruction,
488 state: &mut Self::State,
489 ) -> Result<(), CompilerError> {
490 self.visit_id(instruction.id, state)?;
491 if let Some(lvalue) = &instruction.lvalue {
493 self.visit_lvalue(instruction.id, lvalue, state)?;
494 }
495 if let ReactiveValue::Instruction(iv) = &instruction.value {
497 for place in react_compiler_hir::visitors::each_instruction_value_lvalue(iv) {
498 self.visit_lvalue(instruction.id, &place, state)?;
499 }
500 }
501 let next_value = self.transform_value(instruction.id, &mut instruction.value, state)?;
502 if let TransformedValue::Replace(new_value) = next_value {
503 instruction.value = new_value;
504 }
505 Ok(())
506 }
507
508 fn visit_terminal(
509 &mut self,
510 stmt: &mut ReactiveTerminalStatement,
511 state: &mut Self::State,
512 ) -> Result<(), CompilerError> {
513 self.traverse_terminal(stmt, state)
514 }
515
516 fn traverse_terminal(
517 &mut self,
518 stmt: &mut ReactiveTerminalStatement,
519 state: &mut Self::State,
520 ) -> Result<(), CompilerError> {
521 let terminal = &mut stmt.terminal;
522 let id = terminal_id(terminal);
523 self.visit_id(id, state)?;
524 match terminal {
525 ReactiveTerminal::Break { .. } | ReactiveTerminal::Continue { .. } => {}
526 ReactiveTerminal::Return { value, id, .. } => {
527 self.visit_place(*id, value, state)?;
528 }
529 ReactiveTerminal::Throw { value, id, .. } => {
530 self.visit_place(*id, value, state)?;
531 }
532 ReactiveTerminal::For {
533 init,
534 test,
535 update,
536 loop_block,
537 id,
538 ..
539 } => {
540 let id = *id;
541 let next_init = self.transform_value(id, init, state)?;
542 if let TransformedValue::Replace(new_value) = next_init {
543 *init = new_value;
544 }
545 let next_test = self.transform_value(id, test, state)?;
546 if let TransformedValue::Replace(new_value) = next_test {
547 *test = new_value;
548 }
549 if let Some(update) = update {
550 let next_update = self.transform_value(id, update, state)?;
551 if let TransformedValue::Replace(new_value) = next_update {
552 *update = new_value;
553 }
554 }
555 self.visit_block(loop_block, state)?;
556 }
557 ReactiveTerminal::ForOf {
558 init,
559 test,
560 loop_block,
561 id,
562 ..
563 } => {
564 let id = *id;
565 let next_init = self.transform_value(id, init, state)?;
566 if let TransformedValue::Replace(new_value) = next_init {
567 *init = new_value;
568 }
569 let next_test = self.transform_value(id, test, state)?;
570 if let TransformedValue::Replace(new_value) = next_test {
571 *test = new_value;
572 }
573 self.visit_block(loop_block, state)?;
574 }
575 ReactiveTerminal::ForIn {
576 init,
577 loop_block,
578 id,
579 ..
580 } => {
581 let id = *id;
582 let next_init = self.transform_value(id, init, state)?;
583 if let TransformedValue::Replace(new_value) = next_init {
584 *init = new_value;
585 }
586 self.visit_block(loop_block, state)?;
587 }
588 ReactiveTerminal::DoWhile {
589 loop_block,
590 test,
591 id,
592 ..
593 } => {
594 let id = *id;
595 self.visit_block(loop_block, state)?;
596 let next_test = self.transform_value(id, test, state)?;
597 if let TransformedValue::Replace(new_value) = next_test {
598 *test = new_value;
599 }
600 }
601 ReactiveTerminal::While {
602 test,
603 loop_block,
604 id,
605 ..
606 } => {
607 let id = *id;
608 let next_test = self.transform_value(id, test, state)?;
609 if let TransformedValue::Replace(new_value) = next_test {
610 *test = new_value;
611 }
612 self.visit_block(loop_block, state)?;
613 }
614 ReactiveTerminal::If {
615 test,
616 consequent,
617 alternate,
618 id,
619 ..
620 } => {
621 self.visit_place(*id, test, state)?;
622 self.visit_block(consequent, state)?;
623 if let Some(alt) = alternate {
624 self.visit_block(alt, state)?;
625 }
626 }
627 ReactiveTerminal::Switch {
628 test, cases, id, ..
629 } => {
630 let id = *id;
631 self.visit_place(id, test, state)?;
632 for case in cases.iter_mut() {
633 if let Some(t) = &case.test {
634 self.visit_place(id, t, state)?;
635 }
636 if let Some(block) = &mut case.block {
637 self.visit_block(block, state)?;
638 }
639 }
640 }
641 ReactiveTerminal::Label { block, .. } => {
642 self.visit_block(block, state)?;
643 }
644 ReactiveTerminal::Try {
645 block,
646 handler_binding,
647 handler,
648 id,
649 ..
650 } => {
651 let id = *id;
652 self.visit_block(block, state)?;
653 if let Some(binding) = handler_binding {
654 self.visit_place(id, binding, state)?;
655 }
656 self.visit_block(handler, state)?;
657 }
658 }
659 Ok(())
660 }
661
662 fn visit_scope(
663 &mut self,
664 scope: &mut ReactiveScopeBlock,
665 state: &mut Self::State,
666 ) -> Result<(), CompilerError> {
667 self.traverse_scope(scope, state)
668 }
669
670 fn traverse_scope(
671 &mut self,
672 scope: &mut ReactiveScopeBlock,
673 state: &mut Self::State,
674 ) -> Result<(), CompilerError> {
675 self.visit_block(&mut scope.instructions, state)
676 }
677
678 fn visit_pruned_scope(
679 &mut self,
680 scope: &mut PrunedReactiveScopeBlock,
681 state: &mut Self::State,
682 ) -> Result<(), CompilerError> {
683 self.traverse_pruned_scope(scope, state)
684 }
685
686 fn traverse_pruned_scope(
687 &mut self,
688 scope: &mut PrunedReactiveScopeBlock,
689 state: &mut Self::State,
690 ) -> Result<(), CompilerError> {
691 self.visit_block(&mut scope.instructions, state)
692 }
693
694 fn visit_block(
695 &mut self,
696 block: &mut ReactiveBlock,
697 state: &mut Self::State,
698 ) -> Result<(), CompilerError> {
699 self.traverse_block(block, state)
700 }
701
702 fn transform_instruction(
703 &mut self,
704 instruction: &mut ReactiveInstruction,
705 state: &mut Self::State,
706 ) -> Result<Transformed<ReactiveStatement>, CompilerError> {
707 self.visit_instruction(instruction, state)?;
708 Ok(Transformed::Keep)
709 }
710
711 fn transform_terminal(
712 &mut self,
713 stmt: &mut ReactiveTerminalStatement,
714 state: &mut Self::State,
715 ) -> Result<Transformed<ReactiveStatement>, CompilerError> {
716 self.visit_terminal(stmt, state)?;
717 Ok(Transformed::Keep)
718 }
719
720 fn transform_scope(
721 &mut self,
722 scope: &mut ReactiveScopeBlock,
723 state: &mut Self::State,
724 ) -> Result<Transformed<ReactiveStatement>, CompilerError> {
725 self.visit_scope(scope, state)?;
726 Ok(Transformed::Keep)
727 }
728
729 fn transform_pruned_scope(
730 &mut self,
731 scope: &mut PrunedReactiveScopeBlock,
732 state: &mut Self::State,
733 ) -> Result<Transformed<ReactiveStatement>, CompilerError> {
734 self.visit_pruned_scope(scope, state)?;
735 Ok(Transformed::Keep)
736 }
737
738 fn traverse_block(
739 &mut self,
740 block: &mut ReactiveBlock,
741 state: &mut Self::State,
742 ) -> Result<(), CompilerError> {
743 let mut next_block: Option<Vec<ReactiveStatement>> = None;
744 let len = block.len();
745 for i in 0..len {
746 let mut stmt = std::mem::replace(
748 &mut block[i],
749 ReactiveStatement::Instruction(ReactiveInstruction {
751 id: EvaluationOrder(0),
752 lvalue: None,
753 value: ReactiveValue::Instruction(
754 react_compiler_hir::InstructionValue::Debugger { loc: None },
755 ),
756 effects: None,
757 loc: None,
758 }),
759 );
760 let transformed = match &mut stmt {
761 ReactiveStatement::Instruction(instr) => {
762 self.transform_instruction(instr, state)?
763 }
764 ReactiveStatement::Scope(scope) => self.transform_scope(scope, state)?,
765 ReactiveStatement::PrunedScope(scope) => {
766 self.transform_pruned_scope(scope, state)?
767 }
768 ReactiveStatement::Terminal(terminal) => {
769 self.transform_terminal(terminal, state)?
770 }
771 };
772 match transformed {
773 Transformed::Keep => {
774 if let Some(ref mut nb) = next_block {
775 nb.push(stmt);
776 } else {
777 block[i] = stmt;
779 }
780 }
781 Transformed::Remove => {
782 if next_block.is_none() {
783 next_block = Some(block[..i].to_vec());
784 }
785 }
786 Transformed::Replace(replacement) => {
787 if next_block.is_none() {
788 next_block = Some(block[..i].to_vec());
789 }
790 next_block.as_mut().unwrap().push(replacement);
791 }
792 Transformed::ReplaceMany(replacements) => {
793 if next_block.is_none() {
794 next_block = Some(block[..i].to_vec());
795 }
796 next_block.as_mut().unwrap().extend(replacements);
797 }
798 }
799 }
800 if let Some(nb) = next_block {
801 *block = nb;
802 }
803 Ok(())
804 }
805}
806
807pub fn transform_reactive_function<T: ReactiveFunctionTransform>(
810 func: &mut ReactiveFunction,
811 transform: &mut T,
812 state: &mut T::State,
813) -> Result<(), CompilerError> {
814 transform.visit_block(&mut func.body, state)
815}
816
817fn terminal_id(terminal: &ReactiveTerminal) -> EvaluationOrder {
822 match terminal {
823 ReactiveTerminal::Break { id, .. }
824 | ReactiveTerminal::Continue { id, .. }
825 | ReactiveTerminal::Return { id, .. }
826 | ReactiveTerminal::Throw { id, .. }
827 | ReactiveTerminal::Switch { id, .. }
828 | ReactiveTerminal::DoWhile { id, .. }
829 | ReactiveTerminal::While { id, .. }
830 | ReactiveTerminal::For { id, .. }
831 | ReactiveTerminal::ForOf { id, .. }
832 | ReactiveTerminal::ForIn { id, .. }
833 | ReactiveTerminal::If { id, .. }
834 | ReactiveTerminal::Label { id, .. }
835 | ReactiveTerminal::Try { id, .. } => *id,
836 }
837}