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) {
|
if !flags.contains(DoAbc2Flag::LAZY_INITIALIZE) {
|
||||||
for i in 0..num_scripts {
|
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)?;
|
script.globals(&mut activation.context)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -922,6 +922,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
||||||
Op::FindDef { multiname } => self.op_find_def(*multiname),
|
Op::FindDef { multiname } => self.op_find_def(*multiname),
|
||||||
Op::FindProperty { multiname } => self.op_find_property(*multiname),
|
Op::FindProperty { multiname } => self.op_find_property(*multiname),
|
||||||
Op::FindPropStrict { multiname } => self.op_find_prop_strict(*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::GetDescendants { multiname } => self.op_get_descendants(*multiname),
|
||||||
Op::GetSlot { index } => self.op_get_slot(*index),
|
Op::GetSlot { index } => self.op_get_slot(*index),
|
||||||
Op::SetSlot { index } => self.op_set_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
|
// Verifier ensures that multiname is non-lazy
|
||||||
|
|
||||||
avm_debug!(self.avm2(), "Resolving {:?}", *multiname);
|
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)?;
|
let obj = script.globals(&mut self.context)?;
|
||||||
self.push_stack(obj);
|
self.push_stack(obj);
|
||||||
Ok(FrameControl::Continue)
|
Ok(FrameControl::Continue)
|
||||||
|
@ -1707,6 +1708,17 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
||||||
Ok(FrameControl::Continue)
|
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(
|
fn op_get_descendants(
|
||||||
&mut self,
|
&mut self,
|
||||||
multiname: Gc<'gc, Multiname<'gc>>,
|
multiname: Gc<'gc, Multiname<'gc>>,
|
||||||
|
|
|
@ -263,7 +263,7 @@ impl<'gc> Domain<'gc> {
|
||||||
activation: &mut Activation<'_, 'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
name: QName<'gc>,
|
name: QName<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'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)?;
|
let globals = script.globals(&mut activation.context)?;
|
||||||
|
|
||||||
globals.get_property(&name.into(), activation)
|
globals.get_property(&name.into(), activation)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::avm2::class::Class;
|
use crate::avm2::class::Class;
|
||||||
use crate::avm2::multiname::Multiname;
|
use crate::avm2::multiname::Multiname;
|
||||||
|
use crate::avm2::script::Script;
|
||||||
use crate::string::AvmAtom;
|
use crate::string::AvmAtom;
|
||||||
|
|
||||||
use gc_arena::{Collect, Gc};
|
use gc_arena::{Collect, Gc};
|
||||||
|
@ -146,6 +147,9 @@ pub enum Op<'gc> {
|
||||||
GetScopeObject {
|
GetScopeObject {
|
||||||
index: u8,
|
index: u8,
|
||||||
},
|
},
|
||||||
|
GetScriptGlobals {
|
||||||
|
script: Script<'gc>,
|
||||||
|
},
|
||||||
GetSlot {
|
GetSlot {
|
||||||
index: u32,
|
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
|
// TODO: Fill out all ops, then add scope stack and stack merging, too
|
||||||
let mut state_map: HashMap<i32, Locals<'gc>> = HashMap::new();
|
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);
|
let local_type = local_types.at(*index as usize);
|
||||||
stack.push(local_type);
|
stack.push(local_type);
|
||||||
}
|
}
|
||||||
Op::FindPropStrict { multiname } => {
|
Op::FindPropStrict { multiname } | Op::FindProperty { multiname } => {
|
||||||
stack.pop_for_multiname(*multiname);
|
let multiname = multiname.clone();
|
||||||
|
let mut stack_push_done = false;
|
||||||
|
stack.pop_for_multiname(multiname);
|
||||||
|
|
||||||
// Avoid handling for now
|
if has_simple_scoping {
|
||||||
stack.push_any();
|
let outer_scope = activation.outer();
|
||||||
}
|
if !outer_scope.is_empty() {
|
||||||
Op::FindProperty { multiname } => {
|
if let Some(this_class) = this_class {
|
||||||
stack.pop_for_multiname(*multiname);
|
if this_class.instance_vtable().has_trait(&multiname) {
|
||||||
|
*op = Op::GetScopeObject { index: 0 };
|
||||||
|
|
||||||
// Avoid handling for now
|
stack_push_done = true;
|
||||||
stack.push_any();
|
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 { .. } => {
|
Op::FindDef { .. } => {
|
||||||
// Avoid handling for now
|
// Avoid handling for now
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use crate::avm2::activation::Activation;
|
use crate::avm2::activation::Activation;
|
||||||
use crate::avm2::domain::Domain;
|
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::value::Value;
|
||||||
use crate::avm2::Error;
|
use crate::avm2::Error;
|
||||||
use crate::avm2::{Multiname, Namespace};
|
use crate::avm2::{Multiname, Namespace};
|
||||||
|
@ -195,7 +195,7 @@ impl<'gc> ScopeChain<'gc> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// That didn't work... let's try searching the domain now.
|
// 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((
|
return Ok(Some((
|
||||||
Some(qname.namespace()),
|
Some(qname.namespace()),
|
||||||
script.globals(&mut activation.context)?,
|
script.globals(&mut activation.context)?,
|
||||||
|
@ -234,6 +234,28 @@ impl<'gc> ScopeChain<'gc> {
|
||||||
Ok(found.map(|o| o.1))
|
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(
|
pub fn resolve(
|
||||||
&self,
|
&self,
|
||||||
name: &Multiname<'gc>,
|
name: &Multiname<'gc>,
|
||||||
|
|
|
@ -18,6 +18,7 @@ use crate::tag_utils::SwfMovie;
|
||||||
use crate::PlayerRuntime;
|
use crate::PlayerRuntime;
|
||||||
use gc_arena::{Collect, Gc, GcCell, Mutation};
|
use gc_arena::{Collect, Gc, GcCell, Mutation};
|
||||||
use std::cell::Ref;
|
use std::cell::Ref;
|
||||||
|
use std::fmt::Debug;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use swf::avm2::types::{
|
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
|
/// If the script has not yet been initialized, this will initialize it on
|
||||||
/// the same stack.
|
/// the same stack.
|
||||||
pub fn globals(
|
pub fn globals(&self, context: &mut UpdateContext<'_, 'gc>) -> Result<Object<'gc>, Error<'gc>> {
|
||||||
&mut self,
|
|
||||||
context: &mut UpdateContext<'_, 'gc>,
|
|
||||||
) -> Result<Object<'gc>, Error<'gc>> {
|
|
||||||
let mut write = self.0.write(context.gc_context);
|
let mut write = self.0.write(context.gc_context);
|
||||||
|
|
||||||
if !write.initialized {
|
if !write.initialized {
|
||||||
|
@ -606,3 +604,11 @@ impl<'gc> Script<'gc> {
|
||||||
Ok(Ref::map(read, |read| &read.traits[..]))
|
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