avm1: Implement `Object.registerClass`
This commit is contained in:
parent
d850443c84
commit
041bb6b44c
|
@ -179,12 +179,7 @@ pub fn create_globals<'gc>(
|
|||
boolean::create_proto(gc_context, object_proto, function_proto);
|
||||
|
||||
//TODO: These need to be constructors and should also set `.prototype` on each one
|
||||
let object = FunctionObject::function(
|
||||
gc_context,
|
||||
Executable::Native(object::constructor),
|
||||
Some(function_proto),
|
||||
Some(object_proto),
|
||||
);
|
||||
let object = object::create_object_object(gc_context, object_proto, function_proto);
|
||||
|
||||
let color = FunctionObject::function(
|
||||
gc_context,
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
//! Object prototype
|
||||
use crate::avm1::function::{Executable, FunctionObject};
|
||||
use crate::avm1::property::Attribute::{self, *};
|
||||
use crate::avm1::return_value::ReturnValue;
|
||||
use crate::avm1::{Avm1, Error, Object, TObject, UpdateContext, Value};
|
||||
use crate::character::Character;
|
||||
use enumset::EnumSet;
|
||||
use gc_arena::MutationContext;
|
||||
|
||||
|
@ -129,6 +131,32 @@ fn value_of<'gc>(
|
|||
Ok(ReturnValue::Immediate(this.into()))
|
||||
}
|
||||
|
||||
/// Implements `Object.registerClass`
|
||||
pub fn register_class<'gc>(
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
if let Some(class_name) = args
|
||||
.get(0)
|
||||
.and_then(|v| v.clone().coerce_to_string(avm, context).ok())
|
||||
{
|
||||
if let Some(Character::MovieClip(movie_clip)) = context
|
||||
.library
|
||||
.library_for_movie_mut(context.swf.clone())
|
||||
.get_character_by_export_name(&class_name)
|
||||
{
|
||||
if let Some(constructor) = args.get(1).and_then(|v| v.as_object().ok()) {
|
||||
movie_clip.set_avm1_constructor(context.gc_context, Some(constructor));
|
||||
} else {
|
||||
movie_clip.set_avm1_constructor(context.gc_context, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Value::Undefined.into())
|
||||
}
|
||||
|
||||
/// Partially construct `Object.prototype`.
|
||||
///
|
||||
/// `__proto__` and other cross-linked properties of this object will *not*
|
||||
|
@ -259,3 +287,27 @@ pub fn as_set_prop_flags<'gc>(
|
|||
|
||||
Ok(Value::Undefined.into())
|
||||
}
|
||||
|
||||
pub fn create_object_object<'gc>(
|
||||
gc_context: MutationContext<'gc, '_>,
|
||||
proto: Object<'gc>,
|
||||
fn_proto: Object<'gc>,
|
||||
) -> Object<'gc> {
|
||||
let object_function = FunctionObject::function(
|
||||
gc_context,
|
||||
Executable::Native(constructor),
|
||||
Some(fn_proto),
|
||||
Some(proto),
|
||||
);
|
||||
let mut object = object_function.as_script_object().unwrap();
|
||||
|
||||
object.force_set_function(
|
||||
"registerClass",
|
||||
register_class,
|
||||
gc_context,
|
||||
Attribute::DontEnum | Attribute::DontDelete | Attribute::ReadOnly,
|
||||
Some(fn_proto),
|
||||
);
|
||||
|
||||
object_function
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//! `MovieClip` display object and support code.
|
||||
use crate::avm1::{Avm1, Object, StageObject, Value};
|
||||
use crate::avm1::{Avm1, Object, StageObject, TObject, Value};
|
||||
use crate::backend::audio::AudioStreamHandle;
|
||||
use crate::character::Character;
|
||||
use crate::context::{ActionType, RenderContext, UpdateContext};
|
||||
|
@ -41,6 +41,7 @@ pub struct MovieClipData<'gc> {
|
|||
object: Option<Object<'gc>>,
|
||||
clip_actions: SmallVec<[ClipAction; 2]>,
|
||||
flags: EnumSet<MovieClipFlags>,
|
||||
avm1_constructor: Option<Object<'gc>>,
|
||||
}
|
||||
|
||||
impl<'gc> MovieClip<'gc> {
|
||||
|
@ -58,6 +59,7 @@ impl<'gc> MovieClip<'gc> {
|
|||
object: None,
|
||||
clip_actions: SmallVec::new(),
|
||||
flags: EnumSet::empty(),
|
||||
avm1_constructor: None,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
@ -89,6 +91,7 @@ impl<'gc> MovieClip<'gc> {
|
|||
object: None,
|
||||
clip_actions: SmallVec::new(),
|
||||
flags: MovieClipFlags::Playing.into(),
|
||||
avm1_constructor: None,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
@ -180,6 +183,14 @@ impl<'gc> MovieClip<'gc> {
|
|||
self.0.read().static_data.total_frames
|
||||
}
|
||||
|
||||
pub fn set_avm1_constructor(
|
||||
self,
|
||||
gc_context: MutationContext<'gc, '_>,
|
||||
prototype: Option<Object<'gc>>,
|
||||
) {
|
||||
self.0.write(gc_context).avm1_constructor = prototype;
|
||||
}
|
||||
|
||||
pub fn frame_label_to_number(self, frame_label: &str) -> Option<FrameNumber> {
|
||||
// Frame labels are case insensitive.
|
||||
let label = frame_label.to_ascii_lowercase();
|
||||
|
@ -386,12 +397,32 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
|
|||
|
||||
fn post_instantiation(
|
||||
&mut self,
|
||||
_avm: &mut Avm1<'gc>,
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
display_object: DisplayObject<'gc>,
|
||||
) {
|
||||
let mut mc = self.0.write(context.gc_context);
|
||||
if mc.object.is_none() {
|
||||
if let Some(constructor) = mc.avm1_constructor {
|
||||
if let Ok(prototype) = constructor
|
||||
.get("prototype", avm, context)
|
||||
.and_then(|v| v.resolve(avm, context))
|
||||
.and_then(|v| v.as_object())
|
||||
{
|
||||
let object: Object<'gc> = StageObject::for_display_object(
|
||||
context.gc_context,
|
||||
display_object,
|
||||
Some(prototype),
|
||||
)
|
||||
.into();
|
||||
mc.object = Some(object);
|
||||
if let Ok(result) = constructor.call(avm, context, object, &[]) {
|
||||
let _ = result.resolve(avm, context);
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let object = StageObject::for_display_object(
|
||||
context.gc_context,
|
||||
display_object,
|
||||
|
@ -432,6 +463,7 @@ unsafe impl<'gc> Collect for MovieClipData<'gc> {
|
|||
self.base.trace(cc);
|
||||
self.static_data.trace(cc);
|
||||
self.object.trace(cc);
|
||||
self.avm1_constructor.trace(cc);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -126,6 +126,7 @@ swf_tests! {
|
|||
(equals2_swf5, "avm1/equals2_swf5", 1),
|
||||
(equals2_swf6, "avm1/equals2_swf6", 1),
|
||||
(equals2_swf7, "avm1/equals2_swf7", 1),
|
||||
(register_class, "avm1/register_class", 1),
|
||||
(set_variable_scope, "avm1/set_variable_scope", 1),
|
||||
(slash_syntax, "avm1/slash_syntax", 2),
|
||||
(strictequals_swf6, "avm1/strictequals_swf6", 1),
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
Custom movieclip!
|
||||
// typeof custom_movieclip
|
||||
movieclip
|
||||
// custom_movieclip.__proto__ == custom_mc.prototype
|
||||
true
|
||||
// custom_movieclip.__proto__ == MovieClip.prototype
|
||||
false
|
||||
// custom_movieclip.isCustom()
|
||||
true
|
||||
|
||||
// typeof normal_movieclip
|
||||
movieclip
|
||||
// normal_movieclip.__proto__ == custom_mc.prototype
|
||||
false
|
||||
// normal_movieclip.__proto__ == MovieClip.prototype
|
||||
true
|
||||
// normal_movieclip.isCustom()
|
||||
undefined
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue