avm1: Cache ConstantPools for constant pools with more than 1000 entries

This commit is contained in:
Lord-McSweeney 2023-07-09 19:01:08 -07:00
parent f5b4fbce77
commit c66cfd4518
3 changed files with 75 additions and 16 deletions

View File

@ -12,7 +12,7 @@ use crate::display_object::{DisplayObject, MovieClip, TDisplayObject, TDisplayOb
use crate::ecma_conversions::{f64_to_wrapping_i32, f64_to_wrapping_u32};
use crate::loader::MovieLoaderVMData;
use crate::string::{AvmString, SwfStrExt as _, WStr, WString};
use crate::tag_utils::SwfSlice;
use crate::tag_utils::{SwfPosition, SwfSlice};
use crate::vminterface::Instantiator;
use crate::{avm_error, avm_warn};
use gc_arena::{Gc, GcCell, MutationContext};
@ -25,6 +25,7 @@ use std::cmp::min;
use std::fmt;
use swf::avm1::read::Reader;
use swf::avm1::types::*;
use swf::extensions::ReadSwfExt;
use url::form_urlencoded;
use super::object_reference::MovieClipReference;
@ -480,7 +481,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
Action::CastOp => self.action_cast_op(),
Action::CharToAscii => self.action_char_to_ascii(),
Action::CloneSprite => self.action_clone_sprite(),
Action::ConstantPool(action) => self.action_constant_pool(action),
Action::ConstantPool(action) => self.action_constant_pool(action, data, reader),
Action::Decrement => self.action_decrement(),
Action::DefineFunction(action) => self.action_define_function(action.into(), data),
Action::DefineFunction2(action) => self.action_define_function(action, data),
@ -863,8 +864,18 @@ impl<'a, 'gc> Activation<'a, 'gc> {
fn action_constant_pool(
&mut self,
action: ConstantPool,
data: &SwfSlice,
reader: &mut Reader,
) -> Result<FrameControl<'gc>, Error<'gc>> {
let constants = action
let current_pos = reader.pos(data.movie.data());
let swf_position = SwfPosition::new(data.movie.clone(), current_pos);
let const_pool_cache = self.context.avm1.constant_pool_cache();
let constants = if let Some(constants) = const_pool_cache.get(&swf_position) {
*constants
} else {
let constants: Vec<_> = action
.strings
.iter()
.map(|s| {
@ -875,9 +886,20 @@ impl<'a, 'gc> Activation<'a, 'gc> {
})
.collect();
let consts = Gc::new(self.context.gc_context, constants);
if consts.len() > 1000 {
self.context
.avm1
.set_constant_pool(Gc::new(self.context.gc_context, constants));
.constant_pool_cache()
.insert(swf_position, consts);
}
consts
};
self.context.avm1.set_constant_pool(constants);
self.set_constant_pool(self.context.avm1.constant_pool());
Ok(FrameControl::Continue)

View File

@ -10,10 +10,11 @@ use crate::context::{GcContext, UpdateContext};
use crate::frame_lifecycle::FramePhase;
use crate::prelude::*;
use crate::string::AvmString;
use crate::tag_utils::SwfSlice;
use crate::tag_utils::{SwfPosition, SwfSlice};
use crate::{avm1, avm_debug};
use gc_arena::{Collect, Gc, MutationContext};
use std::borrow::Cow;
use std::collections::HashMap;
use swf::avm1::read::Reader;
use tracing::instrument;
@ -27,6 +28,8 @@ pub struct Avm1<'gc> {
/// don't close over the constant pool they were defined with.
constant_pool: Gc<'gc, Vec<Value<'gc>>>,
constant_pool_cache: HashMap<SwfPosition, Gc<'gc, Vec<Value<'gc>>>>,
/// The global scope (pre-allocated so that it can be reused by fresh `Activation`s).
global_scope: Gc<'gc, Scope<'gc>>,
@ -80,6 +83,7 @@ impl<'gc> Avm1<'gc> {
Self {
player_version,
constant_pool: Gc::new(gc_context, vec![]),
constant_pool_cache: HashMap::new(),
global_scope: Gc::new(gc_context, Scope::from_global_object(globals)),
prototypes,
broadcaster_functions,
@ -363,6 +367,10 @@ impl<'gc> Avm1<'gc> {
self.constant_pool = constant_pool;
}
pub fn constant_pool_cache(&mut self) -> &mut HashMap<SwfPosition, Gc<'gc, Vec<Value<'gc>>>> {
&mut self.constant_pool_cache
}
/// DisplayObject property map.
pub fn display_properties(&self) -> &stage_object::DisplayPropertyMap<'gc> {
&self.display_properties

View File

@ -1,4 +1,5 @@
use gc_arena::Collect;
use std::hash::{Hash, Hasher};
use std::sync::Arc;
use swf::{CharacterId, Fixed8, HeaderExt, Rectangle, TagCode, Twips};
use thiserror::Error;
@ -409,6 +410,34 @@ impl SwfSlice {
}
}
#[derive(Collect)]
#[collect(no_drop)]
pub struct SwfPosition {
pub movie: Arc<SwfMovie>,
pub pos: usize,
}
impl SwfPosition {
pub fn new(movie: Arc<SwfMovie>, pos: usize) -> Self {
SwfPosition { movie, pos }
}
}
impl PartialEq for SwfPosition {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.movie, &other.movie) && self.pos == other.pos
}
}
impl Eq for SwfPosition {}
impl Hash for SwfPosition {
fn hash<H: Hasher>(&self, state: &mut H) {
self.pos.hash(state);
Arc::as_ptr(&self.movie).hash(state);
}
}
/// Decode tags from a SWF stream reader.
///
/// The given `tag_callback` will be called for each decoded tag. It will be