2 * Copyright (C) 2007-2008 Benjamin Otte <otte@gnome.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301 USA
27 #include "swfdec_as_types.h"
28 #include "swfdec_as_object.h"
29 #include "swfdec_as_context.h"
30 #include "swfdec_as_function.h"
31 #include "swfdec_as_number.h"
32 #include "swfdec_as_stack.h"
33 #include "swfdec_as_string.h"
34 #include "swfdec_as_strings.h"
35 #include "swfdec_as_super.h"
36 #include "swfdec_debug.h"
37 #include "swfdec_internal.h"
38 #include "swfdec_movie.h"
43 * SECTION:SwfdecAsValue
44 * @title: SwfdecAsValue
45 * @short_description: exchanging values with the Actionscript engine
47 * This section describes how values are handled inside the Actionscript
48 * engine. Since Actionscript is a dynamically typed language, the variable type
49 * has to be carried with every value. #SwfdecAsValue accomplishes that. Swfdec
50 * allows two possible ways of accessing these values: The common method is to
51 * use the provided functions to explicitly convert the values to a given type
52 * with a function such as swfdec_as_value_to_string (). This is convenient,
53 * but can be very slow as it can call back into the Actionscript engine when
54 * converting various objects. So it can be unsuitable in some cases.
55 * A different possibiltiy is accessing the values directly using the accessor
56 * macros. You must check the type before doing so though. For setting values,
57 * there only exist macros, since type conversion is not necessary.
62 * @SWFDEC_AS_TYPE_UNDEFINED: the special undefined value
63 * @SWFDEC_AS_TYPE_BOOLEAN: a boolean value - true or false
64 * @SWFDEC_AS_TYPE_INT: reserved value for integers. Should the need arise for
65 * performance enhancements - especially on embedded
66 * devices - it might be useful to implement this type.
67 * For now, this type will never appear in Swfdec. Using
68 * it will cause Swfdec to crash.
69 * @SWFDEC_AS_TYPE_NUMBER: a double value - also used for integer numbers
70 * @SWFDEC_AS_TYPE_STRING: a string. Strings are garbage-collected and unique.
71 * @SWFDEC_AS_TYPE_NULL: the spaecial null value
72 * @SWFDEC_AS_TYPE_OBJECT: an object - must be of type #SwfdecAsObject
74 * These are the possible values the Swfdec Actionscript engine knows about.
79 * @type: the type of this value.
81 * This is the type used to present an opaque value in the Actionscript
82 * engine. See #SwfdecAsValueType for possible types. It's similar in
83 * spirit to #GValue. The value held is garbage-collected. Apart from the type
84 * member, use the provided macros to access this structure.
85 * <note>If you memset a SwfdecAsValue to 0, it is a valid undefined value.</note>
89 * SWFDEC_AS_VALUE_SET_UNDEFINED:
90 * @val: value to set as undefined
92 * Sets @val to the special undefined value. If you create a temporary value,
93 * you can instead use code such as |[ SwfdecAsValue val = { 0, }; ]|
97 * SWFDEC_AS_VALUE_GET_BOOLEAN:
98 * @val: value to get, the value must reference a boolean
100 * Gets the boolean associated with value. If you are not sure if the value is
101 * a boolean, use swfdec_as_value_to_boolean () instead.
103 * Returns: %TRUE or %FALSE
107 * SWFDEC_AS_VALUE_SET_BOOLEAN:
109 * @b: boolean value to set, must be either %TRUE or %FALSE
111 * Sets @val to the specified boolean value.
115 * SWFDEC_AS_VALUE_GET_NUMBER:
116 * @val: value to get, the value must reference a number
118 * Gets the number associated with @val. If you are not sure that the value is
119 * a valid number value, consider using swfdec_as_value_to_number() or
120 * swfdec_as_value_to_int() instead.
122 * Returns: a double. It can be NaN or +-Infinity, but not -0.0
126 * SWFDEC_AS_VALUE_SET_NUMBER:
128 * @d: double value to set
130 * Sets @val to the given value. If you are sure the value is a valid
131 * integer value, use SWFDEC_AS_VALUE_SET_INT() instead.
135 * SWFDEC_AS_VALUE_SET_INT:
137 * @d: integer value to set
139 * Sets @val to the given value. Currently this macro is equivalent to
140 * SWFDEC_AS_VALUE_SET_NUMBER(), but this may change in future versions of
145 * SWFDEC_AS_VALUE_GET_STRING:
146 * @val: value to get, the value must reference a string
148 * Gets the string associated with @val. If you are not sure that the value is
149 * a string value, consider using swfdec_as_value_to_string() instead.
151 * Returns: a garbage-collected string.
155 * SWFDEC_AS_VALUE_SET_STRING:
157 * @s: garbage-collected string to use
159 * Sets @val to the given string value.
163 * SWFDEC_AS_VALUE_SET_NULL:
166 * Sets @val to the special null value.
170 * SWFDEC_AS_VALUE_GET_OBJECT:
171 * @val: value to get, the value must reference an object
173 * Gets the object associated with @val. If you are not sure that the value is
174 * an object value, consider using swfdec_as_value_to_object() instead.
176 * Returns: a #SwfdecAsObject
180 * SWFDEC_AS_VALUE_SET_OBJECT:
182 * @o: garbage-collected #SwfdecAsObject to use
184 * Sets @val to the given object. The object must have been added to the
185 * garbage collector via swfdec_as_object_add() previously.
188 /*** actual code ***/
191 * swfdec_as_str_concat:
192 * @cx: a #SwfdecAsContext
196 * Convenience function to concatenate two garbage-collected strings. This
197 * function is equivalent to g_strconcat ().
199 * Returns: A new garbage-collected string
202 swfdec_as_str_concat (SwfdecAsContext
*cx
, const char * s1
, const char *s2
)
207 g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (cx
), SWFDEC_AS_STR_EMPTY
);
208 g_return_val_if_fail (s1
, SWFDEC_AS_STR_EMPTY
);
209 g_return_val_if_fail (s2
, SWFDEC_AS_STR_EMPTY
);
211 s
= g_strconcat (s1
, s2
, NULL
);
212 ret
= swfdec_as_context_get_string (cx
, s
);
219 * swfdec_as_integer_to_string:
220 * @context: a #SwfdecAsContext
221 * @i: an integer that fits into 32 bits
223 * Converts @d into a string using the same conversion algorithm as the
224 * official Flash player.
226 * Returns: a garbage-collected string
229 swfdec_as_integer_to_string (SwfdecAsContext
*context
, int i
)
231 return swfdec_as_context_give_string (context
, g_strdup_printf ("%d", i
));
235 * swfdec_as_double_to_string:
236 * @context: a #SwfdecAsContext
239 * Converts @d into a string using the same conversion algorithm as the
240 * official Flash player.
242 * Returns: a garbage-collected string
244 /* FIXME: this function is still buggy - and it's ugly as hell.
245 * Someone with the right expertise should rewrite it
247 * http://www.cs.indiana.edu/~burger/FP-Printing-PLDI96.pdf
248 * http://lxr.mozilla.org/mozilla/source/js/tamarin/core/MathUtils.cpp
251 swfdec_as_double_to_string (SwfdecAsContext
*context
, double d
)
253 gboolean found
= FALSE
, gotdot
= FALSE
;
255 char tmp
[50], *end
, *start
, *s
;
257 g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context
), SWFDEC_AS_STR_EMPTY
);
260 return SWFDEC_AS_STR_NaN
;
262 return d
< 0 ? SWFDEC_AS_STR__Infinity
: SWFDEC_AS_STR_Infinity
;
265 return SWFDEC_AS_STR_0
;
269 if (ABS (d
) > 0.00001 && ABS (d
) < 1e+15) {
270 g_ascii_formatd (s
, 50, "%.22f", d
);
272 g_ascii_formatd (s
, 50, "%.25e", d
);
278 /* count digits (maximum allowed is 15) */
285 if (*start
< '0' || *start
> '9')
287 if (found
|| *start
!= '0') {
294 /* go to end of string */
295 while (*end
!= 'e' && *end
!= 0)
297 /* round using the next digit */
298 if (*start
>= '5' && *start
<= '9') {
300 /* skip all 9s at the end */
301 while (start
[-1] == '9')
303 /* if we're before the dot, replace 9s with 0s */
304 if (start
[-1] == '.') {
308 while (start
[-1] == '9') {
312 /* write out correct number */
313 if (start
[-1] == '-') {
317 } else if (start
[-1] == ' ') {
323 /* reposition cursor at end */
327 /* remove trailing zeros (note we skipped zero above, so there will be non-0 bytes left) */
329 while (start
[-1] == '0')
331 if (start
[-1] == '.')
349 return swfdec_as_context_get_string (context
, s
);
353 * swfdec_as_value_to_string:
354 * @context: a #SwfdecAsContext
355 * @value: value to be expressed as string
357 * Converts @value to a string according to the rules of Flash. This might
358 * cause calling back into the script engine if the @value is an object. In
359 * that case, the object's valueOf function is called.
360 * <warning>Never use this function for debugging purposes.</warning>
362 * Returns: a garbage-collected string representing @value. The value will
366 swfdec_as_value_to_string (SwfdecAsContext
*context
, const SwfdecAsValue
*value
)
368 g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context
), SWFDEC_AS_STR_EMPTY
);
369 g_return_val_if_fail (SWFDEC_IS_AS_VALUE (value
), SWFDEC_AS_STR_EMPTY
);
371 switch (value
->type
) {
372 case SWFDEC_AS_TYPE_STRING
:
373 return SWFDEC_AS_VALUE_GET_STRING (value
);
374 case SWFDEC_AS_TYPE_UNDEFINED
:
375 if (context
->version
> 6)
376 return SWFDEC_AS_STR_undefined
;
378 return SWFDEC_AS_STR_EMPTY
;
379 case SWFDEC_AS_TYPE_BOOLEAN
:
380 return SWFDEC_AS_VALUE_GET_BOOLEAN (value
) ? SWFDEC_AS_STR_true
: SWFDEC_AS_STR_false
;
381 case SWFDEC_AS_TYPE_NULL
:
382 return SWFDEC_AS_STR_null
;
383 case SWFDEC_AS_TYPE_NUMBER
:
384 return swfdec_as_double_to_string (context
, SWFDEC_AS_VALUE_GET_NUMBER (value
));
385 case SWFDEC_AS_TYPE_OBJECT
:
387 SwfdecAsObject
*object
= SWFDEC_AS_VALUE_GET_OBJECT (value
);
388 if (SWFDEC_IS_MOVIE (object
)) {
389 SwfdecMovie
*movie
= swfdec_movie_resolve (SWFDEC_MOVIE (object
));
391 return SWFDEC_AS_STR_EMPTY
;
393 char *str
= swfdec_movie_get_path (SWFDEC_MOVIE (object
), TRUE
);
394 return swfdec_as_context_give_string (context
, str
);
396 } else if (SWFDEC_IS_AS_STRING (object
)) {
397 return SWFDEC_AS_STRING (object
)->string
;
400 swfdec_as_object_call (object
, SWFDEC_AS_STR_toString
, 0, NULL
, &ret
);
401 if (SWFDEC_AS_VALUE_IS_STRING (&ret
))
402 return SWFDEC_AS_VALUE_GET_STRING (&ret
);
403 else if (SWFDEC_IS_AS_SUPER (SWFDEC_AS_VALUE_GET_OBJECT (value
)))
404 return SWFDEC_AS_STR__type_Object_
;
405 else if (SWFDEC_IS_AS_FUNCTION (SWFDEC_AS_VALUE_GET_OBJECT (value
)))
406 return SWFDEC_AS_STR__type_Function_
;
408 return SWFDEC_AS_STR__type_Object_
;
411 case SWFDEC_AS_TYPE_INT
:
413 g_assert_not_reached ();
414 return SWFDEC_AS_STR_EMPTY
;
419 * swfdec_as_value_to_debug:
420 * @value: a #SwfdecAsValue
422 * Converts the given @value to a string in a safe way. It will not call into
423 * the scripting engine. Its intended use is for output in debuggers.
425 * Returns: a newly allocated string. Free with g_free().
428 swfdec_as_value_to_debug (const SwfdecAsValue
*value
)
430 g_return_val_if_fail (SWFDEC_IS_AS_VALUE (value
), NULL
);
432 switch (value
->type
) {
433 case SWFDEC_AS_TYPE_STRING
:
434 return g_shell_quote (SWFDEC_AS_VALUE_GET_STRING (value
));
435 case SWFDEC_AS_TYPE_UNDEFINED
:
436 return g_strdup ("undefined");
437 case SWFDEC_AS_TYPE_BOOLEAN
:
438 return g_strdup (SWFDEC_AS_VALUE_GET_BOOLEAN (value
) ? "true" : "false");
439 case SWFDEC_AS_TYPE_NULL
:
440 return g_strdup ("null");
441 case SWFDEC_AS_TYPE_NUMBER
:
442 return g_strdup_printf ("%g", SWFDEC_AS_VALUE_GET_NUMBER (value
));
443 case SWFDEC_AS_TYPE_OBJECT
:
444 return swfdec_as_object_get_debug (SWFDEC_AS_VALUE_GET_OBJECT (value
));
445 case SWFDEC_AS_TYPE_INT
:
447 g_assert_not_reached ();
453 * swfdec_as_value_to_number:
454 * @context: a #SwfdecAsContext
455 * @value: a #SwfdecAsValue used by context
457 * Converts the value to a number according to Flash's conversion routines and
458 * the current Flash version. This conversion routine is similar, but not equal
459 * to ECMAScript. For objects, it can call back into the script engine by
460 * calling the object's valueOf function.
462 * Returns: a double value. It can be NaN or +-Infinity. It will not be -0.0.
465 swfdec_as_value_to_number (SwfdecAsContext
*context
, const SwfdecAsValue
*value
)
469 g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context
), 0.0);
470 g_return_val_if_fail (SWFDEC_IS_AS_VALUE (value
), 0.0);
473 swfdec_as_value_to_primitive (&tmp
);
476 case SWFDEC_AS_TYPE_UNDEFINED
:
477 case SWFDEC_AS_TYPE_NULL
:
478 return (context
->version
>= 7) ? NAN
: 0.0;
479 case SWFDEC_AS_TYPE_BOOLEAN
:
480 return SWFDEC_AS_VALUE_GET_BOOLEAN (&tmp
) ? 1 : 0;
481 case SWFDEC_AS_TYPE_NUMBER
:
482 return SWFDEC_AS_VALUE_GET_NUMBER (&tmp
);
483 case SWFDEC_AS_TYPE_STRING
:
489 // FIXME: We should most likely copy Tamarin's code here (MathUtils.cpp)
490 s
= SWFDEC_AS_VALUE_GET_STRING (&tmp
);
491 if (s
== SWFDEC_AS_STR_EMPTY
)
492 return (context
->version
>= 5) ? NAN
: 0.0;
493 if (context
->version
> 5 && s
[0] == '0' &&
494 (s
[1] == 'x' || s
[1] == 'X')) {
495 d
= g_ascii_strtoll (s
+ 2, &end
, 16);
496 } else if (context
->version
> 5 &&
497 (s
[0] == '0' || ((s
[0] == '+' || s
[0] == '-') && s
[1] == '0')) &&
498 s
[strspn (s
+1, "01234567")+1] == '\0') {
499 d
= g_ascii_strtoll (s
, &end
, 8);
501 if (strpbrk (s
, "xXiI") != NULL
)
502 return (context
->version
>= 5) ? NAN
: 0.0;
503 d
= g_ascii_strtod (s
, &end
);
505 if (*end
== '\0' || context
->version
< 5)
506 return d
== -0.0 ? 0.0 : d
;
510 case SWFDEC_AS_TYPE_OBJECT
:
511 return (context
->version
>= 5) ? NAN
: 0.0;
512 case SWFDEC_AS_TYPE_INT
:
514 g_assert_not_reached ();
520 * swfdec_as_double_to_integer:
523 * Converts the given double to an integer using the same rules as the Flash
526 * Returns: an integer
529 swfdec_as_double_to_integer (double d
)
534 d
= fmod (-d
, 4294967296.0);
537 d
= fmod (d
, 4294967296.0);
543 * swfdec_as_value_to_integer:
544 * @context: a #SwfdecAsContext
545 * @value: value to convert
547 * Converts the given value to an integer. This is done similar to the
548 * conversion used by swfdec_as_value_to_number().
550 * Returns: An Integer that can be represented in 32 bits.
553 swfdec_as_value_to_integer (SwfdecAsContext
*context
, const SwfdecAsValue
*value
)
557 d
= swfdec_as_value_to_number (context
, value
);
558 return swfdec_as_double_to_integer (d
);
562 * swfdec_as_value_to_object:
563 * @context: a #SwfdecAsContext
564 * @value: value to convert
566 * Converts a given value to its representation as an object. The object
567 * representation for primitive types is a wrapper object of the corresponding
568 * class (Number for numbers, String for strings, Boolean for bools). If the
569 * value does not have an object representing it, such as undefined and null
570 * values, %NULL is returned.
572 * Returns: object representing @value or %NULL.
575 swfdec_as_value_to_object (SwfdecAsContext
*context
, const SwfdecAsValue
*value
)
577 SwfdecAsFunction
*fun
;
581 g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context
), NULL
);
582 g_return_val_if_fail (SWFDEC_IS_AS_VALUE (value
), NULL
);
584 switch (value
->type
) {
585 case SWFDEC_AS_TYPE_UNDEFINED
:
586 case SWFDEC_AS_TYPE_NULL
:
588 case SWFDEC_AS_TYPE_NUMBER
:
589 s
= SWFDEC_AS_STR_Number
;
591 case SWFDEC_AS_TYPE_STRING
:
592 s
= SWFDEC_AS_STR_String
;
594 case SWFDEC_AS_TYPE_BOOLEAN
:
595 s
= SWFDEC_AS_STR_Boolean
;
597 case SWFDEC_AS_TYPE_OBJECT
:
598 return SWFDEC_AS_VALUE_GET_OBJECT (value
);
599 case SWFDEC_AS_TYPE_INT
:
601 g_assert_not_reached ();
605 swfdec_as_object_get_variable (context
->global
, s
, &val
);
606 if (!SWFDEC_AS_VALUE_IS_OBJECT (&val
) ||
607 !SWFDEC_IS_AS_FUNCTION (fun
= (SwfdecAsFunction
*) SWFDEC_AS_VALUE_GET_OBJECT (&val
)))
609 swfdec_as_object_create (fun
, 1, value
, &val
);
610 if (SWFDEC_AS_VALUE_IS_OBJECT (&val
)) {
611 return SWFDEC_AS_VALUE_GET_OBJECT (&val
);
613 SWFDEC_ERROR ("did not construct an object");
619 * swfdec_as_value_to_boolean:
620 * @context: a #SwfdecAsContext
621 * @value: value to convert
623 * Converts the given value to a boolean according to Flash's rules. Note that
624 * these rules changed significantly for strings between Flash 6 and 7.
626 * Returns: either %TRUE or %FALSE.
629 swfdec_as_value_to_boolean (SwfdecAsContext
*context
, const SwfdecAsValue
*value
)
631 g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context
), FALSE
);
632 g_return_val_if_fail (SWFDEC_IS_AS_VALUE (value
), FALSE
);
634 /* FIXME: what do we do when called in flash 4? */
635 switch (value
->type
) {
636 case SWFDEC_AS_TYPE_UNDEFINED
:
637 case SWFDEC_AS_TYPE_NULL
:
639 case SWFDEC_AS_TYPE_BOOLEAN
:
640 return SWFDEC_AS_VALUE_GET_BOOLEAN (value
);
641 case SWFDEC_AS_TYPE_NUMBER
:
643 double d
= SWFDEC_AS_VALUE_GET_NUMBER (value
);
644 return d
!= 0.0 && !isnan (d
);
646 case SWFDEC_AS_TYPE_STRING
:
647 if (context
->version
<= 6) {
648 double d
= swfdec_as_value_to_number (context
, value
);
649 return d
!= 0.0 && !isnan (d
);
651 return SWFDEC_AS_VALUE_GET_STRING (value
) != SWFDEC_AS_STR_EMPTY
;
653 case SWFDEC_AS_TYPE_OBJECT
:
655 case SWFDEC_AS_TYPE_INT
:
657 g_assert_not_reached ();
663 * swfdec_as_value_to_primitive:
664 * @value: value to convert
666 * Tries to convert the given @value inline to its primitive value. Primitive
667 * values are values that are not objects. If the value is an object, the
668 * object's valueOf function is called. If the result of that function is still
669 * an object, it is returned nonetheless.
672 swfdec_as_value_to_primitive (SwfdecAsValue
*value
)
674 g_return_if_fail (SWFDEC_IS_AS_VALUE (value
));
676 if (SWFDEC_AS_VALUE_IS_OBJECT (value
) && !SWFDEC_IS_MOVIE (SWFDEC_AS_VALUE_GET_OBJECT (value
))) {
677 swfdec_as_object_call (SWFDEC_AS_VALUE_GET_OBJECT (value
), SWFDEC_AS_STR_valueOf
,
682 /* from swfdec_internal.h */
684 swfdec_as_value_to_twips (SwfdecAsContext
*context
, const SwfdecAsValue
*val
,
685 gboolean is_length
, SwfdecTwips
*result
)
689 g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context
), FALSE
);
690 g_return_val_if_fail (val
!= NULL
, FALSE
);
691 g_return_val_if_fail (result
!= NULL
, FALSE
);
693 if (SWFDEC_AS_VALUE_IS_UNDEFINED (val
) || SWFDEC_AS_VALUE_IS_NULL (val
))
696 d
= swfdec_as_value_to_number (context
, val
);
699 if (is_length
&& d
< 0)
702 d
*= SWFDEC_TWIPS_SCALE_FACTOR
;
703 *result
= swfdec_as_double_to_integer (d
);
705 *result
= ABS (*result
);