Skip to content

Commit b02ef67

Browse files
authored
Add eval support for JerryScript debugger (#1588)
The server can accept a string, execute it with eval, and returns with the result converted to string. In case of exception it returns with the exception message. JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg [email protected]
1 parent 6739463 commit b02ef67

File tree

13 files changed

+566
-58
lines changed

13 files changed

+566
-58
lines changed

jerry-core/debugger/jerry-debugger-ws.c

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313
* limitations under the License.
1414
*/
1515

16-
#include "jerry-api.h"
17-
1816
#ifdef JERRY_DEBUGGER
1917

2018
#include <arpa/inet.h>
@@ -383,7 +381,7 @@ jerry_debugger_accept_connection ()
383381
return false;
384382
}
385383

386-
if (!jerry_debugger_send_configuration (JERRY_DEBUGGER_MAX_BUFFER_SIZE - sizeof (jerry_debugger_receive_header_t)))
384+
if (!jerry_debugger_send_configuration (JERRY_DEBUGGER_MAX_RECEIVE_SIZE))
387385
{
388386
return false;
389387
}
@@ -432,6 +430,9 @@ jerry_debugger_send (size_t data_size) /**< data size */
432430
return jerry_debugger_send_tcp (JERRY_CONTEXT (debugger_send_buffer), data_size);
433431
} /* jerry_debugger_send */
434432

433+
JERRY_STATIC_ASSERT (JERRY_DEBUGGER_MAX_RECEIVE_SIZE < 126,
434+
maximum_debug_message_receive_size_must_be_smaller_than_126);
435+
435436
/**
436437
* Receive message from the client.
437438
*
@@ -451,6 +452,8 @@ jerry_debugger_receive (void)
451452

452453
uint8_t *recv_buffer_p = JERRY_CONTEXT (debugger_receive_buffer);
453454
bool resume_exec = false;
455+
uint8_t expected_message_type = 0;
456+
void *message_data = NULL;
454457

455458
while (true)
456459
{
@@ -468,22 +471,28 @@ jerry_debugger_receive (void)
468471
return true;
469472
}
470473

474+
if (expected_message_type != 0)
475+
{
476+
continue;
477+
}
478+
471479
return resume_exec;
472480
}
473481

474482
JERRY_CONTEXT (debugger_receive_buffer_offset) += (uint32_t) byte_recv;
475483

476484
if (JERRY_CONTEXT (debugger_receive_buffer_offset) < sizeof (jerry_debugger_receive_header_t))
477485
{
486+
if (expected_message_type != 0)
487+
{
488+
continue;
489+
}
490+
478491
return resume_exec;
479492
}
480493

481-
const size_t max_packet_size = JERRY_DEBUGGER_MAX_BUFFER_SIZE - sizeof (jerry_debugger_receive_header_t);
482-
483-
JERRY_ASSERT (max_packet_size < 126);
484-
485494
if ((recv_buffer_p[0] & ~JERRY_DEBUGGER_WEBSOCKET_OPCODE_MASK) != JERRY_DEBUGGER_WEBSOCKET_FIN_BIT
486-
|| (recv_buffer_p[1] & JERRY_DEBUGGER_WEBSOCKET_LENGTH_MASK) >= max_packet_size
495+
|| (recv_buffer_p[1] & JERRY_DEBUGGER_WEBSOCKET_LENGTH_MASK) > JERRY_DEBUGGER_MAX_RECEIVE_SIZE
487496
|| !(recv_buffer_p[1] & JERRY_DEBUGGER_WEBSOCKET_MASK_BIT))
488497
{
489498
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Unsupported Websocket message.\n");
@@ -503,6 +512,11 @@ jerry_debugger_receive (void)
503512

504513
if (JERRY_CONTEXT (debugger_receive_buffer_offset) < message_total_size)
505514
{
515+
if (expected_message_type != 0)
516+
{
517+
continue;
518+
}
519+
506520
return resume_exec;
507521
}
508522

@@ -526,9 +540,13 @@ jerry_debugger_receive (void)
526540
}
527541
}
528542

543+
/* The jerry_debugger_process_message function is inlined
544+
* so passing these arguments is essentially free. */
529545
if (!jerry_debugger_process_message (recv_buffer_p + sizeof (jerry_debugger_receive_header_t),
530546
message_size,
531-
&resume_exec))
547+
&resume_exec,
548+
&expected_message_type,
549+
&message_data))
532550
{
533551
return true;
534552
}

