avm1: Allow Avm1String to contain &'static str

This commit is contained in:
Nathan Adams 2020-07-13 00:43:12 +02:00 committed by Mike Welsh
parent ad733f2f21
commit e03e3f6c4e
16 changed files with 106 additions and 95 deletions

View File

@ -257,7 +257,7 @@ mod tests {
with_avm(19, |activation, context, _root| -> Result<(), Error> {
assert_eq!(
VariableDumper::dump(
&Value::String(Avm1String::new(context.gc_context, "".to_string())),
&Value::String(Avm1String::from("")),
" ",
activation,
context
@ -266,10 +266,7 @@ mod tests {
);
assert_eq!(
VariableDumper::dump(
&Value::String(Avm1String::new(
context.gc_context,
"HELLO WORLD".to_string()
)),
&Value::String(Avm1String::from("HELLO WORLD")),
" ",
activation,
context
@ -278,9 +275,8 @@ mod tests {
);
assert_eq!(
VariableDumper::dump(
&Value::String(Avm1String::new(
context.gc_context,
"Escape \"this\" string\nplease! \u{0008}\u{000C}\n\r\t\"\\".to_string()
&Value::String(Avm1String::from(
"Escape \"this\" string\nplease! \u{0008}\u{000C}\n\r\t\"\\"
)),
" ",
activation,
@ -312,7 +308,7 @@ mod tests {
object.set("self", object.into(), activation, context)?;
object.set(
"test",
Value::String(Avm1String::new(context.gc_context, "value".to_string())),
Value::String(Avm1String::from("value")),
activation,
context,
)?;
@ -335,7 +331,7 @@ mod tests {
object.set("self", object.into(), activation, context)?;
object.set(
"test",
Value::String(Avm1String::new(context.gc_context, "value".to_string())),
Value::String(Avm1String::from("value")),
activation,
context,
)?;

View File

@ -448,7 +448,7 @@ impl<'gc> FunctionObject<'gc> {
gc_context,
FunctionObjectData {
function: Some(function.into()),
primitive: Avm1String::new(gc_context, "[type Function]".to_string()).into(),
primitive: Avm1String::from("[type Function]").into(),
},
),
}
@ -553,8 +553,7 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
context.gc_context,
FunctionObjectData {
function: None,
primitive: Avm1String::new(context.gc_context, "[type Function]".to_string())
.into(),
primitive: Avm1String::from("[type Function]").into(),
},
),
};

View File

@ -32,13 +32,13 @@ pub fn create_proto<'gc>(
object.define_value(
gc_context,
"message",
Avm1String::new(gc_context, "Error".to_string()).into(),
Avm1String::from("Error").into(),
EnumSet::empty(),
);
object.define_value(
gc_context,
"name",
Avm1String::new(gc_context, "Error".to_string()).into(),
Avm1String::from("Error").into(),
EnumSet::empty(),
);

View File

@ -93,11 +93,11 @@ pub fn apply<'gc>(
/// Implements `Function.prototype.toString`
fn to_string<'gc>(
_: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_: Object<'gc>,
_: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
Ok(Avm1String::new(context.gc_context, "[type Function]".to_string()).into())
Ok(Avm1String::from("[type Function]").into())
}
/// Partially construct `Function.prototype`.

View File

@ -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(Avm1String::new(context.gc_context, "0".to_string()).into());
return Ok(Avm1String::from("0").into());
}
};
@ -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(Avm1String::new(context.gc_context, "0".to_string()).into())
Ok(Avm1String::from("0").into())
}
}

View File

@ -89,11 +89,11 @@ pub fn has_own_property<'gc>(
/// Implements `Object.prototype.toString`
fn to_string<'gc>(
_: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_: Object<'gc>,
_: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
Ok(Avm1String::new(context.gc_context, "[object Object]".to_string()).into())
Ok(Avm1String::from("[object Object]").into())
}
/// Implements `Object.prototype.isPropertyEnumerable`

View File

@ -87,12 +87,12 @@ fn add_listener<'gc>(
fn align<'gc>(
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("Stage.align: unimplemented");
Ok(Avm1String::new(context.gc_context, "".to_string()).into())
Ok(Avm1String::from("").into())
}
fn set_align<'gc>(
@ -116,22 +116,22 @@ fn height<'gc>(
fn remove_listener<'gc>(
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("Stage.removeListener: unimplemented");
Ok(Avm1String::new(context.gc_context, "".to_string()).into())
Ok(Avm1String::from("").into())
}
fn scale_mode<'gc>(
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("Stage.scaleMode: unimplemented");
Ok(Avm1String::new(context.gc_context, "noScale".to_string()).into())
Ok(Avm1String::from("noScale").into())
}
fn set_scale_mode<'gc>(

View File

@ -395,7 +395,7 @@ fn slice<'gc>(
);
Ok(Avm1String::new(context.gc_context, ret).into())
} else {
Ok(Avm1String::new(context.gc_context, "".to_string()).into())
Ok(Avm1String::from("").into())
}
}

