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)
|
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.
|
/// Execute a script initializer.
|
||||||
pub fn run_stack_frame_for_script(&mut self, script: Script<'gc>) -> Result<(), Error> {
|
pub fn run_stack_frame_for_script(&mut self, script: Script<'gc>) -> Result<(), Error> {
|
||||||
let init = script.init().0.into_bytecode()?;
|
let init = script.init().0.into_bytecode()?;
|
||||||
|
@ -268,6 +294,28 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
Ok(())
|
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.
|
/// Attempts to lock the activation frame for execution.
|
||||||
///
|
///
|
||||||
/// If this frame is already executing, that is an error condition.
|
/// 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> {
|
fn op_construct_super(&mut self, arg_count: u32) -> Result<FrameControl<'gc>, Error> {
|
||||||
let args = self.context.avm2.pop_args(arg_count);
|
let args = self.context.avm2.pop_args(arg_count);
|
||||||
let receiver = self.context.avm2.pop().coerce_to_object(self)?;
|
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
|
self.super_init(receiver, &args)?;
|
||||||
.get_property(receiver, &name, self)?
|
|
||||||
.coerce_to_object(self)?;
|
|
||||||
|
|
||||||
function.call(Some(receiver), &args, self, Some(base_proto))?;
|
|
||||||
|
|
||||||
Ok(FrameControl::Continue)
|
Ok(FrameControl::Continue)
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,7 +87,11 @@ impl<'gc> Executable<'gc> {
|
||||||
) -> Result<Value<'gc>, Error> {
|
) -> Result<Value<'gc>, Error> {
|
||||||
match self {
|
match self {
|
||||||
Executable::Native(nf, receiver) => {
|
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) => {
|
Executable::Action(bm) => {
|
||||||
let receiver = bm.receiver.or(unbound_reciever);
|
let receiver = bm.receiver.or(unbound_reciever);
|
||||||
|
|
|
@ -22,6 +22,8 @@ pub fn instance_init<'gc>(
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<Value<'gc>, Error> {
|
) -> Result<Value<'gc>, Error> {
|
||||||
if let Some(this) = this {
|
if let Some(this) = this {
|
||||||
|
activation.super_init(this, &[])?;
|
||||||
|
|
||||||
if let Some(mut array) = this.as_array_storage_mut(activation.context.gc_context) {
|
if let Some(mut array) = this.as_array_storage_mut(activation.context.gc_context) {
|
||||||
if args.len() == 1 {
|
if args.len() == 1 {
|
||||||
if let Some(expected_len) = args
|
if let Some(expected_len) = args
|
||||||
|
|
|
@ -16,10 +16,14 @@ use swf::Twips;
|
||||||
|
|
||||||
/// Implements `flash.display.DisplayObject`'s instance constructor.
|
/// Implements `flash.display.DisplayObject`'s instance constructor.
|
||||||
pub fn instance_init<'gc>(
|
pub fn instance_init<'gc>(
|
||||||
_activation: &mut Activation<'_, 'gc, '_>,
|
activation: &mut Activation<'_, 'gc, '_>,
|
||||||
_this: Option<Object<'gc>>,
|
this: Option<Object<'gc>>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<Value<'gc>, Error> {
|
) -> Result<Value<'gc>, Error> {
|
||||||
|
if let Some(this) = this {
|
||||||
|
activation.super_init(this, &[])?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined)
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,14 @@ use std::cmp::min;
|
||||||
|
|
||||||
/// Implements `flash.display.DisplayObjectContainer`'s instance constructor.
|
/// Implements `flash.display.DisplayObjectContainer`'s instance constructor.
|
||||||
pub fn instance_init<'gc>(
|
pub fn instance_init<'gc>(
|
||||||
_activation: &mut Activation<'_, 'gc, '_>,
|
activation: &mut Activation<'_, 'gc, '_>,
|
||||||
_this: Option<Object<'gc>>,
|
this: Option<Object<'gc>>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<Value<'gc>, Error> {
|
) -> Result<Value<'gc>, Error> {
|
||||||
|
if let Some(this) = this {
|
||||||
|
activation.super_init(this, &[])?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined)
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,8 @@ pub fn instance_init<'gc>(
|
||||||
.coerce_to_i32(activation)?;
|
.coerce_to_i32(activation)?;
|
||||||
|
|
||||||
if let Some(mut this) = this {
|
if let Some(mut this) = this {
|
||||||
|
activation.super_init(this, &[])?;
|
||||||
|
|
||||||
this.set_property(
|
this.set_property(
|
||||||
this,
|
this,
|
||||||
&QName::new(Namespace::Private("ruffle".into()), "name"),
|
&QName::new(Namespace::Private("ruffle".into()), "name"),
|
||||||
|
|
|
@ -11,10 +11,14 @@ use gc_arena::{GcCell, MutationContext};
|
||||||
|
|
||||||
/// Implements `flash.display.InteractiveObject`'s instance constructor.
|
/// Implements `flash.display.InteractiveObject`'s instance constructor.
|
||||||
pub fn instance_init<'gc>(
|
pub fn instance_init<'gc>(
|
||||||
_activation: &mut Activation<'_, 'gc, '_>,
|
activation: &mut Activation<'_, 'gc, '_>,
|
||||||
_this: Option<Object<'gc>>,
|
this: Option<Object<'gc>>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<Value<'gc>, Error> {
|
) -> Result<Value<'gc>, Error> {
|
||||||
|
if let Some(this) = this {
|
||||||
|
activation.super_init(this, &[])?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined)
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,8 @@ pub fn instance_init<'gc>(
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<Value<'gc>, Error> {
|
) -> Result<Value<'gc>, Error> {
|
||||||
if let Some(this) = this {
|
if let Some(this) = this {
|
||||||
|
activation.super_init(this, &[])?;
|
||||||
|
|
||||||
if this.as_display_object().is_none() {
|
if this.as_display_object().is_none() {
|
||||||
let movie = Arc::new(SwfMovie::empty(activation.context.swf.version()));
|
let movie = Arc::new(SwfMovie::empty(activation.context.swf.version()));
|
||||||
let new_do = MovieClip::new(SwfSlice::empty(movie), activation.context.gc_context);
|
let new_do = MovieClip::new(SwfSlice::empty(movie), activation.context.gc_context);
|
||||||
|
|
|
@ -17,6 +17,8 @@ pub fn instance_init<'gc>(
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<Value<'gc>, Error> {
|
) -> Result<Value<'gc>, Error> {
|
||||||
if let Some(mut this) = this {
|
if let Some(mut this) = this {
|
||||||
|
activation.super_init(this, &[])?;
|
||||||
|
|
||||||
let name = args
|
let name = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.cloned()
|
.cloned()
|
||||||
|
|
|
@ -11,10 +11,14 @@ use gc_arena::{GcCell, MutationContext};
|
||||||
|
|
||||||
/// Implements `flash.display.Sprite`'s instance constructor.
|
/// Implements `flash.display.Sprite`'s instance constructor.
|
||||||
pub fn instance_init<'gc>(
|
pub fn instance_init<'gc>(
|
||||||
_activation: &mut Activation<'_, 'gc, '_>,
|
activation: &mut Activation<'_, 'gc, '_>,
|
||||||
_this: Option<Object<'gc>>,
|
this: Option<Object<'gc>>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<Value<'gc>, Error> {
|
) -> Result<Value<'gc>, Error> {
|
||||||
|
if let Some(this) = this {
|
||||||
|
activation.super_init(this, &[])?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined)
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,25 +18,29 @@ pub fn instance_init<'gc>(
|
||||||
this: Option<Object<'gc>>,
|
this: Option<Object<'gc>>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<Value<'gc>, Error> {
|
) -> Result<Value<'gc>, Error> {
|
||||||
if let Some(mut evt) = this.unwrap().as_event_mut(activation.context.gc_context) {
|
if let Some(this) = this {
|
||||||
evt.set_event_type(
|
activation.super_init(this, &[])?;
|
||||||
args.get(0)
|
|
||||||
.cloned()
|
if let Some(mut evt) = this.as_event_mut(activation.context.gc_context) {
|
||||||
.unwrap_or(Value::Undefined)
|
evt.set_event_type(
|
||||||
.coerce_to_string(activation)?,
|
args.get(0)
|
||||||
);
|
.cloned()
|
||||||
evt.set_bubbles(
|
.unwrap_or(Value::Undefined)
|
||||||
args.get(1)
|
.coerce_to_string(activation)?,
|
||||||
.cloned()
|
);
|
||||||
.unwrap_or(Value::Bool(false))
|
evt.set_bubbles(
|
||||||
.coerce_to_boolean(),
|
args.get(1)
|
||||||
);
|
.cloned()
|
||||||
evt.set_cancelable(
|
.unwrap_or(Value::Bool(false))
|
||||||
args.get(2)
|
.coerce_to_boolean(),
|
||||||
.cloned()
|
);
|
||||||
.unwrap_or(Value::Bool(false))
|
evt.set_cancelable(
|
||||||
.coerce_to_boolean(),
|
args.get(2)
|
||||||
);
|
.cloned()
|
||||||
|
.unwrap_or(Value::Bool(false))
|
||||||
|
.coerce_to_boolean(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined)
|
Ok(Value::Undefined)
|
||||||
|
|
|
@ -12,10 +12,14 @@ use gc_arena::{GcCell, MutationContext};
|
||||||
|
|
||||||
/// Implements `flash.system.ApplicationDomain`'s instance constructor.
|
/// Implements `flash.system.ApplicationDomain`'s instance constructor.
|
||||||
pub fn instance_init<'gc>(
|
pub fn instance_init<'gc>(
|
||||||
_activation: &mut Activation<'_, 'gc, '_>,
|
activation: &mut Activation<'_, 'gc, '_>,
|
||||||
_this: Option<Object<'gc>>,
|
this: Option<Object<'gc>>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<Value<'gc>, Error> {
|
) -> Result<Value<'gc>, Error> {
|
||||||
|
if let Some(this) = this {
|
||||||
|
activation.super_init(this, &[])?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined)
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,10 +13,14 @@ use gc_arena::GcCell;
|
||||||
|
|
||||||
/// Implements `Function`'s instance initializer.
|
/// Implements `Function`'s instance initializer.
|
||||||
pub fn instance_init<'gc>(
|
pub fn instance_init<'gc>(
|
||||||
_activation: &mut Activation<'_, 'gc, '_>,
|
activation: &mut Activation<'_, 'gc, '_>,
|
||||||
_this: Option<Object<'gc>>,
|
this: Option<Object<'gc>>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<Value<'gc>, Error> {
|
) -> Result<Value<'gc>, Error> {
|
||||||
|
if let Some(this) = this {
|
||||||
|
activation.super_init(this, &[])?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined)
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue