2020-06-30 03:58:48 +00:00
|
|
|
//! Active trait definitions
|
|
|
|
|
|
|
|
use crate::avm2::class::Class;
|
2020-07-03 01:49:53 +00:00
|
|
|
use crate::avm2::method::Method;
|
2020-06-30 03:58:48 +00:00
|
|
|
use crate::avm2::names::{Multiname, QName};
|
2020-07-02 03:21:30 +00:00
|
|
|
use crate::avm2::script::TranslationUnit;
|
|
|
|
use crate::avm2::value::{abc_default_value, Value};
|
2020-07-29 03:08:05 +00:00
|
|
|
use crate::avm2::{Avm2, Error};
|
2020-07-02 03:21:30 +00:00
|
|
|
use gc_arena::{Collect, GcCell, MutationContext};
|
|
|
|
use swf::avm2::types::{Trait as AbcTrait, TraitKind as AbcTraitKind};
|
2020-06-30 03:58:48 +00:00
|
|
|
|
|
|
|
/// Represents a trait as loaded into the VM.
|
|
|
|
///
|
|
|
|
/// A trait is an uninstantiated AVM2 property. Traits are used by objects to
|
|
|
|
/// track how to construct their properties when first accessed.
|
|
|
|
///
|
|
|
|
/// This type exists primarily to support classes with native methods. Adobe's
|
|
|
|
/// implementation of AVM2 handles native classes by having a special ABC file
|
|
|
|
/// load before all other code. We instead generate an initial heap in the same
|
|
|
|
/// manner as we do in AVM1, which means that we need to have a way to
|
|
|
|
/// dynamically originate traits that do not come from any particular ABC file.
|
|
|
|
#[derive(Clone, Debug, Collect)]
|
|
|
|
#[collect(no_drop)]
|
|
|
|
pub struct Trait<'gc> {
|
|
|
|
/// The name of this trait.
|
2020-07-14 02:21:18 +00:00
|
|
|
name: QName<'gc>,
|
2020-06-30 03:58:48 +00:00
|
|
|
|
|
|
|
/// Whether or not traits in downstream classes are allowed to override
|
|
|
|
/// this trait.
|
|
|
|
is_final: bool,
|
|
|
|
|
|
|
|
/// Whether or not this trait is intended to override an upstream class's
|
|
|
|
/// trait.
|
|
|
|
is_override: bool,
|
|
|
|
|
|
|
|
/// The kind of trait in use.
|
|
|
|
kind: TraitKind<'gc>,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The fields for a particular kind of trait.
|
|
|
|
///
|
|
|
|
/// The kind of a trait also determines how it's instantiated on the object.
|
|
|
|
/// See each individual variant for more information.
|
|
|
|
#[derive(Clone, Debug, Collect)]
|
|
|
|
#[collect(no_drop)]
|
|
|
|
pub enum TraitKind<'gc> {
|
|
|
|
/// A data field on an object instance that can be read from and written
|
|
|
|
/// to.
|
|
|
|
Slot {
|
|
|
|
slot_id: u32,
|
2020-07-14 02:21:18 +00:00
|
|
|
type_name: Multiname<'gc>,
|
2020-06-30 03:58:48 +00:00
|
|
|
default_value: Option<Value<'gc>>,
|
|
|
|
},
|
|
|
|
|
|
|
|
/// A method on an object that can be called.
|
|
|
|
Method { disp_id: u32, method: Method<'gc> },
|
|
|
|
|
|
|
|
/// A getter property on an object that can be read.
|
|
|
|
Getter { disp_id: u32, method: Method<'gc> },
|
|
|
|
|
|
|
|
/// A setter property on an object that can be written.
|
|
|
|
Setter { disp_id: u32, method: Method<'gc> },
|
|
|
|
|
|
|
|
/// A class property on an object that can be used to construct more
|
|
|
|
/// objects.
|
|
|
|
Class {
|
|
|
|
slot_id: u32,
|
2020-07-02 03:21:30 +00:00
|
|
|
class: GcCell<'gc, Class<'gc>>,
|
2020-06-30 03:58:48 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
/// A free function (not an instance method) that can be called.
|
|
|
|
Function { slot_id: u32, function: Method<'gc> },
|
|
|
|
|
|
|
|
/// A data field on an object that is always a particular value, and cannot
|
|
|
|
/// be overridden.
|
|
|
|
Const {
|
|
|
|
slot_id: u32,
|
2020-07-14 02:21:18 +00:00
|
|
|
type_name: Multiname<'gc>,
|
2020-06-30 03:58:48 +00:00
|
|
|
default_value: Option<Value<'gc>>,
|
|
|
|
},
|
|
|
|
}
|
2020-07-01 03:44:14 +00:00
|
|
|
|
|
|
|
impl<'gc> Trait<'gc> {
|
2020-07-07 04:21:39 +00:00
|
|
|
pub fn from_class(class: GcCell<'gc, Class<'gc>>) -> Self {
|
|
|
|
let name = class.read().name().clone();
|
|
|
|
|
|
|
|
Trait {
|
|
|
|
name,
|
|
|
|
is_final: false,
|
|
|
|
is_override: false,
|
|
|
|
kind: TraitKind::Class { slot_id: 0, class },
|
|
|
|
}
|
|
|
|
}
|
2020-07-11 21:57:46 +00:00
|
|
|
|
|
|
|
pub fn from_method(name: QName<'gc>, method: Method<'gc>) -> Self {
|
|
|
|
Trait {
|
|
|
|
name,
|
|
|
|
is_final: false,
|
|
|
|
is_override: false,
|
|
|
|
kind: TraitKind::Method { disp_id: 0, method },
|
|
|
|
}
|
|
|
|
}
|
2020-07-07 04:21:39 +00:00
|
|
|
|
2020-07-02 03:21:30 +00:00
|
|
|
/// Convert an ABC trait into a loaded trait.
|
|
|
|
pub fn from_abc_trait(
|
2020-07-02 22:48:37 +00:00
|
|
|
unit: TranslationUnit<'gc>,
|
2020-07-02 03:21:30 +00:00
|
|
|
abc_trait: &AbcTrait,
|
2020-07-29 03:08:05 +00:00
|
|
|
avm2: &mut Avm2<'gc>,
|
2020-07-02 03:21:30 +00:00
|
|
|
mc: MutationContext<'gc, '_>,
|
|
|
|
) -> Result<Self, Error> {
|
2020-07-15 00:05:25 +00:00
|
|
|
let name = QName::from_abc_multiname(unit, abc_trait.name.clone(), mc)?;
|
2020-07-02 03:21:30 +00:00
|
|
|
|
2020-07-02 23:51:32 +00:00
|
|
|
Ok(match &abc_trait.kind {
|
2020-07-02 03:21:30 +00:00
|
|
|
AbcTraitKind::Slot {
|
|
|
|
slot_id,
|
|
|
|
type_name,
|
|
|
|
value,
|
|
|
|
} => Trait {
|
|
|
|
name,
|
|
|
|
is_final: abc_trait.is_final,
|
|
|
|
is_override: abc_trait.is_override,
|
|
|
|
kind: TraitKind::Slot {
|
2020-07-02 23:51:32 +00:00
|
|
|
slot_id: *slot_id,
|
2020-07-03 01:05:32 +00:00
|
|
|
type_name: if type_name.0 == 0 {
|
|
|
|
Multiname::any()
|
|
|
|
} else {
|
2020-07-15 00:05:25 +00:00
|
|
|
Multiname::from_abc_multiname_static(unit, type_name.clone(), mc)?
|
2020-07-03 01:05:32 +00:00
|
|
|
},
|
2020-07-02 03:21:30 +00:00
|
|
|
default_value: if let Some(dv) = value {
|
2020-07-29 03:08:05 +00:00
|
|
|
Some(abc_default_value(unit, &dv, avm2, mc)?)
|
2020-07-02 03:21:30 +00:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
AbcTraitKind::Method { disp_id, method } => Trait {
|
|
|
|
name,
|
|
|
|
is_final: abc_trait.is_final,
|
|
|
|
is_override: abc_trait.is_override,
|
|
|
|
kind: TraitKind::Method {
|
2020-07-02 23:51:32 +00:00
|
|
|
disp_id: *disp_id,
|
2020-07-02 22:48:37 +00:00
|
|
|
method: unit.load_method(method.0, mc)?,
|
2020-07-02 03:21:30 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
AbcTraitKind::Getter { disp_id, method } => Trait {
|
|
|
|
name,
|
|
|
|
is_final: abc_trait.is_final,
|
|
|
|
is_override: abc_trait.is_override,
|
|
|
|
kind: TraitKind::Getter {
|
2020-07-02 23:51:32 +00:00
|
|
|
disp_id: *disp_id,
|
2020-07-02 22:48:37 +00:00
|
|
|
method: unit.load_method(method.0, mc)?,
|
2020-07-02 03:21:30 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
AbcTraitKind::Setter { disp_id, method } => Trait {
|
|
|
|
name,
|
|
|
|
is_final: abc_trait.is_final,
|
|
|
|
is_override: abc_trait.is_override,
|
|
|
|
kind: TraitKind::Setter {
|
2020-07-02 23:51:32 +00:00
|
|
|
disp_id: *disp_id,
|
2020-07-02 22:48:37 +00:00
|
|
|
method: unit.load_method(method.0, mc)?,
|
2020-07-02 03:21:30 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
AbcTraitKind::Class { slot_id, class } => Trait {
|
|
|
|
name,
|
|
|
|
is_final: abc_trait.is_final,
|
|
|
|
is_override: abc_trait.is_override,
|
|
|
|
kind: TraitKind::Class {
|
2020-07-02 23:51:32 +00:00
|
|
|
slot_id: *slot_id,
|
2020-07-29 03:08:05 +00:00
|
|
|
class: unit.load_class(class.0, avm2, mc)?,
|
2020-07-02 03:21:30 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
AbcTraitKind::Function { slot_id, function } => Trait {
|
|
|
|
name,
|
|
|
|
is_final: abc_trait.is_final,
|
|
|
|
is_override: abc_trait.is_override,
|
|
|
|
kind: TraitKind::Function {
|
2020-07-02 23:51:32 +00:00
|
|
|
slot_id: *slot_id,
|
2020-07-02 22:48:37 +00:00
|
|
|
function: unit.load_method(function.0, mc)?,
|
2020-07-02 03:21:30 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
AbcTraitKind::Const {
|
|
|
|
slot_id,
|
|
|
|
type_name,
|
|
|
|
value,
|
|
|
|
} => Trait {
|
|
|
|
name,
|
|
|
|
is_final: abc_trait.is_final,
|
|
|
|
is_override: abc_trait.is_override,
|
|
|
|
kind: TraitKind::Const {
|
2020-07-02 23:51:32 +00:00
|
|
|
slot_id: *slot_id,
|
2020-07-03 01:05:32 +00:00
|
|
|
type_name: if type_name.0 == 0 {
|
|
|
|
Multiname::any()
|
|
|
|
} else {
|
2020-07-15 00:05:25 +00:00
|
|
|
Multiname::from_abc_multiname_static(unit, type_name.clone(), mc)?
|
2020-07-03 01:05:32 +00:00
|
|
|
},
|
2020-07-02 03:21:30 +00:00
|
|
|
default_value: if let Some(dv) = value {
|
2020-07-29 03:08:05 +00:00
|
|
|
Some(abc_default_value(unit, &dv, avm2, mc)?)
|
2020-07-02 03:21:30 +00:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-07-14 02:21:18 +00:00
|
|
|
pub fn name(&self) -> &QName<'gc> {
|
2020-07-01 03:44:14 +00:00
|
|
|
&self.name
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn kind(&self) -> &TraitKind<'gc> {
|
|
|
|
&self.kind
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_final(&self) -> bool {
|
|
|
|
self.is_final
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_override(&self) -> bool {
|
|
|
|
self.is_override
|
|
|
|
}
|
|
|
|
}
|