Add Attribute::DontEnum

This commit is contained in:
Nathan Adams 2019-10-08 16:34:08 +02:00
parent f782aaee18
commit 3d09ec81e2
7 changed files with 132 additions and 41 deletions

View File

@ -115,17 +115,22 @@ impl<'gc> Avm1<'gc> {
///
/// This is necessary to support form submission from Flash via a couple of
/// legacy methods, such as the `ActionGetURL2` opcode or `getURL` function.
pub fn locals_into_form_values(&self) -> HashMap<String, String> {
pub fn locals_into_form_values(
&mut self,
context: &mut ActionContext<'_, 'gc, '_>,
) -> HashMap<String, String> {
let mut form_values = HashMap::new();
for (k, v) in self
.current_stack_frame()
let locals = self
.current_stack_frame_mut()
.unwrap()
.scope()
.locals()
.iter_values()
{
form_values.insert(k.clone(), v.clone().into_string());
.scope_mut(context.gc_context)
.locals_cell();
for k in locals.read().get_keys() {
let v = locals
.write(context.gc_context)
.get(&k, self, context, locals);
form_values.insert(k, v.clone().into_string());
}
form_values
@ -877,8 +882,8 @@ impl<'gc> Avm1<'gc> {
}
};
for (k, _) in ob.read().iter_values() {
self.push(Value::String(k.to_owned()));
for k in ob.read().get_keys() {
self.push(Value::String(k));
}
Ok(())
@ -1061,7 +1066,7 @@ impl<'gc> Avm1<'gc> {
fn action_get_url_2(
&mut self,
context: &mut ActionContext,
context: &mut ActionContext<'_, 'gc, '_>,
swf_method: swf::avm1::types::SendVarsMethod,
is_target_sprite: bool,
is_load_vars: bool,
@ -1086,7 +1091,7 @@ impl<'gc> Avm1<'gc> {
}
let vars = match NavigationMethod::from_send_vars_method(swf_method) {
Some(method) => Some((method, self.locals_into_form_values())),
Some(method) => Some((method, self.locals_into_form_values(context))),
None => None,
};

View File

@ -211,7 +211,7 @@ impl<'gc> Executable<'gc> {
arguments.force_set(
"length",
Value::Number(args.len() as f64),
Attribute::DontDelete,
Attribute::DontDelete | Attribute::DontEnum,
);
let argcell = GcCell::allocate(ac.gc_context, arguments);
let child_scope = GcCell::allocate(
@ -258,7 +258,7 @@ impl<'gc> Executable<'gc> {
arguments.force_set(
"length",
Value::Number(args.len() as f64),
Attribute::DontDelete,
Attribute::DontDelete | Attribute::DontEnum,
);
}

View File

@ -28,7 +28,7 @@ pub fn getURL<'a, 'gc>(
Some(Value::String(s)) if s == "POST" => Some(NavigationMethod::POST),
_ => None,
};
let vars_method = method.map(|m| (m, avm.locals_into_form_values()));
let vars_method = method.map(|m| (m, avm.locals_into_form_values(context)));
context.navigator.navigate_to_url(url, window, vars_method);
}

View File

@ -17,7 +17,7 @@ macro_rules! wrap_std {
}
},
$gc_context,
Attribute::DontDelete | Attribute::ReadOnly,
Attribute::DontDelete | Attribute::ReadOnly | Attribute::DontEnum,
);
)*
}};
@ -54,42 +54,42 @@ pub fn create<'gc>(gc_context: MutationContext<'gc, '_>) -> GcCell<'gc, Object<'
math.force_set(
"E",
Value::Number(std::f64::consts::E),
Attribute::DontDelete | Attribute::ReadOnly,
Attribute::DontDelete | Attribute::ReadOnly | Attribute::DontEnum,
);
math.force_set(
"LN10",
Value::Number(std::f64::consts::LN_10),
Attribute::DontDelete | Attribute::ReadOnly,
Attribute::DontDelete | Attribute::ReadOnly | Attribute::DontEnum,
);
math.force_set(
"LN2",
Value::Number(std::f64::consts::LN_2),
Attribute::DontDelete | Attribute::ReadOnly,
Attribute::DontDelete | Attribute::ReadOnly | Attribute::DontEnum,
);
math.force_set(
"LOG10E",
Value::Number(std::f64::consts::LOG10_E),
Attribute::DontDelete | Attribute::ReadOnly,
Attribute::DontDelete | Attribute::ReadOnly | Attribute::DontEnum,
);
math.force_set(
"LOG2E",
Value::Number(std::f64::consts::LOG2_E),
Attribute::DontDelete | Attribute::ReadOnly,
Attribute::DontDelete | Attribute::ReadOnly | Attribute::DontEnum,
);
math.force_set(
"PI",
Value::Number(std::f64::consts::PI),
Attribute::DontDelete | Attribute::ReadOnly,
Attribute::DontDelete | Attribute::ReadOnly | Attribute::DontEnum,
);
math.force_set(
"SQRT1_2",
Value::Number(std::f64::consts::FRAC_1_SQRT_2),
Attribute::DontDelete | Attribute::ReadOnly,
Attribute::DontDelete | Attribute::ReadOnly | Attribute::DontEnum,
);
math.force_set(
"SQRT2",
Value::Number(std::f64::consts::SQRT_2),
Attribute::DontDelete | Attribute::ReadOnly,
Attribute::DontDelete | Attribute::ReadOnly | Attribute::DontEnum,
);
wrap_std!(math, gc_context,
@ -107,8 +107,18 @@ pub fn create<'gc>(gc_context: MutationContext<'gc, '_>) -> GcCell<'gc, Object<'
"tan" => f64::tan
);
math.force_set_function("atan2", atan2, gc_context, Attribute::DontDelete | Attribute::ReadOnly);
math.force_set_function("random", random, gc_context, Attribute::DontDelete | Attribute::ReadOnly);
math.force_set_function(
"atan2",
atan2,
gc_context,
Attribute::DontDelete | Attribute::ReadOnly | Attribute::DontEnum,
);
math.force_set_function(
"random",
random,
gc_context,
Attribute::DontDelete | Attribute::ReadOnly | Attribute::DontEnum,
);
GcCell::allocate(gc_context, math)
}

View File

@ -17,7 +17,7 @@ macro_rules! with_movie_clip {
Value::Undefined
},
$gc_context,
Attribute::DontDelete | Attribute::ReadOnly,
Attribute::DontDelete | Attribute::ReadOnly | Attribute::DontEnum,
);
)*
}};
@ -37,7 +37,7 @@ macro_rules! with_movie_clip_mut {
Value::Undefined
},
$gc_context,
Attribute::DontDelete | Attribute::ReadOnly,
Attribute::DontDelete | Attribute::ReadOnly | Attribute::DontEnum,
);
)*
}};

