avm2: Script scopes should hold the domains they are a part of.

Also, this means we have a `TObject` representation for domains now.
This commit is contained in:
David Wendt 2020-09-24 22:28:24 -04:00 committed by Mike Welsh
parent 6a736b0d2b
commit 1792939212
4 changed files with 120 additions and 9 deletions

View File

@ -6,8 +6,8 @@ use crate::avm2::domain::Domain;
use crate::avm2::method::NativeMethod; use crate::avm2::method::NativeMethod;
use crate::avm2::names::{Namespace, QName}; use crate::avm2::names::{Namespace, QName};
use crate::avm2::object::{ use crate::avm2::object::{
implicit_deriver, ArrayObject, FunctionObject, NamespaceObject, Object, PrimitiveObject, implicit_deriver, ArrayObject, DomainObject, FunctionObject, NamespaceObject, Object,
ScriptObject, StageObject, TObject, PrimitiveObject, ScriptObject, StageObject, TObject,
}; };
use crate::avm2::scope::Scope; use crate::avm2::scope::Scope;
use crate::avm2::script::Script; use crate::avm2::script::Script;
@ -270,7 +270,7 @@ pub fn load_player_globals<'gc>(
domain: GcCell<'gc, Domain<'gc>>, domain: GcCell<'gc, Domain<'gc>>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let mc = activation.context.gc_context; let mc = activation.context.gc_context;
let gs = ScriptObject::bare_object(mc); let gs = DomainObject::from_domain(mc, None, domain);
let script = Script::empty_script(mc, gs); let script = Script::empty_script(mc, gs);
// public / root package // public / root package

View File

@ -3,6 +3,7 @@
use crate::avm2::activation::Activation; use crate::avm2::activation::Activation;
use crate::avm2::array::ArrayStorage; use crate::avm2::array::ArrayStorage;
use crate::avm2::class::Class; use crate::avm2::class::Class;
use crate::avm2::domain::Domain;
use crate::avm2::function::Executable; use crate::avm2::function::Executable;
use crate::avm2::names::{Multiname, Namespace, QName}; use crate::avm2::names::{Multiname, Namespace, QName};
use crate::avm2::scope::Scope; use crate::avm2::scope::Scope;
@ -18,6 +19,7 @@ use std::fmt::Debug;
mod array_object; mod array_object;
mod custom_object; mod custom_object;
mod domain_object;
mod function_object; mod function_object;
mod namespace_object; mod namespace_object;
mod primitive_object; mod primitive_object;
@ -25,6 +27,7 @@ mod script_object;
mod stage_object; mod stage_object;
pub use crate::avm2::object::array_object::ArrayObject; pub use crate::avm2::object::array_object::ArrayObject;
pub use crate::avm2::object::domain_object::DomainObject;
pub use crate::avm2::object::function_object::{implicit_deriver, FunctionObject}; pub use crate::avm2::object::function_object::{implicit_deriver, FunctionObject};
pub use crate::avm2::object::namespace_object::NamespaceObject; pub use crate::avm2::object::namespace_object::NamespaceObject;
pub use crate::avm2::object::primitive_object::PrimitiveObject; pub use crate::avm2::object::primitive_object::PrimitiveObject;
@ -43,6 +46,7 @@ pub use crate::avm2::object::stage_object::StageObject;
NamespaceObject(NamespaceObject<'gc>), NamespaceObject(NamespaceObject<'gc>),
ArrayObject(ArrayObject<'gc>), ArrayObject(ArrayObject<'gc>),
StageObject(StageObject<'gc>), StageObject(StageObject<'gc>),
DomainObject(DomainObject<'gc>),
} }
)] )]
pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy { pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy {
@ -682,7 +686,14 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
/// coercions happen by defining `toString` in a downstream class or /// coercions happen by defining `toString` in a downstream class or
/// prototype; this is then picked up by the VM runtime when doing /// prototype; this is then picked up by the VM runtime when doing
/// coercions. /// coercions.
fn to_string(&self, mc: MutationContext<'gc, '_>) -> Result<Value<'gc>, Error>; fn to_string(&self, mc: MutationContext<'gc, '_>) -> Result<Value<'gc>, Error> {
let class_name = self
.as_proto_class()
.map(|c| c.read().name().local_name())
.unwrap_or_else(|| "Object".into());
Ok(AvmString::new(mc, format!("[object {}]", class_name)).into())
}
/// Implement the result of calling `Object.prototype.toLocaleString` on this /// Implement the result of calling `Object.prototype.toLocaleString` on this
/// object class. /// object class.
@ -823,6 +834,11 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
/// ///
/// If not, then this function does nothing. /// If not, then this function does nothing.
fn init_display_object(&self, _mc: MutationContext<'gc, '_>, _obj: DisplayObject<'gc>) {} fn init_display_object(&self, _mc: MutationContext<'gc, '_>, _obj: DisplayObject<'gc>) {}
/// Unwrap this object as an ApplicationDomain.
fn as_application_domain(&self) -> Option<GcCell<'gc, Domain<'gc>>> {
None
}
} }
pub enum ObjectPtr {} pub enum ObjectPtr {}

View File

@ -0,0 +1,95 @@
//! Application Domain objects for scripts
use crate::avm2::activation::Activation;
use crate::avm2::class::Class;
use crate::avm2::domain::Domain;
use crate::avm2::names::{Namespace, QName};
use crate::avm2::object::script_object::{ScriptObjectClass, ScriptObjectData};
use crate::avm2::object::{Object, ObjectPtr, TObject};
use crate::avm2::scope::Scope;
use crate::avm2::string::AvmString;
use crate::avm2::traits::Trait;
use crate::avm2::value::Value;
use crate::avm2::Error;
use crate::{impl_avm2_custom_object, impl_avm2_custom_object_properties};
use gc_arena::{Collect, GcCell, MutationContext};
#[derive(Clone, Collect, Debug, Copy)]
#[collect(no_drop)]
pub struct DomainObject<'gc>(GcCell<'gc, DomainObjectData<'gc>>);
#[derive(Clone, Collect, Debug)]
#[collect(no_drop)]
pub struct DomainObjectData<'gc> {
/// Base script object
base: ScriptObjectData<'gc>,
/// The domain this object holds
domain: GcCell<'gc, Domain<'gc>>,
}
impl<'gc> DomainObject<'gc> {
pub fn from_domain(
mc: MutationContext<'gc, '_>,
base_proto: Option<Object<'gc>>,
domain: GcCell<'gc, Domain<'gc>>,
) -> Object<'gc> {
let base = ScriptObjectData::base_new(base_proto, ScriptObjectClass::NoClass);
DomainObject(GcCell::allocate(mc, DomainObjectData { base, domain })).into()
}
}
impl<'gc> TObject<'gc> for DomainObject<'gc> {
impl_avm2_custom_object!(base);
impl_avm2_custom_object_properties!(base);
fn as_application_domain(&self) -> Option<GcCell<'gc, Domain<'gc>>> {
Some(self.0.read().domain)
}
fn value_of(&self, _mc: MutationContext<'gc, '_>) -> Result<Value<'gc>, Error> {
let this: Object<'gc> = Object::DomainObject(*self);
Ok(this.into())
}
fn construct(
&self,
activation: &mut Activation<'_, 'gc, '_>,
args: &[Value<'gc>],
) -> Result<Object<'gc>, Error> {
let this: Object<'gc> = Object::DomainObject(*self);
let parent_domain = if let Some(parent_domain) = args
.get(0)
.cloned()
.unwrap_or(Value::Undefined)
.coerce_to_object(activation)?
.as_application_domain()
{
parent_domain
} else {
activation.context.avm2.global_domain()
};
Ok(DomainObject::from_domain(
activation.context.gc_context,
Some(this),
Domain::movie_domain(activation.context.gc_context, parent_domain),
))
}
fn derive(
&self,
activation: &mut Activation<'_, 'gc, '_>,
_class: GcCell<'gc, Class<'gc>>,
_scope: Option<GcCell<'gc, Scope<'gc>>>,
) -> Result<Object<'gc>, Error> {
let this: Object<'gc> = Object::DomainObject(*self);
Ok(DomainObject::from_domain(
activation.context.gc_context,
Some(this),
activation.context.avm2.global_domain(),
))
}
}

View File

@ -3,7 +3,7 @@
use crate::avm2::class::Class; use crate::avm2::class::Class;
use crate::avm2::domain::Domain; use crate::avm2::domain::Domain;
use crate::avm2::method::{BytecodeMethod, Method}; use crate::avm2::method::{BytecodeMethod, Method};
use crate::avm2::object::{Object, ScriptObject}; use crate::avm2::object::{DomainObject, Object};
use crate::avm2::string::AvmString; use crate::avm2::string::AvmString;
use crate::avm2::traits::Trait; use crate::avm2::traits::Trait;
use crate::avm2::value::Value; use crate::avm2::value::Value;
@ -139,9 +139,11 @@ impl<'gc> TranslationUnit<'gc> {
return Ok(*scripts); return Ok(*scripts);
} }
let domain = read.domain;
drop(read); drop(read);
let global = ScriptObject::object(mc, avm2.prototypes().global); let global = DomainObject::from_domain(mc, Some(avm2.prototypes().global), domain);
let script = Script::from_abc_index(self, script_index, global, mc)?; let script = Script::from_abc_index(self, script_index, global, mc)?;
self.0.write(mc).scripts.insert(script_index, script); self.0.write(mc).scripts.insert(script_index, script);
@ -149,9 +151,7 @@ impl<'gc> TranslationUnit<'gc> {
script.write(mc).load_traits(self, script_index, avm2, mc)?; script.write(mc).load_traits(self, script_index, avm2, mc)?;
for traitdef in script.read().traits()? { for traitdef in script.read().traits()? {
self.0 domain
.read()
.domain
.write(mc) .write(mc)
.export_definition(traitdef.name().clone(), script)?; .export_definition(traitdef.name().clone(), script)?;
} }