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,
|
activation,
|
||||||
)? {
|
)? {
|
||||||
Value::Undefined | Value::Null => {
|
Value::Undefined | Value::Null => {
|
||||||
let graphics_proto = activation.context.avm2.prototypes().graphics;
|
let graphics = Value::from(StageObject::graphics_of(activation, dobj)?);
|
||||||
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,
|
|
||||||
));
|
|
||||||
this.set_property(
|
this.set_property(
|
||||||
this,
|
this,
|
||||||
&QName::new(Namespace::private(NS_RUFFLE_INTERNAL), "graphics"),
|
&QName::new(Namespace::private(NS_RUFFLE_INTERNAL), "graphics"),
|
||||||
|
|
|
@ -48,15 +48,7 @@ pub fn graphics<'gc>(
|
||||||
activation,
|
activation,
|
||||||
)? {
|
)? {
|
||||||
Value::Undefined | Value::Null => {
|
Value::Undefined | Value::Null => {
|
||||||
let graphics_proto = activation.context.avm2.prototypes().graphics;
|
let graphics = Value::from(StageObject::graphics_of(activation, dobj)?);
|
||||||
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,
|
|
||||||
));
|
|
||||||
this.set_property(
|
this.set_property(
|
||||||
this,
|
this,
|
||||||
&QName::new(Namespace::private(NS_RUFFLE_INTERNAL), "graphics"),
|
&QName::new(Namespace::private(NS_RUFFLE_INTERNAL), "graphics"),
|
||||||
|
|
|
@ -20,7 +20,16 @@ pub fn stage_deriver<'gc>(
|
||||||
proto: Object<'gc>,
|
proto: Object<'gc>,
|
||||||
activation: &mut Activation<'_, 'gc, '_>,
|
activation: &mut Activation<'_, 'gc, '_>,
|
||||||
) -> Result<Object<'gc>, Error> {
|
) -> 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)]
|
#[derive(Clone, Collect, Debug, Copy)]
|
||||||
|
@ -38,14 +47,31 @@ pub struct StageObjectData<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'gc> StageObject<'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(
|
pub fn for_display_object(
|
||||||
mc: MutationContext<'gc, '_>,
|
activation: &mut Activation<'_, 'gc, '_>,
|
||||||
display_object: DisplayObject<'gc>,
|
display_object: DisplayObject<'gc>,
|
||||||
constr: Object<'gc>,
|
mut constr: Object<'gc>,
|
||||||
proto: Object<'gc>,
|
) -> Result<Self, Error> {
|
||||||
) -> Self {
|
let proto = constr
|
||||||
Self(GcCell::allocate(
|
.get_property(
|
||||||
mc,
|
constr,
|
||||||
|
&QName::new(Namespace::public(), "prototype"),
|
||||||
|
activation,
|
||||||
|
)?
|
||||||
|
.coerce_to_object(activation)?;
|
||||||
|
|
||||||
|
Ok(Self(GcCell::allocate(
|
||||||
|
activation.context.gc_context,
|
||||||
StageObjectData {
|
StageObjectData {
|
||||||
base: ScriptObjectData::base_new(
|
base: ScriptObjectData::base_new(
|
||||||
Some(proto),
|
Some(proto),
|
||||||
|
@ -53,26 +79,47 @@ impl<'gc> StageObject<'gc> {
|
||||||
),
|
),
|
||||||
display_object: Some(display_object),
|
display_object: Some(display_object),
|
||||||
},
|
},
|
||||||
))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a stage object subclass.
|
/// Allocate and construct the AVM2 side of a display object intended to be
|
||||||
pub fn derive(
|
/// 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>,
|
constr: Object<'gc>,
|
||||||
proto: Object<'gc>,
|
) -> Result<Self, Error> {
|
||||||
mc: MutationContext<'gc, '_>,
|
let this = Self::for_display_object(activation, display_object, constr)?;
|
||||||
) -> Result<Object<'gc>, Error> {
|
|
||||||
let base =
|
|
||||||
ScriptObjectData::base_new(Some(proto), ScriptObjectClass::ClassInstance(constr));
|
|
||||||
|
|
||||||
Ok(StageObject(GcCell::allocate(
|
constr.call_native_initializer(Some(this.into()), &[], activation, Some(constr))?;
|
||||||
mc,
|
|
||||||
|
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 {
|
StageObjectData {
|
||||||
base,
|
base: ScriptObjectData::base_new(
|
||||||
display_object: None,
|
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::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::backend::ui::MouseCursor;
|
||||||
use crate::context::{RenderContext, UpdateContext};
|
use crate::context::{RenderContext, UpdateContext};
|
||||||
use crate::display_object::container::{dispatch_added_event, dispatch_removed_event};
|
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() {
|
if self.0.read().object.is_none() {
|
||||||
let simplebutton_proto = context.avm2.prototypes().simplebutton;
|
|
||||||
let simplebutton_constr = context.avm2.constructors().simplebutton;
|
let simplebutton_constr = context.avm2.constructors().simplebutton;
|
||||||
let object = Avm2StageObject::for_display_object(
|
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
||||||
context.gc_context,
|
match Avm2StageObject::for_display_object(
|
||||||
|
&mut activation,
|
||||||
(*self).into(),
|
(*self).into(),
|
||||||
simplebutton_constr,
|
simplebutton_constr,
|
||||||
simplebutton_proto,
|
) {
|
||||||
);
|
Ok(object) => self.0.write(context.gc_context).object = Some(object.into()),
|
||||||
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 (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);
|
let (over_state, over_should_fire) = self.create_state(context, swf::ButtonState::OVER);
|
||||||
|
|
|
@ -7,7 +7,6 @@ use crate::avm1::{
|
||||||
};
|
};
|
||||||
use crate::avm2::{
|
use crate::avm2::{
|
||||||
Activation as Avm2Activation, Object as Avm2Object, StageObject as Avm2StageObject,
|
Activation as Avm2Activation, Object as Avm2Object, StageObject as Avm2StageObject,
|
||||||
TObject as Avm2TObject,
|
|
||||||
};
|
};
|
||||||
use crate::backend::ui::MouseCursor;
|
use crate::backend::ui::MouseCursor;
|
||||||
use crate::context::{RenderContext, UpdateContext};
|
use crate::context::{RenderContext, UpdateContext};
|
||||||
|
@ -1509,29 +1508,23 @@ impl<'gc> EditText<'gc> {
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
display_object: DisplayObject<'gc>,
|
display_object: DisplayObject<'gc>,
|
||||||
) {
|
) {
|
||||||
let textfield_proto = context.avm2.prototypes().textfield;
|
|
||||||
let textfield_constr = context.avm2.constructors().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());
|
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
||||||
|
|
||||||
if let Err(e) =
|
match Avm2StageObject::for_display_object_childless(
|
||||||
textfield_constr.call(Some(object), &[], &mut activation, Some(textfield_constr))
|
&mut activation,
|
||||||
{
|
display_object,
|
||||||
log::error!(
|
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",
|
"Got {} when constructing AVM2 side of dynamic text field",
|
||||||
e
|
e
|
||||||
);
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
self.0.write(activation.context.gc_context).object = Some(object.into());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use crate::avm1::Object as Avm1Object;
|
use crate::avm1::Object as Avm1Object;
|
||||||
use crate::avm2::{
|
use crate::avm2::{
|
||||||
Activation as Avm2Activation, Error as Avm2Error, Object as Avm2Object,
|
Activation as Avm2Activation, Object as Avm2Object, StageObject as Avm2StageObject,
|
||||||
StageObject as Avm2StageObject, TObject as Avm2TObject,
|
|
||||||
};
|
};
|
||||||
use crate::backend::render::ShapeHandle;
|
use crate::backend::render::ShapeHandle;
|
||||||
use crate::context::{RenderContext, UpdateContext};
|
use crate::context::{RenderContext, UpdateContext};
|
||||||
|
@ -115,27 +114,17 @@ impl<'gc> TDisplayObject<'gc> for Graphic<'gc> {
|
||||||
|
|
||||||
fn construct_frame(&self, context: &mut UpdateContext<'_, 'gc, '_>) {
|
fn construct_frame(&self, context: &mut UpdateContext<'_, 'gc, '_>) {
|
||||||
if self.avm_type() == AvmType::Avm2 && matches!(self.object2(), Avm2Value::Undefined) {
|
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(
|
match Avm2StageObject::for_display_object_childless(
|
||||||
context.gc_context,
|
&mut activation,
|
||||||
(*self).into(),
|
(*self).into(),
|
||||||
shape_constr,
|
shape_constr,
|
||||||
shape_proto,
|
) {
|
||||||
)
|
Ok(object) => {
|
||||||
.into();
|
self.0.write(activation.context.gc_context).avm2_object = Some(object.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),
|
|
||||||
Err(e) => log::error!("Got {} when constructing AVM2 side of display object", e),
|
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, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
display_object: DisplayObject<'gc>,
|
display_object: DisplayObject<'gc>,
|
||||||
) {
|
) {
|
||||||
let mut constructor = self
|
let constructor = self
|
||||||
.0
|
.0
|
||||||
.read()
|
.read()
|
||||||
.avm2_constructor
|
.avm2_constructor
|
||||||
|
@ -1521,19 +1521,8 @@ impl<'gc> MovieClip<'gc> {
|
||||||
|
|
||||||
let mut constr_thing = || {
|
let mut constr_thing = || {
|
||||||
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
||||||
let mc_proto = constructor
|
let object =
|
||||||
.get_property(
|
Avm2StageObject::for_display_object(&mut activation, display_object, constructor)?
|
||||||
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();
|
.into();
|
||||||
|
|
||||||
Ok(object)
|
Ok(object)
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
use crate::avm1::Object as Avm1Object;
|
use crate::avm1::Object as Avm1Object;
|
||||||
use crate::avm2::{
|
use crate::avm2::{
|
||||||
Activation as Avm2Activation, Event as Avm2Event, Object as Avm2Object,
|
Activation as Avm2Activation, Event as Avm2Event, Object as Avm2Object,
|
||||||
ScriptObject as Avm2ScriptObject, StageObject as Avm2StageObject, TObject as Avm2TObject,
|
ScriptObject as Avm2ScriptObject, StageObject as Avm2StageObject, Value as Avm2Value,
|
||||||
Value as Avm2Value,
|
|
||||||
};
|
};
|
||||||
use crate::backend::ui::UiBackend;
|
use crate::backend::ui::UiBackend;
|
||||||
use crate::config::Letterbox;
|
use crate::config::Letterbox;
|
||||||
|
@ -518,29 +517,24 @@ impl<'gc> TDisplayObject<'gc> for Stage<'gc> {
|
||||||
_instantiated_by: Instantiator,
|
_instantiated_by: Instantiator,
|
||||||
_run_frame: bool,
|
_run_frame: bool,
|
||||||
) {
|
) {
|
||||||
let stage_proto = context.avm2.prototypes().stage;
|
|
||||||
let stage_constr = context.avm2.constructors().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: 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.
|
// TODO: We should only do this if the movie is actually an AVM2 movie.
|
||||||
// This is necessary for EventDispatcher super-constructor to run.
|
// This is necessary for EventDispatcher super-constructor to run.
|
||||||
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
||||||
if let Err(e) = stage_constr.call_native_initializer(
|
let avm2_stage = Avm2StageObject::for_display_object_childless(
|
||||||
Some(avm2_stage.into()),
|
|
||||||
&[],
|
|
||||||
&mut activation,
|
&mut activation,
|
||||||
Some(stage_constr),
|
(*self).into(),
|
||||||
) {
|
stage_constr,
|
||||||
log::error!("Unable to construct AVM2 Stage: {}", e);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
fn id(&self) -> CharacterId {
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
//! Video player display object
|
//! Video player display object
|
||||||
|
|
||||||
use crate::avm1::{Object as Avm1Object, StageObject as Avm1StageObject};
|
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::render::BitmapInfo;
|
||||||
use crate::backend::video::{EncodedFrame, VideoStreamHandle};
|
use crate::backend::video::{EncodedFrame, VideoStreamHandle};
|
||||||
use crate::bounding_box::BoundingBox;
|
use crate::bounding_box::BoundingBox;
|
||||||
|
@ -381,17 +383,19 @@ impl<'gc> TDisplayObject<'gc> for Video<'gc> {
|
||||||
fn construct_frame(&self, context: &mut UpdateContext<'_, 'gc, '_>) {
|
fn construct_frame(&self, context: &mut UpdateContext<'_, 'gc, '_>) {
|
||||||
let vm_type = self.avm_type();
|
let vm_type = self.avm_type();
|
||||||
if vm_type == AvmType::Avm2 && matches!(self.object2(), Avm2Value::Undefined) {
|
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 video_constr = context.avm2.constructors().video;
|
||||||
|
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
||||||
let object: Avm2Object<'_> = Avm2StageObject::for_display_object(
|
match Avm2StageObject::for_display_object_childless(
|
||||||
context.gc_context,
|
&mut activation,
|
||||||
(*self).into(),
|
(*self).into(),
|
||||||
video_constr,
|
video_constr,
|
||||||
video_proto,
|
) {
|
||||||
)
|
Ok(object) => {
|
||||||
.into();
|
let object: Avm2Object<'gc> = object.into();
|
||||||
self.0.write(context.gc_context).object = Some(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