diff --git a/docs/07.DEBUGGER.md b/docs/07.DEBUGGER.md index 3266388a3..5985710d9 100644 --- a/docs/07.DEBUGGER.md +++ b/docs/07.DEBUGGER.md @@ -26,7 +26,7 @@ can be used for transmitting debugger messages. The debugger client must be connected to the server before the JavaScript application runs. On-the-fly attachment is supported -for one file, right after of engine initialization +for more than one file, right after of engine initialization (this feature available with the python client). The debugging information (e.g. line index of each possible -breakpoint location) is not preserved by JerryScript. The client is expected to be run @@ -208,7 +208,8 @@ jerry_debugger_stop_at_breakpoint (bool enable_stop_at_breakpoint) Stops the engine and puts that into a waiting loop. If the client send a source code and the JerryScript receive that, then the function will -run the source with the initialized options. +run the source with the initialized options, after that the engine will +wait for a new source until the client send a close signal. **Prototype** @@ -222,12 +223,19 @@ jerry_debugger_wait_and_run_client_source (jerry_value_t *return_value) ```c jerry_init (JERRY_INIT_DEBUGGER); - jerry_value_t wait_and_run_value; + jerry_value_t run_result; + jerry_debugger_wait_and_run_type_t receive_status; - if (jerry_debugger_wait_and_run_client_source (&wait_and_run_value) == JERRY_DEBUGGER_SOURCE_RECEIVE_FAILED) + do { - // Handle the fail (e.g. create an error). - } + receive_status = jerry_debugger_wait_and_run_client_source (&run_result); - jerry_release_value (wait_and_run_value); + if (receive_status == JERRY_DEBUGGER_SOURCE_RECEIVE_FAILED) + { + // Handle the fail (e.g. create an error). + } + + jerry_release_value (run_result); + } + while (receive_status == JERRY_DEBUGGER_SOURCE_RECEIVED); ``` diff --git a/jerry-core/api/jerry-debugger.c b/jerry-core/api/jerry-debugger.c index 0f959196c..c8d070887 100644 --- a/jerry-core/api/jerry-debugger.c +++ b/jerry-core/api/jerry-debugger.c @@ -121,7 +121,8 @@ jerry_debugger_cleanup (void) * Sets whether the engine should wait and run a source. * * @return enum JERRY_DEBUGGER_SOURCE_RECEIVE_FAILED - if the source is not received - * JERRY_DEBUGGER_SOURCE_RECEIVED - if the source received + * JERRY_DEBUGGER_SOURCE_RECEIVED - if a source code received + * JERRY_DEBUGGER_SOURCE_END - the end of the source codes */ jerry_debugger_wait_and_run_type_t jerry_debugger_wait_and_run_client_source (jerry_value_t *return_value) /**< [out] parse and run return value */ @@ -136,6 +137,9 @@ jerry_debugger_wait_and_run_client_source (jerry_value_t *return_value) /**< [ou jerry_debugger_uint8_data_t *client_source_data_p = NULL; jerry_debugger_wait_and_run_type_t ret_type = JERRY_DEBUGGER_SOURCE_RECEIVE_FAILED; + /* Notify the client about that the engine is waiting for a source. */ + jerry_debugger_send_type (JERRY_DEBUGGER_WAIT_FOR_SOURCE); + while (true) { if (jerry_debugger_receive (&client_source_data_p)) @@ -145,6 +149,13 @@ jerry_debugger_wait_and_run_client_source (jerry_value_t *return_value) /**< [ou break; } + /* Stop waiting for a new source file. */ + if ((JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CLIENT_NO_SOURCE)) + { + ret_type = JERRY_DEBUGGER_SOURCE_END; + break; + } + /* The source arrived. */ if (!(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CLIENT_SOURCE_MODE)) { diff --git a/jerry-core/debugger/debugger.c b/jerry-core/debugger/debugger.c index ea6a5d266..aad857166 100644 --- a/jerry-core/debugger/debugger.c +++ b/jerry-core/debugger/debugger.c @@ -568,6 +568,25 @@ jerry_debugger_process_message (uint8_t *recv_buffer_p, /**< pointer the the rec return true; } + case JERRY_DEBUGGER_NO_MORE_SOURCES: + { + if (!(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CLIENT_SOURCE_MODE)) + { + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Not in client source mode\n"); + jerry_debugger_close_connection (); + return false; + } + + JERRY_DEBUGGER_CHECK_PACKET_SIZE (jerry_debugger_receive_type_t); + + JERRY_CONTEXT (debugger_flags) = (uint8_t) (JERRY_CONTEXT (debugger_flags) & ~JERRY_DEBUGGER_CLIENT_SOURCE_MODE); + JERRY_CONTEXT (debugger_flags) = (uint8_t) (JERRY_CONTEXT (debugger_flags) | JERRY_DEBUGGER_CLIENT_NO_SOURCE); + + *resume_exec_p = true; + + return true; + } + default: { jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Unexpected message."); diff --git a/jerry-core/debugger/debugger.h b/jerry-core/debugger/debugger.h index d60ad2a0a..74c58d260 100644 --- a/jerry-core/debugger/debugger.h +++ b/jerry-core/debugger/debugger.h @@ -89,6 +89,7 @@ typedef enum JERRY_DEBUGGER_VM_IGNORE = 1u << 3, /**< ignore all breakpoints */ JERRY_DEBUGGER_VM_IGNORE_EXCEPTION = 1u << 4, /**< debugger stop at an exception */ JERRY_DEBUGGER_CLIENT_SOURCE_MODE = 1u << 5, /**< debugger waiting for client code */ + JERRY_DEBUGGER_CLIENT_NO_SOURCE = 1u << 6, /**< debugger leaving the client source loop */ } jerry_debugger_flags_t; /** @@ -119,6 +120,7 @@ typedef enum JERRY_DEBUGGER_BACKTRACE_END = 20, /**< last backtrace data */ JERRY_DEBUGGER_EVAL_RESULT = 21, /**< eval result */ JERRY_DEBUGGER_EVAL_RESULT_END = 22, /**< last part of eval result */ + JERRY_DEBUGGER_WAIT_FOR_SOURCE = 23, /**< engine waiting for a source code */ /* Messages sent by the client to server. */ @@ -130,16 +132,17 @@ typedef enum JERRY_DEBUGGER_STOP = 5, /**< stop execution */ JERRY_DEBUGGER_CLIENT_SOURCE = 6, /**< first message of client source */ JERRY_DEBUGGER_CLIENT_SOURCE_PART = 7, /**< next message of client source */ + JERRY_DEBUGGER_NO_MORE_SOURCES = 8, /**< no more sources notification */ /* The following messages are only available in breakpoint * mode and they switch the engine to run mode. */ - JERRY_DEBUGGER_CONTINUE = 8, /**< continue execution */ - JERRY_DEBUGGER_STEP = 9, /**< next breakpoint, step into functions */ - JERRY_DEBUGGER_NEXT = 10, /**< next breakpoint in the same context */ + JERRY_DEBUGGER_CONTINUE = 9, /**< continue execution */ + JERRY_DEBUGGER_STEP = 10, /**< next breakpoint, step into functions */ + JERRY_DEBUGGER_NEXT = 11, /**< next breakpoint in the same context */ /* The following messages are only available in breakpoint * mode and this mode is kept after the message is processed. */ - JERRY_DEBUGGER_GET_BACKTRACE = 11, /**< get backtrace */ - JERRY_DEBUGGER_EVAL = 12, /**< first message of evaluating a string */ - JERRY_DEBUGGER_EVAL_PART = 13, /**< next message of evaluating a string */ + JERRY_DEBUGGER_GET_BACKTRACE = 12, /**< get backtrace */ + JERRY_DEBUGGER_EVAL = 13, /**< first message of evaluating a string */ + JERRY_DEBUGGER_EVAL_PART = 14, /**< next message of evaluating a string */ } jerry_debugger_header_type_t; /** @@ -308,7 +311,6 @@ typedef struct uint8_t eval_size[sizeof (uint32_t)]; /**< total size of the message */ } jerry_debugger_receive_eval_first_t; - /** * Incoming message: first message of client source. */ diff --git a/jerry-core/include/jerryscript-debugger.h b/jerry-core/include/jerryscript-debugger.h index fab8f538b..cf5ce1870 100644 --- a/jerry-core/include/jerryscript-debugger.h +++ b/jerry-core/include/jerryscript-debugger.h @@ -33,7 +33,8 @@ extern "C" typedef enum { JERRY_DEBUGGER_SOURCE_RECEIVE_FAILED = 0, /**< source is not received */ - JERRY_DEBUGGER_SOURCE_RECEIVED = 1, /**< the source has been received */ + JERRY_DEBUGGER_SOURCE_RECEIVED = 1, /**< a source has been received */ + JERRY_DEBUGGER_SOURCE_END = 2, /**< the end of the sources signal received */ } jerry_debugger_wait_and_run_type_t; /** diff --git a/jerry-debugger/jerry-client-ws.html b/jerry-debugger/jerry-client-ws.html index 1b5484b40..d483f26b8 100644 --- a/jerry-debugger/jerry-client-ws.html +++ b/jerry-debugger/jerry-client-ws.html @@ -58,6 +58,7 @@ var JERRY_DEBUGGER_BACKTRACE = 19; var JERRY_DEBUGGER_BACKTRACE_END = 20; var JERRY_DEBUGGER_EVAL_RESULT = 21; var JERRY_DEBUGGER_EVAL_RESULT_END = 22; +var JERRY_DEBUGGER_WAIT_FOR_SOURCE = 23; // Subtypes of eval var JERRY_DEBUGGER_EVAL_OK = 1; @@ -71,12 +72,13 @@ var JERRY_DEBUGGER_MEMSTATS = 4; var JERRY_DEBUGGER_STOP = 5; var JERRY_DEBUGGER_CLIENT_SOURCE = 6; var JERRY_DEBUGGER_CLIENT_SOURCE_PART = 7; -var JERRY_DEBUGGER_CONTINUE = 8; -var JERRY_DEBUGGER_STEP = 9; -var JERRY_DEBUGGER_NEXT = 10; -var JERRY_DEBUGGER_GET_BACKTRACE = 11; -var JERRY_DEBUGGER_EVAL = 12; -var JERRY_DEBUGGER_EVAL_PART = 13; +var JERRY_DEBUGGER_NO_MORE_SOURCES = 8; +var JERRY_DEBUGGER_CONTINUE = 9; +var JERRY_DEBUGGER_STEP = 10; +var JERRY_DEBUGGER_NEXT = 11; +var JERRY_DEBUGGER_GET_BACKTRACE = 12; +var JERRY_DEBUGGER_EVAL = 13; +var JERRY_DEBUGGER_EVAL_PART = 14; var textBox = document.getElementById("log"); var commandBox = document.getElementById("command"); @@ -921,6 +923,12 @@ function DebuggerClient(address) return; } + case JERRY_DEBUGGER_WAIT_FOR_SOURCE: + { + // This message does not have effect in this client. + return; + } + default: { abortConnection("unexpected message."); diff --git a/jerry-debugger/jerry-client-ws.py b/jerry-debugger/jerry-client-ws.py index 4a6189d9e..65a6fb8ee 100755 --- a/jerry-debugger/jerry-client-ws.py +++ b/jerry-debugger/jerry-client-ws.py @@ -48,6 +48,7 @@ JERRY_DEBUGGER_BACKTRACE = 19 JERRY_DEBUGGER_BACKTRACE_END = 20 JERRY_DEBUGGER_EVAL_RESULT = 21 JERRY_DEBUGGER_EVAL_RESULT_END = 22 +JERRY_DEBUGGER_WAIT_FOR_SOURCE = 23 # Subtypes of eval JERRY_DEBUGGER_EVAL_OK = 1 @@ -62,12 +63,13 @@ JERRY_DEBUGGER_MEMSTATS = 4 JERRY_DEBUGGER_STOP = 5 JERRY_DEBUGGER_CLIENT_SOURCE = 6 JERRY_DEBUGGER_CLIENT_SOURCE_PART = 7 -JERRY_DEBUGGER_CONTINUE = 8 -JERRY_DEBUGGER_STEP = 9 -JERRY_DEBUGGER_NEXT = 10 -JERRY_DEBUGGER_GET_BACKTRACE = 11 -JERRY_DEBUGGER_EVAL = 12 -JERRY_DEBUGGER_EVAL_PART = 13 +JERRY_DEBUGGER_NO_MORE_SOURCES = 8 +JERRY_DEBUGGER_CONTINUE = 9 +JERRY_DEBUGGER_STEP = 10 +JERRY_DEBUGGER_NEXT = 11 +JERRY_DEBUGGER_GET_BACKTRACE = 12 +JERRY_DEBUGGER_EVAL = 13 +JERRY_DEBUGGER_EVAL_PART = 14 MAX_BUFFER_SIZE = 128 WEBSOCKET_BINARY_FRAME = 2 @@ -89,7 +91,7 @@ def arguments_parse(): help="set display range") parser.add_argument("--exception", action="store", default=None, type=int, choices=[0, 1], help="set exception config, usage 1: [Enable] or 0: [Disable]") - parser.add_argument("--client-source", action="store", default=None, type=str, + parser.add_argument("--client-source", action="store", default=[], type=str, nargs="+", help="specify a javascript source file to execute") args = parser.parse_args() @@ -181,6 +183,7 @@ class DebuggerPrompt(Cmd): self.quit = False self.cont = True self.non_interactive = False + self.client_sources = [] def precmd(self, line): self.stop = False @@ -431,16 +434,28 @@ class DebuggerPrompt(Cmd): do_ms = do_memstats - def send_client_source(self, args): - """ Send and execute the specified Javascript source file to the debugger """ - if not args.lower().endswith('.js'): + def store_client_sources(self, args): + self.client_sources = args + + def send_client_source(self): + # Send no more source message if there is no source + if not self.client_sources: + self.send_no_more_source() + return + + path = self.client_sources.pop(0) + if not path.lower().endswith('.js'): sys.exit("Error: Javascript file expected!") return - with open(args, 'r') as f: - content = args + "\0" + f.read() + with open(path, 'r') as f: + content = path + "\0" + f.read() self.send_string(content, JERRY_DEBUGGER_CLIENT_SOURCE) + def send_no_more_source(self): + self.exec_command("", JERRY_DEBUGGER_NO_MORE_SOURCES) + self.cont = True + class Multimap(object): def __init__(self): @@ -963,7 +978,7 @@ def main(): prompt.do_exception(str(args.exception)) if args.client_source is not None: - prompt.send_client_source(str(args.client_source)) + prompt.store_client_sources(args.client_source) while True: if not non_interactive and prompt.cont: @@ -1106,6 +1121,9 @@ def main(): prompt.cmdloop() + elif buffer_type == JERRY_DEBUGGER_WAIT_FOR_SOURCE: + prompt.send_client_source() + else: raise Exception("Unknown message") diff --git a/jerry-main/main-unix.c b/jerry-main/main-unix.c index 2f7499647..5613a3f14 100644 --- a/jerry-main/main-unix.c +++ b/jerry-main/main-unix.c @@ -723,14 +723,28 @@ main (int argc, { is_repl_mode = false; #ifdef JERRY_DEBUGGER - jerry_value_t wait_and_run_value; + jerry_value_t run_result; + jerry_debugger_wait_and_run_type_t receive_status; - if (jerry_debugger_wait_and_run_client_source (&wait_and_run_value) == JERRY_DEBUGGER_SOURCE_RECEIVE_FAILED) + do { - ret_value = jerry_create_error (JERRY_ERROR_COMMON, (jerry_char_t *) "Connection aborted before source arrived."); - } + receive_status = jerry_debugger_wait_and_run_client_source (&run_result); + + if (receive_status == JERRY_DEBUGGER_SOURCE_RECEIVE_FAILED) + { + ret_value = jerry_create_error (JERRY_ERROR_COMMON, + (jerry_char_t *) "Connection aborted before source arrived."); + } + + if (receive_status == JERRY_DEBUGGER_SOURCE_END) + { + jerry_port_log (JERRY_LOG_LEVEL_DEBUG, "No more client source.\n"); + } + + jerry_release_value (run_result); + } + while (receive_status == JERRY_DEBUGGER_SOURCE_RECEIVED); - jerry_release_value (wait_and_run_value); #endif /* JERRY_DEBUGGER */ } diff --git a/tests/debugger/client_source_multiple.cmd b/tests/debugger/client_source_multiple.cmd new file mode 100644 index 000000000..6d14bed18 --- /dev/null +++ b/tests/debugger/client_source_multiple.cmd @@ -0,0 +1,7 @@ +n +n +s +s +s +s +c diff --git a/tests/debugger/client_source_multiple.expected b/tests/debugger/client_source_multiple.expected new file mode 100644 index 000000000..2953f02d6 --- /dev/null +++ b/tests/debugger/client_source_multiple.expected @@ -0,0 +1,15 @@ +Connecting to: localhost:5001 +Stopped at tests/debugger/client_source_multiple_2.js:15 +(jerry-debugger) n +Stopped at tests/debugger/client_source_multiple_1.js:15 +(jerry-debugger) n +Stopped at tests/debugger/client_source_multiple_1.js:27 +(jerry-debugger) s +Stopped at tests/debugger/client_source_multiple_1.js:18 (in foo() at line:17, col:1) +(jerry-debugger) s +Stopped at tests/debugger/client_source_multiple_1.js:19 (in foo() at line:17, col:1) +(jerry-debugger) s +Stopped at tests/debugger/client_source_multiple_2.js:18 (in bar() at line:17, col:1) +(jerry-debugger) s +Stopped at tests/debugger/client_source_multiple_2.js:19 (in bar() at line:17, col:1) +(jerry-debugger) c diff --git a/tests/debugger/client_source_multiple_1.js b/tests/debugger/client_source_multiple_1.js new file mode 100644 index 000000000..f6e5227ca --- /dev/null +++ b/tests/debugger/client_source_multiple_1.js @@ -0,0 +1,27 @@ +// 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. + +print("multiple-client-source-test-file-1"); + +function foo() { + print("foo"); + bar("called-from-test-file-1"); +} + +function crossFoo(str) { + print("crossFoo"); + print("str-argument: " + str); +} + +foo(); diff --git a/tests/debugger/client_source_multiple_2.js b/tests/debugger/client_source_multiple_2.js new file mode 100644 index 000000000..84310465b --- /dev/null +++ b/tests/debugger/client_source_multiple_2.js @@ -0,0 +1,21 @@ +// 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. + +print("multiple-client-source-test-file-2"); + +function bar(str) { + print("bar"); + print("str-argument: " + str); + crossFoo("called-from-test-file-2"); +} diff --git a/tools/run-tests.py b/tools/run-tests.py index 768f35a67..f55f496b6 100755 --- a/tools/run-tests.py +++ b/tools/run-tests.py @@ -218,7 +218,7 @@ def run_jerry_debugger_tests(options): break for test_file in os.listdir(settings.DEBUGGER_TESTS_DIR): - if test_file.endswith(".js"): + if test_file.endswith(".cmd"): test_case, _ = os.path.splitext(test_file) test_case_path = os.path.join(settings.DEBUGGER_TESTS_DIR, test_case) test_cmd = [ diff --git a/tools/runners/run-debugger-test.sh b/tools/runners/run-debugger-test.sh index 3e2518a8a..29c0cc827 100755 --- a/tools/runners/run-debugger-test.sh +++ b/tools/runners/run-debugger-test.sh @@ -21,7 +21,11 @@ CLIENT_ARGS="" if [[ $TEST_CASE == *"client_source"* ]]; then START_DEBUG_SERVER="${JERRY} --start-debug-server --debugger-wait-source &" - CLIENT_ARGS="--client-source ${TEST_CASE}.js" + if [[ $TEST_CASE == *"client_source_multiple"* ]]; then + CLIENT_ARGS="--client-source ${TEST_CASE}_2.js ${TEST_CASE}_1.js" + else + CLIENT_ARGS="--client-source ${TEST_CASE}.js" + fi else START_DEBUG_SERVER="${JERRY} ${TEST_CASE}.js --start-debug-server &" fi