[Swfdec] 17 commits - libswfdec/js libswfdec/swfdec_js.c libswfdec/swfdec_js_global.c libswfdec/swfdec_js.h libswfdec/swfdec_js_movie.c libswfdec/swfdec_movie.c libswfdec/swfdec_player.c libswfdec/swfdec_player_internal.h libswfdec/swfdec_root_movie.c libswfdec/swfdec_root_movie.h libswfdec/swfdec_script.c player/swfdec_player_manager.c test/trace

Benjamin Otte company at kemper.freedesktop.org
Fri Mar 9 08:41:49 PST 2007


 libswfdec/js/jsinterp.c            |    3 
 libswfdec/js/jsinterp.h            |    1 
 libswfdec/swfdec_js.c              |   51 +++++++++
 libswfdec/swfdec_js.h              |    8 +
 libswfdec/swfdec_js_global.c       |   30 +++++
 libswfdec/swfdec_js_movie.c        |  181 +++++++++++++++++++++++++----------
 libswfdec/swfdec_movie.c           |   22 ----
 libswfdec/swfdec_player.c          |   54 ++++++++++
 libswfdec/swfdec_player_internal.h |    7 +
 libswfdec/swfdec_root_movie.c      |   24 ++++
 libswfdec/swfdec_root_movie.h      |    2 
 libswfdec/swfdec_script.c          |  191 +++++++++++++++++++++----------------
 player/swfdec_player_manager.c     |    1 
 test/trace/Makefile.am             |    2 
 test/trace/prototypes.swf          |binary
 test/trace/prototypes.swf.trace    |    7 +
 16 files changed, 432 insertions(+), 152 deletions(-)

New commits:
diff-tree 09541f87302d441396064f480fa3c8da0a07993e (from 46721865097ed6bd7caab49c1418491d882421aa)
Author: Benjamin Otte <otte at gnome.org>
Date:   Fri Mar 9 17:41:20 2007 +0100

    improve stack management
    
    - use new spend member to track end of stack
    - in Actionscript, the stack is infinite (initially all values are undefined)
      Implement this concept via swfdec_script_ensure_stack
    - don't pass the number of free stack spaces to functions anymore now that
      we know where the stack ends

diff --git a/libswfdec/swfdec_script.c b/libswfdec/swfdec_script.c
index 428fc4c..50dbf2b 100644
--- a/libswfdec/swfdec_script.c
+++ b/libswfdec/swfdec_script.c
@@ -125,6 +125,42 @@ swfdec_constant_pool_get_area (SwfdecScr
 
 /*** SUPPORT FUNCTIONS ***/
 
+/**
+ * swfdec_script_ensure_stack:
+ * @cx: #JSContext to check
+ * @n_elements: number of elements the stack should contain
+ *
+ * Ensures that the stack is at least @n_elements values. If not enough stack
+ * space is available, the stack is filled up with JSVAL_VOID.
+ *
+ * Returns: JS_TRUE on success or JS_FALSE on OOM
+ **/
+static inline JSBool
+swfdec_script_ensure_stack (JSContext *cx, guint n_elements)
+{
+  JSStackFrame *fp = cx->fp;
+  guint current = (guint) (fp->sp - fp->spbase);
+
+  if (current >= n_elements)
+    return JS_TRUE;
+
+  if (n_elements > (guint) (fp->spend - fp->spbase)) {
+    SWFDEC_ERROR ("FIXME: implement stack expansion, we got an overflow (want %u, have %u)",
+	n_elements, (fp->spend - fp->spbase));
+    return JS_FALSE;
+  }
+
+  if (current) {
+    n_elements -= current;
+    memmove (fp->spbase + n_elements, fp->spbase, (fp->sp - fp->spbase) * sizeof (jsval));
+  }
+  while (n_elements)  {
+    n_elements--;
+    fp->spbase[n_elements] = JSVAL_VOID;
+  }
+  return JS_TRUE;
+}
+
 static gboolean
 swfdec_action_has_register (JSContext *cx, guint i)
 {
@@ -452,10 +488,11 @@ swfdec_action_constant_pool (JSContext *
 }
 
 static JSBool
-swfdec_action_push (JSContext *cx, guint stackspace, const guint8 *data, guint len)
+swfdec_action_push (JSContext *cx, guint action, const guint8 *data, guint len)
 {
   /* FIXME: supply API for this */
   SwfdecBits bits;
+  guint stackspace = cx->fp->spend - cx->fp->sp;
 
   swfdec_bits_init_data (&bits, data, len);
   while (swfdec_bits_left (&bits) && stackspace-- > 0) {
@@ -644,7 +681,7 @@ swfdec_action_call_function (JSContext *
     return JS_FALSE;
   if (!JS_ValueToECMAUint32 (cx, fp->sp[-2], &n_args))
     return JS_FALSE;
-  if (n_args + 2 > (guint) (fp->sp - fp->spbase))
+  if (!swfdec_script_ensure_stack (cx, n_args + 2))
     return JS_FALSE;
   
   if (!(atom = js_Atomize (cx, s, strlen (s), 0)) ||
@@ -676,7 +713,7 @@ swfdec_action_call_method (JSContext *cx
     return JS_FALSE;
   if (!JS_ValueToECMAUint32 (cx, fp->sp[-3], &n_args))
     return JS_FALSE;
-  if (n_args + 3 > (guint) (fp->sp - fp->spbase))
+  if (!swfdec_script_ensure_stack (cx, n_args + 3))
     return JS_FALSE;
   
   if (!JS_ValueToObject (cx, fp->sp[-2], &obj))
@@ -1343,16 +1380,15 @@ static JSBool
 swfdec_action_start_drag (JSContext *cx, guint action, const guint8 *data, guint len)
 {
   JSStackFrame *fp = cx->fp;
-  guint stack_size = fp->sp - fp->spbase;
   guint n_args = 1;
 
-  if (stack_size < 3)
+  if (!swfdec_script_ensure_stack (cx, 3))
     return JS_FALSE;
   if (!swfdec_eval_jsval (cx, NULL, &fp->sp[-1]))
     return JS_FALSE;
   if (swfdec_value_to_number (cx, fp->sp[-3])) {
     jsval tmp;
-    if (stack_size < 7)
+    if (!swfdec_script_ensure_stack (cx, 7))
       return JS_FALSE;
     n_args = 5;
     /* yay for order */
@@ -1408,7 +1444,7 @@ swfdec_action_new_object (JSContext *cx,
     return JS_FALSE;
   if (!JS_ValueToECMAUint32 (cx, fp->sp[-2], &n_args))
     return JS_FALSE;
-  if ((guint) (fp->sp - fp->spbase) < n_args + 2) {
+  if (!swfdec_script_ensure_stack (cx, n_args + 2)) {
     SWFDEC_ERROR ("not enough stack space");
     return JS_FALSE;
   }
@@ -1447,7 +1483,7 @@ swfdec_action_new_method (JSContext *cx,
     return JS_FALSE;
   if (!JS_ValueToECMAUint32 (cx, fp->sp[-3], &n_args))
     return JS_FALSE;
-  if (n_args + 3 > (guint) (fp->sp - fp->spbase))
+  if (!swfdec_script_ensure_stack (cx, n_args + 3))
     return JS_FALSE;
   
   if (!JS_ValueToObject (cx, fp->sp[-2], &object))
@@ -1489,10 +1525,8 @@ swfdec_action_init_object (JSContext *cx
 
   if (!JS_ValueToECMAUint32 (cx, fp->sp[-1], &n_args))
     return JS_FALSE;
-  if ((guint) (fp->sp - fp->spbase) < 2 * n_args + 1) {
-    SWFDEC_ERROR ("not enough stack space");
+  if (!swfdec_script_ensure_stack (cx, 2 * n_args + 1))
     return JS_FALSE;
-  }
 
   object = JS_NewObject (cx, &js_ObjectClass, NULL, NULL);
   if (object == NULL)
@@ -1519,10 +1553,8 @@ swfdec_action_init_array (JSContext *cx,
 
   if (!JS_ValueToECMAUint32 (cx, fp->sp[-1], &n_items))
     return JS_FALSE;
-  if ((guint) (fp->sp - fp->spbase) < n_items + 1) {
-    SWFDEC_ERROR ("not enough stack space");
+  if (!swfdec_script_ensure_stack (cx, n_items + 1))
     return JS_FALSE;
-  }
 
   /* items are the wrong order on the stack */
   j = - 1 - n_items;
@@ -1540,8 +1572,8 @@ swfdec_action_init_array (JSContext *cx,
 }
 
 static JSBool
-swfdec_action_do_define_function (JSContext *cx, guint action,
-    const guint8 *data, guint len, gboolean v2)
+swfdec_action_define_function (JSContext *cx, guint action,
+    const guint8 *data, guint len)
 {
   const char *function_name;
   guint i, n_args, size;
@@ -1551,6 +1583,7 @@ swfdec_action_do_define_function (JSCont
   gboolean has_preloads = FALSE;
   guint flags = 0;
   guint8 *preloads = NULL;
+  gboolean v2 = (action == 0x8e);
 
   swfdec_bits_init_data (&bits, data, len);
   function_name = swfdec_bits_get_string (&bits);
@@ -1644,7 +1677,7 @@ swfdec_action_do_define_function (JSCont
   fun->swf = script;
   /* attach the function */
   if (*function_name == '\0') {
-    if (action == 0) {
+    if (cx->fp->sp >= cx->fp->spend) {
       SWFDEC_ERROR ("not enough stack space available");
       return JS_FALSE;
     }
@@ -1661,18 +1694,6 @@ swfdec_action_do_define_function (JSCont
 }
 
 static JSBool
-swfdec_action_define_function (JSContext *cx, guint action, const guint8 *data, guint len)
-{
-  return swfdec_action_do_define_function (cx, action, data, len, FALSE);
-}
-
-static JSBool
-swfdec_action_define_function2 (JSContext *cx, guint action, const guint8 *data, guint len)
-{
-  return swfdec_action_do_define_function (cx, action, data, len, TRUE);
-}
-
-static JSBool
 swfdec_action_bitwise (JSContext *cx, guint action, const guint8 *data, guint len)
 {
   guint32 a, b;
@@ -2352,7 +2373,7 @@ static const SwfdecActionSpec actions[25
   /* version 4 */
   [0x8d] = { "WaitForFrame2", swfdec_action_print_wait_for_frame2, 1, 0, { NULL, swfdec_action_wait_for_frame2, swfdec_action_wait_for_frame2, swfdec_action_wait_for_frame2, swfdec_action_wait_for_frame2 } },
   /* version 7 */
-  [0x8e] = { "DefineFunction2", swfdec_action_print_define_function, 0, -1, { NULL, NULL, NULL, NULL, swfdec_action_define_function2 } },
+  [0x8e] = { "DefineFunction2", swfdec_action_print_define_function, 0, -1, { NULL, NULL, NULL, swfdec_action_define_function, swfdec_action_define_function } },
   [0x8f] = { "Try", NULL },
   /* version 5 */
   [0x94] = { "With", NULL },
@@ -2560,7 +2581,10 @@ swfdec_script_interpret (SwfdecScript *s
   guint8 *startpc, *pc, *endpc, *nextpc;
   JSBool ok = JS_TRUE;
   void *mark;
-  jsval *startsp, *endsp, *checksp;
+  jsval *startsp;
+#ifndef G_DISABLE_ASSERT
+  jsval *checksp;
+#endif
   int stack_check;
   guint action, len;
   guint8 *data;
@@ -2636,8 +2660,8 @@ swfdec_script_interpret (SwfdecScript *s
     goto out;
   }
   fp->spbase = startsp;
+  fp->spend = startsp + STACKSIZE;
   fp->sp = startsp;
-  endsp = startsp + STACKSIZE;
   /* Check for too much nesting, or too deep a C stack. */
   if (++cx->interpLevel == MAX_INTERP_LEVEL ||
       !JS_CHECK_STACK_SIZE(cx, stack_check)) {
@@ -2720,33 +2744,32 @@ swfdec_script_interpret (SwfdecScript *s
 	  spec->name ? spec->name : "Unknown", script->version);
       goto internal_error;
     }
-    if (fp->sp - spec->remove < startsp) {
-      SWFDEC_ERROR ("stack underflow while trying to execute %s - requires %u args, only got %u", 
-	  spec->name, spec->remove, fp->sp - fp->spbase);
-      goto internal_error;
+    if (spec->remove > 0 &&
+	!swfdec_script_ensure_stack (cx, spec->remove)) {
+      ok = JS_FALSE;
+      goto out;
     }
-    if (spec->add < 0) {
-      /* HACK FIXME: if added args are -1, we pass the number of free space on the stack 
-       * instead of the action */
-      action = endsp - fp->sp;
-    } else {
-      if (fp->sp + spec->add - MAX (spec->remove, 0) > endsp) {
-	SWFDEC_ERROR ("FIXME: implement stack expansion, we got an overflow");
-	goto internal_error;
-      }
+    if (spec->add > 0 &&
+	fp->sp + spec->add - MAX (spec->remove, 0) > fp->spend) {
+      SWFDEC_ERROR ("FIXME: implement stack expansion, we got an overflow");
+      goto internal_error;
     }
+#ifndef G_DISABLE_ASSERT
     checksp = (spec->add >= 0 && spec->remove >= 0) ? fp->sp + spec->add - spec->remove : NULL;
+#endif
     ok = spec->exec[version] (cx, action, data, len);
     if (!ok) {
       SWFDEC_WARNING ("action %s failed", spec->name);
       goto out;
     }
+#ifndef G_DISABLE_ASSERT
     if (checksp != NULL && checksp != fp->sp) {
       /* check stack was handled like expected */
       g_error ("action %s was supposed to change the stack by %d (+%d -%d), but it changed by %d",
 	  spec->name, spec->add - spec->remove, spec->add, spec->remove,
 	  fp->sp - checksp + spec->add - spec->remove);
     }
+#endif
     if (fp->pc == pc) {
       fp->pc = pc = nextpc;
     } else {
diff-tree 46721865097ed6bd7caab49c1418491d882421aa (from 1066bfc19c0661013ff1801e3a4afb34869075a4)
Author: Benjamin Otte <otte at gnome.org>
Date:   Fri Mar 9 17:38:34 2007 +0100

    add an spend member to JSStackFrame to track the end of the stack

diff --git a/libswfdec/js/jsinterp.c b/libswfdec/js/jsinterp.c
index ab095f6..121962f 100644
--- a/libswfdec/js/jsinterp.c
+++ b/libswfdec/js/jsinterp.c
@@ -844,6 +844,7 @@ have_fun:
     frame.scopeChain = NULL;    /* set below for real, after cx->fp is set */
     frame.pc = NULL;
     frame.spbase = NULL;
+    frame.spend = NULL;
     frame.sharpDepth = 0;
     frame.sharpArray = NULL;
     frame.dormantNext = NULL;
@@ -1138,6 +1139,7 @@ js_Execute(JSContext *cx, JSObject *chai
     frame.pc = NULL;
     frame.sp = oldfp ? oldfp->sp : NULL;
     frame.spbase = NULL;
+    frame.spend = NULL;
     frame.sharpDepth = 0;
     frame.flags = special;
     frame.dormantNext = NULL;
@@ -1502,6 +1504,7 @@ js_Interpret(JSContext *cx, jsval *resul
     }
     sp = newsp + depth;
     fp->spbase = sp;
+    fp->spend = sp + depth;
     SAVE_SP(fp);
 
     while (pc < endpc) {
diff --git a/libswfdec/js/jsinterp.h b/libswfdec/js/jsinterp.h
index 88bab68..32bb3a1 100644
--- a/libswfdec/js/jsinterp.h
+++ b/libswfdec/js/jsinterp.h
@@ -70,6 +70,7 @@ struct JSStackFrame {
     jsbytecode      *pc;            /* program counter */
     jsval           *sp;            /* stack pointer */
     jsval           *spbase;        /* operand stack base */
+    jsval           *spend;         /* end of available stack space */
     uintN           sharpDepth;     /* array/object initializer depth */
     JSObject        *sharpArray;    /* scope for #n= initializer vars */
     uint32          flags;          /* frame flags -- see below */
diff-tree 1066bfc19c0661013ff1801e3a4afb34869075a4 (from f8eb86aa128f859a0c7af715f19bb12e60f83ee9)
Author: Benjamin Otte <otte at gnome.org>
Date:   Fri Mar 9 14:13:28 2007 +0100

    add test for MovieClip's prototype and property handling

diff --git a/test/trace/Makefile.am b/test/trace/Makefile.am
index 19a1340..69b995a 100644
--- a/test/trace/Makefile.am
+++ b/test/trace/Makefile.am
@@ -111,6 +111,8 @@ EXTRA_DIST = \
 	parent-root.swf.trace \
 	preload.swf \
 	preload.swf.trace \
+	prototypes.swf \
+	prototypes.swf.trace \
 	rotation-5.swf \
 	rotation-5.swf.trace \
 	scope.swf \
diff --git a/test/trace/prototypes.swf b/test/trace/prototypes.swf
new file mode 100755
index 0000000..40302b8
Binary files /dev/null and b/test/trace/prototypes.swf differ
diff --git a/test/trace/prototypes.swf.trace b/test/trace/prototypes.swf.trace
new file mode 100755
index 0000000..09ec107
--- /dev/null
+++ b/test/trace/prototypes.swf.trace
@@ -0,0 +1,7 @@
+Check prototypes of movie clips
+undefined
+[object Object]
+undefined
+undefined
+[object Object]
+undefined
diff-tree f8eb86aa128f859a0c7af715f19bb12e60f83ee9 (from 928730a12c6bb59a21a931f814688dcac7f74580)
Author: Benjamin Otte <otte at gnome.org>
Date:   Fri Mar 9 14:11:06 2007 +0100

    use constructors registered via Object.registerClass

diff --git a/libswfdec/swfdec_js_movie.c b/libswfdec/swfdec_js_movie.c
index fce61ef..1aa2b87 100644
--- a/libswfdec/swfdec_js_movie.c
+++ b/libswfdec/swfdec_js_movie.c
@@ -26,6 +26,7 @@
 #include <math.h>
 
 #include <js/jsapi.h>
+#include <js/jsinterp.h> /* for JS_IntetrnalCall */
 #include "swfdec_js.h"
 #include "swfdec_movie.h"
 #include "swfdec_bits.h"
@@ -605,11 +606,13 @@ swfdec_js_movie_to_string (JSContext *cx
   SwfdecMovie *movie;
 
   movie = JS_GetPrivate (cx, obj);
-  g_assert (movie);
-
-  s = swfdec_movie_get_path (movie);
-  string = JS_NewStringCopyZ (cx, s);
-  g_free (s);
+  if (movie) {
+    s = swfdec_movie_get_path (movie);
+    string = JS_NewStringCopyZ (cx, s);
+    g_free (s);
+  } else {
+    string = JS_NewStringCopyZ (cx, "[object Object]");
+  }
   if (string == NULL)
     return JS_FALSE;
   *rval = STRING_TO_JSVAL (string);
@@ -1230,21 +1233,32 @@ void
 swfdec_js_add_movieclip_class (SwfdecPlayer *player)
 {
   JS_InitClass (player->jscx, player->jsobj, NULL,
-      &movieclip_class, swfdec_js_movieclip_new, 0, movieclip_props, movieclip_methods,
+      &movieclip_class, swfdec_js_movieclip_new, 0, NULL, movieclip_methods,
       NULL, NULL);
 }
 
-jsval
-swfdec_js_movie_lookup_class (SwfdecMovie *movie)
+static jsval
+swfdec_js_movie_lookup_class (SwfdecSpriteMovie *movie)
 {
-  return JSVAL_NULL;
+  /* FIXME: write tests on how the lookup movie => name => registered class is performed */
+  SwfdecRootMovie *root;
+  const char *name;
+  
+  /* happens with root movies only */
+  if (movie->sprite == NULL)
+    return JSVAL_NULL;
+  root = SWFDEC_ROOT_MOVIE (SWFDEC_MOVIE (movie)->root);
+  name = swfdec_root_movie_get_export_name (root, SWFDEC_CHARACTER (movie->sprite));
+  if (name == NULL)
+    return JSVAL_NULL;
+  return swfdec_player_get_export_class (root->player, name);
 }
 
 void
 swfdec_js_movie_create_jsobject	(SwfdecMovie *movie)
 {
   SwfdecScriptable *script;
-  jsval fun;
+  jsval fun = JSVAL_NULL;
 
   g_return_if_fail (SWFDEC_IS_MOVIE (movie));
 
@@ -1252,12 +1266,12 @@ swfdec_js_movie_create_jsobject	(SwfdecM
   g_return_if_fail (script->jscx != NULL);
   g_return_if_fail (script->jsobj == NULL);
 
-  fun = swfdec_js_movie_lookup_class (movie);
-  if (fun == JSVAL_NULL) {
+  if (SWFDEC_IS_SPRITE_MOVIE (movie) &&
+      (fun = swfdec_js_movie_lookup_class (SWFDEC_SPRITE_MOVIE (movie))) != JSVAL_NULL) {
+    swfdec_js_construct_object (script->jscx, &movieclip_class, fun, &script->jsobj);
+  } else {
     script->jsobj = JS_NewObject (script->jscx, &movieclip_class,
 	NULL, NULL);
-  } else {
-    swfdec_js_construct_object (script->jscx, &movieclip_class, fun, &script->jsobj);
   }
   if (!script->jsobj ||
       !JS_AddRoot (script->jscx, &script->jsobj)) {
@@ -1268,7 +1282,18 @@ swfdec_js_movie_create_jsobject	(SwfdecM
   }
   g_object_ref (script);
   JS_SetPrivate (script->jscx, script->jsobj, script);
+  if (!JS_DefineProperties (script->jscx, script->jsobj, movieclip_props)) {
+    SWFDEC_ERROR ("failed to define properties for %s %p", 
+	G_OBJECT_TYPE_NAME (movie), movie);
+    return;
+  }
   swfdec_js_movie_add_property (movie);
+  if (fun != JSVAL_NULL) {
+    SWFDEC_LOG ("Executing constructor for %s %p", G_OBJECT_TYPE_NAME (movie), movie);
+    if (!js_InternalCall (script->jscx, script->jsobj, fun, 0, NULL, &fun)) {
+      SWFDEC_ERROR ("constructor execution failed");
+    }
+  }
 }
 
 void
diff-tree 928730a12c6bb59a21a931f814688dcac7f74580 (from c37dc7eeab2b781b1400b15b8e4a1c50b846543b)
Author: Benjamin Otte <otte at gnome.org>
Date:   Fri Mar 9 14:10:31 2007 +0100

    still return JS_TRUE in the failure case, JS_FALSE is for OOM

diff --git a/libswfdec/swfdec_js.c b/libswfdec/swfdec_js.c
index 44f9cb8..3e96503 100644
--- a/libswfdec/swfdec_js.c
+++ b/libswfdec/swfdec_js.c
@@ -497,5 +497,5 @@ swfdec_js_construct_object (JSContext *c
 
 fail:
   *newp = NULL;
-  return JS_FALSE;
+  return JS_TRUE;
 }
diff-tree c37dc7eeab2b781b1400b15b8e4a1c50b846543b (from 2840d8d12a279fe3b7132f236d73789a4c5d3403)
Author: Benjamin Otte <otte at gnome.org>
Date:   Fri Mar 9 14:02:45 2007 +0100

    unregister trace signal when unsetting player

diff --git a/player/swfdec_player_manager.c b/player/swfdec_player_manager.c
index b0bdc55..1f6831c 100644
--- a/player/swfdec_player_manager.c
+++ b/player/swfdec_player_manager.c
@@ -133,6 +133,7 @@ swfdec_player_manager_set_player (Swfdec
 
   if (manager->player) {
     g_signal_handlers_disconnect_by_func (manager->player, breakpoint_hit_cb, manager);
+    g_signal_handlers_disconnect_by_func (manager->player, trace_cb, manager);
     g_object_unref (manager->player);
   }
   manager->player = player;
diff-tree 2840d8d12a279fe3b7132f236d73789a4c5d3403 (from 84b6fb897c2025028cefbd15c6c8b0ae2b19ff10)
Author: Benjamin Otte <otte at gnome.org>
Date:   Fri Mar 9 13:57:50 2007 +0100

    implement ActionGetTime
    
    the action is untested but seems to work

diff --git a/libswfdec/swfdec_script.c b/libswfdec/swfdec_script.c
index b425233..428fc4c 100644
--- a/libswfdec/swfdec_script.c
+++ b/libswfdec/swfdec_script.c
@@ -1942,6 +1942,15 @@ swfdec_action_type_of (JSContext *cx, gu
   return JS_TRUE;
 }
 
+static JSBool
+swfdec_action_get_time (JSContext *cx, guint action, const guint8 *data, guint len)
+{
+  SwfdecPlayer *player = JS_GetContextPrivate (cx);
+
+  *cx->fp->sp++ = INT_TO_JSVAL ((int) SWFDEC_TICKS_TO_MSECS (player->time));
+  return JS_TRUE;
+}
+
 /*** PRINT FUNCTIONS ***/
 
 static char *
@@ -2282,7 +2291,7 @@ static const SwfdecActionSpec actions[25
   [0x31] = { "MBStringLength", NULL },
   [0x32] = { "CharToAscii", NULL },
   [0x33] = { "AsciiToChar", NULL },
-  [0x34] = { "GetTime", NULL },
+  [0x34] = { "GetTime", NULL, 0, 1, { NULL, swfdec_action_get_time, swfdec_action_get_time, swfdec_action_get_time, swfdec_action_get_time } },
   [0x35] = { "MBStringExtract", NULL },
   [0x36] = { "MBCharToAscii", NULL },
   [0x37] = { "MVAsciiToChar", NULL },
@@ -2586,13 +2595,32 @@ swfdec_script_interpret (SwfdecScript *s
       /* FIXME: keep in sync with jsfun.c */
       fp->flags |= JS_BIT (JSFRAME_OVERRIDE_SHIFT);
     }
-    if (script->flags & SWFDEC_SCRIPT_PRELOAD_SUPER ||
-	script->flags & SWFDEC_SCRIPT_PRELOAD_ROOT ||
-	script->flags & SWFDEC_SCRIPT_PRELOAD_PARENT) {
-      SWFDEC_ERROR ("The following preload flags aren't implemented:%s%s%s",
-	  script->flags & SWFDEC_SCRIPT_PRELOAD_SUPER ? " PRELOAD_SUPER" : "",
-	  script->flags & SWFDEC_SCRIPT_PRELOAD_ROOT ? " PRELOAD_ROOT" : "",
-	  script->flags & SWFDEC_SCRIPT_PRELOAD_PARENT ? " PRELOAD_PARENT" : "");
+    if (script->flags & SWFDEC_SCRIPT_PRELOAD_SUPER) {
+      SWFDEC_ERROR ("preloading super isn't implemented");
+    }
+    if (script->flags & SWFDEC_SCRIPT_PRELOAD_ROOT) {
+      JSAtom *atom;
+      JSObject *obj, *pobj;
+      JSProperty *prop;
+      SWFDEC_LOG ("preloading root into register %u", preload_reg);
+      if (!(atom = js_Atomize (cx, "_root", 5, 0)) ||
+	  !js_FindProperty (cx, (jsid) atom, &obj, &pobj, &prop) ||
+          !js_GetProperty (cx, obj, (jsid) atom, &fp->vars[preload_reg++])) {
+	ok = JS_FALSE;
+	goto out;
+      }
+    }
+    if (script->flags & SWFDEC_SCRIPT_PRELOAD_PARENT) {
+      JSAtom *atom;
+      JSObject *obj, *pobj;
+      JSProperty *prop;
+      SWFDEC_LOG ("preloading parent into register %u", preload_reg);
+      if (!(atom = js_Atomize (cx, "_parent", 7, 0)) ||
+	  !js_FindProperty (cx, (jsid) atom, &obj, &pobj, &prop) ||
+          !js_GetProperty (cx, obj, (jsid) atom, &fp->vars[preload_reg++])) {
+	ok = JS_FALSE;
+	goto out;
+      }
     }
     if (script->flags & SWFDEC_SCRIPT_PRELOAD_GLOBAL)
       fp->vars[preload_reg++] = OBJECT_TO_JSVAL (player->jsobj);
diff-tree 84b6fb897c2025028cefbd15c6c8b0ae2b19ff10 (from a76d01ec9ac4fe630bc5acffd6e4380a3b051a9c)
Author: Benjamin Otte <otte at gnome.org>
Date:   Fri Mar 9 09:52:53 2007 +0100

    remove leftover g_print

diff --git a/libswfdec/swfdec_root_movie.c b/libswfdec/swfdec_root_movie.c
index 24a403c..6396f3d 100644
--- a/libswfdec/swfdec_root_movie.c
+++ b/libswfdec/swfdec_root_movie.c
@@ -283,6 +283,5 @@ swfdec_root_movie_get_export_name (Swfde
   if (!g_hash_table_find (root->exports, find_value, &ret))
     return NULL;
 
-  g_print ("found %s\n", (char *) ret);
   return ret;
 }
diff-tree a76d01ec9ac4fe630bc5acffd6e4380a3b051a9c (from b6a5c587aa8824b7e249a3b8bd6bd12e14114eb7)
Author: Benjamin Otte <otte at gnome.org>
Date:   Fri Mar 9 09:51:24 2007 +0100

    This function returns a boolean (TRUE on success, FALSE on failure)

diff --git a/libswfdec/swfdec_js_global.c b/libswfdec/swfdec_js_global.c
index 745e27b..bab231a 100644
--- a/libswfdec/swfdec_js_global.c
+++ b/libswfdec/swfdec_js_global.c
@@ -256,6 +256,7 @@ swfdec_js_object_register_class (JSConte
     return JS_FALSE;
   
   swfdec_player_set_export_class (player, name, argv[1]);
+  *rval = JSVAL_TRUE;
   return JS_TRUE;
 }
 
diff-tree b6a5c587aa8824b7e249a3b8bd6bd12e14114eb7 (from 6d91b775a72e20ccb85501a00a0c326109e5750a)
Author: Benjamin Otte <otte at gnome.org>
Date:   Fri Mar 9 09:45:08 2007 +0100

    implement Object.registerClass

diff --git a/libswfdec/swfdec_js_global.c b/libswfdec/swfdec_js_global.c
index 0a3b6f3..745e27b 100644
--- a/libswfdec/swfdec_js_global.c
+++ b/libswfdec/swfdec_js_global.c
@@ -242,6 +242,28 @@ static JSFunctionSpec global_methods[] =
   { NULL, NULL, 0, 0, 0 }
 };
 
+static JSBool
+swfdec_js_object_register_class (JSContext *cx, JSObject *obj, uintN argc, 
+    jsval *argv, jsval *rval)
+{
+  SwfdecPlayer *player = JS_GetContextPrivate (cx);
+  const char *name;
+  
+  name = swfdec_js_to_string (cx, argv[0]);
+  if (name == NULL)
+    return JS_FALSE;
+  if (!JSVAL_IS_OBJECT(argv[1]))
+    return JS_FALSE;
+  
+  swfdec_player_set_export_class (player, name, argv[1]);
+  return JS_TRUE;
+}
+
+static JSFunctionSpec static_object_methods[] = {
+  { "registerClass",    swfdec_js_object_register_class,	2, 0, 0 },
+  { NULL, NULL, 0, 0, 0 }
+};
+
 void
 swfdec_js_add_globals (SwfdecPlayer *player)
 {
@@ -255,5 +277,12 @@ swfdec_js_add_globals (SwfdecPlayer *pla
       found != JS_TRUE) {
     SWFDEC_ERROR ("failed to initialize global object");
   }
+  if (!JS_GetProperty (player->jscx, player->jsobj, "Object", &val)) {
+    SWFDEC_ERROR ("failed to get 'Object'");
+  }
+  g_assert (JSVAL_IS_OBJECT (val));
+  if (!JS_DefineFunctions (player->jscx, JSVAL_TO_OBJECT (val), static_object_methods)) {
+    SWFDEC_ERROR ("failed to set static Object methods");
+  }
 }
 
diff-tree 6d91b775a72e20ccb85501a00a0c326109e5750a (from 66e2f04e58065e21a104926ef350b7ec65c40249)
Author: Benjamin Otte <otte at gnome.org>
Date:   Fri Mar 9 09:44:22 2007 +0100

    register the standard objects before registering the global stuff
    
    The global stuff modifies some standard objects

diff --git a/libswfdec/swfdec_js.c b/libswfdec/swfdec_js.c
index bca7825..44f9cb8 100644
--- a/libswfdec/swfdec_js.c
+++ b/libswfdec/swfdec_js.c
@@ -105,10 +105,10 @@ swfdec_js_init_player (SwfdecPlayer *pla
     swfdec_js_finish_player (player);
     return;
   }
-  swfdec_js_add_globals (player);
   if (!JS_InitStandardClasses (player->jscx, player->jsobj)) {
     SWFDEC_ERROR ("initializing JS standard classes failed");
   }
+  swfdec_js_add_globals (player);
   swfdec_js_add_mouse (player);
   swfdec_js_add_movieclip_class (player);
   swfdec_js_add_color (player);
diff-tree 66e2f04e58065e21a104926ef350b7ec65c40249 (from c75613127fe44f34f8b37492f23ed0c66c415e57)
Author: Benjamin Otte <otte at gnome.org>
Date:   Fri Mar 9 09:43:32 2007 +0100

    add support for attaching a ActionScript class to a sprite export
    
    This is useful for Object.registerClass

diff --git a/libswfdec/swfdec_player.c b/libswfdec/swfdec_player.c
index 81d06fa..adc8584 100644
--- a/libswfdec/swfdec_player.c
+++ b/libswfdec/swfdec_player.c
@@ -346,12 +346,26 @@ swfdec_player_set_property (GObject *obj
   }
 }
 
+static gboolean
+free_registered_class (gpointer key, gpointer value, gpointer playerp)
+{
+  SwfdecPlayer *player = playerp;
+
+  g_free (key);
+  JS_RemoveRoot (player->jscx, value);
+  g_free (value);
+  return TRUE;
+}
+
 static void
 swfdec_player_dispose (GObject *object)
 {
   SwfdecPlayer *player = SWFDEC_PLAYER (object);
 
   swfdec_player_stop_all_sounds (player);
+  /* this must happen before we finish the JS player, we have roots in there */
+  g_hash_table_foreach_steal (player->registered_classes, free_registered_class, player);
+  g_hash_table_destroy (player->registered_classes);
 
   g_list_foreach (player->roots, (GFunc) swfdec_movie_destroy, NULL);
   g_list_free (player->roots);
@@ -859,6 +873,8 @@ static void
 swfdec_player_init (SwfdecPlayer *player)
 {
   swfdec_js_init_player (player);
+  player->registered_classes = g_hash_table_new_full (g_str_hash, g_str_equal, 
+      g_free, NULL);
 
   player->actions = swfdec_ring_buffer_new_for_type (SwfdecPlayerAction, 16);
   player->cache = swfdec_cache_new (50 * 1024 * 1024); /* 100 MB */
@@ -1006,6 +1022,44 @@ swfdec_player_initialize (SwfdecPlayer *
   g_object_notify (G_OBJECT (player), "initialized");
 }
 
+jsval
+swfdec_player_get_export_class (SwfdecPlayer *player, const char *name)
+{
+  jsval *val = g_hash_table_lookup (player->registered_classes, name);
+
+  if (val)
+    return *val;
+  else
+    return JSVAL_NULL;
+}
+
+void
+swfdec_player_set_export_class (SwfdecPlayer *player, const char *name, jsval val)
+{
+  jsval *insert;
+
+  g_return_if_fail (SWFDEC_IS_PLAYER (player));
+  g_return_if_fail (name != NULL);
+  g_return_if_fail (JSVAL_IS_OBJECT (val));
+
+  insert = g_hash_table_lookup (player->registered_classes, name);
+  if (insert) {
+    JS_RemoveRoot (player->jscx, insert);
+    g_free (insert);
+    g_hash_table_remove (player->registered_classes, name);
+  }
+
+  if (val != JSVAL_NULL) {
+    insert = g_new (jsval, 1);
+    *insert = val;
+    if (!JS_AddRoot (player->jscx, insert)) {
+      g_free (insert);
+      return;
+    }
+    g_hash_table_insert (player->registered_classes, g_strdup (name), insert);
+  }
+}
+
 /** PUBLIC API ***/
 
 /**
diff --git a/libswfdec/swfdec_player_internal.h b/libswfdec/swfdec_player_internal.h
index 6d40273..d81b2cf 100644
--- a/libswfdec/swfdec_player_internal.h
+++ b/libswfdec/swfdec_player_internal.h
@@ -56,6 +56,7 @@ struct _SwfdecPlayer
   JSObject *		jsobj;			/* the global object */
   unsigned int		interval_id;		/* id returned from setInterval call */
   GList *		intervals;		/* all currently running intervals */
+  GHashTable *		registered_classes;	/* name => jsval* to constructor */
   SwfdecListener *	mouse_listener;		/* emitting mouse events */
   SwfdecListener *	key_listener;		/* emitting keyboard events */
 
@@ -114,6 +115,12 @@ void		swfdec_player_lock		(SwfdecPlayer 
 void		swfdec_player_unlock		(SwfdecPlayer *		player);
 void		swfdec_player_perform_actions	(SwfdecPlayer *		player);
 
+jsval		swfdec_player_get_export_class	(SwfdecPlayer *		player,
+						 const char *		name);
+void		swfdec_player_set_export_class	(SwfdecPlayer *		player,
+						 const char *		name,
+						 jsval			val);
+
 void		swfdec_player_invalidate	(SwfdecPlayer *		player,
 						 const SwfdecRect *	rect);
 void		swfdec_player_add_timeout	(SwfdecPlayer *		player,
diff-tree c75613127fe44f34f8b37492f23ed0c66c415e57 (from 5825479e8e4255252521b4ac6421180c38623d54)
Author: Benjamin Otte <otte at gnome.org>
Date:   Fri Mar 9 09:41:59 2007 +0100

    add swfdec_root_movie_get_export_name
    
    Allows to find the name by which a character is exported

diff --git a/libswfdec/swfdec_root_movie.c b/libswfdec/swfdec_root_movie.c
index e7a97f4..24a403c 100644
--- a/libswfdec/swfdec_root_movie.c
+++ b/libswfdec/swfdec_root_movie.c
@@ -261,3 +261,28 @@ swfdec_root_movie_get_export (SwfdecRoot
 
   return g_hash_table_lookup (root->exports, name);
 }
+
+/* evil hack, but pointers are pointers */
+gboolean
+find_value (gpointer key, gpointer value, gpointer inout)
+{
+  if (value != *(gpointer *) inout)
+    return FALSE;
+  *(gpointer *) inout = key;
+  return TRUE;
+}
+
+const char *
+swfdec_root_movie_get_export_name (SwfdecRootMovie *root, SwfdecCharacter *character)
+{
+  gpointer ret = character;
+
+  g_return_val_if_fail (SWFDEC_IS_ROOT_MOVIE (root), NULL);
+  g_return_val_if_fail (SWFDEC_IS_CHARACTER (character), NULL);
+
+  if (!g_hash_table_find (root->exports, find_value, &ret))
+    return NULL;
+
+  g_print ("found %s\n", (char *) ret);
+  return ret;
+}
diff --git a/libswfdec/swfdec_root_movie.h b/libswfdec/swfdec_root_movie.h
index f5a57d6..67c785e 100644
--- a/libswfdec/swfdec_root_movie.h
+++ b/libswfdec/swfdec_root_movie.h
@@ -64,6 +64,8 @@ void		swfdec_root_movie_perform_root_act
 
 gpointer	swfdec_root_movie_get_export		(SwfdecRootMovie *	root,
 							 const char *		name);
+const char *	swfdec_root_movie_get_export_name     	(SwfdecRootMovie *	root,
+							 SwfdecCharacter *	character);
 
 G_END_DECLS
 #endif
diff-tree 5825479e8e4255252521b4ac6421180c38623d54 (from f7e50ab7847da681d78ebf37e436a395cf9d8477)
Author: Benjamin Otte <otte at gnome.org>
Date:   Thu Mar 8 15:33:25 2007 +0100

    use swfdec_js_construct_object

diff --git a/libswfdec/swfdec_script.c b/libswfdec/swfdec_script.c
index 927aa67..b425233 100644
--- a/libswfdec/swfdec_script.c
+++ b/libswfdec/swfdec_script.c
@@ -1397,9 +1397,8 @@ static JSBool
 swfdec_action_new_object (JSContext *cx, guint action, const guint8 *data, guint len)
 {
   JSStackFrame *fp = cx->fp;
-  jsval constructor, proto;
+  jsval constructor;
   JSObject *object;
-  const JSClass *clasp;
   guint n_args;
   const char *name;
 
@@ -1418,20 +1417,10 @@ swfdec_action_new_object (JSContext *cx,
   }
   fp->sp[-1] = constructor;
 
-  if (!JSVAL_IS_OBJECT (constructor) || JSVAL_IS_NULL (constructor))
-    goto fail;
-  object = JSVAL_TO_OBJECT (constructor);
-  if (JS_GetClass (object) != &js_FunctionClass)
-    goto fail;
-  clasp = ((JSFunction *) JS_GetPrivate (cx, object))->clasp;
-  if (!JS_GetProperty (cx, object, "prototype", &proto))
+  if (!swfdec_js_construct_object (cx, NULL, constructor, &object))
     return JS_FALSE;
-  if (!JSVAL_IS_OBJECT (proto)) {
-    SWFDEC_ERROR ("prototype of %s is not an object", name);
-  }
-  object = JS_NewObject (cx, clasp, JSVAL_IS_OBJECT (proto) ? JSVAL_TO_OBJECT (proto) : NULL, NULL);
   if (object == NULL)
-    return JS_FALSE;
+    goto fail;
   fp->sp[-2] = OBJECT_TO_JSVAL (object);
   if (!swfdec_action_call (cx, n_args, JSINVOKE_CONSTRUCT))
     return JS_FALSE;
@@ -1451,8 +1440,7 @@ swfdec_action_new_method (JSContext *cx,
   const char *s;
   guint32 n_args;
   JSObject *object;
-  jsval constructor, proto;
-  const JSClass *clasp;
+  jsval constructor;
   
   s = swfdec_js_to_string (cx, fp->sp[-1]);
   if (s == NULL)
@@ -1476,20 +1464,10 @@ swfdec_action_new_method (JSContext *cx,
     }
   }
   fp->sp[-1] = OBJECT_TO_JSVAL (constructor);
-  if (!JSVAL_IS_OBJECT (constructor) || JSVAL_IS_NULL (constructor))
-    goto fail;
-  object = JSVAL_TO_OBJECT (constructor);
-  if (JS_GetClass (object) != &js_FunctionClass)
-    goto fail;
-  clasp = ((JSFunction *) JS_GetPrivate (cx, object))->clasp;
-  if (!JS_GetProperty (cx, object, "prototype", &proto))
+  if (!swfdec_js_construct_object (cx, NULL, constructor, &object))
     return JS_FALSE;
-  if (!JSVAL_IS_OBJECT (proto)) {
-    SWFDEC_ERROR ("prototype of %s is not an object", s);
-  }
-  object = JS_NewObject (cx, clasp, JSVAL_IS_OBJECT (proto) ? JSVAL_TO_OBJECT (proto) : NULL, NULL);
   if (object == NULL)
-    return JS_FALSE;
+    goto fail;
   fp->sp[-2] = OBJECT_TO_JSVAL (object);
   if (!swfdec_action_call (cx, n_args, JSINVOKE_CONSTRUCT))
     return JS_FALSE;
diff-tree f7e50ab7847da681d78ebf37e436a395cf9d8477 (from a9effebf8a768fa92622c8c941d407d9c4f56d74)
Author: Benjamin Otte <otte at gnome.org>
Date:   Thu Mar 8 15:33:13 2007 +0100

    if no class is specified, get the class from the constructor

diff --git a/libswfdec/swfdec_js.c b/libswfdec/swfdec_js.c
index 0db221c..bca7825 100644
--- a/libswfdec/swfdec_js.c
+++ b/libswfdec/swfdec_js.c
@@ -481,6 +481,8 @@ swfdec_js_construct_object (JSContext *c
   object = JSVAL_TO_OBJECT (constructor);
   if (JS_GetClass (object) != &js_FunctionClass)
     goto fail;
+  if (clasp == NULL)
+    clasp = ((JSFunction *) JS_GetPrivate (cx, object))->clasp;
   if (!JS_GetProperty (cx, object, "prototype", &proto))
     return JS_FALSE;
   if (!JSVAL_IS_OBJECT (proto)) {
diff-tree a9effebf8a768fa92622c8c941d407d9c4f56d74 (from bda7fbc753409b8b186e674fdb30601f1b55ebd9)
Author: Benjamin Otte <otte at gnome.org>
Date:   Thu Mar 8 15:04:00 2007 +0100

    change MovieClip JS object handling
    
    The JS object is no longer created on demand, but when the clip gets created
    and we ensure it won't get GC'ed until destruction time

diff --git a/libswfdec/swfdec_js_movie.c b/libswfdec/swfdec_js_movie.c
index 200f706..fce61ef 100644
--- a/libswfdec/swfdec_js_movie.c
+++ b/libswfdec/swfdec_js_movie.c
@@ -48,6 +48,68 @@ const JSClass movieclip_class = {
     JSCLASS_NO_OPTIONAL_MEMBERS
 };
 
+static void
+swfdec_js_movie_add_property (SwfdecMovie *movie)
+{
+  SwfdecScriptable *script = SWFDEC_SCRIPTABLE (movie);
+  jsval val;
+  JSObject *jsobj;
+  JSContext *cx;
+  JSBool found = JS_FALSE;
+
+  if (!movie->has_name)
+    return;
+  jsobj = swfdec_scriptable_get_object (script);
+  val = OBJECT_TO_JSVAL (jsobj);
+  cx = script->jscx;
+  if (movie->parent) {
+    jsobj = SWFDEC_SCRIPTABLE (movie->parent)->jsobj;
+    if (jsobj == NULL)
+      return;
+    SWFDEC_LOG ("setting %s as property for %s", movie->name, 
+	movie->parent->name);
+  } else {
+    jsobj = SWFDEC_ROOT_MOVIE (movie)->player->jsobj;
+    SWFDEC_LOG ("setting %s as property for _global", movie->name);
+  }
+  if (!JS_SetProperty (cx, jsobj, movie->name, &val) ||
+      !JS_SetPropertyAttributes (cx, jsobj, movie->name, JSPROP_READONLY | JSPROP_PERMANENT, &found) ||
+      found != JS_TRUE) {
+    SWFDEC_ERROR ("could not set property %s correctly", movie->name);
+  }
+}
+
+static void
+swfdec_js_movie_remove_property (SwfdecMovie *movie)
+{
+  SwfdecScriptable *script = SWFDEC_SCRIPTABLE (movie);
+  JSObject *jsobj;
+  JSContext *cx;
+  JSBool found = JS_FALSE;
+  jsval deleted = JSVAL_FALSE;
+
+  if (!movie->has_name ||
+      script->jsobj == NULL)
+    return;
+
+  cx = script->jscx;
+  if (movie->parent) {
+    jsobj = SWFDEC_SCRIPTABLE (movie->parent)->jsobj;
+    if (jsobj == NULL)
+      return;
+  } else {
+    jsobj = SWFDEC_ROOT_MOVIE (movie)->player->jsobj;
+  }
+
+  SWFDEC_LOG ("removing %s as property", movie->name);
+  if (!JS_SetPropertyAttributes (cx, jsobj, movie->name, 0, &found) ||
+      found != JS_TRUE ||
+      !JS_DeleteProperty2 (cx, jsobj, movie->name, &deleted) ||
+      deleted == JSVAL_FALSE) {
+    SWFDEC_ERROR ("could not remove property %s correctly", movie->name);
+  }
+}
+
 static JSBool
 mc_play (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 {
@@ -1172,64 +1234,56 @@ swfdec_js_add_movieclip_class (SwfdecPla
       NULL, NULL);
 }
 
+jsval
+swfdec_js_movie_lookup_class (SwfdecMovie *movie)
+{
+  return JSVAL_NULL;
+}
+
 void
-swfdec_js_movie_add_property (SwfdecMovie *movie)
+swfdec_js_movie_create_jsobject	(SwfdecMovie *movie)
 {
-  SwfdecScriptable *script = SWFDEC_SCRIPTABLE (movie);
-  jsval val;
-  JSObject *jsobj;
-  JSContext *cx;
-  JSBool found = JS_FALSE;
+  SwfdecScriptable *script;
+  jsval fun;
 
-  jsobj = swfdec_scriptable_get_object (script);
-  val = OBJECT_TO_JSVAL (jsobj);
-  cx = script->jscx;
-  if (movie->parent) {
-    jsobj = SWFDEC_SCRIPTABLE (movie->parent)->jsobj;
-    if (jsobj == NULL)
-      return;
-    SWFDEC_LOG ("setting %s as property for %s", movie->name, 
-	movie->parent->name);
+  g_return_if_fail (SWFDEC_IS_MOVIE (movie));
+
+  script = SWFDEC_SCRIPTABLE (movie);
+  g_return_if_fail (script->jscx != NULL);
+  g_return_if_fail (script->jsobj == NULL);
+
+  fun = swfdec_js_movie_lookup_class (movie);
+  if (fun == JSVAL_NULL) {
+    script->jsobj = JS_NewObject (script->jscx, &movieclip_class,
+	NULL, NULL);
   } else {
-    jsobj = SWFDEC_ROOT_MOVIE (movie)->player->jsobj;
-    SWFDEC_LOG ("setting %s as property for _global", movie->name);
+    swfdec_js_construct_object (script->jscx, &movieclip_class, fun, &script->jsobj);
   }
-  if (!JS_SetProperty (cx, jsobj, movie->name, &val) ||
-      !JS_SetPropertyAttributes (cx, jsobj, movie->name, JSPROP_READONLY | JSPROP_PERMANENT, &found) ||
-      found != JS_TRUE) {
-    SWFDEC_ERROR ("could not set property %s correctly", movie->name);
+  if (!script->jsobj ||
+      !JS_AddRoot (script->jscx, &script->jsobj)) {
+    script->jsobj = NULL;
+    SWFDEC_ERROR ("failed to construct JSObject for %s %p", 
+	G_OBJECT_TYPE_NAME (movie), movie);
+    return;
   }
+  g_object_ref (script);
+  JS_SetPrivate (script->jscx, script->jsobj, script);
+  swfdec_js_movie_add_property (movie);
 }
 
 void
-swfdec_js_movie_remove_property (SwfdecMovie *movie)
+swfdec_js_movie_remove_jsobject	(SwfdecMovie *movie)
 {
-  SwfdecScriptable *script = SWFDEC_SCRIPTABLE (movie);
-  JSObject *jsobj;
-  JSContext *cx;
-  JSBool found = JS_FALSE;
-  jsval deleted = JSVAL_FALSE;
+  SwfdecScriptable *script;
 
-  if (!movie->has_name ||
-      script->jsobj == NULL)
+  g_return_if_fail (SWFDEC_IS_MOVIE (movie));
+  script = SWFDEC_SCRIPTABLE (movie);
+  g_return_if_fail (script->jscx != NULL);
+  if (script->jsobj == NULL)
     return;
 
-  cx = script->jscx;
-  if (movie->parent) {
-    jsobj = SWFDEC_SCRIPTABLE (movie->parent)->jsobj;
-    if (jsobj == NULL)
-      return;
-  } else {
-    jsobj = SWFDEC_ROOT_MOVIE (movie)->player->jsobj;
-  }
-
-  SWFDEC_LOG ("removing %s as property", movie->name);
-  if (!JS_SetPropertyAttributes (cx, jsobj, movie->name, 0, &found) ||
-      found != JS_TRUE ||
-      !JS_DeleteProperty2 (cx, jsobj, movie->name, &deleted) ||
-      deleted == JSVAL_FALSE) {
-    SWFDEC_ERROR ("could not remove property %s correctly", movie->name);
-  }
+  swfdec_js_movie_remove_property (movie);
+  JS_RemoveRoot (script->jscx, &script->jsobj);
 }
 
 gboolean
diff --git a/libswfdec/swfdec_movie.c b/libswfdec/swfdec_movie.c
index 0e7715c..146fa2c 100644
--- a/libswfdec/swfdec_movie.c
+++ b/libswfdec/swfdec_movie.c
@@ -308,8 +308,7 @@ swfdec_movie_destroy (SwfdecMovie *movie
   if (movie->parent) {
     movie->parent->list = g_list_remove (movie->parent->list, movie);
   }
-  if (SWFDEC_SCRIPTABLE (movie)->jsobj)
-    swfdec_js_movie_remove_property (movie);
+  swfdec_js_movie_remove_jsobject (movie);
   player->movies = g_list_remove (player->movies, movie);
   g_object_unref (movie);
 }
@@ -649,20 +648,8 @@ swfdec_movie_iterate_end (SwfdecMovie *m
 static JSObject *
 swfdec_movie_create_js_object (SwfdecScriptable *script)
 {
-  GList *walk;
-  JSObject *ret;
-
-  ret = SWFDEC_SCRIPTABLE_CLASS (swfdec_movie_parent_class)->create_js_object (script);
-  if (ret == NULL)
-    return NULL;
-  script->jsobj = ret;
-  /* add all children */
-  for (walk = SWFDEC_MOVIE (script)->list; walk; walk = walk->next) {
-    SwfdecMovie *child = walk->data;
-    if (child->has_name)
-      swfdec_js_movie_add_property (child);
-  }
-  return ret;
+  /* we create the objects manually and ensure persistence */
+  g_assert_not_reached ();
 }
 
 extern const JSClass movieclip_class;
@@ -687,7 +674,6 @@ swfdec_movie_set_name (SwfdecMovie *movi
   g_assert (movie->name == NULL);
   if (movie->content->name) {
     movie->name = g_strdup (movie->content->name);
-    swfdec_js_movie_add_property (movie);
     movie->has_name = TRUE;
   } else if (SWFDEC_IS_SPRITE_MOVIE (movie)) {
     /* FIXME: figure out if it's relative to root or player or something else
@@ -724,6 +710,8 @@ swfdec_movie_set_parent (SwfdecMovie *mo
    * new movies to be created (and added to this list)
    */
   player->movies = g_list_prepend (player->movies, movie);
+  /* we have to create the JSObject here to get actions queued before init_movie executes */
+  swfdec_js_movie_create_jsobject (movie);
   if (klass->init_movie)
     klass->init_movie (movie);
   swfdec_movie_queue_script (movie, SWFDEC_EVENT_LOAD);
diff-tree bda7fbc753409b8b186e674fdb30601f1b55ebd9 (from bb4cc0da47ef8cc6978c1b51e55a0da1339ed5b9)
Author: Benjamin Otte <otte at gnome.org>
Date:   Thu Mar 8 15:01:46 2007 +0100

    add swfdec_js_construct_object
    
    The function constructs an object for the given constructor

diff --git a/libswfdec/swfdec_js.c b/libswfdec/swfdec_js.c
index ce68144..0db221c 100644
--- a/libswfdec/swfdec_js.c
+++ b/libswfdec/swfdec_js.c
@@ -23,6 +23,7 @@
 #include <string.h>
 #include <js/jsapi.h>
 #include <js/jscntxt.h> /* for setting tracefp when debugging */
+#include <libswfdec/js/jsfun.h>
 #include <js/jsdbgapi.h> /* for debugging */
 #include <js/jsopcode.h> /* for debugging */
 #include <js/jsscript.h> /* for debugging */
@@ -450,3 +451,49 @@ swfdec_js_eval_set (JSContext *cx, JSObj
 
   swfdec_js_eval_internal (cx, obj, str, &val, TRUE);
 }
+
+/**
+ * swfdec_js_construct_object:
+ * @cx: the #JSContext
+ * @clasp: class to use for constructing the object
+ * @constructor: a jsval possibly referring to a constructor
+ * @newp: pointer to variable that will take the created object or NULL on 
+ *        failure
+ *
+ * Constructs a JSObject for the given @constructor, if it really is a
+ * constructor. 
+ * <note>The object is only constructed, the constructor is not called.
+ * You can easily do this with JS_Invoke() later.</note>
+ *
+ * Returns: %JS_TRUE on success or %JS_FALSE on OOM.
+ **/
+JSBool
+swfdec_js_construct_object (JSContext *cx, const JSClass *clasp, 
+    jsval constructor, JSObject **newp)
+{
+  JSObject *object;
+  jsval proto;
+
+  g_return_val_if_fail (newp != NULL, JS_FALSE);
+
+  if (!JSVAL_IS_OBJECT (constructor) || constructor == JSVAL_VOID)
+    goto fail;
+  object = JSVAL_TO_OBJECT (constructor);
+  if (JS_GetClass (object) != &js_FunctionClass)
+    goto fail;
+  if (!JS_GetProperty (cx, object, "prototype", &proto))
+    return JS_FALSE;
+  if (!JSVAL_IS_OBJECT (proto)) {
+    SWFDEC_ERROR ("prototype is not an object");
+  }
+  object = JS_NewObject (cx, clasp, JSVAL_IS_OBJECT (proto) ? JSVAL_TO_OBJECT (proto) : NULL, NULL);
+  if (object == NULL)
+    return JS_FALSE;
+
+  *newp = object;
+  return JS_TRUE;
+
+fail:
+  *newp = NULL;
+  return JS_FALSE;
+}
diff --git a/libswfdec/swfdec_js.h b/libswfdec/swfdec_js.h
index 624cbb3..303a90e 100644
--- a/libswfdec/swfdec_js.h
+++ b/libswfdec/swfdec_js.h
@@ -47,8 +47,8 @@ void		swfdec_js_add_sound		(SwfdecPlayer
 void		swfdec_js_add_video		(SwfdecPlayer *		player);
 void		swfdec_js_add_xml		(SwfdecPlayer *		player);
 
-void		swfdec_js_movie_add_property	(SwfdecMovie *		movie);
-void		swfdec_js_movie_remove_property	(SwfdecMovie *		movie);
+void		swfdec_js_movie_create_jsobject	(SwfdecMovie *		movie);
+void		swfdec_js_movie_remove_jsobject	(SwfdecMovie *		movie);
 
 char *		swfdec_js_slash_to_dot		(const char *		slash_str);
 jsval		swfdec_js_eval			(JSContext *		cx,
@@ -59,6 +59,10 @@ void		swfdec_js_eval_set    		(JSContext
 						 const char *		str,
 						 jsval			val);
 
+JSBool		swfdec_js_construct_object	(JSContext *		cx,
+						 const JSClass *	clasp,
+						 jsval			constructor,
+						 JSObject **		newp);
 /* support functions */
 const char *	swfdec_js_to_string		(JSContext *		cx,
 						 jsval			val);


More information about the Swfdec mailing list