avm2: Add an `Op` enum to AVM2; pre-pool double/uint/ints for `pushX` opcodes

This commit is contained in:
Lord-McSweeney 2024-01-05 13:51:33 -08:00 committed by Lord-McSweeney
parent 65848104a6
commit 2022321a03
5 changed files with 705 additions and 135 deletions

View File

@ -50,6 +50,7 @@ mod method;
mod multiname; mod multiname;
mod namespace; mod namespace;
pub mod object; pub mod object;
mod op;
mod parameters; mod parameters;
pub mod property; pub mod property;
mod property_map; mod property_map;

View File

@ -14,13 +14,14 @@ use crate::avm2::object::{
XmlListObject, XmlListObject,
}; };
use crate::avm2::object::{Object, TObject}; use crate::avm2::object::{Object, TObject};
use crate::avm2::op::Op;
use crate::avm2::scope::{search_scope_stack, Scope, ScopeChain}; use crate::avm2::scope::{search_scope_stack, Scope, ScopeChain};
use crate::avm2::script::Script; use crate::avm2::script::Script;
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::Multiname; use crate::avm2::Multiname;
use crate::avm2::Namespace; use crate::avm2::Namespace;
use crate::avm2::QName; use crate::avm2::QName;
use crate::avm2::{value, Avm2, Error}; use crate::avm2::{Avm2, Error};
use crate::context::{GcContext, UpdateContext}; use crate::context::{GcContext, UpdateContext};
use crate::string::{AvmAtom, AvmString}; use crate::string::{AvmAtom, AvmString};
use crate::tag_utils::SwfMovie; use crate::tag_utils::SwfMovie;
@ -31,7 +32,7 @@ use std::cmp::{min, Ordering};
use std::sync::Arc; use std::sync::Arc;
use swf::avm2::types::{ use swf::avm2::types::{
Class as AbcClass, Exception, Index, Method as AbcMethod, MethodFlags as AbcMethodFlags, Class as AbcClass, Exception, Index, Method as AbcMethod, MethodFlags as AbcMethodFlags,
Multiname as AbcMultiname, Namespace as AbcNamespace, Op, Multiname as AbcMultiname, Namespace as AbcNamespace,
}; };
use super::error::make_mismatch_error; use super::error::make_mismatch_error;
@ -710,33 +711,6 @@ impl<'a, 'gc> Activation<'a, 'gc> {
}) })
} }
/// Retrieve a int from the current constant pool.
fn pool_int(
&self,
method: Gc<'gc, BytecodeMethod<'gc>>,
index: Index<i32>,
) -> Result<i32, Error<'gc>> {
value::abc_int(method.translation_unit(), index)
}
/// Retrieve a int from the current constant pool.
fn pool_uint(
&self,
method: Gc<'gc, BytecodeMethod<'gc>>,
index: Index<u32>,
) -> Result<u32, Error<'gc>> {
value::abc_uint(method.translation_unit(), index)
}
/// Retrieve a double from the current constant pool.
fn pool_double(
&self,
method: Gc<'gc, BytecodeMethod<'gc>>,
index: Index<f64>,
) -> Result<f64, Error<'gc>> {
value::abc_double(method.translation_unit(), index)
}
/// Retrieve a string from the current constant pool. /// Retrieve a string from the current constant pool.
fn pool_string<'b>( fn pool_string<'b>(
&mut self, &mut self,
@ -939,16 +913,16 @@ impl<'a, 'gc> Activation<'a, 'gc> {
{ {
let result = match op { let result = match op {
Op::PushByte { value } => self.op_push_byte(*value), Op::PushByte { value } => self.op_push_byte(*value),
Op::PushDouble { value } => self.op_push_double(method, *value), Op::PushDouble { value } => self.op_push_double(*value),
Op::PushFalse => self.op_push_false(), Op::PushFalse => self.op_push_false(),
Op::PushInt { value } => self.op_push_int(method, *value), Op::PushInt { value } => self.op_push_int(*value),
Op::PushNamespace { value } => self.op_push_namespace(method, *value), Op::PushNamespace { value } => self.op_push_namespace(method, *value),
Op::PushNaN => self.op_push_nan(), Op::PushNaN => self.op_push_nan(),
Op::PushNull => self.op_push_null(), Op::PushNull => self.op_push_null(),
Op::PushShort { value } => self.op_push_short(*value), Op::PushShort { value } => self.op_push_short(*value),
Op::PushString { value } => self.op_push_string(method, *value), Op::PushString { value } => self.op_push_string(method, *value),
Op::PushTrue => self.op_push_true(), Op::PushTrue => self.op_push_true(),
Op::PushUint { value } => self.op_push_uint(method, *value), Op::PushUint { value } => self.op_push_uint(*value),
Op::PushUndefined => self.op_push_undefined(), Op::PushUndefined => self.op_push_undefined(),
Op::Pop => self.op_pop(), Op::Pop => self.op_pop(),
Op::Dup => self.op_dup(), Op::Dup => self.op_dup(),
@ -1116,7 +1090,6 @@ impl<'a, 'gc> Activation<'a, 'gc> {
Op::Sxi8 => self.op_sxi8(), Op::Sxi8 => self.op_sxi8(),
Op::Sxi16 => self.op_sxi16(), Op::Sxi16 => self.op_sxi16(),
Op::Throw => self.op_throw(), Op::Throw => self.op_throw(),
_ => self.unknown_op(op),
}; };
if let Err(error) = result { if let Err(error) = result {
@ -1126,11 +1099,6 @@ impl<'a, 'gc> Activation<'a, 'gc> {
} }
} }
fn unknown_op(&mut self, op: &swf::avm2::types::Op) -> Result<FrameControl<'gc>, Error<'gc>> {
tracing::error!("Unknown AVM2 opcode: {:?}", op);
Err("Unknown op".into())
}
fn op_push_byte(&mut self, value: u8) -> Result<FrameControl<'gc>, Error<'gc>> { fn op_push_byte(&mut self, value: u8) -> Result<FrameControl<'gc>, Error<'gc>> {
//TODO: Adobe Animate CC appears to generate signed byte values, and //TODO: Adobe Animate CC appears to generate signed byte values, and
//JPEXS appears to take them. //JPEXS appears to take them.
@ -1138,12 +1106,8 @@ impl<'a, 'gc> Activation<'a, 'gc> {
Ok(FrameControl::Continue) Ok(FrameControl::Continue)
} }
fn op_push_double( fn op_push_double(&mut self, value: f64) -> Result<FrameControl<'gc>, Error<'gc>> {
&mut self, self.push_stack(value);
method: Gc<'gc, BytecodeMethod<'gc>>,
value: Index<f64>,
) -> Result<FrameControl<'gc>, Error<'gc>> {
self.push_stack(self.pool_double(method, value)?);
Ok(FrameControl::Continue) Ok(FrameControl::Continue)
} }
@ -1152,12 +1116,8 @@ impl<'a, 'gc> Activation<'a, 'gc> {
Ok(FrameControl::Continue) Ok(FrameControl::Continue)
} }
fn op_push_int( fn op_push_int(&mut self, value: i32) -> Result<FrameControl<'gc>, Error<'gc>> {
&mut self, self.push_stack(value);
method: Gc<'gc, BytecodeMethod<'gc>>,
value: Index<i32>,
) -> Result<FrameControl<'gc>, Error<'gc>> {
self.push_stack(self.pool_int(method, value)?);
Ok(FrameControl::Continue) Ok(FrameControl::Continue)
} }
@ -1203,12 +1163,8 @@ impl<'a, 'gc> Activation<'a, 'gc> {
Ok(FrameControl::Continue) Ok(FrameControl::Continue)
} }
fn op_push_uint( fn op_push_uint(&mut self, value: u32) -> Result<FrameControl<'gc>, Error<'gc>> {
&mut self, self.push_stack(value);
method: Gc<'gc, BytecodeMethod<'gc>>,
value: Index<u32>,
) -> Result<FrameControl<'gc>, Error<'gc>> {
self.push_stack(self.pool_uint(method, value)?);
Ok(FrameControl::Continue) Ok(FrameControl::Continue)
} }

318
core/src/avm2/op.rs Normal file
View File

@ -0,0 +1,318 @@
use swf::avm2::types::{Class, Exception, Index, LookupSwitch, Method, Multiname, Namespace};
#[derive(Clone, Debug, PartialEq)]
pub enum Op {
Add,
AddI,
ApplyType {
num_types: u32,
},
AsType {
type_name: Index<Multiname>,
},
AsTypeLate,
BitAnd,
BitNot,
BitOr,
BitXor,
Bkpt,
BkptLine {
line_num: u32,
},
Call {
num_args: u32,
},
CallMethod {
index: Index<Method>,
num_args: u32,
},
CallProperty {
index: Index<Multiname>,
num_args: u32,
},
CallPropLex {
index: Index<Multiname>,
num_args: u32,
},
CallPropVoid {
index: Index<Multiname>,
num_args: u32,
},
CallStatic {
index: Index<Method>,
num_args: u32,
},
CallSuper {
index: Index<Multiname>,
num_args: u32,
},
CallSuperVoid {
index: Index<Multiname>,
num_args: u32,
},
CheckFilter,
Coerce {
index: Index<Multiname>,
},
CoerceA,
CoerceB,
CoerceD,
CoerceI,
CoerceO,
CoerceS,
CoerceU,
Construct {
num_args: u32,
},
ConstructProp {
index: Index<Multiname>,
num_args: u32,
},
ConstructSuper {
num_args: u32,
},
ConvertB,
ConvertD,
ConvertI,
ConvertO,
ConvertS,
ConvertU,
Debug {
is_local_register: bool,
register_name: Index<String>,
register: u8,
},
DebugFile {
file_name: Index<String>,
},
DebugLine {
line_num: u32,
},
DecLocal {
index: u32,
},
DecLocalI {
index: u32,
},
Decrement,
DecrementI,
DeleteProperty {
index: Index<Multiname>,
},
Divide,
Dup,
Equals,
EscXAttr,
EscXElem,
FindDef {
index: Index<Multiname>,
},
FindProperty {
index: Index<Multiname>,
},
FindPropStrict {
index: Index<Multiname>,
},
GetDescendants {
index: Index<Multiname>,
},
GetGlobalScope,
GetGlobalSlot {
index: u32,
},
GetLex {
index: Index<Multiname>,
},
GetLocal {
index: u32,
},
GetOuterScope {
index: u32,
},
GetProperty {
index: Index<Multiname>,
},
GetScopeObject {
index: u8,
},
GetSlot {
index: u32,
},
GetSuper {
index: Index<Multiname>,
},
GreaterEquals,
GreaterThan,
HasNext,
HasNext2 {
object_register: u32,
index_register: u32,
},
IfEq {
offset: i32,
},
IfFalse {
offset: i32,
},
IfGe {
offset: i32,
},
IfGt {
offset: i32,
},
IfLe {
offset: i32,
},
IfLt {
offset: i32,
},
IfNe {
offset: i32,
},
IfNge {
offset: i32,
},
IfNgt {
offset: i32,
},
IfNle {
offset: i32,
},
IfNlt {
offset: i32,
},
IfStrictEq {
offset: i32,
},
IfStrictNe {
offset: i32,
},
IfTrue {
offset: i32,
},
In,
IncLocal {
index: u32,
},
IncLocalI {
index: u32,
},
Increment,
IncrementI,
InitProperty {
index: Index<Multiname>,
},
InstanceOf,
IsType {
index: Index<Multiname>,
},
IsTypeLate,
Jump {
offset: i32,
},
Kill {
index: u32,
},
Label,
LessEquals,
LessThan,
Lf32,
Lf64,
Li16,
Li32,
Li8,
LookupSwitch(Box<LookupSwitch>),
LShift,
Modulo,
Multiply,
MultiplyI,
Negate,
NegateI,
NewActivation,
NewArray {
num_args: u32,
},
NewCatch {
index: Index<Exception>,
},
NewClass {
index: Index<Class>,
},
NewFunction {
index: Index<Method>,
},
NewObject {
num_args: u32,
},
NextName,
NextValue,
Nop,
Not,
Pop,
PopScope,
PushByte {
value: u8,
},
PushDouble {
value: f64,
},
PushFalse,
PushInt {
value: i32,
},
PushNamespace {
value: Index<Namespace>,
},
PushNaN,
PushNull,
PushScope,
PushShort {
value: i16,
},
PushString {
value: Index<String>,
},
PushTrue,
PushUint {
value: u32,
},
PushUndefined,
PushWith,
ReturnValue,
ReturnVoid,
RShift,
SetGlobalSlot {
index: u32,
},
SetLocal {
index: u32,
},
SetProperty {
index: Index<Multiname>,
},
SetSlot {
index: u32,
},
SetSuper {
index: Index<Multiname>,
},
Sf32,
Sf64,
Si16,
Si32,
Si8,
StrictEquals,
Subtract,
SubtractI,
Swap,
Sxi1,
Sxi16,
Sxi8,
Throw,
TypeOf,
Timestamp,
URShift,
}
#[cfg(target_pointer_width = "64")]
const _: () = assert!(std::mem::size_of::<Op>() == 16);

View File

@ -1,9 +1,11 @@
use crate::avm2::error::{make_error_1025, make_error_1054, make_error_1107, verify_error}; use crate::avm2::error::{make_error_1025, make_error_1054, make_error_1107, verify_error};
use crate::avm2::method::BytecodeMethod; use crate::avm2::method::BytecodeMethod;
use crate::avm2::op::Op;
use crate::avm2::script::TranslationUnit;
use crate::avm2::{Activation, Error}; use crate::avm2::{Activation, Error};
use std::collections::HashMap; use std::collections::HashMap;
use swf::avm2::read::Reader; use swf::avm2::read::Reader;
use swf::avm2::types::{Index, MethodFlags as AbcMethodFlags, Multiname, Op}; use swf::avm2::types::{Index, MethodFlags as AbcMethodFlags, Multiname, Op as AbcOp};
use swf::error::Error as AbcReadError; use swf::error::Error as AbcReadError;
pub struct VerifiedMethodInfo { pub struct VerifiedMethodInfo {
@ -27,6 +29,7 @@ pub fn verify_method<'gc>(
let body = method let body = method
.body() .body()
.expect("Cannot verify non-native method without body!"); .expect("Cannot verify non-native method without body!");
let translation_unit = method.translation_unit();
let param_count = method.method().params.len(); let param_count = method.method().params.len();
let locals_count = body.num_locals; let locals_count = body.num_locals;
@ -53,8 +56,9 @@ pub fn verify_method<'gc>(
)?)); )?));
} }
// FIXME: This is wrong, verification should happen at the same time as reading. // FIXME: This is wrong, verification/control flow handling should happen at the same
// A side effect of this is that avmplus allows for holes in bytecode. // time as reading. A side effect of this is that avmplus allows for holes in bytecode,
// while this implementation throws errors #1011 or #1021 in those cases.
let mut reader = Reader::new(&body.code); let mut reader = Reader::new(&body.code);
loop { loop {
let op = match reader.read_op() { let op = match reader.read_op() {
@ -193,24 +197,24 @@ pub fn verify_method<'gc>(
new_idx - i - 1 new_idx - i - 1
}; };
match op { match op {
Op::IfEq { offset } AbcOp::IfEq { offset }
| Op::IfFalse { offset } | AbcOp::IfFalse { offset }
| Op::IfGe { offset } | AbcOp::IfGe { offset }
| Op::IfGt { offset } | AbcOp::IfGt { offset }
| Op::IfLe { offset } | AbcOp::IfLe { offset }
| Op::IfLt { offset } | AbcOp::IfLt { offset }
| Op::IfNe { offset } | AbcOp::IfNe { offset }
| Op::IfNge { offset } | AbcOp::IfNge { offset }
| Op::IfNgt { offset } | AbcOp::IfNgt { offset }
| Op::IfNle { offset } | AbcOp::IfNle { offset }
| Op::IfNlt { offset } | AbcOp::IfNlt { offset }
| Op::IfStrictEq { offset } | AbcOp::IfStrictEq { offset }
| Op::IfStrictNe { offset } | AbcOp::IfStrictNe { offset }
| Op::IfTrue { offset } | AbcOp::IfTrue { offset }
| Op::Jump { offset } => { | AbcOp::Jump { offset } => {
*offset = adjusted(i, *offset, true); *offset = adjusted(i, *offset, true);
} }
Op::LookupSwitch(ref mut lookup_switch) => { AbcOp::LookupSwitch(ref mut lookup_switch) => {
lookup_switch.default_offset = adjusted(i, lookup_switch.default_offset, false); lookup_switch.default_offset = adjusted(i, lookup_switch.default_offset, false);
for case in lookup_switch.case_offsets.iter_mut() { for case in lookup_switch.case_offsets.iter_mut() {
*case = adjusted(i, *case, false); *case = adjusted(i, *case, false);
@ -220,8 +224,15 @@ pub fn verify_method<'gc>(
} }
} }
let mut verified_code = Vec::new();
for abc_op in new_code {
let resolved_op = resolve_op(activation, translation_unit, abc_op)?;
verified_code.push(resolved_op);
}
Ok(VerifiedMethodInfo { Ok(VerifiedMethodInfo {
parsed_code: new_code, parsed_code: verified_code,
exceptions: new_exceptions, exceptions: new_exceptions,
}) })
} }
@ -258,7 +269,7 @@ fn verify_code_starting_from<'gc>(
idx_to_byte_offset: &[i32], idx_to_byte_offset: &[i32],
byte_offset_to_idx: &HashMap<i32, i32>, byte_offset_to_idx: &HashMap<i32, i32>,
verified_blocks: &mut Vec<i32>, verified_blocks: &mut Vec<i32>,
ops: &[Op], ops: &[AbcOp],
start_idx: i32, start_idx: i32,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
if verified_blocks.iter().any(|o| *o == start_idx) { if verified_blocks.iter().any(|o| *o == start_idx) {
@ -279,21 +290,21 @@ fn verify_code_starting_from<'gc>(
// Special control flow ops // Special control flow ops
match op { match op {
Op::IfEq { offset } AbcOp::IfEq { offset }
| Op::IfFalse { offset } | AbcOp::IfFalse { offset }
| Op::IfGe { offset } | AbcOp::IfGe { offset }
| Op::IfGt { offset } | AbcOp::IfGt { offset }
| Op::IfLe { offset } | AbcOp::IfLe { offset }
| Op::IfLt { offset } | AbcOp::IfLt { offset }
| Op::IfNe { offset } | AbcOp::IfNe { offset }
| Op::IfNge { offset } | AbcOp::IfNge { offset }
| Op::IfNgt { offset } | AbcOp::IfNgt { offset }
| Op::IfNle { offset } | AbcOp::IfNle { offset }
| Op::IfNlt { offset } | AbcOp::IfNlt { offset }
| Op::IfStrictEq { offset } | AbcOp::IfStrictEq { offset }
| Op::IfStrictNe { offset } | AbcOp::IfStrictNe { offset }
| Op::IfTrue { offset } | AbcOp::IfTrue { offset }
| Op::Jump { offset } => { | AbcOp::Jump { offset } => {
let op_idx = adjust_jump_offset( let op_idx = adjust_jump_offset(
activation, activation,
i, i,
@ -315,7 +326,7 @@ fn verify_code_starting_from<'gc>(
position + 1, position + 1,
)?; )?;
if matches!(op, Op::Jump { .. }) { if matches!(op, AbcOp::Jump { .. }) {
// A Jump is terminal, the code // A Jump is terminal, the code
// after it won't be executed // after it won't be executed
return Ok(()); return Ok(());
@ -324,11 +335,11 @@ fn verify_code_starting_from<'gc>(
} }
// Terminal opcodes // Terminal opcodes
Op::Throw => return Ok(()), AbcOp::Throw => return Ok(()),
Op::ReturnValue => return Ok(()), AbcOp::ReturnValue => return Ok(()),
Op::ReturnVoid => return Ok(()), AbcOp::ReturnVoid => return Ok(()),
Op::LookupSwitch(ref lookup_switch) => { AbcOp::LookupSwitch(ref lookup_switch) => {
let default_idx = adjust_jump_offset( let default_idx = adjust_jump_offset(
activation, activation,
i, i,
@ -376,19 +387,19 @@ fn verify_code_starting_from<'gc>(
// Verifications // Verifications
// Local register verifications // Local register verifications
Op::GetLocal { index } AbcOp::GetLocal { index }
| Op::SetLocal { index } | AbcOp::SetLocal { index }
| Op::Kill { index } | AbcOp::Kill { index }
| Op::DecLocal { index } | AbcOp::DecLocal { index }
| Op::DecLocalI { index } | AbcOp::DecLocalI { index }
| Op::IncLocal { index } | AbcOp::IncLocal { index }
| Op::IncLocalI { index } => { | AbcOp::IncLocalI { index } => {
if *index >= max_locals { if *index >= max_locals {
return Err(make_error_1025(activation, *index)); return Err(make_error_1025(activation, *index));
} }
} }
Op::HasNext2 { AbcOp::HasNext2 {
object_register, object_register,
index_register, index_register,
} => { } => {
@ -401,7 +412,7 @@ fn verify_code_starting_from<'gc>(
} }
// Misc opcode verification // Misc opcode verification
Op::CallMethod { index, .. } => { AbcOp::CallMethod { index, .. } => {
return Err(Error::AvmError(if index.as_u30() == 0 { return Err(Error::AvmError(if index.as_u30() == 0 {
verify_error(activation, "Error #1072: Disp_id 0 is illegal.", 1072)? verify_error(activation, "Error #1072: Disp_id 0 is illegal.", 1072)?
} else { } else {
@ -413,7 +424,7 @@ fn verify_code_starting_from<'gc>(
})); }));
} }
Op::NewActivation => { AbcOp::NewActivation => {
if !method if !method
.method() .method()
.flags .flags
@ -427,7 +438,7 @@ fn verify_code_starting_from<'gc>(
} }
} }
Op::GetLex { index } => { AbcOp::GetLex { index } => {
let multiname = method let multiname = method
.translation_unit() .translation_unit()
.pool_maybe_uninitialized_multiname(*index, &mut activation.context)?; .pool_maybe_uninitialized_multiname(*index, &mut activation.context)?;
@ -454,34 +465,323 @@ fn verify_code_starting_from<'gc>(
)?)) )?))
} }
fn ops_can_throw_error(ops: &[Op], start_idx: u32, end_idx: u32) -> bool { fn ops_can_throw_error(ops: &[AbcOp], start_idx: u32, end_idx: u32) -> bool {
for i in start_idx..end_idx { for i in start_idx..end_idx {
let op = &ops[i as usize]; let op = &ops[i as usize];
match op { match op {
Op::PushByte { .. } AbcOp::PushByte { .. }
| Op::PushDouble { .. } | AbcOp::PushDouble { .. }
| Op::PushFalse | AbcOp::PushFalse
| Op::PushInt { .. } | AbcOp::PushInt { .. }
| Op::PushNamespace { .. } | AbcOp::PushNamespace { .. }
| Op::PushNaN | AbcOp::PushNaN
| Op::PushNull | AbcOp::PushNull
| Op::PushShort { .. } | AbcOp::PushShort { .. }
| Op::PushString { .. } | AbcOp::PushString { .. }
| Op::PushTrue | AbcOp::PushTrue
| Op::PushUint { .. } | AbcOp::PushUint { .. }
| Op::PushUndefined | AbcOp::PushUndefined
| Op::Dup | AbcOp::Dup
| Op::Pop | AbcOp::Pop
| Op::GetLocal { .. } | AbcOp::GetLocal { .. }
| Op::SetLocal { .. } | AbcOp::SetLocal { .. }
| Op::Kill { .. } | AbcOp::Kill { .. }
| Op::Nop | AbcOp::Nop
| Op::Not | AbcOp::Not
| Op::PopScope | AbcOp::PopScope
| Op::ReturnVoid => {} | AbcOp::ReturnVoid => {}
_ => return true, _ => return true,
} }
} }
false false
} }
fn pool_int<'gc>(
activation: &mut Activation<'_, 'gc>,
translation_unit: TranslationUnit<'gc>,
index: Index<i32>,
) -> Result<i32, Error<'gc>> {
if index.0 == 0 {
return Ok(0);
}
translation_unit
.abc()
.constant_pool
.ints
.get(index.0 as usize - 1)
.cloned()
.ok_or_else(|| {
Error::AvmError(
verify_error(
activation,
&format!("Error #1032: Cpool index {} is out of range.", index.0),
1032,
)
.expect("Error should construct"),
)
})
}
fn pool_uint<'gc>(
activation: &mut Activation<'_, 'gc>,
translation_unit: TranslationUnit<'gc>,
index: Index<u32>,
) -> Result<u32, Error<'gc>> {
if index.0 == 0 {
return Ok(0);
}
translation_unit
.abc()
.constant_pool
.uints
.get(index.0 as usize - 1)
.cloned()
.ok_or_else(|| {
Error::AvmError(
verify_error(
activation,
&format!("Error #1032: Cpool index {} is out of range.", index.0),
1032,
)
.expect("Error should construct"),
)
})
}
fn pool_double<'gc>(
activation: &mut Activation<'_, 'gc>,
translation_unit: TranslationUnit<'gc>,
index: Index<f64>,
) -> Result<f64, Error<'gc>> {
if index.0 == 0 {
return Err(Error::AvmError(
verify_error(
activation,
"Error #1032: Cpool index 0 is out of range.",
1032,
)
.expect("Error should construct"),
));
}
translation_unit
.abc()
.constant_pool
.doubles
.get(index.0 as usize - 1)
.cloned()
.ok_or_else(|| {
Error::AvmError(
verify_error(
activation,
&format!("Error #1032: Cpool index {} is out of range.", index.0),
1032,
)
.expect("Error should construct"),
)
})
}
fn resolve_op<'gc>(
activation: &mut Activation<'_, 'gc>,
translation_unit: TranslationUnit<'gc>,
op: AbcOp,
) -> Result<Op, Error<'gc>> {
Ok(match op {
AbcOp::PushByte { value } => Op::PushByte { value },
AbcOp::PushDouble { value } => {
let value = pool_double(activation, translation_unit, value)?;
Op::PushDouble { value }
}
AbcOp::PushFalse => Op::PushFalse,
AbcOp::PushInt { value } => {
let value = pool_int(activation, translation_unit, value)?;
Op::PushInt { value }
}
AbcOp::PushNamespace { value } => Op::PushNamespace { value },
AbcOp::PushNaN => Op::PushNaN,
AbcOp::PushNull => Op::PushNull,
AbcOp::PushShort { value } => Op::PushShort { value },
AbcOp::PushString { value } => Op::PushString { value },
AbcOp::PushTrue => Op::PushTrue,
AbcOp::PushUint { value } => {
let value = pool_uint(activation, translation_unit, value)?;
Op::PushUint { value }
}
AbcOp::PushUndefined => Op::PushUndefined,
AbcOp::Pop => Op::Pop,
AbcOp::Dup => Op::Dup,
AbcOp::GetLocal { index } => Op::GetLocal { index },
AbcOp::SetLocal { index } => Op::SetLocal { index },
AbcOp::Kill { index } => Op::Kill { index },
AbcOp::Call { num_args } => Op::Call { num_args },
AbcOp::CallMethod { index, num_args } => Op::CallMethod { index, num_args },
AbcOp::CallProperty { index, num_args } => Op::CallProperty { index, num_args },
AbcOp::CallPropLex { index, num_args } => Op::CallPropLex { index, num_args },
AbcOp::CallPropVoid { index, num_args } => Op::CallPropVoid { index, num_args },
AbcOp::CallStatic { index, num_args } => Op::CallStatic { index, num_args },
AbcOp::CallSuper { index, num_args } => Op::CallSuper { index, num_args },
AbcOp::CallSuperVoid { index, num_args } => Op::CallSuperVoid { index, num_args },
AbcOp::ReturnValue => Op::ReturnValue,
AbcOp::ReturnVoid => Op::ReturnVoid,
AbcOp::GetProperty { index } => Op::GetProperty { index },
AbcOp::SetProperty { index } => Op::SetProperty { index },
AbcOp::InitProperty { index } => Op::InitProperty { index },
AbcOp::DeleteProperty { index } => Op::DeleteProperty { index },
AbcOp::GetSuper { index } => Op::GetSuper { index },
AbcOp::SetSuper { index } => Op::SetSuper { index },
AbcOp::In => Op::In,
AbcOp::PushScope => Op::PushScope,
AbcOp::NewCatch { index } => Op::NewCatch { index },
AbcOp::PushWith => Op::PushWith,
AbcOp::PopScope => Op::PopScope,
AbcOp::GetOuterScope { index } => Op::GetOuterScope { index },
AbcOp::GetScopeObject { index } => Op::GetScopeObject { index },
AbcOp::GetGlobalScope => Op::GetGlobalScope,
AbcOp::FindDef { index } => Op::FindDef { index },
AbcOp::FindProperty { index } => Op::FindProperty { index },
AbcOp::FindPropStrict { index } => Op::FindPropStrict { index },
AbcOp::GetLex { index } => Op::GetLex { index },
AbcOp::GetDescendants { index } => Op::GetDescendants { index },
AbcOp::GetSlot { index } => Op::GetSlot { index },
AbcOp::SetSlot { index } => Op::SetSlot { index },
AbcOp::GetGlobalSlot { index } => Op::GetGlobalSlot { index },
AbcOp::SetGlobalSlot { index } => Op::SetGlobalSlot { index },
AbcOp::Construct { num_args } => Op::Construct { num_args },
AbcOp::ConstructProp { index, num_args } => Op::ConstructProp { index, num_args },
AbcOp::ConstructSuper { num_args } => Op::ConstructSuper { num_args },
AbcOp::NewActivation => Op::NewActivation,
AbcOp::NewObject { num_args } => Op::NewObject { num_args },
AbcOp::NewFunction { index } => Op::NewFunction { index },
AbcOp::NewClass { index } => Op::NewClass { index },
AbcOp::ApplyType { num_types } => Op::ApplyType { num_types },
AbcOp::NewArray { num_args } => Op::NewArray { num_args },
AbcOp::CoerceA => Op::CoerceA,
AbcOp::CoerceB => Op::CoerceB,
AbcOp::CoerceD => Op::CoerceD,
AbcOp::CoerceI => Op::CoerceI,
AbcOp::CoerceO => Op::CoerceO,
AbcOp::CoerceS => Op::CoerceS,
AbcOp::CoerceU => Op::CoerceU,
AbcOp::ConvertB => Op::ConvertB,
AbcOp::ConvertI => Op::ConvertI,
AbcOp::ConvertD => Op::ConvertD,
AbcOp::ConvertO => Op::ConvertO,
AbcOp::ConvertU => Op::ConvertU,
AbcOp::ConvertS => Op::ConvertS,
AbcOp::Add => Op::Add,
AbcOp::AddI => Op::AddI,
AbcOp::BitAnd => Op::BitAnd,
AbcOp::BitNot => Op::BitNot,
AbcOp::BitOr => Op::BitOr,
AbcOp::BitXor => Op::BitXor,
AbcOp::DecLocal { index } => Op::DecLocal { index },
AbcOp::DecLocalI { index } => Op::DecLocalI { index },
AbcOp::Decrement => Op::Decrement,
AbcOp::DecrementI => Op::DecrementI,
AbcOp::Divide => Op::Divide,
AbcOp::IncLocal { index } => Op::IncLocal { index },
AbcOp::IncLocalI { index } => Op::IncLocalI { index },
AbcOp::Increment => Op::Increment,
AbcOp::IncrementI => Op::IncrementI,
AbcOp::LShift => Op::LShift,
AbcOp::Modulo => Op::Modulo,
AbcOp::Multiply => Op::Multiply,
AbcOp::MultiplyI => Op::MultiplyI,
AbcOp::Negate => Op::Negate,
AbcOp::NegateI => Op::NegateI,
AbcOp::RShift => Op::RShift,
AbcOp::Subtract => Op::Subtract,
AbcOp::SubtractI => Op::SubtractI,
AbcOp::Swap => Op::Swap,
AbcOp::URShift => Op::URShift,
AbcOp::Jump { offset } => Op::Jump { offset },
AbcOp::IfTrue { offset } => Op::IfTrue { offset },
AbcOp::IfFalse { offset } => Op::IfFalse { offset },
AbcOp::IfStrictEq { offset } => Op::IfStrictEq { offset },
AbcOp::IfStrictNe { offset } => Op::IfStrictNe { offset },
AbcOp::IfEq { offset } => Op::IfEq { offset },
AbcOp::IfNe { offset } => Op::IfNe { offset },
AbcOp::IfGe { offset } => Op::IfGe { offset },
AbcOp::IfGt { offset } => Op::IfGt { offset },
AbcOp::IfLe { offset } => Op::IfLe { offset },
AbcOp::IfLt { offset } => Op::IfLt { offset },
AbcOp::IfNge { offset } => Op::IfNge { offset },
AbcOp::IfNgt { offset } => Op::IfNgt { offset },
AbcOp::IfNle { offset } => Op::IfNle { offset },
AbcOp::IfNlt { offset } => Op::IfNlt { offset },
AbcOp::StrictEquals => Op::StrictEquals,
AbcOp::Equals => Op::Equals,
AbcOp::GreaterEquals => Op::GreaterEquals,
AbcOp::GreaterThan => Op::GreaterThan,
AbcOp::LessEquals => Op::LessEquals,
AbcOp::LessThan => Op::LessThan,
AbcOp::Nop => Op::Nop,
AbcOp::Not => Op::Not,
AbcOp::HasNext => Op::HasNext,
AbcOp::HasNext2 {
object_register,
index_register,
} => Op::HasNext2 {
object_register,
index_register,
},
AbcOp::NextName => Op::NextName,
AbcOp::NextValue => Op::NextValue,
AbcOp::IsType { index } => Op::IsType { index },
AbcOp::IsTypeLate => Op::IsTypeLate,
AbcOp::AsType { type_name } => Op::AsType { type_name },
AbcOp::AsTypeLate => Op::AsTypeLate,
AbcOp::InstanceOf => Op::InstanceOf,
AbcOp::Label => Op::Label,
AbcOp::Debug {
is_local_register,
register_name,
register,
} => Op::Debug {
is_local_register,
register_name,
register,
},
AbcOp::DebugFile { file_name } => Op::DebugFile { file_name },
AbcOp::DebugLine { line_num } => Op::DebugLine { line_num },
AbcOp::Bkpt => Op::Bkpt,
AbcOp::BkptLine { line_num } => Op::BkptLine { line_num },
AbcOp::Timestamp => Op::Timestamp,
AbcOp::TypeOf => Op::TypeOf,
AbcOp::EscXAttr => Op::EscXAttr,
AbcOp::EscXElem => Op::EscXElem,
AbcOp::LookupSwitch(lookup_switch) => Op::LookupSwitch(lookup_switch),
AbcOp::Coerce { index } => Op::Coerce { index },
AbcOp::CheckFilter => Op::CheckFilter,
AbcOp::Si8 => Op::Si8,
AbcOp::Si16 => Op::Si16,
AbcOp::Si32 => Op::Si32,
AbcOp::Sf32 => Op::Sf32,
AbcOp::Sf64 => Op::Sf64,
AbcOp::Li8 => Op::Li8,
AbcOp::Li16 => Op::Li16,
AbcOp::Li32 => Op::Li32,
AbcOp::Lf32 => Op::Lf32,
AbcOp::Lf64 => Op::Lf64,
AbcOp::Sxi1 => Op::Sxi1,
AbcOp::Sxi8 => Op::Sxi8,
AbcOp::Sxi16 => Op::Sxi16,
AbcOp::Throw => Op::Throw,
_ => {
tracing::error!("Unimplemented AVM2 op {:?} found during verification", op);
return Err(Error::AvmError(verify_error(
activation,
"Error #1011: Method contained illegal opcode.",
1011,
)?));
}
})
}

View File

@ -1,7 +1,5 @@
use bitflags::bitflags; use bitflags::bitflags;
use std::marker::PhantomData; use std::marker::PhantomData;
#[cfg(target_pointer_width = "64")]
use std::mem::size_of;
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct AbcFile { pub struct AbcFile {
@ -574,6 +572,3 @@ pub enum Op {
Timestamp, Timestamp,
URShift, URShift,
} }
#[cfg(target_pointer_width = "64")]
const _: () = assert!(size_of::<Op>() == 16);