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
This commit is contained in:
Robert Sipka
2018-10-29 14:33:31 +01:00
committed by Akos Kiss
parent 7120b8ec02
commit 34c5e229ee
14 changed files with 914 additions and 13 deletions
+348 -3
View File
@@ -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);
/**
@@ -195,6 +196,332 @@ jerry_debugger_send_backtrace (const uint8_t *recv_buffer_p) /**< pointer to the
jerry_debugger_send (sizeof (jerry_debugger_send_type_t) + message_size);
} /* 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.
*
@@ -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);