avm2: Implement base types for array-shaped objects.
This commit is contained in:
parent
398fd68c80
commit
88fc9b1538
|
@ -23,6 +23,7 @@ macro_rules! avm_debug {
|
||||||
}
|
}
|
||||||
|
|
||||||
mod activation;
|
mod activation;
|
||||||
|
mod array;
|
||||||
mod class;
|
mod class;
|
||||||
mod function;
|
mod function;
|
||||||
mod globals;
|
mod globals;
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
//! Array support types
|
||||||
|
|
||||||
|
use crate::avm2::activation::Activation;
|
||||||
|
use crate::avm2::names::QName;
|
||||||
|
use crate::avm2::object::{Object, TObject};
|
||||||
|
use crate::avm2::string::AvmString;
|
||||||
|
use crate::avm2::value::Value;
|
||||||
|
use crate::avm2::Error;
|
||||||
|
use gc_arena::Collect;
|
||||||
|
|
||||||
|
/// The array storage portion of an array object.
|
||||||
|
///
|
||||||
|
/// Array values may consist of either standard `Value`s or "holes": values
|
||||||
|
/// which are not properties of the associated object and must be resolved in
|
||||||
|
/// the prototype.
|
||||||
|
#[derive(Clone, Collect, Debug)]
|
||||||
|
#[collect(no_drop)]
|
||||||
|
pub struct ArrayStorage<'gc> {
|
||||||
|
storage: Vec<Option<Value<'gc>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gc> ArrayStorage<'gc> {
|
||||||
|
/// Construct new array storage.
|
||||||
|
///
|
||||||
|
/// The length parameter indicates how big the array storage should start
|
||||||
|
/// out as. All array storage consists of holes.
|
||||||
|
pub fn new(length: usize) -> Self {
|
||||||
|
let mut storage = Vec::new();
|
||||||
|
|
||||||
|
storage.resize(length, None);
|
||||||
|
|
||||||
|
Self { storage }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieve a value from array storage by index.
|
||||||
|
///
|
||||||
|
/// Array holes will be resolved on the prototype. No reference to
|
||||||
|
/// class traits will be made.
|
||||||
|
fn get(
|
||||||
|
&self,
|
||||||
|
item: usize,
|
||||||
|
proto: Option<Object<'gc>>,
|
||||||
|
activation: &mut Activation<'_, 'gc, '_>,
|
||||||
|
) -> Result<Value<'gc>, Error> {
|
||||||
|
Ok(self
|
||||||
|
.storage
|
||||||
|
.get(item)
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or(None)
|
||||||
|
.map(Ok)
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
if let Some(mut proto) = proto {
|
||||||
|
proto.get_property(
|
||||||
|
proto,
|
||||||
|
&QName::dynamic_name(AvmString::new(
|
||||||
|
activation.context.gc_context,
|
||||||
|
format!("{}", item),
|
||||||
|
)),
|
||||||
|
activation,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Ok(Value::Undefined)
|
||||||
|
}
|
||||||
|
})?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set an array storage slot to a particular value.
|
||||||
|
///
|
||||||
|
/// If the item index extends beyond the length of the array, then the
|
||||||
|
/// array will be extended with holes.
|
||||||
|
fn set(&mut self, item: usize, value: Value<'gc>) {
|
||||||
|
if self.storage.len() < (item + 1) {
|
||||||
|
self.storage.resize(item + 1, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
*self.storage.get_mut(item).unwrap() = Some(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the length of the array.
|
||||||
|
fn length(&self) -> usize {
|
||||||
|
self.storage.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the length of the array.
|
||||||
|
fn set_length(&mut self, size: usize) {
|
||||||
|
self.storage.resize(size, None)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
//! AVM2 objects.
|
//! AVM2 objects.
|
||||||
|
|
||||||
use crate::avm2::activation::Activation;
|
use crate::avm2::activation::Activation;
|
||||||
|
use crate::avm2::array::ArrayStorage;
|
||||||
use crate::avm2::class::Class;
|
use crate::avm2::class::Class;
|
||||||
use crate::avm2::function::Executable;
|
use crate::avm2::function::Executable;
|
||||||
use crate::avm2::names::{Multiname, Namespace, QName};
|
use crate::avm2::names::{Multiname, Namespace, QName};
|
||||||
|
@ -14,12 +15,14 @@ use ruffle_macros::enum_trait_object;
|
||||||
use std::cell::Ref;
|
use std::cell::Ref;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
mod array_object;
|
||||||
mod custom_object;
|
mod custom_object;
|
||||||
mod function_object;
|
mod function_object;
|
||||||
mod namespace_object;
|
mod namespace_object;
|
||||||
mod primitive_object;
|
mod primitive_object;
|
||||||
mod script_object;
|
mod script_object;
|
||||||
|
|
||||||
|
pub use crate::avm2::object::array_object::ArrayObject;
|
||||||
pub use crate::avm2::object::function_object::FunctionObject;
|
pub use crate::avm2::object::function_object::FunctionObject;
|
||||||
pub use crate::avm2::object::namespace_object::NamespaceObject;
|
pub use crate::avm2::object::namespace_object::NamespaceObject;
|
||||||
pub use crate::avm2::object::primitive_object::PrimitiveObject;
|
pub use crate::avm2::object::primitive_object::PrimitiveObject;
|
||||||
|
@ -35,6 +38,7 @@ pub use crate::avm2::object::script_object::ScriptObject;
|
||||||
FunctionObject(FunctionObject<'gc>),
|
FunctionObject(FunctionObject<'gc>),
|
||||||
PrimitiveObject(PrimitiveObject<'gc>),
|
PrimitiveObject(PrimitiveObject<'gc>),
|
||||||
NamespaceObject(NamespaceObject<'gc>),
|
NamespaceObject(NamespaceObject<'gc>),
|
||||||
|
ArrayObject(ArrayObject<'gc>),
|
||||||
}
|
}
|
||||||
)]
|
)]
|
||||||
pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy {
|
pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy {
|
||||||
|
@ -735,6 +739,11 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
fn as_namespace(&self) -> Option<Ref<Namespace<'gc>>> {
|
fn as_namespace(&self) -> Option<Ref<Namespace<'gc>>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Unwrap this object as array storage.
|
||||||
|
fn as_array_storage(&self) -> Option<Ref<ArrayStorage<'gc>>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum ObjectPtr {}
|
pub enum ObjectPtr {}
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
//! Array-structured objects
|
||||||
|
|
||||||
|
use crate::avm1::AvmString;
|
||||||
|
use crate::avm2::activation::Activation;
|
||||||
|
use crate::avm2::array::ArrayStorage;
|
||||||
|
use crate::avm2::class::Class;
|
||||||
|
use crate::avm2::names::{Namespace, QName};
|
||||||
|
use crate::avm2::object::script_object::{ScriptObjectClass, ScriptObjectData};
|
||||||
|
use crate::avm2::object::{Object, ObjectPtr, TObject};
|
||||||
|
use crate::avm2::scope::Scope;
|
||||||
|
use crate::avm2::traits::Trait;
|
||||||
|
use crate::avm2::value::Value;
|
||||||
|
use crate::avm2::Error;
|
||||||
|
use crate::impl_avm2_custom_object;
|
||||||
|
use gc_arena::{Collect, GcCell, MutationContext};
|
||||||
|
use std::cell::Ref;
|
||||||
|
|
||||||
|
/// An Object which stores numerical properties in an array.
|
||||||
|
#[derive(Collect, Debug, Clone, Copy)]
|
||||||
|
#[collect(no_drop)]
|
||||||
|
pub struct ArrayObject<'gc>(GcCell<'gc, ArrayObjectData<'gc>>);
|
||||||
|
|
||||||
|
#[derive(Collect, Debug, Clone)]
|
||||||
|
#[collect(no_drop)]
|
||||||
|
pub struct ArrayObjectData<'gc> {
|
||||||
|
/// Base script object
|
||||||
|
base: ScriptObjectData<'gc>,
|
||||||
|
|
||||||
|
/// Array-structured properties
|
||||||
|
array: ArrayStorage<'gc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gc> TObject<'gc> for ArrayObject<'gc> {
|
||||||
|
impl_avm2_custom_object!(base);
|
||||||
|
|
||||||
|
fn to_string(&self, _mc: MutationContext<'gc, '_>) -> Result<Value<'gc>, Error> {
|
||||||
|
Ok("function Function() {}".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn value_of(&self, _mc: MutationContext<'gc, '_>) -> Result<Value<'gc>, Error> {
|
||||||
|
Ok(Value::Object(Object::from(*self)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_array_storage(&self) -> Option<Ref<ArrayStorage<'gc>>> {
|
||||||
|
Some(Ref::map(self.0.read(), |aod| &aod.array))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn construct(
|
||||||
|
&self,
|
||||||
|
activation: &mut Activation<'_, 'gc, '_>,
|
||||||
|
_args: &[Value<'gc>],
|
||||||
|
) -> Result<Object<'gc>, Error> {
|
||||||
|
let this: Object<'gc> = Object::ArrayObject(*self);
|
||||||
|
let base = ScriptObjectData::base_new(Some(this), ScriptObjectClass::NoClass);
|
||||||
|
|
||||||
|
Ok(ArrayObject(GcCell::allocate(
|
||||||
|
activation.context.gc_context,
|
||||||
|
ArrayObjectData {
|
||||||
|
base,
|
||||||
|
array: ArrayStorage::new(0),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn derive(
|
||||||
|
&self,
|
||||||
|
activation: &mut Activation<'_, 'gc, '_>,
|
||||||
|
class: GcCell<'gc, Class<'gc>>,
|
||||||
|
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||||
|
) -> Result<Object<'gc>, Error> {
|
||||||
|
let this: Object<'gc> = Object::ArrayObject(*self);
|
||||||
|
let base = ScriptObjectData::base_new(
|
||||||
|
Some(this),
|
||||||
|
ScriptObjectClass::InstancePrototype(class, scope),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(ArrayObject(GcCell::allocate(
|
||||||
|
activation.context.gc_context,
|
||||||
|
ArrayObjectData {
|
||||||
|
base,
|
||||||
|
array: ArrayStorage::new(0),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue