@@ -129,6 +129,7 @@ impl PartialEq for Value<'_> {
129129 }
130130}
131131
132+ /// Value coercions and conversions.
132133impl < ' gc > Value < ' gc > {
133134 /// Yields `true` if the given value is a primitive value.
134135 ///
@@ -186,7 +187,7 @@ impl<'gc> Value<'gc> {
186187 /// `undefined`. This is not a special-cased behavior: All values are
187188 /// callable in `AVM1`. Values that are not callable objects instead
188189 /// return `undefined` rather than yielding a runtime error.
189- pub fn to_primitive_num (
190+ fn to_primitive_num (
190191 self ,
191192 activation : & mut Activation < ' _ , ' gc > ,
192193 ) -> Result < Value < ' gc > , Error < ' gc > > {
@@ -261,97 +262,6 @@ impl<'gc> Value<'gc> {
261262 Ok ( result)
262263 }
263264
264- /// ECMA-262 2nd edition s. 11.8.5 Abstract relational comparison algorithm
265- pub fn abstract_lt (
266- & self ,
267- other : Value < ' gc > ,
268- activation : & mut Activation < ' _ , ' gc > ,
269- ) -> Result < Value < ' gc > , Error < ' gc > > {
270- // If either parameter's `valueOf` results in a non-movieclip object, immediately return false.
271- // This is the common case for objects because `Object.prototype.valueOf` returns the same object.
272- // For example, `{} < {}` is false.
273- let prim_self = self . to_primitive_num ( activation) ?;
274- if matches ! ( prim_self, Value :: Object ( o) if o. as_display_object( ) . is_none( ) ) {
275- return Ok ( false . into ( ) ) ;
276- }
277- let prim_other = other. to_primitive_num ( activation) ?;
278- if matches ! ( prim_other, Value :: Object ( o) if o. as_display_object( ) . is_none( ) ) {
279- return Ok ( false . into ( ) ) ;
280- }
281-
282- let result = match ( prim_self, prim_other) {
283- ( Value :: String ( a) , Value :: String ( b) ) => {
284- let a = a. to_string ( ) ;
285- let b = b. to_string ( ) ;
286- a. bytes ( ) . lt ( b. bytes ( ) ) . into ( )
287- }
288- ( a, b) => {
289- // Coerce to number and compare, with any NaN resulting in undefined.
290- let a = a. primitive_as_number ( activation) ;
291- let b = b. primitive_as_number ( activation) ;
292- a. partial_cmp ( & b) . map_or ( Value :: Undefined , |o| {
293- Value :: Bool ( o == std:: cmp:: Ordering :: Less )
294- } )
295- }
296- } ;
297- Ok ( result)
298- }
299-
300- /// ECMA-262 2nd edition s. 11.9.3 Abstract equality comparison algorithm
301- pub fn abstract_eq (
302- self ,
303- other : Value < ' gc > ,
304- activation : & mut Activation < ' _ , ' gc > ,
305- ) -> Result < bool , Error < ' gc > > {
306- let ( a, b) = if activation. swf_version ( ) > 5 {
307- ( other, self )
308- } else {
309- // SWFv5 always calls `valueOf` even in Object-Object comparisons.
310- // Object.prototype.valueOf returns `this`, which will do pointer comparison below.
311- // In Object-primitive comparisons, `valueOf` will be called a second time below.
312- (
313- other. to_primitive_num ( activation) ?,
314- self . to_primitive_num ( activation) ?,
315- )
316- } ;
317- let result = match ( a, b) {
318- ( Value :: Undefined | Value :: Null , Value :: Undefined | Value :: Null ) => true ,
319- ( Value :: Bool ( a) , Value :: Bool ( b) ) => a == b,
320- ( Value :: String ( a) , Value :: String ( b) ) => a == b,
321- ( Value :: Object ( a) , Value :: Object ( b) ) => Object :: ptr_eq ( a, b) ,
322- ( Value :: Number ( a) , Value :: Number ( b) ) => {
323- // PLAYER-SPECIFIC: NaN == NaN returns true in Flash Player 7+ AVM1, but returns false in Flash Player 6 and lower.
324- // We choose to return true.
325- a == b || ( a. is_nan ( ) && b. is_nan ( ) )
326- }
327-
328- // Bool-to-value-comparison: Coerce bool to 0/1 and compare.
329- ( Value :: Bool ( bool) , val) | ( val, Value :: Bool ( bool) ) => {
330- val. abstract_eq ( Value :: Number ( bool as i64 as f64 ) , activation) ?
331- }
332-
333- // Number-to-value comparison: Coerce value to f64 and compare.
334- // Note that "NaN" == NaN returns false.
335- ( Value :: Number ( num) , string @ Value :: String ( _) )
336- | ( string @ Value :: String ( _) , Value :: Number ( num) ) => {
337- num == string. primitive_as_number ( activation)
338- }
339-
340- ( Value :: MovieClip ( a) , Value :: MovieClip ( b) ) => {
341- a. coerce_to_string ( activation) == b. coerce_to_string ( activation)
342- }
343-
344- // Object-to-value comparison: Call `obj.valueOf` and compare.
345- ( obj @ Value :: Object ( _) , val) | ( val, obj @ Value :: Object ( _) ) => {
346- let obj_val = obj. to_primitive_num ( activation) ?;
347- obj_val. is_primitive ( ) && val. abstract_eq ( obj_val, activation) ?
348- }
349-
350- _ => false ,
351- } ;
352- Ok ( result)
353- }
354-
355265 pub fn coerce_to_u8 ( & self , activation : & mut Activation < ' _ , ' gc > ) -> Result < u8 , Error < ' gc > > {
356266 self . coerce_to_f64 ( activation) . map ( f64_to_wrapping_u8)
357267 }
@@ -447,19 +357,6 @@ impl<'gc> Value<'gc> {
447357 }
448358 }
449359
450- pub fn type_of ( & self , activation : & mut Activation < ' _ , ' gc > ) -> AvmString < ' gc > {
451- match self {
452- Value :: Undefined => istr ! ( "undefined" ) ,
453- Value :: Null => istr ! ( "null" ) ,
454- Value :: Number ( _) => istr ! ( "number" ) ,
455- Value :: Bool ( _) => istr ! ( "boolean" ) ,
456- Value :: String ( _) => istr ! ( "string" ) ,
457- Value :: Object ( object) if object. as_function ( ) . is_some ( ) => istr ! ( "function" ) ,
458- Value :: MovieClip ( _) => istr ! ( "movieclip" ) ,
459- Value :: Object ( _) => istr ! ( "object" ) ,
460- }
461- }
462-
463360 /// Convert `self` to an script object, if possible. Unlike `coerce_to_object`, this
464361 /// doesn't coerce primitives.
465362 pub fn as_object ( self , activation : & mut Activation < ' _ , ' gc > ) -> Option < Object < ' gc > > {
@@ -549,6 +446,114 @@ impl<'gc> Value<'gc> {
549446 }
550447}
551448
449+ /// Value operators.
450+ impl < ' gc > Value < ' gc > {
451+ /// ActionScript 2's `typeof self`.
452+ pub fn type_of ( & self , activation : & mut Activation < ' _ , ' gc > ) -> AvmString < ' gc > {
453+ match self {
454+ Value :: Undefined => istr ! ( "undefined" ) ,
455+ Value :: Null => istr ! ( "null" ) ,
456+ Value :: Number ( _) => istr ! ( "number" ) ,
457+ Value :: Bool ( _) => istr ! ( "boolean" ) ,
458+ Value :: String ( _) => istr ! ( "string" ) ,
459+ Value :: Object ( object) if object. as_function ( ) . is_some ( ) => istr ! ( "function" ) ,
460+ Value :: MovieClip ( _) => istr ! ( "movieclip" ) ,
461+ Value :: Object ( _) => istr ! ( "object" ) ,
462+ }
463+ }
464+
465+ /// ECMA-262 2nd edition s. 11.8.5 Abstract relational comparison algorithm
466+ pub fn abstract_lt (
467+ & self ,
468+ other : Value < ' gc > ,
469+ activation : & mut Activation < ' _ , ' gc > ,
470+ ) -> Result < Value < ' gc > , Error < ' gc > > {
471+ // If either parameter's `valueOf` results in a non-movieclip object, immediately return false.
472+ // This is the common case for objects because `Object.prototype.valueOf` returns the same object.
473+ // For example, `{} < {}` is false.
474+ let prim_self = self . to_primitive_num ( activation) ?;
475+ if matches ! ( prim_self, Value :: Object ( o) if o. as_display_object( ) . is_none( ) ) {
476+ return Ok ( false . into ( ) ) ;
477+ }
478+ let prim_other = other. to_primitive_num ( activation) ?;
479+ if matches ! ( prim_other, Value :: Object ( o) if o. as_display_object( ) . is_none( ) ) {
480+ return Ok ( false . into ( ) ) ;
481+ }
482+
483+ let result = match ( prim_self, prim_other) {
484+ ( Value :: String ( a) , Value :: String ( b) ) => {
485+ let a = a. to_string ( ) ;
486+ let b = b. to_string ( ) ;
487+ a. bytes ( ) . lt ( b. bytes ( ) ) . into ( )
488+ }
489+ ( a, b) => {
490+ // Coerce to number and compare, with any NaN resulting in undefined.
491+ let a = a. primitive_as_number ( activation) ;
492+ let b = b. primitive_as_number ( activation) ;
493+ a. partial_cmp ( & b) . map_or ( Value :: Undefined , |o| {
494+ Value :: Bool ( o == std:: cmp:: Ordering :: Less )
495+ } )
496+ }
497+ } ;
498+ Ok ( result)
499+ }
500+
501+ /// ECMA-262 2nd edition s. 11.9.3 Abstract equality comparison algorithm
502+ pub fn abstract_eq (
503+ self ,
504+ other : Value < ' gc > ,
505+ activation : & mut Activation < ' _ , ' gc > ,
506+ ) -> Result < bool , Error < ' gc > > {
507+ let ( a, b) = if activation. swf_version ( ) > 5 {
508+ ( other, self )
509+ } else {
510+ // SWFv5 always calls `valueOf` even in Object-Object comparisons.
511+ // Object.prototype.valueOf returns `this`, which will do pointer comparison below.
512+ // In Object-primitive comparisons, `valueOf` will be called a second time below.
513+ (
514+ other. to_primitive_num ( activation) ?,
515+ self . to_primitive_num ( activation) ?,
516+ )
517+ } ;
518+ let result = match ( a, b) {
519+ ( Value :: Undefined | Value :: Null , Value :: Undefined | Value :: Null ) => true ,
520+ ( Value :: Bool ( a) , Value :: Bool ( b) ) => a == b,
521+ ( Value :: String ( a) , Value :: String ( b) ) => a == b,
522+ ( Value :: Object ( a) , Value :: Object ( b) ) => Object :: ptr_eq ( a, b) ,
523+ ( Value :: Number ( a) , Value :: Number ( b) ) => {
524+ // PLAYER-SPECIFIC: NaN == NaN returns true in Flash Player 7+ AVM1, but returns false in Flash Player 6 and lower.
525+ // We choose to return true.
526+ a == b || ( a. is_nan ( ) && b. is_nan ( ) )
527+ }
528+
529+ // Bool-to-value-comparison: Coerce bool to 0/1 and compare.
530+ ( Value :: Bool ( bool) , val) | ( val, Value :: Bool ( bool) ) => {
531+ val. abstract_eq ( Value :: Number ( bool as i64 as f64 ) , activation) ?
532+ }
533+
534+ // Number-to-value comparison: Coerce value to f64 and compare.
535+ // Note that "NaN" == NaN returns false.
536+ ( Value :: Number ( num) , string @ Value :: String ( _) )
537+ | ( string @ Value :: String ( _) , Value :: Number ( num) ) => {
538+ num == string. primitive_as_number ( activation)
539+ }
540+
541+ ( Value :: MovieClip ( a) , Value :: MovieClip ( b) ) => {
542+ a. coerce_to_string ( activation) == b. coerce_to_string ( activation)
543+ }
544+
545+ // Object-to-value comparison: Call `obj.valueOf` and compare.
546+ ( obj @ Value :: Object ( _) , val) | ( val, obj @ Value :: Object ( _) ) => {
547+ let obj_val = obj. to_primitive_num ( activation) ?;
548+ obj_val. is_primitive ( ) && val. abstract_eq ( obj_val, activation) ?
549+ }
550+
551+ _ => false ,
552+ } ;
553+ Ok ( result)
554+ }
555+ }
556+
552557/// Calculate `value * 10^exp` through repeated multiplication or division.
553558fn decimal_shift ( mut value : f64 , mut exp : i32 ) -> f64 {
554559 let mut base: f64 = 10.0 ;
0 commit comments