//! ActionScript Virtual Machine 2 (AS3) support use crate::avm2::activation::Activation; use crate::avm2::names::Namespace; use crate::avm2::object::TObject; use crate::avm2::value::Value; use crate::context::UpdateContext; use crate::tag_utils::SwfSlice; use gc_arena::{Collect, GcCell}; use std::io::Cursor; use std::rc::Rc; use swf::avm2::read::Reader; use swf::avm2::types::{AbcFile, Index, MethodBody, Namespace as AbcNamespace, Op}; use swf::read::SwfRead; mod activation; mod function; mod names; mod object; mod return_value; mod script_object; mod value; macro_rules! avm_debug { ($($arg:tt)*) => ( #[cfg(feature = "avm_debug")] log::debug!($($arg)*) ) } /// Boxed error alias. /// /// As AVM2 is a far stricter VM than AVM1, this may eventually be replaced /// with a proper Avm2Error enum. type Error = Box; /// The state of an AVM2 interpreter. #[derive(Collect)] #[collect(no_drop)] pub struct Avm2<'gc> { /// All activation records for the current interpreter. stack_frames: Vec>>, /// Values currently present on the operand stack. stack: Vec>, } impl<'gc> Avm2<'gc> { /// Construct a new AVM interpreter. pub fn new() -> Self { Self { stack_frames: Vec::new(), stack: Vec::new(), } } /// Load an ABC file embedded in a `SwfSlice`. /// /// The `SwfSlice` must resolve to the contents of an ABC file. /// /// The `preload` flag indicates if the file is being encountered as part /// of a preloading operation. If false, then this file has actually been /// encountered as part of normal movie playback and it's final script /// should be executed. pub fn load_abc( &mut self, abc: SwfSlice, context: &mut UpdateContext<'_, 'gc, '_>, preload: bool, ) -> Result<(), Error> { let mut read = Reader::new(abc.as_ref()); let _abc_file = read.read()?; Ok(()) } /// Get the current stack frame (`Activation` object). pub fn current_stack_frame(&self) -> Option>> { self.stack_frames.last().copied() } /// Add a new stack frame to the stack, which can represent any particular /// operation you like that needs to execute AVM2 code. pub fn insert_stack_frame(&mut self, frame: GcCell<'gc, Activation<'gc>>) { self.stack_frames.push(frame) } /// Destroy the current stack frame (if there is one). /// /// The given return value will be pushed on the stack if there is a /// function to return it to. Otherwise, it will be discarded. /// /// NOTE: This means that if you are starting a brand new AVM stack just to /// get it's return value, you won't get that value. Instead, retain a cell /// referencing the oldest activation frame and use that to retrieve the /// return value. fn retire_stack_frame( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, return_value: Value<'gc>, ) -> Result<(), Error> { if let Some(frame) = self.current_stack_frame() { self.stack_frames.pop(); let can_return = !self.stack_frames.is_empty(); if can_return { frame .write(context.gc_context) .set_return_value(return_value.clone()); self.push(return_value); } } Ok(()) } /// Perform some action with the current stack frame's reader. /// /// This function constructs a reader based off the current stack frame's /// reader. You are permitted to mutate the stack frame as you wish. If the /// stack frame we started with still exists in the same location on the /// stack, it's PC will be updated to the Reader's current PC. /// /// Stack frame identity (for the purpose of the above paragraph) is /// determined by the data pointed to by the `SwfSlice` of a given frame. /// /// # Warnings /// /// It is incorrect to call this function multiple times in the same stack. /// Doing so will result in any changes in duplicate readers being ignored. /// Always pass the borrowed reader into functions that need it. pub fn with_current_reader_mut( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, func: F, ) -> Result where F: FnOnce( &mut Self, &mut Reader>, &mut UpdateContext<'_, 'gc, '_>, ) -> Result, { let (frame_cell, action, pc) = { let frame = self .current_stack_frame() .ok_or("No stack frame to read!")?; let mut frame_ref = frame.write(context.gc_context); frame_ref.lock()?; (frame, frame_ref.action(), frame_ref.pc()) }; let abc = action.abc.as_ref(); let method_index = action.abc_method; let method_body_index = action.abc_method_body as usize; let method_body: Result<&MethodBody, Error> = abc.method_bodies.get(method_body_index).ok_or_else(|| { "Attempting to execute a method that does not exist" .to_string() .into() }); let cursor = Cursor::new(method_body?.code.as_ref()); let mut read = Reader::new(cursor); read.get_inner().set_position(pc as u64); let r = func(self, &mut read, context); let mut frame_ref = frame_cell.write(context.gc_context); frame_ref.unlock_execution(); frame_ref.set_pc(read.get_inner().position() as usize); r } /// Execute the AVM stack until it is exhausted. pub fn run_stack_till_empty( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, ) -> Result<(), Error> { while !self.stack_frames.is_empty() { self.with_current_reader_mut(context, |this, r, context| { this.do_next_opcode(context, r) })?; } // Operand stack should be empty at this point. // This is probably a bug on our part, // although bytecode could in theory leave data on the stack. if !self.stack.is_empty() { log::warn!("Operand stack is not empty after execution"); self.stack.clear(); } Ok(()) } /// Execute the AVM stack until a given activation returns. pub fn run_current_frame( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, stop_frame: GcCell<'gc, Activation<'gc>>, ) -> Result<(), Error> { let mut stop_frame_id = None; for (index, frame) in self.stack_frames.iter().enumerate() { if GcCell::ptr_eq(stop_frame, *frame) { stop_frame_id = Some(index); } } if let Some(stop_frame_id) = stop_frame_id { while self .stack_frames .get(stop_frame_id) .map(|fr| GcCell::ptr_eq(stop_frame, *fr)) .unwrap_or(false) { self.with_current_reader_mut(context, |this, r, context| { this.do_next_opcode(context, r) })?; } Ok(()) } else { Err("Attempted to run a frame not on the current interpreter stack".into()) } } /// Push a value onto the operand stack. fn push(&mut self, value: impl Into>) { let value = value.into(); avm_debug!("Stack push {}: {:?}", self.stack.len(), value); self.stack.push(value); } /// Retrieve the top-most value on the operand stack. #[allow(clippy::let_and_return)] fn pop(&mut self) -> Value<'gc> { let value = self.stack.pop().unwrap_or_else(|| { log::warn!("Avm1::pop: Stack underflow"); Value::Undefined }); avm_debug!("Stack pop {}: {:?}", self.stack.len(), value); value } fn register_value(&self, index: u32) -> Result, Error> { self.current_stack_frame() .and_then(|sf| sf.read().local_register(index)) .ok_or_else(|| format!("Out of bounds register read: {}", index).into()) } fn set_register_value( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, index: u32, value: impl Into>, ) -> Result<(), Error> { match self.current_stack_frame().map(|sf| { sf.write(context.gc_context) .set_local_register(index, value, context.gc_context) }) { Some(true) => Ok(()), _ => Err(format!("Out of bounds register write: {}", index).into()), } } /// Retrieve the current constant pool for the currently executing function. fn current_abc(&self) -> Option> { self.current_stack_frame() .map(|sf| sf.read().action().abc.clone()) } /// Retrieve a int from the current constant pool. fn pool_int(&self, index: Index) -> Result { value::abc_int(&self.current_abc().unwrap(), index) } /// Retrieve a int from the current constant pool. fn pool_uint(&self, index: Index) -> Result { value::abc_uint(&self.current_abc().unwrap(), index) } /// Retrieve a double from the current constant pool. fn pool_double(&self, index: Index) -> Result { value::abc_double(&self.current_abc().unwrap(), index) } /// Retrieve a string from the current constant pool. fn pool_string(&self, index: Index) -> Result { value::abc_string(&self.current_abc().unwrap(), index) } /// Retrieve a namespace from the current constant pool. fn pool_namespace(&self, index: Index) -> Result { Namespace::from_abc_namespace(&self.current_abc().unwrap(), index) } /// Run a single action from a given action reader. pub fn do_next_opcode( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, reader: &mut Reader>, ) -> Result<(), Error> { if let Some(op) = reader.read_op()? { avm_debug!("Opcode: {:?}", op); let result = match op { Op::PushByte { value } => self.op_push_byte(value), Op::PushDouble { value } => self.op_push_double(value), Op::PushFalse => self.op_push_false(), Op::PushInt { value } => self.op_push_int(value), Op::PushNamespace { value } => self.op_push_namespace(value), Op::PushNaN => self.op_push_nan(), Op::PushNull => self.op_push_null(), Op::PushShort { value } => self.op_push_short(value), Op::PushString { value } => self.op_push_string(value), Op::PushTrue => self.op_push_true(), Op::PushUint { value } => self.op_push_uint(value), Op::PushUndefined => self.op_push_undefined(), Op::GetLocal { index } => self.op_get_local(index), Op::SetLocal { index } => self.op_set_local(context, index), Op::Call { num_args } => self.op_call(context, num_args), Op::ReturnValue => self.op_return_value(context), Op::ReturnVoid => self.op_return_void(context), _ => self.unknown_op(op), }; if let Err(ref e) = result { log::error!("AVM2 error: {}", e); return result; } } Ok(()) } fn unknown_op(&mut self, op: swf::avm2::types::Op) -> Result<(), Error> { log::error!("Unknown AVM2 opcode: {:?}", op); Err("Unknown op".into()) } fn op_push_byte(&mut self, value: u8) -> Result<(), Error> { self.push(value); Ok(()) } fn op_push_double(&mut self, value: Index) -> Result<(), Error> { self.push(self.pool_double(value)?); Ok(()) } fn op_push_false(&mut self) -> Result<(), Error> { self.push(false); Ok(()) } fn op_push_int(&mut self, value: Index) -> Result<(), Error> { self.push(self.pool_int(value)?); Ok(()) } fn op_push_namespace(&mut self, value: Index) -> Result<(), Error> { self.push(self.pool_namespace(value)?); Ok(()) } fn op_push_nan(&mut self) -> Result<(), Error> { self.push(std::f64::NAN); Ok(()) } fn op_push_null(&mut self) -> Result<(), Error> { self.push(Value::Null); Ok(()) } fn op_push_short(&mut self, value: u32) -> Result<(), Error> { self.push(value); Ok(()) } fn op_push_string(&mut self, value: Index) -> Result<(), Error> { self.push(self.pool_string(value)?); Ok(()) } fn op_push_true(&mut self) -> Result<(), Error> { self.push(true); Ok(()) } fn op_push_uint(&mut self, value: Index) -> Result<(), Error> { self.push(self.pool_uint(value)?); Ok(()) } fn op_push_undefined(&mut self) -> Result<(), Error> { self.push(Value::Undefined); Ok(()) } fn op_get_local(&mut self, register_index: u32) -> Result<(), Error> { self.push(self.register_value(register_index)?); Ok(()) } fn op_set_local( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, register_index: u32, ) -> Result<(), Error> { let value = self.pop(); self.set_register_value(context, register_index, value) } fn op_call( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, arg_count: u32, ) -> Result<(), Error> { let function = self.pop().as_object()?; let receiver = self.pop().as_object()?; let mut args = Vec::new(); for _ in 0..arg_count { args.push(self.pop()); } function.call(receiver, &args, self, context)?.push(self); Ok(()) } fn op_return_value(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) -> Result<(), Error> { let return_value = self.pop(); self.retire_stack_frame(context, return_value) } fn op_return_void(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) -> Result<(), Error> { self.retire_stack_frame(context, Value::Undefined) } }