avm2: Fix many issues with supercalling methods
Supercalling on the global object now works; supercalling in the class initializer now works; and supercalling in static methods now uses the correct superclass.
This commit is contained in:
parent
d885697d9b
commit
f68baf0f76
|
@ -645,13 +645,8 @@ impl<'gc> Avm2<'gc> {
|
|||
}
|
||||
|
||||
/// Pushes an executable on the call stack
|
||||
pub fn push_call(
|
||||
&self,
|
||||
mc: &Mutation<'gc>,
|
||||
method: Method<'gc>,
|
||||
superclass: Option<ClassObject<'gc>>,
|
||||
) {
|
||||
self.call_stack.borrow_mut(mc).push(method, superclass)
|
||||
pub fn push_call(&self, mc: &Mutation<'gc>, method: Method<'gc>, class: Option<Class<'gc>>) {
|
||||
self.call_stack.borrow_mut(mc).push(method, class)
|
||||
}
|
||||
|
||||
/// Pushes script initializer (global init) on the call stack
|
||||
|
|
|
@ -105,7 +105,7 @@ pub struct Activation<'a, 'gc: 'a> {
|
|||
/// is a bytecode method, the movie will instead be the movie that the bytecode method came from.
|
||||
caller_movie: Option<Arc<SwfMovie>>,
|
||||
|
||||
/// The class that yielded the currently executing method.
|
||||
/// The superclass of the class that yielded the currently executing method.
|
||||
///
|
||||
/// This is used to maintain continuity when multiple methods supercall
|
||||
/// into one another. For example, if a class method supercalls a
|
||||
|
@ -115,7 +115,9 @@ pub struct Activation<'a, 'gc: 'a> {
|
|||
/// the same method again.
|
||||
///
|
||||
/// This will not be available outside of method, setter, or getter calls.
|
||||
subclass_object: Option<ClassObject<'gc>>,
|
||||
superclass_object: Option<ClassObject<'gc>>,
|
||||
|
||||
subclass: Option<Class<'gc>>,
|
||||
|
||||
/// The class of all objects returned from `newactivation`.
|
||||
///
|
||||
|
@ -168,7 +170,8 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
outer: ScopeChain::new(context.avm2.stage_domain),
|
||||
caller_domain: None,
|
||||
caller_movie: None,
|
||||
subclass_object: None,
|
||||
superclass_object: None,
|
||||
subclass: None,
|
||||
activation_class: None,
|
||||
stack_depth: context.avm2.stack.len(),
|
||||
scope_depth: context.avm2.scope_stack.len(),
|
||||
|
@ -197,7 +200,8 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
outer: ScopeChain::new(context.avm2.stage_domain),
|
||||
caller_domain: Some(domain),
|
||||
caller_movie: None,
|
||||
subclass_object: None,
|
||||
superclass_object: None,
|
||||
subclass: None,
|
||||
activation_class: None,
|
||||
stack_depth: context.avm2.stack.len(),
|
||||
scope_depth: context.avm2.scope_stack.len(),
|
||||
|
@ -262,7 +266,8 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
outer: ScopeChain::new(domain),
|
||||
caller_domain: Some(domain),
|
||||
caller_movie: script.translation_unit().map(|t| t.movie()),
|
||||
subclass_object: None,
|
||||
superclass_object: Some(context.avm2.classes().object), // The script global class extends Object
|
||||
subclass: Some(script.global_class()),
|
||||
activation_class,
|
||||
stack_depth: context.avm2.stack.len(),
|
||||
scope_depth: context.avm2.scope_stack.len(),
|
||||
|
@ -315,7 +320,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
value: Option<&Value<'gc>>,
|
||||
param_config: &ResolvedParamConfig<'gc>,
|
||||
user_arguments: &[Value<'gc>],
|
||||
callee: Option<Object<'gc>>,
|
||||
bound_class: Option<Class<'gc>>,
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let arg = if let Some(value) = value {
|
||||
value
|
||||
|
@ -330,7 +335,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
self,
|
||||
method,
|
||||
user_arguments,
|
||||
callee,
|
||||
bound_class,
|
||||
)?));
|
||||
};
|
||||
|
||||
|
@ -353,7 +358,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
method: Method<'gc>,
|
||||
user_arguments: &[Value<'gc>],
|
||||
signature: &[ResolvedParamConfig<'gc>],
|
||||
callee: Option<Object<'gc>>,
|
||||
bound_class: Option<Class<'gc>>,
|
||||
) -> Result<Vec<Value<'gc>>, Error<'gc>> {
|
||||
let mut arguments_list = Vec::new();
|
||||
for (arg, param_config) in user_arguments.iter().zip(signature.iter()) {
|
||||
|
@ -362,7 +367,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
Some(arg),
|
||||
param_config,
|
||||
user_arguments,
|
||||
callee,
|
||||
bound_class,
|
||||
)?);
|
||||
}
|
||||
|
||||
|
@ -379,7 +384,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
None,
|
||||
param_config,
|
||||
user_arguments,
|
||||
callee,
|
||||
bound_class,
|
||||
)?);
|
||||
}
|
||||
}
|
||||
|
@ -399,7 +404,8 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
outer: ScopeChain<'gc>,
|
||||
this: Object<'gc>,
|
||||
user_arguments: &[Value<'gc>],
|
||||
subclass_object: Option<ClassObject<'gc>>,
|
||||
superclass_object: Option<ClassObject<'gc>>,
|
||||
bound_class: Option<Class<'gc>>,
|
||||
callee: Object<'gc>,
|
||||
) -> Result<(), Error<'gc>> {
|
||||
let body: Result<_, Error<'gc>> = method
|
||||
|
@ -433,7 +439,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
self.outer = outer;
|
||||
self.caller_domain = Some(outer.domain());
|
||||
self.caller_movie = Some(method.owner_movie());
|
||||
self.subclass_object = subclass_object;
|
||||
self.superclass_object = superclass_object;
|
||||
self.activation_class = activation_class;
|
||||
self.stack_depth = self.context.avm2.stack.len();
|
||||
self.scope_depth = self.context.avm2.scope_stack.len();
|
||||
|
@ -453,7 +459,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
self,
|
||||
Method::Bytecode(method),
|
||||
user_arguments,
|
||||
Some(callee),
|
||||
bound_class,
|
||||
)?));
|
||||
}
|
||||
|
||||
|
@ -462,7 +468,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
Method::Bytecode(method),
|
||||
user_arguments,
|
||||
signature,
|
||||
Some(callee),
|
||||
bound_class,
|
||||
)?;
|
||||
|
||||
{
|
||||
|
@ -523,7 +529,8 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
/// properly supercall.
|
||||
pub fn from_builtin(
|
||||
context: &'a mut UpdateContext<'gc>,
|
||||
subclass_object: Option<ClassObject<'gc>>,
|
||||
superclass_object: Option<ClassObject<'gc>>,
|
||||
subclass: Option<Class<'gc>>,
|
||||
outer: ScopeChain<'gc>,
|
||||
caller_domain: Option<Domain<'gc>>,
|
||||
caller_movie: Option<Arc<SwfMovie>>,
|
||||
|
@ -537,7 +544,8 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
outer,
|
||||
caller_domain,
|
||||
caller_movie,
|
||||
subclass_object,
|
||||
superclass_object,
|
||||
subclass,
|
||||
activation_class: None,
|
||||
stack_depth: context.avm2.stack.len(),
|
||||
scope_depth: context.avm2.scope_stack.len(),
|
||||
|
@ -554,12 +562,8 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let superclass_object = self
|
||||
.subclass_object()
|
||||
.and_then(|c| c.superclass_object())
|
||||
.ok_or_else(|| {
|
||||
Error::from("Attempted to call super constructor without a superclass.")
|
||||
});
|
||||
let superclass_object = superclass_object?;
|
||||
.superclass_object
|
||||
.expect("Superclass object is required to run super_init");
|
||||
|
||||
superclass_object.call_native_init(receiver.into(), args, self)
|
||||
}
|
||||
|
@ -636,15 +640,6 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
self.context.borrow_gc()
|
||||
}
|
||||
|
||||
/// Get the class that defined the currently-executing method, if it
|
||||
/// exists.
|
||||
///
|
||||
/// If the currently-executing method is not part of an ES4 class, then
|
||||
/// this yields `None`.
|
||||
pub fn subclass_object(&self) -> Option<ClassObject<'gc>> {
|
||||
self.subclass_object
|
||||
}
|
||||
|
||||
pub fn scope_frame(&self) -> &[Scope<'gc>] {
|
||||
&self.context.avm2.scope_stack[self.scope_depth..]
|
||||
}
|
||||
|
@ -711,20 +706,22 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
/// Get the superclass of the class that defined the currently-executing
|
||||
/// method, if it exists.
|
||||
///
|
||||
/// If the currently-executing method is not part of an ES4 class, or the
|
||||
/// class does not have a superclass, then this yields an error. The `name`
|
||||
/// parameter allows you to provide the name of a property you were
|
||||
/// attempting to access on the object.
|
||||
pub fn superclass_object(&self, name: &Multiname<'gc>) -> Result<ClassObject<'gc>, Error<'gc>> {
|
||||
self.subclass_object
|
||||
.and_then(|bc| bc.superclass_object())
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"Cannot call supermethod (void) {} without a superclass",
|
||||
name.to_qualified_name(self.context.gc_context)
|
||||
)
|
||||
.into()
|
||||
})
|
||||
/// If the currently-executing method is not part of a class, or the class
|
||||
/// does not have a superclass, then this panics. The `name` parameter
|
||||
/// allows you to provide the name of a property you were attempting to
|
||||
/// access on the object.
|
||||
pub fn superclass_object(&self, name: &Multiname<'gc>) -> ClassObject<'gc> {
|
||||
self.superclass_object.unwrap_or_else(|| {
|
||||
panic!(
|
||||
"Cannot call supermethod {} without a superclass",
|
||||
name.to_qualified_name(self.context.gc_context),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the class that defined the currently-executing method, if it exists.
|
||||
pub fn subclass(&self) -> Option<Class<'gc>> {
|
||||
self.subclass
|
||||
}
|
||||
|
||||
/// Retrieve a namespace from the current constant pool.
|
||||
|
@ -1263,7 +1260,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
let method = self.table_method(method, index, false)?;
|
||||
// TODO: What scope should the function be executed with?
|
||||
let scope = self.create_scopechain();
|
||||
let function = FunctionObject::from_method(self, method, scope, None, None);
|
||||
let function = FunctionObject::from_method(self, method, scope, None, None, None);
|
||||
let value = function.call(receiver, &args, self)?;
|
||||
|
||||
self.push_stack(value);
|
||||
|
@ -1282,7 +1279,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
.pop_stack()
|
||||
.coerce_to_object_or_typeerror(self, Some(&multiname))?;
|
||||
|
||||
let superclass_object = self.superclass_object(&multiname)?;
|
||||
let superclass_object = self.superclass_object(&multiname);
|
||||
|
||||
let value = superclass_object.call_super(&multiname, receiver, &args, self)?;
|
||||
|
||||
|
@ -1302,7 +1299,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
.pop_stack()
|
||||
.coerce_to_object_or_typeerror(self, Some(&multiname))?;
|
||||
|
||||
let superclass_object = self.superclass_object(&multiname)?;
|
||||
let superclass_object = self.superclass_object(&multiname);
|
||||
|
||||
superclass_object.call_super(&multiname, receiver, &args, self)?;
|
||||
|
||||
|
@ -1534,7 +1531,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
.pop_stack()
|
||||
.coerce_to_object_or_typeerror(self, Some(&multiname))?;
|
||||
|
||||
let superclass_object = self.superclass_object(&multiname)?;
|
||||
let superclass_object = self.superclass_object(&multiname);
|
||||
|
||||
let value = superclass_object.get_super(&multiname, object, self)?;
|
||||
|
||||
|
@ -1553,7 +1550,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
.pop_stack()
|
||||
.coerce_to_object_or_typeerror(self, Some(&multiname))?;
|
||||
|
||||
let superclass_object = self.superclass_object(&multiname)?;
|
||||
let superclass_object = self.superclass_object(&multiname);
|
||||
|
||||
superclass_object.set_super(&multiname, value, object, self)?;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::avm2::class::Class;
|
||||
use crate::avm2::function::display_function;
|
||||
use crate::avm2::method::Method;
|
||||
use crate::avm2::object::ClassObject;
|
||||
use crate::string::WString;
|
||||
use gc_arena::Collect;
|
||||
|
||||
|
@ -12,7 +12,7 @@ pub enum CallNode<'gc> {
|
|||
GlobalInit(Script<'gc>),
|
||||
Method {
|
||||
method: Method<'gc>,
|
||||
superclass: Option<ClassObject<'gc>>,
|
||||
class: Option<Class<'gc>>,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -27,8 +27,8 @@ impl<'gc> CallStack<'gc> {
|
|||
Self { stack: Vec::new() }
|
||||
}
|
||||
|
||||
pub fn push(&mut self, method: Method<'gc>, superclass: Option<ClassObject<'gc>>) {
|
||||
self.stack.push(CallNode::Method { method, superclass })
|
||||
pub fn push(&mut self, method: Method<'gc>, class: Option<Class<'gc>>) {
|
||||
self.stack.push(CallNode::Method { method, class })
|
||||
}
|
||||
|
||||
pub fn push_global_init(&mut self, script: Script<'gc>) {
|
||||
|
@ -59,9 +59,7 @@ impl<'gc> CallStack<'gc> {
|
|||
// added by Ruffle
|
||||
output.push_utf8(&format!("global$init() [TU={}]", name));
|
||||
}
|
||||
CallNode::Method { method, superclass } => {
|
||||
display_function(output, method, *superclass)
|
||||
}
|
||||
CallNode::Method { method, class } => display_function(output, method, *class),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ use std::mem::size_of;
|
|||
use super::function::display_function;
|
||||
use super::method::Method;
|
||||
use super::ClassObject;
|
||||
use super::Object;
|
||||
|
||||
/// An error generated while handling AVM2 logic
|
||||
pub enum Error<'gc> {
|
||||
|
@ -778,7 +777,7 @@ pub fn make_mismatch_error<'gc>(
|
|||
activation: &mut Activation<'_, 'gc>,
|
||||
method: Method<'gc>,
|
||||
user_arguments: &[Value<'gc>],
|
||||
callee: Option<Object<'gc>>,
|
||||
bound_class: Option<Class<'gc>>,
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let expected_num_params = method
|
||||
.signature()
|
||||
|
@ -787,17 +786,8 @@ pub fn make_mismatch_error<'gc>(
|
|||
.count();
|
||||
|
||||
let mut function_name = WString::new();
|
||||
let bound_superclass = callee.and_then(|callee| {
|
||||
if let Some(cls) = callee.as_class_object() {
|
||||
Some(cls)
|
||||
} else {
|
||||
callee
|
||||
.as_function_object()
|
||||
.and_then(|f| f.as_executable().and_then(|e| e.bound_superclass()))
|
||||
}
|
||||
});
|
||||
|
||||
display_function(&mut function_name, &method, bound_superclass);
|
||||
display_function(&mut function_name, &method, bound_class);
|
||||
|
||||
return Err(Error::AvmError(argument_error(
|
||||
activation,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::avm2::activation::Activation;
|
||||
use crate::avm2::class::Class;
|
||||
use crate::avm2::method::{Method, ParamConfig};
|
||||
use crate::avm2::object::{ClassObject, Object};
|
||||
use crate::avm2::scope::ScopeChain;
|
||||
|
@ -25,12 +26,14 @@ pub struct BoundMethod<'gc> {
|
|||
/// `Some` value indicates a bound executable.
|
||||
bound_receiver: Option<Object<'gc>>,
|
||||
|
||||
/// The bound class for this method.
|
||||
/// The superclass of the bound class for this method.
|
||||
///
|
||||
/// The `class` is the class that defined this method. If `None`,
|
||||
/// then there is no defining class and `super` operations should fall
|
||||
/// back to the `receiver`.
|
||||
bound_class: Option<ClassObject<'gc>>,
|
||||
/// The `bound_superclass` is the superclass of the class that defined
|
||||
/// this method. If `None`, then there is no defining class and `super`
|
||||
/// operations should be invalid.
|
||||
bound_superclass: Option<ClassObject<'gc>>,
|
||||
|
||||
bound_class: Option<Class<'gc>>,
|
||||
}
|
||||
|
||||
impl<'gc> BoundMethod<'gc> {
|
||||
|
@ -39,12 +42,14 @@ impl<'gc> BoundMethod<'gc> {
|
|||
scope: ScopeChain<'gc>,
|
||||
receiver: Option<Object<'gc>>,
|
||||
superclass: Option<ClassObject<'gc>>,
|
||||
class: Option<Class<'gc>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
method,
|
||||
scope,
|
||||
bound_receiver: receiver,
|
||||
bound_class: superclass,
|
||||
bound_superclass: superclass,
|
||||
bound_class: class,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,6 +75,7 @@ impl<'gc> BoundMethod<'gc> {
|
|||
self.method,
|
||||
self.scope,
|
||||
receiver,
|
||||
self.bound_superclass,
|
||||
self.bound_class,
|
||||
arguments,
|
||||
activation,
|
||||
|
@ -77,7 +83,7 @@ impl<'gc> BoundMethod<'gc> {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn bound_superclass(&self) -> Option<ClassObject<'gc>> {
|
||||
pub fn bound_class(&self) -> Option<Class<'gc>> {
|
||||
self.bound_class
|
||||
}
|
||||
|
||||
|
@ -87,7 +93,7 @@ impl<'gc> BoundMethod<'gc> {
|
|||
|
||||
pub fn debug_full_name(&self) -> WString {
|
||||
let mut output = WString::new();
|
||||
display_function(&mut output, &self.as_method(), self.bound_superclass());
|
||||
display_function(&mut output, &self.as_method(), self.bound_class());
|
||||
output
|
||||
}
|
||||
|
||||
|
@ -135,7 +141,8 @@ pub fn exec<'gc>(
|
|||
method: Method<'gc>,
|
||||
scope: ScopeChain<'gc>,
|
||||
receiver: Object<'gc>,
|
||||
bound_class: Option<ClassObject<'gc>>,
|
||||
bound_superclass: Option<ClassObject<'gc>>,
|
||||
bound_class: Option<Class<'gc>>,
|
||||
mut arguments: &[Value<'gc>],
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
callee: Object<'gc>,
|
||||
|
@ -146,6 +153,7 @@ pub fn exec<'gc>(
|
|||
let caller_movie = activation.caller_movie();
|
||||
let mut activation = Activation::from_builtin(
|
||||
activation.context,
|
||||
bound_superclass,
|
||||
bound_class,
|
||||
scope,
|
||||
caller_domain,
|
||||
|
@ -173,7 +181,7 @@ pub fn exec<'gc>(
|
|||
method,
|
||||
arguments,
|
||||
resolved_signature,
|
||||
Some(callee),
|
||||
bound_class,
|
||||
)?;
|
||||
activation
|
||||
.context
|
||||
|
@ -192,7 +200,15 @@ pub fn exec<'gc>(
|
|||
// This used to be a one step called Activation::from_method,
|
||||
// but avoiding moving an Activation around helps perf
|
||||
let mut activation = Activation::from_nothing(activation.context);
|
||||
activation.init_from_method(bm, scope, receiver, arguments, bound_class, callee)?;
|
||||
activation.init_from_method(
|
||||
bm,
|
||||
scope,
|
||||
receiver,
|
||||
arguments,
|
||||
bound_superclass,
|
||||
bound_class,
|
||||
callee,
|
||||
)?;
|
||||
activation
|
||||
.context
|
||||
.avm2
|
||||
|
@ -229,20 +245,12 @@ impl<'gc> fmt::Debug for BoundMethod<'gc> {
|
|||
pub fn display_function<'gc>(
|
||||
output: &mut WString,
|
||||
method: &Method<'gc>,
|
||||
superclass: Option<ClassObject<'gc>>,
|
||||
bound_class: Option<Class<'gc>>,
|
||||
) {
|
||||
let class_defs = superclass.map(|superclass| {
|
||||
let i_class = superclass.inner_class_definition();
|
||||
let name = i_class.name().to_qualified_name_no_mc();
|
||||
if let Some(bound_class) = bound_class {
|
||||
let name = bound_class.name().to_qualified_name_no_mc();
|
||||
output.push_str(&name);
|
||||
|
||||
(
|
||||
i_class,
|
||||
i_class
|
||||
.c_class()
|
||||
.expect("inner_class_definition should be an i_class"),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
match method {
|
||||
Method::Native(method) => {
|
||||
|
@ -252,27 +260,24 @@ pub fn display_function<'gc>(
|
|||
Method::Bytecode(method) => {
|
||||
// NOTE: The name of a bytecode method refers to the name of the trait that contains the method,
|
||||
// rather than the name of the method itself.
|
||||
if let Some((i_class, c_class)) = class_defs {
|
||||
if c_class
|
||||
if let Some(bound_class) = bound_class {
|
||||
if bound_class
|
||||
.instance_init()
|
||||
.into_bytecode()
|
||||
.map(|b| Gc::ptr_eq(b, *method))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
output.push_utf8("$cinit");
|
||||
} else if !i_class
|
||||
.instance_init()
|
||||
.into_bytecode()
|
||||
.map(|b| Gc::ptr_eq(b, *method))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
// TODO: Ideally, the declaring trait of this executable should already be attached here, that way
|
||||
// we can avoid needing to lookup the trait like this.
|
||||
if bound_class.c_class().is_none() {
|
||||
// If the associated class has no c_class, it is a c_class,
|
||||
// and the instance initializer is the class initializer.
|
||||
output.push_utf8("cinit");
|
||||
}
|
||||
// We purposely do nothing for instance initializers
|
||||
} else {
|
||||
let mut method_trait = None;
|
||||
|
||||
// First search instance traits for the method
|
||||
let instance_traits = i_class.traits();
|
||||
for t in &*instance_traits {
|
||||
let traits = bound_class.traits();
|
||||
for t in &*traits {
|
||||
if let Some(b) = t.as_method().and_then(|m| m.into_bytecode().ok()) {
|
||||
if Gc::ptr_eq(b, *method) {
|
||||
method_trait = Some(t);
|
||||
|
@ -281,21 +286,6 @@ pub fn display_function<'gc>(
|
|||
}
|
||||
}
|
||||
|
||||
let class_traits = c_class.traits();
|
||||
if method_trait.is_none() {
|
||||
// If we can't find it in instance traits, search class traits instead
|
||||
for t in class_traits.iter() {
|
||||
if let Some(b) = t.as_method().and_then(|m| m.into_bytecode().ok()) {
|
||||
if Gc::ptr_eq(b, *method) {
|
||||
// Class traits always start with $
|
||||
output.push_char('$');
|
||||
method_trait = Some(t);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(method_trait) = method_trait {
|
||||
output.push_char('/');
|
||||
match method_trait.kind() {
|
||||
|
@ -317,7 +307,6 @@ pub fn display_function<'gc>(
|
|||
}
|
||||
// TODO: What happens if we can't find the trait?
|
||||
}
|
||||
// We purposely do nothing for instance initializers
|
||||
} else if method.is_function && !method.method_name().is_empty() {
|
||||
output.push_utf8("Function/");
|
||||
output.push_utf8(&method.method_name());
|
||||
|
|
|
@ -133,7 +133,8 @@ pub fn class_init<'gc>(
|
|||
Method::from_builtin(*method, name, gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
|
|
@ -58,7 +58,8 @@ fn class_init<'gc>(
|
|||
Method::from_builtin(to_string, "toString", gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
@ -70,7 +71,8 @@ fn class_init<'gc>(
|
|||
Method::from_builtin(value_of, "valueOf", gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
|
|
@ -20,6 +20,17 @@ pub fn instance_init<'gc>(
|
|||
Err("Classes cannot be constructed.".into())
|
||||
}
|
||||
|
||||
/// Implements `Class`'s native instance initializer.
|
||||
///
|
||||
/// This exists so that super() calls in class initializers will work.
|
||||
fn native_instance_init<'gc>(
|
||||
_activation: &mut Activation<'_, 'gc>,
|
||||
_this: Object<'gc>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
/// Implement's `Class`'s class initializer.
|
||||
pub fn class_init<'gc>(
|
||||
_activation: &mut Activation<'_, 'gc>,
|
||||
|
@ -54,6 +65,15 @@ pub fn create_i_class<'gc>(
|
|||
gc_context,
|
||||
);
|
||||
|
||||
class_i_class.set_native_instance_init(
|
||||
gc_context,
|
||||
Method::from_builtin(
|
||||
native_instance_init,
|
||||
"<Class native instance initializer>",
|
||||
gc_context,
|
||||
),
|
||||
);
|
||||
|
||||
const PUBLIC_INSTANCE_PROPERTIES: &[(
|
||||
&str,
|
||||
Option<NativeMethodImpl>,
|
||||
|
|
|
@ -298,7 +298,8 @@ pub fn class_init<'gc>(
|
|||
Method::from_builtin(*method, name, gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
|
|
@ -59,7 +59,8 @@ pub fn class_init<'gc>(
|
|||
Method::from_builtin(call, "call", activation.context.gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
@ -71,7 +72,8 @@ pub fn class_init<'gc>(
|
|||
Method::from_builtin(apply, "apply", activation.context.gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
@ -83,7 +85,8 @@ pub fn class_init<'gc>(
|
|||
Method::from_builtin(to_string, "toString", activation.context.gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
@ -95,7 +98,8 @@ pub fn class_init<'gc>(
|
|||
Method::from_builtin(to_string, "toLocaleString", activation.context.gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
|
|
@ -58,7 +58,8 @@ fn class_init<'gc>(
|
|||
Method::from_builtin(to_exponential, "toExponential", gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
@ -70,7 +71,8 @@ fn class_init<'gc>(
|
|||
Method::from_builtin(to_fixed, "toFixed", gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
@ -82,7 +84,8 @@ fn class_init<'gc>(
|
|||
Method::from_builtin(to_precision, "toPrecision", gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
@ -94,7 +97,8 @@ fn class_init<'gc>(
|
|||
Method::from_builtin(to_string, "toLocaleString", gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
@ -106,7 +110,8 @@ fn class_init<'gc>(
|
|||
Method::from_builtin(to_string, "toString", gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
@ -118,7 +123,8 @@ fn class_init<'gc>(
|
|||
Method::from_builtin(value_of, "valueOf", gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
|
|
@ -130,7 +130,8 @@ pub fn class_init<'gc>(
|
|||
Method::from_builtin(*method, name, gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
|
|
@ -58,7 +58,8 @@ fn class_init<'gc>(
|
|||
Method::from_builtin(to_exponential, "toExponential", gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
@ -70,7 +71,8 @@ fn class_init<'gc>(
|
|||
Method::from_builtin(to_fixed, "toFixed", gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
@ -82,7 +84,8 @@ fn class_init<'gc>(
|
|||
Method::from_builtin(to_precision, "toPrecision", gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
@ -94,7 +97,8 @@ fn class_init<'gc>(
|
|||
Method::from_builtin(to_string, "toLocaleString", gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
@ -106,7 +110,8 @@ fn class_init<'gc>(
|
|||
Method::from_builtin(to_string, "toString", gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
@ -118,7 +123,8 @@ fn class_init<'gc>(
|
|||
Method::from_builtin(value_of, "valueOf", gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
|
|
@ -24,14 +24,14 @@ fn class_call<'gc>(
|
|||
_this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let this_class = activation.subclass_object().unwrap();
|
||||
let object_class = activation.avm2().classes().object;
|
||||
|
||||
if args.is_empty() {
|
||||
return this_class.construct(activation, args).map(|o| o.into());
|
||||
return object_class.construct(activation, args).map(|o| o.into());
|
||||
}
|
||||
let arg = args.get(0).cloned().unwrap();
|
||||
if matches!(arg, Value::Undefined) || matches!(arg, Value::Null) {
|
||||
return this_class.construct(activation, args).map(|o| o.into());
|
||||
return object_class.construct(activation, args).map(|o| o.into());
|
||||
}
|
||||
Ok(arg)
|
||||
}
|
||||
|
@ -54,7 +54,8 @@ pub fn class_init<'gc>(
|
|||
Method::from_builtin(has_own_property, "hasOwnProperty", gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
@ -66,7 +67,8 @@ pub fn class_init<'gc>(
|
|||
Method::from_builtin(property_is_enumerable, "propertyIsEnumerable", gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
@ -82,7 +84,8 @@ pub fn class_init<'gc>(
|
|||
),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
@ -94,7 +97,8 @@ pub fn class_init<'gc>(
|
|||
Method::from_builtin(is_prototype_of, "isPrototypeOf", gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
@ -106,7 +110,8 @@ pub fn class_init<'gc>(
|
|||
Method::from_builtin(to_string, "toString", gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
@ -118,7 +123,8 @@ pub fn class_init<'gc>(
|
|||
Method::from_builtin(to_locale_string, "toLocaleString", gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
@ -130,7 +136,8 @@ pub fn class_init<'gc>(
|
|||
Method::from_builtin(value_of, "valueOf", gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
|
|
@ -69,7 +69,7 @@ pub fn call_handler<'gc>(
|
|||
_this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let this_class = activation.subclass_object().unwrap();
|
||||
let this_class = activation.avm2().classes().regexp;
|
||||
|
||||
if args.len() == 1 {
|
||||
let arg = args.get(0).cloned().unwrap();
|
||||
|
|
|
@ -76,7 +76,8 @@ pub fn class_init<'gc>(
|
|||
Method::from_builtin(*method, name, gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
|
|
@ -58,7 +58,8 @@ fn class_init<'gc>(
|
|||
Method::from_builtin(to_exponential, "toExponential", gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
@ -70,7 +71,8 @@ fn class_init<'gc>(
|
|||
Method::from_builtin(to_fixed, "toFixed", gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
@ -82,7 +84,8 @@ fn class_init<'gc>(
|
|||
Method::from_builtin(to_precision, "toPrecision", gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
@ -94,7 +97,8 @@ fn class_init<'gc>(
|
|||
Method::from_builtin(to_string, "toLocaleString", gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
@ -106,7 +110,8 @@ fn class_init<'gc>(
|
|||
Method::from_builtin(to_string, "toString", gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
@ -118,7 +123,8 @@ fn class_init<'gc>(
|
|||
Method::from_builtin(value_of, "valueOf", gc_context),
|
||||
scope,
|
||||
None,
|
||||
Some(this_class),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
activation,
|
||||
|
|
|
@ -72,16 +72,18 @@ fn class_call<'gc>(
|
|||
)?));
|
||||
}
|
||||
|
||||
let this_class = activation.subclass_object().unwrap();
|
||||
let this_class = activation
|
||||
.subclass()
|
||||
.expect("Method call without bound class?");
|
||||
|
||||
let value_type = this_class
|
||||
.inner_class_definition()
|
||||
.param()
|
||||
.ok_or("Cannot convert to unparametrized Vector")?; // technically unreachable
|
||||
.expect("Cannot convert to unparametrized Vector"); // technically unreachable
|
||||
|
||||
let arg = args.get(0).cloned().unwrap();
|
||||
let arg = arg.as_object().ok_or("Cannot convert to Vector")?;
|
||||
|
||||
if arg.instance_class() == this_class.inner_class_definition() {
|
||||
if arg.instance_class() == this_class {
|
||||
return Ok(arg.into());
|
||||
}
|
||||
|
||||
|
@ -256,9 +258,9 @@ pub fn concat<'gc>(
|
|||
|
||||
// this is Vector.<int/uint/Number/*>
|
||||
let my_base_vector_class = activation
|
||||
.subclass_object()
|
||||
.expect("Method call without bound class?")
|
||||
.inner_class_definition();
|
||||
.subclass()
|
||||
.expect("Method call without bound class?");
|
||||
|
||||
if !arg.is_of_type(activation, my_base_vector_class) {
|
||||
let base_vector_name = my_base_vector_class
|
||||
.name()
|
||||
|
|
|
@ -600,17 +600,18 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
// Execute immediately if this method doesn't require binding
|
||||
if !full_method.method.needs_arguments_object() {
|
||||
let ClassBoundMethod {
|
||||
method,
|
||||
class,
|
||||
super_class_obj,
|
||||
scope,
|
||||
class_obj,
|
||||
..
|
||||
method,
|
||||
} = full_method;
|
||||
|
||||
return exec(
|
||||
method,
|
||||
scope.expect("Scope should exist here"),
|
||||
self.into(),
|
||||
class_obj,
|
||||
super_class_obj,
|
||||
Some(class),
|
||||
arguments,
|
||||
activation,
|
||||
self.into(), // Callee deliberately invalid.
|
||||
|
|
|
@ -210,7 +210,7 @@ impl<'gc> ClassObject<'gc> {
|
|||
|
||||
self.instance_vtable().init_vtable(
|
||||
class,
|
||||
Some(self),
|
||||
self.superclass_object(),
|
||||
&class.traits(),
|
||||
Some(self.instance_scope()),
|
||||
self.superclass_object().map(|cls| cls.instance_vtable()),
|
||||
|
@ -247,14 +247,16 @@ impl<'gc> ClassObject<'gc> {
|
|||
.c_class()
|
||||
.expect("ClassObject should have an i_class");
|
||||
|
||||
let class_classobject = activation.avm2().classes().class;
|
||||
|
||||
// class vtable == class traits + Class instance traits
|
||||
let class_vtable = VTable::empty(activation.context.gc_context);
|
||||
class_vtable.init_vtable(
|
||||
c_class,
|
||||
Some(self),
|
||||
Some(class_classobject),
|
||||
&c_class.traits(),
|
||||
Some(self.class_scope()),
|
||||
Some(activation.avm2().classes().class.instance_vtable()),
|
||||
Some(class_classobject.instance_vtable()),
|
||||
activation.context.gc_context,
|
||||
);
|
||||
|
||||
|
@ -327,6 +329,7 @@ impl<'gc> ClassObject<'gc> {
|
|||
activation: &mut Activation<'_, 'gc>,
|
||||
) -> Result<(), Error<'gc>> {
|
||||
let object: Object<'gc> = self.into();
|
||||
let class_classobject = activation.avm2().classes().class;
|
||||
|
||||
let scope = self.0.class_scope;
|
||||
let c_class = self
|
||||
|
@ -340,7 +343,8 @@ impl<'gc> ClassObject<'gc> {
|
|||
class_initializer,
|
||||
scope,
|
||||
Some(object),
|
||||
Some(self),
|
||||
Some(class_classobject),
|
||||
Some(c_class),
|
||||
);
|
||||
|
||||
class_init_fn.call(object.into(), &[], activation)?;
|
||||
|
@ -361,7 +365,8 @@ impl<'gc> ClassObject<'gc> {
|
|||
method,
|
||||
scope,
|
||||
receiver.coerce_to_object(activation)?,
|
||||
Some(self),
|
||||
self.superclass_object(),
|
||||
Some(self.inner_class_definition()),
|
||||
arguments,
|
||||
activation,
|
||||
self.into(),
|
||||
|
@ -385,7 +390,8 @@ impl<'gc> ClassObject<'gc> {
|
|||
method,
|
||||
scope,
|
||||
receiver.coerce_to_object(activation)?,
|
||||
Some(self),
|
||||
self.superclass_object(),
|
||||
Some(self.inner_class_definition()),
|
||||
arguments,
|
||||
activation,
|
||||
self.into(),
|
||||
|
@ -444,17 +450,18 @@ impl<'gc> ClassObject<'gc> {
|
|||
if let Some(Property::Method { disp_id, .. }) = property {
|
||||
// todo: handle errors
|
||||
let ClassBoundMethod {
|
||||
class_obj,
|
||||
class,
|
||||
super_class_obj,
|
||||
scope,
|
||||
method,
|
||||
..
|
||||
} = self.instance_vtable().get_full_method(disp_id).unwrap();
|
||||
let callee = FunctionObject::from_method(
|
||||
activation,
|
||||
method,
|
||||
scope.expect("Scope should exist here"),
|
||||
Some(receiver),
|
||||
class_obj,
|
||||
super_class_obj,
|
||||
Some(class),
|
||||
);
|
||||
|
||||
callee.call(receiver.into(), arguments, activation)
|
||||
|
@ -504,17 +511,18 @@ impl<'gc> ClassObject<'gc> {
|
|||
) => {
|
||||
// todo: handle errors
|
||||
let ClassBoundMethod {
|
||||
class_obj,
|
||||
class,
|
||||
super_class_obj,
|
||||
scope,
|
||||
method,
|
||||
..
|
||||
} = self.instance_vtable().get_full_method(disp_id).unwrap();
|
||||
let callee = FunctionObject::from_method(
|
||||
activation,
|
||||
method,
|
||||
scope.expect("Scope should exist here"),
|
||||
Some(receiver),
|
||||
class_obj,
|
||||
super_class_obj,
|
||||
Some(class),
|
||||
);
|
||||
|
||||
// We call getters, but return the actual function object for normal methods
|
||||
|
@ -587,13 +595,13 @@ impl<'gc> ClassObject<'gc> {
|
|||
}) => {
|
||||
// todo: handle errors
|
||||
let ClassBoundMethod {
|
||||
class_obj,
|
||||
class,
|
||||
super_class_obj,
|
||||
scope,
|
||||
method,
|
||||
..
|
||||
} = self.instance_vtable().get_full_method(disp_id).unwrap();
|
||||
let callee =
|
||||
FunctionObject::from_method(activation, method, scope.expect("Scope should exist here"), Some(receiver), class_obj);
|
||||
FunctionObject::from_method(activation, method, scope.expect("Scope should exist here"), Some(receiver), super_class_obj, Some(class));
|
||||
|
||||
callee.call(receiver.into(), &[value], activation)?;
|
||||
Ok(())
|
||||
|
@ -765,7 +773,8 @@ impl<'gc> TObject<'gc> for ClassObject<'gc> {
|
|||
call_handler,
|
||||
scope,
|
||||
self.into(),
|
||||
Some(self),
|
||||
self.superclass_object(),
|
||||
Some(self.inner_class_definition()),
|
||||
arguments,
|
||||
activation,
|
||||
self.into(),
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
//! Function object impl
|
||||
|
||||
use crate::avm2::activation::Activation;
|
||||
use crate::avm2::class::Class;
|
||||
use crate::avm2::function::BoundMethod;
|
||||
use crate::avm2::method::{Method, NativeMethod};
|
||||
use crate::avm2::object::script_object::{ScriptObject, ScriptObjectData};
|
||||
|
@ -50,6 +51,7 @@ pub fn function_allocator<'gc>(
|
|||
activation.create_scopechain(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)),
|
||||
prototype: Lock::new(None),
|
||||
},
|
||||
|
@ -106,7 +108,7 @@ impl<'gc> FunctionObject<'gc> {
|
|||
method: Method<'gc>,
|
||||
scope: ScopeChain<'gc>,
|
||||
) -> Result<FunctionObject<'gc>, Error<'gc>> {
|
||||
let this = Self::from_method(activation, method, scope, None, None);
|
||||
let this = Self::from_method(activation, method, scope, None, None, None);
|
||||
let es3_proto = activation
|
||||
.avm2()
|
||||
.classes()
|
||||
|
@ -127,10 +129,17 @@ impl<'gc> FunctionObject<'gc> {
|
|||
method: Method<'gc>,
|
||||
scope: ScopeChain<'gc>,
|
||||
receiver: Option<Object<'gc>>,
|
||||
subclass_object: Option<ClassObject<'gc>>,
|
||||
superclass_object: Option<ClassObject<'gc>>,
|
||||
subclass: Option<Class<'gc>>,
|
||||
) -> FunctionObject<'gc> {
|
||||
let fn_class = activation.avm2().classes().function;
|
||||
let exec = BoundMethod::from_method(method, scope, receiver, subclass_object);
|
||||
let exec = BoundMethod::from_method(
|
||||
method,
|
||||
scope,
|
||||
receiver,
|
||||
bound_superclass_object,
|
||||
bound_class,
|
||||
);
|
||||
|
||||
FunctionObject(Gc::new(
|
||||
activation.context.gc_context,
|
||||
|
|
|
@ -360,13 +360,13 @@ pub fn optimize<'gc>(
|
|||
// but this works since it's guaranteed to be set in `Activation::from_method`.
|
||||
let this_value = activation.local_register(0);
|
||||
|
||||
let this_class = if let Some(this_class) = activation.subclass_object() {
|
||||
if this_value.is_of_type(activation, this_class.inner_class_definition()) {
|
||||
Some(this_class.inner_class_definition())
|
||||
let this_class = if let Some(this_class) = activation.subclass() {
|
||||
if this_value.is_of_type(activation, this_class) {
|
||||
Some(this_class)
|
||||
} else if let Some(this_object) = this_value.as_object() {
|
||||
if this_object
|
||||
.as_class_object()
|
||||
.map(|c| c.inner_class_definition() == this_class.inner_class_definition())
|
||||
.map(|c| c.inner_class_definition() == this_class)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
// Static method
|
||||
|
|
|
@ -641,7 +641,7 @@ impl<'gc> Script<'gc> {
|
|||
|
||||
globals.vtable().init_vtable(
|
||||
globals.instance_class(),
|
||||
self.0.read().global_class_obj,
|
||||
Some(context.avm2.classes().object),
|
||||
&self.traits()?,
|
||||
Some(scope),
|
||||
None,
|
||||
|
|
|
@ -55,7 +55,7 @@ impl PartialEq for VTable<'_> {
|
|||
#[collect(no_drop)]
|
||||
pub struct ClassBoundMethod<'gc> {
|
||||
pub class: Class<'gc>,
|
||||
pub class_obj: Option<ClassObject<'gc>>,
|
||||
pub super_class_obj: Option<ClassObject<'gc>>,
|
||||
pub scope: Option<ScopeChain<'gc>>,
|
||||
pub method: Method<'gc>,
|
||||
}
|
||||
|
@ -211,7 +211,7 @@ impl<'gc> VTable<'gc> {
|
|||
pub fn init_vtable(
|
||||
self,
|
||||
defining_class_def: Class<'gc>,
|
||||
defining_class: Option<ClassObject<'gc>>,
|
||||
super_class_obj: Option<ClassObject<'gc>>,
|
||||
traits: &[Trait<'gc>],
|
||||
scope: Option<ScopeChain<'gc>>,
|
||||
superclass_vtable: Option<Self>,
|
||||
|
@ -321,7 +321,7 @@ impl<'gc> VTable<'gc> {
|
|||
TraitKind::Method { method, .. } => {
|
||||
let entry = ClassBoundMethod {
|
||||
class: defining_class_def,
|
||||
class_obj: defining_class,
|
||||
super_class_obj,
|
||||
scope,
|
||||
method: *method,
|
||||
};
|
||||
|
@ -350,7 +350,7 @@ impl<'gc> VTable<'gc> {
|
|||
TraitKind::Getter { method, .. } => {
|
||||
let entry = ClassBoundMethod {
|
||||
class: defining_class_def,
|
||||
class_obj: defining_class,
|
||||
super_class_obj,
|
||||
scope,
|
||||
method: *method,
|
||||
};
|
||||
|
@ -388,7 +388,7 @@ impl<'gc> VTable<'gc> {
|
|||
TraitKind::Setter { method, .. } => {
|
||||
let entry = ClassBoundMethod {
|
||||
class: defining_class_def,
|
||||
class_obj: defining_class,
|
||||
super_class_obj,
|
||||
scope,
|
||||
method: *method,
|
||||
};
|
||||
|
@ -518,10 +518,10 @@ impl<'gc> VTable<'gc> {
|
|||
method: ClassBoundMethod<'gc>,
|
||||
) -> FunctionObject<'gc> {
|
||||
let ClassBoundMethod {
|
||||
class_obj,
|
||||
class,
|
||||
super_class_obj,
|
||||
scope,
|
||||
method,
|
||||
..
|
||||
} = method;
|
||||
|
||||
FunctionObject::from_method(
|
||||
|
@ -529,7 +529,8 @@ impl<'gc> VTable<'gc> {
|
|||
method,
|
||||
scope.expect("Scope should exist here"),
|
||||
Some(receiver),
|
||||
class_obj,
|
||||
super_class_obj,
|
||||
Some(class),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue