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 ***/
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 */
45 guint original
; /* amount of bytes we've advanced in the input */
46 guint compiled
; /* amount of bytes we've advancecd in the bytecode */
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 */
65 /*** DEBUGGING STUFF ***/
67 /* NB: this must be called _before_ adding bytecode */
69 compile_state_debug_add (CompileState
*state
, const char *format
, ...) G_GNUC_PRINTF (2, 3);
71 compile_state_debug_add (CompileState
*state
, const char *format
, ...)
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
);
82 state
->command_last
= G_MAXUINT
;
83 g_array_append_val (state
->commands
, command
);
87 compile_state_debug_add_default (CompileState
*state
, guint action
, const char *name
)
89 SwfdecDebuggerCommand command
= { NULL
, };
91 if (state
->command_last
== G_MAXUINT
)
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
);
104 compile_state_debug_finish (CompileState
*state
, SwfdecPlayer
*player
, JSScript
*script
, const char *name
)
106 SwfdecDebuggerCommand
*command
;
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
);
118 g_array_free (state
->commands
, TRUE
);
122 /*** GENERAL FUNCTIONS ***/
125 compile_state_error (CompileState
*state
, char *format
, ...) G_GNUC_PRINTF (2, 3);
127 compile_state_error (CompileState
*state
, char *format
, ...)
131 g_assert (state
->error
== NULL
);
133 va_start (args
, format
);
134 state
->error
= g_strdup_vprintf (format
, args
);
138 /*** ACTION COMPILATION FUNCTIONS ***/
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
);
150 atomize_string (CompileState
*state
, const char *name
)
153 JSAtomListElement
*ale
;
155 atom
= js_Atomize (state
->cx
, name
, strlen (name
), 0);
156 ale
= js_IndexAtom (state
->cx
, atom
, &state
->atoms
);
158 compile_state_error (state
, "Failed to add name %s", name
);
161 return ALE_INDEX (ale
);
165 atomize_double (CompileState
*state
, jsdouble d
)
168 JSAtomListElement
*ale
;
170 atom
= js_AtomizeDouble (state
->cx
, d
, 0);
171 ale
= js_IndexAtom (state
->cx
, atom
, &state
->atoms
);
173 compile_state_error (state
, "Failed to add double %g", d
);
176 return ALE_INDEX (ale
);
180 atomize_int32 (CompileState
*state
, int i
)
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
);
189 compile_state_error (state
, "Failed to add int %d", i
);
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); \
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); \
203 #define THREELINER_INT(state, opcode, _int) THREELINER (state, opcode, (_int) >> 8, (_int))
204 #define THREELINER_ATOM(state, opcode, str) G_STMT_START { \
206 id = atomize_string (state, str); \
207 THREELINER_INT (state, opcode, id); \
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); \
224 #define IFEQ(state, offset) DO_JUMP (state, JSOP_IFEQ, offset)
225 #define IFNE(state, offset) DO_JUMP (state, JSOP_IFNE, offset)
228 compile_state_add_code (CompileState
*state
, const guint8
*code
, guint len
)
232 g_byte_array_append (state
->bytecode
, code
, len
);
236 bind_name (CompileState
*state
, const char *name
)
238 THREELINER_ATOM (state
, JSOP_BINDNAME
, name
);
241 #define TARGET_NAME "__swfdec_target"
243 compile_state_set_target (CompileState
*state
)
245 guint8 command
[3] = { JSOP_BINDNAME
, 0, 0 };
246 compile_state_add_code (state
, command
, 3);
248 command
[0] = JSOP_SETNAME
;
249 compile_state_add_code (state
, command
, 3);
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);
262 compile_state_set_target (state
);
266 compile_state_init (JSContext
*cx
, SwfdecBits
*bits
, int version
, CompileState
*state
)
270 state
->version
= version
;
271 ATOM_LIST_INIT (&state
->atoms
);
272 state
->bytecode
= g_byte_array_new ();
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);
286 compile_state_resolve_jumps (CompileState
*state
)
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
;
304 compile_state_error (state
, "Jumped into action");
307 offset
= g_array_index (state
->offsets
, Offset
,
308 MIN (state
->offsets
->len
- 1, jump
->offset
)).compiled
;
309 offset
-= jump
->bytecode
;
312 if (jump
->extended
) {
313 gint32
*data
= (gint32
*) bytecode
;
314 *data
= GINT32_TO_BE (offset
);
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
));
322 *data
= GINT16_TO_BE (offset
);
327 #define OFFSET_MAIN 3
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
) {
339 js_InitAtomMap (cx
, &clear
, &state
->atoms
);
340 js_FreeAtomMap (cx
, &clear
);
341 SWFDEC_ERROR ("%s", state
->error
);
342 g_free (state
->error
);
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 */
352 script
->main
= script
->code
+ OFFSET_MAIN
;
353 SN_MAKE_TERMINATOR (SCRIPT_NOTES (script
));
354 compile_state_debug_finish (state
, player
, script
, name
);
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
);
366 add_try_catch_block (CompileState
*state
, guint n_bytes
, guint pc_offset
)
369 ptrdiff_t cur_offset
;
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 */
383 tn
.length
= cur_offset
- last
;
385 g_array_append_val (state
->trynotes
, tn
);
387 tn
.start
= cur_offset
;
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 */
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
);
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
);
409 g_array_append_val (state
->jumps
, jump
);
412 /* must be called before adding the jump command to the bytecode */
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
);
423 g_array_append_val (state
->jumps
, jump
);
427 push_target (CompileState
*state
)
429 THREELINER (state
, JSOP_NAME
, 0, 0);
433 push_prop (CompileState
*state
, const char *name
)
435 THREELINER_ATOM (state
, JSOP_GETPROP
, name
);
439 name (CompileState
*state
, const char *name
)
441 THREELINER_ATOM (state
, JSOP_NAME
, name
);
446 push_prop_without_target (CompileState
*state
, const char *name
)
451 id
= atomize_string (state
, name
);
452 command
[0] = JSOP_BINDNAME
;
453 command
[1] = id
>> 8;
455 compile_state_add_code (state
, command
, 3);
456 command
[0] = JSOP_GETPROP
;
457 compile_state_add_code (state
, command
, 3);
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
);
470 call (CompileState
*state
, guint n_arguments
)
472 THREELINER_INT (state
, JSOP_CALL
, n_arguments
);
476 call_void_function (CompileState
*state
, const char *name
)
478 push_prop (state
, name
);
485 #define DEBUG_TRACE(state) G_STMT_START { \
486 ONELINER (state, JSOP_DUP); \
487 compile_trace (state, 0, 0); \
491 compile_trace (CompileState
*state
, guint action
, guint len
)
493 push_uint16 (state
, 1);
494 bind_name (state
, "trace");
495 push_prop (state
, "trace");
502 compile_get_variable (CompileState
*state
, guint action
, guint len
)
504 push_uint16 (state
, 1);
506 push_prop (state
, "eval");
512 compile_set_variable (CompileState
*state
, guint action
, guint len
)
514 /* FIXME: handle paths */
516 FLASHSWAP (state
, 3);
518 ONELINER (state
, JSOP_SETELEM
);
523 push_string (CompileState
*state
, const char *s
)
525 SWFDEC_LOG ("pushing string: %s", s
);
526 THREELINER_ATOM (state
, JSOP_STRING
, s
);
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
);
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
);
547 compile_push (CompileState
*state
, guint action
, guint len
)
549 SwfdecBits
*bits
= state
->bits
;
551 unsigned char *end
= bits
->ptr
+ len
;
556 while (bits
->ptr
< end
) {
557 type
= swfdec_bits_get_u8 (bits
);
558 SWFDEC_LOG ("push type %u", type
);
561 s
= swfdec_bits_skip_string (state
->bits
);
563 compile_state_error (state
, "Push: Could not get string");
566 compile_state_debug_add (state
, "Push \"%s\"", s
);
567 push_string (state
, s
);
570 d
= swfdec_bits_get_float (state
->bits
);
571 compile_state_debug_add (state
, "Push %g", d
);
572 push_double (state
, d
);
575 compile_state_debug_add (state
, "Push null");
576 ONELINER (state
, JSOP_NULL
);
578 case 3: /* undefined */
579 compile_state_debug_add (state
, "Push undefined");
580 ONELINER (state
, JSOP_NULL
);
581 ONELINER (state
, JSOP_VOID
);
583 case 5: /* boolean */
584 type
= swfdec_bits_get_u8 (bits
);
586 compile_state_debug_add (state
, "Push TRUE");
587 ONELINER (state
, JSOP_TRUE
);
589 compile_state_debug_add (state
, "Push FALSE");
590 ONELINER (state
, JSOP_FALSE
);
594 d
= swfdec_bits_get_double (state
->bits
);
595 compile_state_debug_add (state
, "Push %g", d
);
596 push_double (state
, d
);
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
);
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
);
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
);
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
);
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
);
626 case 4: /* register */
628 compile_state_error (state
, "Push: type %u not implemented", type
);
629 swfdec_bits_getbits (bits
, 8 * (end
- bits
->ptr
));
635 compile_goto_label (CompileState
*state
, guint action
, guint len
)
639 s
= swfdec_bits_skip_string (state
->bits
);
640 compile_state_debug_add (state
, "GotoLabel \"%s\"s", s
);
642 push_prop (state
, "gotoAndStop");
644 push_string (state
, s
);
650 compile_goto_frame (CompileState
*state
, guint action
, guint len
)
654 i
= swfdec_bits_get_u16 (state
->bits
);
655 compile_state_debug_add (state
, "GotoFrame %u", i
);
657 push_prop (state
, "gotoAndStop");
659 push_uint16 (state
, i
+ 1);
665 compile_goto_frame2 (CompileState
*state
, guint action
, guint len
)
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");
675 SWFDEC_ERROR ("scene bias not implemented");
677 push_uint16 (state
, 1);
680 push_prop (state
, "gotoAndPlay");
682 push_prop (state
, "gotoAndStop");
688 compile_wait_for_frame (CompileState
*state
, guint action
, guint len
)
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
);
697 push_prop (state
, "_framesloaded");
698 push_uint16 (state
, frame
);
700 compile_state_add_action_jump (state
, jump
, FALSE
);
701 compile_state_add_code (state
, command
, 3);
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);
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);
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
);
729 push_prop (state
, "eval");
731 push_string (state
, s
);
733 compile_state_set_target (state
);
737 compile_set_target_2 (CompileState
*state
, guint action
, guint len
)
739 compile_state_set_target (state
);
743 compile_constant_pool (CompileState
*state
, guint action
, guint len
)
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
);
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
);
770 push_prop (state
, "getURL");
772 push_string (state
, url
);
773 push_string (state
, target
);
779 compile_oneliner_pop (CompileState
*state
, guint action
, guint len
)
787 g_assert_not_reached ();
790 ONELINER (state
, op
);
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);
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 */
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);
816 ONELINER (state
, JSOP_ZERO
);
820 /* Flash < 5 does not know numbers. Therefore we convert booleans to 1 or 0 */
822 boolean_to_number_flash4 (CompileState
*state
)
824 if (state
->version
< 5) {
825 ONELINER (state
, JSOP_ZERO
);
826 ONELINER (state
, JSOP_ADD
);
831 compile_binary_op (CompileState
*state
, guint action
, guint len
)
835 if (state
->version
< 7) {
836 /* lots of code to ensure that non-numeric arguments equal 0. */
838 ensure_number_flash6 (state
);
840 ensure_number_flash6 (state
);
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);
861 ONELINER (state
, JSOP_ZERO
);
866 g_assert_not_reached ();
869 ONELINER (state
, op
);
873 compile_add2 (CompileState
*state
, guint action
, guint len
)
875 if (state
->version
>= 7) {
876 ONELINER (state
, JSOP_ADD
);
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);
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);
892 ONELINER (state
, JSOP_ZERO
);
893 THREELINER_INT (state
, JSOP_GOTO
, 17);
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);
901 THREELINER_INT (state
, JSOP_GOTO
, 5);
903 ONELINER (state
, JSOP_ADD
);
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. */
912 ensure_number_flash6 (state
);
914 ensure_number_flash6 (state
);
916 ONELINER (state
, JSOP_LT
);
918 boolean_to_number_flash4 (state
);
922 compile_comparison (CompileState
*state
, guint action
, guint len
)
926 if (state
->version
< 7) {
927 /* lots of code to ensure that non-numeric arguments equal 0. */
929 ensure_number_flash6 (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);
940 ONELINER (state
, JSOP_VOID
);
941 THREELINER_INT (state
, JSOP_GOTO
, 18);
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);
949 ONELINER (state
, JSOP_VOID
);
950 THREELINER_INT (state
, JSOP_GOTO
, 5);
953 /* FIXME: what about Flash > 7? */
963 g_assert_not_reached ();
966 ONELINER (state
, op
);
970 compile_oneliner (CompileState
*state
, guint action
, guint len
)
994 g_assert_not_reached ();
997 ONELINER (state
, op
);
1001 compile_call_function (CompileState
*state
, guint action
, guint len
)
1003 push_target (state
);
1005 ONELINER (state
, JSOP_GETELEM
);
1006 ONELINER (state
, JSOP_PUSHOBJ
);
1007 ONELINER (state
, JSOP_FLASHCALL
);
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
);
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);
1020 ONELINER (state
, JSOP_FLASHCALL
);
1021 THREELINER_INT (state
, JSOP_GOTO
, 17);
1022 /* exception handling goes here: clean up the stack */
1025 ONELINER (state
, JSOP_DUP
);
1026 THREELINER_INT (state
, JSOP_IFEQ
, 10);
1029 ONELINER (state
, JSOP_ONE
);
1030 ONELINER (state
, JSOP_SUB
);
1031 THREELINER_INT (state
, JSOP_GOTO
, -8);
1032 ONELINER (state
, JSOP_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");
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
;
1056 compile_state_add_code (state
, command
, 3);
1057 push_uint16 (state
, 1);
1059 FLASHSWAP (state
, 3);
1060 push_prop (state
, "startDrag");
1067 compile_increment (CompileState
*state
, guint action
, guint len
)
1069 ONELINER (state
, JSOP_ONE
);
1070 ONELINER (state
, JSOP_ADD
);
1074 compile_decrement (CompileState
*state
, guint action
, guint len
)
1076 ONELINER (state
, JSOP_ONE
);
1077 ONELINER (state
, JSOP_SUB
);
1081 compile_random (CompileState
*state
, guint action
, guint len
)
1083 push_uint16 (state
, 1);
1084 bind_name (state
, "random");
1085 push_prop (state
, "random");
1091 compile_get_property (CompileState
*state
, guint action
, guint len
)
1094 push_uint16 (state
, 2);
1095 push_target (state
);
1096 push_prop (state
, "getProperty");
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");
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
);
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
);
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
);
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");
1153 THREELINER_INT (state
, JSOP_GOTO
, 4);
1154 ONELINER (state
, JSOP_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");
1167 /* use call instead of new here - if this doesn't work, a FLASHNEW opcode is needed */
1173 compile_init_object (CompileState
*state
, guint action
, guint len
)
1175 name (state
, "Object");
1177 THREELINER_INT (state
, JSOP_NEW
, 0);
1179 FLASHSWAP (state
, 3);
1181 THREELINER_INT (state
, JSOP_IFEQ
, 17);
1182 ONELINER (state
, JSOP_ONE
);
1183 ONELINER (state
, JSOP_SUB
);
1184 FLASHSWAP (state
, 5);
1186 FLASHSWAP (state
, 4);
1187 ONELINER (state
, JSOP_SETELEM
);
1189 THREELINER_INT (state
, JSOP_GOTO
, -19);
1195 compile_simple_bind_call (CompileState
*state
, guint action
, guint len
)
1200 name
= "stopAllSounds";
1203 g_assert_not_reached ();
1206 bind_name (state
, name
);
1207 call_void_function (state
, name
);
1211 compile_simple_call (CompileState
*state
, guint action
, guint len
)
1215 /* FIXME: shouldn't some functions here PUSH_OBJ instead of push_target? */
1216 push_target (state
);
1231 name
= "stopAllSounds";
1237 g_assert_not_reached ();
1240 call_void_function (state
, name
);
1248 void (* compile
) (CompileState
*state
, guint action
, guint len
);
1251 static const SwfdecActionSpec
* swfdec_action_find (guint action
);
1254 swfdec_disassemble (SwfdecPlayer
*player
, JSScript
*script
)
1258 for (i
= 0; i
< script
->length
; i
++) {
1259 g_print ("%02X ", script
->code
[i
]);
1262 else if (i
% 4 == 3)
1267 js_Disassemble (player
->jscx
, script
, JS_TRUE
, stdout
);
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
1283 swfdec_compile (SwfdecPlayer
*player
, SwfdecBits
*bits
, int version
, const char *name
)
1285 unsigned int action
, len
;
1286 const SwfdecActionSpec
*current
;
1289 #ifndef SWFDEC_DISABLE_DEBUG
1290 unsigned char *start
= bits
->ptr
;
1292 #ifndef G_DISABLE_ASSERT
1293 unsigned char *target
;
1295 //#define SWFDEC_DUMP_SCRIPTS
1296 #ifdef SWFDEC_DUMP_SCRIPTS
1297 SwfdecBits dump
= *bits
;
1300 g_return_val_if_fail (SWFDEC_IS_PLAYER (player
), NULL
);
1301 g_return_val_if_fail (bits
!= NULL
, 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
);
1313 #ifndef G_DISABLE_ASSERT
1314 target
= bits
->ptr
+ len
;
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,
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,
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
);
1339 swfdec_bits_getbits (bits
, len
* 8);
1340 if (state
.error
== NULL
) {
1342 compile_state_error (&state
, "No compilation function for %s action", current
->name
);
1344 compile_state_error (&state
, "unknown action 0x%02X", action
);
1349 ret
= compile_state_finish (&state
, player
, name
);
1352 swfdec_disassemble (s
, ret
);
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
);
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.
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
[] = {
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
},
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
},
1422 { 0x2a, "Throw", NULL
},
1423 { 0x2b, "Cast", NULL
},
1424 { 0x2c, "Implements", NULL
},
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
},
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
},
1462 { 0x54, "InstanceOf", NULL
},
1463 { 0x55, "Enumerate2", NULL
},
1465 { 0x60, "BitAnd", NULL
},
1466 { 0x61, "BitOr", NULL
},
1467 { 0x62, "BitXor", NULL
},
1468 { 0x63, "BitLShift", NULL
},
1469 { 0x64, "BitRShift", NULL
},
1470 { 0x65, "BitURShift", NULL
},
1472 { 0x66, "StrictEquals", NULL
},
1473 { 0x67, "Greater", compile_comparison
},
1474 { 0x68, "StringGreater", NULL
},
1476 { 0x69, "Extends", NULL
},
1479 { 0x81, "GotoFrame", compile_goto_frame
},
1480 { 0x83, "GetURL", compile_get_url
},
1482 { 0x87, "StoreRegister", NULL
},
1483 { 0x88, "ConstantPool", compile_constant_pool
},
1485 { 0x8a, "WaitForFrame", compile_wait_for_frame
},
1486 { 0x8b, "SetTarget", compile_set_target
},
1487 { 0x8c, "GotoLabel", compile_goto_label
},
1489 { 0x8d, "WaitForFrame2", NULL
},
1491 { 0x8e, "DefineFunction2", NULL
},
1492 { 0x8f, "Try", NULL
},
1494 { 0x94, "With", NULL
},
1496 { 0x96, "Push", compile_push
},
1497 { 0x99, "Jump", compile_jump
},
1498 { 0x9a, "GetURL2", NULL
},
1500 { 0x9b, "DefineFunction", NULL
},
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
);