View File

@ -29,11 +29,11 @@ fn do_conversion<'gc>(
fn get_conversion_mode<'gc>(
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
Ok(Avm1String::new(context.gc_context, "KOREAN".to_string()).into())
Ok(Avm1String::from("KOREAN").into())
}
fn get_enabled<'gc>(
@ -85,49 +85,49 @@ pub fn create<'gc>(
ime.define_value(
gc_context,
"ALPHANUMERIC_FULL",
Avm1String::new(gc_context, "ALPHANUMERIC_FULL".to_string()).into(),
Avm1String::from("ALPHANUMERIC_FULL").into(),
Attribute::DontDelete | ReadOnly | DontEnum,
);
ime.define_value(
gc_context,
"ALPHANUMERIC_HALF",
Avm1String::new(gc_context, "ALPHANUMERIC_HALF".to_string()).into(),
Avm1String::from("ALPHANUMERIC_HALF").into(),
DontDelete | ReadOnly | DontEnum,
);
ime.define_value(
gc_context,
"CHINESE",
Avm1String::new(gc_context, "CHINESE".to_string()).into(),
Avm1String::from("CHINESE").into(),
DontDelete | ReadOnly | DontEnum,
);
ime.define_value(
gc_context,
"JAPANESE_HIRAGANA",
Avm1String::new(gc_context, "JAPANESE_HIRAGANA".to_string()).into(),
Avm1String::from("JAPANESE_HIRAGANA").into(),
DontDelete | ReadOnly | DontEnum,
);
ime.define_value(
gc_context,
"JAPANESE_KATAKANA_FULL",
Avm1String::new(gc_context, "JAPANESE_KATAKANA_FULL".to_string()).into(),
Avm1String::from("JAPANESE_KATAKANA_FULL").into(),
DontDelete | ReadOnly | DontEnum,
);
ime.define_value(
gc_context,
"KOREAN",
Avm1String::new(gc_context, "KOREAN".to_string()).into(),
Avm1String::from("KOREAN").into(),
DontDelete | ReadOnly | DontEnum,
);
ime.define_value(
gc_context,
"UNKNOWN",
Avm1String::new(gc_context, "UNKNOWN".to_string()).into(),
Avm1String::from("UNKNOWN").into(),
DontDelete | ReadOnly | DontEnum,
);

View File

@ -372,7 +372,7 @@ pub fn set_word_wrap<'gc>(
pub fn auto_size<'gc>(
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
@ -381,12 +381,10 @@ pub fn auto_size<'gc>(
.and_then(|dobj| dobj.as_edit_text())
{
return Ok(match etext.autosize() {
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(),
AutoSizeMode::None => Avm1String::from("none").into(),
AutoSizeMode::Left => Avm1String::from("left").into(),
AutoSizeMode::Center => Avm1String::from("center").into(),
AutoSizeMode::Right => Avm1String::from("right").into(),
});
}

View File

@ -234,7 +234,7 @@ pub fn xmlnode_to_string<'gc>(
.into());
}
Ok(Avm1String::new(ac.gc_context, "".to_string()).into())
Ok(Avm1String::from("").into())
}
pub fn xmlnode_local_name<'gc>(

View File

