avm1: Use enum_trait_object for avm1::Object
This commit is contained in:
parent
b27bc578e0
commit
23ca66a7e3
|
@ -14,6 +14,7 @@ libflate = "0.1.26"
|
|||
log = "0.4"
|
||||
minimp3 = { version = "0.3.3", optional = true }
|
||||
puremp3 = { version = "0.1", optional = true }
|
||||
ruffle_macros = { path = "macros" }
|
||||
swf = { path = "../swf" }
|
||||
enumset = "0.4.2"
|
||||
smallvec = "1.0.0"
|
||||
|
|
115
core/src/avm1.rs
115
core/src/avm1.rs
|
@ -33,7 +33,7 @@ mod tests;
|
|||
|
||||
use activation::Activation;
|
||||
pub use globals::SystemPrototypes;
|
||||
pub use object::{Object, ObjectCell};
|
||||
pub use object::{Object, ObjectPtr, TObject};
|
||||
use scope::Scope;
|
||||
pub use script_object::ScriptObject;
|
||||
pub use value::Value;
|
||||
|
@ -53,7 +53,7 @@ pub struct Avm1<'gc> {
|
|||
constant_pool: Vec<String>,
|
||||
|
||||
/// The global object.
|
||||
globals: ObjectCell<'gc>,
|
||||
globals: Object<'gc>,
|
||||
|
||||
/// System builtins that we use internally to construct new objects.
|
||||
prototypes: globals::SystemPrototypes<'gc>,
|
||||
|
@ -116,16 +116,14 @@ impl<'gc> Avm1<'gc> {
|
|||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
) -> HashMap<String, String> {
|
||||
let mut form_values = HashMap::new();
|
||||
let locals = self
|
||||
.current_stack_frame()
|
||||
.unwrap()
|
||||
.read()
|
||||
.scope()
|
||||
.locals_cell();
|
||||
let keys = locals.read().get_keys();
|
||||
let stack_frame = self.current_stack_frame().unwrap();
|
||||
let stack_frame = stack_frame.read();
|
||||
let scope = stack_frame.scope();
|
||||
let locals = scope.locals();
|
||||
let keys = locals.get_keys();
|
||||
|
||||
for k in keys {
|
||||
let v = locals.read().get(&k, self, context, locals);
|
||||
let v = locals.get(&k, self, context, *locals);
|
||||
|
||||
//TODO: What happens if an error occurs inside a virtual property?
|
||||
form_values.insert(
|
||||
|
@ -159,8 +157,7 @@ impl<'gc> Avm1<'gc> {
|
|||
.read()
|
||||
.object()
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.to_owned();
|
||||
.unwrap();
|
||||
let child_scope = GcCell::allocate(
|
||||
action_context.gc_context,
|
||||
Scope::new(global_scope, scope::ScopeClass::Target, clip_obj),
|
||||
|
@ -187,8 +184,7 @@ impl<'gc> Avm1<'gc> {
|
|||
.read()
|
||||
.object()
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.to_owned();
|
||||
.unwrap();
|
||||
let child_scope = GcCell::allocate(
|
||||
action_context.gc_context,
|
||||
Scope::new(global_scope, scope::ScopeClass::Target, clip_obj),
|
||||
|
@ -743,7 +739,7 @@ impl<'gc> Avm1<'gc> {
|
|||
.read()
|
||||
.resolve(fn_name.as_string()?, self, context)?
|
||||
.resolve(self, context)?;
|
||||
let this = context.active_clip.read().object().as_object()?.to_owned();
|
||||
let this = context.active_clip.read().object().as_object()?;
|
||||
target_fn.call(self, context, this, &args)?.push(self);
|
||||
|
||||
Ok(())
|
||||
|
@ -763,19 +759,18 @@ impl<'gc> Avm1<'gc> {
|
|||
|
||||
match method_name {
|
||||
Value::Undefined | Value::Null => {
|
||||
let this = context.active_clip.read().object().as_object()?.to_owned();
|
||||
let this = context.active_clip.read().object().as_object()?;
|
||||
object.call(self, context, this, &args)?.push(self);
|
||||
}
|
||||
Value::String(name) => {
|
||||
if name.is_empty() {
|
||||
object
|
||||
.call(self, context, object.as_object()?.to_owned(), &args)?
|
||||
.call(self, context, object.as_object()?, &args)?
|
||||
.push(self);
|
||||
} else {
|
||||
let target = object.as_object()?.to_owned();
|
||||
let target = object.as_object()?;
|
||||
let callable = object
|
||||
.as_object()?
|
||||
.read()
|
||||
.get(&name, self, context, target)?
|
||||
.resolve(self, context)?;
|
||||
|
||||
|
@ -834,13 +829,8 @@ impl<'gc> Avm1<'gc> {
|
|||
context.gc_context,
|
||||
);
|
||||
let func = Avm1Function::from_df1(swf_version, func_data, name, params, scope);
|
||||
let prototype = GcCell::allocate(
|
||||
context.gc_context,
|
||||
Box::new(ScriptObject::object(
|
||||
context.gc_context,
|
||||
Some(self.prototypes.object),
|
||||
)) as Box<dyn Object<'gc>>,
|
||||
);
|
||||
let prototype =
|
||||
ScriptObject::object(context.gc_context, Some(self.prototypes.object)).into();
|
||||
let func_obj = ScriptObject::function(
|
||||
context.gc_context,
|
||||
func,
|
||||
|
@ -877,13 +867,8 @@ impl<'gc> Avm1<'gc> {
|
|||
context.gc_context,
|
||||
);
|
||||
let func = Avm1Function::from_df2(swf_version, func_data, action_func, scope);
|
||||
let prototype = GcCell::allocate(
|
||||
context.gc_context,
|
||||
Box::new(ScriptObject::object(
|
||||
context.gc_context,
|
||||
Some(self.prototypes.object),
|
||||
)) as Box<dyn Object<'gc>>,
|
||||
);
|
||||
let prototype =
|
||||
ScriptObject::object(context.gc_context, Some(self.prototypes.object)).into();
|
||||
let func_obj = ScriptObject::function(
|
||||
context.gc_context,
|
||||
func,
|
||||
|
@ -935,7 +920,7 @@ impl<'gc> Avm1<'gc> {
|
|||
let name = name_val.as_string()?;
|
||||
let object = self.pop()?.as_object()?;
|
||||
|
||||
let success = object.write(context.gc_context).delete(name);
|
||||
let success = object.delete(context.gc_context, name);
|
||||
self.push(success);
|
||||
|
||||
Ok(())
|
||||
|
@ -991,7 +976,7 @@ impl<'gc> Avm1<'gc> {
|
|||
|
||||
match object {
|
||||
Value::Object(ob) => {
|
||||
for k in ob.read().get_keys() {
|
||||
for k in ob.get_keys() {
|
||||
self.push(k);
|
||||
}
|
||||
}
|
||||
|
@ -1008,7 +993,7 @@ impl<'gc> Avm1<'gc> {
|
|||
let object = self.pop()?.as_object()?;
|
||||
|
||||
self.push(Value::Null); // Sentinel that indicates end of enumeration
|
||||
for k in object.read().get_keys() {
|
||||
for k in object.get_keys() {
|
||||
self.push(k);
|
||||
}
|
||||
|
||||
|
@ -1039,7 +1024,7 @@ impl<'gc> Avm1<'gc> {
|
|||
let name_val = self.pop()?;
|
||||
let name = name_val.coerce_to_string(self, context)?;
|
||||
let object = self.pop()?.as_object()?;
|
||||
object.read().get(&name, self, context, object)?.push(self);
|
||||
object.get(&name, self, context, object)?.push(self);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1107,7 +1092,7 @@ impl<'gc> Avm1<'gc> {
|
|||
}
|
||||
|
||||
/// Obtain a reference to `_global`.
|
||||
pub fn global_object_cell(&self) -> ObjectCell<'gc> {
|
||||
pub fn global_object_cell(&self) -> Object<'gc> {
|
||||
self.globals
|
||||
}
|
||||
|
||||
|
@ -1130,11 +1115,8 @@ impl<'gc> Avm1<'gc> {
|
|||
{
|
||||
if let Some(clip) = node.read().as_movie_clip() {
|
||||
let object = clip.object().as_object()?;
|
||||
if object.read().has_property(var_name) {
|
||||
object
|
||||
.read()
|
||||
.get(var_name, self, context, object)?
|
||||
.push(self);
|
||||
if object.has_property(var_name) {
|
||||
object.get(var_name, self, context, object)?.push(self);
|
||||
} else {
|
||||
self.push(Value::Undefined);
|
||||
}
|
||||
|
@ -1330,7 +1312,7 @@ impl<'gc> Avm1<'gc> {
|
|||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
) -> Result<(), Error> {
|
||||
let num_props = self.pop()?.as_i64()?;
|
||||
let mut object = ScriptObject::object(context.gc_context, Some(self.prototypes.object));
|
||||
let object = ScriptObject::object(context.gc_context, Some(self.prototypes.object));
|
||||
for _ in 0..num_props {
|
||||
let value = self.pop()?;
|
||||
let name = self.pop()?.into_string();
|
||||
|
@ -1339,10 +1321,7 @@ impl<'gc> Avm1<'gc> {
|
|||
object.set(&name, value, self, context, this)?;
|
||||
}
|
||||
|
||||
self.push(Value::Object(GcCell::allocate(
|
||||
context.gc_context,
|
||||
Box::new(object),
|
||||
)));
|
||||
self.push(Value::Object(object.into()));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1356,19 +1335,18 @@ impl<'gc> Avm1<'gc> {
|
|||
|
||||
//TODO: Interface detection on SWF7
|
||||
let prototype = constr
|
||||
.read()
|
||||
.get("prototype", self, context, constr)?
|
||||
.resolve(self, context)?
|
||||
.as_object()?;
|
||||
let mut proto = obj.read().proto();
|
||||
let mut proto = obj.proto();
|
||||
|
||||
while let Some(this_proto) = proto {
|
||||
if GcCell::ptr_eq(this_proto, prototype) {
|
||||
if Object::ptr_eq(this_proto, prototype) {
|
||||
self.push(true);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
proto = this_proto.read().proto();
|
||||
proto = this_proto.proto();
|
||||
}
|
||||
|
||||
self.push(false);
|
||||
|
@ -1498,21 +1476,18 @@ impl<'gc> Avm1<'gc> {
|
|||
}
|
||||
|
||||
let constructor = object
|
||||
.read()
|
||||
.get(&method_name.as_string()?, self, context, object.to_owned())?
|
||||
.get(&method_name.as_string()?, self, context, object)?
|
||||
.resolve(self, context)?
|
||||
.as_object()?;
|
||||
let prototype = constructor
|
||||
.read()
|
||||
.get("prototype", self, context, constructor)?
|
||||
.resolve(self, context)?
|
||||
.as_object()?;
|
||||
|
||||
let this = prototype.read().new(self, context, prototype, &args)?;
|
||||
let this = prototype.new(self, context, prototype, &args)?;
|
||||
|
||||
//TODO: What happens if you `ActionNewMethod` without a method name?
|
||||
constructor
|
||||
.read()
|
||||
.call(self, context, this, &args)?
|
||||
.resolve(self, context)?;
|
||||
|
||||
|
@ -1539,15 +1514,13 @@ impl<'gc> Avm1<'gc> {
|
|||
.resolve(self, context)?
|
||||
.as_object()?;
|
||||
let prototype = constructor
|
||||
.read()
|
||||
.get("prototype", self, context, constructor)?
|
||||
.resolve(self, context)?
|
||||
.as_object()?;
|
||||
|
||||
let this = prototype.read().new(self, context, prototype, &args)?;
|
||||
let this = prototype.new(self, context, prototype, &args)?;
|
||||
|
||||
constructor
|
||||
.read()
|
||||
.call(self, context, this, &args)?
|
||||
.resolve(self, context)?;
|
||||
|
||||
|
@ -1667,9 +1640,7 @@ impl<'gc> Avm1<'gc> {
|
|||
let name = name_val.coerce_to_string(self, context)?;
|
||||
let object = self.pop()?.as_object()?;
|
||||
|
||||
object
|
||||
.write(context.gc_context)
|
||||
.set(&name, value, self, context, object)?;
|
||||
object.set(&name, value, self, context, object)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -1735,13 +1706,9 @@ impl<'gc> Avm1<'gc> {
|
|||
Self::resolve_slash_path_variable(context.target_clip, context.root, var_path)
|
||||
{
|
||||
if let Some(clip) = node.write(context.gc_context).as_movie_clip_mut() {
|
||||
clip.object().as_object()?.write(context.gc_context).set(
|
||||
var_name,
|
||||
value.clone(),
|
||||
self,
|
||||
context,
|
||||
this,
|
||||
)?;
|
||||
clip.object()
|
||||
.as_object()?
|
||||
.set(var_name, value.clone(), self, context, this)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -1791,13 +1758,7 @@ impl<'gc> Avm1<'gc> {
|
|||
}
|
||||
|
||||
let scope = self.current_stack_frame().unwrap().read().scope_cell();
|
||||
let clip_obj = context
|
||||
.active_clip
|
||||
.read()
|
||||
.object()
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.to_owned();
|
||||
let clip_obj = context.active_clip.read().object().as_object().unwrap();
|
||||
|
||||
self.current_stack_frame()
|
||||
.unwrap()
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use crate::avm1::return_value::ReturnValue;
|
||||
use crate::avm1::scope::Scope;
|
||||
use crate::avm1::{Avm1, Error, ObjectCell, Value};
|
||||
use crate::avm1::{Avm1, Error, Object, Value};
|
||||
use crate::context::UpdateContext;
|
||||
use crate::tag_utils::SwfSlice;
|
||||
use gc_arena::{GcCell, MutationContext};
|
||||
|
@ -67,10 +67,10 @@ pub struct Activation<'gc> {
|
|||
scope: GcCell<'gc, Scope<'gc>>,
|
||||
|
||||
/// The immutable value of `this`.
|
||||
this: ObjectCell<'gc>,
|
||||
this: Object<'gc>,
|
||||
|
||||
/// The arguments this function was called by.
|
||||
arguments: Option<ObjectCell<'gc>>,
|
||||
arguments: Option<Object<'gc>>,
|
||||
|
||||
/// The return value of the activation.
|
||||
return_value: Option<Value<'gc>>,
|
||||
|
@ -114,8 +114,8 @@ impl<'gc> Activation<'gc> {
|
|||
swf_version: u8,
|
||||
code: SwfSlice,
|
||||
scope: GcCell<'gc, Scope<'gc>>,
|
||||
this: ObjectCell<'gc>,
|
||||
arguments: Option<ObjectCell<'gc>>,
|
||||
this: Object<'gc>,
|
||||
arguments: Option<Object<'gc>>,
|
||||
) -> Activation<'gc> {
|
||||
Activation {
|
||||
swf_version,
|
||||
|
@ -135,8 +135,8 @@ impl<'gc> Activation<'gc> {
|
|||
swf_version: u8,
|
||||
code: SwfSlice,
|
||||
scope: GcCell<'gc, Scope<'gc>>,
|
||||
this: ObjectCell<'gc>,
|
||||
arguments: Option<ObjectCell<'gc>>,
|
||||
this: Object<'gc>,
|
||||
arguments: Option<Object<'gc>>,
|
||||
) -> Activation<'gc> {
|
||||
Activation {
|
||||
swf_version,
|
||||
|
@ -158,9 +158,10 @@ impl<'gc> Activation<'gc> {
|
|||
/// will prevent the AVM from panicking without a current activation.
|
||||
/// We construct a single scope chain from a global object, and that's about
|
||||
/// it.
|
||||
#[cfg(test)]
|
||||
pub fn from_nothing(
|
||||
swf_version: u8,
|
||||
globals: ObjectCell<'gc>,
|
||||
globals: Object<'gc>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
) -> Activation<'gc> {
|
||||
let global_scope = GcCell::allocate(mc, Scope::from_global_object(globals));
|
||||
|
@ -211,12 +212,14 @@ impl<'gc> Activation<'gc> {
|
|||
}
|
||||
|
||||
/// Change the data being executed.
|
||||
#[allow(dead_code)]
|
||||
pub fn set_data(&mut self, new_data: SwfSlice) {
|
||||
self.data = new_data;
|
||||
}
|
||||
|
||||
/// Determines if a stack frame references the same function as a given
|
||||
/// SwfSlice.
|
||||
#[allow(dead_code)]
|
||||
pub fn is_identical_fn(&self, other: &SwfSlice) -> bool {
|
||||
Arc::ptr_eq(&self.data.data, &other.data)
|
||||
}
|
||||
|
@ -236,6 +239,7 @@ impl<'gc> Activation<'gc> {
|
|||
}
|
||||
|
||||
/// Returns AVM local variable scope for mutation.
|
||||
#[allow(dead_code)]
|
||||
pub fn scope_mut(&mut self, mc: MutationContext<'gc, '_>) -> RefMut<Scope<'gc>> {
|
||||
self.scope.write(mc)
|
||||
}
|
||||
|
@ -252,6 +256,7 @@ impl<'gc> Activation<'gc> {
|
|||
|
||||
/// Indicates whether or not the end of this scope should be handled as an
|
||||
/// implicit function return or the end of a block.
|
||||
#[allow(dead_code)]
|
||||
pub fn can_implicit_return(&self) -> bool {
|
||||
self.is_function
|
||||
}
|
||||
|
@ -296,7 +301,7 @@ impl<'gc> Activation<'gc> {
|
|||
}
|
||||
|
||||
/// Returns value of `this` as a reference.
|
||||
pub fn this_cell(&self) -> ObjectCell<'gc> {
|
||||
pub fn this_cell(&self) -> Object<'gc> {
|
||||
self.this
|
||||
}
|
||||
|
||||
|
@ -358,6 +363,7 @@ impl<'gc> Activation<'gc> {
|
|||
|
||||
/// Retrieve the return value from a completed activation, if the function
|
||||
/// has already returned.
|
||||
#[allow(dead_code)]
|
||||
pub fn return_value(&self) -> Option<Value<'gc>> {
|
||||
self.return_value.clone()
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::avm1::property::Attribute::*;
|
|||
use crate::avm1::return_value::ReturnValue;
|
||||
use crate::avm1::scope::Scope;
|
||||
use crate::avm1::value::Value;
|
||||
use crate::avm1::{Avm1, Error, Object, ObjectCell, ScriptObject, UpdateContext};
|
||||
use crate::avm1::{Avm1, Error, Object, ScriptObject, TObject, UpdateContext};
|
||||
use crate::tag_utils::SwfSlice;
|
||||
use gc_arena::{Collect, CollectionContext, GcCell};
|
||||
use swf::avm1::types::FunctionParam;
|
||||
|
@ -27,7 +27,7 @@ use swf::avm1::types::FunctionParam;
|
|||
pub type NativeFunction<'gc> = fn(
|
||||
&mut Avm1<'gc>,
|
||||
&mut UpdateContext<'_, 'gc, '_>,
|
||||
ObjectCell<'gc>,
|
||||
Object<'gc>,
|
||||
&[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error>;
|
||||
|
||||
|
@ -192,7 +192,7 @@ impl<'gc> Executable<'gc> {
|
|||
&self,
|
||||
avm: &mut Avm1<'gc>,
|
||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: ObjectCell<'gc>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
match self {
|
||||
|
@ -202,29 +202,30 @@ impl<'gc> Executable<'gc> {
|
|||
ac.gc_context,
|
||||
Scope::new_local_scope(af.scope(), ac.gc_context),
|
||||
);
|
||||
let mut arguments =
|
||||
ScriptObject::object(ac.gc_context, Some(avm.prototypes().object));
|
||||
let arguments = ScriptObject::object(ac.gc_context, Some(avm.prototypes().object));
|
||||
if !af.suppress_arguments {
|
||||
for i in 0..args.len() {
|
||||
arguments.define_value(
|
||||
ac.gc_context,
|
||||
&format!("{}", i),
|
||||
args.get(i).unwrap().clone(),
|
||||
DontDelete.into(),
|
||||
)
|
||||
}
|
||||
|
||||
arguments.define_value("length", args.len().into(), DontDelete | DontEnum);
|
||||
arguments.define_value(
|
||||
ac.gc_context,
|
||||
"length",
|
||||
args.len().into(),
|
||||
DontDelete | DontEnum,
|
||||
);
|
||||
}
|
||||
|
||||
let argcell = GcCell::allocate(
|
||||
ac.gc_context,
|
||||
Box::new(arguments) as Box<dyn Object<'gc> + 'gc>,
|
||||
);
|
||||
let argcell = arguments.into();
|
||||
let effective_ver = if avm.current_swf_version() > 5 {
|
||||
af.swf_version()
|
||||
} else {
|
||||
this.read()
|
||||
.as_display_node()
|
||||
this.as_display_node()
|
||||
.map(|dn| dn.read().swf_version())
|
||||
.unwrap_or(ac.player_version)
|
||||
};
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use crate::avm1::fscommand;
|
||||
use crate::avm1::function::Executable;
|
||||
use crate::avm1::return_value::ReturnValue;
|
||||
use crate::avm1::{Avm1, Error, Object, ObjectCell, ScriptObject, UpdateContext, Value};
|
||||
use crate::avm1::{Avm1, Error, Object, ScriptObject, TObject, UpdateContext, Value};
|
||||
use crate::backend::navigator::NavigationMethod;
|
||||
use enumset::EnumSet;
|
||||
use gc_arena::{GcCell, MutationContext};
|
||||
use gc_arena::MutationContext;
|
||||
use rand::Rng;
|
||||
use std::f64;
|
||||
|
||||
|
@ -17,7 +17,7 @@ mod object;
|
|||
pub fn getURL<'a, 'gc>(
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'a, 'gc, '_>,
|
||||
_this: ObjectCell<'gc>,
|
||||
_this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
//TODO: Error behavior if no arguments are present
|
||||
|
@ -45,7 +45,7 @@ pub fn getURL<'a, 'gc>(
|
|||
pub fn random<'gc>(
|
||||
_avm: &mut Avm1<'gc>,
|
||||
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_this: ObjectCell<'gc>,
|
||||
_this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
match args.get(0) {
|
||||
|
@ -57,7 +57,7 @@ pub fn random<'gc>(
|
|||
pub fn boolean<'gc>(
|
||||
avm: &mut Avm1<'gc>,
|
||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_this: ObjectCell<'gc>,
|
||||
_this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
if let Some(val) = args.get(0) {
|
||||
|
@ -70,7 +70,7 @@ pub fn boolean<'gc>(
|
|||
pub fn number<'gc>(
|
||||
avm: &mut Avm1<'gc>,
|
||||
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_this: ObjectCell<'gc>,
|
||||
_this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
if let Some(val) = args.get(0) {
|
||||
|
@ -83,7 +83,7 @@ pub fn number<'gc>(
|
|||
pub fn is_nan<'gc>(
|
||||
avm: &mut Avm1<'gc>,
|
||||
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_this: ObjectCell<'gc>,
|
||||
_this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
if let Some(val) = args.get(0) {
|
||||
|
@ -96,7 +96,7 @@ pub fn is_nan<'gc>(
|
|||
pub fn get_infinity<'gc>(
|
||||
avm: &mut Avm1<'gc>,
|
||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_this: ObjectCell<'gc>,
|
||||
_this: Object<'gc>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
if avm.current_swf_version() > 4 {
|
||||
|
@ -109,7 +109,7 @@ pub fn get_infinity<'gc>(
|
|||
pub fn get_nan<'gc>(
|
||||
avm: &mut Avm1<'gc>,
|
||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_this: ObjectCell<'gc>,
|
||||
_this: Object<'gc>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
if avm.current_swf_version() > 4 {
|
||||
|
@ -124,9 +124,9 @@ pub fn get_nan<'gc>(
|
|||
/// user-modifiable.
|
||||
#[derive(Clone)]
|
||||
pub struct SystemPrototypes<'gc> {
|
||||
pub object: ObjectCell<'gc>,
|
||||
pub function: ObjectCell<'gc>,
|
||||
pub movie_clip: ObjectCell<'gc>,
|
||||
pub object: Object<'gc>,
|
||||
pub function: Object<'gc>,
|
||||
pub movie_clip: Object<'gc>,
|
||||
}
|
||||
|
||||
unsafe impl<'gc> gc_arena::Collect for SystemPrototypes<'gc> {
|
||||
|
@ -141,16 +141,13 @@ unsafe impl<'gc> gc_arena::Collect for SystemPrototypes<'gc> {
|
|||
/// Initialize default global scope and builtins for an AVM1 instance.
|
||||
pub fn create_globals<'gc>(
|
||||
gc_context: MutationContext<'gc, '_>,
|
||||
) -> (
|
||||
SystemPrototypes<'gc>,
|
||||
GcCell<'gc, Box<dyn Object<'gc> + 'gc>>,
|
||||
) {
|
||||
) -> (SystemPrototypes<'gc>, Object<'gc>) {
|
||||
let object_proto = ScriptObject::object_cell(gc_context, None);
|
||||
let function_proto = function::create_proto(gc_context, object_proto);
|
||||
|
||||
object::fill_proto(gc_context, object_proto, function_proto);
|
||||
|
||||
let movie_clip_proto: ObjectCell<'gc> =
|
||||
let movie_clip_proto: Object<'gc> =
|
||||
movie_clip::create_proto(gc_context, object_proto, function_proto);
|
||||
|
||||
//TODO: These need to be constructors and should also set `.prototype` on each one
|
||||
|
@ -174,10 +171,10 @@ pub fn create_globals<'gc>(
|
|||
Some(movie_clip_proto),
|
||||
);
|
||||
|
||||
let mut globals = ScriptObject::bare_object();
|
||||
globals.define_value("Object", object.into(), EnumSet::empty());
|
||||
globals.define_value("Function", function.into(), EnumSet::empty());
|
||||
globals.define_value("MovieClip", movie_clip.into(), EnumSet::empty());
|
||||
let mut globals = ScriptObject::bare_object(gc_context);
|
||||
globals.define_value(gc_context, "Object", object.into(), EnumSet::empty());
|
||||
globals.define_value(gc_context, "Function", function.into(), EnumSet::empty());
|
||||
globals.define_value(gc_context, "MovieClip", movie_clip.into(), EnumSet::empty());
|
||||
globals.force_set_function(
|
||||
"Number",
|
||||
number,
|
||||
|
@ -193,6 +190,7 @@ pub fn create_globals<'gc>(
|
|||
Some(function_proto),
|
||||
);
|
||||
globals.define_value(
|
||||
gc_context,
|
||||
"Math",
|
||||
Value::Object(math::create(
|
||||
gc_context,
|
||||
|
@ -222,8 +220,15 @@ pub fn create_globals<'gc>(
|
|||
EnumSet::empty(),
|
||||
Some(function_proto),
|
||||
);
|
||||
globals.add_property("NaN", Executable::Native(get_nan), None, EnumSet::empty());
|
||||
globals.add_property(
|
||||
gc_context,
|
||||
"NaN",
|
||||
Executable::Native(get_nan),
|
||||
None,
|
||||
EnumSet::empty(),
|
||||
);
|
||||
globals.add_property(
|
||||
gc_context,
|
||||
"Infinity",
|
||||
Executable::Native(get_infinity),
|
||||
None,
|
||||
|
@ -236,7 +241,7 @@ pub fn create_globals<'gc>(
|
|||
function: function_proto,
|
||||
movie_clip: movie_clip_proto,
|
||||
},
|
||||
GcCell::allocate(gc_context, Box::new(globals)),
|
||||
globals.into(),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -245,10 +250,7 @@ pub fn create_globals<'gc>(
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn setup<'gc>(
|
||||
_avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
) -> ObjectCell<'gc> {
|
||||
fn setup<'gc>(_avm: &mut Avm1<'gc>, context: &mut UpdateContext<'_, 'gc, '_>) -> Object<'gc> {
|
||||
create_globals(context.gc_context).1
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! Function prototype
|
||||
|
||||
use crate::avm1::return_value::ReturnValue;
|
||||
use crate::avm1::{Avm1, Error, ObjectCell, ScriptObject, UpdateContext, Value};
|
||||
use crate::avm1::{Avm1, Error, Object, ScriptObject, TObject, UpdateContext, Value};
|
||||
use enumset::EnumSet;
|
||||
use gc_arena::MutationContext;
|
||||
|
||||
|
@ -9,7 +9,7 @@ use gc_arena::MutationContext;
|
|||
pub fn constructor<'gc>(
|
||||
_avm: &mut Avm1<'gc>,
|
||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_this: ObjectCell<'gc>,
|
||||
_this: Object<'gc>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
Ok(Value::Undefined.into())
|
||||
|
@ -18,7 +18,7 @@ pub fn constructor<'gc>(
|
|||
pub fn call<'gc>(
|
||||
_avm: &mut Avm1<'gc>,
|
||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_this: ObjectCell<'gc>,
|
||||
_this: Object<'gc>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
Ok(Value::Undefined.into())
|
||||
|
@ -27,7 +27,7 @@ pub fn call<'gc>(
|
|||
pub fn apply<'gc>(
|
||||
_avm: &mut Avm1<'gc>,
|
||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_this: ObjectCell<'gc>,
|
||||
_this: Object<'gc>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
Ok(Value::Undefined.into())
|
||||
|
@ -40,34 +40,17 @@ pub fn apply<'gc>(
|
|||
/// them in order to obtain a valid ECMAScript `Function` prototype. The
|
||||
/// returned object is also a bare object, which will need to be linked into
|
||||
/// the prototype of `Object`.
|
||||
pub fn create_proto<'gc>(
|
||||
gc_context: MutationContext<'gc, '_>,
|
||||
proto: ObjectCell<'gc>,
|
||||
) -> ObjectCell<'gc> {
|
||||
let function_proto = ScriptObject::object_cell(gc_context, Some(proto));
|
||||
|
||||
pub fn create_proto<'gc>(gc_context: MutationContext<'gc, '_>, proto: Object<'gc>) -> Object<'gc> {
|
||||
let mut function_proto = ScriptObject::object_cell(gc_context, Some(proto));
|
||||
let this = Some(function_proto);
|
||||
function_proto
|
||||
.write(gc_context)
|
||||
.as_script_object_mut()
|
||||
.unwrap()
|
||||
.force_set_function(
|
||||
"call",
|
||||
call,
|
||||
gc_context,
|
||||
EnumSet::empty(),
|
||||
Some(function_proto),
|
||||
);
|
||||
.force_set_function("call", call, gc_context, EnumSet::empty(), this);
|
||||
function_proto
|
||||
.write(gc_context)
|
||||
.as_script_object_mut()
|
||||
.unwrap()
|
||||
.force_set_function(
|
||||
"apply",
|
||||
apply,
|
||||
gc_context,
|
||||
EnumSet::empty(),
|
||||
Some(function_proto),
|
||||
);
|
||||
.force_set_function("apply", apply, gc_context, EnumSet::empty(), this);
|
||||
|
||||
function_proto
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::avm1::object::ObjectCell;
|
||||
use crate::avm1::object::Object;
|
||||
use crate::avm1::property::Attribute::*;
|
||||
use crate::avm1::return_value::ReturnValue;
|
||||
use crate::avm1::{Avm1, Error, Object, ScriptObject, UpdateContext, Value};
|
||||
use gc_arena::{GcCell, MutationContext};
|
||||
use crate::avm1::{Avm1, Error, ScriptObject, TObject, UpdateContext, Value};
|
||||
use gc_arena::MutationContext;
|
||||
use rand::Rng;
|
||||
use std::f64::NAN;
|
||||
|
||||
|
@ -29,7 +29,7 @@ macro_rules! wrap_std {
|
|||
fn atan2<'gc>(
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_this: ObjectCell<'gc>,
|
||||
_this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
if let Some(y) = args.get(0) {
|
||||
|
@ -48,7 +48,7 @@ fn atan2<'gc>(
|
|||
pub fn random<'gc>(
|
||||
_avm: &mut Avm1<'gc>,
|
||||
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_this: ObjectCell<'gc>,
|
||||
_this: Object<'gc>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
Ok(action_context.rng.gen_range(0.0f64, 1.0f64).into())
|
||||
|
@ -56,47 +56,55 @@ pub fn random<'gc>(
|
|||
|
||||
pub fn create<'gc>(
|
||||
gc_context: MutationContext<'gc, '_>,
|
||||
proto: Option<ObjectCell<'gc>>,
|
||||
fn_proto: Option<ObjectCell<'gc>>,
|
||||
) -> ObjectCell<'gc> {
|
||||
proto: Option<Object<'gc>>,
|
||||
fn_proto: Option<Object<'gc>>,
|
||||
) -> Object<'gc> {
|
||||
let mut math = ScriptObject::object(gc_context, proto);
|
||||
|
||||
math.define_value(
|
||||
gc_context,
|
||||
"E",
|
||||
std::f64::consts::E.into(),
|
||||
DontDelete | ReadOnly | DontEnum,
|
||||
);
|
||||
math.define_value(
|
||||
gc_context,
|
||||
"LN10",
|
||||
std::f64::consts::LN_10.into(),
|
||||
DontDelete | ReadOnly | DontEnum,
|
||||
);
|
||||
math.define_value(
|
||||
gc_context,
|
||||
"LN2",
|
||||
std::f64::consts::LN_2.into(),
|
||||
DontDelete | ReadOnly | DontEnum,
|
||||
);
|
||||
math.define_value(
|
||||
gc_context,
|
||||
"LOG10E",
|
||||
std::f64::consts::LOG10_E.into(),
|
||||
DontDelete | ReadOnly | DontEnum,
|
||||
);
|
||||
math.define_value(
|
||||
gc_context,
|
||||
"LOG2E",
|
||||
std::f64::consts::LOG2_E.into(),
|
||||
DontDelete | ReadOnly | DontEnum,
|
||||
);
|
||||
math.define_value(
|
||||
gc_context,
|
||||
"PI",
|
||||
std::f64::consts::PI.into(),
|
||||
DontDelete | ReadOnly | DontEnum,
|
||||
);
|
||||
math.define_value(
|
||||
gc_context,
|
||||
"SQRT1_2",
|
||||
std::f64::consts::FRAC_1_SQRT_2.into(),
|
||||
DontDelete | ReadOnly | DontEnum,
|
||||
);
|
||||
math.define_value(
|
||||
gc_context,
|
||||
"SQRT2",
|
||||
std::f64::consts::SQRT_2.into(),
|
||||
DontDelete | ReadOnly | DontEnum,
|
||||
|
@ -132,7 +140,7 @@ pub fn create<'gc>(
|
|||
fn_proto,
|
||||
);
|
||||
|
||||
GcCell::allocate(gc_context, Box::new(math))
|
||||
math.into()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -140,10 +148,7 @@ mod tests {
|
|||
use super::*;
|
||||
use crate::avm1::test_utils::with_avm;
|
||||
|
||||
fn setup<'gc>(
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
) -> ObjectCell<'gc> {
|
||||
fn setup<'gc>(avm: &mut Avm1<'gc>, context: &mut UpdateContext<'_, 'gc, '_>) -> Object<'gc> {
|
||||
create(
|
||||
context.gc_context,
|
||||
Some(avm.prototypes().object),
|
||||
|
@ -274,26 +279,23 @@ mod tests {
|
|||
#[test]
|
||||
fn test_atan2_nan() {
|
||||
with_avm(19, |avm, context, _root| {
|
||||
let math = GcCell::allocate(
|
||||
let math = create(
|
||||
context.gc_context,
|
||||
create(
|
||||
context.gc_context,
|
||||
Some(avm.prototypes().object),
|
||||
Some(avm.prototypes().function),
|
||||
),
|
||||
Some(avm.prototypes().object),
|
||||
Some(avm.prototypes().function),
|
||||
);
|
||||
|
||||
assert_eq!(atan2(avm, context, *math.read(), &[]).unwrap(), NAN.into());
|
||||
assert_eq!(atan2(avm, context, math, &[]).unwrap(), NAN.into());
|
||||
assert_eq!(
|
||||
atan2(avm, context, *math.read(), &[1.0.into(), Value::Null]).unwrap(),
|
||||
atan2(avm, context, math, &[1.0.into(), Value::Null]).unwrap(),
|
||||
NAN.into()
|
||||
);
|
||||
assert_eq!(
|
||||
atan2(avm, context, *math.read(), &[1.0.into(), Value::Undefined]).unwrap(),
|
||||
atan2(avm, context, math, &[1.0.into(), Value::Undefined]).unwrap(),
|
||||
NAN.into()
|
||||
);
|
||||
assert_eq!(
|
||||
atan2(avm, context, *math.read(), &[Value::Undefined, 1.0.into()]).unwrap(),
|
||||
atan2(avm, context, math, &[Value::Undefined, 1.0.into()]).unwrap(),
|
||||
NAN.into()
|
||||
);
|
||||
});
|
||||
|
@ -302,21 +304,18 @@ mod tests {
|
|||
#[test]
|
||||
fn test_atan2_valid() {
|
||||
with_avm(19, |avm, context, _root| {
|
||||
let math = GcCell::allocate(
|
||||
let math = create(
|
||||
context.gc_context,
|
||||
create(
|
||||
context.gc_context,
|
||||
Some(avm.prototypes().object),
|
||||
Some(avm.prototypes().function),
|
||||
),
|
||||
Some(avm.prototypes().object),
|
||||
Some(avm.prototypes().function),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
atan2(avm, context, *math.read(), &[10.0.into()]).unwrap(),
|
||||
atan2(avm, context, math, &[10.0.into()]).unwrap(),
|
||||
std::f64::consts::FRAC_PI_2.into()
|
||||
);
|
||||
assert_eq!(
|
||||
atan2(avm, context, *math.read(), &[1.0.into(), 2.0.into()]).unwrap(),
|
||||
atan2(avm, context, math, &[1.0.into(), 2.0.into()]).unwrap(),
|
||||
f64::atan2(1.0, 2.0).into()
|
||||
);
|
||||
});
|
||||
|
|
|
@ -3,16 +3,16 @@
|
|||
use crate::avm1::function::Executable;
|
||||
use crate::avm1::property::Attribute::*;
|
||||
use crate::avm1::return_value::ReturnValue;
|
||||
use crate::avm1::{Avm1, Error, Object, ObjectCell, ScriptObject, UpdateContext, Value};
|
||||
use crate::avm1::{Avm1, Error, Object, ScriptObject, TObject, UpdateContext, Value};
|
||||
use crate::display_object::{DisplayNode, DisplayObject, MovieClip};
|
||||
use enumset::EnumSet;
|
||||
use gc_arena::{GcCell, MutationContext};
|
||||
use gc_arena::MutationContext;
|
||||
|
||||
/// Implements `MovieClip`
|
||||
pub fn constructor<'gc>(
|
||||
_avm: &mut Avm1<'gc>,
|
||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_this: ObjectCell<'gc>,
|
||||
_this: Object<'gc>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
Ok(Value::Undefined.into())
|
||||
|
@ -24,7 +24,7 @@ macro_rules! with_movie_clip {
|
|||
$object.force_set_function(
|
||||
$name,
|
||||
|_avm, _context, this, args| -> Result<ReturnValue<'gc>, Error> {
|
||||
if let Some(display_object) = this.read().as_display_node() {
|
||||
if let Some(display_object) = this.as_display_node() {
|
||||
if let Some(movie_clip) = display_object.read().as_movie_clip() {
|
||||
return Ok($fn(movie_clip, args));
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ macro_rules! with_movie_clip_mut {
|
|||
$object.force_set_function(
|
||||
$name,
|
||||
|_avm, context: &mut UpdateContext<'_, 'gc, '_>, this, args| -> Result<ReturnValue<'gc>, Error> {
|
||||
if let Some(display_object) = this.read().as_display_node() {
|
||||
if let Some(display_object) = this.as_display_node() {
|
||||
if let Some(movie_clip) = display_object.write(context.gc_context).as_movie_clip_mut() {
|
||||
return Ok($fn(movie_clip, context, display_object, args).into());
|
||||
}
|
||||
|
@ -63,15 +63,14 @@ macro_rules! with_movie_clip_mut {
|
|||
pub fn overwrite_root<'gc>(
|
||||
_avm: &mut Avm1<'gc>,
|
||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: ObjectCell<'gc>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
let new_val = args
|
||||
.get(0)
|
||||
.map(|v| v.to_owned())
|
||||
.unwrap_or(Value::Undefined);
|
||||
this.write(ac.gc_context)
|
||||
.define_value("_root", new_val, EnumSet::new());
|
||||
this.define_value(ac.gc_context, "_root", new_val, EnumSet::new());
|
||||
|
||||
Ok(Value::Undefined.into())
|
||||
}
|
||||
|
@ -79,24 +78,23 @@ pub fn overwrite_root<'gc>(
|
|||
pub fn overwrite_global<'gc>(
|
||||
_avm: &mut Avm1<'gc>,
|
||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: ObjectCell<'gc>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
let new_val = args
|
||||
.get(0)
|
||||
.map(|v| v.to_owned())
|
||||
.unwrap_or(Value::Undefined);
|
||||
this.write(ac.gc_context)
|
||||
.define_value("_global", new_val, EnumSet::new());
|
||||
this.define_value(ac.gc_context, "_global", new_val, EnumSet::new());
|
||||
|
||||
Ok(Value::Undefined.into())
|
||||
}
|
||||
|
||||
pub fn create_proto<'gc>(
|
||||
gc_context: MutationContext<'gc, '_>,
|
||||
proto: ObjectCell<'gc>,
|
||||
fn_proto: ObjectCell<'gc>,
|
||||
) -> ObjectCell<'gc> {
|
||||
proto: Object<'gc>,
|
||||
fn_proto: Object<'gc>,
|
||||
) -> Object<'gc> {
|
||||
let mut object = ScriptObject::object(gc_context, Some(proto));
|
||||
|
||||
with_movie_clip_mut!(
|
||||
|
@ -139,6 +137,7 @@ pub fn create_proto<'gc>(
|
|||
);
|
||||
|
||||
object.add_property(
|
||||
gc_context,
|
||||
"_global",
|
||||
Executable::Native(|avm, context, _this, _args| Ok(avm.global_object(context).into())),
|
||||
Some(Executable::Native(overwrite_global)),
|
||||
|
@ -146,6 +145,7 @@ pub fn create_proto<'gc>(
|
|||
);
|
||||
|
||||
object.add_property(
|
||||
gc_context,
|
||||
"_root",
|
||||
Executable::Native(|avm, context, _this, _args| Ok(avm.root_object(context).into())),
|
||||
Some(Executable::Native(overwrite_root)),
|
||||
|
@ -153,14 +153,14 @@ pub fn create_proto<'gc>(
|
|||
);
|
||||
|
||||
object.add_property(
|
||||
gc_context,
|
||||
"_parent",
|
||||
Executable::Native(|_avm, _context, this, _args| {
|
||||
Ok(this
|
||||
.read()
|
||||
.as_display_node()
|
||||
.and_then(|mc| mc.read().parent())
|
||||
.and_then(|dn| dn.read().object().as_object().ok())
|
||||
.map(|o| Value::Object(o.to_owned()))
|
||||
.map(Value::Object)
|
||||
.unwrap_or(Value::Undefined)
|
||||
.into())
|
||||
}),
|
||||
|
@ -168,5 +168,5 @@ pub fn create_proto<'gc>(
|
|||
EnumSet::new(),
|
||||
);
|
||||
|
||||
GcCell::allocate(gc_context, Box::new(object))
|
||||
object.into()
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
//! Object prototype
|
||||
use crate::avm1::property::Attribute::*;
|
||||
use crate::avm1::return_value::ReturnValue;
|
||||
use crate::avm1::{Avm1, Error, ObjectCell, UpdateContext, Value};
|
||||
use crate::avm1::{Avm1, Error, Object, TObject, UpdateContext, Value};
|
||||
use enumset::EnumSet;
|
||||
use gc_arena::{GcCell, MutationContext};
|
||||
use gc_arena::MutationContext;
|
||||
|
||||
/// Implements `Object`
|
||||
pub fn constructor<'gc>(
|
||||
_avm: &mut Avm1<'gc>,
|
||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_this: ObjectCell<'gc>,
|
||||
_this: Object<'gc>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
Ok(Value::Undefined.into())
|
||||
|
@ -19,7 +19,7 @@ pub fn constructor<'gc>(
|
|||
pub fn add_property<'gc>(
|
||||
_avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: ObjectCell<'gc>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
let name = args.get(0).unwrap_or(&Value::Undefined);
|
||||
|
@ -28,10 +28,11 @@ pub fn add_property<'gc>(
|
|||
|
||||
match (name, getter) {
|
||||
(Value::String(name), Value::Object(get)) if !name.is_empty() => {
|
||||
if let Some(get_func) = get.read().as_executable() {
|
||||
if let Some(get_func) = get.as_executable() {
|
||||
if let Value::Object(set) = setter {
|
||||
if let Some(set_func) = set.read().as_executable() {
|
||||
this.write(context.gc_context).add_property(
|
||||
if let Some(set_func) = set.as_executable() {
|
||||
this.add_property(
|
||||
context.gc_context,
|
||||
name,
|
||||
get_func,
|
||||
Some(set_func),
|
||||
|
@ -41,12 +42,7 @@ pub fn add_property<'gc>(
|
|||
return Ok(false.into());
|
||||
}
|
||||
} else if let Value::Null = setter {
|
||||
this.write(context.gc_context).add_property(
|
||||
name,
|
||||
get_func,
|
||||
None,
|
||||
ReadOnly.into(),
|
||||
);
|
||||
this.add_property(context.gc_context, name, get_func, None, ReadOnly.into());
|
||||
} else {
|
||||
return Ok(false.into());
|
||||
}
|
||||
|
@ -62,11 +58,11 @@ pub fn add_property<'gc>(
|
|||
pub fn has_own_property<'gc>(
|
||||
_avm: &mut Avm1<'gc>,
|
||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: ObjectCell<'gc>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
match args.get(0) {
|
||||
Some(Value::String(name)) => Ok(Value::Bool(this.read().has_own_property(name)).into()),
|
||||
Some(Value::String(name)) => Ok(Value::Bool(this.has_own_property(name)).into()),
|
||||
_ => Ok(Value::Bool(false).into()),
|
||||
}
|
||||
}
|
||||
|
@ -75,7 +71,7 @@ pub fn has_own_property<'gc>(
|
|||
fn to_string<'gc>(
|
||||
_: &mut Avm1<'gc>,
|
||||
_: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_: ObjectCell<'gc>,
|
||||
_: Object<'gc>,
|
||||
_: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
Ok(ReturnValue::Immediate("[object Object]".into()))
|
||||
|
@ -85,13 +81,11 @@ fn to_string<'gc>(
|
|||
fn is_property_enumerable<'gc>(
|
||||
_: &mut Avm1<'gc>,
|
||||
_: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: ObjectCell<'gc>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
match args.get(0) {
|
||||
Some(Value::String(name)) => {
|
||||
Ok(Value::Bool(this.read().is_property_enumerable(name)).into())
|
||||
}
|
||||
Some(Value::String(name)) => Ok(Value::Bool(this.is_property_enumerable(name)).into()),
|
||||
_ => Ok(Value::Bool(false).into()),
|
||||
}
|
||||
}
|
||||
|
@ -100,7 +94,7 @@ fn is_property_enumerable<'gc>(
|
|||
fn is_prototype_of<'gc>(
|
||||
_: &mut Avm1<'gc>,
|
||||
_: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: ObjectCell<'gc>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
match args.get(0) {
|
||||
|
@ -109,14 +103,14 @@ fn is_prototype_of<'gc>(
|
|||
Ok(ob) => ob,
|
||||
Err(_) => return Ok(Value::Bool(false).into()),
|
||||
};
|
||||
let mut proto = ob.read().proto();
|
||||
let mut proto = ob.proto();
|
||||
|
||||
while let Some(proto_ob) = proto {
|
||||
if GcCell::ptr_eq(this, proto_ob) {
|
||||
if Object::ptr_eq(this, proto_ob) {
|
||||
return Ok(Value::Bool(true).into());
|
||||
}
|
||||
|
||||
proto = proto_ob.read().proto();
|
||||
proto = proto_ob.proto();
|
||||
}
|
||||
|
||||
Ok(Value::Bool(false).into())
|
||||
|
@ -129,7 +123,7 @@ fn is_prototype_of<'gc>(
|
|||
fn value_of<'gc>(
|
||||
_: &mut Avm1<'gc>,
|
||||
_: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: ObjectCell<'gc>,
|
||||
this: Object<'gc>,
|
||||
_: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
Ok(ReturnValue::Immediate(this.into()))
|
||||
|
@ -146,12 +140,10 @@ fn value_of<'gc>(
|
|||
/// bare objects for both and let this function fill Object for you.
|
||||
pub fn fill_proto<'gc>(
|
||||
gc_context: MutationContext<'gc, '_>,
|
||||
object_proto: ObjectCell<'gc>,
|
||||
fn_proto: ObjectCell<'gc>,
|
||||
mut object_proto: Object<'gc>,
|
||||
fn_proto: Object<'gc>,
|
||||
) {
|
||||
let mut ob_proto_write = object_proto.write(gc_context);
|
||||
|
||||
ob_proto_write
|
||||
object_proto
|
||||
.as_script_object_mut()
|
||||
.unwrap()
|
||||
.force_set_function(
|
||||
|
@ -161,7 +153,7 @@ pub fn fill_proto<'gc>(
|
|||
DontDelete | DontEnum,
|
||||
Some(fn_proto),
|
||||
);
|
||||
ob_proto_write
|
||||
object_proto
|
||||
.as_script_object_mut()
|
||||
.unwrap()
|
||||
.force_set_function(
|
||||
|
@ -171,7 +163,7 @@ pub fn fill_proto<'gc>(
|
|||
DontDelete | DontEnum,
|
||||
Some(fn_proto),
|
||||
);
|
||||
ob_proto_write
|
||||
object_proto
|
||||
.as_script_object_mut()
|
||||
.unwrap()
|
||||
.force_set_function(
|
||||
|
@ -181,7 +173,7 @@ pub fn fill_proto<'gc>(
|
|||
DontDelete | DontEnum,
|
||||
Some(fn_proto),
|
||||
);
|
||||
ob_proto_write
|
||||
object_proto
|
||||
.as_script_object_mut()
|
||||
.unwrap()
|
||||
.force_set_function(
|
||||
|
@ -191,7 +183,7 @@ pub fn fill_proto<'gc>(
|
|||
DontDelete | DontEnum,
|
||||
Some(fn_proto),
|
||||
);
|
||||
ob_proto_write
|
||||
object_proto
|
||||
.as_script_object_mut()
|
||||
.unwrap()
|
||||
.force_set_function(
|
||||
|
@ -201,7 +193,7 @@ pub fn fill_proto<'gc>(
|
|||
DontDelete | DontEnum,
|
||||
Some(fn_proto),
|
||||
);
|
||||
ob_proto_write
|
||||
object_proto
|
||||
.as_script_object_mut()
|
||||
.unwrap()
|
||||
.force_set_function(
|
||||
|
|
|
@ -6,15 +6,21 @@ use crate::avm1::return_value::ReturnValue;
|
|||
use crate::avm1::{Avm1, Error, ScriptObject, UpdateContext, Value};
|
||||
use crate::display_object::DisplayNode;
|
||||
use enumset::EnumSet;
|
||||
use gc_arena::{Collect, GcCell};
|
||||
use gc_arena::{Collect, MutationContext};
|
||||
use ruffle_macros::enum_trait_object;
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub type ObjectCell<'gc> = GcCell<'gc, Box<dyn Object<'gc> + 'gc>>;
|
||||
|
||||
/// Represents an object that can be directly interacted with by the AVM
|
||||
/// runtime.
|
||||
pub trait Object<'gc>: 'gc + Collect + Debug {
|
||||
#[enum_trait_object(
|
||||
#[derive(Clone, Collect, Debug, Copy)]
|
||||
#[collect(no_drop)]
|
||||
pub enum Object<'gc> {
|
||||
ScriptObject(ScriptObject<'gc>),
|
||||
}
|
||||
)]
|
||||
pub trait TObject<'gc>: 'gc + Collect + Debug {
|
||||
/// Retrieve a named property from this object exclusively.
|
||||
///
|
||||
/// This function takes a redundant `this` parameter which should be
|
||||
|
@ -28,7 +34,7 @@ pub trait Object<'gc>: 'gc + Collect + Debug {
|
|||
name: &str,
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: ObjectCell<'gc>,
|
||||
this: Object<'gc>,
|
||||
) -> Result<ReturnValue<'gc>, Error>;
|
||||
|
||||
/// Retrieve a named property from the object, or it's prototype.
|
||||
|
@ -37,7 +43,7 @@ pub trait Object<'gc>: 'gc + Collect + Debug {
|
|||
name: &str,
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: ObjectCell<'gc>,
|
||||
this: Object<'gc>,
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
if self.has_own_property(name) {
|
||||
self.get_local(name, avm, context, this)
|
||||
|
@ -50,11 +56,11 @@ pub trait Object<'gc>: 'gc + Collect + Debug {
|
|||
return Err("Encountered an excessively deep prototype chain.".into());
|
||||
}
|
||||
|
||||
if proto.unwrap().read().has_own_property(name) {
|
||||
return proto.unwrap().read().get_local(name, avm, context, this);
|
||||
if proto.unwrap().has_own_property(name) {
|
||||
return proto.unwrap().get_local(name, avm, context, this);
|
||||
}
|
||||
|
||||
proto = proto.unwrap().read().proto();
|
||||
proto = proto.unwrap().proto();
|
||||
depth += 1;
|
||||
}
|
||||
|
||||
|
@ -68,12 +74,12 @@ pub trait Object<'gc>: 'gc + Collect + Debug {
|
|||
/// the object's own `GcCell`, so that it can pass it to user-defined
|
||||
/// overrides that may need to interact with the underlying object.
|
||||
fn set(
|
||||
&mut self,
|
||||
&self,
|
||||
name: &str,
|
||||
value: Value<'gc>,
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: ObjectCell<'gc>,
|
||||
this: Object<'gc>,
|
||||
) -> Result<(), Error>;
|
||||
|
||||
/// Call the underlying object.
|
||||
|
@ -85,7 +91,7 @@ pub trait Object<'gc>: 'gc + Collect + Debug {
|
|||
&self,
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: ObjectCell<'gc>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error>;
|
||||
|
||||
|
@ -104,21 +110,21 @@ pub trait Object<'gc>: 'gc + Collect + Debug {
|
|||
&self,
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: ObjectCell<'gc>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ObjectCell<'gc>, Error>;
|
||||
) -> Result<Object<'gc>, Error>;
|
||||
|
||||
/// Delete a named property from the object.
|
||||
///
|
||||
/// Returns false if the property cannot be deleted.
|
||||
fn delete(&mut self, name: &str) -> bool;
|
||||
fn delete(&self, gc_context: MutationContext<'gc, '_>, name: &str) -> bool;
|
||||
|
||||
/// Retrieve the `__proto__` of a given object.
|
||||
///
|
||||
/// The proto is another object used to resolve methods across a class of
|
||||
/// multiple objects. It should also be accessible as `__proto__` from
|
||||
/// `get`.
|
||||
fn proto(&self) -> Option<ObjectCell<'gc>>;
|
||||
fn proto(&self) -> Option<Object<'gc>>;
|
||||
|
||||
/// Define a value on an object.
|
||||
///
|
||||
|
@ -131,7 +137,13 @@ pub trait Object<'gc>: 'gc + Collect + Debug {
|
|||
/// It is not guaranteed that all objects accept value definitions,
|
||||
/// especially if a property name conflicts with a built-in property, such
|
||||
/// as `__proto__`.
|
||||
fn define_value(&mut self, name: &str, value: Value<'gc>, attributes: EnumSet<Attribute>);
|
||||
fn define_value(
|
||||
&self,
|
||||
gc_context: MutationContext<'gc, '_>,
|
||||
name: &str,
|
||||
value: Value<'gc>,
|
||||
attributes: EnumSet<Attribute>,
|
||||
);
|
||||
|
||||
/// Define a virtual property onto a given object.
|
||||
///
|
||||
|
@ -144,7 +156,8 @@ pub trait Object<'gc>: 'gc + Collect + Debug {
|
|||
/// especially if a property name conflicts with a built-in property, such
|
||||
/// as `__proto__`.
|
||||
fn add_property(
|
||||
&mut self,
|
||||
&self,
|
||||
gc_context: MutationContext<'gc, '_>,
|
||||
name: &str,
|
||||
get: Executable<'gc>,
|
||||
set: Option<Executable<'gc>>,
|
||||
|
@ -184,4 +197,14 @@ pub trait Object<'gc>: 'gc + Collect + Debug {
|
|||
|
||||
/// Get the underlying executable for this object, if it exists.
|
||||
fn as_executable(&self) -> Option<Executable<'gc>>;
|
||||
|
||||
fn as_ptr(&self) -> *const ObjectPtr;
|
||||
}
|
||||
|
||||
pub enum ObjectPtr {}
|
||||
|
||||
impl<'gc> Object<'gc> {
|
||||
pub fn ptr_eq(a: Object<'gc>, b: Object<'gc>) -> bool {
|
||||
a.as_ptr() == b.as_ptr()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
use self::Attribute::*;
|
||||
use crate::avm1::function::Executable;
|
||||
use crate::avm1::return_value::ReturnValue;
|
||||
use crate::avm1::{Avm1, Error, ObjectCell, UpdateContext, Value};
|
||||
use crate::avm1::{Avm1, Error, Object, UpdateContext, Value};
|
||||
use core::fmt;
|
||||
use enumset::{EnumSet, EnumSetType};
|
||||
use std::mem::replace;
|
||||
|
@ -37,7 +37,7 @@ impl<'gc> Property<'gc> {
|
|||
&self,
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: ObjectCell<'gc>,
|
||||
this: Object<'gc>,
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
match self {
|
||||
Property::Virtual { get, .. } => get.exec(avm, context, this, &[]),
|
||||
|
@ -55,7 +55,7 @@ impl<'gc> Property<'gc> {
|
|||
&mut self,
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: ObjectCell<'gc>,
|
||||
this: Object<'gc>,
|
||||
new_value: impl Into<Value<'gc>>,
|
||||
) -> Result<bool, Error> {
|
||||
match self {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! Return value enum
|
||||
|
||||
use crate::avm1::activation::Activation;
|
||||
use crate::avm1::{Avm1, Error, ObjectCell, Value};
|
||||
use crate::avm1::{Avm1, Error, Object, Value};
|
||||
use crate::context::UpdateContext;
|
||||
use gc_arena::{Collect, GcCell};
|
||||
use std::fmt;
|
||||
|
@ -112,6 +112,7 @@ impl<'gc> ReturnValue<'gc> {
|
|||
/// Panic if a value is not immediate.
|
||||
///
|
||||
/// This should only be used in test assertions.
|
||||
#[cfg(test)]
|
||||
pub fn unwrap_immediate(self) -> Value<'gc> {
|
||||
use ReturnValue::*;
|
||||
|
||||
|
@ -146,8 +147,8 @@ impl<'gc> From<bool> for ReturnValue<'gc> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'gc> From<ObjectCell<'gc>> for ReturnValue<'gc> {
|
||||
fn from(object: ObjectCell<'gc>) -> Self {
|
||||
impl<'gc> From<Object<'gc>> for ReturnValue<'gc> {
|
||||
fn from(object: Object<'gc>) -> Self {
|
||||
ReturnValue::Immediate(Value::Object(object))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
//! Represents AVM1 scope chain resolution.
|
||||
|
||||
use crate::avm1::return_value::ReturnValue;
|
||||
use crate::avm1::{Avm1, Error, Object, ObjectCell, ScriptObject, UpdateContext, Value};
|
||||
use crate::avm1::{Avm1, Error, Object, ScriptObject, TObject, UpdateContext, Value};
|
||||
use enumset::EnumSet;
|
||||
use gc_arena::{GcCell, MutationContext};
|
||||
use std::cell::{Ref, RefMut};
|
||||
use std::cell::Ref;
|
||||
|
||||
/// Indicates what kind of scope a scope is.
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
|
@ -30,7 +30,7 @@ pub enum ScopeClass {
|
|||
pub struct Scope<'gc> {
|
||||
parent: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
class: ScopeClass,
|
||||
values: ObjectCell<'gc>,
|
||||
values: Object<'gc>,
|
||||
}
|
||||
|
||||
unsafe impl<'gc> gc_arena::Collect for Scope<'gc> {
|
||||
|
@ -43,7 +43,7 @@ unsafe impl<'gc> gc_arena::Collect for Scope<'gc> {
|
|||
|
||||
impl<'gc> Scope<'gc> {
|
||||
/// Construct a global scope (one without a parent).
|
||||
pub fn from_global_object(globals: ObjectCell<'gc>) -> Scope<'gc> {
|
||||
pub fn from_global_object(globals: Object<'gc>) -> Scope<'gc> {
|
||||
Scope {
|
||||
parent: None,
|
||||
class: ScopeClass::Global,
|
||||
|
@ -119,7 +119,7 @@ impl<'gc> Scope<'gc> {
|
|||
/// scope has been replaced with another given object.
|
||||
pub fn new_target_scope(
|
||||
mut parent: GcCell<'gc, Self>,
|
||||
clip: ObjectCell<'gc>,
|
||||
clip: Object<'gc>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
) -> GcCell<'gc, Self> {
|
||||
let mut bottom_scope = None;
|
||||
|
@ -176,7 +176,7 @@ impl<'gc> Scope<'gc> {
|
|||
/// scope. This requires some scope chain juggling.
|
||||
pub fn new_with_scope(
|
||||
locals: GcCell<'gc, Self>,
|
||||
with_object: ObjectCell<'gc>,
|
||||
with_object: Object<'gc>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
) -> GcCell<'gc, Self> {
|
||||
let parent_scope = locals.read().parent;
|
||||
|
@ -204,7 +204,7 @@ impl<'gc> Scope<'gc> {
|
|||
pub fn new(
|
||||
parent: GcCell<'gc, Self>,
|
||||
class: ScopeClass,
|
||||
with_object: ObjectCell<'gc>,
|
||||
with_object: Object<'gc>,
|
||||
) -> Scope<'gc> {
|
||||
Scope {
|
||||
parent: Some(parent),
|
||||
|
@ -214,18 +214,14 @@ impl<'gc> Scope<'gc> {
|
|||
}
|
||||
|
||||
/// Returns a reference to the current local scope object.
|
||||
pub fn locals(&self) -> Ref<Box<dyn Object<'gc>>> {
|
||||
self.values.read()
|
||||
}
|
||||
|
||||
/// Returns a gc cell of the current local scope object.
|
||||
pub fn locals_cell(&self) -> ObjectCell<'gc> {
|
||||
self.values.to_owned()
|
||||
pub fn locals(&self) -> &Object<'gc> {
|
||||
&self.values
|
||||
}
|
||||
|
||||
/// Returns a reference to the current local scope object for mutation.
|
||||
pub fn locals_mut(&self, mc: MutationContext<'gc, '_>) -> RefMut<Box<dyn Object<'gc>>> {
|
||||
self.values.write(mc)
|
||||
#[allow(dead_code)]
|
||||
pub fn locals_mut(&mut self) -> &mut Object<'gc> {
|
||||
&mut self.values
|
||||
}
|
||||
|
||||
/// Returns a reference to the parent scope object.
|
||||
|
@ -246,7 +242,7 @@ impl<'gc> Scope<'gc> {
|
|||
name: &str,
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: ObjectCell<'gc>,
|
||||
this: Object<'gc>,
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
if self.locals().has_property(name) {
|
||||
return self.locals().get(name, avm, context, this);
|
||||
|
@ -286,11 +282,10 @@ impl<'gc> Scope<'gc> {
|
|||
value: Value<'gc>,
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: ObjectCell<'gc>,
|
||||
this: Object<'gc>,
|
||||
) -> Result<Option<Value<'gc>>, Error> {
|
||||
if self.locals().has_property(name) && self.locals().is_property_overwritable(name) {
|
||||
self.locals_mut(context.gc_context)
|
||||
.set(name, value, avm, context, this)?;
|
||||
self.locals().set(name, value, avm, context, this)?;
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
|
@ -308,14 +303,14 @@ impl<'gc> Scope<'gc> {
|
|||
/// chain. As a result, this function always force sets a property on the
|
||||
/// local object and does not traverse the scope chain.
|
||||
pub fn define(&self, name: &str, value: impl Into<Value<'gc>>, mc: MutationContext<'gc, '_>) {
|
||||
self.locals_mut(mc)
|
||||
.define_value(name, value.into(), EnumSet::empty());
|
||||
self.locals()
|
||||
.define_value(mc, name, value.into(), EnumSet::empty());
|
||||
}
|
||||
|
||||
/// Delete a value from scope
|
||||
pub fn delete(&self, name: &str, mc: MutationContext<'gc, '_>) -> bool {
|
||||
if self.locals().has_property(name) {
|
||||
return self.locals_mut(mc).delete(name);
|
||||
return self.locals().delete(mc, name);
|
||||
}
|
||||
|
||||
if let Some(scope) = self.parent() {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use crate::avm1::function::{Executable, NativeFunction};
|
||||
use crate::avm1::property::{Attribute, Property};
|
||||
use crate::avm1::return_value::ReturnValue;
|
||||
use crate::avm1::{Avm1, Error, Object, ObjectCell, UpdateContext, Value};
|
||||
use crate::avm1::{Avm1, Error, Object, ObjectPtr, TObject, UpdateContext, Value};
|
||||
use crate::display_object::DisplayNode;
|
||||
use core::fmt;
|
||||
use enumset::EnumSet;
|
||||
use gc_arena::{GcCell, MutationContext};
|
||||
use gc_arena::{Collect, GcCell, MutationContext};
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
|
@ -13,16 +13,19 @@ pub const TYPE_OF_OBJECT: &str = "object";
|
|||
pub const TYPE_OF_FUNCTION: &str = "function";
|
||||
pub const TYPE_OF_MOVIE_CLIP: &str = "movieclip";
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ScriptObject<'gc> {
|
||||
prototype: Option<ObjectCell<'gc>>,
|
||||
#[derive(Debug, Copy, Clone, Collect)]
|
||||
#[collect(no_drop)]
|
||||
pub struct ScriptObject<'gc>(GcCell<'gc, ScriptObjectData<'gc>>);
|
||||
|
||||
pub struct ScriptObjectData<'gc> {
|
||||
prototype: Option<Object<'gc>>,
|
||||
display_node: Option<DisplayNode<'gc>>,
|
||||
values: HashMap<String, Property<'gc>>,
|
||||
function: Option<Executable<'gc>>,
|
||||
type_of: &'static str,
|
||||
}
|
||||
|
||||
unsafe impl<'gc> gc_arena::Collect for ScriptObject<'gc> {
|
||||
unsafe impl<'gc> Collect for ScriptObjectData<'gc> {
|
||||
fn trace(&self, cc: gc_arena::CollectionContext) {
|
||||
self.prototype.trace(cc);
|
||||
self.display_node.trace(cc);
|
||||
|
@ -31,7 +34,7 @@ unsafe impl<'gc> gc_arena::Collect for ScriptObject<'gc> {
|
|||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ScriptObject<'_> {
|
||||
impl fmt::Debug for ScriptObjectData<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Object")
|
||||
.field("prototype", &self.prototype)
|
||||
|
@ -44,33 +47,37 @@ impl fmt::Debug for ScriptObject<'_> {
|
|||
|
||||
impl<'gc> ScriptObject<'gc> {
|
||||
pub fn object(
|
||||
_gc_context: MutationContext<'gc, '_>,
|
||||
proto: Option<ObjectCell<'gc>>,
|
||||
) -> ScriptObject<'gc> {
|
||||
ScriptObject {
|
||||
prototype: proto,
|
||||
type_of: TYPE_OF_OBJECT,
|
||||
display_node: None,
|
||||
values: HashMap::new(),
|
||||
function: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs and allocates an empty but normal object in one go.
|
||||
pub fn object_cell(
|
||||
gc_context: MutationContext<'gc, '_>,
|
||||
proto: Option<ObjectCell<'gc>>,
|
||||
) -> ObjectCell<'gc> {
|
||||
GcCell::allocate(
|
||||
proto: Option<Object<'gc>>,
|
||||
) -> ScriptObject<'gc> {
|
||||
ScriptObject(GcCell::allocate(
|
||||
gc_context,
|
||||
Box::new(ScriptObject {
|
||||
ScriptObjectData {
|
||||
prototype: proto,
|
||||
type_of: TYPE_OF_OBJECT,
|
||||
display_node: None,
|
||||
values: HashMap::new(),
|
||||
function: None,
|
||||
}),
|
||||
)
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
/// Constructs and allocates an empty but normal object in one go.
|
||||
pub fn object_cell(
|
||||
gc_context: MutationContext<'gc, '_>,
|
||||
proto: Option<Object<'gc>>,
|
||||
) -> Object<'gc> {
|
||||
ScriptObject(GcCell::allocate(
|
||||
gc_context,
|
||||
ScriptObjectData {
|
||||
prototype: proto,
|
||||
type_of: TYPE_OF_OBJECT,
|
||||
display_node: None,
|
||||
values: HashMap::new(),
|
||||
function: None,
|
||||
},
|
||||
))
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Constructs an object with no values, not even builtins.
|
||||
|
@ -78,28 +85,35 @@ impl<'gc> ScriptObject<'gc> {
|
|||
/// Intended for constructing scope chains, since they exclusively use the
|
||||
/// object values, but can't just have a hashmap because of `with` and
|
||||
/// friends.
|
||||
pub fn bare_object() -> Self {
|
||||
ScriptObject {
|
||||
prototype: None,
|
||||
type_of: TYPE_OF_OBJECT,
|
||||
display_node: None,
|
||||
values: HashMap::new(),
|
||||
function: None,
|
||||
}
|
||||
pub fn bare_object(gc_context: MutationContext<'gc, '_>) -> Self {
|
||||
ScriptObject(GcCell::allocate(
|
||||
gc_context,
|
||||
ScriptObjectData {
|
||||
prototype: None,
|
||||
type_of: TYPE_OF_OBJECT,
|
||||
display_node: None,
|
||||
values: HashMap::new(),
|
||||
function: None,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
/// Construct a function sans prototype.
|
||||
pub fn bare_function(
|
||||
gc_context: MutationContext<'gc, '_>,
|
||||
function: impl Into<Executable<'gc>>,
|
||||
fn_proto: Option<ObjectCell<'gc>>,
|
||||
fn_proto: Option<Object<'gc>>,
|
||||
) -> Self {
|
||||
ScriptObject {
|
||||
prototype: fn_proto,
|
||||
type_of: TYPE_OF_FUNCTION,
|
||||
function: Some(function.into()),
|
||||
display_node: None,
|
||||
values: HashMap::new(),
|
||||
}
|
||||
ScriptObject(GcCell::allocate(
|
||||
gc_context,
|
||||
ScriptObjectData {
|
||||
prototype: fn_proto,
|
||||
type_of: TYPE_OF_FUNCTION,
|
||||
function: Some(function.into()),
|
||||
display_node: None,
|
||||
values: HashMap::new(),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
/// Construct a function from an executable and associated protos.
|
||||
|
@ -113,35 +127,36 @@ impl<'gc> ScriptObject<'gc> {
|
|||
pub fn function(
|
||||
gc_context: MutationContext<'gc, '_>,
|
||||
function: impl Into<Executable<'gc>>,
|
||||
fn_proto: Option<ObjectCell<'gc>>,
|
||||
prototype: Option<ObjectCell<'gc>>,
|
||||
) -> ObjectCell<'gc> {
|
||||
let function = GcCell::allocate(
|
||||
gc_context,
|
||||
Box::new(Self::bare_function(function, fn_proto)) as Box<dyn Object<'gc> + 'gc>,
|
||||
);
|
||||
fn_proto: Option<Object<'gc>>,
|
||||
prototype: Option<Object<'gc>>,
|
||||
) -> Object<'gc> {
|
||||
let function = Self::bare_function(gc_context, function, fn_proto).into();
|
||||
|
||||
//TODO: Can we make these proper sets or no?
|
||||
if let Some(p) = prototype {
|
||||
p.write(gc_context).define_value(
|
||||
p.define_value(
|
||||
gc_context,
|
||||
"constructor",
|
||||
Value::Object(function),
|
||||
Attribute::DontEnum.into(),
|
||||
);
|
||||
function
|
||||
.write(gc_context)
|
||||
.define_value("prototype", p.into(), EnumSet::empty());
|
||||
function.define_value(gc_context, "prototype", p.into(), EnumSet::empty());
|
||||
}
|
||||
|
||||
function
|
||||
}
|
||||
|
||||
pub fn set_display_node(&mut self, display_node: DisplayNode<'gc>) {
|
||||
self.display_node = Some(display_node);
|
||||
pub fn set_display_node(
|
||||
self,
|
||||
gc_context: MutationContext<'gc, '_>,
|
||||
display_node: DisplayNode<'gc>,
|
||||
) {
|
||||
self.0.write(gc_context).display_node = Some(display_node);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn display_node(&self) -> Option<DisplayNode<'gc>> {
|
||||
self.display_node
|
||||
self.0.read().display_node
|
||||
}
|
||||
|
||||
/// Declare a native function on the current object.
|
||||
|
@ -157,27 +172,28 @@ impl<'gc> ScriptObject<'gc> {
|
|||
function: NativeFunction<'gc>,
|
||||
gc_context: MutationContext<'gc, '_>,
|
||||
attributes: A,
|
||||
fn_proto: Option<ObjectCell<'gc>>,
|
||||
fn_proto: Option<Object<'gc>>,
|
||||
) where
|
||||
A: Into<EnumSet<Attribute>>,
|
||||
{
|
||||
self.define_value(
|
||||
gc_context,
|
||||
name,
|
||||
Value::Object(ScriptObject::function(gc_context, function, fn_proto, None)),
|
||||
attributes.into(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn set_prototype(&mut self, prototype: ObjectCell<'gc>) {
|
||||
self.prototype = Some(prototype);
|
||||
pub fn set_prototype(&mut self, gc_context: MutationContext<'gc, '_>, prototype: Object<'gc>) {
|
||||
self.0.write(gc_context).prototype = Some(prototype);
|
||||
}
|
||||
|
||||
pub fn set_type_of(&mut self, type_of: &'static str) {
|
||||
self.type_of = type_of;
|
||||
pub fn set_type_of(&mut self, gc_context: MutationContext<'gc, '_>, type_of: &'static str) {
|
||||
self.0.write(gc_context).type_of = type_of;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> Object<'gc> for ScriptObject<'gc> {
|
||||
impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
||||
/// Get the value of a particular property on this object.
|
||||
///
|
||||
/// The `avm`, `context`, and `this` parameters exist so that this object
|
||||
|
@ -191,16 +207,13 @@ impl<'gc> Object<'gc> for ScriptObject<'gc> {
|
|||
name: &str,
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: ObjectCell<'gc>,
|
||||
this: Object<'gc>,
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
if name == "__proto__" {
|
||||
return Ok(self
|
||||
.prototype
|
||||
.map_or(Value::Undefined, Value::Object)
|
||||
.into());
|
||||
return Ok(self.proto().map_or(Value::Undefined, Value::Object).into());
|
||||
}
|
||||
|
||||
if let Some(value) = self.values.get(name) {
|
||||
if let Some(value) = self.0.read().values.get(name) {
|
||||
return value.get(avm, context, this);
|
||||
}
|
||||
|
||||
|
@ -213,17 +226,22 @@ impl<'gc> Object<'gc> for ScriptObject<'gc> {
|
|||
/// the object's own `GcCell`, so that it can pass it to user-defined
|
||||
/// overrides that may need to interact with the underlying object.
|
||||
fn set(
|
||||
&mut self,
|
||||
&self,
|
||||
name: &str,
|
||||
value: Value<'gc>,
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: ObjectCell<'gc>,
|
||||
this: Object<'gc>,
|
||||
) -> Result<(), Error> {
|
||||
if name == "__proto__" {
|
||||
self.prototype = value.as_object().ok().to_owned();
|
||||
self.0.write(context.gc_context).prototype = value.as_object().ok();
|
||||
} else {
|
||||
match self.values.entry(name.to_owned()) {
|
||||
match self
|
||||
.0
|
||||
.write(context.gc_context)
|
||||
.values
|
||||
.entry(name.to_owned())
|
||||
{
|
||||
Entry::Occupied(mut entry) => {
|
||||
entry.get_mut().set(avm, context, this, value)?;
|
||||
}
|
||||
|
@ -248,10 +266,10 @@ impl<'gc> Object<'gc> for ScriptObject<'gc> {
|
|||
&self,
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: ObjectCell<'gc>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
if let Some(function) = &self.function {
|
||||
if let Some(function) = &self.0.read().function {
|
||||
function.exec(avm, context, this, args)
|
||||
} else {
|
||||
Ok(Value::Undefined.into())
|
||||
|
@ -263,22 +281,20 @@ impl<'gc> Object<'gc> for ScriptObject<'gc> {
|
|||
&self,
|
||||
_avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: ObjectCell<'gc>,
|
||||
this: Object<'gc>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<ObjectCell<'gc>, Error> {
|
||||
Ok(GcCell::allocate(
|
||||
context.gc_context,
|
||||
Box::new(ScriptObject::object(context.gc_context, Some(this))) as Box<dyn Object<'gc>>,
|
||||
))
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
Ok(ScriptObject::object(context.gc_context, Some(this)).into())
|
||||
}
|
||||
|
||||
/// Delete a named property from the object.
|
||||
///
|
||||
/// Returns false if the property cannot be deleted.
|
||||
fn delete(&mut self, name: &str) -> bool {
|
||||
if let Some(prop) = self.values.get(name) {
|
||||
fn delete(&self, gc_context: MutationContext<'gc, '_>, name: &str) -> bool {
|
||||
let mut object = self.0.write(gc_context);
|
||||
if let Some(prop) = object.values.get(name) {
|
||||
if prop.can_delete() {
|
||||
self.values.remove(name);
|
||||
object.values.remove(name);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -287,13 +303,14 @@ impl<'gc> Object<'gc> for ScriptObject<'gc> {
|
|||
}
|
||||
|
||||
fn add_property(
|
||||
&mut self,
|
||||
&self,
|
||||
gc_context: MutationContext<'gc, '_>,
|
||||
name: &str,
|
||||
get: Executable<'gc>,
|
||||
set: Option<Executable<'gc>>,
|
||||
attributes: EnumSet<Attribute>,
|
||||
) {
|
||||
self.values.insert(
|
||||
self.0.write(gc_context).values.insert(
|
||||
name.to_owned(),
|
||||
Property::Virtual {
|
||||
get,
|
||||
|
@ -303,22 +320,30 @@ impl<'gc> Object<'gc> for ScriptObject<'gc> {
|
|||
);
|
||||
}
|
||||
|
||||
fn define_value(&mut self, name: &str, value: Value<'gc>, attributes: EnumSet<Attribute>) {
|
||||
self.values
|
||||
fn define_value(
|
||||
&self,
|
||||
gc_context: MutationContext<'gc, '_>,
|
||||
name: &str,
|
||||
value: Value<'gc>,
|
||||
attributes: EnumSet<Attribute>,
|
||||
) {
|
||||
self.0
|
||||
.write(gc_context)
|
||||
.values
|
||||
.insert(name.to_string(), Property::Stored { value, attributes });
|
||||
}
|
||||
|
||||
fn proto(&self) -> Option<ObjectCell<'gc>> {
|
||||
self.prototype
|
||||
fn proto(&self) -> Option<Object<'gc>> {
|
||||
self.0.read().prototype
|
||||
}
|
||||
|
||||
/// Checks if the object has a given named property.
|
||||
fn has_property(&self, name: &str) -> bool {
|
||||
self.has_own_property(name)
|
||||
|| self
|
||||
.prototype
|
||||
.proto()
|
||||
.as_ref()
|
||||
.map_or(false, |p| p.read().has_property(name))
|
||||
.map_or(false, |p| p.has_property(name))
|
||||
}
|
||||
|
||||
/// Checks if the object has a given named property on itself (and not,
|
||||
|
@ -327,11 +352,13 @@ impl<'gc> Object<'gc> for ScriptObject<'gc> {
|
|||
if name == "__proto__" {
|
||||
return true;
|
||||
}
|
||||
self.values.contains_key(name)
|
||||
self.0.read().values.contains_key(name)
|
||||
}
|
||||
|
||||
fn is_property_overwritable(&self, name: &str) -> bool {
|
||||
self.values
|
||||
self.0
|
||||
.read()
|
||||
.values
|
||||
.get(name)
|
||||
.map(|p| p.is_overwritable())
|
||||
.unwrap_or(false)
|
||||
|
@ -339,7 +366,7 @@ impl<'gc> Object<'gc> for ScriptObject<'gc> {
|
|||
|
||||
/// Checks if a named property appears when enumerating the object.
|
||||
fn is_property_enumerable(&self, name: &str) -> bool {
|
||||
if let Some(prop) = self.values.get(name) {
|
||||
if let Some(prop) = self.0.read().values.get(name) {
|
||||
prop.is_enumerable()
|
||||
} else {
|
||||
false
|
||||
|
@ -348,11 +375,11 @@ impl<'gc> Object<'gc> for ScriptObject<'gc> {
|
|||
|
||||
/// Enumerate the object.
|
||||
fn get_keys(&self) -> HashSet<String> {
|
||||
let mut result = self
|
||||
.prototype
|
||||
.map_or_else(HashSet::new, |p| p.read().get_keys());
|
||||
let mut result = self.proto().map_or_else(HashSet::new, |p| p.get_keys());
|
||||
|
||||
self.values
|
||||
self.0
|
||||
.read()
|
||||
.values
|
||||
.iter()
|
||||
.filter_map(|(k, p)| {
|
||||
if p.is_enumerable() {
|
||||
|
@ -369,7 +396,7 @@ impl<'gc> Object<'gc> for ScriptObject<'gc> {
|
|||
}
|
||||
|
||||
fn as_string(&self) -> String {
|
||||
if self.function.is_some() {
|
||||
if self.0.read().function.is_some() {
|
||||
"[type Function]".to_string()
|
||||
} else {
|
||||
"[object Object]".to_string()
|
||||
|
@ -377,7 +404,7 @@ impl<'gc> Object<'gc> for ScriptObject<'gc> {
|
|||
}
|
||||
|
||||
fn type_of(&self) -> &'static str {
|
||||
self.type_of
|
||||
self.0.read().type_of
|
||||
}
|
||||
|
||||
fn as_script_object(&self) -> Option<&ScriptObject<'gc>> {
|
||||
|
@ -390,7 +417,7 @@ impl<'gc> Object<'gc> for ScriptObject<'gc> {
|
|||
|
||||
/// Get the underlying display node for this object, if it exists.
|
||||
fn as_display_node(&self) -> Option<DisplayNode<'gc>> {
|
||||
self.display_node
|
||||
self.0.read().display_node
|
||||
}
|
||||
|
||||
/// Returns a copy of a given function.
|
||||
|
@ -398,7 +425,11 @@ impl<'gc> Object<'gc> for ScriptObject<'gc> {
|
|||
/// TODO: We have to clone here because of how executables are stored on
|
||||
/// objects directly. This might not be a good idea for performance.
|
||||
fn as_executable(&self) -> Option<Executable<'gc>> {
|
||||
self.function.clone()
|
||||
self.0.read().function.clone()
|
||||
}
|
||||
|
||||
fn as_ptr(&self) -> *const ObjectPtr {
|
||||
self.0.as_ptr() as *const ObjectPtr
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -411,7 +442,7 @@ mod tests {
|
|||
use crate::backend::audio::NullAudioBackend;
|
||||
use crate::backend::navigator::NullNavigatorBackend;
|
||||
use crate::backend::render::NullRenderer;
|
||||
use crate::display_object::{DisplayObject, MovieClip};
|
||||
use crate::display_object::MovieClip;
|
||||
use crate::library::Library;
|
||||
use crate::prelude::*;
|
||||
use gc_arena::rootless_arena;
|
||||
|
@ -420,11 +451,7 @@ mod tests {
|
|||
|
||||
fn with_object<F, R>(swf_version: u8, test: F) -> R
|
||||
where
|
||||
F: for<'a, 'gc> FnOnce(
|
||||
&mut Avm1<'gc>,
|
||||
&mut UpdateContext<'a, 'gc, '_>,
|
||||
ObjectCell<'gc>,
|
||||
) -> R,
|
||||
F: for<'a, 'gc> FnOnce(&mut Avm1<'gc>, &mut UpdateContext<'a, 'gc, '_>, Object<'gc>) -> R,
|
||||
{
|
||||
rootless_arena(|gc_context| {
|
||||
let mut avm = Avm1::new(gc_context, swf_version);
|
||||
|
@ -457,13 +484,7 @@ mod tests {
|
|||
system_prototypes: avm.prototypes().clone(),
|
||||
};
|
||||
|
||||
let object = GcCell::allocate(
|
||||
gc_context,
|
||||
Box::new(ScriptObject::object(
|
||||
gc_context,
|
||||
Some(avm.prototypes().object),
|
||||
)) as Box<dyn Object<'_>>,
|
||||
);
|
||||
let object = ScriptObject::object(gc_context, Some(avm.prototypes().object)).into();
|
||||
|
||||
let globals = avm.global_object_cell();
|
||||
avm.insert_stack_frame(GcCell::allocate(
|
||||
|
@ -479,10 +500,7 @@ mod tests {
|
|||
fn test_get_undefined() {
|
||||
with_object(0, |avm, context, object| {
|
||||
assert_eq!(
|
||||
object
|
||||
.read()
|
||||
.get("not_defined", avm, context, object)
|
||||
.unwrap(),
|
||||
object.get("not_defined", avm, context, object).unwrap(),
|
||||
ReturnValue::Immediate(Value::Undefined)
|
||||
);
|
||||
})
|
||||
|
@ -490,23 +508,23 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_set_get() {
|
||||
with_object(0, |avm, context, object| {
|
||||
with_object(0, |avm, context, mut object| {
|
||||
object.as_script_object_mut().unwrap().define_value(
|
||||
context.gc_context,
|
||||
"forced",
|
||||
"forced".into(),
|
||||
EnumSet::empty(),
|
||||
);
|
||||
object
|
||||
.write(context.gc_context)
|
||||
.as_script_object_mut()
|
||||
.unwrap()
|
||||
.define_value("forced", "forced".into(), EnumSet::empty());
|
||||
object
|
||||
.write(context.gc_context)
|
||||
.set("natural", "natural".into(), avm, context, object)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
object.read().get("forced", avm, context, object).unwrap(),
|
||||
object.get("forced", avm, context, object).unwrap(),
|
||||
ReturnValue::Immediate("forced".into())
|
||||
);
|
||||
assert_eq!(
|
||||
object.read().get("natural", avm, context, object).unwrap(),
|
||||
object.get("natural", avm, context, object).unwrap(),
|
||||
ReturnValue::Immediate("natural".into())
|
||||
);
|
||||
})
|
||||
|
@ -514,33 +532,33 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_set_readonly() {
|
||||
with_object(0, |avm, context, object| {
|
||||
object
|
||||
.write(context.gc_context)
|
||||
.as_script_object_mut()
|
||||
.unwrap()
|
||||
.define_value("normal", "initial".into(), EnumSet::empty());
|
||||
object
|
||||
.write(context.gc_context)
|
||||
.as_script_object_mut()
|
||||
.unwrap()
|
||||
.define_value("readonly", "initial".into(), ReadOnly.into());
|
||||
with_object(0, |avm, context, mut object| {
|
||||
object.as_script_object_mut().unwrap().define_value(
|
||||
context.gc_context,
|
||||
"normal",
|
||||
"initial".into(),
|
||||
EnumSet::empty(),
|
||||
);
|
||||
object.as_script_object_mut().unwrap().define_value(
|
||||
context.gc_context,
|
||||
"readonly",
|
||||
"initial".into(),
|
||||
ReadOnly.into(),
|
||||
);
|
||||
|
||||
object
|
||||
.write(context.gc_context)
|
||||
.set("normal", "replaced".into(), avm, context, object)
|
||||
.unwrap();
|
||||
object
|
||||
.write(context.gc_context)
|
||||
.set("readonly", "replaced".into(), avm, context, object)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
object.read().get("normal", avm, context, object).unwrap(),
|
||||
object.get("normal", avm, context, object).unwrap(),
|
||||
ReturnValue::Immediate("replaced".into())
|
||||
);
|
||||
assert_eq!(
|
||||
object.read().get("readonly", avm, context, object).unwrap(),
|
||||
object.get("readonly", avm, context, object).unwrap(),
|
||||
ReturnValue::Immediate("initial".into())
|
||||
);
|
||||
})
|
||||
|
@ -548,29 +566,30 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_deletable_not_readonly() {
|
||||
with_object(0, |avm, context, object| {
|
||||
object
|
||||
.write(context.gc_context)
|
||||
.as_script_object_mut()
|
||||
.unwrap()
|
||||
.define_value("test", "initial".into(), DontDelete.into());
|
||||
with_object(0, |avm, context, mut object| {
|
||||
object.as_script_object_mut().unwrap().define_value(
|
||||
context.gc_context,
|
||||
"test",
|
||||
"initial".into(),
|
||||
DontDelete.into(),
|
||||
);
|
||||
|
||||
assert_eq!(object.write(context.gc_context).delete("test"), false);
|
||||
assert_eq!(object.delete(context.gc_context, "test"), false);
|
||||
assert_eq!(
|
||||
object.read().get("test", avm, context, object).unwrap(),
|
||||
object.get("test", avm, context, object).unwrap(),
|
||||
ReturnValue::Immediate("initial".into())
|
||||
);
|
||||
|
||||
let this = object;
|
||||
object
|
||||
.write(context.gc_context)
|
||||
.as_script_object_mut()
|
||||
.unwrap()
|
||||
.set("test", "replaced".into(), avm, context, object)
|
||||
.set("test", "replaced".into(), avm, context, this)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(object.write(context.gc_context).delete("test"), false);
|
||||
assert_eq!(object.delete(context.gc_context, "test"), false);
|
||||
assert_eq!(
|
||||
object.read().get("test", avm, context, object).unwrap(),
|
||||
object.get("test", avm, context, object).unwrap(),
|
||||
ReturnValue::Immediate("replaced".into())
|
||||
);
|
||||
})
|
||||
|
@ -578,29 +597,30 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_virtual_get() {
|
||||
with_object(0, |avm, context, object| {
|
||||
with_object(0, |avm, context, mut object| {
|
||||
let getter = Executable::Native(|_avm, _context, _this, _args| {
|
||||
Ok(ReturnValue::Immediate("Virtual!".into()))
|
||||
});
|
||||
|
||||
object
|
||||
.write(context.gc_context)
|
||||
.as_script_object_mut()
|
||||
.unwrap()
|
||||
.add_property("test", getter, None, EnumSet::empty());
|
||||
object.as_script_object_mut().unwrap().add_property(
|
||||
context.gc_context,
|
||||
"test",
|
||||
getter,
|
||||
None,
|
||||
EnumSet::empty(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
object.read().get("test", avm, context, object).unwrap(),
|
||||
object.get("test", avm, context, object).unwrap(),
|
||||
ReturnValue::Immediate("Virtual!".into())
|
||||
);
|
||||
|
||||
// This set should do nothing
|
||||
object
|
||||
.write(context.gc_context)
|
||||
.set("test", "Ignored!".into(), avm, context, object)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
object.read().get("test", avm, context, object).unwrap(),
|
||||
object.get("test", avm, context, object).unwrap(),
|
||||
ReturnValue::Immediate("Virtual!".into())
|
||||
);
|
||||
})
|
||||
|
@ -608,61 +628,58 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_delete() {
|
||||
with_object(0, |avm, context, object| {
|
||||
with_object(0, |avm, context, mut object| {
|
||||
let getter = Executable::Native(|_avm, _context, _this, _args| {
|
||||
Ok(ReturnValue::Immediate("Virtual!".into()))
|
||||
});
|
||||
|
||||
object
|
||||
.write(context.gc_context)
|
||||
.as_script_object_mut()
|
||||
.unwrap()
|
||||
.add_property("virtual", getter.clone(), None, EnumSet::empty());
|
||||
object
|
||||
.write(context.gc_context)
|
||||
.as_script_object_mut()
|
||||
.unwrap()
|
||||
.add_property("virtual_un", getter, None, DontDelete.into());
|
||||
object
|
||||
.write(context.gc_context)
|
||||
.as_script_object_mut()
|
||||
.unwrap()
|
||||
.define_value("stored", "Stored!".into(), EnumSet::empty());
|
||||
object
|
||||
.write(context.gc_context)
|
||||
.as_script_object_mut()
|
||||
.unwrap()
|
||||
.define_value("stored_un", "Stored!".into(), DontDelete.into());
|
||||
|
||||
assert_eq!(object.write(context.gc_context).delete("virtual"), true);
|
||||
assert_eq!(object.write(context.gc_context).delete("virtual_un"), false);
|
||||
assert_eq!(object.write(context.gc_context).delete("stored"), true);
|
||||
assert_eq!(object.write(context.gc_context).delete("stored_un"), false);
|
||||
assert_eq!(
|
||||
object.write(context.gc_context).delete("non_existent"),
|
||||
false
|
||||
object.as_script_object_mut().unwrap().add_property(
|
||||
context.gc_context,
|
||||
"virtual",
|
||||
getter.clone(),
|
||||
None,
|
||||
EnumSet::empty(),
|
||||
);
|
||||
object.as_script_object_mut().unwrap().add_property(
|
||||
context.gc_context,
|
||||
"virtual_un",
|
||||
getter,
|
||||
None,
|
||||
DontDelete.into(),
|
||||
);
|
||||
object.as_script_object_mut().unwrap().define_value(
|
||||
context.gc_context,
|
||||
"stored",
|
||||
"Stored!".into(),
|
||||
EnumSet::empty(),
|
||||
);
|
||||
object.as_script_object_mut().unwrap().define_value(
|
||||
context.gc_context,
|
||||
"stored_un",
|
||||
"Stored!".into(),
|
||||
DontDelete.into(),
|
||||
);
|
||||
|
||||
assert_eq!(object.delete(context.gc_context, "virtual"), true);
|
||||
assert_eq!(object.delete(context.gc_context, "virtual_un"), false);
|
||||
assert_eq!(object.delete(context.gc_context, "stored"), true);
|
||||
assert_eq!(object.delete(context.gc_context, "stored_un"), false);
|
||||
assert_eq!(object.delete(context.gc_context, "non_existent"), false);
|
||||
|
||||
assert_eq!(
|
||||
object.read().get("virtual", avm, context, object).unwrap(),
|
||||
object.get("virtual", avm, context, object).unwrap(),
|
||||
ReturnValue::Immediate(Value::Undefined)
|
||||
);
|
||||
assert_eq!(
|
||||
object
|
||||
.read()
|
||||
.get("virtual_un", avm, context, object)
|
||||
.unwrap(),
|
||||
object.get("virtual_un", avm, context, object).unwrap(),
|
||||
ReturnValue::Immediate("Virtual!".into())
|
||||
);
|
||||
assert_eq!(
|
||||
object.read().get("stored", avm, context, object).unwrap(),
|
||||
object.get("stored", avm, context, object).unwrap(),
|
||||
ReturnValue::Immediate(Value::Undefined)
|
||||
);
|
||||
assert_eq!(
|
||||
object
|
||||
.read()
|
||||
.get("stored_un", avm, context, object)
|
||||
.unwrap(),
|
||||
object.get("stored_un", avm, context, object).unwrap(),
|
||||
ReturnValue::Immediate("Stored!".into())
|
||||
);
|
||||
})
|
||||
|
@ -670,33 +687,39 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_iter_values() {
|
||||
with_object(0, |_avm, context, object| {
|
||||
with_object(0, |_avm, context, mut object| {
|
||||
let getter = Executable::Native(|_avm, _context, _this, _args| {
|
||||
Ok(ReturnValue::Immediate(Value::Null))
|
||||
});
|
||||
|
||||
object
|
||||
.write(context.gc_context)
|
||||
.as_script_object_mut()
|
||||
.unwrap()
|
||||
.define_value("stored", Value::Null, EnumSet::empty());
|
||||
object
|
||||
.write(context.gc_context)
|
||||
.as_script_object_mut()
|
||||
.unwrap()
|
||||
.define_value("stored_hidden", Value::Null, DontEnum.into());
|
||||
object
|
||||
.write(context.gc_context)
|
||||
.as_script_object_mut()
|
||||
.unwrap()
|
||||
.add_property("virtual", getter.clone(), None, EnumSet::empty());
|
||||
object
|
||||
.write(context.gc_context)
|
||||
.as_script_object_mut()
|
||||
.unwrap()
|
||||
.add_property("virtual_hidden", getter, None, DontEnum.into());
|
||||
object.as_script_object_mut().unwrap().define_value(
|
||||
context.gc_context,
|
||||
"stored",
|
||||
Value::Null,
|
||||
EnumSet::empty(),
|
||||
);
|
||||
object.as_script_object_mut().unwrap().define_value(
|
||||
context.gc_context,
|
||||
"stored_hidden",
|
||||
Value::Null,
|
||||
DontEnum.into(),
|
||||
);
|
||||
object.as_script_object_mut().unwrap().add_property(
|
||||
context.gc_context,
|
||||
"virtual",
|
||||
getter.clone(),
|
||||
None,
|
||||
EnumSet::empty(),
|
||||
);
|
||||
object.as_script_object_mut().unwrap().add_property(
|
||||
context.gc_context,
|
||||
"virtual_hidden",
|
||||
getter,
|
||||
None,
|
||||
DontEnum.into(),
|
||||
);
|
||||
|
||||
let keys = object.read().get_keys();
|
||||
let keys = object.get_keys();
|
||||
assert_eq!(keys.len(), 2);
|
||||
assert_eq!(keys.contains(&"stored".to_string()), true);
|
||||
assert_eq!(keys.contains(&"stored_hidden".to_string()), false);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::avm1::activation::Activation;
|
||||
use crate::avm1::{Avm1, ObjectCell, UpdateContext, Value};
|
||||
use crate::avm1::{Avm1, Object, UpdateContext, Value};
|
||||
use crate::backend::audio::NullAudioBackend;
|
||||
use crate::backend::navigator::NullNavigatorBackend;
|
||||
use crate::backend::render::NullRenderer;
|
||||
|
@ -13,11 +13,11 @@ use std::sync::Arc;
|
|||
|
||||
pub fn with_avm<F, R>(swf_version: u8, test: F) -> R
|
||||
where
|
||||
F: for<'a, 'gc> FnOnce(&mut Avm1<'gc>, &mut UpdateContext<'a, 'gc, '_>, ObjectCell<'gc>) -> R,
|
||||
F: for<'a, 'gc> FnOnce(&mut Avm1<'gc>, &mut UpdateContext<'a, 'gc, '_>, Object<'gc>) -> R,
|
||||
{
|
||||
fn in_the_arena<'gc, F, R>(swf_version: u8, test: F, gc_context: MutationContext<'gc, '_>) -> R
|
||||
where
|
||||
F: for<'a> FnOnce(&mut Avm1<'gc>, &mut UpdateContext<'a, 'gc, '_>, ObjectCell<'gc>) -> R,
|
||||
F: for<'a> FnOnce(&mut Avm1<'gc>, &mut UpdateContext<'a, 'gc, '_>, Object<'gc>) -> R,
|
||||
{
|
||||
let mut avm = Avm1::new(gc_context, swf_version);
|
||||
let movie_clip: Box<dyn DisplayObject> = Box::new(MovieClip::new(swf_version, gc_context));
|
||||
|
@ -54,7 +54,7 @@ where
|
|||
Activation::from_nothing(swf_version, globals, gc_context),
|
||||
));
|
||||
|
||||
let this = root.read().object().as_object().unwrap().to_owned();
|
||||
let this = root.read().object().as_object().unwrap();
|
||||
|
||||
test(&mut avm, &mut context, this)
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ macro_rules! test_method {
|
|||
for version in &$versions {
|
||||
let _ = with_avm(*version, |avm, context, _root| -> Result<(), Error> {
|
||||
let object = $object(avm, context);
|
||||
let function = object.read().get($name, avm, context, object)?.unwrap_immediate();
|
||||
let function = object.get($name, avm, context, object)?.unwrap_immediate();
|
||||
|
||||
$(
|
||||
#[allow(unused_mut)]
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::avm1::activation::Activation;
|
||||
use crate::avm1::test_utils::with_avm;
|
||||
use crate::avm1::TObject;
|
||||
use gc_arena::GcCell;
|
||||
|
||||
#[test]
|
||||
|
@ -7,14 +8,12 @@ fn locals_into_form_values() {
|
|||
with_avm(19, |avm, context, _this| {
|
||||
let my_activation =
|
||||
Activation::from_nothing(19, avm.global_object_cell(), context.gc_context);
|
||||
let my_locals = my_activation.scope().locals_cell();
|
||||
let my_locals = my_activation.scope().locals().to_owned();
|
||||
|
||||
my_locals
|
||||
.write(context.gc_context)
|
||||
.set("value1", "string".into(), avm, context, my_locals)
|
||||
.unwrap();
|
||||
my_locals
|
||||
.write(context.gc_context)
|
||||
.set("value2", 2.0.into(), avm, context, my_locals)
|
||||
.unwrap();
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
use crate::avm1::return_value::ReturnValue;
|
||||
use crate::avm1::{Avm1, Error, ObjectCell, UpdateContext};
|
||||
use gc_arena::GcCell;
|
||||
use crate::avm1::{Avm1, Error, Object, TObject, UpdateContext};
|
||||
use std::f64::NAN;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub enum Value<'gc> {
|
||||
Undefined,
|
||||
|
@ -11,7 +10,7 @@ pub enum Value<'gc> {
|
|||
Bool(bool),
|
||||
Number(f64),
|
||||
String(String),
|
||||
Object(ObjectCell<'gc>),
|
||||
Object(Object<'gc>),
|
||||
}
|
||||
|
||||
impl<'gc> From<String> for Value<'gc> {
|
||||
|
@ -32,8 +31,8 @@ impl<'gc> From<bool> for Value<'gc> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'gc> From<ObjectCell<'gc>> for Value<'gc> {
|
||||
fn from(object: ObjectCell<'gc>) -> Self {
|
||||
impl<'gc> From<Object<'gc>> for Value<'gc> {
|
||||
fn from(object: Object<'gc>) -> Self {
|
||||
Value::Object(object)
|
||||
}
|
||||
}
|
||||
|
@ -108,7 +107,7 @@ impl PartialEq for Value<'_> {
|
|||
_ => false,
|
||||
},
|
||||
Value::Object(value) => match other {
|
||||
Value::Object(other_value) => value.as_ptr() == other_value.as_ptr(),
|
||||
Value::Object(other_value) => Object::ptr_eq(*value, *other_value),
|
||||
_ => false,
|
||||
},
|
||||
}
|
||||
|
@ -213,7 +212,6 @@ impl<'gc> Value<'gc> {
|
|||
Ok(match self {
|
||||
Value::Object(object) => {
|
||||
let value_of_impl = object
|
||||
.read()
|
||||
.get("valueOf", avm, context, *object)?
|
||||
.resolve(avm, context)?;
|
||||
|
||||
|
@ -294,12 +292,12 @@ impl<'gc> Value<'gc> {
|
|||
}
|
||||
(Value::String(a), Value::String(b)) => Ok((a == b).into()),
|
||||
(Value::Bool(a), Value::Bool(b)) => Ok((a == b).into()),
|
||||
(Value::Object(a), Value::Object(b)) => Ok(GcCell::ptr_eq(*a, *b).into()),
|
||||
(Value::Object(a), Value::Object(b)) => Ok(Object::ptr_eq(*a, *b).into()),
|
||||
(Value::Object(a), Value::Null) | (Value::Object(a), Value::Undefined) => {
|
||||
Ok((a.as_ptr() == avm.global_object_cell().as_ptr()).into())
|
||||
Ok(Object::ptr_eq(*a, avm.global_object_cell()).into())
|
||||
}
|
||||
(Value::Null, Value::Object(b)) | (Value::Undefined, Value::Object(b)) => {
|
||||
Ok((b.as_ptr() == avm.global_object_cell().as_ptr()).into())
|
||||
Ok(Object::ptr_eq(*b, avm.global_object_cell()).into())
|
||||
}
|
||||
(Value::Undefined, Value::Null) => Ok(true.into()),
|
||||
(Value::Null, Value::Undefined) => Ok(true.into()),
|
||||
|
@ -376,7 +374,7 @@ impl<'gc> Value<'gc> {
|
|||
Value::Bool(v) => v.to_string(),
|
||||
Value::Number(v) => v.to_string(), // TODO(Herschel): Rounding for int?
|
||||
Value::String(v) => v,
|
||||
Value::Object(object) => object.read().as_string(),
|
||||
Value::Object(object) => object.as_string(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -389,7 +387,6 @@ impl<'gc> Value<'gc> {
|
|||
Ok(match self {
|
||||
Value::Object(object) => {
|
||||
let to_string_impl = object
|
||||
.read()
|
||||
.get("toString", avm, context, object)?
|
||||
.resolve(avm, context)?;
|
||||
let fake_args = Vec::new();
|
||||
|
@ -430,7 +427,7 @@ impl<'gc> Value<'gc> {
|
|||
Value::Number(_) => "number",
|
||||
Value::Bool(_) => "boolean",
|
||||
Value::String(_) => "string",
|
||||
Value::Object(object) => object.read().type_of(),
|
||||
Value::Object(object) => object.type_of(),
|
||||
}
|
||||
.to_string(),
|
||||
)
|
||||
|
@ -448,6 +445,7 @@ impl<'gc> Value<'gc> {
|
|||
self.as_f64().map(|n| n as i64)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn as_usize(&self) -> Result<usize, Error> {
|
||||
self.as_f64().map(|n| n as usize)
|
||||
}
|
||||
|
@ -466,9 +464,9 @@ impl<'gc> Value<'gc> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn as_object(&self) -> Result<ObjectCell<'gc>, Error> {
|
||||
pub fn as_object(&self) -> Result<Object<'gc>, Error> {
|
||||
if let Value::Object(object) = self {
|
||||
Ok(object.to_owned())
|
||||
Ok(*object)
|
||||
} else {
|
||||
Err(format!("Expected Object, found {:?}", self).into())
|
||||
}
|
||||
|
@ -478,11 +476,11 @@ impl<'gc> Value<'gc> {
|
|||
&self,
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: ObjectCell<'gc>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
if let Value::Object(object) = self {
|
||||
object.read().call(avm, context, this, args)
|
||||
object.call(avm, context, this, args)
|
||||
} else {
|
||||
Ok(Value::Undefined.into())
|
||||
}
|
||||
|
@ -493,14 +491,13 @@ impl<'gc> Value<'gc> {
|
|||
mod test {
|
||||
use crate::avm1::function::Executable;
|
||||
use crate::avm1::globals::create_globals;
|
||||
use crate::avm1::object::ObjectCell;
|
||||
use crate::avm1::object::{Object, TObject};
|
||||
use crate::avm1::return_value::ReturnValue;
|
||||
use crate::avm1::script_object::ScriptObject;
|
||||
use crate::avm1::test_utils::with_avm;
|
||||
use crate::avm1::{Avm1, Error, Value};
|
||||
use crate::context::UpdateContext;
|
||||
use enumset::EnumSet;
|
||||
use gc_arena::GcCell;
|
||||
use std::f64::{INFINITY, NAN, NEG_INFINITY};
|
||||
|
||||
#[test]
|
||||
|
@ -524,7 +521,7 @@ mod test {
|
|||
fn value_of_impl<'gc>(
|
||||
_: &mut Avm1<'gc>,
|
||||
_: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_: ObjectCell<'gc>,
|
||||
_: Object<'gc>,
|
||||
_: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
Ok(5.0.into())
|
||||
|
@ -538,8 +535,12 @@ mod test {
|
|||
);
|
||||
|
||||
let o = ScriptObject::object_cell(context.gc_context, Some(protos.object));
|
||||
o.write(context.gc_context)
|
||||
.define_value("valueOf", valueof.into(), EnumSet::empty());
|
||||
o.define_value(
|
||||
context.gc_context,
|
||||
"valueOf",
|
||||
valueof.into(),
|
||||
EnumSet::empty(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Value::Object(o).to_primitive_num(avm, context).unwrap(),
|
||||
|
@ -562,10 +563,7 @@ mod test {
|
|||
assert_eq!(f.as_number(avm, context).unwrap(), 0.0);
|
||||
assert!(n.as_number(avm, context).unwrap().is_nan());
|
||||
|
||||
let bo = Value::Object(GcCell::allocate(
|
||||
context.gc_context,
|
||||
Box::new(ScriptObject::bare_object()),
|
||||
));
|
||||
let bo = Value::Object(ScriptObject::bare_object(context.gc_context).into());
|
||||
|
||||
assert!(bo.as_number(avm, context).unwrap().is_nan());
|
||||
});
|
||||
|
@ -585,10 +583,7 @@ mod test {
|
|||
assert_eq!(f.as_number(avm, context).unwrap(), 0.0);
|
||||
assert_eq!(n.as_number(avm, context).unwrap(), 0.0);
|
||||
|
||||
let bo = Value::Object(GcCell::allocate(
|
||||
context.gc_context,
|
||||
Box::new(ScriptObject::bare_object()),
|
||||
));
|
||||
let bo = Value::Object(ScriptObject::bare_object(context.gc_context).into());
|
||||
|
||||
assert_eq!(bo.as_number(avm, context).unwrap(), 0.0);
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::avm1::{ObjectCell, Value};
|
||||
use crate::avm1::{Object, Value};
|
||||
use crate::context::{RenderContext, UpdateContext};
|
||||
use crate::player::NEWEST_PLAYER_VERSION;
|
||||
use crate::prelude::*;
|
||||
|
@ -259,7 +259,7 @@ pub trait DisplayObject<'gc>: 'gc + Collect + Debug {
|
|||
&mut self,
|
||||
_gc_context: MutationContext<'gc, '_>,
|
||||
_display_object: DisplayNode<'gc>,
|
||||
_proto: ObjectCell<'gc>,
|
||||
_proto: Object<'gc>,
|
||||
) {
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! `MovieClip` display object and support code.
|
||||
use crate::avm1::script_object::TYPE_OF_MOVIE_CLIP;
|
||||
use crate::avm1::{ObjectCell, ScriptObject, Value};
|
||||
use crate::avm1::{Object, ScriptObject, TObject, Value};
|
||||
use crate::backend::audio::AudioStreamHandle;
|
||||
use crate::character::Character;
|
||||
use crate::context::{RenderContext, UpdateContext};
|
||||
|
@ -33,7 +33,7 @@ pub struct MovieClip<'gc> {
|
|||
current_frame: FrameNumber,
|
||||
audio_stream: Option<AudioStreamHandle>,
|
||||
children: BTreeMap<Depth, DisplayNode<'gc>>,
|
||||
object: ObjectCell<'gc>,
|
||||
object: Object<'gc>,
|
||||
}
|
||||
|
||||
impl<'gc> MovieClip<'gc> {
|
||||
|
@ -47,7 +47,7 @@ impl<'gc> MovieClip<'gc> {
|
|||
current_frame: 0,
|
||||
audio_stream: None,
|
||||
children: BTreeMap::new(),
|
||||
object: GcCell::allocate(gc_context, Box::new(ScriptObject::bare_object())),
|
||||
object: ScriptObject::bare_object(gc_context).into(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,7 +78,7 @@ impl<'gc> MovieClip<'gc> {
|
|||
current_frame: 0,
|
||||
audio_stream: None,
|
||||
children: BTreeMap::new(),
|
||||
object: GcCell::allocate(gc_context, Box::new(ScriptObject::bare_object())),
|
||||
object: ScriptObject::bare_object(gc_context).into(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -626,18 +626,12 @@ impl<'gc> DisplayObject<'gc> for MovieClip<'gc> {
|
|||
&mut self,
|
||||
gc_context: MutationContext<'gc, '_>,
|
||||
display_object: DisplayNode<'gc>,
|
||||
proto: ObjectCell<'gc>,
|
||||
proto: Object<'gc>,
|
||||
) {
|
||||
let mut object = self.object.write(gc_context);
|
||||
object
|
||||
.as_script_object_mut()
|
||||
.unwrap()
|
||||
.set_display_node(display_object);
|
||||
object
|
||||
.as_script_object_mut()
|
||||
.unwrap()
|
||||
.set_type_of(TYPE_OF_MOVIE_CLIP);
|
||||
object.as_script_object_mut().unwrap().set_prototype(proto);
|
||||
let object = self.object.as_script_object_mut().unwrap();
|
||||
object.set_display_node(gc_context, display_object);
|
||||
object.set_type_of(gc_context, TYPE_OF_MOVIE_CLIP);
|
||||
object.set_prototype(gc_context, proto);
|
||||
}
|
||||
|
||||
fn object(&self) -> Value<'gc> {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::avm1::globals::SystemPrototypes;
|
||||
use crate::avm1::ObjectCell;
|
||||
use crate::avm1::Object;
|
||||
use crate::backend::audio::SoundHandle;
|
||||
use crate::character::Character;
|
||||
use crate::display_object::DisplayObject;
|
||||
|
@ -51,7 +51,7 @@ impl<'gc> Library<'gc> {
|
|||
gc_context: MutationContext<'gc, '_>,
|
||||
prototypes: &SystemPrototypes<'gc>,
|
||||
) -> Result<DisplayNode<'gc>, Box<dyn std::error::Error>> {
|
||||
let (obj, proto): (Box<dyn DisplayObject<'gc>>, ObjectCell<'gc>) = match self
|
||||
let (obj, proto): (Box<dyn DisplayObject<'gc>>, Object<'gc>) = match self
|
||||
.characters
|
||||
.get(&id)
|
||||
{
|
||||
|
|
|
@ -1973,12 +1973,14 @@
|
|||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
|
@ -1993,17 +1995,20 @@
|
|||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
|
@ -2120,7 +2125,8 @@
|
|||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
|
@ -2132,6 +2138,7 @@
|
|||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
|
@ -2146,6 +2153,7 @@
|
|||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
|
@ -2153,12 +2161,14 @@
|
|||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
|
@ -2177,6 +2187,7 @@
|
|||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
|
@ -2257,7 +2268,8 @@
|
|||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
|
@ -2269,6 +2281,7 @@
|
|||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
|
@ -2390,6 +2403,7 @@
|
|||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
|
|
Loading…
Reference in New Issue