avm2: Optimize findproperty and findpropstrict to getouterscope, getscopeobject, and getscriptglobals when possible
This commit is contained in:
parent
f50b68abfd
commit
767b7b4e44
|
@ -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)?;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>>,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue