diff --git a/jerry-core/debugger/debugger.c b/jerry-core/debugger/debugger.c index bb40071ce..649f36571 100644 --- a/jerry-core/debugger/debugger.c +++ b/jerry-core/debugger/debugger.c @@ -40,7 +40,7 @@ typedef struct */ JERRY_STATIC_ASSERT (JERRY_DEBUGGER_MESSAGES_OUT_MAX_COUNT == 32 && JERRY_DEBUGGER_MESSAGES_IN_MAX_COUNT == 21 - && JERRY_DEBUGGER_VERSION == 7, + && JERRY_DEBUGGER_VERSION == 8, debugger_version_correlates_to_message_type_count); /** @@ -529,12 +529,17 @@ jerry_debugger_send_eval (const lit_utf8_byte_t *eval_string_p, /**< evaluated s JERRY_ASSERT (!(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_VM_IGNORE)); JERRY_DEBUGGER_SET_FLAGS (JERRY_DEBUGGER_VM_IGNORE); - ecma_value_t result = ecma_op_eval_chars_buffer (eval_string_p + 1, eval_string_size - 1, ECMA_PARSE_DIRECT_EVAL); + + uint32_t chain_index; + memcpy (&chain_index, eval_string_p, sizeof (uint32_t)); + uint32_t parse_opts = ECMA_PARSE_DIRECT_EVAL | (chain_index << ECMA_PARSE_CHAIN_INDEX_SHIFT); + + ecma_value_t result = ecma_op_eval_chars_buffer (eval_string_p + 5, eval_string_size - 5, parse_opts); JERRY_DEBUGGER_CLEAR_FLAGS (JERRY_DEBUGGER_VM_IGNORE); if (!ECMA_IS_VALUE_ERROR (result)) { - if (eval_string_p[0] != JERRY_DEBUGGER_EVAL_EVAL) + if (eval_string_p[4] != JERRY_DEBUGGER_EVAL_EVAL) { JERRY_DEBUGGER_SET_FLAGS (JERRY_DEBUGGER_VM_EXCEPTION_THROWN); JERRY_CONTEXT (error_value) = result; @@ -543,7 +548,7 @@ jerry_debugger_send_eval (const lit_utf8_byte_t *eval_string_p, /**< evaluated s JERRY_DEBUGGER_SET_FLAGS (JERRY_DEBUGGER_VM_STOP); JERRY_CONTEXT (debugger_stop_context) = NULL; - if (eval_string_p[0] == JERRY_DEBUGGER_EVAL_THROW) + if (eval_string_p[4] == JERRY_DEBUGGER_EVAL_THROW) { JERRY_CONTEXT (status_flags) |= ECMA_STATUS_EXCEPTION; } @@ -918,7 +923,7 @@ jerry_debugger_process_message (const uint8_t *recv_buffer_p, /**< pointer to th case JERRY_DEBUGGER_EVAL: { - if (message_size < sizeof (jerry_debugger_receive_eval_first_t) + 1) + if (message_size < sizeof (jerry_debugger_receive_eval_first_t) + 5) { JERRY_ERROR_MSG ("Invalid message size\n"); jerry_debugger_transport_close (); diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index dfe1abf1d..375e7d17d 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -80,10 +80,18 @@ typedef enum ECMA_TYPE___MAX = ECMA_TYPE_ERROR /** highest value for ecma types */ } ecma_type_t; +#ifdef JERRY_DEBUGGER +/** + * Shift for scope chain index part in ecma_parse_opts + */ +#define ECMA_PARSE_CHAIN_INDEX_SHIFT 16 +#endif + /** * Option flags for script parsing. * Note: * The enum members must be kept in sync with parser_general_flags_t + * The last 16 bits are reserved for scope chain index */ typedef enum { diff --git a/jerry-core/include/jerryscript-debugger.h b/jerry-core/include/jerryscript-debugger.h index 26a074961..735d61e16 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 (7) +#define JERRY_DEBUGGER_VERSION (8) /** * Types for the client source wait and run method. diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index b8567bd4f..71a5dfaf6 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -252,6 +252,26 @@ vm_run_eval (ecma_compiled_code_t *bytecode_data_p, /**< byte-code data */ { this_binding = ecma_copy_value (JERRY_CONTEXT (vm_top_context_p)->this_binding); lex_env_p = JERRY_CONTEXT (vm_top_context_p)->lex_env_p; + +#ifdef JERRY_DEBUGGER + uint32_t chain_index = parse_opts >> ECMA_PARSE_CHAIN_INDEX_SHIFT; + + while (chain_index != 0) + { + lex_env_p = ecma_get_lex_env_outer_reference (lex_env_p); + + if (JERRY_UNLIKELY (lex_env_p == NULL)) + { + return ecma_raise_range_error (ECMA_ERR_MSG ("Invalid scope chain index for eval")); + } + + 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--; + } + } +#endif } else { diff --git a/jerry-debugger/jerry_client.py b/jerry-debugger/jerry_client.py index cc1777245..e56ca96c6 100755 --- a/jerry-debugger/jerry_client.py +++ b/jerry-debugger/jerry_client.py @@ -171,6 +171,29 @@ class DebuggerPrompt(Cmd): self.stop = True do_e = do_eval + def do_eval_at(self, args): + """ Evaluate JavaScript source code at a scope chain level """ + + code = '' + index = 0 + try: + args = args.split(" ", 1) + + index = int(args[0]) + + if len(args) == 2: + code = args[1] + + if index < 0 or index > 65535: + raise ValueError("Invalid scope chain index: %d (must be between 0 and 65535)" % index) + + except ValueError as val_errno: + print("Error: %s" % (val_errno)) + return + + self.debugger.eval_at(code, index) + self.stop = True + def do_memstats(self, _): """ Memory statistics """ self.debugger.memstats() diff --git a/jerry-debugger/jerry_client_ws.py b/jerry-debugger/jerry_client_ws.py index 203590b7a..defee5b7d 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 = 7 +JERRY_DEBUGGER_VERSION = 8 # Messages sent by the server to client. JERRY_DEBUGGER_CONFIGURATION = 1 @@ -553,6 +553,10 @@ class JerryDebugger(object): self._send_string(JERRY_DEBUGGER_EVAL_EVAL + code, JERRY_DEBUGGER_EVAL) self.prompt = False + def eval_at(self, code, index): + self._send_string(JERRY_DEBUGGER_EVAL_EVAL + code, JERRY_DEBUGGER_EVAL, index) + self.prompt = False + def throw(self, code): self._send_string(JERRY_DEBUGGER_EVAL_THROW + code, JERRY_DEBUGGER_EVAL) self.prompt = False @@ -587,12 +591,18 @@ class JerryDebugger(object): return "Stop at exception disabled\n" - def _send_string(self, args, message_type): - size = len(args) + def _send_string(self, args, message_type, index=0): # 1: length of type byte # 4: length of an uint32 value message_header = 1 + 4 + + # Add scope chain index + if message_type == JERRY_DEBUGGER_EVAL: + args = struct.pack(self.byte_order + "I", index) + args + + size = len(args) + max_fragment = min(self.max_message_size - message_header, size) message = struct.pack(self.byte_order + "BBIBI", @@ -601,6 +611,7 @@ class JerryDebugger(object): 0, message_type, size) + if size == max_fragment: self.send_message(message + args) return diff --git a/tests/debugger/do_eval_at.cmd b/tests/debugger/do_eval_at.cmd new file mode 100644 index 000000000..85bb5d431 --- /dev/null +++ b/tests/debugger/do_eval_at.cmd @@ -0,0 +1,20 @@ +eval_at 0 +eval_at 0 b +n +eval_at 0 b +b do_eval_at.js:20 +n +scopes +eval_at 0 b +eval_at 1 b +eval_at 0 b=20 +eval_at 1 b=100 +n +eval_at 0 a +scopes +eval_at 0 b +eval_at -1 b +eval_at 65536 b +eval_at b +eval_at 200 +c diff --git a/tests/debugger/do_eval_at.expected b/tests/debugger/do_eval_at.expected new file mode 100644 index 000000000..b0db16fe3 --- /dev/null +++ b/tests/debugger/do_eval_at.expected @@ -0,0 +1,44 @@ +Connecting to: localhost:5001 +Stopped at tests/debugger/do_eval_at.js:15 +(jerry-debugger) eval_at 0 +undefined +(jerry-debugger) eval_at 0 b +undefined +(jerry-debugger) n +Stopped at tests/debugger/do_eval_at.js:23 +(jerry-debugger) eval_at 0 b +2 +(jerry-debugger) b do_eval_at.js:20 +Breakpoint 1 at tests/debugger/do_eval_at.js:20 (in f() at line:17, col:1) +(jerry-debugger) n +Stopped at breakpoint:1 tests/debugger/do_eval_at.js:20 (in f() at line:17, col:1) +(jerry-debugger) scopes +level | type +0 | local +1 | global +(jerry-debugger) eval_at 0 b +6 +(jerry-debugger) eval_at 1 b +2 +(jerry-debugger) eval_at 0 b=20 +20 +(jerry-debugger) eval_at 1 b=100 +100 +(jerry-debugger) n +Stopped at tests/debugger/do_eval_at.js:25 +(jerry-debugger) eval_at 0 a +23 +(jerry-debugger) scopes +level | type +0 | global +(jerry-debugger) eval_at 0 b +100 +(jerry-debugger) eval_at -1 b +Error: Invalid scope chain index: -1 (must be between 0 and 65535) +(jerry-debugger) eval_at 65536 b +Error: Invalid scope chain index: 65536 (must be between 0 and 65535) +(jerry-debugger) eval_at b +Error: invalid literal for int() with base 10: 'b' +(jerry-debugger) eval_at 200 +Uncaught exception: Error +(jerry-debugger) c diff --git a/tests/debugger/do_eval_at.js b/tests/debugger/do_eval_at.js new file mode 100644 index 000000000..80eb998fe --- /dev/null +++ b/tests/debugger/do_eval_at.js @@ -0,0 +1,25 @@ +// 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 b = 2; + +function f(a) +{ + var b = 6; + return a + b; +} + +var a = f(3); + +null; diff --git a/tests/debugger/do_help.expected b/tests/debugger/do_help.expected index 60fb65430..26cde448b 100644 --- a/tests/debugger/do_help.expected +++ b/tests/debugger/do_help.expected @@ -4,9 +4,10 @@ Stopped at tests/debugger/do_help.js:15 Documented commands (type help ): ======================================== -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 +abort c e finish n s step +b continue eval help next scopes throw +backtrace delete eval_at list quit scroll variables +break display exception memstats res source +bt dump f ms restart src (jerry-debugger) quit