aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Sipka <rsipka.uszeged@partner.samsung.com>2018-10-29 14:33:31 +0100
committerAkos Kiss <akiss@inf.u-szeged.hu>2018-10-29 14:33:31 +0100
commit34c5e229ee35bd2002f2a33300086665c13e9cbe (patch)
treeb654d91d09c2f775a310f4afb0548cd805e57455
parent7120b8ec02592f346b342231177aecc60ae6c7fe (diff)
List scope chain levels and their variables to the selected stack frame. (#2557)
It supports to list the scope chain of the current execution context and see which variables are available. JerryScript-DCO-1.0-Signed-off-by: Robert Sipka rsipka.uszeged@partner.samsung.com
-rw-r--r--jerry-core/debugger/debugger.c351
-rw-r--r--jerry-core/debugger/debugger.h54
-rw-r--r--jerry-core/ecma/base/ecma-globals.h9
-rw-r--r--jerry-core/include/jerryscript-debugger.h2
-rw-r--r--jerry-core/vm/vm.c3
-rwxr-xr-xjerry-debugger/jerry_client.py10
-rw-r--r--jerry-debugger/jerry_client_ws.py151
-rw-r--r--tests/debugger/do_help.expected8
-rw-r--r--tests/debugger/do_scopes.cmd19
-rw-r--r--tests/debugger/do_scopes.expected63
-rw-r--r--tests/debugger/do_scopes.js41
-rw-r--r--tests/debugger/do_variables.cmd28
-rw-r--r--tests/debugger/do_variables.expected128
-rw-r--r--tests/debugger/do_variables.js60
14 files changed, 914 insertions, 13 deletions
diff --git a/jerry-core/debugger/debugger.c b/jerry-core/debugger/debugger.c
index 3f9ed5b4..0a0676b5 100644
--- a/jerry-core/debugger/debugger.c
+++ b/jerry-core/debugger/debugger.c
@@ -18,6 +18,7 @@
#include "ecma-builtin-helpers.h"
#include "ecma-conversion.h"
#include "ecma-eval.h"
+#include "ecma-function-object.h"
#include "ecma-objects.h"
#include "jcontext.h"
#include "jerryscript-port.h"
@@ -37,9 +38,9 @@ typedef struct
* The number of message types in the debugger should reflect the
* debugger versioning.
*/
-JERRY_STATIC_ASSERT (JERRY_DEBUGGER_MESSAGES_OUT_MAX_COUNT == 28
- && JERRY_DEBUGGER_MESSAGES_IN_MAX_COUNT == 19
- && JERRY_DEBUGGER_VERSION == 6,
+JERRY_STATIC_ASSERT (JERRY_DEBUGGER_MESSAGES_OUT_MAX_COUNT == 32
+ && JERRY_DEBUGGER_MESSAGES_IN_MAX_COUNT == 21
+ && JERRY_DEBUGGER_VERSION == 7,
debugger_version_correlates_to_message_type_count);
/**
@@ -196,6 +197,332 @@ jerry_debugger_send_backtrace (const uint8_t *recv_buffer_p) /**< pointer to the
} /* jerry_debugger_send_backtrace */
/**
+ * Send the scope chain types.
+ */
+static void
+jerry_debugger_send_scope_chain (void)
+{
+ vm_frame_ctx_t *iter_frame_ctx_p = JERRY_CONTEXT (vm_top_context_p);
+
+ const size_t max_byte_count = JERRY_DEBUGGER_SEND_MAX (uint8_t);
+ const size_t max_message_size = JERRY_DEBUGGER_SEND_SIZE (max_byte_count, uint8_t);
+
+ JERRY_DEBUGGER_SEND_BUFFER_AS (jerry_debugger_send_string_t, message_type_p);
+ message_type_p->type = JERRY_DEBUGGER_SCOPE_CHAIN;
+
+ size_t buffer_pos = 0;
+ bool next_func_is_local = true;
+ ecma_object_t *lex_env_p = iter_frame_ctx_p->lex_env_p;
+
+ while (true)
+ {
+ JERRY_ASSERT (ecma_is_lexical_environment (lex_env_p));
+
+ if (buffer_pos == max_byte_count)
+ {
+ if (!jerry_debugger_send (max_message_size))
+ {
+ return;
+ }
+
+ buffer_pos = 0;
+ }
+
+ if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE)
+ {
+ if ((lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_NON_CLOSURE) != 0)
+ {
+ message_type_p->string[buffer_pos++] = JERRY_DEBUGGER_SCOPE_NON_CLOSURE;
+ }
+ else if (next_func_is_local)
+ {
+ message_type_p->string[buffer_pos++] = JERRY_DEBUGGER_SCOPE_LOCAL;
+ next_func_is_local = false;
+ }
+ else
+ {
+ message_type_p->string[buffer_pos++] = JERRY_DEBUGGER_SCOPE_CLOSURE;
+ }
+ }
+ else if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND)
+ {
+ if (ecma_get_lex_env_outer_reference (lex_env_p) == NULL)
+ {
+ message_type_p->string[buffer_pos++] = JERRY_DEBUGGER_SCOPE_GLOBAL;
+ break;
+ }
+ else
+ {
+ message_type_p->string[buffer_pos++] = JERRY_DEBUGGER_SCOPE_WITH;
+ }
+ }
+
+ lex_env_p = ecma_get_lex_env_outer_reference (lex_env_p);
+ }
+
+ message_type_p->type = JERRY_DEBUGGER_SCOPE_CHAIN_END;
+
+ jerry_debugger_send (sizeof (jerry_debugger_send_type_t) + buffer_pos);
+} /* jerry_debugger_send_scope_chain */
+
+/**
+ * Get type of the scope variable property.
+ */
+static jerry_debugger_scope_variable_type_t
+jerry_debugger_get_variable_type (ecma_value_t value) /**< input ecma value */
+{
+ jerry_debugger_scope_variable_type_t ret_value = JERRY_DEBUGGER_VALUE_NONE;
+
+ if (ecma_is_value_undefined (value))
+ {
+ ret_value = JERRY_DEBUGGER_VALUE_UNDEFINED;
+ }
+ else if (ecma_is_value_null (value))
+ {
+ ret_value = JERRY_DEBUGGER_VALUE_NULL;
+ }
+ else if (ecma_is_value_boolean (value))
+ {
+ ret_value = JERRY_DEBUGGER_VALUE_BOOLEAN;
+ }
+ else if (ecma_is_value_number (value))
+ {
+ ret_value = JERRY_DEBUGGER_VALUE_NUMBER;
+ }
+ else if (ecma_is_value_string (value))
+ {
+ ret_value = JERRY_DEBUGGER_VALUE_STRING;
+ }
+ else
+ {
+ JERRY_ASSERT (ecma_is_value_object (value));
+
+ if (ecma_object_get_class_name (ecma_get_object_from_value (value)) == LIT_MAGIC_STRING_ARRAY_UL)
+ {
+ ret_value = JERRY_DEBUGGER_VALUE_ARRAY;
+ }
+ else
+ {
+ ret_value = ecma_op_is_callable (value) ? JERRY_DEBUGGER_VALUE_FUNCTION : JERRY_DEBUGGER_VALUE_OBJECT;
+ }
+ }
+
+ JERRY_ASSERT (ret_value != JERRY_DEBUGGER_VALUE_NONE);
+
+ return ret_value;
+} /* jerry_debugger_get_variable_type */
+
+/**
+ * Helper function for jerry_debugger_send_scope_variables.
+ *
+ * It will copies the given scope values type, length and value into the outgoing message string.
+ *
+ * @return true - if the copy was successfully
+ * false - otherwise
+ */
+static bool
+jerry_debugger_copy_variables_to_string_message (jerry_debugger_scope_variable_type_t variable_type, /**< type */
+ ecma_string_t *value_str, /**< property name or value string */
+ jerry_debugger_send_string_t *message_string_p, /**< msg pointer */
+ size_t *buffer_pos) /**< string data position of the message */
+{
+ const size_t max_byte_count = JERRY_DEBUGGER_SEND_MAX (uint8_t);
+ const size_t max_message_size = JERRY_DEBUGGER_SEND_SIZE (max_byte_count, uint8_t);
+
+ ECMA_STRING_TO_UTF8_STRING (value_str, str_buff, str_buff_size);
+
+ size_t str_size = 0;
+ size_t str_limit = 255;
+ bool result = true;
+
+ bool type_processed = false;
+
+ while (true)
+ {
+ if (*buffer_pos == max_byte_count)
+ {
+ if (!jerry_debugger_send (max_message_size))
+ {
+ result = false;
+ break;
+ }
+
+ *buffer_pos = 0;
+ }
+
+ if (!type_processed)
+ {
+ if (variable_type != JERRY_DEBUGGER_VALUE_NONE)
+ {
+ message_string_p->string[*buffer_pos] = variable_type;
+ *buffer_pos += 1;
+ }
+ type_processed = true;
+ continue;
+ }
+
+ if (variable_type == JERRY_DEBUGGER_VALUE_FUNCTION)
+ {
+ str_size = 0; // do not copy function values
+ }
+ else
+ {
+ str_size = (str_buff_size > str_limit) ? str_limit : str_buff_size;
+ }
+
+ message_string_p->string[*buffer_pos] = (uint8_t) str_size;
+ *buffer_pos += 1;
+ break;
+ }
+
+ if (result)
+ {
+ size_t free_bytes = max_byte_count - *buffer_pos;
+ const uint8_t *string_p = str_buff;
+
+ while (str_size > free_bytes)
+ {
+ memcpy (message_string_p->string + *buffer_pos, string_p, free_bytes);
+
+ if (!jerry_debugger_send (max_message_size))
+ {
+ result = false;
+ break;
+ }
+
+ string_p += free_bytes;
+ str_size -= free_bytes;
+ free_bytes = max_byte_count;
+ *buffer_pos = 0;
+ }
+
+ if (result)
+ {
+ memcpy (message_string_p->string + *buffer_pos, string_p, str_size);
+ *buffer_pos += str_size;
+ }
+ }
+
+ ECMA_FINALIZE_UTF8_STRING (str_buff, str_buff_size);
+
+ return result;
+} /* jerry_debugger_copy_variables_to_string_message */
+
+/**
+ * Send variables of the given scope chain level.
+ */
+static void
+jerry_debugger_send_scope_variables (const uint8_t *recv_buffer_p) /**< pointer to the received data */
+{
+ JERRY_DEBUGGER_RECEIVE_BUFFER_AS (jerry_debugger_receive_get_scope_variables_t, get_scope_variables_p);
+
+ uint32_t chain_index;
+ memcpy (&chain_index, get_scope_variables_p->chain_index, sizeof (uint32_t));
+
+ vm_frame_ctx_t *iter_frame_ctx_p = JERRY_CONTEXT (vm_top_context_p);
+ ecma_object_t *lex_env_p = iter_frame_ctx_p->lex_env_p;
+
+ while (chain_index != 0)
+ {
+ lex_env_p = ecma_get_lex_env_outer_reference (lex_env_p);
+
+ if (JERRY_UNLIKELY (lex_env_p == NULL))
+ {
+ jerry_debugger_send_type (JERRY_DEBUGGER_SCOPE_VARIABLES_END);
+ return;
+ }
+
+ if ((ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND)
+ || (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE))
+ {
+ chain_index--;
+ }
+ }
+
+ ecma_property_header_t *prop_iter_p;
+
+ if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE)
+ {
+ prop_iter_p = ecma_get_property_list (lex_env_p);
+ }
+ else
+ {
+ JERRY_ASSERT (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND);
+ ecma_object_t *binding_obj_p = ecma_get_lex_env_binding_object (lex_env_p);
+ prop_iter_p = ecma_get_property_list (binding_obj_p);
+ }
+
+ JERRY_DEBUGGER_SEND_BUFFER_AS (jerry_debugger_send_string_t, message_string_p);
+ message_string_p->type = JERRY_DEBUGGER_SCOPE_VARIABLES;
+
+ size_t buffer_pos = 0;
+
+ while (prop_iter_p != NULL)
+ {
+ JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p));
+
+ ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) prop_iter_p;
+
+ for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++)
+ {
+ if (ECMA_PROPERTY_IS_NAMED_PROPERTY (prop_iter_p->types[i]))
+ {
+ if (ECMA_PROPERTY_GET_NAME_TYPE (prop_iter_p->types[i]) == ECMA_DIRECT_STRING_MAGIC
+ && prop_pair_p->names_cp[i] >= LIT_NON_INTERNAL_MAGIC_STRING__COUNT)
+ {
+ continue;
+ }
+
+ ecma_string_t *prop_name = ecma_string_from_property_name (prop_iter_p->types[i],
+ prop_pair_p->names_cp[i]);
+
+ if (!jerry_debugger_copy_variables_to_string_message (JERRY_DEBUGGER_VALUE_NONE,
+ prop_name,
+ message_string_p,
+ &buffer_pos))
+ {
+ ecma_deref_ecma_string (prop_name);
+ return;
+ }
+
+ ecma_deref_ecma_string (prop_name);
+
+ ecma_property_value_t prop_value_p = prop_pair_p->values[i];
+ ecma_value_t property_value;
+
+ jerry_debugger_scope_variable_type_t variable_type = jerry_debugger_get_variable_type (prop_value_p.value);
+
+ if (variable_type == JERRY_DEBUGGER_VALUE_OBJECT)
+ {
+ property_value = ecma_builtin_json_string_from_object (prop_value_p.value);
+ }
+ else
+ {
+ property_value = ecma_op_to_string (prop_value_p.value);
+ }
+
+ if (!jerry_debugger_copy_variables_to_string_message (variable_type,
+ ecma_get_string_from_value (property_value),
+ message_string_p,
+ &buffer_pos))
+ {
+ ecma_free_value (property_value);
+ return;
+ }
+
+ ecma_free_value (property_value);
+ }
+ }
+
+ prop_iter_p = ECMA_GET_POINTER (ecma_property_header_t,
+ prop_iter_p->next_property_cp);
+ }
+
+ message_string_p->type = JERRY_DEBUGGER_SCOPE_VARIABLES_END;
+ jerry_debugger_send (sizeof (jerry_debugger_send_type_t) + buffer_pos);
+} /* jerry_debugger_send_scope_variables */
+
+/**
* Send result of evaluated expression or throw an error.
*
* @return true - if execution should be resumed
@@ -525,6 +852,24 @@ jerry_debugger_process_message (const uint8_t *recv_buffer_p, /**< pointer to th
return true;
}
+ case JERRY_DEBUGGER_GET_SCOPE_CHAIN:
+ {
+ JERRY_DEBUGGER_CHECK_PACKET_SIZE (jerry_debugger_receive_type_t);
+
+ jerry_debugger_send_scope_chain ();
+
+ return true;
+ }
+
+ case JERRY_DEBUGGER_GET_SCOPE_VARIABLES:
+ {
+ JERRY_DEBUGGER_CHECK_PACKET_SIZE (jerry_debugger_receive_get_scope_variables_t);
+
+ jerry_debugger_send_scope_variables (recv_buffer_p);
+
+ return true;
+ }
+
case JERRY_DEBUGGER_EXCEPTION_CONFIG:
{
JERRY_DEBUGGER_CHECK_PACKET_SIZE (jerry_debugger_receive_exception_config_t);
diff --git a/jerry-core/debugger/debugger.h b/jerry-core/debugger/debugger.h
index eb1bb287..731314df 100644
--- a/jerry-core/debugger/debugger.h
+++ b/jerry-core/debugger/debugger.h
@@ -152,7 +152,10 @@ typedef enum
JERRY_DEBUGGER_WAIT_FOR_SOURCE = 25, /**< engine waiting for source code */
JERRY_DEBUGGER_OUTPUT_RESULT = 26, /**< output sent by the program to the debugger */
JERRY_DEBUGGER_OUTPUT_RESULT_END = 27, /**< last output result data */
-
+ JERRY_DEBUGGER_SCOPE_CHAIN = 28, /**< scope chain */
+ JERRY_DEBUGGER_SCOPE_CHAIN_END = 29, /**< last output of scope chain */
+ JERRY_DEBUGGER_SCOPE_VARIABLES = 30, /**< scope variables */
+ JERRY_DEBUGGER_SCOPE_VARIABLES_END = 31, /**< last output of scope variables */
JERRY_DEBUGGER_MESSAGES_OUT_MAX_COUNT, /**< number of different type of output messages by the debugger */
/* Messages sent by the client to server. */
@@ -182,7 +185,8 @@ typedef enum
JERRY_DEBUGGER_GET_BACKTRACE = 16, /**< get backtrace */
JERRY_DEBUGGER_EVAL = 17, /**< first message of evaluating a string */
JERRY_DEBUGGER_EVAL_PART = 18, /**< next message of evaluating a string */
-
+ JERRY_DEBUGGER_GET_SCOPE_CHAIN = 19, /**< get type names of the scope chain */
+ JERRY_DEBUGGER_GET_SCOPE_VARIABLES = 20, /**< get variables of a scope */
JERRY_DEBUGGER_MESSAGES_IN_MAX_COUNT, /**< number of different type of input messages */
} jerry_debugger_header_type_t;
@@ -230,6 +234,34 @@ typedef enum
} jerry_debugger_output_subtype_t;
/**
+ * Types of scopes.
+ */
+typedef enum
+{
+ JERRY_DEBUGGER_SCOPE_WITH = 1, /**< with */
+ JERRY_DEBUGGER_SCOPE_LOCAL = 2, /**< local */
+ JERRY_DEBUGGER_SCOPE_CLOSURE = 3, /**< closure */
+ JERRY_DEBUGGER_SCOPE_GLOBAL = 4, /**< global */
+ JERRY_DEBUGGER_SCOPE_NON_CLOSURE = 5 /**< non closure */
+} jerry_debugger_scope_chain_type_t;
+
+/**
+ * Type of scope variables.
+ */
+typedef enum
+{
+ JERRY_DEBUGGER_VALUE_NONE = 1,
+ JERRY_DEBUGGER_VALUE_UNDEFINED = 2,
+ JERRY_DEBUGGER_VALUE_NULL = 3,
+ JERRY_DEBUGGER_VALUE_BOOLEAN = 4,
+ JERRY_DEBUGGER_VALUE_NUMBER = 5,
+ JERRY_DEBUGGER_VALUE_STRING = 6,
+ JERRY_DEBUGGER_VALUE_FUNCTION = 7,
+ JERRY_DEBUGGER_VALUE_ARRAY = 8,
+ JERRY_DEBUGGER_VALUE_OBJECT = 9
+} jerry_debugger_scope_variable_type_t;
+
+/**
* Byte data for evaluating expressions and receiving client source.
*/
typedef struct
@@ -365,6 +397,15 @@ typedef struct
} jerry_debugger_send_backtrace_t;
/**
+ * Outgoing message: scope chain.
+ */
+typedef struct
+{
+ uint8_t type; /**< type of the message */
+ uint8_t scope_types[]; /**< scope types */
+} jerry_debugger_send_scope_chain_t;
+
+/**
* Outgoing message: number of total frames in backtrace.
*/
typedef struct
@@ -412,6 +453,15 @@ typedef struct
} jerry_debugger_receive_eval_first_t;
/**
+ * Incoming message: get scope variables
+*/
+typedef struct
+{
+ uint8_t type; /**< type of the message */
+ uint8_t chain_index[sizeof (uint32_t)]; /**< index element of the scope */
+} jerry_debugger_receive_get_scope_variables_t;
+
+/**
* Incoming message: first message of client source.
*/
typedef struct
diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h
index bd78b6cb..7d6aaed2 100644
--- a/jerry-core/ecma/base/ecma-globals.h
+++ b/jerry-core/ecma/base/ecma-globals.h
@@ -701,6 +701,13 @@ typedef enum
#define ECMA_OBJECT_FLAG_EXTENSIBLE 0x20
/**
+ * Non closure flag for debugger.
+ */
+#ifdef JERRY_DEBUGGER
+#define ECMA_OBJECT_FLAG_NON_CLOSURE 0x20
+#endif /* JERRY_DEBUGGER */
+
+/**
* Value for increasing or decreasing the object reference counter.
*/
#define ECMA_OBJECT_REF_ONE (1u << 6)
@@ -719,7 +726,7 @@ typedef struct
/** type : 4 bit : ecma_object_type_t or ecma_lexical_environment_type_t
depending on ECMA_OBJECT_FLAG_BUILT_IN_OR_LEXICAL_ENV
flags : 2 bit : ECMA_OBJECT_FLAG_BUILT_IN_OR_LEXICAL_ENV,
- ECMA_OBJECT_FLAG_EXTENSIBLE
+ ECMA_OBJECT_FLAG_EXTENSIBLE or ECMA_OBJECT_FLAG_NON_CLOSURE
refs : 10 bit (max 1023) */
uint16_t type_flags_refs;
diff --git a/jerry-core/include/jerryscript-debugger.h b/jerry-core/include/jerryscript-debugger.h
index 90594512..26a07496 100644
--- a/jerry-core/include/jerryscript-debugger.h
+++ b/jerry-core/include/jerryscript-debugger.h
@@ -31,7 +31,7 @@ extern "C"
/**
* JerryScript debugger protocol version.
*/
-#define JERRY_DEBUGGER_VERSION (6)
+#define JERRY_DEBUGGER_VERSION (7)
/**
* Types for the client source wait and run method.
diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c
index 7831fdfd..b8567bd4 100644
--- a/jerry-core/vm/vm.c
+++ b/jerry-core/vm/vm.c
@@ -3315,6 +3315,9 @@ error:
}
ecma_object_t *catch_env_p = ecma_create_decl_lex_env (frame_ctx_p->lex_env_p);
+#ifdef JERRY_DEBUGGER
+ catch_env_p->type_flags_refs |= (uint16_t) ECMA_OBJECT_FLAG_NON_CLOSURE;
+#endif /* JERRY_DEBUGGER */
ecma_string_t *catch_name_p = ecma_get_string_from_value (literal_start_p[literal_index]);
ecma_op_create_mutable_binding (catch_env_p, catch_name_p, false);
diff --git a/jerry-debugger/jerry_client.py b/jerry-debugger/jerry_client.py
index 7e34b69c..cc177724 100755
--- a/jerry-debugger/jerry_client.py
+++ b/jerry-debugger/jerry_client.py
@@ -118,6 +118,11 @@ class DebuggerPrompt(Cmd):
self.stop = True
do_bt = do_backtrace
+ def do_variables(self, args):
+ """ Get scope variables from debugger """
+ write(self.debugger.scope_variables(args))
+ self.stop = True
+
def do_src(self, args):
""" Get current source code """
if args:
@@ -172,6 +177,11 @@ class DebuggerPrompt(Cmd):
self.stop = True
do_ms = do_memstats
+ def do_scopes(self, _):
+ """ Memory statistics """
+ self.debugger.scope_chain()
+ self.stop = True
+
def do_abort(self, args):
""" Throw an exception """
self.debugger.abort(args)
diff --git a/jerry-debugger/jerry_client_ws.py b/jerry-debugger/jerry_client_ws.py
index 246e3762..203590b7 100644
--- a/jerry-debugger/jerry_client_ws.py
+++ b/jerry-debugger/jerry_client_ws.py
@@ -24,7 +24,7 @@ import struct
import sys
# Expected debugger protocol version.
-JERRY_DEBUGGER_VERSION = 6
+JERRY_DEBUGGER_VERSION = 7
# Messages sent by the server to client.
JERRY_DEBUGGER_CONFIGURATION = 1
@@ -54,6 +54,10 @@ JERRY_DEBUGGER_EVAL_RESULT_END = 24
JERRY_DEBUGGER_WAIT_FOR_SOURCE = 25
JERRY_DEBUGGER_OUTPUT_RESULT = 26
JERRY_DEBUGGER_OUTPUT_RESULT_END = 27
+JERRY_DEBUGGER_SCOPE_CHAIN = 28
+JERRY_DEBUGGER_SCOPE_CHAIN_END = 29
+JERRY_DEBUGGER_SCOPE_VARIABLES = 30
+JERRY_DEBUGGER_SCOPE_VARIABLES_END = 31
# Debugger option flags
JERRY_DEBUGGER_LITTLE_ENDIAN = 0x1
@@ -94,11 +98,28 @@ JERRY_DEBUGGER_FINISH = 15
JERRY_DEBUGGER_GET_BACKTRACE = 16
JERRY_DEBUGGER_EVAL = 17
JERRY_DEBUGGER_EVAL_PART = 18
+JERRY_DEBUGGER_GET_SCOPE_CHAIN = 19
+JERRY_DEBUGGER_GET_SCOPE_VARIABLES = 20
MAX_BUFFER_SIZE = 128
WEBSOCKET_BINARY_FRAME = 2
WEBSOCKET_FIN_BIT = 0x80
+JERRY_DEBUGGER_SCOPE_WITH = 1
+JERRY_DEBUGGER_SCOPE_LOCAL = 2
+JERRY_DEBUGGER_SCOPE_CLOSURE = 3
+JERRY_DEBUGGER_SCOPE_GLOBAL = 4
+JERRY_DEBUGGER_SCOPE_NON_CLOSURE = 5
+
+JERRY_DEBUGGER_VALUE_NONE = 1
+JERRY_DEBUGGER_VALUE_UNDEFINED = 2
+JERRY_DEBUGGER_VALUE_NULL = 3
+JERRY_DEBUGGER_VALUE_BOOLEAN = 4
+JERRY_DEBUGGER_VALUE_NUMBER = 5
+JERRY_DEBUGGER_VALUE_STRING = 6
+JERRY_DEBUGGER_VALUE_FUNCTION = 7
+JERRY_DEBUGGER_VALUE_ARRAY = 8
+JERRY_DEBUGGER_VALUE_OBJECT = 9
def arguments_parse():
parser = argparse.ArgumentParser(description="JerryScript debugger client")
@@ -264,6 +285,8 @@ class JerryDebugger(object):
self.source_name = ''
self.exception_string = ''
self.frame_index = 0
+ self.scope_vars = ""
+ self.scopes = ""
self.client_sources = []
self.last_breakpoint_hit = None
self.next_breakpoint_index = 0
@@ -394,6 +417,10 @@ class JerryDebugger(object):
self.prompt = False
self._exec_command(JERRY_DEBUGGER_MEMSTATS)
+ def scope_chain(self):
+ self.prompt = False
+ self._exec_command(JERRY_DEBUGGER_GET_SCOPE_CHAIN)
+
def set_break(self, args):
if not args:
return "Error: Breakpoint index expected"
@@ -500,6 +527,28 @@ class JerryDebugger(object):
self.prompt = False
return ""
+ def scope_variables(self, args):
+ index = 0
+ if args:
+ try:
+ index = int(args)
+ if index < 0:
+ print ("Error: A non negative integer number expected")
+ return
+
+ except ValueError as val_errno:
+ return "Error: Non negative integer number expected, %s\n" % (val_errno)
+
+ message = struct.pack(self.byte_order + "BBIB" + self.idx_format,
+ WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT,
+ WEBSOCKET_FIN_BIT + 1 + 4,
+ 0,
+ JERRY_DEBUGGER_GET_SCOPE_VARIABLES,
+ index)
+ self.send_message(message)
+ self.prompt = False
+ return ""
+
def eval(self, code):
self._send_string(JERRY_DEBUGGER_EVAL_EVAL + code, JERRY_DEBUGGER_EVAL)
self.prompt = False
@@ -724,7 +773,6 @@ class JerryDebugger(object):
while True:
data = self.get_message(False)
-
if not self.non_interactive:
if sys.stdin in select.select([sys.stdin], [], [], 0)[0]:
sys.stdin.readline()
@@ -848,6 +896,29 @@ class JerryDebugger(object):
elif buffer_type == JERRY_DEBUGGER_WAIT_FOR_SOURCE:
self.send_client_source()
+
+ elif buffer_type in [JERRY_DEBUGGER_SCOPE_CHAIN, JERRY_DEBUGGER_SCOPE_CHAIN_END]:
+ self.scopes = data[3:]
+
+ if buffer_type == JERRY_DEBUGGER_SCOPE_CHAIN_END:
+ result = self.process_scopes()
+ self.scopes = ""
+
+ self.prompt = True
+
+ return DebuggerAction(DebuggerAction.TEXT, result)
+
+ elif buffer_type in [JERRY_DEBUGGER_SCOPE_VARIABLES, JERRY_DEBUGGER_SCOPE_VARIABLES_END]:
+ self.scope_vars += "".join(data[3:])
+
+ if buffer_type == JERRY_DEBUGGER_SCOPE_VARIABLES_END:
+ result = self.process_scope_variables()
+ self.scope_vars = ""
+
+ self.prompt = True
+
+ return DebuggerAction(DebuggerAction.TEXT, result)
+
else:
raise Exception("Unknown message")
@@ -1203,3 +1274,79 @@ class JerryDebugger(object):
if subtype == JERRY_DEBUGGER_EVAL_ERROR:
return "Uncaught exception: %s" % (message)
return message
+
+ def process_scope_variables(self):
+ buff_size = len(self.scope_vars)
+ buff_pos = 0
+
+ table = [['name', 'type', 'value']]
+
+ while buff_pos != buff_size:
+ # Process name
+ name_length = ord(self.scope_vars[buff_pos:buff_pos + 1])
+ buff_pos += 1
+ name = self.scope_vars[buff_pos:buff_pos + name_length]
+ buff_pos += name_length
+
+ # Process type
+ value_type = ord(self.scope_vars[buff_pos:buff_pos + 1])
+
+ buff_pos += 1
+
+ value_length = ord(self.scope_vars[buff_pos:buff_pos + 1])
+ buff_pos += 1
+ value = self.scope_vars[buff_pos: buff_pos + value_length]
+ buff_pos += value_length
+
+ if value_type == JERRY_DEBUGGER_VALUE_UNDEFINED:
+ table.append([name, 'undefined', value])
+ elif value_type == JERRY_DEBUGGER_VALUE_NULL:
+ table.append([name, 'Null', value])
+ elif value_type == JERRY_DEBUGGER_VALUE_BOOLEAN:
+ table.append([name, 'Boolean', value])
+ elif value_type == JERRY_DEBUGGER_VALUE_NUMBER:
+ table.append([name, 'Number', value])
+ elif value_type == JERRY_DEBUGGER_VALUE_STRING:
+ table.append([name, 'String', value])
+ elif value_type == JERRY_DEBUGGER_VALUE_FUNCTION:
+ table.append([name, 'Function', value])
+ elif value_type == JERRY_DEBUGGER_VALUE_ARRAY:
+ table.append([name, 'Array', '[' + value + ']'])
+ elif value_type == JERRY_DEBUGGER_VALUE_OBJECT:
+ table.append([name, 'Object', value])
+
+ result = self.form_table(table)
+
+ return result
+
+ def process_scopes(self):
+ result = ""
+ table = [['level', 'type']]
+
+ for i, level in enumerate(self.scopes):
+ if ord(level) == JERRY_DEBUGGER_SCOPE_WITH:
+ table.append([str(i), 'with'])
+ elif ord(level) == JERRY_DEBUGGER_SCOPE_GLOBAL:
+ table.append([str(i), 'global'])
+ elif ord(level) == JERRY_DEBUGGER_SCOPE_NON_CLOSURE:
+ # Currently it is only marks the catch closure.
+ table.append([str(i), 'catch'])
+ elif ord(level) == JERRY_DEBUGGER_SCOPE_LOCAL:
+ table.append([str(i), 'local'])
+ elif ord(level) == JERRY_DEBUGGER_SCOPE_CLOSURE:
+ table.append([str(i), 'closure'])
+ else:
+ raise Exception("Unexpected scope chain element")
+
+ result = self.form_table(table)
+
+ return result
+
+ def form_table(self, table):
+ result = ""
+ col_width = [max(len(x) for x in col) for col in zip(*table)]
+ for line in table:
+ result += " | ".join("{:{}}".format(x, col_width[i])
+ for i, x in enumerate(line)) + " \n"
+
+ return result
diff --git a/tests/debugger/do_help.expected b/tests/debugger/do_help.expected
index 0d281948..60fb6543 100644
--- a/tests/debugger/do_help.expected
+++ b/tests/debugger/do_help.expected
@@ -4,9 +4,9 @@ Stopped at tests/debugger/do_help.js:15
Documented commands (type help <topic>):
========================================
-abort bt display exception list next s step
-b c dump f memstats quit scroll throw
-backtrace continue e finish ms res source
-break delete eval help n restart src
+abort bt display exception list next s src
+b c dump f memstats quit scopes step
+backtrace continue e finish ms res scroll throw
+break delete eval help n restart source variables
(jerry-debugger) quit
diff --git a/tests/debugger/do_scopes.cmd b/tests/debugger/do_scopes.cmd
new file mode 100644
index 00000000..69a869c9
--- /dev/null
+++ b/tests/debugger/do_scopes.cmd
@@ -0,0 +1,19 @@
+scopes
+b do_scopes.js:22
+c
+scopes
+c
+scopes
+b do_scopes.js:28
+c
+scopes
+b do_scopes.js:31
+c
+scopes
+b do_scopes.js:33
+c
+scopes
+b do_scopes.js:35
+c
+scopes
+c
diff --git a/tests/debugger/do_scopes.expected b/tests/debugger/do_scopes.expected
new file mode 100644
index 00000000..84e761d3
--- /dev/null
+++ b/tests/debugger/do_scopes.expected
@@ -0,0 +1,63 @@
+Connecting to: localhost:5001
+Stopped at tests/debugger/do_scopes.js:15
+(jerry-debugger) scopes
+level | type
+0 | global
+(jerry-debugger) b do_scopes.js:22
+Breakpoint 1 at tests/debugger/do_scopes.js:22 (in f() at line:17, col:1)
+(jerry-debugger) c
+Exception throw detected (to disable automatic stop type exception 0)
+Exception hint: error
+Stopped at tests/debugger/do_scopes.js:19 (in f() at line:17, col:1)
+(jerry-debugger) scopes
+level | type
+0 | local
+1 | global
+(jerry-debugger) c
+Stopped at breakpoint:1 tests/debugger/do_scopes.js:22 (in f() at line:17, col:1)
+(jerry-debugger) scopes
+level | type
+0 | catch
+1 | local
+2 | global
+(jerry-debugger) b do_scopes.js:28
+Breakpoint 2 at tests/debugger/do_scopes.js:28 (in function() at line:27, col:4)
+(jerry-debugger) c
+Stopped at breakpoint:2 tests/debugger/do_scopes.js:28 (in function() at line:27, col:4)
+(jerry-debugger) scopes
+level | type
+0 | local
+1 | closure
+2 | global
+(jerry-debugger) b do_scopes.js:31
+Breakpoint 3 at tests/debugger/do_scopes.js:31 (in function() at line:27, col:4)
+(jerry-debugger) c
+Stopped at breakpoint:3 tests/debugger/do_scopes.js:31 (in function() at line:27, col:4)
+(jerry-debugger) scopes
+level | type
+0 | with
+1 | local
+2 | closure
+3 | global
+(jerry-debugger) b do_scopes.js:33
+Breakpoint 4 at tests/debugger/do_scopes.js:33 (in function() at line:27, col:4)
+(jerry-debugger) c
+Stopped at breakpoint:4 tests/debugger/do_scopes.js:33 (in function() at line:27, col:4)
+(jerry-debugger) scopes
+level | type
+0 | with
+1 | with
+2 | local
+3 | closure
+4 | global
+(jerry-debugger) b do_scopes.js:35
+Breakpoint 5 at tests/debugger/do_scopes.js:35 (in function() at line:27, col:4)
+(jerry-debugger) c
+Stopped at breakpoint:5 tests/debugger/do_scopes.js:35 (in function() at line:27, col:4)
+(jerry-debugger) scopes
+level | type
+0 | with
+1 | local
+2 | closure
+3 | global
+(jerry-debugger) c
diff --git a/tests/debugger/do_scopes.js b/tests/debugger/do_scopes.js
new file mode 100644
index 00000000..5c14b96f
--- /dev/null
+++ b/tests/debugger/do_scopes.js
@@ -0,0 +1,41 @@
+// 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 c = 4;
+
+function f() {
+ try {
+ throw "error";
+ }
+ catch (err) {
+ var c = 10;
+ }
+
+ var z = true;
+ var g = 0;
+ (function() {
+ var a = [1,2,3]
+ a.y = "abc";
+ with (a) {
+ var h = [4,5,6]
+ with (h) {
+ h.d = "dfg"
+ }
+ a.d = g + c;
+ }
+ })();
+}
+
+f();
+
diff --git a/tests/debugger/do_variables.cmd b/tests/debugger/do_variables.cmd
new file mode 100644
index 00000000..a9b02082
--- /dev/null
+++ b/tests/debugger/do_variables.cmd
@@ -0,0 +1,28 @@
+scopes
+variables
+variables 1
+variables 0
+b tests/debugger/do_variables.js:20
+c
+scopes
+variables 0
+variables 1
+variables 2
+b tests/debugger/do_variables.js:30
+c
+scopes
+variables 1
+variables 0
+b tests/debugger/do_variables.js:33
+c
+c
+scopes
+variables 0
+variables 1
+b tests/debugger/do_variables.js:50
+c
+scopes
+variables 0
+variables 1
+variables 2
+c
diff --git a/tests/debugger/do_variables.expected b/tests/debugger/do_variables.expected
new file mode 100644
index 00000000..a2b8a6c6
--- /dev/null
+++ b/tests/debugger/do_variables.expected
@@ -0,0 +1,128 @@
+Connecting to: localhost:5001
+Stopped at tests/debugger/do_variables.js:15
+(jerry-debugger) scopes
+level | type
+0 | global
+(jerry-debugger) variables
+name | type | value
+f | Function |
+addX | Function |
+z | undefined | undefined
+c | undefined | undefined
+print | Function |
+gc | Function |
+assert | Function |
+(jerry-debugger) variables 1
+name | type | value
+(jerry-debugger) variables 0
+name | type | value
+f | Function |
+addX | Function |
+z | undefined | undefined
+c | undefined | undefined
+print | Function |
+gc | Function |
+assert | Function |
+(jerry-debugger) b tests/debugger/do_variables.js:20
+Breakpoint 1 at tests/debugger/do_variables.js:20 (in function() at line:19, col:10)
+(jerry-debugger) c
+Stopped at breakpoint:1 tests/debugger/do_variables.js:20 (in function() at line:19, col:10)
+(jerry-debugger) scopes
+level | type
+0 | local
+1 | closure
+2 | global
+(jerry-debugger) variables 0
+name | type | value
+n | Number | 9
+b | undefined | undefined
+(jerry-debugger) variables 1
+name | type | value
+x | Number | 3
+(jerry-debugger) variables 2
+name | type | value
+addThree | Function |
+f | Function |
+addX | Function |
+z | Number | 5
+c | Number | 4
+print | Function |
+gc | Function |
+assert | Function |
+(jerry-debugger) b tests/debugger/do_variables.js:30
+Breakpoint 2 at tests/debugger/do_variables.js:30 (in f() at line:28, col:1)
+(jerry-debugger) c
+Stopped at breakpoint:2 tests/debugger/do_variables.js:30 (in f() at line:28, col:1)
+(jerry-debugger) scopes
+level | type
+0 | local
+1 | global
+(jerry-debugger) variables 1
+name | type | value
+d | Number | 12
+addThree | Function |
+f | Function |
+addX | Function |
+z | Number | 5
+c | Number | 4
+print | Function |
+gc | Function |
+assert | Function |
+(jerry-debugger) variables 0
+name | type | value
+b | undefined | undefined
+x | undefined | undefined
+user | undefined | undefined
+m | undefined | undefined
+c | undefined | undefined
+(jerry-debugger) b tests/debugger/do_variables.js:33
+Breakpoint 3 at tests/debugger/do_variables.js:33 (in f() at line:28, col:1)
+(jerry-debugger) c
+Exception throw detected (to disable automatic stop type exception 0)
+Exception hint: error
+Stopped at breakpoint:2 tests/debugger/do_variables.js:30 (in f() at line:28, col:1)
+(jerry-debugger) c
+Stopped at breakpoint:3 tests/debugger/do_variables.js:33 (in f() at line:28, col:1)
+(jerry-debugger) scopes
+level | type
+0 | catch
+1 | local
+2 | global
+(jerry-debugger) variables 0
+name | type | value
+err | String | error
+(jerry-debugger) variables 1
+name | type | value
+b | undefined | undefined
+x | undefined | undefined
+user | undefined | undefined
+m | undefined | undefined
+c | undefined | undefined
+(jerry-debugger) b tests/debugger/do_variables.js:50
+Breakpoint 4 at tests/debugger/do_variables.js:50 (in function() at line:46, col:4)
+(jerry-debugger) c
+Stopped at breakpoint:4 tests/debugger/do_variables.js:50 (in function() at line:46, col:4)
+(jerry-debugger) scopes
+level | type
+0 | with
+1 | local
+2 | closure
+3 | global
+(jerry-debugger) variables 0
+name | type | value
+y | String | abc
+2 | Number | 3
+1 | Number | 2
+0 | Number | 1
+(jerry-debugger) variables 1
+name | type | value
+h | undefined | undefined
+a | Array | [1,2,3]
+(jerry-debugger) variables 2
+name | type | value
+b | Number | 10
+x | Boolean | true
+user | Object | {"name":"John","age":30}
+m | Null | null
+c | Number | 10
+(jerry-debugger) c
diff --git a/tests/debugger/do_variables.js b/tests/debugger/do_variables.js
new file mode 100644
index 00000000..ea769747
--- /dev/null
+++ b/tests/debugger/do_variables.js
@@ -0,0 +1,60 @@
+// 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 c = 4;
+var z = 5;
+
+function addX(x) {
+ return function(n) {
+ var b = 2;
+ return n + x;
+ }
+}
+
+addThree = addX(3);
+d = addThree(c+z);
+
+function f() {
+ try {
+ throw "error";
+ }
+ catch (err) {
+ var c = 10;
+ }
+
+ var m = null;
+
+ var user = {
+ name: "John",
+ age: 30
+ };
+
+ var x = true;
+ var b = 10;
+
+ (function() {
+ var a = [1,2,3]
+ a.y = "abc";
+ with (a) {
+ var h = [4,5,6]
+ with (h) {
+ h.d = "dfg"
+ }
+ a.d = x;
+ }
+ })();
+}
+
+f();
+