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_context.h"
29 #include "swfdec_as_function.h"
30 #include "swfdec_as_gcable.h"
31 #include "swfdec_as_internal.h"
32 #include "swfdec_as_number.h"
33 #include "swfdec_as_object.h"
34 #include "swfdec_as_stack.h"
35 #include "swfdec_as_string.h"
36 #include "swfdec_as_strings.h"
37 #include "swfdec_as_super.h"
38 #include "swfdec_debug.h"
39 #include "swfdec_internal.h"
40 #include "swfdec_movie.h"
45 * SECTION:SwfdecAsValue
46 * @title: SwfdecAsValue
47 * @short_description: exchanging values with the Actionscript engine
49 * This section describes how values are handled inside the Actionscript
50 * engine. Since Actionscript is a dynamically typed language, the variable type
51 * has to be carried with every value. #SwfdecAsValue accomplishes that. Swfdec
52 * allows two possible ways of accessing these values: The common method is to
53 * use the provided functions to explicitly convert the values to a given type
54 * with a function such as swfdec_as_value_to_string (). This is convenient,
55 * but can be very slow as it can call back into the Actionscript engine when
56 * converting various objects. So it can be unsuitable in some cases.
57 * A different possibiltiy is accessing the values directly using the accessor
58 * macros. You must check the type before doing so though. For setting values,
59 * there only exist macros, since type conversion is not necessary.
64 * @SWFDEC_AS_TYPE_UNDEFINED: the special undefined value
65 * @SWFDEC_AS_TYPE_BOOLEAN: a boolean value - true or false
66 * @SWFDEC_AS_TYPE_INT: reserved value for integers. Should the need arise for
67 * performance enhancements - especially on embedded
68 * devices - it might be useful to implement this type.
69 * For now, this type will never appear in Swfdec. Using
70 * it will cause Swfdec to crash.
71 * @SWFDEC_AS_TYPE_NUMBER: a double value - also used for integer numbers
72 * @SWFDEC_AS_TYPE_STRING: a string. Strings are garbage-collected and unique.
73 * @SWFDEC_AS_TYPE_NULL: the spaecial null value
74 * @SWFDEC_AS_TYPE_OBJECT: an object - must be of type #SwfdecAsObject
76 * These are the possible values the Swfdec Actionscript engine knows about.
81 * @type: the type of this value.
83 * This is the type used to present an opaque value in the Actionscript
84 * engine. See #SwfdecAsValueType for possible types. It's similar in
85 * spirit to #GValue. The value held is garbage-collected. Apart from the type
86 * member, use the provided macros to access this structure.
87 * <note>If you memset a SwfdecAsValue to 0, it is a valid undefined value.</note>
91 * SWFDEC_AS_VALUE_SET_UNDEFINED:
92 * @val: value to set as undefined
94 * Sets @val to the special undefined value. If you create a temporary value,
95 * you can instead use code such as |[ SwfdecAsValue val = { 0, }; ]|
99 * SWFDEC_AS_VALUE_GET_BOOLEAN:
100 * @val: value to get, the value must reference a boolean
102 * Gets the boolean associated with value. If you are not sure if the value is
103 * a boolean, use swfdec_as_value_to_boolean () instead.
105 * Returns: %TRUE or %FALSE
109 * SWFDEC_AS_VALUE_SET_BOOLEAN:
111 * @b: boolean value to set, must be either %TRUE or %FALSE
113 * Sets @val to the specified boolean value.
117 * SWFDEC_AS_VALUE_GET_NUMBER:
118 * @val: value to get, the value must reference a number
120 * Gets the number associated with @val. If you are not sure that the value is
121 * a valid number value, consider using swfdec_as_value_to_number() or
122 * swfdec_as_value_to_int() instead.
124 * Returns: a double. It can be NaN or +-Infinity, but not -0.0
128 * SWFDEC_AS_VALUE_GET_STRING:
129 * @val: value to get, the value must reference a string
131 * Gets the string associated with @val. If you are not sure that the value is
132 * a string value, consider using swfdec_as_value_to_string() instead.
134 * Returns: a garbage-collected string.
138 * SWFDEC_AS_VALUE_SET_STRING:
140 * @s: garbage-collected string to use
142 * Sets @val to the given string value.
146 * SWFDEC_AS_VALUE_SET_NULL:
149 * Sets @val to the special null value.
153 * SWFDEC_AS_VALUE_GET_OBJECT:
154 * @val: value to get, the value must reference an object
156 * Gets the object associated with @val. If you are not sure that the value is
157 * an object value, consider using swfdec_as_value_to_object() instead.
159 * Returns: a #SwfdecAsObject
163 * SWFDEC_AS_VALUE_SET_OBJECT:
165 * @o: garbage-collected #SwfdecAsObject to use
167 * Sets @val to the given object. The object must have been added to the
168 * garbage collector via swfdec_as_object_add() previously.
171 /*** actual code ***/
174 * swfdec_as_value_set_int:
176 * @i: integer value to set
178 * Creates a garbage-collected value representing @i and returns it.
179 * Sets @val to the given value. Currently this function is a macro that calls
180 * swfdec_as_value_set_number(), but this may change in future versions of
183 * Returns: The new value representing @i
187 * swfdec_as_value_set_number:
188 * @context: The context to use
189 * @number: double value to set
191 * Creates a garbage-collected value representing @number and returns it.
193 * Returns: The new value representing @number
196 swfdec_as_value_from_number (SwfdecAsContext
*context
,
199 SwfdecAsDoubleValue
*dval
;
201 g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context
), SWFDEC_AS_VALUE_UNDEFINED
);
203 dval
= swfdec_as_gcable_new (context
, SwfdecAsDoubleValue
);
205 SWFDEC_AS_GCABLE_SET_NEXT (dval
, context
->numbers
);
206 context
->numbers
= dval
;
208 return SWFDEC_AS_VALUE_COMBINE (dval
, SWFDEC_AS_TYPE_NUMBER
);
212 * swfdec_as_str_concat:
213 * @cx: a #SwfdecAsContext
217 * Convenience function to concatenate two garbage-collected strings. This
218 * function is equivalent to g_strconcat ().
220 * Returns: A new garbage-collected string
223 swfdec_as_str_concat (SwfdecAsContext
*cx
, const char * s1
, const char *s2
)
228 g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (cx
), SWFDEC_AS_STR_EMPTY
);
229 g_return_val_if_fail (s1
, SWFDEC_AS_STR_EMPTY
);
230 g_return_val_if_fail (s2
, SWFDEC_AS_STR_EMPTY
);
232 s
= g_strconcat (s1
, s2
, NULL
);
233 ret
= swfdec_as_context_get_string (cx
, s
);
240 * swfdec_as_integer_to_string:
241 * @context: a #SwfdecAsContext
242 * @i: an integer that fits into 32 bits
244 * Converts @d into a string using the same conversion algorithm as the
245 * official Flash player.
247 * Returns: a garbage-collected string
250 swfdec_as_integer_to_string (SwfdecAsContext
*context
, int i
)
252 return swfdec_as_context_give_string (context
, g_strdup_printf ("%d", i
));
256 * swfdec_as_double_to_string:
257 * @context: a #SwfdecAsContext
260 * Converts @d into a string using the same conversion algorithm as the
261 * official Flash player.
263 * Returns: a garbage-collected string
265 /* FIXME: this function is still buggy - and it's ugly as hell.
266 * Someone with the right expertise should rewrite it
268 * http://www.cs.indiana.edu/~burger/FP-Printing-PLDI96.pdf
269 * http://lxr.mozilla.org/mozilla/source/js/tamarin/core/MathUtils.cpp
272 swfdec_as_double_to_string (SwfdecAsContext
*context
, double d
)
274 gboolean found
= FALSE
, gotdot
= FALSE
;
276 char tmp
[50], *end
, *start
, *s
;
278 g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context
), SWFDEC_AS_STR_EMPTY
);
281 return SWFDEC_AS_STR_NaN
;
283 return d
< 0 ? SWFDEC_AS_STR__Infinity
: SWFDEC_AS_STR_Infinity
;
286 return SWFDEC_AS_STR_0
;
290 if (ABS (d
) > 0.00001 && ABS (d
) < 1e+15) {
291 g_ascii_formatd (s
, 50, "%.22f", d
);
293 g_ascii_formatd (s
, 50, "%.25e", d
);
299 /* count digits (maximum allowed is 15) */
306 if (*start
< '0' || *start
> '9')
308 if (found
|| *start
!= '0') {
315 /* go to end of string */
316 while (*end
!= 'e' && *end
!= 0)
318 /* round using the next digit */
319 if (*start
>= '5' && *start
<= '9') {
321 /* skip all 9s at the end */
322 while (start
[-1] == '9')
324 /* if we're before the dot, replace 9s with 0s */
325 if (start
[-1] == '.') {
329 while (start
[-1] == '9') {
333 /* write out correct number */
334 if (start
[-1] == '-') {
338 } else if (start
[-1] == ' ') {
344 /* reposition cursor at end */
348 /* remove trailing zeros (note we skipped zero above, so there will be non-0 bytes left) */
350 while (start
[-1] == '0')
352 if (start
[-1] == '.')
370 return swfdec_as_context_get_string (context
, s
);
374 * swfdec_as_value_to_string:
375 * @context: a #SwfdecAsContext
376 * @value: value to be expressed as string
378 * Converts @value to a string according to the rules of Flash. This might
379 * cause calling back into the script engine if the @value is an object. In
380 * that case, the object's valueOf function is called.
381 * <warning>Never use this function for debugging purposes.</warning>
383 * Returns: a garbage-collected string representing @value. The value will
387 swfdec_as_value_to_string (SwfdecAsContext
*context
, SwfdecAsValue value
)
389 g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context
), SWFDEC_AS_STR_EMPTY
);
391 switch (SWFDEC_AS_VALUE_GET_TYPE (value
)) {
392 case SWFDEC_AS_TYPE_STRING
:
393 return SWFDEC_AS_VALUE_GET_STRING (value
);
394 case SWFDEC_AS_TYPE_UNDEFINED
:
395 if (context
->version
> 6)
396 return SWFDEC_AS_STR_undefined
;
398 return SWFDEC_AS_STR_EMPTY
;
399 case SWFDEC_AS_TYPE_BOOLEAN
:
400 return SWFDEC_AS_VALUE_GET_BOOLEAN (value
) ? SWFDEC_AS_STR_true
: SWFDEC_AS_STR_false
;
401 case SWFDEC_AS_TYPE_NULL
:
402 return SWFDEC_AS_STR_null
;
403 case SWFDEC_AS_TYPE_NUMBER
:
404 return swfdec_as_double_to_string (context
, SWFDEC_AS_VALUE_GET_NUMBER (value
));
405 case SWFDEC_AS_TYPE_OBJECT
:
407 SwfdecAsObject
*object
= SWFDEC_AS_VALUE_GET_OBJECT (value
);
408 if (SWFDEC_IS_AS_STRING (object
->relay
)) {
409 return SWFDEC_AS_STRING (object
->relay
)->string
;
412 swfdec_as_object_call (object
, SWFDEC_AS_STR_toString
, 0, NULL
, &ret
);
413 if (SWFDEC_AS_VALUE_IS_STRING (ret
))
414 return SWFDEC_AS_VALUE_GET_STRING (ret
);
415 else if (SWFDEC_IS_AS_SUPER (object
->relay
))
416 return SWFDEC_AS_STR__type_Object_
;
417 else if (SWFDEC_IS_AS_FUNCTION (object
->relay
))
418 return SWFDEC_AS_STR__type_Function_
;
420 return SWFDEC_AS_STR__type_Object_
;
423 case SWFDEC_AS_TYPE_MOVIE
:
425 SwfdecMovie
*movie
= SWFDEC_AS_VALUE_GET_MOVIE (value
);
429 return SWFDEC_AS_STR_EMPTY
;
430 str
= swfdec_movie_get_path (movie
, TRUE
);
431 return swfdec_as_context_give_string (context
, str
);
433 case SWFDEC_AS_TYPE_INT
:
435 g_assert_not_reached ();
436 return SWFDEC_AS_STR_EMPTY
;
441 * swfdec_as_value_to_number:
442 * @context: a #SwfdecAsContext
443 * @value: a #SwfdecAsValue used by context
445 * Converts the value to a number according to Flash's conversion routines and
446 * the current Flash version. This conversion routine is similar, but not equal
447 * to ECMAScript. For objects, it can call back into the script engine by
448 * calling the object's valueOf function.
450 * Returns: a double value. It can be NaN or +-Infinity. It will not be -0.0.
453 swfdec_as_value_to_number (SwfdecAsContext
*context
, SwfdecAsValue value
)
455 g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context
), 0.0);
457 value
= swfdec_as_value_to_primitive (value
);
459 switch (SWFDEC_AS_VALUE_GET_TYPE (value
)) {
460 case SWFDEC_AS_TYPE_UNDEFINED
:
461 case SWFDEC_AS_TYPE_NULL
:
462 return (context
->version
>= 7) ? NAN
: 0.0;
463 case SWFDEC_AS_TYPE_BOOLEAN
:
464 return SWFDEC_AS_VALUE_GET_BOOLEAN (value
) ? 1 : 0;
465 case SWFDEC_AS_TYPE_NUMBER
:
466 return SWFDEC_AS_VALUE_GET_NUMBER (value
);
467 case SWFDEC_AS_TYPE_STRING
:
473 // FIXME: We should most likely copy Tamarin's code here (MathUtils.cpp)
474 s
= SWFDEC_AS_VALUE_GET_STRING (value
);
475 if (s
== SWFDEC_AS_STR_EMPTY
)
476 return (context
->version
>= 5) ? NAN
: 0.0;
477 if (context
->version
> 5 && s
[0] == '0' &&
478 (s
[1] == 'x' || s
[1] == 'X')) {
479 d
= g_ascii_strtoll (s
+ 2, &end
, 16);
480 } else if (context
->version
> 5 &&
481 (s
[0] == '0' || ((s
[0] == '+' || s
[0] == '-') && s
[1] == '0')) &&
482 s
[strspn (s
+1, "01234567")+1] == '\0') {
483 d
= g_ascii_strtoll (s
, &end
, 8);
485 if (strpbrk (s
, "xXiI") != NULL
)
486 return (context
->version
>= 5) ? NAN
: 0.0;
487 d
= g_ascii_strtod (s
, &end
);
489 if (*end
== '\0' || context
->version
< 5)
490 return d
== -0.0 ? 0.0 : d
;
494 case SWFDEC_AS_TYPE_OBJECT
:
495 case SWFDEC_AS_TYPE_MOVIE
:
496 return (context
->version
>= 5) ? NAN
: 0.0;
497 case SWFDEC_AS_TYPE_INT
:
499 g_assert_not_reached ();
505 * swfdec_as_double_to_integer:
508 * Converts the given double to an integer using the same rules as the Flash
511 * Returns: an integer
514 swfdec_as_double_to_integer (double d
)
519 d
= fmod (-d
, 4294967296.0);
522 d
= fmod (d
, 4294967296.0);
528 * swfdec_as_value_to_integer:
529 * @context: a #SwfdecAsContext
530 * @value: value to convert
532 * Converts the given value to an integer. This is done similar to the
533 * conversion used by swfdec_as_value_to_number().
535 * Returns: An Integer that can be represented in 32 bits.
538 swfdec_as_value_to_integer (SwfdecAsContext
*context
, SwfdecAsValue value
)
542 d
= swfdec_as_value_to_number (context
, value
);
543 return swfdec_as_double_to_integer (d
);
547 * swfdec_as_value_to_object:
548 * @context: a #SwfdecAsContext
549 * @value: value to convert
551 * Converts a given value to its representation as an object. The object
552 * representation for primitive types is a wrapper object of the corresponding
553 * class (Number for numbers, String for strings, Boolean for bools). If the
554 * value does not have an object representing it, such as undefined and null
555 * values, %NULL is returned.
557 * Returns: object representing @value or %NULL.
560 swfdec_as_value_to_object (SwfdecAsContext
*context
, SwfdecAsValue value
)
562 SwfdecAsFunction
*fun
;
566 g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context
), NULL
);
568 switch (SWFDEC_AS_VALUE_GET_TYPE (value
)) {
569 case SWFDEC_AS_TYPE_UNDEFINED
:
570 case SWFDEC_AS_TYPE_NULL
:
572 case SWFDEC_AS_TYPE_NUMBER
:
573 s
= SWFDEC_AS_STR_Number
;
575 case SWFDEC_AS_TYPE_STRING
:
576 s
= SWFDEC_AS_STR_String
;
578 case SWFDEC_AS_TYPE_BOOLEAN
:
579 s
= SWFDEC_AS_STR_Boolean
;
581 case SWFDEC_AS_TYPE_OBJECT
:
582 case SWFDEC_AS_TYPE_MOVIE
:
583 return SWFDEC_AS_VALUE_GET_COMPOSITE (value
);
584 case SWFDEC_AS_TYPE_INT
:
586 g_assert_not_reached ();
590 swfdec_as_object_get_variable (context
->global
, s
, &val
);
591 if (!SWFDEC_AS_VALUE_IS_OBJECT (val
) ||
592 !SWFDEC_IS_AS_FUNCTION (fun
= (SwfdecAsFunction
*) (SWFDEC_AS_VALUE_GET_OBJECT (val
)->relay
)))
594 swfdec_as_object_create (fun
, 1, &value
, &val
);
595 if (SWFDEC_AS_VALUE_IS_OBJECT (val
)) {
596 return SWFDEC_AS_VALUE_GET_OBJECT (val
);
598 SWFDEC_ERROR ("did not construct an object");
604 * swfdec_as_value_to_boolean:
605 * @context: a #SwfdecAsContext
606 * @value: value to convert
608 * Converts the given value to a boolean according to Flash's rules. Note that
609 * these rules changed significantly for strings between Flash 6 and 7.
611 * Returns: either %TRUE or %FALSE.
614 swfdec_as_value_to_boolean (SwfdecAsContext
*context
, SwfdecAsValue value
)
616 g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context
), FALSE
);
618 /* FIXME: what do we do when called in flash 4? */
619 switch (SWFDEC_AS_VALUE_GET_TYPE (value
)) {
620 case SWFDEC_AS_TYPE_UNDEFINED
:
621 case SWFDEC_AS_TYPE_NULL
:
623 case SWFDEC_AS_TYPE_BOOLEAN
:
624 return SWFDEC_AS_VALUE_GET_BOOLEAN (value
);
625 case SWFDEC_AS_TYPE_NUMBER
:
627 double d
= SWFDEC_AS_VALUE_GET_NUMBER (value
);
628 return d
!= 0.0 && !isnan (d
);
630 case SWFDEC_AS_TYPE_STRING
:
631 if (context
->version
<= 6) {
632 double d
= swfdec_as_value_to_number (context
, value
);
633 return d
!= 0.0 && !isnan (d
);
635 return SWFDEC_AS_VALUE_GET_STRING (value
) != SWFDEC_AS_STR_EMPTY
;
637 case SWFDEC_AS_TYPE_OBJECT
:
638 case SWFDEC_AS_TYPE_MOVIE
:
640 case SWFDEC_AS_TYPE_INT
:
642 g_assert_not_reached ();
648 * swfdec_as_value_to_primitive:
649 * @value: value to convert
651 * Tries to convert the given @value inline to its primitive value. Primitive
652 * values are values that are not objects. If the value is an object, the
653 * object's valueOf function is called. If the result of that function is still
654 * an object, it is returned nonetheless.
656 * Returns: The primitive value for &value
659 swfdec_as_value_to_primitive (SwfdecAsValue value
)
662 if (SWFDEC_AS_VALUE_IS_OBJECT (value
)) {
663 swfdec_as_object_call (SWFDEC_AS_VALUE_GET_OBJECT (value
), SWFDEC_AS_STR_valueOf
,
670 * swfdec_as_value_get_variable:
672 * @value: the value to get the variable from
673 * @name: name of the variable to get
674 * @ret: The return value to set. May be identical to the passed in @value.
676 * Gets a variable from the given @value. This function is a shortcut for
677 * converting to a #SwfdecAsObject and then calling
678 * swfdec_As_object_get_variable(). When the @value cannot be converted to an
679 * object, @ret is set to undefined.
682 swfdec_as_value_get_variable (SwfdecAsContext
*cx
, const SwfdecAsValue
*value
,
683 const char *name
, SwfdecAsValue
*ret
)
685 SwfdecAsObject
*object
;
687 g_return_if_fail (SWFDEC_IS_AS_CONTEXT (cx
));
688 g_return_if_fail (value
!= NULL
);
689 g_return_if_fail (name
!= NULL
);
690 g_return_if_fail (ret
!= NULL
);
692 object
= swfdec_as_value_to_object (cx
, *value
);
693 if (object
== NULL
) {
694 SWFDEC_AS_VALUE_SET_UNDEFINED (ret
);
697 swfdec_as_object_get_variable (object
, name
, ret
);
700 /* from swfdec_internal.h */
702 swfdec_as_value_to_twips (SwfdecAsContext
*context
, const SwfdecAsValue
*val
,
703 gboolean is_length
, SwfdecTwips
*result
)
707 g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context
), FALSE
);
708 g_return_val_if_fail (val
!= NULL
, FALSE
);
709 g_return_val_if_fail (result
!= NULL
, FALSE
);
711 if (SWFDEC_AS_VALUE_IS_UNDEFINED (*val
) || SWFDEC_AS_VALUE_IS_NULL (*val
))
714 d
= swfdec_as_value_to_number (context
, *val
);
717 if (is_length
&& d
< 0)
720 d
*= SWFDEC_TWIPS_SCALE_FACTOR
;
721 *result
= swfdec_as_double_to_integer (d
);
723 *result
= ABS (*result
);