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 zherczeg.u-szeged@partner.samsung.com
This commit is contained in:
Zoltan Herczeg
2017-02-17 14:31:56 +01:00
committed by GitHub
parent 6739463c1e
commit b02ef67cd2
13 changed files with 566 additions and 58 deletions
+27 -9
View File
@@ -13,8 +13,6 @@
* limitations under the License.
*/
#include "jerry-api.h"
#ifdef JERRY_DEBUGGER
#include <arpa/inet.h>
@@ -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;
}
+10
View File
@@ -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.
*/
+188 -11
View File
@@ -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);
+40 -8
View File
@@ -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 */