View File

@ -26,7 +26,7 @@ fn default_to_string<'gc>(
#[derive(EnumSetType, Debug)]
pub enum Attribute {
DontDelete,
// DontEnum,
DontEnum,
ReadOnly,
}
@ -69,7 +69,9 @@ impl<'gc> Property<'gc> {
function(avm, context, this, &[new_value]);
}
}
Property::Stored { value, attributes, .. } => {
Property::Stored {
value, attributes, ..
} => {
if !attributes.contains(Attribute::ReadOnly) {
replace::<Value<'gc>>(value, new_value);
}
@ -83,6 +85,13 @@ impl<'gc> Property<'gc> {
Property::Stored { attributes, .. } => !attributes.contains(Attribute::DontDelete),
}
}
pub fn is_enumerable(&self) -> bool {
match self {
Property::Virtual { attributes, .. } => !attributes.contains(Attribute::DontEnum),
Property::Stored { attributes, .. } => !attributes.contains(Attribute::DontEnum),
}
}
}
unsafe impl<'gc> gc_arena::Collect for Property<'gc> {
@ -157,7 +166,7 @@ impl<'gc> Object<'gc> {
"toString",
default_to_string,
gc_context,
Attribute::DontDelete,
Attribute::DontDelete | Attribute::DontEnum,
);
result
@ -338,11 +347,17 @@ impl<'gc> Object<'gc> {
self.values.contains_key(name)
}
pub fn iter_values(&self) -> impl Iterator<Item = (&String, &Value<'gc>)> {
self.values.iter().filter_map(|(k, p)| match p {
Property::Virtual { .. } => None,
Property::Stored { value, .. } => Some((k, value)),
})
pub fn get_keys(&self) -> Vec<String> {
self.values
.iter()
.filter_map(|(k, p)| {
if p.is_enumerable() {
Some(k.to_string())
} else {
None
}
})
.collect()
}
pub fn call(
@ -472,8 +487,20 @@ mod tests {
Attribute::ReadOnly,
);
object.write(context.gc_context).set("normal", Value::String("replaced".to_string()), avm, context, object);
object.write(context.gc_context).set("readonly", Value::String("replaced".to_string()), avm, context, object);
object.write(context.gc_context).set(
"normal",
Value::String("replaced".to_string()),
avm,
context,
object,
);
object.write(context.gc_context).set(
"readonly",
Value::String("replaced".to_string()),
avm,
context,
object,
);
assert_eq!(
object.read().get("normal", avm, context, object),
@ -501,7 +528,13 @@ mod tests {
Value::String("initial".to_string())
);
object.write(context.gc_context).set("test", Value::String("replaced".to_string()), avm, context, object);
object.write(context.gc_context).set(
"test",
Value::String("replaced".to_string()),
avm,
context,
object,
);
assert_eq!(object.write(context.gc_context).delete("test"), false);
assert_eq!(
@ -576,7 +609,10 @@ mod tests {
assert_eq!(object.write(context.gc_context).delete("virtual_un"), false);
assert_eq!(object.write(context.gc_context).delete("stored"), true);
assert_eq!(object.write(context.gc_context).delete("stored_un"), false);
assert_eq!(object.write(context.gc_context).delete("non_existent"), false);
assert_eq!(
object.write(context.gc_context).delete("non_existent"),
false
);
assert_eq!(
object.read().get("virtual", avm, context, object),
@ -596,4 +632,39 @@ mod tests {
);
})
}
#[test]
fn test_iter_values() {
with_object(0, |_avm, context, object| {
let getter: NativeFunction = |_avm, _context, _this, _args| Value::Null;
object
.write(context.gc_context)
.force_set("stored", Value::Null, EnumSet::empty());
object.write(context.gc_context).force_set(
"stored_hidden",
Value::Null,
Attribute::DontEnum,
);
object.write(context.gc_context).force_set_virtual(
"virtual",
getter,
None,
EnumSet::empty(),
);
object.write(context.gc_context).force_set_virtual(
"virtual_hidden",
getter,
None,
Attribute::DontEnum,
);
let keys = object.read().get_keys();
assert_eq!(keys.len(), 2);
assert_eq!(keys.contains(&"stored".to_string()), true);
assert_eq!(keys.contains(&"stored_hidden".to_string()), false);
assert_eq!(keys.contains(&"virtual".to_string()), true);
assert_eq!(keys.contains(&"virtual_hidden".to_string()), false);
})
}
}

View File

@ -210,6 +210,11 @@ impl<'gc> Scope<'gc> {
self.values.read()
}
/// Returns a gc cell of the current local scope object.
pub fn locals_cell(&self) -> GcCell<'gc, Object<'gc>> {
self.values.to_owned()
}
/// Returns a reference to the current local scope object for mutation.
pub fn locals_mut(&self, mc: MutationContext<'gc, '_>) -> RefMut<Object<'gc>> {
self.values.write(mc)