diff --git a/core/src/avm2.rs b/core/src/avm2.rs index 3b558497f..36a2688fa 100644 --- a/core/src/avm2.rs +++ b/core/src/avm2.rs @@ -426,6 +426,17 @@ impl<'gc> Avm2<'gc> { Op::GetLocal { index } => self.op_get_local(index), Op::SetLocal { index } => self.op_set_local(context, index), Op::Call { num_args } => self.op_call(context, num_args), + Op::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::ReturnValue => self.op_return_value(context), Op::ReturnVoid => self.op_return_void(context), Op::GetProperty { index } => self.op_get_property(context, index), @@ -562,18 +573,146 @@ impl<'gc> Avm2<'gc> { context: &mut UpdateContext<'_, 'gc, '_>, arg_count: u32, ) -> Result<(), Error> { - let function = self.pop().as_object()?; - let receiver = self.pop().as_object()?; let mut args = Vec::new(); for _ in 0..arg_count { args.push(self.pop()); } + let receiver = self.pop().as_object().ok(); + let function = self.pop().as_object()?; function.call(receiver, &args, self, context)?.push(self); Ok(()) } + fn op_call_method( + &mut self, + context: &mut UpdateContext<'_, 'gc, '_>, + index: Index, + arg_count: u32, + ) -> Result<(), Error> { + let mut args = Vec::new(); + for _ in 0..arg_count { + args.push(self.pop()); + } + 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()); + + function? + .call(Some(receiver), &args, self, context)? + .push(self); + + Ok(()) + } + + fn op_call_property( + &mut self, + context: &mut UpdateContext<'_, 'gc, '_>, + index: Index, + arg_count: u32, + ) -> Result<(), Error> { + let mut args = Vec::new(); + for _ in 0..arg_count { + args.push(self.pop()); + } + 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 function = receiver + .get_property(&name?, self, context)? + .resolve(self, context)? + .as_object()?; + + function + .call(Some(receiver), &args, self, context)? + .push(self); + + Ok(()) + } + + fn op_call_prop_lex( + &mut self, + context: &mut UpdateContext<'_, 'gc, '_>, + index: Index, + arg_count: u32, + ) -> Result<(), Error> { + let mut args = Vec::new(); + for _ in 0..arg_count { + args.push(self.pop()); + } + 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 function = receiver + .get_property(&name?, self, context)? + .resolve(self, context)? + .as_object()?; + + function.call(None, &args, self, context)?.push(self); + + Ok(()) + } + + fn op_call_prop_void( + &mut self, + context: &mut UpdateContext<'_, 'gc, '_>, + index: Index, + arg_count: u32, + ) -> Result<(), Error> { + let mut args = Vec::new(); + for _ in 0..arg_count { + args.push(self.pop()); + } + 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 function = receiver + .get_property(&name?, self, context)? + .resolve(self, context)? + .as_object()?; + + function + .call(Some(receiver), &args, self, context)? + .resolve(self, context)?; + + Ok(()) + } + + fn op_call_static( + &mut self, + context: &mut UpdateContext<'_, 'gc, '_>, + index: Index, + arg_count: u32, + ) -> Result<(), Error> { + let mut args = Vec::new(); + for _ in 0..arg_count { + args.push(self.pop()); + } + 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_abc_method( + context.gc_context, + method, + scope, + self.system_prototypes.function, + ); + + function + .call(Some(receiver), &args, self, context)? + .push(self); + + Ok(()) + } + fn op_return_value(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) -> Result<(), Error> { let return_value = self.pop(); @@ -861,7 +1000,7 @@ impl<'gc> Avm2<'gc> { .as_object()?; let object = proto.construct(self, context, &args)?; - ctor.call(object, &args, self, context)? + ctor.call(Some(object), &args, self, context)? .resolve(self, context)?; Ok(()) @@ -899,7 +1038,7 @@ impl<'gc> Avm2<'gc> { .as_object()?; let object = proto.construct(self, context, &args)?; - ctor.call(object, &args, self, context)? + ctor.call(Some(object), &args, self, context)? .resolve(self, context)?; Ok(()) diff --git a/core/src/avm2/activation.rs b/core/src/avm2/activation.rs index d70bd5213..2595e88ca 100644 --- a/core/src/avm2/activation.rs +++ b/core/src/avm2/activation.rs @@ -95,7 +95,7 @@ pub struct Activation<'gc> { pc: usize, /// The immutable value of `this`. - this: Object<'gc>, + this: Option>, /// The arguments this function was called by. arguments: Option>, @@ -151,7 +151,7 @@ impl<'gc> Activation<'gc> { Ok(Self { method, pc: 0, - this: global, + this: Some(global), arguments: None, is_executing: false, local_registers, @@ -164,7 +164,7 @@ impl<'gc> Activation<'gc> { pub fn from_action( context: &mut UpdateContext<'_, 'gc, '_>, action: &Avm2Function<'gc>, - this: Object<'gc>, + this: Option>, arguments: &[Value<'gc>], ) -> Result { let method = action.method.clone(); @@ -178,7 +178,7 @@ impl<'gc> Activation<'gc> { { let mut write = local_registers.write(context.gc_context); - *write.get_mut(0).unwrap() = this.into(); + *write.get_mut(0).unwrap() = this.map(|t| t.into()).unwrap_or(Value::Null); for i in 0..num_declared_arguments { *write.get_mut(1 + i).unwrap() = arguments diff --git a/core/src/avm2/function.rs b/core/src/avm2/function.rs index 6938cd430..c36c5a8e7 100644 --- a/core/src/avm2/function.rs +++ b/core/src/avm2/function.rs @@ -34,7 +34,7 @@ use swf::avm2::types::{ pub type NativeFunction<'gc> = fn( &mut Avm2<'gc>, &mut UpdateContext<'_, 'gc, '_>, - Object<'gc>, + Option>, &[Value<'gc>], ) -> Result, Error>; @@ -131,7 +131,7 @@ impl<'gc> Executable<'gc> { &self, avm: &mut Avm2<'gc>, context: &mut UpdateContext<'_, 'gc, '_>, - reciever: Object<'gc>, + reciever: Option>, arguments: &[Value<'gc>], ) -> Result, Error> { match self { @@ -316,11 +316,11 @@ impl<'gc> FunctionObject<'gc> { constr.install_trait(avm, context, class.abc(), trait_entry, scope, fn_proto)?; } - constr.install_method( + constr.install_dynamic_property( context.gc_context, QName::new(Namespace::public_namespace(), "prototype"), - class_proto, - ); + class_proto.into(), + )?; Ok(constr) } @@ -453,6 +453,10 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> { self.0.write(mc).base.init_slot(id, value, mc) } + fn get_method(self, id: u32) -> Option> { + self.0.read().base.get_method(id) + } + fn has_property(self, name: &QName) -> bool { self.0.read().base.has_property(name) } @@ -465,9 +469,13 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> { self.0.as_ptr() as *const ObjectPtr } + fn as_executable(&self) -> Option> { + self.0.read().exec.clone() + } + fn call( self, - reciever: Object<'gc>, + reciever: Option>, arguments: &[Value<'gc>], avm: &mut Avm2<'gc>, context: &mut UpdateContext<'_, 'gc, '_>, @@ -499,26 +507,43 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> { .into()) } - fn install_method(&mut self, mc: MutationContext<'gc, '_>, name: QName, function: Object<'gc>) { - self.0.write(mc).base.install_method(name, function) + fn install_method( + &mut self, + mc: MutationContext<'gc, '_>, + name: QName, + disp_id: u32, + function: Object<'gc>, + ) { + self.0 + .write(mc) + .base + .install_method(name, disp_id, function) } fn install_getter( &mut self, mc: MutationContext<'gc, '_>, name: QName, - function: Executable<'gc>, + disp_id: u32, + function: Object<'gc>, ) -> Result<(), Error> { - self.0.write(mc).base.install_getter(name, function) + self.0 + .write(mc) + .base + .install_getter(name, disp_id, function) } fn install_setter( &mut self, mc: MutationContext<'gc, '_>, name: QName, - function: Executable<'gc>, + disp_id: u32, + function: Object<'gc>, ) -> Result<(), Error> { - self.0.write(mc).base.install_setter(name, function) + self.0 + .write(mc) + .base + .install_setter(name, disp_id, function) } fn install_dynamic_property( diff --git a/core/src/avm2/globals.rs b/core/src/avm2/globals.rs index e0585ee08..7dcf3b68b 100644 --- a/core/src/avm2/globals.rs +++ b/core/src/avm2/globals.rs @@ -17,7 +17,7 @@ mod object; fn trace<'gc>( _avm: &mut Avm2<'gc>, _action_context: &mut UpdateContext<'_, 'gc, '_>, - _this: Object<'gc>, + _this: Option>, args: &[Value<'gc>], ) -> Result, Error> { if let Some(s) = args.get(0) { diff --git a/core/src/avm2/globals/flash/display/displayobject.rs b/core/src/avm2/globals/flash/display/displayobject.rs index cf8192ef0..9542041a2 100644 --- a/core/src/avm2/globals/flash/display/displayobject.rs +++ b/core/src/avm2/globals/flash/display/displayobject.rs @@ -12,7 +12,7 @@ use gc_arena::MutationContext; pub fn constructor<'gc>( _avm: &mut Avm2<'gc>, _action_context: &mut UpdateContext<'_, 'gc, '_>, - _this: Object<'gc>, + _this: Option>, _args: &[Value<'gc>], ) -> Result, Error> { Ok(Value::Undefined.into()) diff --git a/core/src/avm2/globals/flash/display/displayobjectcontainer.rs b/core/src/avm2/globals/flash/display/displayobjectcontainer.rs index bd63e1f94..74a5d248e 100644 --- a/core/src/avm2/globals/flash/display/displayobjectcontainer.rs +++ b/core/src/avm2/globals/flash/display/displayobjectcontainer.rs @@ -12,7 +12,7 @@ use gc_arena::MutationContext; pub fn constructor<'gc>( _avm: &mut Avm2<'gc>, _action_context: &mut UpdateContext<'_, 'gc, '_>, - _this: Object<'gc>, + _this: Option>, _args: &[Value<'gc>], ) -> Result, Error> { Ok(Value::Undefined.into()) diff --git a/core/src/avm2/globals/flash/display/interactiveobject.rs b/core/src/avm2/globals/flash/display/interactiveobject.rs index 4500d5e3a..fe736a3a8 100644 --- a/core/src/avm2/globals/flash/display/interactiveobject.rs +++ b/core/src/avm2/globals/flash/display/interactiveobject.rs @@ -12,7 +12,7 @@ use gc_arena::MutationContext; pub fn constructor<'gc>( _avm: &mut Avm2<'gc>, _action_context: &mut UpdateContext<'_, 'gc, '_>, - _this: Object<'gc>, + _this: Option>, _args: &[Value<'gc>], ) -> Result, Error> { Ok(Value::Undefined.into()) diff --git a/core/src/avm2/globals/flash/display/movieclip.rs b/core/src/avm2/globals/flash/display/movieclip.rs index f718717e5..6e39e7c94 100644 --- a/core/src/avm2/globals/flash/display/movieclip.rs +++ b/core/src/avm2/globals/flash/display/movieclip.rs @@ -12,7 +12,7 @@ use gc_arena::MutationContext; pub fn constructor<'gc>( _avm: &mut Avm2<'gc>, _action_context: &mut UpdateContext<'_, 'gc, '_>, - _this: Object<'gc>, + _this: Option>, _args: &[Value<'gc>], ) -> Result, Error> { Ok(Value::Undefined.into()) diff --git a/core/src/avm2/globals/flash/display/sprite.rs b/core/src/avm2/globals/flash/display/sprite.rs index a94194ec0..110afca15 100644 --- a/core/src/avm2/globals/flash/display/sprite.rs +++ b/core/src/avm2/globals/flash/display/sprite.rs @@ -12,7 +12,7 @@ use gc_arena::MutationContext; pub fn constructor<'gc>( _avm: &mut Avm2<'gc>, _action_context: &mut UpdateContext<'_, 'gc, '_>, - _this: Object<'gc>, + _this: Option>, _args: &[Value<'gc>], ) -> Result, Error> { Ok(Value::Undefined.into()) diff --git a/core/src/avm2/globals/flash/events/eventdispatcher.rs b/core/src/avm2/globals/flash/events/eventdispatcher.rs index 10ec93ddd..ea16792cf 100644 --- a/core/src/avm2/globals/flash/events/eventdispatcher.rs +++ b/core/src/avm2/globals/flash/events/eventdispatcher.rs @@ -12,7 +12,7 @@ use gc_arena::MutationContext; pub fn constructor<'gc>( _avm: &mut Avm2<'gc>, _action_context: &mut UpdateContext<'_, 'gc, '_>, - _this: Object<'gc>, + _this: Option>, _args: &[Value<'gc>], ) -> Result, Error> { Ok(Value::Undefined.into()) diff --git a/core/src/avm2/globals/function.rs b/core/src/avm2/globals/function.rs index 9eb58c8a2..878e42ebd 100644 --- a/core/src/avm2/globals/function.rs +++ b/core/src/avm2/globals/function.rs @@ -14,7 +14,7 @@ use gc_arena::MutationContext; pub fn constructor<'gc>( _avm: &mut Avm2<'gc>, _action_context: &mut UpdateContext<'_, 'gc, '_>, - _this: Object<'gc>, + _this: Option>, _args: &[Value<'gc>], ) -> Result, Error> { Ok(Value::Undefined.into()) @@ -24,7 +24,7 @@ pub fn constructor<'gc>( fn to_string<'gc>( _: &mut Avm2<'gc>, _: &mut UpdateContext<'_, 'gc, '_>, - _: Object<'gc>, + _: Option>, _: &[Value<'gc>], ) -> Result, Error> { Ok(ReturnValue::Immediate("[type Function]".into())) @@ -43,6 +43,7 @@ pub fn create_proto<'gc>(gc_context: MutationContext<'gc, '_>, proto: Object<'gc function_proto.install_method( gc_context, QName::new(Namespace::public_namespace(), "toString"), + 0, FunctionObject::from_builtin(gc_context, to_string, function_proto), ); diff --git a/core/src/avm2/globals/object.rs b/core/src/avm2/globals/object.rs index 9982feaa5..98c1b6b98 100644 --- a/core/src/avm2/globals/object.rs +++ b/core/src/avm2/globals/object.rs @@ -11,7 +11,7 @@ use gc_arena::MutationContext; pub fn constructor<'gc>( _avm: &mut Avm2<'gc>, _action_context: &mut UpdateContext<'_, 'gc, '_>, - _this: Object<'gc>, + _this: Option>, _args: &[Value<'gc>], ) -> Result, Error> { Ok(Value::Undefined.into()) diff --git a/core/src/avm2/object.rs b/core/src/avm2/object.rs index 0056e863c..7765ac8a1 100644 --- a/core/src/avm2/object.rs +++ b/core/src/avm2/object.rs @@ -1,8 +1,6 @@ //! AVM2 objects. -use crate::avm2::function::{ - Avm2ClassEntry, Avm2Function, Avm2MethodEntry, Executable, FunctionObject, -}; +use crate::avm2::function::{Avm2ClassEntry, Avm2MethodEntry, Executable, FunctionObject}; use crate::avm2::names::{Multiname, QName}; use crate::avm2::return_value::ReturnValue; use crate::avm2::scope::Scope; @@ -72,6 +70,9 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy mc: MutationContext<'gc, '_>, ) -> Result<(), Error>; + /// Retrieve a method by it's index. + fn get_method(self, id: u32) -> Option>; + /// Resolve a multiname into a single QName, if any of the namespaces /// match. fn resolve_multiname(self, multiname: &Multiname) -> Option { @@ -113,14 +114,21 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy fn proto(&self) -> Option>; /// Install a method (or any other non-slot value) on an object. - fn install_method(&mut self, mc: MutationContext<'gc, '_>, name: QName, function: Object<'gc>); + fn install_method( + &mut self, + mc: MutationContext<'gc, '_>, + name: QName, + disp_id: u32, + function: Object<'gc>, + ); /// Install a getter method on an object property. fn install_getter( &mut self, mc: MutationContext<'gc, '_>, name: QName, - function: Executable<'gc>, + disp_id: u32, + function: Object<'gc>, ) -> Result<(), Error>; /// Install a setter method on an object property. @@ -128,7 +136,8 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy &mut self, mc: MutationContext<'gc, '_>, name: QName, - function: Executable<'gc>, + disp_id: u32, + function: Object<'gc>, ) -> Result<(), Error>; /// Install a dynamic or built-in value property on an object. @@ -183,21 +192,29 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy }; self.install_slot(context.gc_context, trait_name, *slot_id, value); } - AbcTraitKind::Method { method, .. } => { + AbcTraitKind::Method { + disp_id, method, .. + } => { let method = Avm2MethodEntry::from_method_index(abc, method.clone()).unwrap(); let function = FunctionObject::from_abc_method(context.gc_context, method, scope, fn_proto); - self.install_method(context.gc_context, trait_name, function); + self.install_method(context.gc_context, trait_name, *disp_id, function); } - AbcTraitKind::Getter { method, .. } => { + AbcTraitKind::Getter { + disp_id, method, .. + } => { let method = Avm2MethodEntry::from_method_index(abc, method.clone()).unwrap(); - let exec = Avm2Function::from_method(method, scope).into(); - self.install_getter(context.gc_context, trait_name, exec)?; + let function = + FunctionObject::from_abc_method(context.gc_context, method, scope, fn_proto); + self.install_getter(context.gc_context, trait_name, *disp_id, function)?; } - AbcTraitKind::Setter { method, .. } => { + AbcTraitKind::Setter { + disp_id, method, .. + } => { let method = Avm2MethodEntry::from_method_index(abc, method.clone()).unwrap(); - let exec = Avm2Function::from_method(method, scope).into(); - self.install_setter(context.gc_context, trait_name, exec)?; + let function = + FunctionObject::from_abc_method(context.gc_context, method, scope, fn_proto); + self.install_setter(context.gc_context, trait_name, *disp_id, function)?; } AbcTraitKind::Class { slot_id, class } => { let type_entry = Avm2ClassEntry::from_class_index(abc, class.clone()).unwrap(); @@ -251,7 +268,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy /// Call the object. fn call( self, - _reciever: Object<'gc>, + _reciever: Option>, _arguments: &[Value<'gc>], _avm: &mut Avm2<'gc>, _context: &mut UpdateContext<'_, 'gc, '_>, @@ -282,6 +299,11 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy /// Get a raw pointer value for this object. fn as_ptr(&self) -> *const ObjectPtr; + + /// Get this object's `Executable`, if it has one. + fn as_executable(&self) -> Option> { + None + } } pub enum ObjectPtr {} diff --git a/core/src/avm2/property.rs b/core/src/avm2/property.rs index 986f66402..2c3143941 100644 --- a/core/src/avm2/property.rs +++ b/core/src/avm2/property.rs @@ -142,7 +142,7 @@ impl<'gc> Property<'gc> { this: Object<'gc>, ) -> Result, Error> { match self { - Property::Virtual { get: Some(get), .. } => get.exec(avm, context, this, &[]), + Property::Virtual { get: Some(get), .. } => get.exec(avm, context, Some(this), &[]), 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()), @@ -168,7 +168,8 @@ impl<'gc> Property<'gc> { match self { Property::Virtual { set, .. } => { if let Some(function) = set { - let return_value = function.exec(avm, context, this, &[new_value.into()])?; + let return_value = + function.exec(avm, context, Some(this), &[new_value.into()])?; Ok(return_value.is_immediate()) } else { Ok(true) @@ -211,7 +212,8 @@ impl<'gc> Property<'gc> { match self { Property::Virtual { set, .. } => { if let Some(function) = set { - let return_value = function.exec(avm, context, this, &[new_value.into()])?; + let return_value = + function.exec(avm, context, Some(this), &[new_value.into()])?; Ok(return_value.is_immediate()) } else { Ok(true) diff --git a/core/src/avm2/script_object.rs b/core/src/avm2/script_object.rs index 6793e2ae9..53f7d676d 100644 --- a/core/src/avm2/script_object.rs +++ b/core/src/avm2/script_object.rs @@ -27,6 +27,9 @@ pub struct ScriptObjectData<'gc> { /// Slots stored on this object. slots: Vec>, + /// Methods stored on this object. + methods: Vec>>, + /// Implicit prototype (or declared base class) of this script object. proto: Option>, } @@ -91,6 +94,10 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> { self.0.write(mc).init_slot(id, value, mc) } + fn get_method(self, id: u32) -> Option> { + self.0.read().get_method(id) + } + fn has_property(self, name: &QName) -> bool { self.0.read().has_property(name) } @@ -113,26 +120,34 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> { Ok(ScriptObject::object(context.gc_context, this)) } - fn install_method(&mut self, mc: MutationContext<'gc, '_>, name: QName, function: Object<'gc>) { - self.0.write(mc).install_method(name, function) + fn install_method( + &mut self, + mc: MutationContext<'gc, '_>, + name: QName, + disp_id: u32, + function: Object<'gc>, + ) { + self.0.write(mc).install_method(name, disp_id, function) } fn install_getter( &mut self, mc: MutationContext<'gc, '_>, name: QName, - function: Executable<'gc>, + disp_id: u32, + function: Object<'gc>, ) -> Result<(), Error> { - self.0.write(mc).install_getter(name, function) + self.0.write(mc).install_getter(name, disp_id, function) } fn install_setter( &mut self, mc: MutationContext<'gc, '_>, name: QName, - function: Executable<'gc>, + disp_id: u32, + function: Object<'gc>, ) -> Result<(), Error> { - self.0.write(mc).install_setter(name, function) + self.0.write(mc).install_setter(name, disp_id, function) } fn install_dynamic_property( @@ -189,6 +204,7 @@ impl<'gc> ScriptObjectData<'gc> { ScriptObjectData { values: HashMap::new(), slots: Vec::new(), + methods: Vec::new(), proto, } } @@ -306,6 +322,11 @@ impl<'gc> ScriptObjectData<'gc> { } } + /// Retrieve a method from the method table. + pub fn get_method(&self, id: u32) -> Option> { + self.methods.get(id as usize).and_then(|v| *v) + } + pub fn has_property(&self, name: &QName) -> bool { self.values.get(name).is_some() } @@ -315,7 +336,16 @@ impl<'gc> ScriptObjectData<'gc> { } /// Install a method into the object. - pub fn install_method(&mut self, name: QName, function: Object<'gc>) { + pub fn install_method(&mut self, name: QName, disp_id: u32, function: Object<'gc>) { + if disp_id > 0 { + if self.methods.len() <= disp_id as usize { + self.methods + .resize_with(disp_id as usize + 1, Default::default); + } + + *self.methods.get_mut(disp_id as usize).unwrap() = Some(function); + } + self.values.insert(name, Property::new_method(function)); } @@ -324,7 +354,26 @@ impl<'gc> ScriptObjectData<'gc> { /// This is a little more complicated than methods, since virtual property /// slots can be installed in two parts. Thus, we need to support /// installing them in either order. - pub fn install_getter(&mut self, name: QName, function: Executable<'gc>) -> Result<(), Error> { + pub fn install_getter( + &mut self, + name: QName, + disp_id: u32, + function: Object<'gc>, + ) -> Result<(), Error> { + let executable: Result, Error> = function + .as_executable() + .ok_or_else(|| "Attempted to install getter without a valid method".into()); + let executable = executable?; + + if disp_id > 0 { + if self.methods.len() <= disp_id as usize { + self.methods + .resize_with(disp_id as usize + 1, Default::default); + } + + *self.methods.get_mut(disp_id as usize).unwrap() = Some(function); + } + if !self.values.contains_key(&name) { self.values.insert(name.clone(), Property::new_virtual()); } @@ -332,7 +381,7 @@ impl<'gc> ScriptObjectData<'gc> { self.values .get_mut(&name) .unwrap() - .install_virtual_getter(function) + .install_virtual_getter(executable) } /// Install a setter into the object. @@ -340,7 +389,26 @@ impl<'gc> ScriptObjectData<'gc> { /// This is a little more complicated than methods, since virtual property /// slots can be installed in two parts. Thus, we need to support /// installing them in either order. - pub fn install_setter(&mut self, name: QName, function: Executable<'gc>) -> Result<(), Error> { + pub fn install_setter( + &mut self, + name: QName, + disp_id: u32, + function: Object<'gc>, + ) -> Result<(), Error> { + let executable: Result, Error> = function + .as_executable() + .ok_or_else(|| "Attempted to install setter without a valid method".into()); + let executable = executable?; + + if disp_id > 0 { + if self.methods.len() <= disp_id as usize { + self.methods + .resize_with(disp_id as usize + 1, Default::default); + } + + *self.methods.get_mut(disp_id as usize).unwrap() = Some(function); + } + if !self.values.contains_key(&name) { self.values.insert(name.clone(), Property::new_virtual()); } @@ -348,7 +416,7 @@ impl<'gc> ScriptObjectData<'gc> { self.values .get_mut(&name) .unwrap() - .install_virtual_setter(function) + .install_virtual_setter(executable) } pub fn install_dynamic_property(