Wrap ABC loading inside of a `TranslationUnit`.
This commit is contained in:
parent
70e9030072
commit
b4f944b37b
|
@ -39,6 +39,7 @@ mod object;
|
|||
mod property;
|
||||
mod return_value;
|
||||
mod scope;
|
||||
mod script;
|
||||
mod script_object;
|
||||
mod slot;
|
||||
mod r#trait;
|
||||
|
@ -1291,7 +1292,7 @@ impl<'gc> Avm2<'gc> {
|
|||
let method_entry = self.table_method(index)?;
|
||||
let scope = self.current_stack_frame().unwrap().read().scope();
|
||||
|
||||
let mut new_fn = FunctionObject::from_abc_method(
|
||||
let mut new_fn = FunctionObject::from_method(
|
||||
context.gc_context,
|
||||
method_entry,
|
||||
scope,
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
use crate::avm2::function::Method;
|
||||
use crate::avm2::names::{Multiname, Namespace, QName};
|
||||
use crate::avm2::r#trait::{Trait, TraitKind};
|
||||
use crate::avm2::script::TranslationUnit;
|
||||
use crate::avm2::Error;
|
||||
use gc_arena::{Collect, Gc};
|
||||
use gc_arena::{Collect, GcCell, MutationContext};
|
||||
use std::rc::Rc;
|
||||
use swf::avm2::types::{AbcFile, Class as AbcClass, Index, Instance as AbcInstance};
|
||||
|
||||
|
@ -96,7 +97,7 @@ pub struct Class<'gc> {
|
|||
/// These are accessed as normal instance properties; they should not be
|
||||
/// present on prototypes, but instead should shadow any prototype
|
||||
/// properties that would match.
|
||||
instance_traits: Vec<Gc<'gc, Trait<'gc>>>,
|
||||
instance_traits: Vec<Trait<'gc>>,
|
||||
|
||||
/// The class initializer for this class.
|
||||
///
|
||||
|
@ -106,7 +107,10 @@ pub struct Class<'gc> {
|
|||
/// Static traits for a given class.
|
||||
///
|
||||
/// These are accessed as constructor properties.
|
||||
class_traits: Vec<Gc<'gc, Trait<'gc>>>,
|
||||
class_traits: Vec<Trait<'gc>>,
|
||||
|
||||
/// Whether or not this `Class` has loaded it's traits or not.
|
||||
traits_loaded: bool,
|
||||
}
|
||||
|
||||
/// Find traits in a list of traits matching a name.
|
||||
|
@ -117,8 +121,8 @@ pub struct Class<'gc> {
|
|||
/// TODO: This is an O(n^2) algorithm, it sucks.
|
||||
fn do_trait_lookup<'gc>(
|
||||
name: &QName,
|
||||
known_traits: &mut Vec<Gc<'gc, Trait<'gc>>>,
|
||||
all_traits: &[Gc<'gc, Trait<'gc>>],
|
||||
known_traits: &mut Vec<Trait<'gc>>,
|
||||
all_traits: &[Trait<'gc>],
|
||||
) -> Result<(), Error> {
|
||||
for trait_entry in all_traits {
|
||||
if name == trait_entry.name() {
|
||||
|
@ -146,6 +150,121 @@ fn do_trait_lookup<'gc>(
|
|||
}
|
||||
|
||||
impl<'gc> Class<'gc> {
|
||||
/// Construct a class from a `TranslationUnit` and it's class index.
|
||||
///
|
||||
/// The returned class will be allocated, but no traits will be loaded. The
|
||||
/// caller is responsible for storing the class in the `TranslationUnit`
|
||||
/// and calling `load_traits` to complete the trait-loading process.
|
||||
pub fn from_abc_index(
|
||||
unit: &mut TranslationUnit<'gc>,
|
||||
class_index: u32,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
) -> Result<GcCell<'gc, Self>, Error> {
|
||||
let abc_class: Result<&AbcClass, Error> = unit
|
||||
.abc()
|
||||
.classes
|
||||
.get(class_index as usize)
|
||||
.ok_or("LoadError: Class index not valid".into());
|
||||
let abc_class = abc_class?;
|
||||
|
||||
let abc_instance: Result<&AbcInstance, Error> = unit
|
||||
.abc()
|
||||
.instances
|
||||
.get(class_index as usize)
|
||||
.ok_or("LoadError: Instance index not valid".into());
|
||||
let abc_instance = abc_instance?;
|
||||
|
||||
let name = QName::from_abc_multiname(&unit.abc(), abc_instance.name)?;
|
||||
let super_class = if abc_instance.super_name.0 == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(Multiname::from_abc_multiname_static(
|
||||
&unit.abc(),
|
||||
abc_instance.super_name,
|
||||
)?)
|
||||
};
|
||||
|
||||
let protected_namespace = if let Some(ns) = abc_instance.protected_namespace {
|
||||
Some(Namespace::from_abc_namespace(&unit.abc(), ns)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut interfaces = Vec::new();
|
||||
for interface_name in abc_instance.interfaces {
|
||||
interfaces.push(Multiname::from_abc_multiname_static(
|
||||
&unit.abc(),
|
||||
interface_name,
|
||||
)?);
|
||||
}
|
||||
|
||||
let instance_init = unit.load_method(abc_instance.init_method.0)?;
|
||||
let class_init = unit.load_method(abc_class.init_method.0)?;
|
||||
|
||||
Ok(GcCell::allocate(
|
||||
mc,
|
||||
Self {
|
||||
name,
|
||||
super_class,
|
||||
is_sealed: abc_instance.is_sealed,
|
||||
is_final: abc_instance.is_final,
|
||||
is_interface: abc_instance.is_interface,
|
||||
protected_namespace,
|
||||
interfaces,
|
||||
instance_init,
|
||||
instance_traits: Vec::new(),
|
||||
class_init,
|
||||
class_traits: Vec::new(),
|
||||
traits_loaded: false,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
/// Finish the class-loading process by loading traits.
|
||||
///
|
||||
/// This process must be done after the `Class` has been stored in the
|
||||
/// `TranslationUnit`. Failing to do so runs the risk of runaway recursion
|
||||
/// or double-borrows. It should be done before the class is actually
|
||||
/// instantiated into an `Object`.
|
||||
pub fn load_traits(
|
||||
&mut self,
|
||||
unit: &mut TranslationUnit<'gc>,
|
||||
class_index: u32,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
) -> Result<(), Error> {
|
||||
if self.traits_loaded {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.traits_loaded = true;
|
||||
|
||||
let abc_class: Result<&AbcClass, Error> = unit
|
||||
.abc()
|
||||
.classes
|
||||
.get(class_index as usize)
|
||||
.ok_or_else(|| "LoadError: Class index not valid".into());
|
||||
let abc_class = abc_class?;
|
||||
|
||||
let abc_instance: Result<&AbcInstance, Error> = unit
|
||||
.abc()
|
||||
.instances
|
||||
.get(class_index as usize)
|
||||
.ok_or_else(|| "LoadError: Instance index not valid".into());
|
||||
let abc_instance = abc_instance?;
|
||||
|
||||
for abc_trait in abc_instance.traits {
|
||||
self.instance_traits
|
||||
.push(Trait::from_abc_trait(unit, &abc_trait, mc)?);
|
||||
}
|
||||
|
||||
for abc_trait in abc_class.traits {
|
||||
self.class_traits
|
||||
.push(Trait::from_abc_trait(unit, &abc_trait, mc)?);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &QName {
|
||||
&self.name
|
||||
}
|
||||
|
@ -167,7 +286,7 @@ impl<'gc> Class<'gc> {
|
|||
pub fn lookup_class_traits(
|
||||
&self,
|
||||
name: &QName,
|
||||
known_traits: &mut Vec<Gc<'gc, Trait<'gc>>>,
|
||||
known_traits: &mut Vec<Trait<'gc>>,
|
||||
) -> Result<(), Error> {
|
||||
do_trait_lookup(name, known_traits, &self.class_traits)
|
||||
}
|
||||
|
@ -211,7 +330,7 @@ impl<'gc> Class<'gc> {
|
|||
pub fn lookup_instance_traits(
|
||||
&self,
|
||||
name: &QName,
|
||||
known_traits: &mut Vec<Gc<'gc, Trait<'gc>>>,
|
||||
known_traits: &mut Vec<Trait<'gc>>,
|
||||
) -> Result<(), Error> {
|
||||
do_trait_lookup(name, known_traits, &self.instance_traits)
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use crate::avm2::script_object::{ScriptObjectClass, ScriptObjectData};
|
|||
use crate::avm2::value::Value;
|
||||
use crate::avm2::{Avm2, Error};
|
||||
use crate::context::UpdateContext;
|
||||
use gc_arena::{Collect, CollectionContext, Gc, GcCell, MutationContext};
|
||||
use gc_arena::{Collect, CollectionContext, GcCell, MutationContext};
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
use swf::avm2::types::{AbcFile, Index, Method as AbcMethod, MethodBody as AbcMethodBody};
|
||||
|
@ -301,10 +301,11 @@ impl<'gc> FunctionObject<'gc> {
|
|||
pub fn from_class(
|
||||
avm: &mut Avm2<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
class: Gc<'gc, Class<'gc>>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
mut base_class: Object<'gc>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<(Object<'gc>, Object<'gc>), Error> {
|
||||
let class_read = class.read();
|
||||
let super_proto: Result<Object<'gc>, Error> = base_class
|
||||
.get_property(
|
||||
base_class,
|
||||
|
@ -316,7 +317,7 @@ impl<'gc> FunctionObject<'gc> {
|
|||
.map_err(|_| {
|
||||
format!(
|
||||
"Could not resolve superclass prototype {:?}",
|
||||
class
|
||||
class_read
|
||||
.super_class_name()
|
||||
.map(|p| p.local_name())
|
||||
.unwrap_or(Some("Object"))
|
||||
|
@ -327,7 +328,7 @@ impl<'gc> FunctionObject<'gc> {
|
|||
let fn_proto = avm.prototypes().function;
|
||||
let class_constr_proto = avm.prototypes().class;
|
||||
|
||||
let initializer = class.instance_init();
|
||||
let initializer = class_read.instance_init();
|
||||
|
||||
let mut constr: Object<'gc> = FunctionObject(GcCell::allocate(
|
||||
context.gc_context,
|
||||
|
@ -352,7 +353,7 @@ impl<'gc> FunctionObject<'gc> {
|
|||
constr.into(),
|
||||
)?;
|
||||
|
||||
let class_initializer = class.class_init();
|
||||
let class_initializer = class_read.class_init();
|
||||
let class_constr = FunctionObject::from_method(
|
||||
context.gc_context,
|
||||
class_initializer,
|
||||
|
@ -520,14 +521,14 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
|||
self.0.read().base.get_method(id)
|
||||
}
|
||||
|
||||
fn get_trait(self, name: &QName) -> Result<Vec<Gc<'gc, Trait<'gc>>>, Error> {
|
||||
fn get_trait(self, name: &QName) -> Result<Vec<Trait<'gc>>, Error> {
|
||||
self.0.read().base.get_trait(name)
|
||||
}
|
||||
|
||||
fn get_provided_trait(
|
||||
&self,
|
||||
name: &QName,
|
||||
known_traits: &mut Vec<Gc<'gc, Trait<'gc>>>,
|
||||
known_traits: &mut Vec<Trait<'gc>>,
|
||||
) -> Result<(), Error> {
|
||||
self.0.read().base.get_provided_trait(name, known_traits)
|
||||
}
|
||||
|
@ -636,7 +637,7 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
|||
&self,
|
||||
_avm: &mut Avm2<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
class: Gc<'gc, Class<'gc>>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let this: Object<'gc> = Object::FunctionObject(*self);
|
||||
|
@ -654,7 +655,7 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
|||
|
||||
fn to_string(&self) -> Result<Value<'gc>, Error> {
|
||||
if let ScriptObjectClass::ClassConstructor(class, ..) = self.0.read().base.class() {
|
||||
Ok(format!("[class {}]", class.name().local_name()).into())
|
||||
Ok(format!("[class {}]", class.read().name().local_name()).into())
|
||||
} else {
|
||||
Ok("function Function() {}".into())
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use crate::avm2::script_object::ScriptObject;
|
|||
use crate::avm2::value::Value;
|
||||
use crate::avm2::{Avm2, Error};
|
||||
use crate::context::UpdateContext;
|
||||
use gc_arena::{Collect, Gc, GcCell, MutationContext};
|
||||
use gc_arena::{Collect, GcCell, MutationContext};
|
||||
use ruffle_macros::enum_trait_object;
|
||||
use std::fmt::Debug;
|
||||
|
||||
|
@ -192,7 +192,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
/// This function returns `None` if no such trait exists, or the object
|
||||
/// does not have traits. It returns `Err` if *any* trait in the object is
|
||||
/// malformed in some way.
|
||||
fn get_trait(self, name: &QName) -> Result<Vec<Gc<'gc, Trait<'gc>>>, Error>;
|
||||
fn get_trait(self, name: &QName) -> Result<Vec<Trait<'gc>>, Error>;
|
||||
|
||||
/// Populate a list of traits that this object provides.
|
||||
///
|
||||
|
@ -203,7 +203,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
fn get_provided_trait(
|
||||
&self,
|
||||
name: &QName,
|
||||
known_traits: &mut Vec<Gc<'gc, Trait<'gc>>>,
|
||||
known_traits: &mut Vec<Trait<'gc>>,
|
||||
) -> Result<(), Error>;
|
||||
|
||||
/// Retrieves the scope chain of the object at time of it's creation.
|
||||
|
@ -406,7 +406,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
&mut self,
|
||||
avm: &mut Avm2<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
trait_entry: Gc<'gc, Trait<'gc>>,
|
||||
trait_entry: Trait<'gc>,
|
||||
reciever: Object<'gc>,
|
||||
) -> Result<(), Error> {
|
||||
self.install_foreign_trait(avm, context, trait_entry, self.get_scope(), reciever)
|
||||
|
@ -417,7 +417,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
&mut self,
|
||||
avm: &mut Avm2<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
trait_entry: Gc<'gc, Trait<'gc>>,
|
||||
trait_entry: Trait<'gc>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
reciever: Object<'gc>,
|
||||
) -> Result<(), Error> {
|
||||
|
@ -479,10 +479,11 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
self.install_setter(context.gc_context, trait_name, *disp_id, function)?;
|
||||
}
|
||||
TraitKind::Class { slot_id, class } => {
|
||||
let class_read = class.read();
|
||||
//TODO: what happens if this happens on a class defined as a
|
||||
//class trait, without a superclass? How do we get `Object`
|
||||
//then?
|
||||
let super_name = if let Some(sc_name) = class.super_class_name() {
|
||||
let super_name = if let Some(sc_name) = class_read.super_class_name() {
|
||||
self.resolve_multiname(sc_name)?
|
||||
.unwrap_or(QName::dynamic_name("Object"))
|
||||
} else {
|
||||
|
@ -500,7 +501,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
FunctionObject::from_class(avm, context, *class, super_class?, scope)?;
|
||||
self.install_const(
|
||||
context.gc_context,
|
||||
class.name().clone(),
|
||||
class_read.name().clone(),
|
||||
*slot_id,
|
||||
class_object.into(),
|
||||
);
|
||||
|
@ -591,7 +592,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
&self,
|
||||
avm: &mut Avm2<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
class: Gc<'gc, Class<'gc>>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error>;
|
||||
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
//! Whole script representation
|
||||
|
||||
use crate::avm2::activation::Avm2ScriptEntry;
|
||||
use crate::avm2::class::Class;
|
||||
use crate::avm2::function::{Avm2MethodEntry, Method};
|
||||
use crate::avm2::Error;
|
||||
use gc_arena::{Collect, GcCell, MutationContext};
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use swf::avm2::types::{AbcFile, Index};
|
||||
|
||||
#[derive(Clone, Debug, Collect)]
|
||||
#[collect(require_static)]
|
||||
pub struct CollectWrapper<T>(T);
|
||||
|
||||
/// A loaded ABC file, with any loaded ABC items alongside it.
|
||||
///
|
||||
/// A `TranslationUnit` is constructed when ABC loading begins, and it stores
|
||||
/// all loaded ABC items (classes, methods, and scripts) as they are loaded.
|
||||
/// Unit items are loaded lazily and retained in the `TranslationUnit` for
|
||||
/// later retrieval.
|
||||
///
|
||||
/// Loaded versions of ABC items consist of the types `Class`, `Method`, and
|
||||
/// `Script`, all of which correspond to their `swf` equivalents, but with
|
||||
/// names preloaded. This roughly corresponds to the logical "loading" phase of
|
||||
/// ABC execution as documented in the AVM2 Overview. "Linking" takes place by
|
||||
/// constructing the appropriate runtime object for that item.
|
||||
#[derive(Clone, Debug, Collect)]
|
||||
#[collect(no_drop)]
|
||||
pub struct TranslationUnit<'gc> {
|
||||
/// The ABC file that all of the following loaded data comes from.
|
||||
abc: CollectWrapper<Rc<AbcFile>>,
|
||||
|
||||
/// All classes loaded from the ABC's class list.
|
||||
classes: HashMap<u32, GcCell<'gc, Class<'gc>>>,
|
||||
|
||||
/// All methods loaded from the ABC's method list.
|
||||
methods: HashMap<u32, Method<'gc>>,
|
||||
|
||||
/// All scripts loaded from the ABC's scripts list.
|
||||
scripts: HashMap<u32, GcCell<'gc, Avm2ScriptEntry>>,
|
||||
}
|
||||
|
||||
impl<'gc> TranslationUnit<'gc> {
|
||||
pub fn from_abc(abc: Rc<AbcFile>) -> Self {
|
||||
Self {
|
||||
abc: CollectWrapper(abc),
|
||||
classes: HashMap::new(),
|
||||
methods: HashMap::new(),
|
||||
scripts: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve the underlying `AbcFile` for this translation unit.
|
||||
pub fn abc(&self) -> Rc<AbcFile> {
|
||||
self.abc.0
|
||||
}
|
||||
|
||||
/// Load a method from the ABC file and return it's method definition.
|
||||
pub fn load_method(&mut self, method_index: u32) -> Result<Method<'gc>, Error> {
|
||||
if let Some(method) = self.methods.get(&method_index) {
|
||||
return Ok(method.clone());
|
||||
}
|
||||
|
||||
let method: Result<Avm2MethodEntry, Error> =
|
||||
Avm2MethodEntry::from_method_index(self.abc.0, Index::new(method_index))
|
||||
.ok_or_else(|| "Method index does not exist".into());
|
||||
let method = method?.into();
|
||||
|
||||
self.methods.insert(method_index, method);
|
||||
|
||||
return Ok(method);
|
||||
}
|
||||
|
||||
/// Load a class from the ABC file and return it's class definition.
|
||||
pub fn load_class(
|
||||
&mut self,
|
||||
class_index: u32,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
) -> Result<GcCell<'gc, Class<'gc>>, Error> {
|
||||
if let Some(class) = self.classes.get(&class_index) {
|
||||
return Ok(class.clone());
|
||||
}
|
||||
|
||||
let class = Class::from_abc_index(&mut self, class_index, mc)?;
|
||||
self.classes.insert(class_index, class);
|
||||
|
||||
class.write(mc).load_traits(&mut self, class_index, mc)?;
|
||||
|
||||
return Ok(class);
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ use crate::avm2::slot::Slot;
|
|||
use crate::avm2::value::Value;
|
||||
use crate::avm2::{Avm2, Error};
|
||||
use crate::context::UpdateContext;
|
||||
use gc_arena::{Collect, Gc, GcCell, MutationContext};
|
||||
use gc_arena::{Collect, GcCell, MutationContext};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
|
||||
|
@ -33,10 +33,10 @@ pub struct ScriptObject<'gc>(GcCell<'gc, ScriptObjectData<'gc>>);
|
|||
#[collect(no_drop)]
|
||||
pub enum ScriptObjectClass<'gc> {
|
||||
/// Instantiate instance traits, for prototypes.
|
||||
InstancePrototype(Gc<'gc, Class<'gc>>, Option<GcCell<'gc, Scope<'gc>>>),
|
||||
InstancePrototype(GcCell<'gc, Class<'gc>>, Option<GcCell<'gc, Scope<'gc>>>),
|
||||
|
||||
/// Instantiate class traits, for class constructors.
|
||||
ClassConstructor(Gc<'gc, Class<'gc>>, Option<GcCell<'gc, Scope<'gc>>>),
|
||||
ClassConstructor(GcCell<'gc, Class<'gc>>, Option<GcCell<'gc, Scope<'gc>>>),
|
||||
|
||||
/// Do not instantiate any class or instance traits.
|
||||
NoClass,
|
||||
|
@ -152,14 +152,14 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
|||
self.0.read().get_method(id)
|
||||
}
|
||||
|
||||
fn get_trait(self, name: &QName) -> Result<Vec<Gc<'gc, Trait<'gc>>>, Error> {
|
||||
fn get_trait(self, name: &QName) -> Result<Vec<Trait<'gc>>, Error> {
|
||||
self.0.read().get_trait(name)
|
||||
}
|
||||
|
||||
fn get_provided_trait(
|
||||
&self,
|
||||
name: &QName,
|
||||
known_traits: &mut Vec<Gc<'gc, Trait<'gc>>>,
|
||||
known_traits: &mut Vec<Trait<'gc>>,
|
||||
) -> Result<(), Error> {
|
||||
self.0.read().get_provided_trait(name, known_traits)
|
||||
}
|
||||
|
@ -241,7 +241,7 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
|||
&self,
|
||||
_avm: &mut Avm2<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
class: Gc<'gc, Class<'gc>>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let this: Object<'gc> = Object::ScriptObject(*self);
|
||||
|
@ -347,7 +347,7 @@ impl<'gc> ScriptObject<'gc> {
|
|||
pub fn prototype(
|
||||
mc: MutationContext<'gc, '_>,
|
||||
proto: Object<'gc>,
|
||||
class: Gc<'gc, Class<'gc>>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Object<'gc> {
|
||||
let script_class = ScriptObjectClass::InstancePrototype(class, scope);
|
||||
|
@ -526,7 +526,7 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
self.methods.get(id as usize).and_then(|v| *v)
|
||||
}
|
||||
|
||||
pub fn get_trait(&self, name: &QName) -> Result<Vec<Gc<'gc, Trait<'gc>>>, Error> {
|
||||
pub fn get_trait(&self, name: &QName) -> Result<Vec<Trait<'gc>>, Error> {
|
||||
match &self.class {
|
||||
//Class constructors have local traits only.
|
||||
ScriptObjectClass::ClassConstructor(..) => {
|
||||
|
@ -564,14 +564,14 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
pub fn get_provided_trait(
|
||||
&self,
|
||||
name: &QName,
|
||||
known_traits: &mut Vec<Gc<'gc, Trait<'gc>>>,
|
||||
known_traits: &mut Vec<Trait<'gc>>,
|
||||
) -> Result<(), Error> {
|
||||
match &self.class {
|
||||
ScriptObjectClass::ClassConstructor(class, ..) => {
|
||||
class.lookup_class_traits(name, known_traits)
|
||||
class.read().lookup_class_traits(name, known_traits)
|
||||
}
|
||||
ScriptObjectClass::InstancePrototype(class, ..) => {
|
||||
class.lookup_instance_traits(name, known_traits)
|
||||
class.read().lookup_instance_traits(name, known_traits)
|
||||
}
|
||||
ScriptObjectClass::NoClass => Ok(()),
|
||||
}
|
||||
|
@ -606,8 +606,12 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
|
||||
pub fn provides_trait(&self, name: &QName) -> Result<bool, Error> {
|
||||
match &self.class {
|
||||
ScriptObjectClass::ClassConstructor(class, ..) => Ok(class.has_class_trait(name)),
|
||||
ScriptObjectClass::InstancePrototype(class, ..) => Ok(class.has_instance_trait(name)),
|
||||
ScriptObjectClass::ClassConstructor(class, ..) => {
|
||||
Ok(class.read().has_class_trait(name))
|
||||
}
|
||||
ScriptObjectClass::InstancePrototype(class, ..) => {
|
||||
Ok(class.read().has_instance_trait(name))
|
||||
}
|
||||
ScriptObjectClass::NoClass => Ok(false),
|
||||
}
|
||||
}
|
||||
|
@ -644,10 +648,10 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
|
||||
match &self.class {
|
||||
ScriptObjectClass::ClassConstructor(class, ..) => {
|
||||
Ok(class.resolve_any_class_trait(local_name))
|
||||
Ok(class.read().resolve_any_class_trait(local_name))
|
||||
}
|
||||
ScriptObjectClass::InstancePrototype(class, ..) => {
|
||||
Ok(class.resolve_any_instance_trait(local_name))
|
||||
Ok(class.read().resolve_any_instance_trait(local_name))
|
||||
}
|
||||
ScriptObjectClass::NoClass => Ok(None),
|
||||
}
|
||||
|
|
|
@ -3,8 +3,11 @@
|
|||
use crate::avm2::class::Class;
|
||||
use crate::avm2::function::Method;
|
||||
use crate::avm2::names::{Multiname, QName};
|
||||
use crate::avm2::value::Value;
|
||||
use gc_arena::{Collect, Gc};
|
||||
use crate::avm2::script::TranslationUnit;
|
||||
use crate::avm2::value::{abc_default_value, Value};
|
||||
use crate::avm2::Error;
|
||||
use gc_arena::{Collect, GcCell, MutationContext};
|
||||
use swf::avm2::types::{Trait as AbcTrait, TraitKind as AbcTraitKind};
|
||||
|
||||
/// Represents a trait as loaded into the VM.
|
||||
///
|
||||
|
@ -62,7 +65,7 @@ pub enum TraitKind<'gc> {
|
|||
/// objects.
|
||||
Class {
|
||||
slot_id: u32,
|
||||
class: Gc<'gc, Class<'gc>>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
},
|
||||
|
||||
/// A free function (not an instance method) that can be called.
|
||||
|
@ -78,6 +81,99 @@ pub enum TraitKind<'gc> {
|
|||
}
|
||||
|
||||
impl<'gc> Trait<'gc> {
|
||||
/// Convert an ABC trait into a loaded trait.
|
||||
pub fn from_abc_trait(
|
||||
unit: &mut TranslationUnit<'gc>,
|
||||
abc_trait: &AbcTrait,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
) -> Result<Self, Error> {
|
||||
let name = QName::from_abc_multiname(&unit.abc(), abc_trait.name)?;
|
||||
|
||||
Ok(match abc_trait.kind {
|
||||
AbcTraitKind::Slot {
|
||||
slot_id,
|
||||
type_name,
|
||||
value,
|
||||
} => Trait {
|
||||
name,
|
||||
is_final: abc_trait.is_final,
|
||||
is_override: abc_trait.is_override,
|
||||
kind: TraitKind::Slot {
|
||||
slot_id,
|
||||
type_name: Multiname::from_abc_multiname_static(&unit.abc(), type_name)?,
|
||||
default_value: if let Some(dv) = value {
|
||||
Some(abc_default_value(&unit.abc(), &dv)?)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
},
|
||||
},
|
||||
AbcTraitKind::Method { disp_id, method } => Trait {
|
||||
name,
|
||||
is_final: abc_trait.is_final,
|
||||
is_override: abc_trait.is_override,
|
||||
kind: TraitKind::Method {
|
||||
disp_id,
|
||||
method: unit.load_method(method.0)?,
|
||||
},
|
||||
},
|
||||
AbcTraitKind::Getter { disp_id, method } => Trait {
|
||||
name,
|
||||
is_final: abc_trait.is_final,
|
||||
is_override: abc_trait.is_override,
|
||||
kind: TraitKind::Getter {
|
||||
disp_id,
|
||||
method: unit.load_method(method.0)?,
|
||||
},
|
||||
},
|
||||
AbcTraitKind::Setter { disp_id, method } => Trait {
|
||||
name,
|
||||
is_final: abc_trait.is_final,
|
||||
is_override: abc_trait.is_override,
|
||||
kind: TraitKind::Setter {
|
||||
disp_id,
|
||||
method: unit.load_method(method.0)?,
|
||||
},
|
||||
},
|
||||
AbcTraitKind::Class { slot_id, class } => Trait {
|
||||
name,
|
||||
is_final: abc_trait.is_final,
|
||||
is_override: abc_trait.is_override,
|
||||
kind: TraitKind::Class {
|
||||
slot_id,
|
||||
class: unit.load_class(class.0, mc)?,
|
||||
},
|
||||
},
|
||||
AbcTraitKind::Function { slot_id, function } => Trait {
|
||||
name,
|
||||
is_final: abc_trait.is_final,
|
||||
is_override: abc_trait.is_override,
|
||||
kind: TraitKind::Function {
|
||||
slot_id,
|
||||
function: unit.load_method(function.0)?,
|
||||
},
|
||||
},
|
||||
AbcTraitKind::Const {
|
||||
slot_id,
|
||||
type_name,
|
||||
value,
|
||||
} => Trait {
|
||||
name,
|
||||
is_final: abc_trait.is_final,
|
||||
is_override: abc_trait.is_override,
|
||||
kind: TraitKind::Const {
|
||||
slot_id,
|
||||
type_name: Multiname::from_abc_multiname_static(&unit.abc(), type_name)?,
|
||||
default_value: if let Some(dv) = value {
|
||||
Some(abc_default_value(&unit.abc(), &dv)?)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &QName {
|
||||
&self.name
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue