oops, I shifted the wrong value.
[swfdec.git] / swfdec / swfdec_as_object.c
blob2e92e0852ec3ae667a5fbd644c6cbc98699f9c39
1 /* Swfdec
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.
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_as_super.h"
35 #include "swfdec_debug.h"
36 #include "swfdec_movie.h"
38 /**
39 * SECTION:SwfdecAsObject
40 * @title: SwfdecAsObject
41 * @short_description: the base object type for scriptable objects
43 * This is the basic object type in Swfdec. Every object used by the script
44 * engine must be a #SwfdecAsObject. It handles memory management and assigning
45 * variables to it. Almost all functions that are called on objects require that
46 * the objects have been added to the garbage collector previously. For
47 * custom-created objects, you need to do this using swfdec_as_object_add(),
48 * built-in functions that create objects do this manually.
50 * Note that you cannot know the lifetime of a #SwfdecAsObject, since scripts
51 * may assign it as a variable to other objects. So you should not assume to
52 * know when an object gets removed.
55 /**
56 * SwfdecAsObject:
58 * Every object value inside the Swfdec script engine must be a SwfdecAsObject.
59 * If you want to add custom objects to your script engine, you need to create a
60 * subclass. The class provides a number of virtual functions that you can
61 * override to achieve the desired behaviour.
64 /**
65 * SwfdecAsVariableFlag:
66 * @SWFDEC_AS_VARIABLE_HIDDEN: Do not include variable in enumerations and
67 * swfdec_as_object_foreach().
68 * @SWFDEC_AS_VARIABLE_PERMANENT: Do not allow swfdec_as_object_delete_variable()
69 * to delete this variable.
70 * @SWFDEC_AS_VARIABLE_CONSTANT: Do not allow changing the value with
71 * swfdec_as_object_set_variable().
72 * @SWFDEC_AS_VARIABLE_VERSION_6_UP: This symbol is only visible in version 6
73 * and above.
74 * @SWFDEC_AS_VARIABLE_VERSION_NOT_6: This symbols is visible in all versions
75 * but version 6.
76 * @SWFDEC_AS_VARIABLE_VERSION_7_UP: This symbol is only visible in version 7
77 * and above.
78 * @SWFDEC_AS_VARIABLE_VERSION_8_UP: This symbol is only visible in version 8
79 * and above.
80 * @SWFDEC_AS_VARIABLE_VERSION_9_UP: This symbol is only visible in version 9
81 * and above.
83 * These flags are used to describe various properties of a variable inside
84 * Swfdec. You can manually set them with swfdec_as_object_set_variable_flags().
87 /**
88 * SwfdecAsDeleteReturn:
89 * @SWFDEC_AS_DELETE_NOT_FOUND: The variable was not found and therefore
90 * couldn't be deleted.
91 * @SWFDEC_AS_DELETE_DELETED: The variable was deleted.
92 * @SWFDEC_AS_DELETE_NOT_DELETED: The variable was found but could not be
93 * deleted.
95 * This is the return value used by swfdec_as_object_delete_variable(). It
96 * describes the various outcomes of trying to delete a variable.
99 /**
100 * SwfdecAsVariableForeach:
101 * @object: The object this function is run on
102 * @variable: garbage-collected name of the current variables
103 * @value: value of the current variable
104 * @flags: Flags associated with the current variable
105 * @data: User data passed to swfdec_as_object_foreach()
107 * Function prototype for the swfdec_as_object_foreach() function.
109 * Returns: %TRUE to continue running the foreach function, %FALSE to stop
112 typedef struct _SwfdecAsVariable SwfdecAsVariable;
113 struct _SwfdecAsVariable {
114 guint flags; /* SwfdecAsVariableFlag values */
115 SwfdecAsValue value; /* value of property */
116 SwfdecAsFunction * get; /* getter set with swfdec_as_object_add_property */
117 SwfdecAsFunction * set; /* setter or %NULL */
120 typedef struct {
121 SwfdecAsFunction * watch; /* watcher or %NULL */
122 SwfdecAsValue watch_data; /* user data to watcher */
123 guint refcount; /* refcount - misused for recursion detection */
124 } SwfdecAsWatch;
126 G_DEFINE_TYPE (SwfdecAsObject, swfdec_as_object, SWFDEC_TYPE_GC_OBJECT)
128 static gboolean
129 swfdec_as_watch_can_recurse (SwfdecAsWatch *watch)
131 guint version;
133 version = swfdec_gc_object_get_context (watch->watch)->version;
134 if (version <= 6) {
135 return watch->refcount <= 1;
136 } else {
137 return watch->refcount <= 64 + 1;
141 static void
142 swfdec_as_watch_ref (SwfdecAsWatch *watch)
144 watch->refcount++;
147 static void
148 swfdec_as_watch_unref (SwfdecAsWatch *watch)
150 watch->refcount--;
151 if (watch->refcount == 0) {
152 swfdec_as_context_unuse_mem (swfdec_gc_object_get_context (watch->watch),
153 sizeof (SwfdecAsWatch));
154 g_slice_free (SwfdecAsWatch, watch);
158 /* This is a huge hack design-wise, but we can't use watch->watch,
159 * it might be gone already */
160 static gboolean
161 swfdec_as_object_steal_watches (gpointer key, gpointer value, gpointer object)
163 SwfdecAsWatch *watch = value;
165 g_assert (watch->refcount == 1);
166 watch->watch = (SwfdecAsFunction *) object;
167 swfdec_as_watch_unref (watch);
168 return TRUE;
171 static void
172 swfdec_as_object_free_property (gpointer key, gpointer value, gpointer data)
174 SwfdecAsObject *object = data;
176 swfdec_as_context_unuse_mem (swfdec_gc_object_get_context (object), sizeof (SwfdecAsVariable));
177 g_slice_free (SwfdecAsVariable, value);
180 static void
181 swfdec_as_object_dispose (GObject *gobject)
183 SwfdecAsContext *context = swfdec_gc_object_get_context (gobject);
184 SwfdecAsObject *object = SWFDEC_AS_OBJECT (gobject);
186 if (context->debugger) {
187 SwfdecAsDebuggerClass *klass = SWFDEC_AS_DEBUGGER_GET_CLASS (context->debugger);
188 if (klass->remove)
189 klass->remove (context->debugger, context, object);
192 if (object->properties) {
193 g_hash_table_foreach (object->properties, swfdec_as_object_free_property, object);
194 g_hash_table_destroy (object->properties);
195 object->properties = NULL;
197 if (object->watches) {
198 g_hash_table_foreach_steal (object->watches, swfdec_as_object_steal_watches, object);
199 g_hash_table_destroy (object->watches);
200 object->watches = NULL;
202 g_slist_free (object->interfaces);
203 object->interfaces = NULL;
205 G_OBJECT_CLASS (swfdec_as_object_parent_class)->dispose (gobject);
208 static void
209 swfdec_gc_object_mark_property (gpointer key, gpointer value, gpointer unused)
211 SwfdecAsVariable *var = value;
213 swfdec_as_string_mark (key);
214 if (var->get) {
215 swfdec_gc_object_mark (var->get);
216 if (var->set)
217 swfdec_gc_object_mark (var->set);
218 } else {
219 swfdec_as_value_mark (&var->value);
223 static void
224 swfdec_gc_object_mark_watch (gpointer key, gpointer value, gpointer unused)
226 SwfdecAsWatch *watch = value;
228 swfdec_as_string_mark (key);
229 swfdec_gc_object_mark (watch->watch);
230 swfdec_as_value_mark (&watch->watch_data);
233 static void
234 swfdec_as_object_mark (SwfdecGcObject *gc)
236 SwfdecAsObject *object = SWFDEC_AS_OBJECT (gc);
238 if (object->prototype)
239 swfdec_gc_object_mark (object->prototype);
240 g_hash_table_foreach (object->properties, swfdec_gc_object_mark_property, NULL);
241 if (object->watches)
242 g_hash_table_foreach (object->watches, swfdec_gc_object_mark_watch, NULL);
243 g_slist_foreach (object->interfaces, (GFunc) swfdec_gc_object_mark, NULL);
246 static gboolean
247 swfdec_as_object_lookup_case_insensitive (gpointer key, gpointer value, gpointer user_data)
249 return g_ascii_strcasecmp (key, user_data) == 0;
252 static gboolean
253 swfdec_as_variable_name_is_valid (const char *name)
255 return name != SWFDEC_AS_STR_EMPTY;
258 static SwfdecAsVariable *
259 swfdec_as_object_hash_lookup (SwfdecAsObject *object, const char *variable)
261 SwfdecAsVariable *var = g_hash_table_lookup (object->properties, variable);
263 if (var || swfdec_gc_object_get_context (object)->version >= 7)
264 return var;
265 var = g_hash_table_find (object->properties, swfdec_as_object_lookup_case_insensitive, (gpointer) variable);
266 return var;
269 static SwfdecAsVariable *
270 swfdec_as_object_hash_create (SwfdecAsObject *object, const char *variable, guint flags)
272 SwfdecAsVariable *var;
274 if (!swfdec_as_variable_name_is_valid (variable))
275 return NULL;
276 swfdec_as_context_use_mem (swfdec_gc_object_get_context (object), sizeof (SwfdecAsVariable));
277 var = g_slice_new0 (SwfdecAsVariable);
278 var->flags = flags;
279 g_hash_table_insert (object->properties, (gpointer) variable, var);
281 return var;
284 static gboolean
285 swfdec_as_object_variable_enabled_in_version (SwfdecAsVariable *var,
286 guint version)
288 if (var->flags & SWFDEC_AS_VARIABLE_VERSION_6_UP && version < 6)
289 return FALSE;
290 if (var->flags & SWFDEC_AS_VARIABLE_VERSION_NOT_6 && version == 6)
291 return FALSE;
292 if (var->flags & SWFDEC_AS_VARIABLE_VERSION_7_UP && version < 7)
293 return FALSE;
294 if (var->flags & SWFDEC_AS_VARIABLE_VERSION_8_UP && version < 8)
295 return FALSE;
296 if (var->flags & SWFDEC_AS_VARIABLE_VERSION_9_UP && version < 9)
297 return FALSE;
299 return TRUE;
302 static gboolean
303 swfdec_as_object_do_get (SwfdecAsObject *object, SwfdecAsObject *orig,
304 const char *variable, SwfdecAsValue *val, guint *flags)
306 SwfdecAsVariable *var = swfdec_as_object_hash_lookup (object, variable);
308 if (var == NULL)
309 return FALSE;
311 /* variable flag checks */
312 if (!swfdec_as_object_variable_enabled_in_version (var,
313 swfdec_gc_object_get_context (object)->version))
314 return FALSE;
316 if (var->get) {
317 swfdec_as_function_call (var->get, orig, 0, NULL, val);
318 *flags = var->flags;
319 } else {
320 *val = var->value;
321 *flags = var->flags;
323 return TRUE;
326 static SwfdecAsWatch *
327 swfdec_as_watch_new (SwfdecAsFunction *function)
329 SwfdecAsWatch *watch;
331 swfdec_as_context_use_mem (swfdec_gc_object_get_context (function),
332 sizeof (SwfdecAsWatch));
334 watch = g_slice_new (SwfdecAsWatch);
335 watch->refcount = 1;
336 watch->watch = function;
337 SWFDEC_AS_VALUE_SET_UNDEFINED (&watch->watch_data);
338 return watch;
342 * Like swfdec_as_object_get_prototype, but doesn't check 8_UP flag when
343 * version is 7 and doesn't check if the property has been deleted if version
344 * is 6 or earlier
346 static SwfdecAsObject *
347 swfdec_as_object_get_prototype_internal (SwfdecAsObject *object)
349 int version;
351 g_return_val_if_fail (SWFDEC_IS_AS_OBJECT (object), NULL);
353 version = swfdec_gc_object_get_context (object)->version;
355 if (object->prototype == NULL)
356 return NULL;
358 if (object->prototype_flags & SWFDEC_AS_VARIABLE_VERSION_6_UP && version < 6)
359 return NULL;
360 // don't check for NOT_6 flag
361 if (object->prototype_flags & SWFDEC_AS_VARIABLE_VERSION_7_UP && version < 7)
362 return NULL;
363 // don't check 8_UP or 9_UP for version 6, 7 or 8
364 if (object->prototype_flags & (SWFDEC_AS_VARIABLE_VERSION_8_UP | SWFDEC_AS_VARIABLE_VERSION_9_UP) && version < 6)
365 return NULL;
366 // check that it exists, if version < 7
367 if (version < 7 &&
368 !swfdec_as_object_hash_lookup (object, SWFDEC_AS_STR___proto__))
369 return NULL;
371 return object->prototype;
375 * Get's the object->prototype, if propflags allow it for current version and
376 * if it hasn't been deleted from the object already
378 SwfdecAsObject *
379 swfdec_as_object_get_prototype (SwfdecAsObject *object)
381 int version;
382 SwfdecAsObject *prototype;
384 g_return_val_if_fail (SWFDEC_IS_AS_OBJECT (object), NULL);
386 version = swfdec_gc_object_get_context (object)->version;
388 prototype = swfdec_as_object_get_prototype_internal (object);
390 if (prototype == NULL)
391 return NULL;
392 // check 8_UP for version 7, still not for version 6
393 if (object->prototype_flags & SWFDEC_AS_VARIABLE_VERSION_8_UP &&
394 version == 7)
395 return NULL;
396 // check 9_UP flag for version 8, still not for version 7 or 6
397 if (object->prototype_flags & SWFDEC_AS_VARIABLE_VERSION_9_UP &&
398 version == 8)
399 return NULL;
400 // require it to exist even on version >= 7
401 if (version >= 7 &&
402 !swfdec_as_object_hash_lookup (object, SWFDEC_AS_STR___proto__))
403 return NULL;
405 return object->prototype;
408 static SwfdecAsVariable *
409 swfdec_as_object_hash_lookup_with_prototype (SwfdecAsObject *object,
410 const char *variable, SwfdecAsObject **proto)
412 SwfdecAsVariable *var;
413 SwfdecAsObject *proto_;
415 g_return_val_if_fail (swfdec_as_variable_name_is_valid (variable), NULL);
417 proto_ = NULL;
419 // match first level variable even if it has version flags that hide it in
420 // this version
421 var = swfdec_as_object_hash_lookup (object, variable);
422 if (var == NULL && variable != SWFDEC_AS_STR___proto__) {
423 guint i;
425 proto_ = swfdec_as_object_get_prototype (object);
427 for (i = 0; i < SWFDEC_AS_OBJECT_PROTOTYPE_RECURSION_LIMIT && proto_; i++) {
428 var = swfdec_as_object_hash_lookup (proto_, variable);
429 if (var && var->get)
430 break;
431 proto_ = swfdec_as_object_get_prototype (proto_);
432 var = NULL;
435 if (i == SWFDEC_AS_OBJECT_PROTOTYPE_RECURSION_LIMIT) {
436 swfdec_as_context_abort (swfdec_gc_object_get_context (object), "Prototype recursion limit exceeded");
437 return NULL;
441 if (proto != NULL)
442 *proto = proto_;
444 return var;
447 static void
448 swfdec_as_object_do_set (SwfdecAsObject *object, const char *variable,
449 const SwfdecAsValue *val, guint flags)
451 SwfdecAsVariable *var;
452 SwfdecAsWatch *watch;
453 SwfdecAsObject *proto;
455 if (!swfdec_as_variable_name_is_valid (variable))
456 return;
458 var = swfdec_as_object_hash_lookup_with_prototype (object, variable, &proto);
459 if (swfdec_as_context_is_aborted (swfdec_gc_object_get_context (object)))
460 return;
462 // if variable is disabled in this version
463 if (var != NULL && !swfdec_as_object_variable_enabled_in_version (var,
464 swfdec_gc_object_get_context (object)->version)) {
465 if (proto == NULL) {
466 // it's at the top level, remove getter and setter plus overwrite
467 var->get = NULL;
468 var->set = NULL;
469 } else {
470 // it's in proto, we create a new one at the top level
471 var = NULL;
475 if (var == NULL) {
476 var = swfdec_as_object_hash_create (object, variable, flags);
477 if (var == NULL)
478 return;
479 } else {
480 if (var->flags & SWFDEC_AS_VARIABLE_CONSTANT)
481 return;
482 // remove the flags that could make this variable hidden
483 if (swfdec_gc_object_get_context (object)->version == 6) {
484 // version 6, so let's forget SWFDEC_AS_VARIABLE_VERSION_7_UP flag, oops!
485 // we will still set the value though, even if that flag is set
486 var->flags &= ~(SWFDEC_AS_VARIABLE_VERSION_6_UP |
487 SWFDEC_AS_VARIABLE_VERSION_NOT_6 | SWFDEC_AS_VARIABLE_VERSION_8_UP |
488 SWFDEC_AS_VARIABLE_VERSION_9_UP);
489 } else {
490 var->flags &= ~(SWFDEC_AS_VARIABLE_VERSION_6_UP |
491 SWFDEC_AS_VARIABLE_VERSION_NOT_6 | SWFDEC_AS_VARIABLE_VERSION_7_UP |
492 SWFDEC_AS_VARIABLE_VERSION_8_UP | SWFDEC_AS_VARIABLE_VERSION_9_UP);
495 if (object->watches) {
496 SwfdecAsValue ret = *val;
497 watch = g_hash_table_lookup (object->watches, variable);
498 /* FIXME: figure out if this limit here is correct. Add a watch in Flash 7
499 * and set a variable using Flash 6 */
500 if (watch && swfdec_as_watch_can_recurse (watch)) {
501 SwfdecAsValue args[4];
502 SWFDEC_AS_VALUE_SET_STRING (&args[0], variable);
503 args[1] = var->value;
504 args[2] = *val;
505 args[3] = watch->watch_data;
506 swfdec_as_watch_ref (watch);
507 swfdec_as_function_call (watch->watch, object, 4, args, &ret);
508 swfdec_as_watch_unref (watch);
509 var = swfdec_as_object_hash_lookup_with_prototype (object, variable,
510 NULL);
511 if (swfdec_as_context_is_aborted (swfdec_gc_object_get_context (object)))
512 return;
513 if (var == NULL) {
514 SWFDEC_INFO ("watch removed variable %s", variable);
515 return;
519 var->value = ret;
520 } else {
521 watch = NULL;
523 if (var->get) {
524 if (var->set) {
525 SwfdecAsValue tmp;
526 swfdec_as_function_call (var->set, object, 1, val, &tmp);
528 } else if (watch == NULL) {
529 var->value = *val;
532 if (variable == SWFDEC_AS_STR___proto__) {
533 if (SWFDEC_AS_VALUE_IS_OBJECT (val) &&
534 !SWFDEC_IS_MOVIE (SWFDEC_AS_VALUE_GET_OBJECT (val))) {
535 object->prototype = SWFDEC_AS_VALUE_GET_OBJECT (val);
536 object->prototype_flags = var->flags;
537 } else {
538 object->prototype = NULL;
539 object->prototype_flags = 0;
544 static void
545 swfdec_as_object_do_set_flags (SwfdecAsObject *object, const char *variable, guint flags, guint mask)
547 SwfdecAsVariable *var = swfdec_as_object_hash_lookup (object, variable);
549 if (var) {
550 var->flags = (var->flags & ~mask) | flags;
552 if (variable == SWFDEC_AS_STR___proto__)
553 object->prototype_flags = var->flags;
557 static SwfdecAsDeleteReturn
558 swfdec_as_object_do_delete (SwfdecAsObject *object, const char *variable)
560 SwfdecAsVariable *var;
562 var = g_hash_table_lookup (object->properties, variable);
563 if (var == NULL)
564 return SWFDEC_AS_DELETE_NOT_FOUND;
565 if (var->flags & SWFDEC_AS_VARIABLE_PERMANENT)
566 return SWFDEC_AS_DELETE_NOT_DELETED;
568 // Note: We won't remove object->prototype, even if __proto__ is deleted
570 swfdec_as_object_free_property (NULL, var, object);
571 if (!g_hash_table_remove (object->properties, variable)) {
572 g_assert_not_reached ();
574 return SWFDEC_AS_DELETE_DELETED;
577 typedef struct {
578 SwfdecAsObject * object;
579 SwfdecAsVariableForeach func;
580 gpointer data;
581 gboolean retval;
582 } ForeachData;
584 static void
585 swfdec_as_object_hash_foreach (gpointer key, gpointer value, gpointer data)
587 ForeachData *fdata = data;
588 SwfdecAsVariable *var = value;
590 if (!fdata->retval)
591 return;
593 fdata->retval = fdata->func (fdata->object, key, &var->value, var->flags, fdata->data);
596 /* FIXME: does not do Adobe Flash's order for Enumerate actions */
597 static gboolean
598 swfdec_as_object_do_foreach (SwfdecAsObject *object, SwfdecAsVariableForeach func, gpointer data)
600 ForeachData fdata = { object, func, data, TRUE };
602 g_hash_table_foreach (object->properties, swfdec_as_object_hash_foreach, &fdata);
603 return fdata.retval;
606 typedef struct {
607 SwfdecAsObject * object;
608 SwfdecAsVariableForeachRemove func;
609 gpointer data;
610 } ForeachRemoveData;
612 static gboolean
613 swfdec_as_object_hash_foreach_remove (gpointer key, gpointer value, gpointer data)
615 ForeachRemoveData *fdata = data;
616 SwfdecAsVariable *var = value;
618 if (!fdata->func (fdata->object, key, &var->value, var->flags, fdata->data))
619 return FALSE;
621 swfdec_as_object_free_property (NULL, var, fdata->object);
622 return TRUE;
626 * swfdec_as_object_foreach_remove:
627 * @object: a #SwfdecAsObject
628 * @func: function that determines which object to remove
629 * @data: data to pass to @func
631 * Removes all variables form @object where @func returns %TRUE. This is an
632 * internal function for array operations.
634 * Returns: he number of variables removed
636 guint
637 swfdec_as_object_foreach_remove (SwfdecAsObject *object, SwfdecAsVariableForeach func,
638 gpointer data)
640 ForeachRemoveData fdata = { object, func, data };
642 g_return_val_if_fail (SWFDEC_IS_AS_OBJECT (object), 0);
643 g_return_val_if_fail (func != NULL, 0);
645 return g_hash_table_foreach_remove (object->properties,
646 swfdec_as_object_hash_foreach_remove, &fdata);
649 typedef struct {
650 SwfdecAsObject * object;
651 GHashTable * properties_new;
652 SwfdecAsVariableForeachRename func;
653 gpointer data;
654 } ForeachRenameData;
656 static gboolean
657 swfdec_as_object_hash_foreach_rename (gpointer key, gpointer value, gpointer data)
659 ForeachRenameData *fdata = data;
660 SwfdecAsVariable *var = value;
661 const char *key_new;
663 key_new = fdata->func (fdata->object, key, &var->value, var->flags, fdata->data);
664 if (key_new) {
665 g_hash_table_insert (fdata->properties_new, (gpointer) key_new, var);
666 } else {
667 swfdec_as_object_free_property (NULL, var, fdata->object);
670 return TRUE;
674 * swfdec_as_object_foreach_rename:
675 * @object: a #SwfdecAsObject
676 * @func: function determining the new name
677 * @data: data to pass to @func
679 * Calls @func for each variable of @object. The function is then supposed
680 * to return the new name of the variable or %NULL if the variable should be
681 * removed. This is an internal function for array operations.
683 void
684 swfdec_as_object_foreach_rename (SwfdecAsObject *object, SwfdecAsVariableForeachRename func,
685 gpointer data)
687 ForeachRenameData fdata = { object, NULL, func, data };
689 g_return_if_fail (SWFDEC_IS_AS_OBJECT (object));
690 g_return_if_fail (func != NULL);
692 fdata.properties_new = g_hash_table_new (g_direct_hash, g_direct_equal);
693 g_hash_table_foreach_remove (object->properties, swfdec_as_object_hash_foreach_rename, &fdata);
694 g_hash_table_destroy (object->properties);
695 object->properties = fdata.properties_new;
698 static char *
699 swfdec_as_object_do_debug (SwfdecAsObject *object)
701 if (G_OBJECT_TYPE (object) != SWFDEC_TYPE_AS_OBJECT)
702 return g_strdup (G_OBJECT_TYPE_NAME (object));
704 return g_strdup ("Object");
707 static GObject *
708 swfdec_as_object_constructor (GType type, guint n_construct_properties,
709 GObjectConstructParam *construct_properties)
711 GObject *gobject;
712 SwfdecAsContext *context;
714 gobject = G_OBJECT_CLASS (swfdec_as_object_parent_class)->constructor (type,
715 n_construct_properties, construct_properties);
717 context = swfdec_gc_object_get_context (gobject);
718 if (context->debugger) {
719 SwfdecAsDebuggerClass *dklass = SWFDEC_AS_DEBUGGER_GET_CLASS (context->debugger);
720 if (dklass->add)
721 dklass->add (context->debugger, context, SWFDEC_AS_OBJECT (gobject));
724 return gobject;
727 static void
728 swfdec_as_object_class_init (SwfdecAsObjectClass *klass)
730 GObjectClass *object_class = G_OBJECT_CLASS (klass);
731 SwfdecGcObjectClass *gc_class = SWFDEC_GC_OBJECT_CLASS (klass);
733 object_class->constructor = swfdec_as_object_constructor;
734 object_class->dispose = swfdec_as_object_dispose;
736 gc_class->mark = swfdec_as_object_mark;
738 klass->get = swfdec_as_object_do_get;
739 klass->set = swfdec_as_object_do_set;
740 klass->set_flags = swfdec_as_object_do_set_flags;
741 klass->del = swfdec_as_object_do_delete;
742 klass->foreach = swfdec_as_object_do_foreach;
743 klass->debug = swfdec_as_object_do_debug;
746 static void
747 swfdec_as_object_init (SwfdecAsObject *object)
749 object->properties = g_hash_table_new (g_direct_hash, g_direct_equal);
753 * swfdec_as_object_new_empty:
754 * @context: a #SwfdecAsContext
756 * Creates an empty object. The prototype and constructor properties of the
757 * returned object will not be set. You probably want to call
758 * swfdec_as_object_set_constructor() on the returned object yourself.
759 * You may want to use swfdec_as_object_new() instead.
761 * Returns: A new #SwfdecAsObject added to @context
763 SwfdecAsObject *
764 swfdec_as_object_new_empty (SwfdecAsContext *context)
766 g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context), NULL);
768 return g_object_new (SWFDEC_TYPE_AS_OBJECT, "context", context, NULL);
772 * swfdec_as_object_new:
773 * @context: a #SwfdecAsContext
775 * Allocates a new Object. This does the same as the Actionscript code
776 * "new Object()".
778 * Returns: the new object
780 SwfdecAsObject *
781 swfdec_as_object_new (SwfdecAsContext *context)
783 SwfdecAsObject *object;
784 SwfdecAsValue val;
786 g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context), NULL);
787 g_assert (context->Object);
788 g_assert (context->Object_prototype);
790 object = swfdec_as_object_new_empty (context);
791 SWFDEC_AS_VALUE_SET_OBJECT (&val, context->Object);
792 swfdec_as_object_set_variable_and_flags (object, SWFDEC_AS_STR_constructor,
793 &val, SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_PERMANENT);
794 SWFDEC_AS_VALUE_SET_OBJECT (&val, context->Object_prototype);
795 swfdec_as_object_set_variable_and_flags (object, SWFDEC_AS_STR___proto__,
796 &val, SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_PERMANENT);
797 return object;
801 * swfdec_as_object_set_variable:
802 * @object: a #SwfdecAsObject
803 * @variable: garbage-collected name of the variable to set
804 * @value: value to set the variable to
806 * Sets a variable on @object. It is not guaranteed that getting the variable
807 * after setting it results in the same value. This is a mcaro that calls
808 * swfdec_as_object_set_variable_and_flags()
811 * swfdec_as_object_set_variable_and_flags:
812 * @object: a #SwfdecAsObject
813 * @variable: garbage-collected name of the variable to set
814 * @value: value to set the variable to
815 * @default_flags: flags to use if creating the variable anew - the flags will
816 * be ignored if the property already exists.
818 * Sets a variable on @object. It is not guaranteed that getting the variable
819 * after setting it results in the same value, because various mechanisms (like
820 * the Actionscript Object.addProperty function or constant variables) can
821 * avoid this.
823 void
824 swfdec_as_object_set_variable_and_flags (SwfdecAsObject *object,
825 const char *variable, const SwfdecAsValue *value, guint default_flags)
827 SwfdecAsObjectClass *klass;
829 g_return_if_fail (SWFDEC_IS_AS_OBJECT (object));
830 g_return_if_fail (variable != NULL);
831 g_return_if_fail (SWFDEC_IS_AS_VALUE (value));
833 if (swfdec_gc_object_get_context (object)->debugger) {
834 SwfdecAsDebugger *debugger = swfdec_gc_object_get_context (object)->debugger;
835 SwfdecAsDebuggerClass *dklass = SWFDEC_AS_DEBUGGER_GET_CLASS (debugger);
836 if (dklass->set_variable)
837 dklass->set_variable (debugger, swfdec_gc_object_get_context (object), object, variable, value);
839 klass = SWFDEC_AS_OBJECT_GET_CLASS (object);
840 klass->set (object, variable, value, default_flags);
844 * swfdec_as_object_peek_variable:
845 * @object: the object to query
846 * @name: name of the variable to query
848 * Checks if the given @object contains a variable wih the given @name and if
849 * so, returns a pointer to its value. This pointer will be valid until calling
850 * a setting function on the given object again.
851 * <warning><para>This function is internal as it provides a pointer to an
852 * internal structure. Do not use it unless you are sure you need to. This
853 * function skips prototypes, variables added via swfdec_as_value_add_variable()
854 * and does not verify visibility flags.</para></warning>
856 * Returns: a pointer to the queried variable or %NULL if it doesn't exist
858 SwfdecAsValue *
859 swfdec_as_object_peek_variable (SwfdecAsObject *object, const char *name)
861 SwfdecAsVariable *var;
863 var = swfdec_as_object_hash_lookup (object, name);
864 if (var == NULL ||
865 var->get != NULL)
866 return NULL;
868 return &var->value;
872 * swfdec_as_object_get_variable:
873 * @object: a #SwfdecAsObject
874 * @variable: a garbage-collected string containing the name of the variable
875 * @value: pointer to a #SwfdecAsValue that takes the return value or %NULL
877 * Gets the value of the given @variable on @object. It walks the prototype
878 * chain. This is a shortcut macro for
879 * swfdec_as_object_get_variable_and_flags().
881 * Returns: %TRUE if the variable existed, %FALSE otherwise
885 * swfdec_as_object_get_variable_and_flags:
886 * @object: a #SwfdecAsObject
887 * @variable: a garbage-collected string containing the name of the variable
888 * @value: pointer to a #SwfdecAsValue that takes the return value or %NULL
889 * @flags: pointer to a guint taking the variable's flags or %NULL
890 * @pobject: pointer to set to the object that really holds the property or
891 * %NULL
893 * Looks up @variable on @object. It also walks the object's prototype chain.
894 * If the variable exists, its value, flags and the real object containing the
895 * variable will be set and %TRUE will be returned.
897 * Returns: %TRUE if the variable exists, %FALSE otherwise
899 gboolean
900 swfdec_as_object_get_variable_and_flags (SwfdecAsObject *object,
901 const char *variable, SwfdecAsValue *value, guint *flags, SwfdecAsObject **pobject)
903 SwfdecAsObjectClass *klass;
904 guint i;
905 SwfdecAsValue tmp_val;
906 guint tmp_flags;
907 SwfdecAsObject *tmp_pobject, *cur, *resolve;
909 g_return_val_if_fail (SWFDEC_IS_AS_OBJECT (object), FALSE);
910 g_return_val_if_fail (variable != NULL, FALSE);
912 if (value == NULL)
913 value = &tmp_val;
914 if (flags == NULL)
915 flags = &tmp_flags;
916 if (pobject == NULL)
917 pobject = &tmp_pobject;
919 cur = object;
920 resolve = NULL;
921 for (i = 0; i <= SWFDEC_AS_OBJECT_PROTOTYPE_RECURSION_LIMIT && cur != NULL; i++) {
922 klass = SWFDEC_AS_OBJECT_GET_CLASS (cur);
923 if (klass->get (cur, object, variable, value, flags)) {
924 *pobject = cur;
925 return TRUE;
927 if (resolve == NULL) {
928 SwfdecAsVariable *var =
929 swfdec_as_object_hash_lookup (cur, SWFDEC_AS_STR___resolve);
931 if (var != NULL && (swfdec_gc_object_get_context (object)->version <= 6 ||
932 SWFDEC_AS_VALUE_IS_OBJECT (&var->value)))
933 resolve = cur;
935 cur = swfdec_as_object_get_prototype_internal (cur);
937 if (i > SWFDEC_AS_OBJECT_PROTOTYPE_RECURSION_LIMIT) {
938 swfdec_as_context_abort (swfdec_gc_object_get_context (object), "Prototype recursion limit exceeded");
939 SWFDEC_AS_VALUE_SET_UNDEFINED (value);
940 *flags = 0;
941 *pobject = NULL;
942 return FALSE;
944 if (variable != SWFDEC_AS_STR___resolve && resolve != NULL) {
945 SwfdecAsValue argv;
946 SwfdecAsVariable *var;
947 SwfdecAsFunction *fun;
948 SwfdecAsContext *context;
950 *flags = 0;
951 *pobject = resolve;
952 SWFDEC_AS_VALUE_SET_UNDEFINED (value);
953 context = swfdec_gc_object_get_context (resolve);
955 var = swfdec_as_object_hash_lookup (resolve, SWFDEC_AS_STR___resolve);
956 g_assert (var != NULL);
957 if (!SWFDEC_AS_VALUE_IS_OBJECT (&var->value))
958 return FALSE;
959 fun = (SwfdecAsFunction *) SWFDEC_AS_VALUE_GET_OBJECT (&var->value);
960 if (!SWFDEC_IS_AS_FUNCTION (fun))
961 return FALSE;
962 SWFDEC_AS_VALUE_SET_STRING (&argv, variable);
963 swfdec_as_function_call (fun, resolve, 1, &argv, value);
965 return TRUE;
967 //SWFDEC_WARNING ("no such variable %s", variable);
968 SWFDEC_AS_VALUE_SET_UNDEFINED (value);
969 *flags = 0;
970 *pobject = NULL;
971 return FALSE;
975 * swfdec_as_object_has_variable:
976 * @object: a #SwfdecAsObject
977 * @variable: garbage-collected variable name
979 * Checks if a user-set @variable with the given name exists on @object. This
980 * function does not check variables that are available via an overwritten get
981 * function of the object's class.
983 * Returns: the object in the prototype chain that contains @variable or %NULL
984 * if the @object does not contain this variable.
986 SwfdecAsObject *
987 swfdec_as_object_has_variable (SwfdecAsObject *object, const char *variable)
989 guint i;
990 SwfdecAsVariable *var;
992 g_return_val_if_fail (SWFDEC_IS_AS_OBJECT (object), FALSE);
994 for (i = 0; i <= SWFDEC_AS_OBJECT_PROTOTYPE_RECURSION_LIMIT && object != NULL; i++) {
995 var = swfdec_as_object_hash_lookup (object, variable);
996 if (var) {
997 /* FIXME: propflags? */
998 return object;
1000 object = swfdec_as_object_get_prototype_internal (object);
1002 return NULL;
1006 * swfdec_as_object_delete_variable:
1007 * @object: a #SwfdecAsObject
1008 * @variable: garbage-collected name of the variable
1010 * Deletes the given variable if possible. If the variable is protected from
1011 * deletion, it will not be deleted.
1013 * Returns: See #SwfdecAsDeleteReturn for details of the return value.
1015 SwfdecAsDeleteReturn
1016 swfdec_as_object_delete_variable (SwfdecAsObject *object, const char *variable)
1018 SwfdecAsObjectClass *klass;
1020 g_return_val_if_fail (SWFDEC_IS_AS_OBJECT (object), FALSE);
1021 g_return_val_if_fail (variable != NULL, FALSE);
1023 klass = SWFDEC_AS_OBJECT_GET_CLASS (object);
1024 return klass->del (object, variable);
1028 * swfdec_as_object_delete_all_variables:
1029 * @object: a #SwfdecAsObject
1031 * Deletes all user-set variables from the given object.
1033 void
1034 swfdec_as_object_delete_all_variables (SwfdecAsObject *object)
1036 g_return_if_fail (SWFDEC_IS_AS_OBJECT (object));
1038 g_hash_table_foreach (object->properties, swfdec_as_object_free_property, object);
1039 g_hash_table_remove_all (object->properties);
1043 * swfdec_as_object_set_variable_flags:
1044 * @object: a #SwfdecAsObject
1045 * @variable: the variable to modify
1046 * @flags: flags to set
1048 * Sets the given flags for the given variable.
1050 void
1051 swfdec_as_object_set_variable_flags (SwfdecAsObject *object,
1052 const char *variable, SwfdecAsVariableFlag flags)
1054 SwfdecAsObjectClass *klass;
1056 g_return_if_fail (SWFDEC_IS_AS_OBJECT (object));
1057 g_return_if_fail (variable != NULL);
1059 klass = SWFDEC_AS_OBJECT_GET_CLASS (object);
1060 klass->set_flags (object, variable, flags, flags);
1064 * swfdec_as_object_unset_variable_flags:
1065 * @object: a #SwfdecAsObject
1066 * @variable: the variable to modify
1067 * @flags: flags to unset
1069 * Unsets the given flags for the given variable. The variable must exist in
1070 * @object.
1072 void
1073 swfdec_as_object_unset_variable_flags (SwfdecAsObject *object,
1074 const char *variable, SwfdecAsVariableFlag flags)
1076 SwfdecAsObjectClass *klass;
1078 g_return_if_fail (SWFDEC_IS_AS_OBJECT (object));
1079 g_return_if_fail (variable != NULL);
1081 klass = SWFDEC_AS_OBJECT_GET_CLASS (object);
1082 klass->set_flags (object, variable, 0, flags);
1086 * swfdec_as_object_foreach:
1087 * @object: a #SwfdecAsObject
1088 * @func: function to call
1089 * @data: data to pass to @func
1091 * Calls @func for every variable of @object or until @func returns %FALSE. The
1092 * variables of @object must not be modified by @func.
1094 * Returns: %TRUE if @func always returned %TRUE
1096 gboolean
1097 swfdec_as_object_foreach (SwfdecAsObject *object, SwfdecAsVariableForeach func,
1098 gpointer data)
1100 SwfdecAsObjectClass *klass;
1102 g_return_val_if_fail (SWFDEC_IS_AS_OBJECT (object), FALSE);
1103 g_return_val_if_fail (func != NULL, FALSE);
1105 klass = SWFDEC_AS_OBJECT_GET_CLASS (object);
1106 g_return_val_if_fail (klass->foreach != NULL, FALSE);
1107 return klass->foreach (object, func, data);
1110 /*** SIMPLIFICATIONS ***/
1112 static void
1113 swfdec_as_object_do_nothing (SwfdecAsContext *cx, SwfdecAsObject *object,
1114 guint argc, SwfdecAsValue *argv, SwfdecAsValue *retval)
1119 * swfdec_as_object_add_function:
1120 * @object: a #SwfdecAsObject
1121 * @name: name of the function. The string does not have to be
1122 * garbage-collected.
1123 * @native: a native function or %NULL to just not do anything
1125 * Adds @native as a variable named @name to @object. The newly added variable
1126 * will not be enumerated.
1128 * Returns: the newly created #SwfdecAsFunction
1130 SwfdecAsFunction *
1131 swfdec_as_object_add_function (SwfdecAsObject *object, const char *name, SwfdecAsNative native)
1133 g_return_val_if_fail (SWFDEC_IS_AS_OBJECT (object), NULL);
1134 g_return_val_if_fail (name != NULL, NULL);
1136 return swfdec_as_object_add_constructor (object, name, 0, native, NULL);
1140 * swfdec_as_object_add_constructor:
1141 * @object: a #SwfdecAsObject
1142 * @name: name of the function. The string does not have to be
1143 * garbage-collected.
1144 * @construct_type: type used when using this function as a constructor. May
1145 * be 0 to use the default type.
1146 * @native: a native function or %NULL to just not do anything
1147 * @prototype: An optional object to be set as the "prototype" property of the
1148 * new function. The prototype will be hidden and constant.
1150 * Adds @native as a constructor named @name to @object. The newly added variable
1151 * will not be enumerated.
1153 * Returns: the newly created #SwfdecAsFunction
1155 SwfdecAsFunction *
1156 swfdec_as_object_add_constructor (SwfdecAsObject *object, const char *name,
1157 GType construct_type, SwfdecAsNative native, SwfdecAsObject *prototype)
1159 SwfdecAsFunction *function;
1160 SwfdecAsValue val;
1162 g_return_val_if_fail (SWFDEC_IS_AS_OBJECT (object), NULL);
1163 g_return_val_if_fail (name != NULL, NULL);
1164 g_return_val_if_fail (construct_type == 0 || g_type_is_a (construct_type, SWFDEC_TYPE_AS_OBJECT), NULL);
1165 g_return_val_if_fail (prototype == NULL || SWFDEC_IS_AS_OBJECT (prototype), NULL);
1167 if (!native)
1168 native = swfdec_as_object_do_nothing;
1169 function = swfdec_as_native_function_new (swfdec_gc_object_get_context (object), name, native, prototype);
1170 if (construct_type != 0)
1171 swfdec_as_native_function_set_construct_type (SWFDEC_AS_NATIVE_FUNCTION (function), construct_type);
1172 name = swfdec_as_context_get_string (swfdec_gc_object_get_context (object), name);
1173 SWFDEC_AS_VALUE_SET_OBJECT (&val, SWFDEC_AS_OBJECT (function));
1174 /* FIXME: I'd like to make sure no such property exists yet */
1175 swfdec_as_object_set_variable_and_flags (object, name, &val,
1176 SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_PERMANENT);
1177 return function;
1181 * swfdec_as_object_run:
1182 * @object: a #SwfdecAsObject
1183 * @script: script to execute
1185 * Executes the given @script with @object as this pointer.
1187 void
1188 swfdec_as_object_run (SwfdecAsObject *object, SwfdecScript *script)
1190 SwfdecAsFrame frame = { NULL, };
1191 SwfdecAsContext *context;
1193 g_return_if_fail (SWFDEC_IS_AS_OBJECT (object));
1194 g_return_if_fail (script != NULL);
1196 context = swfdec_gc_object_get_context (object);
1197 swfdec_as_frame_init (&frame, context, script);
1198 swfdec_as_frame_set_this (&frame, object);
1199 swfdec_as_frame_preload (&frame);
1200 /* we take no prisoners */
1201 frame.activation = NULL;
1202 swfdec_as_context_run (context);
1203 swfdec_as_stack_pop (context);
1207 * swfdec_as_object_call:
1208 * @object: a #SwfdecAsObject
1209 * @name: garbage-collected string naming the function to call.
1210 * @argc: number of arguments to provide to function
1211 * @argv: arguments or %NULL when @argc is 0
1212 * @return_value: location to take the return value of the call or %NULL to
1213 * ignore the return value.
1215 * Calls the function named @name on the given object. This function is
1216 * essentially equal to the folloeing Actionscript code:
1217 * <informalexample><programlisting>
1218 * @return_value = @object.@name (@argv[0], ..., @argv[argc-1]);
1219 * </programlisting></informalexample>
1221 * Returns: %TRUE if @object had a function with the given name, %FALSE otherwise
1223 gboolean
1224 swfdec_as_object_call (SwfdecAsObject *object, const char *name, guint argc,
1225 SwfdecAsValue *argv, SwfdecAsValue *return_value)
1227 SwfdecAsValue tmp;
1228 SwfdecAsFunction *fun;
1230 g_return_val_if_fail (SWFDEC_IS_AS_OBJECT (object), TRUE);
1231 g_return_val_if_fail (name != NULL, TRUE);
1232 g_return_val_if_fail (argc == 0 || argv != NULL, TRUE);
1233 g_return_val_if_fail (swfdec_gc_object_get_context (object)->global != NULL, TRUE); /* for SwfdecPlayer */
1235 if (return_value)
1236 SWFDEC_AS_VALUE_SET_UNDEFINED (return_value);
1237 swfdec_as_object_get_variable (object, name, &tmp);
1238 if (!SWFDEC_AS_VALUE_IS_OBJECT (&tmp))
1239 return FALSE;
1240 fun = (SwfdecAsFunction *) SWFDEC_AS_VALUE_GET_OBJECT (&tmp);
1241 if (!SWFDEC_IS_AS_FUNCTION (fun))
1242 return FALSE;
1243 swfdec_as_function_call (fun, object, argc, argv, return_value ? return_value : &tmp);
1245 return TRUE;
1249 * swfdec_as_object_create:
1250 * @fun: constructor
1251 * @n_args: number of arguments
1252 * @args: arguments to pass to constructor
1253 * @return_value: pointer for return value or %NULL to push the return value to
1254 * the stack
1256 * Creates a new object for the given constructor and runs the constructor.
1258 void
1259 swfdec_as_object_create (SwfdecAsFunction *fun, guint n_args,
1260 const SwfdecAsValue *args, SwfdecAsValue *return_value)
1262 SwfdecAsValue val;
1263 SwfdecAsObject *new;
1264 SwfdecAsContext *context;
1265 SwfdecAsFunction *cur;
1266 guint i, size = 0;
1267 GType type = 0;
1269 g_return_if_fail (SWFDEC_IS_AS_FUNCTION (fun));
1271 // FIXME: The way we decide object's type is wrong
1272 // It seems to be actually possible to change the type inside a constructor
1273 // (many times) by changing this.__proto__.__constructor__ and calling super
1275 context = swfdec_gc_object_get_context (fun);
1276 cur = fun;
1277 i = 0;
1278 do {
1279 if (SWFDEC_IS_AS_NATIVE_FUNCTION (cur)) {
1280 SwfdecAsNativeFunction *native = SWFDEC_AS_NATIVE_FUNCTION (cur);
1281 if (native->construct_size) {
1282 type = native->construct_type;
1283 size = native->construct_size;
1284 break;
1287 i++;
1288 swfdec_as_object_get_variable (SWFDEC_AS_OBJECT (cur), SWFDEC_AS_STR_prototype, &val);
1289 if (SWFDEC_AS_VALUE_IS_OBJECT (&val)) {
1290 SwfdecAsObject *proto = SWFDEC_AS_VALUE_GET_OBJECT (&val);
1291 swfdec_as_object_get_variable (proto, SWFDEC_AS_STR___constructor__, &val);
1292 if (SWFDEC_AS_VALUE_IS_OBJECT (&val)) {
1293 cur = (SwfdecAsFunction *) SWFDEC_AS_VALUE_GET_OBJECT (&val);
1294 if (SWFDEC_IS_AS_FUNCTION (cur)) {
1295 continue;
1299 cur = NULL;
1300 } while (type == 0 && cur != NULL && i < SWFDEC_AS_OBJECT_PROTOTYPE_RECURSION_LIMIT);
1301 if (type == 0) {
1302 type = SWFDEC_TYPE_AS_OBJECT;
1303 size = sizeof (SwfdecAsObject);
1306 new = g_object_new (type, "context", context, NULL);
1307 /* set initial variables */
1308 if (swfdec_as_object_get_variable (SWFDEC_AS_OBJECT (fun), SWFDEC_AS_STR_prototype, &val)) {
1309 swfdec_as_object_set_variable_and_flags (new, SWFDEC_AS_STR___proto__,
1310 &val, SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_PERMANENT);
1312 SWFDEC_AS_VALUE_SET_OBJECT (&val, SWFDEC_AS_OBJECT (fun));
1313 if (context->version < 7) {
1314 swfdec_as_object_set_variable_and_flags (new, SWFDEC_AS_STR_constructor,
1315 &val, SWFDEC_AS_VARIABLE_HIDDEN);
1317 swfdec_as_object_set_variable_and_flags (new, SWFDEC_AS_STR___constructor__,
1318 &val, SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_VERSION_6_UP);
1320 swfdec_as_function_call_full (fun, new, TRUE, new->prototype, n_args, args, return_value);
1323 void
1324 swfdec_as_object_set_constructor_by_name (SwfdecAsObject *object, const char *name, ...)
1326 SwfdecAsContext *context;
1327 SwfdecAsObject *cur;
1328 SwfdecAsValue val;
1329 va_list args;
1331 g_return_if_fail (SWFDEC_IS_AS_OBJECT (object));
1332 g_return_if_fail (name != NULL);
1334 context = swfdec_gc_object_get_context (object);
1335 va_start (args, name);
1336 cur = context->global;
1337 do {
1338 if (!swfdec_as_object_get_variable (cur, name, &val) ||
1339 !SWFDEC_AS_VALUE_IS_OBJECT (&val)) {
1340 SWFDEC_WARNING ("could not find constructor %s", name);
1341 return;
1343 cur = SWFDEC_AS_VALUE_GET_OBJECT (&val);
1344 name = va_arg (args, const char *);
1345 } while (name != NULL);
1346 va_end (args);
1347 swfdec_as_object_set_constructor (object, cur);
1351 * swfdec_as_object_set_constructor:
1352 * @object: a #SwfdecAsObject
1353 * @construct: the constructor of @object
1355 * Sets the constructor variables for @object. Most objects get these
1356 * variables set automatically, but for objects you created yourself, you want
1357 * to call this function. This is essentially the same as the following script
1358 * code:
1359 * |[ object.constructor = construct;
1360 * object.__proto__ = construct.prototype; ]|
1362 void
1363 swfdec_as_object_set_constructor (SwfdecAsObject *object, SwfdecAsObject *construct)
1365 SwfdecAsValue val;
1367 g_return_if_fail (SWFDEC_IS_AS_OBJECT (object));
1368 g_return_if_fail (SWFDEC_IS_AS_OBJECT (construct));
1370 SWFDEC_AS_VALUE_SET_OBJECT (&val, construct);
1371 swfdec_as_object_set_variable_and_flags (object, SWFDEC_AS_STR_constructor,
1372 &val, SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_PERMANENT);
1373 swfdec_as_object_get_variable (SWFDEC_AS_OBJECT (construct),
1374 SWFDEC_AS_STR_prototype, &val);
1375 if (SWFDEC_AS_VALUE_IS_OBJECT (&val)) {
1376 SwfdecAsObject *proto = SWFDEC_AS_VALUE_GET_OBJECT (&val);
1377 SWFDEC_AS_VALUE_SET_OBJECT (&val, proto);
1378 swfdec_as_object_set_variable_and_flags (object, SWFDEC_AS_STR___proto__,
1379 &val, SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_PERMANENT);
1380 } else {
1381 SWFDEC_WARNING ("constructor has no prototype, not setting any");
1386 * swfdec_as_object_add_variable:
1387 * @object: a #SwfdecAsObject
1388 * @variable: name of the variable
1389 * @get: getter function to call when reading the variable
1390 * @set: setter function to call when writing the variable or %NULL if read-only
1391 * @default_flags: flags to use if creating the variable anew - the flags will
1392 * be ignored if the property already exists.
1394 * Adds a variable to @object in the same way as the Actionscript code
1395 * "object.addProperty()" would do. Accessing the variable will from now on be
1396 * handled by calling the @get or @set functions. A previous value of the
1397 * variable or a previous call to this function will be overwritten.
1399 void
1400 swfdec_as_object_add_variable (SwfdecAsObject *object, const char *variable,
1401 SwfdecAsFunction *get, SwfdecAsFunction *set, guint default_flags)
1403 SwfdecAsVariable *var;
1405 g_return_if_fail (SWFDEC_IS_AS_OBJECT (object));
1406 g_return_if_fail (variable != NULL);
1407 g_return_if_fail (SWFDEC_IS_AS_FUNCTION (get));
1408 g_return_if_fail (set == NULL || SWFDEC_IS_AS_FUNCTION (set));
1410 var = swfdec_as_object_hash_lookup (object, variable);
1411 if (var == NULL)
1412 var = swfdec_as_object_hash_create (object, variable, default_flags);
1413 if (var == NULL)
1414 return;
1415 var->get = get;
1416 var->set = set;
1419 void
1420 swfdec_as_object_add_native_variable (SwfdecAsObject *object,
1421 const char *variable, SwfdecAsNative get, SwfdecAsNative set)
1423 SwfdecAsFunction *get_func, *set_func;
1425 g_return_if_fail (SWFDEC_IS_AS_OBJECT (object));
1426 g_return_if_fail (variable != NULL);
1427 g_return_if_fail (get != NULL);
1429 get_func =
1430 swfdec_as_native_function_new (swfdec_gc_object_get_context (object), variable, get, NULL);
1431 if (get_func == NULL)
1432 return;
1434 if (set != NULL) {
1435 set_func =
1436 swfdec_as_native_function_new (swfdec_gc_object_get_context (object), variable, set, NULL);
1437 } else {
1438 set_func = NULL;
1441 swfdec_as_object_add_variable (object, variable, get_func, set_func, 0);
1444 /*** AS CODE ***/
1446 SWFDEC_AS_NATIVE (101, 2, swfdec_as_object_addProperty)
1447 void
1448 swfdec_as_object_addProperty (SwfdecAsContext *cx, SwfdecAsObject *object,
1449 guint argc, SwfdecAsValue *argv, SwfdecAsValue *retval)
1451 SwfdecAsFunction *get, *set;
1452 const char *name;
1454 SWFDEC_AS_VALUE_SET_BOOLEAN (retval, FALSE);
1455 if (argc < 3)
1456 return;
1457 name = swfdec_as_value_to_string (cx, &argv[0]);
1458 if (!SWFDEC_AS_VALUE_IS_OBJECT (&argv[1]) ||
1459 !SWFDEC_IS_AS_FUNCTION ((get = (SwfdecAsFunction *) SWFDEC_AS_VALUE_GET_OBJECT (&argv[1]))))
1460 return;
1461 if (SWFDEC_AS_VALUE_IS_OBJECT (&argv[2])) {
1462 set = (SwfdecAsFunction *) SWFDEC_AS_VALUE_GET_OBJECT (&argv[2]);
1463 if (!SWFDEC_IS_AS_FUNCTION (set))
1464 return;
1465 } else if (SWFDEC_AS_VALUE_IS_NULL (&argv[2])) {
1466 set = NULL;
1467 } else {
1468 return;
1471 swfdec_as_object_add_variable (object, name, get, set, 0);
1472 SWFDEC_AS_VALUE_SET_BOOLEAN (retval, TRUE);
1475 SWFDEC_AS_NATIVE (101, 5, swfdec_as_object_hasOwnProperty)
1476 void
1477 swfdec_as_object_hasOwnProperty (SwfdecAsContext *cx, SwfdecAsObject *object,
1478 guint argc, SwfdecAsValue *argv, SwfdecAsValue *retval)
1480 SwfdecAsVariable *var;
1481 const char *name;
1483 if (object == NULL)
1484 return;
1486 SWFDEC_AS_VALUE_SET_BOOLEAN (retval, FALSE);
1488 // return false even if no params
1489 if (argc < 1)
1490 return;
1492 name = swfdec_as_value_to_string (swfdec_gc_object_get_context (object), &argv[0]);
1494 if (!(var = swfdec_as_object_hash_lookup (object, name)))
1495 return;
1497 /* This functions only checks NOT 6 flag, and checks it on ALL VERSIONS */
1498 if (var->flags & SWFDEC_AS_VARIABLE_VERSION_NOT_6)
1499 return;
1501 SWFDEC_AS_VALUE_SET_BOOLEAN (retval, TRUE);
1504 SWFDEC_AS_NATIVE (101, 7, swfdec_as_object_isPropertyEnumerable)
1505 void
1506 swfdec_as_object_isPropertyEnumerable (SwfdecAsContext *cx,
1507 SwfdecAsObject *object, guint argc, SwfdecAsValue *argv,
1508 SwfdecAsValue *retval)
1510 SwfdecAsVariable *var;
1511 const char *name;
1513 if (object == NULL)
1514 return;
1516 SWFDEC_AS_VALUE_SET_BOOLEAN (retval, FALSE);
1518 // return false even if no params
1519 if (argc < 1)
1520 return;
1522 name = swfdec_as_value_to_string (swfdec_gc_object_get_context (object), &argv[0]);
1524 if (!(var = swfdec_as_object_hash_lookup (object, name)))
1525 return;
1527 if (var->flags & SWFDEC_AS_VARIABLE_HIDDEN)
1528 return;
1530 SWFDEC_AS_VALUE_SET_BOOLEAN (retval, TRUE);
1533 SWFDEC_AS_NATIVE (101, 6, swfdec_as_object_isPrototypeOf)
1534 void
1535 swfdec_as_object_isPrototypeOf (SwfdecAsContext *cx,
1536 SwfdecAsObject *object, guint argc, SwfdecAsValue *argv,
1537 SwfdecAsValue *retval)
1539 SwfdecAsObject *class;
1541 SWFDEC_AS_VALUE_SET_BOOLEAN (retval, FALSE);
1543 // return false even if no params
1544 if (argc < 1)
1545 return;
1547 class = swfdec_as_value_to_object (cx, &argv[0]);
1548 if (class == NULL)
1549 return;
1551 while ((class = swfdec_as_object_get_prototype (class)) != NULL) {
1552 if (object == class) {
1553 SWFDEC_AS_VALUE_SET_BOOLEAN (retval, TRUE);
1554 return;
1558 // not found, nothing to do
1561 SWFDEC_AS_NATIVE (101, 0, swfdec_as_object_watch)
1562 void
1563 swfdec_as_object_watch (SwfdecAsContext *cx, SwfdecAsObject *object,
1564 guint argc, SwfdecAsValue *argv, SwfdecAsValue *retval)
1566 SwfdecAsWatch *watch;
1567 const char *name;
1569 SWFDEC_AS_VALUE_SET_BOOLEAN (retval, FALSE);
1571 if (argc < 2)
1572 return;
1574 name = swfdec_as_value_to_string (cx, &argv[0]);
1576 if (!SWFDEC_AS_VALUE_IS_OBJECT (&argv[1]))
1577 return;
1579 if (!SWFDEC_IS_AS_FUNCTION (SWFDEC_AS_VALUE_GET_OBJECT (&argv[1])))
1580 return;
1582 if (object->watches == NULL) {
1583 object->watches = g_hash_table_new_full (g_direct_hash, g_direct_equal,
1584 NULL, (GDestroyNotify) swfdec_as_watch_unref);
1585 watch = NULL;
1586 } else {
1587 watch = g_hash_table_lookup (object->watches, name);
1589 if (watch == NULL) {
1590 watch = swfdec_as_watch_new (SWFDEC_AS_FUNCTION (SWFDEC_AS_VALUE_GET_OBJECT (&argv[1])));
1591 if (watch == NULL)
1592 return;
1593 g_hash_table_insert (object->watches, (char *) name, watch);
1594 } else {
1595 watch->watch = SWFDEC_AS_FUNCTION (SWFDEC_AS_VALUE_GET_OBJECT (&argv[1]));
1598 if (argc >= 3) {
1599 watch->watch_data = argv[2];
1600 } else {
1601 SWFDEC_AS_VALUE_SET_UNDEFINED (&watch->watch_data);
1604 SWFDEC_AS_VALUE_SET_BOOLEAN (retval, TRUE);
1607 SWFDEC_AS_NATIVE (101, 1, swfdec_as_object_unwatch)
1608 void
1609 swfdec_as_object_unwatch (SwfdecAsContext *cx, SwfdecAsObject *object,
1610 guint argc, SwfdecAsValue *argv, SwfdecAsValue *retval)
1612 SwfdecAsVariable *var;
1613 const char *name;
1615 if (object == NULL)
1616 return;
1618 SWFDEC_AS_VALUE_SET_BOOLEAN (retval, FALSE);
1620 if (argc < 1)
1621 return;
1623 name = swfdec_as_value_to_string (cx, &argv[0]);
1625 // special case: can't unwatch native properties
1626 if ((var = swfdec_as_object_hash_lookup (object, name))&& var->get != NULL)
1627 return;
1629 if (object->watches != NULL &&
1630 g_hash_table_remove (object->watches, name)) {
1632 SWFDEC_AS_VALUE_SET_BOOLEAN (retval, TRUE);
1634 if (g_hash_table_size (object->watches) == 0) {
1635 g_hash_table_destroy (object->watches);
1636 object->watches = NULL;
1641 SWFDEC_AS_NATIVE (101, 3, swfdec_as_object_valueOf)
1642 void
1643 swfdec_as_object_valueOf (SwfdecAsContext *cx, SwfdecAsObject *object,
1644 guint argc, SwfdecAsValue *argv, SwfdecAsValue *retval)
1646 if (object != NULL)
1647 SWFDEC_AS_VALUE_SET_OBJECT (retval, object);
1650 SWFDEC_AS_NATIVE (101, 4, swfdec_as_object_toString)
1651 void
1652 swfdec_as_object_toString (SwfdecAsContext *cx, SwfdecAsObject *object,
1653 guint argc, SwfdecAsValue *argv, SwfdecAsValue *retval)
1655 if (SWFDEC_IS_AS_FUNCTION (object)) {
1656 SWFDEC_AS_VALUE_SET_STRING (retval, SWFDEC_AS_STR__type_Function_);
1657 } else {
1658 SWFDEC_AS_VALUE_SET_STRING (retval, SWFDEC_AS_STR__object_Object_);
1662 // only available as ASnative
1663 SWFDEC_AS_NATIVE (3, 3, swfdec_as_object_old_constructor)
1664 void
1665 swfdec_as_object_old_constructor (SwfdecAsContext *cx, SwfdecAsObject *object,
1666 guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
1668 SWFDEC_STUB ("old 'Object' function (only available as ASnative)");
1671 void
1672 swfdec_as_object_decode (SwfdecAsObject *object, const char *str)
1674 SwfdecAsContext *cx = swfdec_gc_object_get_context (object);
1675 SwfdecAsValue val;
1676 char **varlist, *p, *unescaped;
1677 guint i;
1679 g_return_if_fail (SWFDEC_IS_AS_OBJECT (object));
1680 g_return_if_fail (str != NULL);
1682 varlist = g_strsplit (str, "&", -1);
1684 for (i = 0; varlist[i] != NULL; i++) {
1685 p = strchr (varlist[i], '=');
1686 if (p != NULL) {
1687 *p++ = '\0';
1688 if (*p == '\0')
1689 p = NULL;
1692 if (p != NULL) {
1693 unescaped = swfdec_as_string_unescape (cx, p);
1694 if (unescaped != NULL) {
1695 SWFDEC_AS_VALUE_SET_STRING (&val,
1696 swfdec_as_context_give_string (cx, unescaped));
1697 } else {
1698 SWFDEC_AS_VALUE_SET_STRING (&val, SWFDEC_AS_STR_EMPTY);
1700 } else {
1701 SWFDEC_AS_VALUE_SET_STRING (&val, SWFDEC_AS_STR_EMPTY);
1703 unescaped = swfdec_as_string_unescape (cx, varlist[i]);
1704 if (unescaped != NULL) {
1705 swfdec_as_object_set_variable (object,
1706 swfdec_as_context_give_string (cx, unescaped), &val);
1709 g_strfreev (varlist);
1712 static void
1713 swfdec_as_object_construct (SwfdecAsContext *cx, SwfdecAsObject *object,
1714 guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
1716 if (argc > 0) {
1717 SwfdecAsObject *result = swfdec_as_value_to_object (cx, &argv[0]);
1718 if (result != NULL) {
1719 if (!cx->frame->construct) {
1720 SWFDEC_AS_VALUE_SET_OBJECT (ret, result);
1721 } else {
1722 SWFDEC_FIXME ("new Object (x) should return x");
1723 SWFDEC_AS_VALUE_SET_OBJECT (ret, object);
1725 return;
1729 if (!cx->frame->construct)
1730 object = swfdec_as_object_new_empty (cx);
1732 SWFDEC_AS_VALUE_SET_OBJECT (ret, object);
1735 void
1736 swfdec_as_object_init_context (SwfdecAsContext *context)
1738 SwfdecAsValue val;
1739 SwfdecAsObject *object, *proto;
1741 proto = swfdec_as_object_new_empty (context);
1742 object = SWFDEC_AS_OBJECT (swfdec_as_object_add_function (context->global,
1743 SWFDEC_AS_STR_Object, swfdec_as_object_construct));
1744 context->Object = object;
1745 context->Object_prototype = proto;
1746 SWFDEC_AS_VALUE_SET_OBJECT (&val, proto);
1747 /* first, set our own */
1748 swfdec_as_object_set_variable_and_flags (object, SWFDEC_AS_STR_prototype,
1749 &val, SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_PERMANENT |
1750 SWFDEC_AS_VARIABLE_CONSTANT);
1752 /* then finish the function prototype (use this order or
1753 * SWFDEC_AS_VARIABLE_CONSTANT won't let us */
1754 swfdec_as_object_set_variable_and_flags (context->Function_prototype,
1755 SWFDEC_AS_STR___proto__, &val,
1756 SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_PERMANENT);
1758 SWFDEC_AS_VALUE_SET_OBJECT (&val, object);
1759 swfdec_as_object_set_variable_and_flags (proto, SWFDEC_AS_STR_constructor,
1760 &val, SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_PERMANENT);
1764 * swfdec_as_object_get_debug:
1765 * @object: a #SwfdecAsObject
1767 * Gets a representation string suitable for debugging. This function is
1768 * guaranteed to not modify the state of the script engine, unlike
1769 * swfdec_as_value_to_string() for example.
1771 * Returns: A newly allocated string. Free it with g_free() after use.
1773 char *
1774 swfdec_as_object_get_debug (SwfdecAsObject *object)
1776 SwfdecAsObjectClass *klass;
1778 g_return_val_if_fail (SWFDEC_IS_AS_OBJECT (object), NULL);
1780 klass = SWFDEC_AS_OBJECT_GET_CLASS (object);
1781 return klass->debug (object);
1785 * swfdec_as_object_resolve:
1786 * @object: a #SwfdecAsObject
1788 * Resolves the object to its real object. Some internal objects should not be
1789 * exposed to scripts, for example #SwfdecAsFrame objects. If an object you want
1790 * to expose might be internal, call this function to resolve it to an object
1791 * that is safe to expose.
1793 * Returns: a non-internal object
1795 SwfdecAsObject *
1796 swfdec_as_object_resolve (SwfdecAsObject *object)
1798 SwfdecAsObjectClass *klass;
1800 g_return_val_if_fail (SWFDEC_IS_AS_OBJECT (object), NULL);
1802 klass = SWFDEC_AS_OBJECT_GET_CLASS (object);
1803 if (klass->resolve == NULL)
1804 return object;
1806 return klass->resolve (object);