Finish compiling at end of bitstream, too, not just with 0 action
[swfdec.git] / libswfdec / swfdec_compiler.c
blob68221e3a868053678f07703aee4e332d38893914
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
5 #include <stdlib.h>
6 #include <string.h>
8 #include <js/jsapi.h>
9 #include <js/jsarena.h>
10 #include <js/jsatom.h>
11 #include <js/jsemit.h>
12 #include <js/jsscript.h>
13 #include <js/jsopcode.h>
15 #include "swfdec_bits.h"
16 #include "swfdec_compiler.h"
17 #include "swfdec_debug.h"
18 #include "swfdec_debugger.h"
19 #include "swfdec_player_internal.h"
20 #include "swfdec_sprite.h"
22 /*** NOTE TO HACKERS ***
24 This file is supposed to contain a compiler that compiles ActionScript code
25 as found in an swf file to Javascript bytecode ready to be interpreted by
26 the Spidermonkey engine.
27 The problem here is that I have not much of a clue on how to do such a thing
28 right, the SpiderMonkey docs are very nice for users but unfortunately not for
29 people mocking with bytecode. I guess the contents of this file reflects that.
30 So if you can do better, this file has just one public function:
31 swfdec_compile (). Feel free to reimplement it in a better way.
33 *** END NOTE TO HACKERS ***/
35 /*** COMPILE STATE ***/
37 typedef struct {
38 guint bytecode; /* offset into bytecode where jump address goes */
39 gboolean extended; /* if this is an extended jump */
40 gboolean use_bytes; /* if TRUE, the offset is in bytes, otherwise it's in actions */
41 guint offset; /* action to jump to */
42 } Jump;
44 typedef struct {
45 guint original; /* amount of bytes we've advanced in the input */
46 guint compiled; /* amount of bytes we've advancecd in the bytecode */
47 } Offset;
49 typedef struct {
50 JSContext * cx; /* JSContext from player */
51 JSAtomList atoms; /* accumulated atoms */
53 SwfdecBits * bits; /* where to read the code from */
54 int version; /* Flash version we're compiling for */
55 GByteArray * bytecode; /* generated bytecode so far */
56 char * error; /* error encountered while compiling */
57 GArray * offsets; /* offsets of actions */
58 GArray * jumps; /* accumulated jumps */
59 GArray * trynotes; /* try/catch blocks */
60 GPtrArray * pool; /* ConstantPool data */
61 GArray * commands; /* debug informations */
62 guint command_last; /* offset to last command */
63 } CompileState;
65 /*** DEBUGGING STUFF ***/
67 /* NB: this must be called _before_ adding bytecode */
68 static void
69 compile_state_debug_add (CompileState *state, const char *format, ...) G_GNUC_PRINTF (2, 3);
70 static void
71 compile_state_debug_add (CompileState *state, const char *format, ...)
73 va_list args;
74 SwfdecDebuggerCommand command = { NULL, };
76 command.code = NULL + state->bytecode->len;
77 command.breakpoint = 0;
78 va_start (args, format);
79 command.description = g_strdup_vprintf (format, args);
80 SWFDEC_DEBUG ("%s", command.description);
81 va_end (args);
82 state->command_last = G_MAXUINT;
83 g_array_append_val (state->commands, command);
86 static void
87 compile_state_debug_add_default (CompileState *state, guint action, const char *name)
89 SwfdecDebuggerCommand command = { NULL, };
91 if (state->command_last == G_MAXUINT)
92 return;
93 if (action & 0x80) {
94 SWFDEC_WARNING ("FIXME: action %s does not provide debugger statements", name);
96 command.code = GUINT_TO_POINTER (state->command_last);
97 command.description = g_strdup (name);
98 state->command_last = G_MAXUINT;
99 g_array_append_val (state->commands, command);
100 SWFDEC_DEBUG ("%s", command.description);
103 static void
104 compile_state_debug_finish (CompileState *state, SwfdecPlayer *player, JSScript *script, const char *name)
106 SwfdecDebuggerCommand *command;
107 guint i;
109 if (SWFDEC_IS_DEBUGGER (player)) {
110 for (i = 0; i < state->commands->len; i++) {
111 command = &g_array_index (state->commands, SwfdecDebuggerCommand, i);
112 command->code = script->code + GPOINTER_TO_UINT (command->code);
114 swfdec_debugger_add_script (SWFDEC_DEBUGGER (player), script, name,
115 (SwfdecDebuggerCommand *) state->commands->data, state->commands->len);
116 g_array_free (state->commands, FALSE);
117 } else {
118 g_array_free (state->commands, TRUE);
122 /*** GENERAL FUNCTIONS ***/
124 static void
125 compile_state_error (CompileState *state, char *format, ...) G_GNUC_PRINTF (2, 3);
126 static void
127 compile_state_error (CompileState *state, char *format, ...)
129 va_list args;
131 g_assert (state->error == NULL);
133 va_start (args, format);
134 state->error = g_strdup_vprintf (format, args);
135 va_end (args);
138 /*** ACTION COMPILATION FUNCTIONS ***/
140 static void
141 compile_state_push_offset (CompileState *state, guint command_len)
143 Offset offset = { command_len, state->bytecode->len };
144 if (state->offsets->len > 0)
145 offset.original += g_array_index (state->offsets, Offset, state->offsets->len - 1).original;
146 g_array_append_val (state->offsets, offset);
149 static jsatomid
150 atomize_string (CompileState *state, const char *name)
152 JSAtom *atom;
153 JSAtomListElement *ale;
155 atom = js_Atomize (state->cx, name, strlen (name), 0);
156 ale = js_IndexAtom (state->cx, atom, &state->atoms);
157 if (ale == NULL) {
158 compile_state_error (state, "Failed to add name %s", name);
159 return 0;
161 return ALE_INDEX (ale);
164 static jsatomid
165 atomize_double (CompileState *state, jsdouble d)
167 JSAtom *atom;
168 JSAtomListElement *ale;
170 atom = js_AtomizeDouble (state->cx, d, 0);
171 ale = js_IndexAtom (state->cx, atom, &state->atoms);
172 if (ale == NULL) {
173 compile_state_error (state, "Failed to add double %g", d);
174 return 0;
176 return ALE_INDEX (ale);
179 static jsatomid
180 atomize_int32 (CompileState *state, int i)
182 JSAtom *atom;
183 JSAtomListElement *ale;
185 g_assert (i >= G_MININT32 && i <= G_MAXINT32);
186 atom = js_AtomizeInt (state->cx, i, 0);
187 ale = js_IndexAtom (state->cx, atom, &state->atoms);
188 if (ale == NULL) {
189 compile_state_error (state, "Failed to add int %d", i);
190 return 0;
192 return ALE_INDEX (ale);
195 #define ONELINER(state, opcode) G_STMT_START { \
196 guint8 command = opcode; \
197 compile_state_add_code (state, &command, 1); \
198 } G_STMT_END
199 #define THREELINER(state, opcode, id1, id2) G_STMT_START { \
200 guint8 command[3] = { opcode, id1, id2 }; \
201 compile_state_add_code (state, command, 3); \
202 } G_STMT_END
203 #define THREELINER_INT(state, opcode, _int) THREELINER (state, opcode, (_int) >> 8, (_int))
204 #define THREELINER_ATOM(state, opcode, str) G_STMT_START { \
205 jsatomid id; \
206 id = atomize_string (state, str); \
207 THREELINER_INT (state, opcode, id); \
208 } G_STMT_END
210 #define PUSH_OBJ(state) ONELINER (state, JSOP_PUSHOBJ)
211 #define POP(state) ONELINER (state, JSOP_POP)
212 #define GE(state) ONELINER (state, JSOP_GE)
213 #define THIS(state) ONELINER (state, JSOP_THIS)
214 #define SWAP(state) ONELINER (state, JSOP_SWAP)
215 #define DUP(state) ONELINER (state, JSOP_DUP)
216 #define FLASHCALL(state) ONELINER (state, JSOP_FLASHCALL)
217 #define FLASHSWAP(state, n) THREELINER_INT (state, JSOP_FLASHSWAP, n)
219 #define DO_JUMP(state, opcode, offset) G_STMT_START {\
220 guint8 command[3] = { opcode, 0, 0 }; \
221 compile_state_add_bytes_jump (state, offset, FALSE); \
222 compile_state_add_code (state, command, 3); \
223 } G_STMT_END
224 #define IFEQ(state, offset) DO_JUMP (state, JSOP_IFEQ, offset)
225 #define IFNE(state, offset) DO_JUMP (state, JSOP_IFNE, offset)
227 static void
228 compile_state_add_code (CompileState *state, const guint8 *code, guint len)
230 if (state->error)
231 return;
232 g_byte_array_append (state->bytecode, code, len);
235 static void
236 bind_name (CompileState *state, const char *name)
238 THREELINER_ATOM (state, JSOP_BINDNAME, name);
241 #define TARGET_NAME "__swfdec_target"
242 static void
243 compile_state_set_target (CompileState *state)
245 guint8 command[3] = { JSOP_BINDNAME, 0, 0 };
246 compile_state_add_code (state, command, 3);
247 SWAP (state);
248 command[0] = JSOP_SETNAME;
249 compile_state_add_code (state, command, 3);
250 POP (state);
252 static void
253 compile_state_add_target (CompileState *state)
255 guint8 command[3] = { JSOP_DEFVAR, 0, 0 };
256 /* add the TARGET variable */
257 if (atomize_string (state, TARGET_NAME) != 0) {
258 g_assert_not_reached ();
260 compile_state_add_code (state, command, 3);
261 THIS (state);
262 compile_state_set_target (state);
265 static void
266 compile_state_init (JSContext *cx, SwfdecBits *bits, int version, CompileState *state)
268 state->cx = cx;
269 state->bits = bits;
270 state->version = version;
271 ATOM_LIST_INIT (&state->atoms);
272 state->bytecode = g_byte_array_new ();
273 state->error = NULL;
274 state->offsets = g_array_new (FALSE, FALSE, sizeof (Offset));
275 state->jumps = g_array_new (FALSE, FALSE, sizeof (Jump));
276 state->trynotes = g_array_new (TRUE, FALSE, sizeof (JSTryNote));
277 state->pool = g_ptr_array_new ();
278 state->commands = g_array_new (TRUE, FALSE, sizeof (SwfdecDebuggerCommand));
279 state->command_last = G_MAXUINT;
281 compile_state_add_target (state);
282 compile_state_push_offset (state, 0);
285 static void
286 compile_state_resolve_jumps (CompileState *state)
288 guint i, j;
289 int offset;
290 Jump *jump;
291 guint8 *bytecode;
293 for (i = 0; i < state->jumps->len; i++) {
294 jump = &g_array_index (state->jumps, Jump, i);
295 bytecode = state->bytecode->data + jump->bytecode + 1;
296 if (jump->use_bytes) {
297 for (j = 0; j < state->offsets->len; j++) {
298 if (g_array_index (state->offsets, Offset, j).original == jump->offset) {
299 offset = g_array_index (state->offsets, Offset, j).compiled;
300 offset -= jump->bytecode;
301 goto finished;
304 compile_state_error (state, "Jumped into action");
305 return;
306 } else {
307 offset = g_array_index (state->offsets, Offset,
308 MIN (state->offsets->len - 1, jump->offset)).compiled;
309 offset -= jump->bytecode;
311 finished:
312 if (jump->extended) {
313 gint32 *data = (gint32 *) bytecode;
314 *data = GINT32_TO_BE (offset);
315 } else {
316 gint16 *data = (gint16 *) bytecode;
317 if (offset > G_MAXINT32 || offset < G_MININT32) {
318 compile_state_error (state, "jump from %u to %u is too big",
319 jump->bytecode, MIN (state->offsets->len - 1, jump->offset));
320 return;
322 *data = GINT16_TO_BE (offset);
327 #define OFFSET_MAIN 3
328 static JSScript *
329 compile_state_finish (CompileState *state, SwfdecPlayer *player, const char *name)
331 JSContext *cx = state->cx;
332 JSScript *script = NULL;
334 if (state->error == NULL)
335 compile_state_resolve_jumps (state);
337 if (state->error != NULL) {
338 JSAtomMap clear;
339 js_InitAtomMap (cx, &clear, &state->atoms);
340 js_FreeAtomMap (cx, &clear);
341 SWFDEC_ERROR ("%s", state->error);
342 g_free (state->error);
343 goto cleanup;
346 script = js_NewScript (cx, state->bytecode->len, 1, state->trynotes->len + 1);
347 memcpy (script->code, state->bytecode->data, state->bytecode->len);
348 memcpy (script->trynotes, state->trynotes->data, (state->trynotes->len + 1) * sizeof (JSTryNote));
349 js_InitAtomMap (cx, &script->atomMap, &state->atoms);
350 /* FIXME: figure out a correct value here */
351 script->depth = 100;
352 script->main = script->code + OFFSET_MAIN;
353 SN_MAKE_TERMINATOR (SCRIPT_NOTES (script));
354 compile_state_debug_finish (state, player, script, name);
356 cleanup:
357 g_ptr_array_free (state->pool, TRUE);
358 g_array_free (state->offsets, TRUE);
359 g_array_free (state->jumps, TRUE);
360 g_array_free (state->trynotes, TRUE);
361 g_byte_array_free (state->bytecode, TRUE);
362 return script;
365 static void
366 add_try_catch_block (CompileState *state, guint n_bytes, guint pc_offset)
368 ptrdiff_t last = 0;
369 ptrdiff_t cur_offset;
370 JSTryNote tn;
372 g_assert (state->bytecode->len >= OFFSET_MAIN);
373 cur_offset = state->bytecode->len - OFFSET_MAIN;
374 if (state->trynotes->len > 0) {
375 JSTryNote *tmp = &g_array_index (state->trynotes, JSTryNote, state->trynotes->len - 1);
376 last = tmp->start + tmp->length;
377 /* FIXME: allow nesting of try/catch blocks */
378 g_assert (last <= cur_offset);
380 if (last < cur_offset) {
381 /* add "no catching here" note */
382 tn.start = last;
383 tn.length = cur_offset - last;
384 tn.catchStart = 0;
385 g_array_append_val (state->trynotes, tn);
387 tn.start = cur_offset;
388 tn.length = n_bytes;
389 tn.catchStart = state->bytecode->len + pc_offset - OFFSET_MAIN;
390 SWFDEC_LOG ("adding try/catch at %u (len %u) to offset %u",
391 tn.start, tn.length, tn.catchStart);
392 g_array_append_val (state->trynotes, tn);
395 /* NB: n_bytes is relative to the start of this action */
396 static void
397 compile_state_add_bytes_jump (CompileState *state, int n_bytes, gboolean extended)
399 Jump jump = { state->bytecode->len, extended, TRUE,
400 n_bytes + g_array_index (state->offsets, Offset, state->offsets->len - 1).original };
402 SWFDEC_LOG ("adding jump to byte %u", jump.offset);
403 if (n_bytes < 0 &&
404 (guint) -n_bytes > g_array_index (state->offsets, Offset, state->offsets->len - 1).original) {
405 compile_state_error (state, "attempting to jump %d bytess backwards at byte %u",
406 -n_bytes, g_array_index (state->offsets, Offset, state->offsets->len - 1).original);
407 return;
409 g_array_append_val (state->jumps, jump);
412 /* must be called before adding the jump command to the bytecode */
413 static void
414 compile_state_add_action_jump (CompileState *state, int n_actions, gboolean extended)
416 Jump jump = { state->bytecode->len, extended, FALSE, state->offsets->len + n_actions };
418 if (n_actions < 0 && state->offsets->len < (guint) -n_actions) {
419 compile_state_error (state, "attempting to jump %d actions backwards in %u. action",
420 -n_actions, state->offsets->len);
421 return;
423 g_array_append_val (state->jumps, jump);
426 static void
427 push_target (CompileState *state)
429 THREELINER (state, JSOP_NAME, 0, 0);
432 static void
433 push_prop (CompileState *state, const char *name)
435 THREELINER_ATOM (state, JSOP_GETPROP, name);
438 static void
439 name (CompileState *state, const char *name)
441 THREELINER_ATOM (state, JSOP_NAME, name);
444 #if 0
445 static void
446 push_prop_without_target (CompileState *state, const char *name)
448 jsatomid id;
449 guint8 command[3];
451 id = atomize_string (state, name);
452 command[0] = JSOP_BINDNAME;
453 command[1] = id >> 8;
454 command[2] = id;
455 compile_state_add_code (state, command, 3);
456 command[0] = JSOP_GETPROP;
457 compile_state_add_code (state, command, 3);
459 #endif
461 static void
462 push_uint16 (CompileState *state, unsigned int i)
464 g_assert (i <= G_MAXUINT16);
465 SWFDEC_LOG ("pushing %u", i);
466 THREELINER_INT (state, JSOP_UINT16, i);
469 static void
470 call (CompileState *state, guint n_arguments)
472 THREELINER_INT (state, JSOP_CALL, n_arguments);
475 static void
476 call_void_function (CompileState *state, const char *name)
478 push_prop (state, name);
479 PUSH_OBJ (state);
480 call (state, 0);
481 POP (state);
484 /* 13 bytes */
485 #define DEBUG_TRACE(state) G_STMT_START { \
486 ONELINER (state, JSOP_DUP); \
487 compile_trace (state, 0, 0); \
488 }G_STMT_END
490 static void
491 compile_trace (CompileState *state, guint action, guint len)
493 push_uint16 (state, 1);
494 bind_name (state, "trace");
495 push_prop (state, "trace");
496 PUSH_OBJ (state);
497 FLASHCALL (state);
498 POP (state);
501 static void
502 compile_get_variable (CompileState *state, guint action, guint len)
504 push_uint16 (state, 1);
505 push_target (state);
506 push_prop (state, "eval");
507 push_target (state);
508 FLASHCALL (state);
511 static void
512 compile_set_variable (CompileState *state, guint action, guint len)
514 /* FIXME: handle paths */
515 push_target (state);
516 FLASHSWAP (state, 3);
517 SWAP (state);
518 ONELINER (state, JSOP_SETELEM);
519 POP (state);
522 static void
523 push_string (CompileState *state, const char *s)
525 SWFDEC_LOG ("pushing string: %s", s);
526 THREELINER_ATOM (state, JSOP_STRING, s);
529 static void
530 push_double (CompileState *state, double d)
532 jsatomid id = atomize_double (state, d);
534 SWFDEC_LOG ("pushing double: %g", d);
535 THREELINER_INT (state, JSOP_NUMBER, id);
538 static void
539 push_int32 (CompileState *state, gint32 i)
541 jsatomid id = atomize_int32 (state, i);
542 SWFDEC_LOG ("pushing int: %d", i);
543 THREELINER_INT (state, JSOP_NUMBER, id);
546 static void
547 compile_push (CompileState *state, guint action, guint len)
549 SwfdecBits *bits = state->bits;
550 guint type;
551 unsigned char *end = bits->ptr + len;
552 double d;
553 int i;
554 const char *s;
556 while (bits->ptr < end) {
557 type = swfdec_bits_get_u8 (bits);
558 SWFDEC_LOG ("push type %u", type);
559 switch (type) {
560 case 0: /* string */
561 s = swfdec_bits_skip_string (state->bits);
562 if (s) {
563 compile_state_error (state, "Push: Could not get string");
564 return;
566 compile_state_debug_add (state, "Push \"%s\"", s);
567 push_string (state, s);
568 break;
569 case 1: /* float */
570 d = swfdec_bits_get_float (state->bits);
571 compile_state_debug_add (state, "Push %g", d);
572 push_double (state, d);
573 break;
574 case 2: /* null */
575 compile_state_debug_add (state, "Push null");
576 ONELINER (state, JSOP_NULL);
577 break;
578 case 3: /* undefined */
579 compile_state_debug_add (state, "Push undefined");
580 ONELINER (state, JSOP_NULL);
581 ONELINER (state, JSOP_VOID);
582 break;
583 case 5: /* boolean */
584 type = swfdec_bits_get_u8 (bits);
585 if (type) {
586 compile_state_debug_add (state, "Push TRUE");
587 ONELINER (state, JSOP_TRUE);
588 } else {
589 compile_state_debug_add (state, "Push FALSE");
590 ONELINER (state, JSOP_FALSE);
592 break;
593 case 6: /* double */
594 d = swfdec_bits_get_double (state->bits);
595 compile_state_debug_add (state, "Push %g", d);
596 push_double (state, d);
597 break;
598 case 7: /* 32bit int */
599 /* FIXME: spec says U32, do they mean this? */
600 i = swfdec_bits_get_u32 (state->bits);
601 compile_state_debug_add (state, "Push %d", i);
602 push_int32 (state, i);
603 break;
604 case 8: /* 8bit ConstantPool address */
605 type = swfdec_bits_get_u8 (bits);
606 if (type >= state->pool->len) {
607 compile_state_error (state, "Constant pool index %u out of range %u",
608 type, state->pool->len);
609 return;
611 s = (const char *) g_ptr_array_index (state->pool, type);
612 compile_state_debug_add (state, "Push \"%s\"", s);
613 push_string (state, s);
614 break;
615 case 9: /* 16bit ConstantPool address */
616 type = swfdec_bits_get_u16 (bits);
617 if (type >= state->pool->len) {
618 compile_state_error (state, "Constant pool index %u out of range %u",
619 type, state->pool->len);
620 return;
622 s = (const char *) g_ptr_array_index (state->pool, type);
623 compile_state_debug_add (state, "Push \"%s\"", s);
624 push_string (state, s);
625 break;
626 case 4: /* register */
627 default:
628 compile_state_error (state, "Push: type %u not implemented", type);
629 swfdec_bits_getbits (bits, 8 * (end - bits->ptr));
634 static void
635 compile_goto_label (CompileState *state, guint action, guint len)
637 const char *s;
639 s = swfdec_bits_skip_string (state->bits);
640 compile_state_debug_add (state, "GotoLabel \"%s\"s", s);
641 push_target (state);
642 push_prop (state, "gotoAndStop");
643 PUSH_OBJ (state);
644 push_string (state, s);
645 call (state, 1);
646 POP (state);
649 static void
650 compile_goto_frame (CompileState *state, guint action, guint len)
652 unsigned int i;
654 i = swfdec_bits_get_u16 (state->bits);
655 compile_state_debug_add (state, "GotoFrame %u", i);
656 push_target (state);
657 push_prop (state, "gotoAndStop");
658 PUSH_OBJ (state);
659 push_uint16 (state, i + 1);
660 call (state, 1);
661 POP (state);
664 static void
665 compile_goto_frame2 (CompileState *state, guint action, guint len)
667 int bias, play;
668 if (swfdec_bits_getbits (state->bits, 6)) {
669 SWFDEC_WARNING ("reserved bits in GotoFrame2 aren't 0");
671 bias = swfdec_bits_getbit (state->bits);
672 play = swfdec_bits_getbit (state->bits);
673 compile_state_debug_add (state, "GotoFrame2 %s", play ? "Play" : "Stop");
674 if (bias) {
675 SWFDEC_ERROR ("scene bias not implemented");
677 push_uint16 (state, 1);
678 push_target (state);
679 if (play)
680 push_prop (state, "gotoAndPlay");
681 else
682 push_prop (state, "gotoAndStop");
683 PUSH_OBJ (state);
684 FLASHCALL (state);
687 static void
688 compile_wait_for_frame (CompileState *state, guint action, guint len)
690 guint frame, jump;
691 guint8 command[3] = { JSOP_IFEQ, 0, 0 };
693 frame = swfdec_bits_get_u16 (state->bits);
694 jump = swfdec_bits_get_u8 (state->bits);
695 compile_state_debug_add (state, "WaitForFrame %u %u", frame, jump);
696 push_target (state);
697 push_prop (state, "_framesloaded");
698 push_uint16 (state, frame);
699 GE (state);
700 compile_state_add_action_jump (state, jump, FALSE);
701 compile_state_add_code (state, command, 3);
704 static void
705 compile_jump (CompileState *state, guint action, guint len)
707 int amount = swfdec_bits_get_s16 (state->bits);
708 compile_state_debug_add (state, "Goto %d", amount);
709 DO_JUMP (state, JSOP_GOTO, amount + 5);
712 static void
713 compile_if (CompileState *state, guint action, guint len)
715 int amount = swfdec_bits_get_s16 (state->bits);
716 /* FIXME: Flash 4 does this differently */
718 compile_state_debug_add (state, "If %d", amount);
719 IFNE (state, amount + 5);
722 static void
723 compile_set_target (CompileState *state, guint action, guint len)
725 const char *s = swfdec_bits_skip_string (state->bits);
727 compile_state_debug_add (state, "SetTarget \"%s\"", s);
728 THIS (state);
729 push_prop (state, "eval");
730 PUSH_OBJ (state);
731 push_string (state, s);
732 call (state, 1);
733 compile_state_set_target (state);
736 static void
737 compile_set_target_2 (CompileState *state, guint action, guint len)
739 compile_state_set_target (state);
742 static void
743 compile_constant_pool (CompileState *state, guint action, guint len)
745 unsigned int i;
746 SwfdecBits *bits = state->bits;
748 /* no debug info please */
749 state->command_last = G_MAXUINT;
750 g_ptr_array_set_size (state->pool, swfdec_bits_get_u16 (bits));
751 for (i = 0; i < state->pool->len; i++) {
752 g_ptr_array_index (state->pool, i) = (gpointer) swfdec_bits_skip_string (bits);
753 if (g_ptr_array_index (state->pool, i) == 0) {
754 compile_state_error (state, "Couldn't get string %u/%ufor constant pool",
755 i, state->pool->len);
756 return;
761 static void
762 compile_get_url (CompileState *state, guint action, guint len)
764 const char *target, *url;
766 url = swfdec_bits_skip_string (state->bits);
767 target = swfdec_bits_skip_string (state->bits);
768 compile_state_debug_add (state, "GetURL \"%s\" \"%s\" ", url, target);
769 push_target (state);
770 push_prop (state, "getURL");
771 PUSH_OBJ (state);
772 push_string (state, url);
773 push_string (state, target);
774 call (state, 2);
775 POP (state);
778 static void
779 compile_oneliner_pop (CompileState *state, guint action, guint len)
781 JSOp op;
782 switch (action) {
783 case 0x4f:
784 op = JSOP_SETELEM;
785 break;
786 default:
787 g_assert_not_reached ();
788 op = JSOP_NOP;
790 ONELINER (state, op);
791 POP (state);
794 static void
795 compile_get_member (CompileState *state, guint action, guint len)
797 add_try_catch_block (state, 1, 4);
798 ONELINER (state, JSOP_GETELEM);
799 THREELINER_INT (state, JSOP_GOTO, 5);
800 POP (state);
801 ONELINER (state, JSOP_VOID);
804 /* Flash < 7 does conversions to numbers way different than JS or Flash 7+
805 * This function is supposed to do that conversion */
806 static void
807 ensure_number_flash6 (CompileState *state)
809 if (state->version < 7) {
810 ONELINER (state, JSOP_DUP);
811 ONELINER (state, JSOP_DUP);
812 ONELINER (state, JSOP_VOID);
813 ONELINER (state, JSOP_EQ);
814 THREELINER_INT (state, JSOP_IFEQ, 5);
815 POP (state);
816 ONELINER (state, JSOP_ZERO);
820 /* Flash < 5 does not know numbers. Therefore we convert booleans to 1 or 0 */
821 static void
822 boolean_to_number_flash4 (CompileState *state)
824 if (state->version < 5) {
825 ONELINER (state, JSOP_ZERO);
826 ONELINER (state, JSOP_ADD);
830 static void
831 compile_binary_op (CompileState *state, guint action, guint len)
833 JSOp op;
835 if (state->version < 7) {
836 /* lots of code to ensure that non-numeric arguments equal 0. */
837 SWAP (state);
838 ensure_number_flash6 (state);
839 SWAP (state);
840 ensure_number_flash6 (state);
842 switch (action) {
843 case 0x0A:
844 op = JSOP_ADD;
845 break;
846 case 0x0B:
847 op = JSOP_SUB;
848 break;
849 case 0x0C:
850 op = JSOP_MUL;
851 break;
852 case 0x0D:
853 if (state->version >= 7) {
854 /* division by undefined isn't NaN, but +-Infinity */
855 ONELINER (state, JSOP_DUP);
856 ONELINER (state, JSOP_DUP);
857 ONELINER (state, JSOP_VOID);
858 ONELINER (state, JSOP_EQ);
859 THREELINER_INT (state, JSOP_IFEQ, 5);
860 POP (state);
861 ONELINER (state, JSOP_ZERO);
863 op = JSOP_DIV;
864 break;
865 default:
866 g_assert_not_reached ();
867 op = JSOP_NOP;
869 ONELINER (state, op);
872 static void
873 compile_add2 (CompileState *state, guint action, guint len)
875 if (state->version >= 7) {
876 ONELINER (state, JSOP_ADD);
877 return;
879 /* pop void arguments and ignore them - special case void + void = 0 */
880 ONELINER (state, JSOP_DUP);
881 ONELINER (state, JSOP_DUP);
882 ONELINER (state, JSOP_VOID);
883 ONELINER (state, JSOP_EQ);
884 THREELINER_INT (state, JSOP_IFEQ, 16);
885 POP (state);
886 ONELINER (state, JSOP_DUP);
887 ONELINER (state, JSOP_DUP);
888 ONELINER (state, JSOP_VOID);
889 ONELINER (state, JSOP_EQ);
890 THREELINER_INT (state, JSOP_IFEQ, 5);
891 POP (state);
892 ONELINER (state, JSOP_ZERO);
893 THREELINER_INT (state, JSOP_GOTO, 17);
894 SWAP (state);
895 ONELINER (state, JSOP_DUP);
896 ONELINER (state, JSOP_DUP);
897 ONELINER (state, JSOP_VOID);
898 ONELINER (state, JSOP_EQ);
899 THREELINER_INT (state, JSOP_IFEQ, 7);
900 POP (state);
901 THREELINER_INT (state, JSOP_GOTO, 5);
902 SWAP (state);
903 ONELINER (state, JSOP_ADD);
906 static void
907 compile_less (CompileState *state, guint action, guint len)
909 if (state->version < 7) {
910 /* lots of code to ensure that non-numeric arguments equal 0. */
911 SWAP (state);
912 ensure_number_flash6 (state);
913 SWAP (state);
914 ensure_number_flash6 (state);
916 ONELINER (state, JSOP_LT);
918 boolean_to_number_flash4 (state);
921 static void
922 compile_comparison (CompileState *state, guint action, guint len)
924 JSOp op;
926 if (state->version < 7) {
927 /* lots of code to ensure that non-numeric arguments equal 0. */
928 SWAP (state);
929 ensure_number_flash6 (state);
930 SWAP (state);
931 ensure_number_flash6 (state);
932 } else if (state->version == 7) {
933 /* Flash 7 returns undefined when one of the inputs is undefined */
934 ONELINER (state, JSOP_DUP);
935 ONELINER (state, JSOP_DUP);
936 ONELINER (state, JSOP_VOID);
937 ONELINER (state, JSOP_EQ);
938 THREELINER_INT (state, JSOP_IFEQ, 8);
939 POP (state);
940 ONELINER (state, JSOP_VOID);
941 THREELINER_INT (state, JSOP_GOTO, 18);
942 SWAP (state);
943 ONELINER (state, JSOP_DUP);
944 ONELINER (state, JSOP_DUP);
945 ONELINER (state, JSOP_VOID);
946 ONELINER (state, JSOP_EQ);
947 THREELINER_INT (state, JSOP_IFEQ, 8);
948 POP (state);
949 ONELINER (state, JSOP_VOID);
950 THREELINER_INT (state, JSOP_GOTO, 5);
951 SWAP (state);
953 /* FIXME: what about Flash > 7? */
955 switch (action) {
956 case 0x48:
957 op = JSOP_LT;
958 break;
959 case 0x67:
960 op = JSOP_GT;
961 break;
962 default:
963 g_assert_not_reached ();
964 op = JSOP_NOP;
966 ONELINER (state, op);
969 static void
970 compile_oneliner (CompileState *state, guint action, guint len)
972 JSOp op;
974 switch (action) {
975 case 0x12:
976 op = JSOP_NOT;
977 break;
978 case 0x17:
979 op = JSOP_POP;
980 break;
981 case 0x47:
982 op = JSOP_ADD;
983 break;
984 case 0x49:
985 op = JSOP_NEW_EQ;
986 break;
987 case 0x4c:
988 op = JSOP_DUP;
989 break;
990 case 0x67:
991 op = JSOP_GT;
992 break;
993 default:
994 g_assert_not_reached ();
995 op = JSOP_NOP;
997 ONELINER (state, op);
1000 static void
1001 compile_call_function (CompileState *state, guint action, guint len)
1003 push_target (state);
1004 SWAP (state);
1005 ONELINER (state, JSOP_GETELEM);
1006 ONELINER (state, JSOP_PUSHOBJ);
1007 ONELINER (state, JSOP_FLASHCALL);
1010 static void
1011 compile_call_method (CompileState *state, guint action, guint len)
1013 add_try_catch_block (state, 1, 6);
1014 ONELINER (state, JSOP_GETELEM);
1015 ONELINER (state, JSOP_PUSHOBJ);
1016 #if 0
1017 /* FIXME: can't add try/catch here, because FLASHCALL removes argument count from the stack (oops) */
1018 add_try_catch_block (state, 1, 5);
1019 #endif
1020 ONELINER (state, JSOP_FLASHCALL);
1021 THREELINER_INT (state, JSOP_GOTO, 17);
1022 /* exception handling goes here: clean up the stack */
1023 POP (state);
1024 POP (state);
1025 ONELINER (state, JSOP_DUP);
1026 THREELINER_INT (state, JSOP_IFEQ, 10);
1027 SWAP (state);
1028 POP (state);
1029 ONELINER (state, JSOP_ONE);
1030 ONELINER (state, JSOP_SUB);
1031 THREELINER_INT (state, JSOP_GOTO, -8);
1032 ONELINER (state, JSOP_VOID);
1035 static void
1036 compile_start_drag (CompileState *state, guint action, guint len)
1038 guint8 command[3] = { JSOP_IFEQ, 0, 27 };
1039 /* FIXME: target relative to this or target? */
1040 push_uint16 (state, 1);
1041 push_target (state);
1042 push_prop (state, "eval");
1043 PUSH_OBJ (state);
1044 FLASHCALL (state);
1045 FLASHSWAP (state, 3);
1046 compile_state_add_code (state, command, 3);
1047 FLASHSWAP (state, 3);
1048 FLASHSWAP (state, 6);
1049 FLASHSWAP (state, 3);
1050 FLASHSWAP (state, 4);
1051 FLASHSWAP (state, 5);
1052 FLASHSWAP (state, 4);
1053 push_uint16 (state, 5);
1054 command[0] = JSOP_GOTO;
1055 command[2] = 6;
1056 compile_state_add_code (state, command, 3);
1057 push_uint16 (state, 1);
1058 SWAP (state);
1059 FLASHSWAP (state, 3);
1060 push_prop (state, "startDrag");
1061 PUSH_OBJ (state);
1062 FLASHCALL (state);
1063 POP (state);
1066 static void
1067 compile_increment (CompileState *state, guint action, guint len)
1069 ONELINER (state, JSOP_ONE);
1070 ONELINER (state, JSOP_ADD);
1073 static void
1074 compile_decrement (CompileState *state, guint action, guint len)
1076 ONELINER (state, JSOP_ONE);
1077 ONELINER (state, JSOP_SUB);
1080 static void
1081 compile_random (CompileState *state, guint action, guint len)
1083 push_uint16 (state, 1);
1084 bind_name (state, "random");
1085 push_prop (state, "random");
1086 PUSH_OBJ (state);
1087 FLASHCALL (state);
1090 static void
1091 compile_get_property (CompileState *state, guint action, guint len)
1093 SWAP (state);
1094 push_uint16 (state, 2);
1095 push_target (state);
1096 push_prop (state, "getProperty");
1097 PUSH_OBJ (state);
1098 FLASHCALL (state);
1101 static void
1102 compile_set_property (CompileState *state, guint action, guint len)
1104 FLASHSWAP (state, 3);
1105 push_uint16 (state, 3);
1106 bind_name (state, "setProperty");
1107 push_prop (state, "setProperty");
1108 PUSH_OBJ (state);
1109 FLASHCALL (state);
1110 POP (state);
1113 static void
1114 compile_string_add (CompileState *state, guint action, guint len)
1116 /* be sure to have strings */
1117 push_string (state, "");
1118 ONELINER (state, JSOP_ADD);
1119 ONELINER (state, JSOP_ADD);
1122 static void
1123 compile_equals (CompileState *state, guint action, guint len)
1125 /* ensure we compare numbers */
1126 ONELINER (state, JSOP_POS);
1127 ONELINER (state, JSOP_EQ);
1128 if (state->version <= 4) {
1129 /* FLash 4 wants 1 or 0 on the stack instead of TRUE or FALSE */
1130 ONELINER (state, JSOP_ZERO);
1131 ONELINER (state, JSOP_BITOR);
1135 static void
1136 compile_to_integer (CompileState *state, guint action, guint len)
1138 /* There's no opcode so we use a bitwise operation that forces a conversion */
1139 ONELINER (state, JSOP_ZERO);
1140 ONELINER (state, JSOP_BITOR);
1143 static void
1144 compile_target_path (CompileState *state, guint action, guint len)
1146 ONELINER (state, JSOP_DUP);
1147 name (state, "MovieClip");
1148 ONELINER (state, JSOP_INSTANCEOF);
1149 THREELINER_INT (state, JSOP_IFEQ, 13);
1150 push_prop (state, "toString");
1151 PUSH_OBJ (state);
1152 call (state, 0);
1153 THREELINER_INT (state, JSOP_GOTO, 4);
1154 ONELINER (state, JSOP_VOID);
1157 static void
1158 compile_new_object (CompileState *state, guint action, guint len)
1160 /* use eval to get at the constructor - spidermonkey doesn't like
1161 * new with strings */
1162 push_uint16 (state, 1);
1163 bind_name (state, "eval");
1164 push_prop (state, "eval");
1165 PUSH_OBJ (state);
1166 FLASHCALL (state);
1167 /* use call instead of new here - if this doesn't work, a FLASHNEW opcode is needed */
1168 PUSH_OBJ (state);
1169 FLASHCALL (state);
1172 static void
1173 compile_init_object (CompileState *state, guint action, guint len)
1175 name (state, "Object");
1176 PUSH_OBJ (state);
1177 THREELINER_INT (state, JSOP_NEW, 0);
1178 DUP (state);
1179 FLASHSWAP (state, 3);
1180 DUP (state);
1181 THREELINER_INT (state, JSOP_IFEQ, 17);
1182 ONELINER (state, JSOP_ONE);
1183 ONELINER (state, JSOP_SUB);
1184 FLASHSWAP (state, 5);
1185 SWAP (state);
1186 FLASHSWAP (state, 4);
1187 ONELINER (state, JSOP_SETELEM);
1188 POP (state);
1189 THREELINER_INT (state, JSOP_GOTO, -19);
1190 POP (state);
1191 POP (state);
1194 static void
1195 compile_simple_bind_call (CompileState *state, guint action, guint len)
1197 char *name;
1198 switch (action) {
1199 case 0x09:
1200 name = "stopAllSounds";
1201 break;
1202 default:
1203 g_assert_not_reached ();
1204 return;
1206 bind_name (state, name);
1207 call_void_function (state, name);
1210 static void
1211 compile_simple_call (CompileState *state, guint action, guint len)
1213 char *name;
1215 /* FIXME: shouldn't some functions here PUSH_OBJ instead of push_target? */
1216 push_target (state);
1217 switch (action) {
1218 case 0x04:
1219 name = "nextFrame";
1220 break;
1221 case 0x05:
1222 name = "prevFrame";
1223 break;
1224 case 0x06:
1225 name = "play";
1226 break;
1227 case 0x07:
1228 name = "stop";
1229 break;
1230 case 0x09:
1231 name = "stopAllSounds";
1232 break;
1233 case 0x28:
1234 name = "stopDrag";
1235 break;
1236 default:
1237 g_assert_not_reached ();
1238 return;
1240 call_void_function (state, name);
1243 /*** COMPILER ***/
1245 typedef struct {
1246 guint action;
1247 const char *name;
1248 void (* compile) (CompileState *state, guint action, guint len);
1249 } SwfdecActionSpec;
1251 static const SwfdecActionSpec * swfdec_action_find (guint action);
1253 void
1254 swfdec_disassemble (SwfdecPlayer *player, JSScript *script)
1256 guint i;
1258 for (i = 0; i < script->length; i ++) {
1259 g_print ("%02X ", script->code[i]);
1260 if (i % 16 == 15)
1261 g_print ("\n");
1262 else if (i % 4 == 3)
1263 g_print (" ");
1265 if (i % 16 != 15)
1266 g_print ("\n");
1267 js_Disassemble (player->jscx, script, JS_TRUE, stdout);
1271 * swfdec_compile:
1272 * @player: a #SwfdecPlayer
1273 * @bits: the data to read
1274 * @version: ActionScript version to compile for
1275 * @name: name describing the script or NULL
1277 * parses the data pointed to by @bits and compiles the ActionScript commands
1278 * encountered into a script for later execution.
1280 * Returns: A new JSScript or NULL on failure
1282 JSScript *
1283 swfdec_compile (SwfdecPlayer *player, SwfdecBits *bits, int version, const char *name)
1285 unsigned int action, len;
1286 const SwfdecActionSpec *current;
1287 CompileState state;
1288 JSScript *ret;
1289 #ifndef SWFDEC_DISABLE_DEBUG
1290 unsigned char *start = bits->ptr;
1291 #endif
1292 #ifndef G_DISABLE_ASSERT
1293 unsigned char *target;
1294 #endif
1295 //#define SWFDEC_DUMP_SCRIPTS
1296 #ifdef SWFDEC_DUMP_SCRIPTS
1297 SwfdecBits dump = *bits;
1298 #endif
1300 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
1301 g_return_val_if_fail (bits != NULL, NULL);
1303 if (name == NULL)
1304 name = "Unnamed script";
1305 compile_state_init (player->jscx, bits, version, &state);
1306 SWFDEC_INFO ("Creating new script in frame");
1307 while (swfdec_bits_left (bits) && (action = swfdec_bits_get_u8 (bits))) {
1308 if (action & 0x80) {
1309 len = swfdec_bits_get_u16 (bits);
1310 } else {
1311 len = 0;
1313 #ifndef G_DISABLE_ASSERT
1314 target = bits->ptr + len;
1315 #endif
1316 current = swfdec_action_find (action);
1317 SWFDEC_LOG ("compiling action %d %s (len %d, total %d)", action,
1318 current ? current->name : "unknown", len > 0 ? 3 + len : 1,
1319 bits->ptr - start);
1320 #if 0
1321 if (state.error == NULL)
1322 g_print (" compiling action %d %s (len %d, total %d)\n", action,
1323 current ? current->name : "unknown", len > 0 ? 3 + len : 1,
1324 bits->ptr - start);
1325 #endif
1326 if (state.error == NULL && current && current->compile) {
1327 state.command_last = state.bytecode->len;
1328 current->compile (&state, action, len);
1329 compile_state_debug_add_default (&state, action, current->name);
1330 compile_state_push_offset (&state, len ? 3 + len : 1);
1331 #ifndef G_DISABLE_ASSERT
1332 if (target != bits->ptr) {
1333 SWFDEC_ERROR ("parsed length and supposed length differ by %d bytes in %s action",
1334 bits->ptr - target, current->name);
1336 bits->ptr = target;
1337 #endif
1338 } else {
1339 swfdec_bits_getbits (bits, len * 8);
1340 if (state.error == NULL) {
1341 if (current) {
1342 compile_state_error (&state, "No compilation function for %s action", current->name);
1343 } else {
1344 compile_state_error (&state, "unknown action 0x%02X", action);
1349 ret = compile_state_finish (&state, player, name);
1350 #if 0
1351 if (ret)
1352 swfdec_disassemble (s, ret);
1353 #endif
1354 #ifdef SWFDEC_DUMP_SCRIPTS
1356 static int dump_count = 0;
1357 char *filename = g_strdup_printf ("script%d", dump_count++);
1358 g_file_set_contents (filename, (char *) dump.ptr, bits->ptr - dump.ptr, NULL);
1359 g_free (filename);
1361 #endif
1363 return ret;
1367 * swfdec_compiler_destroy_script:
1368 * @player: a #SwfdecPlayer
1369 * @script: a JSScript created via swfdec_compile()
1371 * Destroys a script that was previously created via swfdec_compile () for @player.
1372 * All scripts created via swfdec_compile must be removed with this function.
1374 void
1375 swfdec_compiler_destroy_script (SwfdecPlayer *player, JSScript *script)
1377 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1378 g_return_if_fail (script != NULL);
1380 if (SWFDEC_IS_DEBUGGER (player))
1381 swfdec_debugger_remove_script (SWFDEC_DEBUGGER (player), script);
1382 JS_DestroyScript (player->jscx, script);
1385 /* must be sorted! */
1386 SwfdecActionSpec actions[] = {
1387 /* version 3 */
1388 { 0x04, "NextFrame", compile_simple_call },
1389 { 0x05, "PreviousFrame", compile_simple_call },
1390 { 0x06, "Play", compile_simple_call },
1391 { 0x07, "Stop", compile_simple_call },
1392 { 0x08, "ToggleQuality", NULL },
1393 { 0x09, "StopSounds", compile_simple_bind_call },
1394 /* version 4 */
1395 { 0x0a, "Add", compile_binary_op },
1396 { 0x0b, "Subtract", compile_binary_op },
1397 { 0x0c, "Multiply", compile_binary_op },
1398 { 0x0d, "Divide", compile_binary_op },
1399 { 0x0e, "Equals", compile_equals },
1400 { 0x0f, "Less", compile_less },
1401 { 0x10, "And", NULL },
1402 { 0x11, "Or", NULL },
1403 { 0x12, "Not", compile_oneliner },
1404 { 0x13, "StringEquals", NULL },
1405 { 0x14, "StringLength", NULL },
1406 { 0x15, "StringExtract", NULL },
1407 { 0x17, "Pop", compile_oneliner },
1408 { 0x18, "ToInteger", compile_to_integer },
1409 { 0x1c, "GetVariable", compile_get_variable },
1410 { 0x1d, "SetVariable", compile_set_variable },
1411 { 0x20, "SetTarget2", compile_set_target_2 },
1412 { 0x21, "StringAdd", compile_string_add },
1413 { 0x22, "GetProperty", compile_get_property },
1414 { 0x23, "SetProperty", compile_set_property },
1415 { 0x24, "CloneSprite", NULL },
1416 { 0x25, "RemoveSprite", NULL },
1417 { 0x26, "Trace", compile_trace },
1418 { 0x27, "StartDrag", compile_start_drag },
1419 { 0x28, "EndDrag", compile_simple_call },
1420 { 0x29, "StringLess", NULL },
1421 /* version 7 */
1422 { 0x2a, "Throw", NULL },
1423 { 0x2b, "Cast", NULL },
1424 { 0x2c, "Implements", NULL },
1425 /* version 4 */
1426 { 0x30, "RandomNumber", compile_random },
1427 { 0x31, "MBStringLength", NULL },
1428 { 0x32, "CharToAscii", NULL },
1429 { 0x33, "AsciiToChar", NULL },
1430 { 0x34, "GetTime", NULL },
1431 { 0x35, "MBStringExtract", NULL },
1432 { 0x36, "MBCharToAscii", NULL },
1433 { 0x37, "MVAsciiToChar", NULL },
1434 /* version 5 */
1435 { 0x3a, "Delete", NULL },
1436 { 0x3b, "Delete2", NULL },
1437 { 0x3c, "DefineLocal", NULL },
1438 { 0x3d, "CallFunction", compile_call_function },
1439 { 0x3e, "Return", NULL },
1440 { 0x3f, "Modulo", NULL },
1441 { 0x40, "NewObject", compile_new_object },
1442 { 0x41, "DefineLocal2", NULL },
1443 { 0x42, "InitArray", NULL },
1444 { 0x43, "InitObject", compile_init_object },
1445 { 0x44, "Typeof", NULL },
1446 { 0x45, "TargetPath", compile_target_path },
1447 { 0x46, "Enumerate", NULL },
1448 { 0x47, "Add2", compile_add2 },
1449 { 0x48, "Less2", compile_comparison },
1450 { 0x49, "Equals2", compile_oneliner },
1451 { 0x4a, "ToNumber", NULL },
1452 { 0x4b, "ToString", NULL },
1453 { 0x4c, "PushDuplicate", compile_oneliner },
1454 { 0x4d, "Swap", NULL },
1455 { 0x4e, "GetMember", compile_get_member },
1456 { 0x4f, "SetMember", compile_oneliner_pop }, /* apparently the result is ignored */
1457 { 0x50, "Increment", compile_increment },
1458 { 0x51, "Decrement", compile_decrement },
1459 { 0x52, "CallMethod", compile_call_method },
1460 { 0x53, "NewMethod", NULL },
1461 /* version 6 */
1462 { 0x54, "InstanceOf", NULL },
1463 { 0x55, "Enumerate2", NULL },
1464 /* version 5 */
1465 { 0x60, "BitAnd", NULL },
1466 { 0x61, "BitOr", NULL },
1467 { 0x62, "BitXor", NULL },
1468 { 0x63, "BitLShift", NULL },
1469 { 0x64, "BitRShift", NULL },
1470 { 0x65, "BitURShift", NULL },
1471 /* version 6 */
1472 { 0x66, "StrictEquals", NULL },
1473 { 0x67, "Greater", compile_comparison },
1474 { 0x68, "StringGreater", NULL },
1475 /* version 7 */
1476 { 0x69, "Extends", NULL },
1478 /* version 3 */
1479 { 0x81, "GotoFrame", compile_goto_frame },
1480 { 0x83, "GetURL", compile_get_url },
1481 /* version 5 */
1482 { 0x87, "StoreRegister", NULL },
1483 { 0x88, "ConstantPool", compile_constant_pool },
1484 /* version 3 */
1485 { 0x8a, "WaitForFrame", compile_wait_for_frame },
1486 { 0x8b, "SetTarget", compile_set_target },
1487 { 0x8c, "GotoLabel", compile_goto_label },
1488 /* version 4 */
1489 { 0x8d, "WaitForFrame2", NULL },
1490 /* version 7 */
1491 { 0x8e, "DefineFunction2", NULL },
1492 { 0x8f, "Try", NULL },
1493 /* version 5 */
1494 { 0x94, "With", NULL },
1495 /* version 4 */
1496 { 0x96, "Push", compile_push },
1497 { 0x99, "Jump", compile_jump },
1498 { 0x9a, "GetURL2", NULL },
1499 /* version 5 */
1500 { 0x9b, "DefineFunction", NULL },
1501 /* version 4 */
1502 { 0x9d, "If", compile_if },
1503 { 0x9e, "Call", NULL },
1504 { 0x9f, "GotoFrame2", compile_goto_frame2 }
1508 uint_compare (gconstpointer v1, gconstpointer v2)
1510 return *((const unsigned int*) v1) - *((const unsigned int*) v2);
1513 static const SwfdecActionSpec *
1514 swfdec_action_find (guint action)
1516 return bsearch (&action, actions, G_N_ELEMENTS (actions),
1517 sizeof (SwfdecActionSpec), uint_compare);