From 4c1489a8149b4953a72c749b76379083dbdd74be Mon Sep 17 00:00:00 2001 From: David Wendt Date: Tue, 23 Jun 2020 23:43:30 -0400 Subject: [PATCH] Promote bytes to signed representation before pushing. For whatever reason, `pushbyte` appears to be processed as a *signed* byte, despite the clear wording of "*byte_value* is an unsigned byte" in avm2overview.pdf. I guess it's supposed to be manually converted and promoted in this manner. --- core/src/avm2/activation.rs | 15 +++++------ core/src/avm2/value.rs | 53 +++++++++++++++---------------------- 2 files changed, 28 insertions(+), 40 deletions(-) diff --git a/core/src/avm2/activation.rs b/core/src/avm2/activation.rs index 70f2f2da4..c91fe6196 100644 --- a/core/src/avm2/activation.rs +++ b/core/src/avm2/activation.rs @@ -475,7 +475,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> { Op::NewClass { index } => self.op_new_class(method, index), Op::CoerceA => self.op_coerce_a(), Op::ConvertB => self.op_convert_b(), - Op::ConvertD => self.op_convert_d(context), + Op::ConvertD => self.op_convert_d(), Op::Jump { offset } => self.op_jump(offset, reader), Op::IfTrue { offset } => self.op_if_true(offset, reader), Op::IfFalse { offset } => self.op_if_false(offset, reader), @@ -526,7 +526,9 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> { } fn op_push_byte(&mut self, value: u8) -> Result, Error> { - self.context.avm2.push(value); + //TODO: Adobe Animate CC appears to generate signed byte values, and + //JPEXS appears to take them. + self.context.avm2.push(value as i8 as f64); Ok(FrameControl::Continue) } @@ -1317,13 +1319,10 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> { Ok(FrameControl::Continue) } - fn op_convert_d( - &mut self, - context: &mut UpdateContext<'_, 'gc, '_>, - ) -> Result, Error> { - let value = self.avm2.pop().coerce_to_number(self, context)?; + fn op_convert_d(&mut self) -> Result, Error> { + let value = self.context.avm2.pop().coerce_to_number(self)?; - self.avm2.push(Value::Number(value)); + self.context.avm2.push(Value::Number(value)); Ok(FrameControl::Continue) } diff --git a/core/src/avm2/value.rs b/core/src/avm2/value.rs index bc00d798d..96b69d897 100644 --- a/core/src/avm2/value.rs +++ b/core/src/avm2/value.rs @@ -7,7 +7,6 @@ use crate::avm2::object::{Object, TObject}; use crate::avm2::script::TranslationUnit; use crate::avm2::string::AvmString; use crate::avm2::Error; -use crate::context::UpdateContext; use gc_arena::{Collect, MutationContext}; use std::f64::NAN; use swf::avm2::types::{DefaultValue as AbcDefaultValue, Index}; @@ -293,21 +292,17 @@ impl<'gc> Value<'gc> { pub fn coerce_to_primitive( &self, hint: Hint, - activation: &mut Activation<'_, 'gc>, - context: &mut UpdateContext<'_, 'gc, '_>, + activation: &mut Activation<'_, 'gc, '_>, ) -> Result, Error> { match self { Value::Object(o) if hint == Hint::String => { let mut prim = self.clone(); let mut object = *o; - if let Value::Object(f) = object.get_property( - *o, - &QName::dynamic_name("toString"), - activation, - context, - )? { - prim = f.call(Some(*o), &[], activation, context, None)?; + if let Value::Object(f) = + object.get_property(*o, &QName::dynamic_name("toString"), activation)? + { + prim = f.call(Some(*o), &[], activation, None)?; } if prim.is_primitive() { @@ -315,9 +310,9 @@ impl<'gc> Value<'gc> { } if let Value::Object(f) = - object.get_property(*o, &QName::dynamic_name("valueOf"), activation, context)? + object.get_property(*o, &QName::dynamic_name("valueOf"), activation)? { - prim = f.call(Some(*o), &[], activation, context, None)?; + prim = f.call(Some(*o), &[], activation, None)?; } if prim.is_primitive() { @@ -331,22 +326,19 @@ impl<'gc> Value<'gc> { let mut object = *o; if let Value::Object(f) = - object.get_property(*o, &QName::dynamic_name("valueOf"), activation, context)? + object.get_property(*o, &QName::dynamic_name("valueOf"), activation)? { - prim = f.call(Some(*o), &[], activation, context, None)?; + prim = f.call(Some(*o), &[], activation, None)?; } if prim.is_primitive() { return Ok(prim); } - if let Value::Object(f) = object.get_property( - *o, - &QName::dynamic_name("toString"), - activation, - context, - )? { - prim = f.call(Some(*o), &[], activation, context, None)?; + if let Value::Object(f) = + object.get_property(*o, &QName::dynamic_name("toString"), activation)? + { + prim = f.call(Some(*o), &[], activation, None)?; } if prim.is_primitive() { @@ -367,11 +359,7 @@ impl<'gc> Value<'gc> { /// /// Numerical conversions occur according to ECMA-262 3rd Edition's /// ToNumber algorithm which appears to match AVM2. - pub fn coerce_to_number( - &self, - activation: &mut Activation<'_, 'gc>, - context: &mut UpdateContext<'_, 'gc, '_>, - ) -> Result { + pub fn coerce_to_number(&self, activation: &mut Activation<'_, 'gc, '_>) -> Result { Ok(match self { Value::Undefined => f64::NAN, Value::Null => 0.0, @@ -427,13 +415,14 @@ impl<'gc> Value<'gc> { sign * digits.parse().unwrap_or(f64::NAN) } } - Value::Namespace(ns) => { - Value::String(AvmString::new(context.gc_context, ns.as_uri().to_string())) - .coerce_to_number(activation, context)? - } + Value::Namespace(ns) => Value::String(AvmString::new( + activation.context.gc_context, + ns.as_uri().to_string(), + )) + .coerce_to_number(activation)?, Value::Object(_) => self - .coerce_to_primitive(Hint::Number, activation, context)? - .coerce_to_number(activation, context)?, + .coerce_to_primitive(Hint::Number, activation)? + .coerce_to_number(activation)?, }) } }