avm1: Merge #97, impl From<*> for Value

Implement From<*> for Value for better dev ergonomics
This commit is contained in:
Mike Welsh 2019-10-21 11:43:29 -07:00 committed by GitHub
commit e02204a78f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 293 additions and 271 deletions

View File

@ -546,7 +546,7 @@ impl<'gc> Avm1<'gc> {
fn action_add(&mut self, _context: &mut ActionContext) -> Result<(), Error> {
let a = self.pop()?;
let b = self.pop()?;
self.push(Value::Number(b.into_number_v1() + a.into_number_v1()));
self.push(b.into_number_v1() + a.into_number_v1());
Ok(())
}
@ -559,12 +559,12 @@ impl<'gc> Avm1<'gc> {
if let Value::String(a) = a {
let mut s = b.into_string();
s.push_str(&a);
self.push(Value::String(s));
self.push(s);
} else if let Value::String(mut b) = b {
b.push_str(&a.into_string());
self.push(Value::String(b));
self.push(b);
} else {
self.push(Value::Number(b.as_number() + a.as_number()));
self.push(b.as_number() + a.as_number());
}
Ok(())
}
@ -581,7 +581,7 @@ impl<'gc> Avm1<'gc> {
fn action_ascii_to_char(&mut self, _context: &mut ActionContext) -> Result<(), Error> {
// TODO(Herschel): Results on incorrect operands?
let val = (self.pop()?.as_f64()? as u8) as char;
self.push(Value::String(val.to_string()));
self.push(val.to_string());
Ok(())
}
@ -589,7 +589,7 @@ impl<'gc> Avm1<'gc> {
// TODO(Herschel): Results on incorrect operands?
let s = self.pop()?.into_string();
let result = s.bytes().nth(0).unwrap_or(0);
self.push(Value::Number(result.into()));
self.push(result);
Ok(())
}
@ -606,7 +606,7 @@ impl<'gc> Avm1<'gc> {
let a = self.pop()?.as_u32()?;
let b = self.pop()?.as_u32()?;
let result = a & b;
self.push(Value::Number(result.into()));
self.push(result);
Ok(())
}
@ -614,7 +614,7 @@ impl<'gc> Avm1<'gc> {
let a = self.pop()?.as_i32()? & 0b11111; // Only 5 bits used for shift count
let b = self.pop()?.as_i32()?;
let result = b << a;
self.push(Value::Number(result.into()));
self.push(result);
Ok(())
}
@ -622,7 +622,7 @@ impl<'gc> Avm1<'gc> {
let a = self.pop()?.as_u32()?;
let b = self.pop()?.as_u32()?;
let result = a | b;
self.push(Value::Number(result.into()));
self.push(result);
Ok(())
}
@ -630,7 +630,7 @@ impl<'gc> Avm1<'gc> {
let a = self.pop()?.as_i32()? & 0b11111; // Only 5 bits used for shift count
let b = self.pop()?.as_i32()?;
let result = b >> a;
self.push(Value::Number(result.into()));
self.push(result);
Ok(())
}
@ -638,7 +638,7 @@ impl<'gc> Avm1<'gc> {
let a = self.pop()?.as_u32()? & 0b11111; // Only 5 bits used for shift count
let b = self.pop()?.as_u32()?;
let result = b >> a;
self.push(Value::Number(result.into()));
self.push(result);
Ok(())
}
@ -646,7 +646,7 @@ impl<'gc> Avm1<'gc> {
let a = self.pop()?.as_u32()?;
let b = self.pop()?.as_u32()?;
let result = b ^ a;
self.push(Value::Number(result.into()));
self.push(result);
Ok(())
}
@ -750,7 +750,7 @@ impl<'gc> Avm1<'gc> {
fn action_decrement(&mut self, _context: &mut ActionContext) -> Result<(), Error> {
let a = self.pop()?.as_number();
self.push(Value::Number(a - 1.0));
self.push(a - 1.0);
Ok(())
}
@ -774,10 +774,7 @@ impl<'gc> Avm1<'gc> {
context.gc_context,
);
let func = Avm1Function::from_df1(swf_version, func_data, name, params, scope);
let func_obj = Value::Object(GcCell::allocate(
context.gc_context,
Object::action_function(func),
));
let func_obj = GcCell::allocate(context.gc_context, Object::action_function(func));
if name == "" {
self.push(func_obj);
} else {
@ -808,10 +805,7 @@ impl<'gc> Avm1<'gc> {
context.gc_context,
);
let func = Avm1Function::from_df2(swf_version, func_data, action_func, scope);
let func_obj = Value::Object(GcCell::allocate(
context.gc_context,
Object::action_function(func),
));
let func_obj = GcCell::allocate(context.gc_context, Object::action_function(func));
if action_func.name == "" {
self.push(func_obj);
} else {
@ -858,7 +852,7 @@ impl<'gc> Avm1<'gc> {
let object = self.pop()?.as_object()?;
let success = object.write(context.gc_context).delete(name);
self.push(Value::Bool(success));
self.push(success);
Ok(())
}
@ -869,7 +863,7 @@ impl<'gc> Avm1<'gc> {
//Fun fact: This isn't in the Adobe SWF19 spec, but this opcode returns
//a boolean based on if the delete actually deleted something.
let did_exist = Value::Bool(self.current_stack_frame().unwrap().read().is_defined(name));
let did_exist = self.current_stack_frame().unwrap().read().is_defined(name);
self.current_stack_frame()
.unwrap()
@ -890,7 +884,7 @@ impl<'gc> Avm1<'gc> {
// In SWF 4, the result is the string #ERROR#.""
// Seems to be untrue for SWF v4, I get 1.#INF.
self.push(Value::Number(b.into_number_v1() / a.into_number_v1()));
self.push(b.into_number_v1() / a.into_number_v1());
Ok(())
}
@ -919,7 +913,7 @@ impl<'gc> Avm1<'gc> {
};
for k in ob.read().get_keys() {
self.push(Value::String(k));
self.push(k);
}
Ok(())
@ -954,7 +948,7 @@ impl<'gc> Avm1<'gc> {
(Value::Number(a), Value::String(b)) => a == b.parse().unwrap_or(std::f64::NAN),
_ => false,
};
self.push(Value::Bool(result));
self.push(result);
Ok(())
}
@ -1017,7 +1011,7 @@ impl<'gc> Avm1<'gc> {
}
fn action_get_time(&mut self, context: &mut ActionContext) -> Result<(), Error> {
self.push(Value::Number(context.global_time as f64));
self.push(context.global_time as f64);
Ok(())
}
@ -1217,7 +1211,7 @@ impl<'gc> Avm1<'gc> {
fn action_increment(&mut self, _context: &mut ActionContext) -> Result<(), Error> {
let a = self.pop()?.as_number();
self.push(Value::Number(a + 1.0));
self.push(a + 1.0);
Ok(())
}
@ -1272,7 +1266,7 @@ impl<'gc> Avm1<'gc> {
(a, b) => b.as_number() < a.as_number(),
};
self.push(Value::Bool(result));
self.push(result);
Ok(())
}
@ -1289,7 +1283,7 @@ impl<'gc> Avm1<'gc> {
// TODO(Herschel): Results on incorrect operands?
use std::convert::TryFrom;
let val = char::try_from(self.pop()?.as_f64()? as u32)?;
self.push(Value::String(val.to_string()));
self.push(val.to_string());
Ok(())
}
@ -1297,7 +1291,7 @@ impl<'gc> Avm1<'gc> {
// TODO(Herschel): Results on incorrect operands?
let s = self.pop()?.into_string();
let result = s.chars().nth(0).unwrap_or('\0') as u32;
self.push(Value::Number(result.into()));
self.push(result);
Ok(())
}
@ -1307,14 +1301,14 @@ impl<'gc> Avm1<'gc> {
let start = self.pop()?.as_f64()? as usize;
let s = self.pop()?.into_string();
let result = s[len..len + start].to_string(); // TODO(Herschel): Flash uses UTF-16 internally.
self.push(Value::String(result));
self.push(result);
Ok(())
}
fn action_mb_string_length(&mut self, _context: &mut ActionContext) -> Result<(), Error> {
// TODO(Herschel): Result with non-string operands?
let val = self.pop()?.into_string().len();
self.push(Value::Number(val as f64));
self.push(val as f64);
Ok(())
}
@ -1322,7 +1316,7 @@ impl<'gc> Avm1<'gc> {
// AS1 multiply
let a = self.pop()?;
let b = self.pop()?;
self.push(Value::Number(a.into_number_v1() * b.into_number_v1()));
self.push(a.into_number_v1() * b.into_number_v1());
Ok(())
}
@ -1330,7 +1324,7 @@ impl<'gc> Avm1<'gc> {
// TODO: Wrong operands?
let a = self.pop()?.as_f64()?;
let b = self.pop()?.as_f64()?;
self.push(Value::Number(a % b));
self.push(a % b);
Ok(())
}
@ -1432,11 +1426,11 @@ impl<'gc> Avm1<'gc> {
SwfValue::Int(v) => Value::Number(f64::from(*v)),
SwfValue::Float(v) => Value::Number(f64::from(*v)),
SwfValue::Double(v) => Value::Number(*v),
SwfValue::Str(v) => Value::String(v.to_string()),
SwfValue::Str(v) => v.to_string().into(),
SwfValue::Register(v) => self.current_register(*v),
SwfValue::ConstantPool(i) => {
if let Some(value) = self.constant_pool.get(*i as usize) {
Value::String(value.to_string())
value.to_string().into()
} else {
log::warn!(
"ActionPush: Constant pool index {} out of range (len = {})",
@ -1463,7 +1457,7 @@ impl<'gc> Avm1<'gc> {
// and the max value gets converted into an i32, so any number > 2^31 - 1 will return 0.
let max = self.pop()?.into_number_v1() as i32;
let val = context.rng.gen_range(0, max);
self.push(Value::Number(val.into()));
self.push(val);
Ok(())
}
@ -1542,7 +1536,7 @@ impl<'gc> Avm1<'gc> {
let a = self.pop()?;
let b = self.pop()?;
let result = a == b;
self.push(Value::Bool(result));
self.push(result);
Ok(())
}
@ -1593,13 +1587,13 @@ impl<'gc> Avm1<'gc> {
if target.is_empty() {
context.active_clip = context.start_clip;
context.target_clip = Some(context.start_clip);
context.target_path = Value::String(target.to_string());
context.target_path = target.into();
} else if let Some(clip) =
Avm1::resolve_slash_path(context.start_clip, context.root, target)
{
context.target_clip = Some(clip);
context.active_clip = clip;
context.target_path = Value::String(target.to_string());
context.target_path = target.into();
} else {
log::warn!("SetTarget failed: {} not found", target);
// TODO: Emulate AVM1 trace error message.
@ -1701,7 +1695,7 @@ impl<'gc> Avm1<'gc> {
let a = self.pop()?.into_string();
let mut b = self.pop()?.into_string();
b.push_str(&a);
self.push(Value::String(b));
self.push(b);
Ok(())
}
@ -1731,7 +1725,7 @@ impl<'gc> Avm1<'gc> {
.take(len)
.map(|c| c as char)
.collect::<String>();
self.push(Value::String(result));
self.push(result);
Ok(())
}
@ -1753,7 +1747,7 @@ impl<'gc> Avm1<'gc> {
// Only returns byte length.
// TODO(Herschel): Result with non-string operands?
let val = self.pop()?.into_string().bytes().len() as f64;
self.push(Value::Number(val));
self.push(val);
Ok(())
}
@ -1773,7 +1767,7 @@ impl<'gc> Avm1<'gc> {
fn action_subtract(&mut self, _context: &mut ActionContext) -> Result<(), Error> {
let a = self.pop()?;
let b = self.pop()?;
self.push(Value::Number(b.into_number_v1() - a.into_number_v1()));
self.push(b.into_number_v1() - a.into_number_v1());
Ok(())
}
@ -1794,19 +1788,19 @@ impl<'gc> Avm1<'gc> {
fn action_to_integer(&mut self, _context: &mut ActionContext) -> Result<(), Error> {
let val = self.pop()?;
self.push(Value::Number(val.into_number_v1().trunc()));
self.push(val.into_number_v1().trunc());
Ok(())
}
fn action_to_number(&mut self, _context: &mut ActionContext) -> Result<(), Error> {
let val = self.pop()?;
self.push(Value::Number(val.as_number()));
self.push(val.as_number());
Ok(())
}
fn action_to_string(&mut self, _context: &mut ActionContext) -> Result<(), Error> {
let val = self.pop()?;
self.push(Value::String(val.into_string()));
self.push(val.into_string());
Ok(())
}

View File

@ -271,7 +271,7 @@ impl<'gc> Activation<'gc> {
}
/// Define a named local variable within this activation.
pub fn define(&self, name: &str, value: Value<'gc>, mc: MutationContext<'gc, '_>) {
pub fn define(&self, name: &str, value: impl Into<Value<'gc>>, mc: MutationContext<'gc, '_>) {
self.scope().define(name, value, mc)
}
@ -304,10 +304,15 @@ impl<'gc> Activation<'gc> {
}
/// Set a local register.
pub fn set_local_register(&mut self, id: u8, value: Value<'gc>, mc: MutationContext<'gc, '_>) {
pub fn set_local_register(
&mut self,
id: u8,
value: impl Into<Value<'gc>>,
mc: MutationContext<'gc, '_>,
) {
if let Some(ref mut local_registers) = self.local_registers {
if let Some(r) = local_registers.write(mc).get_mut(id) {
*r = value;
*r = value.into();
}
}
}

View File

@ -219,14 +219,14 @@ impl<'gc> Executable<'gc> {
if af.preload_this {
//TODO: What happens if you specify both suppress and
//preload for this?
frame.set_local_register(preload_r, Value::Object(this), ac.gc_context);
frame.set_local_register(preload_r, this, ac.gc_context);
preload_r += 1;
}
if af.preload_arguments {
//TODO: What happens if you specify both suppress and
//preload for arguments?
frame.set_local_register(preload_r, Value::Object(argcell), ac.gc_context);
frame.set_local_register(preload_r, argcell, ac.gc_context);
preload_r += 1;
}

View File

@ -94,11 +94,7 @@ pub fn create_globals<'gc>(gc_context: MutationContext<'gc, '_>) -> Object<'gc>
globals.force_set_function("isNaN", is_nan, gc_context, EnumSet::empty());
globals.force_set_function("Boolean", boolean, gc_context, EnumSet::empty());
globals.force_set(
"Math",
Value::Object(math::create(gc_context)),
EnumSet::empty(),
);
globals.force_set("Math", math::create(gc_context), EnumSet::empty());
globals.force_set_function("getURL", getURL, gc_context, EnumSet::empty());
globals.force_set_function("Number", number, gc_context, EnumSet::empty());
globals.force_set_function("random", random, gc_context, EnumSet::empty());
@ -121,13 +117,18 @@ mod tests {
use crate::avm1::Error;
macro_rules! test_std {
( $test: ident, $fun: expr, $version: expr, $($args: expr => $out: expr),* ) => {
( $test: ident, $fun: expr, $version: expr, $([$($arg: expr),*] => $out: expr),* ) => {
#[test]
fn $test() -> Result<(), Error> {
with_avm($version, |avm, context, this| {
$(
assert_eq!($fun(avm, context, this, $args), $out);
#[allow(unused_mut)]
let mut args: Vec<Value> = Vec::new();
$(
args.push($arg.into());
)*
assert_eq!($fun(avm, context, this, &args), $out.into());
)*
Ok(())
@ -137,82 +138,82 @@ mod tests {
}
test_std!(boolean_function, boolean, 19,
&[Value::Bool(true)] => Value::Bool(true),
&[Value::Bool(false)] => Value::Bool(false),
&[Value::Number(10.0)] => Value::Bool(true),
&[Value::Number(-10.0)] => Value::Bool(true),
&[Value::Number(0.0)] => Value::Bool(false),
&[Value::Number(std::f64::INFINITY)] => Value::Bool(true),
&[Value::Number(std::f64::NAN)] => Value::Bool(false),
&[Value::String("".to_string())] => Value::Bool(false),
&[Value::String("Hello".to_string())] => Value::Bool(true),
&[Value::String(" ".to_string())] => Value::Bool(true),
&[Value::String("0".to_string())] => Value::Bool(true),
&[Value::String("1".to_string())] => Value::Bool(true),
&[] => Value::Bool(false)
[true] => true,
[false] => false,
[10.0] => true,
[-10.0] => true,
[0.0] => false,
[std::f64::INFINITY] => true,
[std::f64::NAN] => false,
[""] => false,
["Hello"] => true,
[" "] => true,
["0"] => true,
["1"] => true,
[] => false
);
test_std!(boolean_function_swf6, boolean, 6,
&[Value::Bool(true)] => Value::Bool(true),
&[Value::Bool(false)] => Value::Bool(false),
&[Value::Number(10.0)] => Value::Bool(true),
&[Value::Number(-10.0)] => Value::Bool(true),
&[Value::Number(0.0)] => Value::Bool(false),
&[Value::Number(std::f64::INFINITY)] => Value::Bool(true),
&[Value::Number(std::f64::NAN)] => Value::Bool(false),
&[Value::String("".to_string())] => Value::Bool(false),
&[Value::String("Hello".to_string())] => Value::Bool(false),
&[Value::String(" ".to_string())] => Value::Bool(false),
&[Value::String("0".to_string())] => Value::Bool(false),
&[Value::String("1".to_string())] => Value::Bool(true),
&[] => Value::Bool(false)
[true] => true,
[false] => false,
[10.0] => true,
[-10.0] => true,
[0.0] => false,
[std::f64::INFINITY] => true,
[std::f64::NAN] => false,
[""] => false,
["Hello"] => false,
[" "] => false,
["0"] => false,
["1"] => true,
[] => false
);
test_std!(is_nan_function, is_nan, 19,
&[Value::Bool(true)] => Value::Bool(false),
&[Value::Bool(false)] => Value::Bool(false),
&[Value::Number(10.0)] => Value::Bool(false),
&[Value::Number(-10.0)] => Value::Bool(false),
&[Value::Number(0.0)] => Value::Bool(false),
&[Value::Number(std::f64::INFINITY)] => Value::Bool(false),
&[Value::Number(std::f64::NAN)] => Value::Bool(true),
&[Value::String("".to_string())] => Value::Bool(false),
&[Value::String("Hello".to_string())] => Value::Bool(true),
&[Value::String(" ".to_string())] => Value::Bool(true),
&[Value::String(" 5 ".to_string())] => Value::Bool(true),
&[Value::String("0".to_string())] => Value::Bool(false),
&[Value::String("1".to_string())] => Value::Bool(false),
&[Value::String("Infinity".to_string())] => Value::Bool(true),
&[Value::String("100a".to_string())] => Value::Bool(true),
&[Value::String("0x10".to_string())] => Value::Bool(false),
&[Value::String("0xhello".to_string())] => Value::Bool(true),
&[Value::String("0x1999999981ffffff".to_string())] => Value::Bool(false),
&[Value::String("0xUIXUIDFKHJDF012345678".to_string())] => Value::Bool(true),
&[Value::String("123e-1".to_string())] => Value::Bool(false),
&[] => Value::Bool(true)
[true] => false,
[false] => false,
[10.0] => false,
[-10.0] => false,
[0.0] => false,
[std::f64::INFINITY] => false,
[std::f64::NAN] => true,
[""] => false,
["Hello"] => true,
[" "] => true,
[" 5 "] => true,
["0"] => false,
["1"] => false,
["Infinity"] => true,
["100a"] => true,
["0x10"] => false,
["0xhello"] => true,
["0x1999999981ffffff"] => false,
["0xUIXUIDFKHJDF012345678"] => true,
["123e-1"] => false,
[] => true
);
test_std!(number_function, number, 19,
&[Value::Bool(true)] => Value::Number(1.0),
&[Value::Bool(false)] => Value::Number(0.0),
&[Value::Number(10.0)] => Value::Number(10.0),
&[Value::Number(-10.0)] => Value::Number(-10.0),
&[Value::Number(0.0)] => Value::Number(0.0),
&[Value::Number(std::f64::INFINITY)] => Value::Number(std::f64::INFINITY),
&[Value::Number(std::f64::NAN)] => Value::Number(std::f64::NAN),
&[Value::String("".to_string())] => Value::Number(0.0),
&[Value::String("Hello".to_string())] => Value::Number(std::f64::NAN),
&[Value::String(" ".to_string())] => Value::Number(std::f64::NAN),
&[Value::String(" 5 ".to_string())] => Value::Number(std::f64::NAN),
&[Value::String("0".to_string())] => Value::Number(0.0),
&[Value::String("1".to_string())] => Value::Number(1.0),
&[Value::String("Infinity".to_string())] => Value::Number(std::f64::NAN),
&[Value::String("100a".to_string())] => Value::Number(std::f64::NAN),
&[Value::String("0x10".to_string())] => Value::Number(16.0),
&[Value::String("0xhello".to_string())] => Value::Number(std::f64::NAN),
&[Value::String("123e-1".to_string())] => Value::Number(12.3),
&[Value::String("0x1999999981ffffff".to_string())] => Value::Number(-2113929217.0),
&[Value::String("0xUIXUIDFKHJDF012345678".to_string())] => Value::Number(std::f64::NAN),
&[] => Value::Number(0.0)
[true] => 1.0,
[false] => 0.0,
[10.0] => 10.0,
[-10.0] => -10.0,
[0.0] => 0.0,
[std::f64::INFINITY] => std::f64::INFINITY,
[std::f64::NAN] => std::f64::NAN,
[""] => 0.0,
["Hello"] => std::f64::NAN,
[" "] => std::f64::NAN,
[" 5 "] => std::f64::NAN,
["0"] => 0.0,
["1"] => 1.0,
["Infinity"] => std::f64::NAN,
["100a"] => std::f64::NAN,
["0x10"] => 16.0,
["0xhello"] => std::f64::NAN,
["123e-1"] => 12.3,
["0x1999999981ffffff"] => -2113929217.0,
["0xUIXUIDFKHJDF012345678"] => std::f64::NAN,
[] => 0.0
);
}

View File

@ -125,7 +125,7 @@ mod tests {
use crate::avm1::Error;
macro_rules! test_std {
( $test: ident, $name: expr, $($args: expr => $out: expr),* ) => {
( $test: ident, $name: expr, $([$($arg: expr),*] => $out: expr),* ) => {
#[test]
fn $test() -> Result<(), Error> {
with_avm(19, |avm, context, _root| {
@ -133,7 +133,12 @@ mod tests {
let function = math.read().get($name, avm, context, math);
$(
assert_eq!(function.call(avm, context, math, $args)?, Some($out));
#[allow(unused_mut)]
let mut args: Vec<Value> = Vec::new();
$(
args.push($arg.into());
)*
assert_eq!(function.call(avm, context, math, &args)?, Some($out.into()));
)*
Ok(())
@ -143,88 +148,88 @@ mod tests {
}
test_std!(test_abs, "abs",
&[] => Value::Number(NAN),
&[Value::Null] => Value::Number(NAN),
&[Value::Number(-50.0)] => Value::Number(50.0),
&[Value::Number(25.0)] => Value::Number(25.0)
[] => NAN,
[Value::Null] => NAN,
[-50.0] => 50.0,
[25.0] => 25.0
);
test_std!(test_acos, "acos",
&[] => Value::Number(NAN),
&[Value::Null] => Value::Number(NAN),
&[Value::Number(-1.0)] => Value::Number(f64::acos(-1.0)),
&[Value::Number(0.0)] => Value::Number(f64::acos(0.0)),
&[Value::Number(1.0)] => Value::Number(f64::acos(1.0))
[] => NAN,
[Value::Null] => NAN,
[-1.0] => f64::acos(-1.0),
[0.0] => f64::acos(0.0),
[1.0] => f64::acos(1.0)
);
test_std!(test_asin, "asin",
&[] => Value::Number(NAN),
&[Value::Null] => Value::Number(NAN),
&[Value::Number(-1.0)] => Value::Number(f64::asin(-1.0)),
&[Value::Number(0.0)] => Value::Number(f64::asin(0.0)),
&[Value::Number(1.0)] => Value::Number(f64::asin(1.0))
[] => NAN,
[Value::Null] => NAN,
[-1.0] => f64::asin(-1.0),
[0.0] => f64::asin(0.0),
[1.0] => f64::asin(1.0)
);
test_std!(test_atan, "atan",
&[] => Value::Number(NAN),
&[Value::Null] => Value::Number(NAN),
&[Value::Number(-1.0)] => Value::Number(f64::atan(-1.0)),
&[Value::Number(0.0)] => Value::Number(f64::atan(0.0)),
&[Value::Number(1.0)] => Value::Number(f64::atan(1.0))
[] => NAN,
[Value::Null] => NAN,
[-1.0] => f64::atan(-1.0),
[0.0] => f64::atan(0.0),
[1.0] => f64::atan(1.0)
);
test_std!(test_ceil, "ceil",
&[] => Value::Number(NAN),
&[Value::Null] => Value::Number(NAN),
&[Value::Number(12.5)] => Value::Number(13.0)
[] => NAN,
[Value::Null] => NAN,
[12.5] => 13.0
);
test_std!(test_cos, "cos",
&[] => Value::Number(NAN),
&[Value::Null] => Value::Number(NAN),
&[Value::Number(0.0)] => Value::Number(1.0),
&[Value::Number(std::f64::consts::PI)] => Value::Number(f64::cos(std::f64::consts::PI))
[] => NAN,
[Value::Null] => NAN,
[0.0] => 1.0,
[std::f64::consts::PI] => f64::cos(std::f64::consts::PI)
);
test_std!(test_exp, "exp",
&[] => Value::Number(NAN),
&[Value::Null] => Value::Number(NAN),
&[Value::Number(1.0)] => Value::Number(f64::exp(1.0)),
&[Value::Number(2.0)] => Value::Number(f64::exp(2.0))
[] => NAN,
[Value::Null] => NAN,
[1.0] => f64::exp(1.0),
[2.0] => f64::exp(2.0)
);
test_std!(test_floor, "floor",
&[] => Value::Number(NAN),
&[Value::Null] => Value::Number(NAN),
&[Value::Number(12.5)] => Value::Number(12.0)
[] => NAN,
[Value::Null] => NAN,
[12.5] => 12.0
);
test_std!(test_round, "round",
&[] => Value::Number(NAN),
&[Value::Null] => Value::Number(NAN),
&[Value::Number(12.5)] => Value::Number(13.0),
&[Value::Number(23.2)] => Value::Number(23.0)
[] => NAN,
[Value::Null] => NAN,
[12.5] => 13.0,
[23.2] => 23.0
);
test_std!(test_sin, "sin",
&[] => Value::Number(NAN),
&[Value::Null] => Value::Number(NAN),
&[Value::Number(0.0)] => Value::Number(f64::sin(0.0)),
&[Value::Number(std::f64::consts::PI / 2.0)] => Value::Number(f64::sin(std::f64::consts::PI / 2.0))
[] => NAN,
[Value::Null] => NAN,
[0.0] => f64::sin(0.0),
[std::f64::consts::PI / 2.0] => f64::sin(std::f64::consts::PI / 2.0)
);
test_std!(test_sqrt, "sqrt",
&[] => Value::Number(NAN),
&[Value::Null] => Value::Number(NAN),
&[Value::Number(0.0)] => Value::Number(f64::sqrt(0.0)),
&[Value::Number(5.0)] => Value::Number(f64::sqrt(5.0))
[] => NAN,
[Value::Null] => NAN,
[0.0] => f64::sqrt(0.0),
[5.0] => f64::sqrt(5.0)
);
test_std!(test_tan, "tan",
&[] => Value::Number(NAN),
&[Value::Null] => Value::Number(NAN),
&[Value::Number(0.0)] => Value::Number(f64::tan(0.0)),
&[Value::Number(1.0)] => Value::Number(f64::tan(1.0))
[] => NAN,
[Value::Null] => NAN,
[0.0] => f64::tan(0.0),
[1.0] => f64::tan(1.0)
);
#[test]

View File

@ -19,7 +19,7 @@ fn default_to_string<'gc>(
_: GcCell<'gc, Object<'gc>>,
_: &[Value<'gc>],
) -> Value<'gc> {
Value::String("[Object object]".to_string())
"[Object object]".into()
}
#[derive(EnumSetType, Debug)]
@ -60,19 +60,19 @@ impl<'gc> Property<'gc> {
avm: &mut Avm1<'gc>,
context: &mut ActionContext<'_, 'gc, '_>,
this: GcCell<'gc, Object<'gc>>,
new_value: Value<'gc>,
new_value: impl Into<Value<'gc>>,
) {
match self {
Property::Virtual { set, .. } => {
if let Some(function) = set {
function(avm, context, this, &[new_value]);
function(avm, context, this, &[new_value.into()]);
}
}
Property::Stored {
value, attributes, ..
} => {
if !attributes.contains(ReadOnly) {
replace::<Value<'gc>>(value, new_value);
replace::<Value<'gc>>(value, new_value.into());
}
}
}
@ -214,7 +214,7 @@ impl<'gc> Object<'gc> {
pub fn set(
&mut self,
name: &str,
value: Value<'gc>,
value: impl Into<Value<'gc>>,
avm: &mut Avm1<'gc>,
context: &mut ActionContext<'_, 'gc, '_>,
this: GcCell<'gc, Object<'gc>>,
@ -225,7 +225,7 @@ impl<'gc> Object<'gc> {
}
Entry::Vacant(entry) => {
entry.insert(Property::Stored {
value,
value: value.into(),
attributes: Default::default(),
});
}
@ -251,14 +251,14 @@ impl<'gc> Object<'gc> {
);
}
pub fn force_set<A>(&mut self, name: &str, value: Value<'gc>, attributes: A)
pub fn force_set<A>(&mut self, name: &str, value: impl Into<Value<'gc>>, attributes: A)
where
A: Into<EnumSet<Attribute>>,
{
self.values.insert(
name.to_string(),
Property::Stored {
value,
value: value.into(),
attributes: attributes.into(),
},
);
@ -275,10 +275,7 @@ impl<'gc> Object<'gc> {
{
self.force_set(
name,
Value::Object(GcCell::allocate(
gc_context,
Object::native_function(function),
)),
GcCell::allocate(gc_context, Object::native_function(function)),
attributes,
)
}
@ -432,26 +429,20 @@ mod tests {
#[test]
fn test_set_get() {
with_object(0, |avm, context, object| {
object.write(context.gc_context).force_set(
"forced",
Value::String("forced".to_string()),
EnumSet::empty(),
);
object.write(context.gc_context).set(
"natural",
Value::String("natural".to_string()),
avm,
context,
object,
);
object
.write(context.gc_context)
.force_set("forced", "forced", EnumSet::empty());
object
.write(context.gc_context)
.set("natural", "natural", avm, context, object);
assert_eq!(
object.read().get("forced", avm, context, object),
Value::String("forced".to_string())
"forced".into()
);
assert_eq!(
object.read().get("natural", avm, context, object),
Value::String("natural".to_string())
"natural".into()
);
})
}
@ -459,39 +450,27 @@ mod tests {
#[test]
fn test_set_readonly() {
with_object(0, |avm, context, object| {
object.write(context.gc_context).force_set(
"normal",
Value::String("initial".to_string()),
EnumSet::empty(),
);
object.write(context.gc_context).force_set(
"readonly",
Value::String("initial".to_string()),
ReadOnly,
);
object
.write(context.gc_context)
.force_set("normal", "initial", EnumSet::empty());
object
.write(context.gc_context)
.force_set("readonly", "initial", ReadOnly);
object.write(context.gc_context).set(
"normal",
Value::String("replaced".to_string()),
avm,
context,
object,
);
object.write(context.gc_context).set(
"readonly",
Value::String("replaced".to_string()),
avm,
context,
object,
);
object
.write(context.gc_context)
.set("normal", "replaced", avm, context, object);
object
.write(context.gc_context)
.set("readonly", "replaced", avm, context, object);
assert_eq!(
object.read().get("normal", avm, context, object),
Value::String("replaced".to_string())
"replaced".into()
);
assert_eq!(
object.read().get("readonly", avm, context, object),
Value::String("initial".to_string())
"initial".into()
);
})
}
@ -499,30 +478,24 @@ mod tests {
#[test]
fn test_deletable_not_readonly() {
with_object(0, |avm, context, object| {
object.write(context.gc_context).force_set(
"test",
Value::String("initial".to_string()),
DontDelete,
);
object
.write(context.gc_context)
.force_set("test", "initial", DontDelete);
assert_eq!(object.write(context.gc_context).delete("test"), false);
assert_eq!(
object.read().get("test", avm, context, object),
Value::String("initial".to_string())
"initial".into()
);
object.write(context.gc_context).set(
"test",
Value::String("replaced".to_string()),
avm,
context,
object,
);
object
.write(context.gc_context)
.set("test", "replaced", avm, context, object);
assert_eq!(object.write(context.gc_context).delete("test"), false);
assert_eq!(
object.read().get("test", avm, context, object),
Value::String("replaced".to_string())
"replaced".into()
);
})
}
@ -530,8 +503,7 @@ mod tests {
#[test]
fn test_virtual_get() {
with_object(0, |avm, context, object| {
let getter: NativeFunction =
|_avm, _context, _this, _args| Value::String("Virtual!".to_string());
let getter: NativeFunction = |_avm, _context, _this, _args| "Virtual!".into();
object.write(context.gc_context).force_set_virtual(
"test",
getter,
@ -541,20 +513,16 @@ mod tests {
assert_eq!(
object.read().get("test", avm, context, object),
Value::String("Virtual!".to_string())
"Virtual!".into()
);
// This set should do nothing
object.write(context.gc_context).set(
"test",
Value::String("Ignored!".to_string()),
avm,
context,
object,
);
object
.write(context.gc_context)
.set("test", "Ignored!", avm, context, object);
assert_eq!(
object.read().get("test", avm, context, object),
Value::String("Virtual!".to_string())
"Virtual!".into()
);
})
}
@ -562,8 +530,7 @@ mod tests {
#[test]
fn test_delete() {
with_object(0, |avm, context, object| {
let getter: NativeFunction =
|_avm, _context, _this, _args| Value::String("Virtual!".to_string());
let getter: NativeFunction = |_avm, _context, _this, _args| "Virtual!".into();
object.write(context.gc_context).force_set_virtual(
"virtual",
@ -577,16 +544,12 @@ mod tests {
None,
DontDelete,
);
object.write(context.gc_context).force_set(
"stored",
Value::String("Stored!".to_string()),
EnumSet::empty(),
);
object.write(context.gc_context).force_set(
"stored_un",
Value::String("Stored!".to_string()),
DontDelete,
);
object
.write(context.gc_context)
.force_set("stored", "Stored!", EnumSet::empty());
object
.write(context.gc_context)
.force_set("stored_un", "Stored!", DontDelete);
assert_eq!(object.write(context.gc_context).delete("virtual"), true);
assert_eq!(object.write(context.gc_context).delete("virtual_un"), false);
@ -603,7 +566,7 @@ mod tests {
);
assert_eq!(
object.read().get("virtual_un", avm, context, object),
Value::String("Virtual!".to_string())
"Virtual!".into()
);
assert_eq!(
object.read().get("stored", avm, context, object),
@ -611,7 +574,7 @@ mod tests {
);
assert_eq!(
object.read().get("stored_un", avm, context, object),
Value::String("Stored!".to_string())
"Stored!".into()
);
})
}

View File

@ -301,7 +301,7 @@ impl<'gc> Scope<'gc> {
/// stored (e.g. not virtual) properties on the lowest object in the scope
/// chain. As a result, this function always force sets a property on the
/// local object and does not traverse the scope chain.
pub fn define(&self, name: &str, value: Value<'gc>, mc: MutationContext<'gc, '_>) {
pub fn define(&self, name: &str, value: impl Into<Value<'gc>>, mc: MutationContext<'gc, '_>) {
self.locals_mut(mc).force_set(name, value, EnumSet::empty());
}

View File

@ -13,6 +13,60 @@ pub enum Value<'gc> {
Object(GcCell<'gc, Object<'gc>>),
}
impl<'gc> From<String> for Value<'gc> {
fn from(string: String) -> Self {
Value::String(string)
}
}
impl<'gc> From<&str> for Value<'gc> {
fn from(string: &str) -> Self {
Value::String(string.to_owned())
}
}
impl<'gc> From<bool> for Value<'gc> {
fn from(value: bool) -> Self {
Value::Bool(value)
}
}
impl<'gc> From<GcCell<'gc, Object<'gc>>> for Value<'gc> {
fn from(object: GcCell<'gc, Object<'gc>>) -> Self {
Value::Object(object)
}
}
impl<'gc> From<f64> for Value<'gc> {
fn from(value: f64) -> Self {
Value::Number(value)
}
}
impl<'gc> From<f32> for Value<'gc> {
fn from(value: f32) -> Self {
Value::Number(f64::from(value))
}
}
impl<'gc> From<u8> for Value<'gc> {
fn from(value: u8) -> Self {
Value::Number(f64::from(value))
}
}
impl<'gc> From<i32> for Value<'gc> {
fn from(value: i32) -> Self {
Value::Number(f64::from(value))
}
}
impl<'gc> From<u32> for Value<'gc> {
fn from(value: u32) -> Self {
Value::Number(f64::from(value))
}
}
unsafe impl<'gc> gc_arena::Collect for Value<'gc> {
fn trace(&self, cc: gc_arena::CollectionContext) {
if let Value::Object(object) = self {