Initial version of JerryScript debugger (#1557)
The debugger supports setting breakpoints, execution control (step, next, continue) and getting backtrace. The communication is WebSocket-based, so a browser can communicate with JerryScript without any intermediate application. JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com JerryScript-DCO-1.0-Signed-off-by: Levente Orban orbanl@inf.u-szeged.hu
This commit is contained in:
committed by
Tilmann Scheller
parent
453066fcf1
commit
025a99ccbb
@@ -6,6 +6,7 @@ sudo: required
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
- OPTS="--check-signed-off-travis --check-cppcheck --check-doxygen --check-vera --check-license"
|
- OPTS="--check-signed-off-travis --check-cppcheck --check-doxygen --check-vera --check-license"
|
||||||
|
- OPTS="--jerry-debugger"
|
||||||
- OPTS="--jerry-tests --jerry-test-suite"
|
- OPTS="--jerry-tests --jerry-test-suite"
|
||||||
- OPTS="--jerry-tests --jerry-test-suite --toolchain=cmake/toolchain_linux_armv7l.cmake" TIMEOUT=300 INSTALL_QEMU_ARM=yes
|
- OPTS="--jerry-tests --jerry-test-suite --toolchain=cmake/toolchain_linux_armv7l.cmake" TIMEOUT=300 INSTALL_QEMU_ARM=yes
|
||||||
- OPTS="--buildoption-test"
|
- OPTS="--buildoption-test"
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ Enum that contains the following elements:
|
|||||||
- JERRY_INIT_SHOW_REGEXP_OPCODES - dump regexp byte-code to log after compilation
|
- JERRY_INIT_SHOW_REGEXP_OPCODES - dump regexp byte-code to log after compilation
|
||||||
- JERRY_INIT_MEM_STATS - dump memory statistics
|
- JERRY_INIT_MEM_STATS - dump memory statistics
|
||||||
- JERRY_INIT_MEM_STATS_SEPARATE - dump memory statistics and reset peak values after parse
|
- JERRY_INIT_MEM_STATS_SEPARATE - dump memory statistics and reset peak values after parse
|
||||||
|
- JERRY_INIT_DEBUGGER - enable all features required by debugging
|
||||||
|
|
||||||
## jerry_error_t
|
## jerry_error_t
|
||||||
|
|
||||||
@@ -211,6 +212,7 @@ jerry_init (jerry_init_flag_t flags)
|
|||||||
- `JERRY_INIT_SHOW_REGEXP_OPCODES` - print compiled regexp byte-code.
|
- `JERRY_INIT_SHOW_REGEXP_OPCODES` - print compiled regexp byte-code.
|
||||||
- `JERRY_INIT_MEM_STATS` - dump memory statistics.
|
- `JERRY_INIT_MEM_STATS` - dump memory statistics.
|
||||||
- `JERRY_INIT_MEM_STATS_SEPARATE` - dump memory statistics and reset peak values after parse.
|
- `JERRY_INIT_MEM_STATS_SEPARATE` - dump memory statistics and reset peak values after parse.
|
||||||
|
- `JERRY_INIT_DEBUGGER` - enable all features required by debugging.
|
||||||
|
|
||||||
**Example**
|
**Example**
|
||||||
|
|
||||||
@@ -451,6 +453,40 @@ jerry_parse (const jerry_char_t *source_p,
|
|||||||
|
|
||||||
- [jerry_run](#jerry_run)
|
- [jerry_run](#jerry_run)
|
||||||
|
|
||||||
|
## jerry_parse_named_resource
|
||||||
|
|
||||||
|
**Summary**
|
||||||
|
|
||||||
|
Parse script and construct an ECMAScript function. The lexical
|
||||||
|
environment is set to the global lexical environment. The name
|
||||||
|
(usually a file name) is also passed to this function which is
|
||||||
|
used by the debugger to find the source code.
|
||||||
|
|
||||||
|
*Note*: The returned value must be freed with [jerry_release_value](#jerry_release_value) when it
|
||||||
|
is no longer needed.
|
||||||
|
|
||||||
|
**Prototype**
|
||||||
|
|
||||||
|
```c
|
||||||
|
jerry_value_t
|
||||||
|
jerry_parse_named_resource (const jerry_char_t *name_p, /**< name (usually a file name) */
|
||||||
|
size_t name_length, /**< length of name */
|
||||||
|
const jerry_char_t *source_p, /**< script source */
|
||||||
|
size_t source_size, /**< script source size */
|
||||||
|
bool is_strict) /**< strict mode */
|
||||||
|
{
|
||||||
|
```
|
||||||
|
|
||||||
|
- `name_p` - name, usually a file name
|
||||||
|
- `name_length` - size of the file name, in bytes
|
||||||
|
- `source_p` - string, containing source code to parse. It must be a valid UTF8 string
|
||||||
|
- `source_size` - size of the string, in bytes
|
||||||
|
- `is_strict` - defines strict mode
|
||||||
|
- return value
|
||||||
|
- function object value, if script was parsed successfully,
|
||||||
|
- thrown error, otherwise
|
||||||
|
|
||||||
|
This function is identical to [jerry_parse](#jerry_parse), except that an additional filename parameter has been added.
|
||||||
|
|
||||||
## jerry_run
|
## jerry_run
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
## JerryScript debugger interface
|
||||||
|
|
||||||
|
JerryScript provides a remote debugger which allows debugging
|
||||||
|
JavaScript programs. The debugger has two main components:
|
||||||
|
a server which is part of the JerryScript binary and a
|
||||||
|
separate client application. Currently two debugger clients
|
||||||
|
are available in the /jerry-debugger subdirectory: an HTML
|
||||||
|
and a Python application. These simple applications demonstrate
|
||||||
|
the communication protocol between the client and server and can
|
||||||
|
be reused by integrated development environments.
|
||||||
|
|
||||||
|
## Setting up the debugger server
|
||||||
|
|
||||||
|
The following arguments must be passed to `tools/build.py`:
|
||||||
|
|
||||||
|
`--jerry-debugger=on --jerry-libc=off`
|
||||||
|
|
||||||
|
At the moment only a Websocket-based implementation is provided
|
||||||
|
by JerryScript which transmits messages over TCP/IP networks.
|
||||||
|
This implementation requires a socket API which is not yet
|
||||||
|
supported by jerry-libc so the standard libc is used instead.
|
||||||
|
In the future any reliable stream or datagram based protocol
|
||||||
|
can be used for transmitting debugger messages.
|
||||||
|
|
||||||
|
## Debugging JavaScript applications
|
||||||
|
|
||||||
|
The debugger client must be connected to the server before the
|
||||||
|
JavaScript application runs. On-the-fly attachment is not supported
|
||||||
|
because the debugging information (e.g. line index of each possible
|
||||||
|
breakpoint location) is not preserved by JerryScript. The client is
|
||||||
|
expected to be run on a system with much more resources and it should
|
||||||
|
be capable of storing this information. JerryScript frees all debug
|
||||||
|
information after it is transmitted to the client to save memory.
|
||||||
|
|
||||||
|
The following argument makes JerryScript wait for a client
|
||||||
|
connection:
|
||||||
|
|
||||||
|
`--start-debug-server`
|
||||||
|
|
||||||
|
It is also recommended to increase the log level to see
|
||||||
|
the *Waiting for client connection* message:
|
||||||
|
|
||||||
|
`--log-level 2`
|
||||||
|
|
||||||
|
The HTML client can connect to the IP address of the server with
|
||||||
|
the `connect` command. The IP address can be localhost
|
||||||
|
if the server and the client are running on the same machine.
|
||||||
|
|
||||||
|
After the connection is established the execution can be
|
||||||
|
controlled by the debugger. The debugger always stops at
|
||||||
|
the first possible breakpoint location. The effect is the
|
||||||
|
same as using the `stop` command. This allows inserting
|
||||||
|
breakpoints right before the meaningful part of the execution
|
||||||
|
starts.
|
||||||
|
|
||||||
|
All available commands of the client can be queried by the
|
||||||
|
`help` command.
|
||||||
|
|
||||||
|
## Integrating debugger support into applications using JerryScript
|
||||||
|
|
||||||
|
The debugger can be enabled by passing the `JERRY_INIT_DEBUGGER` flag
|
||||||
|
to the `jerry_init ()` function which then initializes the debugger
|
||||||
|
and blocks until a client connects.
|
||||||
|
|
||||||
|
When the debugger is enabled it is recommended to use
|
||||||
|
`jerry_parse_named_resource ()` instead of `jerry_parse ()` because
|
||||||
|
the resource name (usually a file name) is also passed to this
|
||||||
|
function. This resource name is used by the client to identify
|
||||||
|
the corresponding resource. In general it is always recommended to
|
||||||
|
use `jerry_parse_named_resource ()` when the resource name is
|
||||||
|
available because it silently ignores the resource name if the
|
||||||
|
debugger is disabled.
|
||||||
@@ -18,6 +18,7 @@ project (${JERRY_CORE_NAME} C)
|
|||||||
|
|
||||||
# Optional features
|
# Optional features
|
||||||
set(FEATURE_CPOINTER_32_BIT OFF CACHE BOOL "Enable 32 bit compressed pointers?")
|
set(FEATURE_CPOINTER_32_BIT OFF CACHE BOOL "Enable 32 bit compressed pointers?")
|
||||||
|
set(FEATURE_DEBUGGER OFF CACHE BOOL "Enable JerryScript debugger?")
|
||||||
set(FEATURE_ERROR_MESSAGES OFF CACHE BOOL "Enable error messages?")
|
set(FEATURE_ERROR_MESSAGES OFF CACHE BOOL "Enable error messages?")
|
||||||
set(FEATURE_JS_PARSER ON CACHE BOOL "Enable js-parser?")
|
set(FEATURE_JS_PARSER ON CACHE BOOL "Enable js-parser?")
|
||||||
set(FEATURE_MEM_STATS OFF CACHE BOOL "Enable memory statistics?")
|
set(FEATURE_MEM_STATS OFF CACHE BOOL "Enable memory statistics?")
|
||||||
@@ -38,6 +39,7 @@ endif()
|
|||||||
|
|
||||||
# Status messages
|
# Status messages
|
||||||
message(STATUS "FEATURE_CPOINTER_32_BIT " ${FEATURE_CPOINTER_32_BIT})
|
message(STATUS "FEATURE_CPOINTER_32_BIT " ${FEATURE_CPOINTER_32_BIT})
|
||||||
|
message(STATUS "FEATURE_DEBUGGER " ${FEATURE_DEBUGGER})
|
||||||
message(STATUS "FEATURE_ERROR_MESSAGES " ${FEATURE_ERROR_MESSAGES})
|
message(STATUS "FEATURE_ERROR_MESSAGES " ${FEATURE_ERROR_MESSAGES})
|
||||||
message(STATUS "FEATURE_JS_PARSER " ${FEATURE_JS_PARSER})
|
message(STATUS "FEATURE_JS_PARSER " ${FEATURE_JS_PARSER})
|
||||||
message(STATUS "FEATURE_MEM_STATS " ${FEATURE_MEM_STATS})
|
message(STATUS "FEATURE_MEM_STATS " ${FEATURE_MEM_STATS})
|
||||||
@@ -55,6 +57,7 @@ message(STATUS "MEM_HEAP_SIZE_KB " ${MEM_HEAP_SIZE_KB})
|
|||||||
# Include directories
|
# Include directories
|
||||||
set(INCLUDE_CORE
|
set(INCLUDE_CORE
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/debugger"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/ecma/base"
|
"${CMAKE_CURRENT_SOURCE_DIR}/ecma/base"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/ecma/builtin-objects"
|
"${CMAKE_CURRENT_SOURCE_DIR}/ecma/builtin-objects"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/ecma/builtin-objects/typedarray"
|
"${CMAKE_CURRENT_SOURCE_DIR}/ecma/builtin-objects/typedarray"
|
||||||
@@ -70,6 +73,7 @@ set(INCLUDE_CORE
|
|||||||
# Sources
|
# Sources
|
||||||
# Jerry core
|
# Jerry core
|
||||||
file(GLOB SOURCE_CORE_API *.c)
|
file(GLOB SOURCE_CORE_API *.c)
|
||||||
|
file(GLOB SOURCE_CORE_DEBUGGER debugger/*.c)
|
||||||
file(GLOB SOURCE_CORE_ECMA_BASE ecma/base/*.c)
|
file(GLOB SOURCE_CORE_ECMA_BASE ecma/base/*.c)
|
||||||
file(GLOB SOURCE_CORE_ECMA_BUILTINS ecma/builtin-objects/*.c)
|
file(GLOB SOURCE_CORE_ECMA_BUILTINS ecma/builtin-objects/*.c)
|
||||||
file(GLOB SOURCE_CORE_ECMA_BUILTINS_TYPEDARRAY ecma/builtin-objects/typedarray/*.c)
|
file(GLOB SOURCE_CORE_ECMA_BUILTINS_TYPEDARRAY ecma/builtin-objects/typedarray/*.c)
|
||||||
@@ -96,6 +100,10 @@ set(SOURCE_CORE_FILES
|
|||||||
${SOURCE_CORE_PARSER_REGEXP}
|
${SOURCE_CORE_PARSER_REGEXP}
|
||||||
${SOURCE_CORE_VM})
|
${SOURCE_CORE_VM})
|
||||||
|
|
||||||
|
if(FEATURE_DEBUGGER)
|
||||||
|
set(SOURCE_CORE_FILES ${SOURCE_CORE_FILES} ${SOURCE_CORE_DEBUGGER})
|
||||||
|
endif()
|
||||||
|
|
||||||
# Jerry port
|
# Jerry port
|
||||||
file(GLOB SOURCE_PORT_FILES "${PORT_DIR}/*.c")
|
file(GLOB SOURCE_PORT_FILES "${PORT_DIR}/*.c")
|
||||||
|
|
||||||
@@ -162,6 +170,11 @@ if(FEATURE_MEM_STATS)
|
|||||||
set(DEFINES_JERRY ${DEFINES_JERRY} JMEM_STATS)
|
set(DEFINES_JERRY ${DEFINES_JERRY} JMEM_STATS)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Enable debugger
|
||||||
|
if(FEATURE_DEBUGGER)
|
||||||
|
set(DEFINES_JERRY ${DEFINES_JERRY} JERRY_DEBUGGER)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Memory management stress-test mode
|
# Memory management stress-test mode
|
||||||
if(FEATURE_MEM_STRESS_TEST)
|
if(FEATURE_MEM_STRESS_TEST)
|
||||||
set(DEFINES_JERRY ${DEFINES_JERRY} JMEM_GC_BEFORE_EACH_ALLOC)
|
set(DEFINES_JERRY ${DEFINES_JERRY} JMEM_GC_BEFORE_EACH_ALLOC)
|
||||||
@@ -185,6 +198,10 @@ else()
|
|||||||
MESSAGE(FATAL_ERROR "Profile file: '${FEATURE_PROFILE}' doesn't exist!")
|
MESSAGE(FATAL_ERROR "Profile file: '${FEATURE_PROFILE}' doesn't exist!")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(JERRY_LIBC AND FEATURE_DEBUGGER)
|
||||||
|
MESSAGE(FATAL_ERROR "This configuration is not supported. Please build against your system libc to enable the JerryScript debugger.")
|
||||||
|
endif()
|
||||||
|
|
||||||
# RegExp byte-code dumps
|
# RegExp byte-code dumps
|
||||||
if(FEATURE_REGEXP_DUMP)
|
if(FEATURE_REGEXP_DUMP)
|
||||||
set(DEFINES_JERRY ${DEFINES_JERRY} REGEXP_DUMP_BYTE_CODE)
|
set(DEFINES_JERRY ${DEFINES_JERRY} REGEXP_DUMP_BYTE_CODE)
|
||||||
|
|||||||
@@ -0,0 +1,547 @@
|
|||||||
|
/* 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 "jerry-api.h"
|
||||||
|
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "jcontext.h"
|
||||||
|
#include "jerry-debugger.h"
|
||||||
|
#include "jerry-port.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debugger socket communication port.
|
||||||
|
*/
|
||||||
|
#define JERRY_DEBUGGER_PORT 5001
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Masking-key is available.
|
||||||
|
*/
|
||||||
|
#define JERRY_DEBUGGER_WEBSOCKET_MASK_BIT 0x80
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opcode type mask.
|
||||||
|
*/
|
||||||
|
#define JERRY_DEBUGGER_WEBSOCKET_OPCODE_MASK 0x0fu
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet length mask.
|
||||||
|
*/
|
||||||
|
#define JERRY_DEBUGGER_WEBSOCKET_LENGTH_MASK 0x7fu
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Payload mask size in bytes of a websocket package.
|
||||||
|
*/
|
||||||
|
#define JERRY_DEBUGGER_WEBSOCKET_MASK_SIZE 4
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Header for incoming packets.
|
||||||
|
*/
|
||||||
|
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 (jerry_init_flags) & JERRY_INIT_DEBUGGER);
|
||||||
|
|
||||||
|
JERRY_CONTEXT (jerry_init_flags) &= (uint32_t) ~JERRY_INIT_DEBUGGER;
|
||||||
|
|
||||||
|
if (log_error)
|
||||||
|
{
|
||||||
|
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: %s\n", strerror (errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
jerry_port_log (JERRY_LOG_LEVEL_DEBUG, "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 (jerry_init_flags) & JERRY_INIT_DEBUGGER);
|
||||||
|
|
||||||
|
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 */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a 6-bit value to a Base64 character.
|
||||||
|
*
|
||||||
|
* @return Base64 character
|
||||||
|
*/
|
||||||
|
static uint8_t
|
||||||
|
jerry_to_base64_character (uint8_t value) /**< 6-bit value */
|
||||||
|
{
|
||||||
|
if (value < 26)
|
||||||
|
{
|
||||||
|
return (uint8_t) (value + 'A');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value < 52)
|
||||||
|
{
|
||||||
|
return (uint8_t) (value - 26 + 'a');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value < 62)
|
||||||
|
{
|
||||||
|
return (uint8_t) (value - 52 + '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value == 62)
|
||||||
|
{
|
||||||
|
return (uint8_t) '+';
|
||||||
|
}
|
||||||
|
|
||||||
|
return (uint8_t) '/';
|
||||||
|
} /* jerry_to_base64_character */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode a byte sequence into Base64 string.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
jerry_to_base64 (const uint8_t *source_p, /**< source data */
|
||||||
|
uint8_t *destination_p, /**< destination buffer */
|
||||||
|
size_t length) /**< length of source, must be divisible by 3 */
|
||||||
|
{
|
||||||
|
while (length >= 3)
|
||||||
|
{
|
||||||
|
uint8_t value = (source_p[0] >> 2);
|
||||||
|
destination_p[0] = jerry_to_base64_character (value);
|
||||||
|
|
||||||
|
value = (uint8_t) (((source_p[0] << 4) | (source_p[1] >> 4)) & 0x3f);
|
||||||
|
destination_p[1] = jerry_to_base64_character (value);
|
||||||
|
|
||||||
|
value = (uint8_t) (((source_p[1] << 2) | (source_p[2] >> 6)) & 0x3f);
|
||||||
|
destination_p[2] = jerry_to_base64_character (value);
|
||||||
|
|
||||||
|
value = (uint8_t) (source_p[2] & 0x3f);
|
||||||
|
destination_p[3] = jerry_to_base64_character (value);
|
||||||
|
|
||||||
|
source_p += 3;
|
||||||
|
destination_p += 4;
|
||||||
|
length -= 3;
|
||||||
|
}
|
||||||
|
} /* jerry_to_base64 */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process WebSocket handshake.
|
||||||
|
*
|
||||||
|
* @return true - if the handshake was completed successfully
|
||||||
|
* false - otherwise
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
jerry_process_handshake (int client_socket, /**< client socket */
|
||||||
|
uint8_t *request_buffer_p) /**< temporary buffer */
|
||||||
|
{
|
||||||
|
size_t request_buffer_size = 1024;
|
||||||
|
uint8_t *request_end_p = request_buffer_p;
|
||||||
|
|
||||||
|
/* 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_port_log (JERRY_LOG_LEVEL_ERROR, "Handshake buffer too small.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t size = recv (client_socket, request_end_p, length, 0);
|
||||||
|
|
||||||
|
if (size < 0)
|
||||||
|
{
|
||||||
|
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: %s\n", strerror (errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
request_end_p += (size_t) size;
|
||||||
|
*request_end_p = 0;
|
||||||
|
|
||||||
|
if (request_end_p > request_buffer_p + 4
|
||||||
|
&& memcmp (request_end_p - 4, "\r\n\r\n", 4) == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check protocol. */
|
||||||
|
const char *text_p = "GET /jerry-debugger";
|
||||||
|
size_t text_len = strlen (text_p);
|
||||||
|
|
||||||
|
if ((size_t) (request_end_p - request_buffer_p) < text_len
|
||||||
|
|| memcmp (request_buffer_p, text_p, text_len) != 0)
|
||||||
|
{
|
||||||
|
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Invalid handshake format.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *websocket_key_p = request_buffer_p + text_len;
|
||||||
|
|
||||||
|
text_p = "Sec-WebSocket-Key:";
|
||||||
|
text_len = strlen (text_p);
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if ((size_t) (request_end_p - websocket_key_p) < text_len)
|
||||||
|
{
|
||||||
|
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Sec-WebSocket-Key not found.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (websocket_key_p[0] == 'S'
|
||||||
|
&& websocket_key_p[-1] == '\n'
|
||||||
|
&& websocket_key_p[-2] == '\r'
|
||||||
|
&& memcmp (websocket_key_p, text_p, text_len) == 0)
|
||||||
|
{
|
||||||
|
websocket_key_p += text_len;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
websocket_key_p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* String terminated by double newlines. */
|
||||||
|
|
||||||
|
while (*websocket_key_p == ' ')
|
||||||
|
{
|
||||||
|
websocket_key_p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *websocket_key_end_p = websocket_key_p;
|
||||||
|
|
||||||
|
while (*websocket_key_end_p > ' ')
|
||||||
|
{
|
||||||
|
websocket_key_end_p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Since the request_buffer_p is not needed anymore it can
|
||||||
|
* be reused for storing the SHA-1 key and Base64 string. */
|
||||||
|
|
||||||
|
const size_t sha1_length = 20;
|
||||||
|
|
||||||
|
jerry_debugger_compute_sha1 (websocket_key_p,
|
||||||
|
(size_t) (websocket_key_end_p - websocket_key_p),
|
||||||
|
(const uint8_t *) "258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
|
||||||
|
36,
|
||||||
|
request_buffer_p);
|
||||||
|
|
||||||
|
/* The SHA-1 key is 20 bytes long but jerry_to_base64 expects
|
||||||
|
* a length divisible by 3 so an extra 0 is appended at the end. */
|
||||||
|
request_buffer_p[sha1_length] = 0;
|
||||||
|
|
||||||
|
jerry_to_base64 (request_buffer_p, request_buffer_p + sha1_length + 1, sha1_length + 1);
|
||||||
|
|
||||||
|
/* Last value must be replaced by equal sign. */
|
||||||
|
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
text_p = "=\r\n\r\n";
|
||||||
|
return jerry_debugger_send_tcp ((const uint8_t *) text_p, strlen (text_p));
|
||||||
|
} /* jerry_process_handshake */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the socket connection.
|
||||||
|
*
|
||||||
|
* @return true - if the connection succeeded
|
||||||
|
* false - otherwise
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
jerry_debugger_accept_connection ()
|
||||||
|
{
|
||||||
|
int server_socket;
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
socklen_t sin_size = sizeof (struct sockaddr_in);
|
||||||
|
|
||||||
|
JERRY_ASSERT (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER);
|
||||||
|
|
||||||
|
/* Disable debugger flag temporarily. */
|
||||||
|
JERRY_CONTEXT (jerry_init_flags) &= (uint32_t) ~JERRY_INIT_DEBUGGER;
|
||||||
|
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_port = htons (JERRY_DEBUGGER_PORT);
|
||||||
|
addr.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
|
||||||
|
if ((server_socket = socket (AF_INET, SOCK_STREAM, 0)) == -1)
|
||||||
|
{
|
||||||
|
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "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_port_log (JERRY_LOG_LEVEL_ERROR, "Error: %s\n", strerror (errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bind (server_socket, (struct sockaddr *)&addr, sizeof (struct sockaddr)) == -1)
|
||||||
|
{
|
||||||
|
close (server_socket);
|
||||||
|
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: %s\n", strerror (errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listen (server_socket, 1) == -1)
|
||||||
|
{
|
||||||
|
close (server_socket);
|
||||||
|
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: %s\n", strerror (errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
jerry_port_log (JERRY_LOG_LEVEL_DEBUG, "Waiting for client connection\n");
|
||||||
|
|
||||||
|
JERRY_CONTEXT (debugger_connection) = accept (server_socket, (struct sockaddr *)&addr, &sin_size);
|
||||||
|
|
||||||
|
if (JERRY_CONTEXT (debugger_connection) == -1)
|
||||||
|
{
|
||||||
|
close (server_socket);
|
||||||
|
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: %s\n", strerror (errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
close (server_socket);
|
||||||
|
|
||||||
|
/* Enable debugger flag again. */
|
||||||
|
JERRY_CONTEXT (jerry_init_flags) |= JERRY_INIT_DEBUGGER;
|
||||||
|
|
||||||
|
bool is_handshake_ok = false;
|
||||||
|
|
||||||
|
JMEM_DEFINE_LOCAL_ARRAY (request_buffer_p, 1024, uint8_t);
|
||||||
|
|
||||||
|
is_handshake_ok = jerry_process_handshake (JERRY_CONTEXT (debugger_connection),
|
||||||
|
request_buffer_p);
|
||||||
|
|
||||||
|
JMEM_FINALIZE_LOCAL_ARRAY (request_buffer_p);
|
||||||
|
|
||||||
|
if (!is_handshake_ok)
|
||||||
|
{
|
||||||
|
jerry_debugger_close_connection ();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!jerry_debugger_send_configuration (JERRY_DEBUGGER_MAX_BUFFER_SIZE - sizeof (jerry_debugger_receive_header_t)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set non-blocking mode. */
|
||||||
|
int socket_flags = fcntl (JERRY_CONTEXT (debugger_connection), F_GETFL, 0);
|
||||||
|
|
||||||
|
if (socket_flags < 0)
|
||||||
|
{
|
||||||
|
jerry_debugger_close_connection_tcp (true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fcntl (JERRY_CONTEXT (debugger_connection), F_SETFL, socket_flags | O_NONBLOCK) == -1)
|
||||||
|
{
|
||||||
|
jerry_debugger_close_connection_tcp (true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
jerry_port_log (JERRY_LOG_LEVEL_DEBUG, "Connected from: %s\n", inet_ntoa (addr.sin_addr));
|
||||||
|
|
||||||
|
JERRY_CONTEXT (debugger_stop_exec) = true;
|
||||||
|
JERRY_CONTEXT (debugger_stop_context) = NULL;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} /* jerry_debugger_accept_connection */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the socket connection to the client.
|
||||||
|
*/
|
||||||
|
inline void __attr_always_inline___
|
||||||
|
jerry_debugger_close_connection (void)
|
||||||
|
{
|
||||||
|
jerry_debugger_close_connection_tcp (false);
|
||||||
|
} /* jerry_debugger_close_connection */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send message to the client side
|
||||||
|
*
|
||||||
|
* @return true - if the data was sent successfully to the debugger client,
|
||||||
|
* false - otherwise
|
||||||
|
*/
|
||||||
|
inline bool __attr_always_inline___
|
||||||
|
jerry_debugger_send (size_t data_size) /**< data size */
|
||||||
|
{
|
||||||
|
return jerry_debugger_send_tcp (JERRY_CONTEXT (debugger_send_buffer), data_size);
|
||||||
|
} /* jerry_debugger_send */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive message from the client.
|
||||||
|
*
|
||||||
|
* Note:
|
||||||
|
* If the function returns with true, the value of
|
||||||
|
* JERRY_CONTEXT (debugger_stop_exec) should be ignored.
|
||||||
|
*
|
||||||
|
* @return true - if execution should be resumed,
|
||||||
|
* false - otherwise
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
jerry_debugger_receive (void)
|
||||||
|
{
|
||||||
|
JERRY_ASSERT (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER);
|
||||||
|
|
||||||
|
JERRY_CONTEXT (debugger_message_delay) = JERRY_DEBUGGER_MESSAGE_FREQUENCY;
|
||||||
|
|
||||||
|
uint8_t *recv_buffer_p = JERRY_CONTEXT (debugger_receive_buffer);
|
||||||
|
bool resume_exec = false;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
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 (byte_recv < 0 && errno != EWOULDBLOCK)
|
||||||
|
{
|
||||||
|
jerry_debugger_close_connection_tcp (true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return resume_exec;
|
||||||
|
}
|
||||||
|
|
||||||
|
JERRY_CONTEXT (debugger_receive_buffer_offset) += (uint32_t) byte_recv;
|
||||||
|
|
||||||
|
if (JERRY_CONTEXT (debugger_receive_buffer_offset) < sizeof (jerry_debugger_receive_header_t))
|
||||||
|
{
|
||||||
|
return resume_exec;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t max_packet_size = JERRY_DEBUGGER_MAX_BUFFER_SIZE - sizeof (jerry_debugger_receive_header_t);
|
||||||
|
|
||||||
|
JERRY_ASSERT (max_packet_size < 126);
|
||||||
|
|
||||||
|
if ((recv_buffer_p[0] & ~JERRY_DEBUGGER_WEBSOCKET_OPCODE_MASK) != JERRY_DEBUGGER_WEBSOCKET_FIN_BIT
|
||||||
|
|| (recv_buffer_p[1] & JERRY_DEBUGGER_WEBSOCKET_LENGTH_MASK) >= max_packet_size
|
||||||
|
|| !(recv_buffer_p[1] & JERRY_DEBUGGER_WEBSOCKET_MASK_BIT))
|
||||||
|
{
|
||||||
|
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "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_port_log (JERRY_LOG_LEVEL_ERROR, "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 (JERRY_CONTEXT (debugger_receive_buffer_offset) < message_total_size)
|
||||||
|
{
|
||||||
|
return resume_exec;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
while (data_p < data_end_p)
|
||||||
|
{
|
||||||
|
/* Invert certain bits with xor operation. */
|
||||||
|
*data_p = *data_p ^ *mask_p;
|
||||||
|
|
||||||
|
data_p++;
|
||||||
|
mask_p++;
|
||||||
|
|
||||||
|
if (mask_p >= mask_end_p)
|
||||||
|
{
|
||||||
|
mask_p -= JERRY_DEBUGGER_WEBSOCKET_MASK_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!jerry_debugger_process_message (recv_buffer_p + sizeof (jerry_debugger_receive_header_t),
|
||||||
|
message_size,
|
||||||
|
&resume_exec))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message_total_size < JERRY_CONTEXT (debugger_receive_buffer_offset))
|
||||||
|
{
|
||||||
|
memcpy (recv_buffer_p,
|
||||||
|
recv_buffer_p + message_total_size,
|
||||||
|
JERRY_CONTEXT (debugger_receive_buffer_offset) - message_total_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
JERRY_CONTEXT (debugger_receive_buffer_offset) -= message_total_size;
|
||||||
|
}
|
||||||
|
} /* jerry_debugger_receive */
|
||||||
|
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/* 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 JERRY_DEBUGGER_WS_H
|
||||||
|
#define JERRY_DEBUGGER_WS_H
|
||||||
|
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
|
||||||
|
#include "ecma-globals.h"
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last fragment of a Websocket package.
|
||||||
|
*/
|
||||||
|
#define JERRY_DEBUGGER_WEBSOCKET_FIN_BIT 0x80
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebSocket opcode types.
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
JERRY_DEBUGGER_WEBSOCKET_TEXT_FRAME = 1, /**< text frame */
|
||||||
|
JERRY_DEBUGGER_WEBSOCKET_BINARY_FRAME = 2, /**< binary frame */
|
||||||
|
JERRY_DEBUGGER_WEBSOCKET_CLOSE_CONNECTION = 8, /**< close connection */
|
||||||
|
JERRY_DEBUGGER_WEBSOCKET_PING = 9, /**< ping (keep alive) frame */
|
||||||
|
JERRY_DEBUGGER_WEBSOCKET_PONG = 10, /**< reply to ping frame */
|
||||||
|
} jerry_websocket_opcode_type_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Header for outgoing packets.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t ws_opcode; /**< Websocket opcode */
|
||||||
|
uint8_t size; /**< size of the message */
|
||||||
|
} jerry_debugger_send_header_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the header of an outgoing message.
|
||||||
|
*/
|
||||||
|
#define JERRY_DEBUGGER_INIT_SEND_MESSAGE(message_p) \
|
||||||
|
(message_p)->header.ws_opcode = JERRY_DEBUGGER_WEBSOCKET_FIN_BIT | JERRY_DEBUGGER_WEBSOCKET_BINARY_FRAME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the size of an outgoing message from type.
|
||||||
|
*/
|
||||||
|
#define JERRY_DEBUGGER_SET_SEND_MESSAGE_SIZE_FROM_TYPE(message_p, type) \
|
||||||
|
(message_p)->header.size = (uint8_t) (sizeof (type) - sizeof (jerry_debugger_send_header_t));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the size of an outgoing message.
|
||||||
|
*/
|
||||||
|
#define JERRY_DEBUGGER_SET_SEND_MESSAGE_SIZE(message_p, byte_size) \
|
||||||
|
(message_p)->header.size = (uint8_t) (byte_size);
|
||||||
|
|
||||||
|
bool jerry_debugger_accept_connection (void);
|
||||||
|
void jerry_debugger_close_connection (void);
|
||||||
|
|
||||||
|
bool jerry_debugger_send (size_t data_size);
|
||||||
|
bool jerry_debugger_receive (void);
|
||||||
|
|
||||||
|
void jerry_debugger_compute_sha1 (const uint8_t *input1, size_t input1_len,
|
||||||
|
const uint8_t *input2, size_t input2_len,
|
||||||
|
uint8_t output[20]);
|
||||||
|
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
|
|
||||||
|
#endif /* JERRY_DEBUGGER_WS_H */
|
||||||
@@ -0,0 +1,446 @@
|
|||||||
|
/* 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 "jerry-api.h"
|
||||||
|
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
|
||||||
|
#include "byte-code.h"
|
||||||
|
#include "jcontext.h"
|
||||||
|
#include "jerry-debugger.h"
|
||||||
|
#include "jerry-port.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type cast the debugger send buffer into a specific type.
|
||||||
|
*/
|
||||||
|
#define JERRY_DEBUGGER_SEND_BUFFER_AS(type, name_p) \
|
||||||
|
type *name_p = (type *) (&JERRY_CONTEXT (debugger_send_buffer))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type cast the debugger receive buffer into a specific type.
|
||||||
|
*/
|
||||||
|
#define JERRY_DEBUGGER_RECEIVE_BUFFER_AS(type, name_p) \
|
||||||
|
type *name_p = ((type *) recv_buffer_p)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free all unreferenced byte code structures which
|
||||||
|
* were not acknowledged by the debugger client.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
jerry_debugger_free_unreferenced_byte_code (void)
|
||||||
|
{
|
||||||
|
jerry_debugger_byte_code_free_t *byte_code_free_p;
|
||||||
|
|
||||||
|
byte_code_free_p = JMEM_CP_GET_POINTER (jerry_debugger_byte_code_free_t,
|
||||||
|
JERRY_CONTEXT (debugger_byte_code_free_head));
|
||||||
|
|
||||||
|
while (byte_code_free_p != NULL)
|
||||||
|
{
|
||||||
|
jerry_debugger_byte_code_free_t *next_byte_code_free_p;
|
||||||
|
next_byte_code_free_p = JMEM_CP_GET_POINTER (jerry_debugger_byte_code_free_t,
|
||||||
|
byte_code_free_p->next_cp);
|
||||||
|
|
||||||
|
jmem_heap_free_block (byte_code_free_p,
|
||||||
|
((size_t) byte_code_free_p->size) << JMEM_ALIGNMENT_LOG);
|
||||||
|
|
||||||
|
byte_code_free_p = next_byte_code_free_p;
|
||||||
|
}
|
||||||
|
} /* jerry_debugger_free_unreferenced_byte_code */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send backtrace.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
jerry_debugger_send_backtrace (uint8_t *recv_buffer_p) /**< pointer the the received data */
|
||||||
|
{
|
||||||
|
JERRY_DEBUGGER_RECEIVE_BUFFER_AS (jerry_debugger_receive_get_backtrace_t, get_backtrace_p);
|
||||||
|
|
||||||
|
uint32_t max_depth;
|
||||||
|
memcpy (&max_depth, get_backtrace_p->max_depth, sizeof (uint32_t));
|
||||||
|
|
||||||
|
if (max_depth == 0)
|
||||||
|
{
|
||||||
|
max_depth = UINT32_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
JERRY_DEBUGGER_SEND_BUFFER_AS (jerry_debugger_send_backtrace_t, backtrace_p);
|
||||||
|
|
||||||
|
JERRY_DEBUGGER_INIT_SEND_MESSAGE (backtrace_p);
|
||||||
|
JERRY_DEBUGGER_SET_SEND_MESSAGE_SIZE_FROM_TYPE (backtrace_p, jerry_debugger_send_backtrace_t);
|
||||||
|
backtrace_p->type = JERRY_DEBUGGER_BACKTRACE;
|
||||||
|
|
||||||
|
vm_frame_ctx_t *frame_ctx_p = JERRY_CONTEXT (vm_top_context_p);
|
||||||
|
|
||||||
|
uint32_t current_frame = 0;
|
||||||
|
|
||||||
|
while (frame_ctx_p != NULL && max_depth > 0)
|
||||||
|
{
|
||||||
|
if (current_frame >= JERRY_DEBUGGER_MAX_SIZE (jerry_debugger_frame_t))
|
||||||
|
{
|
||||||
|
if (!jerry_debugger_send (sizeof (jerry_debugger_send_backtrace_t)))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
current_frame = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
jerry_debugger_frame_t *frame_p = backtrace_p->frames + current_frame;
|
||||||
|
|
||||||
|
jmem_cpointer_t byte_code_cp;
|
||||||
|
JMEM_CP_SET_NON_NULL_POINTER (byte_code_cp, frame_ctx_p->bytecode_header_p);
|
||||||
|
memcpy (frame_p->byte_code_cp, &byte_code_cp, sizeof (jmem_cpointer_t));
|
||||||
|
|
||||||
|
uint32_t offset = (uint32_t) (frame_ctx_p->byte_code_p - (uint8_t *) frame_ctx_p->bytecode_header_p);
|
||||||
|
memcpy (frame_p->offset, &offset, sizeof (uint32_t));
|
||||||
|
|
||||||
|
frame_ctx_p = frame_ctx_p->prev_context_p;
|
||||||
|
current_frame++;
|
||||||
|
max_depth--;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t message_size = current_frame * sizeof (jerry_debugger_frame_t);
|
||||||
|
|
||||||
|
JERRY_DEBUGGER_SET_SEND_MESSAGE_SIZE (backtrace_p, 1 + message_size);
|
||||||
|
backtrace_p->type = JERRY_DEBUGGER_BACKTRACE_END;
|
||||||
|
|
||||||
|
jerry_debugger_send (sizeof (jerry_debugger_send_type_t) + message_size);
|
||||||
|
} /* jerry_debugger_send_backtrace */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check received packet size.
|
||||||
|
*/
|
||||||
|
#define JERRY_DEBUGGER_CHECK_PACKET_SIZE(type) \
|
||||||
|
if (message_size != sizeof (type)) \
|
||||||
|
{ \
|
||||||
|
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Invalid message size\n"); \
|
||||||
|
jerry_debugger_close_connection (); \
|
||||||
|
return false; \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive message from the client.
|
||||||
|
*
|
||||||
|
* @return true - if message is processed successfully
|
||||||
|
* false - otherwise
|
||||||
|
*/
|
||||||
|
inline bool __attr_always_inline___
|
||||||
|
jerry_debugger_process_message (uint8_t *recv_buffer_p, /**< pointer the the received data */
|
||||||
|
uint32_t message_size, /**< message size */
|
||||||
|
bool *resume_exec_p) /**< pointer to the resume exec flag */
|
||||||
|
{
|
||||||
|
/* Process the received message. */
|
||||||
|
switch (recv_buffer_p[0])
|
||||||
|
{
|
||||||
|
case JERRY_DEBUGGER_FREE_BYTE_CODE_CP:
|
||||||
|
{
|
||||||
|
JERRY_DEBUGGER_CHECK_PACKET_SIZE (jerry_debugger_receive_byte_code_cp_t);
|
||||||
|
|
||||||
|
JERRY_DEBUGGER_RECEIVE_BUFFER_AS (jerry_debugger_receive_byte_code_cp_t, byte_code_p);
|
||||||
|
|
||||||
|
jmem_cpointer_t byte_code_free_cp;
|
||||||
|
memcpy (&byte_code_free_cp, byte_code_p->byte_code_cp, sizeof (jmem_cpointer_t));
|
||||||
|
|
||||||
|
jerry_debugger_byte_code_free_t *byte_code_free_p;
|
||||||
|
byte_code_free_p = JMEM_CP_GET_NON_NULL_POINTER (jerry_debugger_byte_code_free_t,
|
||||||
|
byte_code_free_cp);
|
||||||
|
|
||||||
|
if (JERRY_CONTEXT (debugger_byte_code_free_head) == byte_code_free_cp)
|
||||||
|
{
|
||||||
|
JERRY_CONTEXT (debugger_byte_code_free_head) = byte_code_free_p->next_cp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (byte_code_free_p->prev_cp != ECMA_NULL_POINTER)
|
||||||
|
{
|
||||||
|
jerry_debugger_byte_code_free_t *prev_byte_code_free_p;
|
||||||
|
prev_byte_code_free_p = JMEM_CP_GET_NON_NULL_POINTER (jerry_debugger_byte_code_free_t,
|
||||||
|
byte_code_free_p->prev_cp);
|
||||||
|
|
||||||
|
prev_byte_code_free_p->next_cp = byte_code_free_p->next_cp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (byte_code_free_p->next_cp != ECMA_NULL_POINTER)
|
||||||
|
{
|
||||||
|
jerry_debugger_byte_code_free_t *next_byte_code_free_p;
|
||||||
|
next_byte_code_free_p = JMEM_CP_GET_NON_NULL_POINTER (jerry_debugger_byte_code_free_t,
|
||||||
|
byte_code_free_p->next_cp);
|
||||||
|
|
||||||
|
next_byte_code_free_p->prev_cp = byte_code_free_p->prev_cp;
|
||||||
|
}
|
||||||
|
|
||||||
|
jmem_heap_free_block (byte_code_free_p,
|
||||||
|
((size_t) byte_code_free_p->size) << JMEM_ALIGNMENT_LOG);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case JERRY_DEBUGGER_UPDATE_BREAKPOINT:
|
||||||
|
{
|
||||||
|
JERRY_DEBUGGER_CHECK_PACKET_SIZE (jerry_debugger_receive_update_breakpoint_t);
|
||||||
|
|
||||||
|
JERRY_DEBUGGER_RECEIVE_BUFFER_AS (jerry_debugger_receive_update_breakpoint_t, update_breakpoint_p);
|
||||||
|
|
||||||
|
jmem_cpointer_t byte_code_cp;
|
||||||
|
memcpy (&byte_code_cp, update_breakpoint_p->byte_code_cp, sizeof (jmem_cpointer_t));
|
||||||
|
uint8_t *byte_code_p = JMEM_CP_GET_NON_NULL_POINTER (uint8_t, byte_code_cp);
|
||||||
|
|
||||||
|
uint32_t offset;
|
||||||
|
memcpy (&offset, update_breakpoint_p->offset, sizeof (uint32_t));
|
||||||
|
byte_code_p += offset;
|
||||||
|
|
||||||
|
JERRY_ASSERT (*byte_code_p == CBC_BREAKPOINT_ENABLED || *byte_code_p == CBC_BREAKPOINT_DISABLED);
|
||||||
|
|
||||||
|
*byte_code_p = update_breakpoint_p->is_set_breakpoint ? CBC_BREAKPOINT_ENABLED : CBC_BREAKPOINT_DISABLED;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case JERRY_DEBUGGER_STOP:
|
||||||
|
{
|
||||||
|
JERRY_DEBUGGER_CHECK_PACKET_SIZE (jerry_debugger_receive_type_t);
|
||||||
|
|
||||||
|
JERRY_CONTEXT (debugger_stop_exec) = true;
|
||||||
|
JERRY_CONTEXT (debugger_stop_context) = NULL;
|
||||||
|
*resume_exec_p = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case JERRY_DEBUGGER_CONTINUE:
|
||||||
|
{
|
||||||
|
JERRY_DEBUGGER_CHECK_PACKET_SIZE (jerry_debugger_receive_type_t);
|
||||||
|
|
||||||
|
JERRY_CONTEXT (debugger_stop_exec) = false;
|
||||||
|
JERRY_CONTEXT (debugger_stop_context) = NULL;
|
||||||
|
|
||||||
|
*resume_exec_p = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case JERRY_DEBUGGER_STEP:
|
||||||
|
{
|
||||||
|
JERRY_DEBUGGER_CHECK_PACKET_SIZE (jerry_debugger_receive_type_t);
|
||||||
|
|
||||||
|
JERRY_CONTEXT (debugger_stop_exec) = true;
|
||||||
|
JERRY_CONTEXT (debugger_stop_context) = NULL;
|
||||||
|
*resume_exec_p = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case JERRY_DEBUGGER_NEXT:
|
||||||
|
{
|
||||||
|
JERRY_DEBUGGER_CHECK_PACKET_SIZE (jerry_debugger_receive_type_t);
|
||||||
|
|
||||||
|
JERRY_CONTEXT (debugger_stop_exec) = true;
|
||||||
|
JERRY_CONTEXT (debugger_stop_context) = JERRY_CONTEXT (vm_top_context_p);
|
||||||
|
*resume_exec_p = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case JERRY_DEBUGGER_GET_BACKTRACE:
|
||||||
|
{
|
||||||
|
JERRY_DEBUGGER_CHECK_PACKET_SIZE (jerry_debugger_receive_get_backtrace_t);
|
||||||
|
|
||||||
|
jerry_debugger_send_backtrace (recv_buffer_p);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Unexpected message.");
|
||||||
|
jerry_debugger_close_connection ();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} /* jerry_debugger_process_message */
|
||||||
|
|
||||||
|
#undef JERRY_DEBUGGER_CHECK_PACKET_SIZE
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tell the client that a breakpoint has been hit and wait for further debugger commands.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
jerry_debugger_breakpoint_hit (void)
|
||||||
|
{
|
||||||
|
JERRY_ASSERT (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER);
|
||||||
|
|
||||||
|
JERRY_DEBUGGER_SEND_BUFFER_AS (jerry_debugger_send_breakpoint_hit_t, breakpoint_hit_p);
|
||||||
|
|
||||||
|
JERRY_DEBUGGER_INIT_SEND_MESSAGE (breakpoint_hit_p);
|
||||||
|
JERRY_DEBUGGER_SET_SEND_MESSAGE_SIZE_FROM_TYPE (breakpoint_hit_p, jerry_debugger_send_breakpoint_hit_t);
|
||||||
|
breakpoint_hit_p->type = (uint8_t) JERRY_DEBUGGER_BREAKPOINT_HIT;
|
||||||
|
|
||||||
|
vm_frame_ctx_t *frame_ctx_p = JERRY_CONTEXT (vm_top_context_p);
|
||||||
|
|
||||||
|
jmem_cpointer_t byte_code_header_cp;
|
||||||
|
JMEM_CP_SET_NON_NULL_POINTER (byte_code_header_cp, frame_ctx_p->bytecode_header_p);
|
||||||
|
memcpy (breakpoint_hit_p->byte_code_cp, &byte_code_header_cp, sizeof (jmem_cpointer_t));
|
||||||
|
|
||||||
|
uint32_t offset = (uint32_t) (frame_ctx_p->byte_code_p - (uint8_t *) frame_ctx_p->bytecode_header_p);
|
||||||
|
memcpy (breakpoint_hit_p->offset, &offset, sizeof (uint32_t));
|
||||||
|
|
||||||
|
if (!jerry_debugger_send (sizeof (jerry_debugger_send_breakpoint_hit_t)))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!jerry_debugger_receive ())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
JERRY_CONTEXT (debugger_message_delay) = JERRY_DEBUGGER_MESSAGE_FREQUENCY;
|
||||||
|
} /* jerry_debugger_breakpoint_hit */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the type signal to the client.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
jerry_debugger_send_type (jerry_debugger_header_type_t type) /**< message type */
|
||||||
|
{
|
||||||
|
JERRY_ASSERT (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER);
|
||||||
|
|
||||||
|
JERRY_DEBUGGER_SEND_BUFFER_AS (jerry_debugger_send_type_t, message_type_p);
|
||||||
|
|
||||||
|
JERRY_DEBUGGER_INIT_SEND_MESSAGE (message_type_p);
|
||||||
|
JERRY_DEBUGGER_SET_SEND_MESSAGE_SIZE_FROM_TYPE (message_type_p, jerry_debugger_send_type_t)
|
||||||
|
message_type_p->type = (uint8_t) type;
|
||||||
|
|
||||||
|
jerry_debugger_send (sizeof (jerry_debugger_send_type_t));
|
||||||
|
} /* jerry_debugger_send_type */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the type signal to the client.
|
||||||
|
*
|
||||||
|
* @return true - if the data sent successfully to the debugger client,
|
||||||
|
* false - otherwise
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
jerry_debugger_send_configuration (uint8_t max_message_size) /**< maximum message size */
|
||||||
|
{
|
||||||
|
JERRY_DEBUGGER_SEND_BUFFER_AS (jerry_debugger_send_configuration_t, configuration_p);
|
||||||
|
|
||||||
|
/* Helper structure for endianness check. */
|
||||||
|
union
|
||||||
|
{
|
||||||
|
uint16_t uint16_value; /**< a 16-bit value */
|
||||||
|
uint8_t uint8_value[2]; /**< lower and upper byte of a 16-bit value */
|
||||||
|
} endian_data;
|
||||||
|
|
||||||
|
endian_data.uint16_value = 1;
|
||||||
|
|
||||||
|
JERRY_DEBUGGER_INIT_SEND_MESSAGE (configuration_p);
|
||||||
|
JERRY_DEBUGGER_SET_SEND_MESSAGE_SIZE_FROM_TYPE (configuration_p, jerry_debugger_send_configuration_t);
|
||||||
|
configuration_p->type = JERRY_DEBUGGER_CONFIGURATION;
|
||||||
|
configuration_p->max_message_size = max_message_size;
|
||||||
|
configuration_p->cpointer_size = sizeof (jmem_cpointer_t);
|
||||||
|
configuration_p->little_endian = (endian_data.uint8_value[0] == 1);
|
||||||
|
|
||||||
|
return jerry_debugger_send (sizeof (jerry_debugger_send_configuration_t));
|
||||||
|
} /* jerry_debugger_send_configuration */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send raw data to the debugger client.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
jerry_debugger_send_data (jerry_debugger_header_type_t type, /**< message type */
|
||||||
|
const void *data, /**< raw data */
|
||||||
|
size_t size) /**< size of data */
|
||||||
|
{
|
||||||
|
JERRY_ASSERT (size < JERRY_DEBUGGER_MAX_SIZE (uint8_t));
|
||||||
|
|
||||||
|
JERRY_DEBUGGER_SEND_BUFFER_AS (jerry_debugger_send_type_t, message_type_p);
|
||||||
|
|
||||||
|
JERRY_DEBUGGER_INIT_SEND_MESSAGE (message_type_p);
|
||||||
|
JERRY_DEBUGGER_SET_SEND_MESSAGE_SIZE (message_type_p, 1 + size);
|
||||||
|
message_type_p->type = type;
|
||||||
|
memcpy (message_type_p + 1, data, size);
|
||||||
|
|
||||||
|
jerry_debugger_send (sizeof (jerry_debugger_send_type_t) + size);
|
||||||
|
} /* jerry_debugger_send_data */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send string to the debugger client.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
jerry_debugger_send_string (uint8_t message_type, /**< message type */
|
||||||
|
const jerry_char_t *string_p, /**< string data */
|
||||||
|
size_t string_length) /**< length of string */
|
||||||
|
{
|
||||||
|
JERRY_ASSERT (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER);
|
||||||
|
|
||||||
|
const size_t max_fragment_len = JERRY_DEBUGGER_MAX_SIZE (char);
|
||||||
|
|
||||||
|
JERRY_DEBUGGER_SEND_BUFFER_AS (jerry_debugger_send_string_t, message_string_p);
|
||||||
|
|
||||||
|
JERRY_DEBUGGER_INIT_SEND_MESSAGE (message_string_p);
|
||||||
|
JERRY_DEBUGGER_SET_SEND_MESSAGE_SIZE_FROM_TYPE (message_string_p, jerry_debugger_send_string_t);
|
||||||
|
message_string_p->type = message_type;
|
||||||
|
|
||||||
|
while (string_length > max_fragment_len)
|
||||||
|
{
|
||||||
|
memcpy (message_string_p->string, string_p, max_fragment_len);
|
||||||
|
|
||||||
|
if (!jerry_debugger_send (sizeof (jerry_debugger_send_string_t)))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string_length -= max_fragment_len;
|
||||||
|
string_p += max_fragment_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
JERRY_DEBUGGER_SET_SEND_MESSAGE_SIZE (message_string_p, 1 + string_length);
|
||||||
|
message_string_p->type = (uint8_t) (message_type + 1);
|
||||||
|
|
||||||
|
memcpy (message_string_p->string, string_p, string_length);
|
||||||
|
|
||||||
|
jerry_debugger_send (sizeof (jerry_debugger_send_type_t) + string_length);
|
||||||
|
} /* jerry_debugger_send_string */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the function name to the debugger client.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
jerry_debugger_send_function_name (const jerry_char_t *function_name_p, /**< function name */
|
||||||
|
size_t function_name_length) /**< length of function name */
|
||||||
|
{
|
||||||
|
JERRY_ASSERT (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER);
|
||||||
|
|
||||||
|
jerry_debugger_send_string (JERRY_DEBUGGER_FUNCTION_NAME, function_name_p, function_name_length);
|
||||||
|
} /* jerry_debugger_send_function_name */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the function compressed pointer to the debugger client.
|
||||||
|
*
|
||||||
|
* @return true - if the data was sent successfully to the debugger client,
|
||||||
|
* false - otherwise
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
jerry_debugger_send_function_cp (jerry_debugger_header_type_t type, /**< message type */
|
||||||
|
ecma_compiled_code_t *compiled_code_p) /**< byte code pointer */
|
||||||
|
{
|
||||||
|
JERRY_ASSERT (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER);
|
||||||
|
|
||||||
|
JERRY_DEBUGGER_SEND_BUFFER_AS (jerry_debugger_send_byte_code_cp_t, byte_code_cp_p);
|
||||||
|
|
||||||
|
JERRY_DEBUGGER_INIT_SEND_MESSAGE (byte_code_cp_p);
|
||||||
|
JERRY_DEBUGGER_SET_SEND_MESSAGE_SIZE_FROM_TYPE (byte_code_cp_p, jerry_debugger_send_byte_code_cp_t);
|
||||||
|
byte_code_cp_p->type = (uint8_t) type;
|
||||||
|
|
||||||
|
jmem_cpointer_t compiled_code_cp;
|
||||||
|
JMEM_CP_SET_NON_NULL_POINTER (compiled_code_cp, compiled_code_p);
|
||||||
|
memcpy (byte_code_cp_p->byte_code_cp, &compiled_code_cp, sizeof (jmem_cpointer_t));
|
||||||
|
|
||||||
|
return jerry_debugger_send (sizeof (jerry_debugger_send_byte_code_cp_t));
|
||||||
|
} /* jerry_debugger_send_function_cp */
|
||||||
|
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
/* 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 JERRY_DEBUGGER_H
|
||||||
|
#define JERRY_DEBUGGER_H
|
||||||
|
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
|
||||||
|
#include "jerry-debugger-ws.h"
|
||||||
|
#include "ecma-globals.h"
|
||||||
|
|
||||||
|
/* JerryScript debugger protocol is a simplified version of RFC-6455 (WebSockets). */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frequency of calling jerry_debugger_receive() by the VM.
|
||||||
|
*/
|
||||||
|
#define JERRY_DEBUGGER_MESSAGE_FREQUENCY 5
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
#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 */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the maximum number of items for a given type
|
||||||
|
* which can be transmitted by one message.
|
||||||
|
*/
|
||||||
|
#define JERRY_DEBUGGER_MAX_SIZE(type) \
|
||||||
|
((JERRY_DEBUGGER_MAX_BUFFER_SIZE - sizeof (jerry_debugger_send_header_t) - 1) / sizeof (type))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Types for the package.
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
/* Messages sent by the server to client. */
|
||||||
|
JERRY_DEBUGGER_CONFIGURATION = 1, /**< debugger configuration */
|
||||||
|
JERRY_DEBUGGER_PARSE_ERROR = 2, /**< parse error */
|
||||||
|
JERRY_DEBUGGER_BYTE_CODE_CP = 3, /**< byte code compressed pointer */
|
||||||
|
JERRY_DEBUGGER_PARSE_FUNCTION = 4, /**< parsing a new function */
|
||||||
|
JERRY_DEBUGGER_BREAKPOINT_LIST = 5, /**< list of line offsets */
|
||||||
|
JERRY_DEBUGGER_BREAKPOINT_OFFSET_LIST = 6, /**< list of byte code offsets */
|
||||||
|
JERRY_DEBUGGER_RESOURCE_NAME = 7, /**< resource name fragment */
|
||||||
|
JERRY_DEBUGGER_RESOURCE_NAME_END = 8, /**< resource name fragment */
|
||||||
|
JERRY_DEBUGGER_FUNCTION_NAME = 9, /**< function name fragment */
|
||||||
|
JERRY_DEBUGGER_FUNCTION_NAME_END = 10, /**< function name fragment */
|
||||||
|
JERRY_DEBUGGER_RELEASE_BYTE_CODE_CP = 11, /**< invalidate byte code compressed pointer */
|
||||||
|
JERRY_DEBUGGER_BREAKPOINT_HIT = 12, /**< notify breakpoint hit */
|
||||||
|
JERRY_DEBUGGER_BACKTRACE = 13, /**< backtrace data */
|
||||||
|
JERRY_DEBUGGER_BACKTRACE_END = 14, /**< last backtrace data */
|
||||||
|
|
||||||
|
/* Messages sent by the client to server. */
|
||||||
|
JERRY_DEBUGGER_FREE_BYTE_CODE_CP = 1, /**< free byte code compressed pointer */
|
||||||
|
JERRY_DEBUGGER_UPDATE_BREAKPOINT = 2, /**< update breakpoint status */
|
||||||
|
JERRY_DEBUGGER_STOP = 3, /**< stop execution */
|
||||||
|
JERRY_DEBUGGER_CONTINUE = 4, /**< continue execution */
|
||||||
|
JERRY_DEBUGGER_STEP = 5, /**< next breakpoint, step into functions */
|
||||||
|
JERRY_DEBUGGER_NEXT = 6, /**< next breakpoint in the same context */
|
||||||
|
JERRY_DEBUGGER_GET_BACKTRACE = 7, /**< get backtrace */
|
||||||
|
} jerry_debugger_header_type_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delayed free of byte code data.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint16_t size;
|
||||||
|
jmem_cpointer_t prev_cp;
|
||||||
|
jmem_cpointer_t next_cp;
|
||||||
|
} jerry_debugger_byte_code_free_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Outgoing message: JerryScript configuration.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
jerry_debugger_send_header_t header; /**< message header */
|
||||||
|
uint8_t type; /**< type of the message */
|
||||||
|
uint8_t max_message_size; /**< maximum incoming message size */
|
||||||
|
uint8_t cpointer_size; /**< size of compressed pointers */
|
||||||
|
uint8_t little_endian; /**< little endian machine */
|
||||||
|
} jerry_debugger_send_configuration_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Outgoing message: message without arguments.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
jerry_debugger_send_header_t header; /**< message header */
|
||||||
|
uint8_t type; /**< type of the message */
|
||||||
|
} jerry_debugger_send_type_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Incoming message: message without arguments.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t type; /**< type of the message */
|
||||||
|
} jerry_debugger_receive_type_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Outgoing message: string (Source file name or function name).
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
jerry_debugger_send_header_t header; /**< message header */
|
||||||
|
uint8_t type; /**< type of the message */
|
||||||
|
uint8_t string[JERRY_DEBUGGER_MAX_SIZE (uint8_t)]; /**< string data */
|
||||||
|
} jerry_debugger_send_string_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Outgoing message: byte code compressed pointer.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
jerry_debugger_send_header_t header; /**< message header */
|
||||||
|
uint8_t type; /**< type of the message */
|
||||||
|
uint8_t byte_code_cp[sizeof (jmem_cpointer_t)]; /**< byte code compressed pointer */
|
||||||
|
} jerry_debugger_send_byte_code_cp_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Incoming message: byte code compressed pointer.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t type; /**< type of the message */
|
||||||
|
uint8_t byte_code_cp[sizeof (jmem_cpointer_t)]; /**< byte code compressed pointer */
|
||||||
|
} jerry_debugger_receive_byte_code_cp_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Incoming message: update (enable/disable) breakpoint status.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t type; /**< type of the message */
|
||||||
|
uint8_t is_set_breakpoint; /**< set or clear breakpoint */
|
||||||
|
uint8_t byte_code_cp[sizeof (jmem_cpointer_t)]; /**< byte code compressed pointer */
|
||||||
|
uint8_t offset[sizeof (uint32_t)]; /**< breakpoint offset */
|
||||||
|
} jerry_debugger_receive_update_breakpoint_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Outgoing message: notify breakpoint hit.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
jerry_debugger_send_header_t header; /**< message header */
|
||||||
|
uint8_t type; /**< type of the message */
|
||||||
|
uint8_t byte_code_cp[sizeof (jmem_cpointer_t)]; /**< byte code compressed pointer */
|
||||||
|
uint8_t offset[sizeof (uint32_t)]; /**< breakpoint offset */
|
||||||
|
} jerry_debugger_send_breakpoint_hit_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stack frame descriptor for sending backtrace information.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t byte_code_cp[sizeof (jmem_cpointer_t)]; /**< byte code compressed pointer */
|
||||||
|
uint8_t offset[sizeof (uint32_t)]; /**< last breakpoint offset */
|
||||||
|
} jerry_debugger_frame_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Outgoing message: backtrace information.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
jerry_debugger_send_header_t header; /**< message header */
|
||||||
|
uint8_t type; /**< type of the message */
|
||||||
|
jerry_debugger_frame_t frames[JERRY_DEBUGGER_MAX_SIZE (jerry_debugger_frame_t)]; /**< frames */
|
||||||
|
} jerry_debugger_send_backtrace_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Incoming message: get backtrace.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t type; /**< type of the message */
|
||||||
|
uint8_t max_depth[sizeof (uint32_t)]; /**< maximum depth (0 - unlimited) */
|
||||||
|
} jerry_debugger_receive_get_backtrace_t;
|
||||||
|
|
||||||
|
void jerry_debugger_free_unreferenced_byte_code (void);
|
||||||
|
|
||||||
|
bool jerry_debugger_process_message (uint8_t *recv_buffer_p, uint32_t message_size, bool *resume_exec_p);
|
||||||
|
void jerry_debugger_breakpoint_hit (void);
|
||||||
|
|
||||||
|
void jerry_debugger_send_type (jerry_debugger_header_type_t type);
|
||||||
|
bool jerry_debugger_send_configuration (uint8_t max_message_size);
|
||||||
|
void jerry_debugger_send_data (jerry_debugger_header_type_t type, const void *data, size_t size);
|
||||||
|
void jerry_debugger_send_string (uint8_t message_type, const jerry_char_t *string_p, size_t string_length);
|
||||||
|
void jerry_debugger_send_function_name (const jerry_char_t *function_name_p, size_t function_name_length);
|
||||||
|
bool jerry_debugger_send_function_cp (jerry_debugger_header_type_t type, ecma_compiled_code_t *compiled_code_p);
|
||||||
|
void jerry_debugger_send_source_file_name (const jerry_char_t *file_name_p, size_t file_name_length);
|
||||||
|
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
|
|
||||||
|
#endif /* JERRY_DEBUGGER_H */
|
||||||
@@ -0,0 +1,370 @@
|
|||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FIPS-180-1 compliant SHA-1 implementation
|
||||||
|
*
|
||||||
|
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This file is part of mbed TLS (https://tls.mbed.org)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The SHA-1 standard was published by NIST in 1993.
|
||||||
|
*
|
||||||
|
* http://www.itl.nist.gov/fipspubs/fip180-1.htm
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
|
||||||
|
#include "jerry-debugger.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SHA-1 context structure.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t total[2]; /**< number of bytes processed */
|
||||||
|
uint32_t state[5]; /**< intermediate digest state */
|
||||||
|
uint8_t buffer[64]; /**< data block being processed */
|
||||||
|
} jerry_sha1_context;
|
||||||
|
|
||||||
|
/* 32-bit integer manipulation macros (big endian). */
|
||||||
|
|
||||||
|
#define JERRY_SHA1_GET_UINT32_BE(n, b, i) \
|
||||||
|
{ \
|
||||||
|
(n) = (((uint32_t) (b)[(i) + 0]) << 24) \
|
||||||
|
| (((uint32_t) (b)[(i) + 1]) << 16) \
|
||||||
|
| (((uint32_t) (b)[(i) + 2]) << 8) \
|
||||||
|
| ((uint32_t) (b)[(i) + 3]); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define JERRY_SHA1_PUT_UINT32_BE(n, b, i) \
|
||||||
|
{ \
|
||||||
|
(b)[(i) + 0] = (uint8_t) ((n) >> 24); \
|
||||||
|
(b)[(i) + 1] = (uint8_t) ((n) >> 16); \
|
||||||
|
(b)[(i) + 2] = (uint8_t) ((n) >> 8); \
|
||||||
|
(b)[(i) + 3] = (uint8_t) ((n)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize SHA-1 context.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
jerry_sha1_init (jerry_sha1_context *sha1_context_p) /**< SHA-1 context */
|
||||||
|
{
|
||||||
|
memset (sha1_context_p, 0, sizeof (jerry_sha1_context));
|
||||||
|
|
||||||
|
sha1_context_p->total[0] = 0;
|
||||||
|
sha1_context_p->total[1] = 0;
|
||||||
|
|
||||||
|
sha1_context_p->state[0] = 0x67452301;
|
||||||
|
sha1_context_p->state[1] = 0xEFCDAB89;
|
||||||
|
sha1_context_p->state[2] = 0x98BADCFE;
|
||||||
|
sha1_context_p->state[3] = 0x10325476;
|
||||||
|
sha1_context_p->state[4] = 0xC3D2E1F0;
|
||||||
|
} /* jerry_sha1_init */
|
||||||
|
|
||||||
|
#define JERRY_SHA1_P(a, b, c, d, e, x) \
|
||||||
|
do { \
|
||||||
|
e += JERRY_SHA1_SHIFT (a, 5) + JERRY_SHA1_F (b, c, d) + K + x; \
|
||||||
|
b = JERRY_SHA1_SHIFT (b, 30); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update SHA-1 internal buffer status.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
jerry_sha1_process (jerry_sha1_context *sha1_context_p, /**< SHA-1 context */
|
||||||
|
const uint8_t data[64]) /**< data buffer */
|
||||||
|
{
|
||||||
|
uint32_t temp, W[16], A, B, C, D, E;
|
||||||
|
|
||||||
|
JERRY_SHA1_GET_UINT32_BE (W[0], data, 0);
|
||||||
|
JERRY_SHA1_GET_UINT32_BE (W[1], data, 4);
|
||||||
|
JERRY_SHA1_GET_UINT32_BE (W[2], data, 8);
|
||||||
|
JERRY_SHA1_GET_UINT32_BE (W[3], data, 12);
|
||||||
|
JERRY_SHA1_GET_UINT32_BE (W[4], data, 16);
|
||||||
|
JERRY_SHA1_GET_UINT32_BE (W[5], data, 20);
|
||||||
|
JERRY_SHA1_GET_UINT32_BE (W[6], data, 24);
|
||||||
|
JERRY_SHA1_GET_UINT32_BE (W[7], data, 28);
|
||||||
|
JERRY_SHA1_GET_UINT32_BE (W[8], data, 32);
|
||||||
|
JERRY_SHA1_GET_UINT32_BE (W[9], data, 36);
|
||||||
|
JERRY_SHA1_GET_UINT32_BE (W[10], data, 40);
|
||||||
|
JERRY_SHA1_GET_UINT32_BE (W[11], data, 44);
|
||||||
|
JERRY_SHA1_GET_UINT32_BE (W[12], data, 48);
|
||||||
|
JERRY_SHA1_GET_UINT32_BE (W[13], data, 52);
|
||||||
|
JERRY_SHA1_GET_UINT32_BE (W[14], data, 56);
|
||||||
|
JERRY_SHA1_GET_UINT32_BE (W[15], data, 60);
|
||||||
|
|
||||||
|
#define JERRY_SHA1_SHIFT(x, n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
|
||||||
|
|
||||||
|
#define JERRY_SHA1_R(t) \
|
||||||
|
( \
|
||||||
|
temp = W[(t - 3) & 0x0F] ^ W[(t - 8) & 0x0F] ^ W[(t - 14) & 0x0F] ^ W[t & 0x0F], \
|
||||||
|
W[t & 0x0F] = JERRY_SHA1_SHIFT (temp, 1) \
|
||||||
|
)
|
||||||
|
|
||||||
|
A = sha1_context_p->state[0];
|
||||||
|
B = sha1_context_p->state[1];
|
||||||
|
C = sha1_context_p->state[2];
|
||||||
|
D = sha1_context_p->state[3];
|
||||||
|
E = sha1_context_p->state[4];
|
||||||
|
|
||||||
|
uint32_t K = 0x5A827999;
|
||||||
|
|
||||||
|
#define JERRY_SHA1_F(x, y, z) (z ^ (x & (y ^ z)))
|
||||||
|
|
||||||
|
JERRY_SHA1_P (A, B, C, D, E, W[0]);
|
||||||
|
JERRY_SHA1_P (E, A, B, C, D, W[1]);
|
||||||
|
JERRY_SHA1_P (D, E, A, B, C, W[2]);
|
||||||
|
JERRY_SHA1_P (C, D, E, A, B, W[3]);
|
||||||
|
JERRY_SHA1_P (B, C, D, E, A, W[4]);
|
||||||
|
JERRY_SHA1_P (A, B, C, D, E, W[5]);
|
||||||
|
JERRY_SHA1_P (E, A, B, C, D, W[6]);
|
||||||
|
JERRY_SHA1_P (D, E, A, B, C, W[7]);
|
||||||
|
JERRY_SHA1_P (C, D, E, A, B, W[8]);
|
||||||
|
JERRY_SHA1_P (B, C, D, E, A, W[9]);
|
||||||
|
JERRY_SHA1_P (A, B, C, D, E, W[10]);
|
||||||
|
JERRY_SHA1_P (E, A, B, C, D, W[11]);
|
||||||
|
JERRY_SHA1_P (D, E, A, B, C, W[12]);
|
||||||
|
JERRY_SHA1_P (C, D, E, A, B, W[13]);
|
||||||
|
JERRY_SHA1_P (B, C, D, E, A, W[14]);
|
||||||
|
JERRY_SHA1_P (A, B, C, D, E, W[15]);
|
||||||
|
JERRY_SHA1_P (E, A, B, C, D, JERRY_SHA1_R (16));
|
||||||
|
JERRY_SHA1_P (D, E, A, B, C, JERRY_SHA1_R (17));
|
||||||
|
JERRY_SHA1_P (C, D, E, A, B, JERRY_SHA1_R (18));
|
||||||
|
JERRY_SHA1_P (B, C, D, E, A, JERRY_SHA1_R (19));
|
||||||
|
|
||||||
|
#undef JERRY_SHA1_F
|
||||||
|
|
||||||
|
K = 0x6ED9EBA1;
|
||||||
|
|
||||||
|
#define JERRY_SHA1_F(x, y, z) (x ^ y ^ z)
|
||||||
|
|
||||||
|
JERRY_SHA1_P (A, B, C, D, E, JERRY_SHA1_R (20));
|
||||||
|
JERRY_SHA1_P (E, A, B, C, D, JERRY_SHA1_R (21));
|
||||||
|
JERRY_SHA1_P (D, E, A, B, C, JERRY_SHA1_R (22));
|
||||||
|
JERRY_SHA1_P (C, D, E, A, B, JERRY_SHA1_R (23));
|
||||||
|
JERRY_SHA1_P (B, C, D, E, A, JERRY_SHA1_R (24));
|
||||||
|
JERRY_SHA1_P (A, B, C, D, E, JERRY_SHA1_R (25));
|
||||||
|
JERRY_SHA1_P (E, A, B, C, D, JERRY_SHA1_R (26));
|
||||||
|
JERRY_SHA1_P (D, E, A, B, C, JERRY_SHA1_R (27));
|
||||||
|
JERRY_SHA1_P (C, D, E, A, B, JERRY_SHA1_R (28));
|
||||||
|
JERRY_SHA1_P (B, C, D, E, A, JERRY_SHA1_R (29));
|
||||||
|
JERRY_SHA1_P (A, B, C, D, E, JERRY_SHA1_R (30));
|
||||||
|
JERRY_SHA1_P (E, A, B, C, D, JERRY_SHA1_R (31));
|
||||||
|
JERRY_SHA1_P (D, E, A, B, C, JERRY_SHA1_R (32));
|
||||||
|
JERRY_SHA1_P (C, D, E, A, B, JERRY_SHA1_R (33));
|
||||||
|
JERRY_SHA1_P (B, C, D, E, A, JERRY_SHA1_R (34));
|
||||||
|
JERRY_SHA1_P (A, B, C, D, E, JERRY_SHA1_R (35));
|
||||||
|
JERRY_SHA1_P (E, A, B, C, D, JERRY_SHA1_R (36));
|
||||||
|
JERRY_SHA1_P (D, E, A, B, C, JERRY_SHA1_R (37));
|
||||||
|
JERRY_SHA1_P (C, D, E, A, B, JERRY_SHA1_R (38));
|
||||||
|
JERRY_SHA1_P (B, C, D, E, A, JERRY_SHA1_R (39));
|
||||||
|
|
||||||
|
#undef JERRY_SHA1_F
|
||||||
|
|
||||||
|
K = 0x8F1BBCDC;
|
||||||
|
|
||||||
|
#define JERRY_SHA1_F(x, y, z) ((x & y) | (z & (x | y)))
|
||||||
|
|
||||||
|
JERRY_SHA1_P (A, B, C, D, E, JERRY_SHA1_R (40));
|
||||||
|
JERRY_SHA1_P (E, A, B, C, D, JERRY_SHA1_R (41));
|
||||||
|
JERRY_SHA1_P (D, E, A, B, C, JERRY_SHA1_R (42));
|
||||||
|
JERRY_SHA1_P (C, D, E, A, B, JERRY_SHA1_R (43));
|
||||||
|
JERRY_SHA1_P (B, C, D, E, A, JERRY_SHA1_R (44));
|
||||||
|
JERRY_SHA1_P (A, B, C, D, E, JERRY_SHA1_R (45));
|
||||||
|
JERRY_SHA1_P (E, A, B, C, D, JERRY_SHA1_R (46));
|
||||||
|
JERRY_SHA1_P (D, E, A, B, C, JERRY_SHA1_R (47));
|
||||||
|
JERRY_SHA1_P (C, D, E, A, B, JERRY_SHA1_R (48));
|
||||||
|
JERRY_SHA1_P (B, C, D, E, A, JERRY_SHA1_R (49));
|
||||||
|
JERRY_SHA1_P (A, B, C, D, E, JERRY_SHA1_R (50));
|
||||||
|
JERRY_SHA1_P (E, A, B, C, D, JERRY_SHA1_R (51));
|
||||||
|
JERRY_SHA1_P (D, E, A, B, C, JERRY_SHA1_R (52));
|
||||||
|
JERRY_SHA1_P (C, D, E, A, B, JERRY_SHA1_R (53));
|
||||||
|
JERRY_SHA1_P (B, C, D, E, A, JERRY_SHA1_R (54));
|
||||||
|
JERRY_SHA1_P (A, B, C, D, E, JERRY_SHA1_R (55));
|
||||||
|
JERRY_SHA1_P (E, A, B, C, D, JERRY_SHA1_R (56));
|
||||||
|
JERRY_SHA1_P (D, E, A, B, C, JERRY_SHA1_R (57));
|
||||||
|
JERRY_SHA1_P (C, D, E, A, B, JERRY_SHA1_R (58));
|
||||||
|
JERRY_SHA1_P (B, C, D, E, A, JERRY_SHA1_R (59));
|
||||||
|
|
||||||
|
#undef JERRY_SHA1_F
|
||||||
|
|
||||||
|
K = 0xCA62C1D6;
|
||||||
|
|
||||||
|
#define JERRY_SHA1_F(x, y, z) (x ^ y ^ z)
|
||||||
|
|
||||||
|
JERRY_SHA1_P (A, B, C, D, E, JERRY_SHA1_R (60));
|
||||||
|
JERRY_SHA1_P (E, A, B, C, D, JERRY_SHA1_R (61));
|
||||||
|
JERRY_SHA1_P (D, E, A, B, C, JERRY_SHA1_R (62));
|
||||||
|
JERRY_SHA1_P (C, D, E, A, B, JERRY_SHA1_R (63));
|
||||||
|
JERRY_SHA1_P (B, C, D, E, A, JERRY_SHA1_R (64));
|
||||||
|
JERRY_SHA1_P (A, B, C, D, E, JERRY_SHA1_R (65));
|
||||||
|
JERRY_SHA1_P (E, A, B, C, D, JERRY_SHA1_R (66));
|
||||||
|
JERRY_SHA1_P (D, E, A, B, C, JERRY_SHA1_R (67));
|
||||||
|
JERRY_SHA1_P (C, D, E, A, B, JERRY_SHA1_R (68));
|
||||||
|
JERRY_SHA1_P (B, C, D, E, A, JERRY_SHA1_R (69));
|
||||||
|
JERRY_SHA1_P (A, B, C, D, E, JERRY_SHA1_R (70));
|
||||||
|
JERRY_SHA1_P (E, A, B, C, D, JERRY_SHA1_R (71));
|
||||||
|
JERRY_SHA1_P (D, E, A, B, C, JERRY_SHA1_R (72));
|
||||||
|
JERRY_SHA1_P (C, D, E, A, B, JERRY_SHA1_R (73));
|
||||||
|
JERRY_SHA1_P (B, C, D, E, A, JERRY_SHA1_R (74));
|
||||||
|
JERRY_SHA1_P (A, B, C, D, E, JERRY_SHA1_R (75));
|
||||||
|
JERRY_SHA1_P (E, A, B, C, D, JERRY_SHA1_R (76));
|
||||||
|
JERRY_SHA1_P (D, E, A, B, C, JERRY_SHA1_R (77));
|
||||||
|
JERRY_SHA1_P (C, D, E, A, B, JERRY_SHA1_R (78));
|
||||||
|
JERRY_SHA1_P (B, C, D, E, A, JERRY_SHA1_R (79));
|
||||||
|
|
||||||
|
#undef JERRY_SHA1_F
|
||||||
|
|
||||||
|
sha1_context_p->state[0] += A;
|
||||||
|
sha1_context_p->state[1] += B;
|
||||||
|
sha1_context_p->state[2] += C;
|
||||||
|
sha1_context_p->state[3] += D;
|
||||||
|
sha1_context_p->state[4] += E;
|
||||||
|
|
||||||
|
#undef JERRY_SHA1_SHIFT
|
||||||
|
#undef JERRY_SHA1_R
|
||||||
|
} /* jerry_sha1_process */
|
||||||
|
|
||||||
|
#undef JERRY_SHA1_P
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SHA-1 update buffer.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
jerry_sha1_update (jerry_sha1_context *sha1_context_p, /**< SHA-1 context */
|
||||||
|
const uint8_t *source_p, /**< source buffer */
|
||||||
|
size_t source_length) /**< length of source buffer */
|
||||||
|
{
|
||||||
|
size_t fill;
|
||||||
|
uint32_t left;
|
||||||
|
|
||||||
|
if (source_length == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
left = sha1_context_p->total[0] & 0x3F;
|
||||||
|
fill = 64 - left;
|
||||||
|
|
||||||
|
sha1_context_p->total[0] += (uint32_t) source_length;
|
||||||
|
|
||||||
|
/* Check overflow. */
|
||||||
|
if (sha1_context_p->total[0] < (uint32_t) source_length)
|
||||||
|
{
|
||||||
|
sha1_context_p->total[1]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left && source_length >= fill)
|
||||||
|
{
|
||||||
|
memcpy ((void *) (sha1_context_p->buffer + left), source_p, fill);
|
||||||
|
jerry_sha1_process (sha1_context_p, sha1_context_p->buffer);
|
||||||
|
source_p += fill;
|
||||||
|
source_length -= fill;
|
||||||
|
left = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (source_length >= 64)
|
||||||
|
{
|
||||||
|
jerry_sha1_process (sha1_context_p, source_p);
|
||||||
|
source_p += 64;
|
||||||
|
source_length -= 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source_length > 0)
|
||||||
|
{
|
||||||
|
memcpy ((void *) (sha1_context_p->buffer + left), source_p, source_length);
|
||||||
|
}
|
||||||
|
} /* jerry_sha1_update */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SHA-1 final digest.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
jerry_sha1_finish (jerry_sha1_context *sha1_context_p, /**< SHA-1 context */
|
||||||
|
uint8_t destination_p[20]) /**< result */
|
||||||
|
{
|
||||||
|
uint8_t buffer[16];
|
||||||
|
|
||||||
|
uint32_t high = (sha1_context_p->total[0] >> 29) | (sha1_context_p->total[1] << 3);
|
||||||
|
uint32_t low = (sha1_context_p->total[0] << 3);
|
||||||
|
|
||||||
|
uint32_t last = sha1_context_p->total[0] & 0x3F;
|
||||||
|
uint32_t padn = (last < 56) ? (56 - last) : (120 - last);
|
||||||
|
|
||||||
|
memset (buffer, 0, sizeof (buffer));
|
||||||
|
buffer[0] = 0x80;
|
||||||
|
|
||||||
|
while (padn > sizeof (buffer))
|
||||||
|
{
|
||||||
|
jerry_sha1_update (sha1_context_p, buffer, sizeof (buffer));
|
||||||
|
buffer[0] = 0;
|
||||||
|
padn -= (uint32_t) sizeof (buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
jerry_sha1_update (sha1_context_p, buffer, padn);
|
||||||
|
|
||||||
|
JERRY_SHA1_PUT_UINT32_BE (high, buffer, 0);
|
||||||
|
JERRY_SHA1_PUT_UINT32_BE (low, buffer, 4);
|
||||||
|
|
||||||
|
jerry_sha1_update (sha1_context_p, buffer, 8);
|
||||||
|
|
||||||
|
JERRY_SHA1_PUT_UINT32_BE (sha1_context_p->state[0], destination_p, 0);
|
||||||
|
JERRY_SHA1_PUT_UINT32_BE (sha1_context_p->state[1], destination_p, 4);
|
||||||
|
JERRY_SHA1_PUT_UINT32_BE (sha1_context_p->state[2], destination_p, 8);
|
||||||
|
JERRY_SHA1_PUT_UINT32_BE (sha1_context_p->state[3], destination_p, 12);
|
||||||
|
JERRY_SHA1_PUT_UINT32_BE (sha1_context_p->state[4], destination_p, 16);
|
||||||
|
} /* jerry_sha1_finish */
|
||||||
|
|
||||||
|
#undef JERRY_SHA1_GET_UINT32_BE
|
||||||
|
#undef JERRY_SHA1_PUT_UINT32_BE
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the SHA-1 value of the combination of the two input buffers.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
jerry_debugger_compute_sha1 (const uint8_t *source1_p, /**< first part of the input */
|
||||||
|
size_t source1_length, /**< length of the first part */
|
||||||
|
const uint8_t *source2_p, /**< second part of the input */
|
||||||
|
size_t source2_length, /**< length of the second part */
|
||||||
|
uint8_t destination_p[20]) /**< result */
|
||||||
|
{
|
||||||
|
JMEM_DEFINE_LOCAL_ARRAY (sha1_context_p, 1, jerry_sha1_context);
|
||||||
|
|
||||||
|
jerry_sha1_init (sha1_context_p);
|
||||||
|
jerry_sha1_update (sha1_context_p, source1_p, source1_length);
|
||||||
|
jerry_sha1_update (sha1_context_p, source2_p, source2_length);
|
||||||
|
jerry_sha1_finish (sha1_context_p, destination_p);
|
||||||
|
|
||||||
|
JMEM_FINALIZE_LOCAL_ARRAY (sha1_context_p);
|
||||||
|
} /* jerry_debugger_compute_sha1 */
|
||||||
|
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
@@ -19,6 +19,10 @@
|
|||||||
#include "ecma-helpers.h"
|
#include "ecma-helpers.h"
|
||||||
#include "ecma-lcache.h"
|
#include "ecma-lcache.h"
|
||||||
#include "ecma-property-hashmap.h"
|
#include "ecma-property-hashmap.h"
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
#include "jcontext.h"
|
||||||
|
#include "jerry-debugger.h"
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
#include "jrt-bit-fields.h"
|
#include "jrt-bit-fields.h"
|
||||||
#include "byte-code.h"
|
#include "byte-code.h"
|
||||||
#include "re-compiler.h"
|
#include "re-compiler.h"
|
||||||
@@ -1470,6 +1474,37 @@ ecma_bytecode_deref (ecma_compiled_code_t *bytecode_p) /**< byte code pointer */
|
|||||||
ecma_bytecode_deref (bytecode_literal_p);
|
ecma_bytecode_deref (bytecode_literal_p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
if (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER)
|
||||||
|
{
|
||||||
|
if (jerry_debugger_send_function_cp (JERRY_DEBUGGER_RELEASE_BYTE_CODE_CP, bytecode_p))
|
||||||
|
{
|
||||||
|
/* Delay the byte code free until the debugger client is notified.
|
||||||
|
* If the connection is aborted the pointer is still freed by
|
||||||
|
* jerry_debugger_close_connection(). */
|
||||||
|
jerry_debugger_byte_code_free_t *byte_code_free_p = (jerry_debugger_byte_code_free_t *) bytecode_p;
|
||||||
|
jmem_cpointer_t byte_code_free_head = JERRY_CONTEXT (debugger_byte_code_free_head);
|
||||||
|
|
||||||
|
byte_code_free_p->prev_cp = ECMA_NULL_POINTER;
|
||||||
|
byte_code_free_p->next_cp = byte_code_free_head;
|
||||||
|
|
||||||
|
JMEM_CP_SET_NON_NULL_POINTER (JERRY_CONTEXT (debugger_byte_code_free_head),
|
||||||
|
byte_code_free_p);
|
||||||
|
|
||||||
|
if (byte_code_free_head != ECMA_NULL_POINTER)
|
||||||
|
{
|
||||||
|
jerry_debugger_byte_code_free_t *next_byte_code_free_p;
|
||||||
|
|
||||||
|
next_byte_code_free_p = JMEM_CP_GET_NON_NULL_POINTER (jerry_debugger_byte_code_free_t,
|
||||||
|
byte_code_free_head);
|
||||||
|
|
||||||
|
next_byte_code_free_p->prev_cp = JERRY_CONTEXT (debugger_byte_code_free_head);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#define JCONTEXT_H
|
#define JCONTEXT_H
|
||||||
|
|
||||||
#include "ecma-builtins.h"
|
#include "ecma-builtins.h"
|
||||||
|
#include "jerry-debugger.h"
|
||||||
#include "jmem.h"
|
#include "jmem.h"
|
||||||
#include "re-bytecode.h"
|
#include "re-bytecode.h"
|
||||||
#include "vm-defines.h"
|
#include "vm-defines.h"
|
||||||
@@ -81,6 +82,17 @@ typedef struct
|
|||||||
uint8_t re_cache_idx; /**< evicted item index when regex cache is full (round-robin) */
|
uint8_t re_cache_idx; /**< evicted item index when regex cache is full (round-robin) */
|
||||||
#endif /* !CONFIG_DISABLE_REGEXP_BUILTIN */
|
#endif /* !CONFIG_DISABLE_REGEXP_BUILTIN */
|
||||||
|
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
uint32_t debugger_message_delay; /**< call receive message when reaches zero */
|
||||||
|
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 */
|
||||||
|
jmem_cpointer_t debugger_byte_code_free_head; /**< head of byte code free linked list */
|
||||||
|
uint32_t debugger_receive_buffer_offset; /**< receive buffer offset */
|
||||||
|
int debugger_connection; /**< hold the file descriptor for socket communication */
|
||||||
|
bool debugger_stop_exec; /**< stop at the next breakpoint regardless it is enabled */
|
||||||
|
vm_frame_ctx_t *debugger_stop_context; /**< stop only if the current context is equal to this context */
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
|
|
||||||
#ifdef JMEM_STATS
|
#ifdef JMEM_STATS
|
||||||
jmem_heap_stats_t jmem_heap_stats; /**< heap's memory usage statistics */
|
jmem_heap_stats_t jmem_heap_stats; /**< heap's memory usage statistics */
|
||||||
jmem_pools_stats_t jmem_pools_stats; /**< pools' memory usage statistics */
|
jmem_pools_stats_t jmem_pools_stats; /**< pools' memory usage statistics */
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ typedef enum
|
|||||||
JERRY_INIT_SHOW_REGEXP_OPCODES = (1u << 1), /**< dump regexp byte-code to log after compilation */
|
JERRY_INIT_SHOW_REGEXP_OPCODES = (1u << 1), /**< dump regexp byte-code to log after compilation */
|
||||||
JERRY_INIT_MEM_STATS = (1u << 2), /**< dump memory statistics */
|
JERRY_INIT_MEM_STATS = (1u << 2), /**< dump memory statistics */
|
||||||
JERRY_INIT_MEM_STATS_SEPARATE = (1u << 3), /**< dump memory statistics and reset peak values after parse */
|
JERRY_INIT_MEM_STATS_SEPARATE = (1u << 3), /**< dump memory statistics and reset peak values after parse */
|
||||||
|
JERRY_INIT_DEBUGGER = (1u << 4), /**< enable all features required by debugging */
|
||||||
} jerry_init_flag_t;
|
} jerry_init_flag_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -182,6 +183,8 @@ void jerry_gc (void);
|
|||||||
*/
|
*/
|
||||||
bool jerry_run_simple (const jerry_char_t *script_source_p, size_t script_source_size, jerry_init_flag_t flags);
|
bool jerry_run_simple (const jerry_char_t *script_source_p, size_t script_source_size, jerry_init_flag_t flags);
|
||||||
jerry_value_t jerry_parse (const jerry_char_t *source_p, size_t source_size, bool is_strict);
|
jerry_value_t jerry_parse (const jerry_char_t *source_p, size_t source_size, bool is_strict);
|
||||||
|
jerry_value_t jerry_parse_named_resource (const jerry_char_t *name_p, size_t name_length,
|
||||||
|
const jerry_char_t *source_p, size_t source_size, bool is_strict);
|
||||||
jerry_value_t jerry_run (const jerry_value_t func_val);
|
jerry_value_t jerry_run (const jerry_value_t func_val);
|
||||||
jerry_value_t jerry_eval (const jerry_char_t *source_p, size_t source_size, bool is_strict);
|
jerry_value_t jerry_eval (const jerry_char_t *source_p, size_t source_size, bool is_strict);
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#include "ecma-objects-general.h"
|
#include "ecma-objects-general.h"
|
||||||
#include "jcontext.h"
|
#include "jcontext.h"
|
||||||
#include "jerry-api.h"
|
#include "jerry-api.h"
|
||||||
|
#include "jerry-debugger.h"
|
||||||
#include "js-parser.h"
|
#include "js-parser.h"
|
||||||
#include "re-compiler.h"
|
#include "re-compiler.h"
|
||||||
|
|
||||||
@@ -154,6 +155,13 @@ jerry_init (jerry_init_flag_t flags) /**< combination of Jerry flags */
|
|||||||
|
|
||||||
jmem_init ();
|
jmem_init ();
|
||||||
ecma_init ();
|
ecma_init ();
|
||||||
|
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
if (flags & JERRY_INIT_DEBUGGER)
|
||||||
|
{
|
||||||
|
jerry_debugger_accept_connection ();
|
||||||
|
}
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
} /* jerry_init */
|
} /* jerry_init */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -165,6 +173,14 @@ jerry_cleanup (void)
|
|||||||
jerry_assert_api_available ();
|
jerry_assert_api_available ();
|
||||||
|
|
||||||
ecma_finalize ();
|
ecma_finalize ();
|
||||||
|
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
if (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER)
|
||||||
|
{
|
||||||
|
jerry_debugger_close_connection ();
|
||||||
|
}
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
|
|
||||||
jmem_finalize ();
|
jmem_finalize ();
|
||||||
jerry_make_api_unavailable ();
|
jerry_make_api_unavailable ();
|
||||||
} /* jerry_cleanup */
|
} /* jerry_cleanup */
|
||||||
@@ -296,6 +312,35 @@ jerry_parse (const jerry_char_t *source_p, /**< script source */
|
|||||||
#endif /* JERRY_JS_PARSER */
|
#endif /* JERRY_JS_PARSER */
|
||||||
} /* jerry_parse */
|
} /* jerry_parse */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse script and construct an ECMAScript function. The lexical
|
||||||
|
* environment is set to the global lexical environment. The name
|
||||||
|
* (usually a file name) is also passed to this function which is
|
||||||
|
* used by the debugger to find the source code.
|
||||||
|
*
|
||||||
|
* @return function object value - if script was parsed successfully,
|
||||||
|
* thrown error - otherwise
|
||||||
|
*/
|
||||||
|
jerry_value_t
|
||||||
|
jerry_parse_named_resource (const jerry_char_t *name_p, /**< name (usually a file name) */
|
||||||
|
size_t name_length, /**< length of name */
|
||||||
|
const jerry_char_t *source_p, /**< script source */
|
||||||
|
size_t source_size, /**< script source size */
|
||||||
|
bool is_strict) /**< strict mode */
|
||||||
|
{
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
if (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER)
|
||||||
|
{
|
||||||
|
jerry_debugger_send_string (JERRY_DEBUGGER_RESOURCE_NAME, name_p, name_length);
|
||||||
|
}
|
||||||
|
#else /* JERRY_DEBUGGER */
|
||||||
|
JERRY_UNUSED (name_p);
|
||||||
|
JERRY_UNUSED (name_length);
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
|
|
||||||
|
return jerry_parse (source_p, source_size, is_strict);
|
||||||
|
} /* jerry_parse_named_resource */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run an EcmaScript function created by jerry_parse.
|
* Run an EcmaScript function created by jerry_parse.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
#ifndef BYTE_CODE_H
|
#ifndef BYTE_CODE_H
|
||||||
#define BYTE_CODE_H
|
#define BYTE_CODE_H
|
||||||
|
|
||||||
|
#include "ecma-globals.h"
|
||||||
|
|
||||||
/** \addtogroup parser Parser
|
/** \addtogroup parser Parser
|
||||||
* @{
|
* @{
|
||||||
*
|
*
|
||||||
@@ -204,6 +206,20 @@
|
|||||||
/* PARSER_TRY_CONTEXT_STACK_ALLOCATION must be <= 3 */
|
/* PARSER_TRY_CONTEXT_STACK_ALLOCATION must be <= 3 */
|
||||||
#define PARSER_TRY_CONTEXT_STACK_ALLOCATION 2
|
#define PARSER_TRY_CONTEXT_STACK_ALLOCATION 2
|
||||||
|
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
|
||||||
|
#define CBC_BREAKPOINT_OPCODES \
|
||||||
|
CBC_OPCODE (CBC_BREAKPOINT_ENABLED, CBC_NO_FLAG, 0, \
|
||||||
|
VM_OC_BREAKPOINT_ENABLED) \
|
||||||
|
CBC_OPCODE (CBC_BREAKPOINT_DISABLED, CBC_NO_FLAG, 0, \
|
||||||
|
VM_OC_BREAKPOINT_DISABLED) \
|
||||||
|
|
||||||
|
#else /* !JERRY_DEBUGGER */
|
||||||
|
|
||||||
|
#define CBC_BREAKPOINT_OPCODES
|
||||||
|
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opcode definitions.
|
* Opcode definitions.
|
||||||
*/
|
*/
|
||||||
@@ -315,6 +331,7 @@
|
|||||||
VM_OC_RET) \
|
VM_OC_RET) \
|
||||||
CBC_OPCODE (CBC_RETURN_WITH_LITERAL, CBC_HAS_LITERAL_ARG, 0, \
|
CBC_OPCODE (CBC_RETURN_WITH_LITERAL, CBC_HAS_LITERAL_ARG, 0, \
|
||||||
VM_OC_RET | VM_OC_GET_LITERAL) \
|
VM_OC_RET | VM_OC_GET_LITERAL) \
|
||||||
|
CBC_BREAKPOINT_OPCODES \
|
||||||
\
|
\
|
||||||
/* Unary opcodes. */ \
|
/* Unary opcodes. */ \
|
||||||
CBC_UNARY_OPERATION (CBC_PLUS, \
|
CBC_UNARY_OPERATION (CBC_PLUS, \
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
#include "byte-code.h"
|
#include "byte-code.h"
|
||||||
|
#include "jerry-debugger.h"
|
||||||
#include "js-parser.h"
|
#include "js-parser.h"
|
||||||
#include "js-parser-limits.h"
|
#include "js-parser-limits.h"
|
||||||
#include "js-lexer.h"
|
#include "js-lexer.h"
|
||||||
@@ -190,6 +191,16 @@ typedef struct parser_branch_node_t
|
|||||||
parser_branch_t branch; /**< branch */
|
parser_branch_t branch; /**< branch */
|
||||||
} parser_branch_node_t;
|
} parser_branch_node_t;
|
||||||
|
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
/**
|
||||||
|
* Extra information for each breakpoint.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t value; /**< line or offset of the breakpoint */
|
||||||
|
} parser_breakpoint_info_t;
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Those members of a context which needs
|
* Those members of a context which needs
|
||||||
* to be saved when a sub-function is parsed.
|
* to be saved when a sub-function is parsed.
|
||||||
@@ -270,6 +281,12 @@ typedef struct
|
|||||||
int is_show_opcodes; /**< show opcodes */
|
int is_show_opcodes; /**< show opcodes */
|
||||||
uint32_t total_byte_code_size; /**< total byte code size */
|
uint32_t total_byte_code_size; /**< total byte code size */
|
||||||
#endif /* PARSER_DUMP_BYTE_CODE */
|
#endif /* PARSER_DUMP_BYTE_CODE */
|
||||||
|
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
parser_breakpoint_info_t breakpoint_info[JERRY_DEBUGGER_MAX_SIZE (parser_list_t)]; /**< extra data for breakpoints */
|
||||||
|
uint16_t breakpoint_info_count; /**< current breakpoint index */
|
||||||
|
parser_line_counter_t last_breakpoint_line; /**< last line where breakpoint was inserted */
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
} parser_context_t;
|
} parser_context_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -428,6 +445,15 @@ ecma_compiled_code_t *parser_parse_function (parser_context_t *context_p, uint32
|
|||||||
|
|
||||||
void parser_raise_error (parser_context_t *context_p, parser_error_t error);
|
void parser_raise_error (parser_context_t *context_p, parser_error_t error);
|
||||||
|
|
||||||
|
/* Debug functions. */
|
||||||
|
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
|
||||||
|
void parser_append_breakpoint_info (parser_context_t *context_p, jerry_debugger_header_type_t type, uint32_t value);
|
||||||
|
void parser_send_breakpoints (parser_context_t *context_p, jerry_debugger_header_type_t type);
|
||||||
|
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @}
|
* @}
|
||||||
* @}
|
* @}
|
||||||
|
|||||||
@@ -17,6 +17,10 @@
|
|||||||
|
|
||||||
#ifdef JERRY_JS_PARSER
|
#ifdef JERRY_JS_PARSER
|
||||||
|
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
#include "jcontext.h"
|
||||||
|
#endif /*JERRY_DEBUGGER */
|
||||||
|
|
||||||
/** \addtogroup parser Parser
|
/** \addtogroup parser Parser
|
||||||
* @{
|
* @{
|
||||||
*
|
*
|
||||||
@@ -310,6 +314,10 @@ parser_parse_var_statement (parser_context_t *context_p) /**< context */
|
|||||||
JERRY_ASSERT (context_p->token.type == LEXER_LITERAL
|
JERRY_ASSERT (context_p->token.type == LEXER_LITERAL
|
||||||
&& context_p->token.lit_location.type == LEXER_IDENT_LITERAL);
|
&& context_p->token.lit_location.type == LEXER_IDENT_LITERAL);
|
||||||
|
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
parser_line_counter_t ident_line_counter = context_p->line;
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
|
|
||||||
context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_VAR;
|
context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_VAR;
|
||||||
|
|
||||||
parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL);
|
parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL);
|
||||||
@@ -318,6 +326,30 @@ parser_parse_var_statement (parser_context_t *context_p) /**< context */
|
|||||||
|
|
||||||
if (context_p->token.type == LEXER_ASSIGN)
|
if (context_p->token.type == LEXER_ASSIGN)
|
||||||
{
|
{
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
if (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER)
|
||||||
|
{
|
||||||
|
if (ident_line_counter != context_p->last_breakpoint_line)
|
||||||
|
{
|
||||||
|
JERRY_DEBUG_MSG ("Insert var breakpoint: %d (%d)\n", ident_line_counter, context_p->last_breakpoint_line);
|
||||||
|
JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL);
|
||||||
|
|
||||||
|
cbc_argument_t last_cbc = context_p->last_cbc;
|
||||||
|
context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE;
|
||||||
|
|
||||||
|
parser_emit_cbc (context_p, CBC_BREAKPOINT_DISABLED);
|
||||||
|
parser_flush_cbc (context_p);
|
||||||
|
|
||||||
|
parser_append_breakpoint_info (context_p, JERRY_DEBUGGER_BREAKPOINT_LIST, ident_line_counter);
|
||||||
|
|
||||||
|
context_p->last_cbc_opcode = CBC_PUSH_LITERAL;
|
||||||
|
context_p->last_cbc = last_cbc;
|
||||||
|
|
||||||
|
context_p->last_breakpoint_line = ident_line_counter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
|
|
||||||
parser_parse_expression (context_p,
|
parser_parse_expression (context_p,
|
||||||
PARSE_EXPR_STATEMENT | PARSE_EXPR_NO_COMMA | PARSE_EXPR_HAS_LITERAL);
|
PARSE_EXPR_STATEMENT | PARSE_EXPR_NO_COMMA | PARSE_EXPR_HAS_LITERAL);
|
||||||
}
|
}
|
||||||
@@ -369,6 +401,14 @@ parser_parse_function_statement (parser_context_t *context_p) /**< context */
|
|||||||
status_flags |= PARSER_HAS_NON_STRICT_ARG;
|
status_flags |= PARSER_HAS_NON_STRICT_ARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
if (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER)
|
||||||
|
{
|
||||||
|
jerry_debugger_send_function_name ((jerry_char_t *) name_p->u.char_p,
|
||||||
|
name_p->prop.length);
|
||||||
|
}
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
|
|
||||||
if (name_p->status_flags & LEXER_FLAG_INITIALIZED)
|
if (name_p->status_flags & LEXER_FLAG_INITIALIZED)
|
||||||
{
|
{
|
||||||
if (!(name_p->status_flags & (LEXER_FLAG_FUNCTION_NAME | LEXER_FLAG_FUNCTION_ARGUMENT)))
|
if (!(name_p->status_flags & (LEXER_FLAG_FUNCTION_NAME | LEXER_FLAG_FUNCTION_ARGUMENT)))
|
||||||
@@ -1567,6 +1607,14 @@ parser_parse_statements (parser_context_t *context_p) /**< context */
|
|||||||
parser_stack_push_uint8 (context_p, PARSER_STATEMENT_START);
|
parser_stack_push_uint8 (context_p, PARSER_STATEMENT_START);
|
||||||
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
||||||
|
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
/* Set lexical enviroment for the debugger. */
|
||||||
|
if (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER)
|
||||||
|
{
|
||||||
|
context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED;
|
||||||
|
}
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
|
|
||||||
while (context_p->token.type == LEXER_LITERAL
|
while (context_p->token.type == LEXER_LITERAL
|
||||||
&& context_p->token.lit_location.type == LEXER_STRING_LITERAL)
|
&& context_p->token.lit_location.type == LEXER_STRING_LITERAL)
|
||||||
{
|
{
|
||||||
@@ -1654,6 +1702,28 @@ parser_parse_statements (parser_context_t *context_p) /**< context */
|
|||||||
JERRY_ASSERT (context_p->stack_depth == context_p->context_stack_depth);
|
JERRY_ASSERT (context_p->stack_depth == context_p->context_stack_depth);
|
||||||
#endif /* !JERRY_NDEBUG */
|
#endif /* !JERRY_NDEBUG */
|
||||||
|
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
if (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER)
|
||||||
|
{
|
||||||
|
if (context_p->line != context_p->last_breakpoint_line
|
||||||
|
&& context_p->token.type != LEXER_SEMICOLON
|
||||||
|
&& context_p->token.type != LEXER_LEFT_BRACE
|
||||||
|
&& context_p->token.type != LEXER_RIGHT_BRACE
|
||||||
|
&& context_p->token.type != LEXER_KEYW_VAR
|
||||||
|
&& context_p->token.type != LEXER_KEYW_FUNCTION
|
||||||
|
&& context_p->token.type != LEXER_KEYW_CASE
|
||||||
|
&& context_p->token.type != LEXER_KEYW_DEFAULT)
|
||||||
|
{
|
||||||
|
parser_emit_cbc (context_p, CBC_BREAKPOINT_DISABLED);
|
||||||
|
parser_flush_cbc (context_p);
|
||||||
|
|
||||||
|
parser_append_breakpoint_info (context_p, JERRY_DEBUGGER_BREAKPOINT_LIST, context_p->line);
|
||||||
|
|
||||||
|
context_p->last_breakpoint_line = context_p->line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
|
|
||||||
switch (context_p->token.type)
|
switch (context_p->token.type)
|
||||||
{
|
{
|
||||||
case LEXER_SEMICOLON:
|
case LEXER_SEMICOLON:
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#include "ecma-helpers.h"
|
#include "ecma-helpers.h"
|
||||||
#include "ecma-literal-storage.h"
|
#include "ecma-literal-storage.h"
|
||||||
#include "jcontext.h"
|
#include "jcontext.h"
|
||||||
|
#include "jerry-debugger.h"
|
||||||
#include "js-parser-internal.h"
|
#include "js-parser-internal.h"
|
||||||
|
|
||||||
#ifdef JERRY_JS_PARSER
|
#ifdef JERRY_JS_PARSER
|
||||||
@@ -1271,6 +1272,15 @@ parser_post_processing (parser_context_t *context_p) /**< context */
|
|||||||
|
|
||||||
JERRY_ASSERT (context_p->literal_count <= PARSER_MAXIMUM_NUMBER_OF_LITERALS);
|
JERRY_ASSERT (context_p->literal_count <= PARSER_MAXIMUM_NUMBER_OF_LITERALS);
|
||||||
|
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
if ((JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER)
|
||||||
|
&& context_p->breakpoint_info_count > 0)
|
||||||
|
{
|
||||||
|
parser_send_breakpoints (context_p, JERRY_DEBUGGER_BREAKPOINT_LIST);
|
||||||
|
JERRY_ASSERT (context_p->breakpoint_info_count == 0);
|
||||||
|
}
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
|
|
||||||
initializers_length = parser_compute_indicies (context_p,
|
initializers_length = parser_compute_indicies (context_p,
|
||||||
&ident_end,
|
&ident_end,
|
||||||
&uninitialized_var_end,
|
&uninitialized_var_end,
|
||||||
@@ -1573,6 +1583,14 @@ parser_post_processing (parser_context_t *context_p) /**< context */
|
|||||||
PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset);
|
PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset);
|
||||||
flags = cbc_flags[opcode];
|
flags = cbc_flags[opcode];
|
||||||
|
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
if (opcode == CBC_BREAKPOINT_DISABLED)
|
||||||
|
{
|
||||||
|
uint32_t offset = (uint32_t) (((uint8_t *) dst_p) - ((uint8_t *) compiled_code_p) - 1);
|
||||||
|
parser_append_breakpoint_info (context_p, JERRY_DEBUGGER_BREAKPOINT_OFFSET_LIST, offset);
|
||||||
|
}
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
|
|
||||||
if (opcode == CBC_EXT_OPCODE)
|
if (opcode == CBC_EXT_OPCODE)
|
||||||
{
|
{
|
||||||
cbc_ext_opcode_t ext_opcode;
|
cbc_ext_opcode_t ext_opcode;
|
||||||
@@ -1668,6 +1686,15 @@ parser_post_processing (parser_context_t *context_p) /**< context */
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
if ((JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER)
|
||||||
|
&& context_p->breakpoint_info_count > 0)
|
||||||
|
{
|
||||||
|
parser_send_breakpoints (context_p, JERRY_DEBUGGER_BREAKPOINT_OFFSET_LIST);
|
||||||
|
JERRY_ASSERT (context_p->breakpoint_info_count == 0);
|
||||||
|
}
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
|
|
||||||
if (!(context_p->status_flags & PARSER_NO_END_LABEL))
|
if (!(context_p->status_flags & PARSER_NO_END_LABEL))
|
||||||
{
|
{
|
||||||
*dst_p++ = CBC_RETURN_WITH_BLOCK;
|
*dst_p++ = CBC_RETURN_WITH_BLOCK;
|
||||||
@@ -1770,6 +1797,13 @@ parser_post_processing (parser_context_t *context_p) /**< context */
|
|||||||
compiled_code_p);
|
compiled_code_p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
if (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER)
|
||||||
|
{
|
||||||
|
jerry_debugger_send_function_cp (JERRY_DEBUGGER_BYTE_CODE_CP, compiled_code_p);
|
||||||
|
}
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
|
|
||||||
return compiled_code_p;
|
return compiled_code_p;
|
||||||
} /* parser_post_processing */
|
} /* parser_post_processing */
|
||||||
|
|
||||||
@@ -1861,6 +1895,11 @@ parser_parse_source (const uint8_t *source_p, /**< valid UTF-8 source code */
|
|||||||
}
|
}
|
||||||
#endif /* PARSER_DUMP_BYTE_CODE */
|
#endif /* PARSER_DUMP_BYTE_CODE */
|
||||||
|
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
context.breakpoint_info_count = 0;
|
||||||
|
context.last_breakpoint_line = 0;
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
|
|
||||||
PARSER_TRY (context.try_buffer)
|
PARSER_TRY (context.try_buffer)
|
||||||
{
|
{
|
||||||
/* Pushing a dummy value ensures the stack is never empty.
|
/* Pushing a dummy value ensures the stack is never empty.
|
||||||
@@ -1948,6 +1987,15 @@ parser_parse_function (parser_context_t *context_p, /**< context */
|
|||||||
|
|
||||||
JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE);
|
JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE);
|
||||||
|
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
if ((JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER)
|
||||||
|
&& context_p->breakpoint_info_count > 0)
|
||||||
|
{
|
||||||
|
parser_send_breakpoints (context_p, JERRY_DEBUGGER_BREAKPOINT_LIST);
|
||||||
|
context_p->breakpoint_info_count = 0;
|
||||||
|
}
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
|
|
||||||
/* Save private part of the context. */
|
/* Save private part of the context. */
|
||||||
|
|
||||||
saved_context.status_flags = context_p->status_flags;
|
saved_context.status_flags = context_p->status_flags;
|
||||||
@@ -1998,6 +2046,21 @@ parser_parse_function (parser_context_t *context_p, /**< context */
|
|||||||
}
|
}
|
||||||
#endif /* PARSER_DUMP_BYTE_CODE */
|
#endif /* PARSER_DUMP_BYTE_CODE */
|
||||||
|
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
if (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER)
|
||||||
|
{
|
||||||
|
if (context_p->line != context_p->last_breakpoint_line)
|
||||||
|
{
|
||||||
|
parser_emit_cbc (context_p, CBC_BREAKPOINT_DISABLED);
|
||||||
|
parser_flush_cbc (context_p);
|
||||||
|
|
||||||
|
parser_append_breakpoint_info (context_p, JERRY_DEBUGGER_BREAKPOINT_LIST, context_p->line);
|
||||||
|
|
||||||
|
context_p->last_breakpoint_line = context_p->line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
|
|
||||||
lexer_next_token (context_p);
|
lexer_next_token (context_p);
|
||||||
|
|
||||||
if (context_p->status_flags & PARSER_IS_FUNC_EXPRESSION
|
if (context_p->status_flags & PARSER_IS_FUNC_EXPRESSION
|
||||||
@@ -2008,6 +2071,14 @@ parser_parse_function (parser_context_t *context_p, /**< context */
|
|||||||
&context_p->token.lit_location,
|
&context_p->token.lit_location,
|
||||||
LEXER_IDENT_LITERAL);
|
LEXER_IDENT_LITERAL);
|
||||||
|
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
if (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER)
|
||||||
|
{
|
||||||
|
jerry_debugger_send_function_name ((jerry_char_t *) context_p->lit_object.literal_p->u.char_p,
|
||||||
|
context_p->lit_object.literal_p->prop.length);
|
||||||
|
}
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
|
|
||||||
/* The arguments object is created later than the binding to the
|
/* The arguments object is created later than the binding to the
|
||||||
* function expression name, so there is no need to assign special flags. */
|
* function expression name, so there is no need to assign special flags. */
|
||||||
if (context_p->lit_object.type != LEXER_LITERAL_OBJECT_ARGUMENTS)
|
if (context_p->lit_object.type != LEXER_LITERAL_OBJECT_ARGUMENTS)
|
||||||
@@ -2025,6 +2096,13 @@ parser_parse_function (parser_context_t *context_p, /**< context */
|
|||||||
lexer_next_token (context_p);
|
lexer_next_token (context_p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
if (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER)
|
||||||
|
{
|
||||||
|
jerry_debugger_send_type (JERRY_DEBUGGER_PARSE_FUNCTION);
|
||||||
|
}
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
|
|
||||||
if (context_p->token.type != LEXER_LEFT_PAREN)
|
if (context_p->token.type != LEXER_LEFT_PAREN)
|
||||||
{
|
{
|
||||||
parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIST_EXPECTED);
|
parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIST_EXPECTED);
|
||||||
@@ -2223,6 +2301,46 @@ parser_raise_error (parser_context_t *context_p, /**< context */
|
|||||||
JERRY_ASSERT (0);
|
JERRY_ASSERT (0);
|
||||||
} /* parser_raise_error */
|
} /* parser_raise_error */
|
||||||
|
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append a breakpoint info.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
parser_append_breakpoint_info (parser_context_t *context_p, /**< context */
|
||||||
|
jerry_debugger_header_type_t type, /**< message type */
|
||||||
|
uint32_t value) /**< line or offset of the breakpoint */
|
||||||
|
{
|
||||||
|
JERRY_ASSERT (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER);
|
||||||
|
|
||||||
|
if (context_p->breakpoint_info_count >= JERRY_DEBUGGER_MAX_SIZE (parser_list_t))
|
||||||
|
{
|
||||||
|
parser_send_breakpoints (context_p, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
context_p->breakpoint_info[context_p->breakpoint_info_count].value = value;
|
||||||
|
context_p->breakpoint_info_count = (uint16_t) (context_p->breakpoint_info_count + 1);
|
||||||
|
} /* parser_append_breakpoint_info */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send current breakpoint list.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
parser_send_breakpoints (parser_context_t *context_p, /**< context */
|
||||||
|
jerry_debugger_header_type_t type) /**< message type */
|
||||||
|
{
|
||||||
|
JERRY_ASSERT (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER);
|
||||||
|
JERRY_ASSERT (context_p->breakpoint_info_count > 0);
|
||||||
|
|
||||||
|
jerry_debugger_send_data (type,
|
||||||
|
context_p->breakpoint_info,
|
||||||
|
context_p->breakpoint_info_count * sizeof (parser_breakpoint_info_t));
|
||||||
|
|
||||||
|
context_p->breakpoint_info_count = 0;
|
||||||
|
} /* parser_send_breakpoints */
|
||||||
|
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
|
|
||||||
#define PARSE_ERR_POS_START " [line: "
|
#define PARSE_ERR_POS_START " [line: "
|
||||||
#define PARSE_ERR_POS_START_SIZE ((uint32_t) sizeof (PARSE_ERR_POS_START) - 1)
|
#define PARSE_ERR_POS_START_SIZE ((uint32_t) sizeof (PARSE_ERR_POS_START) - 1)
|
||||||
#define PARSE_ERR_POS_MIDDLE ", column: "
|
#define PARSE_ERR_POS_MIDDLE ", column: "
|
||||||
@@ -2253,6 +2371,13 @@ parser_parse_script (const uint8_t *source_p, /**< source code */
|
|||||||
|
|
||||||
if (!*bytecode_data_p)
|
if (!*bytecode_data_p)
|
||||||
{
|
{
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
if (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER)
|
||||||
|
{
|
||||||
|
jerry_debugger_send_type (JERRY_DEBUGGER_PARSE_ERROR);
|
||||||
|
}
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
|
|
||||||
if (parser_error.error == PARSER_ERR_OUT_OF_MEMORY)
|
if (parser_error.error == PARSER_ERR_OUT_OF_MEMORY)
|
||||||
{
|
{
|
||||||
/* It is unlikely that memory can be allocated in an out-of-memory
|
/* It is unlikely that memory can be allocated in an out-of-memory
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ typedef const uint8_t *vm_instr_counter_t;
|
|||||||
/**
|
/**
|
||||||
* Context of interpreter, related to a JS stack frame
|
* Context of interpreter, related to a JS stack frame
|
||||||
*/
|
*/
|
||||||
typedef struct
|
typedef struct vm_frame_ctx_t
|
||||||
{
|
{
|
||||||
const ecma_compiled_code_t *bytecode_header_p; /**< currently executed byte-code data */
|
const ecma_compiled_code_t *bytecode_header_p; /**< currently executed byte-code data */
|
||||||
uint8_t *byte_code_p; /**< current byte code pointer */
|
uint8_t *byte_code_p; /**< current byte code pointer */
|
||||||
@@ -48,6 +48,7 @@ typedef struct
|
|||||||
ecma_value_t *stack_top_p; /**< stack top pointer */
|
ecma_value_t *stack_top_p; /**< stack top pointer */
|
||||||
jmem_cpointer_t *literal_start_p; /**< literal list start pointer */
|
jmem_cpointer_t *literal_start_p; /**< literal list start pointer */
|
||||||
ecma_object_t *lex_env_p; /**< current lexical environment */
|
ecma_object_t *lex_env_p; /**< current lexical environment */
|
||||||
|
struct vm_frame_ctx_t *prev_context_p; /**< previous context */
|
||||||
ecma_value_t this_binding; /**< this binding */
|
ecma_value_t this_binding; /**< this binding */
|
||||||
ecma_value_t call_block_result; /**< preserve block result during a call */
|
ecma_value_t call_block_result; /**< preserve block result during a call */
|
||||||
uint16_t context_depth; /**< current context depth */
|
uint16_t context_depth; /**< current context depth */
|
||||||
|
|||||||
+64
-3
@@ -2305,6 +2305,59 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
|
|||||||
JERRY_ASSERT (frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth == stack_top_p);
|
JERRY_ASSERT (frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth == stack_top_p);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
case VM_OC_BREAKPOINT_ENABLED:
|
||||||
|
{
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
if (!(JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
frame_ctx_p->byte_code_p = byte_code_start_p;
|
||||||
|
|
||||||
|
jerry_debugger_breakpoint_hit ();
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
case VM_OC_BREAKPOINT_DISABLED:
|
||||||
|
{
|
||||||
|
#ifdef JERRY_DEBUGGER
|
||||||
|
if (!(JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_DEBUGGER))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
frame_ctx_p->byte_code_p = byte_code_start_p;
|
||||||
|
|
||||||
|
if (JERRY_CONTEXT (debugger_stop_exec)
|
||||||
|
&& (JERRY_CONTEXT (debugger_stop_context) == NULL
|
||||||
|
|| JERRY_CONTEXT (debugger_stop_context) == JERRY_CONTEXT (vm_top_context_p)))
|
||||||
|
{
|
||||||
|
jerry_debugger_breakpoint_hit ();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JERRY_CONTEXT (debugger_message_delay) > 0)
|
||||||
|
{
|
||||||
|
JERRY_CONTEXT (debugger_message_delay)--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
JERRY_CONTEXT (debugger_message_delay) = JERRY_DEBUGGER_MESSAGE_FREQUENCY;
|
||||||
|
|
||||||
|
if (jerry_debugger_receive ())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JERRY_CONTEXT (debugger_stop_exec))
|
||||||
|
{
|
||||||
|
JERRY_ASSERT (JERRY_CONTEXT (debugger_stop_context) == NULL);
|
||||||
|
jerry_debugger_breakpoint_hit ();
|
||||||
|
}
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
JERRY_UNREACHABLE ();
|
JERRY_UNREACHABLE ();
|
||||||
@@ -2532,7 +2585,6 @@ vm_execute (vm_frame_ctx_t *frame_ctx_p, /**< frame context */
|
|||||||
{
|
{
|
||||||
const ecma_compiled_code_t *bytecode_header_p = frame_ctx_p->bytecode_header_p;
|
const ecma_compiled_code_t *bytecode_header_p = frame_ctx_p->bytecode_header_p;
|
||||||
ecma_value_t completion_value;
|
ecma_value_t completion_value;
|
||||||
vm_frame_ctx_t *prev_context_p;
|
|
||||||
uint16_t argument_end;
|
uint16_t argument_end;
|
||||||
uint16_t register_end;
|
uint16_t register_end;
|
||||||
|
|
||||||
@@ -2577,7 +2629,6 @@ vm_execute (vm_frame_ctx_t *frame_ctx_p, /**< frame context */
|
|||||||
|
|
||||||
JERRY_CONTEXT (is_direct_eval_form_call) = false;
|
JERRY_CONTEXT (is_direct_eval_form_call) = false;
|
||||||
|
|
||||||
prev_context_p = JERRY_CONTEXT (vm_top_context_p);
|
|
||||||
JERRY_CONTEXT (vm_top_context_p) = frame_ctx_p;
|
JERRY_CONTEXT (vm_top_context_p) = frame_ctx_p;
|
||||||
|
|
||||||
vm_init_loop (frame_ctx_p);
|
vm_init_loop (frame_ctx_p);
|
||||||
@@ -2608,7 +2659,16 @@ vm_execute (vm_frame_ctx_t *frame_ctx_p, /**< frame context */
|
|||||||
ecma_fast_free_value (frame_ctx_p->registers_p[i]);
|
ecma_fast_free_value (frame_ctx_p->registers_p[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
JERRY_CONTEXT (vm_top_context_p) = prev_context_p;
|
#ifdef JERRY_DEBUGGER
|
||||||
|
if (JERRY_CONTEXT (debugger_stop_context) == JERRY_CONTEXT (vm_top_context_p))
|
||||||
|
{
|
||||||
|
/* The engine will stop when the next breakpoint is reached. */
|
||||||
|
JERRY_ASSERT (JERRY_CONTEXT (debugger_stop_exec));
|
||||||
|
JERRY_CONTEXT (debugger_stop_context) = NULL;
|
||||||
|
}
|
||||||
|
#endif /* JERRY_DEBUGGER */
|
||||||
|
|
||||||
|
JERRY_CONTEXT (vm_top_context_p) = frame_ctx_p->prev_context_p;
|
||||||
return completion_value;
|
return completion_value;
|
||||||
} /* vm_execute */
|
} /* vm_execute */
|
||||||
|
|
||||||
@@ -2654,6 +2714,7 @@ vm_run (const ecma_compiled_code_t *bytecode_header_p, /**< byte-code data heade
|
|||||||
frame_ctx.byte_code_p = (uint8_t *) literal_p;
|
frame_ctx.byte_code_p = (uint8_t *) literal_p;
|
||||||
frame_ctx.byte_code_start_p = (uint8_t *) literal_p;
|
frame_ctx.byte_code_start_p = (uint8_t *) literal_p;
|
||||||
frame_ctx.lex_env_p = lex_env_p;
|
frame_ctx.lex_env_p = lex_env_p;
|
||||||
|
frame_ctx.prev_context_p = JERRY_CONTEXT (vm_top_context_p);
|
||||||
frame_ctx.this_binding = this_binding_value;
|
frame_ctx.this_binding = this_binding_value;
|
||||||
frame_ctx.context_depth = 0;
|
frame_ctx.context_depth = 0;
|
||||||
frame_ctx.is_eval_code = is_eval_code;
|
frame_ctx.is_eval_code = is_eval_code;
|
||||||
|
|||||||
@@ -203,6 +203,8 @@ typedef enum
|
|||||||
VM_OC_FINALLY, /**< finally */
|
VM_OC_FINALLY, /**< finally */
|
||||||
VM_OC_CONTEXT_END, /**< context end */
|
VM_OC_CONTEXT_END, /**< context end */
|
||||||
VM_OC_JUMP_AND_EXIT_CONTEXT, /**< jump and exit context */
|
VM_OC_JUMP_AND_EXIT_CONTEXT, /**< jump and exit context */
|
||||||
|
VM_OC_BREAKPOINT_ENABLED, /**< enabled breakpoint for debugger */
|
||||||
|
VM_OC_BREAKPOINT_DISABLED, /**< disabled breakpoint for debugger */
|
||||||
} vm_oc_types;
|
} vm_oc_types;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,949 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>
|
||||||
|
JerryScript HTML (WebSocket) Debugger Client
|
||||||
|
</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
textarea {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
margin-top: 10px;
|
||||||
|
width: 657px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h2>JerryScript HTML (WebSocket) Debugger Client</h2>
|
||||||
|
|
||||||
|
<textarea id="log" rows="20" cols="80"></textarea><br>
|
||||||
|
|
||||||
|
Getting help: type 'help' in the command line below.<br>
|
||||||
|
|
||||||
|
<input id="command" type="text" onkeypress="debuggerCommand(event); return true;">
|
||||||
|
<script>
|
||||||
|
var JERRY_DEBUGGER_CONFIGURATION = 1;
|
||||||
|
var JERRY_DEBUGGER_PARSE_ERROR = 2;
|
||||||
|
var JERRY_DEBUGGER_BYTE_CODE_CP = 3;
|
||||||
|
var JERRY_DEBUGGER_PARSE_FUNCTION = 4;
|
||||||
|
var JERRY_DEBUGGER_BREAKPOINT_LIST = 5;
|
||||||
|
var JERRY_DEBUGGER_BREAKPOINT_OFFSET_LIST = 6;
|
||||||
|
var JERRY_DEBUGGER_RESOURCE_NAME = 7;
|
||||||
|
var JERRY_DEBUGGER_RESOURCE_NAME_END = 8;
|
||||||
|
var JERRY_DEBUGGER_FUNCTION_NAME = 9;
|
||||||
|
var JERRY_DEBUGGER_FUNCTION_NAME_END = 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_FREE_BYTE_CODE_CP = 1;
|
||||||
|
var JERRY_DEBUGGER_UPDATE_BREAKPOINT = 2;
|
||||||
|
var JERRY_DEBUGGER_STOP = 3;
|
||||||
|
var JERRY_DEBUGGER_CONTINUE = 4;
|
||||||
|
var JERRY_DEBUGGER_STEP = 5;
|
||||||
|
var JERRY_DEBUGGER_NEXT = 6;
|
||||||
|
var JERRY_DEBUGGER_GET_BACKTRACE = 7;
|
||||||
|
|
||||||
|
var textBox = document.getElementById("log");
|
||||||
|
var commandBox = document.getElementById("command");
|
||||||
|
var socket = null;
|
||||||
|
|
||||||
|
textBox.value = ""
|
||||||
|
commandBox.value = "connect localhost"
|
||||||
|
|
||||||
|
function appendLog(str)
|
||||||
|
{
|
||||||
|
textBox.value += str + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
var debuggerObj = null;
|
||||||
|
|
||||||
|
function DebuggerClient(ipAddr)
|
||||||
|
{
|
||||||
|
appendLog("ws://" + ipAddr + ":5001/jerry-debugger");
|
||||||
|
|
||||||
|
var parseObj = null;
|
||||||
|
var maxMessageSize = 0;
|
||||||
|
var cpointerSize = 0;
|
||||||
|
var littleEndian = true;
|
||||||
|
var functions = { };
|
||||||
|
var lineList = new Multimap();
|
||||||
|
var activeBreakpoints = { };
|
||||||
|
var nextBreakpointIndex = 1;
|
||||||
|
var backtraceFrame = 0;
|
||||||
|
|
||||||
|
function assert(expr)
|
||||||
|
{
|
||||||
|
if (!expr)
|
||||||
|
{
|
||||||
|
throw new Error("Assertion failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Concat the two arrays. The first byte (opcode) of nextArray is ignored. */
|
||||||
|
function concatUint8Arrays(baseArray, nextArray)
|
||||||
|
{
|
||||||
|
if (nextArray.byteLength <= 1)
|
||||||
|
{
|
||||||
|
/* Nothing to append. */
|
||||||
|
return baseArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!baseArray)
|
||||||
|
{
|
||||||
|
/* Cut the first byte (opcode). */
|
||||||
|
return nextArray.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var baseLength = baseArray.byteLength;
|
||||||
|
var nextLength = nextArray.byteLength - 1;
|
||||||
|
|
||||||
|
var result = new Uint8Array(baseArray.byteLength + nextArray.byteLength);
|
||||||
|
result.set(nextArray, baseArray.byteLength - 1);
|
||||||
|
|
||||||
|
/* This set overwrites the opcode. */
|
||||||
|
result.set(baseArray);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cesu8ToString(array)
|
||||||
|
{
|
||||||
|
if (!array)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
var length = array.byteLength;
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
var result = "";
|
||||||
|
|
||||||
|
while (i < length)
|
||||||
|
{
|
||||||
|
var chr = array[i];
|
||||||
|
|
||||||
|
++i;
|
||||||
|
|
||||||
|
if (chr >= 0x7f)
|
||||||
|
{
|
||||||
|
if (chr & 0x20)
|
||||||
|
{
|
||||||
|
/* Three byte long character. */
|
||||||
|
chr = ((chr & 0xf) << 12) | ((array[i] & 0x3f) << 6) | (array[i + 1] & 0x3f);
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Two byte long character. */
|
||||||
|
chr = ((chr & 0x1f) << 6) | (array[i] & 0x3f);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result += String.fromCharCode(chr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function breakpointToString(breakpoint)
|
||||||
|
{
|
||||||
|
var name = breakpoint.func.name;
|
||||||
|
|
||||||
|
var result = breakpoint.func.resource;
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
result = "<unknown>";
|
||||||
|
}
|
||||||
|
|
||||||
|
result += ":" + breakpoint.line;
|
||||||
|
|
||||||
|
if (breakpoint.func.name)
|
||||||
|
{
|
||||||
|
result += " (in " + breakpoint.func.name + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Multimap()
|
||||||
|
{
|
||||||
|
/* Each item is an array of items. */
|
||||||
|
|
||||||
|
var map = { };
|
||||||
|
|
||||||
|
this.get = function(key)
|
||||||
|
{
|
||||||
|
var item = map[key];
|
||||||
|
return item ? item : [ ];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.insert = function(key, value)
|
||||||
|
{
|
||||||
|
var item = map[key];
|
||||||
|
|
||||||
|
if (item)
|
||||||
|
{
|
||||||
|
item.push(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
map[key] = [ value ];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.delete = function(key, value)
|
||||||
|
{
|
||||||
|
var array = map[key];
|
||||||
|
|
||||||
|
assert(array);
|
||||||
|
|
||||||
|
var newLength = array.length - 1;
|
||||||
|
var i = array.indexOf(value);
|
||||||
|
|
||||||
|
assert(i != -1);
|
||||||
|
|
||||||
|
array.splice(i, 1);
|
||||||
|
array.length = newLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var socket = new WebSocket("ws://localhost:5001/jerry-debugger");
|
||||||
|
socket.binaryType = 'arraybuffer';
|
||||||
|
|
||||||
|
function abortConnection(message)
|
||||||
|
{
|
||||||
|
assert(socket && debuggerObj);
|
||||||
|
|
||||||
|
socket.close();
|
||||||
|
socket = null;
|
||||||
|
debuggerObj = null;
|
||||||
|
|
||||||
|
appendLog("Abort connection: " + message);
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.onerror = function(event)
|
||||||
|
{
|
||||||
|
if (socket)
|
||||||
|
{
|
||||||
|
socket = null;
|
||||||
|
debuggerObj = null;
|
||||||
|
appendLog("Connection closed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
socket.onclose = socket.onerror;
|
||||||
|
|
||||||
|
socket.onopen = function(event)
|
||||||
|
{
|
||||||
|
appendLog("Connection created.");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFormatSize(format)
|
||||||
|
{
|
||||||
|
var length = 0;
|
||||||
|
|
||||||
|
for (var i = 0; i < format.length; i++)
|
||||||
|
{
|
||||||
|
if (format[i] == "B")
|
||||||
|
{
|
||||||
|
length++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format[i] == "C")
|
||||||
|
{
|
||||||
|
length += cpointerSize;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(format[i] == "I")
|
||||||
|
|
||||||
|
length += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeMessage(format, message, offset)
|
||||||
|
{
|
||||||
|
/* Format: B=byte I=int32 C=cpointer.
|
||||||
|
* Returns an array of decoded numbers. */
|
||||||
|
|
||||||
|
var result = []
|
||||||
|
var value;
|
||||||
|
|
||||||
|
if (!offset)
|
||||||
|
{
|
||||||
|
offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset + getFormatSize(format) > message.byteLength)
|
||||||
|
{
|
||||||
|
abortConnection("received message too short.");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < format.length; i++)
|
||||||
|
{
|
||||||
|
if (format[i] == "B")
|
||||||
|
{
|
||||||
|
result.push(message[offset])
|
||||||
|
offset++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format[i] == "C" && cpointerSize == 2)
|
||||||
|
{
|
||||||
|
if (littleEndian)
|
||||||
|
{
|
||||||
|
value = message[offset] | (message[offset + 1] << 8);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value = (message[offset] << 8) | message[offset + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push(value);
|
||||||
|
offset += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(format[i] == "I" || (format[i] == "C" && cpointerSize == 4));
|
||||||
|
|
||||||
|
if (littleEndian)
|
||||||
|
{
|
||||||
|
value = (message[offset] | (message[offset + 1] << 8)
|
||||||
|
| (message[offset + 2] << 16) | (message[offset + 3] << 24));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value = ((message[offset] << 24) | (message[offset + 1] << 16)
|
||||||
|
| (message[offset + 2] << 8) | message[offset + 3] << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push(value);
|
||||||
|
offset += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function encodeMessage(format, values)
|
||||||
|
{
|
||||||
|
/* Format: B=byte I=int32 C=cpointer.
|
||||||
|
* Sends a message after the encoding is completed. */
|
||||||
|
|
||||||
|
var length = getFormatSize(format);
|
||||||
|
var message = new Uint8Array(length);
|
||||||
|
var offset = 0;
|
||||||
|
|
||||||
|
for (var i = 0; i < format.length; i++)
|
||||||
|
{
|
||||||
|
var value = values[i];
|
||||||
|
|
||||||
|
if (format[i] == "B")
|
||||||
|
{
|
||||||
|
message[offset] = value;
|
||||||
|
offset++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format[i] == "C" && cpointerSize == 2)
|
||||||
|
{
|
||||||
|
if (littleEndian)
|
||||||
|
{
|
||||||
|
message[offset] = value & 0xff;
|
||||||
|
message[offset + 1] = (value >> 8) & 0xff;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
message[offset] = (value >> 8) & 0xff;
|
||||||
|
message[offset + 1] = value & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += 2;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.send(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.encodeMessage = encodeMessage;
|
||||||
|
|
||||||
|
function ParseSource()
|
||||||
|
{
|
||||||
|
var resourceName = null;
|
||||||
|
var functionName = null;
|
||||||
|
var stack = [ { name: '', lines: [], offsets: [] } ];
|
||||||
|
var newFunctions = [ ];
|
||||||
|
|
||||||
|
this.receive = function(message)
|
||||||
|
{
|
||||||
|
switch (message[0])
|
||||||
|
{
|
||||||
|
case JERRY_DEBUGGER_PARSE_ERROR:
|
||||||
|
{
|
||||||
|
/* Parse error occured in JerryScript. */
|
||||||
|
parseObj = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case JERRY_DEBUGGER_RESOURCE_NAME:
|
||||||
|
case JERRY_DEBUGGER_RESOURCE_NAME_END:
|
||||||
|
{
|
||||||
|
if ((typeof resourceName) == "string")
|
||||||
|
{
|
||||||
|
abortConnection("unexpected message.");
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceName = concatUint8Arrays(resourceName, message);
|
||||||
|
|
||||||
|
if (message[0] == JERRY_DEBUGGER_RESOURCE_NAME_END)
|
||||||
|
{
|
||||||
|
resourceName = cesu8ToString(resourceName);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case JERRY_DEBUGGER_FUNCTION_NAME:
|
||||||
|
case JERRY_DEBUGGER_FUNCTION_NAME_END:
|
||||||
|
{
|
||||||
|
functionName = concatUint8Arrays(functionName, message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case JERRY_DEBUGGER_PARSE_FUNCTION:
|
||||||
|
{
|
||||||
|
if (resourceName == null)
|
||||||
|
{
|
||||||
|
resourceName = "";
|
||||||
|
}
|
||||||
|
else if ((typeof resourceName) != "string")
|
||||||
|
{
|
||||||
|
abortConnection("unexpected message.");
|
||||||
|
}
|
||||||
|
|
||||||
|
stack.push({ name: cesu8ToString(functionName), resource: resourceName, lines: [], offsets: [] });
|
||||||
|
functionName = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case JERRY_DEBUGGER_BREAKPOINT_LIST:
|
||||||
|
case JERRY_DEBUGGER_BREAKPOINT_OFFSET_LIST:
|
||||||
|
{
|
||||||
|
var array;
|
||||||
|
|
||||||
|
if (message.byteLength < 1 + 4)
|
||||||
|
{
|
||||||
|
abortConnection("message too short.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message[0] == JERRY_DEBUGGER_BREAKPOINT_LIST)
|
||||||
|
{
|
||||||
|
array = stack[stack.length - 1].lines;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
array = stack[stack.length - 1].offsets;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 1; i < message.byteLength; i += 4)
|
||||||
|
{
|
||||||
|
array.push(decodeMessage("I", message, i)[0]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case JERRY_DEBUGGER_BYTE_CODE_CP:
|
||||||
|
{
|
||||||
|
var func = stack.pop();
|
||||||
|
func.byte_code_cp = decodeMessage("C", message, 1)[0];
|
||||||
|
|
||||||
|
lines = {}
|
||||||
|
offsets = {}
|
||||||
|
|
||||||
|
func.firstLine = (func.lines.length > 0) ? func.lines[0] : -1;
|
||||||
|
|
||||||
|
for (var i = 0; i < func.lines.length; i++)
|
||||||
|
{
|
||||||
|
var breakpoint = { line: func.lines[i], offset: func.offsets[i], func: func, activeIndex: -1 };
|
||||||
|
|
||||||
|
lines[breakpoint.line] = breakpoint;
|
||||||
|
offsets[breakpoint.offset] = breakpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
func.lines = lines;
|
||||||
|
func.offsets = offsets;
|
||||||
|
|
||||||
|
newFunctions.push(func);
|
||||||
|
|
||||||
|
if (stack.length > 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
func.resource = resourceName;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
abortConnection("unexpected message.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < newFunctions.length; i++)
|
||||||
|
{
|
||||||
|
var func = newFunctions[i];
|
||||||
|
|
||||||
|
functions[func.byte_code_cp] = func
|
||||||
|
|
||||||
|
for (var j in func.lines)
|
||||||
|
{
|
||||||
|
lineList.insert(j, func);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parseObj = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.onmessage = function(event)
|
||||||
|
{
|
||||||
|
var message = new Uint8Array(event.data);
|
||||||
|
|
||||||
|
if (message.byteLength < 1)
|
||||||
|
{
|
||||||
|
abortConnection("message too short.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cpointerSize == 0)
|
||||||
|
{
|
||||||
|
if (message[0] != JERRY_DEBUGGER_CONFIGURATION
|
||||||
|
|| message.byteLength != 4)
|
||||||
|
{
|
||||||
|
abortConnection("the first message must be configuration.");
|
||||||
|
}
|
||||||
|
|
||||||
|
maxMessageSize = message[1]
|
||||||
|
cpointerSize = message[2]
|
||||||
|
littleEndian = (message[3] != 0);
|
||||||
|
|
||||||
|
if (cpointerSize != 2 && cpointerSize != 4)
|
||||||
|
{
|
||||||
|
abortConnection("compressed pointer must be 2 or 4 byte long.");
|
||||||
|
}
|
||||||
|
|
||||||
|
config = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parseObj)
|
||||||
|
{
|
||||||
|
parseObj.receive(message)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (message[0])
|
||||||
|
{
|
||||||
|
case JERRY_DEBUGGER_PARSE_ERROR:
|
||||||
|
case JERRY_DEBUGGER_BYTE_CODE_CP:
|
||||||
|
case JERRY_DEBUGGER_PARSE_FUNCTION:
|
||||||
|
case JERRY_DEBUGGER_BREAKPOINT_LIST:
|
||||||
|
case JERRY_DEBUGGER_RESOURCE_NAME:
|
||||||
|
case JERRY_DEBUGGER_RESOURCE_NAME_END:
|
||||||
|
case JERRY_DEBUGGER_FUNCTION_NAME:
|
||||||
|
case JERRY_DEBUGGER_FUNCTION_NAME_END:
|
||||||
|
{
|
||||||
|
parseObj = new ParseSource()
|
||||||
|
parseObj.receive(message)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case JERRY_DEBUGGER_RELEASE_BYTE_CODE_CP:
|
||||||
|
{
|
||||||
|
var byte_code_cp = decodeMessage("C", message, 1)[0];
|
||||||
|
var func = functions[byte_code_cp];
|
||||||
|
|
||||||
|
for (var i in func.lines)
|
||||||
|
{
|
||||||
|
lineList.delete(i, func);
|
||||||
|
|
||||||
|
var breakpoint = func.lines[i];
|
||||||
|
|
||||||
|
assert(i == breakpoint.line);
|
||||||
|
|
||||||
|
if (breakpoint.activeIndex >= 0)
|
||||||
|
{
|
||||||
|
delete activeBreakpoints[breakpoint.activeIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete functions[byte_code_cp];
|
||||||
|
|
||||||
|
message[0] = JERRY_DEBUGGER_FREE_BYTE_CODE_CP;
|
||||||
|
socket.send(message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case JERRY_DEBUGGER_BREAKPOINT_HIT:
|
||||||
|
{
|
||||||
|
var breakpoint = decodeMessage("CI", message, 1);
|
||||||
|
|
||||||
|
breakpoint = functions[breakpoint[0]].offsets[breakpoint[1]];
|
||||||
|
|
||||||
|
breakpointIndex = "";
|
||||||
|
|
||||||
|
if (breakpoint.activeIndex >= 0)
|
||||||
|
{
|
||||||
|
breakpointIndex = "breakpoint:" + breakpoint.activeIndex + " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
appendLog("Stopped at " + breakpointIndex + breakpointToString(breakpoint));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case JERRY_DEBUGGER_BACKTRACE:
|
||||||
|
case JERRY_DEBUGGER_BACKTRACE_END:
|
||||||
|
{
|
||||||
|
for (var i = 1; i < message.byteLength; i += cpointerSize + 4)
|
||||||
|
{
|
||||||
|
var breakpoint = decodeMessage("CI", message, i);
|
||||||
|
var func = functions[breakpoint[0]];
|
||||||
|
var best_offset = -1;
|
||||||
|
|
||||||
|
for (var offset in func.offsets)
|
||||||
|
{
|
||||||
|
if (offset <= breakpoint[1] && offset > best_offset)
|
||||||
|
{
|
||||||
|
best_offset = offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (best_offset >= 0)
|
||||||
|
{
|
||||||
|
breakpoint = func.offsets[best_offset];
|
||||||
|
appendLog(" frame " + backtraceFrame + ": " + breakpointToString(breakpoint));
|
||||||
|
}
|
||||||
|
else if (func.name)
|
||||||
|
{
|
||||||
|
appendLog(" frame " + backtraceFrame + ": " + func.name + "()");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
appendLog(" frame " + backtraceFrame + ": <unknown>()");
|
||||||
|
}
|
||||||
|
|
||||||
|
++backtraceFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message[0] == JERRY_DEBUGGER_BACKTRACE_END)
|
||||||
|
{
|
||||||
|
backtraceFrame = 0;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
abortConnection("unexpected message.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function insertBreakpoint(breakpoint)
|
||||||
|
{
|
||||||
|
if (breakpoint.activeIndex < 0)
|
||||||
|
{
|
||||||
|
breakpoint.activeIndex = nextBreakpointIndex;
|
||||||
|
activeBreakpoints[nextBreakpointIndex] = breakpoint;
|
||||||
|
nextBreakpointIndex++;
|
||||||
|
|
||||||
|
var values = [ JERRY_DEBUGGER_UPDATE_BREAKPOINT,
|
||||||
|
1,
|
||||||
|
breakpoint.func.byte_code_cp,
|
||||||
|
breakpoint.offset ];
|
||||||
|
|
||||||
|
encodeMessage("BBCI", values);
|
||||||
|
}
|
||||||
|
|
||||||
|
appendLog("Breakpoint " + breakpoint.activeIndex + " at " + breakpointToString(breakpoint));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setBreakpoint = function(str)
|
||||||
|
{
|
||||||
|
line = /^(.+):([1-9][0-9]*)$/.exec(str);
|
||||||
|
|
||||||
|
if (line)
|
||||||
|
{
|
||||||
|
var functionList = lineList.get(line[2]);
|
||||||
|
|
||||||
|
for (var i = 0; i < functionList.length; ++i)
|
||||||
|
{
|
||||||
|
var func = functionList[i];
|
||||||
|
var resource = func.resource;
|
||||||
|
|
||||||
|
if (resource == line[1]
|
||||||
|
|| resource.endsWith("/" + line[1])
|
||||||
|
|| resource.endsWith("\\" + line[1]))
|
||||||
|
{
|
||||||
|
insertBreakpoint(func.lines[line[2]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (var i in functions)
|
||||||
|
{
|
||||||
|
var func = functions[i];
|
||||||
|
|
||||||
|
if (func.name == str && func.firstLine >= 0)
|
||||||
|
{
|
||||||
|
insertBreakpoint(func.lines[func.firstLine]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.deleteBreakpoint = function(index)
|
||||||
|
{
|
||||||
|
breakpoint = activeBreakpoints[index];
|
||||||
|
|
||||||
|
if (!breakpoint)
|
||||||
|
{
|
||||||
|
appendLog("No breakpoint found with index " + index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(breakpoint.activeIndex == index);
|
||||||
|
|
||||||
|
delete activeBreakpoints[index];
|
||||||
|
breakpoint.activeIndex = -1;
|
||||||
|
|
||||||
|
var values = [ JERRY_DEBUGGER_UPDATE_BREAKPOINT,
|
||||||
|
0,
|
||||||
|
breakpoint.func.byte_code_cp,
|
||||||
|
breakpoint.offset ];
|
||||||
|
|
||||||
|
encodeMessage("BBCI", values);
|
||||||
|
|
||||||
|
appendLog("Breakpoint " + index + " is deleted.");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.listBreakpoints = function()
|
||||||
|
{
|
||||||
|
appendLog("List of active breakpoints:");
|
||||||
|
var found = false;
|
||||||
|
|
||||||
|
for (var i in activeBreakpoints)
|
||||||
|
{
|
||||||
|
appendLog(" breakpoint " + i + " at " + breakpointToString(activeBreakpoints[i]));
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
appendLog(" no active breakpoints");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dump = function()
|
||||||
|
{
|
||||||
|
for (var i in functions)
|
||||||
|
{
|
||||||
|
var func = functions[i];
|
||||||
|
var resource = func.resource;
|
||||||
|
|
||||||
|
if (resource == '')
|
||||||
|
{
|
||||||
|
resource = "<unknown>";
|
||||||
|
}
|
||||||
|
|
||||||
|
appendLog("Function 0x" + Number(i).toString(16) + " '" + func.name + "' at " + resource + ":" + func.firstLine);
|
||||||
|
|
||||||
|
for (var j in func.lines)
|
||||||
|
{
|
||||||
|
var active = "";
|
||||||
|
|
||||||
|
if (func.lines[j].active >= 0)
|
||||||
|
{
|
||||||
|
active = " (active: " + func.lines[j].active + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
appendLog(" Breatpoint line: " + j + " at memory offset: " + func.lines[j].offset + active);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function debuggerCommand(event)
|
||||||
|
{
|
||||||
|
if (event.keyCode != 13)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var command = commandBox.value.trim();
|
||||||
|
|
||||||
|
args = /^([a-zA-Z]+)(?:\s+([^\s].*)|)$/.exec(command);
|
||||||
|
|
||||||
|
if (!args)
|
||||||
|
{
|
||||||
|
appendLog("Invalid command");
|
||||||
|
document.getElementById("command").value = "";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!args[2])
|
||||||
|
{
|
||||||
|
args[2] = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args[1] == "help")
|
||||||
|
{
|
||||||
|
appendLog("Debugger commands:\n" +
|
||||||
|
" connect <IP address> - connect to server\n" +
|
||||||
|
" break|b <file_name:line>|<function_name> - set breakpoint\n" +
|
||||||
|
" delete|d <id> - delete breakpoint\n" +
|
||||||
|
" list - list breakpoints\n" +
|
||||||
|
" continue|c - continue execution\n" +
|
||||||
|
" step|s - step-in execution\n" +
|
||||||
|
" next|n - connect to server\n" +
|
||||||
|
" backtrace|bt <max-depth> - get backtrace\n" +
|
||||||
|
" dump - dump all breakpoint data");
|
||||||
|
|
||||||
|
commandBox.value = "";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args[1] == "connect")
|
||||||
|
{
|
||||||
|
if (debuggerObj)
|
||||||
|
{
|
||||||
|
appendLog("Debugger is connected");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args[2] == "")
|
||||||
|
{
|
||||||
|
appendLog("IP address expected");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
appendLog("Connect to: " + args[2]);
|
||||||
|
|
||||||
|
debuggerObj = new DebuggerClient(args[2]);
|
||||||
|
|
||||||
|
commandBox.value = "";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!debuggerObj)
|
||||||
|
{
|
||||||
|
appendLog("Debugger is NOT connected");
|
||||||
|
|
||||||
|
commandBox.value = "";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(args[1])
|
||||||
|
{
|
||||||
|
case "b":
|
||||||
|
case "break":
|
||||||
|
debuggerObj.setBreakpoint(args[2]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "d":
|
||||||
|
case "delete":
|
||||||
|
debuggerObj.deleteBreakpoint(args[2]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "stop":
|
||||||
|
debuggerObj.encodeMessage("B", [ JERRY_DEBUGGER_STOP ]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "c":
|
||||||
|
case "continue":
|
||||||
|
debuggerObj.encodeMessage("B", [ JERRY_DEBUGGER_CONTINUE ]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "s":
|
||||||
|
case "step":
|
||||||
|
debuggerObj.encodeMessage("B", [ JERRY_DEBUGGER_STEP ]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "n":
|
||||||
|
case "next":
|
||||||
|
debuggerObj.encodeMessage("B", [ JERRY_DEBUGGER_NEXT ]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "bt":
|
||||||
|
case "backtrace":
|
||||||
|
max_depth = 0;
|
||||||
|
|
||||||
|
if (args[2])
|
||||||
|
{
|
||||||
|
if (/[1-9][0-9]*/.exec(args[2]))
|
||||||
|
{
|
||||||
|
max_depth = parseInt(args[2]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
appendLog("Invalid maximum depth argument.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
appendLog("Backtrace:");
|
||||||
|
|
||||||
|
debuggerObj.encodeMessage("BI", [ JERRY_DEBUGGER_GET_BACKTRACE, max_depth ]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "list":
|
||||||
|
debuggerObj.listBreakpoints();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "dump":
|
||||||
|
debuggerObj.dump();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
appendLog("Unknown command: " + args[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
commandBox.value = "";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Executable
+679
@@ -0,0 +1,679 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
from cmd import Cmd
|
||||||
|
from struct import *
|
||||||
|
from pprint import pprint # For the readable stack printing.
|
||||||
|
|
||||||
|
# Messages sent by the server to client.
|
||||||
|
JERRY_DEBUGGER_CONFIGURATION = 1
|
||||||
|
JERRY_DEBUGGER_PARSE_ERROR = 2
|
||||||
|
JERRY_DEBUGGER_BYTE_CODE_CP = 3
|
||||||
|
JERRY_DEBUGGER_PARSE_FUNCTION = 4
|
||||||
|
JERRY_DEBUGGER_BREAKPOINT_LIST = 5
|
||||||
|
JERRY_DEBUGGER_BREAKPOINT_OFFSET_LIST = 6
|
||||||
|
JERRY_DEBUGGER_RESOURCE_NAME = 7
|
||||||
|
JERRY_DEBUGGER_RESOURCE_NAME_END = 8
|
||||||
|
JERRY_DEBUGGER_FUNCTION_NAME = 9
|
||||||
|
JERRY_DEBUGGER_FUNCTION_NAME_END = 10
|
||||||
|
JERRY_DEBUGGER_RELEASE_BYTE_CODE_CP = 11
|
||||||
|
JERRY_DEBUGGER_BREAKPOINT_HIT = 12
|
||||||
|
JERRY_DEBUGGER_BACKTRACE = 13
|
||||||
|
JERRY_DEBUGGER_BACKTRACE_END = 14
|
||||||
|
|
||||||
|
# Messages sent by the client to server.
|
||||||
|
JERRY_DEBUGGER_FREE_BYTE_CODE_CP = 1
|
||||||
|
JERRY_DEBUGGER_UPDATE_BREAKPOINT = 2
|
||||||
|
JERRY_DEBUGGER_STOP = 3
|
||||||
|
JERRY_DEBUGGER_CONTINUE = 4
|
||||||
|
JERRY_DEBUGGER_STEP = 5
|
||||||
|
JERRY_DEBUGGER_NEXT = 6
|
||||||
|
JERRY_DEBUGGER_GET_BACKTRACE = 7
|
||||||
|
|
||||||
|
MAX_BUFFER_SIZE = 128
|
||||||
|
WEBSOCKET_BINARY_FRAME = 2
|
||||||
|
WEBSOCKET_FIN_BIT = 0x80
|
||||||
|
|
||||||
|
|
||||||
|
def arguments_parse():
|
||||||
|
parser = argparse.ArgumentParser(description="JerryScript debugger client")
|
||||||
|
|
||||||
|
parser.add_argument("address", action="store", nargs="?", default="localhost:5001", help="specify a unique network address for connection (default: %(default)s)")
|
||||||
|
parser.add_argument("-v", "--verbose", action="store_true", default=False, help="increase verbosity (default: %(default)s)")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.verbose:
|
||||||
|
logging.basicConfig(format="%(levelname)s: %(message)s", level=logging.DEBUG)
|
||||||
|
logging.debug("Debug logging mode: ON")
|
||||||
|
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
class JerryBreakpoint(object):
|
||||||
|
|
||||||
|
def __init__(self, line, offset, function):
|
||||||
|
self.line = line
|
||||||
|
self.offset = offset
|
||||||
|
self.function = function
|
||||||
|
self.active_index = -1
|
||||||
|
|
||||||
|
def to_string(self):
|
||||||
|
result = self.function.source
|
||||||
|
|
||||||
|
if result == "":
|
||||||
|
result = "<unknown>"
|
||||||
|
|
||||||
|
result += ":%d" % (self.line)
|
||||||
|
|
||||||
|
if self.function.name:
|
||||||
|
result += " (in %s)" % (self.function.name)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return ("Breakpoint(line:%d, offset:%d, active_index:%d)"
|
||||||
|
% (self.line, self.offset, self.active_index))
|
||||||
|
|
||||||
|
|
||||||
|
class JerryFunction(object):
|
||||||
|
|
||||||
|
def __init__(self, byte_code_cp, source, name, lines, offsets):
|
||||||
|
self.byte_code_cp = byte_code_cp
|
||||||
|
self.source = source
|
||||||
|
self.name = name
|
||||||
|
self.lines = {}
|
||||||
|
self.offsets = {}
|
||||||
|
self.first_line = -1
|
||||||
|
|
||||||
|
if len > 0:
|
||||||
|
self.first_line = lines[0]
|
||||||
|
|
||||||
|
for i in range(len(lines)):
|
||||||
|
line = lines[i]
|
||||||
|
offset = offsets[i]
|
||||||
|
breakpoint = JerryBreakpoint(line, offset, self)
|
||||||
|
self.lines[line] = breakpoint
|
||||||
|
self.offsets[offset] = breakpoint
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
result = ("Function(byte_code_cp:0x%x, source:\"%s\", name:\"%s\", { "
|
||||||
|
% (self.byte_code_cp, self.source, self.name))
|
||||||
|
|
||||||
|
result += ','.join([str(breakpoint) for breakpoint in self.lines.values()])
|
||||||
|
|
||||||
|
return result + " })"
|
||||||
|
|
||||||
|
|
||||||
|
class DebuggerPrompt(Cmd):
|
||||||
|
|
||||||
|
def __init__(self, debugger):
|
||||||
|
Cmd.__init__(self)
|
||||||
|
self.debugger = debugger
|
||||||
|
self.stop = False
|
||||||
|
|
||||||
|
def precmd(self, line):
|
||||||
|
self.stop = False
|
||||||
|
return line
|
||||||
|
|
||||||
|
def postcmd(self, stop, line):
|
||||||
|
return self.stop
|
||||||
|
|
||||||
|
def insert_breakpoint(self, args):
|
||||||
|
if args == "":
|
||||||
|
print("Error: Breakpoint index expected")
|
||||||
|
else:
|
||||||
|
set_breakpoint(self.debugger, args)
|
||||||
|
|
||||||
|
def do_break(self, args):
|
||||||
|
""" Insert breakpoints on the given lines """
|
||||||
|
self.insert_breakpoint(args)
|
||||||
|
|
||||||
|
def do_b(self, args):
|
||||||
|
""" Insert breakpoints on the given lines """
|
||||||
|
self.insert_breakpoint(args)
|
||||||
|
|
||||||
|
def exec_command(self, args, command_id):
|
||||||
|
self.stop = True
|
||||||
|
if args != "":
|
||||||
|
print("Error: No argument expected")
|
||||||
|
else:
|
||||||
|
self.debugger.send_command(command_id)
|
||||||
|
|
||||||
|
def do_continue(self, args):
|
||||||
|
""" Continue execution """
|
||||||
|
self.exec_command(args, JERRY_DEBUGGER_CONTINUE)
|
||||||
|
|
||||||
|
def do_c(self, args):
|
||||||
|
""" Continue execution """
|
||||||
|
self.exec_command(args, JERRY_DEBUGGER_CONTINUE)
|
||||||
|
|
||||||
|
def do_step(self, args):
|
||||||
|
""" Next breakpoint, step into functions """
|
||||||
|
self.exec_command(args, JERRY_DEBUGGER_STEP)
|
||||||
|
|
||||||
|
def do_s(self, args):
|
||||||
|
""" Next breakpoint, step into functions """
|
||||||
|
self.exec_command(args, JERRY_DEBUGGER_STEP)
|
||||||
|
|
||||||
|
def do_next(self, args):
|
||||||
|
""" Next breakpoint in the same context """
|
||||||
|
self.exec_command(args, JERRY_DEBUGGER_NEXT)
|
||||||
|
|
||||||
|
def do_n(self, args):
|
||||||
|
""" Next breakpoint in the same context """
|
||||||
|
self.exec_command(args, JERRY_DEBUGGER_NEXT)
|
||||||
|
|
||||||
|
def do_list(self, args):
|
||||||
|
""" Lists the available breakpoints """
|
||||||
|
if args != "":
|
||||||
|
print("Error: No argument expected")
|
||||||
|
return
|
||||||
|
|
||||||
|
for breakpoint in self.debugger.active_breakpoint_list.values():
|
||||||
|
source = breakpoint.function.source
|
||||||
|
print("%d: %s" % (breakpoint.active_index, breakpoint.to_string()))
|
||||||
|
|
||||||
|
def do_delete(self, args):
|
||||||
|
""" Delete the given breakpoint """
|
||||||
|
if not args:
|
||||||
|
print("Error: Breakpoint index expected")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
breakpoint_index = int(args)
|
||||||
|
except:
|
||||||
|
print("Error: Integer number expected")
|
||||||
|
return
|
||||||
|
|
||||||
|
if breakpoint_index in self.debugger.active_breakpoint_list:
|
||||||
|
breakpoint = self.debugger.active_breakpoint_list[breakpoint_index]
|
||||||
|
del self.debugger.active_breakpoint_list[breakpoint_index]
|
||||||
|
breakpoint.active_index = -1
|
||||||
|
self.debugger.send_breakpoint(breakpoint)
|
||||||
|
else:
|
||||||
|
print("Error: Breakpoint %d not found" % (breakpoint_index))
|
||||||
|
|
||||||
|
def exec_backtrace(self, args):
|
||||||
|
max_depth = 0
|
||||||
|
|
||||||
|
if args:
|
||||||
|
try:
|
||||||
|
max_depth = int(args)
|
||||||
|
if max_depth <= 0:
|
||||||
|
print("Error: Positive integer number expected")
|
||||||
|
return
|
||||||
|
except:
|
||||||
|
print("Error: Positive integer number expected")
|
||||||
|
return
|
||||||
|
|
||||||
|
message = pack(self.debugger.byte_order + "BBIB" + self.debugger.idx_format,
|
||||||
|
WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT,
|
||||||
|
WEBSOCKET_FIN_BIT + 1 + 4,
|
||||||
|
0,
|
||||||
|
JERRY_DEBUGGER_GET_BACKTRACE,
|
||||||
|
max_depth)
|
||||||
|
self.debugger.send_message(message)
|
||||||
|
self.stop = True
|
||||||
|
|
||||||
|
def do_backtrace(self, args):
|
||||||
|
""" Get backtrace data from debugger """
|
||||||
|
self.exec_backtrace(args)
|
||||||
|
|
||||||
|
def do_bt(self, args):
|
||||||
|
""" Get backtrace data from debugger """
|
||||||
|
self.exec_backtrace(args)
|
||||||
|
|
||||||
|
def do_dump(self, args):
|
||||||
|
""" Dump all of the debugger data """
|
||||||
|
pprint(self.debugger.function_list)
|
||||||
|
|
||||||
|
|
||||||
|
class Multimap(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.map = {}
|
||||||
|
|
||||||
|
def get(self, key):
|
||||||
|
if key in self.map:
|
||||||
|
return self.map[key]
|
||||||
|
return []
|
||||||
|
|
||||||
|
def insert(self, key, value):
|
||||||
|
if key in self.map:
|
||||||
|
self.map[key].append(value)
|
||||||
|
else:
|
||||||
|
self.map[key] = [value]
|
||||||
|
|
||||||
|
def delete(self, key, value):
|
||||||
|
items = self.map[key]
|
||||||
|
|
||||||
|
if len(items) == 1:
|
||||||
|
del self.map[key]
|
||||||
|
else:
|
||||||
|
del items[items.index(value)]
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "Multimap(%s)" % (self.map)
|
||||||
|
|
||||||
|
|
||||||
|
class JerryDebugger(object):
|
||||||
|
|
||||||
|
def __init__(self, address):
|
||||||
|
|
||||||
|
if ":" not in address:
|
||||||
|
print("Wrong address settings: Use the 'IP:PORT' format.")
|
||||||
|
else:
|
||||||
|
self.host, self.port = address.split(":")
|
||||||
|
self.port = int(self.port)
|
||||||
|
print("Address setup: %s:%s" % (self.host, self.port))
|
||||||
|
|
||||||
|
self.message_data = b""
|
||||||
|
self.function_list = {}
|
||||||
|
self.next_breakpoint_index = 0
|
||||||
|
self.active_breakpoint_list = {}
|
||||||
|
self.line_list = Multimap()
|
||||||
|
self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
self.client_socket.connect((self.host, self.port))
|
||||||
|
|
||||||
|
self.send_message(b"GET /jerry-debugger HTTP/1.1\r\n" +
|
||||||
|
b"Upgrade: websocket\r\n" +
|
||||||
|
b"Connection: Upgrade\r\n" +
|
||||||
|
b"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n")
|
||||||
|
result = b""
|
||||||
|
expected = (b"HTTP/1.1 101 Switching Protocols\r\n" +
|
||||||
|
b"Upgrade: websocket\r\n" +
|
||||||
|
b"Connection: Upgrade\r\n" +
|
||||||
|
b"Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n")
|
||||||
|
|
||||||
|
len_expected = len(expected)
|
||||||
|
|
||||||
|
while len(result) < len_expected:
|
||||||
|
result += self.client_socket.recv(1024)
|
||||||
|
|
||||||
|
len_result = len(result)
|
||||||
|
|
||||||
|
if result[0:len_expected] != expected:
|
||||||
|
raise Exception("Unexpected handshake")
|
||||||
|
|
||||||
|
if len_result > len_expected:
|
||||||
|
result = result[len_expected:]
|
||||||
|
|
||||||
|
len_expected = 6
|
||||||
|
# Network configurations, which has the following struct:
|
||||||
|
# header [2] - opcode[1], size[1]
|
||||||
|
# type [1]
|
||||||
|
# max_message_size [1]
|
||||||
|
# cpointer_size [1]
|
||||||
|
# little_endian [1]
|
||||||
|
|
||||||
|
while len(result) < len_expected:
|
||||||
|
result += self.client_socket.recv(1024)
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
self.max_message_size = ord(result[3])
|
||||||
|
self.cp_size = ord(result[4])
|
||||||
|
self.little_endian = ord(result[5])
|
||||||
|
|
||||||
|
if self.little_endian:
|
||||||
|
self.byte_order = "<"
|
||||||
|
logging.debug("Little-endian machine")
|
||||||
|
else:
|
||||||
|
self.byte_order = ">"
|
||||||
|
logging.debug("Big-endian machine")
|
||||||
|
|
||||||
|
if self.cp_size == 2:
|
||||||
|
self.cp_format = "H"
|
||||||
|
else:
|
||||||
|
self.cp_format = "I"
|
||||||
|
|
||||||
|
self.idx_format = "I"
|
||||||
|
|
||||||
|
logging.debug("Compressed pointer size: %d" % (self.cp_size))
|
||||||
|
|
||||||
|
if len_result > len_expected:
|
||||||
|
self.message_data = result[len_expected:]
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.client_socket.close()
|
||||||
|
|
||||||
|
def send_message(self, message):
|
||||||
|
size = len(message)
|
||||||
|
while size > 0:
|
||||||
|
bytes_send = self.client_socket.send(message)
|
||||||
|
if bytes_send < size:
|
||||||
|
message = message[bytes_send:]
|
||||||
|
size -= bytes_send
|
||||||
|
|
||||||
|
def send_breakpoint(self, breakpoint):
|
||||||
|
message = pack(self.byte_order + "BBIBB" + self.cp_format + self.idx_format,
|
||||||
|
WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT,
|
||||||
|
WEBSOCKET_FIN_BIT + 1 + 1 + self.cp_size + 4,
|
||||||
|
0,
|
||||||
|
JERRY_DEBUGGER_UPDATE_BREAKPOINT,
|
||||||
|
int(breakpoint.active_index >= 0),
|
||||||
|
breakpoint.function.byte_code_cp,
|
||||||
|
breakpoint.offset)
|
||||||
|
self.send_message(message)
|
||||||
|
|
||||||
|
def send_command(self, command):
|
||||||
|
message = pack(self.byte_order + "BBIB",
|
||||||
|
WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT,
|
||||||
|
WEBSOCKET_FIN_BIT + 1,
|
||||||
|
0,
|
||||||
|
command)
|
||||||
|
self.send_message(message)
|
||||||
|
|
||||||
|
def get_message(self):
|
||||||
|
if self.message_data is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
while True:
|
||||||
|
if len(self.message_data) >= 2:
|
||||||
|
if ord(self.message_data[0]) != WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT:
|
||||||
|
raise Exception("Unexpected data frame")
|
||||||
|
|
||||||
|
size = ord(self.message_data[1])
|
||||||
|
if size == 0 or size >= 126:
|
||||||
|
raise Exception("Unexpected data frame")
|
||||||
|
|
||||||
|
if len(self.message_data) >= size + 2:
|
||||||
|
result = self.message_data[0:size + 2]
|
||||||
|
self.message_data = self.message_data[size + 2:]
|
||||||
|
return result
|
||||||
|
|
||||||
|
data = self.client_socket.recv(MAX_BUFFER_SIZE)
|
||||||
|
if not data:
|
||||||
|
self.message_data = None
|
||||||
|
return None
|
||||||
|
|
||||||
|
self.message_data += data
|
||||||
|
|
||||||
|
|
||||||
|
def parse_source(debugger, data):
|
||||||
|
source_name = ""
|
||||||
|
function_name = ""
|
||||||
|
stack = [{"lines": [], "offsets": [], "name": ""}]
|
||||||
|
new_function_list = {}
|
||||||
|
|
||||||
|
while True:
|
||||||
|
if data is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
buffer_type = ord(data[2])
|
||||||
|
buffer_size = ord(data[1]) - 1
|
||||||
|
|
||||||
|
logging.debug("Parser buffer type: %d, message size: %d" % (buffer_type, buffer_size))
|
||||||
|
|
||||||
|
if buffer_type == JERRY_DEBUGGER_PARSE_ERROR:
|
||||||
|
logging.error("Parser error!")
|
||||||
|
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]
|
||||||
|
|
||||||
|
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]
|
||||||
|
|
||||||
|
elif buffer_type == JERRY_DEBUGGER_PARSE_FUNCTION:
|
||||||
|
logging.debug("Source name: %s, function name: %s" % (source_name, function_name))
|
||||||
|
stack.append({"name": function_name, "source": source_name, "lines": [], "offsets": []})
|
||||||
|
function_name = ""
|
||||||
|
|
||||||
|
elif buffer_type in [JERRY_DEBUGGER_BREAKPOINT_LIST, JERRY_DEBUGGER_BREAKPOINT_OFFSET_LIST]:
|
||||||
|
name = "lines"
|
||||||
|
if buffer_type == JERRY_DEBUGGER_BREAKPOINT_OFFSET_LIST:
|
||||||
|
name = "offsets"
|
||||||
|
|
||||||
|
logging.debug("Breakpoint %s received" % (name))
|
||||||
|
|
||||||
|
buffer_pos = 3
|
||||||
|
while buffer_size > 0:
|
||||||
|
line = unpack(debugger.byte_order + debugger.idx_format,
|
||||||
|
data[buffer_pos: buffer_pos + 4])
|
||||||
|
stack[-1][name].append(line[0])
|
||||||
|
buffer_pos += 4
|
||||||
|
buffer_size -= 4
|
||||||
|
|
||||||
|
elif buffer_type == JERRY_DEBUGGER_BYTE_CODE_CP:
|
||||||
|
byte_code_cp = unpack(debugger.byte_order + debugger.cp_format,
|
||||||
|
data[3: 3 + debugger.cp_size])[0]
|
||||||
|
|
||||||
|
logging.debug("Byte code cptr received: {0x%x}" % (byte_code_cp))
|
||||||
|
|
||||||
|
func_desc = stack.pop()
|
||||||
|
|
||||||
|
# We know the last item in the list is the general byte code.
|
||||||
|
if len(stack) == 0:
|
||||||
|
func_desc["source"] = source_name
|
||||||
|
|
||||||
|
function = JerryFunction(byte_code_cp,
|
||||||
|
func_desc["source"],
|
||||||
|
func_desc["name"],
|
||||||
|
func_desc["lines"],
|
||||||
|
func_desc["offsets"])
|
||||||
|
|
||||||
|
new_function_list[byte_code_cp] = function
|
||||||
|
|
||||||
|
if len(stack) == 0:
|
||||||
|
logging.debug("Parse completed.")
|
||||||
|
break
|
||||||
|
|
||||||
|
else:
|
||||||
|
logging.error("Parser error!")
|
||||||
|
return
|
||||||
|
|
||||||
|
data = debugger.get_message()
|
||||||
|
|
||||||
|
# Copy the ready list to the global storage.
|
||||||
|
debugger.function_list.update(new_function_list)
|
||||||
|
|
||||||
|
for function in new_function_list.values():
|
||||||
|
for line, breakpoint in function.lines.items():
|
||||||
|
debugger.line_list.insert(line, breakpoint)
|
||||||
|
|
||||||
|
|
||||||
|
def release_function(debugger, data):
|
||||||
|
byte_code_cp = unpack(debugger.byte_order + debugger.cp_format,
|
||||||
|
data[3: 3 + debugger.cp_size])[0]
|
||||||
|
|
||||||
|
function = debugger.function_list[byte_code_cp]
|
||||||
|
|
||||||
|
for line, breakpoint in function.lines.items():
|
||||||
|
debugger.line_list.delete(line, breakpoint)
|
||||||
|
if breakpoint.active_index >= 0:
|
||||||
|
del debugger.active_breakpoint_list[breakpoint.active_index]
|
||||||
|
|
||||||
|
del debugger.function_list[byte_code_cp]
|
||||||
|
|
||||||
|
message = pack(debugger.byte_order + "BBIB" + debugger.cp_format,
|
||||||
|
WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT,
|
||||||
|
WEBSOCKET_FIN_BIT + 1 + debugger.cp_size,
|
||||||
|
0,
|
||||||
|
JERRY_DEBUGGER_FREE_BYTE_CODE_CP,
|
||||||
|
byte_code_cp)
|
||||||
|
|
||||||
|
debugger.send_message(message)
|
||||||
|
|
||||||
|
logging.debug("Function {0x%x} byte-code released" % byte_code_cp)
|
||||||
|
|
||||||
|
|
||||||
|
def enable_breakpoint(debugger, breakpoint):
|
||||||
|
if breakpoint.active_index < 0:
|
||||||
|
debugger.next_breakpoint_index += 1
|
||||||
|
|
||||||
|
debugger.active_breakpoint_list[debugger.next_breakpoint_index] = breakpoint
|
||||||
|
breakpoint.active_index = debugger.next_breakpoint_index
|
||||||
|
debugger.send_breakpoint(breakpoint)
|
||||||
|
|
||||||
|
print ("Breakpoint %d at %s"
|
||||||
|
% (breakpoint.active_index, breakpoint.to_string()))
|
||||||
|
|
||||||
|
|
||||||
|
def set_breakpoint(debugger, string):
|
||||||
|
line = re.match("(.*):(\\d+)$", string)
|
||||||
|
found = False
|
||||||
|
|
||||||
|
if line:
|
||||||
|
source = line.group(1)
|
||||||
|
line = int(line.group(2))
|
||||||
|
|
||||||
|
for breakpoint in debugger.line_list.get(line):
|
||||||
|
func_source = breakpoint.function.source
|
||||||
|
if (source == func_source or
|
||||||
|
func_source.endswith("/" + source) or
|
||||||
|
func_source.endswith("\\" + source)):
|
||||||
|
|
||||||
|
enable_breakpoint(debugger, breakpoint)
|
||||||
|
found = True
|
||||||
|
|
||||||
|
else:
|
||||||
|
for function in debugger.function_list.values():
|
||||||
|
if function.name == string:
|
||||||
|
if function.first_line >= 0:
|
||||||
|
enable_breakpoint(debugger, function.lines[function.first_line])
|
||||||
|
else:
|
||||||
|
print("Function %s has no breakpoints." % (string))
|
||||||
|
found = True
|
||||||
|
|
||||||
|
if not found:
|
||||||
|
print("Breakpoint not found")
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = arguments_parse()
|
||||||
|
|
||||||
|
debugger = JerryDebugger(args.address)
|
||||||
|
|
||||||
|
logging.debug("Connected to JerryScript on %d port" % (debugger.port))
|
||||||
|
|
||||||
|
prompt = DebuggerPrompt(debugger)
|
||||||
|
prompt.prompt = "(jerry-debugger) "
|
||||||
|
|
||||||
|
while True:
|
||||||
|
|
||||||
|
data = debugger.get_message()
|
||||||
|
|
||||||
|
if not data: # Break the while loop if there is no more data.
|
||||||
|
break
|
||||||
|
|
||||||
|
buffer_type = ord(data[2])
|
||||||
|
buffer_size = ord(data[1]) - 1
|
||||||
|
|
||||||
|
logging.debug("Main buffer type: %d, message size: %d" % (buffer_type, buffer_size))
|
||||||
|
|
||||||
|
if buffer_type in [JERRY_DEBUGGER_PARSE_ERROR,
|
||||||
|
JERRY_DEBUGGER_BYTE_CODE_CP,
|
||||||
|
JERRY_DEBUGGER_PARSE_FUNCTION,
|
||||||
|
JERRY_DEBUGGER_BREAKPOINT_LIST,
|
||||||
|
JERRY_DEBUGGER_RESOURCE_NAME,
|
||||||
|
JERRY_DEBUGGER_RESOURCE_NAME_END,
|
||||||
|
JERRY_DEBUGGER_FUNCTION_NAME,
|
||||||
|
JERRY_DEBUGGER_FUNCTION_NAME_END]:
|
||||||
|
parse_source(debugger, data)
|
||||||
|
|
||||||
|
elif buffer_type == JERRY_DEBUGGER_RELEASE_BYTE_CODE_CP:
|
||||||
|
release_function(debugger, data)
|
||||||
|
|
||||||
|
elif buffer_type == JERRY_DEBUGGER_BREAKPOINT_HIT:
|
||||||
|
breakpoint_data = unpack(debugger.byte_order + debugger.cp_format + debugger.idx_format, data[3:])
|
||||||
|
|
||||||
|
function = debugger.function_list[breakpoint_data[0]]
|
||||||
|
breakpoint = function.offsets[breakpoint_data[1]]
|
||||||
|
|
||||||
|
breakpoint_index = ""
|
||||||
|
if breakpoint.active_index >= 0:
|
||||||
|
breakpoint_index = " breakpoint:%d" % (breakpoint.active_index)
|
||||||
|
|
||||||
|
print("Stopped at%s %s" % (breakpoint_index, breakpoint.to_string()))
|
||||||
|
|
||||||
|
prompt.cmdloop()
|
||||||
|
|
||||||
|
elif buffer_type in [JERRY_DEBUGGER_BACKTRACE, JERRY_DEBUGGER_BACKTRACE_END]:
|
||||||
|
frame_index = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
|
||||||
|
buffer_pos = 3
|
||||||
|
while buffer_size > 0:
|
||||||
|
breakpoint_data = unpack(debugger.byte_order + debugger.cp_format + debugger.idx_format,
|
||||||
|
data[buffer_pos: buffer_pos + debugger.cp_size + 4])
|
||||||
|
|
||||||
|
function = debugger.function_list[breakpoint_data[0]]
|
||||||
|
best_offset = -1
|
||||||
|
|
||||||
|
for offset in function.offsets:
|
||||||
|
if offset <= breakpoint_data[1] and offset > best_offset:
|
||||||
|
best_offset = offset
|
||||||
|
|
||||||
|
if best_offset >= 0:
|
||||||
|
breakpoint = function.offsets[best_offset]
|
||||||
|
print("Frame %d: %s" % (frame_index, breakpoint.to_string()))
|
||||||
|
elif function.name:
|
||||||
|
print("Frame %d: %s()" % (frame_index, function.name))
|
||||||
|
else:
|
||||||
|
print("Frame %d: <unknown>()" % (frame_index))
|
||||||
|
|
||||||
|
frame_index += 1
|
||||||
|
buffer_pos += 6
|
||||||
|
buffer_size -= 6
|
||||||
|
|
||||||
|
if buffer_type == JERRY_DEBUGGER_BACKTRACE_END:
|
||||||
|
break
|
||||||
|
|
||||||
|
data = debugger.get_message()
|
||||||
|
buffer_type = ord(data[2])
|
||||||
|
buffer_size = ord(data[1]) - 1
|
||||||
|
|
||||||
|
if buffer_type not in [JERRY_DEBUGGER_BACKTRACE,
|
||||||
|
JERRY_DEBUGGER_BACKTRACE_END]:
|
||||||
|
raise Exception("Backtrace data expected")
|
||||||
|
|
||||||
|
prompt.cmdloop()
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise Exception("Unknown message")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
main()
|
||||||
|
except socket.error as error_msg:
|
||||||
|
try:
|
||||||
|
errno = error_msg.errno
|
||||||
|
msg = str(error_msg)
|
||||||
|
except:
|
||||||
|
errno = error_msg[0]
|
||||||
|
msg = error_msg[1]
|
||||||
|
|
||||||
|
if errno == 111:
|
||||||
|
sys.exit("Failed to connect to the JerryScript debugger.")
|
||||||
|
elif errno == 32:
|
||||||
|
sys.exit("Connection closed.")
|
||||||
|
else:
|
||||||
|
sys.exit("Failed to connect to the JerryScript debugger.\nError: %s" % (msg))
|
||||||
+10
-1
@@ -124,6 +124,7 @@ print_help (char *name)
|
|||||||
" --parse-only\n"
|
" --parse-only\n"
|
||||||
" --show-opcodes\n"
|
" --show-opcodes\n"
|
||||||
" --show-regexp-opcodes\n"
|
" --show-regexp-opcodes\n"
|
||||||
|
" --start-debug-server\n"
|
||||||
" --save-snapshot-for-global FILE\n"
|
" --save-snapshot-for-global FILE\n"
|
||||||
" --save-snapshot-for-eval FILE\n"
|
" --save-snapshot-for-eval FILE\n"
|
||||||
" --save-literals-list-format FILE\n"
|
" --save-literals-list-format FILE\n"
|
||||||
@@ -435,6 +436,10 @@ main (int argc,
|
|||||||
"Ignoring 'show-regexp-opcodes' option because this feature is disabled!\n");
|
"Ignoring 'show-regexp-opcodes' option because this feature is disabled!\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (!strcmp ("--start-debug-server", argv[i]))
|
||||||
|
{
|
||||||
|
flags |= JERRY_INIT_DEBUGGER;
|
||||||
|
}
|
||||||
else if (!strcmp ("--save-snapshot-for-global", argv[i])
|
else if (!strcmp ("--save-snapshot-for-global", argv[i])
|
||||||
|| !strcmp ("--save-snapshot-for-eval", argv[i]))
|
|| !strcmp ("--save-snapshot-for-eval", argv[i]))
|
||||||
{
|
{
|
||||||
@@ -703,7 +708,11 @@ main (int argc,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ret_value = jerry_parse (source_p, source_size, false);
|
ret_value = jerry_parse_named_resource ((jerry_char_t *) file_names[i],
|
||||||
|
strlen (file_names[i]),
|
||||||
|
source_p,
|
||||||
|
source_size,
|
||||||
|
false);
|
||||||
|
|
||||||
if (!jerry_value_has_error_flag (ret_value) && !is_parse_only)
|
if (!jerry_value_has_error_flag (ret_value) && !is_parse_only)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
b do_backtrace.js:32
|
||||||
|
c
|
||||||
|
bt
|
||||||
|
c
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
Address setup: localhost:5001
|
||||||
|
Stopped at tests/debugger/do_backtrace.js:15
|
||||||
|
(jerry-debugger) Breakpoint 1 at tests/debugger/do_backtrace.js:32 (in f4)
|
||||||
|
(jerry-debugger) Stopped at breakpoint:1 tests/debugger/do_backtrace.js:32 (in f4)
|
||||||
|
(jerry-debugger) Frame 0: tests/debugger/do_backtrace.js:32 (in f4)
|
||||||
|
Frame 1: tests/debugger/do_backtrace.js:39 (in foo)
|
||||||
|
Frame 2: tests/debugger/do_backtrace.js:48 (in test)
|
||||||
|
Frame 3: tests/debugger/do_backtrace.js:60
|
||||||
|
(jerry-debugger) Connection closed.
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
// 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("first line of code");
|
||||||
|
|
||||||
|
function f1()
|
||||||
|
{
|
||||||
|
function f2()
|
||||||
|
{
|
||||||
|
return function f3() {
|
||||||
|
a = 4;
|
||||||
|
print("funciton f3");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
x =
|
||||||
|
6;
|
||||||
|
}
|
||||||
|
|
||||||
|
function f4() {
|
||||||
|
print("function f4");
|
||||||
|
}
|
||||||
|
|
||||||
|
function foo()
|
||||||
|
{
|
||||||
|
print("function foo");
|
||||||
|
var tmp = 4;
|
||||||
|
f4();
|
||||||
|
}
|
||||||
|
|
||||||
|
print ("var cat");
|
||||||
|
var cat = 'cat';
|
||||||
|
|
||||||
|
function test()
|
||||||
|
{
|
||||||
|
print("function test");
|
||||||
|
foo();
|
||||||
|
var a = 3;
|
||||||
|
var b = 5;
|
||||||
|
var c = a + b;
|
||||||
|
global_var = c;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
var
|
||||||
|
x =
|
||||||
|
1;
|
||||||
|
|
||||||
|
test();
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
break do_break.js:28
|
||||||
|
list
|
||||||
|
b do_break.js:40
|
||||||
|
delete 1
|
||||||
|
next
|
||||||
|
next
|
||||||
|
continue
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
Address setup: localhost:5001
|
||||||
|
Stopped at tests/debugger/do_break.js:15
|
||||||
|
(jerry-debugger) Breakpoint not found
|
||||||
|
(jerry-debugger) (jerry-debugger) Breakpoint not found
|
||||||
|
(jerry-debugger) Error: Breakpoint 1 not found
|
||||||
|
(jerry-debugger) Stopped at tests/debugger/do_break.js:42
|
||||||
|
(jerry-debugger) Stopped at tests/debugger/do_break.js:43
|
||||||
|
(jerry-debugger) Connection closed.
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
// 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("first line of code");
|
||||||
|
|
||||||
|
function f1()
|
||||||
|
{
|
||||||
|
function f2()
|
||||||
|
{
|
||||||
|
return function f3() {
|
||||||
|
a = 4;
|
||||||
|
print("funciton f3");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
x =
|
||||||
|
6;
|
||||||
|
}
|
||||||
|
|
||||||
|
function f4() {
|
||||||
|
print("function f4");
|
||||||
|
}
|
||||||
|
|
||||||
|
function foo()
|
||||||
|
{
|
||||||
|
print("function foo");
|
||||||
|
var tmp = 4;
|
||||||
|
f4();
|
||||||
|
}
|
||||||
|
|
||||||
|
print ("var cat");
|
||||||
|
var cat = 'cat';
|
||||||
|
|
||||||
|
function test()
|
||||||
|
{
|
||||||
|
print("function test");
|
||||||
|
foo();
|
||||||
|
var a = 3;
|
||||||
|
var b = 5;
|
||||||
|
var c = a + b;
|
||||||
|
global_var = c;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
var
|
||||||
|
x =
|
||||||
|
1;
|
||||||
|
|
||||||
|
test();
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
dump
|
||||||
|
c
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
Address setup: localhost:5001
|
||||||
|
Stopped at tests/debugger/do_dump.js:15
|
||||||
|
(jerry-debugger) {68: Function(byte_code_cp:0x44, source:"tests/debugger/do_dump.js", name:"f4", { Breakpoint(line:32, offset:17, active_index:-1),Breakpoint(line:31, offset:16, active_index:-1) }),
|
||||||
|
79: Function(byte_code_cp:0x4f, source:"tests/debugger/do_dump.js", name:"f1", { Breakpoint(line:17, offset:21, active_index:-1),Breakpoint(line:27, offset:22, active_index:-1) }),
|
||||||
|
102: Function(byte_code_cp:0x66, source:"tests/debugger/do_dump.js", name:"f2", { Breakpoint(line:19, offset:14, active_index:-1),Breakpoint(line:21, offset:15, active_index:-1) }),
|
||||||
|
105: Function(byte_code_cp:0x69, source:"tests/debugger/do_dump.js", name:"foo", { Breakpoint(line:35, offset:20, active_index:-1),Breakpoint(line:37, offset:21, active_index:-1),Breakpoint(line:38, offset:26, active_index:-1),Breakpoint(line:39, offset:31, active_index:-1) }),
|
||||||
|
125: Function(byte_code_cp:0x7d, source:"tests/debugger/do_dump.js", name:"f3", { Breakpoint(line:22, offset:25, active_index:-1),Breakpoint(line:23, offset:30, active_index:-1) }),
|
||||||
|
131: Function(byte_code_cp:0x83, source:"tests/debugger/do_dump.js", name:"", { Breakpoint(line:57, offset:63, active_index:-1),Breakpoint(line:42, offset:54, active_index:-1),Breakpoint(line:43, offset:59, active_index:-1),Breakpoint(line:60, offset:68, active_index:-1),Breakpoint(line:15, offset:49, active_index:-1) }),
|
||||||
|
154: Function(byte_code_cp:0x9a, source:"tests/debugger/do_dump.js", name:"test", { Breakpoint(line:45, offset:26, active_index:-1),Breakpoint(line:47, offset:27, active_index:-1),Breakpoint(line:48, offset:32, active_index:-1),Breakpoint(line:49, offset:36, active_index:-1),Breakpoint(line:50, offset:41, active_index:-1),Breakpoint(line:51, offset:46, active_index:-1),Breakpoint(line:52, offset:52, active_index:-1),Breakpoint(line:53, offset:56, active_index:-1) })}
|
||||||
|
(jerry-debugger) Connection closed.
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
// 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("first line of code");
|
||||||
|
|
||||||
|
function f1()
|
||||||
|
{
|
||||||
|
function f2()
|
||||||
|
{
|
||||||
|
return function f3() {
|
||||||
|
a = 4;
|
||||||
|
print("funciton f3");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
x =
|
||||||
|
6;
|
||||||
|
}
|
||||||
|
|
||||||
|
function f4() {
|
||||||
|
print("function f4");
|
||||||
|
}
|
||||||
|
|
||||||
|
function foo()
|
||||||
|
{
|
||||||
|
print("function foo");
|
||||||
|
var tmp = 4;
|
||||||
|
f4();
|
||||||
|
}
|
||||||
|
|
||||||
|
print ("var cat");
|
||||||
|
var cat = 'cat';
|
||||||
|
|
||||||
|
function test()
|
||||||
|
{
|
||||||
|
print("function test");
|
||||||
|
foo();
|
||||||
|
var a = 3;
|
||||||
|
var b = 5;
|
||||||
|
var c = a + b;
|
||||||
|
global_var = c;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
var
|
||||||
|
x =
|
||||||
|
1;
|
||||||
|
|
||||||
|
test();
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
break do_step.js:25
|
||||||
|
step
|
||||||
|
backtrace
|
||||||
|
b do_step.js:26
|
||||||
|
c
|
||||||
|
s
|
||||||
|
bt
|
||||||
|
c
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
Address setup: localhost:5001
|
||||||
|
Stopped at tests/debugger/do_step.js:25
|
||||||
|
(jerry-debugger) Breakpoint 1 at tests/debugger/do_step.js:25
|
||||||
|
(jerry-debugger) Stopped at tests/debugger/do_step.js:15 (in f1)
|
||||||
|
(jerry-debugger) Frame 0: tests/debugger/do_step.js:15 (in f1)
|
||||||
|
Frame 1: tests/debugger/do_step.js:25
|
||||||
|
(jerry-debugger) Breakpoint 2 at tests/debugger/do_step.js:26
|
||||||
|
(jerry-debugger) Stopped at breakpoint:2 tests/debugger/do_step.js:26
|
||||||
|
(jerry-debugger) Stopped at tests/debugger/do_step.js:20 (in f2)
|
||||||
|
(jerry-debugger) Frame 0: tests/debugger/do_step.js:20 (in f2)
|
||||||
|
Frame 1: tests/debugger/do_step.js:26
|
||||||
|
(jerry-debugger) Connection closed.
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
function f1()
|
||||||
|
{
|
||||||
|
var i = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function f2()
|
||||||
|
{
|
||||||
|
var y = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
f1();
|
||||||
|
f2();
|
||||||
@@ -56,6 +56,7 @@ def get_arguments():
|
|||||||
parser.add_argument('-j', '--jobs', metavar='N', action='store', type=int, default=multiprocessing.cpu_count() + 1, help='Allowed N build jobs at once (default: %(default)s)')
|
parser.add_argument('-j', '--jobs', metavar='N', action='store', type=int, default=multiprocessing.cpu_count() + 1, help='Allowed N build jobs at once (default: %(default)s)')
|
||||||
parser.add_argument('--jerry-cmdline', metavar='X', choices=['ON', 'OFF'], default='ON', type=str.upper, help='build jerry command line tool (%(choices)s; default: %(default)s)')
|
parser.add_argument('--jerry-cmdline', metavar='X', choices=['ON', 'OFF'], default='ON', type=str.upper, help='build jerry command line tool (%(choices)s; default: %(default)s)')
|
||||||
parser.add_argument('--jerry-cmdline-minimal', metavar='X', choices=['ON', 'OFF'], default='OFF', type=str.upper, help='build minimal version of the jerry command line tool (%(choices)s; default: %(default)s)')
|
parser.add_argument('--jerry-cmdline-minimal', metavar='X', choices=['ON', 'OFF'], default='OFF', type=str.upper, help='build minimal version of the jerry command line tool (%(choices)s; default: %(default)s)')
|
||||||
|
parser.add_argument('--jerry-debugger', metavar='X', choices=['ON', 'OFF'], default='OFF', type=str.upper, help='enable the jerry debugger (%(choices)s; default: %(default)s)')
|
||||||
parser.add_argument('--jerry-libc', metavar='X', choices=['ON', 'OFF'], default='ON', type=str.upper, help='build and use jerry-libc (%(choices)s; default: %(default)s)')
|
parser.add_argument('--jerry-libc', metavar='X', choices=['ON', 'OFF'], default='ON', type=str.upper, help='build and use jerry-libc (%(choices)s; default: %(default)s)')
|
||||||
parser.add_argument('--jerry-libm', metavar='X', choices=['ON', 'OFF'], default='ON', type=str.upper, help='build and use jerry-libm (%(choices)s; default: %(default)s)')
|
parser.add_argument('--jerry-libm', metavar='X', choices=['ON', 'OFF'], default='ON', type=str.upper, help='build and use jerry-libm (%(choices)s; default: %(default)s)')
|
||||||
parser.add_argument('--js-parser', metavar='X', choices=['ON', 'OFF'], default='ON', type=str.upper, help='enable js-parser (%(choices)s; default: %(default)s)')
|
parser.add_argument('--js-parser', metavar='X', choices=['ON', 'OFF'], default='ON', type=str.upper, help='enable js-parser (%(choices)s; default: %(default)s)')
|
||||||
@@ -117,6 +118,7 @@ def generate_build_options(arguments):
|
|||||||
|
|
||||||
build_options.append('-DFEATURE_PROFILE=%s' % PROFILE)
|
build_options.append('-DFEATURE_PROFILE=%s' % PROFILE)
|
||||||
|
|
||||||
|
build_options.append('-DFEATURE_DEBUGGER=%s' % arguments.jerry_debugger)
|
||||||
build_options.append('-DFEATURE_SNAPSHOT_EXEC=%s' % arguments.snapshot_exec)
|
build_options.append('-DFEATURE_SNAPSHOT_EXEC=%s' % arguments.snapshot_exec)
|
||||||
build_options.append('-DFEATURE_SNAPSHOT_SAVE=%s' % arguments.snapshot_save)
|
build_options.append('-DFEATURE_SNAPSHOT_SAVE=%s' % arguments.snapshot_save)
|
||||||
build_options.append('-DFEATURE_SYSTEM_ALLOCATOR=%s' % arguments.system_allocator)
|
build_options.append('-DFEATURE_SYSTEM_ALLOCATOR=%s' % arguments.system_allocator)
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ parser.add_argument('--check-doxygen', action='store_true', default=False, help=
|
|||||||
parser.add_argument('--check-vera', action='store_true', default=False, help='Run vera check')
|
parser.add_argument('--check-vera', action='store_true', default=False, help='Run vera check')
|
||||||
parser.add_argument('--check-license', action='store_true', default=False, help='Run license check')
|
parser.add_argument('--check-license', action='store_true', default=False, help='Run license check')
|
||||||
parser.add_argument('--buildoption-test', action='store_true', default=False, help='Run buildoption-test')
|
parser.add_argument('--buildoption-test', action='store_true', default=False, help='Run buildoption-test')
|
||||||
|
parser.add_argument('--jerry-debugger', action='store_true', default=False, help='Run jerry-debugger tests')
|
||||||
parser.add_argument('--jerry-tests', action='store_true', default=False, help='Run jerry-tests')
|
parser.add_argument('--jerry-tests', action='store_true', default=False, help='Run jerry-tests')
|
||||||
parser.add_argument('--jerry-test-suite', action='store_true', default=False, help='Run jerry-test-suite')
|
parser.add_argument('--jerry-test-suite', action='store_true', default=False, help='Run jerry-test-suite')
|
||||||
parser.add_argument('--unittests', action='store_true', default=False, help='Run unittests')
|
parser.add_argument('--unittests', action='store_true', default=False, help='Run unittests')
|
||||||
@@ -96,6 +97,11 @@ test262_test_suite_options = [
|
|||||||
Options('test262_tests'),
|
Options('test262_tests'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Test options for jerry-debugger
|
||||||
|
debugger_test_options = [
|
||||||
|
Options('jerry_debugger_tests', ['--debug', '--jerry-debugger=on', '--jerry-libc=off']),
|
||||||
|
]
|
||||||
|
|
||||||
# Test options for buildoption-test
|
# Test options for buildoption-test
|
||||||
jerry_buildoptions = [
|
jerry_buildoptions = [
|
||||||
Options('buildoption_test-lto', ['--lto=on']),
|
Options('buildoption_test-lto', ['--lto=on']),
|
||||||
@@ -145,6 +151,30 @@ def run_check(runnable):
|
|||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
def run_jerry_debugger_tests():
|
||||||
|
ret_build = ret_test = 0
|
||||||
|
for job in debugger_test_options:
|
||||||
|
ret_build = create_binary(job.build_args)
|
||||||
|
if ret_build:
|
||||||
|
break
|
||||||
|
|
||||||
|
for file in os.listdir(DEBUGGER_TESTS_DIR):
|
||||||
|
if file.endswith(".js"):
|
||||||
|
test_case, _ = os.path.splitext(file)
|
||||||
|
test_case_path = os.path.join (DEBUGGER_TESTS_DIR, test_case)
|
||||||
|
test_cmd = [
|
||||||
|
DEBUGGER_TEST_RUNNER_SCRIPT,
|
||||||
|
get_binary_path(job.out_dir),
|
||||||
|
DEBUGGER_CLIENT_SCRIPT,
|
||||||
|
os.path.relpath (test_case_path, PROJECT_DIR),
|
||||||
|
]
|
||||||
|
if job.test_args:
|
||||||
|
test_cmd.extend(job.test_args)
|
||||||
|
|
||||||
|
ret_test |= run_check(test_cmd)
|
||||||
|
|
||||||
|
return ret_build | ret_test
|
||||||
|
|
||||||
def run_jerry_tests():
|
def run_jerry_tests():
|
||||||
ret_build = ret_test = 0
|
ret_build = ret_test = 0
|
||||||
for job in jerry_tests_options:
|
for job in jerry_tests_options:
|
||||||
@@ -242,6 +272,9 @@ def main():
|
|||||||
if not ret and (script_args.all or script_args.check_license):
|
if not ret and (script_args.all or script_args.check_license):
|
||||||
ret = run_check([LICENSE_SCRIPT])
|
ret = run_check([LICENSE_SCRIPT])
|
||||||
|
|
||||||
|
if not ret and (script_args.all or script_args.jerry_debugger):
|
||||||
|
ret = run_jerry_debugger_tests()
|
||||||
|
|
||||||
if not ret and (script_args.all or script_args.jerry_tests):
|
if not ret and (script_args.all or script_args.jerry_tests):
|
||||||
ret = run_jerry_tests()
|
ret = run_jerry_tests()
|
||||||
|
|
||||||
|
|||||||
Executable
+38
@@ -0,0 +1,38 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
JERRY=$1
|
||||||
|
DEBUGGER_CLIENT=$2
|
||||||
|
TEST_CASE=$3
|
||||||
|
|
||||||
|
START_DEBUG_SERVER="${JERRY} ${TEST_CASE}.js --start-debug-server &"
|
||||||
|
|
||||||
|
echo "$START_DEBUG_SERVER"
|
||||||
|
eval "$START_DEBUG_SERVER"
|
||||||
|
sleep 1s
|
||||||
|
|
||||||
|
RESULT=$((cat "${TEST_CASE}.cmd" | ${DEBUGGER_CLIENT}) 2>&1)
|
||||||
|
DIFF=$(diff -u0 ${TEST_CASE}.expected <(echo "$RESULT"))
|
||||||
|
|
||||||
|
if [ -n "$DIFF" ]
|
||||||
|
then
|
||||||
|
echo "$DIFF"
|
||||||
|
echo "${TEST_CASE} failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "${TEST_CASE} passed"
|
||||||
|
exit 0
|
||||||
@@ -18,6 +18,7 @@ from os import path
|
|||||||
|
|
||||||
TOOLS_DIR = path.dirname(path.abspath(__file__))
|
TOOLS_DIR = path.dirname(path.abspath(__file__))
|
||||||
PROJECT_DIR = path.normpath(path.join(TOOLS_DIR, '..'))
|
PROJECT_DIR = path.normpath(path.join(TOOLS_DIR, '..'))
|
||||||
|
DEBUGGER_TESTS_DIR = path.join(PROJECT_DIR, 'tests/debugger')
|
||||||
JERRY_TESTS_DIR = path.join(PROJECT_DIR, 'tests/jerry')
|
JERRY_TESTS_DIR = path.join(PROJECT_DIR, 'tests/jerry')
|
||||||
JERRY_TEST_SUITE_DIR = path.join(PROJECT_DIR, 'tests/jerry-test-suite')
|
JERRY_TEST_SUITE_DIR = path.join(PROJECT_DIR, 'tests/jerry-test-suite')
|
||||||
JERRY_TEST_SUITE_MINIMAL_LIST = path.join(PROJECT_DIR, 'tests/jerry-test-suite/minimal-profile-list')
|
JERRY_TEST_SUITE_MINIMAL_LIST = path.join(PROJECT_DIR, 'tests/jerry-test-suite/minimal-profile-list')
|
||||||
@@ -26,6 +27,8 @@ TEST262_TEST_SUITE_DIR = path.join(PROJECT_DIR, 'tests/test262')
|
|||||||
|
|
||||||
BUILD_SCRIPT = path.join(TOOLS_DIR, 'build.py')
|
BUILD_SCRIPT = path.join(TOOLS_DIR, 'build.py')
|
||||||
CPPCHECK_SCRIPT = path.join(TOOLS_DIR, 'check-cppcheck.sh')
|
CPPCHECK_SCRIPT = path.join(TOOLS_DIR, 'check-cppcheck.sh')
|
||||||
|
DEBUGGER_CLIENT_SCRIPT = path.join(PROJECT_DIR, 'jerry-debugger/jerry-client-ws.py')
|
||||||
|
DEBUGGER_TEST_RUNNER_SCRIPT = path.join(TOOLS_DIR, 'runners/run-debugger-test.sh')
|
||||||
DOXYGEN_SCRIPT = path.join(TOOLS_DIR, 'check-doxygen.sh')
|
DOXYGEN_SCRIPT = path.join(TOOLS_DIR, 'check-doxygen.sh')
|
||||||
LICENSE_SCRIPT = path.join(TOOLS_DIR, 'check-license.py')
|
LICENSE_SCRIPT = path.join(TOOLS_DIR, 'check-license.py')
|
||||||
SIGNED_OFF_SCRIPT = path.join(TOOLS_DIR, 'check-signed-off.sh')
|
SIGNED_OFF_SCRIPT = path.join(TOOLS_DIR, 'check-signed-off.sh')
|
||||||
|
|||||||
Reference in New Issue
Block a user