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
+10
View File
@@ -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)
+149 -2
View File
@@ -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