avm2: Allow small mutations to ScopeChain

This commit is contained in:
EmperorBale 2021-09-14 15:57:52 -07:00 committed by kmeisthax
parent 9de7d7ba7a
commit 1dd899a76f
5 changed files with 92 additions and 158 deletions

View File

@ -402,10 +402,11 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
let translation_unit = method.translation_unit(); let translation_unit = method.translation_unit();
let abc_method = method.method(); let abc_method = method.method();
let mut dummy_activation = Activation::from_nothing(context.reborrow()); let mut dummy_activation = Activation::from_nothing(context.reborrow());
dummy_activation.set_outer(outer);
let activation_class = let activation_class =
Class::for_activation(&mut dummy_activation, translation_unit, abc_method, body)?; Class::for_activation(&mut dummy_activation, translation_unit, abc_method, body)?;
let activation_class_object = 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); 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 /// Creates a new ScopeChain by chaining the current state of this
/// activation's scope stack with the outer scope. /// activation's scope stack with the outer scope.
pub fn create_scopechain(&self) -> ScopeChain<'gc> { 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 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); self.context.avm2.push(new_class);

View File

@ -288,10 +288,10 @@ fn function<'gc>(
name: &'static str, name: &'static str,
nf: NativeMethodImpl, nf: NativeMethodImpl,
script: Script<'gc>, script: Script<'gc>,
scope: ScopeChain<'gc>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let (_, _, mut domain) = script.init(); let (_, _, mut domain) = script.init();
let mc = activation.context.gc_context; let mc = activation.context.gc_context;
let scope = activation.create_scopechain();
let qname = QName::new(Namespace::package(package), name); let qname = QName::new(Namespace::package(package), name);
let method = Method::from_builtin(nf, name, mc); let method = Method::from_builtin(nf, name, mc);
let as3fn = FunctionObject::from_method(activation, method, scope, None).into(); let as3fn = FunctionObject::from_method(activation, method, scope, None).into();
@ -330,7 +330,6 @@ fn class<'gc>(
activation: &mut Activation<'_, 'gc, '_>, activation: &mut Activation<'_, 'gc, '_>,
class_def: GcCell<'gc, Class<'gc>>, class_def: GcCell<'gc, Class<'gc>>,
script: Script<'gc>, script: Script<'gc>,
scope: ScopeChain<'gc>,
) -> Result<(ClassObject<'gc>, Object<'gc>), Error> { ) -> Result<(ClassObject<'gc>, Object<'gc>), Error> {
let (_, mut global, mut domain) = script.init(); let (_, mut global, mut domain) = script.init();
@ -359,7 +358,7 @@ fn class<'gc>(
let class_name = class_read.name().clone(); let class_name = class_read.name().clone();
drop(class_read); 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( global.install_const(
activation.context.gc_context, activation.context.gc_context,
class_name.clone(), class_name.clone(),
@ -397,8 +396,8 @@ fn constant<'gc>(
} }
macro_rules! avm2_system_class { macro_rules! avm2_system_class {
($field:ident, $activation:ident, $class:expr, $script:expr, $scope:expr) => { ($field:ident, $activation:ident, $class:expr, $script:expr) => {
let (class_object, proto) = class($activation, $class, $script, $scope)?; let (class_object, proto) = class($activation, $class, $script)?;
let sc = $activation.avm2().system_classes.as_mut().unwrap(); let sc = $activation.avm2().system_classes.as_mut().unwrap();
sc.$field = class_object; 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 gs = ScopeChain::new(domain).chain(mc, &[Scope::new(globals)]);
let script = Script::empty_script(mc, globals, domain); 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 // public / root package
// //
// This part of global initialization is very complicated, because // 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, // Hence, this ridiculously complicated dance of classdef, type allocation,
// and partial initialization. // and partial initialization.
let object_classdef = object::create_class(mc); 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 object_proto = ScriptObject::bare_object(mc);
let fn_classdef = function::create_class(mc); let fn_classdef = function::create_class(mc);
let fn_class = 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 fn_proto = ScriptObject::object(mc, object_proto);
let class_classdef = class::create_class(mc); let class_classdef = class::create_class(mc);
let class_class = 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); let class_proto = ScriptObject::object(mc, object_proto);
// Now to weave the Gordian knot... // 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. // After this point, it is safe to initialize any other classes.
// Make sure to initialize superclasses *before* their subclasses! // Make sure to initialize superclasses *before* their subclasses!
avm2_system_class!(global, activation, global_scope::create_class(mc), script);
avm2_system_class!(
global,
activation,
global_scope::create_class(mc),
script,
gs
);
// Oh, one more small hitch: the domain everything gets put into was // 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 // 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_proto(mc, activation.avm2().prototypes().global);
globals.set_instance_of(mc, activation.avm2().classes().global); globals.set_instance_of(mc, activation.avm2().classes().global);
avm2_system_class!(string, activation, string::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, gs); avm2_system_class!(boolean, activation, boolean::create_class(mc), script);
avm2_system_class!(number, activation, number::create_class(mc), script, gs); avm2_system_class!(number, activation, number::create_class(mc), script);
avm2_system_class!(int, activation, int::create_class(mc), script, gs); avm2_system_class!(int, activation, int::create_class(mc), script);
avm2_system_class!(uint, activation, uint::create_class(mc), script, gs); avm2_system_class!(uint, activation, uint::create_class(mc), script);
avm2_system_class!( avm2_system_class!(namespace, activation, namespace::create_class(mc), script);
namespace, avm2_system_class!(qname, activation, qname::create_class(mc), script);
activation, avm2_system_class!(array, activation, array::create_class(mc), script);
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);
function(activation, "", "trace", trace, script, gs)?; function(activation, "", "trace", trace, script)?;
function(activation, "", "isFinite", is_finite, script, gs)?; function(activation, "", "isFinite", is_finite, script)?;
function(activation, "", "isNaN", is_nan, script, gs)?; function(activation, "", "isNaN", is_nan, script)?;
constant(mc, "", "undefined", Value::Undefined, script)?; constant(mc, "", "undefined", Value::Undefined, script)?;
constant(mc, "", "null", Value::Null, script)?; constant(mc, "", "null", Value::Null, script)?;
constant(mc, "", "NaN", f64::NAN.into(), script)?; constant(mc, "", "NaN", f64::NAN.into(), script)?;
constant(mc, "", "Infinity", f64::INFINITY.into(), script)?; constant(mc, "", "Infinity", f64::INFINITY.into(), script)?;
class(activation, math::create_class(mc), script, gs)?; class(activation, math::create_class(mc), script)?;
avm2_system_class!(regexp, activation, regexp::create_class(mc), script, gs); avm2_system_class!(regexp, activation, regexp::create_class(mc), script);
avm2_system_class!(vector, activation, vector::create_class(mc), script, gs); avm2_system_class!(vector, activation, vector::create_class(mc), script);
avm2_system_class!(xml, activation, xml::create_class(mc), script, gs); avm2_system_class!(xml, activation, xml::create_class(mc), script);
avm2_system_class!(xml_list, activation, xml_list::create_class(mc), script, gs); 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` // package `flash.system`
avm2_system_class!( avm2_system_class!(
application_domain, application_domain,
activation, activation,
flash::system::application_domain::create_class(mc), flash::system::application_domain::create_class(mc),
script, script
gs
); );
class( class(
activation, activation,
flash::system::capabilities::create_class(mc), flash::system::capabilities::create_class(mc),
script, script,
gs,
)?; )?;
class( class(
activation, activation,
flash::system::security::create_class(mc), flash::system::security::create_class(mc),
script, script,
gs,
)?;
class(
activation,
flash::system::system::create_class(mc),
script,
gs,
)?; )?;
class(activation, flash::system::system::create_class(mc), script)?;
// package `flash.events` // package `flash.events`
avm2_system_class!( avm2_system_class!(
event, event,
activation, activation,
flash::events::event::create_class(mc), flash::events::event::create_class(mc),
script, script
gs
); );
class( class(
activation, activation,
flash::events::ieventdispatcher::create_interface(mc), flash::events::ieventdispatcher::create_interface(mc),
script, script,
gs,
)?; )?;
class( class(
activation, activation,
flash::events::eventdispatcher::create_class(mc), flash::events::eventdispatcher::create_class(mc),
script, script,
gs,
)?; )?;
class( class(
activation, activation,
flash::events::mouseevent::create_class(mc), flash::events::mouseevent::create_class(mc),
script, script,
gs,
)?; )?;
class( class(
activation, activation,
flash::events::keyboardevent::create_class(mc), flash::events::keyboardevent::create_class(mc),
script, script,
gs,
)?; )?;
class( class(
activation, activation,
flash::events::progressevent::create_class(mc), flash::events::progressevent::create_class(mc),
script, script,
gs,
)?; )?;
// package `flash.utils` // package `flash.utils`
avm2_system_class!( avm2_system_class!(
bytearray, bytearray,
activation, activation,
flash::utils::bytearray::create_class(mc), flash::utils::bytearray::create_class(mc),
script, script
gs
); );
domain.init_default_domain_memory(activation)?; domain.init_default_domain_memory(activation)?;
class( class(activation, flash::utils::endian::create_class(mc), script)?;
activation,
flash::utils::endian::create_class(mc),
script,
gs,
)?;
class( class(
activation, activation,
flash::utils::compression_algorithm::create_class(mc), flash::utils::compression_algorithm::create_class(mc),
script, script,
gs,
)?; )?;
class( class(
activation, activation,
flash::utils::dictionary::create_class(mc), flash::utils::dictionary::create_class(mc),
script, script,
gs,
)?; )?;
function( function(
@ -638,7 +605,6 @@ pub fn load_player_globals<'gc>(
"getTimer", "getTimer",
flash::utils::get_timer, flash::utils::get_timer,
script, script,
gs,
)?; )?;
function( function(
@ -647,7 +613,6 @@ pub fn load_player_globals<'gc>(
"getQualifiedClassName", "getQualifiedClassName",
flash::utils::get_qualified_class_name, flash::utils::get_qualified_class_name,
script, script,
gs,
)?; )?;
function( function(
@ -656,7 +621,6 @@ pub fn load_player_globals<'gc>(
"getQualifiedSuperclassName", "getQualifiedSuperclassName",
flash::utils::get_qualified_super_class_name, flash::utils::get_qualified_super_class_name,
script, script,
gs,
)?; )?;
function( function(
@ -665,7 +629,6 @@ pub fn load_player_globals<'gc>(
"getDefinitionByName", "getDefinitionByName",
flash::utils::get_definition_by_name, flash::utils::get_definition_by_name,
script, script,
gs,
)?; )?;
// package `flash.display` // package `flash.display`
@ -673,157 +636,133 @@ pub fn load_player_globals<'gc>(
activation, activation,
flash::display::ibitmapdrawable::create_interface(mc), flash::display::ibitmapdrawable::create_interface(mc),
script, script,
gs,
)?; )?;
avm2_system_class!( avm2_system_class!(
display_object, display_object,
activation, activation,
flash::display::displayobject::create_class(mc), flash::display::displayobject::create_class(mc),
script, script
gs
); );
avm2_system_class!( avm2_system_class!(
shape, shape,
activation, activation,
flash::display::shape::create_class(mc), flash::display::shape::create_class(mc),
script, script
gs
); );
class( class(
activation, activation,
flash::display::interactiveobject::create_class(mc), flash::display::interactiveobject::create_class(mc),
script, script,
gs,
)?; )?;
avm2_system_class!( avm2_system_class!(
simplebutton, simplebutton,
activation, activation,
flash::display::simplebutton::create_class(mc), flash::display::simplebutton::create_class(mc),
script, script
gs
); );
class( class(
activation, activation,
flash::display::displayobjectcontainer::create_class(mc), flash::display::displayobjectcontainer::create_class(mc),
script, script,
gs,
)?; )?;
avm2_system_class!( avm2_system_class!(
sprite, sprite,
activation, activation,
flash::display::sprite::create_class(mc), flash::display::sprite::create_class(mc),
script, script
gs
); );
avm2_system_class!( avm2_system_class!(
movieclip, movieclip,
activation, activation,
flash::display::movieclip::create_class(mc), flash::display::movieclip::create_class(mc),
script, script
gs
); );
avm2_system_class!( avm2_system_class!(
framelabel, framelabel,
activation, activation,
flash::display::framelabel::create_class(mc), flash::display::framelabel::create_class(mc),
script, script
gs
); );
avm2_system_class!( avm2_system_class!(
scene, scene,
activation, activation,
flash::display::scene::create_class(mc), flash::display::scene::create_class(mc),
script, script
gs
); );
avm2_system_class!( avm2_system_class!(
graphics, graphics,
activation, activation,
flash::display::graphics::create_class(mc), flash::display::graphics::create_class(mc),
script, script
gs
); );
class( class(
activation, activation,
flash::display::jointstyle::create_class(mc), flash::display::jointstyle::create_class(mc),
script, script,
gs,
)?; )?;
class( class(
activation, activation,
flash::display::linescalemode::create_class(mc), flash::display::linescalemode::create_class(mc),
script, script,
gs,
)?; )?;
class( class(
activation, activation,
flash::display::capsstyle::create_class(mc), flash::display::capsstyle::create_class(mc),
script, script,
gs,
)?; )?;
avm2_system_class!( avm2_system_class!(
loaderinfo, loaderinfo,
activation, activation,
flash::display::loaderinfo::create_class(mc), flash::display::loaderinfo::create_class(mc),
script, script
gs
); );
class( class(
activation, activation,
flash::display::actionscriptversion::create_class(mc), flash::display::actionscriptversion::create_class(mc),
script, script,
gs,
)?; )?;
class( class(
activation, activation,
flash::display::swfversion::create_class(mc), flash::display::swfversion::create_class(mc),
script, script,
gs,
)?; )?;
avm2_system_class!( avm2_system_class!(
stage, stage,
activation, activation,
flash::display::stage::create_class(mc), flash::display::stage::create_class(mc),
script, script
gs
); );
class( class(
activation, activation,
flash::display::stagescalemode::create_class(mc), flash::display::stagescalemode::create_class(mc),
script, script,
gs,
)?; )?;
class( class(
activation, activation,
flash::display::stagealign::create_class(mc), flash::display::stagealign::create_class(mc),
script, script,
gs,
)?; )?;
class( class(
activation, activation,
flash::display::stagedisplaystate::create_class(mc), flash::display::stagedisplaystate::create_class(mc),
script, script,
gs,
)?; )?;
class( class(
activation, activation,
flash::display::stagequality::create_class(mc), flash::display::stagequality::create_class(mc),
script, script,
gs,
)?; )?;
avm2_system_class!( avm2_system_class!(
bitmap, bitmap,
activation, activation,
flash::display::bitmap::create_class(mc), flash::display::bitmap::create_class(mc),
script, script
gs
); );
avm2_system_class!( avm2_system_class!(
bitmapdata, bitmapdata,
activation, activation,
flash::display::bitmapdata::create_class(mc), flash::display::bitmapdata::create_class(mc),
script, script
gs
); );
// package `flash.geom` // package `flash.geom`
@ -831,15 +770,13 @@ pub fn load_player_globals<'gc>(
point, point,
activation, activation,
flash::geom::point::create_class(mc), flash::geom::point::create_class(mc),
script, script
gs
); );
avm2_system_class!( avm2_system_class!(
rectangle, rectangle,
activation, activation,
flash::geom::rectangle::create_class(mc), flash::geom::rectangle::create_class(mc),
script, script
gs
); );
// package `flash.media` // package `flash.media`
@ -847,34 +784,25 @@ pub fn load_player_globals<'gc>(
video, video,
activation, activation,
flash::media::video::create_class(mc), flash::media::video::create_class(mc),
script, script
gs
); );
class( class(activation, flash::media::sound::create_class(mc), script)?;
activation,
flash::media::sound::create_class(mc),
script,
gs,
)?;
avm2_system_class!( avm2_system_class!(
soundtransform, soundtransform,
activation, activation,
flash::media::soundtransform::create_class(mc), flash::media::soundtransform::create_class(mc),
script, script
gs
); );
class( class(
activation, activation,
flash::media::soundmixer::create_class(mc), flash::media::soundmixer::create_class(mc),
script, script,
gs,
)?; )?;
avm2_system_class!( avm2_system_class!(
soundchannel, soundchannel,
activation, activation,
flash::media::soundchannel::create_class(mc), flash::media::soundchannel::create_class(mc),
script, script
gs
); );
// package `flash.text` // package `flash.text`
@ -882,35 +810,30 @@ pub fn load_player_globals<'gc>(
textfield, textfield,
activation, activation,
flash::text::textfield::create_class(mc), flash::text::textfield::create_class(mc),
script, script
gs
); );
avm2_system_class!( avm2_system_class!(
textformat, textformat,
activation, activation,
flash::text::textformat::create_class(mc), flash::text::textformat::create_class(mc),
script, script
gs
); );
class( class(
activation, activation,
flash::text::textfieldautosize::create_class(mc), flash::text::textfieldautosize::create_class(mc),
script, script,
gs,
)?; )?;
class( class(
activation, activation,
flash::text::textformatalign::create_class(mc), flash::text::textformatalign::create_class(mc),
script, script,
gs,
)?; )?;
class( class(
activation, activation,
flash::text::textfieldtype::create_class(mc), flash::text::textfieldtype::create_class(mc),
script, script,
gs,
)?; )?;
class(activation, flash::text::font::create_class(mc), script, gs)?; class(activation, flash::text::font::create_class(mc), script)?;
// package `flash.crypto` // package `flash.crypto`
function( function(
@ -919,7 +842,6 @@ pub fn load_player_globals<'gc>(
"generateRandomBytes", "generateRandomBytes",
flash::crypto::generate_random_bytes, flash::crypto::generate_random_bytes,
script, script,
gs,
)?; )?;
Ok(()) Ok(())

View File

@ -85,9 +85,8 @@ impl<'gc> ClassObject<'gc> {
activation: &mut Activation<'_, 'gc, '_>, activation: &mut Activation<'_, 'gc, '_>,
class: GcCell<'gc, Class<'gc>>, class: GcCell<'gc, Class<'gc>>,
superclass_object: Option<ClassObject<'gc>>, superclass_object: Option<ClassObject<'gc>>,
scope: ScopeChain<'gc>, ) -> Result<Self, Error> {
) -> Result<ClassObject<'gc>, Error> { let class_object = Self::from_class_partial(activation, class, superclass_object)?;
let class_object = Self::from_class_partial(activation, class, superclass_object, scope)?;
//TODO: Class prototypes are *not* instances of their class and should //TODO: Class prototypes are *not* instances of their class and should
//not be allocated by the class allocator, but instead should be //not be allocated by the class allocator, but instead should be
@ -130,8 +129,8 @@ impl<'gc> ClassObject<'gc> {
activation: &mut Activation<'_, 'gc, '_>, activation: &mut Activation<'_, 'gc, '_>,
class: GcCell<'gc, Class<'gc>>, class: GcCell<'gc, Class<'gc>>,
superclass_object: Option<ClassObject<'gc>>, superclass_object: Option<ClassObject<'gc>>,
scope: ScopeChain<'gc>,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let scope = activation.create_scopechain();
if let Some(base_class) = superclass_object.map(|b| b.inner_class_definition()) { if let Some(base_class) = superclass_object.map(|b| b.inner_class_definition()) {
if base_class.read().is_final() { if base_class.read().is_final() {
return Err(format!( return Err(format!(
@ -174,7 +173,7 @@ impl<'gc> ClassObject<'gc> {
ClassObjectData { ClassObjectData {
base: ScriptObjectData::base_new(None, None), base: ScriptObjectData::base_new(None, None),
class, class,
scope, scope: scope,
superclass_object, superclass_object,
instance_allocator: Allocator(instance_allocator), instance_allocator: Allocator(instance_allocator),
constructor, constructor,
@ -204,7 +203,7 @@ impl<'gc> ClassObject<'gc> {
pub fn into_finished_class( pub fn into_finished_class(
mut self, mut self,
activation: &mut Activation<'_, 'gc, '_>, activation: &mut Activation<'_, 'gc, '_>,
) -> Result<ClassObject<'gc>, Error> { ) -> Result<Self, Error> {
let class = self.inner_class_definition(); let class = self.inner_class_definition();
let class_class = self.instance_of().ok_or( let class_class = self.instance_of().ok_or(
"Cannot finish initialization of core class without it being linked to a type!", "Cannot finish initialization of core class without it being linked to a type!",
@ -301,8 +300,8 @@ impl<'gc> ClassObject<'gc> {
) -> Result<(), Error> { ) -> Result<(), Error> {
let object: Object<'gc> = self.into(); let object: Object<'gc> = self.into();
let class = self.0.read().class;
let scope = self.0.read().scope; let scope = self.0.read().scope;
let class = self.0.read().class;
let class_read = class.read(); let class_read = class.read();
if !class_read.is_class_initialized() { if !class_read.is_class_initialized() {

View File

@ -6,7 +6,7 @@ use crate::avm2::names::Multiname;
use crate::avm2::object::{Object, TObject}; use crate::avm2::object::{Object, TObject};
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::Error; use crate::avm2::Error;
use gc_arena::{Collect, Gc, MutationContext}; use gc_arena::{Collect, GcCell, MutationContext};
use std::ops::Deref; use std::ops::Deref;
/// Represents a Scope that can be on either a ScopeChain or local ScopeStack. /// 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 /// 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 /// 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. /// 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)] #[derive(Debug, Collect, Clone, Copy)]
#[collect(no_drop)] #[collect(no_drop)]
pub struct ScopeChain<'gc> { pub struct ScopeChain<'gc> {
scopes: Option<Gc<'gc, Vec<Scope<'gc>>>>, scopes: Option<GcCell<'gc, Vec<Scope<'gc>>>>,
domain: Domain<'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 /// 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 { 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. // 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 { match self.scopes {
Some(scopes) => { Some(scopes) => {
// The new ScopeChain is created by cloning the scopes of this ScopeChain, // The new ScopeChain is created by cloning the scopes of this ScopeChain,
// and pushing the new scopes on top of that. // 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); cloned.extend_from_slice(new_scopes);
Self { Self {
scopes: Some(Gc::allocate(mc, cloned)), scopes: Some(GcCell::allocate(mc, cloned)),
domain: self.domain, 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 // We are chaining on top of an empty ScopeChain, so we don't actually
// need to chain anything. // need to chain anything.
Self { Self {
scopes: Some(Gc::allocate(mc, new_scopes.to_vec())), scopes: Some(GcCell::allocate(mc, new_scopes.to_vec())),
domain: self.domain, 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>> { pub fn get(&self, index: usize) -> Option<Scope<'gc>> {
self.scopes self.scopes
.and_then(|scopes| scopes.deref().get(index).cloned()) .and_then(|scopes| scopes.read().get(index).cloned())
} }
pub fn is_empty(&self) -> bool { 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. /// Returns the domain associated with this ScopeChain.
@ -125,7 +137,7 @@ impl<'gc> ScopeChain<'gc> {
) -> Result<Option<Object<'gc>>, Error> { ) -> Result<Option<Object<'gc>>, Error> {
// First search our scopes // First search our scopes
if let Some(scopes) = self.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(); let values = scope.values();
if let Some(qname) = values.resolve_multiname(name)? { if let Some(qname) = values.resolve_multiname(name)? {
// We search the dynamic properties if either conditions are met: // We search the dynamic properties if either conditions are met:

View File

@ -342,11 +342,7 @@ impl<'gc> Script<'gc> {
let script = script?; let script = script?;
for abc_trait in script.traits.iter() { for abc_trait in script.traits.iter() {
drop(write);
let newtrait = Trait::from_abc_trait(unit, abc_trait, activation)?; let newtrait = Trait::from_abc_trait(unit, abc_trait, activation)?;
write = self.0.write(activation.context.gc_context);
write.domain.export_definition( write.domain.export_definition(
newtrait.name().clone(), newtrait.name().clone(),
*self, *self,