chore: cargo fmt

This commit is contained in:
Adrian Wielgosik 2021-12-04 22:32:39 +01:00 committed by Adrian Wielgosik
parent 3d8f611651
commit 0fb075a309
25 changed files with 329 additions and 282 deletions

View File

@ -251,7 +251,12 @@ impl<'gc> SystemClasses<'gc> {
/// the empty object also handed to this function. It is the caller's /// the empty object also handed to this function. It is the caller's
/// responsibility to instantiate each class and replace the empty object /// responsibility to instantiate each class and replace the empty object
/// with that. /// with that.
fn new(object: ClassObject<'gc>, function: ClassObject<'gc>, class: ClassObject<'gc>, global: ClassObject<'gc>) -> Self { fn new(
object: ClassObject<'gc>,
function: ClassObject<'gc>,
class: ClassObject<'gc>,
global: ClassObject<'gc>,
) -> Self {
SystemClasses { SystemClasses {
object, object,
function, function,
@ -505,8 +510,12 @@ pub fn load_player_globals<'gc>(
ScriptObject::bare_object(mc), ScriptObject::bare_object(mc),
)); ));
activation.context.avm2.system_classes = activation.context.avm2.system_classes = Some(SystemClasses::new(
Some(SystemClasses::new(object_class, fn_class, class_class, global_class)); object_class,
fn_class,
class_class,
global_class,
));
// Our activation environment is now functional enough to finish // Our activation environment is now functional enough to finish
// initializing the core class weave. The order of initialization shouldn't // initializing the core class weave. The order of initialization shouldn't

View File

@ -64,8 +64,9 @@ fn class_init<'gc>(
scope, scope,
None, None,
Some(this_class), Some(this_class),
).into(), )
activation .into(),
activation,
)?; )?;
boolean_proto.set_property_local( boolean_proto.set_property_local(
&Multiname::public("valueOf"), &Multiname::public("valueOf"),
@ -75,8 +76,9 @@ fn class_init<'gc>(
scope, scope,
None, None,
Some(this_class), Some(this_class),
).into(), )
activation .into(),
activation,
)?; )?;
boolean_proto.set_local_property_is_enumerable(gc_context, "toString".into(), false)?; boolean_proto.set_local_property_is_enumerable(gc_context, "toString".into(), false)?;
boolean_proto.set_local_property_is_enumerable(gc_context, "valueOf".into(), false)?; boolean_proto.set_local_property_is_enumerable(gc_context, "valueOf".into(), false)?;

View File

@ -4,7 +4,7 @@ use crate::avm2::activation::Activation;
use crate::avm2::class::Class; use crate::avm2::class::Class;
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::{TObject, Object}; use crate::avm2::object::{Object, TObject};
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};
@ -59,9 +59,7 @@ pub fn create_class<'gc>(gc_context: MutationContext<'gc, '_>) -> GcCell<'gc, Cl
&str, &str,
Option<NativeMethodImpl>, Option<NativeMethodImpl>,
Option<NativeMethodImpl>, Option<NativeMethodImpl>,
)] = &[ )] = &[("prototype", Some(prototype), None)];
("prototype", Some(prototype), None),
];
write.define_public_builtin_instance_properties(gc_context, PUBLIC_INSTANCE_PROPERTIES); write.define_public_builtin_instance_properties(gc_context, PUBLIC_INSTANCE_PROPERTIES);
class_class class_class

View File

@ -241,10 +241,7 @@ pub fn to_string<'gc>(
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
let object_proto = activation.avm2().prototypes().object; let object_proto = activation.avm2().prototypes().object;
object_proto object_proto
.get_property( .get_property(&QName::dynamic_name("toString").into(), activation)?
&QName::dynamic_name("toString").into(),
activation,
)?
.coerce_to_object(activation)? .coerce_to_object(activation)?
.call(this, args, activation) .call(this, args, activation)
} }

View File

@ -33,16 +33,10 @@ fn coords<'gc>(
activation: &mut Activation<'_, 'gc, '_>, activation: &mut Activation<'_, 'gc, '_>,
) -> Result<(f64, f64), Error> { ) -> Result<(f64, f64), Error> {
let x = this let x = this
.get_property( .get_property(&QName::new(Namespace::public(), "x").into(), activation)?
&QName::new(Namespace::public(), "x").into(),
activation,
)?
.coerce_to_number(activation)?; .coerce_to_number(activation)?;
let y = this let y = this
.get_property( .get_property(&QName::new(Namespace::public(), "y").into(), activation)?
&QName::new(Namespace::public(), "y").into(),
activation,
)?
.coerce_to_number(activation)?; .coerce_to_number(activation)?;
Ok((x, y)) Ok((x, y))
} }
@ -324,16 +318,10 @@ pub fn to_string<'gc>(
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if let Some(this) = this { if let Some(this) = this {
let x = this let x = this
.get_property( .get_property(&QName::new(Namespace::public(), "x").into(), activation)?
&QName::new(Namespace::public(), "x").into(),
activation,
)?
.coerce_to_string(activation)?; .coerce_to_string(activation)?;
let y = this let y = this
.get_property( .get_property(&QName::new(Namespace::public(), "y").into(), activation)?
&QName::new(Namespace::public(), "y").into(),
activation,
)?
.coerce_to_string(activation)?; .coerce_to_string(activation)?;
return Ok(AvmString::new_utf8( return Ok(AvmString::new_utf8(
activation.context.gc_context, activation.context.gc_context,

View File

@ -27,10 +27,7 @@ pub fn create_rectangle<'gc>(
macro_rules! get_prop { macro_rules! get_prop {
($this:expr, $activation:expr, $name: expr) => { ($this:expr, $activation:expr, $name: expr) => {
$this $this
.get_property( .get_property(&QName::new(Namespace::public(), $name).into(), $activation)?
&QName::new(Namespace::public(), $name).into(),
$activation,
)?
.coerce_to_number($activation) .coerce_to_number($activation)
}; };
} }
@ -454,18 +451,10 @@ pub fn copy_from<'gc>(
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if let Some(rect) = args.get(0) { if let Some(rect) = args.get(0) {
let rect = rect.coerce_to_object(activation)?; let rect = rect.coerce_to_object(activation)?;
let x = rect.get_property( let x = rect.get_property(&QName::new(Namespace::public(), "x").into(), activation)?;
&QName::new(Namespace::public(), "x").into(), let y = rect.get_property(&QName::new(Namespace::public(), "y").into(), activation)?;
activation, let width =
)?; rect.get_property(&QName::new(Namespace::public(), "width").into(), activation)?;
let y = rect.get_property(
&QName::new(Namespace::public(), "y").into(),
activation,
)?;
let width = rect.get_property(
&QName::new(Namespace::public(), "width").into(),
activation,
)?;
let height = rect.get_property( let height = rect.get_property(
&QName::new(Namespace::public(), "height").into(), &QName::new(Namespace::public(), "height").into(),
activation, activation,
@ -752,22 +741,13 @@ pub fn to_string<'gc>(
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if let Some(this) = this { if let Some(this) = this {
let x = this let x = this
.get_property( .get_property(&QName::new(Namespace::public(), "x").into(), activation)?
&QName::new(Namespace::public(), "x").into(),
activation,
)?
.coerce_to_string(activation)?; .coerce_to_string(activation)?;
let y = this let y = this
.get_property( .get_property(&QName::new(Namespace::public(), "y").into(), activation)?
&QName::new(Namespace::public(), "y").into(),
activation,
)?
.coerce_to_string(activation)?; .coerce_to_string(activation)?;
let width = this let width = this
.get_property( .get_property(&QName::new(Namespace::public(), "width").into(), activation)?
&QName::new(Namespace::public(), "width").into(),
activation,
)?
.coerce_to_string(activation)?; .coerce_to_string(activation)?;
let height = this let height = this
.get_property( .get_property(

View File

@ -44,7 +44,7 @@ pub fn class_init<'gc>(
Some(this_class), Some(this_class),
) )
.into(), .into(),
activation activation,
)?; )?;
function_proto.set_property_local( function_proto.set_property_local(
&Multiname::public("apply"), &Multiname::public("apply"),
@ -56,10 +56,18 @@ pub fn class_init<'gc>(
Some(this_class), Some(this_class),
) )
.into(), .into(),
activation activation,
)?;
function_proto.set_local_property_is_enumerable(
activation.context.gc_context,
"call".into(),
false,
)?;
function_proto.set_local_property_is_enumerable(
activation.context.gc_context,
"apply".into(),
false,
)?; )?;
function_proto.set_local_property_is_enumerable(activation.context.gc_context, "call".into(), false)?;
function_proto.set_local_property_is_enumerable(activation.context.gc_context, "apply".into(), false)?;
} }
Ok(Value::Undefined) Ok(Value::Undefined)
} }
@ -135,9 +143,9 @@ fn prototype<'gc>(
if let Some(this) = this { if let Some(this) = this {
if let Some(function) = this.as_function_object() { if let Some(function) = this.as_function_object() {
if let Some(proto) = function.prototype() { if let Some(proto) = function.prototype() {
return Ok(proto.into()) return Ok(proto.into());
} else { } else {
return Ok(Value::Undefined) return Ok(Value::Undefined);
} }
} }
} }
@ -174,10 +182,7 @@ pub fn create_class<'gc>(gc_context: MutationContext<'gc, '_>) -> GcCell<'gc, Cl
let mut write = function_class.write(gc_context); let mut write = function_class.write(gc_context);
// Fixed traits (in AS3 namespace) // Fixed traits (in AS3 namespace)
const AS3_INSTANCE_METHODS: &[(&str, NativeMethodImpl)] = &[ const AS3_INSTANCE_METHODS: &[(&str, NativeMethodImpl)] = &[("call", call), ("apply", apply)];
("call", call),
("apply", apply),
];
write.define_as3_builtin_instance_methods(gc_context, AS3_INSTANCE_METHODS); write.define_as3_builtin_instance_methods(gc_context, AS3_INSTANCE_METHODS);
const PUBLIC_INSTANCE_PROPERTIES: &[( const PUBLIC_INSTANCE_PROPERTIES: &[(

View File

@ -67,7 +67,7 @@ fn class_init<'gc>(
Some(this_class), Some(this_class),
) )
.into(), .into(),
activation activation,
)?; )?;
int_proto.set_property_local( int_proto.set_property_local(
&Multiname::public("toFixed"), &Multiname::public("toFixed"),
@ -79,7 +79,7 @@ fn class_init<'gc>(
Some(this_class), Some(this_class),
) )
.into(), .into(),
activation activation,
)?; )?;
int_proto.set_property_local( int_proto.set_property_local(
&Multiname::public("toPrecision"), &Multiname::public("toPrecision"),
@ -91,7 +91,7 @@ fn class_init<'gc>(
Some(this_class), Some(this_class),
) )
.into(), .into(),
activation activation,
)?; )?;
int_proto.set_property_local( int_proto.set_property_local(
&Multiname::public("toString"), &Multiname::public("toString"),
@ -103,7 +103,7 @@ fn class_init<'gc>(
Some(this_class), Some(this_class),
) )
.into(), .into(),
activation activation,
)?; )?;
int_proto.set_property_local( int_proto.set_property_local(
&Multiname::public("valueOf"), &Multiname::public("valueOf"),
@ -115,7 +115,7 @@ fn class_init<'gc>(
Some(this_class), Some(this_class),
) )
.into(), .into(),
activation activation,
)?; )?;
int_proto.set_local_property_is_enumerable(gc_context, "toExponential".into(), false)?; int_proto.set_local_property_is_enumerable(gc_context, "toExponential".into(), false)?;

