diff --git a/core/src/avm2/function.rs b/core/src/avm2/function.rs index c2d228dd8..1b0636487 100644 --- a/core/src/avm2/function.rs +++ b/core/src/avm2/function.rs @@ -646,6 +646,19 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> { .into()) } + fn to_string(&self) -> Result, Error> { + if let ScriptObjectClass::ClassConstructor(class, ..) = self.0.read().base.class() { + let name = QName::from_abc_multiname(&class.abc(), class.instance().name.clone())?; + Ok(format!("[class {}]", name.local_name()).into()) + } else { + Ok("function Function() {}".into()) + } + } + + fn value_of(&self) -> Result, Error> { + Ok(Value::Object(Object::from(*self))) + } + fn install_method( &mut self, mc: MutationContext<'gc, '_>, diff --git a/core/src/avm2/globals/function.rs b/core/src/avm2/globals/function.rs index d8f82554e..c3892a239 100644 --- a/core/src/avm2/globals/function.rs +++ b/core/src/avm2/globals/function.rs @@ -20,16 +20,6 @@ pub fn constructor<'gc>( Ok(Value::Undefined.into()) } -/// Implements `Function.prototype.toString` -fn to_string<'gc>( - _: &mut Avm2<'gc>, - _: &mut UpdateContext<'_, 'gc, '_>, - _: Option>, - _: &[Value<'gc>], -) -> Result, Error> { - Ok(ReturnValue::Immediate("[type Function]".into())) -} - /// Implements `Function.prototype.call` fn call<'gc>( avm: &mut Avm2<'gc>, @@ -61,12 +51,6 @@ fn call<'gc>( pub fn create_proto<'gc>(gc_context: MutationContext<'gc, '_>, proto: Object<'gc>) -> Object<'gc> { let mut function_proto = ScriptObject::object(gc_context, proto); - function_proto.install_method( - gc_context, - QName::new(Namespace::public_namespace(), "toString"), - 0, - FunctionObject::from_builtin(gc_context, to_string, function_proto), - ); function_proto.install_method( gc_context, QName::new(Namespace::public_namespace(), "call"), diff --git a/core/src/avm2/globals/object.rs b/core/src/avm2/globals/object.rs index 96eb41e3b..ef0e29374 100644 --- a/core/src/avm2/globals/object.rs +++ b/core/src/avm2/globals/object.rs @@ -19,6 +19,32 @@ pub fn constructor<'gc>( Ok(Value::Undefined.into()) } +/// Implements `Object.prototype.toString` +fn to_string<'gc>( + _: &mut Avm2<'gc>, + _: &mut UpdateContext<'_, 'gc, '_>, + this: Option>, + _: &[Value<'gc>], +) -> Result, Error> { + Ok(this + .map(|t| t.to_string()) + .unwrap_or(Ok(Value::Undefined))? + .into()) +} + +/// Implements `Object.prototype.valueOf` +fn value_of<'gc>( + _: &mut Avm2<'gc>, + _: &mut UpdateContext<'_, 'gc, '_>, + this: Option>, + _: &[Value<'gc>], +) -> Result, Error> { + Ok(this + .map(|t| t.value_of()) + .unwrap_or(Ok(Value::Undefined))? + .into()) +} + /// `Object.prototype.hasOwnProperty` pub fn has_own_property<'gc>( _avm: &mut Avm2<'gc>, @@ -127,6 +153,18 @@ pub fn fill_proto<'gc>( mut object_proto: Object<'gc>, fn_proto: Object<'gc>, ) { + object_proto.install_method( + gc_context, + QName::new(Namespace::public_namespace(), "toString"), + 0, + FunctionObject::from_builtin(gc_context, to_string, fn_proto), + ); + object_proto.install_method( + gc_context, + QName::new(Namespace::public_namespace(), "valueOf"), + 0, + FunctionObject::from_builtin(gc_context, value_of, fn_proto), + ); object_proto.install_method( gc_context, QName::new(Namespace::public_namespace(), "hasOwnProperty"), diff --git a/core/src/avm2/object.rs b/core/src/avm2/object.rs index db29dd982..4f4ac9120 100644 --- a/core/src/avm2/object.rs +++ b/core/src/avm2/object.rs @@ -627,6 +627,23 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy scope: Option>>, ) -> Result, Error>; + /// Implement the result of calling `Object.prototype.toString` on this + /// object class. + /// + /// `toString` is a method used to request an object be coerced to a string + /// value. The default implementation is stored here. User-specified string + /// coercions happen by defining `toString` in a downstream class or + /// prototype; this is then picked up by the VM runtime when doing + /// coercions. + fn to_string(&self) -> Result, Error>; + + /// Implement the result of calling `Object.prototype.valueOf` on this + /// object class. + /// + /// `valueOf` is a method used to request an object be coerced to a + /// primitive value. Typically, this would be a number of some kind. + fn value_of(&self) -> Result, Error>; + /// Get a raw pointer value for this object. fn as_ptr(&self) -> *const ObjectPtr; diff --git a/core/src/avm2/script_object.rs b/core/src/avm2/script_object.rs index 68410bdbb..3eef73463 100644 --- a/core/src/avm2/script_object.rs +++ b/core/src/avm2/script_object.rs @@ -247,6 +247,14 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> { )) } + fn to_string(&self) -> Result, Error> { + Ok("[object Object]".into()) + } + + fn value_of(&self) -> Result, Error> { + Ok(Value::Object(Object::from(*self))) + } + fn install_method( &mut self, mc: MutationContext<'gc, '_>, @@ -797,6 +805,10 @@ impl<'gc> ScriptObjectData<'gc> { Ok(()) } + pub fn class(&self) -> &ScriptObjectClass<'gc> { + &self.class + } + /// Install a method into the object. pub fn install_method(&mut self, name: QName, disp_id: u32, function: Object<'gc>) { if disp_id > 0 {