avm1_parser/
cfg.rs

1use crate::avm1::parse_action_header;
2use crate::parse_action;
3use avm1_types::cfg;
4use avm1_types::cfg::{Cfg, CfgBlock, CfgFlow, CfgLabel};
5use avm1_types::raw;
6use core::convert::TryFrom;
7use core::iter::Iterator;
8use core::ops::Range;
9use nom::lib::std::collections::{BTreeMap, HashMap};
10use vec1::Vec1;
11
12type Avm1Index = usize;
13type Avm1Range = Range<usize>;
14
15pub fn parse_cfg(avm1: &[u8]) -> Cfg {
16  let mut idg = IdGen::new();
17  let parser = Avm1Parser::new(avm1);
18  let range: Avm1Range = 0..avm1.len();
19  let mut parse_cx = ParseContext::new(&mut idg, range);
20  parse_into_cfg(&parser, &mut parse_cx)
21}
22
23/// Block identifier generator
24#[derive(Clone, Debug, Eq, PartialEq)]
25struct IdGen(u64);
26
27impl IdGen {
28  fn new() -> Self {
29    IdGen(0)
30  }
31
32  fn next(&mut self) -> u64 {
33    let result: u64 = self.0;
34    self.0 += 1;
35    result
36  }
37}
38
39fn try_add_offset(left: usize, right: i16) -> Option<usize> {
40  if right >= 0 {
41    let right_u16: u16 = u16::try_from(right).unwrap();
42    let right_usize = usize::from(right_u16);
43    left.checked_add(right_usize)
44  } else {
45    let right_u16: u16 = u16::try_from(-right).unwrap();
46    let right_usize = usize::from(right_u16);
47    left.checked_sub(right_usize)
48  }
49}
50
51#[derive(Debug, PartialEq, Eq, Copy, Clone)]
52struct Avm1Parser<'a> {
53  bytes: &'a [u8],
54}
55
56impl<'a> Avm1Parser<'a> {
57  fn new(bytes: &'a [u8]) -> Self {
58    Avm1Parser { bytes }
59  }
60
61  fn get(&self, offset: usize) -> (usize, raw::Action) {
62    if offset >= self.bytes.len() {
63      return (offset, raw::Action::End);
64    }
65    let input: &[u8] = &self.bytes[offset..];
66    match parse_action(input) {
67      Ok((next_input, action)) => (offset + (input.len() - next_input.len()), action),
68      Err(_) => (offset, raw::Action::Error(raw::Error { error: None })),
69    }
70  }
71
72  fn skip(&self, offset: usize, action_count: usize) -> usize {
73    let mut input: &[u8] = &self.bytes[offset..];
74    for _ in 0..action_count {
75      let (next_input, header) = parse_action_header(input).unwrap();
76      input = &next_input[header.length..];
77    }
78    self.bytes.len() - input.len()
79  }
80}
81
82#[derive(Debug, Eq, PartialEq)]
83struct ParseContext<'a> {
84  idg: &'a mut IdGen,
85  layers: Vec<LayerContext>,
86}
87
88/// Enum representing the reachability status of an action
89#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
90enum Reachability {
91  /// The action is only reached through simple linear control flow following a simple action
92  /// TODO: Rename to `advance`
93  Linear,
94  /// The action is reached through a jump:
95  /// - Entry point
96  /// - Jump action
97  /// - Linear flow following two or more simple actions
98  Jump,
99}
100
101impl Reachability {
102  fn set_jump(&mut self) {
103    *self = Reachability::Jump
104  }
105}
106
107#[derive(Clone, Debug, Eq, PartialEq)]
108struct LayerContext {
109  /// Id for this layer
110  id: u64,
111  /// Range for this layer
112  range: Avm1Range,
113  /// Indexes of discovered actions and their reachability status
114  actions: BTreeMap<Avm1Index, Reachability>,
115  /// Stack of discovered actions that were not consumed yet.
116  /// Actions on the stack are consume with `pop`.
117  new_actions: Vec<Avm1Index>,
118}
119
120impl<'a> ParseContext<'a> {
121  fn new(idg: &'a mut IdGen, range: Avm1Range) -> Self {
122    let id: u64 = idg.next();
123    let mut layer = LayerContext {
124      id,
125      range,
126      actions: BTreeMap::new(),
127      new_actions: Vec::new(),
128    };
129    layer.actions.insert(layer.range.start, Reachability::Jump);
130    layer.new_actions.push(layer.range.start);
131    Self {
132      idg,
133      layers: vec![layer],
134    }
135  }
136
137  fn push_layer(&mut self, range: Avm1Range) {
138    let id: u64 = self.idg.next();
139    let mut layer = LayerContext {
140      id,
141      range,
142      actions: BTreeMap::new(),
143      new_actions: Vec::new(),
144    };
145    layer.actions.insert(layer.range.start, Reachability::Jump);
146    layer.new_actions.push(layer.range.start);
147    self.layers.push(layer);
148  }
149
150  fn pop_layer(&mut self) {
151    let top_layer = self.layers.pop();
152    debug_assert!(top_layer.is_some());
153  }
154
155  /// Adds a path reaching `index` through linear flow
156  fn linear(&mut self, index: Avm1Index) {
157    let top_layer = self.layers.last_mut().unwrap();
158    let actions = &mut top_layer.actions;
159    let new_actions = &mut top_layer.new_actions;
160    actions
161      .entry(index)
162      .and_modify(Reachability::set_jump)
163      .or_insert_with(|| {
164        new_actions.push(index);
165        Reachability::Linear
166      });
167  }
168
169  /// Marks `index` as a jump target and returns the target label
170  fn jump(&mut self, index: Avm1Index) -> Option<CfgLabel> {
171    let mut is_top = true;
172    for layer in self.layers.iter_mut().rev() {
173      if layer.range.contains(&index) || (!is_top && index == layer.range.start) {
174        let actions = &mut layer.actions;
175        let new_actions = &mut layer.new_actions;
176
177        actions
178          .entry(index)
179          .and_modify(Reachability::set_jump)
180          .or_insert_with(|| {
181            new_actions.push(index);
182            Reachability::Jump
183          });
184        return Some(CfgLabel(format!("l{}_{}", layer.id, index)));
185      };
186      is_top = false;
187    }
188    None
189  }
190
191  fn pop_action(&mut self) -> Option<Avm1Index> {
192    let top_layer = self.layers.last_mut().unwrap();
193    top_layer.new_actions.pop()
194  }
195
196  fn iter_labels(&self) -> impl Iterator<Item = Avm1Index> + '_ {
197    let top_layer = self.layers.last().unwrap();
198    top_layer.actions.iter().filter_map(|(i, r)| match r {
199      Reachability::Jump => Some(*i),
200      _ => None,
201    })
202  }
203
204  fn top_layer(&self) -> &LayerContext {
205    self.layers.last().unwrap()
206  }
207
208  fn get_target_label(&self, target: Avm1Index) -> Option<CfgLabel> {
209    for layer in self.layers.iter().rev() {
210      if layer.range.contains(&target) {
211        return Some(CfgLabel(format!("l{}_{}", layer.id, target)));
212      }
213    }
214    None
215  }
216}
217
218#[derive(Debug, Eq, PartialEq)]
219enum Parsed {
220  Action(usize, cfg::Action),
221  Flow(CfgFlow),
222}
223
224fn parse_into_cfg(parser: &Avm1Parser, traversal: &mut ParseContext) -> Cfg {
225  let mut parsed: HashMap<usize, Parsed> = HashMap::new();
226
227  while let Some(cur_offset) = traversal.pop_action() {
228    if !traversal.top_layer().range.contains(&cur_offset) {
229      let jump = cfg::Simple {
230        next: traversal.jump(cur_offset),
231      };
232      parsed.insert(cur_offset, Parsed::Flow(CfgFlow::Simple(jump)));
233      continue;
234    }
235
236    let (end_offset, raw) = parser.get(cur_offset);
237
238    let cur_parsed: Parsed = match raw {
239      raw::Action::Add => {
240        traversal.linear(end_offset);
241        Parsed::Action(end_offset, cfg::Action::Add)
242      }
243      raw::Action::Add2 => {
244        traversal.linear(end_offset);
245        Parsed::Action(end_offset, cfg::Action::Add2)
246      }
247      raw::Action::And => {
248        traversal.linear(end_offset);
249        Parsed::Action(end_offset, cfg::Action::And)
250      }
251      raw::Action::AsciiToChar => {
252        traversal.linear(end_offset);
253        Parsed::Action(end_offset, cfg::Action::AsciiToChar)
254      }
255      raw::Action::BitAnd => {
256        traversal.linear(end_offset);
257        Parsed::Action(end_offset, cfg::Action::BitAnd)
258      }
259      raw::Action::BitOr => {
260        traversal.linear(end_offset);
261        Parsed::Action(end_offset, cfg::Action::BitOr)
262      }
263      raw::Action::BitLShift => {
264        traversal.linear(end_offset);
265        Parsed::Action(end_offset, cfg::Action::BitLShift)
266      }
267      raw::Action::BitRShift => {
268        traversal.linear(end_offset);
269        Parsed::Action(end_offset, cfg::Action::BitRShift)
270      }
271      raw::Action::BitURShift => {
272        traversal.linear(end_offset);
273        Parsed::Action(end_offset, cfg::Action::BitURShift)
274      }
275      raw::Action::BitXor => {
276        traversal.linear(end_offset);
277        Parsed::Action(end_offset, cfg::Action::BitXor)
278      }
279      raw::Action::Call => {
280        traversal.linear(end_offset);
281        Parsed::Action(end_offset, cfg::Action::Call)
282      }
283      raw::Action::CallFunction => {
284        traversal.linear(end_offset);
285        Parsed::Action(end_offset, cfg::Action::CallFunction)
286      }
287      raw::Action::CallMethod => {
288        traversal.linear(end_offset);
289        Parsed::Action(end_offset, cfg::Action::CallMethod)
290      }
291      raw::Action::CharToAscii => {
292        traversal.linear(end_offset);
293        Parsed::Action(end_offset, cfg::Action::CharToAscii)
294      }
295      raw::Action::CastOp => {
296        traversal.linear(end_offset);
297        Parsed::Action(end_offset, cfg::Action::CastOp)
298      }
299      raw::Action::CloneSprite => {
300        traversal.linear(end_offset);
301        Parsed::Action(end_offset, cfg::Action::CloneSprite)
302      }
303      raw::Action::ConstantPool(action) => {
304        traversal.linear(end_offset);
305        Parsed::Action(end_offset, cfg::Action::ConstantPool(action))
306      }
307      raw::Action::Decrement => {
308        traversal.linear(end_offset);
309        Parsed::Action(end_offset, cfg::Action::Decrement)
310      }
311      raw::Action::DefineFunction(action) => {
312        let fn_range: Avm1Range = end_offset..(end_offset + usize::from(action.body_size));
313        let mut fn_child_traversal = ParseContext::new(traversal.idg, fn_range.clone());
314        let cfg: Cfg = parse_into_cfg(parser, &mut fn_child_traversal);
315        traversal.linear(fn_range.end);
316        Parsed::Action(
317          fn_range.end,
318          cfg::Action::DefineFunction(Box::new(cfg::DefineFunction {
319            name: action.name,
320            parameters: action.parameters,
321            body: cfg,
322          })),
323        )
324      }
325      raw::Action::DefineFunction2(action) => {
326        let fn_range: Avm1Range = end_offset..(end_offset + usize::from(action.body_size));
327        let mut fn_child_traversal = ParseContext::new(traversal.idg, fn_range.clone());
328        let cfg: Cfg = parse_into_cfg(parser, &mut fn_child_traversal);
329        traversal.linear(fn_range.end);
330        Parsed::Action(
331          fn_range.end,
332          cfg::Action::DefineFunction2(Box::new(cfg::DefineFunction2 {
333            name: action.name,
334            register_count: action.register_count,
335            flags: action.flags,
336            parameters: action.parameters,
337            body: cfg,
338          })),
339        )
340      }
341      raw::Action::DefineLocal => {
342        traversal.linear(end_offset);
343        Parsed::Action(end_offset, cfg::Action::DefineLocal)
344      }
345      raw::Action::DefineLocal2 => {
346        traversal.linear(end_offset);
347        Parsed::Action(end_offset, cfg::Action::DefineLocal2)
348      }
349      raw::Action::Delete => {
350        traversal.linear(end_offset);
351        Parsed::Action(end_offset, cfg::Action::Delete)
352      }
353      raw::Action::Delete2 => {
354        traversal.linear(end_offset);
355        Parsed::Action(end_offset, cfg::Action::Delete2)
356      }
357      raw::Action::Divide => {
358        traversal.linear(end_offset);
359        Parsed::Action(end_offset, cfg::Action::Divide)
360      }
361      raw::Action::End => Parsed::Flow(CfgFlow::Simple(cfg::Simple { next: None })),
362      raw::Action::EndDrag => {
363        traversal.linear(end_offset);
364        Parsed::Action(end_offset, cfg::Action::EndDrag)
365      }
366      raw::Action::Enumerate => {
367        traversal.linear(end_offset);
368        Parsed::Action(end_offset, cfg::Action::Enumerate)
369      }
370      raw::Action::Enumerate2 => {
371        traversal.linear(end_offset);
372        Parsed::Action(end_offset, cfg::Action::Enumerate2)
373      }
374      raw::Action::Equals => {
375        traversal.linear(end_offset);
376        Parsed::Action(end_offset, cfg::Action::Equals)
377      }
378      raw::Action::Equals2 => {
379        traversal.linear(end_offset);
380        Parsed::Action(end_offset, cfg::Action::Equals2)
381      }
382      raw::Action::Error(action) => Parsed::Flow(CfgFlow::Error(cfg::Error { error: action.error })),
383      raw::Action::Extends => {
384        traversal.linear(end_offset);
385        Parsed::Action(end_offset, cfg::Action::Extends)
386      }
387      raw::Action::FsCommand2 => {
388        traversal.linear(end_offset);
389        Parsed::Action(end_offset, cfg::Action::FsCommand2)
390      }
391      raw::Action::GetMember => {
392        traversal.linear(end_offset);
393        Parsed::Action(end_offset, cfg::Action::GetMember)
394      }
395      raw::Action::GetProperty => {
396        traversal.linear(end_offset);
397        Parsed::Action(end_offset, cfg::Action::GetProperty)
398      }
399      raw::Action::GetTime => {
400        traversal.linear(end_offset);
401        Parsed::Action(end_offset, cfg::Action::GetTime)
402      }
403      raw::Action::GetUrl(action) => {
404        traversal.linear(end_offset);
405        Parsed::Action(end_offset, cfg::Action::GetUrl(action))
406      }
407      raw::Action::GetUrl2(action) => {
408        traversal.linear(end_offset);
409        Parsed::Action(end_offset, cfg::Action::GetUrl2(action))
410      }
411      raw::Action::GetVariable => {
412        traversal.linear(end_offset);
413        Parsed::Action(end_offset, cfg::Action::GetVariable)
414      }
415      raw::Action::GotoFrame(action) => {
416        traversal.linear(end_offset);
417        Parsed::Action(end_offset, cfg::Action::GotoFrame(action))
418      }
419      raw::Action::GotoFrame2(action) => {
420        traversal.linear(end_offset);
421        Parsed::Action(end_offset, cfg::Action::GotoFrame2(action))
422      }
423      raw::Action::GotoLabel(action) => {
424        traversal.linear(end_offset);
425        Parsed::Action(end_offset, cfg::Action::GotoLabel(action))
426      }
427      raw::Action::Greater => {
428        traversal.linear(end_offset);
429        Parsed::Action(end_offset, cfg::Action::Greater)
430      }
431      raw::Action::If(action) => {
432        let true_target = if let Some(jump_offset) = try_add_offset(end_offset, action.offset) {
433          traversal.jump(jump_offset)
434        } else {
435          None
436        };
437        let false_target = traversal.jump(end_offset);
438        Parsed::Flow(CfgFlow::If(cfg::If {
439          true_target,
440          false_target,
441        }))
442      }
443      raw::Action::ImplementsOp => {
444        traversal.linear(end_offset);
445        Parsed::Action(end_offset, cfg::Action::ImplementsOp)
446      }
447      raw::Action::Increment => {
448        traversal.linear(end_offset);
449        Parsed::Action(end_offset, cfg::Action::Increment)
450      }
451      raw::Action::InitArray => {
452        traversal.linear(end_offset);
453        Parsed::Action(end_offset, cfg::Action::InitArray)
454      }
455      raw::Action::InitObject => {
456        traversal.linear(end_offset);
457        Parsed::Action(end_offset, cfg::Action::InitObject)
458      }
459      raw::Action::InstanceOf => {
460        traversal.linear(end_offset);
461        Parsed::Action(end_offset, cfg::Action::InstanceOf)
462      }
463      raw::Action::Jump(action) => {
464        let next = if let Some(jump_offset) = try_add_offset(end_offset, action.offset) {
465          traversal.jump(jump_offset)
466        } else {
467          None
468        };
469        Parsed::Flow(CfgFlow::Simple(cfg::Simple { next }))
470      }
471      raw::Action::Less => {
472        traversal.linear(end_offset);
473        Parsed::Action(end_offset, cfg::Action::Less)
474      }
475      raw::Action::Less2 => {
476        traversal.linear(end_offset);
477        Parsed::Action(end_offset, cfg::Action::Less2)
478      }
479      raw::Action::MbAsciiToChar => {
480        traversal.linear(end_offset);
481        Parsed::Action(end_offset, cfg::Action::MbAsciiToChar)
482      }
483      raw::Action::MbCharToAscii => {
484        traversal.linear(end_offset);
485        Parsed::Action(end_offset, cfg::Action::MbCharToAscii)
486      }
487      raw::Action::MbStringExtract => {
488        traversal.linear(end_offset);
489        Parsed::Action(end_offset, cfg::Action::MbStringExtract)
490      }
491      raw::Action::MbStringLength => {
492        traversal.linear(end_offset);
493        Parsed::Action(end_offset, cfg::Action::MbStringLength)
494      }
495      raw::Action::Modulo => {
496        traversal.linear(end_offset);
497        Parsed::Action(end_offset, cfg::Action::Modulo)
498      }
499      raw::Action::Multiply => {
500        traversal.linear(end_offset);
501        Parsed::Action(end_offset, cfg::Action::Multiply)
502      }
503      raw::Action::NewMethod => {
504        traversal.linear(end_offset);
505        Parsed::Action(end_offset, cfg::Action::NewMethod)
506      }
507      raw::Action::NewObject => {
508        traversal.linear(end_offset);
509        Parsed::Action(end_offset, cfg::Action::NewObject)
510      }
511      raw::Action::NextFrame => {
512        traversal.linear(end_offset);
513        Parsed::Action(end_offset, cfg::Action::NextFrame)
514      }
515      raw::Action::Not => {
516        traversal.linear(end_offset);
517        Parsed::Action(end_offset, cfg::Action::Not)
518      }
519      raw::Action::Or => {
520        traversal.linear(end_offset);
521        Parsed::Action(end_offset, cfg::Action::Or)
522      }
523      raw::Action::Play => {
524        traversal.linear(end_offset);
525        Parsed::Action(end_offset, cfg::Action::Play)
526      }
527      raw::Action::Pop => {
528        traversal.linear(end_offset);
529        Parsed::Action(end_offset, cfg::Action::Pop)
530      }
531      raw::Action::PrevFrame => {
532        traversal.linear(end_offset);
533        Parsed::Action(end_offset, cfg::Action::PrevFrame)
534      }
535      raw::Action::Push(action) => {
536        traversal.linear(end_offset);
537        Parsed::Action(end_offset, cfg::Action::Push(action))
538      }
539      raw::Action::PushDuplicate => {
540        traversal.linear(end_offset);
541        Parsed::Action(end_offset, cfg::Action::PushDuplicate)
542      }
543      raw::Action::RandomNumber => {
544        traversal.linear(end_offset);
545        Parsed::Action(end_offset, cfg::Action::RandomNumber)
546      }
547      raw::Action::RemoveSprite => {
548        traversal.linear(end_offset);
549        Parsed::Action(end_offset, cfg::Action::RemoveSprite)
550      }
551      raw::Action::Return => Parsed::Flow(CfgFlow::Return),
552      raw::Action::SetMember => {
553        traversal.linear(end_offset);
554        Parsed::Action(end_offset, cfg::Action::SetMember)
555      }
556      raw::Action::SetProperty => {
557        traversal.linear(end_offset);
558        Parsed::Action(end_offset, cfg::Action::SetProperty)
559      }
560      raw::Action::SetTarget(action) => {
561        traversal.linear(end_offset);
562        Parsed::Action(end_offset, cfg::Action::SetTarget(action))
563      }
564      raw::Action::SetTarget2 => {
565        traversal.linear(end_offset);
566        Parsed::Action(end_offset, cfg::Action::SetTarget2)
567      }
568      raw::Action::SetVariable => {
569        traversal.linear(end_offset);
570        Parsed::Action(end_offset, cfg::Action::SetVariable)
571      }
572      raw::Action::StackSwap => {
573        traversal.linear(end_offset);
574        Parsed::Action(end_offset, cfg::Action::StackSwap)
575      }
576      raw::Action::StartDrag => {
577        traversal.linear(end_offset);
578        Parsed::Action(end_offset, cfg::Action::StartDrag)
579      }
580      raw::Action::Stop => {
581        traversal.linear(end_offset);
582        Parsed::Action(end_offset, cfg::Action::Stop)
583      }
584      raw::Action::StopSounds => {
585        traversal.linear(end_offset);
586        Parsed::Action(end_offset, cfg::Action::StopSounds)
587      }
588      raw::Action::StoreRegister(action) => {
589        traversal.linear(end_offset);
590        Parsed::Action(end_offset, cfg::Action::StoreRegister(action))
591      }
592      raw::Action::StrictEquals => {
593        traversal.linear(end_offset);
594        Parsed::Action(end_offset, cfg::Action::StrictEquals)
595      }
596      raw::Action::StrictMode(action) => {
597        traversal.linear(end_offset);
598        Parsed::Action(end_offset, cfg::Action::StrictMode(action))
599      }
600      raw::Action::StringAdd => {
601        traversal.linear(end_offset);
602        Parsed::Action(end_offset, cfg::Action::StringAdd)
603      }
604      raw::Action::StringEquals => {
605        traversal.linear(end_offset);
606        Parsed::Action(end_offset, cfg::Action::StringEquals)
607      }
608      raw::Action::StringExtract => {
609        traversal.linear(end_offset);
610        Parsed::Action(end_offset, cfg::Action::StringExtract)
611      }
612      raw::Action::StringGreater => {
613        traversal.linear(end_offset);
614        Parsed::Action(end_offset, cfg::Action::StringGreater)
615      }
616      raw::Action::StringLength => {
617        traversal.linear(end_offset);
618        Parsed::Action(end_offset, cfg::Action::StringLength)
619      }
620      raw::Action::StringLess => {
621        traversal.linear(end_offset);
622        Parsed::Action(end_offset, cfg::Action::StringLess)
623      }
624      raw::Action::Subtract => {
625        traversal.linear(end_offset);
626        Parsed::Action(end_offset, cfg::Action::Subtract)
627      }
628      raw::Action::TargetPath => {
629        traversal.linear(end_offset);
630        Parsed::Action(end_offset, cfg::Action::TargetPath)
631      }
632      raw::Action::Throw => Parsed::Flow(CfgFlow::Throw),
633      raw::Action::ToInteger => {
634        traversal.linear(end_offset);
635        Parsed::Action(end_offset, cfg::Action::ToInteger)
636      }
637      raw::Action::ToNumber => {
638        traversal.linear(end_offset);
639        Parsed::Action(end_offset, cfg::Action::ToNumber)
640      }
641      raw::Action::ToString => {
642        traversal.linear(end_offset);
643        Parsed::Action(end_offset, cfg::Action::ToString)
644      }
645      raw::Action::ToggleQuality => {
646        traversal.linear(end_offset);
647        Parsed::Action(end_offset, cfg::Action::ToggleQuality)
648      }
649      raw::Action::Trace => {
650        traversal.linear(end_offset);
651        Parsed::Action(end_offset, cfg::Action::Trace)
652      }
653      raw::Action::Try(action) => {
654        let try_start: Avm1Index = end_offset;
655        let catch_start: Avm1Index = try_start + usize::from(action.r#try);
656        let finally_start: Avm1Index = catch_start + action.catch.as_ref().map_or(0, |c| usize::from(c.size));
657
658        let finally: Option<Cfg> = if let Some(finally_size) = action.finally {
659          traversal.push_layer(finally_start..(finally_start + usize::from(finally_size)));
660          Some(parse_into_cfg(parser, traversal))
661        } else {
662          None
663        };
664
665        let r#try = {
666          traversal.push_layer(try_start..(try_start + usize::from(action.r#try)));
667          let r#try: Cfg = parse_into_cfg(parser, traversal);
668          traversal.pop_layer();
669          r#try
670        };
671
672        let catch = action.catch.map(|raw_catch| {
673          traversal.push_layer(catch_start..(catch_start + usize::from(raw_catch.size)));
674          let body: Cfg = parse_into_cfg(parser, traversal);
675          traversal.pop_layer();
676          cfg::CatchBlock {
677            target: raw_catch.target,
678            body,
679          }
680        });
681
682        if finally.is_some() {
683          traversal.pop_layer();
684        }
685
686        Parsed::Flow(CfgFlow::Try(Box::new(cfg::Try { r#try, catch, finally })))
687      }
688      raw::Action::TypeOf => {
689        traversal.linear(end_offset);
690        Parsed::Action(end_offset, cfg::Action::TypeOf)
691      }
692      raw::Action::Raw(action) => {
693        traversal.linear(end_offset);
694        Parsed::Action(end_offset, cfg::Action::Raw(action))
695      }
696      raw::Action::WaitForFrame(action) => {
697        let loading_offset = parser.skip(end_offset, usize::from(action.skip));
698        let loading_target = traversal.jump(loading_offset);
699        let ready_target = traversal.jump(end_offset);
700        let wff = cfg::WaitForFrame {
701          frame: action.frame,
702          loading_target,
703          ready_target,
704        };
705        Parsed::Flow(CfgFlow::WaitForFrame(wff))
706      }
707      raw::Action::WaitForFrame2(action) => {
708        let loading_offset = parser.skip(end_offset, usize::from(action.skip));
709        let loading_target = traversal.jump(loading_offset);
710        let ready_target = traversal.jump(end_offset);
711        let wff = cfg::WaitForFrame2 {
712          ready_target,
713          loading_target,
714        };
715        Parsed::Flow(CfgFlow::WaitForFrame2(wff))
716      }
717      raw::Action::With(action) => {
718        let range: Avm1Range = end_offset..(end_offset + usize::from(action.size));
719        traversal.push_layer(range);
720        let body: Cfg = parse_into_cfg(parser, traversal);
721        traversal.pop_layer();
722        Parsed::Flow(CfgFlow::With(cfg::With { body }))
723      }
724    };
725
726    {
727      let old: Option<Parsed> = parsed.insert(cur_offset, cur_parsed);
728      debug_assert!(old.is_none());
729    }
730  }
731
732  let mut blocks: Vec<CfgBlock> = Vec::new();
733
734  for start_index in traversal.iter_labels() {
735    let label: CfgLabel = CfgLabel(format!("l{}_{}", traversal.top_layer().id, start_index));
736    let mut builder: CfgBlockBuilder = CfgBlockBuilder::new(label);
737    let mut index: Avm1Index = start_index;
738    let block: CfgBlock = loop {
739      let action = parsed
740        .remove(&index)
741        .expect("`parsed` to have actions found during traversal");
742      match action {
743        Parsed::Action(next, action) => {
744          builder.action(action);
745          index = next
746        }
747        Parsed::Flow(flow) => break builder.flow(flow),
748      };
749      if traversal.top_layer().actions.get(&index) == Some(&Reachability::Jump) {
750        let jump = cfg::Simple {
751          next: traversal.get_target_label(index),
752        };
753        break builder.flow(CfgFlow::Simple(jump));
754      }
755    };
756    blocks.push(block);
757  }
758
759  let blocks: Vec1<CfgBlock> = Vec1::try_from_vec(blocks).unwrap();
760  Cfg { blocks }
761}
762
763struct CfgBlockBuilder {
764  label: CfgLabel,
765  actions: Vec<cfg::Action>,
766}
767
768impl CfgBlockBuilder {
769  fn new(label: CfgLabel) -> Self {
770    Self {
771      label,
772      actions: Vec::new(),
773    }
774  }
775
776  fn action(&mut self, action: cfg::Action) -> &mut Self {
777    self.actions.push(action);
778    self
779  }
780
781  fn flow(self, flow: CfgFlow) -> CfgBlock {
782    CfgBlock {
783      label: self.label,
784      actions: self.actions,
785      flow,
786    }
787  }
788}