diff --git a/jerry-core/debugger/jerry-debugger-ws.c b/jerry-core/debugger/jerry-debugger-ws.c index 6869c81870..ab7880cf52 100644 --- a/jerry-core/debugger/jerry-debugger-ws.c +++ b/jerry-core/debugger/jerry-debugger-ws.c @@ -13,8 +13,6 @@ * limitations under the License. */ -#include "jerry-api.h" - #ifdef JERRY_DEBUGGER #include @@ -383,7 +381,7 @@ jerry_debugger_accept_connection () return false; } - if (!jerry_debugger_send_configuration (JERRY_DEBUGGER_MAX_BUFFER_SIZE - sizeof (jerry_debugger_receive_header_t))) + if (!jerry_debugger_send_configuration (JERRY_DEBUGGER_MAX_RECEIVE_SIZE)) { return false; } @@ -432,6 +430,9 @@ jerry_debugger_send (size_t data_size) /**< data size */ return jerry_debugger_send_tcp (JERRY_CONTEXT (debugger_send_buffer), data_size); } /* jerry_debugger_send */ +JERRY_STATIC_ASSERT (JERRY_DEBUGGER_MAX_RECEIVE_SIZE < 126, + maximum_debug_message_receive_size_must_be_smaller_than_126); + /** * Receive message from the client. * @@ -451,6 +452,8 @@ jerry_debugger_receive (void) 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) { @@ -468,6 +471,11 @@ jerry_debugger_receive (void) return true; } + if (expected_message_type != 0) + { + continue; + } + return resume_exec; } @@ -475,15 +483,16 @@ jerry_debugger_receive (void) if (JERRY_CONTEXT (debugger_receive_buffer_offset) < sizeof (jerry_debugger_receive_header_t)) { + if (expected_message_type != 0) + { + continue; + } + return resume_exec; } - const size_t max_packet_size = JERRY_DEBUGGER_MAX_BUFFER_SIZE - sizeof (jerry_debugger_receive_header_t); - - JERRY_ASSERT (max_packet_size < 126); - if ((recv_buffer_p[0] & ~JERRY_DEBUGGER_WEBSOCKET_OPCODE_MASK) != JERRY_DEBUGGER_WEBSOCKET_FIN_BIT - || (recv_buffer_p[1] & JERRY_DEBUGGER_WEBSOCKET_LENGTH_MASK) >= max_packet_size + || (recv_buffer_p[1] & JERRY_DEBUGGER_WEBSOCKET_LENGTH_MASK) > JERRY_DEBUGGER_MAX_RECEIVE_SIZE || !(recv_buffer_p[1] & JERRY_DEBUGGER_WEBSOCKET_MASK_BIT)) { jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Unsupported Websocket message.\n"); @@ -503,6 +512,11 @@ jerry_debugger_receive (void) if (JERRY_CONTEXT (debugger_receive_buffer_offset) < message_total_size) { + if (expected_message_type != 0) + { + continue; + } + return resume_exec; } @@ -526,9 +540,13 @@ jerry_debugger_receive (void) } } + /* The jerry_debugger_process_message function is inlined + * so passing these arguments is essentially free. */ if (!jerry_debugger_process_message (recv_buffer_p + sizeof (jerry_debugger_receive_header_t), message_size, - &resume_exec)) + &resume_exec, + &expected_message_type, + &message_data)) { return true; } diff --git a/jerry-core/debugger/jerry-debugger-ws.h b/jerry-core/debugger/jerry-debugger-ws.h index cc7b816ce2..8f3123c3d6 100644 --- a/jerry-core/debugger/jerry-debugger-ws.h +++ b/jerry-core/debugger/jerry-debugger-ws.h @@ -27,6 +27,16 @@ */ #define JERRY_DEBUGGER_MAX_BUFFER_SIZE 128 +/** + * Maximum number of bytes can be received in a single message. + */ +#define JERRY_DEBUGGER_MAX_SEND_SIZE (JERRY_DEBUGGER_MAX_BUFFER_SIZE - 1) + +/** + * Maximum number of bytes can be received in a single message. + */ +#define JERRY_DEBUGGER_MAX_RECEIVE_SIZE (JERRY_DEBUGGER_MAX_BUFFER_SIZE - 6) + /** * Last fragment of a Websocket package. */ diff --git a/jerry-core/debugger/jerry-debugger.c b/jerry-core/debugger/jerry-debugger.c index 140ee892a8..9ee87f71f5 100644 --- a/jerry-core/debugger/jerry-debugger.c +++ b/jerry-core/debugger/jerry-debugger.c @@ -13,11 +13,12 @@ * limitations under the License. */ -#include "jerry-api.h" - #ifdef JERRY_DEBUGGER #include "byte-code.h" +#include "ecma-conversion.h" +#include "ecma-eval.h" +#include "ecma-objects.h" #include "jcontext.h" #include "jerry-debugger.h" #include "jerry-port.h" @@ -87,7 +88,7 @@ jerry_debugger_send_backtrace (uint8_t *recv_buffer_p) /**< pointer the the rece while (frame_ctx_p != NULL && max_depth > 0) { - if (current_frame >= JERRY_DEBUGGER_MAX_SIZE (jerry_debugger_frame_t)) + if (current_frame >= JERRY_DEBUGGER_SEND_MAX (jerry_debugger_frame_t)) { if (!jerry_debugger_send (sizeof (jerry_debugger_send_backtrace_t))) { @@ -118,6 +119,79 @@ jerry_debugger_send_backtrace (uint8_t *recv_buffer_p) /**< pointer the the rece jerry_debugger_send (sizeof (jerry_debugger_send_type_t) + message_size); } /* jerry_debugger_send_backtrace */ +/** + * Send result of evaluated expression. + * + * @return true - if no error is occured + * false - otherwise + */ +static bool +jerry_debugger_send_eval (const lit_utf8_byte_t *eval_string_p, /**< evaluated string */ + size_t eval_string_size) /**< evaluated string size */ +{ + JERRY_ASSERT (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER); + + JERRY_CONTEXT (jerry_init_flags) &= (uint32_t) ~JERRY_INIT_DEBUGGER; + ecma_value_t result = ecma_op_eval_chars_buffer (eval_string_p, eval_string_size, true, false); + JERRY_CONTEXT (jerry_init_flags) |= (uint32_t) JERRY_INIT_DEBUGGER; + + if (!ECMA_IS_VALUE_ERROR (result)) + { + ecma_value_t to_string_value = ecma_op_to_string (result); + ecma_free_value (result); + result = to_string_value; + } + + ecma_value_t message = result; + uint8_t type = JERRY_DEBUGGER_EVAL_RESULT; + + if (ECMA_IS_VALUE_ERROR (result)) + { + type = JERRY_DEBUGGER_EVAL_ERROR; + + if (ecma_is_value_object (result)) + { + ecma_string_t *message_string_p = ecma_get_magic_string (LIT_MAGIC_STRING_MESSAGE); + + message = ecma_op_object_find (ecma_get_object_from_value (result), + message_string_p); + + ecma_deref_ecma_string (message_string_p); + + if (!ecma_is_value_string (message) + || ecma_string_is_empty (ecma_get_string_from_value (message))) + { + ecma_free_value (message); + lit_magic_string_id_t id = ecma_object_get_class_name (ecma_get_object_from_value (result)); + ecma_free_value (result); + + const lit_utf8_byte_t *string_p = lit_get_magic_string_utf8 (id); + return jerry_debugger_send_string (JERRY_DEBUGGER_EVAL_ERROR, + string_p, + strlen ((const char *) string_p)); + } + } + else + { + /* Primitve type. */ + message = ecma_op_to_string (result); + JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (message)); + } + + ecma_free_value (result); + } + + ecma_string_t *string_p = ecma_get_string_from_value (message); + + ECMA_STRING_TO_UTF8_STRING (string_p, buffer_p, buffer_size); + bool success = jerry_debugger_send_string (type, buffer_p, buffer_size); + ECMA_FINALIZE_UTF8_STRING (buffer_p, buffer_size); + + ecma_free_value (message); + + return success; +} /* jerry_debugger_send_eval */ + /** * Check received packet size. */ @@ -138,9 +212,65 @@ jerry_debugger_send_backtrace (uint8_t *recv_buffer_p) /**< pointer the the rece 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 */ + 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 */ { /* Process the received message. */ + + if (*expected_message_type_p != 0) + { + JERRY_ASSERT (*expected_message_type_p == JERRY_DEBUGGER_EVAL_PART); + + jerry_debugger_eval_data_t *eval_data_p = (jerry_debugger_eval_data_t *) *message_data_p; + + if (recv_buffer_p[0] != JERRY_DEBUGGER_EVAL_PART) + { + jmem_heap_free_block (eval_data_p, eval_data_p->eval_size + sizeof (jerry_debugger_eval_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); + + if (message_size < sizeof (jerry_debugger_receive_eval_part_t) + 1) + { + jmem_heap_free_block (eval_data_p, eval_data_p->eval_size + sizeof (jerry_debugger_eval_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; + + message_size -= (uint32_t) sizeof (jerry_debugger_receive_eval_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)); + 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), + message_size); + + if (message_size < expected_data) + { + eval_data_p->eval_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)); + *expected_message_type_p = 0; + return result; + } + switch (recv_buffer_p[0]) { case JERRY_DEBUGGER_FREE_BYTE_CODE_CP: @@ -253,6 +383,50 @@ jerry_debugger_process_message (uint8_t *recv_buffer_p, /**< pointer the the rec return true; } + case JERRY_DEBUGGER_EVAL: + { + if (message_size < sizeof (jerry_debugger_receive_eval_first_t) + 1) + { + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Invalid message size\n"); + jerry_debugger_close_connection (); + return false; + } + + JERRY_DEBUGGER_RECEIVE_BUFFER_AS (jerry_debugger_receive_eval_first_t, eval_first_p); + + uint32_t eval_size; + memcpy (&eval_size, eval_first_p->eval_size, sizeof (uint32_t)); + + if (eval_size <= JERRY_DEBUGGER_MAX_RECEIVE_SIZE - sizeof (jerry_debugger_receive_eval_first_t)) + { + if (eval_size != message_size - sizeof (jerry_debugger_receive_eval_first_t)) + { + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Invalid message size\n"); + jerry_debugger_close_connection (); + return false; + } + + 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; + + eval_data_p = (jerry_debugger_eval_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)); + + lit_utf8_byte_t *eval_string_p = (lit_utf8_byte_t *) (eval_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; + *expected_message_type_p = JERRY_DEBUGGER_EVAL_PART; + return true; + } + default: { 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 * const void *data, /**< raw data */ size_t size) /**< size of data */ { - JERRY_ASSERT (size < JERRY_DEBUGGER_MAX_SIZE (uint8_t)); + JERRY_ASSERT (size <= JERRY_DEBUGGER_SEND_MAX (uint8_t)); JERRY_DEBUGGER_SEND_BUFFER_AS (jerry_debugger_send_type_t, message_type_p); @@ -369,15 +543,18 @@ jerry_debugger_send_data (jerry_debugger_header_type_t type, /**< message type * /** * Send string to the debugger client. + * + * @return true - if the data sent successfully to the debugger client, + * false - otherwise */ -void +bool jerry_debugger_send_string (uint8_t message_type, /**< message type */ - const jerry_char_t *string_p, /**< string data */ + const uint8_t *string_p, /**< string data */ size_t string_length) /**< length of string */ { JERRY_ASSERT (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER); - const size_t max_fragment_len = JERRY_DEBUGGER_MAX_SIZE (char); + const size_t max_fragment_len = JERRY_DEBUGGER_SEND_MAX (uint8_t); JERRY_DEBUGGER_SEND_BUFFER_AS (jerry_debugger_send_string_t, message_string_p); @@ -391,7 +568,7 @@ jerry_debugger_send_string (uint8_t message_type, /**< message type */ if (!jerry_debugger_send (sizeof (jerry_debugger_send_string_t))) { - return; + return false; } string_length -= max_fragment_len; @@ -403,14 +580,14 @@ jerry_debugger_send_string (uint8_t message_type, /**< message type */ memcpy (message_string_p->string, string_p, string_length); - jerry_debugger_send (sizeof (jerry_debugger_send_type_t) + string_length); + return jerry_debugger_send (sizeof (jerry_debugger_send_type_t) + string_length); } /* jerry_debugger_send_string */ /** * Send the function name to the debugger client. */ void -jerry_debugger_send_function_name (const jerry_char_t *function_name_p, /**< function name */ +jerry_debugger_send_function_name (const uint8_t *function_name_p, /**< function name */ size_t function_name_length) /**< length of function name */ { JERRY_ASSERT (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER); diff --git a/jerry-core/debugger/jerry-debugger.h b/jerry-core/debugger/jerry-debugger.h index ea26bbe917..e472f6b269 100644 --- a/jerry-core/debugger/jerry-debugger.h +++ b/jerry-core/debugger/jerry-debugger.h @@ -40,8 +40,8 @@ * Calculate the maximum number of items for a given type * which can be transmitted by one message. */ -#define JERRY_DEBUGGER_MAX_SIZE(type) \ - ((JERRY_DEBUGGER_MAX_BUFFER_SIZE - sizeof (jerry_debugger_send_header_t) - 1) / sizeof (type)) +#define JERRY_DEBUGGER_SEND_MAX(type) \ + ((JERRY_DEBUGGER_MAX_SEND_SIZE - sizeof (jerry_debugger_send_header_t) - 1) / sizeof (type)) /** * Types for the package. @@ -63,6 +63,10 @@ typedef enum JERRY_DEBUGGER_BREAKPOINT_HIT = 12, /**< notify breakpoint hit */ JERRY_DEBUGGER_BACKTRACE = 13, /**< backtrace data */ JERRY_DEBUGGER_BACKTRACE_END = 14, /**< last backtrace data */ + JERRY_DEBUGGER_EVAL_RESULT = 15, /**< eval result */ + JERRY_DEBUGGER_EVAL_RESULT_END = 16, /**< last part of eval result */ + JERRY_DEBUGGER_EVAL_ERROR = 17, /**< eval result when an error is occured */ + JERRY_DEBUGGER_EVAL_ERROR_END = 18, /**< last part of eval result when an error is occured */ /* Messages sent by the client to server. */ JERRY_DEBUGGER_FREE_BYTE_CODE_CP = 1, /**< free byte code compressed pointer */ @@ -72,6 +76,8 @@ typedef enum JERRY_DEBUGGER_STEP = 5, /**< next breakpoint, step into functions */ JERRY_DEBUGGER_NEXT = 6, /**< next breakpoint in the same context */ JERRY_DEBUGGER_GET_BACKTRACE = 7, /**< get backtrace */ + JERRY_DEBUGGER_EVAL = 8, /**< first message of evaluating a string */ + JERRY_DEBUGGER_EVAL_PART = 9, /**< next message of evaluating a string */ } jerry_debugger_header_type_t; /** @@ -120,7 +126,7 @@ typedef struct { jerry_debugger_send_header_t header; /**< message header */ uint8_t type; /**< type of the message */ - uint8_t string[JERRY_DEBUGGER_MAX_SIZE (uint8_t)]; /**< string data */ + uint8_t string[JERRY_DEBUGGER_SEND_MAX (uint8_t)]; /**< string data */ } jerry_debugger_send_string_t; /** @@ -180,7 +186,7 @@ typedef struct { jerry_debugger_send_header_t header; /**< message header */ uint8_t type; /**< type of the message */ - jerry_debugger_frame_t frames[JERRY_DEBUGGER_MAX_SIZE (jerry_debugger_frame_t)]; /**< frames */ + jerry_debugger_frame_t frames[JERRY_DEBUGGER_SEND_MAX (jerry_debugger_frame_t)]; /**< frames */ } jerry_debugger_send_backtrace_t; /** @@ -192,18 +198,44 @@ typedef struct uint8_t max_depth[sizeof (uint32_t)]; /**< maximum depth (0 - unlimited) */ } jerry_debugger_receive_get_backtrace_t; +/** + * Incoming message: first message of evaluating expression. + */ +typedef struct +{ + uint8_t type; /**< type of the message */ + 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. + */ +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; + void jerry_debugger_free_unreferenced_byte_code (void); -bool jerry_debugger_process_message (uint8_t *recv_buffer_p, uint32_t message_size, bool *resume_exec_p); +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); void jerry_debugger_breakpoint_hit (void); void jerry_debugger_send_type (jerry_debugger_header_type_t type); bool jerry_debugger_send_configuration (uint8_t max_message_size); void jerry_debugger_send_data (jerry_debugger_header_type_t type, const void *data, size_t size); -void jerry_debugger_send_string (uint8_t message_type, const jerry_char_t *string_p, size_t string_length); -void jerry_debugger_send_function_name (const jerry_char_t *function_name_p, size_t function_name_length); +bool jerry_debugger_send_string (uint8_t message_type, const uint8_t *string_p, size_t string_length); +void jerry_debugger_send_function_name (const uint8_t *function_name_p, size_t function_name_length); bool jerry_debugger_send_function_cp (jerry_debugger_header_type_t type, ecma_compiled_code_t *compiled_code_p); -void jerry_debugger_send_source_file_name (const jerry_char_t *file_name_p, size_t file_name_length); #endif /* JERRY_DEBUGGER */ diff --git a/jerry-core/parser/js/js-parser-internal.h b/jerry-core/parser/js/js-parser-internal.h index e5e03038d0..bf65f1172b 100644 --- a/jerry-core/parser/js/js-parser-internal.h +++ b/jerry-core/parser/js/js-parser-internal.h @@ -283,7 +283,7 @@ typedef struct #endif /* PARSER_DUMP_BYTE_CODE */ #ifdef JERRY_DEBUGGER - parser_breakpoint_info_t breakpoint_info[JERRY_DEBUGGER_MAX_SIZE (parser_list_t)]; /**< extra data for breakpoints */ + parser_breakpoint_info_t breakpoint_info[JERRY_DEBUGGER_SEND_MAX (parser_list_t)]; /**< extra data for breakpoints */ uint16_t breakpoint_info_count; /**< current breakpoint index */ parser_line_counter_t last_breakpoint_line; /**< last line where breakpoint was inserted */ #endif /* JERRY_DEBUGGER */ diff --git a/jerry-core/parser/js/js-parser-statm.c b/jerry-core/parser/js/js-parser-statm.c index 96d54926e9..9b54e5c724 100644 --- a/jerry-core/parser/js/js-parser-statm.c +++ b/jerry-core/parser/js/js-parser-statm.c @@ -404,7 +404,7 @@ parser_parse_function_statement (parser_context_t *context_p) /**< context */ #ifdef JERRY_DEBUGGER if (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER) { - jerry_debugger_send_function_name ((jerry_char_t *) name_p->u.char_p, + jerry_debugger_send_function_name (name_p->u.char_p, name_p->prop.length); } #endif /* JERRY_DEBUGGER */ diff --git a/jerry-core/parser/js/js-parser.c b/jerry-core/parser/js/js-parser.c index ea9a7eed46..a2e10e5209 100644 --- a/jerry-core/parser/js/js-parser.c +++ b/jerry-core/parser/js/js-parser.c @@ -2049,6 +2049,10 @@ parser_parse_function (parser_context_t *context_p, /**< context */ #ifdef JERRY_DEBUGGER if (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER) { + /* This option has a high memory and performance costs, + * but it is necessary for executing eval operations by the debugger. */ + context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED | PARSER_NO_REG_STORE; + if (context_p->line != context_p->last_breakpoint_line) { parser_emit_cbc (context_p, CBC_BREAKPOINT_DISABLED); @@ -2074,7 +2078,7 @@ parser_parse_function (parser_context_t *context_p, /**< context */ #ifdef JERRY_DEBUGGER if (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER) { - jerry_debugger_send_function_name ((jerry_char_t *) context_p->lit_object.literal_p->u.char_p, + jerry_debugger_send_function_name (context_p->lit_object.literal_p->u.char_p, context_p->lit_object.literal_p->prop.length); } #endif /* JERRY_DEBUGGER */ @@ -2313,7 +2317,7 @@ parser_append_breakpoint_info (parser_context_t *context_p, /**< context */ { JERRY_ASSERT (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER); - if (context_p->breakpoint_info_count >= JERRY_DEBUGGER_MAX_SIZE (parser_list_t)) + if (context_p->breakpoint_info_count >= JERRY_DEBUGGER_SEND_MAX (parser_list_t)) { parser_send_breakpoints (context_p, type); } diff --git a/jerry-debugger/jerry-client-ws.html b/jerry-debugger/jerry-client-ws.html index 454c9ed370..3ff574339d 100644 --- a/jerry-debugger/jerry-client-ws.html +++ b/jerry-debugger/jerry-client-ws.html @@ -42,6 +42,10 @@

JerryScript HTML (WebSocket) Debugger Client

var JERRY_DEBUGGER_BREAKPOINT_HIT = 12; var JERRY_DEBUGGER_BACKTRACE = 13; var JERRY_DEBUGGER_BACKTRACE_END = 14; +var JERRY_DEBUGGER_EVAL_RESULT = 15; +var JERRY_DEBUGGER_EVAL_RESULT_END = 16; +var JERRY_DEBUGGER_EVAL_ERROR = 17; +var JERRY_DEBUGGER_EVAL_ERROR_END = 18; var JERRY_DEBUGGER_FREE_BYTE_CODE_CP = 1; var JERRY_DEBUGGER_UPDATE_BREAKPOINT = 2; @@ -50,6 +54,8 @@

JerryScript HTML (WebSocket) Debugger Client

var JERRY_DEBUGGER_STEP = 5; var JERRY_DEBUGGER_NEXT = 6; var JERRY_DEBUGGER_GET_BACKTRACE = 7; +var JERRY_DEBUGGER_EVAL = 8; +var JERRY_DEBUGGER_EVAL_PART = 9; var textBox = document.getElementById("log"); var commandBox = document.getElementById("command"); @@ -78,6 +84,7 @@

JerryScript HTML (WebSocket) Debugger Client

var activeBreakpoints = { }; var nextBreakpointIndex = 1; var backtraceFrame = 0; + var evalResult = null; function assert(expr) { @@ -87,6 +94,24 @@

JerryScript HTML (WebSocket) Debugger Client

} } + function setUint32(array, offset, value) + { + if (littleEndian) + { + array[offset] = value & 0xff; + array[offset + 1] = (value >> 8) & 0xff; + array[offset + 2] = (value >> 16) & 0xff; + array[offset + 3] = (value >> 24) & 0xff; + } + else + { + array[offset] = (value >> 24) & 0xff; + array[offset + 1] = (value >> 16) & 0xff; + array[offset + 2] = (value >> 8) & 0xff; + array[offset + 3] = value & 0xff; + } + } + /* Concat the two arrays. The first byte (opcode) of nextArray is ignored. */ function concatUint8Arrays(baseArray, nextArray) { @@ -105,10 +130,10 @@

JerryScript HTML (WebSocket) Debugger Client

var baseLength = baseArray.byteLength; var nextLength = nextArray.byteLength - 1; - var result = new Uint8Array(baseArray.byteLength + nextArray.byteLength); - result.set(nextArray, baseArray.byteLength - 1); + var result = new Uint8Array(baseLength + nextLength); + result.set(nextArray, baseLength - 1); - /* This set overwrites the opcode. */ + /* This set operation overwrites the opcode. */ result.set(baseArray); return result; @@ -154,6 +179,62 @@

JerryScript HTML (WebSocket) Debugger Client

return result; } + function stringToCesu8(string) + { + assert(string != ""); + + var length = string.length; + var byteLength = length; + + for (var i = 0; i < length; i++) + { + var chr = string.charCodeAt(i); + + if (chr >= 0x7ff) + { + byteLength ++; + } + + if (chr >= 0x7f) + { + byteLength++; + } + } + + var result = new Uint8Array(byteLength + 1 + 4); + + result[0] = JERRY_DEBUGGER_EVAL; + + setUint32(result, 1, byteLength); + + var offset = 5; + + for (var i = 0; i < length; i++) + { + var chr = string.charCodeAt(i); + + if (chr >= 0x7ff) + { + result[offset] = 0xe0 | (chr >> 12); + result[offset + 1] = 0x80 | ((chr >> 6) & 0x3f); + result[offset + 2] = 0x80 | (chr & 0x3f); + offset += 3; + } + else if (chr >= 0x7f) + { + result[offset] = 0xc0 | (chr >> 6); + result[offset + 1] = 0x80 | (chr & 0x3f); + } + else + { + result[offset] = chr; + offset++; + } + } + + return result; + } + function breakpointToString(breakpoint) { var name = breakpoint.func.name; @@ -373,21 +454,7 @@

JerryScript HTML (WebSocket) Debugger Client

continue; } - if (littleEndian) - { - message[offset] = value & 0xff; - message[offset + 1] = (value >> 8) & 0xff; - message[offset + 2] = (value >> 16) & 0xff; - message[offset + 3] = (value >> 24) & 0xff; - } - else - { - message[offset] = (value >> 24) & 0xff; - message[offset + 1] = (value >> 16) & 0xff; - message[offset + 2] = (value >> 8) & 0xff; - message[offset + 3] = value & 0xff; - } - + setUint32(message, offset, value); offset += 4; } @@ -671,6 +738,30 @@

JerryScript HTML (WebSocket) Debugger Client

return; } + case JERRY_DEBUGGER_EVAL_RESULT: + case JERRY_DEBUGGER_EVAL_RESULT_END: + case JERRY_DEBUGGER_EVAL_ERROR: + case JERRY_DEBUGGER_EVAL_ERROR_END: + { + evalResult = concatUint8Arrays(evalResult, message); + + if (message[0] == JERRY_DEBUGGER_EVAL_RESULT_END) + { + appendLog(cesu8ToString(evalResult)); + evalResult = null; + return; + } + + if (message[0] == JERRY_DEBUGGER_EVAL_ERROR_END) + { + appendLog("Uncaught exception: " + cesu8ToString(evalResult)); + evalResult = null; + return; + } + + return; + } + default: { abortConnection("unexpected message."); @@ -775,6 +866,34 @@

JerryScript HTML (WebSocket) Debugger Client

} } + this.sendEval = function(str) + { + if (str == "") + { + return; + } + + var array = stringToCesu8(str); + var byteLength = array.byteLength; + + if (byteLength <= maxMessageSize) + { + socket.send(array); + return; + } + + socket.send(array.slice(0, maxMessageSize)); + + var offset = maxMessageSize - 1; + + while (offset < byteLength) + { + array[offset] = JERRY_DEBUGGER_EVAL_PART; + socket.send(array.slice(offset, offset + maxMessageSize)); + offset += maxMessageSize - 1; + } + } + this.dump = function() { for (var i in functions) @@ -837,6 +956,7 @@

JerryScript HTML (WebSocket) Debugger Client

" continue|c - continue execution\n" + " step|s - step-in execution\n" + " next|n - connect to server\n" + + " eval|e - evaluate expression\n" + " backtrace|bt - get backtrace\n" + " dump - dump all breakpoint data"); @@ -905,6 +1025,11 @@

JerryScript HTML (WebSocket) Debugger Client

debuggerObj.encodeMessage("B", [ JERRY_DEBUGGER_NEXT ]); break; + case "e": + case "eval": + debuggerObj.sendEval(args[2]); + break; + case "bt": case "backtrace": max_depth = 0; @@ -937,6 +1062,7 @@

JerryScript HTML (WebSocket) Debugger Client

default: appendLog("Unknown command: " + args[1]); + break; } commandBox.value = ""; diff --git a/jerry-debugger/jerry-client-ws.py b/jerry-debugger/jerry-client-ws.py index 9c5af02137..cf9b252bce 100755 --- a/jerry-debugger/jerry-client-ws.py +++ b/jerry-debugger/jerry-client-ws.py @@ -39,6 +39,10 @@ JERRY_DEBUGGER_BREAKPOINT_HIT = 12 JERRY_DEBUGGER_BACKTRACE = 13 JERRY_DEBUGGER_BACKTRACE_END = 14 +JERRY_DEBUGGER_EVAL_RESULT = 15 +JERRY_DEBUGGER_EVAL_RESULT_END = 16 +JERRY_DEBUGGER_EVAL_ERROR = 17 +JERRY_DEBUGGER_EVAL_ERROR_END = 18 # Messages sent by the client to server. JERRY_DEBUGGER_FREE_BYTE_CODE_CP = 1 @@ -48,6 +52,8 @@ JERRY_DEBUGGER_STEP = 5 JERRY_DEBUGGER_NEXT = 6 JERRY_DEBUGGER_GET_BACKTRACE = 7 +JERRY_DEBUGGER_EVAL = 8 +JERRY_DEBUGGER_EVAL_PART = 9 MAX_BUFFER_SIZE = 128 WEBSOCKET_BINARY_FRAME = 2 @@ -246,6 +252,58 @@ def do_dump(self, args): """ Dump all of the debugger data """ pprint(self.debugger.function_list) + def eval_string(self, args): + size = len(args) + if size == 0: + return + + # 1: length of type byte + # 4: length of an uint32 value + message_header = 1 + 4 + max_fragment = min(self.debugger.max_message_size - message_header, size) + + message = pack(self.debugger.byte_order + "BBIBI", + WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT, + WEBSOCKET_FIN_BIT + max_fragment + message_header, + 0, + JERRY_DEBUGGER_EVAL, + size) + + if size == max_fragment: + self.debugger.send_message(message + args) + self.stop = True + return + + self.debugger.send_message(message + args[0:max_fragment]) + offset = max_fragment + + # 1: length of type byte + message_header = 1 + + max_fragment = self.debugger.max_message_size - message_header + while offset < size: + next_fragment = min(max_fragment, size - offset) + + message = pack(self.debugger.byte_order + "BBIB", + WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT, + WEBSOCKET_FIN_BIT + next_fragment + message_header, + 0, + JERRY_DEBUGGER_EVAL_PART) + + prev_offset = offset + offset += next_fragment + self.debugger.send_message(message + args[prev_offset:offset]) + + self.stop = True + + def do_eval(self, args): + """ Evaluate JavaScript source code """ + self.eval_string(args) + + def do_e(self, args): + """ Evaluate JavaScript source code """ + self.eval_string(args) + class Multimap(object): @@ -316,6 +374,8 @@ def __init__(self, address): if len_result > len_expected: result = result[len_expected:] + else: + result = b"" len_expected = 6 # Network configurations, which has the following struct: @@ -330,10 +390,13 @@ def __init__(self, address): len_result = len(result) - if (ord(result[0]) != WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT or - ord(result[1]) != 4 or - ord(result[2]) != JERRY_DEBUGGER_CONFIGURATION): - raise Exception("Unexpected configuration") + expected = pack("BBB", + WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT, + 4, + JERRY_DEBUGGER_CONFIGURATION) + + if result[0:3] != expected: + raise Exception("Unexpected configuration") self.max_message_size = ord(result[3]) self.cp_size = ord(result[4]) @@ -434,10 +497,10 @@ def parse_source(debugger, data): return if buffer_type in [JERRY_DEBUGGER_RESOURCE_NAME, JERRY_DEBUGGER_RESOURCE_NAME_END]: - source_name += unpack("%ds" % (buffer_size), data[3:buffer_size+3])[0] + source_name += data[3:] elif buffer_type in [JERRY_DEBUGGER_FUNCTION_NAME, JERRY_DEBUGGER_FUNCTION_NAME_END]: - function_name += unpack("%ds" % (buffer_size), data[3:buffer_size+3])[0] + function_name += data[3:] elif buffer_type == JERRY_DEBUGGER_PARSE_FUNCTION: logging.debug("Source name: %s, function name: %s" % (source_name, function_name)) @@ -656,6 +719,35 @@ def main(): prompt.cmdloop() + elif buffer_type in [JERRY_DEBUGGER_EVAL_RESULT, + JERRY_DEBUGGER_EVAL_RESULT_END, + JERRY_DEBUGGER_EVAL_ERROR, + JERRY_DEBUGGER_EVAL_ERROR_END]: + + message = b"" + eval_type = buffer_type + while True: + message += data[3:] + + if buffer_type in [JERRY_DEBUGGER_EVAL_RESULT_END, + JERRY_DEBUGGER_EVAL_ERROR_END]: + break + + data = debugger.get_message() + buffer_type = ord(data[2]) + buffer_size = ord(data[1]) - 1 + + if buffer_type not in [eval_type, + eval_type + 1]: + raise Exception("Eval result expected") + + if buffer_type == JERRY_DEBUGGER_EVAL_ERROR_END: + print("Uncaught exception: %s" % (message)) + else: + print(message) + + prompt.cmdloop() + else: raise Exception("Unknown message") diff --git a/tests/debugger/do_dump.expected b/tests/debugger/do_dump.expected index 9355065ccf..a1f7bb46ca 100644 --- a/tests/debugger/do_dump.expected +++ b/tests/debugger/do_dump.expected @@ -3,8 +3,8 @@ Stopped at tests/debugger/do_dump.js:15 (jerry-debugger) {68: Function(byte_code_cp:0x44, source:"tests/debugger/do_dump.js", name:"f4", { Breakpoint(line:32, offset:17, active_index:-1),Breakpoint(line:31, offset:16, active_index:-1) }), 79: Function(byte_code_cp:0x4f, source:"tests/debugger/do_dump.js", name:"f1", { Breakpoint(line:17, offset:21, active_index:-1),Breakpoint(line:27, offset:22, active_index:-1) }), 102: Function(byte_code_cp:0x66, source:"tests/debugger/do_dump.js", name:"f2", { Breakpoint(line:19, offset:14, active_index:-1),Breakpoint(line:21, offset:15, active_index:-1) }), - 105: Function(byte_code_cp:0x69, source:"tests/debugger/do_dump.js", name:"foo", { Breakpoint(line:35, offset:20, active_index:-1),Breakpoint(line:37, offset:21, active_index:-1),Breakpoint(line:38, offset:26, active_index:-1),Breakpoint(line:39, offset:31, active_index:-1) }), + 105: Function(byte_code_cp:0x69, source:"tests/debugger/do_dump.js", name:"foo", { Breakpoint(line:35, offset:22, active_index:-1),Breakpoint(line:37, offset:23, active_index:-1),Breakpoint(line:38, offset:28, active_index:-1),Breakpoint(line:39, offset:33, active_index:-1) }), 125: Function(byte_code_cp:0x7d, source:"tests/debugger/do_dump.js", name:"f3", { Breakpoint(line:22, offset:25, active_index:-1),Breakpoint(line:23, offset:30, active_index:-1) }), 131: Function(byte_code_cp:0x83, source:"tests/debugger/do_dump.js", name:"", { Breakpoint(line:57, offset:63, active_index:-1),Breakpoint(line:42, offset:54, active_index:-1),Breakpoint(line:43, offset:59, active_index:-1),Breakpoint(line:60, offset:68, active_index:-1),Breakpoint(line:15, offset:49, active_index:-1) }), - 154: Function(byte_code_cp:0x9a, source:"tests/debugger/do_dump.js", name:"test", { Breakpoint(line:45, offset:26, active_index:-1),Breakpoint(line:47, offset:27, active_index:-1),Breakpoint(line:48, offset:32, active_index:-1),Breakpoint(line:49, offset:36, active_index:-1),Breakpoint(line:50, offset:41, active_index:-1),Breakpoint(line:51, offset:46, active_index:-1),Breakpoint(line:52, offset:52, active_index:-1),Breakpoint(line:53, offset:56, active_index:-1) })} + 154: Function(byte_code_cp:0x9a, source:"tests/debugger/do_dump.js", name:"test", { Breakpoint(line:45, offset:28, active_index:-1),Breakpoint(line:47, offset:29, active_index:-1),Breakpoint(line:48, offset:34, active_index:-1),Breakpoint(line:49, offset:38, active_index:-1),Breakpoint(line:50, offset:43, active_index:-1),Breakpoint(line:51, offset:48, active_index:-1),Breakpoint(line:52, offset:54, active_index:-1),Breakpoint(line:53, offset:58, active_index:-1) })} (jerry-debugger) Connection closed. diff --git a/tests/debugger/do_eval.cmd b/tests/debugger/do_eval.cmd new file mode 100644 index 0000000000..22091f6dfa --- /dev/null +++ b/tests/debugger/do_eval.cmd @@ -0,0 +1,12 @@ +e a +n +eval a +break f +c +e a +n +e b +n +e b +e "1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 XXX 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 YYY 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 ZZZ " + 123 +c diff --git a/tests/debugger/do_eval.expected b/tests/debugger/do_eval.expected new file mode 100644 index 0000000000..249ce2835f --- /dev/null +++ b/tests/debugger/do_eval.expected @@ -0,0 +1,14 @@ +Address setup: localhost:5001 +Stopped at tests/debugger/do_eval.js:15 +(jerry-debugger) undefined +(jerry-debugger) Stopped at tests/debugger/do_eval.js:23 +(jerry-debugger) 5 +(jerry-debugger) Breakpoint 1 at tests/debugger/do_eval.js:17 (in f) +(jerry-debugger) Stopped at breakpoint:1 tests/debugger/do_eval.js:17 (in f) +(jerry-debugger) 3.3 +(jerry-debugger) Stopped at tests/debugger/do_eval.js:19 (in f) +(jerry-debugger) undefined +(jerry-debugger) Stopped at tests/debugger/do_eval.js:20 (in f) +(jerry-debugger) 6 +(jerry-debugger) 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 XXX 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 YYY 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 ZZZ 123 +(jerry-debugger) Connection closed. diff --git a/tests/debugger/do_eval.js b/tests/debugger/do_eval.js new file mode 100644 index 0000000000..17f8d65242 --- /dev/null +++ b/tests/debugger/do_eval.js @@ -0,0 +1,23 @@ +// 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. + +var a = 5; + +function f(a) +{ + var b = 6; + return a + b; +} + +f(3.3)