jerry-core/debugger/jerry-debugger-ws.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,16 @@
2727
*/
2828
#define JERRY_DEBUGGER_MAX_BUFFER_SIZE 128
2929

30+
/**
31+
* Maximum number of bytes can be received in a single message.
32+
*/
33+
#define JERRY_DEBUGGER_MAX_SEND_SIZE (JERRY_DEBUGGER_MAX_BUFFER_SIZE - 1)
34+
35+
/**
36+
* Maximum number of bytes can be received in a single message.
37+
*/
38+
#define JERRY_DEBUGGER_MAX_RECEIVE_SIZE (JERRY_DEBUGGER_MAX_BUFFER_SIZE - 6)
39+
3040
/**
3141
* Last fragment of a Websocket package.
3242
*/

jerry-core/debugger/jerry-debugger.c

Lines changed: 188 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@
1313
* limitations under the License.
1414
*/
1515

16-
#include "jerry-api.h"
17-
1816
#ifdef JERRY_DEBUGGER
1917

2018
#include "byte-code.h"
19+
#include "ecma-conversion.h"
20+
#include "ecma-eval.h"
21+
#include "ecma-objects.h"
2122
#include "jcontext.h"
2223
#include "jerry-debugger.h"
2324
#include "jerry-port.h"
@@ -87,7 +88,7 @@ jerry_debugger_send_backtrace (uint8_t *recv_buffer_p) /**< pointer the the rece
8788

8889
while (frame_ctx_p != NULL && max_depth > 0)
8990
{
90-
if (current_frame >= JERRY_DEBUGGER_MAX_SIZE (jerry_debugger_frame_t))
91+
if (current_frame >= JERRY_DEBUGGER_SEND_MAX (jerry_debugger_frame_t))
9192
{
9293
if (!jerry_debugger_send (sizeof (jerry_debugger_send_backtrace_t)))
9394
{
@@ -118,6 +119,79 @@ jerry_debugger_send_backtrace (uint8_t *recv_buffer_p) /**< pointer the the rece
118119
jerry_debugger_send (sizeof (jerry_debugger_send_type_t) + message_size);
119120
} /* jerry_debugger_send_backtrace */
120121

