avm2: Impl `Vector.concat`
This commit is contained in:
parent
96afc5a6c2
commit
dba9b18540
|
@ -115,6 +115,7 @@ pub struct SystemPrototypes<'gc> {
|
|||
pub sprite: Object<'gc>,
|
||||
pub simplebutton: Object<'gc>,
|
||||
pub regexp: Object<'gc>,
|
||||
pub vector: Object<'gc>,
|
||||
}
|
||||
|
||||
impl<'gc> SystemPrototypes<'gc> {
|
||||
|
@ -164,6 +165,7 @@ impl<'gc> SystemPrototypes<'gc> {
|
|||
sprite: empty,
|
||||
simplebutton: empty,
|
||||
regexp: empty,
|
||||
vector: empty,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -204,6 +206,7 @@ pub struct SystemClasses<'gc> {
|
|||
pub sprite: Object<'gc>,
|
||||
pub simplebutton: Object<'gc>,
|
||||
pub regexp: Object<'gc>,
|
||||
pub vector: Object<'gc>,
|
||||
}
|
||||
|
||||
impl<'gc> SystemClasses<'gc> {
|
||||
|
@ -253,6 +256,7 @@ impl<'gc> SystemClasses<'gc> {
|
|||
sprite: empty,
|
||||
simplebutton: empty,
|
||||
regexp: empty,
|
||||
vector: empty,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -499,8 +503,7 @@ pub fn load_player_globals<'gc>(
|
|||
|
||||
class(activation, math::create_class(mc), domain, script)?;
|
||||
avm2_system_class!(regexp, activation, regexp::create_class(mc), domain, script);
|
||||
class(activation, vector::create_class(mc), domain, script)?;
|
||||
|
||||
avm2_system_class!(vector, activation, vector::create_class(mc), domain, script);
|
||||
avm2_system_class!(xml, activation, xml::create_class(mc), domain, script);
|
||||
avm2_system_class!(
|
||||
xml_list,
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::avm2::class::{Class, ClassAttributes};
|
|||
use crate::avm2::globals::NS_VECTOR;
|
||||
use crate::avm2::method::{Method, NativeMethodImpl};
|
||||
use crate::avm2::names::{Namespace, QName};
|
||||
use crate::avm2::object::{vector_allocator, Object, TObject};
|
||||
use crate::avm2::object::{vector_allocator, Object, TObject, VectorObject};
|
||||
use crate::avm2::value::Value;
|
||||
use crate::avm2::Error;
|
||||
use gc_arena::{GcCell, MutationContext};
|
||||
|
@ -84,6 +84,84 @@ pub fn set_length<'gc>(
|
|||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
/// `Vector.concat` impl
|
||||
pub fn concat<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
this: Option<Object<'gc>>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
if let Some(this) = this {
|
||||
let mut new_vector_storage =
|
||||
if let Some(vector) = this.as_vector_storage_mut(activation.context.gc_context) {
|
||||
vector.clone()
|
||||
} else {
|
||||
return Err("Not a vector-structured object".into());
|
||||
};
|
||||
|
||||
let my_class = this
|
||||
.as_class_object()
|
||||
.ok_or("TypeError: Tried to concat into a bare object")?;
|
||||
let val_class = new_vector_storage.value_type();
|
||||
|
||||
for arg in args.iter().map(|a| a.clone()) {
|
||||
let arg_obj = arg.coerce_to_object(activation)?;
|
||||
let arg_class = arg_obj
|
||||
.as_class()
|
||||
.ok_or("TypeError: Tried to concat from a bare object")?;
|
||||
if !arg.is_of_type(activation, my_class)? {
|
||||
return Err(format!(
|
||||
"TypeError: Cannot coerce argument of type {:?} to argument of type {:?}",
|
||||
arg_class.read().name(),
|
||||
my_class
|
||||
.as_class()
|
||||
.ok_or("TypeError: Tried to concat into a bare object")?
|
||||
.read()
|
||||
.name()
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
let old_vec = arg_obj.as_vector_storage();
|
||||
let old_vec: Vec<Option<Value<'gc>>> = if let Some(old_vec) = old_vec {
|
||||
old_vec.iter().collect()
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
for val in old_vec {
|
||||
if let Some(val) = val {
|
||||
if let Ok(val_obj) = val.coerce_to_object(activation) {
|
||||
if !val.is_of_type(activation, val_class)? {
|
||||
let other_val_class = val_obj
|
||||
.as_class()
|
||||
.ok_or("TypeError: Tried to concat a bare object into a Vector")?;
|
||||
return Err(format!(
|
||||
"TypeError: Cannot coerce Vector value of type {:?} to type {:?}",
|
||||
other_val_class.read().name(),
|
||||
val_class
|
||||
.as_class()
|
||||
.ok_or("TypeError: Tried to concat into a bare object")?
|
||||
.read()
|
||||
.name()
|
||||
)
|
||||
.into());
|
||||
}
|
||||
}
|
||||
|
||||
let coerced_val = val.coerce_to_type(activation, val_class)?;
|
||||
new_vector_storage.push(Some(coerced_val));
|
||||
} else {
|
||||
new_vector_storage.push(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(VectorObject::from_vector(new_vector_storage, activation)?.into());
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
/// Construct `Sprite`'s class.
|
||||
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
|
||||
let class = Class::new(
|
||||
|
@ -106,5 +184,8 @@ pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>
|
|||
)] = &[("length", Some(length), Some(set_length))];
|
||||
write.define_public_builtin_instance_properties(mc, PUBLIC_INSTANCE_PROPERTIES);
|
||||
|
||||
const PUBLIC_INSTANCE_METHODS: &[(&str, NativeMethodImpl)] = &[("concat", concat)];
|
||||
write.define_public_builtin_instance_methods(mc, PUBLIC_INSTANCE_METHODS);
|
||||
|
||||
class
|
||||
}
|
||||
|
|
|
@ -50,6 +50,40 @@ pub struct VectorObjectData<'gc> {
|
|||
vector: VectorStorage<'gc>,
|
||||
}
|
||||
|
||||
impl<'gc> VectorObject<'gc> {
|
||||
/// Wrap an existing vector in an object.
|
||||
pub fn from_vector(
|
||||
vector: VectorStorage<'gc>,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let value_type = vector.value_type();
|
||||
let vector_class = activation.avm2().classes().vector;
|
||||
|
||||
let mut applied_class = vector_class.apply(activation, &[value_type])?;
|
||||
let applied_proto = applied_class
|
||||
.get_property(
|
||||
applied_class,
|
||||
&QName::new(Namespace::public(), "prototype"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
let mut object: Object<'gc> = VectorObject(GcCell::allocate(
|
||||
activation.context.gc_context,
|
||||
VectorObjectData {
|
||||
base: ScriptObjectData::base_new(Some(applied_proto), Some(applied_class)),
|
||||
vector,
|
||||
},
|
||||
))
|
||||
.into();
|
||||
|
||||
object.install_instance_traits(activation, applied_class)?;
|
||||
object.call_native_init(Some(object), &[], activation, Some(applied_class))?;
|
||||
|
||||
Ok(object)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> TObject<'gc> for VectorObject<'gc> {
|
||||
impl_avm2_custom_object!(base);
|
||||
impl_avm2_custom_object_instance!(base);
|
||||
|
|
|
@ -125,4 +125,23 @@ impl<'gc> VectorStorage<'gc> {
|
|||
.map(|v| *v = value)
|
||||
.ok_or_else(|| format!("RangeError: {} is outside the range of the vector", pos).into())
|
||||
}
|
||||
|
||||
/// Push a value to the end of the vector.
|
||||
///
|
||||
/// This function does no coercion as calling it requires mutably borrowing
|
||||
/// the vector (and thus it is unwise to reenter the AVM2 runtime to coerce
|
||||
/// things). You must use the associated `coerce` fn before storing things
|
||||
/// in the vector.
|
||||
pub fn push(&mut self, value: Option<Value<'gc>>) {
|
||||
self.storage.push(value)
|
||||
}
|
||||
|
||||
/// Iterate over vector values.
|
||||
pub fn iter<'a>(
|
||||
&'a self,
|
||||
) -> impl DoubleEndedIterator<Item = Option<Value<'gc>>>
|
||||
+ ExactSizeIterator<Item = Option<Value<'gc>>>
|
||||
+ 'a {
|
||||
self.storage.iter().cloned()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue