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