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#[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#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
90enum Reachability {
91 Linear,
94 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: u64,
111 range: Avm1Range,
113 actions: BTreeMap<Avm1Index, Reachability>,
115 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 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 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}