122+
/**
123+
* Send result of evaluated expression.
124+
*
125+
* @return true - if no error is occured
126+
* false - otherwise
127+
*/
128+
static bool
129+
jerry_debugger_send_eval (const lit_utf8_byte_t *eval_string_p, /**< evaluated string */
130+
size_t eval_string_size) /**< evaluated string size */
131+
{
132+
JERRY_ASSERT (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER);
133+
134+
JERRY_CONTEXT (jerry_init_flags) &= (uint32_t) ~JERRY_INIT_DEBUGGER;
135+
ecma_value_t result = ecma_op_eval_chars_buffer (eval_string_p, eval_string_size, true, false);
136+
JERRY_CONTEXT (jerry_init_flags) |= (uint32_t) JERRY_INIT_DEBUGGER;
137+
138+
if (!ECMA_IS_VALUE_ERROR (result))
139+
{
140+
ecma_value_t to_string_value = ecma_op_to_string (result);
141+
ecma_free_value (result);
142+
result = to_string_value;
143+
}
144+
145+
ecma_value_t message = result;
146+
uint8_t type = JERRY_DEBUGGER_EVAL_RESULT;
147+
148+
if (ECMA_IS_VALUE_ERROR (result))
149+
{
150+
type = JERRY_DEBUGGER_EVAL_ERROR;
151+
152+
if (ecma_is_value_object (result))
153+
{
154+
ecma_string_t *message_string_p = ecma_get_magic_string (LIT_MAGIC_STRING_MESSAGE);
155+
156+
message = ecma_op_object_find (ecma_get_object_from_value (result),
157+
message_string_p);
158+
159+
ecma_deref_ecma_string (message_string_p);
160+
161+
if (!ecma_is_value_string (message)
162+
|| ecma_string_is_empty (ecma_get_string_from_value (message)))
163+
{
164+
ecma_free_value (message);
165+
lit_magic_string_id_t id = ecma_object_get_class_name (ecma_get_object_from_value (result));
166+
ecma_free_value (result);
167+
168+
const lit_utf8_byte_t *string_p = lit_get_magic_string_utf8 (id);
169+
return jerry_debugger_send_string (JERRY_DEBUGGER_EVAL_ERROR,
170+
string_p,
171+
strlen ((const char *) string_p));
172+
}
173+
}
174+
else
175+
{
176+
/* Primitve type. */
177+
message = ecma_op_to_string (result);
178+
JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (message));
179+
}
180+
181+
ecma_free_value (result);
182+
}
183+
184+
ecma_string_t *string_p = ecma_get_string_from_value (message);
185+
186+
ECMA_STRING_TO_UTF8_STRING (string_p, buffer_p, buffer_size);
187+
bool success = jerry_debugger_send_string (type, buffer_p, buffer_size);
188+
ECMA_FINALIZE_UTF8_STRING (buffer_p, buffer_size);
189+
190+
ecma_free_value (message);
191+
192+
return success;
193+
} /* jerry_debugger_send_eval */
194+
121195
/**
122196
* Check received packet size.
123197
*/
@@ -138,9 +212,65 @@ jerry_debugger_send_backtrace (uint8_t *recv_buffer_p) /**< pointer the the rece
138212
inline bool __attr_always_inline___
139213
jerry_debugger_process_message (uint8_t *recv_buffer_p, /**< pointer the the received data */
140214
uint32_t message_size, /**< message size */
141-
bool *resume_exec_p) /**< pointer to the resume exec flag */
215+
bool *resume_exec_p, /**< pointer to the resume exec flag */
216+
uint8_t *expected_message_type_p, /**< expected message type */
217+
void **message_data_p) /**< custom message data */
142218
{
143219
/* Process the received message. */
220+
221+
if (*expected_message_type_p != 0)
222+
{
223+
JERRY_ASSERT (*expected_message_type_p == JERRY_DEBUGGER_EVAL_PART);
224+
225+
jerry_debugger_eval_data_t *eval_data_p = (jerry_debugger_eval_data_t *) *message_data_p;
226+
227+
if (recv_buffer_p[0] != JERRY_DEBUGGER_EVAL_PART)
228+
{
229+
jmem_heap_free_block (eval_data_p, eval_data_p->eval_size + sizeof (jerry_debugger_eval_data_t));
230+
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Unexpected message\n");
231+
jerry_debugger_close_connection ();
232+
return false;
233+
}
234+
235+
JERRY_DEBUGGER_RECEIVE_BUFFER_AS (jerry_debugger_receive_eval_part_t, eval_part_p);
236+
237+
if (message_size < sizeof (jerry_debugger_receive_eval_part_t) + 1)
238+
{
239+
jmem_heap_free_block (eval_data_p, eval_data_p->eval_size + sizeof (jerry_debugger_eval_data_t));
240+
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Invalid message size\n");
241+
jerry_debugger_close_connection ();
242+
return false;
243+
}
244+
245+
uint32_t expected_data = eval_data_p->eval_size - eval_data_p->eval_offset;
246+
247+
message_size -= (uint32_t) sizeof (jerry_debugger_receive_eval_part_t);
248+
249+
if (message_size > expected_data)
250+
{
251+
jmem_heap_free_block (eval_data_p, eval_data_p->eval_size + sizeof (jerry_debugger_eval_data_t));
252+
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Invalid message size\n");
253+
jerry_debugger_close_connection ();
254+
return false;
255+
}
256+
257+
lit_utf8_byte_t *eval_string_p = (lit_utf8_byte_t *) (eval_data_p + 1);
258+
memcpy (eval_string_p + eval_data_p->eval_offset,
259+
(lit_utf8_byte_t *) (eval_part_p + 1),
260+
message_size);
261+
262+
if (message_size < expected_data)
263+
{
264+
eval_data_p->eval_offset += message_size;
265+
return true;
266+
}
267+
268+
bool result = jerry_debugger_send_eval (eval_string_p, eval_data_p->eval_size);
269+
jmem_heap_free_block (eval_data_p, eval_data_p->eval_size + sizeof (jerry_debugger_eval_data_t));
270+
*expected_message_type_p = 0;
271+
return result;
272+
}
273+
144274
switch (recv_buffer_p[0])
145275
{
146276
case JERRY_DEBUGGER_FREE_BYTE_CODE_CP:
@@ -253,6 +383,50 @@ jerry_debugger_process_message (uint8_t *recv_buffer_p, /**< pointer the the rec
253383
return true;
254384
}
255385

386+
case JERRY_DEBUGGER_EVAL:
387+
{
388+
if (message_size < sizeof (jerry_debugger_receive_eval_first_t) + 1)
389+
{
390+
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Invalid message size\n");
391+
jerry_debugger_close_connection ();
392+
return false;
393+
}
394+
395+
JERRY_DEBUGGER_RECEIVE_BUFFER_AS (jerry_debugger_receive_eval_first_t, eval_first_p);
396+
397+
uint32_t eval_size;
398+
memcpy (&eval_size, eval_first_p->eval_size, sizeof (uint32_t));
399+
400+
if (eval_size <= JERRY_DEBUGGER_MAX_RECEIVE_SIZE - sizeof (jerry_debugger_receive_eval_first_t))
401+
{
402+
if (eval_size != message_size - sizeof (jerry_debugger_receive_eval_first_t))
403+
{
404+
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Invalid message size\n");
405+
jerry_debugger_close_connection ();
406+
return false;
407+
}
408+
409+
return jerry_debugger_send_eval ((lit_utf8_byte_t *) (eval_first_p + 1), eval_size);
410+
}
411+
412+
jerry_debugger_eval_data_t *eval_data_p;
413+
size_t eval_data_size = sizeof (jerry_debugger_eval_data_t) + eval_size;
414+
415+
eval_data_p = (jerry_debugger_eval_data_t *) jmem_heap_alloc_block (eval_data_size);
416+
417+
eval_data_p->eval_size = eval_size;
418+
eval_data_p->eval_offset = (uint32_t) (message_size - sizeof (jerry_debugger_receive_eval_first_t));
419+
420+
lit_utf8_byte_t *eval_string_p = (lit_utf8_byte_t *) (eval_data_p + 1);
421+
memcpy (eval_string_p,
422+
(lit_utf8_byte_t *) (eval_first_p + 1),
423+
message_size - sizeof (jerry_debugger_receive_eval_first_t));
424+
425+
*message_data_p = eval_data_p;
426+
*expected_message_type_p = JERRY_DEBUGGER_EVAL_PART;
427+
return true;
428+
}
429+
256430
default:
257431
{
258432
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Unexpected message.");
@@ -355,7 +529,7 @@ jerry_debugger_send_data (jerry_debugger_header_type_t type, /**< message type *
355529
const void *data, /**< raw data */
356530
size_t size) /**< size of data */
357531
{
358-
JERRY_ASSERT (size < JERRY_DEBUGGER_MAX_SIZE (uint8_t));
532+
JERRY_ASSERT (size <= JERRY_DEBUGGER_SEND_MAX (uint8_t));
359533

360534
JERRY_DEBUGGER_SEND_BUFFER_AS (jerry_debugger_send_type_t, message_type_p);
361535

@@ -369,15 +543,18 @@ jerry_debugger_send_data (jerry_debugger_header_type_t type, /**< message type *
369543

370544
/**
371545
* Send string to the debugger client.
546+
*
547+
* @return true - if the data sent successfully to the debugger client,
548+
* false - otherwise
372549
*/
373-
void
550+
bool
374551
jerry_debugger_send_string (uint8_t message_type, /**< message type */
375-
const jerry_char_t *string_p, /**< string data */
552+
const uint8_t *string_p, /**< string data */
376553
size_t string_length) /**< length of string */
377554
{
378555
JERRY_ASSERT (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER);
379556

380-
const size_t max_fragment_len = JERRY_DEBUGGER_MAX_SIZE (char);
557+
const size_t max_fragment_len = JERRY_DEBUGGER_SEND_MAX (uint8_t);
381558

382559
JERRY_DEBUGGER_SEND_BUFFER_AS (jerry_debugger_send_string_t, message_string_p);
383560

@@ -391,7 +568,7 @@ jerry_debugger_send_string (uint8_t message_type, /**< message type */
391568

392569
if (!jerry_debugger_send (sizeof (jerry_debugger_send_string_t)))
393570
{
394-
return;
571+
return false;
395572
}
396573

397574
string_length -= max_fragment_len;
@@ -403,14 +580,14 @@ jerry_debugger_send_string (uint8_t message_type, /**< message type */
403580

404581
memcpy (message_string_p->string, string_p, string_length);
405582

406-
jerry_debugger_send (sizeof (jerry_debugger_send_type_t) + string_length);
583+
return jerry_debugger_send (sizeof (jerry_debugger_send_type_t) + string_length);
407584
} /* jerry_debugger_send_string */
408585

409586
/**
410587
* Send the function name to the debugger client.
411588
*/
412589
void
413-
jerry_debugger_send_function_name (const jerry_char_t *function_name_p, /**< function name */
590+
jerry_debugger_send_function_name (const uint8_t *function_name_p, /**< function name */
414591
size_t function_name_length) /**< length of function name */
415592
{
416593
JERRY_ASSERT (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER);

0 commit comments

Comments
 (0)