avm2: Add a notion of native instance initializers.
Native initializers are a separate, parallel initialization chain intended for all object construction that is not directly triggered by `Op::Construct` and friends. This allows us to implement classes that cannot be directly constructed by user code, but can be constructed by native code or supercalled into from non-native.
This commit is contained in:
parent
4bc1d37029
commit
dcbb5e4284
|
@ -372,7 +372,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
});
|
||||
let base_constr = base_constr?;
|
||||
|
||||
base_constr.call(Some(receiver), args, self, Some(base_constr))
|
||||
base_constr.call_native_initializer(Some(receiver), args, self, Some(base_constr))
|
||||
}
|
||||
|
||||
/// Attempts to lock the activation frame for execution.
|
||||
|
|
|
@ -137,6 +137,20 @@ pub struct Class<'gc> {
|
|||
/// Must be called each time a new class instance is constructed.
|
||||
instance_init: Method<'gc>,
|
||||
|
||||
/// The native instance initializer for this class.
|
||||
///
|
||||
/// This may be provided to allow natively-constructed classes to
|
||||
/// initialize themselves in a different manner from user-constructed ones.
|
||||
/// For example, the user-accessible constructor may error out (as it's not
|
||||
/// a valid class to construct for users), but native code may still call
|
||||
/// it's constructor stack.
|
||||
///
|
||||
/// By default, a class's `native_instance_init` will be initialized to the
|
||||
/// same method as the regular one. You must specify a separate native
|
||||
/// initializer to change initialization behavior based on what code is
|
||||
/// constructing the class.
|
||||
native_instance_init: Method<'gc>,
|
||||
|
||||
/// Instance traits for a given class.
|
||||
///
|
||||
/// These are accessed as normal instance properties; they should not be
|
||||
|
@ -232,6 +246,8 @@ impl<'gc> Class<'gc> {
|
|||
class_init: Method<'gc>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
) -> GcCell<'gc, Self> {
|
||||
let native_instance_init = instance_init.clone();
|
||||
|
||||
GcCell::allocate(
|
||||
mc,
|
||||
Self {
|
||||
|
@ -242,6 +258,7 @@ impl<'gc> Class<'gc> {
|
|||
interfaces: Vec::new(),
|
||||
instance_deriver: Deriver(implicit_deriver),
|
||||
instance_init,
|
||||
native_instance_init,
|
||||
instance_traits: Vec::new(),
|
||||
class_init,
|
||||
class_traits: Vec::new(),
|
||||
|
@ -310,6 +327,7 @@ impl<'gc> Class<'gc> {
|
|||
}
|
||||
|
||||
let instance_init = unit.load_method(abc_instance.init_method.0, mc)?;
|
||||
let native_instance_init = instance_init.clone();
|
||||
let class_init = unit.load_method(abc_class.init_method.0, mc)?;
|
||||
|
||||
let mut attributes = ClassAttributes::empty();
|
||||
|
@ -327,6 +345,7 @@ impl<'gc> Class<'gc> {
|
|||
interfaces,
|
||||
instance_deriver: Deriver(implicit_deriver),
|
||||
instance_init,
|
||||
native_instance_init,
|
||||
instance_traits: Vec::new(),
|
||||
class_init,
|
||||
class_traits: Vec::new(),
|
||||
|
@ -402,6 +421,7 @@ impl<'gc> Class<'gc> {
|
|||
interfaces: Vec::new(),
|
||||
instance_deriver: Deriver(implicit_deriver),
|
||||
instance_init: Method::from_builtin(|_, _, _| Ok(Value::Undefined)),
|
||||
native_instance_init: Method::from_builtin(|_, _, _| Ok(Value::Undefined)),
|
||||
instance_traits: traits,
|
||||
class_init: Method::from_builtin(|_, _, _| Ok(Value::Undefined)),
|
||||
class_traits: Vec::new(),
|
||||
|
@ -650,6 +670,16 @@ impl<'gc> Class<'gc> {
|
|||
self.instance_init.clone()
|
||||
}
|
||||
|
||||
/// Get this class's native-code instance initializer.
|
||||
pub fn native_instance_init(&self) -> Method<'gc> {
|
||||
self.native_instance_init.clone()
|
||||
}
|
||||
|
||||
/// Set a native-code instance initializer for this class.
|
||||
pub fn set_native_instance_init(&mut self, new_native_init: Method<'gc>) {
|
||||
self.native_instance_init = new_native_init;
|
||||
}
|
||||
|
||||
/// Get this class's class initializer.
|
||||
pub fn class_init(&self) -> Method<'gc> {
|
||||
self.class_init.clone()
|
||||
|
|
|
@ -704,6 +704,32 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
Err("Object is not callable".into())
|
||||
}
|
||||
|
||||
/// Call the instance initializer.
|
||||
fn call_initializer(
|
||||
self,
|
||||
_reciever: Option<Object<'gc>>,
|
||||
_arguments: &[Value<'gc>],
|
||||
_activation: &mut Activation<'_, 'gc, '_>,
|
||||
_base_constr: Option<Object<'gc>>,
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
Err("Object is not a Class constructor".into())
|
||||
}
|
||||
|
||||
/// Call the instance's native initializer.
|
||||
///
|
||||
/// The native initializer is called when native code needs to construct an
|
||||
/// object, or when supercalling into a parent constructor (as there are
|
||||
/// classes that cannot be constructed but can be supercalled).
|
||||
fn call_native_initializer(
|
||||
self,
|
||||
_reciever: Option<Object<'gc>>,
|
||||
_arguments: &[Value<'gc>],
|
||||
_activation: &mut Activation<'_, 'gc, '_>,
|
||||
_base_constr: Option<Object<'gc>>,
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
Err("Object is not a Class constructor".into())
|
||||
}
|
||||
|
||||
/// Call an instance method by name.
|
||||
///
|
||||
/// Intended to be called on the current base constructor used for
|
||||
|
|
|
@ -40,6 +40,9 @@ pub struct ClassObjectData<'gc> {
|
|||
|
||||
/// The instance constructor function
|
||||
instance_constr: Executable<'gc>,
|
||||
|
||||
/// The native instance constructor function
|
||||
native_instance_constr: Executable<'gc>,
|
||||
}
|
||||
|
||||
impl<'gc> ClassObject<'gc> {
|
||||
|
@ -126,6 +129,12 @@ impl<'gc> ClassObject<'gc> {
|
|||
None,
|
||||
activation.context.gc_context,
|
||||
);
|
||||
let native_instance_constr = Executable::from_method(
|
||||
class.read().native_instance_init(),
|
||||
scope,
|
||||
None,
|
||||
activation.context.gc_context,
|
||||
);
|
||||
|
||||
let mut constr: Object<'gc> = ClassObject(GcCell::allocate(
|
||||
activation.context.gc_context,
|
||||
|
@ -138,6 +147,7 @@ impl<'gc> ClassObject<'gc> {
|
|||
scope,
|
||||
base_class_constr,
|
||||
instance_constr,
|
||||
native_instance_constr,
|
||||
},
|
||||
))
|
||||
.into();
|
||||
|
@ -176,6 +186,8 @@ impl<'gc> ClassObject<'gc> {
|
|||
) -> Result<Object<'gc>, Error> {
|
||||
let instance_constr =
|
||||
Executable::from_method(class.read().instance_init(), scope, None, mc);
|
||||
let native_instance_constr =
|
||||
Executable::from_method(class.read().native_instance_init(), scope, None, mc);
|
||||
let mut base: Object<'gc> = ClassObject(GcCell::allocate(
|
||||
mc,
|
||||
ClassObjectData {
|
||||
|
@ -187,6 +199,7 @@ impl<'gc> ClassObject<'gc> {
|
|||
scope,
|
||||
base_class_constr,
|
||||
instance_constr,
|
||||
native_instance_constr,
|
||||
},
|
||||
))
|
||||
.into();
|
||||
|
@ -238,6 +251,30 @@ impl<'gc> TObject<'gc> for ClassObject<'gc> {
|
|||
instance_constr.exec(receiver, arguments, activation, base_constr, self.into())
|
||||
}
|
||||
|
||||
fn call_initializer(
|
||||
self,
|
||||
receiver: Option<Object<'gc>>,
|
||||
arguments: &[Value<'gc>],
|
||||
activation: &mut Activation<'_, '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_constr, self.into())
|
||||
}
|
||||
|
||||
fn call_native_initializer(
|
||||
self,
|
||||
receiver: Option<Object<'gc>>,
|
||||
arguments: &[Value<'gc>],
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
base_constr: Option<Object<'gc>>,
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
let native_instance_constr = self.0.read().native_instance_constr;
|
||||
|
||||
native_instance_constr.exec(receiver, arguments, activation, base_constr, self.into())
|
||||
}
|
||||
|
||||
fn construct(
|
||||
mut self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
|
@ -255,9 +292,8 @@ impl<'gc> TObject<'gc> for ClassObject<'gc> {
|
|||
.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)?;
|
||||
self.call_initializer(Some(instance), arguments, activation, Some(constr))?;
|
||||
|
||||
Ok(instance)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue