1/* Copyright JS Foundation and other contributors, http://js.foundation
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "debugger.h"
17#include "jcontext.h"
18#include "jerryscript.h"
19
20#if ENABLED (JERRY_DEBUGGER)
21
22/**
23 * Minimum number of bytes transmitted or received.
24 */
25#define JERRY_DEBUGGER_TRANSPORT_MIN_BUFFER_SIZE 64
26
27/**
28 * Sleep time in milliseconds between each jerry_debugger_receive call
29 */
30#define JERRY_DEBUGGER_TRANSPORT_TIMEOUT 10
31
32/**
33 * Add a new transport layer.
34 */
35void
36jerry_debugger_transport_add (jerry_debugger_transport_header_t *header_p, /**< transport implementation */
37                              size_t send_message_header_size, /**< header bytes reserved for outgoing messages */
38                              size_t max_send_message_size, /**< maximum number of bytes transmitted in a message */
39                              size_t receive_message_header_size, /**< header bytes reserved for incoming messages */
40                              size_t max_receive_message_size) /**< maximum number of bytes received in a message */
41{
42  JERRY_ASSERT (max_send_message_size > JERRY_DEBUGGER_TRANSPORT_MIN_BUFFER_SIZE
43                && max_receive_message_size > JERRY_DEBUGGER_TRANSPORT_MIN_BUFFER_SIZE);
44
45  header_p->next_p = JERRY_CONTEXT (debugger_transport_header_p);
46  JERRY_CONTEXT (debugger_transport_header_p) = header_p;
47
48  uint8_t *payload_p;
49  size_t max_send_size;
50  size_t max_receive_size;
51
52  if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)
53  {
54    payload_p = JERRY_CONTEXT (debugger_send_buffer_payload_p);
55    max_send_size = JERRY_CONTEXT (debugger_max_send_size);
56    max_receive_size = JERRY_CONTEXT (debugger_max_receive_size);
57  }
58  else
59  {
60    JERRY_DEBUGGER_SET_FLAGS (JERRY_DEBUGGER_CONNECTED);
61    payload_p = JERRY_CONTEXT (debugger_send_buffer);
62    max_send_size = JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE;
63    max_receive_size = JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE;
64  }
65
66  JERRY_ASSERT (max_send_size > JERRY_DEBUGGER_TRANSPORT_MIN_BUFFER_SIZE + send_message_header_size);
67  JERRY_ASSERT (max_receive_size > JERRY_DEBUGGER_TRANSPORT_MIN_BUFFER_SIZE + receive_message_header_size);
68
69  JERRY_CONTEXT (debugger_send_buffer_payload_p) = payload_p + send_message_header_size;
70
71  max_send_size = max_send_size - send_message_header_size;
72  max_receive_size = max_receive_size - receive_message_header_size;
73
74  if (max_send_size > max_send_message_size)
75  {
76    max_send_size = max_send_message_size;
77  }
78
79  if (max_receive_size > max_receive_message_size)
80  {
81    max_receive_size = max_receive_message_size;
82  }
83
84  JERRY_CONTEXT (debugger_max_send_size) = (uint8_t) max_send_size;
85  JERRY_CONTEXT (debugger_max_receive_size) = (uint8_t) max_receive_size;
86} /* jerry_debugger_transport_add */
87
88/**
89 * Starts the communication to the debugger client.
90 * Must be called after the connection is successfully established.
91 */
92void
93jerry_debugger_transport_start (void)
94{
95#ifdef ACE_DEBUGGER_CUSTOM
96  JERRY_DEBUGGER_SET_FLAGS (JERRY_DEBUGGER_TRANSPORT_STARTED);
97#endif
98  JERRY_ASSERT (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED);
99
100  if (jerry_debugger_send_configuration (JERRY_CONTEXT (debugger_max_receive_size)))
101  {
102    JERRY_DEBUGGER_SET_FLAGS (JERRY_DEBUGGER_VM_STOP);
103    JERRY_CONTEXT (debugger_stop_context) = NULL;
104  }
105} /* jerry_debugger_transport_start */
106
107/**
108 * Returns true if a debugger client is connected.
109 *
110 * @return true - a debugger client is connected,
111 *         false - otherwise
112 */
113bool
114jerry_debugger_transport_is_connected (void)
115{
116  return (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) != 0;
117} /* jerry_debugger_transport_is_connected */
118
119/**
120 * Notifies the debugger server that the connection is closed.
121 */
122void
123jerry_debugger_transport_close (void)
124{
125  if (!(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED))
126  {
127    return;
128  }
129
130  JERRY_CONTEXT (debugger_flags) = JERRY_DEBUGGER_VM_IGNORE;
131
132  jerry_debugger_transport_header_t *current_p = JERRY_CONTEXT (debugger_transport_header_p);
133
134  JERRY_ASSERT (current_p != NULL);
135
136  do
137  {
138    jerry_debugger_transport_header_t *next_p = current_p->next_p;
139
140    current_p->close (current_p);
141
142    current_p = next_p;
143  }
144  while (current_p != NULL);
145
146  jerry_port_log (JERRY_LOG_LEVEL_DEBUG, "Debugger client connection closed.\n");
147
148  jerry_debugger_free_unreferenced_byte_code ();
149} /* jerry_debugger_transport_close */
150
151/**
152 * Send data over the current connection
153 *
154 * @return true - data sent successfully,
155 *         false - connection closed
156 */
157bool
158jerry_debugger_transport_send (const uint8_t *message_p, /**< message to be sent */
159                               size_t message_length) /**< message length in bytes */
160{
161  JERRY_ASSERT (jerry_debugger_transport_is_connected ());
162  JERRY_ASSERT (message_length > 0);
163
164  jerry_debugger_transport_header_t *header_p = JERRY_CONTEXT (debugger_transport_header_p);
165  uint8_t *payload_p = JERRY_CONTEXT (debugger_send_buffer_payload_p);
166  size_t max_send_size = JERRY_CONTEXT (debugger_max_send_size);
167
168  do
169  {
170    size_t fragment_length = (message_length <= max_send_size ? message_length
171                                                              : max_send_size);
172
173    memcpy (payload_p, message_p, fragment_length);
174
175    if (!header_p->send (header_p, payload_p, fragment_length))
176    {
177      return false;
178    }
179
180    message_p += fragment_length;
181    message_length -= fragment_length;
182  }
183  while (message_length > 0);
184
185  return true;
186} /* jerry_debugger_transport_send */
187
188/**
189 * Receive data from the current connection
190 *
191 * Note:
192 *   A message is received if message_start_p is not NULL
193 *
194 * @return true - function successfully completed,
195 *         false - connection closed
196 */
197bool
198jerry_debugger_transport_receive (jerry_debugger_transport_receive_context_t *context_p) /**< [out] receive
199                                                                                          *   context */
200{
201  JERRY_ASSERT (jerry_debugger_transport_is_connected ());
202
203  context_p->buffer_p = JERRY_CONTEXT (debugger_receive_buffer);
204  context_p->received_length = JERRY_CONTEXT (debugger_received_length);
205  context_p->message_p = NULL;
206  context_p->message_length = 0;
207  context_p->message_total_length = 0;
208
209  jerry_debugger_transport_header_t *header_p = JERRY_CONTEXT (debugger_transport_header_p);
210
211  return header_p->receive (header_p, context_p);
212} /* jerry_debugger_transport_receive */
213
214/**
215 * Clear the message buffer after the message is processed
216 */
217void
218jerry_debugger_transport_receive_completed (jerry_debugger_transport_receive_context_t *context_p) /**< receive
219                                                                                                    *   context */
220{
221  JERRY_ASSERT (context_p->message_p != NULL);
222  JERRY_ASSERT (context_p->buffer_p == JERRY_CONTEXT (debugger_receive_buffer));
223
224  size_t message_total_length = context_p->message_total_length;
225  size_t received_length = context_p->received_length;
226
227  JERRY_ASSERT (message_total_length <= received_length);
228
229  if (message_total_length == 0 || message_total_length == received_length)
230  {
231    /* All received data is processed. */
232    JERRY_CONTEXT (debugger_received_length) = 0;
233    return;
234  }
235
236  uint8_t *buffer_p = context_p->buffer_p;
237  received_length -= message_total_length;
238
239  memmove (buffer_p, buffer_p + message_total_length, received_length);
240
241  JERRY_CONTEXT (debugger_received_length) = (uint16_t) received_length;
242} /* jerry_debugger_transport_receive_completed */
243
244/**
245 * Suspend execution for a predefined time (JERRY_DEBUGGER_TRANSPORT_TIMEOUT ms).
246 */
247void
248jerry_debugger_transport_sleep (void)
249{
250  jerry_port_sleep (JERRY_DEBUGGER_TRANSPORT_TIMEOUT);
251} /* jerry_debugger_transport_sleep */
252
253#endif /* ENABLED (JERRY_DEBUGGER) */
254