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};
|
2021-01-22 00:35:46 +00:00
|
|
|
use bitflags::bitflags;
|
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
|
|
|
|
2021-01-22 00:35:46 +00:00
|
|
|
bitflags! {
|
|
|
|
/// All attributes a trait can have.
|
|
|
|
pub struct TraitAttributes: u8 {
|
|
|
|
/// Whether or not traits in downstream classes are allowed to override
|
|
|
|
/// this trait.
|
|
|
|
const FINAL = 1 << 0;
|
2020-07-20 23:39:28 +00:00
|
|
|
|
2021-01-22 00:35:46 +00:00
|
|
|
/// Whether or not this trait is intended to override an upstream class's
|
|
|
|
/// trait.
|
|
|
|
const OVERRIDE = 1 << 1;
|
|
|
|
}
|
2020-07-20 23:39:28 +00:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2020-07-20 23:39:28 +00:00
|
|
|
/// The attributes set on this trait.
|
2021-05-14 05:59:57 +00:00
|
|
|
#[collect(require_static)]
|
|
|
|
attributes: TraitAttributes,
|
2020-06-30 03:58:48 +00:00
|
|
|
|
|
|
|
/// The kind of trait in use.
|
|
|
|
kind: TraitKind<'gc>,
|
|
|
|
}
|
|
|
|
|
2021-05-14 05:59:57 +00:00
|
|
|
fn trait_attribs_from_abc_traits(abc_trait: &AbcTrait) -> TraitAttributes {
|
2021-01-22 00:35:46 +00:00
|
|
|
let mut attributes = TraitAttributes::empty();
|
|
|
|
attributes.set(TraitAttributes::FINAL, abc_trait.is_final);
|
|
|
|
attributes.set(TraitAttributes::OVERRIDE, abc_trait.is_override);
|
2021-05-14 05:59:57 +00:00
|
|
|
attributes
|
2020-07-20 23:39:28 +00:00
|
|
|
}
|
|
|
|
|
2020-06-30 03:58:48 +00:00
|
|
|
/// 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,
|
2021-05-14 05:59:57 +00:00
|
|
|
attributes: TraitAttributes::empty(),
|
2020-07-07 04:21:39 +00:00
|
|
|
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,
|
2021-05-14 05:59:57 +00:00
|
|
|
attributes: TraitAttributes::empty(),
|
2020-07-11 21:57:46 +00:00
|
|
|
kind: TraitKind::Method { disp_id: 0, method },
|
|
|
|
}
|
|
|
|
}
|
2020-07-20 23:14:33 +00:00
|
|
|
|
|
|
|
pub fn from_getter(name: QName<'gc>, method: Method<'gc>) -> Self {
|
|
|
|
Trait {
|
|
|
|
name,
|
2021-05-14 05:59:57 +00:00
|
|
|
attributes: TraitAttributes::empty(),
|
2020-07-20 23:14:33 +00:00
|
|
|
kind: TraitKind::Getter { disp_id: 0, method },
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn from_setter(name: QName<'gc>, method: Method<'gc>) -> Self {
|
|
|
|
Trait {
|
|
|
|
name,
|
2021-05-14 05:59:57 +00:00
|
|
|
attributes: TraitAttributes::empty(),
|
2020-07-20 23:14:33 +00:00
|
|
|
kind: TraitKind::Setter { disp_id: 0, method },
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn from_function(name: QName<'gc>, function: Method<'gc>) -> Self {
|
|
|
|
Trait {
|
|
|
|
name,
|
2021-05-14 05:59:57 +00:00
|
|
|
attributes: TraitAttributes::empty(),
|
2020-07-20 23:14:33 +00:00
|
|
|
kind: TraitKind::Function {
|
|
|
|
slot_id: 0,
|
|
|
|
function,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2020-07-20 23:20:00 +00:00
|
|
|
|
|
|
|
pub fn from_slot(
|
|
|
|
name: QName<'gc>,
|
|
|
|
type_name: Multiname<'gc>,
|
|
|
|
default_value: Option<Value<'gc>>,
|
|
|
|
) -> Self {
|
|
|
|
Trait {
|
|
|
|
name,
|
2021-05-14 05:59:57 +00:00
|
|
|
attributes: TraitAttributes::empty(),
|
2020-07-20 23:20:00 +00:00
|
|
|
kind: TraitKind::Slot {
|
|
|
|
slot_id: 0,
|
|
|
|
type_name,
|
|
|
|
default_value,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn from_const(
|
|
|
|
name: QName<'gc>,
|
|
|
|
type_name: Multiname<'gc>,
|
|
|
|
default_value: Option<Value<'gc>>,
|
|
|
|
) -> Self {
|
|
|
|
Trait {
|
|
|
|
name,
|
2021-05-14 05:59:57 +00:00
|
|
|
attributes: TraitAttributes::empty(),
|
2020-07-20 23:20:00 +00:00
|
|
|
kind: TraitKind::Slot {
|
|
|
|
slot_id: 0,
|
|
|
|
type_name,
|
|
|
|
default_value,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
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,
|
2020-07-20 23:39:28 +00:00
|
|
|
attributes: trait_attribs_from_abc_traits(abc_trait),
|
2020-07-02 03:21:30 +00:00
|
|
|
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 {
|
2021-06-05 10:53:23 +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,
|
2020-07-20 23:39:28 +00:00
|
|
|
attributes: trait_attribs_from_abc_traits(abc_trait),
|
2020-07-02 03:21:30 +00:00
|
|
|
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,
|
2020-07-20 23:39:28 +00:00
|
|
|
attributes: trait_attribs_from_abc_traits(abc_trait),
|
2020-07-02 03:21:30 +00:00
|
|
|
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,
|
2020-07-20 23:39:28 +00:00
|
|
|
attributes: trait_attribs_from_abc_traits(abc_trait),
|
2020-07-02 03:21:30 +00:00
|
|
|
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,
|
2020-07-20 23:39:28 +00:00
|
|
|
attributes: trait_attribs_from_abc_traits(abc_trait),
|
2020-07-02 03:21:30 +00:00
|
|
|
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,
|
2020-07-20 23:39:28 +00:00
|
|
|
attributes: trait_attribs_from_abc_traits(abc_trait),
|
2020-07-02 03:21:30 +00:00
|
|
|
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,
|
2020-07-20 23:39:28 +00:00
|
|
|
attributes: trait_attribs_from_abc_traits(abc_trait),
|
2020-07-02 03:21:30 +00:00
|
|
|
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 {
|
2021-06-05 10:53:23 +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 {
|
2021-05-14 05:59:57 +00:00
|
|
|
self.attributes.contains(TraitAttributes::FINAL)
|
2020-07-01 03:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_override(&self) -> bool {
|
2021-05-14 05:59:57 +00:00
|
|
|
self.attributes.contains(TraitAttributes::OVERRIDE)
|
2020-07-01 03:44:14 +00:00
|
|
|
}
|
2020-07-20 23:42:03 +00:00
|
|
|
|
2021-01-22 00:35:46 +00:00
|
|
|
pub fn set_attributes(&mut self, attribs: TraitAttributes) {
|
2021-05-14 05:59:57 +00:00
|
|
|
self.attributes = attribs;
|
2020-07-20 23:42:03 +00:00
|
|
|
}
|
2020-07-20 23:50:17 +00:00
|
|
|
|
2021-04-17 21:42:34 +00:00
|
|
|
/// Convenience chaining method that adds the override flag to a trait.
|
|
|
|
pub fn with_override(mut self) -> Self {
|
2021-05-14 05:59:57 +00:00
|
|
|
self.attributes |= TraitAttributes::OVERRIDE;
|
2021-04-17 21:42:34 +00:00
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-07-20 23:50:17 +00:00
|
|
|
/// Set the slot or dispatch ID of this trait.
|
|
|
|
pub fn set_slot_id(&mut self, id: u32) {
|
|
|
|
match &mut self.kind {
|
|
|
|
TraitKind::Slot { slot_id, .. } => *slot_id = id,
|
|
|
|
TraitKind::Method { disp_id, .. } => *disp_id = id,
|
|
|
|
TraitKind::Getter { disp_id, .. } => *disp_id = id,
|
|
|
|
TraitKind::Setter { disp_id, .. } => *disp_id = id,
|
|
|
|
TraitKind::Class { slot_id, .. } => *slot_id = id,
|
|
|
|
TraitKind::Function { slot_id, .. } => *slot_id = id,
|
|
|
|
TraitKind::Const { slot_id, .. } => *slot_id = id,
|
|
|
|
}
|
|
|
|
}
|
2020-07-01 03:44:14 +00:00
|
|
|
}
|