From b4f944b37bad0cdd6fe1a345671f58d22e5a6154 Mon Sep 17 00:00:00 2001 From: David Wendt Date: Wed, 1 Jul 2020 23:21:30 -0400 Subject: [PATCH] Wrap ABC loading inside of a `TranslationUnit`. --- core/src/avm2.rs | 3 +- core/src/avm2/class.rs | 133 +++++++++++++++++++++++++++++++-- core/src/avm2/function.rs | 19 ++--- core/src/avm2/object.rs | 17 +++-- core/src/avm2/script.rs | 92 +++++++++++++++++++++++ core/src/avm2/script_object.rs | 34 +++++---- core/src/avm2/trait.rs | 102 ++++++++++++++++++++++++- 7 files changed, 357 insertions(+), 43 deletions(-) create mode 100644 core/src/avm2/script.rs diff --git a/core/src/avm2.rs b/core/src/avm2.rs index b2827b61f..77c658883 100644 --- a/core/src/avm2.rs +++ b/core/src/avm2.rs @@ -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, diff --git a/core/src/avm2/class.rs b/core/src/avm2/class.rs index 67fcb2eb6..6114ed53c 100644 --- a/core/src/avm2/class.rs +++ b/core/src/avm2/class.rs @@ -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>>, + instance_traits: Vec>, /// 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>>, + class_traits: Vec>, + + /// 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>>, - all_traits: &[Gc<'gc, Trait<'gc>>], + known_traits: &mut Vec>, + 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, 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>>, + known_traits: &mut Vec>, ) -> 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>>, + known_traits: &mut Vec>, ) -> Result<(), Error> { do_trait_lookup(name, known_traits, &self.instance_traits) } diff --git a/core/src/avm2/function.rs b/core/src/avm2/function.rs index 0790ce019..517f25872 100644 --- a/core/src/avm2/function.rs +++ b/core/src/avm2/function.rs @@ -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>>, ) -> Result<(Object<'gc>, Object<'gc>), Error> { + let class_read = class.read(); let super_proto: Result, 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>>, Error> { + fn get_trait(self, name: &QName) -> Result>, Error> { self.0.read().base.get_trait(name) } fn get_provided_trait( &self, name: &QName, - known_traits: &mut Vec>>, + known_traits: &mut Vec>, ) -> 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>>, ) -> Result, Error> { let this: Object<'gc> = Object::FunctionObject(*self); @@ -654,7 +655,7 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> { fn to_string(&self) -> Result, 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()) } diff --git a/core/src/avm2/object.rs b/core/src/avm2/object.rs index 4e0e9ab63..eabfa40c2 100644 --- a/core/src/avm2/object.rs +++ b/core/src/avm2/object.rs @@ -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> + 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>>, Error>; + fn get_trait(self, name: &QName) -> Result>, Error>; /// Populate a list of traits that this object provides. /// @@ -203,7 +203,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy fn get_provided_trait( &self, name: &QName, - known_traits: &mut Vec>>, + known_traits: &mut Vec>, ) -> 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> + 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> + Clone + Copy &mut self, avm: &mut Avm2<'gc>, context: &mut UpdateContext<'_, 'gc, '_>, - trait_entry: Gc<'gc, Trait<'gc>>, + trait_entry: Trait<'gc>, scope: Option>>, reciever: Object<'gc>, ) -> Result<(), Error> { @@ -479,10 +479,11 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + 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> + 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> + Clone + Copy &self, avm: &mut Avm2<'gc>, context: &mut UpdateContext<'_, 'gc, '_>, - class: Gc<'gc, Class<'gc>>, + class: GcCell<'gc, Class<'gc>>, scope: Option>>, ) -> Result, Error>; diff --git a/core/src/avm2/script.rs b/core/src/avm2/script.rs new file mode 100644 index 000000000..9c45a2415 --- /dev/null +++ b/core/src/avm2/script.rs @@ -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); + +/// 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>, + + /// All classes loaded from the ABC's class list. + classes: HashMap>>, + + /// All methods loaded from the ABC's method list. + methods: HashMap>, + + /// All scripts loaded from the ABC's scripts list. + scripts: HashMap>, +} + +impl<'gc> TranslationUnit<'gc> { + pub fn from_abc(abc: Rc) -> 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 { + 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, Error> { + if let Some(method) = self.methods.get(&method_index) { + return Ok(method.clone()); + } + + let method: Result = + 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>, 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); + } +} diff --git a/core/src/avm2/script_object.rs b/core/src/avm2/script_object.rs index 41fd1c8ae..80bfa3b8f 100644 --- a/core/src/avm2/script_object.rs +++ b/core/src/avm2/script_object.rs @@ -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>>), + InstancePrototype(GcCell<'gc, Class<'gc>>, Option>>), /// Instantiate class traits, for class constructors. - ClassConstructor(Gc<'gc, Class<'gc>>, Option>>), + ClassConstructor(GcCell<'gc, Class<'gc>>, Option>>), /// 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>>, Error> { + fn get_trait(self, name: &QName) -> Result>, Error> { self.0.read().get_trait(name) } fn get_provided_trait( &self, name: &QName, - known_traits: &mut Vec>>, + known_traits: &mut Vec>, ) -> 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>>, ) -> Result, 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>>, ) -> 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>>, Error> { + pub fn get_trait(&self, name: &QName) -> Result>, 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>>, + known_traits: &mut Vec>, ) -> 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 { 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), } diff --git a/core/src/avm2/trait.rs b/core/src/avm2/trait.rs index d05d7533d..0726de6c7 100644 --- a/core/src/avm2/trait.rs +++ b/core/src/avm2/trait.rs @@ -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 { + 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 }