@ -955,13 +955,13 @@ mod tests {
object.as_script_object().unwrap().define_value(
context.gc_context,
"forced",
Avm1String::new(context.gc_context, "forced".to_string()).into(),
Avm1String::from("forced").into(),
EnumSet::empty(),
);
object
.set(
"natural",
Avm1String::new(context.gc_context, "natural".to_string()).into(),
Avm1String::from("natural").into(),
activation,
context,
)
@ -969,11 +969,11 @@ mod tests {
assert_eq!(
object.get("forced", activation, context).unwrap(),
Avm1String::new(context.gc_context, "forced".to_string()).into()
Avm1String::from("forced").into()
);
assert_eq!(
object.get("natural", activation, context).unwrap(),
Avm1String::new(context.gc_context, "natural".to_string()).into()
Avm1String::from("natural").into()
);
})
}
@ -984,20 +984,20 @@ mod tests {
object.as_script_object().unwrap().define_value(
context.gc_context,
"normal",
Avm1String::new(context.gc_context, "initial".to_string()).into(),
Avm1String::from("initial").into(),
EnumSet::empty(),
);
object.as_script_object().unwrap().define_value(
context.gc_context,
"readonly",
Avm1String::new(context.gc_context, "initial".to_string()).into(),
Avm1String::from("initial").into(),
ReadOnly.into(),
);
object
.set(
"normal",
Avm1String::new(context.gc_context, "replaced".to_string()).into(),
Avm1String::from("replaced").into(),
activation,
context,
)
@ -1005,7 +1005,7 @@ mod tests {
object
.set(
"readonly",
Avm1String::new(context.gc_context, "replaced".to_string()).into(),
Avm1String::from("replaced").into(),
activation,
context,
)
@ -1013,11 +1013,11 @@ mod tests {
assert_eq!(
object.get("normal", activation, context).unwrap(),
Avm1String::new(context.gc_context, "replaced".to_string()).into()
Avm1String::from("replaced").into()
);
assert_eq!(
object.get("readonly", activation, context).unwrap(),
Avm1String::new(context.gc_context, "initial".to_string()).into()
Avm1String::from("initial").into()
);
})
}
@ -1028,14 +1028,14 @@ mod tests {
object.as_script_object().unwrap().define_value(
context.gc_context,
"test",
Avm1String::new(context.gc_context, "initial".to_string()).into(),
Avm1String::from("initial").into(),
DontDelete.into(),
);
assert_eq!(object.delete(activation, context.gc_context, "test"), false);
assert_eq!(
object.get("test", activation, context).unwrap(),
Avm1String::new(context.gc_context, "initial".to_string()).into()
Avm1String::from("initial").into()
);
object
@ -1043,7 +1043,7 @@ mod tests {
.unwrap()
.set(
"test",
Avm1String::new(context.gc_context, "replaced".to_string()).into(),
Avm1String::from("replaced").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(),
Avm1String::new(context.gc_context, "replaced".to_string()).into()
Avm1String::from("replaced").into()
);
})
}
@ -1060,8 +1060,8 @@ mod tests {
#[test]
fn test_virtual_get() {
with_object(0, |activation, context, object| {
let getter = Executable::Native(|_avm, context, _this, _args| {
Ok(Avm1String::new(context.gc_context, "Virtual!".to_string()).into())
let getter = Executable::Native(|_avm, _context, _this, _args| {
Ok(Avm1String::from("Virtual!").into())
});
object.as_script_object().unwrap().add_property(
@ -1074,21 +1074,21 @@ mod tests {
assert_eq!(
object.get("test", activation, context).unwrap(),
Avm1String::new(context.gc_context, "Virtual!".to_string()).into()
Avm1String::from("Virtual!").into()
);
// This set should do nothing
object
.set(
"test",
Avm1String::new(context.gc_context, "Ignored!".to_string()).into(),
Avm1String::from("Ignored!").into(),
activation,
context,
)
.unwrap();
assert_eq!(
object.get("test", activation, context).unwrap(),
Avm1String::new(context.gc_context, "Virtual!".to_string()).into()
Avm1String::from("Virtual!").into()
);
})
}
@ -1096,8 +1096,8 @@ mod tests {
#[test]
fn test_delete() {
with_object(0, |activation, context, object| {
let getter = Executable::Native(|_avm, context, _this, _args| {
Ok(Avm1String::new(context.gc_context, "Virtual!".to_string()).into())
let getter = Executable::Native(|_avm, _context, _this, _args| {
Ok(Avm1String::from("Virtual!").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",
Avm1String::new(context.gc_context, "Stored!".to_string()).into(),
Avm1String::from("Stored!").into(),
EnumSet::empty(),
);
object.as_script_object().unwrap().define_value(
context.gc_context,
"stored_un",
Avm1String::new(context.gc_context, "Stored!".to_string()).into(),
Avm1String::from("Stored!").into(),
DontDelete.into(),
);
@ -1154,7 +1154,7 @@ mod tests {
);
assert_eq!(
object.get("virtual_un", activation, context).unwrap(),
Avm1String::new(context.gc_context, "Virtual!".to_string()).into()
Avm1String::from("Virtual!").into()
);
assert_eq!(
object.get("stored", activation, context).unwrap(),
@ -1162,7 +1162,7 @@ mod tests {
);
assert_eq!(
object.get("stored_un", activation, context).unwrap(),
Avm1String::new(context.gc_context, "Stored!".to_string()).into()
Avm1String::from("Stored!").into()
);
})
}

View File

@ -889,20 +889,20 @@ fn set_name<'gc>(
fn drop_target<'gc>(
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("Unimplemented property _droptarget");
Ok(Avm1String::new(context.gc_context, "".to_string()).into())
Ok(Avm1String::from("").into())
}
fn url<'gc>(
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("Unimplemented property _url");
Ok(Avm1String::new(context.gc_context, "".to_string()).into())
Ok(Avm1String::from("").into())
}
fn high_quality<'gc>(
@ -964,11 +964,11 @@ fn set_sound_buf_time<'gc>(
fn quality<'gc>(
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("Unimplemented property _quality");
Ok(Avm1String::new(context.gc_context, "HIGH".to_string()).into())
Ok(Avm1String::from("HIGH").into())
}
fn set_quality<'gc>(

View File

@ -3,11 +3,22 @@ use std::ops::Deref;
#[derive(Debug, Clone, Collect)]
#[collect(no_drop)]
pub struct Avm1String<'gc>(Gc<'gc, String>);
enum Source<'gc> {
Owned(Gc<'gc, String>),
Static(&'static str),
}
#[derive(Debug, Clone, Collect)]
#[collect(no_drop)]
pub struct Avm1String<'gc> {
source: Source<'gc>,
}
impl<'gc> Avm1String<'gc> {
pub fn new<S: Into<String>>(gc_context: MutationContext<'gc, '_>, string: S) -> Self {
Self(Gc::allocate(gc_context, string.into()))
Self {
source: Source::Owned(Gc::allocate(gc_context, string.into())),
}
}
pub fn as_str(&self) -> &str {
@ -15,19 +26,33 @@ impl<'gc> Avm1String<'gc> {
}
}
impl<'gc> From<&'static str> for Avm1String<'gc> {
fn from(str: &'static str) -> Self {
Self {
source: Source::Static(str),
}
}
}
impl Deref for Avm1String<'_> {
type Target = str;
#[inline]
fn deref(&self) -> &str {
self.0.deref()
match &self.source {
Source::Owned(str) => str.deref(),
Source::Static(str) => str,
}
}
}
impl AsRef<str> for Avm1String<'_> {
#[inline]
fn as_ref(&self) -> &str {
self
match &self.source {
Source::Owned(str) => str,
Source::Static(str) => str,
}
}
}

View File

@ -9,7 +9,7 @@ fn locals_into_form_values() {
my_locals
.set(
"value1",
Avm1String::new(context.gc_context, "string".to_string()).into(),
Avm1String::from("string").into(),
activation,
context,
)

View File

@ -323,7 +323,7 @@ impl<'gc> Loader<'gc> {
uc,
"broadcastMessage",
&[
Avm1String::new(uc.gc_context, "onLoadStart".to_string()).into(),
Avm1String::from("onLoadStart").into(),
Value::Object(broadcaster),
],
);
@ -359,8 +359,7 @@ impl<'gc> Loader<'gc> {
uc,
"broadcastMessage",
&[
Avm1String::new(uc.gc_context, "onLoadProgress".to_string())
.into(),
Avm1String::from("onLoadProgress").into(),
Value::Object(broadcaster),
length.into(),
length.into(),
@ -397,8 +396,7 @@ impl<'gc> Loader<'gc> {
uc,
"broadcastMessage",
&[
Avm1String::new(uc.gc_context, "onLoadComplete".to_string())
.into(),
Avm1String::from("onLoadComplete").into(),
Value::Object(broadcaster),
],
);
@ -438,14 +436,9 @@ impl<'gc> Loader<'gc> {
uc,
"broadcastMessage",
&[
Avm1String::new(uc.gc_context, "onLoadError".to_string())
.into(),
Avm1String::from("onLoadError").into(),
Value::Object(broadcaster),
Avm1String::new(
uc.gc_context,
"LoadNeverCompleted".to_string(),
)
.into(),
Avm1String::from("LoadNeverCompleted").into(),
],
);
}
@ -520,7 +513,7 @@ impl<'gc> Loader<'gc> {
loaded_clip: DisplayObject<'gc>,
clip_object: Option<Object<'gc>>,
queue: &mut ActionQueue<'gc>,
gc_context: MutationContext<'gc, '_>,
_gc_context: MutationContext<'gc, '_>,
) -> bool {
let (clip, broadcaster, load_complete) = match self {
Loader::Movie {
@ -540,7 +533,7 @@ impl<'gc> Loader<'gc> {
object: broadcaster,
name: "broadcastMessage",
args: vec![
Avm1String::new(gc_context, "onLoadInit".to_string()).into(),
Avm1String::from("onLoadInit").into(),
clip_object.map(|co| co.into()).unwrap_or(Value::Undefined),
],
},