avm2: Builtin constructors should supercall.

This commit is contained in:
David Wendt 2020-12-17 23:01:09 -05:00 committed by Mike Welsh
parent be4e37a55c
commit e8163e43ab
13 changed files with 121 additions and 45 deletions

View File

@ -259,6 +259,32 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
Ok(activation)
}
/// Construct an activation for the execution of a builtin method.
///
/// It is a logic error to attempt to execute builtins within the same
/// activation as the method or script that called them. You must use this
/// function to construct a new activation for the builtin so that it can
/// properly supercall.
pub fn from_builtin(
context: UpdateContext<'a, 'gc, 'gc_context>,
this: Option<Object<'gc>>,
base_proto: Option<Object<'gc>>,
) -> Result<Self, Error> {
let local_registers = GcCell::allocate(context.gc_context, RegisterSet::new(0));
Ok(Self {
this,
arguments: None,
is_executing: false,
local_registers,
return_value: None,
local_scope: ScriptObject::bare_object(context.gc_context),
scope: None,
base_proto,
context,
})
}
/// Execute a script initializer.
pub fn run_stack_frame_for_script(&mut self, script: Script<'gc>) -> Result<(), Error> {
let init = script.init().0.into_bytecode()?;
@ -268,6 +294,28 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
Ok(())
}
/// Call the superclass's instance initializer.
pub fn super_init(
&mut self,
receiver: Object<'gc>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
let name = QName::new(Namespace::public_namespace(), "constructor");
let base_proto: Result<Object<'gc>, Error> =
self.base_proto().and_then(|p| p.proto()).ok_or_else(|| {
"Attempted to call super constructor without a superclass."
.to_string()
.into()
});
let mut base_proto = base_proto?;
let function = base_proto
.get_property(receiver, &name, self)?
.coerce_to_object(self)?;
function.call(Some(receiver), &args, self, Some(base_proto))
}
/// Attempts to lock the activation frame for execution.
///
/// If this frame is already executing, that is an error condition.
@ -1348,20 +1396,8 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
fn op_construct_super(&mut self, arg_count: u32) -> Result<FrameControl<'gc>, Error> {
let args = self.context.avm2.pop_args(arg_count);
let receiver = self.context.avm2.pop().coerce_to_object(self)?;
let name = QName::new(Namespace::public_namespace(), "constructor");
let base_proto: Result<Object<'gc>, Error> =
self.base_proto().and_then(|p| p.proto()).ok_or_else(|| {
"Attempted to call super constructor without a superclass."
.to_string()
.into()
});
let mut base_proto = base_proto?;
let function = base_proto
.get_property(receiver, &name, self)?
.coerce_to_object(self)?;
function.call(Some(receiver), &args, self, Some(base_proto))?;
self.super_init(receiver, &args)?;
Ok(FrameControl::Continue)
}

View File

@ -87,7 +87,11 @@ impl<'gc> Executable<'gc> {
) -> Result<Value<'gc>, Error> {
match self {
Executable::Native(nf, receiver) => {
nf(activation, receiver.or(unbound_reciever), arguments)
let receiver = receiver.or(unbound_reciever);
let mut activation =
Activation::from_builtin(activation.context.reborrow(), receiver, base_proto)?;
nf(&mut activation, receiver, arguments)
}
Executable::Action(bm) => {
let receiver = bm.receiver.or(unbound_reciever);

View File

@ -22,6 +22,8 @@ pub fn instance_init<'gc>(
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
if let Some(this) = this {
activation.super_init(this, &[])?;
if let Some(mut array) = this.as_array_storage_mut(activation.context.gc_context) {
if args.len() == 1 {
if let Some(expected_len) = args

View File

@ -16,10 +16,14 @@ use swf::Twips;
/// Implements `flash.display.DisplayObject`'s instance constructor.
pub fn instance_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>,
activation: &mut Activation<'_, 'gc, '_>,
this: Option<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
if let Some(this) = this {
activation.super_init(this, &[])?;
}
Ok(Value::Undefined)
}

View File

@ -16,10 +16,14 @@ use std::cmp::min;
/// Implements `flash.display.DisplayObjectContainer`'s instance constructor.
pub fn instance_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>,
activation: &mut Activation<'_, 'gc, '_>,
this: Option<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
if let Some(this) = this {
activation.super_init(this, &[])?;
}
Ok(Value::Undefined)
}

View File

@ -28,6 +28,8 @@ pub fn instance_init<'gc>(
.coerce_to_i32(activation)?;
if let Some(mut this) = this {
activation.super_init(this, &[])?;
this.set_property(
this,
&QName::new(Namespace::Private("ruffle".into()), "name"),

View File

@ -11,10 +11,14 @@ use gc_arena::{GcCell, MutationContext};
/// Implements `flash.display.InteractiveObject`'s instance constructor.
pub fn instance_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>,
activation: &mut Activation<'_, 'gc, '_>,
this: Option<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
if let Some(this) = this {
activation.super_init(this, &[])?;
}
Ok(Value::Undefined)
}

View File

@ -23,6 +23,8 @@ pub fn instance_init<'gc>(
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
if let Some(this) = this {
activation.super_init(this, &[])?;
if this.as_display_object().is_none() {
let movie = Arc::new(SwfMovie::empty(activation.context.swf.version()));
let new_do = MovieClip::new(SwfSlice::empty(movie), activation.context.gc_context);

View File

@ -17,6 +17,8 @@ pub fn instance_init<'gc>(
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
if let Some(mut this) = this {
activation.super_init(this, &[])?;
let name = args
.get(0)
.cloned()

View File

@ -11,10 +11,14 @@ use gc_arena::{GcCell, MutationContext};
/// Implements `flash.display.Sprite`'s instance constructor.
pub fn instance_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>,
activation: &mut Activation<'_, 'gc, '_>,
this: Option<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
if let Some(this) = this {
activation.super_init(this, &[])?;
}
Ok(Value::Undefined)
}

View File

@ -18,7 +18,10 @@ pub fn instance_init<'gc>(
this: Option<Object<'gc>>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
if let Some(mut evt) = this.unwrap().as_event_mut(activation.context.gc_context) {
if let Some(this) = this {
activation.super_init(this, &[])?;
if let Some(mut evt) = this.as_event_mut(activation.context.gc_context) {
evt.set_event_type(
args.get(0)
.cloned()
@ -38,6 +41,7 @@ pub fn instance_init<'gc>(
.coerce_to_boolean(),
);
}
}
Ok(Value::Undefined)
}

View File

@ -12,10 +12,14 @@ use gc_arena::{GcCell, MutationContext};
/// Implements `flash.system.ApplicationDomain`'s instance constructor.
pub fn instance_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>,
activation: &mut Activation<'_, 'gc, '_>,
this: Option<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
if let Some(this) = this {
activation.super_init(this, &[])?;
}
Ok(Value::Undefined)
}

View File

@ -13,10 +13,14 @@ use gc_arena::GcCell;
/// Implements `Function`'s instance initializer.
pub fn instance_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>,
activation: &mut Activation<'_, 'gc, '_>,
this: Option<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
if let Some(this) = this {
activation.super_init(this, &[])?;
}
Ok(Value::Undefined)
}