implement breakpoint API (breakpoints don't actually trigger yet)
[swfdec.git] / libswfdec / swfdec_debugger.c
blob722afa470f7b6d43fc4e524700c162b1a9edf65e
1 /* Swfdec
2 * Copyright (C) 2006 Benjamin Otte <otte@gnome.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301 USA
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
24 #include <string.h>
25 #include "swfdec_debugger.h"
26 #include "swfdec_debug.h"
27 #include "swfdec_decoder.h"
28 #include "swfdec_player_internal.h"
29 #include "js/jsdbgapi.h"
31 /*** SwfdecDebuggerScript ***/
33 static SwfdecDebuggerScript *
34 swfdec_debugger_script_new (JSScript *script, const char *name,
35 SwfdecDebuggerCommand *commands, guint n_commands)
37 SwfdecDebuggerScript *ret;
39 ret = g_new0 (SwfdecDebuggerScript, 1);
40 ret->script = script;
41 ret->name = g_strdup (name);
42 ret->commands = commands;
43 ret->n_commands = n_commands;
45 return ret;
48 static void
49 swfdec_debugger_script_free (SwfdecDebuggerScript *script)
51 g_assert (script);
52 g_free (script->commands);
53 g_free (script);
56 /*** SwfdecDebugger ***/
58 enum {
59 SCRIPT_ADDED,
60 SCRIPT_REMOVED,
61 BREAKPOINT_ADDED,
62 BREAKPOINT_REMOVED,
63 LAST_SIGNAL
66 G_DEFINE_TYPE (SwfdecDebugger, swfdec_debugger, SWFDEC_TYPE_PLAYER)
67 guint signals [LAST_SIGNAL] = { 0, };
69 static void
70 swfdec_debugger_dispose (GObject *object)
72 SwfdecDebugger *debugger = SWFDEC_DEBUGGER (object);
74 g_assert (g_hash_table_size (debugger->scripts) == 0);
75 g_hash_table_destroy (debugger->scripts);
76 if (debugger->breakpoints)
77 g_array_free (debugger->breakpoints, TRUE);
79 G_OBJECT_CLASS (swfdec_debugger_parent_class)->dispose (object);
82 static void
83 swfdec_debugger_class_init (SwfdecDebuggerClass *klass)
85 GObjectClass *object_class = G_OBJECT_CLASS (klass);
87 object_class->dispose = swfdec_debugger_dispose;
89 signals[SCRIPT_ADDED] = g_signal_new ("script-added", G_TYPE_FROM_CLASS (klass),
90 G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__POINTER,
91 G_TYPE_NONE, 1, G_TYPE_POINTER);
92 signals[SCRIPT_REMOVED] = g_signal_new ("script-removed", G_TYPE_FROM_CLASS (klass),
93 G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__POINTER,
94 G_TYPE_NONE, 1, G_TYPE_POINTER);
95 signals[BREAKPOINT_ADDED] = g_signal_new ("breakpoint-added",
96 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL,
97 g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT);
98 signals[BREAKPOINT_REMOVED] = g_signal_new ("breakpoint-removed",
99 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL,
100 g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT);
103 static void
104 swfdec_debugger_init (SwfdecDebugger *debugger)
106 debugger->scripts = g_hash_table_new_full (g_direct_hash, g_direct_equal,
107 NULL, (GDestroyNotify) swfdec_debugger_script_free);
111 * swfdec_debugger_new:
113 * Creates a #SwfdecPlayer that can be debugged.
115 * Returns: a new #SwfdecDebugger
117 SwfdecPlayer *
118 swfdec_debugger_new (void)
120 SwfdecPlayer *player;
122 swfdec_init ();
123 player = g_object_new (SWFDEC_TYPE_DEBUGGER, NULL);
124 return player;
127 void
128 swfdec_debugger_add_script (SwfdecDebugger *debugger, JSScript *jscript, const char *name,
129 SwfdecDebuggerCommand *commands, guint n_commands)
131 SwfdecDebuggerScript *dscript = swfdec_debugger_script_new (jscript, name, commands, n_commands);
133 g_hash_table_insert (debugger->scripts, jscript, dscript);
134 g_signal_emit (debugger, signals[SCRIPT_ADDED], 0, dscript);
137 void
138 swfdec_debugger_remove_script (SwfdecDebugger *debugger, JSScript *script)
140 SwfdecDebuggerScript *dscript = g_hash_table_lookup (debugger->scripts, script);
142 g_assert (dscript);
143 g_signal_emit (debugger, signals[SCRIPT_REMOVED], 0, dscript);
144 g_hash_table_remove (debugger->scripts, script);
147 typedef struct {
148 GFunc func;
149 gpointer data;
150 } ForeachData;
152 static void
153 do_foreach (gpointer key, gpointer value, gpointer data)
155 ForeachData *fdata = data;
157 fdata->func (value, fdata->data);
160 void
161 swfdec_debugger_foreach_script (SwfdecDebugger *debugger, GFunc func, gpointer data)
163 ForeachData fdata = { func, data };
164 g_hash_table_foreach (debugger->scripts, do_foreach, &fdata);
167 /*** BREAKPOINTS ***/
169 typedef struct {
170 SwfdecDebuggerScript * script;
171 guint line;
172 } Breakpoint;
174 static JSTrapStatus
175 swfdec_debugger_handle_breakpoint (JSContext *cx, JSScript *script, jsbytecode *pc,
176 jsval *rval, void *closure)
178 return JSTRAP_CONTINUE;
181 guint
182 swfdec_debugger_set_breakpoint (SwfdecDebugger *debugger,
183 SwfdecDebuggerScript *script, guint line)
185 guint i;
186 Breakpoint *br = NULL;
188 g_return_val_if_fail (SWFDEC_IS_DEBUGGER (debugger), 0);
189 g_return_val_if_fail (script != NULL, 0);
190 g_return_val_if_fail (line < script->n_commands, 0);
192 if (debugger->breakpoints == NULL)
193 debugger->breakpoints = g_array_new (FALSE, FALSE, sizeof (Breakpoint));
194 if (script->commands[line].breakpoint != 0)
195 return script->commands[line].breakpoint;
197 for (i = 0; i < debugger->breakpoints->len; i++) {
198 br = &g_array_index (debugger->breakpoints, Breakpoint, i);
199 if (br->script == NULL)
200 break;
201 br = NULL;
203 if (!JS_SetTrap (SWFDEC_PLAYER (debugger)->jscx, script->script,
204 script->commands[line].code, swfdec_debugger_handle_breakpoint,
205 GUINT_TO_POINTER (i)))
206 return 0;
208 if (br == NULL) {
209 g_array_set_size (debugger->breakpoints, debugger->breakpoints->len + 1);
210 br = &g_array_index (debugger->breakpoints, Breakpoint,
211 debugger->breakpoints->len - 1);
213 br->script = script;
214 br->line = line;
215 script->commands[line].breakpoint = i + 1;
216 g_signal_emit (debugger, signals[BREAKPOINT_ADDED], 0, i + 1);
217 return i + 1;
220 void
221 swfdec_debugger_unset_breakpoint (SwfdecDebugger *debugger, guint id)
223 Breakpoint *br;
225 g_return_if_fail (SWFDEC_IS_DEBUGGER (debugger));
226 g_return_if_fail (id > 0);
228 if (debugger->breakpoints == NULL)
229 return;
230 if (id > debugger->breakpoints->len)
231 return;
232 br = &g_array_index (debugger->breakpoints, Breakpoint, id - 1);
233 if (br->script == NULL)
234 return;
236 JS_ClearTrap (SWFDEC_PLAYER (debugger)->jscx, br->script->script,
237 br->script->commands[br->line].code, NULL, NULL);
238 g_signal_emit (debugger, signals[BREAKPOINT_REMOVED], 0, id);
239 br->script->commands[br->line].breakpoint = 0;
240 br->script = NULL;
243 gboolean
244 swfdec_debugger_get_breakpoint (SwfdecDebugger *debugger, guint id,
245 SwfdecDebuggerScript **script, guint *line)
247 Breakpoint *br;
249 g_return_val_if_fail (SWFDEC_IS_DEBUGGER (debugger), FALSE);
250 g_return_val_if_fail (id > 0, FALSE);
252 if (debugger->breakpoints == NULL)
253 return FALSE;
254 if (id > debugger->breakpoints->len)
255 return FALSE;
256 br = &g_array_index (debugger->breakpoints, Breakpoint, id - 1);
257 if (br->script == NULL)
258 return FALSE;
260 if (script)
261 *script = br->script;
262 if (line)
263 *line = br->line;
264 return TRUE;
267 guint
268 swfdec_debugger_get_n_breakpoints (SwfdecDebugger *debugger)
270 g_return_val_if_fail (SWFDEC_IS_DEBUGGER (debugger), 0);
272 if (debugger->breakpoints == NULL)
273 return 0;
275 return debugger->breakpoints->len;