View File

@ -66,7 +66,7 @@ fn class_init<'gc>(
Some(this_class), Some(this_class),
) )
.into(), .into(),
activation activation,
)?; )?;
number_proto.set_property_local( number_proto.set_property_local(
&Multiname::public("toFixed"), &Multiname::public("toFixed"),
@ -78,7 +78,7 @@ fn class_init<'gc>(
Some(this_class), Some(this_class),
) )
.into(), .into(),
activation activation,
)?; )?;
number_proto.set_property_local( number_proto.set_property_local(
&Multiname::public("toPrecision"), &Multiname::public("toPrecision"),
@ -90,7 +90,7 @@ fn class_init<'gc>(
Some(this_class), Some(this_class),
) )
.into(), .into(),
activation activation,
)?; )?;
number_proto.set_property_local( number_proto.set_property_local(
&Multiname::public("toString"), &Multiname::public("toString"),
@ -102,7 +102,7 @@ fn class_init<'gc>(
Some(this_class), Some(this_class),
) )
.into(), .into(),
activation activation,
)?; )?;
number_proto.set_property_local( number_proto.set_property_local(
&Multiname::public("valueOf"), &Multiname::public("valueOf"),
@ -114,7 +114,7 @@ fn class_init<'gc>(
Some(this_class), Some(this_class),
) )
.into(), .into(),
activation activation,
)?; )?;
number_proto.set_local_property_is_enumerable(gc_context, "toExponential".into(), false)?; number_proto.set_local_property_is_enumerable(gc_context, "toExponential".into(), false)?;
number_proto.set_local_property_is_enumerable(gc_context, "toFixed".into(), false)?; number_proto.set_local_property_is_enumerable(gc_context, "toFixed".into(), false)?;

View File

@ -120,12 +120,28 @@ pub fn class_init<'gc>(
activation, activation,
)?; )?;
object_proto.set_local_property_is_enumerable(gc_context, "hasOwnProperty".into(), false)?; object_proto.set_local_property_is_enumerable(
object_proto.set_local_property_is_enumerable(gc_context, "propertyIsEnumerable".into(), false)?; gc_context,
object_proto.set_local_property_is_enumerable(gc_context, "setPropertyIsEnumerable".into(), false)?; "hasOwnProperty".into(),
false,
)?;
object_proto.set_local_property_is_enumerable(
gc_context,
"propertyIsEnumerable".into(),
false,
)?;
object_proto.set_local_property_is_enumerable(
gc_context,
"setPropertyIsEnumerable".into(),
false,
)?;
object_proto.set_local_property_is_enumerable(gc_context, "isPrototypeOf".into(), false)?; object_proto.set_local_property_is_enumerable(gc_context, "isPrototypeOf".into(), false)?;
object_proto.set_local_property_is_enumerable(gc_context, "toString".into(), false)?; object_proto.set_local_property_is_enumerable(gc_context, "toString".into(), false)?;
object_proto.set_local_property_is_enumerable(gc_context, "toLocaleString".into(), false)?; object_proto.set_local_property_is_enumerable(
gc_context,
"toLocaleString".into(),
false,
)?;
object_proto.set_local_property_is_enumerable(gc_context, "valueOf".into(), false)?; object_proto.set_local_property_is_enumerable(gc_context, "valueOf".into(), false)?;
} }

View File

@ -94,8 +94,16 @@ pub fn class_init<'gc>(
activation, activation,
)?; )?;
qname_proto.set_local_property_is_enumerable(activation.context.gc_context, "toString".into(), false)?; qname_proto.set_local_property_is_enumerable(
qname_proto.set_local_property_is_enumerable(activation.context.gc_context, "valueOf".into(), false)?; activation.context.gc_context,
"toString".into(),
false,
)?;
qname_proto.set_local_property_is_enumerable(
activation.context.gc_context,
"valueOf".into(),
false,
)?;
Ok(Value::Undefined) Ok(Value::Undefined)
} }

