avm1: Add Avm1String which wraps Gc<String>

This commit is contained in:
Nathan Adams 2020-07-13 00:16:48 +02:00 committed by Mike Welsh
parent 016b9db3c5
commit ad733f2f21
36 changed files with 294 additions and 226 deletions

View File

@ -25,6 +25,7 @@ pub mod globals;
pub mod object; pub mod object;
mod property; mod property;
mod scope; mod scope;
mod string;
mod timer; mod timer;
mod value; mod value;
@ -41,6 +42,7 @@ pub use object::stage_object::StageObject;
pub use object::{Object, ObjectPtr, TObject}; pub use object::{Object, ObjectPtr, TObject};
use scope::Scope; use scope::Scope;
use smallvec::alloc::borrow::Cow; use smallvec::alloc::borrow::Cow;
pub use string::Avm1String;
pub use timer::Timers; pub use timer::Timers;
pub use value::Value; pub use value::Value;

View File

@ -4,7 +4,9 @@ use crate::avm1::object::{value_object, Object, TObject};
use crate::avm1::property::Attribute; use crate::avm1::property::Attribute;
use crate::avm1::scope::Scope; use crate::avm1::scope::Scope;
use crate::avm1::value::f64_to_wrapping_u32; use crate::avm1::value::f64_to_wrapping_u32;
use crate::avm1::{fscommand, globals, scope, skip_actions, start_drag, Avm1, ScriptObject, Value}; use crate::avm1::{
fscommand, globals, scope, skip_actions, start_drag, Avm1, Avm1String, ScriptObject, Value,
};
use crate::backend::navigator::{NavigationMethod, RequestOptions}; use crate::backend::navigator::{NavigationMethod, RequestOptions};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use crate::display_object::{DisplayObject, MovieClip, TDisplayObject}; use crate::display_object::{DisplayObject, MovieClip, TDisplayObject};
@ -587,11 +589,11 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
if let Value::String(a) = a { if let Value::String(a) = a {
let mut s = b.coerce_to_string(self, context)?.to_string(); let mut s = b.coerce_to_string(self, context)?.to_string();
s.push_str(&a); s.push_str(&a);
self.avm.push(Gc::allocate(context.gc_context, s)); self.avm.push(Avm1String::new(context.gc_context, s));
} else if let Value::String(b) = b { } else if let Value::String(b) = b {
let mut b = b.to_string(); let mut b = b.to_string();
b.push_str(&a.coerce_to_string(self, context)?); b.push_str(&a.coerce_to_string(self, context)?);
self.avm.push(Gc::allocate(context.gc_context, b)); self.avm.push(Avm1String::new(context.gc_context, b));
} else { } else {
let result = b.coerce_to_f64(self, context)? + a.coerce_to_f64(self, context)?; let result = b.coerce_to_f64(self, context)? + a.coerce_to_f64(self, context)?;
self.avm.push(result); self.avm.push(result);
@ -620,7 +622,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
// TODO(Herschel): Results on incorrect operands? // TODO(Herschel): Results on incorrect operands?
let val = (self.avm.pop().coerce_to_f64(self, context)? as u8) as char; let val = (self.avm.pop().coerce_to_f64(self, context)? as u8) as char;
self.avm self.avm
.push(Gc::allocate(context.gc_context, val.to_string())); .push(Avm1String::new(context.gc_context, val.to_string()));
Ok(FrameControl::Continue) Ok(FrameControl::Continue)
} }
@ -1059,7 +1061,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
match object { match object {
Value::Object(ob) => { Value::Object(ob) => {
for k in ob.get_keys(self).into_iter().rev() { for k in ob.get_keys(self).into_iter().rev() {
self.avm.push(Gc::allocate(context.gc_context, k)); self.avm.push(Avm1String::new(context.gc_context, k));
} }
} }
_ => log::error!("Cannot enumerate properties of {}", name), _ => log::error!("Cannot enumerate properties of {}", name),
@ -1078,7 +1080,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
if let Value::Object(object) = value { if let Value::Object(object) = value {
for k in object.get_keys(self).into_iter().rev() { for k in object.get_keys(self).into_iter().rev() {
self.avm.push(Gc::allocate(context.gc_context, k)); self.avm.push(Avm1String::new(context.gc_context, k));
} }
} else { } else {
log::warn!("Cannot enumerate {:?}", value); log::warn!("Cannot enumerate {:?}", value);
@ -1572,7 +1574,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
match result { match result {
Ok(val) => self Ok(val) => self
.avm .avm
.push(Gc::allocate(context.gc_context, val.to_string())), .push(Avm1String::new(context.gc_context, val.to_string())),
Err(e) => log::warn!("Couldn't parse char for action_mb_ascii_to_char: {}", e), Err(e) => log::warn!("Couldn't parse char for action_mb_ascii_to_char: {}", e),
} }
Ok(FrameControl::Continue) Ok(FrameControl::Continue)
@ -1600,7 +1602,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
let val = self.avm.pop(); let val = self.avm.pop();
let s = val.coerce_to_string(self, context)?; let s = val.coerce_to_string(self, context)?;
let result = s[len..len + start].to_string(); // TODO(Herschel): Flash uses UTF-16 internally. let result = s[len..len + start].to_string(); // TODO(Herschel): Flash uses UTF-16 internally.
self.avm.push(Gc::allocate(context.gc_context, result)); self.avm.push(Avm1String::new(context.gc_context, result));
Ok(FrameControl::Continue) Ok(FrameControl::Continue)
} }
@ -1826,11 +1828,11 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
SwfValue::Int(v) => f64::from(*v).into(), SwfValue::Int(v) => f64::from(*v).into(),
SwfValue::Float(v) => f64::from(*v).into(), SwfValue::Float(v) => f64::from(*v).into(),
SwfValue::Double(v) => (*v).into(), SwfValue::Double(v) => (*v).into(),
SwfValue::Str(v) => Gc::allocate(context.gc_context, (*v).to_string()).into(), SwfValue::Str(v) => Avm1String::new(context.gc_context, (*v).to_string()).into(),
SwfValue::Register(v) => self.current_register(*v), SwfValue::Register(v) => self.current_register(*v),
SwfValue::ConstantPool(i) => { SwfValue::ConstantPool(i) => {
if let Some(value) = self.constant_pool().read().get(*i as usize) { if let Some(value) = self.constant_pool().read().get(*i as usize) {
Gc::allocate(context.gc_context, value.to_string()).into() Avm1String::new(context.gc_context, value.to_string()).into()
} else { } else {
log::warn!( log::warn!(
"ActionPush: Constant pool index {} out of range (len = {})", "ActionPush: Constant pool index {} out of range (len = {})",
@ -2123,7 +2125,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
let a = self.avm.pop(); let a = self.avm.pop();
let mut b = self.avm.pop().coerce_to_string(self, context)?.to_string(); let mut b = self.avm.pop().coerce_to_string(self, context)?.to_string();
b.push_str(&a.coerce_to_string(self, context)?); b.push_str(&a.coerce_to_string(self, context)?);
self.avm.push(Gc::allocate(context.gc_context, b)); self.avm.push(Avm1String::new(context.gc_context, b));
Ok(FrameControl::Continue) Ok(FrameControl::Continue)
} }
@ -2158,7 +2160,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
.take(len) .take(len)
.map(|c| c as char) .map(|c| c as char)
.collect::<String>(); .collect::<String>();
self.avm.push(Gc::allocate(context.gc_context, result)); self.avm.push(Avm1String::new(context.gc_context, result));
Ok(FrameControl::Continue) Ok(FrameControl::Continue)
} }
@ -2263,7 +2265,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
let val = self.avm.pop(); let val = self.avm.pop();
let string = val.coerce_to_string(self, context)?; let string = val.coerce_to_string(self, context)?;
self.avm self.avm
.push(Gc::allocate(context.gc_context, string.to_string())); .push(Avm1String::new(context.gc_context, string.to_string()));
Ok(FrameControl::Continue) Ok(FrameControl::Continue)
} }
@ -2289,7 +2291,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
) -> Result<FrameControl<'gc>, Error<'gc>> { ) -> Result<FrameControl<'gc>, Error<'gc>> {
let type_of = self.avm.pop().type_of(); let type_of = self.avm.pop().type_of();
self.avm self.avm
.push(Gc::allocate(context.gc_context, type_of.to_string())); .push(Avm1String::new(context.gc_context, type_of.to_string()));
Ok(FrameControl::Continue) Ok(FrameControl::Continue)
} }

View File

@ -198,8 +198,7 @@ mod tests {
use super::*; use super::*;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::test_utils::with_avm; use crate::avm1::test_utils::with_avm;
use crate::avm1::ScriptObject; use crate::avm1::{Avm1String, ScriptObject};
use gc_arena::Gc;
#[test] #[test]
fn dump_undefined() { fn dump_undefined() {
@ -258,7 +257,7 @@ mod tests {
with_avm(19, |activation, context, _root| -> Result<(), Error> { with_avm(19, |activation, context, _root| -> Result<(), Error> {
assert_eq!( assert_eq!(
VariableDumper::dump( VariableDumper::dump(
&Value::String(Gc::allocate(context.gc_context, "".to_string())), &Value::String(Avm1String::new(context.gc_context, "".to_string())),
" ", " ",
activation, activation,
context context
@ -267,7 +266,10 @@ mod tests {
); );
assert_eq!( assert_eq!(
VariableDumper::dump( VariableDumper::dump(
&Value::String(Gc::allocate(context.gc_context, "HELLO WORLD".to_string())), &Value::String(Avm1String::new(
context.gc_context,
"HELLO WORLD".to_string()
)),
" ", " ",
activation, activation,
context context
@ -276,7 +278,7 @@ mod tests {
); );
assert_eq!( assert_eq!(
VariableDumper::dump( VariableDumper::dump(
&Value::String(Gc::allocate( &Value::String(Avm1String::new(
context.gc_context, context.gc_context,
"Escape \"this\" string\nplease! \u{0008}\u{000C}\n\r\t\"\\".to_string() "Escape \"this\" string\nplease! \u{0008}\u{000C}\n\r\t\"\\".to_string()
)), )),
@ -310,7 +312,7 @@ mod tests {
object.set("self", object.into(), activation, context)?; object.set("self", object.into(), activation, context)?;
object.set( object.set(
"test", "test",
Value::String(Gc::allocate(context.gc_context, "value".to_string())), Value::String(Avm1String::new(context.gc_context, "value".to_string())),
activation, activation,
context, context,
)?; )?;
@ -333,7 +335,7 @@ mod tests {
object.set("self", object.into(), activation, context)?; object.set("self", object.into(), activation, context)?;
object.set( object.set(
"test", "test",
Value::String(Gc::allocate(context.gc_context, "value".to_string())), Value::String(Avm1String::new(context.gc_context, "value".to_string())),
activation, activation,
context, context,
)?; )?;

View File

@ -6,7 +6,7 @@ use crate::avm1::object::super_object::SuperObject;
use crate::avm1::property::{Attribute, Attribute::*}; use crate::avm1::property::{Attribute, Attribute::*};
use crate::avm1::scope::Scope; use crate::avm1::scope::Scope;
use crate::avm1::value::Value; use crate::avm1::value::Value;
use crate::avm1::{Object, ObjectPtr, ScriptObject, TObject, UpdateContext}; use crate::avm1::{Avm1String, Object, ObjectPtr, ScriptObject, TObject, UpdateContext};
use crate::display_object::{DisplayObject, TDisplayObject}; use crate::display_object::{DisplayObject, TDisplayObject};
use crate::tag_utils::SwfSlice; use crate::tag_utils::SwfSlice;
use enumset::EnumSet; use enumset::EnumSet;
@ -448,7 +448,7 @@ impl<'gc> FunctionObject<'gc> {
gc_context, gc_context,
FunctionObjectData { FunctionObjectData {
function: Some(function.into()), function: Some(function.into()),
primitive: Gc::allocate(gc_context, "[type Function]".to_string()).into(), primitive: Avm1String::new(gc_context, "[type Function]".to_string()).into(),
}, },
), ),
} }
@ -553,7 +553,7 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
context.gc_context, context.gc_context,
FunctionObjectData { FunctionObjectData {
function: None, function: None,
primitive: Gc::allocate(context.gc_context, "[type Function]".to_string()) primitive: Avm1String::new(context.gc_context, "[type Function]".to_string())
.into(), .into(),
}, },
), ),

View File

@ -63,8 +63,8 @@ pub fn getURL<'a, 'gc>(
None None
}; };
let method = match args.get(2) { let method = match args.get(2) {
Some(Value::String(s)) if **s == "GET" => Some(NavigationMethod::GET), Some(Value::String(s)) if *s == "GET" => Some(NavigationMethod::GET),
Some(Value::String(s)) if **s == "POST" => Some(NavigationMethod::POST), Some(Value::String(s)) if *s == "POST" => Some(NavigationMethod::POST),
_ => None, _ => None,
}; };
let vars_method = method.map(|m| (m, activation.locals_into_form_values(context))); let vars_method = method.map(|m| (m, activation.locals_into_form_values(context)));

View File

@ -5,9 +5,9 @@ use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject}; use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::object::value_object; use crate::avm1::object::value_object;
use crate::avm1::property::Attribute; use crate::avm1::property::Attribute;
use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value}; use crate::avm1::{Avm1String, Object, ScriptObject, TObject, UpdateContext, Value};
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::{Gc, MutationContext}; use gc_arena::MutationContext;
use smallvec::alloc::borrow::Cow; use smallvec::alloc::borrow::Cow;
use std::cmp::Ordering; use std::cmp::Ordering;
@ -247,7 +247,7 @@ pub fn join<'gc>(
.unwrap_or_else(|| Cow::Borrowed(",")); .unwrap_or_else(|| Cow::Borrowed(","));
let values: Vec<Value<'gc>> = this.array(); let values: Vec<Value<'gc>> = this.array();
Ok(Gc::allocate( Ok(Avm1String::new(
context.gc_context, context.gc_context,
values values
.iter() .iter()

View File

@ -4,10 +4,10 @@ use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject}; use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::object::value_object::ValueObject; use crate::avm1::object::value_object::ValueObject;
use crate::avm1::{Object, TObject, Value}; use crate::avm1::{Avm1String, Object, TObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::{Gc, MutationContext}; use gc_arena::MutationContext;
/// `Boolean` constructor/function /// `Boolean` constructor/function
pub fn boolean<'gc>( pub fn boolean<'gc>(
@ -83,7 +83,7 @@ pub fn to_string<'gc>(
// Must be a bool. // Must be a bool.
// Boolean.prototype.toString.call(x) returns undefined for non-bools. // Boolean.prototype.toString.call(x) returns undefined for non-bools.
if let Value::Bool(b) = vbox.unbox() { if let Value::Bool(b) = vbox.unbox() {
return Ok(Gc::allocate(context.gc_context, b.to_string()).into()); return Ok(Avm1String::new(context.gc_context, b.to_string()).into());
} }
} }

View File

@ -3,10 +3,10 @@
use crate::avm1::activation::Activation; use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::Executable; use crate::avm1::function::Executable;
use crate::avm1::{Object, TObject, Value}; use crate::avm1::{Avm1String, Object, TObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::{Gc, MutationContext}; use gc_arena::MutationContext;
use crate::avm1::object::color_transform_object::ColorTransformObject; use crate::avm1::object::color_transform_object::ColorTransformObject;
use crate::color_transform::ColorTransform; use crate::color_transform::ColorTransform;
@ -264,7 +264,10 @@ fn to_string<'gc>(
this.get("alphaOffset", activation, context)?.coerce_to_string(activation, context)? this.get("alphaOffset", activation, context)?.coerce_to_string(activation, context)?
); );
Ok(Value::String(Gc::allocate(context.gc_context, formatted))) Ok(Value::String(Avm1String::new(
context.gc_context,
formatted,
)))
} }
fn concat<'gc>( fn concat<'gc>(

View File

@ -2,10 +2,10 @@ use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::object::TObject; use crate::avm1::object::TObject;
use crate::avm1::property::Attribute; use crate::avm1::property::Attribute;
use crate::avm1::Object; use crate::avm1::{Avm1String, Object};
use crate::avm1::{ScriptObject, Value}; use crate::avm1::{ScriptObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use gc_arena::{Gc, MutationContext}; use gc_arena::MutationContext;
pub fn constructor<'gc>( pub fn constructor<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
@ -40,7 +40,7 @@ pub fn constructor<'gc>(
this.set( this.set(
"caption", "caption",
Gc::allocate(context.gc_context, caption).into(), Avm1String::new(context.gc_context, caption).into(),
activation, activation,
context, context,
)?; )?;
@ -92,7 +92,7 @@ pub fn copy<'gc>(
context, context,
copy, copy,
&[ &[
Gc::allocate(context.gc_context, caption).into(), Avm1String::new(context.gc_context, caption).into(),
callback.into(), callback.into(),
separator_before.into(), separator_before.into(),
enabled.into(), enabled.into(),

View File

@ -3,9 +3,9 @@
use crate::avm1::activation::Activation; use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::property::Attribute::*; use crate::avm1::property::Attribute::*;
use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value}; use crate::avm1::{Avm1String, Object, ScriptObject, TObject, UpdateContext, Value};
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::{Gc, MutationContext}; use gc_arena::MutationContext;
pub fn constructor<'gc>( pub fn constructor<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
@ -32,13 +32,13 @@ pub fn create_proto<'gc>(
object.define_value( object.define_value(
gc_context, gc_context,
"message", "message",
Gc::allocate(gc_context, "Error".to_string()).into(), Avm1String::new(gc_context, "Error".to_string()).into(),
EnumSet::empty(), EnumSet::empty(),
); );
object.define_value( object.define_value(
gc_context, gc_context,
"name", "name",
Gc::allocate(gc_context, "Error".to_string()).into(), Avm1String::new(gc_context, "Error".to_string()).into(),
EnumSet::empty(), EnumSet::empty(),
); );
@ -60,7 +60,7 @@ fn to_string<'gc>(
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let message = this.get("message", activation, context)?; let message = this.get("message", activation, context)?;
Ok(Gc::allocate( Ok(Avm1String::new(
context.gc_context, context.gc_context,
message.coerce_to_string(activation, context)?.to_string(), message.coerce_to_string(activation, context)?.to_string(),
) )

View File

@ -3,9 +3,9 @@
use crate::avm1::activation::Activation; use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::ExecutionReason; use crate::avm1::function::ExecutionReason;
use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value}; use crate::avm1::{Avm1String, Object, ScriptObject, TObject, UpdateContext, Value};
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::{Gc, MutationContext}; use gc_arena::MutationContext;
/// Implements `Function` /// Implements `Function`
pub fn constructor<'gc>( pub fn constructor<'gc>(
@ -97,7 +97,7 @@ fn to_string<'gc>(
_: Object<'gc>, _: Object<'gc>,
_: &[Value<'gc>], _: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(Gc::allocate(context.gc_context, "[type Function]".to_string()).into()) Ok(Avm1String::new(context.gc_context, "[type Function]".to_string()).into())
} }
/// Partially construct `Function.prototype`. /// Partially construct `Function.prototype`.

View File

@ -3,10 +3,10 @@
use crate::avm1::activation::Activation; use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject}; use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::{Object, ScriptObject, TObject, Value}; use crate::avm1::{Avm1String, Object, ScriptObject, TObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::{Gc, MutationContext}; use gc_arena::MutationContext;
use swf::{Matrix, Twips}; use swf::{Matrix, Twips};
pub fn value_to_matrix<'gc>( pub fn value_to_matrix<'gc>(
@ -403,7 +403,7 @@ fn to_string<'gc>(
let tx = this.get("tx", activation, context)?; let tx = this.get("tx", activation, context)?;
let ty = this.get("ty", activation, context)?; let ty = this.get("ty", activation, context)?;
Ok(Gc::allocate( Ok(Avm1String::new(
context.gc_context, context.gc_context,
format!( format!(
"(a={}, b={}, c={}, d={}, tx={}, ty={})", "(a={}, b={}, c={}, d={}, tx={}, ty={})",

View File

@ -5,13 +5,13 @@ use crate::avm1::error::Error;
use crate::avm1::globals::display_object::{self, AVM_DEPTH_BIAS, AVM_MAX_DEPTH}; use crate::avm1::globals::display_object::{self, AVM_DEPTH_BIAS, AVM_MAX_DEPTH};
use crate::avm1::globals::matrix::gradient_object_to_matrix; use crate::avm1::globals::matrix::gradient_object_to_matrix;
use crate::avm1::property::Attribute::*; use crate::avm1::property::Attribute::*;
use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value}; use crate::avm1::{Avm1String, Object, ScriptObject, TObject, UpdateContext, Value};
use crate::backend::navigator::NavigationMethod; use crate::backend::navigator::NavigationMethod;
use crate::display_object::{DisplayObject, EditText, MovieClip, TDisplayObject}; use crate::display_object::{DisplayObject, EditText, MovieClip, TDisplayObject};
use crate::prelude::*; use crate::prelude::*;
use crate::shape_utils::DrawCommand; use crate::shape_utils::DrawCommand;
use crate::tag_utils::SwfSlice; use crate::tag_utils::SwfSlice;
use gc_arena::{Gc, MutationContext}; use gc_arena::MutationContext;
use swf::{ use swf::{
FillStyle, Gradient, GradientInterpolation, GradientRecord, GradientSpread, LineCapStyle, FillStyle, Gradient, GradientInterpolation, GradientRecord, GradientSpread, LineCapStyle,
LineJoinStyle, LineStyle, Twips, LineJoinStyle, LineStyle, Twips,
@ -900,7 +900,7 @@ fn to_string<'gc>(
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(Gc::allocate(context.gc_context, movie_clip.path()).into()) Ok(Avm1String::new(context.gc_context, movie_clip.path()).into())
} }
fn local_to_global<'gc>( fn local_to_global<'gc>(
@ -945,7 +945,7 @@ fn get_bounds<'gc>(
activation.resolve_target_display_object( activation.resolve_target_display_object(
context, context,
movie_clip.into(), movie_clip.into(),
Gc::allocate(context.gc_context, path.to_string()).into(), Avm1String::new(context.gc_context, path.to_string()).into(),
)? )?
} }
None => Some(movie_clip.into()), None => Some(movie_clip.into()),

View File

@ -5,10 +5,10 @@ use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject}; use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::object::value_object::ValueObject; use crate::avm1::object::value_object::ValueObject;
use crate::avm1::property::Attribute::*; use crate::avm1::property::Attribute::*;
use crate::avm1::{Object, TObject, Value}; use crate::avm1::{Avm1String, Object, TObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::{Gc, MutationContext}; use gc_arena::MutationContext;
/// `Number` constructor/function /// `Number` constructor/function
pub fn number<'gc>( pub fn number<'gc>(
@ -143,7 +143,7 @@ fn to_string<'gc>(
if radix == 10 { if radix == 10 {
// Output number as floating-point decimal. // Output number as floating-point decimal.
Ok(Gc::allocate( Ok(Avm1String::new(
context.gc_context, context.gc_context,
Value::from(this) Value::from(this)
.coerce_to_string(activation, context)? .coerce_to_string(activation, context)?
@ -159,7 +159,7 @@ fn to_string<'gc>(
Ordering::Greater => (n as u32, false), Ordering::Greater => (n as u32, false),
Ordering::Equal => { Ordering::Equal => {
// Bail out immediately if we're 0. // Bail out immediately if we're 0.
return Ok(Gc::allocate(context.gc_context, "0".to_string()).into()); return Ok(Avm1String::new(context.gc_context, "0".to_string()).into());
} }
}; };
@ -177,7 +177,7 @@ fn to_string<'gc>(
i += 1; i += 1;
} }
let out: String = digits[..i].iter().rev().collect(); let out: String = digits[..i].iter().rev().collect();
Ok(Gc::allocate(context.gc_context, out).into()) Ok(Avm1String::new(context.gc_context, out).into())
} else { } else {
// NaN or large numbers. // NaN or large numbers.
// Player version specific behavior: // Player version specific behavior:
@ -185,7 +185,7 @@ fn to_string<'gc>(
// for example, NaN.toString(3) gives "-/.//./..././/0.0./0.". // for example, NaN.toString(3) gives "-/.//./..././/0.0./0.".
// Flash Player 6 will print a much more sane value of 0, so let's go with that. // Flash Player 6 will print a much more sane value of 0, so let's go with that.
// TODO: Allow configuration of player version. // TODO: Allow configuration of player version.
Ok(Gc::allocate(context.gc_context, "0".to_string()).into()) Ok(Avm1String::new(context.gc_context, "0".to_string()).into())
} }
} }

View File

@ -3,10 +3,10 @@ use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject}; use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::property::Attribute::{self, *}; use crate::avm1::property::Attribute::{self, *};
use crate::avm1::{Object, TObject, UpdateContext, Value}; use crate::avm1::{Avm1String, Object, TObject, UpdateContext, Value};
use crate::character::Character; use crate::character::Character;
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::{Gc, MutationContext}; use gc_arena::MutationContext;
use std::borrow::Cow; use std::borrow::Cow;
/// Implements `Object` /// Implements `Object`
@ -93,7 +93,7 @@ fn to_string<'gc>(
_: Object<'gc>, _: Object<'gc>,
_: &[Value<'gc>], _: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(Gc::allocate(context.gc_context, "[object Object]".to_string()).into()) Ok(Avm1String::new(context.gc_context, "[object Object]".to_string()).into())
} }
/// Implements `Object.prototype.isPropertyEnumerable` /// Implements `Object.prototype.isPropertyEnumerable`

View File

@ -4,10 +4,10 @@ use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject}; use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::property::Attribute; use crate::avm1::property::Attribute;
use crate::avm1::{Object, ScriptObject, TObject, Value}; use crate::avm1::{Avm1String, Object, ScriptObject, TObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::{Gc, MutationContext}; use gc_arena::MutationContext;
use std::f64::NAN; use std::f64::NAN;
pub fn point_to_object<'gc>( pub fn point_to_object<'gc>(
@ -233,7 +233,7 @@ fn to_string<'gc>(
let x = this.get("x", activation, context)?; let x = this.get("x", activation, context)?;
let y = this.get("y", activation, context)?; let y = this.get("y", activation, context)?;
Ok(Gc::allocate( Ok(Avm1String::new(
context.gc_context, context.gc_context,
format!( format!(
"(x={}, y={})", "(x={}, y={})",

View File

@ -5,10 +5,10 @@ use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject}; use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::globals::point::{construct_new_point, point_to_object, value_to_point}; use crate::avm1::globals::point::{construct_new_point, point_to_object, value_to_point};
use crate::avm1::property::Attribute; use crate::avm1::property::Attribute;
use crate::avm1::{Object, ScriptObject, TObject, Value}; use crate::avm1::{Avm1String, Object, ScriptObject, TObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::{Gc, MutationContext}; use gc_arena::MutationContext;
use std::f64::NAN; use std::f64::NAN;
fn constructor<'gc>( fn constructor<'gc>(
@ -63,7 +63,7 @@ fn to_string<'gc>(
let width = this.get("width", activation, context)?; let width = this.get("width", activation, context)?;
let height = this.get("height", activation, context)?; let height = this.get("height", activation, context)?;
Ok(Gc::allocate( Ok(Avm1String::new(
context.gc_context, context.gc_context,
format!( format!(
"(x={}, y={}, w={}, h={})", "(x={}, y={}, w={}, h={})",

View File

@ -1,10 +1,10 @@
use crate::avm1::activation::Activation; use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject}; use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::{Object, TObject, Value}; use crate::avm1::{Avm1String, Object, TObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::{Gc, MutationContext}; use gc_arena::MutationContext;
use crate::avm1::object::shared_object::SharedObject; use crate::avm1::object::shared_object::SharedObject;
@ -83,7 +83,7 @@ fn recursive_deserialize<'gc>(
object.define_value( object.define_value(
context.gc_context, context.gc_context,
entry.0, entry.0,
Value::String(Gc::allocate(context.gc_context, val)), Value::String(Avm1String::new(context.gc_context, val)),
EnumSet::empty(), EnumSet::empty(),
); );
} }
@ -91,7 +91,7 @@ fn recursive_deserialize<'gc>(
object.define_value( object.define_value(
context.gc_context, context.gc_context,
entry.0, entry.0,
Value::String(Gc::allocate(context.gc_context, s.clone())), Value::String(Avm1String::new(context.gc_context, s.clone())),
EnumSet::empty(), EnumSet::empty(),
); );
} }

View File

@ -5,8 +5,8 @@ use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::Executable; use crate::avm1::function::Executable;
use crate::avm1::property::Attribute; use crate::avm1::property::Attribute;
use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value}; use crate::avm1::{Avm1String, Object, ScriptObject, TObject, UpdateContext, Value};
use gc_arena::{Gc, MutationContext}; use gc_arena::MutationContext;
pub fn create_stage_object<'gc>( pub fn create_stage_object<'gc>(
gc_context: MutationContext<'gc, '_>, gc_context: MutationContext<'gc, '_>,
@ -92,7 +92,7 @@ fn align<'gc>(
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("Stage.align: unimplemented"); log::warn!("Stage.align: unimplemented");
Ok(Gc::allocate(context.gc_context, "".to_string()).into()) Ok(Avm1String::new(context.gc_context, "".to_string()).into())
} }
fn set_align<'gc>( fn set_align<'gc>(
@ -121,7 +121,7 @@ fn remove_listener<'gc>(
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("Stage.removeListener: unimplemented"); log::warn!("Stage.removeListener: unimplemented");
Ok(Gc::allocate(context.gc_context, "".to_string()).into()) Ok(Avm1String::new(context.gc_context, "".to_string()).into())
} }
fn scale_mode<'gc>( fn scale_mode<'gc>(
@ -131,7 +131,7 @@ fn scale_mode<'gc>(
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("Stage.scaleMode: unimplemented"); log::warn!("Stage.scaleMode: unimplemented");
Ok(Gc::allocate(context.gc_context, "noScale".to_string()).into()) Ok(Avm1String::new(context.gc_context, "noScale".to_string()).into())
} }
fn set_scale_mode<'gc>( fn set_scale_mode<'gc>(

View File

@ -5,11 +5,11 @@ use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject}; use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::object::value_object::ValueObject; use crate::avm1::object::value_object::ValueObject;
use crate::avm1::property::Attribute::*; use crate::avm1::property::Attribute::*;
use crate::avm1::{Object, ScriptObject, TObject, Value}; use crate::avm1::{Avm1String, Object, ScriptObject, TObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use crate::string_utils; use crate::string_utils;
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::{Gc, MutationContext}; use gc_arena::MutationContext;
/// `String` constructor /// `String` constructor
pub fn string<'gc>( pub fn string<'gc>(
@ -20,17 +20,17 @@ pub fn string<'gc>(
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let value = match args.get(0).cloned() { let value = match args.get(0).cloned() {
Some(Value::String(s)) => s, Some(Value::String(s)) => s,
Some(v) => Gc::allocate( Some(v) => Avm1String::new(
ac.gc_context, ac.gc_context,
v.coerce_to_string(activation, ac)?.to_string(), v.coerce_to_string(activation, ac)?.to_string(),
), ),
_ => Gc::allocate(ac.gc_context, String::new()), _ => Avm1String::new(ac.gc_context, String::new()),
}; };
if let Some(mut vbox) = this.as_value_object() { if let Some(mut vbox) = this.as_value_object() {
let len = value.encode_utf16().count(); let len = value.encode_utf16().count();
vbox.set_length(ac.gc_context, len); vbox.set_length(ac.gc_context, len);
vbox.replace_value(ac.gc_context, value.into()); vbox.replace_value(ac.gc_context, value.clone().into());
} }
Ok(value.into()) Ok(value.into())
@ -184,7 +184,7 @@ fn char_at<'gc>(
let this_val = Value::from(this); let this_val = Value::from(this);
let string = match this_val { let string = match this_val {
Value::String(string) => string, Value::String(string) => string,
other => Gc::allocate( other => Avm1String::new(
context.gc_context, context.gc_context,
other.coerce_to_string(activation, context)?.to_string(), other.coerce_to_string(activation, context)?.to_string(),
), ),
@ -202,7 +202,7 @@ fn char_at<'gc>(
} else { } else {
"".into() "".into()
}; };
Ok(Gc::allocate(context.gc_context, ret).into()) Ok(Avm1String::new(context.gc_context, ret).into())
} }
fn char_code_at<'gc>( fn char_code_at<'gc>(
@ -241,7 +241,7 @@ fn concat<'gc>(
let s = arg.coerce_to_string(activation, context)?; let s = arg.coerce_to_string(activation, context)?;
ret.push_str(&s) ret.push_str(&s)
} }
Ok(Gc::allocate(context.gc_context, ret).into()) Ok(Avm1String::new(context.gc_context, ret).into())
} }
fn from_char_code<'gc>( fn from_char_code<'gc>(
@ -260,7 +260,7 @@ fn from_char_code<'gc>(
} }
out.push(utf16_code_unit_to_char(i)); out.push(utf16_code_unit_to_char(i));
} }
Ok(Gc::allocate(context.gc_context, out).into()) Ok(Avm1String::new(context.gc_context, out).into())
} }
fn index_of<'gc>( fn index_of<'gc>(
@ -393,9 +393,9 @@ fn slice<'gc>(
.skip(start_index) .skip(start_index)
.take(end_index - start_index), .take(end_index - start_index),
); );
Ok(Gc::allocate(context.gc_context, ret).into()) Ok(Avm1String::new(context.gc_context, ret).into())
} else { } else {
Ok(Gc::allocate(context.gc_context, "".to_string()).into()) Ok(Avm1String::new(context.gc_context, "".to_string()).into())
} }
} }
@ -408,7 +408,7 @@ fn split<'gc>(
let this_val = Value::from(this); let this_val = Value::from(this);
let this = match this_val { let this = match this_val {
Value::String(string) => string, Value::String(string) => string,
other => Gc::allocate( other => Avm1String::new(
context.gc_context, context.gc_context,
other.coerce_to_string(activation, context)?.to_string(), other.coerce_to_string(activation, context)?.to_string(),
), ),
@ -424,7 +424,7 @@ fn split<'gc>(
for (i, token) in this.split(delimiter.as_ref()).take(limit).enumerate() { for (i, token) in this.split(delimiter.as_ref()).take(limit).enumerate() {
array.set_array_element( array.set_array_element(
i, i,
Gc::allocate(context.gc_context, token.to_string()).into(), Avm1String::new(context.gc_context, token.to_string()).into(),
context.gc_context, context.gc_context,
); );
} }
@ -435,7 +435,7 @@ fn split<'gc>(
for (i, token) in this.chars().take(limit).enumerate() { for (i, token) in this.chars().take(limit).enumerate() {
array.set_array_element( array.set_array_element(
i, i,
Gc::allocate(context.gc_context, token.to_string()).into(), Avm1String::new(context.gc_context, token.to_string()).into(),
context.gc_context, context.gc_context,
); );
} }
@ -456,7 +456,7 @@ fn substr<'gc>(
let this_val = Value::from(this); let this_val = Value::from(this);
let this = match this_val { let this = match this_val {
Value::String(string) => string, Value::String(string) => string,
other => Gc::allocate( other => Avm1String::new(
context.gc_context, context.gc_context,
other.coerce_to_string(activation, context)?.to_string(), other.coerce_to_string(activation, context)?.to_string(),
), ),
@ -473,7 +473,7 @@ fn substr<'gc>(
}; };
let ret = utf16_iter_to_string(this.encode_utf16().skip(start_index).take(len)); let ret = utf16_iter_to_string(this.encode_utf16().skip(start_index).take(len));
Ok(Gc::allocate(context.gc_context, ret).into()) Ok(Avm1String::new(context.gc_context, ret).into())
} }
fn substring<'gc>( fn substring<'gc>(
@ -489,7 +489,7 @@ fn substring<'gc>(
let this_val = Value::from(this); let this_val = Value::from(this);
let this = match this_val { let this = match this_val {
Value::String(string) => string, Value::String(string) => string,
other => Gc::allocate( other => Avm1String::new(
context.gc_context, context.gc_context,
other.coerce_to_string(activation, context)?.to_string(), other.coerce_to_string(activation, context)?.to_string(),
), ),
@ -514,7 +514,7 @@ fn substring<'gc>(
.skip(start_index) .skip(start_index)
.take(end_index - start_index), .take(end_index - start_index),
); );
Ok(Gc::allocate(context.gc_context, ret).into()) Ok(Avm1String::new(context.gc_context, ret).into())
} }
fn to_lower_case<'gc>( fn to_lower_case<'gc>(
@ -525,7 +525,7 @@ fn to_lower_case<'gc>(
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let this_val = Value::from(this); let this_val = Value::from(this);
let this = this_val.coerce_to_string(activation, context)?; let this = this_val.coerce_to_string(activation, context)?;
Ok(Gc::allocate( Ok(Avm1String::new(
context.gc_context, context.gc_context,
this.chars() this.chars()
.map(string_utils::swf_char_to_lowercase) .map(string_utils::swf_char_to_lowercase)
@ -561,7 +561,7 @@ fn to_upper_case<'gc>(
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let this_val = Value::from(this); let this_val = Value::from(this);
let this = this_val.coerce_to_string(activation, context)?; let this = this_val.coerce_to_string(activation, context)?;
Ok(Gc::allocate( Ok(Avm1String::new(
context.gc_context, context.gc_context,
this.chars() this.chars()
.map(string_utils::swf_char_to_uppercase) .map(string_utils::swf_char_to_uppercase)

View File

@ -3,10 +3,10 @@ use crate::avm1::error::Error;
use crate::avm1::function::Executable; use crate::avm1::function::Executable;
use crate::avm1::globals::system::SystemCapabilities; use crate::avm1::globals::system::SystemCapabilities;
use crate::avm1::object::Object; use crate::avm1::object::Object;
use crate::avm1::{ScriptObject, TObject, Value}; use crate::avm1::{Avm1String, ScriptObject, TObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::{Gc, MutationContext}; use gc_arena::MutationContext;
macro_rules! capabilities_func { macro_rules! capabilities_func {
($func_name: ident, $capability: expr) => { ($func_name: ident, $capability: expr) => {
@ -82,7 +82,7 @@ pub fn get_player_type<'gc>(
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(Gc::allocate(context.gc_context, context.system.player_type.to_string()).into()) Ok(Avm1String::new(context.gc_context, context.system.player_type.to_string()).into())
} }
pub fn get_screen_color<'gc>( pub fn get_screen_color<'gc>(
@ -91,7 +91,7 @@ pub fn get_screen_color<'gc>(
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(Gc::allocate(context.gc_context, context.system.screen_color.to_string()).into()) Ok(Avm1String::new(context.gc_context, context.system.screen_color.to_string()).into())
} }
pub fn get_language<'gc>( pub fn get_language<'gc>(
@ -100,7 +100,7 @@ pub fn get_language<'gc>(
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(Gc::allocate( Ok(Avm1String::new(
context.gc_context, context.gc_context,
context context
.system .system
@ -153,7 +153,7 @@ pub fn get_manufacturer<'gc>(
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(Gc::allocate( Ok(Avm1String::new(
context.gc_context, context.gc_context,
context context
.system .system
@ -169,7 +169,7 @@ pub fn get_os_name<'gc>(
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(Gc::allocate(context.gc_context, context.system.os.to_string()).into()) Ok(Avm1String::new(context.gc_context, context.system.os.to_string()).into())
} }
pub fn get_version<'gc>( pub fn get_version<'gc>(
@ -178,7 +178,7 @@ pub fn get_version<'gc>(
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(Gc::allocate( Ok(Avm1String::new(
context.gc_context, context.gc_context,
context.system.get_version_string(activation), context.system.get_version_string(activation),
) )
@ -191,7 +191,7 @@ pub fn get_server_string<'gc>(
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(Gc::allocate( Ok(Avm1String::new(
context.gc_context, context.gc_context,
context.system.get_server_string(activation), context.system.get_server_string(activation),
) )
@ -204,7 +204,7 @@ pub fn get_cpu_architecture<'gc>(
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(Gc::allocate( Ok(Avm1String::new(
context.gc_context, context.gc_context,
context.system.cpu_architecture.to_string(), context.system.cpu_architecture.to_string(),
) )
@ -217,7 +217,7 @@ pub fn get_max_idc_level<'gc>(
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(Gc::allocate(context.gc_context, context.system.idc_level.clone()).into()) Ok(Avm1String::new(context.gc_context, context.system.idc_level.clone()).into())
} }
pub fn create<'gc>( pub fn create<'gc>(

View File

@ -4,9 +4,9 @@ use crate::avm1::listeners::Listeners;
use crate::avm1::object::Object; use crate::avm1::object::Object;
use crate::avm1::property::Attribute; use crate::avm1::property::Attribute;
use crate::avm1::property::Attribute::{DontDelete, DontEnum, ReadOnly}; use crate::avm1::property::Attribute::{DontDelete, DontEnum, ReadOnly};
use crate::avm1::{ScriptObject, TObject, Value}; use crate::avm1::{Avm1String, ScriptObject, TObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use gc_arena::{Gc, MutationContext}; use gc_arena::MutationContext;
use std::convert::Into; use std::convert::Into;
fn on_ime_composition<'gc>( fn on_ime_composition<'gc>(
@ -33,7 +33,7 @@ fn get_conversion_mode<'gc>(
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(Gc::allocate(context.gc_context, "KOREAN".to_string()).into()) Ok(Avm1String::new(context.gc_context, "KOREAN".to_string()).into())
} }
fn get_enabled<'gc>( fn get_enabled<'gc>(
@ -85,49 +85,49 @@ pub fn create<'gc>(
ime.define_value( ime.define_value(
gc_context, gc_context,
"ALPHANUMERIC_FULL", "ALPHANUMERIC_FULL",
Gc::allocate(gc_context, "ALPHANUMERIC_FULL".to_string()).into(), Avm1String::new(gc_context, "ALPHANUMERIC_FULL".to_string()).into(),
Attribute::DontDelete | ReadOnly | DontEnum, Attribute::DontDelete | ReadOnly | DontEnum,
); );
ime.define_value( ime.define_value(
gc_context, gc_context,
"ALPHANUMERIC_HALF", "ALPHANUMERIC_HALF",
Gc::allocate(gc_context, "ALPHANUMERIC_HALF".to_string()).into(), Avm1String::new(gc_context, "ALPHANUMERIC_HALF".to_string()).into(),
DontDelete | ReadOnly | DontEnum, DontDelete | ReadOnly | DontEnum,
); );
ime.define_value( ime.define_value(
gc_context, gc_context,
"CHINESE", "CHINESE",
Gc::allocate(gc_context, "CHINESE".to_string()).into(), Avm1String::new(gc_context, "CHINESE".to_string()).into(),
DontDelete | ReadOnly | DontEnum, DontDelete | ReadOnly | DontEnum,
); );
ime.define_value( ime.define_value(
gc_context, gc_context,
"JAPANESE_HIRAGANA", "JAPANESE_HIRAGANA",
Gc::allocate(gc_context, "JAPANESE_HIRAGANA".to_string()).into(), Avm1String::new(gc_context, "JAPANESE_HIRAGANA".to_string()).into(),
DontDelete | ReadOnly | DontEnum, DontDelete | ReadOnly | DontEnum,
); );
ime.define_value( ime.define_value(
gc_context, gc_context,
"JAPANESE_KATAKANA_FULL", "JAPANESE_KATAKANA_FULL",
Gc::allocate(gc_context, "JAPANESE_KATAKANA_FULL".to_string()).into(), Avm1String::new(gc_context, "JAPANESE_KATAKANA_FULL".to_string()).into(),
DontDelete | ReadOnly | DontEnum, DontDelete | ReadOnly | DontEnum,
); );
ime.define_value( ime.define_value(
gc_context, gc_context,
"KOREAN", "KOREAN",
Gc::allocate(gc_context, "KOREAN".to_string()).into(), Avm1String::new(gc_context, "KOREAN".to_string()).into(),
DontDelete | ReadOnly | DontEnum, DontDelete | ReadOnly | DontEnum,
); );
ime.define_value( ime.define_value(
gc_context, gc_context,
"UNKNOWN", "UNKNOWN",
Gc::allocate(gc_context, "UNKNOWN".to_string()).into(), Avm1String::new(gc_context, "UNKNOWN".to_string()).into(),
DontDelete | ReadOnly | DontEnum, DontDelete | ReadOnly | DontEnum,
); );

View File

@ -2,10 +2,10 @@ use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::Executable; use crate::avm1::function::Executable;
use crate::avm1::object::Object; use crate::avm1::object::Object;
use crate::avm1::{ScriptObject, TObject, Value}; use crate::avm1::{Avm1String, ScriptObject, TObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::{Gc, MutationContext}; use gc_arena::MutationContext;
use std::convert::Into; use std::convert::Into;
fn allow_domain<'gc>( fn allow_domain<'gc>(
@ -54,7 +54,7 @@ fn get_sandbox_type<'gc>(
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(Gc::allocate(context.gc_context, context.system.sandbox_type.to_string()).into()) Ok(Avm1String::new(context.gc_context, context.system.sandbox_type.to_string()).into())
} }
fn get_choose_local_swf_path<'gc>( fn get_choose_local_swf_path<'gc>(

View File

@ -3,10 +3,10 @@ use crate::avm1::error::Error;
use crate::avm1::function::Executable; use crate::avm1::function::Executable;
use crate::avm1::globals::display_object; use crate::avm1::globals::display_object;
use crate::avm1::property::Attribute::*; use crate::avm1::property::Attribute::*;
use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value}; use crate::avm1::{Avm1String, Object, ScriptObject, TObject, UpdateContext, Value};
use crate::display_object::{AutoSizeMode, EditText, TDisplayObject}; use crate::display_object::{AutoSizeMode, EditText, TDisplayObject};
use crate::html::TextFormat; use crate::html::TextFormat;
use gc_arena::{Gc, MutationContext}; use gc_arena::MutationContext;
/// Implements `TextField` /// Implements `TextField`
pub fn constructor<'gc>( pub fn constructor<'gc>(
@ -26,7 +26,7 @@ pub fn get_text<'gc>(
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(display_object) = this.as_display_object() { if let Some(display_object) = this.as_display_object() {
if let Some(text_field) = display_object.as_edit_text() { if let Some(text_field) = display_object.as_edit_text() {
return Ok(Gc::allocate(context.gc_context, text_field.text()).into()); return Ok(Avm1String::new(context.gc_context, text_field.text()).into());
} }
} }
Ok(Value::Undefined) Ok(Value::Undefined)
@ -93,7 +93,7 @@ pub fn get_html_text<'gc>(
if let Some(display_object) = this.as_display_object() { if let Some(display_object) = this.as_display_object() {
if let Some(text_field) = display_object.as_edit_text() { if let Some(text_field) = display_object.as_edit_text() {
if let Ok(text) = text_field.html_text(context) { if let Ok(text) = text_field.html_text(context) {
return Ok(Gc::allocate(context.gc_context, text).into()); return Ok(Avm1String::new(context.gc_context, text).into());
} }
} }
} }
@ -303,7 +303,7 @@ fn variable<'gc>(
.and_then(|dobj| dobj.as_edit_text()) .and_then(|dobj| dobj.as_edit_text())
{ {
if let Some(variable) = etext.variable() { if let Some(variable) = etext.variable() {
return Ok(Gc::allocate(context.gc_context, variable.to_string()).into()); return Ok(Avm1String::new(context.gc_context, variable.to_string()).into());
} }
} }
@ -381,10 +381,12 @@ pub fn auto_size<'gc>(
.and_then(|dobj| dobj.as_edit_text()) .and_then(|dobj| dobj.as_edit_text())
{ {
return Ok(match etext.autosize() { return Ok(match etext.autosize() {
AutoSizeMode::None => Gc::allocate(context.gc_context, "none".to_string()).into(), AutoSizeMode::None => Avm1String::new(context.gc_context, "none".to_string()).into(),
AutoSizeMode::Left => Gc::allocate(context.gc_context, "left".to_string()).into(), AutoSizeMode::Left => Avm1String::new(context.gc_context, "left".to_string()).into(),
AutoSizeMode::Center => Gc::allocate(context.gc_context, "center".to_string()).into(), AutoSizeMode::Center => {
AutoSizeMode::Right => Gc::allocate(context.gc_context, "right".to_string()).into(), Avm1String::new(context.gc_context, "center".to_string()).into()
}
AutoSizeMode::Right => Avm1String::new(context.gc_context, "right".to_string()).into(),
}); });
} }
@ -403,9 +405,9 @@ pub fn set_auto_size<'gc>(
{ {
etext.set_autosize( etext.set_autosize(
match args.get(0).cloned().unwrap_or(Value::Undefined) { match args.get(0).cloned().unwrap_or(Value::Undefined) {
Value::String(s) if *s == "left" => AutoSizeMode::Left, Value::String(s) if s == "left" => AutoSizeMode::Left,
Value::String(s) if *s == "center" => AutoSizeMode::Center, Value::String(s) if s == "center" => AutoSizeMode::Center,
Value::String(s) if *s == "right" => AutoSizeMode::Right, Value::String(s) if s == "right" => AutoSizeMode::Right,
Value::Bool(true) => AutoSizeMode::Left, Value::Bool(true) => AutoSizeMode::Left,
_ => AutoSizeMode::None, _ => AutoSizeMode::None,
}, },

View File

@ -2,8 +2,8 @@
use crate::avm1::activation::Activation; use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value}; use crate::avm1::{Avm1String, Object, ScriptObject, TObject, UpdateContext, Value};
use gc_arena::{Gc, MutationContext}; use gc_arena::MutationContext;
fn map_defined_to_string<'gc>( fn map_defined_to_string<'gc>(
name: &str, name: &str,
@ -16,7 +16,7 @@ fn map_defined_to_string<'gc>(
Some(Value::Undefined) => Value::Null, Some(Value::Undefined) => Value::Null,
Some(Value::Null) => Value::Null, Some(Value::Null) => Value::Null,
None => Value::Null, None => Value::Null,
Some(v) => Gc::allocate( Some(v) => Avm1String::new(
ac.gc_context, ac.gc_context,
v.coerce_to_string(activation, ac)?.to_string(), v.coerce_to_string(activation, ac)?.to_string(),
) )

View File

@ -6,12 +6,12 @@ use crate::avm1::function::Executable;
use crate::avm1::object::script_object::ScriptObject; use crate::avm1::object::script_object::ScriptObject;
use crate::avm1::object::xml_object::XMLObject; use crate::avm1::object::xml_object::XMLObject;
use crate::avm1::property::Attribute::*; use crate::avm1::property::Attribute::*;
use crate::avm1::{Object, TObject, UpdateContext, Value}; use crate::avm1::{Avm1String, Object, TObject, UpdateContext, Value};
use crate::backend::navigator::RequestOptions; use crate::backend::navigator::RequestOptions;
use crate::xml; use crate::xml;
use crate::xml::{XMLDocument, XMLNode}; use crate::xml::{XMLDocument, XMLNode};
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::{Gc, MutationContext}; use gc_arena::MutationContext;
use quick_xml::Error as ParseError; use quick_xml::Error as ParseError;
use std::borrow::Cow; use std::borrow::Cow;
@ -156,7 +156,7 @@ pub fn xmlnode_get_namespace_for_prefix<'gc>(
args.get(0).map(|v| v.coerce_to_string(activation, ac)), args.get(0).map(|v| v.coerce_to_string(activation, ac)),
) { ) {
if let Some(uri) = xmlnode.lookup_uri_for_namespace(&prefix_string?) { if let Some(uri) = xmlnode.lookup_uri_for_namespace(&prefix_string?) {
Ok(Gc::allocate(ac.gc_context, uri).into()) Ok(Avm1String::new(ac.gc_context, uri).into())
} else { } else {
Ok(Value::Null) Ok(Value::Null)
} }
@ -176,7 +176,7 @@ pub fn xmlnode_get_prefix_for_namespace<'gc>(
args.get(0).map(|v| v.coerce_to_string(activation, ac)), args.get(0).map(|v| v.coerce_to_string(activation, ac)),
) { ) {
if let Some(prefix) = xmlnode.lookup_namespace_for_uri(&uri_string?) { if let Some(prefix) = xmlnode.lookup_namespace_for_uri(&uri_string?) {
Ok(Gc::allocate(ac.gc_context, prefix).into()) Ok(Avm1String::new(ac.gc_context, prefix).into())
} else { } else {
Ok(Value::Null) Ok(Value::Null)
} }
@ -224,7 +224,7 @@ pub fn xmlnode_to_string<'gc>(
if let Some(node) = this.as_xml_node() { if let Some(node) = this.as_xml_node() {
let result = node.into_string(&mut is_as2_compatible); let result = node.into_string(&mut is_as2_compatible);
return Ok(Gc::allocate( return Ok(Avm1String::new(
ac.gc_context, ac.gc_context,
result.unwrap_or_else(|e| { result.unwrap_or_else(|e| {
log::warn!("XMLNode toString failed: {}", e); log::warn!("XMLNode toString failed: {}", e);
@ -234,7 +234,7 @@ pub fn xmlnode_to_string<'gc>(
.into()); .into());
} }
Ok(Gc::allocate(ac.gc_context, "".to_string()).into()) Ok(Avm1String::new(ac.gc_context, "".to_string()).into())
} }
pub fn xmlnode_local_name<'gc>( pub fn xmlnode_local_name<'gc>(
@ -246,7 +246,7 @@ pub fn xmlnode_local_name<'gc>(
Ok(this Ok(this
.as_xml_node() .as_xml_node()
.and_then(|n| n.tag_name()) .and_then(|n| n.tag_name())
.map(|n| Gc::allocate(ac.gc_context, n.local_name().to_string()).into()) .map(|n| Avm1String::new(ac.gc_context, n.local_name().to_string()).into())
.unwrap_or_else(|| Value::Null)) .unwrap_or_else(|| Value::Null))
} }
@ -259,7 +259,7 @@ pub fn xmlnode_node_name<'gc>(
Ok(this Ok(this
.as_xml_node() .as_xml_node()
.and_then(|n| n.tag_name()) .and_then(|n| n.tag_name())
.map(|n| Gc::allocate(ac.gc_context, n.node_name().to_string()).into()) .map(|n| Avm1String::new(ac.gc_context, n.node_name().to_string()).into())
.unwrap_or_else(|| Value::Null)) .unwrap_or_else(|| Value::Null))
} }
@ -292,7 +292,7 @@ pub fn xmlnode_node_value<'gc>(
Ok(this Ok(this
.as_xml_node() .as_xml_node()
.and_then(|n| n.node_value()) .and_then(|n| n.node_value())
.map(|n| Gc::allocate(ac.gc_context, n).into()) .map(|n| Avm1String::new(ac.gc_context, n).into())
.unwrap_or_else(|| Value::Null)) .unwrap_or_else(|| Value::Null))
} }
@ -306,7 +306,7 @@ pub fn xmlnode_prefix<'gc>(
.as_xml_node() .as_xml_node()
.and_then(|n| n.tag_name()) .and_then(|n| n.tag_name())
.map(|n| { .map(|n| {
Gc::allocate( Avm1String::new(
ac.gc_context, ac.gc_context,
n.prefix() n.prefix()
.map(|n| n.to_string()) .map(|n| n.to_string())
@ -493,7 +493,7 @@ pub fn xmlnode_namespace_uri<'gc>(
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(node) = this.as_xml_node() { if let Some(node) = this.as_xml_node() {
if let Some(name) = node.tag_name() { if let Some(name) = node.tag_name() {
return Ok(Gc::allocate( return Ok(Avm1String::new(
ac.gc_context, ac.gc_context,
node.lookup_uri_for_namespace(name.prefix().unwrap_or("")) node.lookup_uri_for_namespace(name.prefix().unwrap_or(""))
.unwrap_or_else(|| "".to_string()), .unwrap_or_else(|| "".to_string()),
@ -861,7 +861,7 @@ pub fn xml_on_data<'gc>(
let src = src.coerce_to_string(activation, ac)?; let src = src.coerce_to_string(activation, ac)?;
this.call_method( this.call_method(
"parseXML", "parseXML",
&[Gc::allocate(ac.gc_context, src.to_string()).into()], &[Avm1String::new(ac.gc_context, src.to_string()).into()],
activation, activation,
ac, ac,
)?; )?;
@ -884,7 +884,7 @@ pub fn xml_doc_type_decl<'gc>(
if let Some(doctype) = node.document().doctype() { if let Some(doctype) = node.document().doctype() {
let result = doctype.into_string(&mut |_| true); let result = doctype.into_string(&mut |_| true);
return Ok(Gc::allocate( return Ok(Avm1String::new(
ac.gc_context, ac.gc_context,
result.unwrap_or_else(|e| { result.unwrap_or_else(|e| {
log::warn!("Error occured when serializing DOCTYPE: {}", e); log::warn!("Error occured when serializing DOCTYPE: {}", e);
@ -910,7 +910,7 @@ pub fn xml_xml_decl<'gc>(
if let Err(e) = result { if let Err(e) = result {
log::warn!("Could not generate XML declaration for document: {}", e); log::warn!("Could not generate XML declaration for document: {}", e);
} else if let Ok(Some(result_str)) = result { } else if let Ok(Some(result_str)) = result {
return Ok(Gc::allocate(ac.gc_context, result_str).into()); return Ok(Avm1String::new(ac.gc_context, result_str).into());
} }
} }

View File

@ -2,11 +2,11 @@ use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::{Executable, ExecutionReason, FunctionObject, NativeFunction}; use crate::avm1::function::{Executable, ExecutionReason, FunctionObject, NativeFunction};
use crate::avm1::property::{Attribute, Property}; use crate::avm1::property::{Attribute, Property};
use crate::avm1::{Object, ObjectPtr, TObject, UpdateContext, Value}; use crate::avm1::{Avm1String, Object, ObjectPtr, TObject, UpdateContext, Value};
use crate::property_map::{Entry, PropertyMap}; use crate::property_map::{Entry, PropertyMap};
use core::fmt; use core::fmt;
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::{Collect, Gc, GcCell, MutationContext}; use gc_arena::{Collect, GcCell, MutationContext};
use std::borrow::Cow; use std::borrow::Cow;
pub const TYPE_OF_OBJECT: &str = "object"; pub const TYPE_OF_OBJECT: &str = "object";
@ -45,7 +45,7 @@ impl<'gc> Watcher<'gc> {
base_proto: Option<Object<'gc>>, base_proto: Option<Object<'gc>>,
) -> Result<Value<'gc>, crate::avm1::error::Error<'gc>> { ) -> Result<Value<'gc>, crate::avm1::error::Error<'gc>> {
let args = [ let args = [
Value::String(Gc::allocate(context.gc_context, name.to_string())), Value::String(Avm1String::new(context.gc_context, name.to_string())),
old_value, old_value,
new_value, new_value,
self.user_data.clone(), self.user_data.clone(),
@ -853,7 +853,7 @@ mod tests {
use crate::avm1::activation::ActivationIdentifier; use crate::avm1::activation::ActivationIdentifier;
use crate::avm1::globals::system::SystemProperties; use crate::avm1::globals::system::SystemProperties;
use crate::avm1::property::Attribute::*; use crate::avm1::property::Attribute::*;
use crate::avm1::{Avm1, Timers}; use crate::avm1::{Avm1, Avm1String, Timers};
use crate::backend::audio::NullAudioBackend; use crate::backend::audio::NullAudioBackend;
use crate::backend::input::NullInputBackend; use crate::backend::input::NullInputBackend;
use crate::backend::navigator::NullNavigatorBackend; use crate::backend::navigator::NullNavigatorBackend;
@ -864,7 +864,7 @@ mod tests {
use crate::loader::LoadManager; use crate::loader::LoadManager;
use crate::prelude::*; use crate::prelude::*;
use crate::tag_utils::{SwfMovie, SwfSlice}; use crate::tag_utils::{SwfMovie, SwfSlice};
use gc_arena::{rootless_arena, Gc}; use gc_arena::rootless_arena;
use rand::{rngs::SmallRng, SeedableRng}; use rand::{rngs::SmallRng, SeedableRng};
use std::collections::{BTreeMap, HashMap}; use std::collections::{BTreeMap, HashMap};
use std::sync::Arc; use std::sync::Arc;
@ -955,13 +955,13 @@ mod tests {
object.as_script_object().unwrap().define_value( object.as_script_object().unwrap().define_value(
context.gc_context, context.gc_context,
"forced", "forced",
Gc::allocate(context.gc_context, "forced".to_string()).into(), Avm1String::new(context.gc_context, "forced".to_string()).into(),
EnumSet::empty(), EnumSet::empty(),
); );
object object
.set( .set(
"natural", "natural",
Gc::allocate(context.gc_context, "natural".to_string()).into(), Avm1String::new(context.gc_context, "natural".to_string()).into(),
activation, activation,
context, context,
) )
@ -969,11 +969,11 @@ mod tests {
assert_eq!( assert_eq!(
object.get("forced", activation, context).unwrap(), object.get("forced", activation, context).unwrap(),
Gc::allocate(context.gc_context, "forced".to_string()).into() Avm1String::new(context.gc_context, "forced".to_string()).into()
); );
assert_eq!( assert_eq!(
object.get("natural", activation, context).unwrap(), object.get("natural", activation, context).unwrap(),
Gc::allocate(context.gc_context, "natural".to_string()).into() Avm1String::new(context.gc_context, "natural".to_string()).into()
); );
}) })
} }
@ -984,20 +984,20 @@ mod tests {
object.as_script_object().unwrap().define_value( object.as_script_object().unwrap().define_value(
context.gc_context, context.gc_context,
"normal", "normal",
Gc::allocate(context.gc_context, "initial".to_string()).into(), Avm1String::new(context.gc_context, "initial".to_string()).into(),
EnumSet::empty(), EnumSet::empty(),
); );
object.as_script_object().unwrap().define_value( object.as_script_object().unwrap().define_value(
context.gc_context, context.gc_context,
"readonly", "readonly",
Gc::allocate(context.gc_context, "initial".to_string()).into(), Avm1String::new(context.gc_context, "initial".to_string()).into(),
ReadOnly.into(), ReadOnly.into(),
); );
object object
.set( .set(
"normal", "normal",
Gc::allocate(context.gc_context, "replaced".to_string()).into(), Avm1String::new(context.gc_context, "replaced".to_string()).into(),
activation, activation,
context, context,
) )
@ -1005,7 +1005,7 @@ mod tests {
object object
.set( .set(
"readonly", "readonly",
Gc::allocate(context.gc_context, "replaced".to_string()).into(), Avm1String::new(context.gc_context, "replaced".to_string()).into(),
activation, activation,
context, context,
) )
@ -1013,11 +1013,11 @@ mod tests {
assert_eq!( assert_eq!(
object.get("normal", activation, context).unwrap(), object.get("normal", activation, context).unwrap(),
Gc::allocate(context.gc_context, "replaced".to_string()).into() Avm1String::new(context.gc_context, "replaced".to_string()).into()
); );
assert_eq!( assert_eq!(
object.get("readonly", activation, context).unwrap(), object.get("readonly", activation, context).unwrap(),
Gc::allocate(context.gc_context, "initial".to_string()).into() Avm1String::new(context.gc_context, "initial".to_string()).into()
); );
}) })
} }
@ -1028,14 +1028,14 @@ mod tests {
object.as_script_object().unwrap().define_value( object.as_script_object().unwrap().define_value(
context.gc_context, context.gc_context,
"test", "test",
Gc::allocate(context.gc_context, "initial".to_string()).into(), Avm1String::new(context.gc_context, "initial".to_string()).into(),
DontDelete.into(), DontDelete.into(),
); );
assert_eq!(object.delete(activation, context.gc_context, "test"), false); assert_eq!(object.delete(activation, context.gc_context, "test"), false);
assert_eq!( assert_eq!(
object.get("test", activation, context).unwrap(), object.get("test", activation, context).unwrap(),
Gc::allocate(context.gc_context, "initial".to_string()).into() Avm1String::new(context.gc_context, "initial".to_string()).into()
); );
object object
@ -1043,7 +1043,7 @@ mod tests {
.unwrap() .unwrap()
.set( .set(
"test", "test",
Gc::allocate(context.gc_context, "replaced".to_string()).into(), Avm1String::new(context.gc_context, "replaced".to_string()).into(),
activation, activation,
context, context,
) )
@ -1052,7 +1052,7 @@ mod tests {
assert_eq!(object.delete(activation, context.gc_context, "test"), false); assert_eq!(object.delete(activation, context.gc_context, "test"), false);
assert_eq!( assert_eq!(
object.get("test", activation, context).unwrap(), object.get("test", activation, context).unwrap(),
Gc::allocate(context.gc_context, "replaced".to_string()).into() Avm1String::new(context.gc_context, "replaced".to_string()).into()
); );
}) })
} }
@ -1061,7 +1061,7 @@ mod tests {
fn test_virtual_get() { fn test_virtual_get() {
with_object(0, |activation, context, object| { with_object(0, |activation, context, object| {
let getter = Executable::Native(|_avm, context, _this, _args| { let getter = Executable::Native(|_avm, context, _this, _args| {
Ok(Gc::allocate(context.gc_context, "Virtual!".to_string()).into()) Ok(Avm1String::new(context.gc_context, "Virtual!".to_string()).into())
}); });
object.as_script_object().unwrap().add_property( object.as_script_object().unwrap().add_property(
@ -1074,21 +1074,21 @@ mod tests {
assert_eq!( assert_eq!(
object.get("test", activation, context).unwrap(), object.get("test", activation, context).unwrap(),
Gc::allocate(context.gc_context, "Virtual!".to_string()).into() Avm1String::new(context.gc_context, "Virtual!".to_string()).into()
); );
// This set should do nothing // This set should do nothing
object object
.set( .set(
"test", "test",
Gc::allocate(context.gc_context, "Ignored!".to_string()).into(), Avm1String::new(context.gc_context, "Ignored!".to_string()).into(),
activation, activation,
context, context,
) )
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
object.get("test", activation, context).unwrap(), object.get("test", activation, context).unwrap(),
Gc::allocate(context.gc_context, "Virtual!".to_string()).into() Avm1String::new(context.gc_context, "Virtual!".to_string()).into()
); );
}) })
} }
@ -1097,7 +1097,7 @@ mod tests {
fn test_delete() { fn test_delete() {
with_object(0, |activation, context, object| { with_object(0, |activation, context, object| {
let getter = Executable::Native(|_avm, context, _this, _args| { let getter = Executable::Native(|_avm, context, _this, _args| {
Ok(Gc::allocate(context.gc_context, "Virtual!".to_string()).into()) Ok(Avm1String::new(context.gc_context, "Virtual!".to_string()).into())
}); });
object.as_script_object().unwrap().add_property( object.as_script_object().unwrap().add_property(
@ -1117,13 +1117,13 @@ mod tests {
object.as_script_object().unwrap().define_value( object.as_script_object().unwrap().define_value(
context.gc_context, context.gc_context,
"stored", "stored",
Gc::allocate(context.gc_context, "Stored!".to_string()).into(), Avm1String::new(context.gc_context, "Stored!".to_string()).into(),
EnumSet::empty(), EnumSet::empty(),
); );
object.as_script_object().unwrap().define_value( object.as_script_object().unwrap().define_value(
context.gc_context, context.gc_context,
"stored_un", "stored_un",
Gc::allocate(context.gc_context, "Stored!".to_string()).into(), Avm1String::new(context.gc_context, "Stored!".to_string()).into(),
DontDelete.into(), DontDelete.into(),
); );
@ -1154,7 +1154,7 @@ mod tests {
); );
assert_eq!( assert_eq!(
object.get("virtual_un", activation, context).unwrap(), object.get("virtual_un", activation, context).unwrap(),
Gc::allocate(context.gc_context, "Virtual!".to_string()).into() Avm1String::new(context.gc_context, "Virtual!".to_string()).into()
); );
assert_eq!( assert_eq!(
object.get("stored", activation, context).unwrap(), object.get("stored", activation, context).unwrap(),
@ -1162,7 +1162,7 @@ mod tests {
); );
assert_eq!( assert_eq!(
object.get("stored_un", activation, context).unwrap(), object.get("stored_un", activation, context).unwrap(),
Gc::allocate(context.gc_context, "Stored!".to_string()).into() Avm1String::new(context.gc_context, "Stored!".to_string()).into()
); );
}) })
} }

View File

@ -5,12 +5,12 @@ use crate::avm1::error::Error;
use crate::avm1::function::Executable; use crate::avm1::function::Executable;
use crate::avm1::object::search_prototype; use crate::avm1::object::search_prototype;
use crate::avm1::property::Attribute; use crate::avm1::property::Attribute;
use crate::avm1::{Object, ObjectPtr, ScriptObject, TDisplayObject, TObject, Value}; use crate::avm1::{Avm1String, Object, ObjectPtr, ScriptObject, TDisplayObject, TObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use crate::display_object::{DisplayObject, EditText, MovieClip}; use crate::display_object::{DisplayObject, EditText, MovieClip};
use crate::property_map::PropertyMap; use crate::property_map::PropertyMap;
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::{Collect, Gc, GcCell, MutationContext}; use gc_arena::{Collect, GcCell, MutationContext};
use std::borrow::Cow; use std::borrow::Cow;
use std::fmt; use std::fmt;
@ -853,7 +853,7 @@ fn target<'gc>(
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>, this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(Gc::allocate(context.gc_context, this.slash_path()).into()) Ok(Avm1String::new(context.gc_context, this.slash_path()).into())
} }
fn frames_loaded<'gc>( fn frames_loaded<'gc>(
@ -873,7 +873,7 @@ fn name<'gc>(
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>, this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(Gc::allocate(context.gc_context, this.name().to_string()).into()) Ok(Avm1String::new(context.gc_context, this.name().to_string()).into())
} }
fn set_name<'gc>( fn set_name<'gc>(
@ -893,7 +893,7 @@ fn drop_target<'gc>(
_this: DisplayObject<'gc>, _this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("Unimplemented property _droptarget"); log::warn!("Unimplemented property _droptarget");
Ok(Gc::allocate(context.gc_context, "".to_string()).into()) Ok(Avm1String::new(context.gc_context, "".to_string()).into())
} }
fn url<'gc>( fn url<'gc>(
@ -902,7 +902,7 @@ fn url<'gc>(
_this: DisplayObject<'gc>, _this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("Unimplemented property _url"); log::warn!("Unimplemented property _url");
Ok(Gc::allocate(context.gc_context, "".to_string()).into()) Ok(Avm1String::new(context.gc_context, "".to_string()).into())
} }
fn high_quality<'gc>( fn high_quality<'gc>(
@ -968,7 +968,7 @@ fn quality<'gc>(
_this: DisplayObject<'gc>, _this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("Unimplemented property _quality"); log::warn!("Unimplemented property _quality");
Ok(Gc::allocate(context.gc_context, "HIGH".to_string()).into()) Ok(Avm1String::new(context.gc_context, "HIGH".to_string()).into())
} }
fn set_quality<'gc>( fn set_quality<'gc>(

View File

@ -5,10 +5,10 @@ use crate::avm1::error::Error;
use crate::avm1::function::Executable; use crate::avm1::function::Executable;
use crate::avm1::object::{ObjectPtr, TObject}; use crate::avm1::object::{ObjectPtr, TObject};
use crate::avm1::property::Attribute; use crate::avm1::property::Attribute;
use crate::avm1::{Object, ScriptObject, UpdateContext, Value}; use crate::avm1::{Avm1String, Object, ScriptObject, UpdateContext, Value};
use crate::xml::{XMLName, XMLNode}; use crate::xml::{XMLName, XMLNode};
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::{Collect, Gc, MutationContext}; use gc_arena::{Collect, MutationContext};
use std::borrow::Cow; use std::borrow::Cow;
use std::fmt; use std::fmt;
@ -67,7 +67,7 @@ impl<'gc> TObject<'gc> for XMLAttributesObject<'gc> {
Ok(self Ok(self
.node() .node()
.attribute_value(&XMLName::from_str(name)) .attribute_value(&XMLName::from_str(name))
.map(|s| Gc::allocate(context.gc_context, s).into()) .map(|s| Avm1String::new(context.gc_context, s).into())
.unwrap_or_else(|| Value::Undefined)) .unwrap_or_else(|| Value::Undefined))
} }

56
core/src/avm1/string.rs Normal file
View File

@ -0,0 +1,56 @@
use gc_arena::{Collect, Gc, MutationContext};
use std::ops::Deref;
#[derive(Debug, Clone, Collect)]
#[collect(no_drop)]
pub struct Avm1String<'gc>(Gc<'gc, String>);
impl<'gc> Avm1String<'gc> {
pub fn new<S: Into<String>>(gc_context: MutationContext<'gc, '_>, string: S) -> Self {
Self(Gc::allocate(gc_context, string.into()))
}
pub fn as_str(&self) -> &str {
self
}
}
impl Deref for Avm1String<'_> {
type Target = str;
#[inline]
fn deref(&self) -> &str {
self.0.deref()
}
}
impl AsRef<str> for Avm1String<'_> {
#[inline]
fn as_ref(&self) -> &str {
self
}
}
macro_rules! impl_eq {
($lhs:ty, $rhs: ty) => {
#[allow(unused_lifetimes)]
impl<'a, 'b> PartialEq<$rhs> for $lhs {
#[inline]
fn eq(&self, other: &$rhs) -> bool {
PartialEq::eq(&self[..], &other[..])
}
}
#[allow(unused_lifetimes)]
impl<'a, 'b> PartialEq<$lhs> for $rhs {
#[inline]
fn eq(&self, other: &$lhs) -> bool {
PartialEq::eq(&self[..], &other[..])
}
}
};
}
impl_eq! { Avm1String<'_>, str }
impl_eq! { Avm1String<'_>, &'a str }
impl_eq! { Avm1String<'_>, String }

View File

@ -1,7 +1,6 @@
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::test_utils::with_avm; use crate::avm1::test_utils::with_avm;
use crate::avm1::TObject; use crate::avm1::{Avm1String, TObject};
use gc_arena::Gc;
#[test] #[test]
fn locals_into_form_values() { fn locals_into_form_values() {
@ -10,7 +9,7 @@ fn locals_into_form_values() {
my_locals my_locals
.set( .set(
"value1", "value1",
Gc::allocate(context.gc_context, "string".to_string()).into(), Avm1String::new(context.gc_context, "string".to_string()).into(),
activation, activation,
context, context,
) )

View File

@ -1,8 +1,7 @@
use crate::avm1::activation::Activation; use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::object::value_object::ValueObject; use crate::avm1::object::value_object::ValueObject;
use crate::avm1::{Object, TObject, UpdateContext}; use crate::avm1::{Avm1String, Object, TObject, UpdateContext};
use gc_arena::Gc;
use std::borrow::Cow; use std::borrow::Cow;
use std::f64::NAN; use std::f64::NAN;
@ -13,12 +12,12 @@ pub enum Value<'gc> {
Null, Null,
Bool(bool), Bool(bool),
Number(f64), Number(f64),
String(Gc<'gc, String>), String(Avm1String<'gc>),
Object(Object<'gc>), Object(Object<'gc>),
} }
impl<'gc> From<Gc<'gc, String>> for Value<'gc> { impl<'gc> From<Avm1String<'gc>> for Value<'gc> {
fn from(string: Gc<'gc, String>) -> Self { fn from(string: Avm1String<'gc>) -> Self {
Value::String(string) Value::String(string)
} }
} }
@ -596,10 +595,9 @@ mod test {
use crate::avm1::object::script_object::ScriptObject; use crate::avm1::object::script_object::ScriptObject;
use crate::avm1::object::{Object, TObject}; use crate::avm1::object::{Object, TObject};
use crate::avm1::test_utils::with_avm; use crate::avm1::test_utils::with_avm;
use crate::avm1::Value; use crate::avm1::{Avm1String, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::Gc;
use std::f64::{INFINITY, NAN, NEG_INFINITY}; use std::f64::{INFINITY, NAN, NEG_INFINITY};
#[test] #[test]
@ -792,8 +790,8 @@ mod test {
#[test] #[test]
fn abstract_lt_str() { fn abstract_lt_str() {
with_avm(8, |activation, context, _this| -> Result<(), Error> { with_avm(8, |activation, context, _this| -> Result<(), Error> {
let a = Value::String(Gc::allocate(context.gc_context, "a".to_owned())); let a = Value::String(Avm1String::new(context.gc_context, "a".to_owned()));
let b = Value::String(Gc::allocate(context.gc_context, "b".to_owned())); let b = Value::String(Avm1String::new(context.gc_context, "b".to_owned()));
assert_eq!( assert_eq!(
a.abstract_lt(b, activation, context).unwrap(), a.abstract_lt(b, activation, context).unwrap(),
@ -807,8 +805,8 @@ mod test {
#[test] #[test]
fn abstract_gt_str() { fn abstract_gt_str() {
with_avm(8, |activation, context, _this| -> Result<(), Error> { with_avm(8, |activation, context, _this| -> Result<(), Error> {
let a = Value::String(Gc::allocate(context.gc_context, "a".to_owned())); let a = Value::String(Avm1String::new(context.gc_context, "a".to_owned()));
let b = Value::String(Gc::allocate(context.gc_context, "b".to_owned())); let b = Value::String(Avm1String::new(context.gc_context, "b".to_owned()));
assert_eq!( assert_eq!(
b.abstract_lt(a, activation, context).unwrap(), b.abstract_lt(a, activation, context).unwrap(),

View File

@ -1,7 +1,7 @@
//! `EditText` display object and support code. //! `EditText` display object and support code.
use crate::avm1::activation::Activation; use crate::avm1::activation::Activation;
use crate::avm1::globals::text_field::attach_virtual_properties; use crate::avm1::globals::text_field::attach_virtual_properties;
use crate::avm1::{Avm1, Object, StageObject, TObject, Value}; use crate::avm1::{Avm1, Avm1String, Object, StageObject, TObject, Value};
use crate::context::{RenderContext, UpdateContext}; use crate::context::{RenderContext, UpdateContext};
use crate::display_object::{DisplayObjectBase, TDisplayObject}; use crate::display_object::{DisplayObjectBase, TDisplayObject};
use crate::drawing::Drawing; use crate::drawing::Drawing;
@ -743,7 +743,7 @@ impl<'gc> EditText<'gc> {
if !text.is_empty() { if !text.is_empty() {
let _ = object.set( let _ = object.set(
property, property,
Gc::allocate(context.gc_context, self.text()).into(), Avm1String::new(context.gc_context, self.text()).into(),
activation, activation,
context, context,
); );
@ -814,7 +814,7 @@ impl<'gc> EditText<'gc> {
|activation, context| { |activation, context| {
let _ = object.set( let _ = object.set(
property, property,
Gc::allocate(context.gc_context, text).into(), Avm1String::new(context.gc_context, text).into(),
activation, activation,
context, context,
); );

View File

@ -1,11 +1,11 @@
//! Classes that store formatting options //! Classes that store formatting options
use crate::avm1::activation::Activation; use crate::avm1::activation::Activation;
use crate::avm1::{Object, ScriptObject, TObject, Value}; use crate::avm1::{Avm1String, Object, ScriptObject, TObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use crate::html::iterators::TextSpanIter; use crate::html::iterators::TextSpanIter;
use crate::tag_utils::SwfMovie; use crate::tag_utils::SwfMovie;
use crate::xml::{Step, XMLDocument, XMLName, XMLNode}; use crate::xml::{Step, XMLDocument, XMLName, XMLNode};
use gc_arena::{Collect, Gc, MutationContext}; use gc_arena::{Collect, MutationContext};
use std::borrow::Cow; use std::borrow::Cow;
use std::cmp::{min, Ordering}; use std::cmp::{min, Ordering};
use std::sync::Arc; use std::sync::Arc;
@ -362,7 +362,7 @@ impl TextFormat {
"font", "font",
self.font self.font
.clone() .clone()
.map(|v| Gc::allocate(uc.gc_context, v).into()) .map(|v| Avm1String::new(uc.gc_context, v).into())
.unwrap_or(Value::Null), .unwrap_or(Value::Null),
activation, activation,
uc, uc,
@ -386,7 +386,7 @@ impl TextFormat {
"align", "align",
self.align self.align
.map(|v| { .map(|v| {
Gc::allocate( Avm1String::new(
uc.gc_context, uc.gc_context,
match v { match v {
swf::TextAlign::Left => "left", swf::TextAlign::Left => "left",
@ -472,7 +472,7 @@ impl TextFormat {
"url", "url",
self.url self.url
.clone() .clone()
.map(|v| Gc::allocate(uc.gc_context, v).into()) .map(|v| Avm1String::new(uc.gc_context, v).into())
.unwrap_or(Value::Null), .unwrap_or(Value::Null),
activation, activation,
uc, uc,
@ -481,7 +481,7 @@ impl TextFormat {
"target", "target",
self.target self.target
.clone() .clone()
.map(|v| Gc::allocate(uc.gc_context, v).into()) .map(|v| Avm1String::new(uc.gc_context, v).into())
.unwrap_or(Value::Null), .unwrap_or(Value::Null),
activation, activation,
uc, uc,

View File

@ -1,14 +1,14 @@
//! Management of async loaders //! Management of async loaders
use crate::avm1::activation::{Activation, ActivationIdentifier}; use crate::avm1::activation::{Activation, ActivationIdentifier};
use crate::avm1::{Object, TObject, Value}; use crate::avm1::{Avm1String, Object, TObject, Value};
use crate::backend::navigator::OwnedFuture; use crate::backend::navigator::OwnedFuture;
use crate::context::{ActionQueue, ActionType}; use crate::context::{ActionQueue, ActionType};
use crate::display_object::{DisplayObject, MorphShape, TDisplayObject}; use crate::display_object::{DisplayObject, MorphShape, TDisplayObject};
use crate::player::{Player, NEWEST_PLAYER_VERSION}; use crate::player::{Player, NEWEST_PLAYER_VERSION};
use crate::tag_utils::SwfMovie; use crate::tag_utils::SwfMovie;
use crate::xml::XMLNode; use crate::xml::XMLNode;
use gc_arena::{Collect, CollectionContext, Gc, MutationContext}; use gc_arena::{Collect, CollectionContext, MutationContext};
use generational_arena::{Arena, Index}; use generational_arena::{Arena, Index};
use std::string::FromUtf8Error; use std::string::FromUtf8Error;
use std::sync::{Arc, Mutex, Weak}; use std::sync::{Arc, Mutex, Weak};
@ -323,7 +323,7 @@ impl<'gc> Loader<'gc> {
uc, uc,
"broadcastMessage", "broadcastMessage",
&[ &[
Gc::allocate(uc.gc_context, "onLoadStart".to_string()).into(), Avm1String::new(uc.gc_context, "onLoadStart".to_string()).into(),
Value::Object(broadcaster), Value::Object(broadcaster),
], ],
); );
@ -359,7 +359,7 @@ impl<'gc> Loader<'gc> {
uc, uc,
"broadcastMessage", "broadcastMessage",
&[ &[
Gc::allocate(uc.gc_context, "onLoadProgress".to_string()) Avm1String::new(uc.gc_context, "onLoadProgress".to_string())
.into(), .into(),
Value::Object(broadcaster), Value::Object(broadcaster),
length.into(), length.into(),
@ -397,7 +397,7 @@ impl<'gc> Loader<'gc> {
uc, uc,
"broadcastMessage", "broadcastMessage",
&[ &[
Gc::allocate(uc.gc_context, "onLoadComplete".to_string()) Avm1String::new(uc.gc_context, "onLoadComplete".to_string())
.into(), .into(),
Value::Object(broadcaster), Value::Object(broadcaster),
], ],
@ -438,10 +438,14 @@ impl<'gc> Loader<'gc> {
uc, uc,
"broadcastMessage", "broadcastMessage",
&[ &[
Gc::allocate(uc.gc_context, "onLoadError".to_string()).into(), Avm1String::new(uc.gc_context, "onLoadError".to_string())
Value::Object(broadcaster),
Gc::allocate(uc.gc_context, "LoadNeverCompleted".to_string())
.into(), .into(),
Value::Object(broadcaster),
Avm1String::new(
uc.gc_context,
"LoadNeverCompleted".to_string(),
)
.into(),
], ],
); );
} }
@ -495,7 +499,7 @@ impl<'gc> Loader<'gc> {
for (k, v) in form_urlencoded::parse(&data) { for (k, v) in form_urlencoded::parse(&data) {
that.set( that.set(
&k, &k,
Gc::allocate(uc.gc_context, v.into_owned()).into(), Avm1String::new(uc.gc_context, v.into_owned()).into(),
&mut activation, &mut activation,
uc, uc,
)?; )?;
@ -536,7 +540,7 @@ impl<'gc> Loader<'gc> {
object: broadcaster, object: broadcaster,
name: "broadcastMessage", name: "broadcastMessage",
args: vec![ args: vec![
Gc::allocate(gc_context, "onLoadInit".to_string()).into(), Avm1String::new(gc_context, "onLoadInit".to_string()).into(),
clip_object.map(|co| co.into()).unwrap_or(Value::Undefined), clip_object.map(|co| co.into()).unwrap_or(Value::Undefined),
], ],
}, },
@ -598,7 +602,7 @@ impl<'gc> Loader<'gc> {
NEWEST_PLAYER_VERSION, NEWEST_PLAYER_VERSION,
uc, uc,
"onData", "onData",
&[Gc::allocate(uc.gc_context, xmlstring).into()], &[Avm1String::new(uc.gc_context, xmlstring).into()],
); );
Ok(()) Ok(())

View File

@ -3,7 +3,7 @@ use crate::avm1::debug::VariableDumper;
use crate::avm1::globals::system::SystemProperties; use crate::avm1::globals::system::SystemProperties;
use crate::avm1::listeners::SystemListener; use crate::avm1::listeners::SystemListener;
use crate::avm1::object::Object; use crate::avm1::object::Object;
use crate::avm1::{Avm1, TObject, Timers, Value}; use crate::avm1::{Avm1, Avm1String, TObject, Timers, Value};
use crate::backend::input::{InputBackend, MouseCursor}; use crate::backend::input::{InputBackend, MouseCursor};
use crate::backend::storage::StorageBackend; use crate::backend::storage::StorageBackend;
use crate::backend::{ use crate::backend::{
@ -18,7 +18,7 @@ use crate::prelude::*;
use crate::tag_utils::SwfMovie; use crate::tag_utils::SwfMovie;
use crate::transform::TransformStack; use crate::transform::TransformStack;
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::{make_arena, ArenaParameters, Collect, Gc, GcCell}; use gc_arena::{make_arena, ArenaParameters, Collect, GcCell};
use log::info; use log::info;
use rand::{rngs::SmallRng, SeedableRng}; use rand::{rngs::SmallRng, SeedableRng};
use std::collections::{BTreeMap, HashMap}; use std::collections::{BTreeMap, HashMap};
@ -292,7 +292,7 @@ impl Player {
object.define_value( object.define_value(
context.gc_context, context.gc_context,
"$version", "$version",
Gc::allocate( Avm1String::new(
context.gc_context, context.gc_context,
context.system.get_version_string(&mut activation), context.system.get_version_string(&mut activation),
) )