Skip to content

Commit c66cfd4

Browse files
avm1: Cache ConstantPools for constant pools with more than 1000 entries
1 parent f5b4fbc commit c66cfd4

File tree

3 files changed

+74
-15
lines changed

3 files changed

+74
-15
lines changed

core/src/avm1/activation.rs

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::display_object::{DisplayObject, MovieClip, TDisplayObject, TDisplayOb
1212
use crate::ecma_conversions::{f64_to_wrapping_i32, f64_to_wrapping_u32};
1313
use crate::loader::MovieLoaderVMData;
1414
use crate::string::{AvmString, SwfStrExt as _, WStr, WString};
15-
use crate::tag_utils::SwfSlice;
15+
use crate::tag_utils::{SwfPosition, SwfSlice};
1616
use crate::vminterface::Instantiator;
1717
use crate::{avm_error, avm_warn};
1818
use gc_arena::{Gc, GcCell, MutationContext};
@@ -25,6 +25,7 @@ use std::cmp::min;
2525
use std::fmt;
2626
use swf::avm1::read::Reader;
2727
use swf::avm1::types::*;
28+
use swf::extensions::ReadSwfExt;
2829
use url::form_urlencoded;
2930

3031
use super::object_reference::MovieClipReference;
@@ -480,7 +481,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
480481
Action::CastOp => self.action_cast_op(),
481482
Action::CharToAscii => self.action_char_to_ascii(),
482483
Action::CloneSprite => self.action_clone_sprite(),
483-
Action::ConstantPool(action) => self.action_constant_pool(action),
484+
Action::ConstantPool(action) => self.action_constant_pool(action, data, reader),
484485
Action::Decrement => self.action_decrement(),
485486
Action::DefineFunction(action) => self.action_define_function(action.into(), data),
486487
Action::DefineFunction2(action) => self.action_define_function(action, data),
@@ -863,21 +864,42 @@ impl<'a, 'gc> Activation<'a, 'gc> {
863864
fn action_constant_pool(
864865
&mut self,
865866
action: ConstantPool,
867+
data: &SwfSlice,
868+
reader: &mut Reader,
866869
) -> Result<FrameControl<'gc>, Error<'gc>> {
867-
let constants = action
868-
.strings
869-
.iter()
870-
.map(|s| {
870+
let current_pos = reader.pos(data.movie.data());
871+
let swf_position = SwfPosition::new(data.movie.clone(), current_pos);
872+
873+
let const_pool_cache = self.context.avm1.constant_pool_cache();
874+
875+
let constants = if let Some(constants) = const_pool_cache.get(&swf_position) {
876+
*constants
877+
} else {
878+
let constants: Vec<_> = action
879+
.strings
880+
.iter()
881+
.map(|s| {
882+
self.context
883+
.interner
884+
.intern_wstr(self.context.gc_context, s.decode(self.encoding()))
885+
.into()
886+
})
887+
.collect();
888+
889+
let consts = Gc::new(self.context.gc_context, constants);
890+
891+
if consts.len() > 1000 {
871892
self.context
872-
.interner
873-
.intern_wstr(self.context.gc_context, s.decode(self.encoding()))
874-
.into()
875-
})
876-
.collect();
893+
.avm1
894+
.constant_pool_cache()
895+
.insert(swf_position, consts);
896+
}
897+
898+
consts
899+
};
900+
901+
self.context.avm1.set_constant_pool(constants);
877902

878-
self.context
879-
.avm1
880-
.set_constant_pool(Gc::new(self.context.gc_context, constants));
881903
self.set_constant_pool(self.context.avm1.constant_pool());
882904

883905
Ok(FrameControl::Continue)

core/src/avm1/runtime.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ use crate::context::{GcContext, UpdateContext};
1010
use crate::frame_lifecycle::FramePhase;
1111
use crate::prelude::*;
1212
use crate::string::AvmString;
13-
use crate::tag_utils::SwfSlice;
13+
use crate::tag_utils::{SwfPosition, SwfSlice};
1414
use crate::{avm1, avm_debug};
1515
use gc_arena::{Collect, Gc, MutationContext};
1616
use std::borrow::Cow;
17+
use std::collections::HashMap;
1718
use swf::avm1::read::Reader;
1819
use tracing::instrument;
1920

@@ -27,6 +28,8 @@ pub struct Avm1<'gc> {
2728
/// don't close over the constant pool they were defined with.
2829
constant_pool: Gc<'gc, Vec<Value<'gc>>>,
2930

31+
constant_pool_cache: HashMap<SwfPosition, Gc<'gc, Vec<Value<'gc>>>>,
32+
3033
/// The global scope (pre-allocated so that it can be reused by fresh `Activation`s).
3134
global_scope: Gc<'gc, Scope<'gc>>,
3235

@@ -80,6 +83,7 @@ impl<'gc> Avm1<'gc> {
8083
Self {
8184
player_version,
8285
constant_pool: Gc::new(gc_context, vec![]),
86+
constant_pool_cache: HashMap::new(),
8387
global_scope: Gc::new(gc_context, Scope::from_global_object(globals)),
8488
prototypes,
8589
broadcaster_functions,
@@ -363,6 +367,10 @@ impl<'gc> Avm1<'gc> {
363367
self.constant_pool = constant_pool;
364368
}
365369

370+
pub fn constant_pool_cache(&mut self) -> &mut HashMap<SwfPosition, Gc<'gc, Vec<Value<'gc>>>> {
371+
&mut self.constant_pool_cache
372+
}
373+
366374
/// DisplayObject property map.
367375
pub fn display_properties(&self) -> &stage_object::DisplayPropertyMap<'gc> {
368376
&self.display_properties

core/src/tag_utils.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use gc_arena::Collect;
2+
use std::hash::{Hash, Hasher};
23
use std::sync::Arc;
34
use swf::{CharacterId, Fixed8, HeaderExt, Rectangle, TagCode, Twips};
45
use thiserror::Error;
@@ -409,6 +410,34 @@ impl SwfSlice {
409410
}
410411
}
411412

413+
#[derive(Collect)]
414+
#[collect(no_drop)]
415+
pub struct SwfPosition {
416+
pub movie: Arc<SwfMovie>,
417+
pub pos: usize,
418+
}
419+
420+
impl SwfPosition {
421+
pub fn new(movie: Arc<SwfMovie>, pos: usize) -> Self {
422+
SwfPosition { movie, pos }
423+
}
424+
}
425+
426+
impl PartialEq for SwfPosition {
427+
fn eq(&self, other: &Self) -> bool {
428+
Arc::ptr_eq(&self.movie, &other.movie) && self.pos == other.pos
429+
}
430+
}
431+
432+
impl Eq for SwfPosition {}
433+
434+
impl Hash for SwfPosition {
435+
fn hash<H: Hasher>(&self, state: &mut H) {
436+
self.pos.hash(state);
437+
Arc::as_ptr(&self.movie).hash(state);
438+
}
439+
}
440+
412441
/// Decode tags from a SWF stream reader.
413442
///
414443
/// The given `tag_callback` will be called for each decoded tag. It will be

0 commit comments

Comments
 (0)