Implement `isPropertyEnumerable` and `isPrototypeOf`.
This commit is contained in:
parent
aa7997b658
commit
b4e9b8442e
|
@ -1433,11 +1433,13 @@ impl<'gc> Avm1<'gc> {
|
||||||
|
|
||||||
let constructor = object
|
let constructor = object
|
||||||
.read()
|
.read()
|
||||||
.get(&method_name.as_string()?, self, context, object.to_owned())
|
.get(&method_name.as_string()?, self, context, object.to_owned())?
|
||||||
|
.resolve(self, context)?
|
||||||
.as_object()?;
|
.as_object()?;
|
||||||
let proto = constructor
|
let proto = constructor
|
||||||
.read()
|
.read()
|
||||||
.get("prototype", self, context, constructor.to_owned());
|
.get("prototype", self, context, constructor.to_owned())?
|
||||||
|
.resolve(self, context)?;
|
||||||
let this = GcCell::allocate(
|
let this = GcCell::allocate(
|
||||||
context.gc_context,
|
context.gc_context,
|
||||||
Object::object(
|
Object::object(
|
||||||
|
@ -1447,7 +1449,10 @@ impl<'gc> Avm1<'gc> {
|
||||||
);
|
);
|
||||||
|
|
||||||
//TODO: What happens if you `ActionNewMethod` without a method name?
|
//TODO: What happens if you `ActionNewMethod` without a method name?
|
||||||
constructor.read().call(self, context, this, &args);
|
constructor
|
||||||
|
.read()
|
||||||
|
.call(self, context, this, &args)?
|
||||||
|
.ignore();
|
||||||
|
|
||||||
//TODO: The SWF docs are really cagey about what the hell happens with
|
//TODO: The SWF docs are really cagey about what the hell happens with
|
||||||
//user defined constructors... do we need stack continuations?!
|
//user defined constructors... do we need stack continuations?!
|
||||||
|
@ -1470,11 +1475,13 @@ impl<'gc> Avm1<'gc> {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.clone()
|
.clone()
|
||||||
.read()
|
.read()
|
||||||
.resolve(fn_name.as_string()?, self, context)
|
.resolve(fn_name.as_string()?, self, context)?
|
||||||
|
.resolve(self, context)?
|
||||||
.as_object()?;
|
.as_object()?;
|
||||||
let proto = constructor
|
let proto = constructor
|
||||||
.read()
|
.read()
|
||||||
.get("prototype", self, context, constructor.to_owned());
|
.get("prototype", self, context, constructor.to_owned())?
|
||||||
|
.resolve(self, context)?;
|
||||||
let this = GcCell::allocate(
|
let this = GcCell::allocate(
|
||||||
context.gc_context,
|
context.gc_context,
|
||||||
Object::object(
|
Object::object(
|
||||||
|
@ -1483,7 +1490,10 @@ impl<'gc> Avm1<'gc> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
constructor.read().call(self, context, this, &args);
|
constructor
|
||||||
|
.read()
|
||||||
|
.call(self, context, this, &args)?
|
||||||
|
.ignore();
|
||||||
|
|
||||||
self.push(Value::Undefined);
|
self.push(Value::Undefined);
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ pub fn add_property<'gc>(
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: GcCell<'gc, Object<'gc>>,
|
this: GcCell<'gc, Object<'gc>>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Value<'gc> {
|
) -> Result<ReturnValue<'gc>, Error> {
|
||||||
let name = args.get(0).unwrap_or(&Value::Undefined);
|
let name = args.get(0).unwrap_or(&Value::Undefined);
|
||||||
let getter = args.get(1).unwrap_or(&Value::Undefined);
|
let getter = args.get(1).unwrap_or(&Value::Undefined);
|
||||||
let setter = args.get(2).unwrap_or(&Value::Undefined);
|
let setter = args.get(2).unwrap_or(&Value::Undefined);
|
||||||
|
@ -29,19 +29,19 @@ pub fn add_property<'gc>(
|
||||||
EnumSet::empty(),
|
EnumSet::empty(),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return Value::Bool(false);
|
return Ok(false.into());
|
||||||
}
|
}
|
||||||
} else if let Value::Null = setter {
|
} else if let Value::Null = setter {
|
||||||
this.write(context.gc_context)
|
this.write(context.gc_context)
|
||||||
.force_set_virtual(name, get_func, None, ReadOnly);
|
.force_set_virtual(name, get_func, None, ReadOnly);
|
||||||
} else {
|
} else {
|
||||||
return Value::Bool(false);
|
return Ok(false.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Value::Bool(false)
|
Ok(false.into())
|
||||||
}
|
}
|
||||||
_ => Value::Bool(false),
|
_ => Ok(false.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,6 +68,46 @@ fn to_string<'gc>(
|
||||||
Ok(ReturnValue::Immediate("[Object object]".into()))
|
Ok(ReturnValue::Immediate("[Object object]".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Implements `Object.prototype.isPropertyEnumerable`
|
||||||
|
fn is_property_enumerable<'gc>(
|
||||||
|
_: &mut Avm1<'gc>,
|
||||||
|
_: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
|
this: GcCell<'gc, Object<'gc>>,
|
||||||
|
args: &[Value<'gc>],
|
||||||
|
) -> Result<ReturnValue<'gc>, Error> {
|
||||||
|
match args.get(0) {
|
||||||
|
Some(Value::String(name)) => {
|
||||||
|
Ok(Value::Bool(this.read().is_property_enumerable(name)).into())
|
||||||
|
}
|
||||||
|
_ => Ok(Value::Bool(false).into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements `Object.prototype.isPrototypeOf`
|
||||||
|
fn is_prototype_of<'gc>(
|
||||||
|
_: &mut Avm1<'gc>,
|
||||||
|
_: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
|
this: GcCell<'gc, Object<'gc>>,
|
||||||
|
args: &[Value<'gc>],
|
||||||
|
) -> Result<ReturnValue<'gc>, Error> {
|
||||||
|
match args.get(0) {
|
||||||
|
Some(Value::Object(ob)) => {
|
||||||
|
let mut proto = ob.read().prototype().cloned();
|
||||||
|
|
||||||
|
while let Some(proto_ob) = proto {
|
||||||
|
if GcCell::ptr_eq(this, proto_ob) {
|
||||||
|
return Ok(Value::Bool(true).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
proto = proto_ob.read().prototype().cloned();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Value::Bool(false).into())
|
||||||
|
}
|
||||||
|
_ => Ok(Value::Bool(false).into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Partially construct `Object.prototype`.
|
/// Partially construct `Object.prototype`.
|
||||||
///
|
///
|
||||||
/// `__proto__` and other cross-linked properties of this object will *not*
|
/// `__proto__` and other cross-linked properties of this object will *not*
|
||||||
|
@ -88,14 +128,28 @@ pub fn fill_proto<'gc>(
|
||||||
"addProperty",
|
"addProperty",
|
||||||
add_property,
|
add_property,
|
||||||
gc_context,
|
gc_context,
|
||||||
EnumSet::empty(),
|
DontDelete | DontEnum,
|
||||||
Some(fn_proto),
|
Some(fn_proto),
|
||||||
);
|
);
|
||||||
ob_proto_write.force_set_function(
|
ob_proto_write.force_set_function(
|
||||||
"hasOwnProperty",
|
"hasOwnProperty",
|
||||||
has_own_property,
|
has_own_property,
|
||||||
gc_context,
|
gc_context,
|
||||||
EnumSet::empty(),
|
DontDelete | DontEnum,
|
||||||
|
Some(fn_proto),
|
||||||
|
);
|
||||||
|
ob_proto_write.force_set_function(
|
||||||
|
"isPropertyEnumerable",
|
||||||
|
is_property_enumerable,
|
||||||
|
gc_context,
|
||||||
|
DontDelete | DontEnum,
|
||||||
|
Some(fn_proto),
|
||||||
|
);
|
||||||
|
ob_proto_write.force_set_function(
|
||||||
|
"isPrototypeOf",
|
||||||
|
is_prototype_of,
|
||||||
|
gc_context,
|
||||||
|
DontDelete | DontEnum,
|
||||||
Some(fn_proto),
|
Some(fn_proto),
|
||||||
);
|
);
|
||||||
ob_proto_write.force_set_function(
|
ob_proto_write.force_set_function(
|
||||||
|
|
|
@ -389,6 +389,14 @@ impl<'gc> Object<'gc> {
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_property_enumerable(&self, name: &str) -> bool {
|
||||||
|
if let Some(prop) = self.values.get(name) {
|
||||||
|
prop.is_enumerable()
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_keys(&self) -> Vec<String> {
|
pub fn get_keys(&self) -> Vec<String> {
|
||||||
self.values
|
self.values
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -435,7 +443,7 @@ impl<'gc> Object<'gc> {
|
||||||
/// Returns a copy of a given function.
|
/// Returns a copy of a given function.
|
||||||
///
|
///
|
||||||
/// TODO: We have to clone here because of how executables are stored on
|
/// TODO: We have to clone here because of how executables are stored on
|
||||||
/// objects. This might not be a good idea for performance.
|
/// objects directly. This might not be a good idea for performance.
|
||||||
pub fn as_executable(&self) -> Option<Executable<'gc>> {
|
pub fn as_executable(&self) -> Option<Executable<'gc>> {
|
||||||
self.function.clone()
|
self.function.clone()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue