From 97e005622b63de57c2912f4527cc1f75bbb5e026 Mon Sep 17 00:00:00 2001 From: David Wendt Date: Sat, 4 Jul 2020 17:18:41 -0400 Subject: [PATCH] Invert the role of `Avm2` and it's `Activation`, similar to what was done with `Avm1` and it's `Activation`. This also results in a far reduced role for `ReturnValue`, since I also took the liberty of removing most of it's use. Furthermore, I also made it apply equally to native and AVM2 code, which ensures all native implementations of methods don't double-borrow. In AVM1, `ReturnValue` was actually removed entirely, because it's not needed. I attempted to do the same, but the fact that we're currently embedding `ScriptObjectData` in native objects means that we need it for virtual properties. Otherwise, virtual property implementations will see locked objects, which is bad. --- core/src/avm2.rs | 1416 +---------------- core/src/avm2/activation.rs | 1374 +++++++++++++++- core/src/avm2/function.rs | 103 +- core/src/avm2/globals.rs | 10 +- core/src/avm2/globals/class.rs | 8 +- .../globals/flash/display/displayobject.rs | 10 +- .../flash/display/displayobjectcontainer.rs | 10 +- .../flash/display/interactiveobject.rs | 10 +- .../avm2/globals/flash/display/movieclip.rs | 10 +- core/src/avm2/globals/flash/display/sprite.rs | 10 +- .../globals/flash/events/eventdispatcher.rs | 10 +- core/src/avm2/globals/function.rs | 20 +- core/src/avm2/globals/object.rs | 51 +- core/src/avm2/method.rs | 8 +- core/src/avm2/object.rs | 58 +- core/src/avm2/property.rs | 36 +- core/src/avm2/return_value.rs | 227 +-- core/src/avm2/scope.rs | 28 +- core/src/avm2/script_object.rs | 86 +- core/src/player.rs | 2 - 20 files changed, 1658 insertions(+), 1829 deletions(-) diff --git a/core/src/avm2.rs b/core/src/avm2.rs index 739e86608..d1da0b356 100644 --- a/core/src/avm2.rs +++ b/core/src/avm2.rs @@ -1,28 +1,17 @@ //! ActionScript Virtual Machine 2 (AS3) support use crate::avm2::activation::Activation; -use crate::avm2::class::Class; -use crate::avm2::function::FunctionObject; use crate::avm2::globals::SystemPrototypes; -use crate::avm2::method::BytecodeMethod; -use crate::avm2::names::{Multiname, Namespace, QName}; use crate::avm2::object::{Object, TObject}; -use crate::avm2::return_value::ReturnValue; use crate::avm2::scope::Scope; -use crate::avm2::script::{Script, TranslationUnit}; -use crate::avm2::script_object::ScriptObject; +use crate::avm2::script::Script; +use crate::avm2::script::TranslationUnit; use crate::avm2::value::Value; use crate::context::UpdateContext; use crate::tag_utils::SwfSlice; use gc_arena::{Collect, GcCell, MutationContext}; -use std::io::Cursor; use std::rc::Rc; use swf::avm2::read::Reader; -use swf::avm2::types::{ - AbcFile, Class as AbcClass, Index, Method as AbcMethod, MethodBody, Multiname as AbcMultiname, - Namespace as AbcNamespace, Op, -}; -use swf::read::SwfRead; #[macro_export] macro_rules! avm_debug { @@ -58,9 +47,6 @@ type Error = Box; #[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>, @@ -77,7 +63,6 @@ impl<'gc> Avm2<'gc> { let (globals, system_prototypes) = globals::construct_global_scope(mc); Self { - stack_frames: Vec::new(), stack: Vec::new(), globals, system_prototypes, @@ -89,6 +74,17 @@ impl<'gc> Avm2<'gc> { &self.system_prototypes } + /// Run a script's initializer method. + pub fn run_script_initializer( + &mut self, + script: GcCell<'gc, Script<'gc>>, + context: &mut UpdateContext<'_, 'gc, '_>, + ) -> Result<(), Error> { + let mut init_activation = Activation::from_script(self, context, script, self.globals)?; + + init_activation.run_stack_frame_for_script(context, script) + } + /// Load an ABC file embedded in a `SwfSlice`. /// /// The `SwfSlice` must resolve to the contents of an ABC file. @@ -106,21 +102,25 @@ impl<'gc> Avm2<'gc> { for i in (0..abc_file.scripts.len()).rev() { let script = tunit.load_script(i as u32, context.gc_context)?; - let scope = Scope::push_scope(None, self.globals(), context.gc_context); + let mut globals = self.globals(); + let scope = Scope::push_scope(None, globals, context.gc_context); + let mut null_activation = Activation::from_nothing(self, context); // TODO: Lazyinit means we shouldn't do this until traits are // actually mentioned... for trait_entry in script.read().traits()?.iter() { - self.globals().install_foreign_trait( - self, + globals.install_foreign_trait( + &mut null_activation, context, trait_entry.clone(), Some(scope), - self.globals(), + globals, )?; } - self.insert_stack_frame_for_script(context, script)?; + drop(null_activation); + + self.run_script_initializer(script, context)?; } Ok(()) @@ -130,191 +130,6 @@ impl<'gc> Avm2<'gc> { self.globals } - /// 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) - } - - /// Add a new stack frame for executing an entrypoint script. - pub fn insert_stack_frame_for_script( - &mut self, - context: &mut UpdateContext<'_, 'gc, '_>, - script: GcCell<'gc, Script<'gc>>, - ) -> Result<(), Error> { - self.stack_frames.push(GcCell::allocate( - context.gc_context, - Activation::from_script(context, script, self.globals)?, - )); - - Ok(()) - } - - /// Destroy the current stack frame (if there is one) with a return value. - /// - /// 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(()) - } - - /// Destroy the current stack frame (if there is one) with an exception. - /// - /// TODO: This function should allow exception recovery at some point in - /// the future. - /// - /// 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 unwind_stack_frame(&mut self) { - if let Some(_frame) = self.current_stack_frame() { - self.stack_frames.pop(); - } - } - - /// 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 (abc, frame_cell, method_body_index, 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()?; - - let method = frame_ref.method(); - let abc = method.abc(); - let _method_index = method.abc_method; - let method_body_index = method.abc_method_body as usize; - - (abc, frame, method_body_index, frame_ref.pc()) - }; - - 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(); @@ -344,1191 +159,4 @@ impl<'gc> Avm2<'gc> { args } - - 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().method().abc()) - } - - fn current_translation_unit(&self) -> Option> { - self.current_stack_frame() - .map(|sf| sf.read().method().translation_unit()) - } - - /// 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) - } - - /// Retrieve a multiname from the current constant pool. - fn pool_multiname(&mut self, index: Index) -> Result { - Multiname::from_abc_multiname(&self.current_abc().unwrap(), index, self) - } - - /// Retrieve a static, or non-runtime, multiname from the current constant - /// pool. - fn pool_multiname_static(&mut self, index: Index) -> Result { - Multiname::from_abc_multiname_static(&self.current_abc().unwrap(), index) - } - - /// Retrieve a method entry from the current ABC file's method table. - fn table_method(&mut self, index: Index) -> Result, Error> { - BytecodeMethod::from_method_index(self.current_translation_unit().unwrap(), index.clone()) - .ok_or_else(|| format!("Method index {} does not exist", index.0).into()) - } - - /// Retrieve a class entry from the current ABC file's method table. - fn table_class( - &mut self, - index: Index, - context: &mut UpdateContext<'_, 'gc, '_>, - ) -> Result>, Error> { - self.current_translation_unit() - .unwrap() - .load_class(index.0, context.gc_context) - } - - /// 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> { - let op = reader.read_op(); - if let Ok(Some(op)) = 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::Pop => self.op_pop(), - Op::Dup => self.op_dup(), - Op::GetLocal { index } => self.op_get_local(index), - Op::SetLocal { index } => self.op_set_local(context, index), - Op::Kill { index } => self.op_kill(context, index), - Op::Call { num_args } => self.op_call(context, num_args), - Op::CallMethod { index, num_args } => self.op_call_method(context, index, num_args), - Op::CallProperty { index, num_args } => { - self.op_call_property(context, index, num_args) - } - Op::CallPropLex { index, num_args } => { - self.op_call_prop_lex(context, index, num_args) - } - Op::CallPropVoid { index, num_args } => { - self.op_call_prop_void(context, index, num_args) - } - Op::CallStatic { index, num_args } => self.op_call_static(context, index, num_args), - Op::CallSuper { index, num_args } => self.op_call_super(context, index, num_args), - Op::CallSuperVoid { index, num_args } => { - self.op_call_super_void(context, index, num_args) - } - Op::ReturnValue => self.op_return_value(context), - Op::ReturnVoid => self.op_return_void(context), - Op::GetProperty { index } => self.op_get_property(context, index), - Op::SetProperty { index } => self.op_set_property(context, index), - Op::InitProperty { index } => self.op_init_property(context, index), - Op::DeleteProperty { index } => self.op_delete_property(context, index), - Op::GetSuper { index } => self.op_get_super(context, index), - Op::SetSuper { index } => self.op_set_super(context, index), - Op::PushScope => self.op_push_scope(context), - Op::PushWith => self.op_push_with(context), - Op::PopScope => self.op_pop_scope(context), - Op::GetScopeObject { index } => self.op_get_scope_object(index), - Op::GetGlobalScope => self.op_get_global_scope(), - Op::FindProperty { index } => self.op_find_property(context, index), - Op::FindPropStrict { index } => self.op_find_prop_strict(context, index), - Op::GetLex { index } => self.op_get_lex(context, index), - Op::GetSlot { index } => self.op_get_slot(index), - Op::SetSlot { index } => self.op_set_slot(context, index), - Op::GetGlobalSlot { index } => self.op_get_global_slot(index), - Op::SetGlobalSlot { index } => self.op_set_global_slot(context, index), - Op::Construct { num_args } => self.op_construct(context, num_args), - Op::ConstructProp { index, num_args } => { - self.op_construct_prop(context, index, num_args) - } - Op::ConstructSuper { num_args } => self.op_construct_super(context, num_args), - Op::NewActivation => self.op_new_activation(context), - Op::NewObject { num_args } => self.op_new_object(context, num_args), - Op::NewFunction { index } => self.op_new_function(context, index), - Op::NewClass { index } => self.op_new_class(context, index), - Op::CoerceA => self.op_coerce_a(), - Op::Jump { offset } => self.op_jump(offset, reader), - Op::IfTrue { offset } => self.op_if_true(offset, reader), - Op::IfFalse { offset } => self.op_if_false(offset, reader), - Op::IfStrictEq { offset } => self.op_if_strict_eq(offset, reader), - Op::IfStrictNe { offset } => self.op_if_strict_ne(offset, reader), - Op::StrictEquals => self.op_strict_equals(), - Op::HasNext => self.op_has_next(), - Op::HasNext2 { - object_register, - index_register, - } => self.op_has_next_2(context, object_register, index_register), - Op::NextName => self.op_next_name(), - Op::NextValue => self.op_next_value(context), - Op::Label => Ok(()), - Op::Debug { - is_local_register, - register_name, - register, - } => self.op_debug(is_local_register, register_name, register), - Op::DebugFile { file_name } => self.op_debug_file(file_name), - Op::DebugLine { line_num } => self.op_debug_line(line_num), - _ => self.unknown_op(op), - }; - - if let Err(ref e) = result { - log::error!("AVM2 error: {}", e); - self.unwind_stack_frame(); - return result; - } - } else if let Ok(None) = op { - log::error!("Unknown opcode!"); - self.unwind_stack_frame(); - return Err("Unknown opcode!".into()); - } else if let Err(e) = op { - log::error!("Parse error: {:?}", e); - self.unwind_stack_frame(); - return Err(e.into()); - } - - 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_pop(&mut self) -> Result<(), Error> { - self.pop(); - - Ok(()) - } - - fn op_dup(&mut self) -> Result<(), Error> { - self.push(self.stack.last().cloned().unwrap_or(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_kill( - &mut self, - context: &mut UpdateContext<'_, 'gc, '_>, - register_index: u32, - ) -> Result<(), Error> { - self.set_register_value(context, register_index, Value::Undefined) - } - - fn op_call( - &mut self, - context: &mut UpdateContext<'_, 'gc, '_>, - arg_count: u32, - ) -> Result<(), Error> { - let args = self.pop_args(arg_count); - let receiver = self.pop().as_object().ok(); - let function = self.pop().as_object()?; - let base_proto = receiver.and_then(|r| r.proto()); - let value = function.call(receiver, &args, self, context, base_proto)?; - - self.push(value); - - Ok(()) - } - - fn op_call_method( - &mut self, - context: &mut UpdateContext<'_, 'gc, '_>, - index: Index, - arg_count: u32, - ) -> Result<(), Error> { - let args = self.pop_args(arg_count); - let receiver = self.pop().as_object()?; - let function: Result, Error> = receiver - .get_method(index.0) - .ok_or_else(|| format!("Object method {} does not exist", index.0).into()); - let base_proto = receiver.proto(); - let value = function?.call(Some(receiver), &args, self, context, base_proto)?; - - self.push(value); - - Ok(()) - } - - fn op_call_property( - &mut self, - context: &mut UpdateContext<'_, 'gc, '_>, - index: Index, - arg_count: u32, - ) -> Result<(), Error> { - let args = self.pop_args(arg_count); - let multiname = self.pool_multiname(index)?; - let mut receiver = self.pop().as_object()?; - let name: Result = receiver - .resolve_multiname(&multiname)? - .ok_or_else(|| format!("Could not find method {:?}", multiname.local_name()).into()); - let name = name?; - let base_proto = receiver.get_base_proto(&name)?; - let function = receiver - .get_property(receiver, &name, self, context)? - .as_object()?; - let value = function.call(Some(receiver), &args, self, context, base_proto)?; - - self.push(value); - - Ok(()) - } - - fn op_call_prop_lex( - &mut self, - context: &mut UpdateContext<'_, 'gc, '_>, - index: Index, - arg_count: u32, - ) -> Result<(), Error> { - let args = self.pop_args(arg_count); - let multiname = self.pool_multiname(index)?; - let mut receiver = self.pop().as_object()?; - let name: Result = receiver - .resolve_multiname(&multiname)? - .ok_or_else(|| format!("Could not find method {:?}", multiname.local_name()).into()); - let function = receiver - .get_property(receiver, &name?, self, context)? - .as_object()?; - let value = function.call(None, &args, self, context, None)?; - - self.push(value); - - Ok(()) - } - - fn op_call_prop_void( - &mut self, - context: &mut UpdateContext<'_, 'gc, '_>, - index: Index, - arg_count: u32, - ) -> Result<(), Error> { - let args = self.pop_args(arg_count); - let multiname = self.pool_multiname(index)?; - let mut receiver = self.pop().as_object()?; - let name: Result = receiver - .resolve_multiname(&multiname)? - .ok_or_else(|| format!("Could not find method {:?}", multiname.local_name()).into()); - let name = name?; - let base_proto = receiver.get_base_proto(&name)?; - let function = receiver - .get_property(receiver, &name, self, context)? - .as_object()?; - - function.call(Some(receiver), &args, self, context, base_proto)?; - - Ok(()) - } - - fn op_call_static( - &mut self, - context: &mut UpdateContext<'_, 'gc, '_>, - index: Index, - arg_count: u32, - ) -> Result<(), Error> { - let args = self.pop_args(arg_count); - let receiver = self.pop().as_object()?; - let method = self.table_method(index)?; - let scope = self.current_stack_frame().unwrap().read().scope(); //TODO: Is this correct? - let function = FunctionObject::from_method( - context.gc_context, - method.into(), - scope, - self.system_prototypes.function, - None, - ); - let value = function.call(Some(receiver), &args, self, context, receiver.proto())?; - - self.push(value); - - Ok(()) - } - - fn op_call_super( - &mut self, - context: &mut UpdateContext<'_, 'gc, '_>, - index: Index, - arg_count: u32, - ) -> Result<(), Error> { - let args = self.pop_args(arg_count); - let multiname = self.pool_multiname(index)?; - let receiver = self.pop().as_object()?; - let name: Result = receiver - .resolve_multiname(&multiname)? - .ok_or_else(|| format!("Could not find method {:?}", multiname.local_name()).into()); - let base_proto: Result, Error> = self - .current_stack_frame() - .unwrap() - .read() - .base_proto() - .and_then(|bp| bp.proto()) - .ok_or_else(|| { - "Attempted to call super method without a superclass." - .to_string() - .into() - }); - let base_proto = base_proto?; - let mut base = base_proto.construct(self, context, &[])?; //TODO: very hacky workaround - - let function = base - .get_property(receiver, &name?, self, context)? - .as_object()?; - - let value = function.call(Some(receiver), &args, self, context, Some(base_proto))?; - - self.push(value); - - Ok(()) - } - - fn op_call_super_void( - &mut self, - context: &mut UpdateContext<'_, 'gc, '_>, - index: Index, - arg_count: u32, - ) -> Result<(), Error> { - let args = self.pop_args(arg_count); - let multiname = self.pool_multiname(index)?; - let receiver = self.pop().as_object()?; - let name: Result = receiver - .resolve_multiname(&multiname)? - .ok_or_else(|| format!("Could not find method {:?}", multiname.local_name()).into()); - let base_proto: Result, Error> = self - .current_stack_frame() - .unwrap() - .read() - .base_proto() - .and_then(|bp| bp.proto()) - .ok_or_else(|| { - "Attempted to call super method without a superclass." - .to_string() - .into() - }); - let base_proto = base_proto?; - let mut base = base_proto.construct(self, context, &[])?; //TODO: very hacky workaround - - let function = base - .get_property(receiver, &name?, self, context)? - .as_object()?; - - function.call(Some(receiver), &args, self, context, Some(base_proto))?; - - 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) - } - - fn op_get_property( - &mut self, - context: &mut UpdateContext<'_, 'gc, '_>, - index: Index, - ) -> Result<(), Error> { - let multiname = self.pool_multiname(index)?; - let mut object = self.pop().as_object()?; - - let name: Result = object.resolve_multiname(&multiname)?.ok_or_else(|| { - format!("Could not resolve property {:?}", multiname.local_name()).into() - }); - - let value = object.get_property(object, &name?, self, context)?; - self.push(value); - - Ok(()) - } - - fn op_set_property( - &mut self, - context: &mut UpdateContext<'_, 'gc, '_>, - index: Index, - ) -> Result<(), Error> { - let value = self.pop(); - let multiname = self.pool_multiname(index)?; - let mut object = self.pop().as_object()?; - - if let Some(name) = object.resolve_multiname(&multiname)? { - object.set_property(object, &name, value, self, context) - } else { - //TODO: Non-dynamic objects should fail - //TODO: This should only work if the public namespace is present - let local_name: Result<&str, Error> = multiname - .local_name() - .ok_or_else(|| "Cannot set property using any name".into()); - let name = QName::dynamic_name(local_name?); - object.set_property(object, &name, value, self, context) - } - } - - fn op_init_property( - &mut self, - context: &mut UpdateContext<'_, 'gc, '_>, - index: Index, - ) -> Result<(), Error> { - let value = self.pop(); - let multiname = self.pool_multiname(index)?; - let mut object = self.pop().as_object()?; - - if let Some(name) = object.resolve_multiname(&multiname)? { - object.init_property(object, &name, value, self, context) - } else { - //TODO: Non-dynamic objects should fail - //TODO: This should only work if the public namespace is present - let local_name: Result<&str, Error> = multiname - .local_name() - .ok_or_else(|| "Cannot set property using any name".into()); - let name = QName::dynamic_name(local_name?); - object.init_property(object, &name, value, self, context) - } - } - - fn op_delete_property( - &mut self, - context: &mut UpdateContext<'_, 'gc, '_>, - index: Index, - ) -> Result<(), Error> { - let multiname = self.pool_multiname(index)?; - let object = self.pop().as_object()?; - - if let Some(name) = object.resolve_multiname(&multiname)? { - self.push(object.delete_property(context.gc_context, &name)) - } else { - self.push(false) - } - - Ok(()) - } - - fn op_get_super( - &mut self, - context: &mut UpdateContext<'_, 'gc, '_>, - index: Index, - ) -> Result<(), Error> { - let multiname = self.pool_multiname(index)?; - let object = self.pop().as_object()?; - let base_proto: Result, Error> = self - .current_stack_frame() - .unwrap() - .read() - .base_proto() - .and_then(|p| p.proto()) - .ok_or_else(|| "Attempted to get property on non-existent super object".into()); - let base_proto = base_proto?; - let mut base = base_proto.construct(self, context, &[])?; //TODO: very hacky workaround - - let name: Result = base.resolve_multiname(&multiname)?.ok_or_else(|| { - format!( - "Could not resolve {:?} as super property", - multiname.local_name() - ) - .into() - }); - - let value = base.get_property(object, &name?, self, context)?; - - self.push(value); - - Ok(()) - } - - fn op_set_super( - &mut self, - context: &mut UpdateContext<'_, 'gc, '_>, - index: Index, - ) -> Result<(), Error> { - let value = self.pop(); - let multiname = self.pool_multiname(index)?; - let object = self.pop().as_object()?; - let base_proto: Result, Error> = self - .current_stack_frame() - .unwrap() - .read() - .base_proto() - .and_then(|p| p.proto()) - .ok_or_else(|| "Attempted to get property on non-existent super object".into()); - let base_proto = base_proto?; - let mut base = base_proto.construct(self, context, &[])?; //TODO: very hacky workaround - - let name: Result = base.resolve_multiname(&multiname)?.ok_or_else(|| { - format!( - "Could not resolve {:?} as super property", - multiname.local_name() - ) - .into() - }); - - base.set_property(object, &name?, value, self, context)?; - - Ok(()) - } - - fn op_push_scope(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) -> Result<(), Error> { - let object = self.pop().as_object()?; - let activation = self.current_stack_frame().unwrap(); - let mut write = activation.write(context.gc_context); - let scope_stack = write.scope(); - let new_scope = Scope::push_scope(scope_stack, object, context.gc_context); - - write.set_scope(Some(new_scope)); - - Ok(()) - } - - fn op_push_with(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) -> Result<(), Error> { - let object = self.pop().as_object()?; - let activation = self.current_stack_frame().unwrap(); - let mut write = activation.write(context.gc_context); - let scope_stack = write.scope(); - let new_scope = Scope::push_with(scope_stack, object, context.gc_context); - - write.set_scope(Some(new_scope)); - - Ok(()) - } - - fn op_pop_scope(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) -> Result<(), Error> { - let activation = self.current_stack_frame().unwrap(); - let mut write = activation.write(context.gc_context); - let scope_stack = write.scope(); - let new_scope = scope_stack.and_then(|s| s.read().pop_scope()); - - write.set_scope(new_scope); - - Ok(()) - } - - fn op_get_scope_object(&mut self, mut index: u8) -> Result<(), Error> { - let mut scope = self.current_stack_frame().unwrap().read().scope(); - - while index > 0 { - if let Some(child_scope) = scope { - scope = child_scope.read().parent_cell(); - } - - index -= 1; - } - - self.push( - scope - .map(|s| s.read().locals().clone().into()) - .unwrap_or(Value::Undefined), - ); - - Ok(()) - } - - fn op_get_global_scope(&mut self) -> Result<(), Error> { - let mut scope = self.current_stack_frame().unwrap().read().scope(); - - while let Some(this_scope) = scope { - let parent = this_scope.read().parent_cell(); - if parent.is_none() { - break; - } - - scope = parent; - } - - self.push( - scope - .map(|s| s.read().locals().clone().into()) - .unwrap_or(Value::Undefined), - ); - - Ok(()) - } - - fn op_find_property( - &mut self, - context: &mut UpdateContext<'_, 'gc, '_>, - index: Index, - ) -> Result<(), Error> { - let multiname = self.pool_multiname(index)?; - avm_debug!("Resolving {:?}", multiname); - let result = if let Some(scope) = self.current_stack_frame().unwrap().read().scope() { - scope.read().find(&multiname, self, context)? - } else { - None - }; - - self.push(result.map(|o| o.into()).unwrap_or(Value::Undefined)); - - Ok(()) - } - - fn op_find_prop_strict( - &mut self, - context: &mut UpdateContext<'_, 'gc, '_>, - index: Index, - ) -> Result<(), Error> { - let multiname = self.pool_multiname(index)?; - avm_debug!("Resolving {:?}", multiname); - let found: Result, Error> = - if let Some(scope) = self.current_stack_frame().unwrap().read().scope() { - scope.read().find(&multiname, self, context)? - } else { - None - } - .ok_or_else(|| format!("Property does not exist: {:?}", multiname.local_name()).into()); - let result: Value<'gc> = found?.into(); - - self.push(result); - - Ok(()) - } - - fn op_get_lex( - &mut self, - context: &mut UpdateContext<'_, 'gc, '_>, - index: Index, - ) -> Result<(), Error> { - let multiname = self.pool_multiname_static(index)?; - avm_debug!("Resolving {:?}", multiname); - let found: Result, Error> = - if let Some(scope) = self.current_stack_frame().unwrap().read().scope() { - scope - .write(context.gc_context) - .resolve(&multiname, self, context)? - } else { - None - } - .ok_or_else(|| format!("Property does not exist: {:?}", multiname.local_name()).into()); - let result: Value<'gc> = found?.resolve(self, context)?; - - self.push(result); - - Ok(()) - } - - fn op_get_slot(&mut self, index: u32) -> Result<(), Error> { - let object = self.pop().as_object()?; - let value = object.get_slot(index)?; - - self.push(value); - - Ok(()) - } - - fn op_set_slot( - &mut self, - context: &mut UpdateContext<'_, 'gc, '_>, - index: u32, - ) -> Result<(), Error> { - let object = self.pop().as_object()?; - let value = self.pop(); - - object.set_slot(index, value, context.gc_context) - } - - fn op_get_global_slot(&mut self, index: u32) -> Result<(), Error> { - let value = self.globals.get_slot(index)?; - - self.push(value); - - Ok(()) - } - - fn op_set_global_slot( - &mut self, - context: &mut UpdateContext<'_, 'gc, '_>, - index: u32, - ) -> Result<(), Error> { - let value = self.pop(); - - self.globals.set_slot(index, value, context.gc_context) - } - - fn op_construct( - &mut self, - context: &mut UpdateContext<'_, 'gc, '_>, - arg_count: u32, - ) -> Result<(), Error> { - let args = self.pop_args(arg_count); - let mut ctor = self.pop().as_object()?; - - let proto = ctor - .get_property( - ctor, - &QName::new(Namespace::public_namespace(), "prototype"), - self, - context, - )? - .as_object()?; - - let object = proto.construct(self, context, &args)?; - ctor.call(Some(object), &args, self, context, object.proto())?; - - self.push(object); - - Ok(()) - } - - fn op_construct_prop( - &mut self, - context: &mut UpdateContext<'_, 'gc, '_>, - index: Index, - arg_count: u32, - ) -> Result<(), Error> { - let args = self.pop_args(arg_count); - let multiname = self.pool_multiname(index)?; - let mut source = self.pop().as_object()?; - - let ctor_name: Result = - source.resolve_multiname(&multiname)?.ok_or_else(|| { - format!("Could not resolve property {:?}", multiname.local_name()).into() - }); - let mut ctor = source - .get_property(source, &ctor_name?, self, context)? - .as_object()?; - let proto = ctor - .get_property( - ctor, - &QName::new(Namespace::public_namespace(), "prototype"), - self, - context, - )? - .as_object()?; - - let object = proto.construct(self, context, &args)?; - ctor.call(Some(object), &args, self, context, Some(proto))?; - - self.push(object); - - Ok(()) - } - - fn op_construct_super( - &mut self, - context: &mut UpdateContext<'_, 'gc, '_>, - arg_count: u32, - ) -> Result<(), Error> { - let args = self.pop_args(arg_count); - let receiver = self.pop().as_object()?; - let name = QName::new(Namespace::public_namespace(), "constructor"); - let base_proto: Result, Error> = self - .current_stack_frame() - .unwrap() - .read() - .base_proto() - .and_then(|p| p.proto()) - .ok_or_else(|| { - "Attempted to call super constructor without a superclass." - .to_string() - .into() - }); - let mut base_proto = base_proto?; - - let function = base_proto - .get_property(receiver, &name, self, context)? - .as_object()?; - - function.call(Some(receiver), &args, self, context, Some(base_proto))?; - - Ok(()) - } - - fn op_new_activation(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) -> Result<(), Error> { - self.push(ScriptObject::bare_object(context.gc_context)); - - Ok(()) - } - - fn op_new_object( - &mut self, - context: &mut UpdateContext<'_, 'gc, '_>, - num_args: u32, - ) -> Result<(), Error> { - let mut object = ScriptObject::object(context.gc_context, self.system_prototypes.object); - - for _ in 0..num_args { - let value = self.pop(); - let name = self.pop(); - - object.set_property( - object, - &QName::new(Namespace::public_namespace(), name.as_string()?), - value, - self, - context, - )?; - } - - self.push(object); - - Ok(()) - } - - fn op_new_function( - &mut self, - context: &mut UpdateContext<'_, 'gc, '_>, - index: Index, - ) -> Result<(), Error> { - let method_entry = self.table_method(index)?; - let scope = self.current_stack_frame().unwrap().read().scope(); - - let mut new_fn = FunctionObject::from_method( - context.gc_context, - method_entry.into(), - scope, - self.system_prototypes.function, - None, - ); - let es3_proto = ScriptObject::object(context.gc_context, self.prototypes().object); - - new_fn.install_slot( - context.gc_context, - QName::new(Namespace::public_namespace(), "prototype"), - 0, - es3_proto.into(), - ); - - self.push(new_fn); - - Ok(()) - } - - fn op_new_class( - &mut self, - context: &mut UpdateContext<'_, 'gc, '_>, - index: Index, - ) -> Result<(), Error> { - let base_class = self.pop().as_object()?; - let class_entry = self.table_class(index, context)?; - let scope = self.current_stack_frame().unwrap().read().scope(); - - let (new_class, class_init) = - FunctionObject::from_class(self, context, class_entry, base_class, scope)?; - - class_init.call(Some(new_class), &[], self, context, None)?; - - self.push(new_class); - - Ok(()) - } - - fn op_coerce_a(&mut self) -> Result<(), Error> { - Ok(()) - } - - fn op_jump(&mut self, offset: i32, reader: &mut Reader>) -> Result<(), Error> { - reader.seek(offset as i64)?; - - Ok(()) - } - - fn op_if_true(&mut self, offset: i32, reader: &mut Reader>) -> Result<(), Error> { - let value = self.pop().as_bool()?; - - if value { - reader.seek(offset as i64)?; - } - - Ok(()) - } - - fn op_if_false( - &mut self, - offset: i32, - reader: &mut Reader>, - ) -> Result<(), Error> { - let value = self.pop().as_bool()?; - - if !value { - reader.seek(offset as i64)?; - } - - Ok(()) - } - - fn op_if_strict_eq( - &mut self, - offset: i32, - reader: &mut Reader>, - ) -> Result<(), Error> { - let value2 = self.pop(); - let value1 = self.pop(); - - if value1 == value2 { - reader.seek(offset as i64)?; - } - - Ok(()) - } - - fn op_if_strict_ne( - &mut self, - offset: i32, - reader: &mut Reader>, - ) -> Result<(), Error> { - let value2 = self.pop(); - let value1 = self.pop(); - - if value1 != value2 { - reader.seek(offset as i64)?; - } - - Ok(()) - } - - fn op_strict_equals(&mut self) -> Result<(), Error> { - let value2 = self.pop(); - let value1 = self.pop(); - - self.push(value1 == value2); - - Ok(()) - } - - fn op_has_next(&mut self) -> Result<(), Error> { - //TODO: After adding ints, change this to ints. - let cur_index = self.pop().as_number()?; - let object = self.pop().as_object()?; - - let next_index = cur_index as u32 + 1; - - if object.get_enumerant_name(next_index).is_some() { - self.push(next_index as f32); - } else { - self.push(0.0); - } - - Ok(()) - } - - fn op_has_next_2( - &mut self, - context: &mut UpdateContext<'_, 'gc, '_>, - object_register: u32, - index_register: u32, - ) -> Result<(), Error> { - //TODO: After adding ints, change this to ints. - let cur_index = self.register_value(index_register)?.as_number()?; - let mut object = Some(self.register_value(object_register)?.as_object()?); - - let mut next_index = cur_index as u32 + 1; - - while let Some(cur_object) = object { - if cur_object.get_enumerant_name(next_index).is_none() { - next_index = 1; - object = cur_object.proto(); - } else { - break; - } - } - - if object.is_none() { - next_index = 0; - } - - self.push(next_index != 0); - self.set_register_value(context, index_register, next_index)?; - self.set_register_value( - context, - object_register, - object.map(|v| v.into()).unwrap_or(Value::Null), - )?; - - Ok(()) - } - - fn op_next_name(&mut self) -> Result<(), Error> { - //TODO: After adding ints, change this to ints. - let cur_index = self.pop().as_number()?; - let object = self.pop().as_object()?; - - let name = object - .get_enumerant_name(cur_index as u32) - .map(|n| n.local_name().into()); - - self.push(name.unwrap_or(Value::Undefined)); - - Ok(()) - } - - fn op_next_value(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) -> Result<(), Error> { - //TODO: After adding ints, change this to ints. - let cur_index = self.pop().as_number()?; - let mut object = self.pop().as_object()?; - - let name = object.get_enumerant_name(cur_index as u32); - let value = if let Some(name) = name { - object.get_property(object, &name, self, context)? - } else { - Value::Undefined - }; - - self.push(value); - - Ok(()) - } - - #[allow(unused_variables)] - fn op_debug( - &mut self, - is_local_register: bool, - register_name: Index, - register: u8, - ) -> Result<(), Error> { - if is_local_register { - let register_name = self.pool_string(register_name)?; - let value = self.register_value(register as u32)?; - - avm_debug!("Debug: {} = {:?}", register_name, value); - } else { - avm_debug!("Unknown debugging mode!"); - } - - Ok(()) - } - - #[allow(unused_variables)] - fn op_debug_file(&mut self, file_name: Index) -> Result<(), Error> { - let file_name = self.pool_string(file_name)?; - - avm_debug!("File: {}", file_name); - - Ok(()) - } - - #[allow(unused_variables)] - fn op_debug_line(&mut self, line_num: u32) -> Result<(), Error> { - avm_debug!("Line: {}", line_num); - - Ok(()) - } } diff --git a/core/src/avm2/activation.rs b/core/src/avm2/activation.rs index 4cf6016c9..d6a362edc 100644 --- a/core/src/avm2/activation.rs +++ b/core/src/avm2/activation.rs @@ -1,15 +1,24 @@ //! Activation frames +use crate::avm2::class::Class; +use crate::avm2::function::FunctionObject; use crate::avm2::method::BytecodeMethod; -use crate::avm2::object::Object; +use crate::avm2::names::{Multiname, Namespace, QName}; +use crate::avm2::object::{Object, TObject}; use crate::avm2::scope::Scope; use crate::avm2::script::Script; use crate::avm2::script_object::ScriptObject; use crate::avm2::value::Value; -use crate::avm2::Error; +use crate::avm2::{value, Avm2, Error}; use crate::context::UpdateContext; use gc_arena::{Collect, GcCell, MutationContext}; use smallvec::SmallVec; +use std::io::Cursor; +use swf::avm2::read::Reader; +use swf::avm2::types::{ + Class as AbcClass, Index, Method as AbcMethod, Multiname as AbcMultiname, + Namespace as AbcNamespace, Op, +}; /// Represents a particular register set. /// @@ -45,15 +54,18 @@ impl<'gc> RegisterSet<'gc> { } } -/// Represents a single activation of a given AVM2 function or keyframe. -#[derive(Clone, Collect)] -#[collect(no_drop)] -pub struct Activation<'gc> { - /// The AVM method entry we're executing code out of. - method: BytecodeMethod<'gc>, +#[derive(Debug, Clone)] +enum FrameControl<'gc> { + Continue, + Return(Value<'gc>), +} - /// The current location of the instruction stream being executed. - pc: usize, +/// Represents a single activation of a given AVM2 function or keyframe. +#[derive(Collect)] +#[collect(no_drop)] +pub struct Activation<'a, 'gc: 'a> { + /// The AVM2 instance we execute under. + avm2: &'a mut Avm2<'gc>, /// The immutable value of `this`. this: Option>, @@ -92,8 +104,35 @@ pub struct Activation<'gc> { base_proto: Option>, } -impl<'gc> Activation<'gc> { +impl<'a, 'gc: 'a> Activation<'a, 'gc> { + /// Construct an activation that does not represent any particular scope. + /// + /// This exists primarily for non-AVM2 related manipulations of the + /// interpreter environment that require an activation. For example, + /// loading traits into an object, or running tests. + /// + /// It is a logic error to attempt to run AVM2 code in a nothing + /// `Activation`. + pub fn from_nothing(avm2: &'a mut Avm2<'gc>, context: &mut UpdateContext<'_, 'gc, '_>) -> Self { + let local_registers = GcCell::allocate(context.gc_context, RegisterSet::new(0)); + + Self { + avm2, + this: None, + arguments: None, + is_executing: false, + local_registers, + return_value: None, + local_scope: ScriptObject::bare_object(context.gc_context), + scope: None, + base_proto: None, + } + } + + /// Construct an activation for the execution of a particular script's + /// initializer method. pub fn from_script( + avm2: &'a mut Avm2<'gc>, context: &mut UpdateContext<'_, 'gc, '_>, script: GcCell<'gc, Script<'gc>>, global: Object<'gc>, @@ -110,8 +149,7 @@ impl<'gc> Activation<'gc> { .unwrap() = global.into(); Ok(Self { - method, - pc: 0, + avm2, this: Some(global), arguments: None, is_executing: false, @@ -123,14 +161,17 @@ impl<'gc> Activation<'gc> { }) } - pub fn from_action( + /// Construct an activation for the execution of a particular bytecode + /// method. + pub fn from_method( + avm2: &'a mut Avm2<'gc>, context: &mut UpdateContext<'_, 'gc, '_>, method: BytecodeMethod<'gc>, scope: Option>>, this: Option>, arguments: &[Value<'gc>], base_proto: Option>, - ) -> Result { + ) -> Self { let num_locals = method.body().num_locals; let num_declared_arguments = method.method().params.len() as u32; let local_registers = GcCell::allocate( @@ -150,9 +191,8 @@ impl<'gc> Activation<'gc> { } } - Ok(Self { - method, - pc: 0, + Self { + avm2, this, arguments: None, is_executing: false, @@ -161,7 +201,20 @@ impl<'gc> Activation<'gc> { local_scope: ScriptObject::bare_object(context.gc_context), scope, base_proto, - }) + } + } + + /// Execute a script initializer. + pub fn run_stack_frame_for_script( + &mut self, + context: &mut UpdateContext<'_, 'gc, '_>, + script: GcCell<'gc, Script<'gc>>, + ) -> Result<(), Error> { + let init = script.read().init().into_entry()?; + + self.run_actions(init, context)?; + + Ok(()) } /// Attempts to lock the activation frame for execution. @@ -183,24 +236,13 @@ impl<'gc> Activation<'gc> { self.is_executing = false; } - /// Obtain a reference to the method being executed. - pub fn method(&self) -> &BytecodeMethod<'gc> { - &self.method - } - - /// Get the PC value. - pub fn pc(&self) -> usize { - self.pc - } - - /// Set the PC value. - pub fn set_pc(&mut self, new_pc: usize) { - self.pc = new_pc; - } - /// Retrieve a local register. - pub fn local_register(&self, id: u32) -> Option> { - self.local_registers.read().get(id).cloned() + pub fn local_register(&self, id: u32) -> Result, Error> { + self.local_registers + .read() + .get(id) + .cloned() + .ok_or_else(|| format!("Out of bounds register read: {}", id).into()) } /// Get the current scope stack. @@ -221,16 +263,20 @@ impl<'gc> Activation<'gc> { id: u32, value: impl Into>, mc: MutationContext<'gc, '_>, - ) -> bool { + ) -> Result<(), Error> { if let Some(r) = self.local_registers.write(mc).get_mut(id) { *r = value.into(); - true + Ok(()) } else { - false + Err(format!("Out of bounds register write: {}", id).into()) } } + pub fn avm2(&mut self) -> &mut Avm2<'gc> { + self.avm2 + } + /// Set the return value. pub fn set_return_value(&mut self, value: Value<'gc>) { self.return_value = Some(value); @@ -241,4 +287,1254 @@ impl<'gc> Activation<'gc> { pub fn base_proto(&self) -> Option> { self.base_proto } + + /// Retrieve a int from the current constant pool. + fn pool_int(&self, method: BytecodeMethod<'gc>, index: Index) -> Result { + value::abc_int(&method.abc(), index) + } + + /// Retrieve a int from the current constant pool. + fn pool_uint(&self, method: BytecodeMethod<'gc>, index: Index) -> Result { + value::abc_uint(&method.abc(), index) + } + + /// Retrieve a double from the current constant pool. + fn pool_double(&self, method: BytecodeMethod<'gc>, index: Index) -> Result { + value::abc_double(&method.abc(), index) + } + + /// Retrieve a string from the current constant pool. + fn pool_string( + &self, + method: BytecodeMethod<'gc>, + index: Index, + ) -> Result { + value::abc_string(&method.abc(), index) + } + + /// Retrieve a namespace from the current constant pool. + fn pool_namespace( + &self, + method: BytecodeMethod<'gc>, + index: Index, + ) -> Result { + Namespace::from_abc_namespace(&method.abc(), index) + } + + /// Retrieve a multiname from the current constant pool. + fn pool_multiname( + &mut self, + method: BytecodeMethod<'gc>, + index: Index, + ) -> Result { + Multiname::from_abc_multiname(&method.abc(), index, self.avm2) + } + + /// Retrieve a static, or non-runtime, multiname from the current constant + /// pool. + fn pool_multiname_static( + &mut self, + method: BytecodeMethod<'gc>, + index: Index, + ) -> Result { + Multiname::from_abc_multiname_static(&method.abc(), index) + } + + /// Retrieve a method entry from the current ABC file's method table. + fn table_method( + &mut self, + method: BytecodeMethod<'gc>, + index: Index, + ) -> Result, Error> { + BytecodeMethod::from_method_index(method.translation_unit(), index.clone()) + .ok_or_else(|| format!("Method index {} does not exist", index.0).into()) + } + + /// Retrieve a class entry from the current ABC file's method table. + fn table_class( + &mut self, + method: BytecodeMethod<'gc>, + index: Index, + context: &mut UpdateContext<'_, 'gc, '_>, + ) -> Result>, Error> { + method + .translation_unit() + .load_class(index.0, context.gc_context) + } + + pub fn run_actions( + &mut self, + method: BytecodeMethod<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + ) -> Result, Error> { + let clone_method = method.clone(); + let mut read = Reader::new(Cursor::new(clone_method.body().code.as_ref())); + + loop { + let result = self.do_next_opcode(method.clone(), context, &mut read); + match result { + Ok(FrameControl::Return(value)) => break Ok(value), + Ok(FrameControl::Continue) => {} + Err(e) => break Err(e), + } + } + } + + /// Run a single action from a given action reader. + fn do_next_opcode( + &mut self, + method: BytecodeMethod<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + reader: &mut Reader>, + ) -> Result, Error> { + let op = reader.read_op(); + if let Ok(Some(op)) = op { + avm_debug!("Opcode: {:?}", op); + + let result = match op { + Op::PushByte { value } => self.op_push_byte(value), + Op::PushDouble { value } => self.op_push_double(method, value), + Op::PushFalse => self.op_push_false(), + Op::PushInt { value } => self.op_push_int(method, value), + Op::PushNamespace { value } => self.op_push_namespace(method, 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(method, value), + Op::PushTrue => self.op_push_true(), + Op::PushUint { value } => self.op_push_uint(method, value), + Op::PushUndefined => self.op_push_undefined(), + Op::Pop => self.op_pop(), + Op::Dup => self.op_dup(), + Op::GetLocal { index } => self.op_get_local(index), + Op::SetLocal { index } => self.op_set_local(context, index), + Op::Kill { index } => self.op_kill(context, index), + Op::Call { num_args } => self.op_call(context, num_args), + Op::CallMethod { index, num_args } => self.op_call_method(context, index, num_args), + Op::CallProperty { index, num_args } => { + self.op_call_property(method, context, index, num_args) + } + Op::CallPropLex { index, num_args } => { + self.op_call_prop_lex(method, context, index, num_args) + } + Op::CallPropVoid { index, num_args } => { + self.op_call_prop_void(method, context, index, num_args) + } + Op::CallStatic { index, num_args } => { + self.op_call_static(method, context, index, num_args) + } + Op::CallSuper { index, num_args } => { + self.op_call_super(method, context, index, num_args) + } + Op::CallSuperVoid { index, num_args } => { + self.op_call_super_void(method, context, index, num_args) + } + Op::ReturnValue => self.op_return_value(), + Op::ReturnVoid => self.op_return_void(), + Op::GetProperty { index } => self.op_get_property(method, context, index), + Op::SetProperty { index } => self.op_set_property(method, context, index), + Op::InitProperty { index } => self.op_init_property(method, context, index), + Op::DeleteProperty { index } => self.op_delete_property(method, context, index), + Op::GetSuper { index } => self.op_get_super(method, context, index), + Op::SetSuper { index } => self.op_set_super(method, context, index), + Op::PushScope => self.op_push_scope(context), + Op::PushWith => self.op_push_with(context), + Op::PopScope => self.op_pop_scope(), + Op::GetScopeObject { index } => self.op_get_scope_object(index), + Op::GetGlobalScope => self.op_get_global_scope(), + Op::FindProperty { index } => self.op_find_property(method, context, index), + Op::FindPropStrict { index } => self.op_find_prop_strict(method, context, index), + Op::GetLex { index } => self.op_get_lex(method, context, index), + Op::GetSlot { index } => self.op_get_slot(index), + Op::SetSlot { index } => self.op_set_slot(context, index), + Op::GetGlobalSlot { index } => self.op_get_global_slot(index), + Op::SetGlobalSlot { index } => self.op_set_global_slot(context, index), + Op::Construct { num_args } => self.op_construct(context, num_args), + Op::ConstructProp { index, num_args } => { + self.op_construct_prop(method, context, index, num_args) + } + Op::ConstructSuper { num_args } => self.op_construct_super(context, num_args), + Op::NewActivation => self.op_new_activation(context), + Op::NewObject { num_args } => self.op_new_object(context, num_args), + Op::NewFunction { index } => self.op_new_function(method, context, index), + Op::NewClass { index } => self.op_new_class(method, context, index), + Op::CoerceA => self.op_coerce_a(), + Op::Jump { offset } => self.op_jump(offset, reader), + Op::IfTrue { offset } => self.op_if_true(offset, reader), + Op::IfFalse { offset } => self.op_if_false(offset, reader), + Op::IfStrictEq { offset } => self.op_if_strict_eq(offset, reader), + Op::IfStrictNe { offset } => self.op_if_strict_ne(offset, reader), + Op::StrictEquals => self.op_strict_equals(), + Op::HasNext => self.op_has_next(), + Op::HasNext2 { + object_register, + index_register, + } => self.op_has_next_2(context, object_register, index_register), + Op::NextName => self.op_next_name(), + Op::NextValue => self.op_next_value(context), + Op::Label => Ok(FrameControl::Continue), + Op::Debug { + is_local_register, + register_name, + register, + } => self.op_debug(method, is_local_register, register_name, register), + Op::DebugFile { file_name } => self.op_debug_file(method, file_name), + Op::DebugLine { line_num } => self.op_debug_line(line_num), + _ => self.unknown_op(op), + }; + + if let Err(e) = result { + log::error!("AVM2 error: {}", e); + return Err(e); + } + result + } else if let Ok(None) = op { + log::error!("Unknown opcode!"); + Err("Unknown opcode!".into()) + } else if let Err(e) = op { + log::error!("Parse error: {:?}", e); + Err(e.into()) + } else { + unreachable!(); + } + } + + 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.avm2.push(value); + Ok(FrameControl::Continue) + } + + fn op_push_double( + &mut self, + method: BytecodeMethod<'gc>, + value: Index, + ) -> Result, Error> { + self.avm2.push(self.pool_double(method, value)?); + Ok(FrameControl::Continue) + } + + fn op_push_false(&mut self) -> Result, Error> { + self.avm2.push(false); + Ok(FrameControl::Continue) + } + + fn op_push_int( + &mut self, + method: BytecodeMethod<'gc>, + value: Index, + ) -> Result, Error> { + self.avm2.push(self.pool_int(method, value)?); + Ok(FrameControl::Continue) + } + + fn op_push_namespace( + &mut self, + method: BytecodeMethod<'gc>, + value: Index, + ) -> Result, Error> { + self.avm2.push(self.pool_namespace(method, value)?); + Ok(FrameControl::Continue) + } + + fn op_push_nan(&mut self) -> Result, Error> { + self.avm2.push(std::f64::NAN); + Ok(FrameControl::Continue) + } + + fn op_push_null(&mut self) -> Result, Error> { + self.avm2.push(Value::Null); + Ok(FrameControl::Continue) + } + + fn op_push_short(&mut self, value: u32) -> Result, Error> { + self.avm2.push(value); + Ok(FrameControl::Continue) + } + + fn op_push_string( + &mut self, + method: BytecodeMethod<'gc>, + value: Index, + ) -> Result, Error> { + self.avm2.push(self.pool_string(method, value)?); + Ok(FrameControl::Continue) + } + + fn op_push_true(&mut self) -> Result, Error> { + self.avm2.push(true); + Ok(FrameControl::Continue) + } + + fn op_push_uint( + &mut self, + method: BytecodeMethod<'gc>, + value: Index, + ) -> Result, Error> { + self.avm2.push(self.pool_uint(method, value)?); + Ok(FrameControl::Continue) + } + + fn op_push_undefined(&mut self) -> Result, Error> { + self.avm2.push(Value::Undefined); + Ok(FrameControl::Continue) + } + + fn op_pop(&mut self) -> Result, Error> { + self.avm2.pop(); + + Ok(FrameControl::Continue) + } + + fn op_dup(&mut self) -> Result, Error> { + self.avm2 + .push(self.avm2.stack.last().cloned().unwrap_or(Value::Undefined)); + + Ok(FrameControl::Continue) + } + + fn op_get_local(&mut self, register_index: u32) -> Result, Error> { + self.avm2.push(self.local_register(register_index)?); + Ok(FrameControl::Continue) + } + + fn op_set_local( + &mut self, + context: &mut UpdateContext<'_, 'gc, '_>, + register_index: u32, + ) -> Result, Error> { + let value = self.avm2.pop(); + + self.set_local_register(register_index, value, context.gc_context)?; + + Ok(FrameControl::Continue) + } + + fn op_kill( + &mut self, + context: &mut UpdateContext<'_, 'gc, '_>, + register_index: u32, + ) -> Result, Error> { + self.set_local_register(register_index, Value::Undefined, context.gc_context)?; + + Ok(FrameControl::Continue) + } + + fn op_call( + &mut self, + context: &mut UpdateContext<'_, 'gc, '_>, + arg_count: u32, + ) -> Result, Error> { + let args = self.avm2.pop_args(arg_count); + let receiver = self.avm2.pop().as_object().ok(); + let function = self.avm2.pop().as_object()?; + let base_proto = receiver.and_then(|r| r.proto()); + let value = function.call(receiver, &args, self, context, base_proto)?; + + self.avm2.push(value); + + Ok(FrameControl::Continue) + } + + fn op_call_method( + &mut self, + context: &mut UpdateContext<'_, 'gc, '_>, + index: Index, + arg_count: u32, + ) -> Result, Error> { + let args = self.avm2.pop_args(arg_count); + let receiver = self.avm2.pop().as_object()?; + let function: Result, Error> = receiver + .get_method(index.0) + .ok_or_else(|| format!("Object method {} does not exist", index.0).into()); + let base_proto = receiver.proto(); + let value = function?.call(Some(receiver), &args, self, context, base_proto)?; + + self.avm2.push(value); + + Ok(FrameControl::Continue) + } + + fn op_call_property( + &mut self, + method: BytecodeMethod<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + index: Index, + arg_count: u32, + ) -> Result, Error> { + let args = self.avm2.pop_args(arg_count); + let multiname = self.pool_multiname(method, index)?; + let mut receiver = self.avm2.pop().as_object()?; + let name: Result = receiver + .resolve_multiname(&multiname)? + .ok_or_else(|| format!("Could not find method {:?}", multiname.local_name()).into()); + let name = name?; + let base_proto = receiver.get_base_proto(&name)?; + let function = receiver + .get_property(receiver, &name, self, context)? + .as_object()?; + let value = function.call(Some(receiver), &args, self, context, base_proto)?; + + self.avm2.push(value); + + Ok(FrameControl::Continue) + } + + fn op_call_prop_lex( + &mut self, + method: BytecodeMethod<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + index: Index, + arg_count: u32, + ) -> Result, Error> { + let args = self.avm2.pop_args(arg_count); + let multiname = self.pool_multiname(method, index)?; + let mut receiver = self.avm2.pop().as_object()?; + let name: Result = receiver + .resolve_multiname(&multiname)? + .ok_or_else(|| format!("Could not find method {:?}", multiname.local_name()).into()); + let function = receiver + .get_property(receiver, &name?, self, context)? + .as_object()?; + let value = function.call(None, &args, self, context, None)?; + + self.avm2.push(value); + + Ok(FrameControl::Continue) + } + + fn op_call_prop_void( + &mut self, + method: BytecodeMethod<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + index: Index, + arg_count: u32, + ) -> Result, Error> { + let args = self.avm2.pop_args(arg_count); + let multiname = self.pool_multiname(method, index)?; + let mut receiver = self.avm2.pop().as_object()?; + let name: Result = receiver + .resolve_multiname(&multiname)? + .ok_or_else(|| format!("Could not find method {:?}", multiname.local_name()).into()); + let name = name?; + let base_proto = receiver.get_base_proto(&name)?; + let function = receiver + .get_property(receiver, &name, self, context)? + .as_object()?; + + function.call(Some(receiver), &args, self, context, base_proto)?; + + Ok(FrameControl::Continue) + } + + fn op_call_static( + &mut self, + method: BytecodeMethod<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + index: Index, + arg_count: u32, + ) -> Result, Error> { + let args = self.avm2.pop_args(arg_count); + let receiver = self.avm2.pop().as_object()?; + let method = self.table_method(method, index)?; + let scope = self.scope(); //TODO: Is this correct? + let function = FunctionObject::from_method( + context.gc_context, + method.into(), + scope, + self.avm2.prototypes().function, + None, + ); + let value = function.call(Some(receiver), &args, self, context, receiver.proto())?; + + self.avm2.push(value); + + Ok(FrameControl::Continue) + } + + fn op_call_super( + &mut self, + method: BytecodeMethod<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + index: Index, + arg_count: u32, + ) -> Result, Error> { + let args = self.avm2.pop_args(arg_count); + let multiname = self.pool_multiname(method, index)?; + let receiver = self.avm2.pop().as_object()?; + let name: Result = receiver + .resolve_multiname(&multiname)? + .ok_or_else(|| format!("Could not find method {:?}", multiname.local_name()).into()); + let base_proto: Result, Error> = + self.base_proto().and_then(|bp| bp.proto()).ok_or_else(|| { + "Attempted to call super method without a superclass." + .to_string() + .into() + }); + let base_proto = base_proto?; + let mut base = base_proto.construct(self, context, &[])?; //TODO: very hacky workaround + + let function = base + .get_property(receiver, &name?, self, context)? + .as_object()?; + + let value = function.call(Some(receiver), &args, self, context, Some(base_proto))?; + + self.avm2.push(value); + + Ok(FrameControl::Continue) + } + + fn op_call_super_void( + &mut self, + method: BytecodeMethod<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + index: Index, + arg_count: u32, + ) -> Result, Error> { + let args = self.avm2.pop_args(arg_count); + let multiname = self.pool_multiname(method, index)?; + let receiver = self.avm2.pop().as_object()?; + let name: Result = receiver + .resolve_multiname(&multiname)? + .ok_or_else(|| format!("Could not find method {:?}", multiname.local_name()).into()); + let base_proto: Result, Error> = + self.base_proto().and_then(|bp| bp.proto()).ok_or_else(|| { + "Attempted to call super method without a superclass." + .to_string() + .into() + }); + let base_proto = base_proto?; + let mut base = base_proto.construct(self, context, &[])?; //TODO: very hacky workaround + + let function = base + .get_property(receiver, &name?, self, context)? + .as_object()?; + + function.call(Some(receiver), &args, self, context, Some(base_proto))?; + + Ok(FrameControl::Continue) + } + + fn op_return_value(&mut self) -> Result, Error> { + let return_value = self.avm2.pop(); + + Ok(FrameControl::Return(return_value)) + } + + fn op_return_void(&mut self) -> Result, Error> { + Ok(FrameControl::Return(Value::Undefined)) + } + + fn op_get_property( + &mut self, + method: BytecodeMethod<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + index: Index, + ) -> Result, Error> { + let multiname = self.pool_multiname(method, index)?; + let mut object = self.avm2.pop().as_object()?; + + let name: Result = object.resolve_multiname(&multiname)?.ok_or_else(|| { + format!("Could not resolve property {:?}", multiname.local_name()).into() + }); + + let value = object.get_property(object, &name?, self, context)?; + self.avm2.push(value); + + Ok(FrameControl::Continue) + } + + fn op_set_property( + &mut self, + method: BytecodeMethod<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + index: Index, + ) -> Result, Error> { + let value = self.avm2.pop(); + let multiname = self.pool_multiname(method, index)?; + let mut object = self.avm2.pop().as_object()?; + + if let Some(name) = object.resolve_multiname(&multiname)? { + object.set_property(object, &name, value, self, context)?; + } else { + //TODO: Non-dynamic objects should fail + //TODO: This should only work if the public namespace is present + let local_name: Result<&str, Error> = multiname + .local_name() + .ok_or_else(|| "Cannot set property using any name".into()); + let name = QName::dynamic_name(local_name?); + object.set_property(object, &name, value, self, context)?; + } + + Ok(FrameControl::Continue) + } + + fn op_init_property( + &mut self, + method: BytecodeMethod<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + index: Index, + ) -> Result, Error> { + let value = self.avm2.pop(); + let multiname = self.pool_multiname(method, index)?; + let mut object = self.avm2.pop().as_object()?; + + if let Some(name) = object.resolve_multiname(&multiname)? { + object.init_property(object, &name, value, self, context)?; + } else { + //TODO: Non-dynamic objects should fail + //TODO: This should only work if the public namespace is present + let local_name: Result<&str, Error> = multiname + .local_name() + .ok_or_else(|| "Cannot set property using any name".into()); + let name = QName::dynamic_name(local_name?); + object.init_property(object, &name, value, self, context)?; + } + + Ok(FrameControl::Continue) + } + + fn op_delete_property( + &mut self, + method: BytecodeMethod<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + index: Index, + ) -> Result, Error> { + let multiname = self.pool_multiname(method, index)?; + let object = self.avm2.pop().as_object()?; + + if let Some(name) = object.resolve_multiname(&multiname)? { + self.avm2 + .push(object.delete_property(context.gc_context, &name)) + } else { + self.avm2.push(false) + } + + Ok(FrameControl::Continue) + } + + fn op_get_super( + &mut self, + method: BytecodeMethod<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + index: Index, + ) -> Result, Error> { + let multiname = self.pool_multiname(method, index)?; + let object = self.avm2.pop().as_object()?; + let base_proto: Result, Error> = self + .base_proto() + .and_then(|p| p.proto()) + .ok_or_else(|| "Attempted to get property on non-existent super object".into()); + let base_proto = base_proto?; + let mut base = base_proto.construct(self, context, &[])?; //TODO: very hacky workaround + + let name: Result = base.resolve_multiname(&multiname)?.ok_or_else(|| { + format!( + "Could not resolve {:?} as super property", + multiname.local_name() + ) + .into() + }); + + let value = base.get_property(object, &name?, self, context)?; + + self.avm2.push(value); + + Ok(FrameControl::Continue) + } + + fn op_set_super( + &mut self, + method: BytecodeMethod<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + index: Index, + ) -> Result, Error> { + let value = self.avm2.pop(); + let multiname = self.pool_multiname(method, index)?; + let object = self.avm2.pop().as_object()?; + let base_proto: Result, Error> = self + .base_proto() + .and_then(|p| p.proto()) + .ok_or_else(|| "Attempted to get property on non-existent super object".into()); + let base_proto = base_proto?; + let mut base = base_proto.construct(self, context, &[])?; //TODO: very hacky workaround + + let name: Result = base.resolve_multiname(&multiname)?.ok_or_else(|| { + format!( + "Could not resolve {:?} as super property", + multiname.local_name() + ) + .into() + }); + + base.set_property(object, &name?, value, self, context)?; + + Ok(FrameControl::Continue) + } + + fn op_push_scope( + &mut self, + context: &mut UpdateContext<'_, 'gc, '_>, + ) -> Result, Error> { + let object = self.avm2.pop().as_object()?; + let scope_stack = self.scope(); + let new_scope = Scope::push_scope(scope_stack, object, context.gc_context); + + self.set_scope(Some(new_scope)); + + Ok(FrameControl::Continue) + } + + fn op_push_with( + &mut self, + context: &mut UpdateContext<'_, 'gc, '_>, + ) -> Result, Error> { + let object = self.avm2.pop().as_object()?; + let scope_stack = self.scope(); + let new_scope = Scope::push_with(scope_stack, object, context.gc_context); + + self.set_scope(Some(new_scope)); + + Ok(FrameControl::Continue) + } + + fn op_pop_scope(&mut self) -> Result, Error> { + let scope_stack = self.scope(); + let new_scope = scope_stack.and_then(|s| s.read().pop_scope()); + + self.set_scope(new_scope); + + Ok(FrameControl::Continue) + } + + fn op_get_scope_object(&mut self, mut index: u8) -> Result, Error> { + let mut scope = self.scope(); + + while index > 0 { + if let Some(child_scope) = scope { + scope = child_scope.read().parent_cell(); + } + + index -= 1; + } + + self.avm2.push( + scope + .map(|s| s.read().locals().clone().into()) + .unwrap_or(Value::Undefined), + ); + + Ok(FrameControl::Continue) + } + + fn op_get_global_scope(&mut self) -> Result, Error> { + let mut scope = self.scope(); + + while let Some(this_scope) = scope { + let parent = this_scope.read().parent_cell(); + if parent.is_none() { + break; + } + + scope = parent; + } + + self.avm2.push( + scope + .map(|s| s.read().locals().clone().into()) + .unwrap_or(Value::Undefined), + ); + + Ok(FrameControl::Continue) + } + + fn op_find_property( + &mut self, + method: BytecodeMethod<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + index: Index, + ) -> Result, Error> { + let multiname = self.pool_multiname(method, index)?; + avm_debug!("Resolving {:?}", multiname); + let result = if let Some(scope) = self.scope() { + scope.read().find(&multiname, self, context)? + } else { + None + }; + + self.avm2 + .push(result.map(|o| o.into()).unwrap_or(Value::Undefined)); + + Ok(FrameControl::Continue) + } + + fn op_find_prop_strict( + &mut self, + method: BytecodeMethod<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + index: Index, + ) -> Result, Error> { + let multiname = self.pool_multiname(method, index)?; + avm_debug!("Resolving {:?}", multiname); + let found: Result, Error> = if let Some(scope) = self.scope() { + scope.read().find(&multiname, self, context)? + } else { + None + } + .ok_or_else(|| format!("Property does not exist: {:?}", multiname.local_name()).into()); + let result: Value<'gc> = found?.into(); + + self.avm2.push(result); + + Ok(FrameControl::Continue) + } + + fn op_get_lex( + &mut self, + method: BytecodeMethod<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + index: Index, + ) -> Result, Error> { + let multiname = self.pool_multiname_static(method, index)?; + avm_debug!("Resolving {:?}", multiname); + let found: Result, Error> = if let Some(scope) = self.scope() { + scope + .write(context.gc_context) + .resolve(&multiname, self, context)? + } else { + None + } + .ok_or_else(|| format!("Property does not exist: {:?}", multiname.local_name()).into()); + let result: Value<'gc> = found?; + + self.avm2.push(result); + + Ok(FrameControl::Continue) + } + + fn op_get_slot(&mut self, index: u32) -> Result, Error> { + let object = self.avm2.pop().as_object()?; + let value = object.get_slot(index)?; + + self.avm2.push(value); + + Ok(FrameControl::Continue) + } + + fn op_set_slot( + &mut self, + context: &mut UpdateContext<'_, 'gc, '_>, + index: u32, + ) -> Result, Error> { + let object = self.avm2.pop().as_object()?; + let value = self.avm2.pop(); + + object.set_slot(index, value, context.gc_context)?; + + Ok(FrameControl::Continue) + } + + fn op_get_global_slot(&mut self, index: u32) -> Result, Error> { + let value = self.avm2.globals().get_slot(index)?; + + self.avm2.push(value); + + Ok(FrameControl::Continue) + } + + fn op_set_global_slot( + &mut self, + context: &mut UpdateContext<'_, 'gc, '_>, + index: u32, + ) -> Result, Error> { + let value = self.avm2.pop(); + + self.avm2 + .globals() + .set_slot(index, value, context.gc_context)?; + + Ok(FrameControl::Continue) + } + + fn op_construct( + &mut self, + context: &mut UpdateContext<'_, 'gc, '_>, + arg_count: u32, + ) -> Result, Error> { + let args = self.avm2.pop_args(arg_count); + let mut ctor = self.avm2.pop().as_object()?; + + let proto = ctor + .get_property( + ctor, + &QName::new(Namespace::public_namespace(), "prototype"), + self, + context, + )? + .as_object()?; + + let object = proto.construct(self, context, &args)?; + ctor.call(Some(object), &args, self, context, object.proto())?; + + self.avm2.push(object); + + Ok(FrameControl::Continue) + } + + fn op_construct_prop( + &mut self, + method: BytecodeMethod<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + index: Index, + arg_count: u32, + ) -> Result, Error> { + let args = self.avm2.pop_args(arg_count); + let multiname = self.pool_multiname(method, index)?; + let mut source = self.avm2.pop().as_object()?; + + let ctor_name: Result = + source.resolve_multiname(&multiname)?.ok_or_else(|| { + format!("Could not resolve property {:?}", multiname.local_name()).into() + }); + let mut ctor = source + .get_property(source, &ctor_name?, self, context)? + .as_object()?; + let proto = ctor + .get_property( + ctor, + &QName::new(Namespace::public_namespace(), "prototype"), + self, + context, + )? + .as_object()?; + + let object = proto.construct(self, context, &args)?; + ctor.call(Some(object), &args, self, context, Some(proto))?; + + self.avm2.push(object); + + Ok(FrameControl::Continue) + } + + fn op_construct_super( + &mut self, + context: &mut UpdateContext<'_, 'gc, '_>, + arg_count: u32, + ) -> Result, Error> { + let args = self.avm2.pop_args(arg_count); + let receiver = self.avm2.pop().as_object()?; + let name = QName::new(Namespace::public_namespace(), "constructor"); + let base_proto: Result, Error> = + self.base_proto().and_then(|p| p.proto()).ok_or_else(|| { + "Attempted to call super constructor without a superclass." + .to_string() + .into() + }); + let mut base_proto = base_proto?; + + let function = base_proto + .get_property(receiver, &name, self, context)? + .as_object()?; + + function.call(Some(receiver), &args, self, context, Some(base_proto))?; + + Ok(FrameControl::Continue) + } + + fn op_new_activation( + &mut self, + context: &mut UpdateContext<'_, 'gc, '_>, + ) -> Result, Error> { + self.avm2 + .push(ScriptObject::bare_object(context.gc_context)); + + Ok(FrameControl::Continue) + } + + fn op_new_object( + &mut self, + context: &mut UpdateContext<'_, 'gc, '_>, + num_args: u32, + ) -> Result, Error> { + let mut object = ScriptObject::object(context.gc_context, self.avm2.prototypes().object); + + for _ in 0..num_args { + let value = self.avm2.pop(); + let name = self.avm2.pop(); + + object.set_property( + object, + &QName::new(Namespace::public_namespace(), name.as_string()?), + value, + self, + context, + )?; + } + + self.avm2.push(object); + + Ok(FrameControl::Continue) + } + + fn op_new_function( + &mut self, + method: BytecodeMethod<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + index: Index, + ) -> Result, Error> { + let method_entry = self.table_method(method, index)?; + let scope = self.scope(); + + let mut new_fn = FunctionObject::from_method( + context.gc_context, + method_entry.into(), + scope, + self.avm2.prototypes().function, + None, + ); + let es3_proto = ScriptObject::object(context.gc_context, self.avm2.prototypes().object); + + new_fn.install_slot( + context.gc_context, + QName::new(Namespace::public_namespace(), "prototype"), + 0, + es3_proto.into(), + ); + + self.avm2.push(new_fn); + + Ok(FrameControl::Continue) + } + + fn op_new_class( + &mut self, + method: BytecodeMethod<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + index: Index, + ) -> Result, Error> { + let base_class = self.avm2.pop().as_object()?; + let class_entry = self.table_class(method, index, context)?; + let scope = self.scope(); + + let (new_class, class_init) = + FunctionObject::from_class(self, context, class_entry, base_class, scope)?; + + class_init.call(Some(new_class), &[], self, context, None)?; + + self.avm2.push(new_class); + + Ok(FrameControl::Continue) + } + + fn op_coerce_a(&mut self) -> Result, Error> { + Ok(FrameControl::Continue) + } + + fn op_jump( + &mut self, + offset: i32, + reader: &mut Reader>, + ) -> Result, Error> { + reader.seek(offset as i64)?; + + Ok(FrameControl::Continue) + } + + fn op_if_true( + &mut self, + offset: i32, + reader: &mut Reader>, + ) -> Result, Error> { + let value = self.avm2.pop().as_bool()?; + + if value { + reader.seek(offset as i64)?; + } + + Ok(FrameControl::Continue) + } + + fn op_if_false( + &mut self, + offset: i32, + reader: &mut Reader>, + ) -> Result, Error> { + let value = self.avm2.pop().as_bool()?; + + if !value { + reader.seek(offset as i64)?; + } + + Ok(FrameControl::Continue) + } + + fn op_if_strict_eq( + &mut self, + offset: i32, + reader: &mut Reader>, + ) -> Result, Error> { + let value2 = self.avm2.pop(); + let value1 = self.avm2.pop(); + + if value1 == value2 { + reader.seek(offset as i64)?; + } + + Ok(FrameControl::Continue) + } + + fn op_if_strict_ne( + &mut self, + offset: i32, + reader: &mut Reader>, + ) -> Result, Error> { + let value2 = self.avm2.pop(); + let value1 = self.avm2.pop(); + + if value1 != value2 { + reader.seek(offset as i64)?; + } + + Ok(FrameControl::Continue) + } + + fn op_strict_equals(&mut self) -> Result, Error> { + let value2 = self.avm2.pop(); + let value1 = self.avm2.pop(); + + self.avm2.push(value1 == value2); + + Ok(FrameControl::Continue) + } + + fn op_has_next(&mut self) -> Result, Error> { + //TODO: After adding ints, change this to ints. + let cur_index = self.avm2.pop().as_number()?; + let object = self.avm2.pop().as_object()?; + + let next_index = cur_index as u32 + 1; + + if object.get_enumerant_name(next_index).is_some() { + self.avm2.push(next_index as f32); + } else { + self.avm2.push(0.0); + } + + Ok(FrameControl::Continue) + } + + fn op_has_next_2( + &mut self, + context: &mut UpdateContext<'_, 'gc, '_>, + object_register: u32, + index_register: u32, + ) -> Result, Error> { + //TODO: After adding ints, change this to ints. + let cur_index = self.local_register(index_register)?.as_number()?; + let mut object = Some(self.local_register(object_register)?.as_object()?); + + let mut next_index = cur_index as u32 + 1; + + while let Some(cur_object) = object { + if cur_object.get_enumerant_name(next_index).is_none() { + next_index = 1; + object = cur_object.proto(); + } else { + break; + } + } + + if object.is_none() { + next_index = 0; + } + + self.avm2.push(next_index != 0); + self.set_local_register(index_register, next_index, context.gc_context)?; + self.set_local_register( + object_register, + object.map(|v| v.into()).unwrap_or(Value::Null), + context.gc_context, + )?; + + Ok(FrameControl::Continue) + } + + fn op_next_name(&mut self) -> Result, Error> { + //TODO: After adding ints, change this to ints. + let cur_index = self.avm2.pop().as_number()?; + let object = self.avm2.pop().as_object()?; + + let name = object + .get_enumerant_name(cur_index as u32) + .map(|n| n.local_name().into()); + + self.avm2.push(name.unwrap_or(Value::Undefined)); + + Ok(FrameControl::Continue) + } + + fn op_next_value( + &mut self, + context: &mut UpdateContext<'_, 'gc, '_>, + ) -> Result, Error> { + //TODO: After adding ints, change this to ints. + let cur_index = self.avm2.pop().as_number()?; + let mut object = self.avm2.pop().as_object()?; + + let name = object.get_enumerant_name(cur_index as u32); + let value = if let Some(name) = name { + object.get_property(object, &name, self, context)? + } else { + Value::Undefined + }; + + self.avm2.push(value); + + Ok(FrameControl::Continue) + } + + #[allow(unused_variables)] + fn op_debug( + &mut self, + method: BytecodeMethod<'gc>, + is_local_register: bool, + register_name: Index, + register: u8, + ) -> Result, Error> { + if is_local_register { + let register_name = self.pool_string(method, register_name)?; + let value = self.local_register(register as u32)?; + + avm_debug!("Debug: {} = {:?}", register_name, value); + } else { + avm_debug!("Unknown debugging mode!"); + } + + Ok(FrameControl::Continue) + } + + #[allow(unused_variables)] + fn op_debug_file( + &mut self, + method: BytecodeMethod<'gc>, + file_name: Index, + ) -> Result, Error> { + let file_name = self.pool_string(method, file_name)?; + + avm_debug!("File: {}", file_name); + + Ok(FrameControl::Continue) + } + + #[allow(unused_variables)] + fn op_debug_line(&mut self, line_num: u32) -> Result, Error> { + avm_debug!("Line: {}", line_num); + + Ok(FrameControl::Continue) + } } diff --git a/core/src/avm2/function.rs b/core/src/avm2/function.rs index 84d959144..697924930 100644 --- a/core/src/avm2/function.rs +++ b/core/src/avm2/function.rs @@ -6,11 +6,10 @@ use crate::avm2::method::{BytecodeMethod, Method, NativeMethod}; use crate::avm2::names::{Namespace, QName}; use crate::avm2::object::{Object, ObjectPtr, TObject}; use crate::avm2::r#trait::Trait; -use crate::avm2::return_value::ReturnValue; use crate::avm2::scope::Scope; use crate::avm2::script_object::{ScriptObjectClass, ScriptObjectData}; use crate::avm2::value::Value; -use crate::avm2::{Avm2, Error}; +use crate::avm2::Error; use crate::context::UpdateContext; use gc_arena::{Collect, CollectionContext, GcCell, MutationContext}; use std::fmt; @@ -76,42 +75,43 @@ impl<'gc> Executable<'gc> { /// Execute a method. /// /// The function will either be called directly if it is a Rust builtin, or - /// placed on the stack of the passed-in AVM2 otherwise. As a result, we - /// return a `ReturnValue` which can be used to force execution of the - /// given stack frame and obtain it's return value or to push said value - /// onto the AVM operand stack. + /// executed on the same AVM2 instance as the activation passed in here. + /// The value returned in either case will be provided here. + /// + /// It is a panicing logic error to attempt to execute user code while any + /// reachable object is currently under a GcCell write lock. pub fn exec( &self, unbound_reciever: Option>, arguments: &[Value<'gc>], - avm: &mut Avm2<'gc>, + activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, base_proto: Option>, - ) -> Result, Error> { + ) -> Result, Error> { match self { - Executable::Native(nf, reciever) => { - nf(avm, context, reciever.or(unbound_reciever), arguments) - } + Executable::Native(nf, reciever) => nf( + activation, + context, + reciever.or(unbound_reciever), + arguments, + ), Executable::Action { method, scope, reciever, } => { let reciever = reciever.or(unbound_reciever); - let activation = GcCell::allocate( - context.gc_context, - Activation::from_action( - context, - method.clone(), - *scope, - reciever, - arguments, - base_proto, - )?, + let mut activation = Activation::from_method( + activation.avm2(), + context, + method.clone(), + *scope, + reciever, + arguments, + base_proto, ); - avm.insert_stack_frame(activation); - Ok(activation.into()) + activation.run_actions(method.clone(), context) } } } @@ -161,7 +161,7 @@ impl<'gc> FunctionObject<'gc> { /// initializer method that you should call before interacting with the /// class. The latter should be called using the former as a reciever. pub fn from_class( - avm: &mut Avm2<'gc>, + activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, class: GcCell<'gc, Class<'gc>>, mut base_class: Object<'gc>, @@ -172,7 +172,7 @@ impl<'gc> FunctionObject<'gc> { .get_property( base_class, &QName::new(Namespace::public_namespace(), "prototype"), - avm, + activation, context, )? .as_object() @@ -187,9 +187,9 @@ impl<'gc> FunctionObject<'gc> { ) .into() }); - let mut class_proto = super_proto?.derive(avm, context, class, scope)?; - let fn_proto = avm.prototypes().function; - let class_constr_proto = avm.prototypes().class; + let mut class_proto = super_proto?.derive(activation, context, class, scope)?; + let fn_proto = activation.avm2().prototypes().function; + let class_constr_proto = activation.avm2().prototypes().class; let initializer = class_read.instance_init(); @@ -303,13 +303,15 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> { self, reciever: Object<'gc>, name: &QName, - avm: &mut Avm2<'gc>, + activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, ) -> Result, Error> { - self.0 - .read() - .base - .get_property_local(reciever, name, avm, context) + let read = self.0.read(); + let rv = read.base.get_property_local(reciever, name, activation)?; + + drop(read); + + rv.resolve(activation, context) } fn set_property_local( @@ -317,16 +319,17 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> { reciever: Object<'gc>, name: &QName, value: Value<'gc>, - avm: &mut Avm2<'gc>, + activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, ) -> Result<(), Error> { - let rv = self - .0 - .write(context.gc_context) + let mut write = self.0.write(context.gc_context); + let rv = write .base - .set_property_local(reciever, name, value, avm, context)?; + .set_property_local(reciever, name, value, activation, context)?; - rv.resolve(avm, context)?; + drop(write); + + rv.resolve(activation, context)?; Ok(()) } @@ -336,16 +339,17 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> { reciever: Object<'gc>, name: &QName, value: Value<'gc>, - avm: &mut Avm2<'gc>, + activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, ) -> Result<(), Error> { - let rv = self - .0 - .write(context.gc_context) + let mut write = self.0.write(context.gc_context); + let rv = write .base - .init_property_local(reciever, name, value, avm, context)?; + .init_property_local(reciever, name, value, activation, context)?; - rv.resolve(avm, context)?; + drop(write); + + rv.resolve(activation, context)?; Ok(()) } @@ -468,13 +472,12 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> { self, reciever: Option>, arguments: &[Value<'gc>], - avm: &mut Avm2<'gc>, + activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, base_proto: Option>, ) -> Result, Error> { if let Some(exec) = &self.0.read().exec { - exec.exec(reciever, arguments, avm, context, base_proto)? - .resolve(avm, context) + exec.exec(reciever, arguments, activation, context, base_proto) } else { Err("Not a callable function!".into()) } @@ -482,7 +485,7 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> { fn construct( &self, - _avm: &mut Avm2<'gc>, + _activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, _args: &[Value<'gc>], ) -> Result, Error> { @@ -498,7 +501,7 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> { fn derive( &self, - _avm: &mut Avm2<'gc>, + _activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, class: GcCell<'gc, Class<'gc>>, scope: Option>>, diff --git a/core/src/avm2/globals.rs b/core/src/avm2/globals.rs index 498613104..5972ee927 100644 --- a/core/src/avm2/globals.rs +++ b/core/src/avm2/globals.rs @@ -1,13 +1,13 @@ //! Global scope built-ins +use crate::avm2::activation::Activation; use crate::avm2::function::FunctionObject; use crate::avm2::method::NativeMethod; use crate::avm2::names::{Namespace, QName}; use crate::avm2::object::{Object, TObject}; -use crate::avm2::return_value::ReturnValue; use crate::avm2::script_object::ScriptObject; use crate::avm2::value::Value; -use crate::avm2::{Avm2, Error}; +use crate::avm2::Error; use crate::context::UpdateContext; use gc_arena::{Collect, MutationContext}; use std::f64::NAN; @@ -18,16 +18,16 @@ mod function; mod object; fn trace<'gc>( - _avm: &mut Avm2<'gc>, + _activation: &mut Activation<'_, 'gc>, _action_context: &mut UpdateContext<'_, 'gc, '_>, _this: Option>, args: &[Value<'gc>], -) -> Result, Error> { +) -> Result, Error> { if let Some(s) = args.get(0) { log::info!(target: "avm_trace", "{}", s.clone().coerce_string()); } - Ok(Value::Undefined.into()) + Ok(Value::Undefined) } /// This structure represents all system builtins' prototypes. diff --git a/core/src/avm2/globals/class.rs b/core/src/avm2/globals/class.rs index e7144bb79..c738f2403 100644 --- a/core/src/avm2/globals/class.rs +++ b/core/src/avm2/globals/class.rs @@ -1,10 +1,10 @@ //! `Class` builtin/prototype +use crate::avm2::activation::Activation; use crate::avm2::object::Object; -use crate::avm2::return_value::ReturnValue; use crate::avm2::script_object::ScriptObject; use crate::avm2::value::Value; -use crate::avm2::{Avm2, Error}; +use crate::avm2::Error; use crate::context::UpdateContext; use gc_arena::MutationContext; @@ -13,11 +13,11 @@ use gc_arena::MutationContext; /// Notably, you cannot construct new classes this way, so this returns an /// error. pub fn constructor<'gc>( - _avm: &mut Avm2<'gc>, + _activation: &mut Activation<'_, 'gc>, _action_context: &mut UpdateContext<'_, 'gc, '_>, _this: Option>, _args: &[Value<'gc>], -) -> Result, Error> { +) -> Result, Error> { Err("Classes cannot be constructed.".into()) } diff --git a/core/src/avm2/globals/flash/display/displayobject.rs b/core/src/avm2/globals/flash/display/displayobject.rs index 9542041a2..30278237d 100644 --- a/core/src/avm2/globals/flash/display/displayobject.rs +++ b/core/src/avm2/globals/flash/display/displayobject.rs @@ -1,21 +1,21 @@ //! `flash.display.DisplayObject` builtin/prototype +use crate::avm2::activation::Activation; use crate::avm2::object::Object; -use crate::avm2::return_value::ReturnValue; use crate::avm2::script_object::ScriptObject; use crate::avm2::value::Value; -use crate::avm2::{Avm2, Error}; +use crate::avm2::Error; use crate::context::UpdateContext; use gc_arena::MutationContext; /// Implements `flash.display.DisplayObject`'s constructor. pub fn constructor<'gc>( - _avm: &mut Avm2<'gc>, + _activation: &mut Activation<'_, 'gc>, _action_context: &mut UpdateContext<'_, 'gc, '_>, _this: Option>, _args: &[Value<'gc>], -) -> Result, Error> { - Ok(Value::Undefined.into()) +) -> Result, Error> { + Ok(Value::Undefined) } /// Construct `DisplayObject.prototype`. diff --git a/core/src/avm2/globals/flash/display/displayobjectcontainer.rs b/core/src/avm2/globals/flash/display/displayobjectcontainer.rs index 74a5d248e..7b09b5c5a 100644 --- a/core/src/avm2/globals/flash/display/displayobjectcontainer.rs +++ b/core/src/avm2/globals/flash/display/displayobjectcontainer.rs @@ -1,21 +1,21 @@ //! `flash.display.DisplayObjectContainer` builtin/prototype +use crate::avm2::activation::Activation; use crate::avm2::object::Object; -use crate::avm2::return_value::ReturnValue; use crate::avm2::script_object::ScriptObject; use crate::avm2::value::Value; -use crate::avm2::{Avm2, Error}; +use crate::avm2::Error; use crate::context::UpdateContext; use gc_arena::MutationContext; /// Implements `flash.display.DisplayObjectContainer`'s constructor. pub fn constructor<'gc>( - _avm: &mut Avm2<'gc>, + _activation: &mut Activation<'_, 'gc>, _action_context: &mut UpdateContext<'_, 'gc, '_>, _this: Option>, _args: &[Value<'gc>], -) -> Result, Error> { - Ok(Value::Undefined.into()) +) -> Result, Error> { + Ok(Value::Undefined) } /// Construct `DisplayObjectContainer.prototype`. diff --git a/core/src/avm2/globals/flash/display/interactiveobject.rs b/core/src/avm2/globals/flash/display/interactiveobject.rs index fe736a3a8..8fc2844fd 100644 --- a/core/src/avm2/globals/flash/display/interactiveobject.rs +++ b/core/src/avm2/globals/flash/display/interactiveobject.rs @@ -1,21 +1,21 @@ //! `flash.display.InteractiveObject` builtin/prototype +use crate::avm2::activation::Activation; use crate::avm2::object::Object; -use crate::avm2::return_value::ReturnValue; use crate::avm2::script_object::ScriptObject; use crate::avm2::value::Value; -use crate::avm2::{Avm2, Error}; +use crate::avm2::Error; use crate::context::UpdateContext; use gc_arena::MutationContext; /// Implements `flash.display.InteractiveObject`'s constructor. pub fn constructor<'gc>( - _avm: &mut Avm2<'gc>, + _activation: &mut Activation<'_, 'gc>, _action_context: &mut UpdateContext<'_, 'gc, '_>, _this: Option>, _args: &[Value<'gc>], -) -> Result, Error> { - Ok(Value::Undefined.into()) +) -> Result, Error> { + Ok(Value::Undefined) } /// Construct `InteractiveObject.prototype`. diff --git a/core/src/avm2/globals/flash/display/movieclip.rs b/core/src/avm2/globals/flash/display/movieclip.rs index 6e39e7c94..1a97f8ea4 100644 --- a/core/src/avm2/globals/flash/display/movieclip.rs +++ b/core/src/avm2/globals/flash/display/movieclip.rs @@ -1,21 +1,21 @@ //! `flash.display.MovieClip` builtin/prototype +use crate::avm2::activation::Activation; use crate::avm2::object::Object; -use crate::avm2::return_value::ReturnValue; use crate::avm2::script_object::ScriptObject; use crate::avm2::value::Value; -use crate::avm2::{Avm2, Error}; +use crate::avm2::Error; use crate::context::UpdateContext; use gc_arena::MutationContext; /// Implements `flash.display.MovieClip`'s constructor. pub fn constructor<'gc>( - _avm: &mut Avm2<'gc>, + _activation: &mut Activation<'_, 'gc>, _action_context: &mut UpdateContext<'_, 'gc, '_>, _this: Option>, _args: &[Value<'gc>], -) -> Result, Error> { - Ok(Value::Undefined.into()) +) -> Result, Error> { + Ok(Value::Undefined) } /// Construct `MovieClip.prototype`. diff --git a/core/src/avm2/globals/flash/display/sprite.rs b/core/src/avm2/globals/flash/display/sprite.rs index 110afca15..8763921d6 100644 --- a/core/src/avm2/globals/flash/display/sprite.rs +++ b/core/src/avm2/globals/flash/display/sprite.rs @@ -1,21 +1,21 @@ //! `flash.display.Sprite` builtin/prototype +use crate::avm2::activation::Activation; use crate::avm2::object::Object; -use crate::avm2::return_value::ReturnValue; use crate::avm2::script_object::ScriptObject; use crate::avm2::value::Value; -use crate::avm2::{Avm2, Error}; +use crate::avm2::Error; use crate::context::UpdateContext; use gc_arena::MutationContext; /// Implements `flash.display.Sprite`'s constructor. pub fn constructor<'gc>( - _avm: &mut Avm2<'gc>, + _activation: &mut Activation<'_, 'gc>, _action_context: &mut UpdateContext<'_, 'gc, '_>, _this: Option>, _args: &[Value<'gc>], -) -> Result, Error> { - Ok(Value::Undefined.into()) +) -> Result, Error> { + Ok(Value::Undefined) } /// Construct `Sprite.prototype`. diff --git a/core/src/avm2/globals/flash/events/eventdispatcher.rs b/core/src/avm2/globals/flash/events/eventdispatcher.rs index ea16792cf..172a8ddda 100644 --- a/core/src/avm2/globals/flash/events/eventdispatcher.rs +++ b/core/src/avm2/globals/flash/events/eventdispatcher.rs @@ -1,21 +1,21 @@ //! `flash.events.EventDispatcher` builtin/prototype +use crate::avm2::activation::Activation; use crate::avm2::object::Object; -use crate::avm2::return_value::ReturnValue; use crate::avm2::script_object::ScriptObject; use crate::avm2::value::Value; -use crate::avm2::{Avm2, Error}; +use crate::avm2::Error; use crate::context::UpdateContext; use gc_arena::MutationContext; /// Implements `flash.events.EventDispatcher`'s constructor. pub fn constructor<'gc>( - _avm: &mut Avm2<'gc>, + _activation: &mut Activation<'_, 'gc>, _action_context: &mut UpdateContext<'_, 'gc, '_>, _this: Option>, _args: &[Value<'gc>], -) -> Result, Error> { - Ok(Value::Undefined.into()) +) -> Result, Error> { + Ok(Value::Undefined) } /// Construct `EventDispatcher.prototype`. diff --git a/core/src/avm2/globals/function.rs b/core/src/avm2/globals/function.rs index 4f3fba60b..7f9ffc8ff 100644 --- a/core/src/avm2/globals/function.rs +++ b/core/src/avm2/globals/function.rs @@ -1,42 +1,40 @@ //! Function builtin and prototype +use crate::avm2::activation::Activation; use crate::avm2::function::FunctionObject; use crate::avm2::names::{Namespace, QName}; use crate::avm2::object::{Object, TObject}; -use crate::avm2::return_value::ReturnValue; use crate::avm2::script_object::ScriptObject; use crate::avm2::value::Value; -use crate::avm2::{Avm2, Error}; +use crate::avm2::Error; use crate::context::UpdateContext; use gc_arena::MutationContext; /// Implements `Function` pub fn constructor<'gc>( - _avm: &mut Avm2<'gc>, + _activation: &mut Activation<'_, 'gc>, _action_context: &mut UpdateContext<'_, 'gc, '_>, _this: Option>, _args: &[Value<'gc>], -) -> Result, Error> { - Ok(Value::Undefined.into()) +) -> Result, Error> { + Ok(Value::Undefined) } /// Implements `Function.prototype.call` fn call<'gc>( - avm: &mut Avm2<'gc>, + activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, func: Option>, args: &[Value<'gc>], -) -> Result, Error> { +) -> Result, Error> { let this = args.get(0).and_then(|v| v.as_object().ok()); let base_proto = this.and_then(|that| that.proto()); if let Some(func) = func { if args.len() > 1 { - Ok(func - .call(this, &args[1..], avm, context, base_proto)? - .into()) + Ok(func.call(this, &args[1..], activation, context, base_proto)?) } else { - Ok(func.call(this, &[], avm, context, base_proto)?.into()) + Ok(func.call(this, &[], activation, context, base_proto)?) } } else { Err("Not a callable function".into()) diff --git a/core/src/avm2/globals/object.rs b/core/src/avm2/globals/object.rs index 5bea90207..f1a1e1442 100644 --- a/core/src/avm2/globals/object.rs +++ b/core/src/avm2/globals/object.rs @@ -1,70 +1,65 @@ //! Object builtin and prototype +use crate::avm2::activation::Activation; use crate::avm2::function::FunctionObject; use crate::avm2::names::{Namespace, QName}; use crate::avm2::object::{Object, TObject}; -use crate::avm2::return_value::ReturnValue; use crate::avm2::value::Value; -use crate::avm2::{Avm2, Error}; +use crate::avm2::Error; use crate::context::UpdateContext; use gc_arena::MutationContext; /// Implements `Object` pub fn constructor<'gc>( - _avm: &mut Avm2<'gc>, + _activation: &mut Activation<'_, 'gc>, _context: &mut UpdateContext<'_, 'gc, '_>, _this: Option>, _args: &[Value<'gc>], -) -> Result, Error> { - Ok(Value::Undefined.into()) +) -> Result, Error> { + Ok(Value::Undefined) } /// Implements `Object.prototype.toString` fn to_string<'gc>( - _: &mut Avm2<'gc>, + _: &mut Activation<'_, 'gc>, _: &mut UpdateContext<'_, 'gc, '_>, this: Option>, _: &[Value<'gc>], -) -> Result, Error> { +) -> Result, Error> { Ok(this .map(|t| t.to_string()) - .unwrap_or(Ok(Value::Undefined))? - .into()) + .unwrap_or(Ok(Value::Undefined))?) } /// Implements `Object.prototype.toLocaleString` fn to_locale_string<'gc>( - _: &mut Avm2<'gc>, + _: &mut Activation<'_, 'gc>, _: &mut UpdateContext<'_, 'gc, '_>, this: Option>, _: &[Value<'gc>], -) -> Result, Error> { +) -> Result, Error> { Ok(this .map(|t| t.to_string()) - .unwrap_or(Ok(Value::Undefined))? - .into()) + .unwrap_or(Ok(Value::Undefined))?) } /// Implements `Object.prototype.valueOf` fn value_of<'gc>( - _: &mut Avm2<'gc>, + _: &mut Activation<'_, 'gc>, _: &mut UpdateContext<'_, 'gc, '_>, this: Option>, _: &[Value<'gc>], -) -> Result, Error> { - Ok(this - .map(|t| t.value_of()) - .unwrap_or(Ok(Value::Undefined))? - .into()) +) -> Result, Error> { + Ok(this.map(|t| t.value_of()).unwrap_or(Ok(Value::Undefined))?) } /// `Object.prototype.hasOwnProperty` pub fn has_own_property<'gc>( - _avm: &mut Avm2<'gc>, + _activation: &mut Activation<'_, 'gc>, _context: &mut UpdateContext<'_, 'gc, '_>, this: Option>, args: &[Value<'gc>], -) -> Result, Error> { +) -> Result, Error> { let this: Result, Error> = this.ok_or_else(|| "No valid this parameter".into()); let this = this?; let name: Result<&Value<'gc>, Error> = args.get(0).ok_or_else(|| "No name specified".into()); @@ -82,11 +77,11 @@ pub fn has_own_property<'gc>( /// `Object.prototype.isPrototypeOf` pub fn is_prototype_of<'gc>( - _avm: &mut Avm2<'gc>, + _activation: &mut Activation<'_, 'gc>, _context: &mut UpdateContext<'_, 'gc, '_>, this: Option>, args: &[Value<'gc>], -) -> Result, Error> { +) -> Result, Error> { let search_proto: Result, Error> = this.ok_or_else(|| "No valid this parameter".into()); let search_proto = search_proto?; @@ -105,11 +100,11 @@ pub fn is_prototype_of<'gc>( /// `Object.prototype.propertyIsEnumerable` pub fn property_is_enumerable<'gc>( - _avm: &mut Avm2<'gc>, + _activation: &mut Activation<'_, 'gc>, _context: &mut UpdateContext<'_, 'gc, '_>, this: Option>, args: &[Value<'gc>], -) -> Result, Error> { +) -> Result, Error> { let this: Result, Error> = this.ok_or_else(|| "No valid this parameter".into()); let this = this?; let name: Result<&Value<'gc>, Error> = args.get(0).ok_or_else(|| "No name specified".into()); @@ -127,11 +122,11 @@ pub fn property_is_enumerable<'gc>( /// `Object.prototype.setPropertyIsEnumerable` pub fn set_property_is_enumerable<'gc>( - _avm: &mut Avm2<'gc>, + _activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, this: Option>, args: &[Value<'gc>], -) -> Result, Error> { +) -> Result, Error> { let this: Result, Error> = this.ok_or_else(|| "No valid this parameter".into()); let this = this?; let name: Result<&Value<'gc>, Error> = args.get(0).ok_or_else(|| "No name specified".into()); @@ -149,7 +144,7 @@ pub fn set_property_is_enumerable<'gc>( } } - Ok(Value::Undefined.into()) + Ok(Value::Undefined) } /// Partially construct `Object.prototype`. diff --git a/core/src/avm2/method.rs b/core/src/avm2/method.rs index 1bd5d6a32..c2189143c 100644 --- a/core/src/avm2/method.rs +++ b/core/src/avm2/method.rs @@ -1,10 +1,10 @@ //! AVM2 methods +use crate::avm2::activation::Activation; use crate::avm2::object::Object; -use crate::avm2::return_value::ReturnValue; use crate::avm2::script::TranslationUnit; use crate::avm2::value::Value; -use crate::avm2::{Avm2, Error}; +use crate::avm2::Error; use crate::context::UpdateContext; use gc_arena::{Collect, CollectionContext}; use std::fmt; @@ -30,11 +30,11 @@ pub struct CollectWrapper(T); /// your function yields `None`, you must ensure that the top-most activation /// in the AVM1 runtime will return with the value of this function. pub type NativeMethod<'gc> = fn( - &mut Avm2<'gc>, + &mut Activation<'_, 'gc>, &mut UpdateContext<'_, 'gc, '_>, Option>, &[Value<'gc>], -) -> Result, Error>; +) -> Result, Error>; /// Represents a reference to an AVM2 method and body. #[derive(Collect, Clone, Debug)] diff --git a/core/src/avm2/object.rs b/core/src/avm2/object.rs index 104295457..056da591d 100644 --- a/core/src/avm2/object.rs +++ b/core/src/avm2/object.rs @@ -1,5 +1,6 @@ //! AVM2 objects. +use crate::avm2::activation::Activation; use crate::avm2::class::Class; use crate::avm2::function::{Executable, FunctionObject}; use crate::avm2::names::{Multiname, Namespace, QName}; @@ -7,7 +8,7 @@ use crate::avm2::r#trait::{Trait, TraitKind}; use crate::avm2::scope::Scope; use crate::avm2::script_object::ScriptObject; use crate::avm2::value::Value; -use crate::avm2::{Avm2, Error}; +use crate::avm2::Error; use crate::context::UpdateContext; use gc_arena::{Collect, GcCell, MutationContext}; use ruffle_macros::enum_trait_object; @@ -30,7 +31,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy self, reciever: Object<'gc>, name: &QName, - avm: &mut Avm2<'gc>, + activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, ) -> Result, Error>; @@ -39,23 +40,23 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy &mut self, reciever: Object<'gc>, name: &QName, - avm: &mut Avm2<'gc>, + activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, ) -> Result, Error> { if !self.has_instantiated_property(name) { for abc_trait in self.get_trait(name)? { - self.install_trait(avm, context, abc_trait, reciever)?; + self.install_trait(activation, context, abc_trait, reciever)?; } } let has_no_getter = self.has_own_virtual_setter(name) && !self.has_own_virtual_getter(name); if self.has_own_property(name)? && !has_no_getter { - return self.get_property_local(reciever, name, avm, context); + return self.get_property_local(reciever, name, activation, context); } if let Some(mut proto) = self.proto() { - return proto.get_property(reciever, name, avm, context); + return proto.get_property(reciever, name, activation, context); } Ok(Value::Undefined) @@ -83,7 +84,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy reciever: Object<'gc>, name: &QName, value: Value<'gc>, - avm: &mut Avm2<'gc>, + activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, ) -> Result<(), Error>; @@ -93,17 +94,17 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy reciever: Object<'gc>, name: &QName, value: Value<'gc>, - avm: &mut Avm2<'gc>, + activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, ) -> Result<(), Error> { if !self.has_instantiated_property(name) { for abc_trait in self.get_trait(name)? { - self.install_trait(avm, context, abc_trait, reciever)?; + self.install_trait(activation, context, abc_trait, reciever)?; } } if self.has_own_virtual_setter(name) { - return self.set_property_local(reciever, name, value, avm, context); + return self.set_property_local(reciever, name, value, activation, context); } let mut proto = self.proto(); @@ -112,13 +113,13 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy //we're calling a virtual setter. If you call `set_property` on //a non-virtual you will actually alter the prototype. if my_proto.has_own_virtual_setter(name) { - return my_proto.set_property(reciever, name, value, avm, context); + return my_proto.set_property(reciever, name, value, activation, context); } proto = my_proto.proto(); } - reciever.set_property_local(reciever, name, value, avm, context) + reciever.set_property_local(reciever, name, value, activation, context) } /// Init a property on this specific object. @@ -127,7 +128,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy reciever: Object<'gc>, name: &QName, value: Value<'gc>, - avm: &mut Avm2<'gc>, + activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, ) -> Result<(), Error>; @@ -137,17 +138,17 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy reciever: Object<'gc>, name: &QName, value: Value<'gc>, - avm: &mut Avm2<'gc>, + activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, ) -> Result<(), Error> { if !self.has_instantiated_property(name) { for abc_trait in self.get_trait(name)? { - self.install_trait(avm, context, abc_trait, reciever)?; + self.install_trait(activation, context, abc_trait, reciever)?; } } if self.has_own_virtual_setter(name) { - return self.init_property_local(reciever, name, value, avm, context); + return self.init_property_local(reciever, name, value, activation, context); } let mut proto = self.proto(); @@ -156,13 +157,13 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy //we're calling a virtual setter. If you call `set_property` on //a non-virtual you will actually alter the prototype. if my_proto.has_own_virtual_setter(name) { - return my_proto.init_property(reciever, name, value, avm, context); + return my_proto.init_property(reciever, name, value, activation, context); } proto = my_proto.proto(); } - reciever.init_property_local(reciever, name, value, avm, context) + reciever.init_property_local(reciever, name, value, activation, context) } /// Retrieve a slot by it's index. @@ -404,24 +405,24 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy /// object. fn install_trait( &mut self, - avm: &mut Avm2<'gc>, + activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, trait_entry: Trait<'gc>, reciever: Object<'gc>, ) -> Result<(), Error> { - self.install_foreign_trait(avm, context, trait_entry, self.get_scope(), reciever) + self.install_foreign_trait(activation, context, trait_entry, self.get_scope(), reciever) } /// Install a trait from anywyere. fn install_foreign_trait( &mut self, - avm: &mut Avm2<'gc>, + activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, trait_entry: Trait<'gc>, scope: Option>>, reciever: Object<'gc>, ) -> Result<(), Error> { - let fn_proto = avm.prototypes().function; + let fn_proto = activation.avm2().prototypes().function; let trait_name = trait_entry.name().clone(); avm_debug!( "Installing trait {:?} of kind {:?}", @@ -491,14 +492,14 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy }; let super_class: Result, Error> = self - .get_property(reciever, &super_name, avm, context)? + .get_property(reciever, &super_name, activation, context)? .as_object() .map_err(|_e| { format!("Could not resolve superclass {:?}", super_name.local_name()).into() }); let (class_object, _cinit) = - FunctionObject::from_class(avm, context, *class, super_class?, scope)?; + FunctionObject::from_class(activation, context, *class, super_class?, scope)?; self.install_const( context.gc_context, class_read.name().clone(), @@ -516,7 +517,8 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy fn_proto, None, ); - let es3_proto = ScriptObject::object(context.gc_context, avm.prototypes().object); + let es3_proto = + ScriptObject::object(context.gc_context, activation.avm2().prototypes().object); fobject.install_slot( context.gc_context, @@ -548,7 +550,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy self, _reciever: Option>, _arguments: &[Value<'gc>], - _avm: &mut Avm2<'gc>, + _activation: &mut Activation<'_, 'gc>, _context: &mut UpdateContext<'_, 'gc, '_>, _base_proto: Option>, ) -> Result, Error> { @@ -574,7 +576,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy /// purely so that host objects can be constructed by the VM. fn construct( &self, - avm: &mut Avm2<'gc>, + activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, args: &[Value<'gc>], ) -> Result, Error>; @@ -590,7 +592,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy /// traits. fn derive( &self, - avm: &mut Avm2<'gc>, + activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, class: GcCell<'gc, Class<'gc>>, scope: Option>>, diff --git a/core/src/avm2/property.rs b/core/src/avm2/property.rs index b51794e9b..75e21dfe0 100644 --- a/core/src/avm2/property.rs +++ b/core/src/avm2/property.rs @@ -5,8 +5,7 @@ use crate::avm2::function::Executable; use crate::avm2::object::{Object, TObject}; use crate::avm2::return_value::ReturnValue; use crate::avm2::value::Value; -use crate::avm2::{Avm2, Error}; -use crate::context::UpdateContext; +use crate::avm2::Error; use enumset::{EnumSet, EnumSetType}; use gc_arena::{Collect, CollectionContext}; @@ -136,15 +135,16 @@ impl<'gc> Property<'gc> { /// user-defined. pub fn get( &self, - avm: &mut Avm2<'gc>, - context: &mut UpdateContext<'_, 'gc, '_>, this: Object<'gc>, base_proto: Option>, ) -> Result, Error> { match self { - Property::Virtual { get: Some(get), .. } => { - get.exec(Some(this), &[], avm, context, base_proto) - } + Property::Virtual { get: Some(get), .. } => Ok(ReturnValue::defer_execution( + get.clone(), + Some(this), + vec![], + base_proto, + )), Property::Virtual { get: None, .. } => Ok(Value::Undefined.into()), Property::Stored { value, .. } => Ok(value.to_owned().into()), Property::Slot { slot_id, .. } => this.get_slot(*slot_id).map(|v| v.into()), @@ -160,8 +160,6 @@ impl<'gc> Property<'gc> { /// is encountered. pub fn set( &mut self, - avm: &mut Avm2<'gc>, - context: &mut UpdateContext<'_, 'gc, '_>, this: Object<'gc>, base_proto: Option>, new_value: impl Into>, @@ -169,13 +167,12 @@ impl<'gc> Property<'gc> { match self { Property::Virtual { set, .. } => { if let Some(function) = set { - return function.exec( + return Ok(ReturnValue::defer_execution( + function.clone(), Some(this), - &[new_value.into()], - avm, - context, + vec![new_value.into()], base_proto, - ); + )); } Ok(Value::Undefined.into()) @@ -207,8 +204,6 @@ impl<'gc> Property<'gc> { /// is encountered. pub fn init( &mut self, - avm: &mut Avm2<'gc>, - context: &mut UpdateContext<'_, 'gc, '_>, this: Object<'gc>, base_proto: Option>, new_value: impl Into>, @@ -216,13 +211,12 @@ impl<'gc> Property<'gc> { match self { Property::Virtual { set, .. } => { if let Some(function) = set { - return function.exec( + return Ok(ReturnValue::defer_execution( + function.clone(), Some(this), - &[new_value.into()], - avm, - context, + vec![new_value.into()], base_proto, - ); + )); } Ok(Value::Undefined.into()) diff --git a/core/src/avm2/return_value.rs b/core/src/avm2/return_value.rs index a9d692350..6d0e56d65 100644 --- a/core/src/avm2/return_value.rs +++ b/core/src/avm2/return_value.rs @@ -1,190 +1,113 @@ //! Return value enum use crate::avm2::activation::Activation; -use crate::avm2::names::Namespace; +use crate::avm2::function::Executable; use crate::avm2::object::Object; -use crate::avm2::{Avm2, Error, Value}; +use crate::avm2::{Error, Value}; use crate::context::UpdateContext; -use gc_arena::{Collect, GcCell}; use std::fmt; -/// Represents the return value of a function call. +/// Represents the return value of a function call that has not yet executed. /// -/// Since function calls can result in invoking native code or adding a new -/// activation onto the AVM stack, you need another type to represent how the -/// return value will be delivered to you. +/// It is a panicking logic error to attempt to run AVM2 code while any +/// reachable object is in a locked state. Ergo, it is sometimes necessary to +/// be able to return what *should* be called rather than actually running the +/// code on the current Rust stack. This type exists to force deferred +/// execution of some child AVM2 frame. /// -/// This function contains a handful of utility methods for deciding what to do -/// with a given value regardless of how it is delivered to the calling -/// function. +/// It is also possible to stuff a regular `Value` in here - this is provided +/// for the convenience of functions that may be able to resolve a value +/// without needing a free stack. `ReturnValue` should not be used as a generic +/// wrapper for `Value`, as it can also defer actual execution, and it should +/// be resolved at the earliest safe opportunity. /// -/// It is `must_use` - failing to use a return value is a compiler warning. We -/// provide a helper function specifically to indicate that you aren't -/// interested in the result of a call. +/// It is `must_use` - failing to resolve a return value is a compiler warning. #[must_use = "Return values must be used"] -#[derive(Clone, Collect)] -#[collect(no_drop)] pub enum ReturnValue<'gc> { - /// Indicates that the return value is available immediately. + /// ReturnValue has already been computed. + /// + /// This exists primarily for functions that don't necessarily need to + /// always defer code execution - say, if they already have the result and + /// do not need a free stack frame to run an activation on. Immediate(Value<'gc>), - /// Indicates that the return value is the result of a given user-defined - /// function call. The activation record returned is the frame that needs - /// to return to get your value. - ResultOf(GcCell<'gc, Activation<'gc>>), -} - -impl PartialEq for ReturnValue<'_> { - fn eq(&self, other: &Self) -> bool { - use ReturnValue::*; - - match (self, other) { - (Immediate(val1), Immediate(val2)) => val1 == val2, - (ResultOf(frame1), ResultOf(frame2)) => GcCell::ptr_eq(*frame1, *frame2), - _ => false, - } - } + /// ReturnValue has not yet been computed. + /// + /// This exists for functions that do need to reference the result of user + /// code in order to produce their result. + ResultOf { + executable: Executable<'gc>, + unbound_reciever: Option>, + arguments: Vec>, + base_proto: Option>, + }, } impl fmt::Debug for ReturnValue<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use ReturnValue::*; - match self { - Immediate(val) => write!(f, "Immediate({:?})", val), - ResultOf(_frame) => write!(f, "ResultOf()"), + Self::Immediate(v) => f.debug_tuple("ReturnValue::Immediate").field(v).finish(), + Self::ResultOf { + executable: _executable, + unbound_reciever, + arguments, + base_proto, + } => f + .debug_struct("ReturnValue") + .field("executable", &"") + .field("unbound_reciever", unbound_reciever) + .field("arguments", arguments) + .field("base_proto", base_proto) + .finish(), } } } impl<'gc> ReturnValue<'gc> { - /// Mark a given return value as intended to be pushed onto the stack. - /// - /// The natural result of a stack frame retiring is to be pushed, so this - /// only ensures that Immediate values are pushed. - pub fn push(self, avm: &mut Avm2<'gc>) { - use ReturnValue::*; - - match self { - Immediate(val) => avm.push(val), - ResultOf(_frame) => {} - }; + /// Construct a new return value. + pub fn defer_execution( + executable: Executable<'gc>, + unbound_reciever: Option>, + arguments: Vec>, + base_proto: Option>, + ) -> Self { + Self::ResultOf { + executable, + unbound_reciever, + arguments, + base_proto, + } } - /// Force a return value to resolve on the Rust stack by recursing back - /// into the AVM. + /// Resolve the underlying deferred execution. + /// + /// All return values must eventually resolved - it is a compile error to + /// fail to do so. pub fn resolve( self, - avm: &mut Avm2<'gc>, + activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, ) -> Result, Error> { - use ReturnValue::*; - match self { - Immediate(val) => Ok(val), - ResultOf(frame) => { - avm.run_current_frame(context, frame)?; - - Ok(avm.pop()) - } - } - } - - pub fn is_immediate(&self) -> bool { - use ReturnValue::*; - - if let Immediate(_v) = self { - true - } else { - false - } - } - - /// Panic if a value is not immediate. - /// - /// This should only be used in test assertions. - #[cfg(test)] - pub fn unwrap_immediate(self) -> Value<'gc> { - use ReturnValue::*; - - match self { - Immediate(val) => val, - _ => panic!("Unwrapped a non-immediate return value"), + Self::Immediate(v) => Ok(v), + Self::ResultOf { + executable, + unbound_reciever, + arguments, + base_proto, + } => executable.exec( + unbound_reciever, + &arguments, + activation, + context, + base_proto, + ), } } } impl<'gc> From> for ReturnValue<'gc> { - fn from(val: Value<'gc>) -> Self { - ReturnValue::Immediate(val) - } -} - -impl<'gc> From for ReturnValue<'gc> { - fn from(string: String) -> Self { - ReturnValue::Immediate(Value::String(string)) - } -} - -impl<'gc> From<&str> for ReturnValue<'gc> { - fn from(string: &str) -> Self { - ReturnValue::Immediate(Value::String(string.to_owned())) - } -} - -impl<'gc> From for ReturnValue<'gc> { - fn from(value: bool) -> Self { - ReturnValue::Immediate(Value::Bool(value)) - } -} - -impl<'gc, T> From for ReturnValue<'gc> -where - Object<'gc>: From, -{ - fn from(value: T) -> Self { - ReturnValue::Immediate(Value::Object(Object::from(value))) - } -} - -impl<'gc> From for ReturnValue<'gc> { - fn from(value: f64) -> Self { - ReturnValue::Immediate(Value::Number(value)) - } -} - -impl<'gc> From for ReturnValue<'gc> { - fn from(value: f32) -> Self { - ReturnValue::Immediate(Value::Number(f64::from(value))) - } -} - -impl<'gc> From for ReturnValue<'gc> { - fn from(value: u8) -> Self { - ReturnValue::Immediate(Value::Number(f64::from(value))) - } -} - -impl<'gc> From for ReturnValue<'gc> { - fn from(value: i32) -> Self { - ReturnValue::Immediate(Value::Number(f64::from(value))) - } -} - -impl<'gc> From for ReturnValue<'gc> { - fn from(value: u32) -> Self { - ReturnValue::Immediate(Value::Number(f64::from(value))) - } -} - -impl<'gc> From for ReturnValue<'gc> { - fn from(value: Namespace) -> Self { - ReturnValue::Immediate(Value::Namespace(value)) - } -} - -impl<'gc> From>> for ReturnValue<'gc> { - fn from(frame: GcCell<'gc, Activation<'gc>>) -> Self { - ReturnValue::ResultOf(frame) + fn from(v: Value<'gc>) -> Self { + Self::Immediate(v) } } diff --git a/core/src/avm2/scope.rs b/core/src/avm2/scope.rs index 025946b17..b7dac37cb 100644 --- a/core/src/avm2/scope.rs +++ b/core/src/avm2/scope.rs @@ -1,9 +1,10 @@ //! Represents AVM2 scope chain resolution. +use crate::avm2::activation::Activation; use crate::avm2::names::Multiname; use crate::avm2::object::{Object, TObject}; -use crate::avm2::return_value::ReturnValue; -use crate::avm2::{Avm2, Error}; +use crate::avm2::value::Value; +use crate::avm2::Error; use crate::context::UpdateContext; use gc_arena::{Collect, GcCell, MutationContext}; use std::cell::Ref; @@ -99,7 +100,7 @@ impl<'gc> Scope<'gc> { pub fn find( &self, name: &Multiname, - avm: &mut Avm2<'gc>, + activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, ) -> Result>, Error> { if let Some(qname) = self.locals().resolve_multiname(name)? { @@ -109,7 +110,7 @@ impl<'gc> Scope<'gc> { } if let Some(scope) = self.parent() { - return scope.find(name, avm, context); + return scope.find(name, activation, context); } Ok(None) @@ -122,21 +123,24 @@ impl<'gc> Scope<'gc> { pub fn resolve( &mut self, name: &Multiname, - avm: &mut Avm2<'gc>, + activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, - ) -> Result>, Error> { + ) -> Result>, Error> { if let Some(qname) = self.locals().resolve_multiname(name)? { if self.locals().has_property(&qname)? { - return Ok(Some( - self.values - .get_property(self.values, &qname, avm, context)? - .into(), - )); + return Ok(Some(self.values.get_property( + self.values, + &qname, + activation, + context, + )?)); } } if let Some(parent) = self.parent { - return parent.write(context.gc_context).resolve(name, avm, context); + return parent + .write(context.gc_context) + .resolve(name, activation, context); } //TODO: Should undefined variables halt execution? diff --git a/core/src/avm2/script_object.rs b/core/src/avm2/script_object.rs index 80bfa3b8f..fa06a05e6 100644 --- a/core/src/avm2/script_object.rs +++ b/core/src/avm2/script_object.rs @@ -1,5 +1,6 @@ //! Default AVM2 object impl +use crate::avm2::activation::Activation; use crate::avm2::class::Class; use crate::avm2::function::Executable; use crate::avm2::names::{Namespace, QName}; @@ -10,7 +11,7 @@ use crate::avm2::return_value::ReturnValue; use crate::avm2::scope::Scope; use crate::avm2::slot::Slot; use crate::avm2::value::Value; -use crate::avm2::{Avm2, Error}; +use crate::avm2::Error; use crate::context::UpdateContext; use gc_arena::{Collect, GcCell, MutationContext}; use std::collections::HashMap; @@ -74,12 +75,15 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> { self, reciever: Object<'gc>, name: &QName, - avm: &mut Avm2<'gc>, + activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, ) -> Result, Error> { - self.0 + let rv = self + .0 .read() - .get_property_local(reciever, name, avm, context) + .get_property_local(reciever, name, activation)?; + + rv.resolve(activation, context) } fn set_property_local( @@ -87,15 +91,15 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> { reciever: Object<'gc>, name: &QName, value: Value<'gc>, - avm: &mut Avm2<'gc>, + activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, ) -> Result<(), Error> { let rv = self .0 .write(context.gc_context) - .set_property_local(reciever, name, value, avm, context)?; + .set_property_local(reciever, name, value, activation, context)?; - rv.resolve(avm, context)?; + rv.resolve(activation, context)?; Ok(()) } @@ -105,15 +109,15 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> { reciever: Object<'gc>, name: &QName, value: Value<'gc>, - avm: &mut Avm2<'gc>, + activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, ) -> Result<(), Error> { let rv = self .0 .write(context.gc_context) - .init_property_local(reciever, name, value, avm, context)?; + .init_property_local(reciever, name, value, activation, context)?; - rv.resolve(avm, context)?; + rv.resolve(activation, context)?; Ok(()) } @@ -229,7 +233,7 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> { fn construct( &self, - _avm: &mut Avm2<'gc>, + _activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, _args: &[Value<'gc>], ) -> Result, Error> { @@ -239,7 +243,7 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> { fn derive( &self, - _avm: &mut Avm2<'gc>, + _activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, class: GcCell<'gc, Class<'gc>>, scope: Option>>, @@ -376,23 +380,14 @@ impl<'gc> ScriptObjectData<'gc> { &self, reciever: Object<'gc>, name: &QName, - avm: &mut Avm2<'gc>, - context: &mut UpdateContext<'_, 'gc, '_>, - ) -> Result, Error> { + activation: &mut Activation<'_, 'gc>, + ) -> Result, Error> { let prop = self.values.get(name); if let Some(prop) = prop { - prop.get( - avm, - context, - reciever, - avm.current_stack_frame() - .and_then(|sf| sf.read().base_proto()) - .or(self.proto), - )? - .resolve(avm, context) + prop.get(reciever, activation.base_proto().or(self.proto)) } else { - Ok(Value::Undefined) + Ok(Value::Undefined.into()) } } @@ -401,25 +396,26 @@ impl<'gc> ScriptObjectData<'gc> { reciever: Object<'gc>, name: &QName, value: Value<'gc>, - avm: &mut Avm2<'gc>, + activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, ) -> Result, Error> { - if let Some(prop) = self.values.get_mut(name) { + let slot_id = if let Some(prop) = self.values.get(name) { if let Some(slot_id) = prop.slot_id() { - self.set_slot(slot_id, value, context.gc_context)?; - Ok(Value::Undefined.into()) + Some(slot_id) } else { - let proto = self.proto; - prop.set( - avm, - context, - reciever, - avm.current_stack_frame() - .and_then(|sf| sf.read().base_proto()) - .or(proto), - value, - ) + None } + } else { + None + }; + + if let Some(slot_id) = slot_id { + self.set_slot(slot_id, value, context.gc_context)?; + Ok(Value::Undefined.into()) + } else if self.values.contains_key(name) { + let prop = self.values.get_mut(name).unwrap(); + let proto = self.proto; + prop.set(reciever, activation.base_proto().or(proto), value) } else { //TODO: Not all classes are dynamic like this self.enumerants.push(name.clone()); @@ -435,7 +431,7 @@ impl<'gc> ScriptObjectData<'gc> { reciever: Object<'gc>, name: &QName, value: Value<'gc>, - avm: &mut Avm2<'gc>, + activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, ) -> Result, Error> { if let Some(prop) = self.values.get_mut(name) { @@ -444,15 +440,7 @@ impl<'gc> ScriptObjectData<'gc> { Ok(Value::Undefined.into()) } else { let proto = self.proto; - prop.init( - avm, - context, - reciever, - avm.current_stack_frame() - .and_then(|sf| sf.read().base_proto()) - .or(proto), - value, - ) + prop.init(reciever, activation.base_proto().or(proto), value) } } else { //TODO: Not all classes are dynamic like this diff --git a/core/src/player.rs b/core/src/player.rs index 4b8a61e17..5ced943ba 100644 --- a/core/src/player.rs +++ b/core/src/player.rs @@ -878,8 +878,6 @@ impl Player { } } } - // Execute the stack frame (if any). - let _ = avm2.run_stack_till_empty(context); } }