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;
mod property;
mod scope;
mod string;
mod timer;
mod value;
@ -41,6 +42,7 @@ pub use object::stage_object::StageObject;
pub use object::{Object, ObjectPtr, TObject};
use scope::Scope;
use smallvec::alloc::borrow::Cow;
pub use string::Avm1String;
pub use timer::Timers;
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::scope::Scope;
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::context::UpdateContext;
use crate::display_object::{DisplayObject, MovieClip, TDisplayObject};
@ -587,11 +589,11 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
if let Value::String(a) = a {
let mut s = b.coerce_to_string(self, context)?.to_string();
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 {
let mut b = b.to_string();
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 {
let result = b.coerce_to_f64(self, context)? + a.coerce_to_f64(self, context)?;
self.avm.push(result);
@ -620,7 +622,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
// TODO(Herschel): Results on incorrect operands?
let val = (self.avm.pop().coerce_to_f64(self, context)? as u8) as char;
self.avm
.push(Gc::allocate(context.gc_context, val.to_string()));
.push(Avm1String::new(context.gc_context, val.to_string()));
Ok(FrameControl::Continue)
}
@ -1059,7 +1061,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
match object {
Value::Object(ob) => {
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),
@ -1078,7 +1080,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
if let Value::Object(object) = value {
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 {
log::warn!("Cannot enumerate {:?}", value);
@ -1572,7 +1574,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
match result {
Ok(val) => self
.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),
}
Ok(FrameControl::Continue)
@ -1600,7 +1602,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
let val = self.avm.pop();
let s = val.coerce_to_string(self, context)?;
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)
}
@ -1826,11 +1828,11 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
SwfValue::Int(v) => f64::from(*v).into(),
SwfValue::Float(v) => f64::from(*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::ConstantPool(i) => {
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 {
log::warn!(
"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 mut b = self.avm.pop().coerce_to_string(self, context)?.to_string();
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)
}
@ -2158,7 +2160,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
.take(len)
.map(|c| c as char)
.collect::<String>();
self.avm.push(Gc::allocate(context.gc_context, result));
self.avm.push(Avm1String::new(context.gc_context, result));
Ok(FrameControl::Continue)
}
@ -2263,7 +2265,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
let val = self.avm.pop();
let string = val.coerce_to_string(self, context)?;
self.avm
.push(Gc::allocate(context.gc_context, string.to_string()));
.push(Avm1String::new(context.gc_context, string.to_string()));
Ok(FrameControl::Continue)
}
@ -2289,7 +2291,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
) -> Result<FrameControl<'gc>, Error<'gc>> {
let type_of = self.avm.pop().type_of();
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)
}

View File

@ -198,8 +198,7 @@ mod tests {
use super::*;
use crate::avm1::error::Error;
use crate::avm1::test_utils::with_avm;
use crate::avm1::ScriptObject;
use gc_arena::Gc;
use crate::avm1::{Avm1String, ScriptObject};
#[test]
fn dump_undefined() {
@ -258,7 +257,7 @@ mod tests {
with_avm(19, |activation, context, _root| -> Result<(), Error> {
assert_eq!(
VariableDumper::dump(
&Value::String(Gc::allocate(context.gc_context, "".to_string())),
&Value::String(Avm1String::new(context.gc_context, "".to_string())),
" ",
activation,
context
@ -267,7 +266,10 @@ mod tests {
);
assert_eq!(
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,
context
@ -276,7 +278,7 @@ mod tests {
);
assert_eq!(
VariableDumper::dump(
&Value::String(Gc::allocate(
&Value::String(Avm1String::new(
context.gc_context,
"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(
"test",
Value::String(Gc::allocate(context.gc_context, "value".to_string())),
Value::String(Avm1String::new(context.gc_context, "value".to_string())),
activation,
context,
)?;
@ -333,7 +335,7 @@ mod tests {
object.set("self", object.into(), activation, context)?;
object.set(
"test",
Value::String(Gc::allocate(context.gc_context, "value".to_string())),
Value::String(Avm1String::new(context.gc_context, "value".to_string())),
activation,
context,
)?;

View File

@ -6,7 +6,7 @@ use crate::avm1::object::super_object::SuperObject;
use crate::avm1::property::{Attribute, Attribute::*};
use crate::avm1::scope::Scope;
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::tag_utils::SwfSlice;
use enumset::EnumSet;
@ -448,7 +448,7 @@ impl<'gc> FunctionObject<'gc> {
gc_context,
FunctionObjectData {
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,
FunctionObjectData {
function: None,
primitive: Gc::allocate(context.gc_context, "[type Function]".to_string())
primitive: Avm1String::new(context.gc_context, "[type Function]".to_string())
.into(),
},
),

View File

@ -63,8 +63,8 @@ pub fn getURL<'a, 'gc>(
None
};
let method = match args.get(2) {
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 == "GET" => Some(NavigationMethod::GET),
Some(Value::String(s)) if *s == "POST" => Some(NavigationMethod::POST),
_ => None,
};
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::object::value_object;
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 gc_arena::{Gc, MutationContext};
use gc_arena::MutationContext;
use smallvec::alloc::borrow::Cow;
use std::cmp::Ordering;
@ -247,7 +247,7 @@ pub fn join<'gc>(
.unwrap_or_else(|| Cow::Borrowed(","));
let values: Vec<Value<'gc>> = this.array();
Ok(Gc::allocate(
Ok(Avm1String::new(
context.gc_context,
values
.iter()

View File

@ -4,10 +4,10 @@ use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject};
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 enumset::EnumSet;
use gc_arena::{Gc, MutationContext};
use gc_arena::MutationContext;
/// `Boolean` constructor/function
pub fn boolean<'gc>(
@ -83,7 +83,7 @@ pub fn to_string<'gc>(
// Must be a bool.
// Boolean.prototype.toString.call(x) returns undefined for non-bools.
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::error::Error;
use crate::avm1::function::Executable;
use crate::avm1::{Object, TObject, Value};
use crate::avm1::{Avm1String, Object, TObject, Value};
use crate::context::UpdateContext;
use enumset::EnumSet;
use gc_arena::{Gc, MutationContext};
use gc_arena::MutationContext;
use crate::avm1::object::color_transform_object::ColorTransformObject;
use crate::color_transform::ColorTransform;
@ -264,7 +264,10 @@ fn to_string<'gc>(
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>(

View File

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

View File

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

View File

@ -3,9 +3,9 @@
use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
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 gc_arena::{Gc, MutationContext};
use gc_arena::MutationContext;
/// Implements `Function`
pub fn constructor<'gc>(
@ -97,7 +97,7 @@ fn to_string<'gc>(
_: Object<'gc>,
_: &[Value<'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`.

View File

@ -3,10 +3,10 @@
use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
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 enumset::EnumSet;
use gc_arena::{Gc, MutationContext};
use gc_arena::MutationContext;
use swf::{Matrix, Twips};
pub fn value_to_matrix<'gc>(
@ -403,7 +403,7 @@ fn to_string<'gc>(
let tx = this.get("tx", activation, context)?;
let ty = this.get("ty", activation, context)?;
Ok(Gc::allocate(
Ok(Avm1String::new(
context.gc_context,
format!(
"(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::matrix::gradient_object_to_matrix;
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::display_object::{DisplayObject, EditText, MovieClip, TDisplayObject};
use crate::prelude::*;
use crate::shape_utils::DrawCommand;
use crate::tag_utils::SwfSlice;
use gc_arena::{Gc, MutationContext};
use gc_arena::MutationContext;
use swf::{
FillStyle, Gradient, GradientInterpolation, GradientRecord, GradientSpread, LineCapStyle,
LineJoinStyle, LineStyle, Twips,
@ -900,7 +900,7 @@ fn to_string<'gc>(
context: &mut UpdateContext<'_, 'gc, '_>,
_args: &[Value<'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>(
@ -945,7 +945,7 @@ fn get_bounds<'gc>(
activation.resolve_target_display_object(
context,
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()),

View File

@ -5,10 +5,10 @@ use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::object::value_object::ValueObject;
use crate::avm1::property::Attribute::*;
use crate::avm1::{Object, TObject, Value};
use crate::avm1::{Avm1String, Object, TObject, Value};
use crate::context::UpdateContext;
use enumset::EnumSet;
use gc_arena::{Gc, MutationContext};
use gc_arena::MutationContext;
/// `Number` constructor/function
pub fn number<'gc>(
@ -143,7 +143,7 @@ fn to_string<'gc>(
if radix == 10 {
// Output number as floating-point decimal.
Ok(Gc::allocate(
Ok(Avm1String::new(
context.gc_context,
Value::from(this)
.coerce_to_string(activation, context)?
@ -159,7 +159,7 @@ fn to_string<'gc>(
Ordering::Greater => (n as u32, false),
Ordering::Equal => {
// 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;
}
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 {
// NaN or large numbers.
// Player version specific behavior:
@ -185,7 +185,7 @@ fn to_string<'gc>(
// 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.
// 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::function::{Executable, FunctionObject};
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 enumset::EnumSet;
use gc_arena::{Gc, MutationContext};
use gc_arena::MutationContext;
use std::borrow::Cow;
/// Implements `Object`
@ -93,7 +93,7 @@ fn to_string<'gc>(
_: Object<'gc>,
_: &[Value<'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`

View File

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

View File

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

View File

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

View File

@ -5,8 +5,8 @@ use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::function::Executable;
use crate::avm1::property::Attribute;
use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
use gc_arena::{Gc, MutationContext};
use crate::avm1::{Avm1String, Object, ScriptObject, TObject, UpdateContext, Value};
use gc_arena::MutationContext;
pub fn create_stage_object<'gc>(
gc_context: MutationContext<'gc, '_>,
@ -92,7 +92,7 @@ fn align<'gc>(
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
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>(
@ -121,7 +121,7 @@ fn remove_listener<'gc>(
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
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>(
@ -131,7 +131,7 @@ fn scale_mode<'gc>(
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
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>(

View File

@ -5,11 +5,11 @@ use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::object::value_object::ValueObject;
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::string_utils;
use enumset::EnumSet;
use gc_arena::{Gc, MutationContext};
use gc_arena::MutationContext;
/// `String` constructor
pub fn string<'gc>(
@ -20,17 +20,17 @@ pub fn string<'gc>(
) -> Result<Value<'gc>, Error<'gc>> {
let value = match args.get(0).cloned() {
Some(Value::String(s)) => s,
Some(v) => Gc::allocate(
Some(v) => Avm1String::new(
ac.gc_context,
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() {
let len = value.encode_utf16().count();
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())
@ -184,7 +184,7 @@ fn char_at<'gc>(
let this_val = Value::from(this);
let string = match this_val {
Value::String(string) => string,
other => Gc::allocate(
other => Avm1String::new(
context.gc_context,
other.coerce_to_string(activation, context)?.to_string(),
),
@ -202,7 +202,7 @@ fn char_at<'gc>(
} else {
"".into()
};
Ok(Gc::allocate(context.gc_context, ret).into())
Ok(Avm1String::new(context.gc_context, ret).into())
}
fn char_code_at<'gc>(
@ -241,7 +241,7 @@ fn concat<'gc>(
let s = arg.coerce_to_string(activation, context)?;
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>(
@ -260,7 +260,7 @@ fn from_char_code<'gc>(
}
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>(
@ -393,9 +393,9 @@ fn slice<'gc>(
.skip(start_index)
.take(end_index - start_index),
);
Ok(Gc::allocate(context.gc_context, ret).into())
Ok(Avm1String::new(context.gc_context, ret).into())
} 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 = match this_val {
Value::String(string) => string,
other => Gc::allocate(
other => Avm1String::new(
context.gc_context,
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() {
array.set_array_element(
i,
Gc::allocate(context.gc_context, token.to_string()).into(),
Avm1String::new(context.gc_context, token.to_string()).into(),
context.gc_context,
);
}
@ -435,7 +435,7 @@ fn split<'gc>(
for (i, token) in this.chars().take(limit).enumerate() {
array.set_array_element(
i,
Gc::allocate(context.gc_context, token.to_string()).into(),
Avm1String::new(context.gc_context, token.to_string()).into(),
context.gc_context,
);
}
@ -456,7 +456,7 @@ fn substr<'gc>(
let this_val = Value::from(this);
let this = match this_val {
Value::String(string) => string,
other => Gc::allocate(
other => Avm1String::new(
context.gc_context,
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));
Ok(Gc::allocate(context.gc_context, ret).into())
Ok(Avm1String::new(context.gc_context, ret).into())
}
fn substring<'gc>(
@ -489,7 +489,7 @@ fn substring<'gc>(
let this_val = Value::from(this);
let this = match this_val {
Value::String(string) => string,
other => Gc::allocate(
other => Avm1String::new(
context.gc_context,
other.coerce_to_string(activation, context)?.to_string(),
),
@ -514,7 +514,7 @@ fn substring<'gc>(
.skip(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>(
@ -525,7 +525,7 @@ fn to_lower_case<'gc>(
) -> Result<Value<'gc>, Error<'gc>> {
let this_val = Value::from(this);
let this = this_val.coerce_to_string(activation, context)?;
Ok(Gc::allocate(
Ok(Avm1String::new(
context.gc_context,
this.chars()
.map(string_utils::swf_char_to_lowercase)
@ -561,7 +561,7 @@ fn to_upper_case<'gc>(
) -> Result<Value<'gc>, Error<'gc>> {
let this_val = Value::from(this);
let this = this_val.coerce_to_string(activation, context)?;
Ok(Gc::allocate(
Ok(Avm1String::new(
context.gc_context,
this.chars()
.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::globals::system::SystemCapabilities;
use crate::avm1::object::Object;
use crate::avm1::{ScriptObject, TObject, Value};
use crate::avm1::{Avm1String, ScriptObject, TObject, Value};
use crate::context::UpdateContext;
use enumset::EnumSet;
use gc_arena::{Gc, MutationContext};
use gc_arena::MutationContext;
macro_rules! capabilities_func {
($func_name: ident, $capability: expr) => {
@ -82,7 +82,7 @@ pub fn get_player_type<'gc>(
_this: Object<'gc>,
_args: &[Value<'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>(
@ -91,7 +91,7 @@ pub fn get_screen_color<'gc>(
_this: Object<'gc>,
_args: &[Value<'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>(
@ -100,7 +100,7 @@ pub fn get_language<'gc>(
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
Ok(Gc::allocate(
Ok(Avm1String::new(
context.gc_context,
context
.system
@ -153,7 +153,7 @@ pub fn get_manufacturer<'gc>(
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
Ok(Gc::allocate(
Ok(Avm1String::new(
context.gc_context,
context
.system
@ -169,7 +169,7 @@ pub fn get_os_name<'gc>(
_this: Object<'gc>,
_args: &[Value<'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>(
@ -178,7 +178,7 @@ pub fn get_version<'gc>(
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
Ok(Gc::allocate(
Ok(Avm1String::new(
context.gc_context,
context.system.get_version_string(activation),
)
@ -191,7 +191,7 @@ pub fn get_server_string<'gc>(
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
Ok(Gc::allocate(
Ok(Avm1String::new(
context.gc_context,
context.system.get_server_string(activation),
)
@ -204,7 +204,7 @@ pub fn get_cpu_architecture<'gc>(
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
Ok(Gc::allocate(
Ok(Avm1String::new(
context.gc_context,
context.system.cpu_architecture.to_string(),
)
@ -217,7 +217,7 @@ pub fn get_max_idc_level<'gc>(
_this: Object<'gc>,
_args: &[Value<'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>(

View File

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

View File

@ -2,10 +2,10 @@ use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::function::Executable;
use crate::avm1::object::Object;
use crate::avm1::{ScriptObject, TObject, Value};
use crate::avm1::{Avm1String, ScriptObject, TObject, Value};
use crate::context::UpdateContext;
use enumset::EnumSet;
use gc_arena::{Gc, MutationContext};
use gc_arena::MutationContext;
use std::convert::Into;
fn allow_domain<'gc>(
@ -54,7 +54,7 @@ fn get_sandbox_type<'gc>(
_this: Object<'gc>,
_args: &[Value<'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>(

View File

@ -3,10 +3,10 @@ use crate::avm1::error::Error;
use crate::avm1::function::Executable;
use crate::avm1::globals::display_object;
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::html::TextFormat;
use gc_arena::{Gc, MutationContext};
use gc_arena::MutationContext;
/// Implements `TextField`
pub fn constructor<'gc>(
@ -26,7 +26,7 @@ pub fn get_text<'gc>(
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(display_object) = this.as_display_object() {
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)
@ -93,7 +93,7 @@ pub fn get_html_text<'gc>(
if let Some(display_object) = this.as_display_object() {
if let Some(text_field) = display_object.as_edit_text() {
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())
{
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())
{
return Ok(match etext.autosize() {
AutoSizeMode::None => Gc::allocate(context.gc_context, "none".to_string()).into(),
AutoSizeMode::Left => Gc::allocate(context.gc_context, "left".to_string()).into(),
AutoSizeMode::Center => Gc::allocate(context.gc_context, "center".to_string()).into(),
AutoSizeMode::Right => Gc::allocate(context.gc_context, "right".to_string()).into(),
AutoSizeMode::None => Avm1String::new(context.gc_context, "none".to_string()).into(),
AutoSizeMode::Left => Avm1String::new(context.gc_context, "left".to_string()).into(),
AutoSizeMode::Center => {
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(
match args.get(0).cloned().unwrap_or(Value::Undefined) {
Value::String(s) if *s == "left" => AutoSizeMode::Left,
Value::String(s) if *s == "center" => AutoSizeMode::Center,
Value::String(s) if *s == "right" => AutoSizeMode::Right,
Value::String(s) if s == "left" => AutoSizeMode::Left,
Value::String(s) if s == "center" => AutoSizeMode::Center,
Value::String(s) if s == "right" => AutoSizeMode::Right,
Value::Bool(true) => AutoSizeMode::Left,
_ => AutoSizeMode::None,
},

View File

@ -2,8 +2,8 @@
use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
use gc_arena::{Gc, MutationContext};
use crate::avm1::{Avm1String, Object, ScriptObject, TObject, UpdateContext, Value};
use gc_arena::MutationContext;
fn map_defined_to_string<'gc>(
name: &str,
@ -16,7 +16,7 @@ fn map_defined_to_string<'gc>(
Some(Value::Undefined) => Value::Null,
Some(Value::Null) => Value::Null,
None => Value::Null,
Some(v) => Gc::allocate(
Some(v) => Avm1String::new(
ac.gc_context,
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::xml_object::XMLObject;
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::xml;
use crate::xml::{XMLDocument, XMLNode};
use enumset::EnumSet;
use gc_arena::{Gc, MutationContext};
use gc_arena::MutationContext;
use quick_xml::Error as ParseError;
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)),
) {
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 {
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)),
) {
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 {
Ok(Value::Null)
}
@ -224,7 +224,7 @@ pub fn xmlnode_to_string<'gc>(
if let Some(node) = this.as_xml_node() {
let result = node.into_string(&mut is_as2_compatible);
return Ok(Gc::allocate(
return Ok(Avm1String::new(
ac.gc_context,
result.unwrap_or_else(|e| {
log::warn!("XMLNode toString failed: {}", e);
@ -234,7 +234,7 @@ pub fn xmlnode_to_string<'gc>(
.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>(
@ -246,7 +246,7 @@ pub fn xmlnode_local_name<'gc>(
Ok(this
.as_xml_node()
.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))
}
@ -259,7 +259,7 @@ pub fn xmlnode_node_name<'gc>(
Ok(this
.as_xml_node()
.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))
}
@ -292,7 +292,7 @@ pub fn xmlnode_node_value<'gc>(
Ok(this
.as_xml_node()
.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))
}
@ -306,7 +306,7 @@ pub fn xmlnode_prefix<'gc>(
.as_xml_node()
.and_then(|n| n.tag_name())
.map(|n| {
Gc::allocate(
Avm1String::new(
ac.gc_context,
n.prefix()
.map(|n| n.to_string())
@ -493,7 +493,7 @@ pub fn xmlnode_namespace_uri<'gc>(
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(node) = this.as_xml_node() {
if let Some(name) = node.tag_name() {
return Ok(Gc::allocate(
return Ok(Avm1String::new(
ac.gc_context,
node.lookup_uri_for_namespace(name.prefix().unwrap_or(""))
.unwrap_or_else(|| "".to_string()),
@ -861,7 +861,7 @@ pub fn xml_on_data<'gc>(
let src = src.coerce_to_string(activation, ac)?;
this.call_method(
"parseXML",
&[Gc::allocate(ac.gc_context, src.to_string()).into()],
&[Avm1String::new(ac.gc_context, src.to_string()).into()],
activation,
ac,
)?;
@ -884,7 +884,7 @@ pub fn xml_doc_type_decl<'gc>(
if let Some(doctype) = node.document().doctype() {
let result = doctype.into_string(&mut |_| true);
return Ok(Gc::allocate(
return Ok(Avm1String::new(
ac.gc_context,
result.unwrap_or_else(|e| {
log::warn!("Error occured when serializing DOCTYPE: {}", e);
@ -910,7 +910,7 @@ pub fn xml_xml_decl<'gc>(
if let Err(e) = result {
log::warn!("Could not generate XML declaration for document: {}", e);
} 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::function::{Executable, ExecutionReason, FunctionObject, NativeFunction};
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 core::fmt;
use enumset::EnumSet;
use gc_arena::{Collect, Gc, GcCell, MutationContext};
use gc_arena::{Collect, GcCell, MutationContext};
use std::borrow::Cow;
pub const TYPE_OF_OBJECT: &str = "object";
@ -45,7 +45,7 @@ impl<'gc> Watcher<'gc> {
base_proto: Option<Object<'gc>>,
) -> Result<Value<'gc>, crate::avm1::error::Error<'gc>> {
let args = [
Value::String(Gc::allocate(context.gc_context, name.to_string())),
Value::String(Avm1String::new(context.gc_context, name.to_string())),
old_value,
new_value,
self.user_data.clone(),
@ -853,7 +853,7 @@ mod tests {
use crate::avm1::activation::ActivationIdentifier;
use crate::avm1::globals::system::SystemProperties;
use crate::avm1::property::Attribute::*;
use crate::avm1::{Avm1, Timers};
use crate::avm1::{Avm1, Avm1String, Timers};
use crate::backend::audio::NullAudioBackend;
use crate::backend::input::NullInputBackend;
use crate::backend::navigator::NullNavigatorBackend;
@ -864,7 +864,7 @@ mod tests {
use crate::loader::LoadManager;
use crate::prelude::*;
use crate::tag_utils::{SwfMovie, SwfSlice};
use gc_arena::{rootless_arena, Gc};
use gc_arena::rootless_arena;
use rand::{rngs::SmallRng, SeedableRng};
use std::collections::{BTreeMap, HashMap};
use std::sync::Arc;
@ -955,13 +955,13 @@ mod tests {
object.as_script_object().unwrap().define_value(
context.gc_context,
"forced",
Gc::allocate(context.gc_context, "forced".to_string()).into(),
Avm1String::new(context.gc_context, "forced".to_string()).into(),
EnumSet::empty(),
);
object
.set(
"natural",
Gc::allocate(context.gc_context, "natural".to_string()).into(),
Avm1String::new(context.gc_context, "natural".to_string()).into(),
activation,
context,
)
@ -969,11 +969,11 @@ mod tests {
assert_eq!(
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!(
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(
context.gc_context,
"normal",
Gc::allocate(context.gc_context, "initial".to_string()).into(),
Avm1String::new(context.gc_context, "initial".to_string()).into(),
EnumSet::empty(),
);
object.as_script_object().unwrap().define_value(
context.gc_context,
"readonly",
Gc::allocate(context.gc_context, "initial".to_string()).into(),
Avm1String::new(context.gc_context, "initial".to_string()).into(),
ReadOnly.into(),
);
object
.set(
"normal",
Gc::allocate(context.gc_context, "replaced".to_string()).into(),
Avm1String::new(context.gc_context, "replaced".to_string()).into(),
activation,
context,
)
@ -1005,7 +1005,7 @@ mod tests {
object
.set(
"readonly",
Gc::allocate(context.gc_context, "replaced".to_string()).into(),
Avm1String::new(context.gc_context, "replaced".to_string()).into(),
activation,
context,
)
@ -1013,11 +1013,11 @@ mod tests {
assert_eq!(
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!(
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(
context.gc_context,
"test",
Gc::allocate(context.gc_context, "initial".to_string()).into(),
Avm1String::new(context.gc_context, "initial".to_string()).into(),
DontDelete.into(),
);
assert_eq!(object.delete(activation, context.gc_context, "test"), false);
assert_eq!(
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
@ -1043,7 +1043,7 @@ mod tests {
.unwrap()
.set(
"test",
Gc::allocate(context.gc_context, "replaced".to_string()).into(),
Avm1String::new(context.gc_context, "replaced".to_string()).into(),
activation,
context,
)
@ -1052,7 +1052,7 @@ mod tests {
assert_eq!(object.delete(activation, context.gc_context, "test"), false);
assert_eq!(
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() {
with_object(0, |activation, context, object| {
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(
@ -1074,21 +1074,21 @@ mod tests {
assert_eq!(
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
object
.set(
"test",
Gc::allocate(context.gc_context, "Ignored!".to_string()).into(),
Avm1String::new(context.gc_context, "Ignored!".to_string()).into(),
activation,
context,
)
.unwrap();
assert_eq!(
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() {
with_object(0, |activation, context, object| {
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(
@ -1117,13 +1117,13 @@ mod tests {
object.as_script_object().unwrap().define_value(
context.gc_context,
"stored",
Gc::allocate(context.gc_context, "Stored!".to_string()).into(),
Avm1String::new(context.gc_context, "Stored!".to_string()).into(),
EnumSet::empty(),
);
object.as_script_object().unwrap().define_value(
context.gc_context,
"stored_un",
Gc::allocate(context.gc_context, "Stored!".to_string()).into(),
Avm1String::new(context.gc_context, "Stored!".to_string()).into(),
DontDelete.into(),
);
@ -1154,7 +1154,7 @@ mod tests {
);
assert_eq!(
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!(
object.get("stored", activation, context).unwrap(),
@ -1162,7 +1162,7 @@ mod tests {
);
assert_eq!(
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::object::search_prototype;
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::display_object::{DisplayObject, EditText, MovieClip};
use crate::property_map::PropertyMap;
use enumset::EnumSet;
use gc_arena::{Collect, Gc, GcCell, MutationContext};
use gc_arena::{Collect, GcCell, MutationContext};
use std::borrow::Cow;
use std::fmt;
@ -853,7 +853,7 @@ fn target<'gc>(
context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'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>(
@ -873,7 +873,7 @@ fn name<'gc>(
context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'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>(
@ -893,7 +893,7 @@ fn drop_target<'gc>(
_this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
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>(
@ -902,7 +902,7 @@ fn url<'gc>(
_this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
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>(
@ -968,7 +968,7 @@ fn quality<'gc>(
_this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
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>(

View File

@ -5,10 +5,10 @@ use crate::avm1::error::Error;
use crate::avm1::function::Executable;
use crate::avm1::object::{ObjectPtr, TObject};
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 enumset::EnumSet;
use gc_arena::{Collect, Gc, MutationContext};
use gc_arena::{Collect, MutationContext};
use std::borrow::Cow;
use std::fmt;
@ -67,7 +67,7 @@ impl<'gc> TObject<'gc> for XMLAttributesObject<'gc> {
Ok(self
.node()
.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))
}

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

View File

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

View File

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

View File

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

View File

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

View File

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