View File

@ -67,7 +67,7 @@ fn class_init<'gc>(
Some(this_class), Some(this_class),
) )
.into(), .into(),
activation activation,
)?; )?;
uint_proto.set_property_local( uint_proto.set_property_local(
&Multiname::public("toFixed"), &Multiname::public("toFixed"),
@ -79,7 +79,7 @@ fn class_init<'gc>(
Some(this_class), Some(this_class),
) )
.into(), .into(),
activation activation,
)?; )?;
uint_proto.set_property_local( uint_proto.set_property_local(
&Multiname::public("toPrecision"), &Multiname::public("toPrecision"),
@ -91,7 +91,7 @@ fn class_init<'gc>(
Some(this_class), Some(this_class),
) )
.into(), .into(),
activation activation,
)?; )?;
uint_proto.set_property_local( uint_proto.set_property_local(
&Multiname::public("toString"), &Multiname::public("toString"),
@ -103,7 +103,7 @@ fn class_init<'gc>(
Some(this_class), Some(this_class),
) )
.into(), .into(),
activation activation,
)?; )?;
uint_proto.set_property_local( uint_proto.set_property_local(
&Multiname::public("valueOf"), &Multiname::public("valueOf"),
@ -115,7 +115,7 @@ fn class_init<'gc>(
Some(this_class), Some(this_class),
) )
.into(), .into(),
activation activation,
)?; )?;
uint_proto.set_local_property_is_enumerable(gc_context, "toExponential".into(), false)?; uint_proto.set_local_property_is_enumerable(gc_context, "toExponential".into(), false)?;
uint_proto.set_local_property_is_enumerable(gc_context, "toFixed".into(), false)?; uint_proto.set_local_property_is_enumerable(gc_context, "toFixed".into(), false)?;

View File

@ -170,7 +170,11 @@ pub fn specialized_class_init<'gc>(
.into(), .into(),
activation, activation,
)?; )?;
proto.set_local_property_is_enumerable(activation.context.gc_context, (*pubname).into(), false)?; proto.set_local_property_is_enumerable(
activation.context.gc_context,
(*pubname).into(),
false,
)?;
} }
} }

View File

@ -8,12 +8,12 @@ use crate::avm2::domain::Domain;
use crate::avm2::events::{DispatchList, Event}; use crate::avm2::events::{DispatchList, Event};
use crate::avm2::function::Executable; use crate::avm2::function::Executable;
use crate::avm2::names::{Multiname, Namespace, QName}; use crate::avm2::names::{Multiname, Namespace, QName};
use crate::avm2::property::Property;
use crate::avm2::regexp::RegExp; use crate::avm2::regexp::RegExp;
use crate::avm2::value::{Hint, Value}; use crate::avm2::value::{Hint, Value};
use crate::avm2::vector::VectorStorage; use crate::avm2::vector::VectorStorage;
use crate::avm2::property::Property;
use crate::avm2::Error;
use crate::avm2::vtable::VTable; use crate::avm2::vtable::VTable;
use crate::avm2::Error;
use crate::backend::audio::{SoundHandle, SoundInstanceHandle}; use crate::backend::audio::{SoundHandle, SoundInstanceHandle};
use crate::bitmap::bitmap_data::BitmapData; use crate::bitmap::bitmap_data::BitmapData;
use crate::display_object::DisplayObject; use crate::display_object::DisplayObject;
@ -141,14 +141,16 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
activation: &mut Activation<'_, 'gc, '_>, activation: &mut Activation<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
match self.vtable().and_then(|vtable| vtable.get_trait(multiname)) { match self.vtable().and_then(|vtable| vtable.get_trait(multiname)) {
Some(Property::Slot{slot_id}) | Some(Property::ConstSlot{slot_id}) Some(Property::Slot { slot_id }) | Some(Property::ConstSlot { slot_id }) => {
=> self.base().get_slot(slot_id), self.base().get_slot(slot_id)
}
Some(Property::Method { disp_id }) => { Some(Property::Method { disp_id }) => {
if let Some(bound_method) = self.base().get_bound_method(disp_id) { if let Some(bound_method) = self.base().get_bound_method(disp_id) {
return Ok(bound_method.into()); return Ok(bound_method.into());
} }
let vtable = self.vtable().unwrap(); let vtable = self.vtable().unwrap();
if let Some(bound_method) = vtable.make_bound_method(activation, self.into(), disp_id) if let Some(bound_method) =
vtable.make_bound_method(activation, self.into(), disp_id)
{ {
self.install_bound_method(activation.context.gc_context, disp_id, bound_method); self.install_bound_method(activation.context.gc_context, disp_id, bound_method);
Ok(bound_method.into()) Ok(bound_method.into())
@ -162,9 +164,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
Some(Property::Virtual { get: None, .. }) => { Some(Property::Virtual { get: None, .. }) => {
Err("Illegal read of write-only property".into()) Err("Illegal read of write-only property".into())
} }
None => { None => self.get_property_local(multiname, activation),
self.get_property_local(multiname, activation)
}
} }
} }
@ -197,22 +197,18 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
activation: &mut Activation<'_, 'gc, '_>, activation: &mut Activation<'_, 'gc, '_>,
) -> Result<(), Error> { ) -> Result<(), Error> {
match self.vtable().and_then(|vtable| vtable.get_trait(multiname)) { match self.vtable().and_then(|vtable| vtable.get_trait(multiname)) {
Some(Property::Slot{slot_id}) => self.base_mut(activation.context.gc_context).set_slot(slot_id, value, activation.context.gc_context), Some(Property::Slot { slot_id }) => self
Some(Property::ConstSlot{..}) => { .base_mut(activation.context.gc_context)
Err("Illegal write to read-only property".into()) .set_slot(slot_id, value, activation.context.gc_context),
} Some(Property::ConstSlot { .. }) => Err("Illegal write to read-only property".into()),
Some(Property::Method{..}) => { Some(Property::Method { .. }) => Err("Cannot assign to a method".into()),
Err("Cannot assign to a method".into())
}
Some(Property::Virtual { set: Some(set), .. }) => { Some(Property::Virtual { set: Some(set), .. }) => {
self.call_method(set, &[value], activation).map(|_| ()) self.call_method(set, &[value], activation).map(|_| ())
} }
Some(Property::Virtual { set: None, .. }) => { Some(Property::Virtual { set: None, .. }) => {
Err("Illegal write to read-only property".into()) Err("Illegal write to read-only property".into())
} }
None => { None => self.set_property_local(multiname, value, activation),
self.set_property_local(multiname, value, activation)
}
} }
} }
@ -246,23 +242,19 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
activation: &mut Activation<'_, 'gc, '_>, activation: &mut Activation<'_, 'gc, '_>,
) -> Result<(), Error> { ) -> Result<(), Error> {
match self.vtable().and_then(|vtable| vtable.get_trait(multiname)) { match self.vtable().and_then(|vtable| vtable.get_trait(multiname)) {
Some(Property::Slot{slot_id}) | Some(Property::ConstSlot{slot_id}) Some(Property::Slot { slot_id }) | Some(Property::ConstSlot { slot_id }) => self
=> self.base_mut(activation.context.gc_context).set_slot(slot_id, value, activation.context.gc_context), .base_mut(activation.context.gc_context)
Some(Property::Method{..}) => { .set_slot(slot_id, value, activation.context.gc_context),
Err("Cannot assign to a method".into()) Some(Property::Method { .. }) => Err("Cannot assign to a method".into()),
}
Some(Property::Virtual { set: Some(set), .. }) => { Some(Property::Virtual { set: Some(set), .. }) => {
self.call_method(set, &[value], activation).map(|_| ()) self.call_method(set, &[value], activation).map(|_| ())
} }
Some(Property::Virtual { set: None, .. }) => { Some(Property::Virtual { set: None, .. }) => {
Err("Illegal write to read-only property".into()) Err("Illegal write to read-only property".into())
} }
None => { None => self.init_property_local(multiname, value, activation),
self.init_property_local(multiname, value, activation)
} }
} }
}
/// Call a local property of the object. The Multiname should always be public. /// Call a local property of the object. The Multiname should always be public.
/// ///
@ -279,7 +271,9 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
// Note: normally this would just call into ScriptObjectData::call_property_local // Note: normally this would just call into ScriptObjectData::call_property_local
// but because calling into ScriptObjectData borrows it for entire duration, // but because calling into ScriptObjectData borrows it for entire duration,
// we run a risk of a double borrow if the inner call borrows again. // we run a risk of a double borrow if the inner call borrows again.
let result = self.base().get_property_local(multiname, activation)? let result = self
.base()
.get_property_local(multiname, activation)?
.coerce_to_object(activation)?; .coerce_to_object(activation)?;
result.call(Some(self.into()), arguments, activation) result.call(Some(self.into()), arguments, activation)
} }
@ -298,14 +292,15 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
match self.vtable().and_then(|vtable| vtable.get_trait(multiname)) { match self.vtable().and_then(|vtable| vtable.get_trait(multiname)) {
Some(Property::Slot { slot_id }) | Some(Property::ConstSlot { slot_id }) => { Some(Property::Slot { slot_id }) | Some(Property::ConstSlot { slot_id }) => {
let obj = self.base().get_slot(slot_id)? let obj = self
.base()
.get_slot(slot_id)?
.coerce_to_object(activation)?; .coerce_to_object(activation)?;
obj.call(Some(self.into()), arguments, activation) obj.call(Some(self.into()), arguments, activation)
} }
Some(Property::Method { disp_id }) => { Some(Property::Method { disp_id }) => {
let vtable = self.vtable().unwrap(); let vtable = self.vtable().unwrap();
if let Some((superclass, method)) = vtable.get_full_method(disp_id) if let Some((superclass, method)) = vtable.get_full_method(disp_id) {
{
if !method.needs_arguments_object() { if !method.needs_arguments_object() {
let scope = superclass.unwrap().instance_scope(); let scope = superclass.unwrap().instance_scope();
Executable::from_method(method, scope, None, superclass).exec( Executable::from_method(method, scope, None, superclass).exec(
@ -318,25 +313,30 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
if let Some(bound_method) = self.base().get_bound_method(disp_id) { if let Some(bound_method) = self.base().get_bound_method(disp_id) {
return bound_method.call(Some(self.into()), arguments, activation); return bound_method.call(Some(self.into()), arguments, activation);
} }
let bound_method = vtable.make_bound_method(activation, self.into(), disp_id).unwrap(); let bound_method = vtable
self.install_bound_method(activation.context.gc_context, disp_id, bound_method); .make_bound_method(activation, self.into(), disp_id)
.unwrap();
self.install_bound_method(
activation.context.gc_context,
disp_id,
bound_method,
);
bound_method.call(Some(self.into()), arguments, activation) bound_method.call(Some(self.into()), arguments, activation)
} }
} else { } else {
Err("Method not found".into()) Err("Method not found".into())
} }
}, }
Some(Property::Virtual { get: Some(get), .. }) => { Some(Property::Virtual { get: Some(get), .. }) => {
let obj = self.call_method(get, &[], activation)? let obj = self
.call_method(get, &[], activation)?
.coerce_to_object(activation)?; .coerce_to_object(activation)?;
obj.call(Some(self.into()), arguments, activation) obj.call(Some(self.into()), arguments, activation)
} }
Some(Property::Virtual { get: None, .. }) => { Some(Property::Virtual { get: None, .. }) => {
Err("Illegal read of write-only property".into()) Err("Illegal read of write-only property".into())
} }
None => { None => self.call_property_local(multiname, arguments, activation),
self.call_property_local(multiname, arguments, activation)
}
} }
} }
@ -383,9 +383,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if self.base().get_bound_method(id).is_none() { if self.base().get_bound_method(id).is_none() {
if let Some(vtable) = self.vtable() { if let Some(vtable) = self.vtable() {
if let Some(bound_method) = if let Some(bound_method) = vtable.make_bound_method(activation, self.into(), id) {
vtable.make_bound_method(activation, self.into(), id)
{
self.install_bound_method(activation.context.gc_context, id, bound_method); self.install_bound_method(activation.context.gc_context, id, bound_method);
} }
} }
@ -459,15 +457,17 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
) -> Result<bool, Error> { ) -> Result<bool, Error> {
match self.vtable().and_then(|vtable| vtable.get_trait(multiname)) { match self.vtable().and_then(|vtable| vtable.get_trait(multiname)) {
None => { None => {
if self.instance_of_class_definition() if self
.instance_of_class_definition()
.map(|c| c.read().is_sealed()) .map(|c| c.read().is_sealed())
.unwrap_or(false) { .unwrap_or(false)
{
Ok(false) Ok(false)
} else { } else {
self.delete_property_local(activation, multiname) self.delete_property_local(activation, multiname)
} }
}, }
_ => Ok(false) _ => Ok(false),
} }
} }
@ -578,7 +578,6 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
base.install_bound_method(disp_id, function) base.install_bound_method(disp_id, function)
} }
/// Install a const trait on the global object. /// Install a const trait on the global object.
/// This should only ever be called on the `global` object, during initialization. /// This should only ever be called on the `global` object, during initialization.
fn install_const_late( fn install_const_late(
@ -587,16 +586,17 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
name: QName<'gc>, name: QName<'gc>,
value: Value<'gc>, value: Value<'gc>,
) { ) {
let new_slot_id = self.vtable().unwrap().install_const_trait_late(mc, name, value.clone()); let new_slot_id = self
self.base_mut(mc).install_const_slot_late(new_slot_id, value); .vtable()
.unwrap()
.install_const_trait_late(mc, name, value.clone());
self.base_mut(mc)
.install_const_slot_late(new_slot_id, value);
} }
fn install_instance_slots(&mut self, activation: &mut Activation<'_, 'gc, '_>) {
fn install_instance_slots( self.base_mut(activation.context.gc_context)
&mut self, .install_instance_slots();
activation: &mut Activation<'_, 'gc, '_>,
) {
self.base_mut(activation.context.gc_context).install_instance_slots();
} }
/// Call the object. /// Call the object.

View File

@ -2,7 +2,7 @@
use crate::avm2::activation::Activation; use crate::avm2::activation::Activation;
use crate::avm2::array::ArrayStorage; use crate::avm2::array::ArrayStorage;
use crate::avm2::names::{Multiname}; use crate::avm2::names::Multiname;
use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::script_object::ScriptObjectData;
use crate::avm2::object::{ClassObject, Object, ObjectPtr, TObject}; use crate::avm2::object::{ClassObject, Object, ObjectPtr, TObject};
use crate::avm2::value::Value; use crate::avm2::value::Value;
@ -155,13 +155,20 @@ impl<'gc> TObject<'gc> for ArrayObject<'gc> {
if name.contains_public_namespace() { if name.contains_public_namespace() {
if let Some(name) = name.local_name() { if let Some(name) = name.local_name() {
if let Ok(index) = name.parse::<usize>() { if let Ok(index) = name.parse::<usize>() {
self.0.write(activation.context.gc_context).array.delete(index); self.0
.write(activation.context.gc_context)
.array
.delete(index);
return Ok(true); return Ok(true);
} }
} }
} }
Ok(self.0.write(activation.context.gc_context).base.delete_property_local(name)) Ok(self
.0
.write(activation.context.gc_context)
.base
.delete_property_local(name))
} }
fn has_own_property(self, name: &Multiname<'gc>) -> bool { fn has_own_property(self, name: &Multiname<'gc>) -> bool {

View File

@ -1,6 +1,6 @@
use crate::avm2::activation::Activation; use crate::avm2::activation::Activation;
use crate::avm2::bytearray::ByteArrayStorage; use crate::avm2::bytearray::ByteArrayStorage;
use crate::avm2::names::{Multiname}; use crate::avm2::names::Multiname;
use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::script_object::ScriptObjectData;
use crate::avm2::object::{ClassObject, Object, ObjectPtr, TObject}; use crate::avm2::object::{ClassObject, Object, ObjectPtr, TObject};
use crate::avm2::value::Value; use crate::avm2::value::Value;
@ -153,13 +153,20 @@ impl<'gc> TObject<'gc> for ByteArrayObject<'gc> {
if name.contains_public_namespace() { if name.contains_public_namespace() {
if let Some(name) = name.local_name() { if let Some(name) = name.local_name() {
if let Ok(index) = name.parse::<usize>() { if let Ok(index) = name.parse::<usize>() {
self.0.write(activation.context.gc_context).storage.delete(index); self.0
.write(activation.context.gc_context)
.storage
.delete(index);
return Ok(true); return Ok(true);
} }
} }
} }
Ok(self.0.write(activation.context.gc_context).base.delete_property_local(name)) Ok(self
.0
.write(activation.context.gc_context)
.base
.delete_property_local(name))
} }
fn has_own_property(self, name: &Multiname<'gc>) -> bool { fn has_own_property(self, name: &Multiname<'gc>) -> bool {

View File

@ -4,7 +4,7 @@ use crate::avm2::activation::Activation;
use crate::avm2::class::{Allocator, AllocatorFn, Class}; use crate::avm2::class::{Allocator, AllocatorFn, Class};
use crate::avm2::function::Executable; use crate::avm2::function::Executable;
use crate::avm2::method::Method; use crate::avm2::method::Method;
use crate::avm2::names::{QName}; use crate::avm2::names::QName;
use crate::avm2::object::function_object::FunctionObject; use crate::avm2::object::function_object::FunctionObject;
use crate::avm2::object::script_object::{scriptobject_allocator, ScriptObjectData}; use crate::avm2::object::script_object::{scriptobject_allocator, ScriptObjectData};
use crate::avm2::object::{Multiname, Object, ObjectPtr, TObject}; use crate::avm2::object::{Multiname, Object, ObjectPtr, TObject};
@ -101,7 +101,11 @@ impl<'gc> ClassObject<'gc> {
activation: &mut Activation<'_, 'gc, '_>, activation: &mut Activation<'_, 'gc, '_>,
superclass_object: Option<ClassObject<'gc>>, superclass_object: Option<ClassObject<'gc>>,
) -> Result<Object<'gc>, Error> { ) -> Result<Object<'gc>, Error> {
let proto = activation.avm2().classes().object.construct(activation, &[])?; let proto = activation
.avm2()
.classes()
.object
.construct(activation, &[])?;
if let Some(superclass_object) = superclass_object { if let Some(superclass_object) = superclass_object {
let base_proto = superclass_object.prototype(); let base_proto = superclass_object.prototype();
@ -244,7 +248,7 @@ impl<'gc> ClassObject<'gc> {
class.read().instance_traits(), class.read().instance_traits(),
self.instance_scope(), self.instance_scope(),
self.superclass_object().map(|cls| cls.instance_vtable()), self.superclass_object().map(|cls| cls.instance_vtable()),
activation activation,
)?; )?;
// class vtable == class traits + Class instance traits // class vtable == class traits + Class instance traits
@ -253,7 +257,7 @@ impl<'gc> ClassObject<'gc> {
class.read().class_traits(), class.read().class_traits(),
self.class_scope(), self.class_scope(),
Some(self.instance_of().unwrap().instance_vtable()), Some(self.instance_of().unwrap().instance_vtable()),
activation activation,
)?; )?;
self.link_interfaces(activation)?; self.link_interfaces(activation)?;
@ -265,7 +269,8 @@ impl<'gc> ClassObject<'gc> {
fn install_class_vtable_and_slots(&mut self, activation: &mut Activation<'_, 'gc, '_>) { fn install_class_vtable_and_slots(&mut self, activation: &mut Activation<'_, 'gc, '_>) {
self.set_vtable(activation.context.gc_context, self.class_vtable()); self.set_vtable(activation.context.gc_context, self.class_vtable());
self.base_mut(activation.context.gc_context).install_instance_slots(); self.base_mut(activation.context.gc_context)
.install_instance_slots();
} }
/// Link this class to a prototype. /// Link this class to a prototype.
@ -278,12 +283,12 @@ impl<'gc> ClassObject<'gc> {
class_proto.set_property_local( class_proto.set_property_local(
&Multiname::public("constructor"), &Multiname::public("constructor"),
self.into(), self.into(),
activation activation,
)?; )?;
class_proto.set_local_property_is_enumerable( class_proto.set_local_property_is_enumerable(
activation.context.gc_context, activation.context.gc_context,
"constructor".into(), "constructor".into(),
false false,
)?; )?;
Ok(()) Ok(())
@ -347,7 +352,7 @@ impl<'gc> ClassObject<'gc> {
self.instance_vtable().copy_property_for_interface( self.instance_vtable().copy_property_for_interface(
activation.context.gc_context, activation.context.gc_context,
public_name, public_name,
interface_trait.name() interface_trait.name(),
); );
} }
} }
@ -535,7 +540,8 @@ impl<'gc> ClassObject<'gc> {
} }
if let Some(Property::Method { disp_id, .. }) = property { if let Some(Property::Method { disp_id, .. }) = property {
// todo: handle errors // todo: handle errors
let (superclass_object, method) = self.instance_vtable().get_full_method(disp_id).unwrap(); let (superclass_object, method) =
self.instance_vtable().get_full_method(disp_id).unwrap();
let scope = superclass_object.unwrap().class_scope(); let scope = superclass_object.unwrap().class_scope();
let callee = FunctionObject::from_method( let callee = FunctionObject::from_method(
activation, activation,
@ -589,9 +595,13 @@ impl<'gc> ClassObject<'gc> {
) )
.into()); .into());
} }
if let Some(Property::Virtual{get: Some(disp_id), ..}) = property { if let Some(Property::Virtual {
get: Some(disp_id), ..
}) = property
{
// todo: handle errors // todo: handle errors
let (superclass_object, method) = self.instance_vtable().get_full_method(disp_id).unwrap(); let (superclass_object, method) =
self.instance_vtable().get_full_method(disp_id).unwrap();
let scope = superclass_object.unwrap().class_scope(); let scope = superclass_object.unwrap().class_scope();
let callee = FunctionObject::from_method( let callee = FunctionObject::from_method(
activation, activation,
@ -647,9 +657,13 @@ impl<'gc> ClassObject<'gc> {
) )
.into()); .into());
} }
if let Some(Property::Virtual{set: Some(disp_id), ..}) = property { if let Some(Property::Virtual {
set: Some(disp_id), ..
}) = property
{
// todo: handle errors // todo: handle errors
let (superclass_object, method) = self.instance_vtable().get_full_method(disp_id).unwrap(); let (superclass_object, method) =
self.instance_vtable().get_full_method(disp_id).unwrap();
let scope = superclass_object.unwrap().class_scope(); let scope = superclass_object.unwrap().class_scope();
let callee = FunctionObject::from_method( let callee = FunctionObject::from_method(
activation, activation,
@ -778,8 +792,7 @@ impl<'gc> TObject<'gc> for ClassObject<'gc> {
fn has_own_property(self, name: &Multiname<'gc>) -> bool { fn has_own_property(self, name: &Multiname<'gc>) -> bool {
let read = self.0.read(); let read = self.0.read();
read.base.has_own_dynamic_property(name) read.base.has_own_dynamic_property(name) || self.class_vtable().has_trait(name)
|| self.class_vtable().has_trait(name)
} }
fn as_class_object(&self) -> Option<ClassObject<'gc>> { fn as_class_object(&self) -> Option<ClassObject<'gc>> {
@ -891,8 +904,10 @@ impl<'gc> TObject<'gc> for ClassObject<'gc> {
Some(class_object), Some(class_object),
parameterized_class.read().instance_traits(), parameterized_class.read().instance_traits(),
class_object.instance_scope(), class_object.instance_scope(),
class_object.superclass_object().map(|cls| cls.instance_vtable()), class_object
activation .superclass_object()
.map(|cls| cls.instance_vtable()),
activation,
)?; )?;
// class vtable == class traits + Class instance traits // class vtable == class traits + Class instance traits
@ -901,7 +916,7 @@ impl<'gc> TObject<'gc> for ClassObject<'gc> {
parameterized_class.read().class_traits(), parameterized_class.read().class_traits(),
class_object.class_scope(), class_object.class_scope(),
Some(class_object.instance_of().unwrap().instance_vtable()), Some(class_object.instance_of().unwrap().instance_vtable()),
activation activation,
)?; )?;
class_object.link_prototype(activation, class_proto)?; class_object.link_prototype(activation, class_proto)?;

