avm1: Store `__proto__` as a regular property
`__proto__` seems to behave much like a regular data property. So simply remove the `prototype` field of `ScriptObject` in favor of storing the prototype in the general properties hash map.
This commit is contained in:
parent
962bd41732
commit
fd3f9f34de
|
@ -680,12 +680,16 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
|||
self.base.delete(activation, name)
|
||||
}
|
||||
|
||||
fn proto(&self) -> Value<'gc> {
|
||||
self.base.proto()
|
||||
fn proto(&self, activation: &mut Activation<'_, 'gc, '_>) -> Value<'gc> {
|
||||
self.base.proto(activation)
|
||||
}
|
||||
|
||||
fn set_proto(&self, gc_context: MutationContext<'gc, '_>, prototype: Value<'gc>) {
|
||||
self.base.set_proto(gc_context, prototype);
|
||||
fn set_proto(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
prototype: Value<'gc>,
|
||||
) -> Result<(), Error<'gc>> {
|
||||
self.base.set_proto(activation, prototype)
|
||||
}
|
||||
|
||||
fn define_value(
|
||||
|
|
|
@ -141,7 +141,7 @@ fn is_prototype_of<'gc>(
|
|||
match args.get(0) {
|
||||
Some(val) => {
|
||||
let ob = val.coerce_to_object(activation);
|
||||
Ok(this.is_prototype_of(ob).into())
|
||||
Ok(this.is_prototype_of(activation, ob).into())
|
||||
}
|
||||
_ => Ok(false.into()),
|
||||
}
|
||||
|
|
|
@ -114,10 +114,6 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
name: &str,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
if name == "__proto__" {
|
||||
return Ok(self.proto());
|
||||
}
|
||||
|
||||
let this = (*self).into();
|
||||
Ok(search_prototype(Value::Object(this), name, activation, this)?.0)
|
||||
}
|
||||
|
@ -142,11 +138,6 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
if name == "__proto__" {
|
||||
self.set_proto(activation.context.gc_context, value);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let this = (*self).into();
|
||||
if !self.has_own_property(activation, name) {
|
||||
// Before actually inserting a new property, we need to crawl the
|
||||
|
@ -170,7 +161,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
proto = this_proto.proto();
|
||||
proto = this_proto.proto(activation);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -273,14 +264,18 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
/// The proto is another object used to resolve methods across a class of
|
||||
/// multiple objects. It should also be accessible as `__proto__` from
|
||||
/// `get`.
|
||||
fn proto(&self) -> Value<'gc>;
|
||||
fn proto(&self, activation: &mut Activation<'_, 'gc, '_>) -> Value<'gc>;
|
||||
|
||||
/// Sets the `__proto__` of a given object.
|
||||
///
|
||||
/// The proto is another object used to resolve methods across a class of
|
||||
/// multiple objects. It should also be accessible as `__proto__` in
|
||||
/// `set`.
|
||||
fn set_proto(&self, gc_context: MutationContext<'gc, '_>, prototype: Value<'gc>);
|
||||
fn set_proto(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
prototype: Value<'gc>,
|
||||
) -> Result<(), Error<'gc>>;
|
||||
|
||||
/// Define a value on an object.
|
||||
///
|
||||
|
@ -415,7 +410,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
prototype: Object<'gc>,
|
||||
) -> Result<bool, Error<'gc>> {
|
||||
let mut proto_stack = vec![];
|
||||
if let Value::Object(p) = self.proto() {
|
||||
if let Value::Object(p) = self.proto(activation) {
|
||||
proto_stack.push(p);
|
||||
}
|
||||
|
||||
|
@ -424,7 +419,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
return Ok(true);
|
||||
}
|
||||
|
||||
if let Value::Object(p) = this_proto.proto() {
|
||||
if let Value::Object(p) = this_proto.proto(activation) {
|
||||
proto_stack.push(p);
|
||||
}
|
||||
|
||||
|
@ -560,15 +555,19 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
fn as_ptr(&self) -> *const ObjectPtr;
|
||||
|
||||
/// Check if this object is in the prototype chain of the specified test object.
|
||||
fn is_prototype_of(&self, other: Object<'gc>) -> bool {
|
||||
let mut proto = other.proto();
|
||||
fn is_prototype_of(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
other: Object<'gc>,
|
||||
) -> bool {
|
||||
let mut proto = other.proto(activation);
|
||||
|
||||
while let Value::Object(proto_ob) = proto {
|
||||
if self.as_ptr() == proto_ob.as_ptr() {
|
||||
return true;
|
||||
}
|
||||
|
||||
proto = proto_ob.proto();
|
||||
proto = proto_ob.proto(activation);
|
||||
}
|
||||
|
||||
false
|
||||
|
@ -635,7 +634,7 @@ pub fn search_prototype<'gc>(
|
|||
return Ok((value?, Some(p)));
|
||||
}
|
||||
|
||||
proto = p.proto();
|
||||
proto = p.proto(activation);
|
||||
depth += 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -221,12 +221,16 @@ impl<'gc> TObject<'gc> for ArrayObject<'gc> {
|
|||
.set_attributes(gc_context, name, set_attributes, clear_attributes)
|
||||
}
|
||||
|
||||
fn proto(&self) -> Value<'gc> {
|
||||
self.0.read().proto()
|
||||
fn proto(&self, activation: &mut Activation<'_, 'gc, '_>) -> Value<'gc> {
|
||||
self.0.read().proto(activation)
|
||||
}
|
||||
|
||||
fn set_proto(&self, gc_context: MutationContext<'gc, '_>, prototype: Value<'gc>) {
|
||||
self.0.read().set_proto(gc_context, prototype);
|
||||
fn set_proto(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
prototype: Value<'gc>,
|
||||
) -> Result<(), Error<'gc>> {
|
||||
self.0.read().set_proto(activation, prototype)
|
||||
}
|
||||
|
||||
fn has_property(&self, activation: &mut Activation<'_, 'gc, '_>, name: &str) -> bool {
|
||||
|
|
|
@ -93,16 +93,16 @@ macro_rules! impl_custom_object {
|
|||
self.0.read().$field.delete(activation, name)
|
||||
}
|
||||
|
||||
fn proto(&self) -> crate::avm1::Value<'gc> {
|
||||
self.0.read().$field.proto()
|
||||
fn proto(&self, activation: &mut crate::avm1::Activation<'_, 'gc, '_>) -> crate::avm1::Value<'gc> {
|
||||
self.0.read().$field.proto(activation)
|
||||
}
|
||||
|
||||
fn set_proto(
|
||||
&self,
|
||||
gc_context: gc_arena::MutationContext<'gc, '_>,
|
||||
activation: &mut crate::avm1::Activation<'_, 'gc, '_>,
|
||||
prototype: crate::avm1::Value<'gc>,
|
||||
) {
|
||||
self.0.read().$field.set_proto(gc_context, prototype);
|
||||
) -> Result<(), crate::avm1::Error<'gc>> {
|
||||
self.0.read().$field.set_proto(activation, prototype)
|
||||
}
|
||||
|
||||
fn define_value(
|
||||
|
|
|
@ -66,7 +66,6 @@ pub struct ScriptObject<'gc>(GcCell<'gc, ScriptObjectData<'gc>>);
|
|||
#[derive(Collect)]
|
||||
#[collect(no_drop)]
|
||||
pub struct ScriptObjectData<'gc> {
|
||||
prototype: Value<'gc>,
|
||||
values: PropertyMap<Property<'gc>>,
|
||||
interfaces: Vec<Object<'gc>>,
|
||||
type_of: &'static str,
|
||||
|
@ -76,7 +75,6 @@ pub struct ScriptObjectData<'gc> {
|
|||
impl fmt::Debug for ScriptObjectData<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Object")
|
||||
.field("prototype", &self.prototype)
|
||||
.field("values", &self.values)
|
||||
.field("watchers", &self.watchers)
|
||||
.finish()
|
||||
|
@ -85,16 +83,24 @@ impl fmt::Debug for ScriptObjectData<'_> {
|
|||
|
||||
impl<'gc> ScriptObject<'gc> {
|
||||
pub fn object(gc_context: MutationContext<'gc, '_>, proto: Option<Object<'gc>>) -> Self {
|
||||
Self(GcCell::allocate(
|
||||
let object = Self(GcCell::allocate(
|
||||
gc_context,
|
||||
ScriptObjectData {
|
||||
prototype: proto.map_or(Value::Undefined, Value::Object),
|
||||
type_of: TYPE_OF_OBJECT,
|
||||
values: PropertyMap::new(),
|
||||
interfaces: vec![],
|
||||
watchers: PropertyMap::new(),
|
||||
},
|
||||
))
|
||||
));
|
||||
if let Some(proto) = proto {
|
||||
object.define_value(
|
||||
gc_context,
|
||||
"__proto__",
|
||||
proto.into(),
|
||||
Attribute::DONT_ENUM | Attribute::DONT_DELETE,
|
||||
);
|
||||
}
|
||||
object
|
||||
}
|
||||
|
||||
/// Constructs and allocates an empty but normal object in one go.
|
||||
|
@ -429,18 +435,22 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
|||
}
|
||||
}
|
||||
|
||||
fn proto(&self) -> Value<'gc> {
|
||||
self.0.read().prototype
|
||||
fn proto(&self, activation: &mut Activation<'_, 'gc, '_>) -> Value<'gc> {
|
||||
self.get_data("__proto__", activation)
|
||||
}
|
||||
|
||||
fn set_proto(&self, gc_context: MutationContext<'gc, '_>, prototype: Value<'gc>) {
|
||||
self.0.write(gc_context).prototype = prototype;
|
||||
fn set_proto(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
prototype: Value<'gc>,
|
||||
) -> Result<(), Error<'gc>> {
|
||||
self.set_data("__proto__", prototype, activation)
|
||||
}
|
||||
|
||||
/// Checks if the object has a given named property.
|
||||
fn has_property(&self, activation: &mut Activation<'_, 'gc, '_>, name: &str) -> bool {
|
||||
self.has_own_property(activation, name)
|
||||
|| if let Value::Object(proto) = self.proto() {
|
||||
|| if let Value::Object(proto) = self.proto(activation) {
|
||||
proto.has_property(activation, name)
|
||||
} else {
|
||||
false
|
||||
|
@ -450,9 +460,6 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
|||
/// Checks if the object has a given named property on itself (and not,
|
||||
/// say, the object's prototype or superclass)
|
||||
fn has_own_property(&self, activation: &mut Activation<'_, 'gc, '_>, name: &str) -> bool {
|
||||
if name == "__proto__" {
|
||||
return true;
|
||||
}
|
||||
self.0
|
||||
.read()
|
||||
.values
|
||||
|
@ -488,7 +495,7 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
|||
|
||||
/// Enumerate the object.
|
||||
fn get_keys(&self, activation: &mut Activation<'_, 'gc, '_>) -> Vec<String> {
|
||||
let proto_keys = if let Value::Object(proto) = self.proto() {
|
||||
let proto_keys = if let Value::Object(proto) = self.proto(activation) {
|
||||
proto.get_keys(activation)
|
||||
} else {
|
||||
Vec::new()
|
||||
|
|
|
@ -269,12 +269,16 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
|
|||
self.0.read().base.delete(activation, name)
|
||||
}
|
||||
|
||||
fn proto(&self) -> Value<'gc> {
|
||||
self.0.read().base.proto()
|
||||
fn proto(&self, activation: &mut Activation<'_, 'gc, '_>) -> Value<'gc> {
|
||||
self.0.read().base.proto(activation)
|
||||
}
|
||||
|
||||
fn set_proto(&self, gc_context: MutationContext<'gc, '_>, prototype: Value<'gc>) {
|
||||
self.0.read().base.set_proto(gc_context, prototype);
|
||||
fn set_proto(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
prototype: Value<'gc>,
|
||||
) -> Result<(), Error<'gc>> {
|
||||
self.0.read().base.set_proto(activation, prototype)
|
||||
}
|
||||
|
||||
fn define_value(
|
||||
|
|
|
@ -55,17 +55,12 @@ impl<'gc> SuperObject<'gc> {
|
|||
)))
|
||||
}
|
||||
|
||||
/// Retrieve the prototype that `super` should be pulling from.
|
||||
fn super_proto(self) -> Value<'gc> {
|
||||
self.0.read().base_proto.proto()
|
||||
}
|
||||
|
||||
/// Retrieve the constructor associated with the super proto.
|
||||
fn super_constr(
|
||||
self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
) -> Result<Option<Object<'gc>>, Error<'gc>> {
|
||||
if let Value::Object(super_proto) = self.super_proto() {
|
||||
if let Value::Object(super_proto) = self.proto(activation) {
|
||||
Ok(Some(
|
||||
super_proto
|
||||
.get("__constructor__", activation)?
|
||||
|
@ -107,7 +102,7 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> {
|
|||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
if let Some(constr) = self.super_constr(activation)? {
|
||||
let super_proto = match self.super_proto() {
|
||||
let super_proto = match self.proto(activation) {
|
||||
Value::Object(o) => Some(o),
|
||||
_ => None,
|
||||
};
|
||||
|
@ -124,7 +119,8 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> {
|
|||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let child = self.0.read().child;
|
||||
let (method, base_proto) = search_prototype(self.super_proto(), name, activation, child)?;
|
||||
let (method, base_proto) =
|
||||
search_prototype(self.proto(activation), name, activation, child)?;
|
||||
|
||||
if method.is_primitive() {
|
||||
avm_warn!(activation, "Super method {} is not callable", name);
|
||||
|
@ -147,7 +143,7 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> {
|
|||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
this: Object<'gc>,
|
||||
) -> Result<Object<'gc>, Error<'gc>> {
|
||||
if let Value::Object(proto) = self.proto() {
|
||||
if let Value::Object(proto) = self.proto(activation) {
|
||||
proto.create_bare_object(activation, this)
|
||||
} else {
|
||||
// TODO: What happens when you `new super` but there's no
|
||||
|
@ -161,14 +157,19 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> {
|
|||
false
|
||||
}
|
||||
|
||||
fn proto(&self) -> Value<'gc> {
|
||||
self.super_proto()
|
||||
fn proto(&self, activation: &mut Activation<'_, 'gc, '_>) -> Value<'gc> {
|
||||
self.0.read().base_proto.proto(activation)
|
||||
}
|
||||
|
||||
fn set_proto(&self, gc_context: MutationContext<'gc, '_>, prototype: Value<'gc>) {
|
||||
fn set_proto(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
prototype: Value<'gc>,
|
||||
) -> Result<(), Error<'gc>> {
|
||||
if let Value::Object(prototype) = prototype {
|
||||
self.0.write(gc_context).base_proto = prototype;
|
||||
self.0.write(activation.context.gc_context).base_proto = prototype;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn define_value(
|
||||
|
|
|
@ -178,12 +178,16 @@ impl<'gc> TObject<'gc> for XmlAttributesObject<'gc> {
|
|||
.set_attributes(gc_context, name, set_attributes, clear_attributes)
|
||||
}
|
||||
|
||||
fn proto(&self) -> Value<'gc> {
|
||||
self.base().proto()
|
||||
fn proto(&self, activation: &mut Activation<'_, 'gc, '_>) -> Value<'gc> {
|
||||
self.base().proto(activation)
|
||||
}
|
||||
|
||||
fn set_proto(&self, gc_context: MutationContext<'gc, '_>, prototype: Value<'gc>) {
|
||||
self.base().set_proto(gc_context, prototype);
|
||||
fn set_proto(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
prototype: Value<'gc>,
|
||||
) -> Result<(), Error<'gc>> {
|
||||
self.base().set_proto(activation, prototype)
|
||||
}
|
||||
|
||||
fn has_property(&self, activation: &mut Activation<'_, 'gc, '_>, name: &str) -> bool {
|
||||
|
|
|
@ -179,12 +179,16 @@ impl<'gc> TObject<'gc> for XmlIdMapObject<'gc> {
|
|||
.set_attributes(gc_context, name, set_attributes, clear_attributes)
|
||||
}
|
||||
|
||||
fn proto(&self) -> Value<'gc> {
|
||||
self.base().proto()
|
||||
fn proto(&self, activation: &mut Activation<'_, 'gc, '_>) -> Value<'gc> {
|
||||
self.base().proto(activation)
|
||||
}
|
||||
|
||||
fn set_proto(&self, gc_context: MutationContext<'gc, '_>, prototype: Value<'gc>) {
|
||||
self.base().set_proto(gc_context, prototype);
|
||||
fn set_proto(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
prototype: Value<'gc>,
|
||||
) -> Result<(), Error<'gc>> {
|
||||
self.base().set_proto(activation, prototype)
|
||||
}
|
||||
|
||||
fn has_property(&self, activation: &mut Activation<'_, 'gc, '_>, name: &str) -> bool {
|
||||
|
|
|
@ -1309,7 +1309,7 @@ impl Player {
|
|||
);
|
||||
if let Ok(prototype) = constructor.get("prototype", &mut activation) {
|
||||
if let Value::Object(object) = actions.clip.object() {
|
||||
object.set_proto(activation.context.gc_context, prototype);
|
||||
object.set_proto(&mut activation, prototype).unwrap(); // TODO: What happens on error?
|
||||
for event in events {
|
||||
let _ = activation.run_child_frame_for_action(
|
||||
"[Actions]",
|
||||
|
|
Loading…
Reference in New Issue