Merge pull request #7095 from adrian17/avm2-fix-bare-function-scope
avm2: Pull methods' scopes from vtable, not class
This commit is contained in:
commit
6eb4b485f0
|
@ -12,7 +12,7 @@ use crate::avm2::property::Property;
|
||||||
use crate::avm2::regexp::RegExp;
|
use crate::avm2::regexp::RegExp;
|
||||||
use crate::avm2::value::{Hint, Value};
|
use crate::avm2::value::{Hint, Value};
|
||||||
use crate::avm2::vector::VectorStorage;
|
use crate::avm2::vector::VectorStorage;
|
||||||
use crate::avm2::vtable::VTable;
|
use crate::avm2::vtable::{ClassBoundMethod, VTable};
|
||||||
use crate::avm2::Error;
|
use crate::avm2::Error;
|
||||||
use crate::backend::audio::{SoundHandle, SoundInstanceHandle};
|
use crate::backend::audio::{SoundHandle, SoundInstanceHandle};
|
||||||
use crate::bitmap::bitmap_data::BitmapData;
|
use crate::bitmap::bitmap_data::BitmapData;
|
||||||
|
@ -303,14 +303,18 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
}
|
}
|
||||||
Some(Property::Method { disp_id }) => {
|
Some(Property::Method { disp_id }) => {
|
||||||
let vtable = self.vtable().unwrap();
|
let vtable = self.vtable().unwrap();
|
||||||
if let Some((superclass, method)) = vtable.get_full_method(disp_id) {
|
if let Some(ClassBoundMethod {
|
||||||
|
class,
|
||||||
|
scope,
|
||||||
|
method,
|
||||||
|
}) = vtable.get_full_method(disp_id)
|
||||||
|
{
|
||||||
if !method.needs_arguments_object() {
|
if !method.needs_arguments_object() {
|
||||||
let scope = superclass.unwrap().instance_scope();
|
Executable::from_method(method, scope, None, Some(class)).exec(
|
||||||
Executable::from_method(method, scope, None, superclass).exec(
|
|
||||||
Some(self.into()),
|
Some(self.into()),
|
||||||
arguments,
|
arguments,
|
||||||
activation,
|
activation,
|
||||||
superclass.unwrap().into(), //Deliberately invalid.
|
class.into(), //Deliberately invalid.
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
if let Some(bound_method) = self.get_bound_method(disp_id) {
|
if let Some(bound_method) = self.get_bound_method(disp_id) {
|
||||||
|
|
|
@ -11,7 +11,7 @@ use crate::avm2::object::{Multiname, Object, ObjectPtr, TObject};
|
||||||
use crate::avm2::property::Property;
|
use crate::avm2::property::Property;
|
||||||
use crate::avm2::scope::{Scope, ScopeChain};
|
use crate::avm2::scope::{Scope, ScopeChain};
|
||||||
use crate::avm2::value::Value;
|
use crate::avm2::value::Value;
|
||||||
use crate::avm2::vtable::VTable;
|
use crate::avm2::vtable::{ClassBoundMethod, VTable};
|
||||||
use crate::avm2::Error;
|
use crate::avm2::Error;
|
||||||
use crate::string::AvmString;
|
use crate::string::AvmString;
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
|
@ -245,7 +245,7 @@ impl<'gc> ClassObject<'gc> {
|
||||||
class.read().validate_class(self.superclass_object())?;
|
class.read().validate_class(self.superclass_object())?;
|
||||||
|
|
||||||
self.instance_vtable().init_vtable(
|
self.instance_vtable().init_vtable(
|
||||||
Some(self),
|
self,
|
||||||
class.read().instance_traits(),
|
class.read().instance_traits(),
|
||||||
self.instance_scope(),
|
self.instance_scope(),
|
||||||
self.superclass_object().map(|cls| cls.instance_vtable()),
|
self.superclass_object().map(|cls| cls.instance_vtable()),
|
||||||
|
@ -254,7 +254,7 @@ impl<'gc> ClassObject<'gc> {
|
||||||
|
|
||||||
// class vtable == class traits + Class instance traits
|
// class vtable == class traits + Class instance traits
|
||||||
self.class_vtable().init_vtable(
|
self.class_vtable().init_vtable(
|
||||||
Some(self),
|
self,
|
||||||
class.read().class_traits(),
|
class.read().class_traits(),
|
||||||
self.class_scope(),
|
self.class_scope(),
|
||||||
Some(self.instance_of().unwrap().instance_vtable()),
|
Some(self.instance_of().unwrap().instance_vtable()),
|
||||||
|
@ -542,15 +542,17 @@ impl<'gc> ClassObject<'gc> {
|
||||||
}
|
}
|
||||||
if let Some(Property::Method { disp_id, .. }) = property {
|
if let Some(Property::Method { disp_id, .. }) = property {
|
||||||
// todo: handle errors
|
// todo: handle errors
|
||||||
let (superclass_object, method) =
|
let ClassBoundMethod {
|
||||||
self.instance_vtable().get_full_method(disp_id).unwrap();
|
class,
|
||||||
let scope = superclass_object.unwrap().instance_scope();
|
scope,
|
||||||
|
method,
|
||||||
|
} = self.instance_vtable().get_full_method(disp_id).unwrap();
|
||||||
let callee = FunctionObject::from_method(
|
let callee = FunctionObject::from_method(
|
||||||
activation,
|
activation,
|
||||||
method.clone(),
|
method.clone(),
|
||||||
scope,
|
scope,
|
||||||
Some(reciever),
|
Some(reciever),
|
||||||
superclass_object,
|
Some(class),
|
||||||
);
|
);
|
||||||
|
|
||||||
callee.call(Some(reciever), arguments, activation)
|
callee.call(Some(reciever), arguments, activation)
|
||||||
|
@ -602,15 +604,17 @@ impl<'gc> ClassObject<'gc> {
|
||||||
}) = property
|
}) = property
|
||||||
{
|
{
|
||||||
// todo: handle errors
|
// todo: handle errors
|
||||||
let (superclass_object, method) =
|
let ClassBoundMethod {
|
||||||
self.instance_vtable().get_full_method(disp_id).unwrap();
|
class,
|
||||||
let scope = superclass_object.unwrap().class_scope();
|
scope,
|
||||||
|
method,
|
||||||
|
} = self.instance_vtable().get_full_method(disp_id).unwrap();
|
||||||
let callee = FunctionObject::from_method(
|
let callee = FunctionObject::from_method(
|
||||||
activation,
|
activation,
|
||||||
method.clone(),
|
method.clone(),
|
||||||
scope,
|
scope,
|
||||||
Some(reciever),
|
Some(reciever),
|
||||||
superclass_object,
|
Some(class),
|
||||||
);
|
);
|
||||||
|
|
||||||
callee.call(Some(reciever), &[], activation)
|
callee.call(Some(reciever), &[], activation)
|
||||||
|
@ -664,15 +668,17 @@ impl<'gc> ClassObject<'gc> {
|
||||||
}) = property
|
}) = property
|
||||||
{
|
{
|
||||||
// todo: handle errors
|
// todo: handle errors
|
||||||
let (superclass_object, method) =
|
let ClassBoundMethod {
|
||||||
self.instance_vtable().get_full_method(disp_id).unwrap();
|
class,
|
||||||
let scope = superclass_object.unwrap().class_scope();
|
scope,
|
||||||
|
method,
|
||||||
|
} = self.instance_vtable().get_full_method(disp_id).unwrap();
|
||||||
let callee = FunctionObject::from_method(
|
let callee = FunctionObject::from_method(
|
||||||
activation,
|
activation,
|
||||||
method.clone(),
|
method.clone(),
|
||||||
scope,
|
scope,
|
||||||
Some(reciever),
|
Some(reciever),
|
||||||
superclass_object,
|
Some(class),
|
||||||
);
|
);
|
||||||
|
|
||||||
callee.call(Some(reciever), &[value], activation)?;
|
callee.call(Some(reciever), &[value], activation)?;
|
||||||
|
@ -904,7 +910,7 @@ impl<'gc> TObject<'gc> for ClassObject<'gc> {
|
||||||
.validate_class(class_object.superclass_object())?;
|
.validate_class(class_object.superclass_object())?;
|
||||||
|
|
||||||
class_object.instance_vtable().init_vtable(
|
class_object.instance_vtable().init_vtable(
|
||||||
Some(class_object),
|
class_object,
|
||||||
parameterized_class.read().instance_traits(),
|
parameterized_class.read().instance_traits(),
|
||||||
class_object.instance_scope(),
|
class_object.instance_scope(),
|
||||||
class_object
|
class_object
|
||||||
|
@ -915,7 +921,7 @@ impl<'gc> TObject<'gc> for ClassObject<'gc> {
|
||||||
|
|
||||||
// class vtable == class traits + Class instance traits
|
// class vtable == class traits + Class instance traits
|
||||||
class_object.class_vtable().init_vtable(
|
class_object.class_vtable().init_vtable(
|
||||||
Some(class_object),
|
class_object,
|
||||||
parameterized_class.read().class_traits(),
|
parameterized_class.read().class_traits(),
|
||||||
class_object.class_scope(),
|
class_object.class_scope(),
|
||||||
Some(class_object.instance_of().unwrap().instance_vtable()),
|
Some(class_object.instance_of().unwrap().instance_vtable()),
|
||||||
|
|
|
@ -379,7 +379,7 @@ impl<'gc> Script<'gc> {
|
||||||
let scope = ScopeChain::new(domain);
|
let scope = ScopeChain::new(domain);
|
||||||
|
|
||||||
globals.vtable().unwrap().init_vtable(
|
globals.vtable().unwrap().init_vtable(
|
||||||
None,
|
globals.instance_of().unwrap(),
|
||||||
&self.traits()?,
|
&self.traits()?,
|
||||||
scope,
|
scope,
|
||||||
None,
|
None,
|
||||||
|
|
|
@ -19,6 +19,7 @@ pub struct VTable<'gc>(GcCell<'gc, VTableData<'gc>>);
|
||||||
#[derive(Collect, Debug, Clone)]
|
#[derive(Collect, Debug, Clone)]
|
||||||
#[collect(no_drop)]
|
#[collect(no_drop)]
|
||||||
pub struct VTableData<'gc> {
|
pub struct VTableData<'gc> {
|
||||||
|
/// should always be Some post-initialization
|
||||||
defining_class: Option<ClassObject<'gc>>,
|
defining_class: Option<ClassObject<'gc>>,
|
||||||
|
|
||||||
/// should always be Some post-initialization
|
/// should always be Some post-initialization
|
||||||
|
@ -28,10 +29,22 @@ pub struct VTableData<'gc> {
|
||||||
|
|
||||||
resolved_traits: PropertyMap<'gc, Property>,
|
resolved_traits: PropertyMap<'gc, Property>,
|
||||||
|
|
||||||
method_table: Vec<(Option<ClassObject<'gc>>, Method<'gc>)>,
|
method_table: Vec<ClassBoundMethod<'gc>>,
|
||||||
|
|
||||||
default_slots: Vec<Option<Value<'gc>>>,
|
default_slots: Vec<Option<Value<'gc>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: it might make more sense to just bind the Method to the VTable (and this its class and scope) directly
|
||||||
|
// would also be nice to somehow remove the Option-ness from `defining_class` and `scope` fields for this
|
||||||
|
// to be more intuitive and cheaper
|
||||||
|
#[derive(Collect, Debug, Clone)]
|
||||||
|
#[collect(no_drop)]
|
||||||
|
pub struct ClassBoundMethod<'gc> {
|
||||||
|
pub class: ClassObject<'gc>,
|
||||||
|
pub scope: ScopeChain<'gc>,
|
||||||
|
pub method: Method<'gc>,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'gc> VTable<'gc> {
|
impl<'gc> VTable<'gc> {
|
||||||
pub fn empty(mc: MutationContext<'gc, '_>) -> Self {
|
pub fn empty(mc: MutationContext<'gc, '_>) -> Self {
|
||||||
VTable(GcCell::allocate(
|
VTable(GcCell::allocate(
|
||||||
|
@ -73,10 +86,10 @@ impl<'gc> VTable<'gc> {
|
||||||
.method_table
|
.method_table
|
||||||
.get(disp_id as usize)
|
.get(disp_id as usize)
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(|x| x.1)
|
.map(|x| x.method)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_full_method(self, disp_id: u32) -> Option<(Option<ClassObject<'gc>>, Method<'gc>)> {
|
pub fn get_full_method(self, disp_id: u32) -> Option<ClassBoundMethod<'gc>> {
|
||||||
self.0.read().method_table.get(disp_id as usize).cloned()
|
self.0.read().method_table.get(disp_id as usize).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +105,7 @@ impl<'gc> VTable<'gc> {
|
||||||
#[allow(clippy::if_same_then_else)]
|
#[allow(clippy::if_same_then_else)]
|
||||||
pub fn init_vtable(
|
pub fn init_vtable(
|
||||||
self,
|
self,
|
||||||
defining_class: Option<ClassObject<'gc>>,
|
defining_class: ClassObject<'gc>,
|
||||||
traits: &[Trait<'gc>],
|
traits: &[Trait<'gc>],
|
||||||
scope: ScopeChain<'gc>,
|
scope: ScopeChain<'gc>,
|
||||||
superclass_vtable: Option<Self>,
|
superclass_vtable: Option<Self>,
|
||||||
|
@ -152,15 +165,13 @@ impl<'gc> VTable<'gc> {
|
||||||
let mut write = self.0.write(activation.context.gc_context);
|
let mut write = self.0.write(activation.context.gc_context);
|
||||||
let write = write.deref_mut();
|
let write = write.deref_mut();
|
||||||
|
|
||||||
write.defining_class = defining_class;
|
write.defining_class = Some(defining_class);
|
||||||
write.scope = Some(scope);
|
write.scope = Some(scope);
|
||||||
|
|
||||||
if let Some(defining_class) = defining_class {
|
|
||||||
write.protected_namespace = defining_class
|
write.protected_namespace = defining_class
|
||||||
.inner_class_definition()
|
.inner_class_definition()
|
||||||
.read()
|
.read()
|
||||||
.protected_namespace();
|
.protected_namespace();
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(superclass_vtable) = superclass_vtable {
|
if let Some(superclass_vtable) = superclass_vtable {
|
||||||
write.resolved_traits = superclass_vtable.0.read().resolved_traits.clone();
|
write.resolved_traits = superclass_vtable.0.read().resolved_traits.clone();
|
||||||
|
@ -193,7 +204,11 @@ impl<'gc> VTable<'gc> {
|
||||||
for trait_data in traits {
|
for trait_data in traits {
|
||||||
match trait_data.kind() {
|
match trait_data.kind() {
|
||||||
TraitKind::Method { method, .. } => {
|
TraitKind::Method { method, .. } => {
|
||||||
let entry = (defining_class, method.clone());
|
let entry = ClassBoundMethod {
|
||||||
|
class: defining_class,
|
||||||
|
scope,
|
||||||
|
method: method.clone(),
|
||||||
|
};
|
||||||
match resolved_traits.get(trait_data.name()) {
|
match resolved_traits.get(trait_data.name()) {
|
||||||
Some(Property::Method { disp_id, .. }) => {
|
Some(Property::Method { disp_id, .. }) => {
|
||||||
let disp_id = *disp_id as usize;
|
let disp_id = *disp_id as usize;
|
||||||
|
@ -210,7 +225,11 @@ impl<'gc> VTable<'gc> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TraitKind::Getter { method, .. } => {
|
TraitKind::Getter { method, .. } => {
|
||||||
let entry = (defining_class, method.clone());
|
let entry = ClassBoundMethod {
|
||||||
|
class: defining_class,
|
||||||
|
scope,
|
||||||
|
method: method.clone(),
|
||||||
|
};
|
||||||
match resolved_traits.get_mut(trait_data.name()) {
|
match resolved_traits.get_mut(trait_data.name()) {
|
||||||
Some(Property::Virtual {
|
Some(Property::Virtual {
|
||||||
get: Some(disp_id), ..
|
get: Some(disp_id), ..
|
||||||
|
@ -232,7 +251,11 @@ impl<'gc> VTable<'gc> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TraitKind::Setter { method, .. } => {
|
TraitKind::Setter { method, .. } => {
|
||||||
let entry = (defining_class, method.clone());
|
let entry = ClassBoundMethod {
|
||||||
|
class: defining_class,
|
||||||
|
scope,
|
||||||
|
method: method.clone(),
|
||||||
|
};
|
||||||
match resolved_traits.get_mut(trait_data.name()) {
|
match resolved_traits.get_mut(trait_data.name()) {
|
||||||
Some(Property::Virtual {
|
Some(Property::Virtual {
|
||||||
set: Some(disp_id), ..
|
set: Some(disp_id), ..
|
||||||
|
@ -310,14 +333,18 @@ impl<'gc> VTable<'gc> {
|
||||||
receiver: Object<'gc>,
|
receiver: Object<'gc>,
|
||||||
disp_id: u32,
|
disp_id: u32,
|
||||||
) -> Option<FunctionObject<'gc>> {
|
) -> Option<FunctionObject<'gc>> {
|
||||||
if let Some((superclass, method)) = self.get_full_method(disp_id) {
|
if let Some(ClassBoundMethod {
|
||||||
let scope = self.0.read().scope.unwrap();
|
class,
|
||||||
|
scope,
|
||||||
|
method,
|
||||||
|
}) = self.get_full_method(disp_id)
|
||||||
|
{
|
||||||
Some(FunctionObject::from_method(
|
Some(FunctionObject::from_method(
|
||||||
activation,
|
activation,
|
||||||
method,
|
method,
|
||||||
scope,
|
scope,
|
||||||
Some(receiver),
|
Some(receiver),
|
||||||
superclass,
|
Some(class),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
Loading…
Reference in New Issue