diff --git a/docs/07.DEBUGGER.md b/docs/07.DEBUGGER.md index 99ab673dc..3266388a3 100644 --- a/docs/07.DEBUGGER.md +++ b/docs/07.DEBUGGER.md @@ -25,18 +25,25 @@ can be used for transmitting debugger messages. ## Debugging JavaScript applications The debugger client must be connected to the server before the -JavaScript application runs. On-the-fly attachment is not supported -because the debugging information (e.g. line index of each possible -breakpoint location) is not preserved by JerryScript. The client is -expected to be run on a system with much more resources and it should -be capable of storing this information. JerryScript frees all debug -information after it is transmitted to the client to save memory. +JavaScript application runs. On-the-fly attachment is supported +for one file, right after of engine initialization +(this feature available with the python client). The debugging +information (e.g. line index of each possible -breakpoint location) +is not preserved by JerryScript. The client is expected to be run +on a system with much more resources and it should be capable of +storing this information. JerryScript frees all debug information +after it is transmitted to the client to save memory. The following argument makes JerryScript wait for a client connection: `--start-debug-server` +The following argument makes JerryScript wait for a client +source code: + +`--debugger-wait-source` + It is also recommended to increase the log level to see the *Waiting for client connection* message: @@ -194,3 +201,33 @@ jerry_debugger_stop_at_breakpoint (bool enable_stop_at_breakpoint) jerry_debugger_stop_at_breakpoint (false); } ``` + +### jerry_debugger_wait_and_run_client_source + +**Summary** + +Stops the engine and puts that into a waiting loop. If the client send +a source code and the JerryScript receive that, then the function will +run the source with the initialized options. + +**Prototype** + +```c +jerry_debugger_wait_and_run_type_t +jerry_debugger_wait_and_run_client_source (jerry_value_t *return_value) +``` + +**Example** + +```c + jerry_init (JERRY_INIT_DEBUGGER); + + jerry_value_t wait_and_run_value; + + if (jerry_debugger_wait_and_run_client_source (&wait_and_run_value) == JERRY_DEBUGGER_SOURCE_RECEIVE_FAILED) + { + // Handle the fail (e.g. create an error). + } + + jerry_release_value (wait_and_run_value); +``` diff --git a/jerry-core/api/jerry-debugger.c b/jerry-core/api/jerry-debugger.c index 0338c3c06..51423cf3b 100644 --- a/jerry-core/api/jerry-debugger.c +++ b/jerry-core/api/jerry-debugger.c @@ -116,3 +116,82 @@ jerry_debugger_cleanup (void) } #endif /* JERRY_DEBUGGER */ } /* jerry_debugger_cleanup */ + +/** + * Sets whether the engine should wait and run a source. + * + * @return enum JERRY_DEBUGGER_SOURCE_RECEIVE_FAILED - if the source is not received + * JERRY_DEBUGGER_SOURCE_RECEIVED - if the source received + */ +jerry_debugger_wait_and_run_type_t +jerry_debugger_wait_and_run_client_source (jerry_value_t *return_value) /**< [out] parse and run return value */ +{ + *return_value = jerry_create_undefined (); + +#ifdef JERRY_DEBUGGER + if ((JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) + && !(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_BREAKPOINT_MODE)) + { + JERRY_CONTEXT (debugger_flags) = (uint8_t) (JERRY_CONTEXT (debugger_flags) | JERRY_DEBUGGER_CLIENT_SOURCE_MODE); + jerry_debugger_uint8_data_t *client_source_data_p = NULL; + + while (true) + { + if (jerry_debugger_receive (&client_source_data_p)) + { + if (!(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)) + { + break; + } + + /* The source arrived. */ + if (!(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CLIENT_SOURCE_MODE)) + { + JERRY_ASSERT (client_source_data_p != NULL); + + jerry_char_t *string_p = (jerry_char_t *) (client_source_data_p + 1); + size_t name_size = strlen ((const char *) string_p); + + *return_value = jerry_parse_named_resource (string_p, + name_size, + (string_p + name_size + 1), + (client_source_data_p->uint8_size - name_size - 1), + false); + + if (!jerry_value_has_error_flag (*return_value)) + { + jerry_value_t func_val = *return_value; + *return_value = jerry_run (func_val); + jerry_release_value (func_val); + + return JERRY_DEBUGGER_SOURCE_RECEIVED; + } + else + { + jmem_heap_free_block (client_source_data_p, + client_source_data_p->uint8_size + sizeof (jerry_debugger_uint8_data_t)); + + return JERRY_DEBUGGER_SOURCE_RECEIVE_FAILED; + } + } + } + + jerry_debugger_sleep (); + } + + JERRY_ASSERT (!(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CLIENT_SOURCE_MODE) + || !(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)); + + if (client_source_data_p != NULL) + { + /* The data may partly arrived. */ + jmem_heap_free_block (client_source_data_p, + client_source_data_p->uint8_size + sizeof (jerry_debugger_uint8_data_t)); + } + } + + return JERRY_DEBUGGER_SOURCE_RECEIVE_FAILED; +#else + return JERRY_DEBUGGER_SOURCE_RECEIVE_FAILED; +#endif /* JERRY_DEBUGGER */ +} /* jerry_debugger_wait_and_run_client_source */ diff --git a/jerry-core/debugger/debugger-ws.c b/jerry-core/debugger/debugger-ws.c index 3b2255f13..411724924 100644 --- a/jerry-core/debugger/debugger-ws.c +++ b/jerry-core/debugger/debugger-ws.c @@ -44,6 +44,13 @@ */ #define JERRY_DEBUGGER_WEBSOCKET_MASK_SIZE 4 +/** + * + */ +#define JERRY_DEBUGGER_RECEIVE_DATA_MODE \ + (JERRY_DEBUGGER_BREAKPOINT_MODE | JERRY_DEBUGGER_CLIENT_SOURCE_MODE) + + /** * Header for incoming packets. */ @@ -433,16 +440,18 @@ JERRY_STATIC_ASSERT (JERRY_DEBUGGER_MAX_RECEIVE_SIZE < 126, * false - otherwise */ bool -jerry_debugger_receive (void) +jerry_debugger_receive (jerry_debugger_uint8_data_t **message_data_p) /**< [out] data received from client */ { JERRY_ASSERT (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED); + JERRY_ASSERT (message_data_p != NULL ? (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_RECEIVE_DATA_MODE) + : !(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_RECEIVE_DATA_MODE)); + JERRY_CONTEXT (debugger_message_delay) = JERRY_DEBUGGER_MESSAGE_FREQUENCY; uint8_t *recv_buffer_p = JERRY_CONTEXT (debugger_receive_buffer); bool resume_exec = false; uint8_t expected_message_type = 0; - void *message_data = NULL; while (true) { @@ -532,7 +541,7 @@ jerry_debugger_receive (void) message_size, &resume_exec, &expected_message_type, - &message_data)) + message_data_p)) { return true; } diff --git a/jerry-core/debugger/debugger-ws.h b/jerry-core/debugger/debugger-ws.h index 20b55c6c7..63e5d3013 100644 --- a/jerry-core/debugger/debugger-ws.h +++ b/jerry-core/debugger/debugger-ws.h @@ -63,6 +63,23 @@ typedef struct uint8_t size; /**< size of the message */ } jerry_debugger_send_header_t; +/** + * Incoming message: next message of string data. + */ +typedef struct +{ + uint8_t type; /**< type of the message */ +} jerry_debugger_receive_uint8_data_part_t; + +/** + * Byte data for evaluating expressions and receiving client source. + */ +typedef struct +{ + uint32_t uint8_size; /**< total size of the client source */ + uint32_t uint8_offset; /**< current offset in the client source */ +} jerry_debugger_uint8_data_t; + /** * Initialize the header of an outgoing message. */ @@ -85,7 +102,7 @@ bool jerry_debugger_accept_connection (void); void jerry_debugger_close_connection (void); bool jerry_debugger_send (size_t data_size); -bool jerry_debugger_receive (void); +bool jerry_debugger_receive (jerry_debugger_uint8_data_t **message_data_p); void jerry_debugger_compute_sha1 (const uint8_t *input1, size_t input1_len, const uint8_t *input2, size_t input2_len, diff --git a/jerry-core/debugger/debugger.c b/jerry-core/debugger/debugger.c index 64436ea39..accab4f30 100644 --- a/jerry-core/debugger/debugger.c +++ b/jerry-core/debugger/debugger.c @@ -45,11 +45,6 @@ #define JERRY_DEBUGGER_RECEIVE_BUFFER_AS(type, name_p) \ type *name_p = ((type *) recv_buffer_p) -/** - * Sleep time in milliseconds between each jerry_debugger_receive call - */ -#define JERRY_DEBUGGER_TIMEOUT 100 - /** * Free all unreferenced byte code structures which * were not acknowledged by the debugger client. @@ -218,19 +213,17 @@ jerry_debugger_send_eval (const lit_utf8_byte_t *eval_string_p, /**< evaluated s * Suspend execution for a given time. * Note: If the platform does not have nanosleep or usleep, this function does not sleep at all. */ -static void -jerry_debugger_sleep (unsigned milliseconds) /**< suspending time */ +void +jerry_debugger_sleep (void) { #ifdef HAVE_TIME_H nanosleep (&(const struct timespec) { - milliseconds / 1000, (milliseconds % 1000) * 1000000L /* Seconds, nanoseconds */ + JERRY_DEBUGGER_TIMEOUT / 1000, (JERRY_DEBUGGER_TIMEOUT % 1000) * 1000000L /* Seconds, nanoseconds */ } , NULL); #elif defined (HAVE_UNISTD_H) - usleep ((useconds_t) milliseconds * 1000); -#else /* If neither of the libs found */ - JERRY_UNUSED (milliseconds); + usleep ((useconds_t) JERRY_DEBUGGER_TIMEOUT * 1000); #endif /* HAVE_TIME_H */ } /* jerry_debugger_sleep */ @@ -255,8 +248,8 @@ inline bool __attr_always_inline___ jerry_debugger_process_message (uint8_t *recv_buffer_p, /**< pointer the the received data */ uint32_t message_size, /**< message size */ bool *resume_exec_p, /**< pointer to the resume exec flag */ - uint8_t *expected_message_type_p, /**< expected message type */ - void **message_data_p) /**< custom message data */ + uint8_t *expected_message_type_p, /**< message type */ + jerry_debugger_uint8_data_t **message_data_p) /**< custom message data */ { /* Process the received message. */ @@ -270,53 +263,65 @@ jerry_debugger_process_message (uint8_t *recv_buffer_p, /**< pointer the the rec if (*expected_message_type_p != 0) { - JERRY_ASSERT (*expected_message_type_p == JERRY_DEBUGGER_EVAL_PART); + JERRY_ASSERT (*expected_message_type_p == JERRY_DEBUGGER_EVAL_PART + || *expected_message_type_p == JERRY_DEBUGGER_CLIENT_SOURCE_PART); - jerry_debugger_eval_data_t *eval_data_p = (jerry_debugger_eval_data_t *) *message_data_p; + jerry_debugger_uint8_data_t *uint8_data_p = (jerry_debugger_uint8_data_t *) *message_data_p; - if (recv_buffer_p[0] != JERRY_DEBUGGER_EVAL_PART) + if (recv_buffer_p[0] != *expected_message_type_p) { - jmem_heap_free_block (eval_data_p, eval_data_p->eval_size + sizeof (jerry_debugger_eval_data_t)); + jmem_heap_free_block (uint8_data_p, uint8_data_p->uint8_size + sizeof (jerry_debugger_uint8_data_t)); jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Unexpected message\n"); jerry_debugger_close_connection (); return false; } - JERRY_DEBUGGER_RECEIVE_BUFFER_AS (jerry_debugger_receive_eval_part_t, eval_part_p); + JERRY_DEBUGGER_RECEIVE_BUFFER_AS (jerry_debugger_receive_uint8_data_part_t, uint8_data_part_p); - if (message_size < sizeof (jerry_debugger_receive_eval_part_t) + 1) + if (message_size < sizeof (jerry_debugger_receive_uint8_data_part_t) + 1) { - jmem_heap_free_block (eval_data_p, eval_data_p->eval_size + sizeof (jerry_debugger_eval_data_t)); + jmem_heap_free_block (uint8_data_p, uint8_data_p->uint8_size + sizeof (jerry_debugger_uint8_data_t)); jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Invalid message size\n"); jerry_debugger_close_connection (); return false; } - uint32_t expected_data = eval_data_p->eval_size - eval_data_p->eval_offset; + uint32_t expected_data = uint8_data_p->uint8_size - uint8_data_p->uint8_offset; - message_size -= (uint32_t) sizeof (jerry_debugger_receive_eval_part_t); + message_size -= (uint32_t) sizeof (jerry_debugger_receive_uint8_data_part_t); if (message_size > expected_data) { - jmem_heap_free_block (eval_data_p, eval_data_p->eval_size + sizeof (jerry_debugger_eval_data_t)); + jmem_heap_free_block (uint8_data_p, uint8_data_p->uint8_size + sizeof (jerry_debugger_uint8_data_t)); jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Invalid message size\n"); jerry_debugger_close_connection (); return false; } - lit_utf8_byte_t *eval_string_p = (lit_utf8_byte_t *) (eval_data_p + 1); - memcpy (eval_string_p + eval_data_p->eval_offset, - (lit_utf8_byte_t *) (eval_part_p + 1), + lit_utf8_byte_t *string_p = (lit_utf8_byte_t *) (uint8_data_p + 1); + memcpy (string_p + uint8_data_p->uint8_offset, + (lit_utf8_byte_t *) (uint8_data_part_p + 1), message_size); if (message_size < expected_data) { - eval_data_p->eval_offset += message_size; + uint8_data_p->uint8_offset += message_size; return true; } - bool result = jerry_debugger_send_eval (eval_string_p, eval_data_p->eval_size); - jmem_heap_free_block (eval_data_p, eval_data_p->eval_size + sizeof (jerry_debugger_eval_data_t)); + bool result = false; + if (*expected_message_type_p == JERRY_DEBUGGER_EVAL_PART) + { + result = jerry_debugger_send_eval (string_p, uint8_data_p->uint8_size); + } + else + { + result = true; + JERRY_CONTEXT (debugger_flags) = (uint8_t) (JERRY_CONTEXT (debugger_flags) + & ~JERRY_DEBUGGER_CLIENT_SOURCE_MODE); + *resume_exec_p = true; + } + *expected_message_type_p = 0; return result; } @@ -486,24 +491,82 @@ jerry_debugger_process_message (uint8_t *recv_buffer_p, /**< pointer the the rec return jerry_debugger_send_eval ((lit_utf8_byte_t *) (eval_first_p + 1), eval_size); } - jerry_debugger_eval_data_t *eval_data_p; - size_t eval_data_size = sizeof (jerry_debugger_eval_data_t) + eval_size; + jerry_debugger_uint8_data_t *eval_uint8_data_p; + size_t eval_data_size = sizeof (jerry_debugger_uint8_data_t) + eval_size; - eval_data_p = (jerry_debugger_eval_data_t *) jmem_heap_alloc_block (eval_data_size); + eval_uint8_data_p = (jerry_debugger_uint8_data_t *) jmem_heap_alloc_block (eval_data_size); - eval_data_p->eval_size = eval_size; - eval_data_p->eval_offset = (uint32_t) (message_size - sizeof (jerry_debugger_receive_eval_first_t)); + eval_uint8_data_p->uint8_size = eval_size; + eval_uint8_data_p->uint8_offset = (uint32_t) (message_size - sizeof (jerry_debugger_receive_eval_first_t)); - lit_utf8_byte_t *eval_string_p = (lit_utf8_byte_t *) (eval_data_p + 1); + lit_utf8_byte_t *eval_string_p = (lit_utf8_byte_t *) (eval_uint8_data_p + 1); memcpy (eval_string_p, (lit_utf8_byte_t *) (eval_first_p + 1), message_size - sizeof (jerry_debugger_receive_eval_first_t)); - *message_data_p = eval_data_p; + *message_data_p = eval_uint8_data_p; *expected_message_type_p = JERRY_DEBUGGER_EVAL_PART; return true; } + case JERRY_DEBUGGER_CLIENT_SOURCE: + { + if (message_size <= sizeof (jerry_debugger_receive_client_source_first_t)) + { + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Invalid message size\n"); + jerry_debugger_close_connection (); + return false; + } + + if (!(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CLIENT_SOURCE_MODE)) + { + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Not in client source mode\n"); + jerry_debugger_close_connection (); + return false; + } + + JERRY_DEBUGGER_RECEIVE_BUFFER_AS (jerry_debugger_receive_client_source_first_t, client_source_first_p); + + uint32_t client_source_size; + memcpy (&client_source_size, client_source_first_p->code_size, sizeof (uint32_t)); + + if (client_source_size <= JERRY_DEBUGGER_MAX_RECEIVE_SIZE - sizeof (jerry_debugger_receive_client_source_first_t) + && client_source_size != message_size - sizeof (jerry_debugger_receive_client_source_first_t)) + { + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Invalid message size\n"); + jerry_debugger_close_connection (); + return false; + } + + jerry_debugger_uint8_data_t *client_source_data_p; + size_t client_source_data_size = sizeof (jerry_debugger_uint8_data_t) + client_source_size; + + client_source_data_p = (jerry_debugger_uint8_data_t *) jmem_heap_alloc_block (client_source_data_size); + + client_source_data_p->uint8_size = client_source_size; + client_source_data_p->uint8_offset = (uint32_t) (message_size + - sizeof (jerry_debugger_receive_client_source_first_t)); + + lit_utf8_byte_t *client_source_string_p = (lit_utf8_byte_t *) (client_source_data_p + 1); + memcpy (client_source_string_p, + (lit_utf8_byte_t *) (client_source_first_p + 1), + message_size - sizeof (jerry_debugger_receive_client_source_first_t)); + + *message_data_p = client_source_data_p; + + if (client_source_data_p->uint8_size != client_source_data_p->uint8_offset) + { + *expected_message_type_p = JERRY_DEBUGGER_CLIENT_SOURCE_PART; + } + else + { + JERRY_CONTEXT (debugger_flags) = (uint8_t) (JERRY_CONTEXT (debugger_flags) + & ~JERRY_DEBUGGER_CLIENT_SOURCE_MODE); + *resume_exec_p = true; + } + return true; + } + default: { jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Unexpected message."); @@ -545,9 +608,17 @@ jerry_debugger_breakpoint_hit (uint8_t message_type) /**< message type */ JERRY_CONTEXT (debugger_flags) = (uint8_t) (JERRY_CONTEXT (debugger_flags) | JERRY_DEBUGGER_BREAKPOINT_MODE); - while (!jerry_debugger_receive ()) + jerry_debugger_uint8_data_t *uint8_data = NULL; + + while (!jerry_debugger_receive (&uint8_data)) { - jerry_debugger_sleep (JERRY_DEBUGGER_TIMEOUT); + jerry_debugger_sleep (); + } + + if (uint8_data != NULL) + { + jmem_heap_free_block (uint8_data, + uint8_data->uint8_size + sizeof (jerry_debugger_uint8_data_t)); } JERRY_CONTEXT (debugger_flags) = (uint8_t) (JERRY_CONTEXT (debugger_flags) & ~JERRY_DEBUGGER_BREAKPOINT_MODE); diff --git a/jerry-core/debugger/debugger.h b/jerry-core/debugger/debugger.h index 2072c8fb0..5244866f7 100644 --- a/jerry-core/debugger/debugger.h +++ b/jerry-core/debugger/debugger.h @@ -28,6 +28,11 @@ */ #define JERRY_DEBUGGER_MESSAGE_FREQUENCY 5 +/** + * Sleep time in milliseconds between each jerry_debugger_receive call + */ +#define JERRY_DEBUGGER_TIMEOUT 100 + /** * Limited resources available for the engine, so it is important to * check the maximum buffer size. It needs to be between 64 and 256 bytes. @@ -78,6 +83,7 @@ typedef enum JERRY_DEBUGGER_VM_STOP = 1u << 2, /**< stop at the next breakpoint regardless it is enabled */ JERRY_DEBUGGER_VM_IGNORE = 1u << 3, /**< ignore all breakpoints */ JERRY_DEBUGGER_VM_IGNORE_EXCEPTION = 1u << 4, /**< debugger stop at an exception */ + JERRY_DEBUGGER_CLIENT_SOURCE_MODE = 1u << 5, /**< debugger waiting for client code */ } jerry_debugger_flags_t; /** @@ -119,16 +125,18 @@ typedef enum JERRY_DEBUGGER_EXCEPTION_CONFIG = 3, /**< exception handler config */ JERRY_DEBUGGER_MEMSTATS = 4, /**< list memory statistics */ JERRY_DEBUGGER_STOP = 5, /**< stop execution */ + JERRY_DEBUGGER_CLIENT_SOURCE = 6, /**< first message of client source */ + JERRY_DEBUGGER_CLIENT_SOURCE_PART = 7, /**< next message of client source */ /* The following messages are only available in breakpoint * mode and they switch the engine to run mode. */ - JERRY_DEBUGGER_CONTINUE = 6, /**< continue execution */ - JERRY_DEBUGGER_STEP = 7, /**< next breakpoint, step into functions */ - JERRY_DEBUGGER_NEXT = 8, /**< next breakpoint in the same context */ + JERRY_DEBUGGER_CONTINUE = 8, /**< continue execution */ + JERRY_DEBUGGER_STEP = 9, /**< next breakpoint, step into functions */ + JERRY_DEBUGGER_NEXT = 10, /**< next breakpoint in the same context */ /* The following messages are only available in breakpoint * mode and this mode is kept after the message is processed. */ - JERRY_DEBUGGER_GET_BACKTRACE = 9, /**< get backtrace */ - JERRY_DEBUGGER_EVAL = 10, /**< first message of evaluating a string */ - JERRY_DEBUGGER_EVAL_PART = 11, /**< next message of evaluating a string */ + JERRY_DEBUGGER_GET_BACKTRACE = 11, /**< get backtrace */ + JERRY_DEBUGGER_EVAL = 12, /**< first message of evaluating a string */ + JERRY_DEBUGGER_EVAL_PART = 13, /**< next message of evaluating a string */ } jerry_debugger_header_type_t; /** @@ -288,27 +296,23 @@ typedef struct uint8_t eval_size[sizeof (uint32_t)]; /**< total size of the message */ } jerry_debugger_receive_eval_first_t; + /** - * Incoming message: next message of evaluating expression. + * Incoming message: first message of client source. */ typedef struct { uint8_t type; /**< type of the message */ -} jerry_debugger_receive_eval_part_t; - -/** - * Data for evaluating expressions - */ -typedef struct -{ - uint32_t eval_size; /**< total size of the eval string */ - uint32_t eval_offset; /**< current offset in the eval string */ -} jerry_debugger_eval_data_t; + uint8_t code_size[sizeof (uint32_t)]; /**< total size of the message */ +} jerry_debugger_receive_client_source_first_t; void jerry_debugger_free_unreferenced_byte_code (void); +void jerry_debugger_sleep (void); + bool jerry_debugger_process_message (uint8_t *recv_buffer_p, uint32_t message_size, - bool *resume_exec_p, uint8_t *expected_message_p, void **message_data_p); + bool *resume_exec_p, uint8_t *expected_message_p, + jerry_debugger_uint8_data_t **message_data_p); void jerry_debugger_breakpoint_hit (uint8_t message_type); void jerry_debugger_send_type (jerry_debugger_header_type_t type); diff --git a/jerry-core/ecma/base/ecma-gc.c b/jerry-core/ecma/base/ecma-gc.c index 3160dd36b..8e957a2bb 100644 --- a/jerry-core/ecma/base/ecma-gc.c +++ b/jerry-core/ecma/base/ecma-gc.c @@ -803,7 +803,7 @@ ecma_free_unused_memory (jmem_free_unused_memory_severity_t severity) /**< sever && JERRY_CONTEXT (debugger_byte_code_free_tail) != ECMA_NULL_POINTER) { /* Wait until all byte code is freed or the connection is aborted. */ - jerry_debugger_receive (); + jerry_debugger_receive (NULL); } #endif /* JERRY_DEBUGGER */ diff --git a/jerry-core/include/jerryscript-debugger.h b/jerry-core/include/jerryscript-debugger.h index d1b0a3765..fab8f538b 100644 --- a/jerry-core/include/jerryscript-debugger.h +++ b/jerry-core/include/jerryscript-debugger.h @@ -27,6 +27,15 @@ extern "C" * @{ */ +/** + * Types for the client source wait and run method. + */ +typedef enum +{ + JERRY_DEBUGGER_SOURCE_RECEIVE_FAILED = 0, /**< source is not received */ + JERRY_DEBUGGER_SOURCE_RECEIVED = 1, /**< the source has been received */ +} jerry_debugger_wait_and_run_type_t; + /** * Engine debugger functions. */ @@ -34,6 +43,7 @@ bool jerry_debugger_is_connected (void); void jerry_debugger_stop (void); void jerry_debugger_continue (void); void jerry_debugger_stop_at_breakpoint (bool enable_stop_at_breakpoint); +jerry_debugger_wait_and_run_type_t jerry_debugger_wait_and_run_client_source (jerry_value_t *return_value); void jerry_debugger_init (uint16_t port); void jerry_debugger_cleanup (void); diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index 8ac3bf7d4..893f6ae20 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -2405,7 +2405,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ JERRY_CONTEXT (debugger_message_delay) = JERRY_DEBUGGER_MESSAGE_FREQUENCY; - if (jerry_debugger_receive ()) + if (jerry_debugger_receive (NULL)) { continue; } diff --git a/jerry-debugger/jerry-client-ws.html b/jerry-debugger/jerry-client-ws.html index c59da1d3b..b2fb45c05 100644 --- a/jerry-debugger/jerry-client-ws.html +++ b/jerry-debugger/jerry-client-ws.html @@ -65,12 +65,14 @@ var JERRY_DEBUGGER_UPDATE_BREAKPOINT = 2; var JERRY_DEBUGGER_EXCEPTION_CONFIG = 3; var JERRY_DEBUGGER_MEMSTATS = 4; var JERRY_DEBUGGER_STOP = 5; -var JERRY_DEBUGGER_CONTINUE = 6; -var JERRY_DEBUGGER_STEP = 7; -var JERRY_DEBUGGER_NEXT = 8; -var JERRY_DEBUGGER_GET_BACKTRACE = 9; -var JERRY_DEBUGGER_EVAL = 10; -var JERRY_DEBUGGER_EVAL_PART = 11; +var JERRY_DEBUGGER_CLIENT_SOURCE = 6; +var JERRY_DEBUGGER_CLIENT_SOURCE_PART = 7; +var JERRY_DEBUGGER_CONTINUE = 8; +var JERRY_DEBUGGER_STEP = 9; +var JERRY_DEBUGGER_NEXT = 10; +var JERRY_DEBUGGER_GET_BACKTRACE = 11; +var JERRY_DEBUGGER_EVAL = 12; +var JERRY_DEBUGGER_EVAL_PART = 13; var textBox = document.getElementById("log"); var commandBox = document.getElementById("command"); diff --git a/jerry-debugger/jerry-client-ws.py b/jerry-debugger/jerry-client-ws.py index 45d1221e7..49182cd92 100755 --- a/jerry-debugger/jerry-client-ws.py +++ b/jerry-debugger/jerry-client-ws.py @@ -58,12 +58,14 @@ JERRY_DEBUGGER_UPDATE_BREAKPOINT = 2 JERRY_DEBUGGER_EXCEPTION_CONFIG = 3 JERRY_DEBUGGER_MEMSTATS = 4 JERRY_DEBUGGER_STOP = 5 -JERRY_DEBUGGER_CONTINUE = 6 -JERRY_DEBUGGER_STEP = 7 -JERRY_DEBUGGER_NEXT = 8 -JERRY_DEBUGGER_GET_BACKTRACE = 9 -JERRY_DEBUGGER_EVAL = 10 -JERRY_DEBUGGER_EVAL_PART = 11 +JERRY_DEBUGGER_CLIENT_SOURCE = 6 +JERRY_DEBUGGER_CLIENT_SOURCE_PART = 7 +JERRY_DEBUGGER_CONTINUE = 8 +JERRY_DEBUGGER_STEP = 9 +JERRY_DEBUGGER_NEXT = 10 +JERRY_DEBUGGER_GET_BACKTRACE = 11 +JERRY_DEBUGGER_EVAL = 12 +JERRY_DEBUGGER_EVAL_PART = 13 MAX_BUFFER_SIZE = 128 WEBSOCKET_BINARY_FRAME = 2 @@ -85,6 +87,8 @@ def arguments_parse(): help="set display range") parser.add_argument("--exception", action="store", default=None, type=int, choices=[0, 1], help="set exception config, usage 1: [Enable] or 0: [Disable]") + parser.add_argument("--client-source", action="store", default=None, type=str, + help="specify a javascript source file to execute") args = parser.parse_args() @@ -345,7 +349,7 @@ class DebuggerPrompt(Cmd): pprint(self.debugger.function_list) - def eval_string(self, args): + def send_string(self, args, message_type): size = len(args) if size == 0: return @@ -359,7 +363,7 @@ class DebuggerPrompt(Cmd): WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT, WEBSOCKET_FIN_BIT + max_fragment + message_header, 0, - JERRY_DEBUGGER_EVAL, + message_type, size) if size == max_fragment: @@ -370,6 +374,11 @@ class DebuggerPrompt(Cmd): self.debugger.send_message(message + args[0:max_fragment]) offset = max_fragment + if message_type == JERRY_DEBUGGER_EVAL: + message_type = JERRY_DEBUGGER_EVAL_PART + else: + message_type = JERRY_DEBUGGER_CLIENT_SOURCE_PART + # 1: length of type byte message_header = 1 @@ -381,7 +390,7 @@ class DebuggerPrompt(Cmd): WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT, WEBSOCKET_FIN_BIT + next_fragment + message_header, 0, - JERRY_DEBUGGER_EVAL_PART) + message_type) prev_offset = offset offset += next_fragment @@ -391,7 +400,7 @@ class DebuggerPrompt(Cmd): def do_eval(self, args): """ Evaluate JavaScript source code """ - self.eval_string(args) + self.send_string(args, JERRY_DEBUGGER_EVAL) do_e = do_eval @@ -420,6 +429,16 @@ class DebuggerPrompt(Cmd): do_ms = do_memstats + def send_client_source(self, args): + """ Send and execute the specified Javascript source file to the debugger """ + if not args.lower().endswith('.js'): + sys.exit("Error: Javascript file expected!") + return + + with open(args, 'r') as f: + content = args + "\0" + f.read() + self.send_string(content, JERRY_DEBUGGER_CLIENT_SOURCE) + class Multimap(object): def __init__(self): @@ -941,6 +960,9 @@ def main(): if args.exception is not None: prompt.do_exception(str(args.exception)) + if args.client_source is not None: + prompt.send_client_source(str(args.client_source)) + while True: if not non_interactive and prompt.cont: if sys.stdin in select.select([sys.stdin], [], [], 0)[0]: diff --git a/jerry-main/main-unix.c b/jerry-main/main-unix.c index bfb8f634f..2f7499647 100644 --- a/jerry-main/main-unix.c +++ b/jerry-main/main-unix.c @@ -281,6 +281,7 @@ typedef enum OPT_SHOW_RE_OP, OPT_DEBUG_SERVER, OPT_DEBUG_PORT, + OPT_DEBUGGER_WAIT_SOURCE, OPT_SAVE_SNAP_GLOBAL, OPT_SAVE_SNAP_EVAL, OPT_SAVE_LIT_LIST, @@ -312,6 +313,8 @@ static const cli_opt_t main_opts[] = .help = "start debug server and wait for a connecting client"), CLI_OPT_DEF (.id = OPT_DEBUG_PORT, .longopt = "debug-port", .meta = "NUM", .help = "debug server port (default: 5001)"), + CLI_OPT_DEF (.id = OPT_DEBUGGER_WAIT_SOURCE, .longopt = "debugger-wait-source", + .help = "wait for an executable source from the client"), CLI_OPT_DEF (.id = OPT_SAVE_SNAP_GLOBAL, .longopt = "save-snapshot-for-global", .meta = "FILE", .help = "save binary snapshot of parsed JS input (for execution in global context)"), CLI_OPT_DEF (.id = OPT_SAVE_SNAP_EVAL, .longopt = "save-snapshot-for-eval", .meta = "FILE", @@ -407,6 +410,7 @@ main (int argc, uint16_t debug_port = 5001; bool is_repl_mode = false; + bool is_wait_mode = false; bool no_prompt = false; cli_state_t cli_state = cli_init (main_opts, argc - 1, argv + 1); @@ -472,6 +476,14 @@ main (int argc, } break; } + case OPT_DEBUGGER_WAIT_SOURCE: + { + if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg)) + { + is_wait_mode = true; + } + break; + } case OPT_SAVE_SNAP_GLOBAL: case OPT_SAVE_SNAP_EVAL: { @@ -707,6 +719,21 @@ main (int argc, } } + if (is_wait_mode) + { + is_repl_mode = false; +#ifdef JERRY_DEBUGGER + jerry_value_t wait_and_run_value; + + if (jerry_debugger_wait_and_run_client_source (&wait_and_run_value) == JERRY_DEBUGGER_SOURCE_RECEIVE_FAILED) + { + ret_value = jerry_create_error (JERRY_ERROR_COMMON, (jerry_char_t *) "Connection aborted before source arrived."); + } + + jerry_release_value (wait_and_run_value); +#endif /* JERRY_DEBUGGER */ + } + if (is_repl_mode) { const char *prompt = !no_prompt ? "jerry> " : ""; diff --git a/tests/debugger/client_source.cmd b/tests/debugger/client_source.cmd new file mode 100644 index 000000000..b32044fea --- /dev/null +++ b/tests/debugger/client_source.cmd @@ -0,0 +1,4 @@ +s +s +s +continue diff --git a/tests/debugger/client_source.expected b/tests/debugger/client_source.expected new file mode 100644 index 000000000..9259a2806 --- /dev/null +++ b/tests/debugger/client_source.expected @@ -0,0 +1,9 @@ +Connecting to: localhost:5001 +Stopped at tests/debugger/client_source.js:15 +(jerry-debugger) s +Stopped at tests/debugger/client_source.js:40 +(jerry-debugger) s +Stopped at tests/debugger/client_source.js:35 (in test() at line:33, col:1) +(jerry-debugger) s +Stopped at tests/debugger/client_source.js:36 (in test() at line:33, col:1) +(jerry-debugger) continue diff --git a/tests/debugger/client_source.js b/tests/debugger/client_source.js new file mode 100644 index 000000000..1b8972486 --- /dev/null +++ b/tests/debugger/client_source.js @@ -0,0 +1,40 @@ +// Copyright JS Foundation and other contributors, http://js.foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +print("client-source-test"); + +function finish(z) { + print("function finish"); + print("finish: " + z); +} + +function bar(y) { + print("function bar"); + finish(y + "-bar"); +} + +function foo(x) +{ + print("function foo"); + bar(x + "-foo"); +} + +function test() +{ + print("function test"); + var x = "test"; + foo(x); +} + +test(); diff --git a/tools/runners/run-debugger-test.sh b/tools/runners/run-debugger-test.sh index d7b967ae0..3e2518a8a 100755 --- a/tools/runners/run-debugger-test.sh +++ b/tools/runners/run-debugger-test.sh @@ -17,8 +17,14 @@ JERRY=$1 DEBUGGER_CLIENT=$2 TEST_CASE=$3 +CLIENT_ARGS="" -START_DEBUG_SERVER="${JERRY} ${TEST_CASE}.js --start-debug-server &" +if [[ $TEST_CASE == *"client_source"* ]]; then + START_DEBUG_SERVER="${JERRY} --start-debug-server --debugger-wait-source &" + CLIENT_ARGS="--client-source ${TEST_CASE}.js" +else + START_DEBUG_SERVER="${JERRY} ${TEST_CASE}.js --start-debug-server &" +fi echo "$START_DEBUG_SERVER" eval "$START_DEBUG_SERVER" @@ -26,7 +32,7 @@ sleep 1s RESULT_TEMP=`mktemp ${TEST_CASE}.out.XXXXXXXXXX` -(cat "${TEST_CASE}.cmd" | ${DEBUGGER_CLIENT} --non-interactive) &> ${RESULT_TEMP} +(cat "${TEST_CASE}.cmd" | ${DEBUGGER_CLIENT} --non-interactive ${CLIENT_ARGS}) &> ${RESULT_TEMP} diff -U0 ${TEST_CASE}.expected ${RESULT_TEMP} STATUS_CODE=$?