Abolish `context.root` completely.
`_root` is calculated dynamically based on the clip the currently executing function was called in. Other things that used `context.root` have been changed to either update all layers or just update layer 0, which is the former `context.root`.
This commit is contained in:
parent
88b10f21c4
commit
aa6aba13dd
|
@ -149,15 +149,14 @@ impl<'gc> Avm1<'gc> {
|
|||
|
||||
/// The current target clip of the executing code, or `root` if there is none.
|
||||
/// Actions that affect `root` after an invalid `tellTarget` will use this.
|
||||
pub fn target_clip_or_root(
|
||||
&self,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
) -> DisplayObject<'gc> {
|
||||
///
|
||||
/// The `root` is determined relative to the base clip that defined the
|
||||
pub fn target_clip_or_root(&self) -> DisplayObject<'gc> {
|
||||
self.current_stack_frame()
|
||||
.unwrap()
|
||||
.read()
|
||||
.target_clip()
|
||||
.unwrap_or(context.root)
|
||||
.unwrap_or_else(|| self.base_clip().root())
|
||||
}
|
||||
|
||||
/// Convert the current locals pool into a set of form values.
|
||||
|
@ -695,7 +694,7 @@ impl<'gc> Avm1<'gc> {
|
|||
start: DisplayObject<'gc>,
|
||||
path: &str,
|
||||
) -> Result<Option<Object<'gc>>, Error> {
|
||||
let root = context.root;
|
||||
let root = start.root();
|
||||
|
||||
// Empty path resolves immediately to start clip.
|
||||
if path.is_empty() {
|
||||
|
@ -809,8 +808,7 @@ impl<'gc> Avm1<'gc> {
|
|||
path: &'s str,
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
// Resolve a variable path for a GetVariable action.
|
||||
let root = context.root;
|
||||
let start = self.target_clip().unwrap_or(root);
|
||||
let start = self.target_clip_or_root();
|
||||
|
||||
// Find the right-most : or . in the path.
|
||||
// If we have one, we must resolve as a target path.
|
||||
|
@ -878,8 +876,7 @@ impl<'gc> Avm1<'gc> {
|
|||
value: Value<'gc>,
|
||||
) -> Result<(), Error> {
|
||||
// Resolve a variable path for a GetVariable action.
|
||||
let root = context.root;
|
||||
let start = self.target_clip().unwrap_or(root);
|
||||
let start = self.target_clip_or_root();
|
||||
|
||||
// If the target clip is invalid, we default to root for the variable path.
|
||||
if path.is_empty() {
|
||||
|
@ -1092,7 +1089,7 @@ impl<'gc> Avm1<'gc> {
|
|||
let depth = self.pop();
|
||||
let target = self.pop();
|
||||
let source = self.pop();
|
||||
let start_clip = self.target_clip_or_root(context);
|
||||
let start_clip = self.target_clip_or_root();
|
||||
let source_clip = self.resolve_target_display_object(context, start_clip, source)?;
|
||||
|
||||
if let Some(movie_clip) = source_clip.and_then(|o| o.as_movie_clip()) {
|
||||
|
@ -1161,7 +1158,7 @@ impl<'gc> Avm1<'gc> {
|
|||
fn action_call(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) -> Result<(), Error> {
|
||||
// Runs any actions on the given frame.
|
||||
let frame = self.pop();
|
||||
let clip = self.target_clip_or_root(context);
|
||||
let clip = self.target_clip_or_root();
|
||||
if let Some(clip) = clip.as_movie_clip() {
|
||||
// Use frame # if parameter is a number, otherwise cast to string and check for frame labels.
|
||||
let frame = if let Ok(frame) = frame.as_u32() {
|
||||
|
@ -1180,7 +1177,7 @@ impl<'gc> Avm1<'gc> {
|
|||
// so we want to push the stack frames in reverse order.
|
||||
for action in clip.actions_on_frame(context, frame).rev() {
|
||||
self.insert_stack_frame_for_action(
|
||||
self.target_clip_or_root(context),
|
||||
self.target_clip_or_root(),
|
||||
self.current_swf_version(),
|
||||
action,
|
||||
context,
|
||||
|
@ -1214,7 +1211,7 @@ impl<'gc> Avm1<'gc> {
|
|||
.read()
|
||||
.resolve(fn_name.as_string()?, self, context)?
|
||||
.resolve(self, context)?;
|
||||
let this = self.target_clip_or_root(context).object().as_object()?;
|
||||
let this = self.target_clip_or_root().object().as_object()?;
|
||||
target_fn.call(self, context, this, &args)?.push(self);
|
||||
|
||||
Ok(())
|
||||
|
@ -1235,7 +1232,7 @@ impl<'gc> Avm1<'gc> {
|
|||
|
||||
match method_name {
|
||||
Value::Undefined | Value::Null => {
|
||||
let this = self.target_clip_or_root(context).object();
|
||||
let this = self.target_clip_or_root().object();
|
||||
if let Ok(this) = this.as_object() {
|
||||
object.call(self, context, this, &args)?.push(self);
|
||||
} else {
|
||||
|
@ -1340,7 +1337,7 @@ impl<'gc> Avm1<'gc> {
|
|||
params,
|
||||
scope,
|
||||
constant_pool,
|
||||
self.target_clip_or_root(context),
|
||||
self.target_clip_or_root(),
|
||||
);
|
||||
let prototype =
|
||||
ScriptObject::object(context.gc_context, Some(self.prototypes.object)).into();
|
||||
|
@ -1606,8 +1603,8 @@ impl<'gc> Avm1<'gc> {
|
|||
}
|
||||
|
||||
/// Obtain the value of `_root`.
|
||||
pub fn root_object(&self, context: &mut UpdateContext<'_, 'gc, '_>) -> Value<'gc> {
|
||||
context.root.object()
|
||||
pub fn root_object(&self, _context: &mut UpdateContext<'_, 'gc, '_>) -> Value<'gc> {
|
||||
self.base_clip().root().object()
|
||||
}
|
||||
|
||||
/// Obtain the value of `_global`.
|
||||
|
@ -1692,11 +1689,11 @@ impl<'gc> Avm1<'gc> {
|
|||
if let Value::Object(target) = target {
|
||||
target.as_display_object()
|
||||
} else {
|
||||
let start = self.target_clip_or_root(context);
|
||||
let start = self.target_clip_or_root();
|
||||
self.resolve_target_display_object(context, start, target.clone())?
|
||||
}
|
||||
} else {
|
||||
Some(self.target_clip_or_root(context))
|
||||
Some(self.target_clip_or_root())
|
||||
};
|
||||
|
||||
if is_load_vars {
|
||||
|
@ -2209,7 +2206,7 @@ impl<'gc> Avm1<'gc> {
|
|||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
) -> Result<(), Error> {
|
||||
let target = self.pop();
|
||||
let start_clip = self.target_clip_or_root(context);
|
||||
let start_clip = self.target_clip_or_root();
|
||||
let target_clip = self.resolve_target_display_object(context, start_clip, target)?;
|
||||
|
||||
if let Some(target_clip) = target_clip.and_then(|o| o.as_movie_clip()) {
|
||||
|
@ -2295,16 +2292,15 @@ impl<'gc> Avm1<'gc> {
|
|||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
target: &str,
|
||||
) -> Result<(), Error> {
|
||||
let stack_frame = self.current_stack_frame().unwrap();
|
||||
let mut sf = stack_frame.write(context.gc_context);
|
||||
let base_clip = sf.base_clip();
|
||||
let base_clip = self.base_clip();
|
||||
let new_target_clip;
|
||||
if target.is_empty() {
|
||||
sf.set_target_clip(Some(base_clip));
|
||||
new_target_clip = Some(base_clip);
|
||||
} else if let Some(clip) = self
|
||||
.resolve_target_path(context, base_clip, target)?
|
||||
.and_then(|o| o.as_display_object())
|
||||
{
|
||||
sf.set_target_clip(Some(clip));
|
||||
new_target_clip = Some(clip);
|
||||
} else {
|
||||
log::warn!("SetTarget failed: {} not found", target);
|
||||
// TODO: Emulate AVM1 trace error message.
|
||||
|
@ -2313,13 +2309,17 @@ impl<'gc> Avm1<'gc> {
|
|||
// When SetTarget has an invalid target, subsequent GetVariables act
|
||||
// as if they are targeting root, but subsequent Play/Stop/etc.
|
||||
// fail silenty.
|
||||
sf.set_target_clip(None);
|
||||
new_target_clip = None;
|
||||
}
|
||||
|
||||
let stack_frame = self.current_stack_frame().unwrap();
|
||||
let mut sf = stack_frame.write(context.gc_context);
|
||||
sf.set_target_clip(new_target_clip);
|
||||
|
||||
let scope = sf.scope_cell();
|
||||
let clip_obj = sf
|
||||
.target_clip()
|
||||
.unwrap_or(context.root)
|
||||
.unwrap_or_else(|| sf.base_clip().root())
|
||||
.object()
|
||||
.as_object()
|
||||
.unwrap();
|
||||
|
@ -2367,7 +2367,7 @@ impl<'gc> Avm1<'gc> {
|
|||
let scope = sf.scope_cell();
|
||||
let clip_obj = sf
|
||||
.target_clip()
|
||||
.unwrap_or(context.root)
|
||||
.unwrap_or_else(|| sf.base_clip().root())
|
||||
.object()
|
||||
.as_object()
|
||||
.unwrap();
|
||||
|
@ -2385,7 +2385,7 @@ impl<'gc> Avm1<'gc> {
|
|||
|
||||
fn action_start_drag(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) -> Result<(), Error> {
|
||||
let target = self.pop();
|
||||
let start_clip = self.target_clip_or_root(context);
|
||||
let start_clip = self.target_clip_or_root();
|
||||
let display_object = self.resolve_target_display_object(context, start_clip, target)?;
|
||||
if let Some(display_object) = display_object {
|
||||
let lock_center = self.pop();
|
||||
|
|
|
@ -82,7 +82,7 @@ fn target<'gc>(
|
|||
// This means calls on the same `Color` object could set the color of different clips
|
||||
// depending on which timeline its called from!
|
||||
let target = this.get("target", avm, context)?.resolve(avm, context)?;
|
||||
let start_clip = avm.target_clip_or_root(context);
|
||||
let start_clip = avm.target_clip_or_root();
|
||||
avm.resolve_target_display_object(context, start_clip, target)
|
||||
}
|
||||
|
||||
|
|
|
@ -102,8 +102,8 @@ pub fn hit_test<'gc>(
|
|||
if x.is_finite() && y.is_finite() {
|
||||
// The docs say the point is in "Stage coordinates", but actually they are in root coordinates.
|
||||
// root can be moved via _root._x etc., so we actually have to transform from root to world space.
|
||||
let point = context
|
||||
.root
|
||||
let point = movie_clip
|
||||
.root()
|
||||
.local_to_global((Twips::from_pixels(x), Twips::from_pixels(y)));
|
||||
return Ok(movie_clip.hit_test(point).into());
|
||||
}
|
||||
|
|
|
@ -800,7 +800,7 @@ pub fn xml_load<'gc>(
|
|||
this.set("loaded", false.into(), avm, ac)?;
|
||||
|
||||
let fetch = ac.navigator.fetch(url, RequestOptions::get());
|
||||
let target_clip = avm.target_clip_or_root(ac);
|
||||
let target_clip = avm.target_clip_or_root();
|
||||
let process = ac.load_manager.load_xml_into_node(
|
||||
ac.player.clone().unwrap(),
|
||||
node,
|
||||
|
|
|
@ -597,7 +597,6 @@ mod tests {
|
|||
player_version: 32,
|
||||
swf: &swf,
|
||||
layers: &mut layers,
|
||||
root,
|
||||
rng: &mut SmallRng::from_seed([0u8; 16]),
|
||||
action_queue: &mut crate::context::ActionQueue::new(),
|
||||
audio: &mut NullAudioBackend::new(),
|
||||
|
|
|
@ -36,7 +36,6 @@ where
|
|||
player_version: 32,
|
||||
swf: &swf,
|
||||
layers: &mut layers,
|
||||
root,
|
||||
rng: &mut SmallRng::from_seed([0u8; 16]),
|
||||
audio: &mut NullAudioBackend::new(),
|
||||
input: &mut NullInputBackend::new(),
|
||||
|
|
|
@ -10,7 +10,7 @@ fn locals_into_form_values() {
|
|||
19,
|
||||
avm.global_object_cell(),
|
||||
context.gc_context,
|
||||
context.root,
|
||||
*context.layers.get(&0).expect("root layer in test"),
|
||||
);
|
||||
let my_locals = my_activation.scope().locals().to_owned();
|
||||
|
||||
|
|
|
@ -68,10 +68,6 @@ pub struct UpdateContext<'a, 'gc, 'gc_context> {
|
|||
/// All loaded layers of the current player.
|
||||
pub layers: &'a mut BTreeMap<u32, DisplayObject<'gc>>,
|
||||
|
||||
/// The root of the current timeline being updated.
|
||||
/// This will always be one of the layers in `layers`.
|
||||
pub root: DisplayObject<'gc>,
|
||||
|
||||
/// The current set of system-specified prototypes to use when constructing
|
||||
/// new built-in objects.
|
||||
pub system_prototypes: avm1::SystemPrototypes<'gc>,
|
||||
|
@ -106,9 +102,6 @@ pub struct QueuedActions<'gc> {
|
|||
/// The movie clip this ActionScript is running on.
|
||||
pub clip: DisplayObject<'gc>,
|
||||
|
||||
/// The root timeline this action was queued in.
|
||||
pub root: DisplayObject<'gc>,
|
||||
|
||||
/// The type of action this is, along with the corresponding bytecode/method data.
|
||||
pub action_type: ActionType<'gc>,
|
||||
|
||||
|
@ -145,13 +138,11 @@ impl<'gc> ActionQueue<'gc> {
|
|||
pub fn queue_actions(
|
||||
&mut self,
|
||||
clip: DisplayObject<'gc>,
|
||||
root: DisplayObject<'gc>,
|
||||
action_type: ActionType<'gc>,
|
||||
is_unload: bool,
|
||||
) {
|
||||
self.queue.push_back(QueuedActions {
|
||||
clip,
|
||||
root,
|
||||
action_type,
|
||||
is_unload,
|
||||
})
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::avm1::{Object, Value};
|
||||
use crate::avm1::{Object, TObject, Value};
|
||||
use crate::context::{RenderContext, UpdateContext};
|
||||
use crate::player::NEWEST_PLAYER_VERSION;
|
||||
use crate::prelude::*;
|
||||
|
@ -366,7 +366,7 @@ impl<'gc> DisplayObjectBase<'gc> {
|
|||
Text(Text<'gc>),
|
||||
}
|
||||
)]
|
||||
pub trait TDisplayObject<'gc>: 'gc + Collect + Debug {
|
||||
pub trait TDisplayObject<'gc>: 'gc + Collect + Debug + Into<DisplayObject<'gc>> {
|
||||
fn id(&self) -> CharacterId;
|
||||
fn depth(&self) -> Depth;
|
||||
fn set_depth(&self, gc_context: MutationContext<'gc, '_>, depth: Depth);
|
||||
|
@ -804,6 +804,34 @@ pub trait TDisplayObject<'gc>: 'gc + Collect + Debug {
|
|||
fn allow_as_mask(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Obtain the top-most parent of the display tree hierarchy.
|
||||
///
|
||||
/// This function can panic in the rare case that a top-level display
|
||||
/// object has not been post-instantiated, or that a top-level display
|
||||
/// object does not implement `object`.
|
||||
fn root(&self) -> DisplayObject<'gc> {
|
||||
let mut parent = self.parent();
|
||||
|
||||
while let Some(p) = parent {
|
||||
let grandparent = p.parent();
|
||||
|
||||
if grandparent.is_none() {
|
||||
break;
|
||||
}
|
||||
|
||||
parent = grandparent;
|
||||
}
|
||||
|
||||
parent
|
||||
.or_else(|| {
|
||||
self.object()
|
||||
.as_object()
|
||||
.ok()
|
||||
.and_then(|o| o.as_display_object())
|
||||
})
|
||||
.expect("All objects must have root")
|
||||
}
|
||||
}
|
||||
|
||||
pub enum DisplayObjectPtr {}
|
||||
|
|
|
@ -370,7 +370,6 @@ impl<'gc> ButtonData<'gc> {
|
|||
handled = ButtonEventResult::Handled;
|
||||
context.action_queue.queue_actions(
|
||||
parent,
|
||||
context.root,
|
||||
ActionType::Normal {
|
||||
bytecode: action.action_data.clone(),
|
||||
},
|
||||
|
|
|
@ -923,7 +923,6 @@ impl<'gc> MovieClipData<'gc> {
|
|||
{
|
||||
context.action_queue.queue_actions(
|
||||
self_display_object,
|
||||
context.root,
|
||||
ActionType::Normal {
|
||||
bytecode: clip_action.action_data.clone(),
|
||||
},
|
||||
|
@ -958,7 +957,6 @@ impl<'gc> MovieClipData<'gc> {
|
|||
if let Some(name) = name {
|
||||
context.action_queue.queue_actions(
|
||||
self_display_object,
|
||||
context.root,
|
||||
ActionType::Method {
|
||||
object: self.object.unwrap(),
|
||||
name,
|
||||
|
@ -991,7 +989,6 @@ impl<'gc> MovieClipData<'gc> {
|
|||
context.load_manager.movie_clip_on_load(
|
||||
self_display_object,
|
||||
self.object,
|
||||
context.root,
|
||||
context.action_queue,
|
||||
);
|
||||
}
|
||||
|
@ -1772,7 +1769,6 @@ impl<'gc, 'a> MovieClipData<'gc> {
|
|||
})?;
|
||||
context.action_queue.queue_actions(
|
||||
self_display_object,
|
||||
context.root,
|
||||
ActionType::Normal { bytecode: slice },
|
||||
false,
|
||||
);
|
||||
|
@ -1806,7 +1802,6 @@ impl<'gc, 'a> MovieClipData<'gc> {
|
|||
})?;
|
||||
context.action_queue.queue_actions(
|
||||
self_display_object,
|
||||
context.root,
|
||||
ActionType::Init { bytecode: slice },
|
||||
true,
|
||||
);
|
||||
|
|
|
@ -89,13 +89,12 @@ impl<'gc> LoadManager<'gc> {
|
|||
&mut self,
|
||||
loaded_clip: DisplayObject<'gc>,
|
||||
clip_object: Option<Object<'gc>>,
|
||||
root: DisplayObject<'gc>,
|
||||
queue: &mut ActionQueue<'gc>,
|
||||
) {
|
||||
let mut invalidated_loaders = vec![];
|
||||
|
||||
for (index, loader) in self.0.iter_mut() {
|
||||
if loader.movie_clip_loaded(loaded_clip, clip_object, root, queue) {
|
||||
if loader.movie_clip_loaded(loaded_clip, clip_object, queue) {
|
||||
invalidated_loaders.push(index);
|
||||
}
|
||||
}
|
||||
|
@ -433,7 +432,6 @@ impl<'gc> Loader<'gc> {
|
|||
&mut self,
|
||||
loaded_clip: DisplayObject<'gc>,
|
||||
clip_object: Option<Object<'gc>>,
|
||||
root: DisplayObject<'gc>,
|
||||
queue: &mut ActionQueue<'gc>,
|
||||
) -> bool {
|
||||
let (clip, broadcaster) = match self {
|
||||
|
@ -449,7 +447,6 @@ impl<'gc> Loader<'gc> {
|
|||
if let Some(broadcaster) = broadcaster {
|
||||
queue.queue_actions(
|
||||
clip,
|
||||
root,
|
||||
ActionType::Method {
|
||||
object: broadcaster,
|
||||
name: "broadcastMessage",
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::backend::{
|
|||
};
|
||||
use crate::context::{ActionQueue, ActionType, RenderContext, UpdateContext};
|
||||
use crate::display_object::{MorphShape, MovieClip};
|
||||
use crate::events::{ButtonEvent, ButtonKeyCode, ClipEvent, PlayerEvent};
|
||||
use crate::events::{ButtonEvent, ButtonEventResult, ButtonKeyCode, ClipEvent, PlayerEvent};
|
||||
use crate::library::Library;
|
||||
use crate::loader::LoadManager;
|
||||
use crate::prelude::*;
|
||||
|
@ -369,9 +369,14 @@ impl Player {
|
|||
|
||||
if button_event.is_some() {
|
||||
self.mutate_with_update_context(|_avm, context| {
|
||||
let root = context.root;
|
||||
if let Some(button_event) = button_event {
|
||||
root.propagate_button_event(context, button_event);
|
||||
let layers: Vec<DisplayObject<'_>> = context.layers.values().copied().collect();
|
||||
for layer in layers {
|
||||
if let Some(button_event) = button_event {
|
||||
let state = layer.propagate_button_event(context, button_event);
|
||||
if state == ButtonEventResult::Handled {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -387,16 +392,17 @@ impl Player {
|
|||
|
||||
if clip_event.is_some() || mouse_event_name.is_some() {
|
||||
self.mutate_with_update_context(|_avm, context| {
|
||||
let root = context.root;
|
||||
let layers: Vec<DisplayObject<'_>> = context.layers.values().copied().collect();
|
||||
|
||||
if let Some(clip_event) = clip_event {
|
||||
root.propagate_clip_event(context, clip_event);
|
||||
for layer in layers {
|
||||
if let Some(clip_event) = clip_event {
|
||||
layer.propagate_clip_event(context, clip_event);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(mouse_event_name) = mouse_event_name {
|
||||
context.action_queue.queue_actions(
|
||||
root,
|
||||
root,
|
||||
*context.layers.get(&0).expect("root layer"),
|
||||
ActionType::NotifyListeners {
|
||||
listener: SystemListener::Mouse,
|
||||
method: mouse_event_name,
|
||||
|
@ -521,7 +527,7 @@ impl Player {
|
|||
fn preload(&mut self) {
|
||||
self.mutate_with_update_context(|_avm, context| {
|
||||
let mut morph_shapes = fnv::FnvHashMap::default();
|
||||
let root = context.root;
|
||||
let root = *context.layers.get(&0).expect("root layer");
|
||||
root.as_movie_clip()
|
||||
.unwrap()
|
||||
.preload(context, &mut morph_shapes);
|
||||
|
@ -550,8 +556,6 @@ impl Player {
|
|||
}
|
||||
|
||||
for mut layer in layers {
|
||||
update_context.root = layer;
|
||||
|
||||
layer.run_frame(update_context);
|
||||
}
|
||||
})
|
||||
|
@ -636,8 +640,6 @@ impl Player {
|
|||
continue;
|
||||
}
|
||||
|
||||
context.root = actions.root;
|
||||
|
||||
match actions.action_type {
|
||||
// DoAction/clip event code
|
||||
ActionType::Normal { bytecode } => {
|
||||
|
@ -772,7 +774,6 @@ impl Player {
|
|||
let mouse_hovered_object = root_data.mouse_hovered_object;
|
||||
let (layers, library, action_queue, avm, drag_object, load_manager) =
|
||||
root_data.update_context_params();
|
||||
let layer0 = layers.get(&0).expect("Layer 0 should always exist");
|
||||
|
||||
let mut update_context = UpdateContext {
|
||||
player_version,
|
||||
|
@ -787,7 +788,6 @@ impl Player {
|
|||
input,
|
||||
action_queue,
|
||||
gc_context,
|
||||
root: *layer0,
|
||||
layers,
|
||||
mouse_hovered_object,
|
||||
mouse_position,
|
||||
|
|
Loading…
Reference in New Issue