swf: Improve `ActionTry` read/write

* Use `TryFlags` instead of hard-coded binary literals.
* Rename `try_length`, `catch_length`, `finally_length` to `try_size`,
`catch_size`, `finally_size` to match SWF19 namings.
* Refactor write and fix a few bugs there:
    * The actions length should not include the try, catch, finally
    bodies, only the metadata of `flags`, `try_size`, `catch_size`,
    `finally_size` and catch variable (either as `u8` or `SwfStr`).
    * A placeholder byte should be written in place of the catch variable
    when there is no catch clause.
This commit is contained in:
relrelb 2021-10-02 12:48:09 +03:00 committed by relrelb
parent d49ea10267
commit 281455be6d
3 changed files with 61 additions and 53 deletions

View File

@ -330,27 +330,27 @@ impl<'a> Reader<'a> {
} }
fn read_try(&mut self, length: &mut usize) -> Result<Action<'a>> { fn read_try(&mut self, length: &mut usize) -> Result<Action<'a>> {
let flags = self.read_u8()?; let flags = TryFlags::from_bits_truncate(self.read_u8()?);
let try_length: usize = (self.read_u16()?).into(); let try_size: usize = self.read_u16()?.into();
let catch_length: usize = (self.read_u16()?).into(); let catch_size: usize = self.read_u16()?.into();
let finally_length: usize = (self.read_u16()?).into(); let finally_size: usize = self.read_u16()?.into();
*length += try_length + catch_length + finally_length; *length += try_size + catch_size + finally_size;
let catch_var = if flags & 0b100 == 0 { let catch_var = if flags.contains(TryFlags::CATCH_IN_REGISTER) {
CatchVar::Var(self.read_str()?)
} else {
CatchVar::Register(self.read_u8()?) CatchVar::Register(self.read_u8()?)
} else {
CatchVar::Var(self.read_str()?)
}; };
let try_body = self.read_slice(try_length)?; let try_body = self.read_slice(try_size)?;
let catch_body = self.read_slice(catch_length)?; let catch_body = self.read_slice(catch_size)?;
let finally_body = self.read_slice(finally_length)?; let finally_body = self.read_slice(finally_size)?;
Ok(Action::Try(TryBlock { Ok(Action::Try(TryBlock {
try_body, try_body,
catch_body: if flags & 0b1 != 0 { catch_body: if flags.contains(TryFlags::CATCH_BLOCK) {
Some((catch_var, catch_body)) Some((catch_var, catch_body))
} else { } else {
None None
}, },
finally_body: if flags & 0b10 != 0 { finally_body: if flags.contains(TryFlags::FINALLY_BLOCK) {
Some(finally_body) Some(finally_body)
} else { } else {
None None

View File

@ -194,3 +194,11 @@ pub enum CatchVar<'a> {
Var(&'a SwfStr), Var(&'a SwfStr),
Register(u8), Register(u8),
} }
bitflags! {
pub struct TryFlags: u8 {
const CATCH_BLOCK = 1 << 0;
const FINALLY_BLOCK = 1 << 1;
const CATCH_IN_REGISTER = 1 << 2;
}
}

View File

@ -291,51 +291,51 @@ impl<W: Write> Writer<W> {
Action::ToString => self.write_action_header(OpCode::ToString, 0)?, Action::ToString => self.write_action_header(OpCode::ToString, 0)?,
Action::Trace => self.write_action_header(OpCode::Trace, 0)?, Action::Trace => self.write_action_header(OpCode::Trace, 0)?,
Action::Try(ref try_block) => { Action::Try(ref try_block) => {
let try_length; let len = 7 + if let Some((CatchVar::Var(name), _)) = try_block.catch_body {
let catch_length;
let finally_length;
let mut action_buf = vec![];
{
action_buf.write_all(try_block.try_body)?;
try_length = try_block.try_body.len();
catch_length = if let Some((_, catch)) = try_block.catch_body {
action_buf.write_all(catch)?;
catch.len()
} else {
0
};
finally_length = if let Some(finally) = try_block.finally_body {
action_buf.write_all(finally)?;
finally.len()
} else {
0
};
}
let len = 7
+ action_buf.len()
+ if let Some((CatchVar::Var(name), _)) = try_block.catch_body {
name.len() + 1 name.len() + 1
} else { } else {
1 1
}; };
self.write_action_header(OpCode::Try, len)?; self.write_action_header(OpCode::Try, len)?;
self.write_u8(
if let Some((CatchVar::Register(_), _)) = try_block.catch_body { let mut flags = TryFlags::empty();
0b100 flags.set(TryFlags::CATCH_BLOCK, try_block.catch_body.is_some());
} else { flags.set(TryFlags::FINALLY_BLOCK, try_block.finally_body.is_some());
0 flags.set(
} | if try_block.finally_body.is_some() { 0b10 } else { 0 } TryFlags::CATCH_IN_REGISTER,
| if try_block.catch_body.is_some() { 0b1 } else { 0 }, matches!(try_block.catch_body, Some((CatchVar::Register(_), _))),
)?; );
self.write_u16(try_length as u16)?; self.write_u8(flags.bits())?;
self.write_u16(catch_length as u16)?;
self.write_u16(finally_length as u16)?; let try_size = try_block.try_body.len();
self.write_u16(try_size as u16)?;
let catch_size = try_block
.catch_body
.as_ref()
.map_or(0, |(_, catch_body)| catch_body.len());
self.write_u16(catch_size as u16)?;
let finally_size = try_block
.finally_body
.map_or(0, |finally_body| finally_body.len());
self.write_u16(finally_size as u16)?;
match try_block.catch_body { match try_block.catch_body {
Some((CatchVar::Var(name), _)) => self.write_string(name)?, Some((CatchVar::Var(name), _)) => self.write_string(name)?,
Some((CatchVar::Register(i), _)) => self.write_u8(i)?, Some((CatchVar::Register(i), _)) => self.write_u8(i)?,
_ => (), None => self.write_u8(0)?,
}
self.output.write_all(try_block.try_body)?;
if let Some((_, catch_body)) = try_block.catch_body {
self.output.write_all(catch_body)?;
}
if let Some(finally_body) = try_block.finally_body {
self.output.write_all(finally_body)?;
} }
self.output.write_all(&action_buf)?;
} }
Action::TypeOf => self.write_action_header(OpCode::TypeOf, 0)?, Action::TypeOf => self.write_action_header(OpCode::TypeOf, 0)?,
Action::WaitForFrame { Action::WaitForFrame {