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
+144 -18
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 = "";
+98 -6
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")