core: simplify callstack handling in Player

Grab the AVM2 callstack handle before constructing the GC root,
so that we can set it directly instead of modify the GC root once
constructed. This means we can remove an extra GcCell.

Also switch the callstack object from GcCell to GcRefLock.
This commit is contained in:
Moulins 2024-08-02 14:38:19 +02:00 committed by Nathan Adams
parent 0097aea381
commit 0b3395d21f
3 changed files with 55 additions and 68 deletions

View File

@ -15,7 +15,8 @@ use crate::tag_utils::SwfMovie;
use crate::PlayerRuntime; use crate::PlayerRuntime;
use fnv::FnvHashMap; use fnv::FnvHashMap;
use gc_arena::{Collect, GcCell, Mutation}; use gc_arena::lock::GcRefLock;
use gc_arena::{Collect, Mutation};
use std::sync::Arc; use std::sync::Arc;
use swf::avm2::read::Reader; use swf::avm2::read::Reader;
use swf::DoAbc2Flag; use swf::DoAbc2Flag;
@ -112,7 +113,7 @@ pub struct Avm2<'gc> {
scope_stack: Vec<Scope<'gc>>, scope_stack: Vec<Scope<'gc>>,
/// The current call stack of the player. /// The current call stack of the player.
call_stack: GcCell<'gc, CallStack<'gc>>, call_stack: GcRefLock<'gc, CallStack<'gc>>,
/// This domain is used exclusively for classes from playerglobals /// This domain is used exclusively for classes from playerglobals
playerglobals_domain: Domain<'gc>, playerglobals_domain: Domain<'gc>,
@ -216,7 +217,7 @@ impl<'gc> Avm2<'gc> {
player_runtime, player_runtime,
stack: Vec::new(), stack: Vec::new(),
scope_stack: Vec::new(), scope_stack: Vec::new(),
call_stack: GcCell::new(context.gc_context, CallStack::new()), call_stack: GcRefLock::new(context.gc_context, CallStack::new().into()),
playerglobals_domain, playerglobals_domain,
stage_domain, stage_domain,
system_classes: None, system_classes: None,
@ -650,20 +651,20 @@ impl<'gc> Avm2<'gc> {
method: Method<'gc>, method: Method<'gc>,
superclass: Option<ClassObject<'gc>>, superclass: Option<ClassObject<'gc>>,
) { ) {
self.call_stack.write(mc).push(method, superclass) self.call_stack.borrow_mut(mc).push(method, superclass)
} }
/// Pushes script initializer (global init) on the call stack /// Pushes script initializer (global init) on the call stack
pub fn push_global_init(&self, mc: &Mutation<'gc>, script: Script<'gc>) { pub fn push_global_init(&self, mc: &Mutation<'gc>, script: Script<'gc>) {
self.call_stack.write(mc).push_global_init(script) self.call_stack.borrow_mut(mc).push_global_init(script)
} }
/// Pops an executable off the call stack /// Pops an executable off the call stack
pub fn pop_call(&self, mc: &Mutation<'gc>) -> Option<CallNode<'gc>> { pub fn pop_call(&self, mc: &Mutation<'gc>) -> Option<CallNode<'gc>> {
self.call_stack.write(mc).pop() self.call_stack.borrow_mut(mc).pop()
} }
pub fn call_stack(&self) -> GcCell<'gc, CallStack<'gc>> { pub fn call_stack(&self) -> GcRefLock<'gc, CallStack<'gc>> {
self.call_stack self.call_stack
} }

View File

@ -20,7 +20,7 @@ pub fn error_allocator<'gc>(
let base = ScriptObjectData::new(class); let base = ScriptObjectData::new(class);
let call_stack = (enabled!(Level::INFO) || cfg!(feature = "avm_debug")) let call_stack = (enabled!(Level::INFO) || cfg!(feature = "avm_debug"))
.then(|| activation.avm2().call_stack().read().clone()) .then(|| activation.avm2().call_stack().borrow().clone())
.unwrap_or_default(); .unwrap_or_default();
Ok(ErrorObject(Gc::new( Ok(ErrorObject(Gc::new(

View File

@ -50,7 +50,8 @@ use crate::tag_utils::SwfMovie;
use crate::timer::Timers; use crate::timer::Timers;
use crate::vminterface::Instantiator; use crate::vminterface::Instantiator;
use crate::DefaultFont; use crate::DefaultFont;
use gc_arena::{Collect, DynamicRootSet, GcCell, Rootable}; use gc_arena::lock::GcRefLock;
use gc_arena::{Collect, DynamicRootSet, Rootable};
use rand::{rngs::SmallRng, SeedableRng}; use rand::{rngs::SmallRng, SeedableRng};
use ruffle_render::backend::{null::NullRenderer, RenderBackend, ViewportDimensions}; use ruffle_render::backend::{null::NullRenderer, RenderBackend, ViewportDimensions};
use ruffle_render::commands::CommandList; use ruffle_render::commands::CommandList;
@ -77,14 +78,8 @@ pub const FALLBACK_DEVICE_FONT_TAG: &[u8] = include_bytes!("../assets/noto-sans-
#[derive(Collect)] #[derive(Collect)]
#[collect(no_drop)] #[collect(no_drop)]
struct GcRoot<'gc> { struct GcRoot<'gc> {
callstack: GcCell<'gc, GcCallstack<'gc>>, avm2_callstack: GcRefLock<'gc, CallStack<'gc>>,
data: GcCell<'gc, GcRootData<'gc>>, data: GcRefLock<'gc, GcRootData<'gc>>,
}
#[derive(Collect, Default)]
#[collect(no_drop)]
struct GcCallstack<'gc> {
avm2: Option<GcCell<'gc, CallStack<'gc>>>,
} }
#[derive(Clone)] #[derive(Clone)]
@ -97,12 +92,9 @@ impl StaticCallstack {
if let Some(arena) = self.arena.upgrade() { if let Some(arena) = self.arena.upgrade() {
if let Ok(arena) = arena.try_borrow() { if let Ok(arena) = arena.try_borrow() {
arena.mutate(|_, root| { arena.mutate(|_, root| {
let callstack = root.callstack.read(); let callstack = root.avm2_callstack.borrow();
if let Some(callstack) = callstack.avm2 { if !callstack.is_empty() {
let stack = callstack.read(); f(&callstack);
if !stack.is_empty() {
f(&stack)
}
} }
}) })
} }
@ -635,7 +627,7 @@ impl Player {
pub fn clear_custom_menu_items(&mut self) { pub fn clear_custom_menu_items(&mut self) {
self.gc_arena.borrow().mutate(|gc_context, gc_root| { self.gc_arena.borrow().mutate(|gc_context, gc_root| {
let mut root_data = gc_root.data.write(gc_context); let mut root_data = gc_root.data.borrow_mut(gc_context);
root_data.current_context_menu = None; root_data.current_context_menu = None;
}); });
} }
@ -1924,7 +1916,7 @@ impl Player {
let invalidated = self let invalidated = self
.gc_arena .gc_arena
.borrow() .borrow()
.mutate(|_, gc_root| gc_root.data.read().stage.invalidated()); .mutate(|_, gc_root| gc_root.data.borrow().stage.invalidated());
if invalidated { if invalidated {
self.update(|context| { self.update(|context| {
let stage = context.stage; let stage = context.stage;
@ -1935,7 +1927,7 @@ impl Player {
let mut background_color = Color::WHITE; let mut background_color = Color::WHITE;
let (cache_draws, commands) = self.gc_arena.borrow().mutate(|gc_context, gc_root| { let (cache_draws, commands) = self.gc_arena.borrow().mutate(|gc_context, gc_root| {
let root_data = gc_root.data.read(); let root_data = gc_root.data.borrow();
let stage = root_data.stage; let stage = root_data.stage;
let mut cache_draws = vec![]; let mut cache_draws = vec![];
@ -2135,7 +2127,7 @@ impl Player {
F: for<'a, 'gc> FnOnce(&mut UpdateContext<'a, 'gc>) -> R, F: for<'a, 'gc> FnOnce(&mut UpdateContext<'a, 'gc>) -> R,
{ {
self.gc_arena.borrow().mutate(|gc_context, gc_root| { self.gc_arena.borrow().mutate(|gc_context, gc_root| {
let mut root_data = gc_root.data.write(gc_context); let mut root_data = gc_root.data.borrow_mut(gc_context);
#[allow(unused_variables)] #[allow(unused_variables)]
let ( let (
@ -2726,45 +2718,43 @@ impl PlayerBuilder {
gc_context, gc_context,
interner: &mut interner, interner: &mut interner,
}; };
let dynamic_root = DynamicRootSet::new(gc_context);
let data = GcRootData {
audio_manager: AudioManager::new(),
action_queue: ActionQueue::new(),
avm1: Avm1::new(&mut init, player_version),
avm2: Avm2::new(&mut init, player_version, player_runtime),
interner,
current_context_menu: None,
drag_object: None,
external_interface: ExternalInterface::new(
external_interface_providers,
fs_command_provider,
),
library: Library::empty(),
load_manager: LoadManager::new(),
mouse_data: MouseData {
hovered: None,
pressed: None,
right_pressed: None,
middle_pressed: None,
},
avm1_shared_objects: HashMap::new(),
avm2_shared_objects: HashMap::new(),
stage: Stage::empty(gc_context, fullscreen, fake_movie),
timers: Timers::new(),
unbound_text_fields: Vec::new(),
stream_manager: StreamManager::new(),
sockets: Sockets::empty(),
net_connections: NetConnections::default(),
local_connections: LocalConnections::empty(),
dynamic_root: DynamicRootSet::new(gc_context),
post_frame_callbacks: Vec::new(),
};
GcRoot { GcRoot {
callstack: GcCell::new(gc_context, GcCallstack::default()), avm2_callstack: data.avm2.call_stack(),
data: GcCell::new( data: GcRefLock::new(gc_context, data.into()),
gc_context,
GcRootData {
audio_manager: AudioManager::new(),
action_queue: ActionQueue::new(),
avm1: Avm1::new(&mut init, player_version),
avm2: Avm2::new(&mut init, player_version, player_runtime),
interner,
current_context_menu: None,
drag_object: None,
external_interface: ExternalInterface::new(
external_interface_providers,
fs_command_provider,
),
library: Library::empty(),
load_manager: LoadManager::new(),
mouse_data: MouseData {
hovered: None,
pressed: None,
right_pressed: None,
middle_pressed: None,
},
avm1_shared_objects: HashMap::new(),
avm2_shared_objects: HashMap::new(),
stage: Stage::empty(gc_context, fullscreen, fake_movie),
timers: Timers::new(),
unbound_text_fields: Vec::new(),
stream_manager: StreamManager::new(),
sockets: Sockets::empty(),
net_connections: NetConnections::default(),
local_connections: LocalConnections::empty(),
dynamic_root,
post_frame_callbacks: Vec::new(),
},
),
} }
} }
@ -2915,10 +2905,6 @@ impl PlayerBuilder {
crate::avm2::specification::capture_specification(context, &stub_path); crate::avm2::specification::capture_specification(context, &stub_path);
} }
}); });
player_lock.gc_arena.borrow().mutate(|context, root| {
let call_stack = root.data.read().avm2.call_stack();
root.callstack.write(context).avm2 = Some(call_stack);
});
player_lock.audio.set_frame_rate(frame_rate); player_lock.audio.set_frame_rate(frame_rate);
player_lock.set_letterbox(self.letterbox); player_lock.set_letterbox(self.letterbox);
player_lock.set_quality(self.quality); player_lock.set_quality(self.quality);