core: Replace Value::into_string in favour of coerce_to_string or manual matching

This commit is contained in:
Nathan Adams 2020-06-19 19:08:24 +02:00 committed by Mike Welsh
parent 5662b2d4d9
commit f4b4d0ebb7
3 changed files with 83 additions and 59 deletions

View File

@ -177,7 +177,6 @@ impl<'gc> Avm1<'gc> {
let scope = stack_frame.scope();
let locals = scope.locals();
let keys = locals.get_keys(self);
let swf_version = self.current_swf_version();
for k in keys {
let v = locals.get(&k, self, context);
@ -187,8 +186,9 @@ impl<'gc> Avm1<'gc> {
k,
v.ok()
.unwrap_or_else(|| Value::Undefined)
.clone()
.into_string(swf_version),
.coerce_to_string(self, context)
.unwrap_or_else(|_| Cow::Borrowed("undefined"))
.to_string(),
);
}
@ -1096,17 +1096,24 @@ impl<'gc> Avm1<'gc> {
Ok(())
}
fn action_ascii_to_char(&mut self, _context: &mut UpdateContext) -> Result<(), Error> {
fn action_ascii_to_char(
&mut self,
_context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error> {
// TODO(Herschel): Results on incorrect operands?
let val = (self.pop().as_f64()? as u8) as char;
self.push(val.to_string());
Ok(())
}
fn action_char_to_ascii(&mut self, _context: &mut UpdateContext) -> Result<(), Error> {
fn action_char_to_ascii(
&mut self,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error> {
// TODO(Herschel): Results on incorrect operands?
let s = self.pop().into_string(self.current_swf_version());
let result = s.bytes().next().unwrap_or(0);
let val = self.pop();
let string = val.coerce_to_string(self, context)?;
let result = string.bytes().next().unwrap_or(0);
self.push(result);
Ok(())
}
@ -1715,13 +1722,14 @@ impl<'gc> Avm1<'gc> {
// TODO: Support `LoadVariablesFlag`, `LoadTargetFlag`
// TODO: What happens if there's only one string?
let target = self.pop();
let url = self.pop().into_string(self.current_swf_version());
let url_val = self.pop();
let url = url_val.coerce_to_string(self, context)?;
if let Some(fscommand) = fscommand::parse(&url) {
return fscommand::handle(fscommand, self, context);
}
let window_target = target.clone().into_string(self.current_swf_version());
let window_target = target.coerce_to_string(self, context)?;
let clip_target: Option<DisplayObject<'gc>> = if is_target_sprite {
if let Value::Object(target) = target {
target.as_display_object()
@ -1742,7 +1750,7 @@ impl<'gc> Avm1<'gc> {
.coerce_to_object(self, context);
let (url, opts) = self.locals_into_request_options(
context,
Cow::Owned(url),
url,
NavigationMethod::from_send_vars_method(swf_method),
);
let fetch = context.navigator.fetch(&url, opts);
@ -1760,7 +1768,7 @@ impl<'gc> Avm1<'gc> {
if let Some(clip_target) = clip_target {
let (url, opts) = self.locals_into_request_options(
context,
Cow::Owned(url),
url,
NavigationMethod::from_send_vars_method(swf_method),
);
let fetch = context.navigator.fetch(&url, opts);
@ -1780,9 +1788,11 @@ impl<'gc> Avm1<'gc> {
None => None,
};
context
.navigator
.navigate_to_url(url, Some(window_target), vars);
context.navigator.navigate_to_url(
url.to_string(),
Some(window_target.to_string()),
vars,
);
}
Ok(())
@ -1894,7 +1904,8 @@ impl<'gc> Avm1<'gc> {
let object = ScriptObject::object(context.gc_context, Some(self.prototypes.object));
for _ in 0..num_props {
let value = self.pop();
let name = self.pop().into_string(self.current_swf_version());
let name_val = self.pop();
let name = name_val.coerce_to_string(self, context)?;
object.set(&name, value, self, context)?;
}
@ -1992,28 +2003,40 @@ impl<'gc> Avm1<'gc> {
Ok(())
}
fn action_mb_char_to_ascii(&mut self, _context: &mut UpdateContext) -> Result<(), Error> {
fn action_mb_char_to_ascii(
&mut self,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error> {
// TODO(Herschel): Results on incorrect operands?
let s = self.pop().into_string(self.current_swf_version());
let val = self.pop();
let s = val.coerce_to_string(self, context)?;
let result = s.chars().next().unwrap_or('\0') as u32;
self.push(result);
Ok(())
}
fn action_mb_string_extract(&mut self, _context: &mut UpdateContext) -> Result<(), Error> {
fn action_mb_string_extract(
&mut self,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error> {
// TODO(Herschel): Result with incorrect operands?
let len = self.pop().as_f64()? as usize;
let start = self.pop().as_f64()? as usize;
let s = self.pop().into_string(self.current_swf_version());
let val = self.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.push(result);
Ok(())
}
fn action_mb_string_length(&mut self, _context: &mut UpdateContext) -> Result<(), Error> {
fn action_mb_string_length(
&mut self,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error> {
// TODO(Herschel): Result with non-string operands?
let val = self.pop().into_string(self.current_swf_version()).len();
self.push(val as f64);
let val = self.pop();
let len = val.coerce_to_string(self, context)?.len();
self.push(len as f64);
Ok(())
}
@ -2463,13 +2486,12 @@ impl<'gc> Avm1<'gc> {
Ok(())
}
fn action_string_add(&mut self, _context: &mut UpdateContext) -> Result<(), Error> {
fn action_string_add(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) -> Result<(), Error> {
// SWFv4 string concatenation
// TODO(Herschel): Result with non-string operands?
let swf_version = self.current_swf_version();
let a = self.pop().into_string(swf_version);
let mut b = self.pop().into_string(swf_version);
b.push_str(&a);
let a = self.pop();
let mut b = self.pop().coerce_to_string(self, context)?.to_string();
b.push_str(&a.coerce_to_string(self, context)?);
self.push(b);
Ok(())
}
@ -2486,12 +2508,16 @@ impl<'gc> Avm1<'gc> {
Ok(())
}
fn action_string_extract(&mut self, _context: &mut UpdateContext) -> Result<(), Error> {
fn action_string_extract(
&mut self,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error> {
// SWFv4 substring
// TODO(Herschel): Result with incorrect operands?
let len = self.pop().as_f64()? as usize;
let start = self.pop().as_f64()? as usize;
let s = self.pop().into_string(self.current_swf_version());
let val = self.pop();
let s = val.coerce_to_string(self, context)?;
// This is specifically a non-UTF8 aware substring.
// SWFv4 only used ANSI strings.
let result = s
@ -2520,16 +2546,16 @@ impl<'gc> Avm1<'gc> {
Ok(())
}
fn action_string_length(&mut self, _context: &mut UpdateContext) -> Result<(), Error> {
fn action_string_length(
&mut self,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error> {
// AS1 strlen
// Only returns byte length.
// TODO(Herschel): Result with non-string operands?
let val = self
.pop()
.into_string(self.current_swf_version())
.bytes()
.len() as f64;
self.push(val);
let val = self.pop();
let len = val.coerce_to_string(self, context)?.bytes().len() as f64;
self.push(len);
Ok(())
}

View File

@ -45,14 +45,17 @@ pub fn getURL<'a, 'gc>(
) -> Result<ReturnValue<'gc>, Error> {
//TODO: Error behavior if no arguments are present
if let Some(url_val) = args.get(0) {
let swf_version = avm.current_swf_version();
let url = url_val.clone().into_string(swf_version);
let url = url_val.coerce_to_string(avm, context)?;
if let Some(fscommand) = fscommand::parse(&url) {
fscommand::handle(fscommand, avm, context);
return Ok(Value::Undefined.into());
}
let window = args.get(1).map(|v| v.clone().into_string(swf_version));
let window = if let Some(window) = args.get(1) {
Some(window.coerce_to_string(avm, context)?.to_string())
} else {
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),
@ -60,7 +63,9 @@ pub fn getURL<'a, 'gc>(
};
let vars_method = method.map(|m| (m, avm.locals_into_form_values(context)));
context.navigator.navigate_to_url(url, window, vars_method);
context
.navigator
.navigate_to_url(url.to_string(), window, vars_method);
}
Ok(Value::Undefined.into())

View File

@ -400,24 +400,6 @@ impl<'gc> Value<'gc> {
}
}
/// Coerce a value to a string without calling object methods.
pub fn into_string(self, swf_version: u8) -> String {
match self {
Value::Undefined => {
if swf_version >= 7 {
"undefined".to_string()
} else {
"".to_string()
}
}
Value::Null => "null".to_string(),
Value::Bool(v) => v.to_string(),
Value::Number(v) => f64_to_string(v),
Value::String(v) => v,
Value::Object(object) => object.as_string(),
}
}
/// Coerce a number to an `u16` following the ECMAScript specifications for `ToUInt16`.
/// The value will be wrapped modulo 2^16.
/// This will call `valueOf` and do any conversions that are necessary.
@ -478,7 +460,18 @@ impl<'gc> Value<'gc> {
Value::String(s) => Cow::Owned(s),
_ => Cow::Borrowed("[type Object]"),
},
_ => Cow::Owned(self.to_owned().into_string(avm.current_swf_version())),
Value::Undefined => {
if avm.current_swf_version() >= 7 {
Cow::Borrowed("undefined")
} else {
Cow::Borrowed("")
}
}
Value::Null => Cow::Borrowed("null"),
Value::Bool(true) => Cow::Borrowed("true"),
Value::Bool(false) => Cow::Borrowed("false"),
Value::Number(v) => Cow::Owned(f64_to_string(*v)),
Value::String(v) => Cow::Borrowed(v),
})
}