avm1: Merge #97, impl From<*> for Value
Implement From<*> for Value for better dev ergonomics
This commit is contained in:
commit
e02204a78f
|
@ -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(())
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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()
|
||||
);
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue