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:
@@ -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 = "";
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user