avm2: Optimize findproperty and findpropstrict to getouterscope, getscopeobject, and getscriptglobals when possible

This commit is contained in:
Lord-McSweeney 2024-04-27 18:32:45 -07:00 committed by Lord-McSweeney
parent f50b68abfd
commit 767b7b4e44
7 changed files with 124 additions and 18 deletions

View File

@ -585,7 +585,7 @@ impl<'gc> Avm2<'gc> {
if !flags.contains(DoAbc2Flag::LAZY_INITIALIZE) {
for i in 0..num_scripts {
if let Some(mut script) = tunit.get_script(i) {
if let Some(script) = tunit.get_script(i) {
script.globals(&mut activation.context)?;
}
}

View File

@ -922,6 +922,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
Op::FindDef { multiname } => self.op_find_def(*multiname),
Op::FindProperty { multiname } => self.op_find_property(*multiname),
Op::FindPropStrict { multiname } => self.op_find_prop_strict(*multiname),
Op::GetScriptGlobals { script } => self.op_get_script_globals(*script),
Op::GetDescendants { multiname } => self.op_get_descendants(*multiname),
Op::GetSlot { index } => self.op_get_slot(*index),
Op::SetSlot { index } => self.op_set_slot(*index),
@ -1667,7 +1668,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
// Verifier ensures that multiname is non-lazy
avm_debug!(self.avm2(), "Resolving {:?}", *multiname);
let (_, mut script) = self.domain().find_defining_script(self, &multiname)?;
let (_, script) = self.domain().find_defining_script(self, &multiname)?;
let obj = script.globals(&mut self.context)?;
self.push_stack(obj);
Ok(FrameControl::Continue)
@ -1707,6 +1708,17 @@ impl<'a, 'gc> Activation<'a, 'gc> {
Ok(FrameControl::Continue)
}
fn op_get_script_globals(
&mut self,
script: Script<'gc>,
) -> Result<FrameControl<'gc>, Error<'gc>> {
let globals = script.globals(&mut self.context)?;
self.push_stack(globals);
Ok(FrameControl::Continue)
}
fn op_get_descendants(
&mut self,
multiname: Gc<'gc, Multiname<'gc>>,

View File

@ -263,7 +263,7 @@ impl<'gc> Domain<'gc> {
activation: &mut Activation<'_, 'gc>,
name: QName<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
let (name, mut script) = self.find_defining_script(activation, &name.into())?;
let (name, script) = self.find_defining_script(activation, &name.into())?;
let globals = script.globals(&mut activation.context)?;
globals.get_property(&name.into(), activation)

View File

@ -1,5 +1,6 @@
use crate::avm2::class::Class;
use crate::avm2::multiname::Multiname;
use crate::avm2::script::Script;
use crate::string::AvmAtom;
use gc_arena::{Collect, Gc};
@ -146,6 +147,9 @@ pub enum Op<'gc> {
GetScopeObject {
index: u8,
},
GetScriptGlobals {
script: Script<'gc>,
},
GetSlot {
index: u32,
},

View File

@ -248,6 +248,23 @@ pub fn optimize<'gc>(
}
}
let mut has_simple_scoping = false;
if !jump_targets.contains_key(&0) || !jump_targets.contains_key(&1) {
if matches!(code.get(0), Some(Op::GetLocal { index: 0 }))
&& matches!(code.get(1), Some(Op::PushScope))
{
has_simple_scoping = true;
for op in code.iter().skip(2) {
match op {
Op::PushScope | Op::PushWith | Op::PopScope => {
has_simple_scoping = false;
}
_ => {}
}
}
}
}
// TODO: Fill out all ops, then add scope stack and stack merging, too
let mut state_map: HashMap<i32, Locals<'gc>> = HashMap::new();
@ -730,17 +747,62 @@ pub fn optimize<'gc>(
let local_type = local_types.at(*index as usize);
stack.push(local_type);
}
Op::FindPropStrict { multiname } => {
stack.pop_for_multiname(*multiname);
Op::FindPropStrict { multiname } | Op::FindProperty { multiname } => {
let multiname = multiname.clone();
let mut stack_push_done = false;
stack.pop_for_multiname(multiname);
// Avoid handling for now
stack.push_any();
}
Op::FindProperty { multiname } => {
stack.pop_for_multiname(*multiname);
if has_simple_scoping {
let outer_scope = activation.outer();
if !outer_scope.is_empty() {
if let Some(this_class) = this_class {
if this_class.instance_vtable().has_trait(&multiname) {
*op = Op::GetScopeObject { index: 0 };
// Avoid handling for now
stack.push_any();
stack_push_done = true;
stack.push_class_object(this_class);
}
} else {
stack_push_done = true;
stack.push_any();
}
}
if !stack_push_done {
if let Some((class, index)) =
outer_scope.get_entry_for_multiname(&multiname)
{
*op = Op::GetOuterScope {
index: index as u32,
};
stack_push_done = true;
if let Some(class) = class {
stack.push_class_object(class);
} else {
stack.push_any();
}
}
}
if !stack_push_done {
if let Ok(Some((_, script))) =
outer_scope.domain().get_defining_script(&multiname)
{
*op = Op::GetScriptGlobals { script };
stack_push_done = true;
// Pushing the `global` class doesn't work because of the `fork_vtable` hack
stack.push_any();
}
}
// Ignore global scope for now
}
if !stack_push_done {
stack.push_any();
}
}
Op::FindDef { .. } => {
// Avoid handling for now

View File

@ -2,7 +2,7 @@
use crate::avm2::activation::Activation;
use crate::avm2::domain::Domain;
use crate::avm2::object::{Object, TObject};
use crate::avm2::object::{ClassObject, Object, TObject};
use crate::avm2::value::Value;
use crate::avm2::Error;
use crate::avm2::{Multiname, Namespace};
@ -195,7 +195,7 @@ impl<'gc> ScopeChain<'gc> {
}
}
// That didn't work... let's try searching the domain now.
if let Some((qname, mut script)) = self.domain.get_defining_script(multiname)? {
if let Some((qname, script)) = self.domain.get_defining_script(multiname)? {
return Ok(Some((
Some(qname.namespace()),
script.globals(&mut activation.context)?,
@ -234,6 +234,28 @@ impl<'gc> ScopeChain<'gc> {
Ok(found.map(|o| o.1))
}
pub fn get_entry_for_multiname(
&self,
multiname: &Multiname<'gc>,
) -> Option<(Option<ClassObject<'gc>>, u32)> {
if let Some(container) = self.container {
for (index, scope) in container.scopes.iter().enumerate().skip(1).rev() {
if scope.with() {
// If this is a `with` scope, stop here because
// dynamic properties could be added at any time
return None;
}
let values = scope.values();
if values.has_trait(&multiname) {
return Some((values.instance_of(), index as u32));
}
}
}
None
}
pub fn resolve(
&self,
name: &Multiname<'gc>,

View File

@ -18,6 +18,7 @@ use crate::tag_utils::SwfMovie;
use crate::PlayerRuntime;
use gc_arena::{Collect, Gc, GcCell, Mutation};
use std::cell::Ref;
use std::fmt::Debug;
use std::rc::Rc;
use std::sync::Arc;
use swf::avm2::types::{
@ -558,10 +559,7 @@ impl<'gc> Script<'gc> {
///
/// If the script has not yet been initialized, this will initialize it on
/// the same stack.
pub fn globals(
&mut self,
context: &mut UpdateContext<'_, 'gc>,
) -> Result<Object<'gc>, Error<'gc>> {
pub fn globals(&self, context: &mut UpdateContext<'_, 'gc>) -> Result<Object<'gc>, Error<'gc>> {
let mut write = self.0.write(context.gc_context);
if !write.initialized {
@ -606,3 +604,11 @@ impl<'gc> Script<'gc> {
Ok(Ref::map(read, |read| &read.traits[..]))
}
}
impl<'gc> Debug for Script<'gc> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
f.debug_struct("Script")
.field("ptr", &self.0.as_ptr())
.finish()
}
}