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 sprite: Object<'gc>,
|
||||||
pub simplebutton: Object<'gc>,
|
pub simplebutton: Object<'gc>,
|
||||||
pub regexp: Object<'gc>,
|
pub regexp: Object<'gc>,
|
||||||
|
pub vector: Object<'gc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'gc> SystemPrototypes<'gc> {
|
impl<'gc> SystemPrototypes<'gc> {
|
||||||
|
@ -164,6 +165,7 @@ impl<'gc> SystemPrototypes<'gc> {
|
||||||
sprite: empty,
|
sprite: empty,
|
||||||
simplebutton: empty,
|
simplebutton: empty,
|
||||||
regexp: empty,
|
regexp: empty,
|
||||||
|
vector: empty,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -204,6 +206,7 @@ pub struct SystemClasses<'gc> {
|
||||||
pub sprite: Object<'gc>,
|
pub sprite: Object<'gc>,
|
||||||
pub simplebutton: Object<'gc>,
|
pub simplebutton: Object<'gc>,
|
||||||
pub regexp: Object<'gc>,
|
pub regexp: Object<'gc>,
|
||||||
|
pub vector: Object<'gc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'gc> SystemClasses<'gc> {
|
impl<'gc> SystemClasses<'gc> {
|
||||||
|
@ -253,6 +256,7 @@ impl<'gc> SystemClasses<'gc> {
|
||||||
sprite: empty,
|
sprite: empty,
|
||||||
simplebutton: empty,
|
simplebutton: empty,
|
||||||
regexp: empty,
|
regexp: empty,
|
||||||
|
vector: empty,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -499,8 +503,7 @@ pub fn load_player_globals<'gc>(
|
||||||
|
|
||||||
class(activation, math::create_class(mc), domain, script)?;
|
class(activation, math::create_class(mc), domain, script)?;
|
||||||
avm2_system_class!(regexp, activation, regexp::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, activation, xml::create_class(mc), domain, script);
|
||||||
avm2_system_class!(
|
avm2_system_class!(
|
||||||
xml_list,
|
xml_list,
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::avm2::class::{Class, ClassAttributes};
|
||||||
use crate::avm2::globals::NS_VECTOR;
|
use crate::avm2::globals::NS_VECTOR;
|
||||||
use crate::avm2::method::{Method, NativeMethodImpl};
|
use crate::avm2::method::{Method, NativeMethodImpl};
|
||||||
use crate::avm2::names::{Namespace, QName};
|
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::value::Value;
|
||||||
use crate::avm2::Error;
|
use crate::avm2::Error;
|
||||||
use gc_arena::{GcCell, MutationContext};
|
use gc_arena::{GcCell, MutationContext};
|
||||||
|
@ -84,6 +84,84 @@ pub fn set_length<'gc>(
|
||||||
Ok(Value::Undefined)
|
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.
|
/// Construct `Sprite`'s class.
|
||||||
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
|
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
|
||||||
let class = Class::new(
|
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))];
|
)] = &[("length", Some(length), Some(set_length))];
|
||||||
write.define_public_builtin_instance_properties(mc, PUBLIC_INSTANCE_PROPERTIES);
|
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
|
class
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,40 @@ pub struct VectorObjectData<'gc> {
|
||||||
vector: VectorStorage<'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<'gc> TObject<'gc> for VectorObject<'gc> {
|
||||||
impl_avm2_custom_object!(base);
|
impl_avm2_custom_object!(base);
|
||||||
impl_avm2_custom_object_instance!(base);
|
impl_avm2_custom_object_instance!(base);
|
||||||
|
|
|
@ -125,4 +125,23 @@ impl<'gc> VectorStorage<'gc> {
|
||||||
.map(|v| *v = value)
|
.map(|v| *v = value)
|
||||||
.ok_or_else(|| format!("RangeError: {} is outside the range of the vector", pos).into())
|
.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