bump libtool version
[swfdec.git] / libswfdec / swfdec_as_object.c
blob0ff384b89e8ec7e900bdb5c467b081f44b543c6c
1 /* Swfdec
2 * Copyright (C) 2007 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.
8 *
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
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
24 #include <string.h>
26 #include "swfdec_as_object.h"
27 #include "swfdec_as_context.h"
28 #include "swfdec_as_frame_internal.h"
29 #include "swfdec_as_internal.h"
30 #include "swfdec_as_native_function.h"
31 #include "swfdec_as_stack.h"
32 #include "swfdec_as_string.h"
33 #include "swfdec_as_strings.h"
34 #include "swfdec_debug.h"
35 #include "swfdec_movie.h"
37 #define SWFDEC_AS_OBJECT_PROTOTYPE_RECURSION_LIMIT 256
39 /**
40 * SECTION:SwfdecAsObject
41 * @title: SwfdecAsObject
42 * @short_description: the base object type for scriptable objects
44 * This is the basic object type in Swfdec. Every object used by the script
45 * engine must be a #SwfdecAsObject. It handles memory management and assigning
46 * variables to it. Almost all functions that are called on objects require that
47 * the objects have been added to the garbage collector previously. For
48 * custom-created objects, you need to do this using swfdec_as_object_add(),
49 * built-in functions that create objects do this manually.
51 * Note that you cannot know the lifetime of a #SwfdecAsObject, since scripts
52 * may assign it as a variable to other objects. So you should not assume to
53 * know when an object gets removed.
56 /**
57 * SwfdecAsObject:
59 * Every object value inside the Swfdec script engine must be a SwfdecAsObject.
60 * If you want to add custom objects to your script engine, you need to create a
61 * subclass. The class provides a number of virtual functions that you can
62 * override to achieve the desired behaviour.
65 /**
66 * SwfdecAsVariableFlag:
67 * @SWFDEC_AS_VARIABLE_HIDDEN: Do not include variable in enumerations and
68 * swfdec_as_object_foreach().
69 * @SWFDEC_AS_VARIABLE_PERMANENT: Do not all swfdec_as_object_delete_variable()
70 * to delete this variable.
71 * @SWFDEC_AS_VARIABLE_CONSTANT: Do not allow changing the value with
72 * swfdec_as_object_set_variable().
73 * @SWFDEC_AS_VARIABLE_VERSION_6_UP: This symbol is only visible in version 6
74 * and above.
75 * @SWFDEC_AS_VARIABLE_VERSION_NOT_6: This symbols is visible in all versions
76 * but version 6.
77 * @SWFDEC_AS_VARIABLE_VERSION_7_UP: This symbol is only visible in version 7
78 * and above.
79 * @SWFDEC_AS_VARIABLE_VERSION_8_UP: This symbol is only visible in version 8
80 * and above.
82 * These flags are used to describe various properties of a variable inside
83 * Swfdec. You can manually set them with swfdec_as_object_set_variable_flags().
86 /**
87 * SwfdecAsDeleteReturn:
88 * @SWFDEC_AS_DELETE_NOT_FOUND: The variable was not found and therefore
89 * couldn't be deleted.
90 * @SWFDEC_AS_DELETE_DELETED: The variable was deleted.
91 * @SWFDEC_AS_DELETE_NOT_DELETED: The variable was found but could not be
92 * deleted.
94 * This is the return value used by swfdec_as_object_delete_variable(). It
95 * describes the various outcomes of trying to delete a variable.
98 /**
99 * SwfdecAsVariableForeach:
100 * @object: The object this function is run on
101 * @variable: garbage-collected name of the current variables
102 * @value: value of the current variable
103 * @flags: Flags associated with the current variable
104 * @data: User dta passed to swfdec_as_object_foreach()
106 * Function prototype for the swfdec_as_object_foreach() function.
108 * Returns: %TRUE to continue running the foreach function, %FALSE to stop
111 typedef struct _SwfdecAsVariable SwfdecAsVariable;
112 struct _SwfdecAsVariable {
113 guint flags; /* SwfdecAsVariableFlag values */
114 SwfdecAsValue value; /* value of property */
115 SwfdecAsFunction * get; /* getter set with swfdec_as_object_add_property */
116 SwfdecAsFunction * set; /* setter or %NULL */
119 typedef struct {
120 SwfdecAsFunction * watch; /* watcher or %NULL */
121 SwfdecAsValue watch_data; /* user data to watcher */
122 guint refcount; /* refcount - misused for recursion detection */
123 } SwfdecAsWatch;
125 G_DEFINE_TYPE (SwfdecAsObject, swfdec_as_object, G_TYPE_OBJECT)
127 static void
128 swfdec_as_object_dispose (GObject *gobject)
130 SwfdecAsObject *object = SWFDEC_AS_OBJECT (gobject);
132 g_assert (object->properties == NULL);
134 G_OBJECT_CLASS (swfdec_as_object_parent_class)->dispose (gobject);
137 static void
138 swfdec_as_object_mark_property (gpointer key, gpointer value, gpointer unused)
140 SwfdecAsVariable *var = value;
142 swfdec_as_string_mark (key);
143 if (var->get) {
144 swfdec_as_object_mark (SWFDEC_AS_OBJECT (var->get));
145 if (var->set)
146 swfdec_as_object_mark (SWFDEC_AS_OBJECT (var->set));
147 } else {
148 swfdec_as_value_mark (&var->value);
152 static void
153 swfdec_as_object_mark_watch (gpointer key, gpointer value, gpointer unused)
155 SwfdecAsWatch *watch = value;
157 swfdec_as_string_mark (key);
158 swfdec_as_object_mark (SWFDEC_AS_OBJECT (watch->watch));
159 swfdec_as_value_mark (&watch->watch_data);
162 static void
163 swfdec_as_object_do_mark (SwfdecAsObject *object)
165 if (object->prototype)
166 swfdec_as_object_mark (object->prototype);
167 g_hash_table_foreach (object->properties, swfdec_as_object_mark_property, NULL);
168 if (object->watches)
169 g_hash_table_foreach (object->watches, swfdec_as_object_mark_watch, NULL);
172 static void
173 swfdec_as_object_do_add (SwfdecAsObject *object)
177 static gboolean
178 swfdec_as_object_lookup_case_insensitive (gpointer key, gpointer value, gpointer user_data)
180 return g_ascii_strcasecmp (key, user_data) == 0;
183 static gboolean
184 swfdec_as_variable_name_is_valid (const char *name)
186 return name != SWFDEC_AS_STR_EMPTY;
189 static inline SwfdecAsVariable *
190 swfdec_as_object_hash_lookup (SwfdecAsObject *object, const char *variable)
192 SwfdecAsVariable *var = g_hash_table_lookup (object->properties, variable);
194 if (var || object->context->version >= 7)
195 return var;
196 var = g_hash_table_find (object->properties, swfdec_as_object_lookup_case_insensitive, (gpointer) variable);
197 return var;
200 static inline SwfdecAsVariable *
201 swfdec_as_object_hash_create (SwfdecAsObject *object, const char *variable, guint flags)
203 SwfdecAsVariable *var;
205 if (!swfdec_as_context_use_mem (object->context, sizeof (SwfdecAsVariable)))
206 return NULL;
207 if (!swfdec_as_variable_name_is_valid (variable))
208 return NULL;
209 var = g_slice_new0 (SwfdecAsVariable);
210 var->flags = flags;
211 g_hash_table_insert (object->properties, (gpointer) variable, var);
213 return var;
216 static gboolean
217 swfdec_as_object_do_get (SwfdecAsObject *object, SwfdecAsObject *orig,
218 const char *variable, SwfdecAsValue *val, guint *flags)
220 SwfdecAsVariable *var = swfdec_as_object_hash_lookup (object, variable);
222 if (var == NULL)
223 return FALSE;
225 /* variable flag checks */
226 if (var->flags & SWFDEC_AS_VARIABLE_VERSION_6_UP && object->context->version < 6)
227 return FALSE;
228 if (var->flags & SWFDEC_AS_VARIABLE_VERSION_NOT_6 && object->context->version == 6)
229 return FALSE;
230 if (var->flags & SWFDEC_AS_VARIABLE_VERSION_7_UP && object->context->version < 7)
231 return FALSE;
232 if (var->flags & SWFDEC_AS_VARIABLE_VERSION_8_UP && object->context->version < 8)
233 return FALSE;
235 if (var->get) {
236 swfdec_as_function_call (var->get, orig, 0, NULL, val);
237 swfdec_as_context_run (object->context);
238 *flags = var->flags;
239 } else {
240 *val = var->value;
241 *flags = var->flags;
243 return TRUE;
246 static SwfdecAsWatch *
247 swfdec_as_watch_new (SwfdecAsFunction *function)
249 SwfdecAsWatch *watch;
251 if (!swfdec_as_context_use_mem (SWFDEC_AS_OBJECT (function)->context,
252 sizeof (SwfdecAsWatch)))
253 return NULL;
255 watch = g_slice_new (SwfdecAsWatch);
256 watch->refcount = 1;
257 watch->watch = function;
258 SWFDEC_AS_VALUE_SET_UNDEFINED (&watch->watch_data);
259 return watch;
262 static inline gboolean
263 swfdec_as_watch_can_recurse (SwfdecAsWatch *watch)
265 guint version;
267 version = SWFDEC_AS_OBJECT (watch->watch)->context->version;
268 if (version <= 6) {
269 return watch->refcount <= 1;
270 } else {
271 return watch->refcount <= 64 + 1;
275 static inline void
276 swfdec_as_watch_ref (SwfdecAsWatch *watch)
278 watch->refcount++;
281 static inline void
282 swfdec_as_watch_unref (SwfdecAsWatch *watch)
284 watch->refcount--;
285 if (watch->refcount == 0) {
286 swfdec_as_context_unuse_mem (SWFDEC_AS_OBJECT (watch->watch)->context,
287 sizeof (SwfdecAsWatch));
288 g_slice_free (SwfdecAsWatch, watch);
292 SwfdecAsObject *
293 swfdec_as_object_prototype_for_version (SwfdecAsObject *object, guint version,
294 gboolean check7)
296 if (object->prototype == NULL)
297 return NULL;
299 if (object->prototype_flags & SWFDEC_AS_VARIABLE_VERSION_6_UP && version < 6)
300 return NULL;
301 // don't check for NOT_6 flag
302 if (object->prototype_flags & SWFDEC_AS_VARIABLE_VERSION_7_UP && version < 7)
303 return NULL;
304 // only check 8_UP for version < 6
305 if (object->prototype_flags & SWFDEC_AS_VARIABLE_VERSION_8_UP &&
306 version < 6)
307 return NULL;
308 if (check7) {
309 if (object->prototype_flags & SWFDEC_AS_VARIABLE_VERSION_8_UP && version == 7)
310 return NULL;
313 return object->prototype;
316 static void
317 swfdec_as_object_do_set (SwfdecAsObject *object, const char *variable,
318 const SwfdecAsValue *val, guint flags)
320 SwfdecAsVariable *var;
321 SwfdecAsWatch *watch;
323 if (!swfdec_as_variable_name_is_valid (variable))
324 return;
326 var = swfdec_as_object_hash_lookup (object, variable);
327 if (var == NULL && variable != SWFDEC_AS_STR___proto__) {
328 guint i;
329 SwfdecAsObject *proto;
331 proto = swfdec_as_object_prototype_for_version (object,
332 object->context->version, FALSE);
334 for (i = 0; i < SWFDEC_AS_OBJECT_PROTOTYPE_RECURSION_LIMIT && proto; i++) {
335 var = swfdec_as_object_hash_lookup (proto, variable);
336 if (var && var->get)
337 break;
338 proto = swfdec_as_object_prototype_for_version (proto,
339 proto->context->version, FALSE);
340 var = NULL;
342 if (i == SWFDEC_AS_OBJECT_PROTOTYPE_RECURSION_LIMIT) {
343 swfdec_as_context_abort (object->context, "Prototype recursion limit exceeded");
344 return;
347 if (var == NULL) {
348 var = swfdec_as_object_hash_create (object, variable, flags);
349 if (var == NULL)
350 return;
351 } else {
352 if (var->flags & SWFDEC_AS_VARIABLE_CONSTANT)
353 return;
354 // remove the flags that could make this variable hidden
355 if (object->context->version == 6) {
356 // version 6, so let's forget SWFDEC_AS_VARIABLE_VERSION_7_UP flag, oops!
357 // we will still set the value though, even if that flag is set
358 var->flags &= ~(SWFDEC_AS_VARIABLE_VERSION_6_UP |
359 SWFDEC_AS_VARIABLE_VERSION_NOT_6 | SWFDEC_AS_VARIABLE_VERSION_8_UP);
360 } else {
361 var->flags &= ~(SWFDEC_AS_VARIABLE_VERSION_6_UP |
362 SWFDEC_AS_VARIABLE_VERSION_NOT_6 | SWFDEC_AS_VARIABLE_VERSION_7_UP |
363 SWFDEC_AS_VARIABLE_VERSION_8_UP);
366 if (object->watches) {
367 SwfdecAsValue ret = *val;
368 watch = g_hash_table_lookup (object->watches, variable);
369 /* FIXME: figure out if this limit here is correct. Add a watch in Flash 7
370 * and set a variable using Flash 6 */
371 if (watch && swfdec_as_watch_can_recurse (watch)) {
372 SwfdecAsValue args[4];
373 SWFDEC_AS_VALUE_SET_STRING (&args[0], variable);
374 args[1] = var->value;
375 args[2] = *val;
376 args[3] = watch->watch_data;
377 swfdec_as_watch_ref (watch);
378 swfdec_as_function_call (watch->watch, object, 4, args, &ret);
379 swfdec_as_context_run (object->context);
380 swfdec_as_watch_unref (watch);
381 var = swfdec_as_object_hash_lookup (object, variable);
382 if (var == NULL)
383 return;
386 var->value = ret;
387 } else {
388 watch = NULL;
390 if (var->get) {
391 if (var->set) {
392 SwfdecAsValue tmp;
393 swfdec_as_function_call (var->set, object, 1, val, &tmp);
394 swfdec_as_context_run (object->context);
396 } else if (watch == NULL) {
397 var->value = *val;
400 if (variable == SWFDEC_AS_STR___proto__) {
401 if (SWFDEC_AS_VALUE_IS_OBJECT (val) &&
402 !SWFDEC_IS_MOVIE (SWFDEC_AS_VALUE_GET_OBJECT (val))) {
403 object->prototype = SWFDEC_AS_VALUE_GET_OBJECT (val);
404 object->prototype_flags = var->flags;
405 } else {
406 object->prototype = NULL;
407 object->prototype_flags = 0;
412 static void
413 swfdec_as_object_do_set_flags (SwfdecAsObject *object, const char *variable, guint flags, guint mask)
415 SwfdecAsVariable *var = swfdec_as_object_hash_lookup (object, variable);
417 if (var) {
418 var->flags = (var->flags & ~mask) | flags;
420 if (variable == SWFDEC_AS_STR___proto__)
421 object->prototype_flags = var->flags;
425 static void
426 swfdec_as_object_free_property (gpointer key, gpointer value, gpointer data)
428 SwfdecAsObject *object = data;
430 swfdec_as_context_unuse_mem (object->context, sizeof (SwfdecAsVariable));
431 g_slice_free (SwfdecAsVariable, value);
434 static SwfdecAsDeleteReturn
435 swfdec_as_object_do_delete (SwfdecAsObject *object, const char *variable)
437 SwfdecAsVariable *var;
439 var = g_hash_table_lookup (object->properties, variable);
440 if (var == NULL)
441 return SWFDEC_AS_DELETE_NOT_FOUND;
442 if (var->flags & SWFDEC_AS_VARIABLE_PERMANENT)
443 return SWFDEC_AS_DELETE_NOT_DELETED;
445 if (variable == SWFDEC_AS_STR___proto__ && object->context->version <= 6) {
446 object->prototype = NULL;
447 object->prototype_flags = 0;
449 swfdec_as_object_free_property (NULL, var, object);
450 if (!g_hash_table_remove (object->properties, variable)) {
451 g_assert_not_reached ();
453 return SWFDEC_AS_DELETE_DELETED;
456 typedef struct {
457 SwfdecAsObject * object;
458 SwfdecAsVariableForeach func;
459 gpointer data;
460 gboolean retval;
461 } ForeachData;
463 static void
464 swfdec_as_object_hash_foreach (gpointer key, gpointer value, gpointer data)
466 ForeachData *fdata = data;
467 SwfdecAsVariable *var = value;
469 if (!fdata->retval)
470 return;
472 fdata->retval = fdata->func (fdata->object, key, &var->value, var->flags, fdata->data);
475 /* FIXME: does not do Adobe Flash's order for Enumerate actions */
476 static gboolean
477 swfdec_as_object_do_foreach (SwfdecAsObject *object, SwfdecAsVariableForeach func, gpointer data)
479 ForeachData fdata = { object, func, data, TRUE };
481 g_hash_table_foreach (object->properties, swfdec_as_object_hash_foreach, &fdata);
482 return fdata.retval;
485 typedef struct {
486 SwfdecAsObject * object;
487 SwfdecAsVariableForeachRemove func;
488 gpointer data;
489 } ForeachRemoveData;
491 static gboolean
492 swfdec_as_object_hash_foreach_remove (gpointer key, gpointer value, gpointer data)
494 ForeachRemoveData *fdata = data;
495 SwfdecAsVariable *var = value;
497 if (!fdata->func (fdata->object, key, &var->value, var->flags, fdata->data))
498 return FALSE;
500 swfdec_as_object_free_property (NULL, var, fdata->object);
501 return TRUE;
505 * swfdec_as_object_foreach_remove:
506 * @object: a #SwfdecAsObject
507 * @func: function that determines which object to remove
508 * @data: data to pass to @func
510 * Removes all variables form @object where @func returns %TRUE. This is an
511 * internal function for array operations.
513 * Returns: he number of variables removed
515 guint
516 swfdec_as_object_foreach_remove (SwfdecAsObject *object, SwfdecAsVariableForeach func,
517 gpointer data)
519 ForeachRemoveData fdata = { object, func, data };
521 g_return_val_if_fail (SWFDEC_IS_AS_OBJECT (object), 0);
522 g_return_val_if_fail (func != NULL, 0);
524 return g_hash_table_foreach_remove (object->properties,
525 swfdec_as_object_hash_foreach_remove, &fdata);
528 typedef struct {
529 SwfdecAsObject * object;
530 GHashTable * properties_new;
531 SwfdecAsVariableForeachRename func;
532 gpointer data;
533 } ForeachRenameData;
535 static gboolean
536 swfdec_as_object_hash_foreach_rename (gpointer key, gpointer value, gpointer data)
538 ForeachRenameData *fdata = data;
539 SwfdecAsVariable *var = value;
540 const char *key_new;
542 key_new = fdata->func (fdata->object, key, &var->value, var->flags, fdata->data);
543 if (key_new) {
544 g_hash_table_insert (fdata->properties_new, (gpointer) key_new, var);
545 } else {
546 swfdec_as_object_free_property (NULL, var, fdata->object);
549 return TRUE;
553 * swfdec_as_object_foreach_rename:
554 * @object: a #SwfdecAsObject
555 * @func: function determining the new name
556 * @data: data to pass to @func
558 * Calls @func for each variable of @object. The function is then supposed
559 * to return the new name of the variable or %NULL if the variable should be
560 * removed. This is an internal function for array operations.
562 void
563 swfdec_as_object_foreach_rename (SwfdecAsObject *object, SwfdecAsVariableForeachRename func,
564 gpointer data)
566 ForeachRenameData fdata = { object, NULL, func, data };
568 g_return_if_fail (SWFDEC_IS_AS_OBJECT (object));
569 g_return_if_fail (func != NULL);
571 fdata.properties_new = g_hash_table_new (g_direct_hash, g_direct_equal);
572 g_hash_table_foreach_remove (object->properties, swfdec_as_object_hash_foreach_rename, &fdata);
573 g_hash_table_destroy (object->properties);
574 object->properties = fdata.properties_new;
577 static char *
578 swfdec_as_object_do_debug (SwfdecAsObject *object)
580 if (G_OBJECT_TYPE (object) != SWFDEC_TYPE_AS_OBJECT)
581 return g_strdup (G_OBJECT_TYPE_NAME (object));
583 return g_strdup ("Object");
586 static void
587 swfdec_as_object_class_init (SwfdecAsObjectClass *klass)
589 GObjectClass *object_class = G_OBJECT_CLASS (klass);
591 object_class->dispose = swfdec_as_object_dispose;
593 klass->mark = swfdec_as_object_do_mark;
594 klass->add = swfdec_as_object_do_add;
595 klass->get = swfdec_as_object_do_get;
596 klass->set = swfdec_as_object_do_set;
597 klass->set_flags = swfdec_as_object_do_set_flags;
598 klass->del = swfdec_as_object_do_delete;
599 klass->foreach = swfdec_as_object_do_foreach;
600 klass->debug = swfdec_as_object_do_debug;
603 static void
604 swfdec_as_object_init (SwfdecAsObject *object)
609 * swfdec_as_object_new_empty:
610 * @context: a #SwfdecAsContext
612 * Creates an empty object. The prototype and constructor properties of the
613 * returned object will not be set. You probably want to call
614 * swfdec_as_object_set_constructor() on the returned object yourself.
615 * You may want to use swfdec_as_object_new() instead.
617 * Returns: A new #SwfdecAsObject adde to @context or %NULL on OOM.
619 SwfdecAsObject *
620 swfdec_as_object_new_empty (SwfdecAsContext *context)
622 SwfdecAsObject *object;
624 g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context), NULL);
626 if (!swfdec_as_context_use_mem (context, sizeof (SwfdecAsObject)))
627 return NULL;
628 object = g_object_new (SWFDEC_TYPE_AS_OBJECT, NULL);
629 swfdec_as_object_add (object, context, sizeof (SwfdecAsObject));
630 return object;
634 * swfdec_as_object_new:
635 * @context: a #SwfdecAsContext
637 * Allocates a new Object. This does the same as the Actionscript code
638 * "new Object()".
640 * Returns: the new object or NULL on out of memory.
642 SwfdecAsObject *
643 swfdec_as_object_new (SwfdecAsContext *context)
645 SwfdecAsObject *object;
646 SwfdecAsValue val;
648 g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context), NULL);
649 g_assert (context->Object);
650 g_assert (context->Object_prototype);
652 object = swfdec_as_object_new_empty (context);
653 SWFDEC_AS_VALUE_SET_OBJECT (&val, context->Object);
654 swfdec_as_object_set_variable_and_flags (object, SWFDEC_AS_STR_constructor,
655 &val, SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_PERMANENT);
656 SWFDEC_AS_VALUE_SET_OBJECT (&val, context->Object_prototype);
657 swfdec_as_object_set_variable_and_flags (object, SWFDEC_AS_STR___proto__,
658 &val, SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_PERMANENT);
659 return object;
663 * swfdec_as_object_add:
664 * @object: #SwfdecAsObject to make garbage-collected
665 * @context: #SwfdecAsContext that should manage the object
666 * @size: size the object currently uses
668 * Takes over the reference to @object for the garbage collector of @context.
669 * The object may not already be part of a different context. The given @size
670 * must have been allocated before with swfdec_as_context_use_mem ().
671 * Note that after swfdec_as_object_add() the garbage collector might hold the
672 * only reference to @object.
674 void
675 swfdec_as_object_add (SwfdecAsObject *object, SwfdecAsContext *context, gsize size)
677 SwfdecAsObjectClass *klass;
679 g_return_if_fail (SWFDEC_IS_AS_OBJECT (object));
680 g_return_if_fail (SWFDEC_IS_AS_CONTEXT (context));
681 g_return_if_fail (object->properties == NULL);
683 object->context = context;
684 object->size = size;
685 g_hash_table_insert (context->objects, object, object);
686 object->properties = g_hash_table_new (g_direct_hash, g_direct_equal);
687 klass = SWFDEC_AS_OBJECT_GET_CLASS (object);
688 g_return_if_fail (klass->add);
689 klass->add (object);
690 if (context->debugger) {
691 SwfdecAsDebuggerClass *dklass = SWFDEC_AS_DEBUGGER_GET_CLASS (context->debugger);
692 if (dklass->add)
693 dklass->add (context->debugger, context, object);
697 /* This is a huge hack design-wise, but we can't use watch->watch,
698 * it might be gone already */
699 static gboolean
700 swfdec_as_object_steal_watches (gpointer key, gpointer value, gpointer object)
702 SwfdecAsWatch *watch = value;
704 g_assert (watch->refcount == 1);
705 watch->watch = (SwfdecAsFunction *) object;
706 swfdec_as_watch_unref (watch);
707 return TRUE;
710 void
711 swfdec_as_object_collect (SwfdecAsObject *object)
713 g_return_if_fail (SWFDEC_IS_AS_OBJECT (object));
714 g_return_if_fail (object->properties != NULL);
716 g_hash_table_foreach (object->properties, swfdec_as_object_free_property, object);
717 g_hash_table_destroy (object->properties);
718 object->properties = NULL;
719 if (object->watches) {
720 g_hash_table_foreach_steal (object->watches, swfdec_as_object_steal_watches, object);
721 g_hash_table_destroy (object->watches);
722 object->watches = NULL;
724 if (object->size)
725 swfdec_as_context_unuse_mem (object->context, object->size);
726 g_object_unref (object);
730 * swfdec_as_object_set_variable:
731 * @object: a #SwfdecAsObject
732 * @variable: garbage-collected name of the variable to set
733 * @value: value to set the variable to
735 * Sets a variable on @object. It is not guaranteed that getting the variable
736 * after setting it results in the same value. This is a mcaro that calls
737 * swfdec_as_object_set_variable_and_flags()
740 * swfdec_as_object_set_variable_and_flags:
741 * @object: a #SwfdecAsObject
742 * @variable: garbage-collected name of the variable to set
743 * @value: value to set the variable to
744 * @default_flags: flags to use if creating the variable anew - the flags will
745 * be ignored if the property already exists.
747 * Sets a variable on @object. It is not guaranteed that getting the variable
748 * after setting it results in the same value, because various mechanisms (like
749 * the Actionscript Object.addProperty function or constant variables) can
750 * avoid this.
752 void
753 swfdec_as_object_set_variable_and_flags (SwfdecAsObject *object,
754 const char *variable, const SwfdecAsValue *value, guint default_flags)
756 SwfdecAsObjectClass *klass;
758 g_return_if_fail (SWFDEC_IS_AS_OBJECT (object));
759 g_return_if_fail (variable != NULL);
760 g_return_if_fail (SWFDEC_IS_AS_VALUE (value));
762 if (object->context->debugger) {
763 SwfdecAsDebugger *debugger = object->context->debugger;
764 SwfdecAsDebuggerClass *dklass = SWFDEC_AS_DEBUGGER_GET_CLASS (debugger);
765 if (dklass->set_variable)
766 dklass->set_variable (debugger, object->context, object, variable, value);
768 klass = SWFDEC_AS_OBJECT_GET_CLASS (object);
769 klass->set (object, variable, value, default_flags);
773 * swfdec_as_object_get_variable:
774 * @object: a #SwfdecAsObject
775 * @variable: a garbage-collected string containing the name of the variable
776 * @value: pointer to a #SwfdecAsValue that takes the return value or %NULL
778 * Gets the value of the given @variable on @object. It walks the prototype
779 * chain. This is a shortcut macro for
780 * swfdec_as_object_get_variable_and_flags().
782 * Returns: %TRUE if the variable existed, %FALSE otherwise
786 * swfdec_as_object_get_variable_and_flags:
787 * @object: a #SwfdecAsObject
788 * @variable: a garbage-collected string containing the name of the variable
789 * @value: pointer to a #SwfdecAsValue that takes the return value or %NULL
790 * @flags: pointer to a guint taking the variable's flags or %NULL
791 * @pobject: pointer to set to the object that really holds the property or
792 * %NULL
794 * Looks up @variable on @object. It also walks the object's prototype chain.
795 * If the variable exists, its value, flags and the real object containing the
796 * variable will be set and %TRUE will be returned.
798 * Returns: %TRUE if the variable exists, %FALSE otherwise
800 gboolean
801 swfdec_as_object_get_variable_and_flags (SwfdecAsObject *object,
802 const char *variable, SwfdecAsValue *value, guint *flags, SwfdecAsObject **pobject)
804 SwfdecAsObjectClass *klass;
805 guint i;
806 SwfdecAsValue tmp_val;
807 guint tmp_flags;
808 SwfdecAsObject *tmp_pobject, *cur;
810 g_return_val_if_fail (SWFDEC_IS_AS_OBJECT (object), FALSE);
811 g_return_val_if_fail (variable != NULL, FALSE);
813 if (value == NULL)
814 value = &tmp_val;
815 if (flags == NULL)
816 flags = &tmp_flags;
817 if (pobject == NULL)
818 pobject = &tmp_pobject;
820 cur = object;
821 for (i = 0; i <= SWFDEC_AS_OBJECT_PROTOTYPE_RECURSION_LIMIT && cur != NULL; i++) {
822 klass = SWFDEC_AS_OBJECT_GET_CLASS (cur);
823 if (klass->get (cur, object, variable, value, flags)) {
824 *pobject = cur;
825 return TRUE;
827 cur = swfdec_as_object_prototype_for_version (cur, cur->context->version,
828 FALSE);
830 if (i > SWFDEC_AS_OBJECT_PROTOTYPE_RECURSION_LIMIT) {
831 swfdec_as_context_abort (object->context, "Prototype recursion limit exceeded");
832 *flags = 0;
833 *pobject = NULL;
834 return FALSE;
836 //SWFDEC_WARNING ("no such variable %s", variable);
837 SWFDEC_AS_VALUE_SET_UNDEFINED (value);
838 *flags = 0;
839 *pobject = NULL;
840 return FALSE;
844 * swfdec_as_object_delete_variable:
845 * @object: a #SwfdecAsObject
846 * @variable: garbage-collected name of the variable
848 * Deletes the given variable if possible. If the variable is protected from
849 * deletion, it will not be deleted.
851 * Returns: See #SwfdecAsDeleteReutnr for details of the return value.
853 SwfdecAsDeleteReturn
854 swfdec_as_object_delete_variable (SwfdecAsObject *object, const char *variable)
856 SwfdecAsObjectClass *klass;
858 g_return_val_if_fail (SWFDEC_IS_AS_OBJECT (object), FALSE);
859 g_return_val_if_fail (variable != NULL, FALSE);
861 klass = SWFDEC_AS_OBJECT_GET_CLASS (object);
862 return klass->del (object, variable);
866 * swfdec_as_object_set_variable_flags:
867 * @object: a #SwfdecAsObject
868 * @variable: the variable to modify
869 * @flags: flags to set
871 * Sets the given flags for the given variable.
873 void
874 swfdec_as_object_set_variable_flags (SwfdecAsObject *object,
875 const char *variable, SwfdecAsVariableFlag flags)
877 SwfdecAsObjectClass *klass;
879 g_return_if_fail (SWFDEC_IS_AS_OBJECT (object));
880 g_return_if_fail (variable != NULL);
882 klass = SWFDEC_AS_OBJECT_GET_CLASS (object);
883 klass->set_flags (object, variable, flags, flags);
887 * swfdec_as_object_unset_variable_flags:
888 * @object: a #SwfdecAsObject
889 * @variable: the variable to modify
890 * @flags: flags to unset
892 * Unsets the given flags for the given variable. The variable must exist in
893 * @object.
895 void
896 swfdec_as_object_unset_variable_flags (SwfdecAsObject *object,
897 const char *variable, SwfdecAsVariableFlag flags)
899 SwfdecAsObjectClass *klass;
901 g_return_if_fail (SWFDEC_IS_AS_OBJECT (object));
902 g_return_if_fail (variable != NULL);
904 klass = SWFDEC_AS_OBJECT_GET_CLASS (object);
905 klass->set_flags (object, variable, 0, flags);
909 * swfdec_as_object_foreach:
910 * @object: a #SwfdecAsObject
911 * @func: function to call
912 * @data: data to pass to @func
914 * Calls @func for every variable of @object or until @func returns %FALSE. The
915 * variables of @object must not be modified by @func.
917 * Returns: %TRUE if @func always returned %TRUE
919 gboolean
920 swfdec_as_object_foreach (SwfdecAsObject *object, SwfdecAsVariableForeach func,
921 gpointer data)
923 SwfdecAsObjectClass *klass;
925 g_return_val_if_fail (SWFDEC_IS_AS_OBJECT (object), FALSE);
926 g_return_val_if_fail (func != NULL, FALSE);
928 klass = SWFDEC_AS_OBJECT_GET_CLASS (object);
929 g_return_val_if_fail (klass->foreach != NULL, FALSE);
930 return klass->foreach (object, func, data);
933 /*** SIMPLIFICATIONS ***/
935 static void
936 swfdec_as_object_do_nothing (SwfdecAsContext *cx, SwfdecAsObject *object,
937 guint argc, SwfdecAsValue *argv, SwfdecAsValue *retval)
942 * swfdec_as_object_add_function:
943 * @object: a #SwfdecAsObject
944 * @name: name of the function. The string does not have to be
945 * garbage-collected.
946 * @type: the required type of the this Object to make this function execute.
947 * May be 0 to accept any type.
948 * @native: a native function or %NULL to just not do anything
949 * @min_args: minimum number of arguments to pass to @native
951 * Adds @native as a variable named @name to @object. The newly added variable
952 * will not be enumerated.
954 * Returns: the newly created #SwfdecAsFunction or %NULL on error.
956 SwfdecAsFunction *
957 swfdec_as_object_add_function (SwfdecAsObject *object, const char *name, GType type,
958 SwfdecAsNative native, guint min_args)
960 g_return_val_if_fail (SWFDEC_IS_AS_OBJECT (object), NULL);
961 g_return_val_if_fail (name != NULL, NULL);
962 g_return_val_if_fail (type == 0 || g_type_is_a (type, SWFDEC_TYPE_AS_OBJECT), NULL);
964 return swfdec_as_object_add_constructor (object, name, type, 0, native, min_args, NULL);
968 * swfdec_as_object_add_constructor:
969 * @object: a #SwfdecAsObject
970 * @name: name of the function. The string does not have to be
971 * garbage-collected.
972 * @type: the required type of the this Object to make this function execute.
973 * May be 0 to accept any type.
974 * @construct_type: type used when using this function as a constructor. May
975 * be 0 to use the default type.
976 * @native: a native function or %NULL to just not do anything
977 * @min_args: minimum number of arguments to pass to @native
978 * @prototype: An optional object to be set as the "prototype" property of the
979 * new function. The prototype will be hidden and constant.
981 * Adds @native as a constructor named @name to @object. The newly added variable
982 * will not be enumerated.
984 * Returns: the newly created #SwfdecAsFunction or %NULL on error.
986 SwfdecAsFunction *
987 swfdec_as_object_add_constructor (SwfdecAsObject *object, const char *name, GType type,
988 GType construct_type, SwfdecAsNative native, guint min_args, SwfdecAsObject *prototype)
990 SwfdecAsFunction *function;
991 SwfdecAsValue val;
993 g_return_val_if_fail (SWFDEC_IS_AS_OBJECT (object), NULL);
994 g_return_val_if_fail (name != NULL, NULL);
995 g_return_val_if_fail (type == 0 || g_type_is_a (type, SWFDEC_TYPE_AS_OBJECT), NULL);
996 g_return_val_if_fail (construct_type == 0 || g_type_is_a (construct_type, SWFDEC_TYPE_AS_OBJECT), NULL);
997 g_return_val_if_fail (prototype == NULL || SWFDEC_IS_AS_OBJECT (prototype), NULL);
999 if (!native)
1000 native = swfdec_as_object_do_nothing;
1001 function = swfdec_as_native_function_new (object->context, name, native, min_args, prototype);
1002 if (function == NULL)
1003 return NULL;
1004 if (type != 0)
1005 swfdec_as_native_function_set_object_type (SWFDEC_AS_NATIVE_FUNCTION (function), type);
1006 if (construct_type != 0)
1007 swfdec_as_native_function_set_construct_type (SWFDEC_AS_NATIVE_FUNCTION (function), construct_type);
1008 name = swfdec_as_context_get_string (object->context, name);
1009 SWFDEC_AS_VALUE_SET_OBJECT (&val, SWFDEC_AS_OBJECT (function));
1010 /* FIXME: I'd like to make sure no such property exists yet */
1011 swfdec_as_object_set_variable_and_flags (object, name, &val,
1012 SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_PERMANENT);
1013 return function;
1017 * swfdec_as_object_run:
1018 * @object: a #SwfdecAsObject
1019 * @script: script to execute
1021 * Executes the given @script with @object as this pointer.
1023 void
1024 swfdec_as_object_run (SwfdecAsObject *object, SwfdecScript *script)
1026 SwfdecAsContext *context;
1027 SwfdecAsFrame *frame;
1029 g_return_if_fail (SWFDEC_IS_AS_OBJECT (object));
1030 g_return_if_fail (script != NULL);
1032 context = object->context;
1033 frame = swfdec_as_frame_new (context, script);
1034 if (frame == NULL)
1035 return;
1036 swfdec_as_frame_set_this (frame, object);
1037 swfdec_as_frame_preload (frame);
1038 swfdec_as_context_run (context);
1039 swfdec_as_stack_pop (context);
1043 * swfdec_as_object_call:
1044 * @object: a #SwfdecAsObject
1045 * @name: garbage-collected string naming the function to call.
1046 * @argc: number of arguments to provide to function
1047 * @argv: arguments or %NULL when @argc is 0
1048 * @return_value: location to take the return value of the call or %NULL to
1049 * ignore the return value.
1051 * Calls the function named @name on the given object. This function is
1052 * essentially equal to the folloeing Actionscript code:
1053 * <informalexample><programlisting>
1054 * @return_value = @object.@name (@argv[0], ..., @argv[argc-1]);
1055 * </programlisting></informalexample>
1057 void
1058 swfdec_as_object_call (SwfdecAsObject *object, const char *name, guint argc,
1059 SwfdecAsValue *argv, SwfdecAsValue *return_value)
1061 static SwfdecAsValue tmp; /* ignored */
1062 SwfdecAsFunction *fun;
1064 g_return_if_fail (SWFDEC_IS_AS_OBJECT (object));
1065 g_return_if_fail (name != NULL);
1066 g_return_if_fail (argc == 0 || argv != NULL);
1068 if (return_value)
1069 SWFDEC_AS_VALUE_SET_UNDEFINED (return_value);
1070 swfdec_as_object_get_variable (object, name, &tmp);
1071 if (!SWFDEC_AS_VALUE_IS_OBJECT (&tmp))
1072 return;
1073 fun = (SwfdecAsFunction *) SWFDEC_AS_VALUE_GET_OBJECT (&tmp);
1074 if (!SWFDEC_IS_AS_FUNCTION (fun))
1075 return;
1076 swfdec_as_function_call (fun, object, argc, argv, return_value ? return_value : &tmp);
1077 swfdec_as_context_run (object->context);
1081 * swfdec_as_object_has_function:
1082 * @object: a #SwfdecAsObject
1083 * @name: garbage-collected name of th function
1085 * Convenience function that checks of @object has a variable that references
1086 * a function.
1088 * Returns: %TRUE if object.name is a function.
1090 gboolean
1091 swfdec_as_object_has_function (SwfdecAsObject *object, const char *name)
1093 SwfdecAsValue val;
1095 g_return_val_if_fail (SWFDEC_IS_AS_OBJECT (object), FALSE);
1096 g_return_val_if_fail (name != NULL, FALSE);
1098 swfdec_as_object_get_variable (object, name, &val);
1099 if (!SWFDEC_AS_VALUE_IS_OBJECT (&val))
1100 return FALSE;
1101 return SWFDEC_IS_AS_FUNCTION (SWFDEC_AS_VALUE_GET_OBJECT (&val));
1105 * swfdec_as_object_create:
1106 * @fun: constructor
1107 * @n_args: number of arguments
1108 * @args: arguments to pass to constructor
1110 * Creates a new object for the given constructor and pushes the constructor on
1111 * top of the stack. To actually run the constructor, you need to call
1112 * swfdec_as_context_run(). After the constructor has been run, the new object
1113 * will be pushed to the top of the stack.
1115 void
1116 swfdec_as_object_create (SwfdecAsFunction *fun, guint n_args,
1117 const SwfdecAsValue *args)
1119 SwfdecAsValue val;
1120 SwfdecAsObject *new;
1121 SwfdecAsContext *context;
1122 SwfdecAsFunction *cur;
1123 guint size;
1124 GType type = 0;
1126 g_return_if_fail (SWFDEC_IS_AS_FUNCTION (fun));
1128 context = SWFDEC_AS_OBJECT (fun)->context;
1129 cur = fun;
1130 do {
1131 if (SWFDEC_IS_AS_NATIVE_FUNCTION (cur)) {
1132 SwfdecAsNativeFunction *native = SWFDEC_AS_NATIVE_FUNCTION (cur);
1133 if (native->construct_size) {
1134 type = native->construct_type;
1135 size = native->construct_size;
1136 break;
1139 swfdec_as_object_get_variable (SWFDEC_AS_OBJECT (cur), SWFDEC_AS_STR_prototype, &val);
1140 if (SWFDEC_AS_VALUE_IS_OBJECT (&val)) {
1141 SwfdecAsObject *proto = SWFDEC_AS_VALUE_GET_OBJECT (&val);
1142 swfdec_as_object_get_variable (proto, SWFDEC_AS_STR___constructor__, &val);
1143 if (SWFDEC_AS_VALUE_IS_OBJECT (&val)) {
1144 cur = (SwfdecAsFunction *) SWFDEC_AS_VALUE_GET_OBJECT (&val);
1145 if (SWFDEC_IS_AS_FUNCTION (cur)) {
1146 continue;
1150 cur = NULL;
1151 } while (type == 0 && cur != NULL);
1152 if (type == 0) {
1153 type = SWFDEC_TYPE_AS_OBJECT;
1154 size = sizeof (SwfdecAsObject);
1156 if (swfdec_as_context_use_mem (context, size)) {
1157 new = g_object_new (type, NULL);
1158 swfdec_as_object_add (new, context, size);
1159 /* set initial variables */
1160 if (swfdec_as_object_get_variable (SWFDEC_AS_OBJECT (fun), SWFDEC_AS_STR_prototype, &val)) {
1161 swfdec_as_object_set_variable_and_flags (new, SWFDEC_AS_STR___proto__,
1162 &val, SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_PERMANENT);
1164 SWFDEC_AS_VALUE_SET_OBJECT (&val, SWFDEC_AS_OBJECT (fun));
1165 if (context->version < 7) {
1166 swfdec_as_object_set_variable_and_flags (new, SWFDEC_AS_STR_constructor,
1167 &val, SWFDEC_AS_VARIABLE_HIDDEN);
1169 swfdec_as_object_set_variable_and_flags (new, SWFDEC_AS_STR___constructor__,
1170 &val, SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_VERSION_6_UP);
1171 } else {
1172 /* need to do this, since we must push something to the frame stack */
1173 new = NULL;
1175 swfdec_as_function_call (fun, new, n_args, args, NULL);
1176 context->frame->construct = TRUE;
1180 * swfdec_as_object_set_constructor:
1181 * @object: a #SwfdecAsObject
1182 * @construct: the constructor of @object
1184 * Sets the constructor variables for @object. Most objects get these
1185 * variables set automatically, but for objects you created yourself, you want
1186 * to call this function. This is essentially the same as the following script
1187 * code:
1188 * |[ object.constructor = construct;
1189 * object.__proto__ = construct.prototype; ]|
1191 void
1192 swfdec_as_object_set_constructor (SwfdecAsObject *object, SwfdecAsObject *construct)
1194 SwfdecAsValue val;
1195 SwfdecAsObject *proto;
1197 g_return_if_fail (SWFDEC_IS_AS_OBJECT (object));
1198 g_return_if_fail (SWFDEC_IS_AS_OBJECT (construct));
1200 swfdec_as_object_get_variable (SWFDEC_AS_OBJECT (construct),
1201 SWFDEC_AS_STR_prototype, &val);
1202 if (SWFDEC_AS_VALUE_IS_OBJECT (&val)) {
1203 proto = SWFDEC_AS_VALUE_GET_OBJECT (&val);
1204 } else {
1205 SWFDEC_WARNING ("constructor has no prototype, using Object.prototype");
1206 proto = object->context->Object_prototype;
1208 SWFDEC_AS_VALUE_SET_OBJECT (&val, construct);
1209 swfdec_as_object_set_variable_and_flags (object, SWFDEC_AS_STR_constructor,
1210 &val, SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_PERMANENT);
1211 SWFDEC_AS_VALUE_SET_OBJECT (&val, proto);
1212 swfdec_as_object_set_variable_and_flags (object, SWFDEC_AS_STR___proto__,
1213 &val, SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_PERMANENT);
1217 * swfdec_as_object_add_variable:
1218 * @object: a #SwfdecAsObject
1219 * @variable: name of the variable
1220 * @get: getter function to call when reading the variable
1221 * @set: setter function to call when writing the variable or %NULL if read-only
1222 * @default_flags: flags to use if creating the variable anew - the flags will
1223 * be ignored if the property already exists.
1225 * Adds a variable to @object in the same way as the Actionscript code
1226 * "object.addProperty()" would do. Accessing the variable will from now on be
1227 * handled by calling the @get or @set functions. A previous value of the
1228 * variable or a previous call to this function will be overwritten.
1230 void
1231 swfdec_as_object_add_variable (SwfdecAsObject *object, const char *variable,
1232 SwfdecAsFunction *get, SwfdecAsFunction *set, guint default_flags)
1234 SwfdecAsVariable *var;
1236 g_return_if_fail (SWFDEC_IS_AS_OBJECT (object));
1237 g_return_if_fail (variable != NULL);
1238 g_return_if_fail (SWFDEC_IS_AS_FUNCTION (get));
1239 g_return_if_fail (set == NULL || SWFDEC_IS_AS_FUNCTION (set));
1241 var = swfdec_as_object_hash_lookup (object, variable);
1242 if (var == NULL)
1243 var = swfdec_as_object_hash_create (object, variable, default_flags);
1244 if (var == NULL)
1245 return;
1246 var->get = get;
1247 var->set = set;
1250 /*** AS CODE ***/
1252 SWFDEC_AS_NATIVE (101, 2, swfdec_as_object_addProperty)
1253 void
1254 swfdec_as_object_addProperty (SwfdecAsContext *cx, SwfdecAsObject *object,
1255 guint argc, SwfdecAsValue *argv, SwfdecAsValue *retval)
1257 SwfdecAsFunction *get, *set;
1258 const char *name;
1260 SWFDEC_AS_VALUE_SET_BOOLEAN (retval, FALSE);
1261 if (argc < 3)
1262 return;
1263 name = swfdec_as_value_to_string (cx, &argv[0]);
1264 if (!SWFDEC_AS_VALUE_IS_OBJECT (&argv[1]) ||
1265 !SWFDEC_IS_AS_FUNCTION ((get = (SwfdecAsFunction *) SWFDEC_AS_VALUE_GET_OBJECT (&argv[1]))))
1266 return;
1267 if (SWFDEC_AS_VALUE_IS_OBJECT (&argv[2])) {
1268 set = (SwfdecAsFunction *) SWFDEC_AS_VALUE_GET_OBJECT (&argv[2]);
1269 if (!SWFDEC_IS_AS_FUNCTION (set))
1270 return;
1271 } else if (SWFDEC_AS_VALUE_IS_NULL (&argv[2])) {
1272 set = NULL;
1273 } else {
1274 return;
1277 swfdec_as_object_add_variable (object, name, get, set, 0);
1278 SWFDEC_AS_VALUE_SET_BOOLEAN (retval, TRUE);
1281 SWFDEC_AS_NATIVE (101, 5, swfdec_as_object_hasOwnProperty)
1282 void
1283 swfdec_as_object_hasOwnProperty (SwfdecAsContext *cx, SwfdecAsObject *object,
1284 guint argc, SwfdecAsValue *argv, SwfdecAsValue *retval)
1286 SwfdecAsVariable *var;
1287 const char *name;
1289 SWFDEC_AS_VALUE_SET_BOOLEAN (retval, FALSE);
1291 // return false even if no params
1292 if (argc < 1)
1293 return;
1295 name = swfdec_as_value_to_string (object->context, &argv[0]);
1297 if (!(var = swfdec_as_object_hash_lookup (object, name)))
1298 return;
1300 /* This functions only checks NOT 6 flag, and checks it on ALL VERSIONS */
1301 if (var->flags & SWFDEC_AS_VARIABLE_VERSION_NOT_6)
1302 return;
1304 SWFDEC_AS_VALUE_SET_BOOLEAN (retval, TRUE);
1307 SWFDEC_AS_NATIVE (101, 7, swfdec_as_object_isPropertyEnumerable)
1308 void
1309 swfdec_as_object_isPropertyEnumerable (SwfdecAsContext *cx,
1310 SwfdecAsObject *object, guint argc, SwfdecAsValue *argv,
1311 SwfdecAsValue *retval)
1313 SwfdecAsVariable *var;
1314 const char *name;
1316 SWFDEC_AS_VALUE_SET_BOOLEAN (retval, FALSE);
1318 // return false even if no params
1319 if (argc < 1)
1320 return;
1322 name = swfdec_as_value_to_string (object->context, &argv[0]);
1324 if (!(var = swfdec_as_object_hash_lookup (object, name)))
1325 return;
1327 if (var->flags & SWFDEC_AS_VARIABLE_HIDDEN)
1328 return;
1330 SWFDEC_AS_VALUE_SET_BOOLEAN (retval, TRUE);
1333 SWFDEC_AS_NATIVE (101, 6, swfdec_as_object_isPrototypeOf)
1334 void
1335 swfdec_as_object_isPrototypeOf (SwfdecAsContext *cx,
1336 SwfdecAsObject *object, guint argc, SwfdecAsValue *argv,
1337 SwfdecAsValue *retval)
1339 SwfdecAsObject *class;
1341 SWFDEC_AS_VALUE_SET_BOOLEAN (retval, FALSE);
1343 // return false even if no params
1344 if (argc < 1)
1345 return;
1347 class = swfdec_as_value_to_object (cx, &argv[0]);
1348 if (class == NULL)
1349 return;
1351 while ((class = swfdec_as_object_prototype_for_version (class, cx->version, TRUE)) != NULL) {
1352 if (object == class) {
1353 SWFDEC_AS_VALUE_SET_BOOLEAN (retval, TRUE);
1354 return;
1358 // not found, nothing to do
1361 SWFDEC_AS_NATIVE (101, 0, swfdec_as_object_watch)
1362 void
1363 swfdec_as_object_watch (SwfdecAsContext *cx, SwfdecAsObject *object,
1364 guint argc, SwfdecAsValue *argv, SwfdecAsValue *retval)
1366 SwfdecAsWatch *watch;
1367 const char *name;
1369 SWFDEC_AS_VALUE_SET_BOOLEAN (retval, FALSE);
1371 if (argc < 2)
1372 return;
1374 name = swfdec_as_value_to_string (cx, &argv[0]);
1376 if (!SWFDEC_AS_VALUE_IS_OBJECT (&argv[1]))
1377 return;
1379 if (!SWFDEC_IS_AS_FUNCTION (SWFDEC_AS_VALUE_GET_OBJECT (&argv[1])))
1380 return;
1382 if (object->watches == NULL) {
1383 object->watches = g_hash_table_new_full (g_direct_hash, g_direct_equal,
1384 NULL, (GDestroyNotify) swfdec_as_watch_unref);
1385 watch = NULL;
1386 } else {
1387 watch = g_hash_table_lookup (object->watches, name);
1389 if (watch == NULL) {
1390 watch = swfdec_as_watch_new (SWFDEC_AS_FUNCTION (SWFDEC_AS_VALUE_GET_OBJECT (&argv[1])));
1391 if (watch == NULL)
1392 return;
1393 g_hash_table_insert (object->watches, (char *) name, watch);
1394 } else {
1395 watch->watch = SWFDEC_AS_FUNCTION (SWFDEC_AS_VALUE_GET_OBJECT (&argv[1]));
1398 if (argc >= 3) {
1399 watch->watch_data = argv[2];
1400 } else {
1401 SWFDEC_AS_VALUE_SET_UNDEFINED (&watch->watch_data);
1404 SWFDEC_AS_VALUE_SET_BOOLEAN (retval, TRUE);
1407 SWFDEC_AS_NATIVE (101, 1, swfdec_as_object_unwatch)
1408 void
1409 swfdec_as_object_unwatch (SwfdecAsContext *cx, SwfdecAsObject *object,
1410 guint argc, SwfdecAsValue *argv, SwfdecAsValue *retval)
1412 SwfdecAsVariable *var;
1413 const char *name;
1415 SWFDEC_AS_VALUE_SET_BOOLEAN (retval, FALSE);
1417 if (argc < 1)
1418 return;
1420 name = swfdec_as_value_to_string (cx, &argv[0]);
1422 // special case: can't unwatch native properties
1423 if ((var = swfdec_as_object_hash_lookup (object, name))&& var->get != NULL)
1424 return;
1426 if (object->watches != NULL &&
1427 g_hash_table_remove (object->watches, name)) {
1429 SWFDEC_AS_VALUE_SET_BOOLEAN (retval, TRUE);
1431 if (g_hash_table_size (object->watches) == 0) {
1432 g_hash_table_destroy (object->watches);
1433 object->watches = NULL;
1438 SWFDEC_AS_NATIVE (101, 3, swfdec_as_object_valueOf)
1439 void
1440 swfdec_as_object_valueOf (SwfdecAsContext *cx, SwfdecAsObject *object,
1441 guint argc, SwfdecAsValue *argv, SwfdecAsValue *retval)
1443 SWFDEC_AS_VALUE_SET_OBJECT (retval, object);
1446 SWFDEC_AS_NATIVE (101, 4, swfdec_as_object_toString)
1447 void
1448 swfdec_as_object_toString (SwfdecAsContext *cx, SwfdecAsObject *object,
1449 guint argc, SwfdecAsValue *argv, SwfdecAsValue *retval)
1451 if (SWFDEC_IS_AS_FUNCTION (object)) {
1452 SWFDEC_AS_VALUE_SET_STRING (retval, SWFDEC_AS_STR__type_Function_);
1453 } else {
1454 SWFDEC_AS_VALUE_SET_STRING (retval, SWFDEC_AS_STR__object_Object_);
1458 void
1459 swfdec_as_object_decode (SwfdecAsObject *object, const char *str)
1461 SwfdecAsValue val;
1462 char **varlist, *p;
1463 guint i;
1465 str = swfdec_as_string_unescape (object->context, str);
1466 if (str == NULL)
1467 return;
1469 varlist = g_strsplit (str, "&", -1);
1471 for (i = 0; varlist[i] != NULL; i++) {
1472 p = strchr (varlist[i], '=');
1473 if (p != NULL) {
1474 *p++ = '\0';
1475 if (*p == '\0')
1476 p = NULL;
1479 if (p != NULL) {
1480 SWFDEC_AS_VALUE_SET_STRING (&val,
1481 swfdec_as_context_get_string (object->context, p));
1482 } else {
1483 SWFDEC_AS_VALUE_SET_STRING (&val, SWFDEC_AS_STR_EMPTY);
1485 swfdec_as_object_set_variable (object,
1486 swfdec_as_context_get_string (object->context, varlist[i]), &val);
1490 void
1491 swfdec_as_object_init_context (SwfdecAsContext *context, guint version)
1493 SwfdecAsValue val;
1494 SwfdecAsObject *object, *proto;
1496 proto = swfdec_as_object_new_empty (context);
1497 if (!proto)
1498 return;
1499 object = SWFDEC_AS_OBJECT (swfdec_as_object_add_function (context->global,
1500 SWFDEC_AS_STR_Object, 0, NULL, 0));
1501 if (!object)
1502 return;
1503 context->Object = object;
1504 context->Object_prototype = proto;
1505 SWFDEC_AS_VALUE_SET_OBJECT (&val, proto);
1506 /* first, set our own */
1507 swfdec_as_object_set_variable_and_flags (object, SWFDEC_AS_STR_prototype,
1508 &val, SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_PERMANENT |
1509 SWFDEC_AS_VARIABLE_CONSTANT);
1511 /* then finish the function prototype (use this order or
1512 * SWFDEC_AS_VARIABLE_CONSTANT won't let us */
1513 swfdec_as_object_set_variable_and_flags (context->Function_prototype,
1514 SWFDEC_AS_STR___proto__, &val,
1515 SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_PERMANENT);
1517 SWFDEC_AS_VALUE_SET_OBJECT (&val, object);
1518 swfdec_as_object_set_variable_and_flags (proto, SWFDEC_AS_STR_constructor,
1519 &val, SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_PERMANENT);
1523 * swfdec_as_object_get_debug:
1524 * @object: a #SwfdecAsObject
1526 * Gets a representation string suitable for debugging. This function is
1527 * guaranteed to not modify the state of the script engine, unlike
1528 * swfdec_as_value_to_string() for example.
1530 * Returns: A newly allocated string. Free it with g_free() after use.
1532 char *
1533 swfdec_as_object_get_debug (SwfdecAsObject *object)
1535 SwfdecAsObjectClass *klass;
1537 g_return_val_if_fail (SWFDEC_IS_AS_OBJECT (object), NULL);
1539 klass = SWFDEC_AS_OBJECT_GET_CLASS (object);
1540 return klass->debug (object);
1544 * swfdec_as_object_resolve:
1545 * @object: a #SwfdecAsObject
1547 * Resolves the object to its real object. Some internal objects should not be
1548 * exposed to scripts, for example #SwfdecAsFrame objects. If an object you want
1549 * to expose might be internal, call this function to resolve it to an object
1550 * that is safe to expose.
1552 * Returns: a non-internal object
1554 SwfdecAsObject *
1555 swfdec_as_object_resolve (SwfdecAsObject *object)
1557 SwfdecAsObjectClass *klass;
1559 g_return_val_if_fail (SWFDEC_IS_AS_OBJECT (object), NULL);
1561 klass = SWFDEC_AS_OBJECT_GET_CLASS (object);
1562 if (klass->resolve == NULL)
1563 return object;
1565 return klass->resolve (object);