avm2: Move the class association from prototypes to instances.
This also incurred a large number of ancillary changes, as it turns out nearly every native object is currently pulling a prototype and sticking it into an object. Right now, I have it instead pulling the constructor out of the prototype, but a future PR will also remove `system_prototypes` as well. Other ancillary changes include: * `Domain` now supports partial initialization to avoid an order-of-events issue. Accessing domain memory on a partially-initialized `Domain` will panic. * `Domain` construction requires a full `activation` now, except for `global_scope` which needs to be initialized later with valid domain memory before user code runs. * Pretty much every native object constructor now takes a proto/constr pair * Trait lookup was rewritten to handle this. It's still buggy - seven tests don't work * `TObject.construct` now actually does the full object construction dance. This allows `ClassObject` to implement the ES4 object construction pathway directly while `FunctionObject` maintains ES3 compatibility. This is a tentative commit; there are still seven failing tests that I need to fix.
This commit is contained in:
parent
f26f193508
commit
323da9ded3
|
@ -144,8 +144,20 @@ impl<'gc> Avm2<'gc> {
|
|||
target: Object<'gc>,
|
||||
) -> Result<bool, Error> {
|
||||
use crate::avm2::events::dispatch_event;
|
||||
let event_proto = context.avm2.system_prototypes.as_ref().unwrap().event;
|
||||
let event_object = EventObject::from_event(context.gc_context, Some(event_proto), event);
|
||||
|
||||
let mut activation = Activation::from_nothing(context.reborrow());
|
||||
let mut event_proto = activation.avm2().prototypes().event;
|
||||
let event_constr = event_proto
|
||||
.get_property(
|
||||
event_proto,
|
||||
&QName::new(Namespace::public(), "constructor"),
|
||||
&mut activation,
|
||||
)?
|
||||
.coerce_to_object(&mut activation)?;
|
||||
drop(activation);
|
||||
|
||||
let event_object =
|
||||
EventObject::from_event(context.gc_context, event_constr, Some(event_proto), event);
|
||||
let mut activation = Activation::from_nothing(context.reborrow());
|
||||
|
||||
dispatch_event(&mut activation, target, event_object)
|
||||
|
@ -255,7 +267,7 @@ impl<'gc> Avm2<'gc> {
|
|||
let tunit = TranslationUnit::from_abc(abc_file.clone(), domain, context.gc_context);
|
||||
|
||||
for i in (0..abc_file.scripts.len()).rev() {
|
||||
let mut script = tunit.load_script(i as u32, context.avm2, context.gc_context)?;
|
||||
let mut script = tunit.load_script(i as u32, context)?;
|
||||
|
||||
if !lazy_init {
|
||||
script.globals(context)?;
|
||||
|
|
|
@ -105,20 +105,20 @@ pub struct Activation<'a, 'gc: 'a, 'gc_context: 'a> {
|
|||
/// A `scope` of `None` indicates that the scope stack is empty.
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
|
||||
/// The base prototype of `this`.
|
||||
/// The base class constructor that yielded the currently executing method.
|
||||
///
|
||||
/// This will not be available if this is not a method call.
|
||||
base_proto: Option<Object<'gc>>,
|
||||
base_constr: Option<Object<'gc>>,
|
||||
|
||||
/// The proto of all objects returned from `newactivation`.
|
||||
/// The constructor 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>>,
|
||||
/// and we will not allocate a constructor for one.
|
||||
activation_constr: Option<Object<'gc>>,
|
||||
|
||||
pub context: UpdateContext<'a, 'gc, 'gc_context>,
|
||||
}
|
||||
|
@ -144,8 +144,8 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
return_value: None,
|
||||
local_scope: ScriptObject::bare_object(context.gc_context),
|
||||
scope: None,
|
||||
base_proto: None,
|
||||
activation_proto: None,
|
||||
base_constr: None,
|
||||
activation_constr: None,
|
||||
context,
|
||||
}
|
||||
}
|
||||
|
@ -185,8 +185,8 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
return_value: None,
|
||||
local_scope: ScriptObject::bare_object(context.gc_context),
|
||||
scope,
|
||||
base_proto: None,
|
||||
activation_proto: None,
|
||||
base_constr: None,
|
||||
activation_constr: None,
|
||||
context,
|
||||
})
|
||||
}
|
||||
|
@ -194,12 +194,12 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
/// Construct an activation for the execution of a particular bytecode
|
||||
/// method.
|
||||
pub fn from_method(
|
||||
context: UpdateContext<'a, 'gc, 'gc_context>,
|
||||
mut context: UpdateContext<'a, 'gc, 'gc_context>,
|
||||
method: Gc<'gc, BytecodeMethod<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
this: Option<Object<'gc>>,
|
||||
arguments: &[Value<'gc>],
|
||||
base_proto: Option<Object<'gc>>,
|
||||
base_constr: Option<Object<'gc>>,
|
||||
callee: Object<'gc>,
|
||||
) -> Result<Self, Error> {
|
||||
let body: Result<_, Error> = method
|
||||
|
@ -227,22 +227,18 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
}
|
||||
}
|
||||
|
||||
let activation_proto = if method.method().needs_activation {
|
||||
let activation_constr = 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,
|
||||
)?;
|
||||
let activation_class =
|
||||
Class::from_method_body(&mut context, translation_unit, abc_method, body)?;
|
||||
let mut dummy_activation = Activation::from_nothing(context.reborrow());
|
||||
let (constr, _cinit) =
|
||||
ClassObject::from_class(&mut dummy_activation, activation_class, None, scope)?;
|
||||
|
||||
Some(ScriptObject::bare_prototype(
|
||||
context.gc_context,
|
||||
activation_class,
|
||||
scope,
|
||||
))
|
||||
drop(dummy_activation);
|
||||
|
||||
Some(constr)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
@ -256,8 +252,8 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
return_value: None,
|
||||
local_scope: ScriptObject::bare_object(context.gc_context),
|
||||
scope,
|
||||
base_proto,
|
||||
activation_proto,
|
||||
base_constr,
|
||||
activation_constr,
|
||||
context,
|
||||
};
|
||||
|
||||
|
@ -274,15 +270,18 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
unreachable!();
|
||||
};
|
||||
|
||||
let mut array_proto = activation.avm2().prototypes().array;
|
||||
let array_constr = array_proto
|
||||
.get_property(
|
||||
array_proto,
|
||||
&QName::new(Namespace::public(), "constructor"),
|
||||
&mut activation,
|
||||
)?
|
||||
.coerce_to_object(&mut activation)?;
|
||||
let mut args_object = ArrayObject::from_array(
|
||||
args_array,
|
||||
activation
|
||||
.context
|
||||
.avm2
|
||||
.system_prototypes
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.array,
|
||||
array_constr,
|
||||
array_proto,
|
||||
activation.context.gc_context,
|
||||
);
|
||||
|
||||
|
@ -318,7 +317,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
context: UpdateContext<'a, 'gc, 'gc_context>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
this: Option<Object<'gc>>,
|
||||
base_proto: Option<Object<'gc>>,
|
||||
base_constr: Option<Object<'gc>>,
|
||||
) -> Result<Self, Error> {
|
||||
let local_registers = GcCell::allocate(context.gc_context, RegisterSet::new(0));
|
||||
|
||||
|
@ -331,8 +330,8 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
return_value: None,
|
||||
local_scope: ScriptObject::bare_object(context.gc_context),
|
||||
scope,
|
||||
base_proto,
|
||||
activation_proto: None,
|
||||
base_constr,
|
||||
activation_constr: None,
|
||||
context,
|
||||
})
|
||||
}
|
||||
|
@ -369,20 +368,17 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
receiver: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
let name = QName::new(Namespace::public(), "constructor");
|
||||
let base_proto: Result<Object<'gc>, Error> =
|
||||
self.base_proto().and_then(|p| p.proto()).ok_or_else(|| {
|
||||
let base_constr: Result<Object<'gc>, Error> = self
|
||||
.base_constr()
|
||||
.and_then(|c| c.base_class_constr())
|
||||
.ok_or_else(|| {
|
||||
"Attempted to call super constructor without a superclass."
|
||||
.to_string()
|
||||
.into()
|
||||
});
|
||||
let mut base_proto = base_proto?;
|
||||
let base_constr = base_constr?;
|
||||
|
||||
let function = base_proto
|
||||
.get_property(receiver, &name, self)?
|
||||
.coerce_to_object(self)?;
|
||||
|
||||
function.call(Some(receiver), args, self, Some(base_proto))
|
||||
base_constr.call(Some(receiver), args, self, Some(base_constr))
|
||||
}
|
||||
|
||||
/// Attempts to lock the activation frame for execution.
|
||||
|
@ -452,8 +448,8 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
|
||||
/// Get the base prototype of the object that the currently executing
|
||||
/// method was retrieved from, if one exists.
|
||||
pub fn base_proto(&self) -> Option<Object<'gc>> {
|
||||
self.base_proto
|
||||
pub fn base_constr(&self) -> Option<Object<'gc>> {
|
||||
self.base_constr
|
||||
}
|
||||
|
||||
/// Retrieve a int from the current constant pool.
|
||||
|
@ -542,7 +538,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
) -> Result<GcCell<'gc, Class<'gc>>, Error> {
|
||||
method
|
||||
.translation_unit()
|
||||
.load_class(index.0, self.context.avm2, self.context.gc_context)
|
||||
.load_class(index.0, &mut self.context)
|
||||
}
|
||||
|
||||
pub fn run_actions(
|
||||
|
@ -817,10 +813,19 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
value: Index<AbcNamespace>,
|
||||
) -> Result<FrameControl<'gc>, Error> {
|
||||
let ns = self.pool_namespace(method, value, self.context.gc_context)?;
|
||||
let mut ns_proto = self.context.avm2.prototypes().namespace;
|
||||
let ns_constr = ns_proto
|
||||
.get_property(
|
||||
ns_proto,
|
||||
&QName::new(Namespace::public(), "constructor"),
|
||||
self,
|
||||
)?
|
||||
.coerce_to_object(self)?;
|
||||
|
||||
self.context.avm2.push(NamespaceObject::from_namespace(
|
||||
ns,
|
||||
self.context.avm2.prototypes().namespace,
|
||||
ns_constr,
|
||||
ns_proto,
|
||||
self.context.gc_context,
|
||||
)?);
|
||||
Ok(FrameControl::Continue)
|
||||
|
@ -952,11 +957,15 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
.resolve_multiname(&multiname)?
|
||||
.ok_or_else(|| format!("Could not find method {:?}", multiname.local_name()).into());
|
||||
let name = name?;
|
||||
let base_proto = receiver.get_base_proto(&name)?;
|
||||
let base_constr = if let Some(c) = receiver.as_constr() {
|
||||
c.find_base_constr_for_trait(&name)?
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let function = receiver
|
||||
.get_property(receiver, &name, self)?
|
||||
.coerce_to_object(self)?;
|
||||
let value = function.call(Some(receiver), &args, self, base_proto)?;
|
||||
let value = function.call(Some(receiver), &args, self, base_constr)?;
|
||||
|
||||
self.context.avm2.push(value);
|
||||
|
||||
|
@ -998,12 +1007,16 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
.resolve_multiname(&multiname)?
|
||||
.ok_or_else(|| format!("Could not find method {:?}", multiname.local_name()).into());
|
||||
let name = name?;
|
||||
let base_proto = receiver.get_base_proto(&name)?;
|
||||
let base_constr = if let Some(c) = receiver.as_constr() {
|
||||
c.find_base_constr_for_trait(&name)?
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let function = receiver
|
||||
.get_property(receiver, &name, self)?
|
||||
.coerce_to_object(self)?;
|
||||
|
||||
function.call(Some(receiver), &args, self, base_proto)?;
|
||||
function.call(Some(receiver), &args, self, base_constr)?;
|
||||
|
||||
Ok(FrameControl::Continue)
|
||||
}
|
||||
|
@ -1025,7 +1038,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
self.context.avm2.prototypes().function,
|
||||
None,
|
||||
);
|
||||
let value = function.call(Some(receiver), &args, self, receiver.proto())?;
|
||||
let value = function.call(Some(receiver), &args, self, receiver.as_constr())?;
|
||||
|
||||
self.context.avm2.push(value);
|
||||
|
||||
|
@ -1041,23 +1054,23 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
let args = self.context.avm2.pop_args(arg_count);
|
||||
let multiname = self.pool_multiname(method, index)?;
|
||||
let receiver = self.context.avm2.pop().coerce_to_object(self)?;
|
||||
|
||||
let name: Result<QName, Error> = receiver
|
||||
.resolve_multiname(&multiname)?
|
||||
.ok_or_else(|| format!("Could not find method {:?}", multiname.local_name()).into());
|
||||
let base_proto: Result<Object<'gc>, Error> =
|
||||
self.base_proto().and_then(|bp| bp.proto()).ok_or_else(|| {
|
||||
let name = name?;
|
||||
|
||||
let base_constr: Result<Object<'gc>, Error> = self
|
||||
.base_constr()
|
||||
.and_then(|bc| bc.base_class_constr())
|
||||
.ok_or_else(|| {
|
||||
"Attempted to call super method without a superclass."
|
||||
.to_string()
|
||||
.into()
|
||||
});
|
||||
let base_proto = base_proto?;
|
||||
let mut base = base_proto.construct(self, &[])?; //TODO: very hacky workaround
|
||||
let base_constr = base_constr?;
|
||||
|
||||
let function = base
|
||||
.get_property(receiver, &name?, self)?
|
||||
.coerce_to_object(self)?;
|
||||
|
||||
let value = function.call(Some(receiver), &args, self, Some(base_proto))?;
|
||||
let value = base_constr.call_instance_method(&name, Some(receiver), &args, self)?;
|
||||
|
||||
self.context.avm2.push(value);
|
||||
|
||||
|
@ -1073,23 +1086,23 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
let args = self.context.avm2.pop_args(arg_count);
|
||||
let multiname = self.pool_multiname(method, index)?;
|
||||
let receiver = self.context.avm2.pop().coerce_to_object(self)?;
|
||||
|
||||
let name: Result<QName, Error> = receiver
|
||||
.resolve_multiname(&multiname)?
|
||||
.ok_or_else(|| format!("Could not find method {:?}", multiname.local_name()).into());
|
||||
let base_proto: Result<Object<'gc>, Error> =
|
||||
self.base_proto().and_then(|bp| bp.proto()).ok_or_else(|| {
|
||||
let name = name?;
|
||||
|
||||
let base_constr: Result<Object<'gc>, Error> = self
|
||||
.base_constr()
|
||||
.and_then(|bc| bc.base_class_constr())
|
||||
.ok_or_else(|| {
|
||||
"Attempted to call super method without a superclass."
|
||||
.to_string()
|
||||
.into()
|
||||
});
|
||||
let base_proto = base_proto?;
|
||||
let mut base = base_proto.construct(self, &[])?; //TODO: very hacky workaround
|
||||
let base_constr = base_constr?;
|
||||
|
||||
let function = base
|
||||
.get_property(receiver, &name?, self)?
|
||||
.coerce_to_object(self)?;
|
||||
|
||||
function.call(Some(receiver), &args, self, Some(base_proto))?;
|
||||
base_constr.call_instance_method(&name, Some(receiver), &args, self)?;
|
||||
|
||||
Ok(FrameControl::Continue)
|
||||
}
|
||||
|
@ -1120,7 +1133,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
// dynamic properties not yet set
|
||||
if name.is_err()
|
||||
&& !object
|
||||
.as_proto_class()
|
||||
.as_class()
|
||||
.map(|c| c.read().is_sealed())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
|
@ -1198,7 +1211,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
// Unknown properties on a dynamic class delete successfully.
|
||||
self.context.avm2.push(
|
||||
!object
|
||||
.as_proto_class()
|
||||
.as_class()
|
||||
.map(|c| c.read().is_sealed())
|
||||
.unwrap_or(false),
|
||||
)
|
||||
|
@ -1214,22 +1227,23 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
) -> Result<FrameControl<'gc>, Error> {
|
||||
let multiname = self.pool_multiname(method, index)?;
|
||||
let object = self.context.avm2.pop().coerce_to_object(self)?;
|
||||
let base_proto: Result<Object<'gc>, Error> = self
|
||||
.base_proto()
|
||||
.and_then(|p| p.proto())
|
||||
.ok_or_else(|| "Attempted to get property on non-existent super object".into());
|
||||
let base_proto = base_proto?;
|
||||
let mut base = base_proto.construct(self, &[])?; //TODO: very hacky workaround
|
||||
|
||||
let name: Result<QName, Error> = base.resolve_multiname(&multiname)?.ok_or_else(|| {
|
||||
format!(
|
||||
"Could not resolve {:?} as super property",
|
||||
multiname.local_name()
|
||||
)
|
||||
let name: Result<QName, Error> = object
|
||||
.resolve_multiname(&multiname)?
|
||||
.ok_or_else(|| format!("Could not find method {:?}", multiname.local_name()).into());
|
||||
let name = name?;
|
||||
|
||||
let base_constr: Result<Object<'gc>, Error> = self
|
||||
.base_constr()
|
||||
.and_then(|bc| bc.base_class_constr())
|
||||
.ok_or_else(|| {
|
||||
"Attempted to call super method without a superclass."
|
||||
.to_string()
|
||||
.into()
|
||||
});
|
||||
let base_constr = base_constr?;
|
||||
|
||||
let value = base.get_property(object, &name?, self)?;
|
||||
let value = base_constr.call_instance_getter(&name, Some(object), self)?;
|
||||
|
||||
self.context.avm2.push(value);
|
||||
|
||||
|
@ -1244,22 +1258,23 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
let value = self.context.avm2.pop();
|
||||
let multiname = self.pool_multiname(method, index)?;
|
||||
let object = self.context.avm2.pop().coerce_to_object(self)?;
|
||||
let base_proto: Result<Object<'gc>, Error> = self
|
||||
.base_proto()
|
||||
.and_then(|p| p.proto())
|
||||
.ok_or_else(|| "Attempted to get property on non-existent super object".into());
|
||||
let base_proto = base_proto?;
|
||||
let mut base = base_proto.construct(self, &[])?; //TODO: very hacky workaround
|
||||
|
||||
let name: Result<QName, Error> = base.resolve_multiname(&multiname)?.ok_or_else(|| {
|
||||
format!(
|
||||
"Could not resolve {:?} as super property",
|
||||
multiname.local_name()
|
||||
)
|
||||
let name: Result<QName, Error> = object
|
||||
.resolve_multiname(&multiname)?
|
||||
.ok_or_else(|| format!("Could not find method {:?}", multiname.local_name()).into());
|
||||
let name = name?;
|
||||
|
||||
let base_constr: Result<Object<'gc>, Error> = self
|
||||
.base_constr()
|
||||
.and_then(|bc| bc.base_class_constr())
|
||||
.ok_or_else(|| {
|
||||
"Attempted to call super method without a superclass."
|
||||
.to_string()
|
||||
.into()
|
||||
});
|
||||
let base_constr = base_constr?;
|
||||
|
||||
base.set_property(object, &name?, value, self)?;
|
||||
base_constr.call_instance_setter(&name, value, Some(object), self)?;
|
||||
|
||||
Ok(FrameControl::Continue)
|
||||
}
|
||||
|
@ -1433,14 +1448,9 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
|
||||
fn op_construct(&mut self, arg_count: u32) -> Result<FrameControl<'gc>, Error> {
|
||||
let args = self.context.avm2.pop_args(arg_count);
|
||||
let mut ctor = self.context.avm2.pop().coerce_to_object(self)?;
|
||||
let ctor = self.context.avm2.pop().coerce_to_object(self)?;
|
||||
|
||||
let proto = ctor
|
||||
.get_property(ctor, &QName::new(Namespace::public(), "prototype"), self)?
|
||||
.coerce_to_object(self)?;
|
||||
|
||||
let object = proto.construct(self, &args)?;
|
||||
ctor.call(Some(object), &args, self, object.proto())?;
|
||||
let object = ctor.construct(self, &args)?;
|
||||
|
||||
self.context.avm2.push(object);
|
||||
|
||||
|
@ -1461,15 +1471,12 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
source.resolve_multiname(&multiname)?.ok_or_else(|| {
|
||||
format!("Could not resolve property {:?}", multiname.local_name()).into()
|
||||
});
|
||||
let mut ctor = source
|
||||
.get_property(source, &ctor_name?, self)?
|
||||
.coerce_to_object(self)?;
|
||||
let proto = ctor
|
||||
.get_property(ctor, &QName::new(Namespace::public(), "prototype"), self)?
|
||||
let ctor_name = ctor_name?;
|
||||
let ctor = source
|
||||
.get_property(source, &ctor_name, self)?
|
||||
.coerce_to_object(self)?;
|
||||
|
||||
let object = proto.construct(self, &args)?;
|
||||
ctor.call(Some(object), &args, self, Some(proto))?;
|
||||
let object = ctor.construct(self, &args)?;
|
||||
|
||||
self.context.avm2.push(object);
|
||||
|
||||
|
@ -1486,16 +1493,13 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
}
|
||||
|
||||
fn op_new_activation(&mut self) -> Result<FrameControl<'gc>, Error> {
|
||||
if let Some(activation_proto) = self.activation_proto {
|
||||
self.context.avm2.push(ScriptObject::object(
|
||||
self.context.gc_context,
|
||||
activation_proto,
|
||||
));
|
||||
let instance = if let Some(activation_constr) = self.activation_constr {
|
||||
activation_constr.construct(self, &[])?
|
||||
} else {
|
||||
self.context
|
||||
.avm2
|
||||
.push(ScriptObject::bare_object(self.context.gc_context));
|
||||
}
|
||||
ScriptObject::bare_object(self.context.gc_context)
|
||||
};
|
||||
|
||||
self.context.avm2.push(instance);
|
||||
|
||||
Ok(FrameControl::Continue)
|
||||
}
|
||||
|
@ -1583,11 +1587,17 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
fn op_new_array(&mut self, num_args: u32) -> Result<FrameControl<'gc>, Error> {
|
||||
let args = self.context.avm2.pop_args(num_args);
|
||||
let array = ArrayStorage::from_args(&args[..]);
|
||||
let array_obj = ArrayObject::from_array(
|
||||
array,
|
||||
self.context.avm2.system_prototypes.clone().unwrap().array,
|
||||
self.context.gc_context,
|
||||
);
|
||||
let mut array_proto = self.context.avm2.prototypes().array;
|
||||
let array_constr = array_proto
|
||||
.get_property(
|
||||
array_proto,
|
||||
&QName::new(Namespace::public(), "constructor"),
|
||||
self,
|
||||
)?
|
||||
.coerce_to_object(self)?;
|
||||
|
||||
let array_obj =
|
||||
ArrayObject::from_array(array, array_constr, array_proto, self.context.gc_context);
|
||||
|
||||
self.context.avm2.push(array_obj);
|
||||
|
||||
|
|
|
@ -4,11 +4,11 @@ use crate::avm2::activation::Activation;
|
|||
use crate::avm2::method::{Method, NativeMethod};
|
||||
use crate::avm2::names::{Multiname, Namespace, QName};
|
||||
use crate::avm2::object::{Object, ScriptObject, TObject};
|
||||
use crate::avm2::scope::Scope;
|
||||
use crate::avm2::script::TranslationUnit;
|
||||
use crate::avm2::string::AvmString;
|
||||
use crate::avm2::traits::{Trait, TraitKind};
|
||||
use crate::avm2::{Avm2, Error};
|
||||
use crate::avm2::Error;
|
||||
use crate::context::UpdateContext;
|
||||
use bitflags::bitflags;
|
||||
use gc_arena::{Collect, GcCell, MutationContext};
|
||||
use std::fmt;
|
||||
|
@ -42,17 +42,14 @@ bitflags! {
|
|||
/// Parameters for the deriver are:
|
||||
///
|
||||
/// * `constr` - The class constructor that was called (or will be called) to
|
||||
/// construct this object. This may either be a base class (if we're creating
|
||||
/// a derived class prototype object) or the current class (if we're creating
|
||||
/// an instance of the new class)
|
||||
/// construct this object. This must be the current class (using a base class
|
||||
/// will cause the wrong class to be read for traits).
|
||||
/// * `proto` - The prototype attached to the class constructor.
|
||||
/// * `activation` - This is the current AVM2 activation.
|
||||
/// * `class` - The class we are attempting to derive.
|
||||
/// * `scope` - The scope the class was declared in.
|
||||
pub type DeriverFn = for<'gc> fn(
|
||||
Object<'gc>,
|
||||
Object<'gc>,
|
||||
&mut Activation<'_, 'gc, '_>,
|
||||
GcCell<'gc, Class<'gc>>,
|
||||
Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error>;
|
||||
|
||||
#[derive(Clone, Collect)]
|
||||
|
@ -73,9 +70,8 @@ impl fmt::Debug for Deriver {
|
|||
/// not exist, we default to `ScriptObject`.
|
||||
pub fn implicit_deriver<'gc>(
|
||||
mut constr: Object<'gc>,
|
||||
proto: Object<'gc>,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let mut base_constr = Some(constr);
|
||||
let mut base_class = constr.as_class();
|
||||
|
@ -94,7 +90,7 @@ pub fn implicit_deriver<'gc>(
|
|||
}
|
||||
|
||||
if let Some(base_deriver) = instance_deriver {
|
||||
base_deriver(constr, activation, class, scope)
|
||||
base_deriver(constr, proto, activation)
|
||||
} else {
|
||||
let base_proto = constr
|
||||
.get_property(
|
||||
|
@ -104,11 +100,10 @@ pub fn implicit_deriver<'gc>(
|
|||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
Ok(ScriptObject::prototype(
|
||||
Ok(ScriptObject::instance(
|
||||
activation.context.gc_context,
|
||||
constr,
|
||||
base_proto,
|
||||
class,
|
||||
scope,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -349,8 +344,7 @@ impl<'gc> Class<'gc> {
|
|||
&mut self,
|
||||
unit: TranslationUnit<'gc>,
|
||||
class_index: u32,
|
||||
avm2: &mut Avm2<'gc>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
uc: &mut UpdateContext<'_, 'gc, '_>,
|
||||
) -> Result<(), Error> {
|
||||
if self.traits_loaded {
|
||||
return Ok(());
|
||||
|
@ -373,38 +367,32 @@ impl<'gc> Class<'gc> {
|
|||
|
||||
for abc_trait in abc_instance.traits.iter() {
|
||||
self.instance_traits
|
||||
.push(Trait::from_abc_trait(unit, abc_trait, avm2, mc)?);
|
||||
.push(Trait::from_abc_trait(unit, abc_trait, uc)?);
|
||||
}
|
||||
|
||||
for abc_trait in abc_class.traits.iter() {
|
||||
self.class_traits
|
||||
.push(Trait::from_abc_trait(unit, abc_trait, avm2, mc)?);
|
||||
.push(Trait::from_abc_trait(unit, abc_trait, uc)?);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn from_method_body(
|
||||
avm2: &mut Avm2<'gc>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
uc: &mut UpdateContext<'_, '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 name = translation_unit.pool_string(method.name.as_u30(), uc.gc_context)?;
|
||||
let mut traits = Vec::with_capacity(body.traits.len());
|
||||
for trait_entry in &body.traits {
|
||||
traits.push(Trait::from_abc_trait(
|
||||
translation_unit,
|
||||
trait_entry,
|
||||
avm2,
|
||||
mc,
|
||||
)?);
|
||||
|
||||
for trait_entry in body.traits.iter() {
|
||||
traits.push(Trait::from_abc_trait(translation_unit, trait_entry, uc)?);
|
||||
}
|
||||
|
||||
Ok(GcCell::allocate(
|
||||
mc,
|
||||
uc.gc_context,
|
||||
Self {
|
||||
name: QName::dynamic_name(name),
|
||||
super_class: None,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! Application Domains
|
||||
|
||||
use crate::avm2::activation::Activation;
|
||||
use crate::avm2::names::{Multiname, QName};
|
||||
use crate::avm2::names::{Multiname, Namespace, QName};
|
||||
use crate::avm2::object::{ByteArrayObject, TObject};
|
||||
use crate::avm2::script::Script;
|
||||
use crate::avm2::value::Value;
|
||||
|
@ -25,7 +25,12 @@ struct DomainData<'gc> {
|
|||
parent: Option<Domain<'gc>>,
|
||||
|
||||
/// The bytearray used for storing domain memory
|
||||
pub domain_memory: ByteArrayObject<'gc>,
|
||||
///
|
||||
/// Note: While this property is optional, it is not recommended to set it
|
||||
/// to `None`. It is only optional to avoid an order-of-events problem in
|
||||
/// player globals setup (we need a global domain to put globals into, but
|
||||
/// that domain needs the bytearray global)
|
||||
pub domain_memory: Option<ByteArrayObject<'gc>>,
|
||||
}
|
||||
|
||||
impl<'gc> Domain<'gc> {
|
||||
|
@ -33,33 +38,41 @@ impl<'gc> Domain<'gc> {
|
|||
///
|
||||
/// This is intended exclusively for creating the player globals domain,
|
||||
/// hence the name.
|
||||
///
|
||||
/// Note: the global domain will be created without valid domain memory.
|
||||
/// You must initialize domain memory later on after the ByteArray class is
|
||||
/// instantiated but before user code runs.
|
||||
pub fn global_domain(mc: MutationContext<'gc, '_>) -> Domain<'gc> {
|
||||
let domain_memory = ByteArrayObject::new(mc, None);
|
||||
domain_memory.as_bytearray_mut(mc).unwrap().set_length(1024);
|
||||
|
||||
Self(GcCell::allocate(
|
||||
mc,
|
||||
DomainData {
|
||||
defs: HashMap::new(),
|
||||
parent: None,
|
||||
domain_memory,
|
||||
domain_memory: None,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
/// Create a new domain with a given parent.
|
||||
pub fn movie_domain(mc: MutationContext<'gc, '_>, parent: Domain<'gc>) -> Domain<'gc> {
|
||||
let domain_memory = ByteArrayObject::new(mc, None);
|
||||
domain_memory.as_bytearray_mut(mc).unwrap().set_length(1024);
|
||||
|
||||
Self(GcCell::allocate(
|
||||
mc,
|
||||
///
|
||||
/// This function must not be called before the player globals have been
|
||||
/// fully allocated.
|
||||
pub fn movie_domain(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
parent: Domain<'gc>,
|
||||
) -> Domain<'gc> {
|
||||
let this = Self(GcCell::allocate(
|
||||
activation.context.gc_context,
|
||||
DomainData {
|
||||
defs: HashMap::new(),
|
||||
parent: Some(parent),
|
||||
domain_memory,
|
||||
domain_memory: None,
|
||||
},
|
||||
))
|
||||
));
|
||||
|
||||
this.init_default_domain_memory(activation).unwrap();
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
/// Get the parent of this domain
|
||||
|
@ -159,7 +172,10 @@ impl<'gc> Domain<'gc> {
|
|||
}
|
||||
|
||||
pub fn domain_memory(&self) -> ByteArrayObject<'gc> {
|
||||
self.0.read().domain_memory
|
||||
self.0
|
||||
.read()
|
||||
.domain_memory
|
||||
.expect("Domain must have valid memory at all times")
|
||||
}
|
||||
|
||||
pub fn set_domain_memory(
|
||||
|
@ -167,6 +183,41 @@ impl<'gc> Domain<'gc> {
|
|||
mc: MutationContext<'gc, '_>,
|
||||
domain_memory: ByteArrayObject<'gc>,
|
||||
) {
|
||||
self.0.write(mc).domain_memory = domain_memory
|
||||
self.0.write(mc).domain_memory = Some(domain_memory)
|
||||
}
|
||||
|
||||
/// Allocate the default domain memory for this domain, if it does not
|
||||
/// already exist.
|
||||
///
|
||||
/// This function is only necessary to be called for domains created via
|
||||
/// `global_domain`. It will do nothing on already fully-initialized
|
||||
/// domains.
|
||||
pub fn init_default_domain_memory(
|
||||
self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
) -> Result<(), Error> {
|
||||
let mut bytearray_proto = activation.avm2().prototypes().bytearray;
|
||||
let bytearray_constr = bytearray_proto
|
||||
.get_property(
|
||||
bytearray_proto,
|
||||
&QName::new(Namespace::public(), "constructor"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
let domain_memory = ByteArrayObject::new(
|
||||
activation.context.gc_context,
|
||||
bytearray_constr,
|
||||
Some(bytearray_proto),
|
||||
);
|
||||
domain_memory
|
||||
.as_bytearray_mut(activation.context.gc_context)
|
||||
.unwrap()
|
||||
.set_length(1024);
|
||||
|
||||
let mut write = self.0.write(activation.context.gc_context);
|
||||
write.domain_memory.get_or_insert(domain_memory);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ impl<'gc> Executable<'gc> {
|
|||
unbound_reciever: Option<Object<'gc>>,
|
||||
arguments: &[Value<'gc>],
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
base_proto: Option<Object<'gc>>,
|
||||
base_constr: Option<Object<'gc>>,
|
||||
callee: Object<'gc>,
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
match self {
|
||||
|
@ -93,7 +93,7 @@ impl<'gc> Executable<'gc> {
|
|||
activation.context.reborrow(),
|
||||
scope,
|
||||
receiver,
|
||||
base_proto,
|
||||
base_constr,
|
||||
)?;
|
||||
|
||||
nf(&mut activation, receiver, arguments)
|
||||
|
@ -106,7 +106,7 @@ impl<'gc> Executable<'gc> {
|
|||
bm.scope,
|
||||
receiver,
|
||||
arguments,
|
||||
base_proto,
|
||||
base_constr,
|
||||
callee,
|
||||
)?;
|
||||
|
||||
|
|
|
@ -192,10 +192,12 @@ fn function<'gc>(
|
|||
fn dynamic_class<'gc>(
|
||||
mc: MutationContext<'gc, '_>,
|
||||
constr: Object<'gc>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
mut domain: Domain<'gc>,
|
||||
script: Script<'gc>,
|
||||
) -> Result<(), Error> {
|
||||
let class = constr
|
||||
.as_class()
|
||||
.ok_or("Attempted to create builtin dynamic class without class on it's constructor!")?;
|
||||
let name = class.read().name().clone();
|
||||
|
||||
script
|
||||
|
@ -288,7 +290,7 @@ pub fn load_player_globals<'gc>(
|
|||
domain: Domain<'gc>,
|
||||
) -> Result<(), Error> {
|
||||
let mc = activation.context.gc_context;
|
||||
let gs = DomainObject::from_domain(mc, None, domain);
|
||||
let gs = DomainObject::from_early_domain(mc, domain);
|
||||
let script = Script::empty_script(mc, gs);
|
||||
|
||||
// public / root package
|
||||
|
@ -296,18 +298,18 @@ pub fn load_player_globals<'gc>(
|
|||
// We have to do this particular dance so that we have Object methods whose
|
||||
// functions have call/apply in their prototypes, and that Function is also
|
||||
// a subclass of Object.
|
||||
let (object_proto, object_class) = object::create_proto(activation, gs);
|
||||
let (fn_proto, fn_class) = function::create_proto(activation, gs, object_proto);
|
||||
let object_proto = object::create_proto(activation);
|
||||
let fn_proto = function::create_proto(activation, object_proto);
|
||||
|
||||
let object_constr = object::fill_proto(activation, object_proto, fn_proto)?;
|
||||
let function_constr = function::fill_proto(activation, fn_proto, object_constr);
|
||||
let object_constr = object::fill_proto(activation, gs, object_proto, fn_proto)?;
|
||||
let function_constr = function::fill_proto(activation, gs, fn_proto, object_constr);
|
||||
|
||||
let (class_constr, class_proto, class_class) =
|
||||
let (class_constr, class_proto) =
|
||||
class::create_class(activation, gs, object_constr, object_proto, fn_proto);
|
||||
|
||||
dynamic_class(mc, object_constr, object_class, domain, script)?;
|
||||
dynamic_class(mc, function_constr, fn_class, domain, script)?;
|
||||
dynamic_class(mc, class_constr, class_class, domain, script)?;
|
||||
dynamic_class(mc, object_constr, domain, script)?;
|
||||
dynamic_class(mc, function_constr, domain, script)?;
|
||||
dynamic_class(mc, class_constr, domain, script)?;
|
||||
|
||||
// At this point, we need at least a partial set of system prototypes in
|
||||
// order to continue initializing the player. The rest of the prototypes
|
||||
|
@ -425,6 +427,9 @@ pub fn load_player_globals<'gc>(
|
|||
domain,
|
||||
script,
|
||||
)?;
|
||||
gs.as_application_domain()
|
||||
.unwrap()
|
||||
.init_default_domain_memory(activation)?;
|
||||
|
||||
class(
|
||||
activation,
|
||||
|
@ -436,7 +441,6 @@ pub fn load_player_globals<'gc>(
|
|||
class(
|
||||
activation,
|
||||
flash::utils::compression_algorithm::create_class(mc),
|
||||
implicit_deriver,
|
||||
domain,
|
||||
script,
|
||||
)?;
|
||||
|
|
|
@ -96,15 +96,19 @@ pub fn build_array<'gc>(
|
|||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
array: ArrayStorage<'gc>,
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
let mut array_proto = activation.avm2().prototypes().array;
|
||||
let array_constr = array_proto
|
||||
.get_property(
|
||||
array_proto,
|
||||
&QName::new(Namespace::public(), "constructor"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
Ok(ArrayObject::from_array(
|
||||
array,
|
||||
activation
|
||||
.context
|
||||
.avm2
|
||||
.system_prototypes
|
||||
.as_ref()
|
||||
.map(|sp| sp.array)
|
||||
.unwrap(),
|
||||
array_constr,
|
||||
array_proto,
|
||||
activation.context.gc_context,
|
||||
)
|
||||
.into())
|
||||
|
|
|
@ -8,7 +8,6 @@ use crate::avm2::object::{ClassObject, Object, ScriptObject, TObject};
|
|||
use crate::avm2::scope::Scope;
|
||||
use crate::avm2::value::Value;
|
||||
use crate::avm2::Error;
|
||||
use gc_arena::GcCell;
|
||||
|
||||
/// Implements `Class`'s instance initializer.
|
||||
///
|
||||
|
@ -38,7 +37,7 @@ pub fn create_class<'gc>(
|
|||
super_constr: Object<'gc>,
|
||||
super_proto: Object<'gc>,
|
||||
fn_proto: Object<'gc>,
|
||||
) -> (Object<'gc>, Object<'gc>, GcCell<'gc, Class<'gc>>) {
|
||||
) -> (Object<'gc>, Object<'gc>) {
|
||||
let class_class = Class::new(
|
||||
QName::new(Namespace::public(), "Class"),
|
||||
Some(QName::new(Namespace::public(), "Object").into()),
|
||||
|
@ -48,20 +47,17 @@ pub fn create_class<'gc>(
|
|||
);
|
||||
|
||||
let scope = Scope::push_scope(globals.get_scope(), globals, activation.context.gc_context);
|
||||
let proto = ScriptObject::prototype(
|
||||
activation.context.gc_context,
|
||||
super_proto,
|
||||
class_class,
|
||||
Some(scope),
|
||||
);
|
||||
let proto = ScriptObject::object(activation.context.gc_context, super_proto);
|
||||
|
||||
let constr = ClassObject::from_builtin_constr(
|
||||
activation.context.gc_context,
|
||||
Some(super_constr),
|
||||
class_class,
|
||||
Some(scope),
|
||||
proto,
|
||||
fn_proto,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
(constr, proto, class_class)
|
||||
(constr, proto)
|
||||
}
|
||||
|
|
|
@ -560,6 +560,15 @@ pub fn loader_info<'gc>(
|
|||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
if let Some(dobj) = this.and_then(|this| this.as_display_object()) {
|
||||
let mut loaderinfo_proto = activation.avm2().prototypes().loaderinfo;
|
||||
let loaderinfo_constr = loaderinfo_proto
|
||||
.get_property(
|
||||
loaderinfo_proto,
|
||||
&QName::new(Namespace::public(), "constructor"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
if let Some(root) = dobj.avm2_root(&mut activation.context) {
|
||||
if DisplayObject::ptr_eq(root, dobj) {
|
||||
let movie = dobj.movie();
|
||||
|
@ -568,7 +577,8 @@ pub fn loader_info<'gc>(
|
|||
let obj = LoaderInfoObject::from_movie(
|
||||
movie,
|
||||
root,
|
||||
activation.context.avm2.prototypes().loaderinfo,
|
||||
loaderinfo_constr,
|
||||
loaderinfo_proto,
|
||||
activation.context.gc_context,
|
||||
)?;
|
||||
|
||||
|
@ -581,7 +591,8 @@ pub fn loader_info<'gc>(
|
|||
|
||||
if DisplayObject::ptr_eq(dobj, activation.context.stage.into()) {
|
||||
return Ok(LoaderInfoObject::from_stage(
|
||||
activation.context.avm2.prototypes().loaderinfo,
|
||||
loaderinfo_constr,
|
||||
loaderinfo_proto,
|
||||
activation.context.gc_context,
|
||||
)
|
||||
.into());
|
||||
|
|
|
@ -66,11 +66,21 @@ pub fn application_domain<'gc>(
|
|||
) -> Result<Value<'gc>, Error> {
|
||||
if let Some(this) = this {
|
||||
if let Some(loader_stream) = this.as_loader_stream() {
|
||||
let mut appdomain_proto = activation.avm2().prototypes().application_domain;
|
||||
let appdomain_constr = appdomain_proto
|
||||
.get_property(
|
||||
appdomain_proto,
|
||||
&QName::new(Namespace::public(), "constructor"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
match &*loader_stream {
|
||||
LoaderStream::Stage => {
|
||||
return Ok(DomainObject::from_domain(
|
||||
activation.context.gc_context,
|
||||
Some(activation.context.avm2.prototypes().application_domain),
|
||||
appdomain_constr,
|
||||
Some(appdomain_proto),
|
||||
activation.context.avm2.global_domain(),
|
||||
)
|
||||
.into());
|
||||
|
@ -82,7 +92,8 @@ pub fn application_domain<'gc>(
|
|||
.library_for_movie_mut(movie.clone());
|
||||
return Ok(DomainObject::from_domain(
|
||||
activation.context.gc_context,
|
||||
Some(activation.context.avm2.prototypes().application_domain),
|
||||
appdomain_constr,
|
||||
Some(appdomain_proto),
|
||||
library.avm2_domain(),
|
||||
)
|
||||
.into());
|
||||
|
@ -290,9 +301,20 @@ pub fn bytes<'gc>(
|
|||
return Err("Error: The stage's loader info does not have a bytestream".into())
|
||||
}
|
||||
LoaderStream::Swf(root, _) => {
|
||||
let ba_proto = activation.context.avm2.prototypes().bytearray;
|
||||
let ba =
|
||||
ByteArrayObject::construct(activation.context.gc_context, Some(ba_proto));
|
||||
let mut ba_proto = activation.context.avm2.prototypes().bytearray;
|
||||
let ba_constr = ba_proto
|
||||
.get_property(
|
||||
ba_proto,
|
||||
&QName::new(Namespace::public(), "constructor"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
let ba = ByteArrayObject::construct(
|
||||
activation.context.gc_context,
|
||||
ba_constr,
|
||||
Some(ba_proto),
|
||||
);
|
||||
let mut ba_write = ba.as_bytearray_mut(activation.context.gc_context).unwrap();
|
||||
|
||||
// First, write a fake header corresponding to an
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
use crate::avm2::activation::Activation;
|
||||
use crate::avm2::array::ArrayStorage;
|
||||
use crate::avm2::class::Class;
|
||||
use crate::avm2::globals::flash::display::{framelabel, scene};
|
||||
use crate::avm2::method::{Method, NativeMethod};
|
||||
use crate::avm2::names::{Namespace, QName};
|
||||
use crate::avm2::object::{ArrayObject, Object, TObject};
|
||||
|
@ -163,26 +162,42 @@ fn labels_for_scene<'gc>(
|
|||
start: scene_start,
|
||||
length: scene_length,
|
||||
} = scene;
|
||||
let frame_label_proto = activation.context.avm2.prototypes().framelabel;
|
||||
let mut frame_label_proto = activation.context.avm2.prototypes().framelabel;
|
||||
let labels = mc.labels_in_range(*scene_start, scene_start + scene_length);
|
||||
let mut frame_labels = Vec::with_capacity(labels.len());
|
||||
let frame_label_constr = frame_label_proto
|
||||
.get_property(
|
||||
frame_label_proto,
|
||||
&QName::new(Namespace::public(), "constructor"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
for (name, frame) in labels {
|
||||
let name: Value<'gc> = AvmString::new(activation.context.gc_context, name).into();
|
||||
let local_frame = frame - scene_start + 1;
|
||||
let args = [name, local_frame.into()];
|
||||
let frame_label = frame_label_proto.construct(activation, &args)?;
|
||||
|
||||
framelabel::instance_init(activation, Some(frame_label), &args)?;
|
||||
let frame_label = frame_label_constr.construct(activation, &args)?;
|
||||
|
||||
frame_labels.push(Some(frame_label.into()));
|
||||
}
|
||||
|
||||
let mut array_proto = activation.avm2().prototypes().array;
|
||||
let array_constr = array_proto
|
||||
.get_property(
|
||||
array_proto,
|
||||
&QName::new(Namespace::public(), "constructor"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
Ok((
|
||||
scene_name.to_string(),
|
||||
*scene_length,
|
||||
ArrayObject::from_array(
|
||||
ArrayStorage::from_storage(frame_labels),
|
||||
activation.context.avm2.prototypes().array,
|
||||
array_constr,
|
||||
array_proto,
|
||||
activation.context.gc_context,
|
||||
),
|
||||
))
|
||||
|
@ -225,16 +240,21 @@ pub fn current_scene<'gc>(
|
|||
length: mc.total_frames(),
|
||||
});
|
||||
let (scene_name, scene_length, scene_labels) = labels_for_scene(activation, mc, &scene)?;
|
||||
let scene_proto = activation.context.avm2.prototypes().scene;
|
||||
let mut scene_proto = activation.context.avm2.prototypes().scene;
|
||||
let scene_constr = scene_proto
|
||||
.get_property(
|
||||
scene_proto,
|
||||
&QName::new(Namespace::public(), "constructor"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
let args = [
|
||||
AvmString::new(activation.context.gc_context, scene_name).into(),
|
||||
scene_labels.into(),
|
||||
scene_length.into(),
|
||||
];
|
||||
|
||||
let scene = scene_proto.construct(activation, &args)?;
|
||||
|
||||
scene::instance_init(activation, Some(scene), &args)?;
|
||||
let scene = scene_constr.construct(activation, &args)?;
|
||||
|
||||
return Ok(scene.into());
|
||||
}
|
||||
|
@ -265,23 +285,38 @@ pub fn scenes<'gc>(
|
|||
for scene in mc_scenes {
|
||||
let (scene_name, scene_length, scene_labels) =
|
||||
labels_for_scene(activation, mc, &scene)?;
|
||||
let scene_proto = activation.context.avm2.prototypes().scene;
|
||||
let mut scene_proto = activation.context.avm2.prototypes().scene;
|
||||
let scene_constr = scene_proto
|
||||
.get_property(
|
||||
scene_proto,
|
||||
&QName::new(Namespace::public(), "constructor"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
let args = [
|
||||
AvmString::new(activation.context.gc_context, scene_name).into(),
|
||||
scene_labels.into(),
|
||||
scene_length.into(),
|
||||
];
|
||||
|
||||
let scene = scene_proto.construct(activation, &args)?;
|
||||
|
||||
scene::instance_init(activation, Some(scene), &args)?;
|
||||
let scene = scene_constr.construct(activation, &args)?;
|
||||
|
||||
scene_objects.push(Some(scene.into()));
|
||||
}
|
||||
|
||||
let mut array_proto = activation.avm2().prototypes().array;
|
||||
let array_constr = array_proto
|
||||
.get_property(
|
||||
array_proto,
|
||||
&QName::new(Namespace::public(), "constructor"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
return Ok(ArrayObject::from_array(
|
||||
ArrayStorage::from_storage(scene_objects),
|
||||
activation.context.avm2.prototypes().array,
|
||||
array_constr,
|
||||
array_proto,
|
||||
activation.context.gc_context,
|
||||
)
|
||||
.into());
|
||||
|
|
|
@ -62,10 +62,20 @@ pub fn graphics<'gc>(
|
|||
activation,
|
||||
)? {
|
||||
Value::Undefined | Value::Null => {
|
||||
let graphics_proto = activation.context.avm2.prototypes().graphics;
|
||||
let mut graphics_proto = activation.context.avm2.prototypes().graphics;
|
||||
let graphics_constr = graphics_proto
|
||||
.get_property(
|
||||
graphics_proto,
|
||||
&QName::new(Namespace::public(), "constructor"),
|
||||
activation,
|
||||
)
|
||||
.and_then(|v| v.coerce_to_object(activation))
|
||||
.expect("Video proto needs constr");
|
||||
|
||||
let graphics = Value::from(StageObject::for_display_object(
|
||||
activation.context.gc_context,
|
||||
dobj,
|
||||
graphics_constr,
|
||||
graphics_proto,
|
||||
));
|
||||
this.set_property(
|
||||
|
|
|
@ -48,10 +48,20 @@ pub fn graphics<'gc>(
|
|||
activation,
|
||||
)? {
|
||||
Value::Undefined | Value::Null => {
|
||||
let graphics_proto = activation.context.avm2.prototypes().graphics;
|
||||
let mut graphics_proto = activation.context.avm2.prototypes().graphics;
|
||||
let graphics_constr = graphics_proto
|
||||
.get_property(
|
||||
graphics_proto,
|
||||
&QName::new(Namespace::public(), "constructor"),
|
||||
activation,
|
||||
)
|
||||
.and_then(|v| v.coerce_to_object(activation))
|
||||
.expect("Video proto needs constr");
|
||||
|
||||
let graphics = Value::from(StageObject::for_display_object(
|
||||
activation.context.gc_context,
|
||||
dobj,
|
||||
graphics_constr,
|
||||
graphics_proto,
|
||||
));
|
||||
this.set_property(
|
||||
|
|
|
@ -142,10 +142,18 @@ pub fn clone<'gc>(
|
|||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
if let Some(evt) = this.unwrap().as_event() {
|
||||
let evt_proto = activation.avm2().system_prototypes.as_ref().unwrap().event;
|
||||
let mut evt_proto = activation.avm2().prototypes().event;
|
||||
let evt_constr = evt_proto
|
||||
.get_property(
|
||||
evt_proto,
|
||||
&QName::new(Namespace::public(), "constructor"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
return Ok(EventObject::from_event(
|
||||
activation.context.gc_context,
|
||||
evt_constr,
|
||||
Some(evt_proto),
|
||||
evt.clone(),
|
||||
)
|
||||
|
|
|
@ -10,9 +10,17 @@ fn create_point<'gc>(
|
|||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
coords: (f64, f64),
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
let proto = activation.context.avm2.prototypes().point;
|
||||
let mut point_proto = activation.context.avm2.prototypes().point;
|
||||
let point_constr = point_proto
|
||||
.get_property(
|
||||
point_proto,
|
||||
&QName::new(Namespace::public(), "constructor"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
let args = [Value::Number(coords.0), Value::Number(coords.1)];
|
||||
let new_point = proto.construct(activation, &args)?;
|
||||
let new_point = point_constr.construct(activation, &args)?;
|
||||
instance_init(activation, Some(new_point), &args)?;
|
||||
|
||||
Ok(new_point.into())
|
||||
|
|
|
@ -39,10 +39,20 @@ pub fn current_domain<'gc>(
|
|||
) -> Result<Value<'gc>, Error> {
|
||||
let globals = activation.scope().map(|s| s.read().globals());
|
||||
let appdomain = globals.and_then(|g| g.as_application_domain());
|
||||
let mut appdomain_proto = activation.avm2().prototypes().application_domain;
|
||||
let appdomain_constr = appdomain_proto
|
||||
.get_property(
|
||||
appdomain_proto,
|
||||
&QName::new(Namespace::public(), "constructor"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
if let Some(appdomain) = appdomain {
|
||||
return Ok(DomainObject::from_domain(
|
||||
activation.context.gc_context,
|
||||
Some(activation.context.avm2.prototypes().application_domain),
|
||||
appdomain_constr,
|
||||
Some(appdomain_proto),
|
||||
appdomain,
|
||||
)
|
||||
.into());
|
||||
|
@ -59,9 +69,19 @@ pub fn parent_domain<'gc>(
|
|||
) -> Result<Value<'gc>, Error> {
|
||||
if let Some(appdomain) = this.and_then(|this| this.as_application_domain()) {
|
||||
if let Some(parent_domain) = appdomain.parent_domain() {
|
||||
let mut appdomain_proto = activation.avm2().prototypes().application_domain;
|
||||
let appdomain_constr = appdomain_proto
|
||||
.get_property(
|
||||
appdomain_proto,
|
||||
&QName::new(Namespace::public(), "constructor"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
return Ok(DomainObject::from_domain(
|
||||
activation.context.gc_context,
|
||||
Some(activation.context.avm2.prototypes().application_domain),
|
||||
appdomain_constr,
|
||||
Some(appdomain_proto),
|
||||
parent_domain,
|
||||
)
|
||||
.into());
|
||||
|
|
|
@ -9,7 +9,6 @@ use crate::avm2::object::{ClassObject, FunctionObject, Object, ScriptObject, TOb
|
|||
use crate::avm2::scope::Scope;
|
||||
use crate::avm2::value::Value;
|
||||
use crate::avm2::Error;
|
||||
use gc_arena::GcCell;
|
||||
|
||||
/// Implements `Function`'s instance initializer.
|
||||
pub fn instance_init<'gc>(
|
||||
|
@ -90,12 +89,21 @@ fn apply<'gc>(
|
|||
|
||||
/// Create Function prototype.
|
||||
///
|
||||
/// This function creates a suitable class and object prototype attached to it.
|
||||
/// This function creates a suitable prototype and returns it.
|
||||
pub fn create_proto<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
globals: Object<'gc>,
|
||||
super_proto: Object<'gc>,
|
||||
) -> (Object<'gc>, GcCell<'gc, Class<'gc>>) {
|
||||
) -> Object<'gc> {
|
||||
ScriptObject::object(activation.context.gc_context, super_proto)
|
||||
}
|
||||
|
||||
/// Fill `Function.prototype` and allocate it's constructor.
|
||||
pub fn fill_proto<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
globals: Object<'gc>,
|
||||
mut function_proto: Object<'gc>,
|
||||
super_constr: Object<'gc>,
|
||||
) -> Object<'gc> {
|
||||
let function_class = Class::new(
|
||||
QName::new(Namespace::public(), "Function"),
|
||||
Some(QName::new(Namespace::public(), "Object").into()),
|
||||
|
@ -104,22 +112,7 @@ pub fn create_proto<'gc>(
|
|||
activation.context.gc_context,
|
||||
);
|
||||
let scope = Scope::push_scope(globals.get_scope(), globals, activation.context.gc_context);
|
||||
let function_proto = ScriptObject::prototype(
|
||||
activation.context.gc_context,
|
||||
super_proto,
|
||||
function_class,
|
||||
Some(scope),
|
||||
);
|
||||
|
||||
(function_proto, function_class)
|
||||
}
|
||||
|
||||
/// Fill `Function.prototype` and allocate it's constructor.
|
||||
pub fn fill_proto<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
mut function_proto: Object<'gc>,
|
||||
super_class: Object<'gc>,
|
||||
) -> Object<'gc> {
|
||||
function_proto.install_method(
|
||||
activation.context.gc_context,
|
||||
QName::new(Namespace::as3_namespace(), "call"),
|
||||
|
@ -135,7 +128,9 @@ pub fn fill_proto<'gc>(
|
|||
|
||||
ClassObject::from_builtin_constr(
|
||||
activation.context.gc_context,
|
||||
Some(super_class),
|
||||
Some(super_constr),
|
||||
function_class,
|
||||
Some(scope),
|
||||
function_proto,
|
||||
function_proto,
|
||||
)
|
||||
|
|
|
@ -9,7 +9,6 @@ use crate::avm2::scope::Scope;
|
|||
use crate::avm2::traits::Trait;
|
||||
use crate::avm2::value::Value;
|
||||
use crate::avm2::Error;
|
||||
use gc_arena::GcCell;
|
||||
|
||||
/// Implements `Object`'s instance initializer.
|
||||
pub fn instance_init<'gc>(
|
||||
|
@ -155,38 +154,8 @@ pub fn set_property_is_enumerable<'gc>(
|
|||
/// This function creates a suitable class and object prototype attached to it,
|
||||
/// but does not actually fill it with methods. That requires a valid function
|
||||
/// prototype, and is thus done by `fill_proto` below.
|
||||
pub fn create_proto<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
globals: Object<'gc>,
|
||||
) -> (Object<'gc>, GcCell<'gc, Class<'gc>>) {
|
||||
let object_class = Class::new(
|
||||
QName::new(Namespace::public(), "Object"),
|
||||
None,
|
||||
Method::from_builtin(instance_init),
|
||||
Method::from_builtin(class_init),
|
||||
activation.context.gc_context,
|
||||
);
|
||||
let mut write = object_class.write(activation.context.gc_context);
|
||||
|
||||
write.define_class_trait(Trait::from_const(
|
||||
QName::new(Namespace::public(), "length"),
|
||||
QName::new(Namespace::public(), "int").into(),
|
||||
None,
|
||||
));
|
||||
|
||||
// Fixed traits (in AS3 namespace)
|
||||
const PUBLIC_INSTANCE_METHODS: &[(&str, NativeMethod)] = &[
|
||||
("hasOwnProperty", has_own_property),
|
||||
("isPrototypeOf", is_prototype_of),
|
||||
("propertyIsEnumerable", property_is_enumerable),
|
||||
];
|
||||
write.define_as3_builtin_instance_methods(PUBLIC_INSTANCE_METHODS);
|
||||
|
||||
let scope = Scope::push_scope(globals.get_scope(), globals, activation.context.gc_context);
|
||||
let proto =
|
||||
ScriptObject::bare_prototype(activation.context.gc_context, object_class, Some(scope));
|
||||
|
||||
(proto, object_class)
|
||||
pub fn create_proto<'gc>(activation: &mut Activation<'_, 'gc, '_>) -> Object<'gc> {
|
||||
ScriptObject::bare_object(activation.context.gc_context)
|
||||
}
|
||||
|
||||
/// Finish constructing `Object.prototype`, and also construct `Object`.
|
||||
|
@ -200,10 +169,12 @@ pub fn create_proto<'gc>(
|
|||
/// bare objects for both and let this function fill Object for you.
|
||||
pub fn fill_proto<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
globals: Object<'gc>,
|
||||
mut object_proto: Object<'gc>,
|
||||
fn_proto: Object<'gc>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let gc_context = activation.context.gc_context;
|
||||
|
||||
object_proto.install_dynamic_property(
|
||||
gc_context,
|
||||
QName::new(Namespace::public(), "hasOwnProperty"),
|
||||
|
@ -240,6 +211,39 @@ pub fn fill_proto<'gc>(
|
|||
FunctionObject::from_builtin(gc_context, value_of, fn_proto).into(),
|
||||
)?;
|
||||
|
||||
let object_constr = ClassObject::from_builtin_constr(gc_context, None, object_proto, fn_proto)?;
|
||||
Ok(object_constr)
|
||||
let object_class = Class::new(
|
||||
QName::new(Namespace::public(), "Object"),
|
||||
None,
|
||||
Method::from_builtin(instance_init),
|
||||
Method::from_builtin(class_init),
|
||||
gc_context,
|
||||
);
|
||||
let mut write = object_class.write(gc_context);
|
||||
|
||||
write.define_class_trait(Trait::from_const(
|
||||
QName::new(Namespace::public(), "length"),
|
||||
QName::new(Namespace::public(), "int").into(),
|
||||
None,
|
||||
));
|
||||
|
||||
// Fixed traits (in AS3 namespace)
|
||||
const PUBLIC_INSTANCE_METHODS: &[(&str, NativeMethod)] = &[
|
||||
("hasOwnProperty", has_own_property),
|
||||
("isPrototypeOf", is_prototype_of),
|
||||
("propertyIsEnumerable", property_is_enumerable),
|
||||
];
|
||||
write.define_as3_builtin_instance_methods(PUBLIC_INSTANCE_METHODS);
|
||||
|
||||
drop(write);
|
||||
|
||||
let scope = Scope::push_scope(globals.get_scope(), globals, gc_context);
|
||||
|
||||
ClassObject::from_builtin_constr(
|
||||
gc_context,
|
||||
None,
|
||||
object_class,
|
||||
Some(scope),
|
||||
object_proto,
|
||||
fn_proto,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -209,15 +209,19 @@ pub fn exec<'gc>(
|
|||
None => return Ok(Value::Null),
|
||||
};
|
||||
|
||||
let mut regexp_proto = activation.avm2().prototypes().array;
|
||||
let regexp_constr = regexp_proto
|
||||
.get_property(
|
||||
regexp_proto,
|
||||
&QName::new(Namespace::public(), "constructor"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
let object = ArrayObject::from_array(
|
||||
storage,
|
||||
activation
|
||||
.context
|
||||
.avm2
|
||||
.system_prototypes
|
||||
.as_ref()
|
||||
.map(|sp| sp.array)
|
||||
.unwrap(),
|
||||
regexp_constr,
|
||||
regexp_proto,
|
||||
activation.context.gc_context,
|
||||
);
|
||||
|
||||
|
|
|
@ -113,17 +113,25 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
/// Retrieve the base prototype that a particular QName trait is defined in.
|
||||
/// Retrieve the base class constructor that a particular QName trait is
|
||||
/// defined in.
|
||||
///
|
||||
/// Must be called on a class constructor; will error out if called on
|
||||
/// anything else.
|
||||
///
|
||||
/// This function returns `None` for non-trait properties, such as actually
|
||||
/// defined prototype methods for ES3-style classes.
|
||||
fn get_base_proto(self, name: &QName<'gc>) -> Result<Option<Object<'gc>>, Error> {
|
||||
if self.provides_trait(name)? {
|
||||
fn find_base_constr_for_trait(self, name: &QName<'gc>) -> Result<Option<Object<'gc>>, Error> {
|
||||
let class = self
|
||||
.as_class()
|
||||
.ok_or("Cannot get base traits on non-class object")?;
|
||||
|
||||
if class.read().has_instance_trait(name) {
|
||||
return Ok(Some(self.into()));
|
||||
}
|
||||
|
||||
if let Some(proto) = self.proto() {
|
||||
return proto.get_base_proto(name);
|
||||
if let Some(base) = self.base_class_constr() {
|
||||
return base.find_base_constr_for_trait(name);
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
|
@ -305,26 +313,6 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
/// prototype chain bearing this name is malformed in some way.
|
||||
fn get_trait_slot(self, id: u32) -> Result<Option<Trait<'gc>>, Error>;
|
||||
|
||||
/// Populate a list of traits that this object provides for a given name.
|
||||
///
|
||||
/// This function yields traits for class constructors and prototypes, but
|
||||
/// not instances. For resolving traits for normal `TObject` methods, use
|
||||
/// `get_trait` and `has_trait` as it will tell you if the current object
|
||||
/// has a given trait.
|
||||
fn get_provided_trait(
|
||||
&self,
|
||||
name: &QName<'gc>,
|
||||
known_traits: &mut Vec<Trait<'gc>>,
|
||||
) -> Result<(), Error>;
|
||||
|
||||
/// Populate a list of traits that this object provides for a given slot.
|
||||
///
|
||||
/// This function yields traits for class constructors and prototypes, but
|
||||
/// not instances. For resolving traits for normal `TObject` methods, use
|
||||
/// `get_trait` and `has_trait` as it will tell you if the current object
|
||||
/// has a given trait.
|
||||
fn get_provided_trait_slot(&self, id: u32) -> Result<Option<Trait<'gc>>, Error>;
|
||||
|
||||
/// Retrieves the scope chain of the object at time of its creation.
|
||||
///
|
||||
/// The scope chain is used to determine the starting scope stack when an
|
||||
|
@ -397,11 +385,6 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
/// Returns true if an object has one or more traits of a given name.
|
||||
fn has_trait(self, name: &QName<'gc>) -> Result<bool, Error>;
|
||||
|
||||
/// Returns true if an object is part of a class that defines a trait of a
|
||||
/// given name on itself (as opposed to merely inheriting a superclass
|
||||
/// trait.)
|
||||
fn provides_trait(self, name: &QName<'gc>) -> Result<bool, Error>;
|
||||
|
||||
/// Indicates whether or not a property or *instantiated* trait exists on
|
||||
/// an object and is not part of the prototype chain.
|
||||
///
|
||||
|
@ -732,49 +715,200 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
_reciever: Option<Object<'gc>>,
|
||||
_arguments: &[Value<'gc>],
|
||||
_activation: &mut Activation<'_, 'gc, '_>,
|
||||
_base_proto: Option<Object<'gc>>,
|
||||
_base_constr: Option<Object<'gc>>,
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
Err("Object is not callable".into())
|
||||
}
|
||||
|
||||
/// Construct a host object of some kind and return its cell.
|
||||
/// Call an instance method by name.
|
||||
///
|
||||
/// Intended to be called on the current base constructor used for
|
||||
/// searching for traits. That constructor's base classes will be searched
|
||||
/// for an appropriately named method trait, and if found, the method will
|
||||
/// be called with the given reciever, arguments and new ancestor
|
||||
/// constructor.
|
||||
fn call_instance_method(
|
||||
self,
|
||||
name: &QName<'gc>,
|
||||
reciever: Option<Object<'gc>>,
|
||||
arguments: &[Value<'gc>],
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
let constr_with_trait = self.find_base_constr_for_trait(&name)?.ok_or_else(|| {
|
||||
format!(
|
||||
"Attempted to supercall method {:?}, which does not exist",
|
||||
name
|
||||
)
|
||||
})?;
|
||||
let mut class_traits = Vec::new();
|
||||
constr_with_trait
|
||||
.as_class()
|
||||
.unwrap()
|
||||
.read()
|
||||
.lookup_instance_traits(name, &mut class_traits)?;
|
||||
let base_trait = class_traits.first().ok_or_else(|| {
|
||||
format!(
|
||||
"Attempted to supercall method {:?}, which does not exist",
|
||||
name
|
||||
)
|
||||
})?;
|
||||
let scope = constr_with_trait.get_scope();
|
||||
|
||||
if let TraitKind::Method { method, .. } = base_trait.kind() {
|
||||
let callee = FunctionObject::from_method(
|
||||
activation.context.gc_context,
|
||||
method.clone(),
|
||||
scope,
|
||||
activation.avm2().prototypes().function,
|
||||
reciever,
|
||||
);
|
||||
|
||||
callee.call(reciever, arguments, activation, Some(constr_with_trait))
|
||||
} else {
|
||||
Err(format!(
|
||||
"Attempted to supercall method {:?}, which does not exist",
|
||||
name
|
||||
)
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Call an instance getter by name.
|
||||
///
|
||||
/// Intended to be called on the current base constructor used for
|
||||
/// searching for traits. That constructor's base classes will be searched
|
||||
/// for an appropriately named getter trait, and if found, the getter will
|
||||
/// be called with the given reciever, arguments and new ancestor
|
||||
/// constructor.
|
||||
fn call_instance_getter(
|
||||
self,
|
||||
name: &QName<'gc>,
|
||||
reciever: Option<Object<'gc>>,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
let constr_with_trait = self.find_base_constr_for_trait(&name)?.ok_or_else(|| {
|
||||
format!(
|
||||
"Attempted to supercall method {:?}, which does not exist",
|
||||
name
|
||||
)
|
||||
})?;
|
||||
let mut class_traits = Vec::new();
|
||||
constr_with_trait
|
||||
.as_class()
|
||||
.unwrap()
|
||||
.read()
|
||||
.lookup_instance_traits(name, &mut class_traits)?;
|
||||
let base_trait = class_traits.first().ok_or_else(|| {
|
||||
format!(
|
||||
"Attempted to supercall method {:?}, which does not exist",
|
||||
name
|
||||
)
|
||||
})?;
|
||||
let scope = constr_with_trait.get_scope();
|
||||
|
||||
if let TraitKind::Getter { method, .. } = base_trait.kind() {
|
||||
let callee = FunctionObject::from_method(
|
||||
activation.context.gc_context,
|
||||
method.clone(),
|
||||
scope,
|
||||
activation.avm2().prototypes().function,
|
||||
reciever,
|
||||
);
|
||||
|
||||
callee.call(reciever, &[], activation, Some(constr_with_trait))
|
||||
} else {
|
||||
Err(format!(
|
||||
"Attempted to supercall getter for {:?}, which does not exist",
|
||||
name
|
||||
)
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Call an instance setter by name.
|
||||
///
|
||||
/// Intended to be called on the current base constructor used for
|
||||
/// searching for traits. That constructor's base classes will be searched
|
||||
/// for an appropriately named setter trait, and if found, the setter will
|
||||
/// be called with the given reciever, arguments and new ancestor
|
||||
/// constructor.
|
||||
fn call_instance_setter(
|
||||
self,
|
||||
name: &QName<'gc>,
|
||||
value: Value<'gc>,
|
||||
reciever: Option<Object<'gc>>,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
) -> Result<(), Error> {
|
||||
let constr_with_trait = self.find_base_constr_for_trait(&name)?.ok_or_else(|| {
|
||||
format!(
|
||||
"Attempted to supercall method {:?}, which does not exist",
|
||||
name
|
||||
)
|
||||
})?;
|
||||
let mut class_traits = Vec::new();
|
||||
constr_with_trait
|
||||
.as_class()
|
||||
.unwrap()
|
||||
.read()
|
||||
.lookup_instance_traits(name, &mut class_traits)?;
|
||||
let base_trait = class_traits.first().ok_or_else(|| {
|
||||
format!(
|
||||
"Attempted to supercall method {:?}, which does not exist",
|
||||
name
|
||||
)
|
||||
})?;
|
||||
let scope = constr_with_trait.get_scope();
|
||||
|
||||
if let TraitKind::Setter { method, .. } = base_trait.kind() {
|
||||
let callee = FunctionObject::from_method(
|
||||
activation.context.gc_context,
|
||||
method.clone(),
|
||||
scope,
|
||||
activation.avm2().prototypes().function,
|
||||
reciever,
|
||||
);
|
||||
|
||||
callee.call(reciever, &[value], activation, Some(constr_with_trait))?;
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!(
|
||||
"Attempted to supercall setter for {:?}, which does not exist",
|
||||
name
|
||||
)
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a Class or Function and return an instance of it.
|
||||
///
|
||||
/// As the first step in object construction, the `construct` method is
|
||||
/// called on the prototype to create a new object. The prototype may
|
||||
/// construct any object implementation it wants, however, it's expected
|
||||
/// to produce a like `TObject` implementor with itself as the new object's
|
||||
/// proto.
|
||||
/// called on the constructor to create a new object. The constructor is
|
||||
/// then expected to perform the following steps, in order:
|
||||
///
|
||||
/// After construction, the constructor function is `call`ed with the new
|
||||
/// object as `this` to initialize the object.
|
||||
///
|
||||
/// `construct`ed objects should instantiate instance traits of the class
|
||||
/// that this prototype represents.
|
||||
///
|
||||
/// The arguments passed to the constructor are provided here; however, all
|
||||
/// object construction should happen in `call`, not `new`. `new` exists
|
||||
/// purely so that host objects can be constructed by the VM.
|
||||
/// 1. Allocate the instance object. If the constructor is a `Class`, then
|
||||
/// use the class's instance deriver to allocate the object. Otherwise,
|
||||
/// allocate it as the same type as the constructor's explicit `prototype`.
|
||||
/// 2. Associate the instance object with the constructor's explicit
|
||||
/// `prototype`.
|
||||
/// 3. If the instance has traits, install them at this time.
|
||||
/// 4. Call the constructor method with the newly-allocated object as
|
||||
/// reciever. For `Function`s, this is just the function's method.
|
||||
/// 5. Yield the allocated object. (The return values of constructors are
|
||||
/// ignored.)
|
||||
fn construct(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Object<'gc>, Error>;
|
||||
self,
|
||||
_activation: &mut Activation<'_, 'gc, '_>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
Err("Object is not constructable".into())
|
||||
}
|
||||
|
||||
/// Construct a host object prototype of some kind and return it.
|
||||
///
|
||||
/// This is called specifically to construct prototypes. The primary
|
||||
/// difference is that a new class and scope closure are defined here.
|
||||
/// Objects constructed from the new prototype should use that new class
|
||||
/// and scope closure when instantiating non-prototype traits.
|
||||
///
|
||||
/// Unlike `construct`, `derive`d objects should *not* instantiate instance
|
||||
/// traits.
|
||||
fn derive(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error>;
|
||||
/// This is called specifically to allocate old-style ES3 instances. The
|
||||
/// returned object should have no properties upon it.
|
||||
fn derive(&self, activation: &mut Activation<'_, 'gc, '_>) -> Result<Object<'gc>, Error>;
|
||||
|
||||
/// Determine the type of primitive coercion this object would prefer, in
|
||||
/// the case that there is no obvious reason to prefer one type over the
|
||||
|
@ -796,7 +930,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
/// coercions.
|
||||
fn to_string(&self, mc: MutationContext<'gc, '_>) -> Result<Value<'gc>, Error> {
|
||||
let class_name = self
|
||||
.as_proto_class()
|
||||
.as_class()
|
||||
.map(|c| c.read().name().local_name())
|
||||
.unwrap_or_else(|| "Object".into());
|
||||
|
||||
|
@ -813,7 +947,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
/// of the class that created this object).
|
||||
fn to_locale_string(&self, mc: MutationContext<'gc, '_>) -> Result<Value<'gc>, Error> {
|
||||
let class_name = self
|
||||
.as_proto_class()
|
||||
.as_class()
|
||||
.map(|c| c.read().name().local_name())
|
||||
.unwrap_or_else(|| "Object".into());
|
||||
|
||||
|
@ -892,28 +1026,21 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
/// Get this object's `Class`, if it has one.
|
||||
fn as_class(&self) -> Option<GcCell<'gc, Class<'gc>>>;
|
||||
|
||||
/// Get this object's constructor, if it has one.
|
||||
fn as_constr(&self) -> Option<Object<'gc>>;
|
||||
|
||||
/// Associate the object with a particular constructor.
|
||||
///
|
||||
/// This turns the object into an instance of that class. It should only be
|
||||
/// used in situations where the object cannot be made an instance of the
|
||||
/// class at allocation time, such as during early runtime setup.
|
||||
fn set_constr(self, mc: MutationContext<'gc, '_>, constr: Object<'gc>);
|
||||
|
||||
/// Get the base class constructor of this object.
|
||||
fn base_class_constr(self) -> Option<Object<'gc>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Get this object's `Class`, or any `Class` on its prototype chain.
|
||||
///
|
||||
/// This only yields `None` for bare objects.
|
||||
fn as_proto_class(&self) -> Option<GcCell<'gc, Class<'gc>>> {
|
||||
let mut class = self.as_class();
|
||||
|
||||
while class.is_none() {
|
||||
if let Some(proto) = self.proto() {
|
||||
class = proto.as_class();
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
class
|
||||
}
|
||||
|
||||
/// Get this object's `Executable`, if it has one.
|
||||
fn as_executable(&self) -> Option<Executable<'gc>> {
|
||||
None
|
||||
|
|
|
@ -17,20 +17,11 @@ use std::cell::{Ref, RefMut};
|
|||
|
||||
/// A class instance deriver that constructs array objects.
|
||||
pub fn array_deriver<'gc>(
|
||||
mut constr: Object<'gc>,
|
||||
constr: Object<'gc>,
|
||||
proto: Object<'gc>,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let base_proto = constr
|
||||
.get_property(
|
||||
constr,
|
||||
&QName::new(Namespace::public(), "prototype"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
ArrayObject::derive(base_proto, activation.context.gc_context, class, scope)
|
||||
ArrayObject::derive(constr, proto, activation.context.gc_context)
|
||||
}
|
||||
|
||||
/// An Object which stores numerical properties in an array.
|
||||
|
@ -50,8 +41,13 @@ pub struct ArrayObjectData<'gc> {
|
|||
|
||||
impl<'gc> ArrayObject<'gc> {
|
||||
/// Construct a fresh array.
|
||||
pub fn construct(base_proto: Object<'gc>, mc: MutationContext<'gc, '_>) -> Object<'gc> {
|
||||
let base = ScriptObjectData::base_new(Some(base_proto), ScriptObjectClass::NoClass);
|
||||
pub fn construct(
|
||||
constr: Object<'gc>,
|
||||
proto: Object<'gc>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
) -> Object<'gc> {
|
||||
let base =
|
||||
ScriptObjectData::base_new(Some(proto), ScriptObjectClass::ClassInstance(constr));
|
||||
|
||||
ArrayObject(GcCell::allocate(
|
||||
mc,
|
||||
|
@ -65,15 +61,12 @@ impl<'gc> ArrayObject<'gc> {
|
|||
|
||||
/// Construct a primitive subclass.
|
||||
pub fn derive(
|
||||
constr: Object<'gc>,
|
||||
base_proto: Object<'gc>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let base = ScriptObjectData::base_new(
|
||||
Some(base_proto),
|
||||
ScriptObjectClass::InstancePrototype(class, scope),
|
||||
);
|
||||
let base =
|
||||
ScriptObjectData::base_new(Some(base_proto), ScriptObjectClass::ClassInstance(constr));
|
||||
|
||||
Ok(ArrayObject(GcCell::allocate(
|
||||
mc,
|
||||
|
@ -88,10 +81,12 @@ impl<'gc> ArrayObject<'gc> {
|
|||
/// Wrap an existing array in an object.
|
||||
pub fn from_array(
|
||||
array: ArrayStorage<'gc>,
|
||||
base_proto: Object<'gc>,
|
||||
constr: Object<'gc>,
|
||||
proto: Object<'gc>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
) -> Object<'gc> {
|
||||
let base = ScriptObjectData::base_new(Some(base_proto), ScriptObjectClass::NoClass);
|
||||
let base =
|
||||
ScriptObjectData::base_new(Some(proto), ScriptObjectClass::ClassInstance(constr));
|
||||
|
||||
ArrayObject(GcCell::allocate(mc, ArrayObjectData { base, array })).into()
|
||||
}
|
||||
|
@ -242,11 +237,7 @@ impl<'gc> TObject<'gc> for ArrayObject<'gc> {
|
|||
Some(RefMut::map(self.0.write(mc), |aod| &mut aod.array))
|
||||
}
|
||||
|
||||
fn construct(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
fn derive(&self, activation: &mut Activation<'_, 'gc, '_>) -> Result<Object<'gc>, Error> {
|
||||
let this: Object<'gc> = Object::ArrayObject(*self);
|
||||
let base = ScriptObjectData::base_new(Some(this), ScriptObjectClass::NoClass);
|
||||
|
||||
|
@ -259,26 +250,4 @@ impl<'gc> TObject<'gc> for ArrayObject<'gc> {
|
|||
))
|
||||
.into())
|
||||
}
|
||||
|
||||
fn derive(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let this: Object<'gc> = Object::ArrayObject(*self);
|
||||
let base = ScriptObjectData::base_new(
|
||||
Some(this),
|
||||
ScriptObjectClass::InstancePrototype(class, scope),
|
||||
);
|
||||
|
||||
Ok(ArrayObject(GcCell::allocate(
|
||||
activation.context.gc_context,
|
||||
ArrayObjectData {
|
||||
base,
|
||||
array: ArrayStorage::new(0),
|
||||
},
|
||||
))
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,20 +15,11 @@ use std::cell::{Ref, RefMut};
|
|||
|
||||
/// A class instance deriver that constructs ByteArray objects.
|
||||
pub fn bytearray_deriver<'gc>(
|
||||
mut constr: Object<'gc>,
|
||||
constr: Object<'gc>,
|
||||
proto: Object<'gc>,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let base_proto = constr
|
||||
.get_property(
|
||||
constr,
|
||||
&QName::new(Namespace::public(), "prototype"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
ByteArrayObject::derive(base_proto, activation.context.gc_context, class, scope)
|
||||
ByteArrayObject::derive(constr, proto, activation.context.gc_context)
|
||||
}
|
||||
|
||||
#[derive(Clone, Collect, Debug, Copy)]
|
||||
|
@ -47,9 +38,10 @@ pub struct ByteArrayObjectData<'gc> {
|
|||
impl<'gc> ByteArrayObject<'gc> {
|
||||
pub fn new(
|
||||
mc: MutationContext<'gc, '_>,
|
||||
base_proto: Option<Object<'gc>>,
|
||||
constr: Object<'gc>,
|
||||
proto: Option<Object<'gc>>,
|
||||
) -> ByteArrayObject<'gc> {
|
||||
let base = ScriptObjectData::base_new(base_proto, ScriptObjectClass::NoClass);
|
||||
let base = ScriptObjectData::base_new(proto, ScriptObjectClass::ClassInstance(constr));
|
||||
|
||||
ByteArrayObject(GcCell::allocate(
|
||||
mc,
|
||||
|
@ -60,20 +52,21 @@ impl<'gc> ByteArrayObject<'gc> {
|
|||
))
|
||||
}
|
||||
|
||||
pub fn construct(mc: MutationContext<'gc, '_>, base_proto: Option<Object<'gc>>) -> Object<'gc> {
|
||||
Self::new(mc, base_proto).into()
|
||||
pub fn construct(
|
||||
mc: MutationContext<'gc, '_>,
|
||||
constr: Object<'gc>,
|
||||
base_proto: Option<Object<'gc>>,
|
||||
) -> Object<'gc> {
|
||||
Self::new(mc, constr, base_proto).into()
|
||||
}
|
||||
|
||||
pub fn derive(
|
||||
constr: Object<'gc>,
|
||||
base_proto: Object<'gc>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let base = ScriptObjectData::base_new(
|
||||
Some(base_proto),
|
||||
ScriptObjectClass::InstancePrototype(class, scope),
|
||||
);
|
||||
let base =
|
||||
ScriptObjectData::base_new(Some(base_proto), ScriptObjectClass::ClassInstance(constr));
|
||||
|
||||
Ok(ByteArrayObject(GcCell::allocate(
|
||||
mc,
|
||||
|
@ -219,29 +212,9 @@ impl<'gc> TObject<'gc> for ByteArrayObject<'gc> {
|
|||
self.0.read().base.resolve_any_trait(local_name)
|
||||
}
|
||||
|
||||
fn construct(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
fn derive(&self, activation: &mut Activation<'_, 'gc, '_>) -> Result<Object<'gc>, Error> {
|
||||
let this: Object<'gc> = Object::ByteArrayObject(*self);
|
||||
Ok(ByteArrayObject::construct(
|
||||
activation.context.gc_context,
|
||||
Some(this),
|
||||
))
|
||||
}
|
||||
|
||||
fn derive(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let this: Object<'gc> = Object::ByteArrayObject(*self);
|
||||
let base = ScriptObjectData::base_new(
|
||||
Some(this),
|
||||
ScriptObjectClass::InstancePrototype(class, scope),
|
||||
);
|
||||
let base = ScriptObjectData::base_new(Some(this), ScriptObjectClass::NoClass);
|
||||
|
||||
Ok(ByteArrayObject(GcCell::allocate(
|
||||
activation.context.gc_context,
|
||||
|
|
|
@ -58,11 +58,20 @@ impl<'gc> ClassObject<'gc> {
|
|||
base_class_constr: Option<Object<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<(Object<'gc>, Object<'gc>), Error> {
|
||||
let class_proto = if let Some(base_class_constr) = base_class_constr {
|
||||
//TODO: Class prototypes are *not* instances of their class and should
|
||||
//not be allocated by a deriver, but instead should be regular objects
|
||||
let class_proto = if let Some(mut base_class_constr) = base_class_constr {
|
||||
let base_proto = base_class_constr
|
||||
.get_property(
|
||||
base_class_constr,
|
||||
&QName::new(Namespace::public(), "prototype"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
let derive = class.read().instance_deriver();
|
||||
derive(base_class_constr, activation, class, scope)?
|
||||
derive(base_class_constr, base_proto, activation)?
|
||||
} else {
|
||||
ScriptObject::bare_prototype(activation.context.gc_context, class, scope)
|
||||
ScriptObject::bare_object(activation.context.gc_context)
|
||||
};
|
||||
|
||||
ClassObject::from_class_and_proto(activation, class, base_class_constr, class_proto, scope)
|
||||
|
@ -160,14 +169,11 @@ impl<'gc> ClassObject<'gc> {
|
|||
pub fn from_builtin_constr(
|
||||
mc: MutationContext<'gc, '_>,
|
||||
base_class_constr: Option<Object<'gc>>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
mut prototype: Object<'gc>,
|
||||
fn_proto: Object<'gc>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let scope = prototype.get_scope();
|
||||
let class: Result<_, Error> = prototype
|
||||
.as_class()
|
||||
.ok_or_else(|| "Cannot construct builtin type without a class".into());
|
||||
let class = class?;
|
||||
let instance_constr =
|
||||
Executable::from_method(class.read().instance_init(), scope, None, mc);
|
||||
let mut base: Object<'gc> = ClassObject(GcCell::allocate(
|
||||
|
@ -225,31 +231,38 @@ impl<'gc> TObject<'gc> for ClassObject<'gc> {
|
|||
receiver: Option<Object<'gc>>,
|
||||
arguments: &[Value<'gc>],
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
base_proto: Option<Object<'gc>>,
|
||||
base_constr: Option<Object<'gc>>,
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
let instance_constr = self.0.read().instance_constr;
|
||||
|
||||
instance_constr.exec(receiver, arguments, activation, base_proto, self.into())
|
||||
instance_constr.exec(receiver, arguments, activation, base_constr, self.into())
|
||||
}
|
||||
|
||||
fn construct(
|
||||
&self,
|
||||
mut self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_args: &[Value<'gc>],
|
||||
arguments: &[Value<'gc>],
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
Ok(ClassObject(GcCell::allocate(
|
||||
activation.context.gc_context,
|
||||
self.0.read().clone(),
|
||||
))
|
||||
.into())
|
||||
let class = self.as_class().ok_or("Cannot construct classless class!")?;
|
||||
let deriver = class.read().instance_deriver();
|
||||
let constr: Object<'gc> = self.into();
|
||||
let prototype = self
|
||||
.get_property(
|
||||
constr,
|
||||
&QName::new(Namespace::public(), "prototype"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
let instance = deriver(constr, prototype, activation)?;
|
||||
let instance_constr = self.0.read().instance_constr;
|
||||
|
||||
instance_constr.exec(Some(instance), arguments, activation, Some(constr), constr)?;
|
||||
|
||||
Ok(instance)
|
||||
}
|
||||
|
||||
fn derive(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_class: GcCell<'gc, Class<'gc>>,
|
||||
_scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
fn derive(&self, activation: &mut Activation<'_, 'gc, '_>) -> Result<Object<'gc>, Error> {
|
||||
Ok(ClassObject(GcCell::allocate(
|
||||
activation.context.gc_context,
|
||||
self.0.read().clone(),
|
||||
|
|
|
@ -132,18 +132,6 @@ macro_rules! impl_avm2_custom_object {
|
|||
self.0.read().$field.get_trait_slot(id)
|
||||
}
|
||||
|
||||
fn get_provided_trait(
|
||||
&self,
|
||||
name: &QName<'gc>,
|
||||
known_traits: &mut Vec<Trait<'gc>>,
|
||||
) -> Result<(), Error> {
|
||||
self.0.read().$field.get_provided_trait(name, known_traits)
|
||||
}
|
||||
|
||||
fn get_provided_trait_slot(&self, id: u32) -> Result<Option<Trait<'gc>>, Error> {
|
||||
self.0.read().$field.get_provided_trait_slot(id)
|
||||
}
|
||||
|
||||
fn get_scope(self) -> Option<GcCell<'gc, Scope<'gc>>> {
|
||||
self.0.read().$field.get_scope()
|
||||
}
|
||||
|
@ -152,10 +140,6 @@ macro_rules! impl_avm2_custom_object {
|
|||
self.0.read().$field.has_trait(name)
|
||||
}
|
||||
|
||||
fn provides_trait(self, name: &QName<'gc>) -> Result<bool, Error> {
|
||||
self.0.read().$field.provides_trait(name)
|
||||
}
|
||||
|
||||
fn has_instantiated_property(self, name: &QName<'gc>) -> bool {
|
||||
self.0.read().$field.has_instantiated_property(name)
|
||||
}
|
||||
|
@ -204,6 +188,14 @@ macro_rules! impl_avm2_custom_object {
|
|||
self.0.read().base.as_class()
|
||||
}
|
||||
|
||||
fn as_constr(&self) -> Option<Object<'gc>> {
|
||||
self.0.read().base.as_constr()
|
||||
}
|
||||
|
||||
fn set_constr(self, mc: MutationContext<'gc, '_>, constr: Object<'gc>) {
|
||||
self.0.write(mc).base.set_constr(constr);
|
||||
}
|
||||
|
||||
fn install_method(
|
||||
&mut self,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
|
|
|
@ -75,19 +75,14 @@ impl<'gc> TObject<'gc> for DispatchObject<'gc> {
|
|||
impl_avm2_custom_object_properties!(base);
|
||||
|
||||
fn construct(
|
||||
&self,
|
||||
self,
|
||||
_activation: &mut Activation<'_, 'gc, '_>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
Err("Cannot construct internal event dispatcher structures.".into())
|
||||
}
|
||||
|
||||
fn derive(
|
||||
&self,
|
||||
_activation: &mut Activation<'_, 'gc, '_>,
|
||||
_class: GcCell<'gc, Class<'gc>>,
|
||||
_scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
fn derive(&self, _activation: &mut Activation<'_, 'gc, '_>) -> Result<Object<'gc>, Error> {
|
||||
Err("Cannot subclass internal event dispatcher structures.".into())
|
||||
}
|
||||
|
||||
|
|
|
@ -16,32 +16,20 @@ use gc_arena::{Collect, GcCell, MutationContext};
|
|||
|
||||
/// A class instance deriver that constructs AppDomain objects.
|
||||
pub fn appdomain_deriver<'gc>(
|
||||
mut constr: Object<'gc>,
|
||||
constr: Object<'gc>,
|
||||
proto: Object<'gc>,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let scope = constr
|
||||
.get_scope()
|
||||
.ok_or("Constructor has an empty scope stack")?;
|
||||
let domain = scope
|
||||
.unwrap()
|
||||
.read()
|
||||
.globals()
|
||||
.as_application_domain()
|
||||
.unwrap();
|
||||
let base_proto = constr
|
||||
.get_property(
|
||||
constr,
|
||||
&QName::new(Namespace::public(), "prototype"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
.ok_or("Constructor scope must have an appdomain at the bottom of it's scope stack")?;
|
||||
|
||||
DomainObject::derive(
|
||||
activation.context.gc_context,
|
||||
base_proto,
|
||||
domain,
|
||||
class,
|
||||
scope,
|
||||
)
|
||||
DomainObject::derive(constr, proto, domain, activation.context.gc_context)
|
||||
}
|
||||
|
||||
#[derive(Clone, Collect, Debug, Copy)]
|
||||
|
@ -59,28 +47,37 @@ pub struct DomainObjectData<'gc> {
|
|||
}
|
||||
|
||||
impl<'gc> DomainObject<'gc> {
|
||||
/// Create a new domain without association with any class or prototype.
|
||||
///
|
||||
/// This should only be called during early player runtime initialization.
|
||||
/// It will return a `Domain` with no proto or instance constructor link,
|
||||
/// meaning that you will have to set those yourself.
|
||||
pub fn from_early_domain(mc: MutationContext<'gc, '_>, domain: Domain<'gc>) -> Object<'gc> {
|
||||
let base = ScriptObjectData::base_new(None, ScriptObjectClass::NoClass);
|
||||
|
||||
DomainObject(GcCell::allocate(mc, DomainObjectData { base, domain })).into()
|
||||
}
|
||||
|
||||
pub fn from_domain(
|
||||
mc: MutationContext<'gc, '_>,
|
||||
constr: Object<'gc>,
|
||||
base_proto: Option<Object<'gc>>,
|
||||
domain: Domain<'gc>,
|
||||
) -> Object<'gc> {
|
||||
let base = ScriptObjectData::base_new(base_proto, ScriptObjectClass::NoClass);
|
||||
let base = ScriptObjectData::base_new(base_proto, ScriptObjectClass::ClassInstance(constr));
|
||||
|
||||
DomainObject(GcCell::allocate(mc, DomainObjectData { base, domain })).into()
|
||||
}
|
||||
|
||||
/// Construct a primitive subclass.
|
||||
pub fn derive(
|
||||
mc: MutationContext<'gc, '_>,
|
||||
constr: Object<'gc>,
|
||||
base_proto: Object<'gc>,
|
||||
domain: Domain<'gc>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let base = ScriptObjectData::base_new(
|
||||
Some(base_proto),
|
||||
ScriptObjectClass::InstancePrototype(class, scope),
|
||||
);
|
||||
let base =
|
||||
ScriptObjectData::base_new(Some(base_proto), ScriptObjectClass::ClassInstance(constr));
|
||||
|
||||
Ok(DomainObject(GcCell::allocate(mc, DomainObjectData { base, domain })).into())
|
||||
}
|
||||
|
@ -100,40 +97,19 @@ impl<'gc> TObject<'gc> for DomainObject<'gc> {
|
|||
Ok(this.into())
|
||||
}
|
||||
|
||||
fn construct(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let this: Object<'gc> = Object::DomainObject(*self);
|
||||
let parent_domain = if let Some(parent_domain) = args
|
||||
.get(0)
|
||||
.cloned()
|
||||
.unwrap_or(Value::Undefined)
|
||||
.coerce_to_object(activation)?
|
||||
.as_application_domain()
|
||||
{
|
||||
parent_domain
|
||||
} else {
|
||||
activation.context.avm2.global_domain()
|
||||
};
|
||||
fn derive(&self, activation: &mut Activation<'_, 'gc, '_>) -> Result<Object<'gc>, Error> {
|
||||
let mut this: Object<'gc> = Object::DomainObject(*self);
|
||||
let constr = this
|
||||
.get_property(
|
||||
this,
|
||||
&QName::new(Namespace::public(), "constructor"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
Ok(DomainObject::from_domain(
|
||||
activation.context.gc_context,
|
||||
Some(this),
|
||||
Domain::movie_domain(activation.context.gc_context, parent_domain),
|
||||
))
|
||||
}
|
||||
|
||||
fn derive(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_class: GcCell<'gc, Class<'gc>>,
|
||||
_scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let this: Object<'gc> = Object::DomainObject(*self);
|
||||
Ok(DomainObject::from_domain(
|
||||
activation.context.gc_context,
|
||||
constr,
|
||||
Some(this),
|
||||
activation.context.avm2.global_domain(),
|
||||
))
|
||||
|
|
|
@ -17,24 +17,14 @@ use std::cell::{Ref, RefMut};
|
|||
|
||||
/// A class instance deriver that constructs Event objects.
|
||||
pub fn event_deriver<'gc>(
|
||||
mut constr: Object<'gc>,
|
||||
constr: Object<'gc>,
|
||||
proto: Object<'gc>,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let base_proto = constr
|
||||
.get_property(
|
||||
constr,
|
||||
&QName::new(Namespace::public(), "prototype"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
Ok(EventObject::derive(
|
||||
base_proto,
|
||||
constr,
|
||||
proto,
|
||||
activation.context.gc_context,
|
||||
class,
|
||||
scope,
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -56,25 +46,23 @@ impl<'gc> EventObject<'gc> {
|
|||
/// Convert a bare event into it's object representation.
|
||||
pub fn from_event(
|
||||
mc: MutationContext<'gc, '_>,
|
||||
constr: Object<'gc>,
|
||||
base_proto: Option<Object<'gc>>,
|
||||
event: Event<'gc>,
|
||||
) -> Object<'gc> {
|
||||
let base = ScriptObjectData::base_new(base_proto, ScriptObjectClass::NoClass);
|
||||
let base = ScriptObjectData::base_new(base_proto, ScriptObjectClass::ClassInstance(constr));
|
||||
|
||||
EventObject(GcCell::allocate(mc, EventObjectData { base, event })).into()
|
||||
}
|
||||
|
||||
/// Instantiate an event subclass.
|
||||
pub fn derive(
|
||||
constr: Object<'gc>,
|
||||
base_proto: Object<'gc>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Object<'gc> {
|
||||
let base = ScriptObjectData::base_new(
|
||||
Some(base_proto),
|
||||
ScriptObjectClass::InstancePrototype(class, scope),
|
||||
);
|
||||
let base =
|
||||
ScriptObjectData::base_new(Some(base_proto), ScriptObjectClass::ClassInstance(constr));
|
||||
|
||||
EventObject(GcCell::allocate(
|
||||
mc,
|
||||
|
@ -91,33 +79,17 @@ impl<'gc> TObject<'gc> for EventObject<'gc> {
|
|||
impl_avm2_custom_object!(base);
|
||||
impl_avm2_custom_object_properties!(base);
|
||||
|
||||
fn construct(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let this: Object<'gc> = Object::EventObject(*self);
|
||||
Ok(EventObject::from_event(
|
||||
activation.context.gc_context,
|
||||
Some(this),
|
||||
Event::new(""),
|
||||
))
|
||||
}
|
||||
fn derive(&self, activation: &mut Activation<'_, 'gc, '_>) -> Result<Object<'gc>, Error> {
|
||||
let base = ScriptObjectData::base_new(Some((*self).into()), ScriptObjectClass::NoClass);
|
||||
|
||||
fn derive(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let this: Object<'gc> = Object::EventObject(*self);
|
||||
|
||||
Ok(Self::derive(
|
||||
this,
|
||||
Ok(EventObject(GcCell::allocate(
|
||||
activation.context.gc_context,
|
||||
class,
|
||||
scope,
|
||||
EventObjectData {
|
||||
base,
|
||||
event: Event::new(""),
|
||||
},
|
||||
))
|
||||
.into())
|
||||
}
|
||||
|
||||
fn value_of(&self, mc: MutationContext<'gc, '_>) -> Result<Value<'gc>, Error> {
|
||||
|
|
|
@ -100,20 +100,37 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
|||
receiver: Option<Object<'gc>>,
|
||||
arguments: &[Value<'gc>],
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
base_proto: Option<Object<'gc>>,
|
||||
base_constr: Option<Object<'gc>>,
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
if let Some(exec) = &self.0.read().exec {
|
||||
exec.exec(receiver, arguments, activation, base_proto, self.into())
|
||||
exec.exec(receiver, arguments, activation, base_constr, self.into())
|
||||
} else {
|
||||
Err("Not a callable function!".into())
|
||||
}
|
||||
}
|
||||
|
||||
fn construct(
|
||||
&self,
|
||||
mut self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_args: &[Value<'gc>],
|
||||
arguments: &[Value<'gc>],
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let constr: Object<'gc> = self.into();
|
||||
let prototype = self
|
||||
.get_property(
|
||||
constr,
|
||||
&QName::new(Namespace::public(), "prototype"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
let instance = prototype.derive(activation)?;
|
||||
|
||||
self.call(Some(instance), arguments, activation, None)?;
|
||||
|
||||
Ok(instance)
|
||||
}
|
||||
|
||||
fn derive(&self, activation: &mut Activation<'_, 'gc, '_>) -> Result<Object<'gc>, Error> {
|
||||
let this: Object<'gc> = Object::FunctionObject(*self);
|
||||
let base = ScriptObjectData::base_new(Some(this), ScriptObjectClass::NoClass);
|
||||
|
||||
|
@ -123,23 +140,4 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
|||
))
|
||||
.into())
|
||||
}
|
||||
|
||||
fn derive(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let this: Object<'gc> = Object::FunctionObject(*self);
|
||||
let base = ScriptObjectData::base_new(
|
||||
Some(this),
|
||||
ScriptObjectClass::InstancePrototype(class, scope),
|
||||
);
|
||||
|
||||
Ok(FunctionObject(GcCell::allocate(
|
||||
activation.context.gc_context,
|
||||
FunctionObjectData { base, exec: None },
|
||||
))
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,20 +19,11 @@ use std::sync::Arc;
|
|||
|
||||
/// A class instance deriver that constructs LoaderInfo objects.
|
||||
pub fn loaderinfo_deriver<'gc>(
|
||||
mut constr: Object<'gc>,
|
||||
constr: Object<'gc>,
|
||||
proto: Object<'gc>,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let base_proto = constr
|
||||
.get_property(
|
||||
constr,
|
||||
&QName::new(Namespace::public(), "prototype"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
LoaderInfoObject::derive(base_proto, activation.context.gc_context, class, scope)
|
||||
LoaderInfoObject::derive(constr, proto, activation.context.gc_context)
|
||||
}
|
||||
|
||||
/// Represents a thing which can be loaded by a loader.
|
||||
|
@ -73,10 +64,12 @@ impl<'gc> LoaderInfoObject<'gc> {
|
|||
pub fn from_movie(
|
||||
movie: Arc<SwfMovie>,
|
||||
root: DisplayObject<'gc>,
|
||||
constr: Object<'gc>,
|
||||
base_proto: Object<'gc>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let base = ScriptObjectData::base_new(Some(base_proto), ScriptObjectClass::NoClass);
|
||||
let base =
|
||||
ScriptObjectData::base_new(Some(base_proto), ScriptObjectClass::ClassInstance(constr));
|
||||
let loaded_stream = Some(LoaderStream::Swf(movie, root));
|
||||
|
||||
Ok(LoaderInfoObject(GcCell::allocate(
|
||||
|
@ -90,8 +83,13 @@ impl<'gc> LoaderInfoObject<'gc> {
|
|||
}
|
||||
|
||||
/// Create a loader info object for the stage.
|
||||
pub fn from_stage(base_proto: Object<'gc>, mc: MutationContext<'gc, '_>) -> Object<'gc> {
|
||||
let base = ScriptObjectData::base_new(Some(base_proto), ScriptObjectClass::NoClass);
|
||||
pub fn from_stage(
|
||||
constr: Object<'gc>,
|
||||
base_proto: Object<'gc>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
) -> Object<'gc> {
|
||||
let base =
|
||||
ScriptObjectData::base_new(Some(base_proto), ScriptObjectClass::ClassInstance(constr));
|
||||
|
||||
LoaderInfoObject(GcCell::allocate(
|
||||
mc,
|
||||
|
@ -105,15 +103,12 @@ impl<'gc> LoaderInfoObject<'gc> {
|
|||
|
||||
/// Construct a loader-info subclass.
|
||||
pub fn derive(
|
||||
constr: Object<'gc>,
|
||||
base_proto: Object<'gc>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let base = ScriptObjectData::base_new(
|
||||
Some(base_proto),
|
||||
ScriptObjectClass::InstancePrototype(class, scope),
|
||||
);
|
||||
let base =
|
||||
ScriptObjectData::base_new(Some(base_proto), ScriptObjectClass::ClassInstance(constr));
|
||||
|
||||
Ok(LoaderInfoObject(GcCell::allocate(
|
||||
mc,
|
||||
|
@ -131,18 +126,14 @@ impl<'gc> TObject<'gc> for LoaderInfoObject<'gc> {
|
|||
impl_avm2_custom_object_properties!(base);
|
||||
|
||||
fn value_of(&self, mc: MutationContext<'gc, '_>) -> Result<Value<'gc>, Error> {
|
||||
if let Some(class) = self.as_proto_class() {
|
||||
if let Some(class) = self.as_class() {
|
||||
Ok(AvmString::new(mc, format!("[object {}]", class.read().name().local_name())).into())
|
||||
} else {
|
||||
Ok("[object Object]".into())
|
||||
}
|
||||
}
|
||||
|
||||
fn construct(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
fn derive(&self, activation: &mut Activation<'_, 'gc, '_>) -> Result<Object<'gc>, Error> {
|
||||
let this: Object<'gc> = Object::LoaderInfoObject(*self);
|
||||
let base = ScriptObjectData::base_new(Some(this), ScriptObjectClass::NoClass);
|
||||
|
||||
|
@ -156,28 +147,6 @@ impl<'gc> TObject<'gc> for LoaderInfoObject<'gc> {
|
|||
.into())
|
||||
}
|
||||
|
||||
fn derive(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let this: Object<'gc> = Object::LoaderInfoObject(*self);
|
||||
let base = ScriptObjectData::base_new(
|
||||
Some(this),
|
||||
ScriptObjectClass::InstancePrototype(class, scope),
|
||||
);
|
||||
|
||||
Ok(LoaderInfoObject(GcCell::allocate(
|
||||
activation.context.gc_context,
|
||||
LoaderInfoObjectData {
|
||||
base,
|
||||
loaded_stream: None,
|
||||
},
|
||||
))
|
||||
.into())
|
||||
}
|
||||
|
||||
/// Unwrap this object's loader stream
|
||||
fn as_loader_stream(&self) -> Option<Ref<LoaderStream<'gc>>> {
|
||||
if self.0.read().loaded_stream.is_some() {
|
||||
|
|
|
@ -16,20 +16,11 @@ use std::cell::Ref;
|
|||
|
||||
/// A class instance deriver that constructs namespace objects.
|
||||
pub fn namespace_deriver<'gc>(
|
||||
mut constr: Object<'gc>,
|
||||
constr: Object<'gc>,
|
||||
proto: Object<'gc>,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let base_proto = constr
|
||||
.get_property(
|
||||
constr,
|
||||
&QName::new(Namespace::public(), "prototype"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
NamespaceObject::derive(base_proto, activation.context.gc_context, class, scope)
|
||||
NamespaceObject::derive(constr, proto, activation.context.gc_context)
|
||||
}
|
||||
|
||||
/// An Object which represents a boxed namespace name.
|
||||
|
@ -51,10 +42,12 @@ impl<'gc> NamespaceObject<'gc> {
|
|||
/// Box a namespace into an object.
|
||||
pub fn from_namespace(
|
||||
namespace: Namespace<'gc>,
|
||||
constr: Object<'gc>,
|
||||
base_proto: Object<'gc>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let base = ScriptObjectData::base_new(Some(base_proto), ScriptObjectClass::NoClass);
|
||||
let base =
|
||||
ScriptObjectData::base_new(Some(base_proto), ScriptObjectClass::ClassInstance(constr));
|
||||
|
||||
Ok(NamespaceObject(GcCell::allocate(
|
||||
mc,
|
||||
|
@ -65,15 +58,12 @@ impl<'gc> NamespaceObject<'gc> {
|
|||
|
||||
/// Construct a namespace subclass.
|
||||
pub fn derive(
|
||||
constr: Object<'gc>,
|
||||
base_proto: Object<'gc>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let base = ScriptObjectData::base_new(
|
||||
Some(base_proto),
|
||||
ScriptObjectClass::InstancePrototype(class, scope),
|
||||
);
|
||||
let base =
|
||||
ScriptObjectData::base_new(Some(base_proto), ScriptObjectClass::ClassInstance(constr));
|
||||
|
||||
Ok(NamespaceObject(GcCell::allocate(
|
||||
mc,
|
||||
|
@ -102,11 +92,7 @@ impl<'gc> TObject<'gc> for NamespaceObject<'gc> {
|
|||
Some(Ref::map(self.0.read(), |s| &s.namespace))
|
||||
}
|
||||
|
||||
fn construct(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
fn derive(&self, activation: &mut Activation<'_, 'gc, '_>) -> Result<Object<'gc>, Error> {
|
||||
let this: Object<'gc> = Object::NamespaceObject(*self);
|
||||
let base = ScriptObjectData::base_new(Some(this), ScriptObjectClass::NoClass);
|
||||
|
||||
|
@ -119,26 +105,4 @@ impl<'gc> TObject<'gc> for NamespaceObject<'gc> {
|
|||
))
|
||||
.into())
|
||||
}
|
||||
|
||||
fn derive(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let this: Object<'gc> = Object::NamespaceObject(*self);
|
||||
let base = ScriptObjectData::base_new(
|
||||
Some(this),
|
||||
ScriptObjectClass::InstancePrototype(class, scope),
|
||||
);
|
||||
|
||||
Ok(NamespaceObject(GcCell::allocate(
|
||||
activation.context.gc_context,
|
||||
NamespaceObjectData {
|
||||
base,
|
||||
namespace: Namespace::public(),
|
||||
},
|
||||
))
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,20 +17,11 @@ use gc_arena::{Collect, GcCell, MutationContext};
|
|||
|
||||
/// A class instance deriver that constructs primitive objects.
|
||||
pub fn primitive_deriver<'gc>(
|
||||
mut constr: Object<'gc>,
|
||||
constr: Object<'gc>,
|
||||
proto: Object<'gc>,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let base_proto = constr
|
||||
.get_property(
|
||||
constr,
|
||||
&QName::new(Namespace::public(), "prototype"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
PrimitiveObject::derive(base_proto, activation.context.gc_context, class, scope)
|
||||
PrimitiveObject::derive(constr, proto, activation.context.gc_context)
|
||||
}
|
||||
|
||||
/// An Object which represents a primitive value of some other kind.
|
||||
|
@ -52,6 +43,7 @@ impl<'gc> PrimitiveObject<'gc> {
|
|||
/// Box a primitive into an object.
|
||||
pub fn from_primitive(
|
||||
primitive: Value<'gc>,
|
||||
constr: Object<'gc>,
|
||||
base_proto: Object<'gc>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
|
@ -59,7 +51,8 @@ impl<'gc> PrimitiveObject<'gc> {
|
|||
return Err("Attempted to box an object as a primitive".into());
|
||||
}
|
||||
|
||||
let base = ScriptObjectData::base_new(Some(base_proto), ScriptObjectClass::NoClass);
|
||||
let base =
|
||||
ScriptObjectData::base_new(Some(base_proto), ScriptObjectClass::ClassInstance(constr));
|
||||
|
||||
Ok(PrimitiveObject(GcCell::allocate(
|
||||
mc,
|
||||
|
@ -70,15 +63,12 @@ impl<'gc> PrimitiveObject<'gc> {
|
|||
|
||||
/// Construct a primitive subclass.
|
||||
pub fn derive(
|
||||
constr: Object<'gc>,
|
||||
base_proto: Object<'gc>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let base = ScriptObjectData::base_new(
|
||||
Some(base_proto),
|
||||
ScriptObjectClass::InstancePrototype(class, scope),
|
||||
);
|
||||
let base =
|
||||
ScriptObjectData::base_new(Some(base_proto), ScriptObjectClass::ClassInstance(constr));
|
||||
|
||||
Ok(PrimitiveObject(GcCell::allocate(
|
||||
mc,
|
||||
|
@ -104,7 +94,7 @@ impl<'gc> TObject<'gc> for PrimitiveObject<'gc> {
|
|||
val @ Value::Integer(_) | val @ Value::Unsigned(_) => Ok(val),
|
||||
_ => {
|
||||
let class_name = self
|
||||
.as_proto_class()
|
||||
.as_class()
|
||||
.map(|c| c.read().name().local_name())
|
||||
.unwrap_or_else(|| "Object".into());
|
||||
|
||||
|
@ -117,11 +107,7 @@ impl<'gc> TObject<'gc> for PrimitiveObject<'gc> {
|
|||
Ok(self.0.read().primitive.clone())
|
||||
}
|
||||
|
||||
fn construct(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
fn derive(&self, activation: &mut Activation<'_, 'gc, '_>) -> Result<Object<'gc>, Error> {
|
||||
let this: Object<'gc> = Object::PrimitiveObject(*self);
|
||||
let base = ScriptObjectData::base_new(Some(this), ScriptObjectClass::NoClass);
|
||||
|
||||
|
@ -135,28 +121,6 @@ impl<'gc> TObject<'gc> for PrimitiveObject<'gc> {
|
|||
.into())
|
||||
}
|
||||
|
||||
fn derive(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let this: Object<'gc> = Object::PrimitiveObject(*self);
|
||||
let base = ScriptObjectData::base_new(
|
||||
Some(this),
|
||||
ScriptObjectClass::InstancePrototype(class, scope),
|
||||
);
|
||||
|
||||
Ok(PrimitiveObject(GcCell::allocate(
|
||||
activation.context.gc_context,
|
||||
PrimitiveObjectData {
|
||||
base,
|
||||
primitive: Value::Undefined,
|
||||
},
|
||||
))
|
||||
.into())
|
||||
}
|
||||
|
||||
fn as_primitive_mut(&self, mc: MutationContext<'gc, '_>) -> Option<RefMut<Value<'gc>>> {
|
||||
Some(RefMut::map(self.0.write(mc), |pod| &mut pod.primitive))
|
||||
}
|
||||
|
|
|
@ -17,24 +17,14 @@ use std::cell::{Ref, RefMut};
|
|||
|
||||
/// A class instance deriver that constructs RegExp objects.
|
||||
pub fn regexp_deriver<'gc>(
|
||||
mut constr: Object<'gc>,
|
||||
constr: Object<'gc>,
|
||||
proto: Object<'gc>,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let base_proto = constr
|
||||
.get_property(
|
||||
constr,
|
||||
&QName::new(Namespace::public(), "prototype"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
Ok(RegExpObject::derive(
|
||||
base_proto,
|
||||
constr,
|
||||
proto,
|
||||
activation.context.gc_context,
|
||||
class,
|
||||
scope,
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -54,25 +44,23 @@ pub struct RegExpObjectData<'gc> {
|
|||
impl<'gc> RegExpObject<'gc> {
|
||||
pub fn from_regexp(
|
||||
mc: MutationContext<'gc, '_>,
|
||||
constr: Object<'gc>,
|
||||
base_proto: Option<Object<'gc>>,
|
||||
regexp: RegExp<'gc>,
|
||||
) -> Object<'gc> {
|
||||
let base = ScriptObjectData::base_new(base_proto, ScriptObjectClass::NoClass);
|
||||
let base = ScriptObjectData::base_new(base_proto, ScriptObjectClass::ClassInstance(constr));
|
||||
|
||||
RegExpObject(GcCell::allocate(mc, RegExpObjectData { base, regexp })).into()
|
||||
}
|
||||
|
||||
/// Instantiate a regexp subclass.
|
||||
pub fn derive(
|
||||
constr: Object<'gc>,
|
||||
base_proto: Object<'gc>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Object<'gc> {
|
||||
let base = ScriptObjectData::base_new(
|
||||
Some(base_proto),
|
||||
ScriptObjectClass::InstancePrototype(class, scope),
|
||||
);
|
||||
let base =
|
||||
ScriptObjectData::base_new(Some(base_proto), ScriptObjectClass::ClassInstance(constr));
|
||||
|
||||
RegExpObject(GcCell::allocate(
|
||||
mc,
|
||||
|
@ -89,32 +77,17 @@ impl<'gc> TObject<'gc> for RegExpObject<'gc> {
|
|||
impl_avm2_custom_object!(base);
|
||||
impl_avm2_custom_object_properties!(base);
|
||||
|
||||
fn construct(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let this: Object<'gc> = Object::RegExpObject(*self);
|
||||
Ok(RegExpObject::from_regexp(
|
||||
activation.context.gc_context,
|
||||
Some(this),
|
||||
RegExp::new(""),
|
||||
))
|
||||
}
|
||||
fn derive(&self, activation: &mut Activation<'_, 'gc, '_>) -> Result<Object<'gc>, Error> {
|
||||
let base = ScriptObjectData::base_new(Some((*self).into()), ScriptObjectClass::NoClass);
|
||||
|
||||
fn derive(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let this: Object<'gc> = Object::RegExpObject(*self);
|
||||
Ok(Self::derive(
|
||||
this,
|
||||
Ok(RegExpObject(GcCell::allocate(
|
||||
activation.context.gc_context,
|
||||
class,
|
||||
scope,
|
||||
RegExpObjectData {
|
||||
base,
|
||||
regexp: RegExp::new(""),
|
||||
},
|
||||
))
|
||||
.into())
|
||||
}
|
||||
|
||||
fn to_string(&self, _mc: MutationContext<'gc, '_>) -> Result<Value<'gc>, Error> {
|
||||
|
|
|
@ -33,12 +33,12 @@ pub struct ScriptObject<'gc>(GcCell<'gc, ScriptObjectData<'gc>>);
|
|||
#[derive(Clone, Collect, Debug)]
|
||||
#[collect(no_drop)]
|
||||
pub enum ScriptObjectClass<'gc> {
|
||||
/// Instantiate instance traits, for prototypes.
|
||||
InstancePrototype(GcCell<'gc, Class<'gc>>, Option<GcCell<'gc, Scope<'gc>>>),
|
||||
|
||||
/// Instantiate class traits, for class constructors.
|
||||
ClassConstructor(GcCell<'gc, Class<'gc>>, Option<GcCell<'gc, Scope<'gc>>>),
|
||||
|
||||
/// Instantiate instance traits, for class instances.
|
||||
ClassInstance(Object<'gc>),
|
||||
|
||||
/// Do not instantiate any class or instance traits.
|
||||
NoClass,
|
||||
}
|
||||
|
@ -172,18 +172,6 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
|||
self.0.read().get_trait_slot(id)
|
||||
}
|
||||
|
||||
fn get_provided_trait(
|
||||
&self,
|
||||
name: &QName<'gc>,
|
||||
known_traits: &mut Vec<Trait<'gc>>,
|
||||
) -> Result<(), Error> {
|
||||
self.0.read().get_provided_trait(name, known_traits)
|
||||
}
|
||||
|
||||
fn get_provided_trait_slot(&self, id: u32) -> Result<Option<Trait<'gc>>, Error> {
|
||||
self.0.read().get_provided_trait_slot(id)
|
||||
}
|
||||
|
||||
fn get_scope(self) -> Option<GcCell<'gc, Scope<'gc>>> {
|
||||
self.0.read().get_scope()
|
||||
}
|
||||
|
@ -207,10 +195,6 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
|||
self.0.read().has_trait(name)
|
||||
}
|
||||
|
||||
fn provides_trait(self, name: &QName<'gc>) -> Result<bool, Error> {
|
||||
self.0.read().provides_trait(name)
|
||||
}
|
||||
|
||||
fn has_instantiated_property(self, name: &QName<'gc>) -> bool {
|
||||
self.0.read().has_instantiated_property(name)
|
||||
}
|
||||
|
@ -254,32 +238,13 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
|||
self.0.as_ptr() as *const ObjectPtr
|
||||
}
|
||||
|
||||
fn construct(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
fn derive(&self, activation: &mut Activation<'_, 'gc, '_>) -> Result<Object<'gc>, Error> {
|
||||
let this: Object<'gc> = Object::ScriptObject(*self);
|
||||
Ok(ScriptObject::object(activation.context.gc_context, this))
|
||||
}
|
||||
|
||||
fn derive(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let this: Object<'gc> = Object::ScriptObject(*self);
|
||||
Ok(ScriptObject::prototype(
|
||||
activation.context.gc_context,
|
||||
this,
|
||||
class,
|
||||
scope,
|
||||
))
|
||||
}
|
||||
|
||||
fn to_string(&self, mc: MutationContext<'gc, '_>) -> Result<Value<'gc>, Error> {
|
||||
if let Some(class) = self.as_proto_class() {
|
||||
if let Some(class) = self.as_class() {
|
||||
Ok(AvmString::new(mc, format!("[object {}]", class.read().name().local_name())).into())
|
||||
} else {
|
||||
Ok("[object Object]".into())
|
||||
|
@ -360,6 +325,14 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
|||
fn as_class(&self) -> Option<GcCell<'gc, Class<'gc>>> {
|
||||
self.0.read().as_class()
|
||||
}
|
||||
|
||||
fn as_constr(&self) -> Option<Object<'gc>> {
|
||||
self.0.read().as_constr()
|
||||
}
|
||||
|
||||
fn set_constr(self, mc: MutationContext<'gc, '_>, constr: Object<'gc>) {
|
||||
self.0.write(mc).set_constr(constr);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> ScriptObject<'gc> {
|
||||
|
@ -375,26 +348,6 @@ impl<'gc> ScriptObject<'gc> {
|
|||
.into()
|
||||
}
|
||||
|
||||
/// Construct a bare class prototype with no base class.
|
||||
///
|
||||
/// This is used in cases where a prototype needs to exist, but it does not
|
||||
/// 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(
|
||||
mc: MutationContext<'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Object<'gc> {
|
||||
let script_class = ScriptObjectClass::InstancePrototype(class, scope);
|
||||
|
||||
ScriptObject(GcCell::allocate(
|
||||
mc,
|
||||
ScriptObjectData::base_new(None, script_class),
|
||||
))
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Construct an object with a prototype.
|
||||
pub fn object(mc: MutationContext<'gc, '_>, proto: Object<'gc>) -> Object<'gc> {
|
||||
ScriptObject(GcCell::allocate(
|
||||
|
@ -404,18 +357,15 @@ impl<'gc> ScriptObject<'gc> {
|
|||
.into()
|
||||
}
|
||||
|
||||
/// Construct a prototype for an ES4 class.
|
||||
pub fn prototype(
|
||||
/// Construct an instance with a class and scope stack.
|
||||
pub fn instance(
|
||||
mc: MutationContext<'gc, '_>,
|
||||
constr: Object<'gc>,
|
||||
proto: Object<'gc>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Object<'gc> {
|
||||
let script_class = ScriptObjectClass::InstancePrototype(class, scope);
|
||||
|
||||
ScriptObject(GcCell::allocate(
|
||||
mc,
|
||||
ScriptObjectData::base_new(Some(proto), script_class),
|
||||
ScriptObjectData::base_new(Some(proto), ScriptObjectClass::ClassInstance(constr)),
|
||||
))
|
||||
.into()
|
||||
}
|
||||
|
@ -443,7 +393,15 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
let prop = self.values.get(name);
|
||||
|
||||
if let Some(prop) = prop {
|
||||
prop.get(receiver, activation.base_proto().or(self.proto))
|
||||
prop.get(
|
||||
receiver,
|
||||
Some(
|
||||
activation
|
||||
.base_constr()
|
||||
.or_else(|| self.as_constr())
|
||||
.unwrap_or(receiver),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
Ok(Value::Undefined.into())
|
||||
}
|
||||
|
@ -456,6 +414,7 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
value: Value<'gc>,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
let constr = self.as_constr();
|
||||
let slot_id = if let Some(prop) = self.values.get(name) {
|
||||
if let Some(slot_id) = prop.slot_id() {
|
||||
Some(slot_id)
|
||||
|
@ -473,8 +432,11 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
Ok(Value::Undefined.into())
|
||||
} else if self.values.contains_key(name) {
|
||||
let prop = self.values.get_mut(name).unwrap();
|
||||
let proto = self.proto;
|
||||
prop.set(receiver, activation.base_proto().or(proto), value)
|
||||
prop.set(
|
||||
receiver,
|
||||
Some(activation.base_constr().or(constr).unwrap_or(receiver)),
|
||||
value,
|
||||
)
|
||||
} else {
|
||||
//TODO: Not all classes are dynamic like this
|
||||
self.enumerants.push(name.clone());
|
||||
|
@ -492,6 +454,7 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
value: Value<'gc>,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
let constr = self.as_constr();
|
||||
if let Some(prop) = self.values.get_mut(name) {
|
||||
if let Some(slot_id) = prop.slot_id() {
|
||||
// This doesn't need the non-local version of this property
|
||||
|
@ -500,8 +463,11 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
self.init_slot_local(slot_id, value, activation.context.gc_context)?;
|
||||
Ok(Value::Undefined.into())
|
||||
} else {
|
||||
let proto = self.proto;
|
||||
prop.init(receiver, activation.base_proto().or(proto), value)
|
||||
prop.init(
|
||||
receiver,
|
||||
Some(activation.base_constr().or(constr).unwrap_or(receiver)),
|
||||
value,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
//TODO: Not all classes are dynamic like this
|
||||
|
@ -584,89 +550,67 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
pub fn get_trait(&self, name: &QName<'gc>) -> Result<Vec<Trait<'gc>>, Error> {
|
||||
match &self.class {
|
||||
//Class constructors have local traits only.
|
||||
ScriptObjectClass::ClassConstructor(..) => {
|
||||
ScriptObjectClass::ClassConstructor(class, ..) => {
|
||||
let mut known_traits = Vec::new();
|
||||
self.get_provided_trait(name, &mut known_traits)?;
|
||||
class.read().lookup_class_traits(name, &mut known_traits)?;
|
||||
|
||||
Ok(known_traits)
|
||||
}
|
||||
|
||||
//Prototypes do not have traits available locally, but they provide
|
||||
//traits instead.
|
||||
ScriptObjectClass::InstancePrototype(..) => Ok(Vec::new()),
|
||||
//Class instances have all instance traits from all superclasses.
|
||||
ScriptObjectClass::ClassInstance(constr) => {
|
||||
let mut constr_list = Vec::new();
|
||||
let mut cur_constr = Some(*constr);
|
||||
while let Some(constr) = cur_constr {
|
||||
constr_list.push(constr);
|
||||
|
||||
//Instances walk the prototype chain to build a list of known
|
||||
//traits provided by the classes attached to those prototypes.
|
||||
ScriptObjectClass::NoClass => {
|
||||
let mut known_traits = Vec::new();
|
||||
let mut chain = Vec::new();
|
||||
let mut proto = self.proto();
|
||||
|
||||
while let Some(p) = proto {
|
||||
chain.push(p);
|
||||
proto = p.proto();
|
||||
cur_constr = constr.base_class_constr();
|
||||
}
|
||||
|
||||
for proto in chain.iter().rev() {
|
||||
proto.get_provided_trait(name, &mut known_traits)?;
|
||||
let mut known_traits = Vec::new();
|
||||
for constr in constr_list.iter().rev() {
|
||||
let cur_class = constr
|
||||
.as_class()
|
||||
.ok_or("Object is not a class constructor")?;
|
||||
cur_class
|
||||
.read()
|
||||
.lookup_instance_traits(name, &mut known_traits)?;
|
||||
}
|
||||
|
||||
Ok(known_traits)
|
||||
}
|
||||
|
||||
// Bare objects, ES3 objects, and prototypes do not have traits.
|
||||
ScriptObjectClass::NoClass => Ok(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_trait_slot(&self, id: u32) -> Result<Option<Trait<'gc>>, Error> {
|
||||
match &self.class {
|
||||
//Class constructors have local slot traits only.
|
||||
ScriptObjectClass::ClassConstructor(..) => self.get_provided_trait_slot(id),
|
||||
|
||||
//Prototypes do not have traits available locally, but they provide
|
||||
//traits instead.
|
||||
ScriptObjectClass::InstancePrototype(..) => Ok(None),
|
||||
|
||||
//Instances walk the prototype chain to build a list of known
|
||||
//traits provided by the classes attached to those prototypes.
|
||||
ScriptObjectClass::NoClass => {
|
||||
let mut proto = self.proto();
|
||||
|
||||
while let Some(p) = proto {
|
||||
if let Some(trait_val) = p.get_provided_trait_slot(id)? {
|
||||
return Ok(Some(trait_val));
|
||||
ScriptObjectClass::ClassConstructor(class, ..) => {
|
||||
class.read().lookup_class_traits_by_slot(id)
|
||||
}
|
||||
|
||||
proto = p.proto();
|
||||
//Class instances have all instance slot traits from all superclasses.
|
||||
ScriptObjectClass::ClassInstance(constr) => {
|
||||
let mut cur_constr = Some(*constr);
|
||||
|
||||
while let Some(constr) = cur_constr {
|
||||
let cur_class = constr
|
||||
.as_class()
|
||||
.ok_or("Object is not a class constructor")?;
|
||||
if let Some(inst_trait) = cur_class.read().lookup_instance_traits_by_slot(id)? {
|
||||
return Ok(Some(inst_trait));
|
||||
}
|
||||
|
||||
cur_constr = constr.base_class_constr();
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_provided_trait(
|
||||
&self,
|
||||
name: &QName<'gc>,
|
||||
known_traits: &mut Vec<Trait<'gc>>,
|
||||
) -> Result<(), Error> {
|
||||
match &self.class {
|
||||
ScriptObjectClass::ClassConstructor(class, ..) => {
|
||||
class.read().lookup_class_traits(name, known_traits)
|
||||
}
|
||||
ScriptObjectClass::InstancePrototype(class, ..) => {
|
||||
class.read().lookup_instance_traits(name, known_traits)
|
||||
}
|
||||
ScriptObjectClass::NoClass => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_provided_trait_slot(&self, id: u32) -> Result<Option<Trait<'gc>>, Error> {
|
||||
match &self.class {
|
||||
ScriptObjectClass::ClassConstructor(class, ..) => {
|
||||
class.read().lookup_class_traits_by_slot(id)
|
||||
}
|
||||
ScriptObjectClass::InstancePrototype(class, ..) => {
|
||||
class.read().lookup_instance_traits_by_slot(id)
|
||||
}
|
||||
// Bare objects, ES3 objects, and prototypes do not have traits.
|
||||
ScriptObjectClass::NoClass => Ok(None),
|
||||
}
|
||||
}
|
||||
|
@ -674,38 +618,30 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
pub fn has_trait(&self, name: &QName<'gc>) -> Result<bool, Error> {
|
||||
match &self.class {
|
||||
//Class constructors have local traits only.
|
||||
ScriptObjectClass::ClassConstructor(..) => self.provides_trait(name),
|
||||
ScriptObjectClass::ClassConstructor(class, ..) => {
|
||||
Ok(class.read().has_class_trait(name))
|
||||
}
|
||||
|
||||
//Prototypes do not have traits available locally, but we walk
|
||||
//through them to find traits (see `provides_trait`)
|
||||
ScriptObjectClass::InstancePrototype(..) => Ok(false),
|
||||
//Class instances have instance traits from any class in the base
|
||||
//class chain.
|
||||
ScriptObjectClass::ClassInstance(constr) => {
|
||||
let mut cur_constr = Some(*constr);
|
||||
|
||||
//Instances walk the prototype chain to build a list of known
|
||||
//traits provided by the classes attached to those prototypes.
|
||||
ScriptObjectClass::NoClass => {
|
||||
let mut proto = self.proto();
|
||||
|
||||
while let Some(p) = proto {
|
||||
if p.provides_trait(name)? {
|
||||
while let Some(constr) = cur_constr {
|
||||
let cur_class = constr
|
||||
.as_class()
|
||||
.ok_or("Object is not a class constructor")?;
|
||||
if cur_class.read().has_instance_trait(name) {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
proto = p.proto();
|
||||
cur_constr = constr.base_class_constr();
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn provides_trait(&self, name: &QName<'gc>) -> Result<bool, Error> {
|
||||
match &self.class {
|
||||
ScriptObjectClass::ClassConstructor(class, ..) => {
|
||||
Ok(class.read().has_class_trait(name))
|
||||
}
|
||||
ScriptObjectClass::InstancePrototype(class, ..) => {
|
||||
Ok(class.read().has_instance_trait(name))
|
||||
}
|
||||
// Bare objects, ES3 objects, and prototypes do not have traits.
|
||||
ScriptObjectClass::NoClass => Ok(false),
|
||||
}
|
||||
}
|
||||
|
@ -713,8 +649,8 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
pub fn get_scope(&self) -> Option<GcCell<'gc, Scope<'gc>>> {
|
||||
match &self.class {
|
||||
ScriptObjectClass::ClassConstructor(_class, scope) => *scope,
|
||||
ScriptObjectClass::InstancePrototype(_class, scope) => *scope,
|
||||
ScriptObjectClass::NoClass => self.proto().and_then(|proto| proto.get_scope()),
|
||||
ScriptObjectClass::ClassInstance(constr) => constr.get_scope(),
|
||||
ScriptObjectClass::NoClass => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -725,11 +661,7 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
}
|
||||
}
|
||||
|
||||
let trait_ns = match self.class {
|
||||
ScriptObjectClass::ClassConstructor(..) => self.resolve_any_trait(local_name)?,
|
||||
ScriptObjectClass::NoClass => self.resolve_any_trait(local_name)?,
|
||||
_ => None,
|
||||
};
|
||||
let trait_ns = self.resolve_any_trait(local_name)?;
|
||||
|
||||
if trait_ns.is_none() {
|
||||
if let Some(proto) = self.proto() {
|
||||
|
@ -757,8 +689,21 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
ScriptObjectClass::ClassConstructor(class, ..) => {
|
||||
Ok(class.read().resolve_any_class_trait(local_name))
|
||||
}
|
||||
ScriptObjectClass::InstancePrototype(class, ..) => {
|
||||
Ok(class.read().resolve_any_instance_trait(local_name))
|
||||
ScriptObjectClass::ClassInstance(constr) => {
|
||||
let mut cur_constr = Some(*constr);
|
||||
|
||||
while let Some(constr) = cur_constr {
|
||||
let cur_class = constr
|
||||
.as_class()
|
||||
.ok_or("Object is not a class constructor")?;
|
||||
if let Some(ns) = cur_class.read().resolve_any_instance_trait(local_name) {
|
||||
return Ok(Some(ns));
|
||||
}
|
||||
|
||||
cur_constr = constr.base_class_constr();
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
ScriptObjectClass::NoClass => Ok(None),
|
||||
}
|
||||
|
@ -989,8 +934,26 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
pub fn as_class(&self) -> Option<GcCell<'gc, Class<'gc>>> {
|
||||
match self.class {
|
||||
ScriptObjectClass::ClassConstructor(class, _) => Some(class),
|
||||
ScriptObjectClass::InstancePrototype(class, _) => Some(class),
|
||||
ScriptObjectClass::ClassInstance(constr) => constr.as_class(),
|
||||
ScriptObjectClass::NoClass => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the class constructor for this object, if it has one.
|
||||
pub fn as_constr(&self) -> Option<Object<'gc>> {
|
||||
match self.class {
|
||||
ScriptObjectClass::ClassConstructor(..) => None,
|
||||
ScriptObjectClass::ClassInstance(constr) => Some(constr),
|
||||
ScriptObjectClass::NoClass => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Associate the object with a particular constructor.
|
||||
///
|
||||
/// This turns the object into an instance of that class. It should only be
|
||||
/// used in situations where the object cannot be made an instance of the
|
||||
/// class at allocation time, such as during early runtime setup.
|
||||
pub fn set_constr(&mut self, constr: Object<'gc>) {
|
||||
self.class = ScriptObjectClass::ClassInstance(constr);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,20 +16,11 @@ use gc_arena::{Collect, GcCell, MutationContext};
|
|||
|
||||
/// A class instance deriver that constructs Stage objects.
|
||||
pub fn stage_deriver<'gc>(
|
||||
mut constr: Object<'gc>,
|
||||
constr: Object<'gc>,
|
||||
proto: Object<'gc>,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let base_proto = constr
|
||||
.get_property(
|
||||
constr,
|
||||
&QName::new(Namespace::public(), "prototype"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
StageObject::derive(base_proto, activation.context.gc_context, class, scope)
|
||||
StageObject::derive(constr, proto, activation.context.gc_context)
|
||||
}
|
||||
|
||||
#[derive(Clone, Collect, Debug, Copy)]
|
||||
|
@ -50,12 +41,16 @@ impl<'gc> StageObject<'gc> {
|
|||
pub fn for_display_object(
|
||||
mc: MutationContext<'gc, '_>,
|
||||
display_object: DisplayObject<'gc>,
|
||||
constr: Object<'gc>,
|
||||
proto: Object<'gc>,
|
||||
) -> Self {
|
||||
Self(GcCell::allocate(
|
||||
mc,
|
||||
StageObjectData {
|
||||
base: ScriptObjectData::base_new(Some(proto), ScriptObjectClass::NoClass),
|
||||
base: ScriptObjectData::base_new(
|
||||
Some(proto),
|
||||
ScriptObjectClass::ClassInstance(constr),
|
||||
),
|
||||
display_object: Some(display_object),
|
||||
},
|
||||
))
|
||||
|
@ -63,15 +58,12 @@ impl<'gc> StageObject<'gc> {
|
|||
|
||||
/// Construct a stage object subclass.
|
||||
pub fn derive(
|
||||
base_proto: Object<'gc>,
|
||||
constr: Object<'gc>,
|
||||
proto: Object<'gc>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let base = ScriptObjectData::base_new(
|
||||
Some(base_proto),
|
||||
ScriptObjectClass::InstancePrototype(class, scope),
|
||||
);
|
||||
let base =
|
||||
ScriptObjectData::base_new(Some(proto), ScriptObjectClass::ClassInstance(constr));
|
||||
|
||||
Ok(StageObject(GcCell::allocate(
|
||||
mc,
|
||||
|
@ -191,18 +183,6 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
|
|||
self.0.read().base.get_trait_slot(id)
|
||||
}
|
||||
|
||||
fn get_provided_trait(
|
||||
&self,
|
||||
name: &QName<'gc>,
|
||||
known_traits: &mut Vec<Trait<'gc>>,
|
||||
) -> Result<(), Error> {
|
||||
self.0.read().base.get_provided_trait(name, known_traits)
|
||||
}
|
||||
|
||||
fn get_provided_trait_slot(&self, id: u32) -> Result<Option<Trait<'gc>>, Error> {
|
||||
self.0.read().base.get_provided_trait_slot(id)
|
||||
}
|
||||
|
||||
fn get_scope(self) -> Option<GcCell<'gc, Scope<'gc>>> {
|
||||
self.0.read().base.get_scope()
|
||||
}
|
||||
|
@ -226,10 +206,6 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
|
|||
self.0.read().base.has_trait(name)
|
||||
}
|
||||
|
||||
fn provides_trait(self, name: &QName<'gc>) -> Result<bool, Error> {
|
||||
self.0.read().base.provides_trait(name)
|
||||
}
|
||||
|
||||
fn has_instantiated_property(self, name: &QName<'gc>) -> bool {
|
||||
self.0.read().base.has_instantiated_property(name)
|
||||
}
|
||||
|
@ -282,6 +258,14 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
|
|||
self.0.read().base.as_class()
|
||||
}
|
||||
|
||||
fn as_constr(&self) -> Option<Object<'gc>> {
|
||||
self.0.read().base.as_constr()
|
||||
}
|
||||
|
||||
fn set_constr(self, mc: MutationContext<'gc, '_>, constr: Object<'gc>) {
|
||||
self.0.write(mc).base.set_constr(constr);
|
||||
}
|
||||
|
||||
fn as_display_object(&self) -> Option<DisplayObject<'gc>> {
|
||||
self.0.read().display_object
|
||||
}
|
||||
|
@ -300,11 +284,7 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
|
|||
Err("Not a callable function!".into())
|
||||
}
|
||||
|
||||
fn construct(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
fn derive(&self, activation: &mut Activation<'_, 'gc, '_>) -> Result<Object<'gc>, Error> {
|
||||
let this: Object<'gc> = Object::StageObject(*self);
|
||||
let base = ScriptObjectData::base_new(Some(this), ScriptObjectClass::NoClass);
|
||||
|
||||
|
@ -318,28 +298,6 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
|
|||
.into())
|
||||
}
|
||||
|
||||
fn derive(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let this: Object<'gc> = Object::StageObject(*self);
|
||||
let base = ScriptObjectData::base_new(
|
||||
Some(this),
|
||||
ScriptObjectClass::InstancePrototype(class, scope),
|
||||
);
|
||||
|
||||
Ok(StageObject(GcCell::allocate(
|
||||
activation.context.gc_context,
|
||||
StageObjectData {
|
||||
base,
|
||||
display_object: None,
|
||||
},
|
||||
))
|
||||
.into())
|
||||
}
|
||||
|
||||
fn value_of(&self, _mc: MutationContext<'gc, '_>) -> Result<Value<'gc>, Error> {
|
||||
Ok(Value::Object(Object::from(*self)))
|
||||
}
|
||||
|
|
|
@ -15,20 +15,11 @@ use gc_arena::{Collect, GcCell, MutationContext};
|
|||
|
||||
/// A class instance deriver that constructs XML objects.
|
||||
pub fn xml_deriver<'gc>(
|
||||
mut constr: Object<'gc>,
|
||||
constr: Object<'gc>,
|
||||
proto: Object<'gc>,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let base_proto = constr
|
||||
.get_property(
|
||||
constr,
|
||||
&QName::new(Namespace::public(), "prototype"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
XmlObject::derive(base_proto, activation.context.gc_context, class, scope)
|
||||
XmlObject::derive(constr, proto, activation.context.gc_context)
|
||||
}
|
||||
|
||||
#[derive(Clone, Collect, Debug, Copy)]
|
||||
|
@ -45,15 +36,12 @@ pub struct XmlObjectData<'gc> {
|
|||
impl<'gc> XmlObject<'gc> {
|
||||
/// Instantiate an xml subclass.
|
||||
pub fn derive(
|
||||
constr: Object<'gc>,
|
||||
base_proto: Object<'gc>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let base = ScriptObjectData::base_new(
|
||||
Some(base_proto),
|
||||
ScriptObjectClass::InstancePrototype(class, scope),
|
||||
);
|
||||
let base =
|
||||
ScriptObjectData::base_new(Some(base_proto), ScriptObjectClass::ClassInstance(constr));
|
||||
|
||||
Ok(XmlObject(GcCell::allocate(mc, XmlObjectData { base })).into())
|
||||
}
|
||||
|
@ -72,26 +60,15 @@ impl<'gc> TObject<'gc> for XmlObject<'gc> {
|
|||
impl_avm2_custom_object!(base);
|
||||
impl_avm2_custom_object_properties!(base);
|
||||
|
||||
fn construct(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
fn derive(&self, activation: &mut Activation<'_, 'gc, '_>) -> Result<Object<'gc>, Error> {
|
||||
let this: Object<'gc> = Object::XmlObject(*self);
|
||||
Ok(Self::empty_object(
|
||||
activation.context.gc_context,
|
||||
Some(this),
|
||||
))
|
||||
}
|
||||
let base = ScriptObjectData::base_new(Some(this), ScriptObjectClass::NoClass);
|
||||
|
||||
fn derive(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
class: GcCell<'gc, Class<'gc>>,
|
||||
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let this: Object<'gc> = Object::XmlObject(*self);
|
||||
Self::derive(this, activation.context.gc_context, class, scope)
|
||||
Ok(XmlObject(GcCell::allocate(
|
||||
activation.context.gc_context,
|
||||
XmlObjectData { base },
|
||||
))
|
||||
.into())
|
||||
}
|
||||
|
||||
fn value_of(&self, _mc: MutationContext<'gc, '_>) -> Result<Value<'gc>, Error> {
|
||||
|
|
|
@ -131,14 +131,14 @@ impl<'gc> Property<'gc> {
|
|||
pub fn get(
|
||||
&self,
|
||||
this: Object<'gc>,
|
||||
base_proto: Option<Object<'gc>>,
|
||||
base_constr: Option<Object<'gc>>,
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
match self {
|
||||
Property::Virtual { get: Some(get), .. } => Ok(ReturnValue::defer_execution(
|
||||
*get,
|
||||
Some(this),
|
||||
vec![],
|
||||
base_proto,
|
||||
base_constr,
|
||||
)),
|
||||
Property::Virtual { get: None, .. } => Ok(Value::Undefined.into()),
|
||||
Property::Stored { value, .. } => Ok(value.to_owned().into()),
|
||||
|
@ -159,7 +159,7 @@ impl<'gc> Property<'gc> {
|
|||
pub fn set(
|
||||
&mut self,
|
||||
this: Object<'gc>,
|
||||
base_proto: Option<Object<'gc>>,
|
||||
base_constr: Option<Object<'gc>>,
|
||||
new_value: impl Into<Value<'gc>>,
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
match self {
|
||||
|
@ -169,7 +169,7 @@ impl<'gc> Property<'gc> {
|
|||
*function,
|
||||
Some(this),
|
||||
vec![new_value.into()],
|
||||
base_proto,
|
||||
base_constr,
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -203,7 +203,7 @@ impl<'gc> Property<'gc> {
|
|||
pub fn init(
|
||||
&mut self,
|
||||
this: Object<'gc>,
|
||||
base_proto: Option<Object<'gc>>,
|
||||
base_constr: Option<Object<'gc>>,
|
||||
new_value: impl Into<Value<'gc>>,
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
match self {
|
||||
|
@ -213,7 +213,7 @@ impl<'gc> Property<'gc> {
|
|||
*function,
|
||||
Some(this),
|
||||
vec![new_value.into()],
|
||||
base_proto,
|
||||
base_constr,
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ pub enum ReturnValue<'gc> {
|
|||
callee: Object<'gc>,
|
||||
unbound_reciever: Option<Object<'gc>>,
|
||||
arguments: Vec<Value<'gc>>,
|
||||
base_proto: Option<Object<'gc>>,
|
||||
base_constr: Option<Object<'gc>>,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -49,13 +49,13 @@ impl fmt::Debug for ReturnValue<'_> {
|
|||
callee,
|
||||
unbound_reciever,
|
||||
arguments,
|
||||
base_proto,
|
||||
base_constr,
|
||||
} => f
|
||||
.debug_struct("ReturnValue")
|
||||
.field("callee", callee)
|
||||
.field("unbound_reciever", unbound_reciever)
|
||||
.field("arguments", arguments)
|
||||
.field("base_proto", base_proto)
|
||||
.field("base_constr", base_constr)
|
||||
.finish(),
|
||||
}
|
||||
}
|
||||
|
@ -67,13 +67,13 @@ impl<'gc> ReturnValue<'gc> {
|
|||
callee: Object<'gc>,
|
||||
unbound_reciever: Option<Object<'gc>>,
|
||||
arguments: Vec<Value<'gc>>,
|
||||
base_proto: Option<Object<'gc>>,
|
||||
base_constr: Option<Object<'gc>>,
|
||||
) -> Self {
|
||||
Self::ResultOf {
|
||||
callee,
|
||||
unbound_reciever,
|
||||
arguments,
|
||||
base_proto,
|
||||
base_constr,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,12 +88,12 @@ impl<'gc> ReturnValue<'gc> {
|
|||
callee,
|
||||
unbound_reciever,
|
||||
arguments,
|
||||
base_proto,
|
||||
base_constr,
|
||||
} => callee.as_executable().unwrap().exec(
|
||||
unbound_reciever,
|
||||
&arguments,
|
||||
activation,
|
||||
base_proto,
|
||||
base_constr,
|
||||
callee,
|
||||
),
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ use crate::avm2::activation::Activation;
|
|||
use crate::avm2::class::Class;
|
||||
use crate::avm2::domain::Domain;
|
||||
use crate::avm2::method::{BytecodeMethod, Method};
|
||||
use crate::avm2::names::{Namespace, QName};
|
||||
use crate::avm2::object::{DomainObject, Object, TObject};
|
||||
use crate::avm2::scope::Scope;
|
||||
use crate::avm2::string::AvmString;
|
||||
|
@ -109,8 +110,7 @@ impl<'gc> TranslationUnit<'gc> {
|
|||
pub fn load_class(
|
||||
self,
|
||||
class_index: u32,
|
||||
avm2: &mut Avm2<'gc>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
uc: &mut UpdateContext<'_, 'gc, '_>,
|
||||
) -> Result<GcCell<'gc, Class<'gc>>, Error> {
|
||||
let read = self.0.read();
|
||||
if let Some(class) = read.classes.get(&class_index) {
|
||||
|
@ -119,10 +119,15 @@ impl<'gc> TranslationUnit<'gc> {
|
|||
|
||||
drop(read);
|
||||
|
||||
let class = Class::from_abc_index(self, class_index, mc)?;
|
||||
self.0.write(mc).classes.insert(class_index, class);
|
||||
let class = Class::from_abc_index(self, class_index, uc.gc_context)?;
|
||||
self.0
|
||||
.write(uc.gc_context)
|
||||
.classes
|
||||
.insert(class_index, class);
|
||||
|
||||
class.write(mc).load_traits(self, class_index, avm2, mc)?;
|
||||
class
|
||||
.write(uc.gc_context)
|
||||
.load_traits(self, class_index, uc)?;
|
||||
|
||||
Ok(class)
|
||||
}
|
||||
|
@ -131,8 +136,7 @@ impl<'gc> TranslationUnit<'gc> {
|
|||
pub fn load_script(
|
||||
self,
|
||||
script_index: u32,
|
||||
avm2: &mut Avm2<'gc>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
uc: &mut UpdateContext<'_, 'gc, '_>,
|
||||
) -> Result<Script<'gc>, Error> {
|
||||
let read = self.0.read();
|
||||
if let Some(scripts) = read.scripts.get(&script_index) {
|
||||
|
@ -143,15 +147,32 @@ impl<'gc> TranslationUnit<'gc> {
|
|||
|
||||
drop(read);
|
||||
|
||||
let global = DomainObject::from_domain(mc, Some(avm2.prototypes().global), domain);
|
||||
let mut activation = Activation::from_nothing(uc.reborrow());
|
||||
|
||||
let mut script = Script::from_abc_index(self, script_index, global, mc)?;
|
||||
self.0.write(mc).scripts.insert(script_index, script);
|
||||
let mut global_proto = activation.context.avm2.prototypes().global;
|
||||
let global_constr = global_proto
|
||||
.get_property(
|
||||
global_proto,
|
||||
&QName::new(Namespace::public(), "constructor"),
|
||||
&mut activation,
|
||||
)?
|
||||
.coerce_to_object(&mut activation)?;
|
||||
|
||||
script.load_traits(self, script_index, avm2, mc)?;
|
||||
drop(activation);
|
||||
|
||||
let global =
|
||||
DomainObject::from_domain(uc.gc_context, global_constr, Some(global_proto), domain);
|
||||
|
||||
let mut script = Script::from_abc_index(self, script_index, global, uc.gc_context)?;
|
||||
self.0
|
||||
.write(uc.gc_context)
|
||||
.scripts
|
||||
.insert(script_index, script);
|
||||
|
||||
script.load_traits(self, script_index, uc)?;
|
||||
|
||||
for traitdef in script.traits()?.iter() {
|
||||
domain.export_definition(traitdef.name().clone(), script, mc)?;
|
||||
domain.export_definition(traitdef.name().clone(), script, uc.gc_context)?;
|
||||
}
|
||||
|
||||
Ok(script)
|
||||
|
@ -302,10 +323,9 @@ impl<'gc> Script<'gc> {
|
|||
&mut self,
|
||||
unit: TranslationUnit<'gc>,
|
||||
script_index: u32,
|
||||
avm2: &mut Avm2<'gc>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
uc: &mut UpdateContext<'_, 'gc, '_>,
|
||||
) -> Result<(), Error> {
|
||||
let mut write = self.0.write(mc);
|
||||
let mut write = self.0.write(uc.gc_context);
|
||||
|
||||
if write.traits_loaded {
|
||||
return Ok(());
|
||||
|
@ -323,9 +343,9 @@ impl<'gc> Script<'gc> {
|
|||
for abc_trait in script.traits.iter() {
|
||||
drop(write);
|
||||
|
||||
let newtrait = Trait::from_abc_trait(unit, abc_trait, avm2, mc)?;
|
||||
let newtrait = Trait::from_abc_trait(unit, abc_trait, uc)?;
|
||||
|
||||
write = self.0.write(mc);
|
||||
write = self.0.write(uc.gc_context);
|
||||
write.traits.push(newtrait);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,9 +5,10 @@ use crate::avm2::method::Method;
|
|||
use crate::avm2::names::{Multiname, QName};
|
||||
use crate::avm2::script::TranslationUnit;
|
||||
use crate::avm2::value::{abc_default_value, Value};
|
||||
use crate::avm2::{Avm2, Error};
|
||||
use crate::avm2::Error;
|
||||
use crate::context::UpdateContext;
|
||||
use bitflags::bitflags;
|
||||
use gc_arena::{Collect, GcCell, MutationContext};
|
||||
use gc_arena::{Collect, GcCell};
|
||||
use swf::avm2::types::{Trait as AbcTrait, TraitKind as AbcTraitKind};
|
||||
|
||||
bitflags! {
|
||||
|
@ -179,10 +180,9 @@ impl<'gc> Trait<'gc> {
|
|||
pub fn from_abc_trait(
|
||||
unit: TranslationUnit<'gc>,
|
||||
abc_trait: &AbcTrait,
|
||||
avm2: &mut Avm2<'gc>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
uc: &mut UpdateContext<'_, 'gc, '_>,
|
||||
) -> Result<Self, Error> {
|
||||
let name = QName::from_abc_multiname(unit, abc_trait.name.clone(), mc)?;
|
||||
let name = QName::from_abc_multiname(unit, abc_trait.name.clone(), uc.gc_context)?;
|
||||
|
||||
Ok(match &abc_trait.kind {
|
||||
AbcTraitKind::Slot {
|
||||
|
@ -197,10 +197,14 @@ impl<'gc> Trait<'gc> {
|
|||
type_name: if type_name.0 == 0 {
|
||||
Multiname::any()
|
||||
} else {
|
||||
Multiname::from_abc_multiname_static(unit, type_name.clone(), mc)?
|
||||
Multiname::from_abc_multiname_static(
|
||||
unit,
|
||||
type_name.clone(),
|
||||
uc.gc_context,
|
||||
)?
|
||||
},
|
||||
default_value: if let Some(dv) = value {
|
||||
Some(abc_default_value(unit, dv, avm2, mc)?)
|
||||
Some(abc_default_value(unit, dv, uc)?)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
|
@ -211,7 +215,7 @@ impl<'gc> Trait<'gc> {
|
|||
attributes: trait_attribs_from_abc_traits(abc_trait),
|
||||
kind: TraitKind::Method {
|
||||
disp_id: *disp_id,
|
||||
method: unit.load_method(method.0, mc)?,
|
||||
method: unit.load_method(method.0, uc.gc_context)?,
|
||||
},
|
||||
},
|
||||
AbcTraitKind::Getter { disp_id, method } => Trait {
|
||||
|
@ -219,7 +223,7 @@ impl<'gc> Trait<'gc> {
|
|||
attributes: trait_attribs_from_abc_traits(abc_trait),
|
||||
kind: TraitKind::Getter {
|
||||
disp_id: *disp_id,
|
||||
method: unit.load_method(method.0, mc)?,
|
||||
method: unit.load_method(method.0, uc.gc_context)?,
|
||||
},
|
||||
},
|
||||
AbcTraitKind::Setter { disp_id, method } => Trait {
|
||||
|
@ -227,7 +231,7 @@ impl<'gc> Trait<'gc> {
|
|||
attributes: trait_attribs_from_abc_traits(abc_trait),
|
||||
kind: TraitKind::Setter {
|
||||
disp_id: *disp_id,
|
||||
method: unit.load_method(method.0, mc)?,
|
||||
method: unit.load_method(method.0, uc.gc_context)?,
|
||||
},
|
||||
},
|
||||
AbcTraitKind::Class { slot_id, class } => Trait {
|
||||
|
@ -235,7 +239,7 @@ impl<'gc> Trait<'gc> {
|
|||
attributes: trait_attribs_from_abc_traits(abc_trait),
|
||||
kind: TraitKind::Class {
|
||||
slot_id: *slot_id,
|
||||
class: unit.load_class(class.0, avm2, mc)?,
|
||||
class: unit.load_class(class.0, uc)?,
|
||||
},
|
||||
},
|
||||
AbcTraitKind::Function { slot_id, function } => Trait {
|
||||
|
@ -243,7 +247,7 @@ impl<'gc> Trait<'gc> {
|
|||
attributes: trait_attribs_from_abc_traits(abc_trait),
|
||||
kind: TraitKind::Function {
|
||||
slot_id: *slot_id,
|
||||
function: unit.load_method(function.0, mc)?,
|
||||
function: unit.load_method(function.0, uc.gc_context)?,
|
||||
},
|
||||
},
|
||||
AbcTraitKind::Const {
|
||||
|
@ -258,10 +262,14 @@ impl<'gc> Trait<'gc> {
|
|||
type_name: if type_name.0 == 0 {
|
||||
Multiname::any()
|
||||
} else {
|
||||
Multiname::from_abc_multiname_static(unit, type_name.clone(), mc)?
|
||||
Multiname::from_abc_multiname_static(
|
||||
unit,
|
||||
type_name.clone(),
|
||||
uc.gc_context,
|
||||
)?
|
||||
},
|
||||
default_value: if let Some(dv) = value {
|
||||
Some(abc_default_value(unit, dv, avm2, mc)?)
|
||||
Some(abc_default_value(unit, dv, uc)?)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
|
|
|
@ -6,7 +6,8 @@ use crate::avm2::names::QName;
|
|||
use crate::avm2::object::{NamespaceObject, Object, PrimitiveObject, TObject};
|
||||
use crate::avm2::script::TranslationUnit;
|
||||
use crate::avm2::string::AvmString;
|
||||
use crate::avm2::{Avm2, Error};
|
||||
use crate::avm2::Error;
|
||||
use crate::context::UpdateContext;
|
||||
use crate::ecma_conversions::{f64_to_wrapping_i32, f64_to_wrapping_u32};
|
||||
use gc_arena::{Collect, MutationContext};
|
||||
use std::cell::Ref;
|
||||
|
@ -184,14 +185,15 @@ pub fn abc_double(translation_unit: TranslationUnit<'_>, index: Index<f64>) -> R
|
|||
pub fn abc_default_value<'gc>(
|
||||
translation_unit: TranslationUnit<'gc>,
|
||||
default: &AbcDefaultValue,
|
||||
avm2: &mut Avm2<'gc>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
uc: &mut UpdateContext<'_, 'gc, '_>,
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
match default {
|
||||
AbcDefaultValue::Int(i) => abc_int(translation_unit, *i).map(|v| v.into()),
|
||||
AbcDefaultValue::Uint(u) => abc_uint(translation_unit, *u).map(|v| v.into()),
|
||||
AbcDefaultValue::Double(d) => abc_double(translation_unit, *d).map(|v| v.into()),
|
||||
AbcDefaultValue::String(s) => translation_unit.pool_string(s.0, mc).map(|v| v.into()),
|
||||
AbcDefaultValue::String(s) => translation_unit
|
||||
.pool_string(s.0, uc.gc_context)
|
||||
.map(|v| v.into()),
|
||||
AbcDefaultValue::True => Ok(true.into()),
|
||||
AbcDefaultValue::False => Ok(false.into()),
|
||||
AbcDefaultValue::Null => Ok(Value::Null),
|
||||
|
@ -202,12 +204,28 @@ pub fn abc_default_value<'gc>(
|
|||
| AbcDefaultValue::Protected(ns)
|
||||
| AbcDefaultValue::Explicit(ns)
|
||||
| AbcDefaultValue::StaticProtected(ns)
|
||||
| AbcDefaultValue::Private(ns) => Ok(NamespaceObject::from_namespace(
|
||||
Namespace::from_abc_namespace(translation_unit, ns.clone(), mc)?,
|
||||
avm2.prototypes().namespace,
|
||||
mc,
|
||||
| AbcDefaultValue::Private(ns) => {
|
||||
let mut activation = Activation::from_nothing(uc.reborrow());
|
||||
|
||||
let mut ns_proto = activation.avm2().prototypes().namespace;
|
||||
let ns_constr = ns_proto
|
||||
.get_property(
|
||||
ns_proto,
|
||||
&QName::new(Namespace::public(), "constructor"),
|
||||
&mut activation,
|
||||
)?
|
||||
.into()),
|
||||
.coerce_to_object(&mut activation)?;
|
||||
|
||||
drop(activation);
|
||||
|
||||
Ok(NamespaceObject::from_namespace(
|
||||
Namespace::from_abc_namespace(translation_unit, ns.clone(), uc.gc_context)?,
|
||||
ns_constr,
|
||||
ns_proto,
|
||||
uc.gc_context,
|
||||
)?
|
||||
.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -561,7 +579,7 @@ impl<'gc> Value<'gc> {
|
|||
_ => {}
|
||||
};
|
||||
|
||||
let proto = match self {
|
||||
let mut proto = match self {
|
||||
Value::Bool(_) => activation.avm2().prototypes().boolean,
|
||||
Value::Number(_) => activation.avm2().prototypes().number,
|
||||
Value::Unsigned(_) => activation.avm2().prototypes().uint,
|
||||
|
@ -569,8 +587,15 @@ impl<'gc> Value<'gc> {
|
|||
Value::String(_) => activation.avm2().prototypes().string,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let constr = proto
|
||||
.get_property(
|
||||
proto,
|
||||
&QName::new(Namespace::public(), "constructor"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
PrimitiveObject::from_primitive(self.clone(), proto, activation.context.gc_context)
|
||||
PrimitiveObject::from_primitive(self.clone(), constr, proto, activation.context.gc_context)
|
||||
}
|
||||
|
||||
/// Determine if two values are abstractly equal to each other.
|
||||
|
|
|
@ -441,10 +441,22 @@ impl<'gc> TDisplayObject<'gc> for Avm2Button<'gc> {
|
|||
}
|
||||
|
||||
if self.0.read().object.is_none() {
|
||||
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
||||
let mut simplebutton_proto = activation.avm2().prototypes().simplebutton;
|
||||
let simplebutton_constr = simplebutton_proto
|
||||
.get_property(
|
||||
simplebutton_proto,
|
||||
&Avm2QName::new(Avm2Namespace::public(), "constructor"),
|
||||
&mut activation,
|
||||
)
|
||||
.unwrap()
|
||||
.coerce_to_object(&mut activation)
|
||||
.unwrap();
|
||||
let object = Avm2StageObject::for_display_object(
|
||||
context.gc_context,
|
||||
(*self).into(),
|
||||
context.avm2.prototypes().simplebutton,
|
||||
simplebutton_constr,
|
||||
simplebutton_proto,
|
||||
);
|
||||
self.0.write(context.gc_context).object = Some(object.into());
|
||||
|
||||
|
|
|
@ -1509,22 +1509,28 @@ impl<'gc> EditText<'gc> {
|
|||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
display_object: DisplayObject<'gc>,
|
||||
) {
|
||||
let mut proto = context.avm2.prototypes().textfield;
|
||||
let object: Avm2Object<'gc> =
|
||||
Avm2StageObject::for_display_object(context.gc_context, display_object, proto).into();
|
||||
|
||||
let mut textfield_proto = context.avm2.prototypes().textfield;
|
||||
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
||||
let constr = proto
|
||||
let textfield_constr = textfield_proto
|
||||
.get_property(
|
||||
proto,
|
||||
textfield_proto,
|
||||
&Avm2QName::new(Avm2Namespace::public(), "constructor"),
|
||||
&mut activation,
|
||||
)
|
||||
.unwrap()
|
||||
.coerce_to_object(&mut activation)
|
||||
.unwrap();
|
||||
.and_then(|v| v.coerce_to_object(&mut activation))
|
||||
.expect("Textfield proto needs constr");
|
||||
|
||||
if let Err(e) = constr.call(Some(object), &[], &mut activation, Some(proto)) {
|
||||
let object: Avm2Object<'gc> = Avm2StageObject::for_display_object(
|
||||
activation.context.gc_context,
|
||||
display_object,
|
||||
textfield_constr,
|
||||
textfield_proto,
|
||||
)
|
||||
.into();
|
||||
|
||||
if let Err(e) =
|
||||
textfield_constr.call(Some(object), &[], &mut activation, Some(textfield_constr))
|
||||
{
|
||||
log::error!(
|
||||
"Got {} when constructing AVM2 side of dynamic text field",
|
||||
e
|
||||
|
|
|
@ -118,10 +118,10 @@ impl<'gc> TDisplayObject<'gc> for Graphic<'gc> {
|
|||
if self.avm_type() == AvmType::Avm2 && matches!(self.object2(), Avm2Value::Undefined) {
|
||||
let mut allocator = || {
|
||||
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
||||
let mut proto = activation.context.avm2.prototypes().shape;
|
||||
let constr = proto
|
||||
let mut shape_proto = activation.context.avm2.prototypes().shape;
|
||||
let shape_constr = shape_proto
|
||||
.get_property(
|
||||
proto,
|
||||
shape_proto,
|
||||
&Avm2QName::new(Avm2Namespace::public(), "constructor"),
|
||||
&mut activation,
|
||||
)?
|
||||
|
@ -130,10 +130,11 @@ impl<'gc> TDisplayObject<'gc> for Graphic<'gc> {
|
|||
let object = Avm2StageObject::for_display_object(
|
||||
activation.context.gc_context,
|
||||
(*self).into(),
|
||||
proto,
|
||||
shape_constr,
|
||||
shape_proto,
|
||||
)
|
||||
.into();
|
||||
constr.call(Some(object), &[], &mut activation, Some(proto))?;
|
||||
shape_constr.call(Some(object), &[], &mut activation, Some(shape_proto))?;
|
||||
|
||||
Ok(object)
|
||||
};
|
||||
|
|
|
@ -1529,7 +1529,7 @@ impl<'gc> MovieClip<'gc> {
|
|||
|
||||
let mut constr_thing = || {
|
||||
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
||||
let proto = constructor
|
||||
let mc_proto = constructor
|
||||
.get_property(
|
||||
constructor,
|
||||
&Avm2QName::new(Avm2Namespace::public(), "prototype"),
|
||||
|
@ -1539,7 +1539,8 @@ impl<'gc> MovieClip<'gc> {
|
|||
let object = Avm2StageObject::for_display_object(
|
||||
activation.context.gc_context,
|
||||
display_object,
|
||||
proto,
|
||||
constructor,
|
||||
mc_proto,
|
||||
)
|
||||
.into();
|
||||
|
||||
|
@ -1560,7 +1561,7 @@ impl<'gc> MovieClip<'gc> {
|
|||
/// will allocate the object first before doing so. This function is
|
||||
/// intended to be called from `post_instantiate`.
|
||||
fn construct_as_avm2_object(self, context: &mut UpdateContext<'_, 'gc, '_>) {
|
||||
let mut constructor = self.0.read().avm2_constructor.unwrap_or_else(|| {
|
||||
let constructor = self.0.read().avm2_constructor.unwrap_or_else(|| {
|
||||
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
||||
let mut mc_proto = activation.context.avm2.prototypes().movieclip;
|
||||
mc_proto
|
||||
|
@ -1577,14 +1578,7 @@ impl<'gc> MovieClip<'gc> {
|
|||
if let Avm2Value::Object(object) = self.object2() {
|
||||
let mut constr_thing = || {
|
||||
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
||||
let proto = constructor
|
||||
.get_property(
|
||||
constructor,
|
||||
&Avm2QName::new(Avm2Namespace::public(), "prototype"),
|
||||
&mut activation,
|
||||
)?
|
||||
.coerce_to_object(&mut activation)?;
|
||||
constructor.call(Some(object), &[], &mut activation, Some(proto))?;
|
||||
constructor.call(Some(object), &[], &mut activation, Some(constructor))?;
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::avm1::Object as Avm1Object;
|
|||
use crate::avm2::{
|
||||
Activation as Avm2Activation, Event as Avm2Event, Namespace as Avm2Namespace,
|
||||
Object as Avm2Object, QName as Avm2QName, ScriptObject as Avm2ScriptObject,
|
||||
StageObject as Avm2StageObject, Value as Avm2Value,
|
||||
StageObject as Avm2StageObject, TObject as Avm2TObject, Value as Avm2Value,
|
||||
};
|
||||
use crate::backend::ui::UiBackend;
|
||||
use crate::config::Letterbox;
|
||||
|
@ -518,28 +518,32 @@ impl<'gc> TDisplayObject<'gc> for Stage<'gc> {
|
|||
_instantiated_by: Instantiator,
|
||||
_run_frame: bool,
|
||||
) {
|
||||
let stage_proto = context.avm2.prototypes().stage;
|
||||
let avm2_stage =
|
||||
Avm2StageObject::for_display_object(context.gc_context, (*self).into(), stage_proto);
|
||||
|
||||
// TODO: Replace this when we have a convenience method for constructing AVM2 native objects.
|
||||
// TODO: We should only do this if the movie is actually an AVM2 movie.
|
||||
// This is necessary for EventDispatcher super-constructor to run.
|
||||
use crate::avm2::TObject;
|
||||
let mut stage_proto = context.avm2.prototypes().stage;
|
||||
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
||||
let mut proto = activation.context.avm2.prototypes().stage;
|
||||
if let Err(e) = proto
|
||||
let stage_constr = stage_proto
|
||||
.get_property(
|
||||
proto,
|
||||
stage_proto,
|
||||
&Avm2QName::new(Avm2Namespace::public(), "constructor"),
|
||||
&mut activation,
|
||||
)
|
||||
.and_then(|v| v.coerce_to_object(&mut activation))
|
||||
.and_then(|constr| {
|
||||
// TODO: Stage's AS-visible constructor actually throws. Have to call non-throwing native constructor here.
|
||||
constr.call(Some(avm2_stage.into()), &[], &mut activation, Some(proto))
|
||||
})
|
||||
{
|
||||
.expect("Stage proto needs constr");
|
||||
let avm2_stage = Avm2StageObject::for_display_object(
|
||||
activation.context.gc_context,
|
||||
(*self).into(),
|
||||
stage_constr,
|
||||
stage_proto,
|
||||
);
|
||||
|
||||
// TODO: Replace this when we have a convenience method for constructing AVM2 native objects.
|
||||
// TODO: We should only do this if the movie is actually an AVM2 movie.
|
||||
// This is necessary for EventDispatcher super-constructor to run.
|
||||
if let Err(e) = stage_constr.call(
|
||||
Some(avm2_stage.into()),
|
||||
&[],
|
||||
&mut activation,
|
||||
Some(stage_constr),
|
||||
) {
|
||||
log::error!("Unable to construct AVM2 Stage: {}", e);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
//! Video player display object
|
||||
|
||||
use crate::avm1::{Object as Avm1Object, StageObject as Avm1StageObject};
|
||||
use crate::avm2::{Object as Avm2Object, StageObject as Avm2StageObject};
|
||||
use crate::avm2::{
|
||||
Activation as Avm2Activation, Namespace as Avm2Namespace, Object as Avm2Object,
|
||||
QName as Avm2QName, StageObject as Avm2StageObject, TObject as Avm2TObject,
|
||||
};
|
||||
use crate::backend::render::BitmapInfo;
|
||||
use crate::backend::video::{EncodedFrame, VideoStreamHandle};
|
||||
use crate::bounding_box::BoundingBox;
|
||||
|
@ -381,13 +384,25 @@ impl<'gc> TDisplayObject<'gc> for Video<'gc> {
|
|||
fn construct_frame(&self, context: &mut UpdateContext<'_, 'gc, '_>) {
|
||||
let vm_type = self.avm_type();
|
||||
if vm_type == AvmType::Avm2 && matches!(self.object2(), Avm2Value::Undefined) {
|
||||
let mut video_proto = context.avm2.prototypes().video;
|
||||
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
||||
let video_constr = video_proto
|
||||
.get_property(
|
||||
video_proto,
|
||||
&Avm2QName::new(Avm2Namespace::public(), "constructor"),
|
||||
&mut activation,
|
||||
)
|
||||
.and_then(|v| v.coerce_to_object(&mut activation))
|
||||
.expect("Video proto needs constr");
|
||||
|
||||
let object: Avm2Object<'_> = Avm2StageObject::for_display_object(
|
||||
context.gc_context,
|
||||
activation.context.gc_context,
|
||||
(*self).into(),
|
||||
context.avm2.prototypes().video,
|
||||
video_constr,
|
||||
video_proto,
|
||||
)
|
||||
.into();
|
||||
self.0.write(context.gc_context).object = Some(object.into());
|
||||
self.0.write(activation.context.gc_context).object = Some(object.into());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -708,7 +708,7 @@ impl TextFormat {
|
|||
.coerce_to_object(activation)?;
|
||||
let mut object = Avm2ScriptObject::object(activation.context.gc_context, proto);
|
||||
|
||||
constr.call(Some(object), &[], activation, Some(proto))?;
|
||||
constr.call(Some(object), &[], activation, Some(constr))?;
|
||||
|
||||
object.set_property(
|
||||
object,
|
||||
|
@ -849,9 +849,20 @@ impl TextFormat {
|
|||
|
||||
if let Some(ts) = &self.tab_stops {
|
||||
let tab_stop_storage = ts.iter().copied().collect();
|
||||
|
||||
let mut array_proto = activation.avm2().prototypes().array;
|
||||
let array_constr = array_proto
|
||||
.get_property(
|
||||
array_proto,
|
||||
&Avm2QName::new(Avm2Namespace::public(), "constructor"),
|
||||
activation,
|
||||
)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
let tab_stops = Avm2ArrayObject::from_array(
|
||||
tab_stop_storage,
|
||||
activation.context.avm2.prototypes().array,
|
||||
array_constr,
|
||||
array_proto,
|
||||
activation.context.gc_context,
|
||||
);
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use crate::avm1::activation::{Activation, ActivationIdentifier};
|
||||
use crate::avm1::{Avm1, AvmString, Object, TObject, Value};
|
||||
use crate::avm2::Domain as Avm2Domain;
|
||||
use crate::avm2::{Activation as Avm2Activation, Domain as Avm2Domain};
|
||||
use crate::backend::navigator::OwnedFuture;
|
||||
use crate::context::{ActionQueue, ActionType};
|
||||
use crate::display_object::{DisplayObject, MorphShape, TDisplayObject};
|
||||
|
@ -498,11 +498,12 @@ impl<'gc> Loader<'gc> {
|
|||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let domain =
|
||||
Avm2Domain::movie_domain(uc.gc_context, uc.avm2.global_domain());
|
||||
let library = uc.library.library_for_movie_mut(movie.clone());
|
||||
|
||||
library.set_avm2_domain(domain);
|
||||
let mut activation = Avm2Activation::from_nothing(uc.reborrow());
|
||||
let parent_domain = activation.avm2().global_domain();
|
||||
let domain = Avm2Domain::movie_domain(&mut activation, parent_domain);
|
||||
uc.library
|
||||
.library_for_movie_mut(movie.clone())
|
||||
.set_avm2_domain(domain);
|
||||
|
||||
if let Some(broadcaster) = broadcaster {
|
||||
Avm1::run_stack_frame_for_method(
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::avm1::globals::system::SystemProperties;
|
|||
use crate::avm1::object::Object;
|
||||
use crate::avm1::property::Attribute;
|
||||
use crate::avm1::{Avm1, AvmString, ScriptObject, TObject, Timers, Value};
|
||||
use crate::avm2::{Avm2, Domain as Avm2Domain};
|
||||
use crate::avm2::{Activation as Avm2Activation, Avm2, Domain as Avm2Domain};
|
||||
use crate::backend::{
|
||||
audio::{AudioBackend, AudioManager},
|
||||
locale::LocaleBackend,
|
||||
|
@ -389,14 +389,21 @@ impl Player {
|
|||
context.swf.width().to_pixels() as u32,
|
||||
context.swf.height().to_pixels() as u32,
|
||||
);
|
||||
let domain = Avm2Domain::movie_domain(context.gc_context, context.avm2.global_domain());
|
||||
|
||||
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
||||
let global_domain = activation.avm2().global_domain();
|
||||
let domain = Avm2Domain::movie_domain(&mut activation, global_domain);
|
||||
|
||||
drop(activation);
|
||||
|
||||
context
|
||||
.library
|
||||
.library_for_movie_mut(context.swf.clone())
|
||||
.set_avm2_domain(domain);
|
||||
|
||||
let root: DisplayObject =
|
||||
MovieClip::from_movie(context.gc_context, context.swf.clone()).into();
|
||||
|
||||
let library = context.library.library_for_movie_mut(context.swf.clone());
|
||||
|
||||
library.set_avm2_domain(domain);
|
||||
|
||||
root.set_depth(context.gc_context, 0);
|
||||
let flashvars = if !context.swf.parameters().is_empty() {
|
||||
let object = ScriptObject::object(context.gc_context, None);
|
||||
|
|
Loading…
Reference in New Issue