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 fnv::FnvHashMap;
use gc_arena::{Collect, GcCell, Mutation};
use gc_arena::lock::GcRefLock;
use gc_arena::{Collect, Mutation};
use std::sync::Arc;
use swf::avm2::read::Reader;
use swf::DoAbc2Flag;
@ -112,7 +113,7 @@ pub struct Avm2<'gc> {
scope_stack: Vec<Scope<'gc>>,
/// 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
playerglobals_domain: Domain<'gc>,
@ -216,7 +217,7 @@ impl<'gc> Avm2<'gc> {
player_runtime,
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,
stage_domain,
system_classes: None,
@ -650,20 +651,20 @@ impl<'gc> Avm2<'gc> {
method: Method<'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
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
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
}

View File

@ -20,7 +20,7 @@ pub fn error_allocator<'gc>(
let base = ScriptObjectData::new(class);
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();
Ok(ErrorObject(Gc::new(

View File

@ -50,7 +50,8 @@ use crate::tag_utils::SwfMovie;
use crate::timer::Timers;
use crate::vminterface::Instantiator;
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 ruffle_render::backend::{null::NullRenderer, RenderBackend, ViewportDimensions};
use ruffle_render::commands::CommandList;
@ -77,14 +78,8 @@ pub const FALLBACK_DEVICE_FONT_TAG: &[u8] = include_bytes!("../assets/noto-sans-
#[derive(Collect)]
#[collect(no_drop)]
struct GcRoot<'gc> {
callstack: GcCell<'gc, GcCallstack<'gc>>,
data: GcCell<'gc, GcRootData<'gc>>,
}
#[derive(Collect, Default)]
#[collect(no_drop)]
struct GcCallstack<'gc> {
avm2: Option<GcCell<'gc, CallStack<'gc>>>,
avm2_callstack: GcRefLock<'gc, CallStack<'gc>>,
data: GcRefLock<'gc, GcRootData<'gc>>,
}
#[derive(Clone)]
@ -97,12 +92,9 @@ impl StaticCallstack {
if let Some(arena) = self.arena.upgrade() {
if let Ok(arena) = arena.try_borrow() {
arena.mutate(|_, root| {
let callstack = root.callstack.read();
if let Some(callstack) = callstack.avm2 {
let stack = callstack.read();
if !stack.is_empty() {
f(&stack)
}
let callstack = root.avm2_callstack.borrow();
if !callstack.is_empty() {
f(&callstack);
}
})
}
@ -635,7 +627,7 @@ impl Player {
pub fn clear_custom_menu_items(&mut self) {
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;
});
}
@ -1924,7 +1916,7 @@ impl Player {
let invalidated = self
.gc_arena
.borrow()
.mutate(|_, gc_root| gc_root.data.read().stage.invalidated());
.mutate(|_, gc_root| gc_root.data.borrow().stage.invalidated());
if invalidated {
self.update(|context| {
let stage = context.stage;
@ -1935,7 +1927,7 @@ impl Player {
let mut background_color = Color::WHITE;
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 mut cache_draws = vec![];
@ -2135,7 +2127,7 @@ impl Player {
F: for<'a, 'gc> FnOnce(&mut UpdateContext<'a, 'gc>) -> R,
{
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)]
let (
@ -2726,45 +2718,43 @@ impl PlayerBuilder {
gc_context,
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 {
callstack: GcCell::new(gc_context, GcCallstack::default()),
data: GcCell::new(
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(),
},
),
avm2_callstack: data.avm2.call_stack(),
data: GcRefLock::new(gc_context, data.into()),
}
}
@ -2915,10 +2905,6 @@ impl PlayerBuilder {
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.set_letterbox(self.letterbox);
player_lock.set_quality(self.quality);