Allow no-hint primitive coercion

This commit is contained in:
David Wendt 2020-07-11 14:35:12 -04:00 committed by Mike Welsh
parent 76ab8570e4
commit 2ef03c6019
2 changed files with 37 additions and 41 deletions

View File

@ -486,10 +486,10 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
Op::IfFalse { offset } => self.op_if_false(offset, reader), Op::IfFalse { offset } => self.op_if_false(offset, reader),
Op::IfStrictEq { offset } => self.op_if_strict_eq(offset, reader), Op::IfStrictEq { offset } => self.op_if_strict_eq(offset, reader),
Op::IfStrictNe { offset } => self.op_if_strict_ne(offset, reader), Op::IfStrictNe { offset } => self.op_if_strict_ne(offset, reader),
Op::IfEq { offset } => self.op_if_eq(context, offset, reader), Op::IfEq { offset } => self.op_if_eq(offset, reader),
Op::IfNe { offset } => self.op_if_ne(context, offset, reader), Op::IfNe { offset } => self.op_if_ne(offset, reader),
Op::StrictEquals => self.op_strict_equals(), Op::StrictEquals => self.op_strict_equals(),
Op::Equals => self.op_equals(context), Op::Equals => self.op_equals(),
Op::Not => self.op_not(), Op::Not => self.op_not(),
Op::HasNext => self.op_has_next(), Op::HasNext => self.op_has_next(),
Op::HasNext2 { Op::HasNext2 {
@ -1450,14 +1450,13 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
fn op_if_eq( fn op_if_eq(
&mut self, &mut self,
context: &mut UpdateContext<'_, 'gc, '_>,
offset: i32, offset: i32,
reader: &mut Reader<Cursor<&[u8]>>, reader: &mut Reader<Cursor<&[u8]>>,
) -> Result<FrameControl<'gc>, Error> { ) -> Result<FrameControl<'gc>, Error> {
let value2 = self.avm2.pop(); let value2 = self.context.avm2.pop();
let value1 = self.avm2.pop(); let value1 = self.context.avm2.pop();
if value1.abstract_eq(&value2, self, context)? { if value1.abstract_eq(&value2, self)? {
reader.seek(offset as i64)?; reader.seek(offset as i64)?;
} }
@ -1466,14 +1465,13 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
fn op_if_ne( fn op_if_ne(
&mut self, &mut self,
context: &mut UpdateContext<'_, 'gc, '_>,
offset: i32, offset: i32,
reader: &mut Reader<Cursor<&[u8]>>, reader: &mut Reader<Cursor<&[u8]>>,
) -> Result<FrameControl<'gc>, Error> { ) -> Result<FrameControl<'gc>, Error> {
let value2 = self.avm2.pop(); let value2 = self.context.avm2.pop();
let value1 = self.avm2.pop(); let value1 = self.context.avm2.pop();
if !value1.abstract_eq(&value2, self, context)? { if !value1.abstract_eq(&value2, self)? {
reader.seek(offset as i64)?; reader.seek(offset as i64)?;
} }
@ -1489,16 +1487,13 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
Ok(FrameControl::Continue) Ok(FrameControl::Continue)
} }
fn op_equals( fn op_equals(&mut self) -> Result<FrameControl<'gc>, Error> {
&mut self, let value2 = self.context.avm2.pop();
context: &mut UpdateContext<'_, 'gc, '_>, let value1 = self.context.avm2.pop();
) -> Result<FrameControl<'gc>, Error> {
let value2 = self.avm2.pop();
let value1 = self.avm2.pop();
let result = value1.abstract_eq(&value2, self, context)?; let result = value1.abstract_eq(&value2, self)?;
self.avm2.push(result); self.context.avm2.push(result);
Ok(FrameControl::Continue) Ok(FrameControl::Continue)
} }

View File

@ -278,9 +278,12 @@ impl<'gc> Value<'gc> {
/// ToPrimitive algorithm which appears to match AVM2. /// ToPrimitive algorithm which appears to match AVM2.
pub fn coerce_to_primitive( pub fn coerce_to_primitive(
&self, &self,
hint: Hint, hint: Option<Hint>,
activation: &mut Activation<'_, 'gc, '_>, activation: &mut Activation<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
// TODO: `Date` is default-hinted as a `String` for some reason
let hint = hint.unwrap_or(Hint::Number);
match self { match self {
Value::Object(o) if hint == Hint::String => { Value::Object(o) if hint == Hint::String => {
let mut prim = self.clone(); let mut prim = self.clone();
@ -408,7 +411,7 @@ impl<'gc> Value<'gc> {
)) ))
.coerce_to_number(activation)?, .coerce_to_number(activation)?,
Value::Object(_) => self Value::Object(_) => self
.coerce_to_primitive(Hint::Number, activation)? .coerce_to_primitive(Some(Hint::Number), activation)?
.coerce_to_number(activation)?, .coerce_to_number(activation)?,
}) })
} }
@ -517,7 +520,7 @@ impl<'gc> Value<'gc> {
Value::String(s) => *s, Value::String(s) => *s,
Value::Namespace(ns) => ns.as_uri(), Value::Namespace(ns) => ns.as_uri(),
Value::Object(_) => self Value::Object(_) => self
.coerce_to_primitive(Hint::String, activation)? .coerce_to_primitive(Some(Hint::String), activation)?
.coerce_to_string(activation)?, .coerce_to_string(activation)?,
}) })
} }
@ -546,8 +549,7 @@ impl<'gc> Value<'gc> {
pub fn abstract_eq( pub fn abstract_eq(
&self, &self,
other: &Value<'gc>, other: &Value<'gc>,
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc, '_>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<bool, Error> { ) -> Result<bool, Error> {
match (self, other) { match (self, other) {
(Value::Undefined, Value::Undefined) => Ok(true), (Value::Undefined, Value::Undefined) => Ok(true),
@ -573,49 +575,48 @@ impl<'gc> Value<'gc> {
(Value::Undefined, Value::Null) => Ok(true), (Value::Undefined, Value::Null) => Ok(true),
(Value::Null, Value::Undefined) => Ok(true), (Value::Null, Value::Undefined) => Ok(true),
(Value::Number(_), Value::String(_)) => { (Value::Number(_), Value::String(_)) => {
let number_other = Value::from(other.coerce_to_number(activation, context)?); let number_other = Value::from(other.coerce_to_number(activation)?);
self.abstract_eq(&number_other, activation, context) self.abstract_eq(&number_other, activation)
} }
(Value::String(_), Value::Number(_)) => { (Value::String(_), Value::Number(_)) => {
let number_self = Value::from(self.coerce_to_number(activation, context)?); let number_self = Value::from(self.coerce_to_number(activation)?);
number_self.abstract_eq(other, activation, context) number_self.abstract_eq(other, activation)
} }
(Value::Bool(_), _) => { (Value::Bool(_), _) => {
let number_self = Value::from(self.coerce_to_number(activation, context)?); let number_self = Value::from(self.coerce_to_number(activation)?);
number_self.abstract_eq(other, activation, context) number_self.abstract_eq(other, activation)
} }
(_, Value::Bool(_)) => { (_, Value::Bool(_)) => {
let number_other = Value::from(other.coerce_to_number(activation, context)?); let number_other = Value::from(other.coerce_to_number(activation)?);
self.abstract_eq(&number_other, activation, context) self.abstract_eq(&number_other, activation)
} }
(Value::String(_), Value::Object(_)) | (Value::Number(_), Value::Object(_)) => { (Value::String(_), Value::Object(_)) | (Value::Number(_), Value::Object(_)) => {
//TODO: Should this be `Hint::Number`, `Hint::String`, or no-hint? //TODO: Should this be `Hint::Number`, `Hint::String`, or no-hint?
let primitive_other = let primitive_other = other.coerce_to_primitive(Some(Hint::Number), activation)?;
other.coerce_to_primitive(Hint::Number, activation, context)?;
self.abstract_eq(&primitive_other, activation, context) self.abstract_eq(&primitive_other, activation)
} }
(Value::Object(_), Value::String(_)) | (Value::Object(_), Value::Number(_)) => { (Value::Object(_), Value::String(_)) | (Value::Object(_), Value::Number(_)) => {
//TODO: Should this be `Hint::Number`, `Hint::String`, or no-hint? //TODO: Should this be `Hint::Number`, `Hint::String`, or no-hint?
let primitive_self = self.coerce_to_primitive(Hint::Number, activation, context)?; let primitive_self = self.coerce_to_primitive(Some(Hint::Number), activation)?;
primitive_self.abstract_eq(other, activation, context) primitive_self.abstract_eq(other, activation)
} }
//TODO: This is entirely a shot in the dark. //TODO: This is entirely a shot in the dark.
(Value::Namespace(_), _) => { (Value::Namespace(_), _) => {
let string_self = self.coerce_to_primitive(Hint::String, activation, context)?; let string_self = self.coerce_to_primitive(Some(Hint::String), activation)?;
string_self.abstract_eq(other, activation, context) string_self.abstract_eq(other, activation)
} }
//TODO: So is this. //TODO: So is this.
(_, Value::Namespace(_)) => { (_, Value::Namespace(_)) => {
let string_other = other.coerce_to_primitive(Hint::String, activation, context)?; let string_other = other.coerce_to_primitive(Some(Hint::String), activation)?;
self.abstract_eq(&string_other, activation, context) self.abstract_eq(&string_other, activation)
} }
_ => Ok(false), _ => Ok(false),
} }