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.
This commit is contained in:
relrelb 2021-10-09 01:19:38 +03:00 committed by Mike Welsh
parent 1772def6e6
commit 7008f9bacc
2 changed files with 14 additions and 13 deletions

View File

@ -582,18 +582,19 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
Attribute::DONT_ENUM, Attribute::DONT_ENUM,
); );
} }
let base_proto = this.proto(activation).coerce_to_object(activation);
if let Some(exec) = &self.data.read().constructor { if let Some(exec) = &self.data.read().constructor {
let _ = exec.exec( let _ = exec.exec(
"[ctor]", "[ctor]",
activation, activation,
this, this,
None, Some(base_proto),
args, args,
ExecutionReason::FunctionCall, ExecutionReason::FunctionCall,
(*self).into(), (*self).into(),
)?; )?;
} else { } else {
let _ = self.call("[ctor]".into(), activation, this, None, args)?; let _ = self.call("[ctor]".into(), activation, this, Some(base_proto), args)?;
} }
Ok(()) Ok(())
} }
@ -629,14 +630,14 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
"[ctor]", "[ctor]",
activation, activation,
this, this,
None, Some(prototype),
args, args,
ExecutionReason::FunctionCall, ExecutionReason::FunctionCall,
(*self).into(), (*self).into(),
)?; )?;
Ok(this) Ok(this)
} else { } else {
let _ = self.call("[ctor]".into(), activation, this, None, args)?; let _ = self.call("[ctor]".into(), activation, this, Some(prototype), args)?;
Ok(this.into()) Ok(this.into())
} }
} }

View File

@ -74,15 +74,15 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> {
_base_proto: Option<Object<'gc>>, _base_proto: Option<Object<'gc>>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Value::Object(proto) = self.proto(activation) { let constructor = self
let constructor = proto .0
.read()
.base_proto
.get("__constructor__", activation)? .get("__constructor__", activation)?
.coerce_to_object(activation); .coerce_to_object(activation);
let this = self.0.read().this; let this = self.0.read().this;
constructor.call(name, activation, this, Some(proto), args) let base_proto = self.proto(activation).coerce_to_object(activation);
} else { constructor.call(name, activation, this, Some(base_proto), args)
Ok(Value::Undefined)
}
} }
fn call_method( fn call_method(