View File

@ -151,7 +151,11 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
Ok(FunctionObject(GcCell::allocate( Ok(FunctionObject(GcCell::allocate(
activation.context.gc_context, activation.context.gc_context,
// todo: should this be None? // todo: should this be None?
FunctionObjectData { base, exec, prototype: None }, FunctionObjectData {
base,
exec,
prototype: None,
},
)) ))
.into()) .into())
} }

View File

@ -1,16 +1,16 @@
//! Default AVM2 object impl //! Default AVM2 object impl
use crate::avm2::activation::Activation; use crate::avm2::activation::Activation;
use crate::avm2::names::{Multiname}; use crate::avm2::names::Multiname;
use crate::avm2::object::{FunctionObject, ClassObject, Object, ObjectPtr, TObject}; use crate::avm2::object::{ClassObject, FunctionObject, Object, ObjectPtr, TObject};
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::Error;
use crate::avm2::vtable::VTable; use crate::avm2::vtable::VTable;
use crate::avm2::Error;
use crate::string::AvmString; use crate::string::AvmString;
use fnv::FnvHashMap; use fnv::FnvHashMap;
use std::collections::hash_map::Entry;
use gc_arena::{Collect, GcCell, MutationContext}; use gc_arena::{Collect, GcCell, MutationContext};
use std::cell::{Ref, RefMut}; use std::cell::{Ref, RefMut};
use std::collections::hash_map::Entry;
use std::fmt::Debug; use std::fmt::Debug;
/// A class instance allocator that allocates `ScriptObject`s. /// A class instance allocator that allocates `ScriptObject`s.
@ -153,7 +153,6 @@ impl<'gc> ScriptObjectData<'gc> {
let value = self.values.get(&local_name); let value = self.values.get(&local_name);
if let Some(value) = value { if let Some(value) = value {
return Ok(value.clone()); return Ok(value.clone());
} else { } else {
@ -165,7 +164,8 @@ impl<'gc> ScriptObjectData<'gc> {
// Special case: Unresolvable properties on dynamic classes are treated // Special case: Unresolvable properties on dynamic classes are treated
// as dynamic properties that have not yet been set, and yield // as dynamic properties that have not yet been set, and yield
// `undefined` // `undefined`
if self.instance_of() if self
.instance_of()
.map(|cls| cls.inner_class_definition().read().is_sealed()) .map(|cls| cls.inner_class_definition().read().is_sealed())
.unwrap_or(false) .unwrap_or(false)
{ {
@ -181,11 +181,14 @@ impl<'gc> ScriptObjectData<'gc> {
value: Value<'gc>, value: Value<'gc>,
_activation: &mut Activation<'_, 'gc, '_>, _activation: &mut Activation<'_, 'gc, '_>,
) -> Result<(), Error> { ) -> Result<(), Error> {
if self.instance_of() if self
.instance_of()
.map(|cls| cls.inner_class_definition().read().is_sealed()) .map(|cls| cls.inner_class_definition().read().is_sealed())
.unwrap_or(false) .unwrap_or(false)
{ {
return Err(format!("Cannot set undefined property {:?}", multiname.local_name()).into()); return Err(
format!("Cannot set undefined property {:?}", multiname.local_name()).into(),
);
} }
if !multiname.contains_public_namespace() { if !multiname.contains_public_namespace() {
@ -198,7 +201,9 @@ impl<'gc> ScriptObjectData<'gc> {
}; };
match self.values.entry(local_name) { match self.values.entry(local_name) {
Entry::Occupied(mut o) => { o.insert(value); }, Entry::Occupied(mut o) => {
o.insert(value);
}
Entry::Vacant(v) => { Entry::Vacant(v) => {
//TODO: Not all classes are dynamic like this //TODO: Not all classes are dynamic like this
self.enumerants.push(local_name); self.enumerants.push(local_name);
@ -281,11 +286,7 @@ impl<'gc> ScriptObjectData<'gc> {
/// Set a slot by its index. This does extend the array if needed. /// Set a slot by its index. This does extend the array if needed.
/// This should only be used during AVM initialization, not at runtime. /// This should only be used during AVM initialization, not at runtime.
pub fn install_const_slot_late( pub fn install_const_slot_late(&mut self, id: u32, value: Value<'gc>) {
&mut self,
id: u32,
value: Value<'gc>,
) {
if self.slots.len() < id as usize + 1 { if self.slots.len() < id as usize + 1 {
self.slots.resize(id as usize + 1, Value::Undefined); self.slots.resize(id as usize + 1, Value::Undefined);
} }
@ -348,10 +349,7 @@ impl<'gc> ScriptObjectData<'gc> {
// sentinel. // sentinel.
let true_index = (index as usize).checked_sub(1)?; let true_index = (index as usize).checked_sub(1)?;
self.enumerants self.enumerants.get(true_index).cloned().map(|q| q.into())
.get(true_index)
.cloned()
.map(|q| q.into())
} }
pub fn property_is_enumerable(&self, name: AvmString<'gc>) -> bool { pub fn property_is_enumerable(&self, name: AvmString<'gc>) -> bool {
@ -399,8 +397,6 @@ impl<'gc> ScriptObjectData<'gc> {
*self.bound_methods.get_mut(disp_id as usize).unwrap() = Some(function); *self.bound_methods.get_mut(disp_id as usize).unwrap() = Some(function);
} }
/// Get the class object for this object, if it has one. /// Get the class object for this object, if it has one.
pub fn instance_of(&self) -> Option<ClassObject<'gc>> { pub fn instance_of(&self) -> Option<ClassObject<'gc>> {
self.instance_of self.instance_of

View File

@ -1,13 +1,13 @@
//! Vector storage object //! Vector storage object
use crate::avm2::activation::Activation; use crate::avm2::activation::Activation;
use crate::avm2::names::{Multiname}; use crate::avm2::names::Multiname;
use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::script_object::ScriptObjectData;
use crate::avm2::object::{ClassObject, Object, ObjectPtr, TObject}; use crate::avm2::object::{ClassObject, Object, ObjectPtr, TObject};
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::string::AvmString;
use crate::avm2::vector::VectorStorage; use crate::avm2::vector::VectorStorage;
use crate::avm2::Error; use crate::avm2::Error;
use crate::string::AvmString;
use gc_arena::{Collect, GcCell, MutationContext}; use gc_arena::{Collect, GcCell, MutationContext};
use std::cell::{Ref, RefMut}; use std::cell::{Ref, RefMut};
@ -179,11 +179,16 @@ impl<'gc> TObject<'gc> for VectorObject<'gc> {
) -> Result<bool, Error> { ) -> Result<bool, Error> {
if name.contains_public_namespace() if name.contains_public_namespace()
&& name.local_name().is_some() && name.local_name().is_some()
&& name.local_name().unwrap().parse::<usize>().is_ok() { && name.local_name().unwrap().parse::<usize>().is_ok()
{
return Ok(true); return Ok(true);
} }
Ok(self.0.write(activation.context.gc_context).base.delete_property_local(name)) Ok(self
.0
.write(activation.context.gc_context)
.base
.delete_property_local(name))
} }
fn has_own_property(self, name: &Multiname<'gc>) -> bool { fn has_own_property(self, name: &Multiname<'gc>) -> bool {

View File

@ -5,19 +5,10 @@ use gc_arena::Collect;
#[derive(Debug, Collect, Clone, Copy)] #[derive(Debug, Collect, Clone, Copy)]
#[collect(require_static)] #[collect(require_static)]
pub enum Property { pub enum Property {
Virtual { Virtual { get: Option<u32>, set: Option<u32> },
get: Option<u32>, Method { disp_id: u32 },
set: Option<u32>, Slot { slot_id: u32 },
}, ConstSlot { slot_id: u32 },
Method {
disp_id: u32,
},
Slot {
slot_id: u32,
},
ConstSlot {
slot_id: u32,
},
} }
impl Property { impl Property {
@ -26,11 +17,17 @@ impl Property {
} }
pub fn new_getter(disp_id: u32) -> Self { pub fn new_getter(disp_id: u32) -> Self {
Property::Virtual { get: Some(disp_id), set: None, } Property::Virtual {
get: Some(disp_id),
set: None,
}
} }
pub fn new_setter(disp_id: u32) -> Self { pub fn new_setter(disp_id: u32) -> Self {
Property::Virtual { get: None, set: Some(disp_id) } Property::Virtual {
get: None,
set: Some(disp_id),
}
} }
pub fn new_slot(slot_id: u32) -> Self { pub fn new_slot(slot_id: u32) -> Self {

View File

@ -2,7 +2,7 @@
use crate::avm2::activation::Activation; use crate::avm2::activation::Activation;
use crate::avm2::domain::Domain; use crate::avm2::domain::Domain;
use crate::avm2::names::{Multiname}; use crate::avm2::names::Multiname;
use crate::avm2::object::{Object, TObject}; use crate::avm2::object::{Object, TObject};
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::Error; use crate::avm2::Error;
@ -198,7 +198,11 @@ impl<'gc> ScopeStack<'gc> {
/// The `global` parameter indicates whether we are on global$init (script initializer). /// The `global` parameter indicates whether we are on global$init (script initializer).
/// When the `global` parameter is true, the scope at depth 0 is considered the global scope, and is /// When the `global` parameter is true, the scope at depth 0 is considered the global scope, and is
/// searched for dynamic properties. /// searched for dynamic properties.
pub fn find(&self, multiname: &Multiname<'gc>, global: bool) -> Result<Option<Object<'gc>>, Error> { pub fn find(
&self,
multiname: &Multiname<'gc>,
global: bool,
) -> Result<Option<Object<'gc>>, Error> {
for (depth, scope) in self.scopes.iter().enumerate().rev() { for (depth, scope) in self.scopes.iter().enumerate().rev() {
let values = scope.values(); let values = scope.values();

View File

@ -5,9 +5,9 @@ use crate::avm2::class::Class;
use crate::avm2::domain::Domain; use crate::avm2::domain::Domain;
use crate::avm2::method::{BytecodeMethod, Method}; use crate::avm2::method::{BytecodeMethod, Method};
use crate::avm2::object::{Object, TObject}; use crate::avm2::object::{Object, TObject};
use crate::avm2::scope::ScopeChain;
use crate::avm2::traits::Trait; use crate::avm2::traits::Trait;
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::scope::ScopeChain;
use crate::avm2::{Avm2, Error}; use crate::avm2::{Avm2, Error};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use crate::string::AvmString; use crate::string::AvmString;
@ -371,7 +371,6 @@ impl<'gc> Script<'gc> {
) -> Result<Object<'gc>, Error> { ) -> Result<Object<'gc>, Error> {
let mut write = self.0.write(context.gc_context); let mut write = self.0.write(context.gc_context);
if !write.initialized { if !write.initialized {
write.initialized = true; write.initialized = true;

View File

@ -1,17 +1,16 @@
use crate::avm2::activation::Activation; use crate::avm2::activation::Activation;
use crate::avm2::property_map::PropertyMap; use crate::avm2::method::Method;
use crate::avm2::names::{Multiname, QName};
use crate::avm2::object::{ClassObject, FunctionObject, Object};
use crate::avm2::property::Property; use crate::avm2::property::Property;
use crate::avm2::value::Value; use crate::avm2::property_map::PropertyMap;
use crate::avm2::scope::ScopeChain; use crate::avm2::scope::ScopeChain;
use crate::avm2::traits::{Trait, TraitKind}; use crate::avm2::traits::{Trait, TraitKind};
use crate::avm2::value::Value;
use crate::avm2::Error; use crate::avm2::Error;
use crate::avm2::method::Method;
use crate::avm2::names::{QName, Multiname};
use crate::avm2::object::{FunctionObject, ClassObject, Object};
use gc_arena::{Collect, GcCell, MutationContext}; use gc_arena::{Collect, GcCell, MutationContext};
use std::ops::DerefMut;
use std::cell::Ref; use std::cell::Ref;
use std::ops::DerefMut;
#[derive(Collect, Debug, Clone, Copy)] #[derive(Collect, Debug, Clone, Copy)]
#[collect(no_drop)] #[collect(no_drop)]
@ -58,7 +57,12 @@ impl<'gc> VTable<'gc> {
} }
pub fn get_method(self, disp_id: u32) -> Option<Method<'gc>> { pub fn get_method(self, disp_id: u32) -> Option<Method<'gc>> {
self.0.read().method_table.get(disp_id as usize).cloned().map(|x| x.1) self.0
.read()
.method_table
.get(disp_id as usize)
.cloned()
.map(|x| x.1)
} }
pub fn get_full_method(self, disp_id: u32) -> Option<(Option<ClassObject<'gc>>, Method<'gc>)> { pub fn get_full_method(self, disp_id: u32) -> Option<(Option<ClassObject<'gc>>, Method<'gc>)> {
@ -82,7 +86,6 @@ impl<'gc> VTable<'gc> {
superclass_vtable: Option<Self>, superclass_vtable: Option<Self>,
activation: &mut Activation<'_, 'gc, '_>, activation: &mut Activation<'_, 'gc, '_>,
) -> Result<(), Error> { ) -> Result<(), Error> {
// Let's talk about slot_ids and disp_ids. // Let's talk about slot_ids and disp_ids.
// Specification is one thing, but reality is another. // Specification is one thing, but reality is another.
@ -134,11 +137,9 @@ impl<'gc> VTable<'gc> {
// so long-term it's still something we should verify. // so long-term it's still something we should verify.
// (and it's far from the only verification check we lack anyway) // (and it's far from the only verification check we lack anyway)
let mut write = self.0.write(activation.context.gc_context); let mut write = self.0.write(activation.context.gc_context);
let write = write.deref_mut(); let write = write.deref_mut();
write.defining_class = defining_class; write.defining_class = defining_class;
write.scope = Some(scope); write.scope = Some(scope);
@ -148,8 +149,11 @@ impl<'gc> VTable<'gc> {
write.default_slots = superclass_vtable.0.read().default_slots.clone(); write.default_slots = superclass_vtable.0.read().default_slots.clone();
} }
let (resolved_traits, method_table, default_slots) = let (resolved_traits, method_table, default_slots) = (
(&mut write.resolved_traits, &mut write.method_table, &mut write.default_slots); &mut write.resolved_traits,
&mut write.method_table,
&mut write.default_slots,
);
for trait_data in traits { for trait_data in traits {
match trait_data.kind() { match trait_data.kind() {
@ -159,53 +163,60 @@ impl<'gc> VTable<'gc> {
Some(Property::Method { disp_id, .. }) => { Some(Property::Method { disp_id, .. }) => {
let disp_id = *disp_id as usize; let disp_id = *disp_id as usize;
method_table[disp_id] = entry; method_table[disp_id] = entry;
}, }
// note: ideally overwriting other property types // note: ideally overwriting other property types
// should be a VerifyError // should be a VerifyError
_ => { _ => {
let disp_id = method_table.len() as u32; let disp_id = method_table.len() as u32;
method_table.push(entry); method_table.push(entry);
resolved_traits.insert(trait_data.name(), Property::new_method(disp_id)); resolved_traits
.insert(trait_data.name(), Property::new_method(disp_id));
} }
} }
} }
TraitKind::Getter { method, .. } => { TraitKind::Getter { method, .. } => {
let entry = (defining_class, method.clone()); let entry = (defining_class, method.clone());
match resolved_traits.get_mut(trait_data.name()) { match resolved_traits.get_mut(trait_data.name()) {
Some(Property::Virtual{get: Some(disp_id), ..}) => { Some(Property::Virtual {
get: Some(disp_id), ..
}) => {
let disp_id = *disp_id as usize; let disp_id = *disp_id as usize;
method_table[disp_id] = entry; method_table[disp_id] = entry;
}, }
Some(Property::Virtual { get, .. }) => { Some(Property::Virtual { get, .. }) => {
let disp_id = method_table.len() as u32; let disp_id = method_table.len() as u32;
*get = Some(disp_id); *get = Some(disp_id);
method_table.push(entry); method_table.push(entry);
}, }
_ => { _ => {
let disp_id = method_table.len() as u32; let disp_id = method_table.len() as u32;
method_table.push(entry); method_table.push(entry);
resolved_traits.insert(trait_data.name(), Property::new_getter(disp_id)); resolved_traits
.insert(trait_data.name(), Property::new_getter(disp_id));
}
} }
} }
},
TraitKind::Setter { method, .. } => { TraitKind::Setter { method, .. } => {
let entry = (defining_class, method.clone()); let entry = (defining_class, method.clone());
match resolved_traits.get_mut(trait_data.name()) { match resolved_traits.get_mut(trait_data.name()) {
Some(Property::Virtual{set: Some(disp_id), ..}) => { Some(Property::Virtual {
set: Some(disp_id), ..
}) => {
method_table[*disp_id as usize] = entry; method_table[*disp_id as usize] = entry;
}, }
Some(Property::Virtual { set, .. }) => { Some(Property::Virtual { set, .. }) => {
let disp_id = method_table.len() as u32; let disp_id = method_table.len() as u32;
method_table.push(entry); method_table.push(entry);
*set = Some(disp_id); *set = Some(disp_id);
}, }
_ => { _ => {
let disp_id = method_table.len() as u32; let disp_id = method_table.len() as u32;
method_table.push(entry); method_table.push(entry);
resolved_traits.insert(trait_data.name(), Property::new_setter(disp_id)); resolved_traits
.insert(trait_data.name(), Property::new_setter(disp_id));
}
} }
} }
},
TraitKind::Slot { slot_id, .. } TraitKind::Slot { slot_id, .. }
| TraitKind::Const { slot_id, .. } | TraitKind::Const { slot_id, .. }
| TraitKind::Function { slot_id, .. } | TraitKind::Function { slot_id, .. }
@ -233,10 +244,12 @@ impl<'gc> VTable<'gc> {
} as u32; } as u32;
let new_prop = match trait_data.kind() { let new_prop = match trait_data.kind() {
TraitKind::Slot{..} | TraitKind::Function{..} TraitKind::Slot { .. } | TraitKind::Function { .. } => {
=> Property::new_slot(new_slot_id), Property::new_slot(new_slot_id)
TraitKind::Const{..} | TraitKind::Class {..} }
=> Property::new_const_slot(new_slot_id), TraitKind::Const { .. } | TraitKind::Class { .. } => {
Property::new_const_slot(new_slot_id)
}
_ => unreachable!(), _ => unreachable!(),
}; };
@ -245,11 +258,9 @@ impl<'gc> VTable<'gc> {
} }
} }
Ok(()) Ok(())
} }
/// Retrieve a bound instance method suitable for use as a value. /// Retrieve a bound instance method suitable for use as a value.
/// ///
/// This returns the bound method object itself, as well as it's dispatch /// This returns the bound method object itself, as well as it's dispatch
@ -268,15 +279,13 @@ impl<'gc> VTable<'gc> {
) -> Option<FunctionObject<'gc>> { ) -> Option<FunctionObject<'gc>> {
if let Some((superclass, method)) = self.get_full_method(disp_id) { if let Some((superclass, method)) = self.get_full_method(disp_id) {
let scope = self.0.read().scope.unwrap(); let scope = self.0.read().scope.unwrap();
Some( Some(FunctionObject::from_method(
FunctionObject::from_method(
activation, activation,
method, method,
scope, scope,
Some(receiver), Some(receiver),
superclass, superclass,
), ))
)
} else { } else {
None None
} }
@ -295,7 +304,9 @@ impl<'gc> VTable<'gc> {
write.default_slots.push(Some(value)); write.default_slots.push(Some(value));
let new_slot_id = write.default_slots.len() as u32 - 1; let new_slot_id = write.default_slots.len() as u32 - 1;
write.resolved_traits.insert(name, Property::new_slot(new_slot_id)); write
.resolved_traits
.insert(name, Property::new_slot(new_slot_id));
new_slot_id new_slot_id
} }
@ -316,7 +327,6 @@ impl<'gc> VTable<'gc> {
write.resolved_traits.insert(interface_name, prop); write.resolved_traits.insert(interface_name, prop);
} }
} }
} }
fn trait_to_default_value<'gc>( fn trait_to_default_value<'gc>(
@ -328,14 +338,11 @@ fn trait_to_default_value<'gc>(
TraitKind::Slot { default_value, .. } => default_value.clone(), TraitKind::Slot { default_value, .. } => default_value.clone(),
TraitKind::Const { default_value, .. } => default_value.clone(), TraitKind::Const { default_value, .. } => default_value.clone(),
TraitKind::Function { function, .. } => { TraitKind::Function { function, .. } => {
FunctionObject::from_function( FunctionObject::from_function(activation, function.clone(), scope)
activation, .unwrap()
function.clone(), .into()
scope,
).unwrap().into()
} }
TraitKind::Class { .. } => Value::Undefined, TraitKind::Class { .. } => Value::Undefined,
_ => unreachable!() _ => unreachable!(),
} }
} }

View File

@ -1189,8 +1189,7 @@ impl<'gc> MovieClip<'gc> {
if let Avm2Value::Object(c) = child.object2() { if let Avm2Value::Object(c) = child.object2() {
let name = Avm2QName::new(Avm2Namespace::public(), child.name()); let name = Avm2QName::new(Avm2Namespace::public(), child.name());
let mut activation = Avm2Activation::from_nothing(context.reborrow()); let mut activation = Avm2Activation::from_nothing(context.reborrow());
if let Err(e) = p.init_property(&name.into(), c.into(), &mut activation) if let Err(e) = p.init_property(&name.into(), c.into(), &mut activation) {
{
log::error!( log::error!(
"Got error when setting AVM2 child named \"{}\": {}", "Got error when setting AVM2 child named \"{}\": {}",
&child.name(), &child.name(),