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:
parent
1772def6e6
commit
7008f9bacc
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(
|
||||||
|
|
Loading…
Reference in New Issue