avm2: Split `StageObject` associated constructors into two versions: allocation-only (`for_display_object`) and allocation-and-initialization (`for_display_object_childless`).
The latter is intended for display objects that *do not* have children and can be both allocated and initialized in one shot. Hence the name.
This commit is contained in:
parent
46a4da9dc5
commit
e4201625a1
|
@ -62,15 +62,7 @@ pub fn graphics<'gc>(
|
|||
activation,
|
||||
)? {
|
||||
Value::Undefined | Value::Null => {
|
||||
let graphics_proto = activation.context.avm2.prototypes().graphics;
|
||||
let graphics_constr = activation.context.avm2.constructors().graphics;
|
||||
|
||||
let graphics = Value::from(StageObject::for_display_object(
|
||||
activation.context.gc_context,
|
||||
dobj,
|
||||
graphics_constr,
|
||||
graphics_proto,
|
||||
));
|
||||
let graphics = Value::from(StageObject::graphics_of(activation, dobj)?);
|
||||
this.set_property(
|
||||
this,
|
||||
&QName::new(Namespace::private(NS_RUFFLE_INTERNAL), "graphics"),
|
||||
|
|
|
@ -48,15 +48,7 @@ pub fn graphics<'gc>(
|
|||
activation,
|
||||
)? {
|
||||
Value::Undefined | Value::Null => {
|
||||
let graphics_proto = activation.context.avm2.prototypes().graphics;
|
||||
let graphics_constr = activation.context.avm2.constructors().graphics;
|
||||
|
||||
let graphics = Value::from(StageObject::for_display_object(
|
||||
activation.context.gc_context,
|
||||
dobj,
|
||||
graphics_constr,
|
||||
graphics_proto,
|
||||
));
|
||||
let graphics = Value::from(StageObject::graphics_of(activation, dobj)?);
|
||||
this.set_property(
|
||||
this,
|
||||
&QName::new(Namespace::private(NS_RUFFLE_INTERNAL), "graphics"),
|
||||
|
|
|
@ -20,7 +20,16 @@ pub fn stage_deriver<'gc>(
|
|||
proto: Object<'gc>,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
StageObject::derive(constr, proto, activation.context.gc_context)
|
||||
let base = ScriptObjectData::base_new(Some(proto), ScriptObjectClass::ClassInstance(constr));
|
||||
|
||||
Ok(StageObject(GcCell::allocate(
|
||||
activation.context.gc_context,
|
||||
StageObjectData {
|
||||
base,
|
||||
display_object: None,
|
||||
},
|
||||
))
|
||||
.into())
|
||||
}
|
||||
|
||||
#[derive(Clone, Collect, Debug, Copy)]
|
||||
|
@ -38,14 +47,31 @@ pub struct StageObjectData<'gc> {
|
|||
}
|
||||
|
||||
impl<'gc> StageObject<'gc> {
|
||||
/// Allocate the AVM2 side of a display object intended to be of a given
|
||||
/// constructor's type.
|
||||
///
|
||||
/// This function makes no attempt to construct the returned object. You
|
||||
/// are responsible for calling the native initializer of the given
|
||||
/// constructor at a later time. Typically, a display object that can
|
||||
/// contain movie-constructed children must first allocate itself (using
|
||||
/// this function), construct it's children, and then finally initialize
|
||||
/// itself. Display objects that do not need to use this flow should use
|
||||
/// `for_display_object_childless`.
|
||||
pub fn for_display_object(
|
||||
mc: MutationContext<'gc, '_>,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
display_object: DisplayObject<'gc>,
|
||||
constr: Object<'gc>,
|
||||
proto: Object<'gc>,
|
||||
) -> Self {
|
||||
Self(GcCell::allocate(
|
||||
mc,
|
||||
mut constr: Object<'gc>,
|
||||
) -> Result<Self, Error> {
|
||||
let proto = constr
|
||||
.get_property(
|
||||
constr,
|
||||
&QName::new(Namespace::public(), "prototype"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
Ok(Self(GcCell::allocate(
|
||||
activation.context.gc_context,
|
||||
StageObjectData {
|
||||
base: ScriptObjectData::base_new(
|
||||
Some(proto),
|
||||
|
@ -53,26 +79,47 @@ impl<'gc> StageObject<'gc> {
|
|||
),
|
||||
display_object: Some(display_object),
|
||||
},
|
||||
))
|
||||
)))
|
||||
}
|
||||
|
||||
/// Construct a stage object subclass.
|
||||
pub fn derive(
|
||||
/// Allocate and construct the AVM2 side of a display object intended to be
|
||||
/// of a given constructor's type.
|
||||
///
|
||||
/// This function is intended for display objects that do not have children
|
||||
/// and thus do not need to be allocated and initialized in separate phases.
|
||||
pub fn for_display_object_childless(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
display_object: DisplayObject<'gc>,
|
||||
constr: Object<'gc>,
|
||||
proto: Object<'gc>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let base =
|
||||
ScriptObjectData::base_new(Some(proto), ScriptObjectClass::ClassInstance(constr));
|
||||
) -> Result<Self, Error> {
|
||||
let this = Self::for_display_object(activation, display_object, constr)?;
|
||||
|
||||
Ok(StageObject(GcCell::allocate(
|
||||
mc,
|
||||
constr.call_native_initializer(Some(this.into()), &[], activation, Some(constr))?;
|
||||
|
||||
Ok(this)
|
||||
}
|
||||
|
||||
/// Create a `graphics` object for a given display object.
|
||||
pub fn graphics_of(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
display_object: DisplayObject<'gc>,
|
||||
) -> Result<Self, Error> {
|
||||
let constr = activation.avm2().constructors().graphics;
|
||||
let proto = activation.avm2().prototypes().graphics;
|
||||
let this = Self(GcCell::allocate(
|
||||
activation.context.gc_context,
|
||||
StageObjectData {
|
||||
base,
|
||||
display_object: None,
|
||||
base: ScriptObjectData::base_new(
|
||||
Some(proto),
|
||||
ScriptObjectClass::ClassInstance(constr),
|
||||
),
|
||||
display_object: Some(display_object),
|
||||
},
|
||||
))
|
||||
.into())
|
||||
));
|
||||
|
||||
constr.call_native_initializer(Some(this.into()), &[], activation, Some(constr))?;
|
||||
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
use crate::avm1::Object as Avm1Object;
|
||||
use crate::avm2::{Object as Avm2Object, StageObject as Avm2StageObject, Value as Avm2Value};
|
||||
use crate::avm2::{
|
||||
Activation as Avm2Activation, Object as Avm2Object, StageObject as Avm2StageObject,
|
||||
Value as Avm2Value,
|
||||
};
|
||||
use crate::backend::ui::MouseCursor;
|
||||
use crate::context::{RenderContext, UpdateContext};
|
||||
use crate::display_object::container::{dispatch_added_event, dispatch_removed_event};
|
||||
|
@ -426,15 +429,16 @@ impl<'gc> TDisplayObject<'gc> for Avm2Button<'gc> {
|
|||
}
|
||||
|
||||
if self.0.read().object.is_none() {
|
||||
let simplebutton_proto = context.avm2.prototypes().simplebutton;
|
||||
let simplebutton_constr = context.avm2.constructors().simplebutton;
|
||||
let object = Avm2StageObject::for_display_object(
|
||||
context.gc_context,
|
||||
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
||||
match Avm2StageObject::for_display_object(
|
||||
&mut activation,
|
||||
(*self).into(),
|
||||
simplebutton_constr,
|
||||
simplebutton_proto,
|
||||
);
|
||||
self.0.write(context.gc_context).object = Some(object.into());
|
||||
) {
|
||||
Ok(object) => self.0.write(context.gc_context).object = Some(object.into()),
|
||||
Err(e) => log::error!("Got {} when constructing AVM2 side of button", e),
|
||||
};
|
||||
|
||||
let (up_state, up_should_fire) = self.create_state(context, swf::ButtonState::UP);
|
||||
let (over_state, over_should_fire) = self.create_state(context, swf::ButtonState::OVER);
|
||||
|
|
|
@ -7,7 +7,6 @@ use crate::avm1::{
|
|||
};
|
||||
use crate::avm2::{
|
||||
Activation as Avm2Activation, Object as Avm2Object, StageObject as Avm2StageObject,
|
||||
TObject as Avm2TObject,
|
||||
};
|
||||
use crate::backend::ui::MouseCursor;
|
||||
use crate::context::{RenderContext, UpdateContext};
|
||||
|
@ -1509,29 +1508,23 @@ impl<'gc> EditText<'gc> {
|
|||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
display_object: DisplayObject<'gc>,
|
||||
) {
|
||||
let textfield_proto = context.avm2.prototypes().textfield;
|
||||
let textfield_constr = context.avm2.constructors().textfield;
|
||||
|
||||
let object: Avm2Object<'gc> = Avm2StageObject::for_display_object(
|
||||
context.gc_context,
|
||||
display_object,
|
||||
textfield_constr,
|
||||
textfield_proto,
|
||||
)
|
||||
.into();
|
||||
|
||||
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
||||
|
||||
if let Err(e) =
|
||||
textfield_constr.call(Some(object), &[], &mut activation, Some(textfield_constr))
|
||||
{
|
||||
log::error!(
|
||||
match Avm2StageObject::for_display_object_childless(
|
||||
&mut activation,
|
||||
display_object,
|
||||
textfield_constr,
|
||||
) {
|
||||
Ok(object) => {
|
||||
let object: Avm2Object<'gc> = object.into();
|
||||
self.0.write(activation.context.gc_context).object = Some(object.into())
|
||||
}
|
||||
Err(e) => log::error!(
|
||||
"Got {} when constructing AVM2 side of dynamic text field",
|
||||
e
|
||||
);
|
||||
),
|
||||
}
|
||||
|
||||
self.0.write(activation.context.gc_context).object = Some(object.into());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use crate::avm1::Object as Avm1Object;
|
||||
use crate::avm2::{
|
||||
Activation as Avm2Activation, Error as Avm2Error, Object as Avm2Object,
|
||||
StageObject as Avm2StageObject, TObject as Avm2TObject,
|
||||
Activation as Avm2Activation, Object as Avm2Object, StageObject as Avm2StageObject,
|
||||
};
|
||||
use crate::backend::render::ShapeHandle;
|
||||
use crate::context::{RenderContext, UpdateContext};
|
||||
|
@ -115,27 +114,17 @@ impl<'gc> TDisplayObject<'gc> for Graphic<'gc> {
|
|||
|
||||
fn construct_frame(&self, context: &mut UpdateContext<'_, 'gc, '_>) {
|
||||
if self.avm_type() == AvmType::Avm2 && matches!(self.object2(), Avm2Value::Undefined) {
|
||||
let mut allocator = || {
|
||||
let shape_proto = context.avm2.prototypes().shape;
|
||||
let shape_constr = context.avm2.constructors().shape;
|
||||
let shape_constr = context.avm2.constructors().shape;
|
||||
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
||||
|
||||
let object = Avm2StageObject::for_display_object(
|
||||
context.gc_context,
|
||||
(*self).into(),
|
||||
shape_constr,
|
||||
shape_proto,
|
||||
)
|
||||
.into();
|
||||
|
||||
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
||||
shape_constr.call(Some(object), &[], &mut activation, Some(shape_constr))?;
|
||||
|
||||
Ok(object)
|
||||
};
|
||||
let result: Result<Avm2Object<'gc>, Avm2Error> = allocator();
|
||||
|
||||
match result {
|
||||
Ok(object) => self.0.write(context.gc_context).avm2_object = Some(object),
|
||||
match Avm2StageObject::for_display_object_childless(
|
||||
&mut activation,
|
||||
(*self).into(),
|
||||
shape_constr,
|
||||
) {
|
||||
Ok(object) => {
|
||||
self.0.write(activation.context.gc_context).avm2_object = Some(object.into())
|
||||
}
|
||||
Err(e) => log::error!("Got {} when constructing AVM2 side of display object", e),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1513,7 +1513,7 @@ impl<'gc> MovieClip<'gc> {
|
|||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
display_object: DisplayObject<'gc>,
|
||||
) {
|
||||
let mut constructor = self
|
||||
let constructor = self
|
||||
.0
|
||||
.read()
|
||||
.avm2_constructor
|
||||
|
@ -1521,20 +1521,9 @@ impl<'gc> MovieClip<'gc> {
|
|||
|
||||
let mut constr_thing = || {
|
||||
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
||||
let mc_proto = constructor
|
||||
.get_property(
|
||||
constructor,
|
||||
&Avm2QName::new(Avm2Namespace::public(), "prototype"),
|
||||
&mut activation,
|
||||
)?
|
||||
.coerce_to_object(&mut activation)?;
|
||||
let object = Avm2StageObject::for_display_object(
|
||||
activation.context.gc_context,
|
||||
display_object,
|
||||
constructor,
|
||||
mc_proto,
|
||||
)
|
||||
.into();
|
||||
let object =
|
||||
Avm2StageObject::for_display_object(&mut activation, display_object, constructor)?
|
||||
.into();
|
||||
|
||||
Ok(object)
|
||||
};
|
||||
|
|
|
@ -3,8 +3,7 @@
|
|||
use crate::avm1::Object as Avm1Object;
|
||||
use crate::avm2::{
|
||||
Activation as Avm2Activation, Event as Avm2Event, Object as Avm2Object,
|
||||
ScriptObject as Avm2ScriptObject, StageObject as Avm2StageObject, TObject as Avm2TObject,
|
||||
Value as Avm2Value,
|
||||
ScriptObject as Avm2ScriptObject, StageObject as Avm2StageObject, Value as Avm2Value,
|
||||
};
|
||||
use crate::backend::ui::UiBackend;
|
||||
use crate::config::Letterbox;
|
||||
|
@ -518,29 +517,24 @@ impl<'gc> TDisplayObject<'gc> for Stage<'gc> {
|
|||
_instantiated_by: Instantiator,
|
||||
_run_frame: bool,
|
||||
) {
|
||||
let stage_proto = context.avm2.prototypes().stage;
|
||||
let stage_constr = context.avm2.constructors().stage;
|
||||
let avm2_stage = Avm2StageObject::for_display_object(
|
||||
context.gc_context,
|
||||
(*self).into(),
|
||||
stage_constr,
|
||||
stage_proto,
|
||||
);
|
||||
|
||||
// TODO: Replace this when we have a convenience method for constructing AVM2 native objects.
|
||||
// TODO: We should only do this if the movie is actually an AVM2 movie.
|
||||
// This is necessary for EventDispatcher super-constructor to run.
|
||||
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
||||
if let Err(e) = stage_constr.call_native_initializer(
|
||||
Some(avm2_stage.into()),
|
||||
&[],
|
||||
let avm2_stage = Avm2StageObject::for_display_object_childless(
|
||||
&mut activation,
|
||||
Some(stage_constr),
|
||||
) {
|
||||
log::error!("Unable to construct AVM2 Stage: {}", e);
|
||||
}
|
||||
(*self).into(),
|
||||
stage_constr,
|
||||
);
|
||||
|
||||
self.0.write(activation.context.gc_context).avm2_object = avm2_stage.into();
|
||||
match avm2_stage {
|
||||
Ok(avm2_stage) => {
|
||||
self.0.write(activation.context.gc_context).avm2_object = avm2_stage.into()
|
||||
}
|
||||
Err(e) => log::error!("Unable to construct AVM2 Stage: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
fn id(&self) -> CharacterId {
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
//! Video player display object
|
||||
|
||||
use crate::avm1::{Object as Avm1Object, StageObject as Avm1StageObject};
|
||||
use crate::avm2::{Object as Avm2Object, StageObject as Avm2StageObject};
|
||||
use crate::avm2::{
|
||||
Activation as Avm2Activation, Object as Avm2Object, StageObject as Avm2StageObject,
|
||||
};
|
||||
use crate::backend::render::BitmapInfo;
|
||||
use crate::backend::video::{EncodedFrame, VideoStreamHandle};
|
||||
use crate::bounding_box::BoundingBox;
|
||||
|
@ -381,17 +383,19 @@ impl<'gc> TDisplayObject<'gc> for Video<'gc> {
|
|||
fn construct_frame(&self, context: &mut UpdateContext<'_, 'gc, '_>) {
|
||||
let vm_type = self.avm_type();
|
||||
if vm_type == AvmType::Avm2 && matches!(self.object2(), Avm2Value::Undefined) {
|
||||
let video_proto = context.avm2.prototypes().video;
|
||||
let video_constr = context.avm2.constructors().video;
|
||||
|
||||
let object: Avm2Object<'_> = Avm2StageObject::for_display_object(
|
||||
context.gc_context,
|
||||
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
||||
match Avm2StageObject::for_display_object_childless(
|
||||
&mut activation,
|
||||
(*self).into(),
|
||||
video_constr,
|
||||
video_proto,
|
||||
)
|
||||
.into();
|
||||
self.0.write(context.gc_context).object = Some(object.into());
|
||||
) {
|
||||
Ok(object) => {
|
||||
let object: Avm2Object<'gc> = object.into();
|
||||
self.0.write(context.gc_context).object = Some(object.into())
|
||||
}
|
||||
Err(e) => log::error!("Got {} when constructing AVM2 side of video player", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue