avm2: Allow small mutations to ScopeChain
This commit is contained in:
parent
9de7d7ba7a
commit
1dd899a76f
|
@ -402,10 +402,11 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
let translation_unit = method.translation_unit();
|
||||
let abc_method = method.method();
|
||||
let mut dummy_activation = Activation::from_nothing(context.reborrow());
|
||||
dummy_activation.set_outer(outer);
|
||||
let activation_class =
|
||||
Class::for_activation(&mut dummy_activation, translation_unit, abc_method, body)?;
|
||||
let activation_class_object =
|
||||
ClassObject::from_class(&mut dummy_activation, activation_class, None, outer)?;
|
||||
ClassObject::from_class(&mut dummy_activation, activation_class, None)?;
|
||||
|
||||
drop(dummy_activation);
|
||||
|
||||
|
@ -579,6 +580,11 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Sets the outer scope of this activation
|
||||
pub fn set_outer(&mut self, new_outer: ScopeChain<'gc>) {
|
||||
self.outer = new_outer;
|
||||
}
|
||||
|
||||
/// Creates a new ScopeChain by chaining the current state of this
|
||||
/// activation's scope stack with the outer scope.
|
||||
pub fn create_scopechain(&self) -> ScopeChain<'gc> {
|
||||
|
@ -1726,9 +1732,8 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
};
|
||||
|
||||
let class_entry = self.table_class(method, index)?;
|
||||
let scope = self.create_scopechain();
|
||||
|
||||
let new_class = ClassObject::from_class(self, class_entry, base_class, scope)?;
|
||||
let new_class = ClassObject::from_class(self, class_entry, base_class)?;
|
||||
|
||||
self.context.avm2.push(new_class);
|
||||
|
||||
|
|
|
@ -288,10 +288,10 @@ fn function<'gc>(
|
|||
name: &'static str,
|
||||
nf: NativeMethodImpl,
|
||||
script: Script<'gc>,
|
||||
scope: ScopeChain<'gc>,
|
||||
) -> Result<(), Error> {
|
||||
let (_, _, mut domain) = script.init();
|
||||
let mc = activation.context.gc_context;
|
||||
let scope = activation.create_scopechain();
|
||||
let qname = QName::new(Namespace::package(package), name);
|
||||
let method = Method::from_builtin(nf, name, mc);
|
||||
let as3fn = FunctionObject::from_method(activation, method, scope, None).into();
|
||||
|
@ -330,7 +330,6 @@ fn class<'gc>(
|
|||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
class_def: GcCell<'gc, Class<'gc>>,
|
||||
script: Script<'gc>,
|
||||
scope: ScopeChain<'gc>,
|
||||
) -> Result<(ClassObject<'gc>, Object<'gc>), Error> {
|
||||
let (_, mut global, mut domain) = script.init();
|
||||
|
||||
|
@ -359,7 +358,7 @@ fn class<'gc>(
|
|||
let class_name = class_read.name().clone();
|
||||
drop(class_read);
|
||||
|
||||
let class_object = ClassObject::from_class(activation, class_def, super_class, scope)?;
|
||||
let class_object = ClassObject::from_class(activation, class_def, super_class)?;
|
||||
global.install_const(
|
||||
activation.context.gc_context,
|
||||
class_name.clone(),
|
||||
|
@ -397,8 +396,8 @@ fn constant<'gc>(
|
|||
}
|
||||
|
||||
macro_rules! avm2_system_class {
|
||||
($field:ident, $activation:ident, $class:expr, $script:expr, $scope:expr) => {
|
||||
let (class_object, proto) = class($activation, $class, $script, $scope)?;
|
||||
($field:ident, $activation:ident, $class:expr, $script:expr) => {
|
||||
let (class_object, proto) = class($activation, $class, $script)?;
|
||||
|
||||
let sc = $activation.avm2().system_classes.as_mut().unwrap();
|
||||
sc.$field = class_object;
|
||||
|
@ -424,6 +423,9 @@ pub fn load_player_globals<'gc>(
|
|||
let gs = ScopeChain::new(domain).chain(mc, &[Scope::new(globals)]);
|
||||
let script = Script::empty_script(mc, globals, domain);
|
||||
|
||||
// Set the outer scope of this activation to the global scope.
|
||||
activation.set_outer(gs);
|
||||
|
||||
// public / root package
|
||||
//
|
||||
// This part of global initialization is very complicated, because
|
||||
|
@ -439,17 +441,17 @@ pub fn load_player_globals<'gc>(
|
|||
// Hence, this ridiculously complicated dance of classdef, type allocation,
|
||||
// and partial initialization.
|
||||
let object_classdef = object::create_class(mc);
|
||||
let object_class = ClassObject::from_class_partial(activation, object_classdef, None, gs)?;
|
||||
let object_class = ClassObject::from_class_partial(activation, object_classdef, None)?;
|
||||
let object_proto = ScriptObject::bare_object(mc);
|
||||
|
||||
let fn_classdef = function::create_class(mc);
|
||||
let fn_class =
|
||||
ClassObject::from_class_partial(activation, fn_classdef, Some(object_class.into()), gs)?;
|
||||
ClassObject::from_class_partial(activation, fn_classdef, Some(object_class.into()))?;
|
||||
let fn_proto = ScriptObject::object(mc, object_proto);
|
||||
|
||||
let class_classdef = class::create_class(mc);
|
||||
let class_class =
|
||||
ClassObject::from_class_partial(activation, class_classdef, Some(object_class.into()), gs)?;
|
||||
ClassObject::from_class_partial(activation, class_classdef, Some(object_class.into()))?;
|
||||
let class_proto = ScriptObject::object(mc, object_proto);
|
||||
|
||||
// Now to weave the Gordian knot...
|
||||
|
@ -489,14 +491,7 @@ pub fn load_player_globals<'gc>(
|
|||
|
||||
// After this point, it is safe to initialize any other classes.
|
||||
// Make sure to initialize superclasses *before* their subclasses!
|
||||
|
||||
avm2_system_class!(
|
||||
global,
|
||||
activation,
|
||||
global_scope::create_class(mc),
|
||||
script,
|
||||
gs
|
||||
);
|
||||
avm2_system_class!(global, activation, global_scope::create_class(mc), script);
|
||||
|
||||
// Oh, one more small hitch: the domain everything gets put into was
|
||||
// actually made *before* the core class weave, so let's fix that up now
|
||||
|
@ -504,132 +499,104 @@ pub fn load_player_globals<'gc>(
|
|||
globals.set_proto(mc, activation.avm2().prototypes().global);
|
||||
globals.set_instance_of(mc, activation.avm2().classes().global);
|
||||
|
||||
avm2_system_class!(string, activation, string::create_class(mc), script, gs);
|
||||
avm2_system_class!(boolean, activation, boolean::create_class(mc), script, gs);
|
||||
avm2_system_class!(number, activation, number::create_class(mc), script, gs);
|
||||
avm2_system_class!(int, activation, int::create_class(mc), script, gs);
|
||||
avm2_system_class!(uint, activation, uint::create_class(mc), script, gs);
|
||||
avm2_system_class!(
|
||||
namespace,
|
||||
activation,
|
||||
namespace::create_class(mc),
|
||||
script,
|
||||
gs
|
||||
);
|
||||
avm2_system_class!(qname, activation, qname::create_class(mc), script, gs);
|
||||
avm2_system_class!(array, activation, array::create_class(mc), script, gs);
|
||||
avm2_system_class!(string, activation, string::create_class(mc), script);
|
||||
avm2_system_class!(boolean, activation, boolean::create_class(mc), script);
|
||||
avm2_system_class!(number, activation, number::create_class(mc), script);
|
||||
avm2_system_class!(int, activation, int::create_class(mc), script);
|
||||
avm2_system_class!(uint, activation, uint::create_class(mc), script);
|
||||
avm2_system_class!(namespace, activation, namespace::create_class(mc), script);
|
||||
avm2_system_class!(qname, activation, qname::create_class(mc), script);
|
||||
avm2_system_class!(array, activation, array::create_class(mc), script);
|
||||
|
||||
function(activation, "", "trace", trace, script, gs)?;
|
||||
function(activation, "", "isFinite", is_finite, script, gs)?;
|
||||
function(activation, "", "isNaN", is_nan, script, gs)?;
|
||||
function(activation, "", "trace", trace, script)?;
|
||||
function(activation, "", "isFinite", is_finite, script)?;
|
||||
function(activation, "", "isNaN", is_nan, script)?;
|
||||
constant(mc, "", "undefined", Value::Undefined, script)?;
|
||||
constant(mc, "", "null", Value::Null, script)?;
|
||||
constant(mc, "", "NaN", f64::NAN.into(), script)?;
|
||||
constant(mc, "", "Infinity", f64::INFINITY.into(), script)?;
|
||||
|
||||
class(activation, math::create_class(mc), script, gs)?;
|
||||
avm2_system_class!(regexp, activation, regexp::create_class(mc), script, gs);
|
||||
avm2_system_class!(vector, activation, vector::create_class(mc), script, gs);
|
||||
avm2_system_class!(xml, activation, xml::create_class(mc), script, gs);
|
||||
avm2_system_class!(xml_list, activation, xml_list::create_class(mc), script, gs);
|
||||
class(activation, math::create_class(mc), script)?;
|
||||
avm2_system_class!(regexp, activation, regexp::create_class(mc), script);
|
||||
avm2_system_class!(vector, activation, vector::create_class(mc), script);
|
||||
avm2_system_class!(xml, activation, xml::create_class(mc), script);
|
||||
avm2_system_class!(xml_list, activation, xml_list::create_class(mc), script);
|
||||
|
||||
avm2_system_class!(date, activation, date::create_class(mc), script, gs);
|
||||
avm2_system_class!(date, activation, date::create_class(mc), script);
|
||||
|
||||
// package `flash.system`
|
||||
avm2_system_class!(
|
||||
application_domain,
|
||||
activation,
|
||||
flash::system::application_domain::create_class(mc),
|
||||
script,
|
||||
gs
|
||||
script
|
||||
);
|
||||
class(
|
||||
activation,
|
||||
flash::system::capabilities::create_class(mc),
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
class(
|
||||
activation,
|
||||
flash::system::security::create_class(mc),
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
class(
|
||||
activation,
|
||||
flash::system::system::create_class(mc),
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
class(activation, flash::system::system::create_class(mc), script)?;
|
||||
|
||||
// package `flash.events`
|
||||
avm2_system_class!(
|
||||
event,
|
||||
activation,
|
||||
flash::events::event::create_class(mc),
|
||||
script,
|
||||
gs
|
||||
script
|
||||
);
|
||||
class(
|
||||
activation,
|
||||
flash::events::ieventdispatcher::create_interface(mc),
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
class(
|
||||
activation,
|
||||
flash::events::eventdispatcher::create_class(mc),
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
class(
|
||||
activation,
|
||||
flash::events::mouseevent::create_class(mc),
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
class(
|
||||
activation,
|
||||
flash::events::keyboardevent::create_class(mc),
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
class(
|
||||
activation,
|
||||
flash::events::progressevent::create_class(mc),
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
// package `flash.utils`
|
||||
avm2_system_class!(
|
||||
bytearray,
|
||||
activation,
|
||||
flash::utils::bytearray::create_class(mc),
|
||||
script,
|
||||
gs
|
||||
script
|
||||
);
|
||||
|
||||
domain.init_default_domain_memory(activation)?;
|
||||
|
||||
class(
|
||||
activation,
|
||||
flash::utils::endian::create_class(mc),
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
class(activation, flash::utils::endian::create_class(mc), script)?;
|
||||
|
||||
class(
|
||||
activation,
|
||||
flash::utils::compression_algorithm::create_class(mc),
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
|
||||
class(
|
||||
activation,
|
||||
flash::utils::dictionary::create_class(mc),
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
|
||||
function(
|
||||
|
@ -638,7 +605,6 @@ pub fn load_player_globals<'gc>(
|
|||
"getTimer",
|
||||
flash::utils::get_timer,
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
|
||||
function(
|
||||
|
@ -647,7 +613,6 @@ pub fn load_player_globals<'gc>(
|
|||
"getQualifiedClassName",
|
||||
flash::utils::get_qualified_class_name,
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
|
||||
function(
|
||||
|
@ -656,7 +621,6 @@ pub fn load_player_globals<'gc>(
|
|||
"getQualifiedSuperclassName",
|
||||
flash::utils::get_qualified_super_class_name,
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
|
||||
function(
|
||||
|
@ -665,7 +629,6 @@ pub fn load_player_globals<'gc>(
|
|||
"getDefinitionByName",
|
||||
flash::utils::get_definition_by_name,
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
|
||||
// package `flash.display`
|
||||
|
@ -673,157 +636,133 @@ pub fn load_player_globals<'gc>(
|
|||
activation,
|
||||
flash::display::ibitmapdrawable::create_interface(mc),
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
avm2_system_class!(
|
||||
display_object,
|
||||
activation,
|
||||
flash::display::displayobject::create_class(mc),
|
||||
script,
|
||||
gs
|
||||
script
|
||||
);
|
||||
avm2_system_class!(
|
||||
shape,
|
||||
activation,
|
||||
flash::display::shape::create_class(mc),
|
||||
script,
|
||||
gs
|
||||
script
|
||||
);
|
||||
class(
|
||||
activation,
|
||||
flash::display::interactiveobject::create_class(mc),
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
avm2_system_class!(
|
||||
simplebutton,
|
||||
activation,
|
||||
flash::display::simplebutton::create_class(mc),
|
||||
script,
|
||||
gs
|
||||
script
|
||||
);
|
||||
class(
|
||||
activation,
|
||||
flash::display::displayobjectcontainer::create_class(mc),
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
avm2_system_class!(
|
||||
sprite,
|
||||
activation,
|
||||
flash::display::sprite::create_class(mc),
|
||||
script,
|
||||
gs
|
||||
script
|
||||
);
|
||||
avm2_system_class!(
|
||||
movieclip,
|
||||
activation,
|
||||
flash::display::movieclip::create_class(mc),
|
||||
script,
|
||||
gs
|
||||
script
|
||||
);
|
||||
avm2_system_class!(
|
||||
framelabel,
|
||||
activation,
|
||||
flash::display::framelabel::create_class(mc),
|
||||
script,
|
||||
gs
|
||||
script
|
||||
);
|
||||
avm2_system_class!(
|
||||
scene,
|
||||
activation,
|
||||
flash::display::scene::create_class(mc),
|
||||
script,
|
||||
gs
|
||||
script
|
||||
);
|
||||
avm2_system_class!(
|
||||
graphics,
|
||||
activation,
|
||||
flash::display::graphics::create_class(mc),
|
||||
script,
|
||||
gs
|
||||
script
|
||||
);
|
||||
class(
|
||||
activation,
|
||||
flash::display::jointstyle::create_class(mc),
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
class(
|
||||
activation,
|
||||
flash::display::linescalemode::create_class(mc),
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
class(
|
||||
activation,
|
||||
flash::display::capsstyle::create_class(mc),
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
avm2_system_class!(
|
||||
loaderinfo,
|
||||
activation,
|
||||
flash::display::loaderinfo::create_class(mc),
|
||||
script,
|
||||
gs
|
||||
script
|
||||
);
|
||||
class(
|
||||
activation,
|
||||
flash::display::actionscriptversion::create_class(mc),
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
class(
|
||||
activation,
|
||||
flash::display::swfversion::create_class(mc),
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
avm2_system_class!(
|
||||
stage,
|
||||
activation,
|
||||
flash::display::stage::create_class(mc),
|
||||
script,
|
||||
gs
|
||||
script
|
||||
);
|
||||
class(
|
||||
activation,
|
||||
flash::display::stagescalemode::create_class(mc),
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
class(
|
||||
activation,
|
||||
flash::display::stagealign::create_class(mc),
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
class(
|
||||
activation,
|
||||
flash::display::stagedisplaystate::create_class(mc),
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
class(
|
||||
activation,
|
||||
flash::display::stagequality::create_class(mc),
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
avm2_system_class!(
|
||||
bitmap,
|
||||
activation,
|
||||
flash::display::bitmap::create_class(mc),
|
||||
script,
|
||||
gs
|
||||
script
|
||||
);
|
||||
avm2_system_class!(
|
||||
bitmapdata,
|
||||
activation,
|
||||
flash::display::bitmapdata::create_class(mc),
|
||||
script,
|
||||
gs
|
||||
script
|
||||
);
|
||||
|
||||
// package `flash.geom`
|
||||
|
@ -831,15 +770,13 @@ pub fn load_player_globals<'gc>(
|
|||
point,
|
||||
activation,
|
||||
flash::geom::point::create_class(mc),
|
||||
script,
|
||||
gs
|
||||
script
|
||||
);
|
||||
avm2_system_class!(
|
||||
rectangle,
|
||||
activation,
|
||||
flash::geom::rectangle::create_class(mc),
|
||||
script,
|
||||
gs
|
||||
script
|
||||
);
|
||||
|
||||
// package `flash.media`
|
||||
|
@ -847,34 +784,25 @@ pub fn load_player_globals<'gc>(
|
|||
video,
|
||||
activation,
|
||||
flash::media::video::create_class(mc),
|
||||
script,
|
||||
gs
|
||||
script
|
||||
);
|
||||
class(
|
||||
activation,
|
||||
flash::media::sound::create_class(mc),
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
class(activation, flash::media::sound::create_class(mc), script)?;
|
||||
avm2_system_class!(
|
||||
soundtransform,
|
||||
activation,
|
||||
flash::media::soundtransform::create_class(mc),
|
||||
script,
|
||||
gs
|
||||
script
|
||||
);
|
||||
class(
|
||||
activation,
|
||||
flash::media::soundmixer::create_class(mc),
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
avm2_system_class!(
|
||||
soundchannel,
|
||||
activation,
|
||||
flash::media::soundchannel::create_class(mc),
|
||||
script,
|
||||
gs
|
||||
script
|
||||
);
|
||||
|
||||
// package `flash.text`
|
||||
|
@ -882,35 +810,30 @@ pub fn load_player_globals<'gc>(
|
|||
textfield,
|
||||
activation,
|
||||
flash::text::textfield::create_class(mc),
|
||||
script,
|
||||
gs
|
||||
script
|
||||
);
|
||||
avm2_system_class!(
|
||||
textformat,
|
||||
activation,
|
||||
flash::text::textformat::create_class(mc),
|
||||
script,
|
||||
gs
|
||||
script
|
||||
);
|
||||
class(
|
||||
activation,
|
||||
flash::text::textfieldautosize::create_class(mc),
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
class(
|
||||
activation,
|
||||
flash::text::textformatalign::create_class(mc),
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
class(
|
||||
activation,
|
||||
flash::text::textfieldtype::create_class(mc),
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
class(activation, flash::text::font::create_class(mc), script, gs)?;
|
||||
class(activation, flash::text::font::create_class(mc), script)?;
|
||||
|
||||
// package `flash.crypto`
|
||||
function(
|
||||
|
@ -919,7 +842,6 @@ pub fn load_player_globals<'gc>(
|
|||
"generateRandomBytes",
|
||||
flash::crypto::generate_random_bytes,
|
||||
script,
|
||||
gs,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -85,9 +85,8 @@ impl<'gc> ClassObject<'gc> {
|
|||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
superclass_object: Option<ClassObject<'gc>>,
|
||||
scope: ScopeChain<'gc>,
|
||||
) -> Result<ClassObject<'gc>, Error> {
|
||||
let class_object = Self::from_class_partial(activation, class, superclass_object, scope)?;
|
||||
) -> Result<Self, Error> {
|
||||
let class_object = Self::from_class_partial(activation, class, superclass_object)?;
|
||||
|
||||
//TODO: Class prototypes are *not* instances of their class and should
|
||||
//not be allocated by the class allocator, but instead should be
|
||||
|
@ -130,8 +129,8 @@ impl<'gc> ClassObject<'gc> {
|
|||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
superclass_object: Option<ClassObject<'gc>>,
|
||||
scope: ScopeChain<'gc>,
|
||||
) -> Result<Self, Error> {
|
||||
let scope = activation.create_scopechain();
|
||||
if let Some(base_class) = superclass_object.map(|b| b.inner_class_definition()) {
|
||||
if base_class.read().is_final() {
|
||||
return Err(format!(
|
||||
|
@ -174,7 +173,7 @@ impl<'gc> ClassObject<'gc> {
|
|||
ClassObjectData {
|
||||
base: ScriptObjectData::base_new(None, None),
|
||||
class,
|
||||
scope,
|
||||
scope: scope,
|
||||
superclass_object,
|
||||
instance_allocator: Allocator(instance_allocator),
|
||||
constructor,
|
||||
|
@ -204,7 +203,7 @@ impl<'gc> ClassObject<'gc> {
|
|||
pub fn into_finished_class(
|
||||
mut self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
) -> Result<ClassObject<'gc>, Error> {
|
||||
) -> Result<Self, Error> {
|
||||
let class = self.inner_class_definition();
|
||||
let class_class = self.instance_of().ok_or(
|
||||
"Cannot finish initialization of core class without it being linked to a type!",
|
||||
|
@ -301,8 +300,8 @@ impl<'gc> ClassObject<'gc> {
|
|||
) -> Result<(), Error> {
|
||||
let object: Object<'gc> = self.into();
|
||||
|
||||
let class = self.0.read().class;
|
||||
let scope = self.0.read().scope;
|
||||
let class = self.0.read().class;
|
||||
let class_read = class.read();
|
||||
|
||||
if !class_read.is_class_initialized() {
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::avm2::names::Multiname;
|
|||
use crate::avm2::object::{Object, TObject};
|
||||
use crate::avm2::value::Value;
|
||||
use crate::avm2::Error;
|
||||
use gc_arena::{Collect, Gc, MutationContext};
|
||||
use gc_arena::{Collect, GcCell, MutationContext};
|
||||
use std::ops::Deref;
|
||||
|
||||
/// Represents a Scope that can be on either a ScopeChain or local ScopeStack.
|
||||
|
@ -59,10 +59,13 @@ impl<'gc> Scope<'gc> {
|
|||
/// ScopeChain's are copy-on-write, meaning when we chain new scopes on top of a ScopeChain, we
|
||||
/// actually create a completely brand new ScopeChain. The Domain of the ScopeChain we are chaining
|
||||
/// on top of will be used for the new ScopeChain.
|
||||
///
|
||||
/// Alternatively, the `add` method allows mutating the ScopeChain, but it should be used with caution.
|
||||
/// Mutating a ScopeChain that functions/classes saved could cause bizzare bugs.
|
||||
#[derive(Debug, Collect, Clone, Copy)]
|
||||
#[collect(no_drop)]
|
||||
pub struct ScopeChain<'gc> {
|
||||
scopes: Option<Gc<'gc, Vec<Scope<'gc>>>>,
|
||||
scopes: Option<GcCell<'gc, Vec<Scope<'gc>>>>,
|
||||
domain: Domain<'gc>,
|
||||
}
|
||||
|
||||
|
@ -77,19 +80,15 @@ impl<'gc> ScopeChain<'gc> {
|
|||
|
||||
/// Creates a new ScopeChain by chaining new scopes on top of this ScopeChain
|
||||
pub fn chain(&self, mc: MutationContext<'gc, '_>, new_scopes: &[Scope<'gc>]) -> Self {
|
||||
if new_scopes.is_empty() {
|
||||
// If we are not actually adding any new scopes, we don't need to do anything.
|
||||
return *self;
|
||||
}
|
||||
// TODO: This current implementation is a bit expensive, but it is exactly what avmplus does, so it's good enough for now.
|
||||
match self.scopes {
|
||||
Some(scopes) => {
|
||||
// The new ScopeChain is created by cloning the scopes of this ScopeChain,
|
||||
// and pushing the new scopes on top of that.
|
||||
let mut cloned = scopes.deref().clone();
|
||||
let mut cloned = scopes.read().deref().clone();
|
||||
cloned.extend_from_slice(new_scopes);
|
||||
Self {
|
||||
scopes: Some(Gc::allocate(mc, cloned)),
|
||||
scopes: Some(GcCell::allocate(mc, cloned)),
|
||||
domain: self.domain,
|
||||
}
|
||||
}
|
||||
|
@ -97,20 +96,33 @@ impl<'gc> ScopeChain<'gc> {
|
|||
// We are chaining on top of an empty ScopeChain, so we don't actually
|
||||
// need to chain anything.
|
||||
Self {
|
||||
scopes: Some(Gc::allocate(mc, new_scopes.to_vec())),
|
||||
scopes: Some(GcCell::allocate(mc, new_scopes.to_vec())),
|
||||
domain: self.domain,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a new scope to this ScopeChain. Unlike `chain`, this will actually mutate the
|
||||
/// underlying scopes.
|
||||
///
|
||||
/// If this ScopeChain is empty (self.scopes is None), this is a no-op.
|
||||
///
|
||||
/// WARNING: This can cause bizarre bugs, because it can change the saved ScopeChain
|
||||
/// of functions/classes that are using it.
|
||||
pub fn add(&self, mc: MutationContext<'gc, '_>, scope: Scope<'gc>) {
|
||||
self.scopes.map(|scopes| scopes.write(mc).push(scope));
|
||||
}
|
||||
|
||||
pub fn get(&self, index: usize) -> Option<Scope<'gc>> {
|
||||
self.scopes
|
||||
.and_then(|scopes| scopes.deref().get(index).cloned())
|
||||
.and_then(|scopes| scopes.read().get(index).cloned())
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.scopes.map(|scopes| scopes.is_empty()).unwrap_or(true)
|
||||
self.scopes
|
||||
.map(|scopes| scopes.read().is_empty())
|
||||
.unwrap_or(true)
|
||||
}
|
||||
|
||||
/// Returns the domain associated with this ScopeChain.
|
||||
|
@ -125,7 +137,7 @@ impl<'gc> ScopeChain<'gc> {
|
|||
) -> Result<Option<Object<'gc>>, Error> {
|
||||
// First search our scopes
|
||||
if let Some(scopes) = self.scopes {
|
||||
for (depth, scope) in scopes.iter().enumerate().rev() {
|
||||
for (depth, scope) in scopes.read().iter().enumerate().rev() {
|
||||
let values = scope.values();
|
||||
if let Some(qname) = values.resolve_multiname(name)? {
|
||||
// We search the dynamic properties if either conditions are met:
|
||||
|
|
|
@ -342,11 +342,7 @@ impl<'gc> Script<'gc> {
|
|||
let script = script?;
|
||||
|
||||
for abc_trait in script.traits.iter() {
|
||||
drop(write);
|
||||
|
||||
let newtrait = Trait::from_abc_trait(unit, abc_trait, activation)?;
|
||||
|
||||
write = self.0.write(activation.context.gc_context);
|
||||
write.domain.export_definition(
|
||||
newtrait.name().clone(),
|
||||
*self,
|
||||
|
|
Loading…
Reference in New Issue