Very rudimentary/basic/not-good implementation of `String`, plus auto-boxing for primitive strings getting their methods taken.
This commit is contained in:
parent
4d1e49882b
commit
2f9d50cdb8
|
@ -19,6 +19,7 @@ pub(crate) mod movie_clip;
|
||||||
mod object;
|
mod object;
|
||||||
mod sound;
|
mod sound;
|
||||||
mod stage;
|
mod stage;
|
||||||
|
mod string;
|
||||||
pub(crate) mod text_field;
|
pub(crate) mod text_field;
|
||||||
mod xml;
|
mod xml;
|
||||||
|
|
||||||
|
@ -140,6 +141,7 @@ pub struct SystemPrototypes<'gc> {
|
||||||
pub text_field: Object<'gc>,
|
pub text_field: Object<'gc>,
|
||||||
pub array: Object<'gc>,
|
pub array: Object<'gc>,
|
||||||
pub xml_node: Object<'gc>,
|
pub xml_node: Object<'gc>,
|
||||||
|
pub string: Object<'gc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<'gc> gc_arena::Collect for SystemPrototypes<'gc> {
|
unsafe impl<'gc> gc_arena::Collect for SystemPrototypes<'gc> {
|
||||||
|
@ -152,6 +154,7 @@ unsafe impl<'gc> gc_arena::Collect for SystemPrototypes<'gc> {
|
||||||
self.text_field.trace(cc);
|
self.text_field.trace(cc);
|
||||||
self.array.trace(cc);
|
self.array.trace(cc);
|
||||||
self.xml_node.trace(cc);
|
self.xml_node.trace(cc);
|
||||||
|
self.string.trace(cc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,6 +183,8 @@ pub fn create_globals<'gc>(
|
||||||
|
|
||||||
let xml_proto: Object<'gc> = xml::create_xml_proto(gc_context, xmlnode_proto, function_proto);
|
let xml_proto: Object<'gc> = xml::create_xml_proto(gc_context, xmlnode_proto, function_proto);
|
||||||
|
|
||||||
|
let string_proto: Object<'gc> = string::create_proto(gc_context, object_proto, function_proto);
|
||||||
|
|
||||||
//TODO: These need to be constructors and should also set `.prototype` on each one
|
//TODO: These need to be constructors and should also set `.prototype` on each one
|
||||||
let object = ScriptObject::function(
|
let object = ScriptObject::function(
|
||||||
gc_context,
|
gc_context,
|
||||||
|
@ -236,6 +241,12 @@ pub fn create_globals<'gc>(
|
||||||
Some(function_proto),
|
Some(function_proto),
|
||||||
Some(xml_proto),
|
Some(xml_proto),
|
||||||
);
|
);
|
||||||
|
let string = ScriptObject::function(
|
||||||
|
gc_context,
|
||||||
|
Executable::Native(string::string_constructor),
|
||||||
|
Some(function_proto),
|
||||||
|
Some(string_proto),
|
||||||
|
);
|
||||||
|
|
||||||
let listeners = SystemListeners::new(gc_context, Some(array_proto));
|
let listeners = SystemListeners::new(gc_context, Some(array_proto));
|
||||||
|
|
||||||
|
@ -249,6 +260,7 @@ pub fn create_globals<'gc>(
|
||||||
globals.define_value(gc_context, "TextField", text_field.into(), EnumSet::empty());
|
globals.define_value(gc_context, "TextField", text_field.into(), EnumSet::empty());
|
||||||
globals.define_value(gc_context, "XMLNode", xmlnode.into(), EnumSet::empty());
|
globals.define_value(gc_context, "XMLNode", xmlnode.into(), EnumSet::empty());
|
||||||
globals.define_value(gc_context, "XML", xml.into(), EnumSet::empty());
|
globals.define_value(gc_context, "XML", xml.into(), EnumSet::empty());
|
||||||
|
globals.define_value(gc_context, "String", string.into(), EnumSet::empty());
|
||||||
globals.force_set_function(
|
globals.force_set_function(
|
||||||
"Number",
|
"Number",
|
||||||
number,
|
number,
|
||||||
|
@ -357,6 +369,7 @@ pub fn create_globals<'gc>(
|
||||||
text_field: text_field_proto,
|
text_field: text_field_proto,
|
||||||
array: array_proto,
|
array: array_proto,
|
||||||
xml_node: xmlnode_proto,
|
xml_node: xmlnode_proto,
|
||||||
|
string: string_proto,
|
||||||
},
|
},
|
||||||
globals.into(),
|
globals.into(),
|
||||||
listeners,
|
listeners,
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
//! `String` class impl
|
||||||
|
|
||||||
|
use crate::avm1::return_value::ReturnValue;
|
||||||
|
use crate::avm1::value_object::ValueObject;
|
||||||
|
use crate::avm1::{Avm1, Error, Object, TObject, Value};
|
||||||
|
use crate::context::UpdateContext;
|
||||||
|
use enumset::EnumSet;
|
||||||
|
use gc_arena::MutationContext;
|
||||||
|
|
||||||
|
/// `String` constructor
|
||||||
|
pub fn string_constructor<'gc>(
|
||||||
|
avm: &mut Avm1<'gc>,
|
||||||
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
|
this: Object<'gc>,
|
||||||
|
args: &[Value<'gc>],
|
||||||
|
) -> Result<ReturnValue<'gc>, Error> {
|
||||||
|
let arg = args
|
||||||
|
.get(0)
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or(Value::Undefined)
|
||||||
|
.coerce_to_string(avm, ac)?;
|
||||||
|
|
||||||
|
if let Some(mut vbox) = this.as_value_object() {
|
||||||
|
vbox.replace_value(ac.gc_context, arg.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Value::Undefined.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `String.toString` / `String.valueOf` impl
|
||||||
|
pub fn to_string_value_of<'gc>(
|
||||||
|
avm: &mut Avm1<'gc>,
|
||||||
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
|
this: Object<'gc>,
|
||||||
|
_args: &[Value<'gc>],
|
||||||
|
) -> Result<ReturnValue<'gc>, Error> {
|
||||||
|
if let Some(vbox) = this.as_value_object() {
|
||||||
|
return Ok(vbox.unbox().coerce_to_string(avm, ac)?.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: This normally falls back to `[object Object]` or `[type Function]`,
|
||||||
|
//implying that `toString` and `valueOf` are inherent object properties and
|
||||||
|
//not just methods.
|
||||||
|
Ok(Value::Undefined.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `String.toUpperCase` impl
|
||||||
|
pub fn to_upper_case<'gc>(
|
||||||
|
avm: &mut Avm1<'gc>,
|
||||||
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
|
this: Object<'gc>,
|
||||||
|
_args: &[Value<'gc>],
|
||||||
|
) -> Result<ReturnValue<'gc>, Error> {
|
||||||
|
let sval = Value::Object(this).coerce_to_string(avm, ac)?;
|
||||||
|
|
||||||
|
Ok(sval.to_uppercase().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `String.prototype` definition
|
||||||
|
pub fn create_proto<'gc>(
|
||||||
|
gc_context: MutationContext<'gc, '_>,
|
||||||
|
proto: Object<'gc>,
|
||||||
|
fn_proto: Object<'gc>,
|
||||||
|
) -> Object<'gc> {
|
||||||
|
let string_proto = ValueObject::empty_box(gc_context, Some(proto));
|
||||||
|
|
||||||
|
string_proto.as_script_object().unwrap().force_set_function(
|
||||||
|
"toString",
|
||||||
|
to_string_value_of,
|
||||||
|
gc_context,
|
||||||
|
EnumSet::empty(),
|
||||||
|
Some(fn_proto),
|
||||||
|
);
|
||||||
|
string_proto.as_script_object().unwrap().force_set_function(
|
||||||
|
"valueOf",
|
||||||
|
to_string_value_of,
|
||||||
|
gc_context,
|
||||||
|
EnumSet::empty(),
|
||||||
|
Some(fn_proto),
|
||||||
|
);
|
||||||
|
string_proto.as_script_object().unwrap().force_set_function(
|
||||||
|
"toUpperCase",
|
||||||
|
to_upper_case,
|
||||||
|
gc_context,
|
||||||
|
EnumSet::empty(),
|
||||||
|
Some(fn_proto),
|
||||||
|
);
|
||||||
|
|
||||||
|
string_proto
|
||||||
|
}
|
|
@ -42,16 +42,21 @@ impl<'gc> ValueObject<'gc> {
|
||||||
pub fn boxed(
|
pub fn boxed(
|
||||||
gc_context: MutationContext<'gc, '_>,
|
gc_context: MutationContext<'gc, '_>,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
_system_prototypes: &SystemPrototypes<'gc>,
|
system_prototypes: &SystemPrototypes<'gc>,
|
||||||
) -> Object<'gc> {
|
) -> Object<'gc> {
|
||||||
if let Value::Object(ob) = value {
|
if let Value::Object(ob) = value {
|
||||||
ob
|
ob
|
||||||
} else {
|
} else {
|
||||||
|
let proto = match value {
|
||||||
|
Value::String(_) => Some(system_prototypes.string),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
ValueObject(GcCell::allocate(
|
ValueObject(GcCell::allocate(
|
||||||
gc_context,
|
gc_context,
|
||||||
ValueObjectData {
|
ValueObjectData {
|
||||||
base: ScriptObject::object(gc_context, None),
|
base: ScriptObject::object(gc_context, proto),
|
||||||
value: value,
|
value,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
.into()
|
.into()
|
||||||
|
|
Loading…
Reference in New Issue