diff options
author | Zoltan Herczeg <zherczeg.u-szeged@partner.samsung.com> | 2018-07-12 18:21:08 +0200 |
---|---|---|
committer | yichoi <duddlf.choi@samsung.com> | 2018-07-13 01:21:08 +0900 |
commit | 857ba99694c9a48c34f6e35af69a70e3eed407b1 (patch) | |
tree | 6367cb2e4a0a4f3124090d27e798258b37cc8653 /jerry-core | |
parent | 43aae199ce60e0b8484ecb8f3deb1dc909dbfc12 (diff) |
Rework JerryScript transport layer. (#2421)
Introducing jerryscript-debugger-transport.h interface, which allows
chaining multiple protocols (e.g. tcp and websocket).
JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com
Diffstat (limited to 'jerry-core')
-rw-r--r-- | jerry-core/api/jerry-debugger-transport.c | 273 | ||||
-rw-r--r-- | jerry-core/api/jerry-debugger.c | 14 | ||||
-rw-r--r-- | jerry-core/api/jerry.c | 3 | ||||
-rw-r--r-- | jerry-core/debugger/debugger-tcp.c | 237 | ||||
-rw-r--r-- | jerry-core/debugger/debugger-tcp.h | 27 | ||||
-rw-r--r-- | jerry-core/debugger/debugger-ws.c | 448 | ||||
-rw-r--r-- | jerry-core/debugger/debugger-ws.h | 30 | ||||
-rw-r--r-- | jerry-core/debugger/debugger.c | 143 | ||||
-rw-r--r-- | jerry-core/debugger/debugger.h | 26 | ||||
-rw-r--r-- | jerry-core/include/jerryscript-debugger-transport.h | 107 | ||||
-rw-r--r-- | jerry-core/include/jerryscript-debugger.h | 2 | ||||
-rw-r--r-- | jerry-core/jcontext/jcontext.h | 12 | ||||
-rw-r--r-- | jerry-core/parser/js/js-parser-internal.h | 10 | ||||
-rw-r--r-- | jerry-core/parser/js/js-parser.c | 2 |
14 files changed, 947 insertions, 387 deletions
diff --git a/jerry-core/api/jerry-debugger-transport.c b/jerry-core/api/jerry-debugger-transport.c new file mode 100644 index 00000000..d4ba0a3d --- /dev/null +++ b/jerry-core/api/jerry-debugger-transport.c @@ -0,0 +1,273 @@ +/* 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. + */ + +#include "debugger.h" +#include "jcontext.h" +#include "jerryscript.h" + +#ifdef JERRY_DEBUGGER + +/** + * Minimum number of bytes transmitted or received. + */ +#define JERRY_DEBUGGER_TRANSPORT_MIN_BUFFER_SIZE 64 + +/** + * Sleep time in milliseconds between each jerry_debugger_receive call + */ +#define JERRY_DEBUGGER_TRANSPORT_TIMEOUT 100 + +/** + * Allocate memory for a connection. + * + * @return allocated memory on success + * NULL otherwise + */ +void * +jerry_debugger_transport_malloc (size_t size) /**< size of the memory block */ +{ + return jmem_heap_alloc_block_null_on_error (size); +} /* jerry_debugger_transport_malloc */ + +/** + * Free memory allocated for a connection. + */ +void +jerry_debugger_transport_free (void *mem_p, /**< value returned by jerry_debugger_transport_malloc() */ + size_t size) /**< same size passed to jerry_debugger_transport_malloc() */ +{ + jmem_heap_free_block (mem_p, size); +} /* jerry_debugger_transport_free */ + +/** + * Add a new transport layer. + */ +void +jerry_debugger_transport_add (jerry_debugger_transport_header_t *header_p, /**< transport implementation */ + size_t send_message_header_size, /**< header bytes reserved for outgoing messages */ + size_t max_send_message_size, /**< maximum number of bytes transmitted in a message */ + size_t receive_message_header_size, /**< header bytes reserved for incoming messages */ + size_t max_receive_message_size) /**< maximum number of bytes received in a message */ +{ + JERRY_ASSERT (max_send_message_size > JERRY_DEBUGGER_TRANSPORT_MIN_BUFFER_SIZE + && max_receive_message_size > JERRY_DEBUGGER_TRANSPORT_MIN_BUFFER_SIZE); + + header_p->next_p = JERRY_CONTEXT (debugger_transport_header_p); + JERRY_CONTEXT (debugger_transport_header_p) = header_p; + + uint8_t *payload_p; + size_t max_send_size; + size_t max_receive_size; + + if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) + { + payload_p = JERRY_CONTEXT (debugger_send_buffer_payload_p); + max_send_size = JERRY_CONTEXT (debugger_max_send_size); + max_receive_size = JERRY_CONTEXT (debugger_max_receive_size); + } + else + { + JERRY_DEBUGGER_SET_FLAGS (JERRY_DEBUGGER_CONNECTED); + payload_p = JERRY_CONTEXT (debugger_send_buffer); + max_send_size = JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE; + max_receive_size = JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE; + } + + JERRY_ASSERT (max_send_size > JERRY_DEBUGGER_TRANSPORT_MIN_BUFFER_SIZE + send_message_header_size); + JERRY_ASSERT (max_receive_size > JERRY_DEBUGGER_TRANSPORT_MIN_BUFFER_SIZE + receive_message_header_size); + + JERRY_CONTEXT (debugger_send_buffer_payload_p) = payload_p + send_message_header_size; + + max_send_size = max_send_size - send_message_header_size; + max_receive_size = max_receive_size - receive_message_header_size; + + if (max_send_size > max_send_message_size) + { + max_send_size = max_send_message_size; + } + + if (max_receive_size > max_receive_message_size) + { + max_receive_size = max_receive_message_size; + } + + JERRY_CONTEXT (debugger_max_send_size) = (uint8_t) max_send_size; + JERRY_CONTEXT (debugger_max_receive_size) = (uint8_t) max_receive_size; +} /* jerry_debugger_transport_add */ + +/** + * Starts the communication to the debugger client. + * Must be called after the connection is successfully established. + */ +void +jerry_debugger_transport_start (void) +{ + JERRY_ASSERT (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED); + + if (jerry_debugger_send_configuration (JERRY_CONTEXT (debugger_max_receive_size))) + { + JERRY_DEBUGGER_SET_FLAGS (JERRY_DEBUGGER_VM_STOP); + JERRY_CONTEXT (debugger_stop_context) = NULL; + } +} /* jerry_debugger_transport_start */ + +/** + * Returns true if a debugger client is connected. + * + * @return true - a debugger client is connected, + * false - otherwise + */ +bool +jerry_debugger_transport_is_connected (void) +{ + return (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) != 0; +} /* jerry_debugger_transport_is_connected */ + +/** + * Notifies the debugger server that the connection is closed. + */ +void +jerry_debugger_transport_close (void) +{ + if (!(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)) + { + return; + } + + jerry_debugger_transport_header_t *current_p = JERRY_CONTEXT (debugger_transport_header_p); + + JERRY_ASSERT (current_p != NULL); + + do + { + jerry_debugger_transport_header_t *next_p = current_p->next_p; + + current_p->close (current_p); + + current_p = next_p; + } + while (current_p != NULL); + + JERRY_CONTEXT (debugger_flags) = JERRY_DEBUGGER_VM_IGNORE; + + jerry_port_log (JERRY_LOG_LEVEL_DEBUG, "Debugger client connection closed.\n"); + + jerry_debugger_free_unreferenced_byte_code (); +} /* jerry_debugger_transport_close */ + +/** + * Send data over the current connection + * + * @return true - data sent successfully, + * false - connection closed + */ +bool +jerry_debugger_transport_send (const uint8_t *message_p, /**< message to be sent */ + size_t message_length) /**< message length in bytes */ +{ + JERRY_ASSERT (jerry_debugger_transport_is_connected ()); + JERRY_ASSERT (message_length > 0); + + jerry_debugger_transport_header_t *header_p = JERRY_CONTEXT (debugger_transport_header_p); + uint8_t *payload_p = JERRY_CONTEXT (debugger_send_buffer_payload_p); + size_t max_send_size = JERRY_CONTEXT (debugger_max_send_size); + + do + { + size_t fragment_length = (message_length <= max_send_size ? message_length + : max_send_size); + + memcpy (payload_p, message_p, fragment_length); + + if (!header_p->send (header_p, payload_p, fragment_length)) + { + return false; + } + + message_p += fragment_length; + message_length -= fragment_length; + } + while (message_length > 0); + + return true; +} /* jerry_debugger_transport_send */ + +/** + * Receive data from the current connection + * + * Note: + * A message is received if message_start_p is not NULL + * + * @return true - function successfully completed, + * false - connection closed + */ +bool +jerry_debugger_transport_receive (jerry_debugger_transport_receive_context_t *context_p) /**< [out] receive + * context */ +{ + JERRY_ASSERT (jerry_debugger_transport_is_connected ()); + + context_p->buffer_p = JERRY_CONTEXT (debugger_receive_buffer); + context_p->received_length = JERRY_CONTEXT (debugger_received_length); + context_p->message_p = NULL; + context_p->message_length = 0; + context_p->message_total_length = 0; + + jerry_debugger_transport_header_t *header_p = JERRY_CONTEXT (debugger_transport_header_p); + + return header_p->receive (header_p, context_p); +} /* jerry_debugger_transport_receive */ + +/** + * Clear the message buffer after the message is processed + */ +void +jerry_debugger_transport_receive_completed (jerry_debugger_transport_receive_context_t *context_p) /**< receive + * context */ +{ + JERRY_ASSERT (context_p->message_p != NULL); + JERRY_ASSERT (context_p->buffer_p == JERRY_CONTEXT (debugger_receive_buffer)); + + size_t message_total_length = context_p->message_total_length; + size_t received_length = context_p->received_length; + + JERRY_ASSERT (message_total_length <= received_length); + + if (message_total_length == 0 || message_total_length == received_length) + { + /* All received data is processed. */ + JERRY_CONTEXT (debugger_received_length) = 0; + return; + } + + uint8_t *buffer_p = context_p->buffer_p; + received_length -= message_total_length; + + memmove (buffer_p, buffer_p + message_total_length, received_length); + + JERRY_CONTEXT (debugger_received_length) = (uint16_t) received_length; +} /* jerry_debugger_transport_receive_completed */ + +/** + * Suspend execution for a given time. + * Note: If the platform does not have nanosleep or usleep, this function does not sleep at all. + */ +void +jerry_debugger_transport_sleep (void) +{ + jerry_port_sleep (JERRY_DEBUGGER_TRANSPORT_TIMEOUT); +} /* jerry_debugger_transport_sleep */ + +#endif /* JERRY_DEBUGGER */ diff --git a/jerry-core/api/jerry-debugger.c b/jerry-core/api/jerry-debugger.c index 717ad840..22bca84d 100644 --- a/jerry-core/api/jerry-debugger.c +++ b/jerry-core/api/jerry-debugger.c @@ -16,6 +16,8 @@ #include "debugger.h" #include "jcontext.h" #include "jerryscript.h" +#include "debugger-tcp.h" +#include "debugger-ws.h" /** * Checks whether the debugger is connected. @@ -96,8 +98,14 @@ void jerry_debugger_init (uint16_t port) /**< server port number */ { #ifdef JERRY_DEBUGGER - JERRY_CONTEXT (debugger_port) = port; - jerry_debugger_accept_connection (); + if (!jerry_debugger_tcp_create (port) + || !jerry_debugger_ws_create ()) + { + jerry_debugger_transport_close (); + return; + } + + jerry_debugger_transport_start (); #else /* !JERRY_DEBUGGER */ JERRY_UNUSED (port); #endif /* JERRY_DEBUGGER */ @@ -173,7 +181,7 @@ jerry_debugger_wait_for_client_source (jerry_debugger_wait_for_source_callback_t } } - jerry_debugger_sleep (); + jerry_debugger_transport_sleep (); } JERRY_ASSERT (!(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CLIENT_SOURCE_MODE) diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index 622705fb..6a8f9166 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -35,6 +35,7 @@ #include "ecma-typedarray-object.h" #include "jcontext.h" #include "jerryscript.h" +#include "jerryscript-debugger-transport.h" #include "jmem.h" #include "js-parser.h" #include "re-compiler.h" @@ -199,7 +200,7 @@ jerry_cleanup (void) #ifdef JERRY_DEBUGGER if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) { - jerry_debugger_close_connection (); + jerry_debugger_transport_close (); } #endif /* JERRY_DEBUGGER */ diff --git a/jerry-core/debugger/debugger-tcp.c b/jerry-core/debugger/debugger-tcp.c new file mode 100644 index 00000000..7b80a530 --- /dev/null +++ b/jerry-core/debugger/debugger-tcp.c @@ -0,0 +1,237 @@ +/* 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. + */ + +#include "debugger-tcp.h" +#include "jrt.h" + +#ifdef JERRY_DEBUGGER + +#include <arpa/inet.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> + +/** + * Implementation of transport over tcp/ip. + */ +typedef struct +{ + jerry_debugger_transport_header_t header; /**< transport header */ + int tcp_socket; /**< tcp socket */ +} jerry_debugger_transport_tcp_t; + +/** + * Log tcp error message. + */ +static void +jerry_debugger_tcp_log_error (void) +{ + JERRY_ERROR_MSG ("Error: %s\n", strerror (errno)); +} /* jerry_debugger_tcp_log_error */ + +/** + * Close a tcp connection. + */ +static void +jerry_debugger_tcp_close (jerry_debugger_transport_header_t *header_p) /**< tcp implementation */ +{ + JERRY_ASSERT (jerry_debugger_transport_is_connected ()); + + jerry_debugger_transport_tcp_t *tcp_p = (jerry_debugger_transport_tcp_t *) header_p; + + JERRY_DEBUG_MSG ("TCP connection closed.\n"); + + close (tcp_p->tcp_socket); + + jerry_debugger_transport_free ((void *) header_p, sizeof (jerry_debugger_transport_tcp_t)); +} /* jerry_debugger_tcp_close */ + +/** + * Send data over a tcp connection. + * + * @return true - if the data has been sent successfully + * false - otherwise + */ +static bool +jerry_debugger_tcp_send (jerry_debugger_transport_header_t *header_p, /**< tcp implementation */ + uint8_t *message_p, /**< message to be sent */ + size_t message_length) /**< message length in bytes */ +{ + JERRY_ASSERT (jerry_debugger_transport_is_connected ()); + + jerry_debugger_transport_tcp_t *tcp_p = (jerry_debugger_transport_tcp_t *) header_p; + + do + { + ssize_t sent_bytes = send (tcp_p->tcp_socket, message_p, message_length, 0); + + if (sent_bytes < 0) + { + if (errno == EWOULDBLOCK) + { + continue; + } + + jerry_debugger_tcp_log_error (); + jerry_debugger_transport_close (); + return false; + } + + message_p += sent_bytes; + message_length -= (size_t) sent_bytes; + } + while (message_length > 0); + + return true; +} /* jerry_debugger_tcp_send */ + +/** + * Receive data from a tcp connection. + */ +static bool +jerry_debugger_tcp_receive (jerry_debugger_transport_header_t *header_p, /**< tcp implementation */ + jerry_debugger_transport_receive_context_t *receive_context_p) /**< receive context */ +{ + jerry_debugger_transport_tcp_t *tcp_p = (jerry_debugger_transport_tcp_t *) header_p; + + uint8_t *buffer_p = receive_context_p->buffer_p + receive_context_p->received_length; + size_t buffer_size = JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE - receive_context_p->received_length; + + ssize_t length = recv (tcp_p->tcp_socket, buffer_p, buffer_size, 0); + + if (length < 0) + { + if (errno != EWOULDBLOCK) + { + jerry_debugger_tcp_log_error (); + jerry_debugger_transport_close (); + return false; + } + length = 0; + } + + receive_context_p->received_length += (size_t) length; + + if (receive_context_p->received_length > 0) + { + receive_context_p->message_p = receive_context_p->buffer_p; + receive_context_p->message_length = receive_context_p->received_length; + } + + return true; +} /* jerry_debugger_tcp_receive */ + +/** + * Create a tcp connection. + * + * @return true if successful, + * false otherwise + */ +bool +jerry_debugger_tcp_create (uint16_t port) /**< listening port */ +{ + int server_socket; + struct sockaddr_in addr; + socklen_t sin_size = sizeof (struct sockaddr_in); + + addr.sin_family = AF_INET; + addr.sin_port = htons (port); + addr.sin_addr.s_addr = INADDR_ANY; + + if ((server_socket = socket (AF_INET, SOCK_STREAM, 0)) == -1) + { + JERRY_ERROR_MSG ("Error: %s\n", strerror (errno)); + return false; + } + + int opt_value = 1; + + if (setsockopt (server_socket, SOL_SOCKET, SO_REUSEADDR, &opt_value, sizeof (int)) == -1) + { + close (server_socket); + JERRY_ERROR_MSG ("Error: %s\n", strerror (errno)); + return false; + } + + if (bind (server_socket, (struct sockaddr *)&addr, sizeof (struct sockaddr)) == -1) + { + close (server_socket); + JERRY_ERROR_MSG ("Error: %s\n", strerror (errno)); + return false; + } + + if (listen (server_socket, 1) == -1) + { + close (server_socket); + JERRY_ERROR_MSG ("Error: %s\n", strerror (errno)); + return false; + } + + JERRY_DEBUG_MSG ("Waiting for client connection\n"); + + int tcp_socket = accept (server_socket, (struct sockaddr *)&addr, &sin_size); + + close (server_socket); + + if (tcp_socket == -1) + { + JERRY_ERROR_MSG ("Error: %s\n", strerror (errno)); + return false; + } + + /* Set non-blocking mode. */ + int socket_flags = fcntl (tcp_socket, F_GETFL, 0); + + if (socket_flags < 0) + { + close (tcp_socket); + return false; + } + + if (fcntl (tcp_socket, F_SETFL, socket_flags | O_NONBLOCK) == -1) + { + close (tcp_socket); + return false; + } + + JERRY_DEBUG_MSG ("Connected from: %s\n", inet_ntoa (addr.sin_addr)); + + size_t size = sizeof (jerry_debugger_transport_tcp_t); + + jerry_debugger_transport_header_t *header_p; + header_p = (jerry_debugger_transport_header_t *) jerry_debugger_transport_malloc (size); + + if (!header_p) + { + close (tcp_socket); + return false; + } + + header_p->close = jerry_debugger_tcp_close; + header_p->send = jerry_debugger_tcp_send; + header_p->receive = jerry_debugger_tcp_receive; + + ((jerry_debugger_transport_tcp_t *) header_p)->tcp_socket = tcp_socket; + + jerry_debugger_transport_add (header_p, + 0, + JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE, + 0, + JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE); + + return true; +} /* jerry_debugger_tcp_create */ + +#endif /* JERRY_DEBUGGER */ diff --git a/jerry-core/debugger/debugger-tcp.h b/jerry-core/debugger/debugger-tcp.h new file mode 100644 index 00000000..a6d11aa8 --- /dev/null +++ b/jerry-core/debugger/debugger-tcp.h @@ -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. + */ + +#ifndef DEBUGGER_TCP_H +#define DEBUGGER_TCP_H + +#include "jerryscript-debugger-transport.h" + +#ifdef JERRY_DEBUGGER + +bool jerry_debugger_tcp_create (uint16_t port); + +#endif /* JERRY_DEBUGGER */ + +#endif /* !DEBUGGER_TCP_H */ diff --git a/jerry-core/debugger/debugger-ws.c b/jerry-core/debugger/debugger-ws.c index 0f12a693..d980b9b7 100644 --- a/jerry-core/debugger/debugger-ws.c +++ b/jerry-core/debugger/debugger-ws.c @@ -13,17 +13,11 @@ * limitations under the License. */ -#include "debugger.h" -#include "jcontext.h" -#include "jerryscript-port.h" +#include "debugger-ws.h" +#include "jrt.h" #ifdef JERRY_DEBUGGER -#include <arpa/inet.h> -#include <errno.h> -#include <fcntl.h> -#include <unistd.h> - /* JerryScript debugger protocol is a simplified version of RFC-6455 (WebSockets). */ /** @@ -62,12 +56,6 @@ #define JERRY_DEBUGGER_WEBSOCKET_ONE_BYTE_LEN_MAX 125 /** - * Waiting for data from the client. - */ -#define JERRY_DEBUGGER_RECEIVE_DATA_MODE \ - (JERRY_DEBUGGER_BREAKPOINT_MODE | JERRY_DEBUGGER_CLIENT_SOURCE_MODE) - -/** * WebSocket opcode types. */ typedef enum @@ -87,65 +75,7 @@ typedef struct uint8_t ws_opcode; /**< websocket opcode */ uint8_t size; /**< size of the message */ uint8_t mask[4]; /**< mask bytes */ -} jerry_debugger_receive_header_t; - -/** - * Close the socket connection to the client. - */ -static void -jerry_debugger_close_connection_tcp (bool log_error) /**< log error */ -{ - JERRY_ASSERT (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED); - - JERRY_CONTEXT (debugger_flags) = JERRY_DEBUGGER_VM_IGNORE; - - if (log_error) - { - JERRY_ERROR_MSG ("Error: %s\n", strerror (errno)); - } - - JERRY_DEBUG_MSG ("Debugger client connection closed.\n"); - - close (JERRY_CONTEXT (debugger_connection)); - JERRY_CONTEXT (debugger_connection) = -1; - - jerry_debugger_free_unreferenced_byte_code (); -} /* jerry_debugger_close_connection_tcp */ - -/** - * Send message to the client side. - * - * @return true - if the data was sent successfully to the client side - * false - otherwise - */ -static bool -jerry_debugger_send_tcp (const uint8_t *data_p, /**< data pointer */ - size_t data_size) /**< data size */ -{ - JERRY_ASSERT (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED); - - do - { - ssize_t sent_bytes = send (JERRY_CONTEXT (debugger_connection), data_p, data_size, 0); - - if (sent_bytes < 0) - { - if (errno == EWOULDBLOCK) - { - continue; - } - - jerry_debugger_close_connection_tcp (true); - return false; - } - - data_size -= (size_t) sent_bytes; - data_p += sent_bytes; - } - while (data_size > 0); - - return true; -} /* jerry_debugger_send_tcp */ +} jerry_websocket_receive_header_t; /** * Convert a 6-bit value to a Base64 character. @@ -213,8 +143,7 @@ jerry_to_base64 (const uint8_t *source_p, /**< source data */ * false - otherwise */ static bool -jerry_process_handshake (int client_socket, /**< client socket */ - uint8_t *request_buffer_p) /**< temporary buffer */ +jerry_process_handshake (uint8_t *request_buffer_p) /**< temporary buffer */ { size_t request_buffer_size = 1024; uint8_t *request_end_p = request_buffer_p; @@ -222,23 +151,33 @@ jerry_process_handshake (int client_socket, /**< client socket */ /* Buffer request text until the double newlines are received. */ while (true) { - size_t length = request_buffer_size - 1u - (size_t) (request_end_p - request_buffer_p); - - if (length == 0) + jerry_debugger_transport_receive_context_t context; + if (!jerry_debugger_transport_receive (&context)) { - JERRY_ERROR_MSG ("Handshake buffer too small.\n"); + JERRY_ASSERT (!jerry_debugger_transport_is_connected ()); return false; } - ssize_t size = recv (client_socket, request_end_p, length, 0); + if (context.message_p == NULL) + { + jerry_debugger_transport_sleep (); + continue; + } + + size_t length = request_buffer_size - 1u - (size_t) (request_end_p - request_buffer_p); - if (size < 0) + if (length < context.message_length) { - JERRY_ERROR_MSG ("Error: %s\n", strerror (errno)); + JERRY_ERROR_MSG ("Handshake buffer too small.\n"); return false; } - request_end_p += (size_t) size; + /* Both stream and datagram packets are supported. */ + memcpy (request_end_p, context.message_p, context.message_length); + + jerry_debugger_transport_receive_completed (&context); + + request_end_p += (size_t) context.message_length; *request_end_p = 0; if (request_end_p > request_buffer_p + 4 @@ -319,293 +258,194 @@ jerry_process_handshake (int client_socket, /**< client socket */ text_p = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "; - if (!jerry_debugger_send_tcp ((const uint8_t *) text_p, strlen (text_p)) - || !jerry_debugger_send_tcp (request_buffer_p + sha1_length + 1, 27)) + if (!jerry_debugger_transport_send ((const uint8_t *) text_p, strlen (text_p)) + || !jerry_debugger_transport_send (request_buffer_p + sha1_length + 1, 27)) { return false; } text_p = "=\r\n\r\n"; - return jerry_debugger_send_tcp ((const uint8_t *) text_p, strlen (text_p)); + return jerry_debugger_transport_send ((const uint8_t *) text_p, strlen (text_p)); } /* jerry_process_handshake */ /** - * Initialize the socket connection. - * - * @return true - if the connection succeeded - * false - otherwise + * Close a tcp connection. */ -bool -jerry_debugger_accept_connection (void) +static void +jerry_debugger_ws_close (jerry_debugger_transport_header_t *header_p) /**< tcp implementation */ { - int server_socket; - struct sockaddr_in addr; - socklen_t sin_size = sizeof (struct sockaddr_in); + JERRY_ASSERT (jerry_debugger_transport_is_connected ()); - uint8_t *payload_p = JERRY_CONTEXT (debugger_send_buffer) + JERRY_DEBUGGER_WEBSOCKET_HEADER_SIZE; - JERRY_CONTEXT (debugger_send_buffer_payload_p) = payload_p; + jerry_debugger_transport_free ((void *) header_p, sizeof (jerry_debugger_transport_header_t)); +} /* jerry_debugger_ws_close */ - uint8_t max_send_size = (JERRY_DEBUGGER_MAX_BUFFER_SIZE - JERRY_DEBUGGER_WEBSOCKET_HEADER_SIZE); - if (max_send_size > JERRY_DEBUGGER_WEBSOCKET_ONE_BYTE_LEN_MAX) - { - max_send_size = JERRY_DEBUGGER_WEBSOCKET_ONE_BYTE_LEN_MAX; - } - JERRY_CONTEXT (debugger_max_send_size) = max_send_size; - - uint8_t receive_header_size = (JERRY_DEBUGGER_WEBSOCKET_HEADER_SIZE + JERRY_DEBUGGER_WEBSOCKET_MASK_SIZE); - uint8_t max_receive_size = (uint8_t) (JERRY_DEBUGGER_MAX_BUFFER_SIZE - receive_header_size); +/** + * Send data over a websocket connection. + * + * @return true - if the data has been sent successfully + * false - otherwise + */ +static bool +jerry_debugger_ws_send (jerry_debugger_transport_header_t *header_p, /**< tcp implementation */ + uint8_t *message_p, /**< message to be sent */ + size_t message_length) /**< message length in bytes */ +{ + JERRY_ASSERT (message_length <= JERRY_DEBUGGER_WEBSOCKET_ONE_BYTE_LEN_MAX); - if (max_receive_size > JERRY_DEBUGGER_WEBSOCKET_ONE_BYTE_LEN_MAX) - { - max_receive_size = JERRY_DEBUGGER_WEBSOCKET_ONE_BYTE_LEN_MAX; - } - JERRY_CONTEXT (debugger_max_receive_size) = max_receive_size; + message_p[-2] = JERRY_DEBUGGER_WEBSOCKET_FIN_BIT | JERRY_DEBUGGER_WEBSOCKET_BINARY_FRAME; + message_p[-1] = (uint8_t) message_length; - addr.sin_family = AF_INET; - addr.sin_port = htons (JERRY_CONTEXT (debugger_port)); - addr.sin_addr.s_addr = INADDR_ANY; + return header_p->next_p->send (header_p->next_p, message_p - 2, message_length + 2); +} /* jerry_debugger_ws_send */ - if ((server_socket = socket (AF_INET, SOCK_STREAM, 0)) == -1) +/** + * Receive data from a websocket connection. + */ +static bool +jerry_debugger_ws_receive (jerry_debugger_transport_header_t *header_p, /**< tcp implementation */ + jerry_debugger_transport_receive_context_t *receive_context_p) /**< receive context */ +{ + if (!header_p->next_p->receive (header_p->next_p, receive_context_p)) { - JERRY_ERROR_MSG ("Error: %s\n", strerror (errno)); return false; } - int opt_value = 1; - - if (setsockopt (server_socket, SOL_SOCKET, SO_REUSEADDR, &opt_value, sizeof (int)) == -1) + if (receive_context_p->message_p == NULL) { - close (server_socket); - JERRY_ERROR_MSG ("Error: %s\n", strerror (errno)); - return false; + return true; } - if (bind (server_socket, (struct sockaddr *)&addr, sizeof (struct sockaddr)) == -1) - { - close (server_socket); - JERRY_ERROR_MSG ("Error: %s\n", strerror (errno)); - return false; - } + size_t message_total_length = receive_context_p->message_total_length; - if (listen (server_socket, 1) == -1) + if (message_total_length == 0) { - close (server_socket); - JERRY_ERROR_MSG ("Error: %s\n", strerror (errno)); - return false; + /* Byte stream. */ + if (receive_context_p->message_length < sizeof (jerry_websocket_receive_header_t)) + { + receive_context_p->message_p = NULL; + return true; + } } - - JERRY_DEBUG_MSG ("Waiting for client connection\n"); - - JERRY_CONTEXT (debugger_connection) = accept (server_socket, (struct sockaddr *)&addr, &sin_size); - - if (JERRY_CONTEXT (debugger_connection) == -1) + else { - close (server_socket); - JERRY_ERROR_MSG ("Error: %s\n", strerror (errno)); - return false; + /* Datagram packet. */ + JERRY_ASSERT (receive_context_p->message_length >= sizeof (jerry_websocket_receive_header_t)); } - close (server_socket); - - JERRY_DEBUGGER_SET_FLAGS (JERRY_DEBUGGER_CONNECTED); - - bool is_handshake_ok = false; - - JMEM_DEFINE_LOCAL_ARRAY (request_buffer_p, 1024, uint8_t); + uint8_t *message_p = receive_context_p->message_p; - is_handshake_ok = jerry_process_handshake (JERRY_CONTEXT (debugger_connection), - request_buffer_p); - - JMEM_FINALIZE_LOCAL_ARRAY (request_buffer_p); - - if (!is_handshake_ok) + if ((message_p[0] & ~JERRY_DEBUGGER_WEBSOCKET_OPCODE_MASK) != JERRY_DEBUGGER_WEBSOCKET_FIN_BIT + || (message_p[1] & JERRY_DEBUGGER_WEBSOCKET_LENGTH_MASK) > JERRY_DEBUGGER_WEBSOCKET_ONE_BYTE_LEN_MAX + || !(message_p[1] & JERRY_DEBUGGER_WEBSOCKET_MASK_BIT)) { - jerry_debugger_close_connection (); + JERRY_ERROR_MSG ("Unsupported Websocket message.\n"); + jerry_debugger_transport_close (); return false; } - if (!jerry_debugger_send_configuration (max_receive_size)) + if ((message_p[0] & JERRY_DEBUGGER_WEBSOCKET_OPCODE_MASK) != JERRY_DEBUGGER_WEBSOCKET_BINARY_FRAME) { + JERRY_ERROR_MSG ("Unsupported Websocket opcode.\n"); + jerry_debugger_transport_close (); return false; } - /* Set non-blocking mode. */ - int socket_flags = fcntl (JERRY_CONTEXT (debugger_connection), F_GETFL, 0); + size_t message_length = (size_t) (message_p[1] & JERRY_DEBUGGER_WEBSOCKET_LENGTH_MASK); - if (socket_flags < 0) + if (message_total_length == 0) { - jerry_debugger_close_connection_tcp (true); - return false; - } + size_t new_total_length = message_length + sizeof (jerry_websocket_receive_header_t); - if (fcntl (JERRY_CONTEXT (debugger_connection), F_SETFL, socket_flags | O_NONBLOCK) == -1) + /* Byte stream. */ + if (receive_context_p->message_length < new_total_length) + { + receive_context_p->message_p = NULL; + return true; + } + + receive_context_p->message_total_length = new_total_length; + } + else { - jerry_debugger_close_connection_tcp (true); - return false; + /* Datagram packet. */ + JERRY_ASSERT (receive_context_p->message_length == (message_length + sizeof (jerry_websocket_receive_header_t))); } - JERRY_DEBUG_MSG ("Connected from: %s\n", inet_ntoa (addr.sin_addr)); + message_p += sizeof (jerry_websocket_receive_header_t); - JERRY_DEBUGGER_SET_FLAGS (JERRY_DEBUGGER_VM_STOP); - JERRY_CONTEXT (debugger_stop_context) = NULL; + receive_context_p->message_p = message_p; + receive_context_p->message_length = message_length; - return true; -} /* jerry_debugger_accept_connection */ + /* Unmask data bytes. */ + const uint8_t *mask_p = message_p - JERRY_DEBUGGER_WEBSOCKET_MASK_SIZE; + const uint8_t *mask_end_p = message_p; + const uint8_t *message_end_p = message_p + message_length; -/** - * Close the socket connection to the client. - */ -inline void JERRY_ATTR_ALWAYS_INLINE -jerry_debugger_close_connection (void) -{ - jerry_debugger_close_connection_tcp (false); -} /* jerry_debugger_close_connection */ + while (message_p < message_end_p) + { + /* Invert certain bits with xor operation. */ + *message_p = *message_p ^ *mask_p; -/** - * Send message to the client side - * - * @return true - if the data was sent successfully to the debugger client, - * false - otherwise - */ -bool -jerry_debugger_send (size_t data_size) /**< data size */ -{ - JERRY_ASSERT (data_size <= JERRY_CONTEXT (debugger_max_send_size)); + message_p++; + mask_p++; - uint8_t *header_p = JERRY_CONTEXT (debugger_send_buffer); - header_p[0] = JERRY_DEBUGGER_WEBSOCKET_FIN_BIT | JERRY_DEBUGGER_WEBSOCKET_BINARY_FRAME; - header_p[1] = (uint8_t) data_size; + if (JERRY_UNLIKELY (mask_p >= mask_end_p)) + { + mask_p -= JERRY_DEBUGGER_WEBSOCKET_MASK_SIZE; + } + } - return jerry_debugger_send_tcp (header_p, data_size + JERRY_DEBUGGER_WEBSOCKET_HEADER_SIZE); -} /* jerry_debugger_send */ + return true; +} /* jerry_debugger_ws_receive */ /** - * Receive message from the client. - * - * Note: - * If the function returns with true, the value of - * JERRY_DEBUGGER_VM_STOP flag should be ignored. + * Initialize the websocket transportation layer. * - * @return true - if execution should be resumed, + * @return true - if the connection succeeded * false - otherwise */ bool -jerry_debugger_receive (jerry_debugger_uint8_data_t **message_data_p) /**< [out] data received from client */ +jerry_debugger_ws_create (void) { - JERRY_ASSERT (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED); - JERRY_ASSERT (JERRY_CONTEXT (debugger_max_receive_size) <= JERRY_DEBUGGER_WEBSOCKET_ONE_BYTE_LEN_MAX); - - JERRY_ASSERT (message_data_p != NULL ? !!(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_RECEIVE_DATA_MODE) - : !(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_RECEIVE_DATA_MODE)); - - JERRY_CONTEXT (debugger_message_delay) = JERRY_DEBUGGER_MESSAGE_FREQUENCY; + bool is_handshake_ok = false; - uint8_t *recv_buffer_p = JERRY_CONTEXT (debugger_receive_buffer); - bool resume_exec = false; - uint8_t expected_message_type = 0; + const size_t buffer_size = 1024; + uint8_t *request_buffer_p = (uint8_t *) jerry_debugger_transport_malloc (buffer_size); - while (true) + if (!request_buffer_p) { - uint32_t offset = JERRY_CONTEXT (debugger_receive_buffer_offset); - - ssize_t byte_recv = recv (JERRY_CONTEXT (debugger_connection), - recv_buffer_p + offset, - JERRY_DEBUGGER_MAX_BUFFER_SIZE - offset, - 0); - - if (byte_recv < 0) - { - if (errno != EWOULDBLOCK) - { - jerry_debugger_close_connection_tcp (true); - return true; - } - - byte_recv = 0; - } - - offset += (uint32_t) byte_recv; - JERRY_CONTEXT (debugger_receive_buffer_offset) = (uint16_t) offset; - - if (offset < sizeof (jerry_debugger_receive_header_t)) - { - if (expected_message_type != 0) - { - continue; - } - - return resume_exec; - } - - if ((recv_buffer_p[0] & ~JERRY_DEBUGGER_WEBSOCKET_OPCODE_MASK) != JERRY_DEBUGGER_WEBSOCKET_FIN_BIT - || (recv_buffer_p[1] & JERRY_DEBUGGER_WEBSOCKET_LENGTH_MASK) > JERRY_CONTEXT (debugger_max_receive_size) - || !(recv_buffer_p[1] & JERRY_DEBUGGER_WEBSOCKET_MASK_BIT)) - { - JERRY_ERROR_MSG ("Unsupported Websocket message.\n"); - jerry_debugger_close_connection (); - return true; - } - - if ((recv_buffer_p[0] & JERRY_DEBUGGER_WEBSOCKET_OPCODE_MASK) != JERRY_DEBUGGER_WEBSOCKET_BINARY_FRAME) - { - JERRY_ERROR_MSG ("Unsupported Websocket opcode.\n"); - jerry_debugger_close_connection (); - return true; - } - - uint32_t message_size = (uint32_t) (recv_buffer_p[1] & JERRY_DEBUGGER_WEBSOCKET_LENGTH_MASK); - uint32_t message_total_size = (uint32_t) (message_size + sizeof (jerry_debugger_receive_header_t)); - - if (offset < message_total_size) - { - if (expected_message_type != 0) - { - continue; - } + return false; + } - return resume_exec; - } + is_handshake_ok = jerry_process_handshake (request_buffer_p); - /* Unmask data bytes. */ - uint8_t *data_p = recv_buffer_p + sizeof (jerry_debugger_receive_header_t); - const uint8_t *mask_p = data_p - JERRY_DEBUGGER_WEBSOCKET_MASK_SIZE; - const uint8_t *mask_end_p = data_p; - const uint8_t *data_end_p = data_p + message_size; + jerry_debugger_transport_free ((void *) request_buffer_p, buffer_size); - while (data_p < data_end_p) - { - /* Invert certain bits with xor operation. */ - *data_p = *data_p ^ *mask_p; + if (!is_handshake_ok && jerry_debugger_transport_is_connected ()) + { + return false; + } - data_p++; - mask_p++; + const size_t interface_size = sizeof (jerry_debugger_transport_header_t); + jerry_debugger_transport_header_t *header_p; + header_p = (jerry_debugger_transport_header_t *) jerry_debugger_transport_malloc (interface_size); - if (mask_p >= mask_end_p) - { - mask_p -= JERRY_DEBUGGER_WEBSOCKET_MASK_SIZE; - } - } + if (!header_p) + { + return false; + } - /* The jerry_debugger_process_message function is inlined - * so passing these arguments is essentially free. */ - if (!jerry_debugger_process_message (recv_buffer_p + sizeof (jerry_debugger_receive_header_t), - message_size, - &resume_exec, - &expected_message_type, - message_data_p)) - { - return true; - } + header_p->close = jerry_debugger_ws_close; + header_p->send = jerry_debugger_ws_send; + header_p->receive = jerry_debugger_ws_receive; - if (message_total_size < offset) - { - memmove (recv_buffer_p, - recv_buffer_p + message_total_size, - offset - message_total_size); - } + jerry_debugger_transport_add (header_p, + JERRY_DEBUGGER_WEBSOCKET_HEADER_SIZE, + JERRY_DEBUGGER_WEBSOCKET_ONE_BYTE_LEN_MAX, + JERRY_DEBUGGER_WEBSOCKET_HEADER_SIZE + JERRY_DEBUGGER_WEBSOCKET_MASK_SIZE, + JERRY_DEBUGGER_WEBSOCKET_ONE_BYTE_LEN_MAX); - JERRY_CONTEXT (debugger_receive_buffer_offset) = (uint16_t) (offset - message_total_size); - } -} /* jerry_debugger_receive */ + return true; +} /* jerry_debugger_ws_create */ #endif /* JERRY_DEBUGGER */ diff --git a/jerry-core/debugger/debugger-ws.h b/jerry-core/debugger/debugger-ws.h index 3a7c97c8..7be74bf1 100644 --- a/jerry-core/debugger/debugger-ws.h +++ b/jerry-core/debugger/debugger-ws.h @@ -16,39 +16,13 @@ #ifndef DEBUGGER_WS_H #define DEBUGGER_WS_H -#include "ecma-globals.h" +#include "jerryscript-debugger-transport.h" #ifdef JERRY_DEBUGGER /* JerryScript debugger protocol is a simplified version of RFC-6455 (WebSockets). */ -/** - * Maximum number of bytes transmitted or received. - */ -#define JERRY_DEBUGGER_MAX_BUFFER_SIZE 128 - -/** - * Incoming message: next message of string data. - */ -typedef struct -{ - uint8_t type; /**< type of the message */ -} jerry_debugger_receive_uint8_data_part_t; - -/** - * Byte data for evaluating expressions and receiving client source. - */ -typedef struct -{ - uint32_t uint8_size; /**< total size of the client source */ - uint32_t uint8_offset; /**< current offset in the client source */ -} jerry_debugger_uint8_data_t; - -bool jerry_debugger_accept_connection (void); -void jerry_debugger_close_connection (void); - -bool jerry_debugger_send (size_t data_size); -bool jerry_debugger_receive (jerry_debugger_uint8_data_t **message_data_p); +bool jerry_debugger_ws_create (void); void jerry_debugger_compute_sha1 (const uint8_t *input1, size_t input1_len, const uint8_t *input2, size_t input2_len, diff --git a/jerry-core/debugger/debugger.c b/jerry-core/debugger/debugger.c index a3591ad7..26b52d59 100644 --- a/jerry-core/debugger/debugger.c +++ b/jerry-core/debugger/debugger.c @@ -20,11 +20,20 @@ #include "ecma-eval.h" #include "ecma-objects.h" #include "jcontext.h" +#include "jerryscript-port.h" #include "lit-char-helpers.h" #ifdef JERRY_DEBUGGER /** + * Incoming message: next message of string data. + */ +typedef struct +{ + uint8_t type; /**< type of the message */ +} jerry_debugger_receive_uint8_data_part_t; + +/** * The number of message types in the debugger should reflect the * debugger versioning. */ @@ -34,6 +43,12 @@ JERRY_STATIC_ASSERT (JERRY_DEBUGGER_MESSAGES_OUT_MAX_COUNT == 27 debugger_version_correlates_to_message_type_count); /** + * Waiting for data from the client. + */ +#define JERRY_DEBUGGER_RECEIVE_DATA_MODE \ + (JERRY_DEBUGGER_BREAKPOINT_MODE | JERRY_DEBUGGER_CLIENT_SOURCE_MODE) + +/** * Type cast the debugger send buffer into a specific type. */ #define JERRY_DEBUGGER_SEND_BUFFER_AS(type, name_p) \ @@ -71,10 +86,27 @@ jerry_debugger_free_unreferenced_byte_code (void) } /* jerry_debugger_free_unreferenced_byte_code */ /** + * Send data over an active connection. + * + * @return true - if the data was sent successfully + * false - otherwise + */ +static bool +jerry_debugger_send (size_t message_length) /**< message length in bytes */ +{ + JERRY_ASSERT (message_length <= JERRY_CONTEXT (debugger_max_send_size)); + + jerry_debugger_transport_header_t *header_p = JERRY_CONTEXT (debugger_transport_header_p); + uint8_t *payload_p = JERRY_CONTEXT (debugger_send_buffer_payload_p); + + return header_p->send (header_p, payload_p, message_length); +} /* jerry_debugger_send */ + +/** * Send backtrace. */ static void -jerry_debugger_send_backtrace (uint8_t *recv_buffer_p) /**< pointer to the received data */ +jerry_debugger_send_backtrace (const uint8_t *recv_buffer_p) /**< pointer to the received data */ { JERRY_DEBUGGER_RECEIVE_BUFFER_AS (jerry_debugger_receive_get_backtrace_t, get_backtrace_p); @@ -244,23 +276,13 @@ jerry_debugger_send_eval (const lit_utf8_byte_t *eval_string_p, /**< evaluated s } /* jerry_debugger_send_eval */ /** - * Suspend execution for a given time. - * Note: If the platform does not have nanosleep or usleep, this function does not sleep at all. - */ -void -jerry_debugger_sleep (void) -{ - jerry_port_sleep (JERRY_DEBUGGER_TIMEOUT); -} /* jerry_debugger_sleep */ - -/** * Check received packet size. */ #define JERRY_DEBUGGER_CHECK_PACKET_SIZE(type) \ if (message_size != sizeof (type)) \ { \ JERRY_ERROR_MSG ("Invalid message size\n"); \ - jerry_debugger_close_connection (); \ + jerry_debugger_transport_close (); \ return false; \ } @@ -270,8 +292,8 @@ jerry_debugger_sleep (void) * @return true - if message is processed successfully * false - otherwise */ -inline bool JERRY_ATTR_ALWAYS_INLINE -jerry_debugger_process_message (uint8_t *recv_buffer_p, /**< pointer to the received data */ +static inline bool JERRY_ATTR_ALWAYS_INLINE +jerry_debugger_process_message (const uint8_t *recv_buffer_p, /**< pointer to the received data */ uint32_t message_size, /**< message size */ bool *resume_exec_p, /**< pointer to the resume exec flag */ uint8_t *expected_message_type_p, /**< message type */ @@ -283,7 +305,7 @@ jerry_debugger_process_message (uint8_t *recv_buffer_p, /**< pointer to the rece && !(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_BREAKPOINT_MODE)) { JERRY_ERROR_MSG ("Message requires breakpoint mode\n"); - jerry_debugger_close_connection (); + jerry_debugger_transport_close (); return false; } @@ -298,7 +320,7 @@ jerry_debugger_process_message (uint8_t *recv_buffer_p, /**< pointer to the rece { jmem_heap_free_block (uint8_data_p, uint8_data_p->uint8_size + sizeof (jerry_debugger_uint8_data_t)); JERRY_ERROR_MSG ("Unexpected message\n"); - jerry_debugger_close_connection (); + jerry_debugger_transport_close (); return false; } @@ -308,7 +330,7 @@ jerry_debugger_process_message (uint8_t *recv_buffer_p, /**< pointer to the rece { jmem_heap_free_block (uint8_data_p, uint8_data_p->uint8_size + sizeof (jerry_debugger_uint8_data_t)); JERRY_ERROR_MSG ("Invalid message size\n"); - jerry_debugger_close_connection (); + jerry_debugger_transport_close (); return false; } @@ -320,7 +342,7 @@ jerry_debugger_process_message (uint8_t *recv_buffer_p, /**< pointer to the rece { jmem_heap_free_block (uint8_data_p, uint8_data_p->uint8_size + sizeof (jerry_debugger_uint8_data_t)); JERRY_ERROR_MSG ("Invalid message size\n"); - jerry_debugger_close_connection (); + jerry_debugger_transport_close (); return false; } @@ -370,7 +392,7 @@ jerry_debugger_process_message (uint8_t *recv_buffer_p, /**< pointer to the rece if (byte_code_free_cp != JERRY_CONTEXT (debugger_byte_code_free_tail)) { JERRY_ERROR_MSG ("Invalid byte code free order\n"); - jerry_debugger_close_connection (); + jerry_debugger_transport_close (); return false; } @@ -531,7 +553,7 @@ jerry_debugger_process_message (uint8_t *recv_buffer_p, /**< pointer to the rece if (!(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_PARSER_WAIT_MODE)) { JERRY_ERROR_MSG ("Not in parser wait mode\n"); - jerry_debugger_close_connection (); + jerry_debugger_transport_close (); return false; } @@ -544,7 +566,7 @@ jerry_debugger_process_message (uint8_t *recv_buffer_p, /**< pointer to the rece if (message_size < sizeof (jerry_debugger_receive_eval_first_t) + 1) { JERRY_ERROR_MSG ("Invalid message size\n"); - jerry_debugger_close_connection (); + jerry_debugger_transport_close (); return false; } @@ -558,7 +580,7 @@ jerry_debugger_process_message (uint8_t *recv_buffer_p, /**< pointer to the rece if (eval_size != message_size - sizeof (jerry_debugger_receive_eval_first_t)) { JERRY_ERROR_MSG ("Invalid message size\n"); - jerry_debugger_close_connection (); + jerry_debugger_transport_close (); return false; } @@ -594,14 +616,14 @@ jerry_debugger_process_message (uint8_t *recv_buffer_p, /**< pointer to the rece if (message_size <= sizeof (jerry_debugger_receive_client_source_first_t)) { JERRY_ERROR_MSG ("Invalid message size\n"); - jerry_debugger_close_connection (); + jerry_debugger_transport_close (); return false; } if (!(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CLIENT_SOURCE_MODE)) { JERRY_ERROR_MSG ("Not in client source mode\n"); - jerry_debugger_close_connection (); + jerry_debugger_transport_close (); return false; } @@ -616,7 +638,7 @@ jerry_debugger_process_message (uint8_t *recv_buffer_p, /**< pointer to the rece && client_source_size != message_size - header_size) { JERRY_ERROR_MSG ("Invalid message size\n"); - jerry_debugger_close_connection (); + jerry_debugger_transport_close (); return false; } @@ -653,7 +675,7 @@ jerry_debugger_process_message (uint8_t *recv_buffer_p, /**< pointer to the rece if (!(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CLIENT_SOURCE_MODE)) { JERRY_ERROR_MSG ("Not in client source mode\n"); - jerry_debugger_close_connection (); + jerry_debugger_transport_close (); return false; } @@ -671,7 +693,7 @@ jerry_debugger_process_message (uint8_t *recv_buffer_p, /**< pointer to the rece if (!(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CLIENT_SOURCE_MODE)) { JERRY_ERROR_MSG ("Not in client source mode\n"); - jerry_debugger_close_connection (); + jerry_debugger_transport_close (); return false; } @@ -687,12 +709,75 @@ jerry_debugger_process_message (uint8_t *recv_buffer_p, /**< pointer to the rece default: { JERRY_ERROR_MSG ("Unexpected message."); - jerry_debugger_close_connection (); + jerry_debugger_transport_close (); return false; } } } /* jerry_debugger_process_message */ +/** + * Receive message from the client. + * + * Note: + * If the function returns with true, the value of + * JERRY_DEBUGGER_VM_STOP flag should be ignored. + * + * @return true - if execution should be resumed, + * false - otherwise + */ +bool +jerry_debugger_receive (jerry_debugger_uint8_data_t **message_data_p) /**< [out] data received from client */ +{ + JERRY_ASSERT (jerry_debugger_transport_is_connected ()); + + JERRY_ASSERT (message_data_p != NULL ? !!(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_RECEIVE_DATA_MODE) + : !(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_RECEIVE_DATA_MODE)); + + JERRY_CONTEXT (debugger_message_delay) = JERRY_DEBUGGER_MESSAGE_FREQUENCY; + + bool resume_exec = false; + uint8_t expected_message_type = 0; + + while (true) + { + jerry_debugger_transport_receive_context_t context; + if (!jerry_debugger_transport_receive (&context)) + { + JERRY_ASSERT (!(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)); + return true; + } + + if (context.message_p == NULL) + { + if (expected_message_type != 0) + { + jerry_debugger_transport_sleep (); + continue; + } + + return resume_exec; + } + + /* Only datagram packets are supported. */ + JERRY_ASSERT (context.message_total_length > 0); + + /* The jerry_debugger_process_message function is inlined + * so passing these arguments is essentially free. */ + if (!jerry_debugger_process_message (context.message_p, + (uint32_t) context.message_length, + &resume_exec, + &expected_message_type, + message_data_p)) + { + JERRY_ASSERT (!(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)); + return true; + } + + jerry_debugger_transport_receive_completed (&context); + } +} /* jerry_debugger_receive */ + + #undef JERRY_DEBUGGER_CHECK_PACKET_SIZE /** @@ -727,7 +812,7 @@ jerry_debugger_breakpoint_hit (uint8_t message_type) /**< message type */ while (!jerry_debugger_receive (&uint8_data)) { - jerry_debugger_sleep (); + jerry_debugger_transport_sleep (); } if (uint8_data != NULL) diff --git a/jerry-core/debugger/debugger.h b/jerry-core/debugger/debugger.h index 40239438..7321f5b3 100644 --- a/jerry-core/debugger/debugger.h +++ b/jerry-core/debugger/debugger.h @@ -18,6 +18,7 @@ #include "debugger-ws.h" #include "ecma-globals.h" +#include "jerryscript-debugger-transport.h" #ifdef JERRY_DEBUGGER @@ -34,22 +35,17 @@ #define JERRY_DEBUGGER_MESSAGE_FREQUENCY 5 /** - * Sleep time in milliseconds between each jerry_debugger_receive call + * This constant represents that the string to be sent has no subtype. */ -#define JERRY_DEBUGGER_TIMEOUT 100 - -/** - * This constant represents that the string to be sent has no subtype. - */ #define JERRY_DEBUGGER_NO_SUBTYPE 0 /** * Limited resources available for the engine, so it is important to * check the maximum buffer size. It needs to be between 64 and 256 bytes. */ -#if JERRY_DEBUGGER_MAX_BUFFER_SIZE < 64 || JERRY_DEBUGGER_MAX_BUFFER_SIZE > 256 +#if JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE < 64 || JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE > 256 #error Please define the MAX_BUFFER_SIZE between 64 and 256 bytes. -#endif /* JERRY_DEBUGGER_MAX_BUFFER_SIZE < 64 || JERRY_DEBUGGER_MAX_BUFFER_SIZE > 256 */ +#endif /* JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE < 64 || JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE > 256 */ /** * Calculate the maximum number of items for a given type @@ -227,6 +223,15 @@ typedef enum } jerry_debugger_output_subtype_t; /** + * Byte data for evaluating expressions and receiving client source. + */ +typedef struct +{ + uint32_t uint8_size; /**< total size of the client source */ + uint32_t uint8_offset; /**< current offset in the client source */ +} jerry_debugger_uint8_data_t; + +/** * Delayed free of byte code data. */ typedef struct @@ -400,11 +405,8 @@ typedef struct void jerry_debugger_free_unreferenced_byte_code (void); -void jerry_debugger_sleep (void); +bool jerry_debugger_receive (jerry_debugger_uint8_data_t **message_data_p); -bool jerry_debugger_process_message (uint8_t *recv_buffer_p, uint32_t message_size, - bool *resume_exec_p, uint8_t *expected_message_p, - jerry_debugger_uint8_data_t **message_data_p); void jerry_debugger_breakpoint_hit (uint8_t message_type); void jerry_debugger_send_type (jerry_debugger_header_type_t type); diff --git a/jerry-core/include/jerryscript-debugger-transport.h b/jerry-core/include/jerryscript-debugger-transport.h new file mode 100644 index 00000000..24e4e853 --- /dev/null +++ b/jerry-core/include/jerryscript-debugger-transport.h @@ -0,0 +1,107 @@ +/* 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. + */ + +#ifndef JERRYSCRIPT_DEBUGGER_TRANSPORT_H +#define JERRYSCRIPT_DEBUGGER_TRANSPORT_H + +#include <jerryscript-core.h> + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/** \addtogroup jerry-debugger-transport Jerry engine debugger interface - transport control + * @{ + */ + +/** + * Maximum number of bytes transmitted or received. + */ +#define JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE 128 + +/** + * Receive message context. + */ +typedef struct +{ + uint8_t *buffer_p; /**< buffer for storing the received data */ + size_t received_length; /**< number of currently received bytes */ + uint8_t *message_p; /**< start of the received message */ + size_t message_length; /**< length of the received message */ + size_t message_total_length; /**< total length for datagram protocols, + * 0 for stream protocols */ +} jerry_debugger_transport_receive_context_t; + +/** + * Forward definition of jerry_debugger_transport_header_t. + */ +struct jerry_debugger_transport_interface_t; + +/** + * Close connection callback. + */ +typedef void (*jerry_debugger_transport_close_t) (struct jerry_debugger_transport_interface_t *header_p); + +/** + * Send data callback. + */ +typedef bool (*jerry_debugger_transport_send_t) (struct jerry_debugger_transport_interface_t *header_p, + uint8_t *message_p, size_t message_length); + +/** + * Receive data callback. + */ +typedef bool (*jerry_debugger_transport_receive_t) (struct jerry_debugger_transport_interface_t *header_p, + jerry_debugger_transport_receive_context_t *context_p); + +/** + * Transport layer header. + */ +typedef struct jerry_debugger_transport_interface_t +{ + /* The following fields must be filled before calling jerry_debugger_transport_add(). */ + jerry_debugger_transport_close_t close; /**< close connection callback */ + jerry_debugger_transport_send_t send; /**< send data callback */ + jerry_debugger_transport_receive_t receive; /**< receive data callback */ + + /* The following fields are filled by jerry_debugger_transport_add(). */ + struct jerry_debugger_transport_interface_t *next_p; /**< next transport layer */ +} jerry_debugger_transport_header_t; + +void * jerry_debugger_transport_malloc (size_t size); +void jerry_debugger_transport_free (void *mem_p, size_t size); +void jerry_debugger_transport_add (jerry_debugger_transport_header_t *header_p, + size_t send_message_header_size, size_t max_send_message_size, + size_t receive_message_header_size, size_t max_receive_message_size); +void jerry_debugger_transport_start (void); + +bool jerry_debugger_transport_is_connected (void); +void jerry_debugger_transport_close (void); + +bool jerry_debugger_transport_send (const uint8_t *message_p, size_t message_length); +bool jerry_debugger_transport_receive (jerry_debugger_transport_receive_context_t *context_p); +void jerry_debugger_transport_receive_completed (jerry_debugger_transport_receive_context_t *context_p); + +void jerry_debugger_transport_sleep (void); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* !JERRYSCRIPT_DEBUGGER_TRANSPORT_H */ diff --git a/jerry-core/include/jerryscript-debugger.h b/jerry-core/include/jerryscript-debugger.h index c6e73a79..a111c6b6 100644 --- a/jerry-core/include/jerryscript-debugger.h +++ b/jerry-core/include/jerryscript-debugger.h @@ -16,7 +16,7 @@ #ifndef JERRYSCRIPT_DEBUGGER_H #define JERRYSCRIPT_DEBUGGER_H -#include <stdbool.h> +#include <jerryscript-core.h> #ifdef __cplusplus extern "C" diff --git a/jerry-core/jcontext/jcontext.h b/jerry-core/jcontext/jcontext.h index f1abe682..e98d4511 100644 --- a/jerry-core/jcontext/jcontext.h +++ b/jerry-core/jcontext/jcontext.h @@ -27,6 +27,7 @@ #include "re-bytecode.h" #include "vm-defines.h" #include "jerryscript.h" +#include "jerryscript-debugger-transport.h" /** \addtogroup context Context * @{ @@ -109,19 +110,18 @@ typedef struct #endif /* JERRY_VM_EXEC_STOP */ #ifdef JERRY_DEBUGGER - uint8_t debugger_send_buffer[JERRY_DEBUGGER_MAX_BUFFER_SIZE]; /**< buffer for sending messages */ - uint8_t debugger_receive_buffer[JERRY_DEBUGGER_MAX_BUFFER_SIZE]; /**< buffer for receiving messages */ + uint8_t debugger_send_buffer[JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE]; /**< buffer for sending messages */ + uint8_t debugger_receive_buffer[JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE]; /**< buffer for receiving messages */ + jerry_debugger_transport_header_t *debugger_transport_header_p; /**< head of transport protocol chain */ uint8_t *debugger_send_buffer_payload_p; /**< start where the outgoing message can be written */ vm_frame_ctx_t *debugger_stop_context; /**< stop only if the current context is equal to this context */ jmem_cpointer_t debugger_byte_code_free_head; /**< head of byte code free linked list */ jmem_cpointer_t debugger_byte_code_free_tail; /**< tail of byte code free linked list */ uint32_t debugger_flags; /**< debugger flags */ - uint16_t debugger_receive_buffer_offset; /**< receive buffer offset */ - uint16_t debugger_port; /**< debugger socket communication port */ + uint16_t debugger_received_length; /**< length of currently received bytes */ uint8_t debugger_message_delay; /**< call receive message when reaches zero */ - uint8_t debugger_max_send_size; /**< maximum amount of data that can be written */ + uint8_t debugger_max_send_size; /**< maximum amount of data that can be sent */ uint8_t debugger_max_receive_size; /**< maximum amount of data that can be received */ - int debugger_connection; /**< holds the file descriptor of the socket communication */ #endif /* JERRY_DEBUGGER */ #ifdef JERRY_ENABLE_LINE_INFO diff --git a/jerry-core/parser/js/js-parser-internal.h b/jerry-core/parser/js/js-parser-internal.h index 52ba022d..fd7e5405 100644 --- a/jerry-core/parser/js/js-parser-internal.h +++ b/jerry-core/parser/js/js-parser-internal.h @@ -232,6 +232,13 @@ typedef struct { uint32_t value; /**< line or offset of the breakpoint */ } parser_breakpoint_info_t; + +/** + * Maximum number of breakpoint info. + */ +#define PARSER_MAX_BREAKPOINT_INFO_COUNT \ + (JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE / sizeof (parser_breakpoint_info_t)) + #endif /* JERRY_DEBUGGER */ /** @@ -316,8 +323,7 @@ typedef struct #endif /* PARSER_DUMP_BYTE_CODE */ #ifdef JERRY_DEBUGGER - /** extra data for each breakpoint */ - parser_breakpoint_info_t breakpoint_info[JERRY_DEBUGGER_MAX_BUFFER_SIZE / sizeof (parser_breakpoint_info_t)]; + parser_breakpoint_info_t breakpoint_info[PARSER_MAX_BREAKPOINT_INFO_COUNT]; /**< breakpoint info list */ uint16_t breakpoint_info_count; /**< current breakpoint index */ parser_line_counter_t last_breakpoint_line; /**< last line where breakpoint has been inserted */ #endif /* JERRY_DEBUGGER */ diff --git a/jerry-core/parser/js/js-parser.c b/jerry-core/parser/js/js-parser.c index df4e61d0..bb191a9b 100644 --- a/jerry-core/parser/js/js-parser.c +++ b/jerry-core/parser/js/js-parser.c @@ -2949,7 +2949,7 @@ parser_parse_script (const uint8_t *arg_list_p, /**< function argument list */ break; } - jerry_debugger_sleep (); + jerry_debugger_transport_sleep (); } } #endif /* JERRY_DEBUGGER */ |