Wrap ABC loading inside of a `TranslationUnit`.

This commit is contained in:
David Wendt 2020-07-01 23:21:30 -04:00
parent 70e9030072
commit b4f944b37b
7 changed files with 357 additions and 43 deletions

View File

@ -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,

View File

@ -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)
}

View File

@ -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())
}

View File

@ -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>;

92
core/src/avm2/script.rs Normal file
View File

@ -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);
}
}

View File

@ -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),
}

View File

@ -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
}