core: Split construct and call for function objects
This commit is contained in:
parent
337e3292dd
commit
e83dbf7327
|
@ -1799,7 +1799,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
|
|||
);
|
||||
}
|
||||
|
||||
constructor.call("[ctor]", self, context, this, None, &args)?;
|
||||
constructor.construct("[ctor]", self, context, this, None, &args)?;
|
||||
|
||||
self.avm.push(this);
|
||||
|
||||
|
|
|
@ -419,6 +419,8 @@ pub struct FunctionObject<'gc> {
|
|||
struct FunctionObjectData<'gc> {
|
||||
/// The code that will be invoked when this object is called.
|
||||
function: Option<Executable<'gc>>,
|
||||
/// The code that will be invoked when this object is constructed.
|
||||
constructor: Option<Executable<'gc>>,
|
||||
|
||||
/// The value to be returned by `toString` and `valueOf`.
|
||||
primitive: Value<'gc>,
|
||||
|
@ -428,24 +430,29 @@ impl<'gc> FunctionObject<'gc> {
|
|||
/// Construct a function sans prototype.
|
||||
pub fn bare_function(
|
||||
gc_context: MutationContext<'gc, '_>,
|
||||
function: impl Into<Executable<'gc>>,
|
||||
function: Option<impl Into<Executable<'gc>>>,
|
||||
constructor: Option<impl Into<Executable<'gc>>>,
|
||||
fn_proto: Option<Object<'gc>>,
|
||||
) -> Self {
|
||||
let base = ScriptObject::object(gc_context, fn_proto);
|
||||
|
||||
let func = function.map(|x| x.into());
|
||||
let cons = constructor.map(|x| x.into());
|
||||
|
||||
FunctionObject {
|
||||
base,
|
||||
data: GcCell::allocate(
|
||||
gc_context,
|
||||
FunctionObjectData {
|
||||
function: Some(function.into()),
|
||||
function: func,
|
||||
primitive: "[type Function]".into(),
|
||||
constructor: cons,
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a function from an executable and associated protos.
|
||||
/// Construct a function with any combination of regular and constructor parts.
|
||||
///
|
||||
/// Since prototypes need to link back to themselves, this function builds
|
||||
/// both objects itself and returns the function to you, fully allocated.
|
||||
|
@ -453,13 +460,14 @@ impl<'gc> FunctionObject<'gc> {
|
|||
/// `fn_proto` refers to the implicit proto of the function object, and the
|
||||
/// `prototype` refers to the explicit prototype of the function. If
|
||||
/// provided, the function and it's prototype will be linked to each other.
|
||||
pub fn function(
|
||||
fn allocate_function(
|
||||
context: MutationContext<'gc, '_>,
|
||||
function: impl Into<Executable<'gc>>,
|
||||
function: Option<impl Into<Executable<'gc>>>,
|
||||
constructor: Option<impl Into<Executable<'gc>>>,
|
||||
fn_proto: Option<Object<'gc>>,
|
||||
prototype: Option<Object<'gc>>,
|
||||
) -> Object<'gc> {
|
||||
let function = Self::bare_function(context, function, fn_proto).into();
|
||||
let function = Self::bare_function(context, function, constructor, fn_proto).into();
|
||||
|
||||
if let Some(p) = prototype {
|
||||
p.define_value(
|
||||
|
@ -473,6 +481,47 @@ impl<'gc> FunctionObject<'gc> {
|
|||
|
||||
function
|
||||
}
|
||||
|
||||
/// Construct a regular function from an executable and associated protos.
|
||||
pub fn function(
|
||||
context: MutationContext<'gc, '_>,
|
||||
function: impl Into<Executable<'gc>>,
|
||||
fn_proto: Option<Object<'gc>>,
|
||||
prototype: Option<Object<'gc>>,
|
||||
) -> Object<'gc> {
|
||||
// Avoid type inference issues
|
||||
let none: Option<Executable> = None;
|
||||
Self::allocate_function(context, Some(function), none, fn_proto, prototype)
|
||||
}
|
||||
|
||||
/// Construct a constructor function from an executable and associated protos.
|
||||
pub fn constructor(
|
||||
context: MutationContext<'gc, '_>,
|
||||
constructor: impl Into<Executable<'gc>>,
|
||||
fn_proto: Option<Object<'gc>>,
|
||||
prototype: Option<Object<'gc>>,
|
||||
) -> Object<'gc> {
|
||||
// Avoid type inference issues
|
||||
let none: Option<Executable> = None;
|
||||
Self::allocate_function(context, none, Some(constructor), fn_proto, prototype)
|
||||
}
|
||||
|
||||
/// Construct a regular and constructor function from an executable and associated protos.
|
||||
pub fn function_and_constructor(
|
||||
context: MutationContext<'gc, '_>,
|
||||
function: impl Into<Executable<'gc>>,
|
||||
constructor: impl Into<Executable<'gc>>,
|
||||
fn_proto: Option<Object<'gc>>,
|
||||
prototype: Option<Object<'gc>>,
|
||||
) -> Object<'gc> {
|
||||
Self::allocate_function(
|
||||
context,
|
||||
Some(function),
|
||||
Some(constructor),
|
||||
fn_proto,
|
||||
prototype,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
||||
|
@ -521,6 +570,32 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
|||
}
|
||||
}
|
||||
|
||||
fn construct(
|
||||
&self,
|
||||
name: &str,
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: Object<'gc>,
|
||||
base_proto: Option<Object<'gc>>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
println!("Constructing a function {}: {:?}", name, &self.data.read());
|
||||
if let Some(exec) = &self.data.read().constructor {
|
||||
exec.exec(
|
||||
name,
|
||||
activation,
|
||||
context,
|
||||
this,
|
||||
base_proto,
|
||||
args,
|
||||
ExecutionReason::FunctionCall,
|
||||
(*self).into(),
|
||||
)
|
||||
} else {
|
||||
self.call(name, activation, context, this, base_proto, args)
|
||||
}
|
||||
}
|
||||
|
||||
fn call_setter(
|
||||
&self,
|
||||
name: &str,
|
||||
|
@ -547,6 +622,7 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
|||
FunctionObjectData {
|
||||
function: None,
|
||||
primitive: "[type Function]".into(),
|
||||
constructor: None,
|
||||
},
|
||||
),
|
||||
};
|
||||
|
|
|
@ -282,74 +282,74 @@ pub fn create_globals<'gc>(
|
|||
let context_menu_item_proto =
|
||||
context_menu_item::create_proto(gc_context, object_proto, function_proto);
|
||||
|
||||
let button = FunctionObject::function(
|
||||
let button = FunctionObject::constructor(
|
||||
gc_context,
|
||||
Executable::Native(button::constructor),
|
||||
Some(function_proto),
|
||||
Some(button_proto),
|
||||
);
|
||||
let color = FunctionObject::function(
|
||||
let color = FunctionObject::constructor(
|
||||
gc_context,
|
||||
Executable::Native(color::constructor),
|
||||
Some(function_proto),
|
||||
Some(color_proto),
|
||||
);
|
||||
let error = FunctionObject::function(
|
||||
let error = FunctionObject::constructor(
|
||||
gc_context,
|
||||
Executable::Native(error::constructor),
|
||||
Some(function_proto),
|
||||
Some(error_proto),
|
||||
);
|
||||
let function = FunctionObject::function(
|
||||
let function = FunctionObject::constructor(
|
||||
gc_context,
|
||||
Executable::Native(function::constructor),
|
||||
Some(function_proto),
|
||||
Some(function_proto),
|
||||
);
|
||||
let load_vars = FunctionObject::function(
|
||||
let load_vars = FunctionObject::constructor(
|
||||
gc_context,
|
||||
Executable::Native(load_vars::constructor),
|
||||
Some(function_proto),
|
||||
Some(load_vars_proto),
|
||||
);
|
||||
let movie_clip = FunctionObject::function(
|
||||
let movie_clip = FunctionObject::constructor(
|
||||
gc_context,
|
||||
Executable::Native(movie_clip::constructor),
|
||||
Some(function_proto),
|
||||
Some(movie_clip_proto),
|
||||
);
|
||||
let movie_clip_loader = FunctionObject::function(
|
||||
let movie_clip_loader = FunctionObject::constructor(
|
||||
gc_context,
|
||||
Executable::Native(movie_clip_loader::constructor),
|
||||
Some(function_proto),
|
||||
Some(movie_clip_loader_proto),
|
||||
);
|
||||
let sound = FunctionObject::function(
|
||||
let sound = FunctionObject::constructor(
|
||||
gc_context,
|
||||
Executable::Native(sound::constructor),
|
||||
Some(function_proto),
|
||||
Some(sound_proto),
|
||||
);
|
||||
let text_field = FunctionObject::function(
|
||||
let text_field = FunctionObject::constructor(
|
||||
gc_context,
|
||||
Executable::Native(text_field::constructor),
|
||||
Some(function_proto),
|
||||
Some(text_field_proto),
|
||||
);
|
||||
let text_format = FunctionObject::function(
|
||||
let text_format = FunctionObject::constructor(
|
||||
gc_context,
|
||||
Executable::Native(text_format::constructor),
|
||||
Some(function_proto),
|
||||
Some(text_format_proto),
|
||||
);
|
||||
let array = array::create_array_object(gc_context, Some(array_proto), Some(function_proto));
|
||||
let xmlnode = FunctionObject::function(
|
||||
let xmlnode = FunctionObject::constructor(
|
||||
gc_context,
|
||||
Executable::Native(xml::xmlnode_constructor),
|
||||
Some(function_proto),
|
||||
Some(xmlnode_proto),
|
||||
);
|
||||
let xml = FunctionObject::function(
|
||||
let xml = FunctionObject::constructor(
|
||||
gc_context,
|
||||
Executable::Native(xml::xml_constructor),
|
||||
Some(function_proto),
|
||||
|
@ -434,7 +434,7 @@ pub fn create_globals<'gc>(
|
|||
globals.define_value(
|
||||
gc_context,
|
||||
"ContextMenu",
|
||||
FunctionObject::function(
|
||||
FunctionObject::constructor(
|
||||
gc_context,
|
||||
Executable::Native(context_menu::constructor),
|
||||
Some(function_proto),
|
||||
|
@ -447,7 +447,7 @@ pub fn create_globals<'gc>(
|
|||
globals.define_value(
|
||||
gc_context,
|
||||
"ContextMenuItem",
|
||||
FunctionObject::function(
|
||||
FunctionObject::constructor(
|
||||
gc_context,
|
||||
Executable::Native(context_menu_item::constructor),
|
||||
Some(function_proto),
|
||||
|
|
|
@ -38,7 +38,7 @@ pub fn create_array_object<'gc>(
|
|||
array_proto: Option<Object<'gc>>,
|
||||
fn_proto: Option<Object<'gc>>,
|
||||
) -> Object<'gc> {
|
||||
let array = FunctionObject::function(
|
||||
let array = FunctionObject::constructor(
|
||||
gc_context,
|
||||
Executable::Native(constructor),
|
||||
fn_proto,
|
||||
|
@ -87,7 +87,7 @@ pub fn create_array_object<'gc>(
|
|||
array
|
||||
}
|
||||
|
||||
/// Implements `Array`
|
||||
/// Implements `Array` constructor
|
||||
pub fn constructor<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
|
|
|
@ -9,25 +9,40 @@ use crate::context::UpdateContext;
|
|||
use enumset::EnumSet;
|
||||
use gc_arena::MutationContext;
|
||||
|
||||
/// `Boolean` constructor/function
|
||||
pub fn boolean<'gc>(
|
||||
/// `Boolean` constructor
|
||||
pub fn constructor<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let (ret_value, cons_value) = if let Some(val) = args.get(0) {
|
||||
let b = Value::Bool(val.as_bool(activation.current_swf_version()));
|
||||
(b.clone(), b)
|
||||
let cons_value = if let Some(val) = args.get(0) {
|
||||
Value::Bool(val.as_bool(activation.current_swf_version()))
|
||||
} else {
|
||||
(Value::Undefined, Value::Bool(false))
|
||||
Value::Bool(false)
|
||||
};
|
||||
|
||||
// If called from a constructor, populate `this`.
|
||||
// Called from a constructor, populate `this`.
|
||||
if let Some(mut vbox) = this.as_value_object() {
|
||||
vbox.replace_value(context.gc_context, cons_value);
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
/// `Boolean` function
|
||||
pub fn boolean_function<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let ret_value = if let Some(val) = args.get(0) {
|
||||
Value::Bool(val.as_bool(activation.current_swf_version()))
|
||||
} else {
|
||||
Value::Undefined
|
||||
};
|
||||
|
||||
// If called as a function, return the value.
|
||||
// Boolean() with no argument returns undefined.
|
||||
Ok(ret_value)
|
||||
|
@ -38,9 +53,10 @@ pub fn create_boolean_object<'gc>(
|
|||
boolean_proto: Option<Object<'gc>>,
|
||||
fn_proto: Option<Object<'gc>>,
|
||||
) -> Object<'gc> {
|
||||
FunctionObject::function(
|
||||
FunctionObject::function_and_constructor(
|
||||
gc_context,
|
||||
Executable::Native(boolean),
|
||||
Executable::Native(boolean_function),
|
||||
Executable::Native(constructor),
|
||||
fn_proto,
|
||||
boolean_proto,
|
||||
)
|
||||
|
|
|
@ -10,7 +10,7 @@ use crate::context::UpdateContext;
|
|||
use enumset::EnumSet;
|
||||
use gc_arena::MutationContext;
|
||||
|
||||
/// `Number` constructor/function
|
||||
/// `Number` constructor
|
||||
pub fn number<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
|
@ -28,6 +28,22 @@ pub fn number<'gc>(
|
|||
vbox.replace_value(context.gc_context, value.into());
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
/// `Number` function
|
||||
pub fn number_function<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let value = if let Some(val) = args.get(0) {
|
||||
val.coerce_to_f64(activation, context)?
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
// If Number is called as a function, return the value.
|
||||
Ok(value.into())
|
||||
}
|
||||
|
@ -37,8 +53,9 @@ pub fn create_number_object<'gc>(
|
|||
number_proto: Option<Object<'gc>>,
|
||||
fn_proto: Option<Object<'gc>>,
|
||||
) -> Object<'gc> {
|
||||
let number = FunctionObject::function(
|
||||
let number = FunctionObject::function_and_constructor(
|
||||
gc_context,
|
||||
Executable::Native(number_function),
|
||||
Executable::Native(number),
|
||||
fn_proto,
|
||||
number_proto,
|
||||
|
|
|
@ -372,7 +372,7 @@ pub fn create_object_object<'gc>(
|
|||
proto: Object<'gc>,
|
||||
fn_proto: Object<'gc>,
|
||||
) -> Object<'gc> {
|
||||
let object_function = FunctionObject::function(
|
||||
let object_function = FunctionObject::constructor(
|
||||
gc_context,
|
||||
Executable::Native(constructor),
|
||||
Some(fn_proto),
|
||||
|
|
|
@ -30,6 +30,22 @@ pub fn string<'gc>(
|
|||
vbox.replace_value(ac.gc_context, value.clone().into());
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
/// `String` function
|
||||
pub fn string_function<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let value = match args.get(0).cloned() {
|
||||
Some(Value::String(s)) => s,
|
||||
Some(v) => v.coerce_to_string(activation, ac)?,
|
||||
_ => AvmString::new(ac.gc_context, String::new()),
|
||||
};
|
||||
|
||||
Ok(value.into())
|
||||
}
|
||||
|
||||
|
@ -38,8 +54,9 @@ pub fn create_string_object<'gc>(
|
|||
string_proto: Option<Object<'gc>>,
|
||||
fn_proto: Option<Object<'gc>>,
|
||||
) -> Object<'gc> {
|
||||
let string = FunctionObject::function(
|
||||
let string = FunctionObject::function_and_constructor(
|
||||
gc_context,
|
||||
Executable::Native(string_function),
|
||||
Executable::Native(string),
|
||||
fn_proto,
|
||||
string_proto,
|
||||
|
|
|
@ -108,6 +108,19 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>>;
|
||||
|
||||
/// Construct the underlying object.
|
||||
fn construct(
|
||||
&self,
|
||||
_name: &str,
|
||||
_activation: &mut Activation<'_, 'gc>,
|
||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_this: Object<'gc>,
|
||||
_base_proto: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
/// Call a method on the object.
|
||||
///
|
||||
/// It is highly recommended to use this convenience method to perform
|
||||
|
|
|
@ -62,7 +62,7 @@ impl<'gc> ValueObject<'gc> {
|
|||
// Constructor populates the boxed object with the value.
|
||||
match &value {
|
||||
Value::Bool(_) => {
|
||||
let _ = crate::avm1::globals::boolean::boolean(
|
||||
let _ = crate::avm1::globals::boolean::constructor(
|
||||
activation,
|
||||
context,
|
||||
obj.into(),
|
||||
|
|
Loading…
Reference in New Issue