Skip to content

Commit 8a86535

Browse files
committed
avm1: Add Value::as_object to resolve MovieClips without coercing primitives
This removes some duplicated code, and clarifies the intent.
1 parent 9b3d688 commit 8a86535

File tree

3 files changed

+55
-116
lines changed

3 files changed

+55
-116
lines changed

core/src/avm1/activation.rs

Lines changed: 27 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,16 @@ impl<'a, 'gc> Activation<'a, 'gc> {
571571
self.context.avm1.push(value);
572572
}
573573

574+
fn pop_call_args(&mut self, num_args: usize) -> Vec<Value<'gc>> {
575+
(0..num_args.min(self.context.avm1.stack_len()))
576+
.map(|_| {
577+
let arg = self.context.avm1.pop();
578+
// Unwrap MovieClipReferences, if possible.
579+
arg.as_object(self).map(Value::from).unwrap_or(arg)
580+
})
581+
.collect()
582+
}
583+
574584
fn action_add(&mut self) -> Result<FrameControl<'gc>, Error<'gc>> {
575585
let a = self.context.avm1.pop();
576586
let b = self.context.avm1.pop();
@@ -754,16 +764,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
754764
let fn_name_value = self.context.avm1.pop();
755765
let fn_name = fn_name_value.coerce_to_string(self)?;
756766
let num_args = self.context.avm1.pop().coerce_to_u32(self)? as usize;
757-
let num_args = num_args.min(self.context.avm1.stack_len());
758-
let mut args = Vec::with_capacity(num_args);
759-
for _ in 0..num_args {
760-
let arg = self.context.avm1.pop();
761-
if let Value::MovieClip(_) = arg {
762-
args.push(Value::Object(arg.coerce_to_object(self)));
763-
} else {
764-
args.push(arg);
765-
}
766-
}
767+
let args = self.pop_call_args(num_args);
767768

768769
let variable = self.get_variable(fn_name)?;
769770

@@ -784,16 +785,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
784785
let method_name = self.context.avm1.pop();
785786
let object_val = self.context.avm1.pop();
786787
let num_args = self.context.avm1.pop().coerce_to_u32(self)? as usize;
787-
let num_args = num_args.min(self.context.avm1.stack_len());
788-
let mut args = Vec::with_capacity(num_args);
789-
for _ in 0..num_args {
790-
let arg = self.context.avm1.pop();
791-
if let Value::MovieClip(_) = arg {
792-
args.push(Value::Object(arg.coerce_to_object(self)));
793-
} else {
794-
args.push(arg);
795-
}
796-
}
788+
let args = self.pop_call_args(num_args);
797789

798790
// Can not call method on undefined/null.
799791
if matches!(object_val, Value::Undefined | Value::Null) {
@@ -825,13 +817,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
825817
let obj = self.context.avm1.pop();
826818
let constr = self.context.avm1.pop().coerce_to_object(self);
827819

828-
let is_instance_of = if let Value::Object(obj) = obj {
829-
let prototype = constr
830-
.get(istr!(self, "prototype"), self)?
831-
.coerce_to_object(self);
832-
obj.is_instance_of(self, constr, prototype)?
833-
} else if let Value::MovieClip(_) = obj {
834-
let obj = obj.coerce_to_object(self);
820+
let is_instance_of = if let Some(obj) = obj.as_object(self) {
835821
let prototype = constr
836822
.get(istr!(self, "prototype"), self)?
837823
.coerce_to_object(self);
@@ -944,10 +930,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
944930
let name = name_val.coerce_to_string(self)?;
945931
let object = self.context.avm1.pop();
946932

947-
let success = if let Value::Object(object) = object {
948-
object.delete(self, name)
949-
} else if let Value::MovieClip(_) = object {
950-
let object = object.coerce_to_object(self);
933+
let success = if let Some(object) = object.as_object(self) {
951934
object.delete(self, name)
952935
} else {
953936
avm_warn!(self, "Cannot delete property {} from {:?}", name, object);
@@ -1003,23 +986,16 @@ impl<'a, 'gc> Activation<'a, 'gc> {
1003986
fn action_enumerate(&mut self) -> Result<FrameControl<'gc>, Error<'gc>> {
1004987
let name_value = self.context.avm1.pop();
1005988
let name = name_value.coerce_to_string(self)?;
1006-
let object: Value<'gc> = self.get_variable(name)?.into();
989+
let value: Value<'gc> = self.get_variable(name)?.into();
1007990
self.context.avm1.push(Value::Undefined); // Sentinel that indicates end of enumeration
1008991

1009-
match object {
1010-
Value::MovieClip(_) => {
1011-
let ob = object.coerce_to_object(self);
1012-
for k in ob.get_keys(self, false).into_iter().rev() {
1013-
self.stack_push(k.into());
1014-
}
1015-
}
1016-
Value::Object(ob) => {
1017-
for k in ob.get_keys(self, false).into_iter().rev() {
1018-
self.stack_push(k.into());
1019-
}
992+
if let Some(object) = value.as_object(self) {
993+
for k in object.get_keys(self, false).into_iter().rev() {
994+
self.stack_push(k.into());
1020995
}
1021-
_ => avm_error!(self, "Cannot enumerate properties of {}", name),
1022-
};
996+
} else {
997+
avm_error!(self, "Cannot enumerate properties of {}", name);
998+
}
1023999

10241000
Ok(FrameControl::Continue)
10251001
}
@@ -1029,12 +1005,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
10291005

10301006
self.context.avm1.push(Value::Undefined); // Sentinel that indicates end of enumeration
10311007

1032-
if let Value::MovieClip(_) = value {
1033-
let object = value.coerce_to_object(self);
1034-
for k in object.get_keys(self, false).into_iter().rev() {
1035-
self.stack_push(k.into());
1036-
}
1037-
} else if let Value::Object(object) = value {
1008+
if let Some(object) = value.as_object(self) {
10381009
for k in object.get_keys(self, false).into_iter().rev() {
10391010
self.stack_push(k.into());
10401011
}
@@ -1253,11 +1224,8 @@ impl<'a, 'gc> Activation<'a, 'gc> {
12531224
let mut clip_target: Option<DisplayObject<'gc>> = if level_target > -1 {
12541225
self.get_level(level_target)
12551226
} else if action.is_load_vars() || action.is_target_sprite() {
1256-
if let Value::Object(target) = target_val {
1227+
if let Some(target) = target_val.as_object(self) {
12571228
target.as_display_object()
1258-
} else if let Value::MovieClip(_) = target_val {
1259-
let tgt = target_val.coerce_to_object(self);
1260-
tgt.as_display_object()
12611229
} else {
12621230
let start = self.target_clip_or_root();
12631231
self.resolve_target_display_object(start, target_val, true)?
@@ -1508,13 +1476,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
15081476
let constr = self.context.avm1.pop().coerce_to_object(self);
15091477
let obj = self.context.avm1.pop();
15101478

1511-
let result = if let Value::Object(obj) = obj {
1512-
let prototype = constr
1513-
.get(istr!(self, "prototype"), self)?
1514-
.coerce_to_object(self);
1515-
obj.is_instance_of(self, constr, prototype)?
1516-
} else if let Value::MovieClip(_) = obj {
1517-
let obj = obj.coerce_to_object(self);
1479+
let result = if let Some(obj) = obj.as_object(self) {
15181480
let prototype = constr
15191481
.get(istr!(self, "prototype"), self)?
15201482
.coerce_to_object(self);
@@ -1676,16 +1638,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
16761638
let method_name = self.context.avm1.pop();
16771639
let object_val = self.context.avm1.pop();
16781640
let num_args = self.context.avm1.pop().coerce_to_u32(self)? as usize;
1679-
let num_args = num_args.min(self.context.avm1.stack_len());
1680-
let mut args = Vec::with_capacity(num_args);
1681-
for _ in 0..num_args {
1682-
let arg = self.context.avm1.pop();
1683-
if let Value::MovieClip(_) = arg {
1684-
args.push(Value::Object(arg.coerce_to_object(self)));
1685-
} else {
1686-
args.push(arg);
1687-
}
1688-
}
1641+
let args = self.pop_call_args(num_args);
16891642

16901643
// Can not call method on undefined/null.
16911644
if matches!(object_val, Value::Undefined | Value::Null) {
@@ -1728,16 +1681,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
17281681
let fn_name_val = self.context.avm1.pop();
17291682
let fn_name = fn_name_val.coerce_to_string(self)?;
17301683
let num_args = self.context.avm1.pop().coerce_to_u32(self)? as usize;
1731-
let num_args = num_args.min(self.context.avm1.stack_len());
1732-
let mut args = Vec::with_capacity(num_args);
1733-
for _ in 0..num_args {
1734-
let arg = self.context.avm1.pop();
1735-
if let Value::MovieClip(_) = arg {
1736-
args.push(Value::Object(arg.coerce_to_object(self)));
1737-
} else {
1738-
args.push(arg);
1739-
}
1740-
}
1684+
let args = self.pop_call_args(num_args);
17411685

17421686
let name_value: Value<'gc> = self.resolve(fn_name)?.into();
17431687
let constructor = name_value.coerce_to_object(self);
@@ -2683,10 +2627,8 @@ impl<'a, 'gc> Activation<'a, 'gc> {
26832627
first_element = false;
26842628

26852629
// Resolve the value to an object while traversing the path.
2686-
object = if let Value::Object(o) = val {
2630+
object = if let Some(o) = val.as_object(self) {
26872631
o
2688-
} else if let Value::MovieClip(_) = val {
2689-
val.coerce_to_object(self)
26902632
} else {
26912633
return Ok(None);
26922634
};

core/src/avm1/globals/as_broadcaster.rs

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -155,21 +155,12 @@ pub fn broadcast_internal<'gc>(
155155
let length = listeners.length(activation)?;
156156
for i in 0..length {
157157
let listener = listeners.get_element(activation, i);
158-
159-
if let Value::Object(listener_obj) = listener {
158+
if let Some(obj) = listener.as_object(activation) {
160159
if method_name.is_empty() {
161-
listener_obj.call(method_name, activation, listener, call_args)?;
160+
obj.call(method_name, activation, listener, call_args)?;
162161
} else {
163-
listener_obj.call_method(
164-
method_name,
165-
call_args,
166-
activation,
167-
ExecutionReason::Special,
168-
)?;
162+
obj.call_method(method_name, call_args, activation, ExecutionReason::Special)?;
169163
}
170-
} else if let Value::MovieClip(_) = listener {
171-
let object = listener.coerce_to_object(activation);
172-
object.call_method(method_name, call_args, activation, ExecutionReason::Special)?;
173164
}
174165
}
175166

core/src/avm1/value.rs

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -460,32 +460,38 @@ impl<'gc> Value<'gc> {
460460
}
461461
}
462462

463+
/// Convert `self` to an script object, if possible. Unlike `coerce_to_object`, this
464+
/// doesn't coerce primitives.
465+
pub fn as_object(self, activation: &mut Activation<'_, 'gc>) -> Option<Object<'gc>> {
466+
match self {
467+
Value::Object(obj) => Some(obj),
468+
Value::MovieClip(mcr) => mcr.coerce_to_object(activation),
469+
_ => None,
470+
}
471+
}
472+
463473
pub fn coerce_to_object(self, activation: &mut Activation<'_, 'gc>) -> Object<'gc> {
464-
let (value, proto) = match self {
465-
// If we're given an object, we return it directly.
466-
Value::Object(obj) => return obj,
467-
Value::MovieClip(mcr) => {
468-
if let Some(obj) = mcr.coerce_to_object(activation) {
469-
return obj;
470-
} else {
471-
(Value::Undefined, None)
472-
}
473-
}
474-
// Else, select the correct prototype for it from the system prototypes list.
475-
Value::Null | Value::Undefined => (self, None),
476-
Value::Bool(_) => (self, Some(activation.prototypes().boolean)),
477-
Value::Number(_) => (self, Some(activation.prototypes().number)),
478-
Value::String(_) => (self, Some(activation.prototypes().string)),
474+
// If we're given an object, we return it directly.
475+
if let Some(obj) = self.as_object(activation) {
476+
return obj;
477+
}
478+
// Else, select the correct prototype for it from the system prototypes list.
479+
let proto = match self {
480+
Value::Object(_) => unreachable!(),
481+
Value::MovieClip(_) | Value::Null | Value::Undefined => None,
482+
Value::Bool(_) => Some(activation.prototypes().boolean),
483+
Value::Number(_) => Some(activation.prototypes().number),
484+
Value::String(_) => Some(activation.prototypes().string),
479485
};
480486

481487
let obj = Object::new(&activation.context.strings, proto);
482488

483489
// Constructor populates the boxed object with the value.
484490
use crate::avm1::globals;
485-
match value {
486-
Value::Bool(_) => drop(globals::boolean::constructor(activation, obj, &[value])),
487-
Value::Number(_) => drop(globals::number::constructor(activation, obj, &[value])),
488-
Value::String(_) => drop(globals::string::constructor(activation, obj, &[value])),
491+
match self {
492+
Value::Bool(_) => drop(globals::boolean::constructor(activation, obj, &[self])),
493+
Value::Number(_) => drop(globals::number::constructor(activation, obj, &[self])),
494+
Value::String(_) => drop(globals::string::constructor(activation, obj, &[self])),
489495
_ => (),
490496
}
491497

0 commit comments

Comments
 (0)