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?;
|
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.
|
/// 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.
|
/// Must be called each time a new class instance is constructed.
|
||||||
instance_init: Method<'gc>,
|
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.
|
/// Instance traits for a given class.
|
||||||
///
|
///
|
||||||
/// These are accessed as normal instance properties; they should not be
|
/// These are accessed as normal instance properties; they should not be
|
||||||
|
@ -232,6 +246,8 @@ impl<'gc> Class<'gc> {
|
||||||
class_init: Method<'gc>,
|
class_init: Method<'gc>,
|
||||||
mc: MutationContext<'gc, '_>,
|
mc: MutationContext<'gc, '_>,
|
||||||
) -> GcCell<'gc, Self> {
|
) -> GcCell<'gc, Self> {
|
||||||
|
let native_instance_init = instance_init.clone();
|
||||||
|
|
||||||
GcCell::allocate(
|
GcCell::allocate(
|
||||||
mc,
|
mc,
|
||||||
Self {
|
Self {
|
||||||
|
@ -242,6 +258,7 @@ impl<'gc> Class<'gc> {
|
||||||
interfaces: Vec::new(),
|
interfaces: Vec::new(),
|
||||||
instance_deriver: Deriver(implicit_deriver),
|
instance_deriver: Deriver(implicit_deriver),
|
||||||
instance_init,
|
instance_init,
|
||||||
|
native_instance_init,
|
||||||
instance_traits: Vec::new(),
|
instance_traits: Vec::new(),
|
||||||
class_init,
|
class_init,
|
||||||
class_traits: Vec::new(),
|
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 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 class_init = unit.load_method(abc_class.init_method.0, mc)?;
|
||||||
|
|
||||||
let mut attributes = ClassAttributes::empty();
|
let mut attributes = ClassAttributes::empty();
|
||||||
|
@ -327,6 +345,7 @@ impl<'gc> Class<'gc> {
|
||||||
interfaces,
|
interfaces,
|
||||||
instance_deriver: Deriver(implicit_deriver),
|
instance_deriver: Deriver(implicit_deriver),
|
||||||
instance_init,
|
instance_init,
|
||||||
|
native_instance_init,
|
||||||
instance_traits: Vec::new(),
|
instance_traits: Vec::new(),
|
||||||
class_init,
|
class_init,
|
||||||
class_traits: Vec::new(),
|
class_traits: Vec::new(),
|
||||||
|
@ -402,6 +421,7 @@ impl<'gc> Class<'gc> {
|
||||||
interfaces: Vec::new(),
|
interfaces: Vec::new(),
|
||||||
instance_deriver: Deriver(implicit_deriver),
|
instance_deriver: Deriver(implicit_deriver),
|
||||||
instance_init: Method::from_builtin(|_, _, _| Ok(Value::Undefined)),
|
instance_init: Method::from_builtin(|_, _, _| Ok(Value::Undefined)),
|
||||||
|
native_instance_init: Method::from_builtin(|_, _, _| Ok(Value::Undefined)),
|
||||||
instance_traits: traits,
|
instance_traits: traits,
|
||||||
class_init: Method::from_builtin(|_, _, _| Ok(Value::Undefined)),
|
class_init: Method::from_builtin(|_, _, _| Ok(Value::Undefined)),
|
||||||
class_traits: Vec::new(),
|
class_traits: Vec::new(),
|
||||||
|
@ -650,6 +670,16 @@ impl<'gc> Class<'gc> {
|
||||||
self.instance_init.clone()
|
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.
|
/// Get this class's class initializer.
|
||||||
pub fn class_init(&self) -> Method<'gc> {
|
pub fn class_init(&self) -> Method<'gc> {
|
||||||
self.class_init.clone()
|
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())
|
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.
|
/// Call an instance method by name.
|
||||||
///
|
///
|
||||||
/// Intended to be called on the current base constructor used for
|
/// Intended to be called on the current base constructor used for
|
||||||
|
|
|
@ -40,6 +40,9 @@ pub struct ClassObjectData<'gc> {
|
||||||
|
|
||||||
/// The instance constructor function
|
/// The instance constructor function
|
||||||
instance_constr: Executable<'gc>,
|
instance_constr: Executable<'gc>,
|
||||||
|
|
||||||
|
/// The native instance constructor function
|
||||||
|
native_instance_constr: Executable<'gc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'gc> ClassObject<'gc> {
|
impl<'gc> ClassObject<'gc> {
|
||||||
|
@ -126,6 +129,12 @@ impl<'gc> ClassObject<'gc> {
|
||||||
None,
|
None,
|
||||||
activation.context.gc_context,
|
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(
|
let mut constr: Object<'gc> = ClassObject(GcCell::allocate(
|
||||||
activation.context.gc_context,
|
activation.context.gc_context,
|
||||||
|
@ -138,6 +147,7 @@ impl<'gc> ClassObject<'gc> {
|
||||||
scope,
|
scope,
|
||||||
base_class_constr,
|
base_class_constr,
|
||||||
instance_constr,
|
instance_constr,
|
||||||
|
native_instance_constr,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
.into();
|
.into();
|
||||||
|
@ -176,6 +186,8 @@ impl<'gc> ClassObject<'gc> {
|
||||||
) -> Result<Object<'gc>, Error> {
|
) -> Result<Object<'gc>, Error> {
|
||||||
let instance_constr =
|
let instance_constr =
|
||||||
Executable::from_method(class.read().instance_init(), scope, None, mc);
|
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(
|
let mut base: Object<'gc> = ClassObject(GcCell::allocate(
|
||||||
mc,
|
mc,
|
||||||
ClassObjectData {
|
ClassObjectData {
|
||||||
|
@ -187,6 +199,7 @@ impl<'gc> ClassObject<'gc> {
|
||||||
scope,
|
scope,
|
||||||
base_class_constr,
|
base_class_constr,
|
||||||
instance_constr,
|
instance_constr,
|
||||||
|
native_instance_constr,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
.into();
|
.into();
|
||||||
|
@ -238,6 +251,30 @@ impl<'gc> TObject<'gc> for ClassObject<'gc> {
|
||||||
instance_constr.exec(receiver, arguments, activation, base_constr, self.into())
|
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(
|
fn construct(
|
||||||
mut self,
|
mut self,
|
||||||
activation: &mut Activation<'_, 'gc, '_>,
|
activation: &mut Activation<'_, 'gc, '_>,
|
||||||
|
@ -255,9 +292,8 @@ impl<'gc> TObject<'gc> for ClassObject<'gc> {
|
||||||
.coerce_to_object(activation)?;
|
.coerce_to_object(activation)?;
|
||||||
|
|
||||||
let instance = deriver(constr, prototype, 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)
|
Ok(instance)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue