2019-09-03 00:44:24 +00:00
|
|
|
use crate::avm1::{ActionContext, Value, Avm1};
|
2019-08-28 23:29:43 +00:00
|
|
|
use crate::display_object::DisplayNode;
|
|
|
|
use core::fmt;
|
|
|
|
use gc_arena::{GcCell, MutationContext};
|
2019-08-26 22:53:50 +00:00
|
|
|
use std::collections::HashMap;
|
|
|
|
|
2019-08-28 23:29:43 +00:00
|
|
|
pub type NativeFunction<'gc> =
|
2019-09-03 00:44:24 +00:00
|
|
|
fn(&mut Avm1<'gc>, &mut ActionContext<'_, 'gc, '_>, GcCell<'gc, Object<'gc>>, &[Value<'gc>]) -> Value<'gc>;
|
2019-08-28 23:29:43 +00:00
|
|
|
|
2019-08-31 12:09:37 +00:00
|
|
|
pub const TYPE_OF_OBJECT: &str = "object";
|
|
|
|
pub const TYPE_OF_FUNCTION: &str = "function";
|
|
|
|
pub const TYPE_OF_MOVIE_CLIP: &str = "movieclip";
|
|
|
|
|
2019-08-31 16:28:28 +00:00
|
|
|
fn default_to_string<'gc>(
|
2019-09-03 00:44:24 +00:00
|
|
|
_: &mut Avm1<'gc>,
|
2019-09-02 17:28:38 +00:00
|
|
|
_: &mut ActionContext<'_, 'gc, '_>,
|
2019-08-31 16:28:28 +00:00
|
|
|
_: GcCell<'gc, Object<'gc>>,
|
|
|
|
_: &[Value<'gc>],
|
|
|
|
) -> Value<'gc> {
|
2019-08-31 15:54:15 +00:00
|
|
|
Value::String("[Object object]".to_string())
|
|
|
|
}
|
|
|
|
|
2019-08-31 12:09:37 +00:00
|
|
|
#[derive(Clone)]
|
2019-08-26 22:53:50 +00:00
|
|
|
pub struct Object<'gc> {
|
2019-08-28 23:29:43 +00:00
|
|
|
display_node: Option<DisplayNode<'gc>>,
|
2019-08-26 22:53:50 +00:00
|
|
|
values: HashMap<String, Value<'gc>>,
|
2019-08-28 23:29:43 +00:00
|
|
|
function: Option<NativeFunction<'gc>>,
|
2019-08-31 12:09:37 +00:00
|
|
|
type_of: &'static str,
|
2019-08-28 23:29:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
unsafe impl<'gc> gc_arena::Collect for Object<'gc> {
|
|
|
|
fn trace(&self, cc: gc_arena::CollectionContext) {
|
|
|
|
self.display_node.trace(cc);
|
|
|
|
self.values.trace(cc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Debug for Object<'_> {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
f.debug_struct("Object")
|
|
|
|
.field("display_node", &self.display_node)
|
|
|
|
.field("values", &self.values)
|
|
|
|
.field("function", &self.function.is_some())
|
|
|
|
.finish()
|
|
|
|
}
|
2019-08-26 22:53:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'gc> Object<'gc> {
|
2019-08-31 15:54:15 +00:00
|
|
|
pub fn object(gc_context: MutationContext<'gc, '_>) -> Self {
|
|
|
|
let mut result = Self {
|
2019-08-31 12:09:37 +00:00
|
|
|
type_of: TYPE_OF_OBJECT,
|
|
|
|
display_node: None,
|
|
|
|
values: HashMap::new(),
|
|
|
|
function: None,
|
2019-08-31 15:54:15 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
result.set_function("toString", default_to_string, gc_context);
|
|
|
|
|
|
|
|
result
|
2019-08-28 23:29:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn function(function: NativeFunction<'gc>) -> Self {
|
2019-08-26 22:53:50 +00:00
|
|
|
Self {
|
2019-08-31 12:09:37 +00:00
|
|
|
type_of: TYPE_OF_FUNCTION,
|
2019-08-28 23:29:43 +00:00
|
|
|
function: Some(function),
|
2019-08-31 12:09:37 +00:00
|
|
|
display_node: None,
|
|
|
|
values: HashMap::new(),
|
2019-08-26 22:53:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-28 23:29:43 +00:00
|
|
|
pub fn set_display_node(&mut self, display_node: DisplayNode<'gc>) {
|
|
|
|
self.display_node = Some(display_node);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn display_node(&self) -> Option<DisplayNode<'gc>> {
|
|
|
|
self.display_node
|
2019-08-26 22:53:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set(&mut self, name: &str, value: Value<'gc>) {
|
|
|
|
self.values.insert(name.to_owned(), value);
|
|
|
|
}
|
2019-08-28 23:29:43 +00:00
|
|
|
|
2019-09-02 20:19:09 +00:00
|
|
|
pub fn set_object(&mut self, name: &str, object: GcCell<'gc, Object<'gc>>) {
|
|
|
|
self.values.insert(name.to_owned(), Value::Object(object));
|
|
|
|
}
|
|
|
|
|
2019-08-30 18:37:30 +00:00
|
|
|
pub fn set_function(
|
|
|
|
&mut self,
|
|
|
|
name: &str,
|
|
|
|
function: NativeFunction<'gc>,
|
|
|
|
gc_context: MutationContext<'gc, '_>,
|
|
|
|
) {
|
|
|
|
self.set(
|
|
|
|
name,
|
|
|
|
Value::Object(GcCell::allocate(gc_context, Object::function(function))),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-08-28 23:29:43 +00:00
|
|
|
pub fn get(&self, name: &str) -> Value<'gc> {
|
|
|
|
if let Some(value) = self.values.get(name) {
|
|
|
|
return value.to_owned();
|
|
|
|
}
|
|
|
|
Value::Undefined
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn has_property(&self, name: &str) -> bool {
|
|
|
|
self.values.contains_key(name)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn has_own_property(&self, name: &str) -> bool {
|
|
|
|
self.values.contains_key(name)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn call(
|
|
|
|
&self,
|
2019-09-03 00:44:24 +00:00
|
|
|
avm: &mut Avm1<'gc>,
|
2019-09-02 17:28:38 +00:00
|
|
|
context: &mut ActionContext<'_, 'gc, '_>,
|
2019-08-28 23:29:43 +00:00
|
|
|
this: GcCell<'gc, Object<'gc>>,
|
|
|
|
args: &[Value<'gc>],
|
|
|
|
) -> Value<'gc> {
|
|
|
|
if let Some(function) = self.function {
|
2019-09-03 00:44:24 +00:00
|
|
|
function(avm, context, this, args)
|
2019-08-28 23:29:43 +00:00
|
|
|
} else {
|
|
|
|
Value::Undefined
|
|
|
|
}
|
|
|
|
}
|
2019-08-31 12:09:37 +00:00
|
|
|
|
2019-08-31 16:28:28 +00:00
|
|
|
pub fn as_string(&self) -> String {
|
|
|
|
if self.function.is_some() {
|
|
|
|
"[type Function]".to_string()
|
|
|
|
} else {
|
|
|
|
"[object Object]".to_string()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-31 12:09:37 +00:00
|
|
|
pub fn set_type_of(&mut self, type_of: &'static str) {
|
|
|
|
self.type_of = type_of;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn type_of(&self) -> &'static str {
|
|
|
|
self.type_of
|
|
|
|
}
|
2019-08-26 22:53:50 +00:00
|
|
|
}
|