From 7008f9baccf52ae7a420050282752ee98c37f04c Mon Sep 17 00:00:00 2001 From: relrelb Date: Sat, 9 Oct 2021 01:19:38 +0300 Subject: [PATCH] avm1: Correct `base_proto` for constructions Previously constructions had `base_proto` set to the newly-created object (`this`). However this doesn't match the `base_proto` of method calls, which is `this.__proto__` (or more precisely where the function is found on the prototype chain). This caused wrong behavior when using the `super` object from within constructors. Change `base_proto` in that case to be `this.__proto__`, which aligns with method calls. In order to keep things working, `SuperObject::call` needs to look-up one level less than before. An alternative can be changing `base_proto` for method calls instead, but that seems to be harder because this would require `search_prototype` to return the before-last visited object in the prototype chain. --- core/src/avm1/function.rs | 9 +++++---- core/src/avm1/object/super_object.rs | 18 +++++++++--------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/core/src/avm1/function.rs b/core/src/avm1/function.rs index 0f7755770..58d6eb0af 100644 --- a/core/src/avm1/function.rs +++ b/core/src/avm1/function.rs @@ -582,18 +582,19 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> { Attribute::DONT_ENUM, ); } + let base_proto = this.proto(activation).coerce_to_object(activation); if let Some(exec) = &self.data.read().constructor { let _ = exec.exec( "[ctor]", activation, this, - None, + Some(base_proto), args, ExecutionReason::FunctionCall, (*self).into(), )?; } else { - let _ = self.call("[ctor]".into(), activation, this, None, args)?; + let _ = self.call("[ctor]".into(), activation, this, Some(base_proto), args)?; } Ok(()) } @@ -629,14 +630,14 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> { "[ctor]", activation, this, - None, + Some(prototype), args, ExecutionReason::FunctionCall, (*self).into(), )?; Ok(this) } else { - let _ = self.call("[ctor]".into(), activation, this, None, args)?; + let _ = self.call("[ctor]".into(), activation, this, Some(prototype), args)?; Ok(this.into()) } } diff --git a/core/src/avm1/object/super_object.rs b/core/src/avm1/object/super_object.rs index cd7a1d31a..d5f3b587c 100644 --- a/core/src/avm1/object/super_object.rs +++ b/core/src/avm1/object/super_object.rs @@ -74,15 +74,15 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> { _base_proto: Option>, args: &[Value<'gc>], ) -> Result, Error<'gc>> { - if let Value::Object(proto) = self.proto(activation) { - let constructor = proto - .get("__constructor__", activation)? - .coerce_to_object(activation); - let this = self.0.read().this; - constructor.call(name, activation, this, Some(proto), args) - } else { - Ok(Value::Undefined) - } + let constructor = self + .0 + .read() + .base_proto + .get("__constructor__", activation)? + .coerce_to_object(activation); + let this = self.0.read().this; + let base_proto = self.proto(activation).coerce_to_object(activation); + constructor.call(name, activation, this, Some(base_proto), args) } fn call_method(