1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
3 Copyright (C) 2009 Maxim Ermilov <zaspire@rambler.ru>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 #include <libanjuta/anjuta-plugin.h>
20 #include <libanjuta/interfaces/ianjuta-debugger.h>
21 #include <libanjuta/interfaces/ianjuta-debugger-breakpoint.h>
22 #include <libanjuta/interfaces/ianjuta-debugger-variable.h>
23 #include <libanjuta/interfaces/ianjuta-terminal.h>
24 #include "debugger-server.h"
25 #include "debugger-js.h"
27 typedef struct _DebuggerJsPrivate DebuggerJsPrivate
;
28 struct _DebuggerJsPrivate
30 IAnjutaTerminal
*terminal
;
32 gboolean started
, exited
;
34 IAnjutaDebugger
*data
;
35 gchar
*working_directory
;
36 gchar
*current_source_file
;
42 DebuggerServer
*server
;
47 #define DEBUGGER_JS_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DEBUGGER_TYPE_JS, DebuggerJsPrivate))
55 static guint js_signals
[LAST_SIGNAL
] = { 0 };
57 G_DEFINE_TYPE (DebuggerJs
, debugger_js
, G_TYPE_OBJECT
);
63 VARIABLE_LIST_CHILDREN
,
73 IAnjutaDebuggerCallback callback
;
82 }VareableListChildren
;
88 debugger_js_init (DebuggerJs
*object
)
90 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
92 priv
->current_source_file
= NULL
;
93 priv
->working_directory
= g_strdup (".");
94 priv
->dataRecived
= FALSE
;
95 priv
->started
= FALSE
;
98 priv
->breakpoint
= NULL
;
102 priv
->task_queue
= NULL
;
104 priv
->terminal
= NULL
;
105 priv
->filename
= NULL
;
107 priv
->current_line
= 0;
111 static void on_child_exited (IAnjutaTerminal
*obj
, gint pid
, gint status
, gpointer user_data
);
114 debugger_js_finalize (GObject
*object
)
116 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
118 g_assert (priv
!= NULL
);
120 g_signal_handlers_disconnect_by_func(G_OBJECT (priv
->terminal
), on_child_exited
, object
);
122 g_free (priv
->filename
);
123 g_free (priv
->working_directory
);
124 g_free (priv
->current_source_file
);
125 g_list_foreach (priv
->breakpoint
, (GFunc
)g_free
, NULL
);
126 g_list_free (priv
->breakpoint
);
127 debugger_server_stop (priv
->server
);
128 g_object_unref (priv
->server
);
129 g_list_foreach (priv
->task_queue
, (GFunc
)g_free
, NULL
);
130 g_list_free (priv
->task_queue
);
132 G_OBJECT_CLASS (debugger_js_parent_class
)->finalize (object
);
136 debugger_js_debugger_error (DebuggerJs
*self
, const gchar
*text
)
138 g_warning ("%s", text
);
142 debugger_js_class_init (DebuggerJsClass
*klass
)
144 GObjectClass
* object_class
= G_OBJECT_CLASS (klass
);
146 g_type_class_add_private (klass
, sizeof (DebuggerJsPrivate
));
148 object_class
->finalize
= debugger_js_finalize
;
150 klass
->DebuggerError
= debugger_js_debugger_error
;
152 js_signals
[DEBUGGER_ERROR
] =
153 g_signal_new ("DebuggerError",
154 G_OBJECT_CLASS_TYPE (klass
),
156 G_STRUCT_OFFSET (DebuggerJsClass
, DebuggerError
),
158 g_cclosure_marshal_VOID__POINTER
,
159 G_TYPE_NONE
, 1, G_TYPE_POINTER
);
163 task_added (DebuggerJs
* object
)
165 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
167 g_signal_emit_by_name (priv
->data
, "debugger-ready", IANJUTA_DEBUGGER_BUSY
);
171 variable_create (DebuggerJs
* object
, struct Task
*task
)
173 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
175 IAnjutaDebuggerVariableObject
*var
= g_new (IAnjutaDebuggerVariableObject
, 1);
176 gchar
*str
= debugger_server_get_line (priv
->server
);
177 var
->expression
= g_strdup (task
->name
);
178 var
->name
= g_strdup (task
->name
);
179 var
->type
= g_strdup ("object");
180 var
->value
= g_strdup ("object");
182 var
->has_more
= FALSE
;
185 var
->deleted
= FALSE
;
189 var
->type
= g_strdup ("string");
190 var
->value
= g_strdup (str
);
195 for (i
= 0, k
= 0, size
= strlen (str
); i
< size
; i
++)
212 info_thread (DebuggerJs
* object
, struct Task
*task
)
214 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
215 IAnjutaDebuggerFrame
* frame
;
219 frame
= g_new0 (IAnjutaDebuggerFrame
, 1);
221 frame
->thread
= thread
;
222 frame
->address
= 0xFFFF;
223 frame
->file
= g_strdup (priv
->current_source_file
);
224 frame
->line
= priv
->current_line
;
225 frame
->library
= NULL
;
226 frame
->function
= NULL
;
232 list_frame (DebuggerJs
* object
, struct Task
*task
)
234 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
236 IAnjutaDebuggerFrame
* frame
;
240 gchar
*line
= debugger_server_get_line (priv
->server
);
242 for (k
= line
, i
= 0, size
= strlen (line
); i
<= size
; i
++)
246 gchar
*filename
= g_new (gchar
, strlen (line
) + 1);
249 frame
= g_new0 (IAnjutaDebuggerFrame
, 1);
252 if (sscanf (k
, " LINE# %d %s", &lineno
, filename
) != 2)
254 g_signal_emit_by_name (object
, "DebuggerError", "Invalid data arrived", G_TYPE_NONE
);
257 frame
->file
= filename
;
258 frame
->line
= lineno
;
260 frame
->function
= NULL
;
261 frame
->library
= NULL
;
263 var
= g_list_append (var
, frame
);
268 /* g_list_foreach (var, GFunc (g_free), NULL);
274 list_thread (DebuggerJs
* object
, struct Task
*task
)
276 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
278 IAnjutaDebuggerFrame
* frame
;
281 frame
= g_new0 (IAnjutaDebuggerFrame
, 1);
284 frame
->file
= g_strdup (priv
->current_source_file
);
285 frame
->line
= priv
->current_line
;
286 frame
->library
= NULL
;
287 frame
->function
= NULL
;
288 list
= g_list_append (list
, frame
);
294 list_local (DebuggerJs
* object
, struct Task
*task
)
296 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
300 gchar
*line
= debugger_server_get_line (priv
->server
);
302 for (k
= line
, i
= 0, size
= strlen (line
); i
<= size
; i
++)
307 var
= g_list_append (var
, g_strdup (k
));
317 varibale_list_children (DebuggerJs
* object
, struct Task
*task
)
319 size_t i
, k
, j
, size
;
320 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
322 IAnjutaDebuggerVariableObject
*var
= NULL
;
323 gchar
*str
= debugger_server_get_line (priv
->server
);
325 for (i
= 0, k
= 0, j
= 0, size
= strlen (str
); i
< size
; i
++)
337 var
= g_new (IAnjutaDebuggerVariableObject
, 1);
338 var
->type
= g_strdup (str
+ k
+ 1);
339 var
->value
= g_strdup ("");
341 var
->has_more
= FALSE
;
346 var
->expression
= g_strconcat (task
->this_data
.VareableListChildren
.name
, ".", str
+ k
+ 2, NULL
);
347 var
->name
= g_strconcat (task
->this_data
.VareableListChildren
.name
, ".", str
+ k
+ 2, NULL
);
353 ret
= g_list_append (ret
, var
);
354 for (z
= 1; z
&& i
< size
; i
++)
366 g_assert (var
== NULL
);
373 on_data_arrived (DebuggerServer
*server
, gpointer user_data
)
375 DebuggerJs
* object
= DEBUGGER_JS (user_data
);
376 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
378 if (priv
->task_queue
!= NULL
)
380 struct Task
*task
= (struct Task
*)priv
->task_queue
->data
;
384 while (task
->line_required
<= debugger_server_get_line_col (priv
->server
))
387 switch (task
->task_type
)
390 task
->callback (NULL
, task
->user_data
, NULL
);
392 case VARIABLE_CREATE
:
393 result
= variable_create (object
, task
);
394 task
->callback (result
, task
->user_data
, NULL
);
397 result
= info_thread (object
, task
);
398 task
->callback (result
, task
->user_data
, NULL
);
401 result
= list_thread (object
, task
);
402 task
->callback (result
, task
->user_data
, NULL
);
405 result
= list_frame (object
, task
);
406 task
->callback (result
, task
->user_data
, NULL
);
409 result
= list_local (object
, task
);
410 task
->callback (result
, task
->user_data
, NULL
);
412 case BREAKPOINT_LIST
:
413 task
->callback (priv
->breakpoint
, task
->user_data
, NULL
);//TODO: Return Copy
415 case VARIABLE_LIST_CHILDREN
:
416 result
= varibale_list_children (object
, task
);
417 task
->callback (result
, task
->user_data
, NULL
);
420 printf ("%d\n", task
->task_type
);
421 g_assert_not_reached ();
425 g_signal_emit_by_name (priv
->data
, "debugger-ready", debugger_js_get_state (object
));
427 priv
->task_queue
= g_list_delete_link (priv
->task_queue
, priv
->task_queue
);
428 if (!priv
->task_queue
)
430 task
= (struct Task
*)priv
->task_queue
->data
;
433 if (priv
->task_queue
== NULL
&& debugger_server_get_line_col (priv
->server
) > 0)
436 gchar
*line
= debugger_server_get_line (server
);
440 g_assert (strlen (line
) != 0);
442 priv
->dataRecived
= TRUE
;
443 file
= (gchar
*)g_malloc (strlen (line
));
445 if (priv
->current_source_file
)
446 g_free (priv
->current_source_file
);
448 if (sscanf (line
, "Line #%d File:%s\n", &lineno
, file
) == 2) //TODO: Correct recive filename
450 priv
->current_line
= lineno
;
451 priv
->current_source_file
= file
;
453 g_signal_emit_by_name (priv
->data
, "program-moved", 0, 0, 0, file
, lineno
);
456 g_signal_emit_by_name (object
, "DebuggerError", "Invalid data arrived", G_TYPE_NONE
);
463 on_error (DebuggerServer
*server
, const gchar
* error
, gpointer user_data
)
465 DebuggerJs
* object
= DEBUGGER_JS (user_data
);
466 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
468 g_assert (error
!= NULL
);
470 g_signal_emit_by_name (priv
->data
, "debugger-ready", IANJUTA_DEBUGGER_STOPPED
);
472 priv
->started
= TRUE
;
475 g_signal_emit (object
, js_signals
[DEBUGGER_ERROR
], 0, error
);
479 debugger_js_new (int port
, const gchar
* filename
, IAnjutaDebugger
*data
)
481 DebuggerJs
* object
= g_object_new (DEBUGGER_TYPE_JS
, NULL
);
482 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
484 g_assert (data
!= NULL
);
485 g_assert (filename
!= NULL
);
488 priv
->terminal
= anjuta_shell_get_interface (ANJUTA_PLUGIN (data
)->shell
, IAnjutaTerminal
, NULL
);
491 g_warning ("Terminal plugin does not installed.");
493 priv
->server
= debugger_server_new (port
);
496 if (priv
->server
== NULL
)
498 g_object_unref (object
);
501 g_signal_connect (priv
->server
, "data-arrived", G_CALLBACK (on_data_arrived
), object
);
502 g_signal_connect (priv
->server
, "error", G_CALLBACK (on_error
), object
);
504 priv
->filename
= g_strdup (filename
);
505 g_signal_emit_by_name (data
, "debugger-started");
511 debugger_js_get_state (DebuggerJs
*object
)
513 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
516 return IANJUTA_DEBUGGER_BUSY
;
518 return IANJUTA_DEBUGGER_PROGRAM_LOADED
;
520 return IANJUTA_DEBUGGER_STOPPED
;
521 if (debugger_server_get_line_col (priv
->server
) || priv
->dataRecived
)
522 return IANJUTA_DEBUGGER_PROGRAM_STOPPED
;
524 return IANJUTA_DEBUGGER_PROGRAM_RUNNING
;
528 debugger_js_set_work_dir (DebuggerJs
*object
, const gchar
* work_dir
)
530 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
532 g_assert (work_dir
!= NULL
);
534 if (priv
->working_directory
)
535 g_free (priv
->working_directory
);
536 priv
->working_directory
= g_strdup (work_dir
);
540 debugger_js_start_remote (DebuggerJs
*object
, gint port
)
542 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
544 g_assert (DEBUGGER_IS_SERVER (priv
->server
));
545 g_object_unref (priv
->server
);
546 priv
->server
= debugger_server_new (port
);
548 on_error (NULL
, _("Error: cant bind port"), object
);
551 g_signal_connect (priv
->server
, "data-arrived", G_CALLBACK (on_data_arrived
), object
);
552 g_signal_connect (priv
->server
, "error", G_CALLBACK (on_error
), object
);
554 g_signal_emit_by_name (priv
->data
, "program-running");
555 priv
->started
= TRUE
;
559 on_child_exited (IAnjutaTerminal
*obj
, gint pid
, gint status
, gpointer user_data
)
561 DebuggerJs
* object
= DEBUGGER_JS (user_data
);
562 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
564 g_assert (priv
!= NULL
);
566 debugger_server_stop (priv
->server
);
568 priv
->started
= TRUE
;
570 kill (priv
->pid
, SIGKILL
);
571 g_signal_emit_by_name (priv
->data
, "debugger-ready", IANJUTA_DEBUGGER_STOPPED
);
575 debugger_js_start (DebuggerJs
*object
, const gchar
*arguments
)
577 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
579 g_assert (priv
->port
);
581 gchar
*port
= g_strdup_printf ("--js-port %d", priv
->port
);
582 gchar
*str
= g_strconcat (priv
->filename
, " --debug 127.0.0.1 ", port
, arguments
, NULL
);
585 g_assert (priv
->terminal
!= NULL
);
587 g_signal_emit_by_name (priv
->data
, "program-running");
588 g_signal_connect (G_OBJECT (priv
->terminal
), "child-exited",
589 G_CALLBACK (on_child_exited
), object
);
590 priv
->pid
= ianjuta_terminal_execute_command (priv
->terminal
, priv
->working_directory
, str
, NULL
, NULL
);
593 g_signal_emit_by_name (object
, "DebuggerError", "Cannot start programm", G_TYPE_NONE
);
594 priv
->started
= TRUE
;
600 debugger_js_continue (DebuggerJs
*object
)
602 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
604 priv
->dataRecived
= FALSE
;
605 debugger_server_send_line (priv
->server
, "continue");
609 debugger_js_stepin (DebuggerJs
*object
)
611 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
613 priv
->dataRecived
= FALSE
;
614 debugger_server_send_line (priv
->server
, "stepin");
618 debugger_js_stepover (DebuggerJs
*object
)
620 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
622 priv
->dataRecived
= FALSE
;
623 debugger_server_send_line (priv
->server
, "stepover");
627 debugger_js_stepout (DebuggerJs
*object
)
629 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
631 priv
->dataRecived
= FALSE
;
632 debugger_server_send_line (priv
->server
, "stepout");
636 debugger_js_stop (DebuggerJs
*object
)
638 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
640 debugger_server_stop (priv
->server
);
643 kill (priv
->pid
, SIGKILL
);
644 g_signal_emit_by_name (priv
->data
, "debugger-ready", IANJUTA_DEBUGGER_STOPPED
);
648 debugger_js_add_breakpoint (DebuggerJs
*object
, const gchar
* file
, guint line
)
650 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
653 g_assert (file
!= NULL
);
655 IAnjutaDebuggerBreakpointItem
* bp
= g_new (IAnjutaDebuggerBreakpointItem
, 1);
656 bp
->type
= IANJUTA_DEBUGGER_BREAKPOINT_ON_LINE
;
660 bp
->file
= g_strdup (file
);
661 debugger_server_send_line (priv
->server
, "add");
662 bp
->id
= priv
->BID
++;
664 str
= g_strdup_printf ("%d %s", line
, bp
->file
);
665 debugger_server_send_line (priv
->server
, str
);
668 priv
->breakpoint
= g_list_append (priv
->breakpoint
, bp
);
672 debugger_js_breakpoint_list (DebuggerJs
*object
, IAnjutaDebuggerGListCallback callback
, gpointer user_data
)
674 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
679 struct Task
*task
= g_new (struct Task
, 1);
680 task
->user_data
= user_data
;
681 task
->callback
= (IAnjutaDebuggerCallback
)callback
;
682 task
->line_required
= 0;
683 task
->task_type
= BREAKPOINT_LIST
;
685 priv
->task_queue
= g_list_append (priv
->task_queue
, task
);
689 debugger_js_signal (DebuggerJs
*object
, IAnjutaDebuggerCallback callback
, gpointer user_data
)
691 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
696 struct Task
*task
= g_new (struct Task
, 1);
697 task
->user_data
= user_data
;
698 task
->callback
= callback
;
699 task
->line_required
= 0;
700 task
->task_type
= SIGNAL
;
702 priv
->task_queue
= g_list_append (priv
->task_queue
, task
);
706 debugger_js_variable_list_children (DebuggerJs
*object
, IAnjutaDebuggerGListCallback callback
, const gchar
*name
, gpointer user_data
)
708 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
710 g_assert (name
!= NULL
);
713 struct Task
*task
= g_new (struct Task
, 1);
714 task
->user_data
= user_data
;
715 task
->callback
= (IAnjutaDebuggerCallback
)callback
;
716 task
->line_required
= 1;
717 task
->task_type
= VARIABLE_LIST_CHILDREN
;
718 task
->this_data
.VareableListChildren
.name
= g_strdup (name
);
720 debugger_server_send_line (priv
->server
, "eval");
721 debugger_server_send_line (priv
->server
, name
);
723 priv
->task_queue
= g_list_append (priv
->task_queue
, task
);
727 debugger_js_list_local (DebuggerJs
*object
, IAnjutaDebuggerGListCallback callback
, gpointer user_data
)
729 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
734 struct Task
*task
= g_new (struct Task
, 1);
735 task
->user_data
= user_data
;
736 task
->callback
= (IAnjutaDebuggerCallback
)callback
;
737 task
->line_required
= 1;
738 task
->task_type
= LIST_LOCAL
;
740 debugger_server_send_line (priv
->server
, "list");
742 priv
->task_queue
= g_list_append (priv
->task_queue
, task
);
746 debugger_js_list_thread (DebuggerJs
*object
, IAnjutaDebuggerGListCallback callback
, gpointer user_data
)
748 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
753 struct Task
*task
= g_new (struct Task
, 1);
754 task
->user_data
= user_data
;
755 task
->callback
= (IAnjutaDebuggerCallback
)callback
;
756 task
->line_required
= 0;
757 task
->task_type
= LIST_THREAD
;
759 priv
->task_queue
= g_list_append (priv
->task_queue
, task
);
763 debugger_js_list_frame (DebuggerJs
*object
, IAnjutaDebuggerGListCallback callback
, gpointer user_data
)
765 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
770 struct Task
*task
= g_new (struct Task
, 1);
771 task
->user_data
= user_data
;
772 task
->callback
= (IAnjutaDebuggerCallback
)callback
;
773 task
->line_required
= 1;
774 task
->task_type
= LIST_FRAME
;
776 debugger_server_send_line (priv
->server
, "stacktrace");
778 priv
->task_queue
= g_list_append (priv
->task_queue
, task
);
782 debugger_js_info_thread (DebuggerJs
*object
, IAnjutaDebuggerGListCallback callback
, gint thread
, gpointer user_data
)
784 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
789 struct Task
*task
= g_new (struct Task
, 1);
790 task
->user_data
= user_data
;
791 task
->callback
= (IAnjutaDebuggerCallback
)callback
;
792 task
->line_required
= 0;
793 task
->task_type
= INFO_THREAD
;
795 priv
->task_queue
= g_list_append (priv
->task_queue
, task
);
799 debugger_js_variable_create (DebuggerJs
*object
, IAnjutaDebuggerVariableCallback callback
, const gchar
*name
, gpointer user_data
)
801 DebuggerJsPrivate
*priv
= DEBUGGER_JS_PRIVATE(object
);
805 g_assert (strlen (name
) >= 1);
808 struct Task
*task
= g_new (struct Task
, 1);
809 task
->user_data
= user_data
;
810 task
->callback
= (IAnjutaDebuggerCallback
)callback
;
811 task
->line_required
= 1;
812 task
->name
= g_strdup (name
);
813 task
->task_type
= VARIABLE_CREATE
;
815 debugger_server_send_line (priv
->server
, "eval");
816 debugger_server_send_line (priv
->server
, name
);
818 priv
->task_queue
= g_list_append (priv
->task_queue
, task
);