avm2: `newactivation` should return an object which implements the traits listed in the associated method `body`'s trait list
This commit is contained in:
parent
6c5098d3c7
commit
9318028b52
|
@ -107,6 +107,16 @@ pub struct Activation<'a, 'gc: 'a, 'gc_context: 'a> {
|
||||||
/// This will not be available if this is not a method call.
|
/// This will not be available if this is not a method call.
|
||||||
base_proto: Option<Object<'gc>>,
|
base_proto: Option<Object<'gc>>,
|
||||||
|
|
||||||
|
/// The proto of all objects returned from `newactivation`.
|
||||||
|
///
|
||||||
|
/// In method calls that call for an activation object, this will be
|
||||||
|
/// configured as the anonymous class whose traits match the method's
|
||||||
|
/// declared traits.
|
||||||
|
///
|
||||||
|
/// If this is `None`, then the method did not ask for an activation object
|
||||||
|
/// and we will not construct a prototype for one.
|
||||||
|
activation_proto: Option<Object<'gc>>,
|
||||||
|
|
||||||
pub context: UpdateContext<'a, 'gc, 'gc_context>,
|
pub context: UpdateContext<'a, 'gc, 'gc_context>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,6 +141,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
local_scope: ScriptObject::bare_object(context.gc_context),
|
local_scope: ScriptObject::bare_object(context.gc_context),
|
||||||
scope: None,
|
scope: None,
|
||||||
base_proto: None,
|
base_proto: None,
|
||||||
|
activation_proto: None,
|
||||||
context,
|
context,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -170,6 +181,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
local_scope: ScriptObject::bare_object(context.gc_context),
|
local_scope: ScriptObject::bare_object(context.gc_context),
|
||||||
scope,
|
scope,
|
||||||
base_proto: None,
|
base_proto: None,
|
||||||
|
activation_proto: None,
|
||||||
context,
|
context,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -188,7 +200,8 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
let body: Result<_, Error> = method
|
let body: Result<_, Error> = method
|
||||||
.body()
|
.body()
|
||||||
.ok_or_else(|| "Cannot execute non-native method without body".into());
|
.ok_or_else(|| "Cannot execute non-native method without body".into());
|
||||||
let num_locals = body?.num_locals;
|
let body = body?;
|
||||||
|
let num_locals = body.num_locals;
|
||||||
let has_rest_or_args = method.method().needs_arguments_object || method.method().needs_rest;
|
let has_rest_or_args = method.method().needs_arguments_object || method.method().needs_rest;
|
||||||
let arg_register = if has_rest_or_args { 1 } else { 0 };
|
let arg_register = if has_rest_or_args { 1 } else { 0 };
|
||||||
let num_declared_arguments = method.method().params.len() as u32;
|
let num_declared_arguments = method.method().params.len() as u32;
|
||||||
|
@ -209,6 +222,26 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let activation_proto = if method.method().needs_activation {
|
||||||
|
let translation_unit = method.translation_unit();
|
||||||
|
let abc_method = method.method();
|
||||||
|
let activation_class = Class::from_method_body(
|
||||||
|
context.avm2,
|
||||||
|
context.gc_context,
|
||||||
|
translation_unit,
|
||||||
|
abc_method,
|
||||||
|
body,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Some(ScriptObject::bare_prototype(
|
||||||
|
context.gc_context,
|
||||||
|
activation_class,
|
||||||
|
scope,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let mut activation = Self {
|
let mut activation = Self {
|
||||||
this,
|
this,
|
||||||
arguments: None,
|
arguments: None,
|
||||||
|
@ -218,6 +251,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
local_scope: ScriptObject::bare_object(context.gc_context),
|
local_scope: ScriptObject::bare_object(context.gc_context),
|
||||||
scope,
|
scope,
|
||||||
base_proto,
|
base_proto,
|
||||||
|
activation_proto,
|
||||||
context,
|
context,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -291,6 +325,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
local_scope: ScriptObject::bare_object(context.gc_context),
|
local_scope: ScriptObject::bare_object(context.gc_context),
|
||||||
scope,
|
scope,
|
||||||
base_proto,
|
base_proto,
|
||||||
|
activation_proto: None,
|
||||||
context,
|
context,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1440,9 +1475,16 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_new_activation(&mut self) -> Result<FrameControl<'gc>, Error> {
|
fn op_new_activation(&mut self) -> Result<FrameControl<'gc>, Error> {
|
||||||
self.context
|
if let Some(activation_proto) = self.activation_proto {
|
||||||
.avm2
|
self.context.avm2.push(ScriptObject::object(
|
||||||
.push(ScriptObject::bare_object(self.context.gc_context));
|
self.context.gc_context,
|
||||||
|
activation_proto,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
self.context
|
||||||
|
.avm2
|
||||||
|
.push(ScriptObject::bare_object(self.context.gc_context));
|
||||||
|
}
|
||||||
|
|
||||||
Ok(FrameControl::Continue)
|
Ok(FrameControl::Continue)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,9 @@ use crate::avm2::{Avm2, Error};
|
||||||
use crate::collect::CollectWrapper;
|
use crate::collect::CollectWrapper;
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use gc_arena::{Collect, GcCell, MutationContext};
|
use gc_arena::{Collect, GcCell, MutationContext};
|
||||||
use swf::avm2::types::{Class as AbcClass, Instance as AbcInstance};
|
use swf::avm2::types::{
|
||||||
|
Class as AbcClass, Instance as AbcInstance, Method as AbcMethod, MethodBody as AbcMethodBody,
|
||||||
|
};
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
/// All possible attributes for a given class.
|
/// All possible attributes for a given class.
|
||||||
|
@ -270,6 +272,46 @@ impl<'gc> Class<'gc> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_method_body(
|
||||||
|
avm2: &mut Avm2<'gc>,
|
||||||
|
mc: MutationContext<'gc, '_>,
|
||||||
|
translation_unit: TranslationUnit<'gc>,
|
||||||
|
method: &AbcMethod,
|
||||||
|
body: &AbcMethodBody,
|
||||||
|
) -> Result<GcCell<'gc, Self>, Error> {
|
||||||
|
let name = translation_unit.pool_string(method.name.as_u30(), mc)?;
|
||||||
|
let mut traits = Vec::new();
|
||||||
|
|
||||||
|
for trait_entry in body.traits.iter() {
|
||||||
|
traits.push(Trait::from_abc_trait(
|
||||||
|
translation_unit,
|
||||||
|
&trait_entry,
|
||||||
|
avm2,
|
||||||
|
mc,
|
||||||
|
)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(GcCell::allocate(
|
||||||
|
mc,
|
||||||
|
Self {
|
||||||
|
name: QName::dynamic_name(name),
|
||||||
|
super_class: None,
|
||||||
|
attributes: CollectWrapper(ClassAttributes::empty()),
|
||||||
|
protected_namespace: None,
|
||||||
|
interfaces: Vec::new(),
|
||||||
|
instance_init: Method::from_builtin(|_, _, _| {
|
||||||
|
Err("Do not call activation initializers!".into())
|
||||||
|
}),
|
||||||
|
instance_traits: traits,
|
||||||
|
class_init: Method::from_builtin(|_, _, _| {
|
||||||
|
Err("Do not call activation class initializers!".into())
|
||||||
|
}),
|
||||||
|
class_traits: Vec::new(),
|
||||||
|
traits_loaded: true,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn name(&self) -> &QName<'gc> {
|
pub fn name(&self) -> &QName<'gc> {
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
|
|
@ -365,8 +365,10 @@ impl<'gc> ScriptObject<'gc> {
|
||||||
|
|
||||||
/// Construct a bare class prototype with no base class.
|
/// Construct a bare class prototype with no base class.
|
||||||
///
|
///
|
||||||
/// This appears to be used specifically for interfaces, which have no base
|
/// This is used in cases where a prototype needs to exist, but it does not
|
||||||
/// class.
|
/// need to extend `Object`. This is the case for interfaces and activation
|
||||||
|
/// objects, both of which need to participate in the class mechanism but
|
||||||
|
/// are not `Object`s.
|
||||||
pub fn bare_prototype(
|
pub fn bare_prototype(
|
||||||
mc: MutationContext<'gc, '_>,
|
mc: MutationContext<'gc, '_>,
|
||||||
class: GcCell<'gc, Class<'gc>>,
|
class: GcCell<'gc, Class<'gc>>,
|
||||||
|
|
Loading…
Reference in New Issue