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,
);
}
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())
}
}

View File

@ -74,15 +74,15 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> {
_base_proto: Option<Object<'gc>>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, 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(