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);
|
boolean::create_proto(gc_context, object_proto, function_proto);
|
||||||
|
|
||||||
//TODO: These need to be constructors and should also set `.prototype` on each one
|
//TODO: These need to be constructors and should also set `.prototype` on each one
|
||||||
let object = FunctionObject::function(
|
let object = object::create_object_object(gc_context, object_proto, function_proto);
|
||||||
gc_context,
|
|
||||||
Executable::Native(object::constructor),
|
|
||||||
Some(function_proto),
|
|
||||||
Some(object_proto),
|
|
||||||
);
|
|
||||||
|
|
||||||
let color = FunctionObject::function(
|
let color = FunctionObject::function(
|
||||||
gc_context,
|
gc_context,
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
//! Object prototype
|
//! Object prototype
|
||||||
|
use crate::avm1::function::{Executable, FunctionObject};
|
||||||
use crate::avm1::property::Attribute::{self, *};
|
use crate::avm1::property::Attribute::{self, *};
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::return_value::ReturnValue;
|
||||||
use crate::avm1::{Avm1, Error, Object, TObject, UpdateContext, Value};
|
use crate::avm1::{Avm1, Error, Object, TObject, UpdateContext, Value};
|
||||||
|
use crate::character::Character;
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
use gc_arena::MutationContext;
|
use gc_arena::MutationContext;
|
||||||
|
|
||||||
|
@ -129,6 +131,32 @@ fn value_of<'gc>(
|
||||||
Ok(ReturnValue::Immediate(this.into()))
|
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`.
|
/// Partially construct `Object.prototype`.
|
||||||
///
|
///
|
||||||
/// `__proto__` and other cross-linked properties of this object will *not*
|
/// `__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())
|
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.
|
//! `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::backend::audio::AudioStreamHandle;
|
||||||
use crate::character::Character;
|
use crate::character::Character;
|
||||||
use crate::context::{ActionType, RenderContext, UpdateContext};
|
use crate::context::{ActionType, RenderContext, UpdateContext};
|
||||||
|
@ -41,6 +41,7 @@ pub struct MovieClipData<'gc> {
|
||||||
object: Option<Object<'gc>>,
|
object: Option<Object<'gc>>,
|
||||||
clip_actions: SmallVec<[ClipAction; 2]>,
|
clip_actions: SmallVec<[ClipAction; 2]>,
|
||||||
flags: EnumSet<MovieClipFlags>,
|
flags: EnumSet<MovieClipFlags>,
|
||||||
|
avm1_constructor: Option<Object<'gc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'gc> MovieClip<'gc> {
|
impl<'gc> MovieClip<'gc> {
|
||||||
|
@ -58,6 +59,7 @@ impl<'gc> MovieClip<'gc> {
|
||||||
object: None,
|
object: None,
|
||||||
clip_actions: SmallVec::new(),
|
clip_actions: SmallVec::new(),
|
||||||
flags: EnumSet::empty(),
|
flags: EnumSet::empty(),
|
||||||
|
avm1_constructor: None,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -89,6 +91,7 @@ impl<'gc> MovieClip<'gc> {
|
||||||
object: None,
|
object: None,
|
||||||
clip_actions: SmallVec::new(),
|
clip_actions: SmallVec::new(),
|
||||||
flags: MovieClipFlags::Playing.into(),
|
flags: MovieClipFlags::Playing.into(),
|
||||||
|
avm1_constructor: None,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -180,6 +183,14 @@ impl<'gc> MovieClip<'gc> {
|
||||||
self.0.read().static_data.total_frames
|
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> {
|
pub fn frame_label_to_number(self, frame_label: &str) -> Option<FrameNumber> {
|
||||||
// Frame labels are case insensitive.
|
// Frame labels are case insensitive.
|
||||||
let label = frame_label.to_ascii_lowercase();
|
let label = frame_label.to_ascii_lowercase();
|
||||||
|
@ -386,12 +397,32 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
|
||||||
|
|
||||||
fn post_instantiation(
|
fn post_instantiation(
|
||||||
&mut self,
|
&mut self,
|
||||||
_avm: &mut Avm1<'gc>,
|
avm: &mut Avm1<'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
display_object: DisplayObject<'gc>,
|
display_object: DisplayObject<'gc>,
|
||||||
) {
|
) {
|
||||||
let mut mc = self.0.write(context.gc_context);
|
let mut mc = self.0.write(context.gc_context);
|
||||||
if mc.object.is_none() {
|
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(
|
let object = StageObject::for_display_object(
|
||||||
context.gc_context,
|
context.gc_context,
|
||||||
display_object,
|
display_object,
|
||||||
|
@ -432,6 +463,7 @@ unsafe impl<'gc> Collect for MovieClipData<'gc> {
|
||||||
self.base.trace(cc);
|
self.base.trace(cc);
|
||||||
self.static_data.trace(cc);
|
self.static_data.trace(cc);
|
||||||
self.object.trace(cc);
|
self.object.trace(cc);
|
||||||
|
self.avm1_constructor.trace(cc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -126,6 +126,7 @@ swf_tests! {
|
||||||
(equals2_swf5, "avm1/equals2_swf5", 1),
|
(equals2_swf5, "avm1/equals2_swf5", 1),
|
||||||
(equals2_swf6, "avm1/equals2_swf6", 1),
|
(equals2_swf6, "avm1/equals2_swf6", 1),
|
||||||
(equals2_swf7, "avm1/equals2_swf7", 1),
|
(equals2_swf7, "avm1/equals2_swf7", 1),
|
||||||
|
(register_class, "avm1/register_class", 1),
|
||||||
(set_variable_scope, "avm1/set_variable_scope", 1),
|
(set_variable_scope, "avm1/set_variable_scope", 1),
|
||||||
(slash_syntax, "avm1/slash_syntax", 2),
|
(slash_syntax, "avm1/slash_syntax", 2),
|
||||||
(strictequals_swf6, "avm1/strictequals_swf6", 1),
|
(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