avm1: Implement Boolean class

This commit is contained in:
Mike Welsh 2020-01-20 00:32:32 -08:00
parent e71099edd5
commit d9e7a6a960
3 changed files with 122 additions and 20 deletions

View File

@ -10,6 +10,7 @@ use rand::Rng;
use std::f64;
mod array;
pub(crate) mod boolean;
mod color;
mod function;
mod key;
@ -65,19 +66,6 @@ pub fn random<'gc>(
}
}
pub fn boolean<'gc>(
avm: &mut Avm1<'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> {
if let Some(val) = args.get(0) {
Ok(val.as_bool(avm.current_swf_version()).into())
} else {
Ok(false.into())
}
}
pub fn is_nan<'gc>(
avm: &mut Avm1<'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>,
@ -131,6 +119,7 @@ pub struct SystemPrototypes<'gc> {
pub xml_node: Object<'gc>,
pub string: Object<'gc>,
pub number: Object<'gc>,
pub boolean: Object<'gc>,
}
unsafe impl<'gc> gc_arena::Collect for SystemPrototypes<'gc> {
@ -145,6 +134,7 @@ unsafe impl<'gc> gc_arena::Collect for SystemPrototypes<'gc> {
self.xml_node.trace(cc);
self.string.trace(cc);
self.number.trace(cc);
self.boolean.trace(cc);
}
}
@ -175,6 +165,8 @@ pub fn create_globals<'gc>(
let string_proto: Object<'gc> = string::create_proto(gc_context, object_proto, function_proto);
let number_proto: Object<'gc> = number::create_proto(gc_context, object_proto, function_proto);
let boolean_proto: Object<'gc> =
boolean::create_proto(gc_context, object_proto, function_proto);
//TODO: These need to be constructors and should also set `.prototype` on each one
let object = ScriptObject::function(
@ -234,6 +226,8 @@ pub fn create_globals<'gc>(
);
let string = string::create_string_object(gc_context, Some(string_proto), Some(function_proto));
let number = number::create_number_object(gc_context, Some(number_proto), Some(function_proto));
let boolean =
boolean::create_boolean_object(gc_context, Some(boolean_proto), Some(function_proto));
let listeners = SystemListeners::new(gc_context, Some(array_proto));
@ -249,13 +243,8 @@ pub fn create_globals<'gc>(
globals.define_value(gc_context, "XML", xml.into(), EnumSet::empty());
globals.define_value(gc_context, "String", string.into(), EnumSet::empty());
globals.define_value(gc_context, "Number", number.into(), EnumSet::empty());
globals.force_set_function(
"Boolean",
boolean,
gc_context,
EnumSet::empty(),
Some(function_proto),
);
globals.define_value(gc_context, "Boolean", boolean.into(), EnumSet::empty());
globals.define_value(
gc_context,
"Math",
@ -352,6 +341,7 @@ pub fn create_globals<'gc>(
xml_node: xmlnode_proto,
string: string_proto,
number: number_proto,
boolean: boolean_proto,
},
globals.into(),
listeners,

View File

@ -0,0 +1,107 @@
//! `Boolean` class impl
use crate::avm1::function::Executable;
use crate::avm1::return_value::ReturnValue;
use crate::avm1::value_object::ValueObject;
use crate::avm1::{Avm1, Error, Object, ScriptObject, TObject, Value};
use crate::context::UpdateContext;
use enumset::EnumSet;
use gc_arena::MutationContext;
/// `Boolean` constructor/function
pub fn boolean<'gc>(
avm: &mut Avm1<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> {
let (ret_value, cons_value) = if let Some(val) = args.get(0) {
let b = Value::Bool(val.as_bool(avm.current_swf_version()));
(b.clone(), b)
} else {
(Value::Undefined, Value::Bool(false))
};
// If called from a constructor, populate `this`.
if let Some(mut vbox) = this.as_value_object() {
vbox.replace_value(context.gc_context, cons_value);
}
// If called as a function, return the value.
// Boolean() with no argument returns undefined.
Ok(ret_value.into())
}
pub fn create_boolean_object<'gc>(
gc_context: MutationContext<'gc, '_>,
boolean_proto: Option<Object<'gc>>,
fn_proto: Option<Object<'gc>>,
) -> Object<'gc> {
ScriptObject::function(
gc_context,
Executable::Native(boolean),
fn_proto,
boolean_proto,
)
}
/// Creates `Boolean.prototype`.
pub fn create_proto<'gc>(
gc_context: MutationContext<'gc, '_>,
proto: Object<'gc>,
fn_proto: Object<'gc>,
) -> Object<'gc> {
let boolean_proto = ValueObject::empty_box(gc_context, Some(proto));
let mut object = boolean_proto.as_script_object().unwrap();
object.force_set_function(
"toString",
to_string,
gc_context,
EnumSet::empty(),
Some(fn_proto),
);
object.force_set_function(
"valueOf",
value_of,
gc_context,
EnumSet::empty(),
Some(fn_proto),
);
boolean_proto
}
pub fn to_string<'gc>(
_avm: &mut Avm1<'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> {
if let Some(vbox) = this.as_value_object() {
// Must be a bool.
// Boolean.prototype.toString.call(x) returns undefined for non-bools.
if let Value::Bool(b) = vbox.unbox() {
return Ok(b.to_string().into());
}
}
Ok(Value::Undefined.into())
}
pub fn value_of<'gc>(
_avm: &mut Avm1<'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> {
if let Some(vbox) = this.as_value_object() {
// Must be a bool.
// Boolean.prototype.valueOf.call(x) returns undefined for non-bools.
if let Value::Bool(b) = vbox.unbox() {
return Ok(b.into());
}
}
Ok(Value::Undefined.into())
}

View File

@ -47,6 +47,7 @@ impl<'gc> ValueObject<'gc> {
ob
} else {
let proto = match &value {
Value::Bool(_) => Some(avm.prototypes.boolean),
Value::Number(_) => Some(avm.prototypes.number),
Value::String(_) => Some(avm.prototypes.string),
_ => None,
@ -62,6 +63,10 @@ impl<'gc> ValueObject<'gc> {
// Constructor populates the boxed object with the value.
match &value {
Value::Bool(_) => {
let _ =
crate::avm1::globals::boolean::boolean(avm, context, obj.into(), &[value]);
}
Value::Number(_) => {
let _ =
crate::avm1::globals::number::number(avm, context, obj.into(), &[value]);