Implement `isPropertyEnumerable` and `isPrototypeOf`.

This commit is contained in:
David Wendt 2019-10-19 11:06:33 -04:00
parent aa7997b658
commit b4e9b8442e
3 changed files with 86 additions and 14 deletions

View File

@ -1433,11 +1433,13 @@ impl<'gc> Avm1<'gc> {
let constructor = object
.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()?;
let proto = constructor
.read()
.get("prototype", self, context, constructor.to_owned());
.get("prototype", self, context, constructor.to_owned())?
.resolve(self, context)?;
let this = GcCell::allocate(
context.gc_context,
Object::object(
@ -1447,7 +1449,10 @@ impl<'gc> Avm1<'gc> {
);
//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
//user defined constructors... do we need stack continuations?!
@ -1470,11 +1475,13 @@ impl<'gc> Avm1<'gc> {
.unwrap()
.clone()
.read()
.resolve(fn_name.as_string()?, self, context)
.resolve(fn_name.as_string()?, self, context)?
.resolve(self, context)?
.as_object()?;
let proto = constructor
.read()
.get("prototype", self, context, constructor.to_owned());
.get("prototype", self, context, constructor.to_owned())?
.resolve(self, context)?;
let this = GcCell::allocate(
context.gc_context,
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);

View File

@ -12,7 +12,7 @@ pub fn add_property<'gc>(
context: &mut UpdateContext<'_, 'gc, '_>,
this: GcCell<'gc, Object<'gc>>,
args: &[Value<'gc>],
) -> Value<'gc> {
) -> Result<ReturnValue<'gc>, Error> {
let name = args.get(0).unwrap_or(&Value::Undefined);
let getter = args.get(1).unwrap_or(&Value::Undefined);
let setter = args.get(2).unwrap_or(&Value::Undefined);
@ -29,19 +29,19 @@ pub fn add_property<'gc>(
EnumSet::empty(),
);
} else {
return Value::Bool(false);
return Ok(false.into());
}
} else if let Value::Null = setter {
this.write(context.gc_context)
.force_set_virtual(name, get_func, None, ReadOnly);
} 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()))
}
/// 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`.
///
/// `__proto__` and other cross-linked properties of this object will *not*
@ -88,14 +128,28 @@ pub fn fill_proto<'gc>(
"addProperty",
add_property,
gc_context,
EnumSet::empty(),
DontDelete | DontEnum,
Some(fn_proto),
);
ob_proto_write.force_set_function(
"hasOwnProperty",
has_own_property,
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),
);
ob_proto_write.force_set_function(

View File

@ -389,6 +389,14 @@ impl<'gc> Object<'gc> {
.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> {
self.values
.iter()
@ -435,7 +443,7 @@ impl<'gc> Object<'gc> {
/// Returns a copy of a given function.
///
/// 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>> {
self.function.clone()
}