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

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;
}

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.
*/

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);

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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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);
}

View File

@ -42,6 +42,10 @@ var JERRY_DEBUGGER_RELEASE_BYTE_CODE_CP = 11;
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 @@ var JERRY_DEBUGGER_CONTINUE = 4;
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 @@ function DebuggerClient(ipAddr)
var activeBreakpoints = { };
var nextBreakpointIndex = 1;
var backtraceFrame = 0;
var evalResult = null;
function assert(expr)
{
@ -87,6 +94,24 @@ function DebuggerClient(ipAddr)
}
}
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 @@ function DebuggerClient(ipAddr)
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 @@ function DebuggerClient(ipAddr)
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 @@ function DebuggerClient(ipAddr)
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 @@ function DebuggerClient(ipAddr)
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 @@ function DebuggerClient(ipAddr)
}
}
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 @@ function debuggerCommand(event)
" continue|c - continue execution\n" +
" step|s - step-in execution\n" +
" next|n - connect to server\n" +
" eval|e - evaluate expression\n" +
" backtrace|bt <max-depth> - get backtrace\n" +
" dump - dump all breakpoint data");
@ -905,6 +1025,11 @@ function debuggerCommand(event)
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 @@ function debuggerCommand(event)
default:
appendLog("Unknown command: " + args[1]);
break;
}
commandBox.value = "";

View File

@ -39,6 +39,10 @@ JERRY_DEBUGGER_RELEASE_BYTE_CODE_CP = 11
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_CONTINUE = 4
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 @@ class DebuggerPrompt(Cmd):
""" 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 @@ class JerryDebugger(object):
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 @@ class JerryDebugger(object):
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")

View File

@ -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.

View File

@ -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

View File

@ -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.

23
tests/debugger/do_eval.js Normal file
View File

@ -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)