avm2: Builtin constructors should supercall.
This commit is contained in:
parent
be4e37a55c
commit
e8163e43ab
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue