mirror of
https://github.com/Samsung/escargot.git
synced 2026-06-22 10:01:50 +00:00
Add support for breakpoints in the Devtools Debugger
- Add, remove breakpoints - Resume execution - Step Into, Step Out, Step Over - Deactivate/Reactivate all breakpoints Signed-off-by: Máté Tokodi <mate.tokodi@szteszoftver.hu>
This commit is contained in:
parent
769e86e32a
commit
74cc8347e3
7 changed files with 564 additions and 239 deletions
|
|
@ -20,11 +20,14 @@
|
|||
#ifndef __Debugger__
|
||||
#define __Debugger__
|
||||
|
||||
#include "runtime/Environment.h"
|
||||
#include "util/Vector.h"
|
||||
|
||||
#ifdef ESCARGOT_DEBUGGER
|
||||
namespace Escargot {
|
||||
|
||||
class ByteCode;
|
||||
|
||||
#define ESCARGOT_DEBUGGER_MAX_STACK_TRACE_LENGTH 8
|
||||
|
||||
/* WebSocket max length encoded in one byte. */
|
||||
|
|
@ -57,6 +60,17 @@ public:
|
|||
uint32_t offset; // bytecode offset
|
||||
};
|
||||
|
||||
struct BreakpointByteCodeLocation {
|
||||
BreakpointByteCodeLocation(const uint32_t line, ByteCode* breakpointByteCode)
|
||||
: line(line)
|
||||
, byteCode(breakpointByteCode)
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t line; // source code line
|
||||
ByteCode* byteCode; // bytecode pointer
|
||||
};
|
||||
|
||||
typedef std::vector<BreakpointLocation> BreakpointLocationVector;
|
||||
|
||||
struct BreakpointLocationsInfo {
|
||||
|
|
@ -150,6 +164,24 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
static LexicalEnvironment* getFunctionLexEnv(ExecutionState* state)
|
||||
{
|
||||
LexicalEnvironment* lexEnv = state->lexicalEnvironment();
|
||||
|
||||
while (lexEnv) {
|
||||
EnvironmentRecord* record = lexEnv->record();
|
||||
|
||||
if (record->isDeclarativeEnvironmentRecord()
|
||||
&& record->asDeclarativeEnvironmentRecord()->isFunctionEnvironmentRecord()) {
|
||||
return lexEnv;
|
||||
}
|
||||
|
||||
lexEnv = lexEnv->outerEnvironment();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void setStopState(ExecutionState* stopState)
|
||||
{
|
||||
m_stopState = stopState;
|
||||
|
|
|
|||
|
|
@ -16,14 +16,18 @@
|
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "Escargot.h"
|
||||
#include "DebuggerTcp.h"
|
||||
#include "DebuggerDevtools.h"
|
||||
#include "DebuggerHttpRouter.h"
|
||||
#include "DebuggerDevtoolsMessageBuilder.h"
|
||||
|
||||
#include "interpreter/ByteCode.h"
|
||||
#include "parser/Script.h"
|
||||
|
||||
#include "rapidjson/document.h"
|
||||
#include "rapidjson/prettywriter.h"
|
||||
#include "rapidjson/stringbuffer.h"
|
||||
|
|
@ -33,15 +37,44 @@
|
|||
|
||||
namespace Escargot {
|
||||
|
||||
bool DebuggerDevtools::sendMessage(const std::string& msg, const int length)
|
||||
template <typename... Args>
|
||||
std::string string_format(const std::string& format, Args... args)
|
||||
{
|
||||
const int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1; // Extra space for '\0'
|
||||
if (size_s <= 0) {
|
||||
throw std::runtime_error("Error during formatting.");
|
||||
}
|
||||
const auto size = static_cast<size_t>(size_s);
|
||||
const std::unique_ptr<char[]> buf(new char[size]);
|
||||
std::snprintf(buf.get(), size, format.c_str(), args...);
|
||||
return { buf.get(), buf.get() + size - 1 }; // We don't want the '\0' inside
|
||||
}
|
||||
|
||||
|
||||
bool DebuggerDevtools::sendMessage(const std::string& msg, const size_t length)
|
||||
{
|
||||
if (UNLIKELY(!m_networkEnabled || !m_debuggerEnabled || !m_runtimeEnabled)) {
|
||||
m_pendingMessages.emplace_back(msg);
|
||||
return true;
|
||||
}
|
||||
|
||||
ESCARGOT_LOG_INFO("Sending message: %s\n", msg.c_str());
|
||||
return send(0, msg.c_str(), length == -1 ? msg.length() : length);
|
||||
const bool result = send(0, msg.c_str(), length == static_cast<size_t>(-1) ? msg.length() : length);
|
||||
if (result) {
|
||||
ESCARGOT_LOG_INFO("Sent message: %s\n", msg.c_str());
|
||||
} else {
|
||||
ESCARGOT_LOG_ERROR("Error sending message!");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DebuggerDevtools::sendJSONDocument(const rapidjson::Document& document)
|
||||
{
|
||||
rapidjson::StringBuffer sb;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
|
||||
document.Accept(writer);
|
||||
const char* jsonReplyString = sb.GetString();
|
||||
|
||||
return sendMessage(jsonReplyString);
|
||||
}
|
||||
|
||||
void DebuggerDevtools::init(const char* options, Context* context)
|
||||
|
|
@ -83,7 +116,19 @@ uint8_t DebuggerDevtools::registerScript(String* source, String* srcName)
|
|||
return newId;
|
||||
}
|
||||
|
||||
void DebuggerDevtools::parseCompleted(String* source, String* srcName, size_t originLineOffset, String* error)
|
||||
static void computeEndLocation(const LChar* src, size_t length, uint32_t& endLine, uint32_t& endColumn)
|
||||
{
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
if (src[i] == '\n') {
|
||||
endLine++;
|
||||
endColumn = 0;
|
||||
} else {
|
||||
endColumn++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DebuggerDevtools::parseCompleted(String* source, String* srcName, const size_t originLineOffset, String* error)
|
||||
{
|
||||
if (!enabled()) {
|
||||
return;
|
||||
|
|
@ -94,44 +139,121 @@ void DebuggerDevtools::parseCompleted(String* source, String* srcName, size_t or
|
|||
return;
|
||||
}
|
||||
|
||||
uint8_t scriptId = registerScript(source, srcName);
|
||||
const uint8_t scriptId = registerScript(source, srcName);
|
||||
std::set<BreakpointByteCodeLocation, decltype(compareBreakpointLocations)*> breakpointLocationsSet(compareBreakpointLocations);
|
||||
m_breakpointInfo.emplace(scriptId, breakpointLocationsSet);
|
||||
|
||||
sendMessage(DebuggerDevtoolsMessageBuilder::buildScriptParsedMessage(scriptId, source, srcName));
|
||||
const size_t breakpointLocationsSize = m_breakpointLocationsVector.size();
|
||||
|
||||
if (originLineOffset > 0) {
|
||||
for (size_t i = 0; i < breakpointLocationsSize; i++) {
|
||||
// adjust line offset for manipulated source code
|
||||
// inserted breakpoint's line info should be bigger than `originLineOffset`
|
||||
BreakpointLocationVector& locationVector = m_breakpointLocationsVector[i]->breakpointLocations;
|
||||
for (auto& j : locationVector) {
|
||||
ASSERT(j.line > originLineOffset);
|
||||
j.line -= originLineOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < breakpointLocationsSize; i++) {
|
||||
/* function bytecode information */
|
||||
InterpretedCodeBlock* codeBlock = reinterpret_cast<InterpretedCodeBlock*>(m_breakpointLocationsVector[i]->weakCodeRef);
|
||||
uint8_t* byteCodeStart = codeBlock->byteCodeBlock()->m_code.data();
|
||||
|
||||
/* save breakpoint locations. */
|
||||
BreakpointLocationVector breakpointLocations = m_breakpointLocationsVector[i]->breakpointLocations;
|
||||
for (auto& breakpointLocation : breakpointLocations) {
|
||||
auto b = BreakpointByteCodeLocation(breakpointLocation.line, reinterpret_cast<ByteCode*>(byteCodeStart + breakpointLocation.offset));
|
||||
b.byteCode->m_loc.line = breakpointLocation.line;
|
||||
m_breakpointInfo[scriptId].insert(b);
|
||||
}
|
||||
}
|
||||
|
||||
rapidjson::Document reply;
|
||||
reply.SetObject();
|
||||
|
||||
rapidjson::Value method(rapidjson::kStringType);
|
||||
rapidjson::Value paramsObject(rapidjson::kObjectType);
|
||||
rapidjson::Value resolvedBreakpointsArray(rapidjson::kArrayType);
|
||||
|
||||
reply.AddMember("method", "Debugger.scriptParsed", reply.GetAllocator());
|
||||
reply.AddMember("params", paramsObject, reply.GetAllocator());
|
||||
|
||||
rapidjson::Value scriptIdValue;
|
||||
std::string scriptIdString = string_format("%d", scriptId);
|
||||
scriptIdValue.SetString(scriptIdString.c_str(), scriptIdString.length(), reply.GetAllocator());
|
||||
reply["params"].AddMember("scriptId", scriptIdValue, reply.GetAllocator());
|
||||
|
||||
rapidjson::Value urlString;
|
||||
std::string url = reinterpret_cast<const char*>(srcName->characters8());
|
||||
urlString.SetString(url.c_str(), url.length(), reply.GetAllocator());
|
||||
reply["params"].AddMember("url", urlString, reply.GetAllocator());
|
||||
|
||||
reply["params"].AddMember("startLine", 0, reply.GetAllocator());
|
||||
reply["params"].AddMember("startColumn", 0, reply.GetAllocator());
|
||||
|
||||
uint32_t endLine = 0;
|
||||
uint32_t endColumn = 0;
|
||||
computeEndLocation(source->characters8(), source->length(), endLine, endColumn);
|
||||
reply["params"].AddMember("endLine", endLine, reply.GetAllocator());
|
||||
reply["params"].AddMember("endColumn", endColumn, reply.GetAllocator());
|
||||
|
||||
reply["params"].AddMember("executionContextId", 1, reply.GetAllocator());
|
||||
|
||||
sendJSONDocument(reply);
|
||||
}
|
||||
|
||||
void DebuggerDevtools::sendPausedEvent(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state)
|
||||
void DebuggerDevtools::sendPausedEvent(ByteCodeBlock* byteCodeBlock, const uint32_t offset, ExecutionState* state, const bool breakpoint)
|
||||
{
|
||||
// TODO: Placeholder info
|
||||
const char* msg = "{\"method\":\"Debugger.paused\","
|
||||
"\"params\":{"
|
||||
"\"callFrames\":[{"
|
||||
"\"callFrameId\":\"frame:0\","
|
||||
"\"functionName\":\"\","
|
||||
"\"location\":{"
|
||||
"\"scriptId\":\"1\","
|
||||
"\"lineNumber\":0,"
|
||||
"\"columnNumber\":0"
|
||||
"},"
|
||||
"\"url\":\"hello.js\","
|
||||
"\"scopeChain\":[{"
|
||||
"\"type\":\"global\","
|
||||
"\"object\":{"
|
||||
"\"type\":\"object\","
|
||||
"\"className\":\"global\","
|
||||
"\"description\":\"global\","
|
||||
"\"objectId\":\"global:1\""
|
||||
"}"
|
||||
"}],"
|
||||
"\"this\":{"
|
||||
"\"type\":\"undefined\""
|
||||
"}"
|
||||
"}],"
|
||||
"\"reason\":\"breakpoint\","
|
||||
"\"hitBreakpoints\":[\"breakpoint:1\"]"
|
||||
"}"
|
||||
"}";
|
||||
const auto* byteCode = reinterpret_cast<ByteCode*>(byteCodeBlock->m_code.data() + offset);
|
||||
const auto* filename = reinterpret_cast<const char*>(byteCodeBlock->codeBlock()->script()->srcName()->characters8());
|
||||
const uint8_t scripId = m_scriptIdByUrl[reinterpret_cast<const char*>(byteCodeBlock->codeBlock()->script()->srcName()->characters8())];
|
||||
|
||||
sendMessage(msg);
|
||||
const uint64_t line = byteCode->m_loc.line - 1; // chrome starts line indexes at 0
|
||||
const uint64_t column = byteCode->m_loc.column - 1; // chrome starts column indexes at 0
|
||||
|
||||
// TODO: some placeholder info
|
||||
const std::string msg = string_format("{\"method\":\"Debugger.paused\","
|
||||
"\"params\":{"
|
||||
"\"callFrames\":[{"
|
||||
"\"callFrameId\":\"frame:0\","
|
||||
"\"functionName\":\"%s\","
|
||||
"\"location\":{"
|
||||
"\"scriptId\":\"%d\","
|
||||
"\"lineNumber\":%lu,"
|
||||
"\"columnNumber\":%lu"
|
||||
"},"
|
||||
"\"url\":\"%s\","
|
||||
"\"scopeChain\":[{"
|
||||
"\"type\":\"global\","
|
||||
"\"object\":{"
|
||||
"\"type\":\"object\","
|
||||
"\"className\":\"global\","
|
||||
"\"description\":\"global\","
|
||||
"\"objectId\":\"global:1\""
|
||||
"}"
|
||||
"}],"
|
||||
"\"this\":{"
|
||||
"\"type\":\"undefined\""
|
||||
"}"
|
||||
"}],"
|
||||
"\"reason\":\"%s\","
|
||||
"\"hitBreakpoints\":[\"%s:%lu:%lu\"]"
|
||||
"}"
|
||||
"}",
|
||||
reinterpret_cast<const char*>(byteCodeBlock->codeBlock()->functionName().string()->characters8()),
|
||||
scripId,
|
||||
line,
|
||||
column,
|
||||
filename,
|
||||
breakpoint ? "Breakpoint" : "Break on start",
|
||||
filename,
|
||||
line,
|
||||
column);
|
||||
|
||||
sendMessage(msg, msg.length());
|
||||
}
|
||||
|
||||
void DebuggerDevtools::stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state)
|
||||
|
|
@ -144,8 +266,11 @@ void DebuggerDevtools::stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t o
|
|||
return;
|
||||
}
|
||||
|
||||
uint8_t* byteCodeStart = byteCodeBlock->m_code.data();
|
||||
sendPausedEvent(byteCodeBlock, offset, state);
|
||||
sendPausedEvent(byteCodeBlock, offset, state, !m_startBreakpoint);
|
||||
|
||||
if (m_startBreakpoint) {
|
||||
m_startBreakpoint = false;
|
||||
}
|
||||
|
||||
if (!enabled()) {
|
||||
return;
|
||||
|
|
@ -191,7 +316,7 @@ constexpr MessageType messageType(const char (&methodName)[N], const MessageHand
|
|||
return MessageType{ methodName, handler };
|
||||
}
|
||||
|
||||
bool DebuggerDevtools::resume(rapidjson::Document& jsonMessage)
|
||||
bool DebuggerDevtools::resume(rapidjson::Document& jsonMessage, ExecutionState* state)
|
||||
{
|
||||
replyOK(jsonMessage);
|
||||
|
||||
|
|
@ -202,126 +327,360 @@ bool DebuggerDevtools::resume(rapidjson::Document& jsonMessage)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool DebuggerDevtools::sendProperties(rapidjson::Document& jsonMessage)
|
||||
bool DebuggerDevtools::stepInto(rapidjson::Document& jsonMessage, ExecutionState* state)
|
||||
{
|
||||
uint32_t requestId = jsonMessage["id"].GetUint();
|
||||
replyOK(jsonMessage);
|
||||
|
||||
char buffer[4096];
|
||||
if (m_stopState == ESCARGOT_DEBUGGER_IN_EVAL_MODE) {
|
||||
return true;
|
||||
}
|
||||
m_stopState = ESCARGOT_DEBUGGER_ALWAYS_STOP;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DebuggerDevtools::stepOver(rapidjson::Document& jsonMessage, ExecutionState* state)
|
||||
{
|
||||
replyOK(jsonMessage);
|
||||
|
||||
if (m_stopState != ESCARGOT_DEBUGGER_IN_WAIT_MODE) {
|
||||
return true;
|
||||
}
|
||||
m_stopState = state;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DebuggerDevtools::stepOut(rapidjson::Document& jsonMessage, ExecutionState* state)
|
||||
{
|
||||
replyOK(jsonMessage);
|
||||
|
||||
if (m_stopState != ESCARGOT_DEBUGGER_IN_WAIT_MODE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const LexicalEnvironment* lexEnv = getFunctionLexEnv(state);
|
||||
|
||||
if (!lexEnv) {
|
||||
m_stopState = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
ExecutionState* stopState = state->parent();
|
||||
|
||||
while (stopState && getFunctionLexEnv(stopState) == lexEnv) {
|
||||
stopState = stopState->parent();
|
||||
}
|
||||
|
||||
m_stopState = stopState;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DebuggerDevtools::sendProperties(rapidjson::Document& jsonMessage, ExecutionState* state)
|
||||
{
|
||||
// TODO: placeholder info
|
||||
snprintf(buffer, sizeof(buffer),
|
||||
"{\"id\":%u,\"result\":{"
|
||||
"\"result\":["
|
||||
"{"
|
||||
"\"name\":\"print\","
|
||||
"\"configurable\":true,"
|
||||
"\"enumerable\":true,"
|
||||
"\"value\":{"
|
||||
"\"type\":\"function\","
|
||||
"\"description\":\"function print()\""
|
||||
"}"
|
||||
"},"
|
||||
"{"
|
||||
"\"name\":\"c\","
|
||||
"\"configurable\":true,"
|
||||
"\"enumerable\":true,"
|
||||
"\"value\":{"
|
||||
"\"type\":\"number\","
|
||||
"\"value\":8,"
|
||||
"\"description\":\"8\""
|
||||
"}"
|
||||
"},"
|
||||
"{"
|
||||
"\"name\":\"globalVar\","
|
||||
"\"configurable\":true,"
|
||||
"\"enumerable\":true,"
|
||||
"\"value\":{"
|
||||
"\"type\":\"string\","
|
||||
"\"value\":\"escargot\","
|
||||
"\"description\":\"escargot\""
|
||||
"}"
|
||||
"}"
|
||||
"]"
|
||||
"}}",
|
||||
requestId);
|
||||
const std::string msg = string_format("{\"id\":%u,\"result\":{"
|
||||
"\"result\":["
|
||||
"{"
|
||||
"\"name\":\"print\","
|
||||
"\"configurable\":true,"
|
||||
"\"enumerable\":true,"
|
||||
"\"value\":{"
|
||||
"\"type\":\"function\","
|
||||
"\"description\":\"function print()\""
|
||||
"}"
|
||||
"},"
|
||||
"{"
|
||||
"\"name\":\"c\","
|
||||
"\"configurable\":true,"
|
||||
"\"enumerable\":true,"
|
||||
"\"value\":{"
|
||||
"\"type\":\"number\","
|
||||
"\"value\":8,"
|
||||
"\"description\":\"8\""
|
||||
"}"
|
||||
"},"
|
||||
"{"
|
||||
"\"name\":\"globalVar\","
|
||||
"\"configurable\":true,"
|
||||
"\"enumerable\":true,"
|
||||
"\"value\":{"
|
||||
"\"type\":\"string\","
|
||||
"\"value\":\"escargot\","
|
||||
"\"description\":\"escargot\""
|
||||
"}"
|
||||
"}"
|
||||
"]"
|
||||
"}}",
|
||||
jsonMessage["id"].GetUint());
|
||||
|
||||
sendMessage(buffer);
|
||||
sendMessage(msg, msg.length());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DebuggerDevtools::sendSourceCode(rapidjson::Document& jsonMessage)
|
||||
bool DebuggerDevtools::sendSourceCode(rapidjson::Document& jsonMessage, ExecutionState* state)
|
||||
{
|
||||
const uint32_t requestId = jsonMessage["id"].GetUint();
|
||||
const uint32_t scriptId = std::stoi(jsonMessage["params"]["scriptId"].GetString());
|
||||
|
||||
auto it = m_scriptsById.find(scriptId);
|
||||
const auto it = m_scriptsById.find(scriptId);
|
||||
if (it == m_scriptsById.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String* source = it->second.source;
|
||||
const String* source = it->second.source;
|
||||
|
||||
if (!source->is8Bit()) {
|
||||
ESCARGOT_LOG_ERROR("Only 8 bit characters are supported right now...");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string message = DebuggerDevtoolsMessageBuilder::buildSourceCodeMessage(requestId, source);
|
||||
const std::string message = DebuggerDevtoolsMessageBuilder::buildSourceCodeMessage(requestId, source);
|
||||
return sendMessage(message);
|
||||
}
|
||||
|
||||
bool DebuggerDevtools::replyOK(rapidjson::Document& jsonMessage)
|
||||
bool DebuggerDevtools::replyOK(rapidjson::Document& jsonMessage, ExecutionState* state)
|
||||
{
|
||||
char reply[32];
|
||||
const int jsonReplyStringLength = snprintf(reply, sizeof(reply),
|
||||
R"({"id": %d, "result": {}})",
|
||||
jsonMessage["id"].GetInt());
|
||||
const std::string jsonReplyString = string_format(R"({"id": %d, "result": {}})",
|
||||
jsonMessage["id"].GetInt());
|
||||
|
||||
sendMessage(reply, jsonReplyStringLength);
|
||||
sendMessage(jsonReplyString, jsonReplyString.length());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DebuggerDevtools::enableNetwork(rapidjson::Document& jsonMessage)
|
||||
bool DebuggerDevtools::enableNetwork(rapidjson::Document& jsonMessage, ExecutionState* state)
|
||||
{
|
||||
this->m_networkEnabled = true;
|
||||
return replyOK(jsonMessage);
|
||||
}
|
||||
|
||||
bool DebuggerDevtools::enableDebugger(rapidjson::Document& jsonMessage)
|
||||
bool DebuggerDevtools::enableDebugger(rapidjson::Document& jsonMessage, ExecutionState* state)
|
||||
{
|
||||
this->m_debuggerEnabled = true;
|
||||
return replyOK(jsonMessage);
|
||||
|
||||
rapidjson::Document reply;
|
||||
reply.SetObject();
|
||||
|
||||
reply.AddMember("id", jsonMessage["id"], reply.GetAllocator());
|
||||
|
||||
rapidjson::Value result(rapidjson::kObjectType);
|
||||
reply.AddMember("result", result, reply.GetAllocator());
|
||||
|
||||
rapidjson::Value debuggerIdValue;
|
||||
std::string debuggerIdString = string_format("escargot-%d", rand() % 1000000);
|
||||
debuggerIdValue.SetString(debuggerIdString.c_str(), debuggerIdString.length(), reply.GetAllocator());
|
||||
|
||||
reply["result"].AddMember("debuggerId", debuggerIdValue, reply.GetAllocator());
|
||||
|
||||
return sendJSONDocument(reply);
|
||||
}
|
||||
|
||||
bool DebuggerDevtools::enableRuntime(rapidjson::Document& jsonMessage)
|
||||
bool DebuggerDevtools::enableRuntime(rapidjson::Document& jsonMessage, ExecutionState* state)
|
||||
{
|
||||
this->m_runtimeEnabled = true;
|
||||
return replyOK(jsonMessage);
|
||||
}
|
||||
|
||||
bool DebuggerDevtools::enableProfiler(rapidjson::Document& jsonMessage)
|
||||
bool DebuggerDevtools::enableProfiler(rapidjson::Document& jsonMessage, ExecutionState* state)
|
||||
{
|
||||
this->m_profilerEnabled = true;
|
||||
return replyOK(jsonMessage);
|
||||
}
|
||||
|
||||
bool DebuggerDevtools::setPauseOnExceptions(rapidjson::Document& jsonMessage)
|
||||
bool DebuggerDevtools::setPauseOnExceptions(rapidjson::Document& jsonMessage, ExecutionState* state)
|
||||
{
|
||||
// TODO: handdle other parameters: state: none (unset), uncaught, caught, all
|
||||
this->m_pauseOnExceptions = true;
|
||||
return replyOK(jsonMessage);
|
||||
}
|
||||
|
||||
bool DebuggerDevtools::replyMethodNotFound(rapidjson::Document& jsonMessage)
|
||||
void setBreakpointState(ByteCode* breakpoint, const bool enabled)
|
||||
{
|
||||
char reply[256];
|
||||
#if defined(ESCARGOT_COMPUTED_GOTO_INTERPRETER)
|
||||
if (enabled) {
|
||||
if (breakpoint->m_opcodeInAddress != g_opcodeTable.m_addressTable[BreakpointDisabledOpcode]) {
|
||||
return;
|
||||
}
|
||||
breakpoint->m_opcodeInAddress = g_opcodeTable.m_addressTable[BreakpointEnabledOpcode];
|
||||
} else {
|
||||
if (breakpoint->m_opcodeInAddress != g_opcodeTable.m_addressTable[BreakpointEnabledOpcode]) {
|
||||
return;
|
||||
}
|
||||
breakpoint->m_opcodeInAddress = g_opcodeTable.m_addressTable[BreakpointDisabledOpcode];
|
||||
}
|
||||
#else
|
||||
if (enabled) {
|
||||
if (breakpoint->m_opcode != BreakpointDisabledOpcode) {
|
||||
return;
|
||||
}
|
||||
breakpoint->m_opcode = BreakpointEnabledOpcode;
|
||||
} else {
|
||||
if (breakpoint->m_opcode != BreakpointEnabledOpcode) {
|
||||
return;
|
||||
}
|
||||
breakpoint->m_opcode = BreakpointDisabledOpcode;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
const int jsonReplyStringLength = snprintf(reply, sizeof(reply),
|
||||
R"({"id":%d,"error":{"code":-32601,"message":"'%s' wasn't found"}})",
|
||||
jsonMessage["id"].GetInt(),
|
||||
jsonMessage["method"].GetString());
|
||||
bool DebuggerDevtools::setBreakpointsActive(rapidjson::Document& jsonMessage, ExecutionState* state)
|
||||
{
|
||||
this->m_breakpointsActive = jsonMessage["params"]["active"].GetBool();
|
||||
|
||||
sendMessage(reply, jsonReplyStringLength);
|
||||
for (ByteCode* breakpointBytecode : m_setBreakPoints) {
|
||||
setBreakpointState(breakpointBytecode, m_breakpointsActive);
|
||||
}
|
||||
|
||||
return replyOK(jsonMessage);
|
||||
}
|
||||
|
||||
bool DebuggerDevtools::setBreakpointByUrl(rapidjson::Document& jsonMessage, ExecutionState* state)
|
||||
{
|
||||
const std::string breakpointCondition = jsonMessage["params"]["condition"].GetString();
|
||||
if (!breakpointCondition.empty()) {
|
||||
ESCARGOT_LOG_ERROR("Warning: Breakpoint conditions are not supported!");
|
||||
}
|
||||
|
||||
std::string breakpointFile = jsonMessage["params"]["url"].GetString();
|
||||
if (breakpointFile.find("file://") == 0) {
|
||||
breakpointFile.erase(0, 7);
|
||||
}
|
||||
const uint8_t scriptId = this->m_scriptIdByUrl[breakpointFile];
|
||||
std::string scriptIdString = string_format("%d", scriptId);
|
||||
|
||||
const uint32_t lineNumber = jsonMessage["params"]["lineNumber"].GetUint() + 1; // chrome starts line indexes at 0
|
||||
const uint32_t columnNumber = jsonMessage["params"]["columnNumber"].GetUint() + 1; // chrome starts column indexes at 0
|
||||
|
||||
rapidjson::Document reply;
|
||||
reply.SetObject();
|
||||
|
||||
rapidjson::Value resultObject(rapidjson::kObjectType);
|
||||
rapidjson::Value resultArray(rapidjson::kArrayType);
|
||||
rapidjson::Value breakpointID(rapidjson::kStringType);
|
||||
|
||||
reply.AddMember("id", jsonMessage["id"].GetInt(), reply.GetAllocator());
|
||||
reply.AddMember("result", resultObject, reply.GetAllocator());
|
||||
reply["result"].AddMember("locations", resultArray, reply.GetAllocator());
|
||||
|
||||
for (BreakpointByteCodeLocation breakpointInfo : m_breakpointInfo[scriptId]) {
|
||||
if ((lineNumber == breakpointInfo.line && (columnNumber == breakpointInfo.byteCode->m_loc.column || columnNumber == 1))
|
||||
|| (reply["result"]["locations"].Empty() && breakpointInfo.line > lineNumber)) {
|
||||
rapidjson::Value locationObject(rapidjson::kObjectType);
|
||||
rapidjson::Value scriptIdValue;
|
||||
|
||||
scriptIdValue.SetString(scriptIdString.c_str(), scriptIdString.length(), reply.GetAllocator());
|
||||
|
||||
locationObject.AddMember("scriptId", scriptIdValue, reply.GetAllocator());
|
||||
locationObject.AddMember("lineNumber", breakpointInfo.line - 1, reply.GetAllocator());
|
||||
locationObject.AddMember("columnNumber", breakpointInfo.byteCode->m_loc.column - 1, reply.GetAllocator());
|
||||
|
||||
reply["result"]["locations"].PushBack(locationObject, reply.GetAllocator());
|
||||
|
||||
std::string breakpointIdString = string_format("%s:%d:%lu", breakpointFile.c_str(), breakpointInfo.line, breakpointInfo.byteCode->m_loc.column);
|
||||
breakpointID.SetString(breakpointIdString.c_str(), breakpointIdString.length(), reply.GetAllocator());
|
||||
reply["result"].AddMember("breakpointId", breakpointID, reply.GetAllocator());
|
||||
|
||||
/* Enable breakpoint */
|
||||
setBreakpointState(breakpointInfo.byteCode, true);
|
||||
m_setBreakPoints.emplace(breakpointInfo.byteCode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const auto ret = sendJSONDocument(reply);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool DebuggerDevtools::removeBreakpoint(rapidjson::Document& jsonMessage, ExecutionState* state)
|
||||
{
|
||||
const std::string breakpointId = jsonMessage["params"]["breakpointId"].GetString();
|
||||
|
||||
std::stringstream ss(breakpointId);
|
||||
std::string breakpointIdInfo[3];
|
||||
constexpr char delim = ':';
|
||||
int i = 0;
|
||||
|
||||
while (std::getline(ss, breakpointIdInfo[i], delim)) {
|
||||
++i;
|
||||
}
|
||||
ASSERT(i == 3);
|
||||
|
||||
const std::string breakpointFile = breakpointIdInfo[0];
|
||||
const uint32_t lineNumber = stoi(breakpointIdInfo[1]);
|
||||
const uint32_t columnNumber = stoi(breakpointIdInfo[2]);
|
||||
const uint8_t scriptId = this->m_scriptIdByUrl[breakpointFile];
|
||||
|
||||
for (BreakpointByteCodeLocation breakpointInfo : m_breakpointInfo[scriptId]) {
|
||||
if (lineNumber == breakpointInfo.line && columnNumber == breakpointInfo.byteCode->m_loc.column) {
|
||||
/* Disable breakpoint */
|
||||
setBreakpointState(breakpointInfo.byteCode, false);
|
||||
m_setBreakPoints.erase(breakpointInfo.byteCode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return replyOK(jsonMessage);
|
||||
}
|
||||
|
||||
bool DebuggerDevtools::sendPossibleBreakpoints(rapidjson::Document& jsonMessage, ExecutionState* state)
|
||||
{
|
||||
if (jsonMessage["params"]["restrictToFunction"].GetBool()) {
|
||||
ESCARGOT_LOG_ERROR("Warning: restrictToFunction is not supported\n");
|
||||
}
|
||||
|
||||
std::string scriptIdString = jsonMessage["params"]["start"]["scriptId"].GetString();
|
||||
const uint8_t scriptId = std::stoi(scriptIdString);
|
||||
|
||||
if (m_breakpointInfo.find(scriptId) == m_breakpointInfo.end()) {
|
||||
ESCARGOT_LOG_ERROR("Script Id not found: %d", scriptId);
|
||||
return replyOK(jsonMessage);
|
||||
}
|
||||
|
||||
if (scriptId != std::stoi(jsonMessage["params"]["end"]["scriptId"].GetString())) {
|
||||
ESCARGOT_LOG_ERROR("Error: Script ranges across multiple scripts not supported!\n");
|
||||
return replyMethodNotFound(jsonMessage);
|
||||
}
|
||||
|
||||
const uint32_t startLine = jsonMessage["params"]["start"]["lineNumber"].GetUint() + 1; // chrome starts line indexes at 0
|
||||
const uint32_t startColumn = jsonMessage["params"]["start"]["columnNumber"].GetUint() + 1; // chrome starts column indexes at 0
|
||||
const uint32_t endLine = jsonMessage["params"]["end"]["lineNumber"].GetUint() + 1; // chrome starts line indexes at 0
|
||||
const uint32_t endColumn = jsonMessage["params"]["end"]["columnNumber"].GetUint() + 1; // chrome starts column indexes at 0
|
||||
|
||||
rapidjson::Document reply;
|
||||
reply.SetObject();
|
||||
|
||||
rapidjson::Value resultObject(rapidjson::kObjectType);
|
||||
rapidjson::Value resultArray(rapidjson::kArrayType);
|
||||
|
||||
reply.AddMember("id", jsonMessage["id"].GetInt(), reply.GetAllocator());
|
||||
reply.AddMember("result", resultObject, reply.GetAllocator());
|
||||
reply["result"].AddMember("locations", resultArray, reply.GetAllocator());
|
||||
|
||||
for (BreakpointByteCodeLocation breakpointInfo : m_breakpointInfo[scriptId]) {
|
||||
if (((startLine <= breakpointInfo.line && breakpointInfo.line <= endLine)
|
||||
&& startColumn <= breakpointInfo.byteCode->m_loc.column && breakpointInfo.byteCode->m_loc.column <= endColumn)
|
||||
|| (reply["result"]["locations"].Empty() && breakpointInfo.line > startLine && breakpointInfo.line > endLine)) {
|
||||
rapidjson::Value locationObject(rapidjson::kObjectType);
|
||||
rapidjson::Value scriptIdValue;
|
||||
|
||||
scriptIdValue.SetString(scriptIdString.c_str(), scriptIdString.length(), reply.GetAllocator());
|
||||
|
||||
locationObject.AddMember("scriptId", scriptIdValue, reply.GetAllocator());
|
||||
locationObject.AddMember("lineNumber", breakpointInfo.line - 1, reply.GetAllocator());
|
||||
locationObject.AddMember("columnNumber", breakpointInfo.byteCode->m_loc.column - 1, reply.GetAllocator());
|
||||
|
||||
reply["result"]["locations"].PushBack(locationObject, reply.GetAllocator());
|
||||
}
|
||||
}
|
||||
|
||||
return sendJSONDocument(reply);
|
||||
}
|
||||
|
||||
bool DebuggerDevtools::replyMethodNotFound(rapidjson::Document& jsonMessage, ExecutionState* state)
|
||||
{
|
||||
const std::string jsonReplyString = string_format(R"({"id":%d,"error":{"code":-32601,"message":"'%s' wasn't found"}})",
|
||||
jsonMessage["id"].GetInt(), jsonMessage["method"].GetString());
|
||||
|
||||
sendMessage(jsonReplyString, jsonReplyString.length());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -334,11 +693,18 @@ bool DebuggerDevtools::processEvents(ExecutionState* state, Optional<ByteCodeBlo
|
|||
// NOTE: keep sorted
|
||||
static constexpr MessageType messageTypes[] = {
|
||||
messageType("Debugger.enable", &DebuggerDevtools::enableDebugger),
|
||||
messageType("Debugger.getPossibleBreakpoints", &DebuggerDevtools::sendPossibleBreakpoints),
|
||||
messageType("Debugger.getScriptSource", &DebuggerDevtools::sendSourceCode),
|
||||
messageType("Debugger.removeBreakpoint", &DebuggerDevtools::removeBreakpoint),
|
||||
messageType("Debugger.resume", &DebuggerDevtools::resume),
|
||||
messageType("Debugger.setAsyncCallStackDepth", &DebuggerDevtools::replyOK), // we may be able to set something for this one
|
||||
messageType("Debugger.setBlackboxPatterns", &DebuggerDevtools::replyOK), // we ignore this for now, but if needed set skipSourceName in DebuggerTcp
|
||||
messageType("Debugger.setBreakpointByUrl", &DebuggerDevtools::setBreakpointByUrl),
|
||||
messageType("Debugger.setBreakpointsActive", &DebuggerDevtools::setBreakpointsActive),
|
||||
messageType("Debugger.setPauseOnExceptions", &DebuggerDevtools::setPauseOnExceptions),
|
||||
messageType("Debugger.stepInto", &DebuggerDevtools::stepInto),
|
||||
messageType("Debugger.stepOut", &DebuggerDevtools::stepOut),
|
||||
messageType("Debugger.stepOver", &DebuggerDevtools::stepOver),
|
||||
messageType("Network.clearAcceptedEncodingsOverride", &DebuggerDevtools::replyMethodNotFound),
|
||||
messageType("Network.emulateNetworkConditionsByRule", &DebuggerDevtools::replyMethodNotFound),
|
||||
messageType("Network.enable", &DebuggerDevtools::enableNetwork),
|
||||
|
|
@ -348,7 +714,7 @@ bool DebuggerDevtools::processEvents(ExecutionState* state, Optional<ByteCodeBlo
|
|||
messageType("Profiler.enable", &DebuggerDevtools::enableProfiler),
|
||||
messageType("Runtime.enable", &DebuggerDevtools::enableRuntime),
|
||||
messageType("Runtime.getProperties", &DebuggerDevtools::sendProperties),
|
||||
messageType("Runtime.runIfWaitingForDebugger", &DebuggerDevtools::resume),
|
||||
messageType("Runtime.runIfWaitingForDebugger", &DebuggerDevtools::replyOK),
|
||||
};
|
||||
|
||||
while (true) {
|
||||
|
|
@ -389,7 +755,7 @@ bool DebuggerDevtools::processEvents(ExecutionState* state, Optional<ByteCodeBlo
|
|||
continue;
|
||||
}
|
||||
|
||||
return (this->*message.handler)(jsonMessage);
|
||||
return (this->*message.handler)(jsonMessage, state);
|
||||
}
|
||||
ESCARGOT_LOG_ERROR("Debugger function not supported: %s\n", reinterpret_cast<const char*>(buffer));
|
||||
this->replyMethodNotFound(jsonMessage); // maybe reply with not supported instead? if there is such a reply in the spec
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "Debugger.h"
|
||||
#include "DebuggerTcp.h"
|
||||
#include "interpreter/ByteCode.h"
|
||||
#include "rapidjson/document.h"
|
||||
#include "rapidjson/rapidjson.h"
|
||||
|
||||
|
|
@ -48,7 +49,8 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
bool sendMessage(const std::string& msg, const int length = -1);
|
||||
bool sendMessage(const std::string& msg, size_t length = -1);
|
||||
bool sendJSONDocument(const rapidjson::Document& document);
|
||||
void init(const char* options, Context* context) override;
|
||||
bool skipSourceCode(String* srcName) const override;
|
||||
|
||||
|
|
@ -61,22 +63,37 @@ public:
|
|||
bool getWaitBeforeExitClient() override;
|
||||
|
||||
|
||||
void sendPausedEvent(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state);
|
||||
void sendPausedEvent(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state, bool breakpoint = false);
|
||||
|
||||
protected:
|
||||
bool processEvents(ExecutionState* state, Optional<ByteCodeBlock*> byteCodeBlock, bool isBlockingRequest = true) override;
|
||||
|
||||
private:
|
||||
bool sendProperties(rapidjson::Document& jsonMessage);
|
||||
bool resume(rapidjson::Document& jsonMessage);
|
||||
bool sendSourceCode(rapidjson::Document& jsonMessage);
|
||||
bool replyOK(rapidjson::Document& jsonMessage);
|
||||
bool enableNetwork(rapidjson::Document& jsonMessage);
|
||||
bool enableDebugger(rapidjson::Document& jsonMessage);
|
||||
bool enableRuntime(rapidjson::Document& jsonMessage);
|
||||
bool enableProfiler(rapidjson::Document& jsonMessage);
|
||||
bool setPauseOnExceptions(rapidjson::Document& jsonMessage);
|
||||
bool replyMethodNotFound(rapidjson::Document& jsonMessage);
|
||||
bool sendProperties(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool resume(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool stepOver(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool stepOut(rapidjson::Document& jsonMessage, ExecutionState* state);
|
||||
bool stepInto(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool sendSourceCode(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool replyOK(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool enableNetwork(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool enableDebugger(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool enableRuntime(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool enableProfiler(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool setPauseOnExceptions(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool setBreakpointsActive(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool setBreakpointByUrl(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool removeBreakpoint(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool sendPossibleBreakpoints(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool replyMethodNotFound(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
|
||||
static bool compareBreakpointLocations(const BreakpointByteCodeLocation& a, const BreakpointByteCodeLocation& b)
|
||||
{
|
||||
if (LIKELY(a.line != b.line)) {
|
||||
return a.line < b.line;
|
||||
}
|
||||
return a.byteCode->m_loc.column <= b.byteCode->m_loc.column;
|
||||
}
|
||||
|
||||
uint8_t registerScript(String* url, String* source);
|
||||
|
||||
|
|
@ -85,15 +102,19 @@ private:
|
|||
bool m_runtimeEnabled = false;
|
||||
bool m_profilerEnabled = false;
|
||||
bool m_pauseOnExceptions = false;
|
||||
bool m_breakpointsActive = false;
|
||||
bool m_startBreakpoint = true;
|
||||
|
||||
std::unordered_map<uint8_t, ScriptInfo> m_scriptsById;
|
||||
std::unordered_map<std::string, uint8_t> m_scriptIdByUrl;
|
||||
uint8_t m_nextScriptId = 1;
|
||||
std::unordered_map<uint8_t, std::set<BreakpointByteCodeLocation, decltype(compareBreakpointLocations)*>> m_breakpointInfo;
|
||||
|
||||
std::vector<std::string> m_pendingMessages;
|
||||
std::set<ByteCode*> m_setBreakPoints; // stores set breakpoints for enabling/disabling in bulk with the `Deactivate Breakpoints` button
|
||||
};
|
||||
|
||||
using MessageHandler = bool (DebuggerDevtools::*)(rapidjson::Document&);
|
||||
using MessageHandler = bool (DebuggerDevtools::*)(rapidjson::Document&, ExecutionState* state);
|
||||
|
||||
struct MessageType {
|
||||
const char* methodName;
|
||||
|
|
|
|||
|
|
@ -24,57 +24,12 @@
|
|||
#include "Escargot.h"
|
||||
|
||||
#include "runtime/String.h" // for split function
|
||||
#include "interpreter/ByteCode.h"
|
||||
#include "rapidjson/document.h"
|
||||
#include "rapidjson/prettywriter.h"
|
||||
#include "rapidjson/stringbuffer.h"
|
||||
|
||||
namespace Escargot {
|
||||
|
||||
static std::string jsonEscape(const LChar* src, size_t len)
|
||||
{
|
||||
std::string out;
|
||||
out.reserve(len + 16);
|
||||
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
LChar c = src[i];
|
||||
switch (c) {
|
||||
case '\"':
|
||||
out += "\\\"";
|
||||
break;
|
||||
case '\\':
|
||||
out += "\\\\";
|
||||
break;
|
||||
case '\n':
|
||||
out += "\\n";
|
||||
break;
|
||||
case '\r':
|
||||
out += "\\r";
|
||||
break;
|
||||
case '\t':
|
||||
out += "\\t";
|
||||
break;
|
||||
default:
|
||||
out += c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static void computeEndLocation(const LChar* src, size_t length, uint32_t& endLine, uint32_t& endColumn)
|
||||
{
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
if (src[i] == '\n') {
|
||||
endLine++;
|
||||
endColumn = 0;
|
||||
} else {
|
||||
endColumn++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string DebuggerDevtoolsMessageBuilder::buildEmptyMessage(const uint32_t id)
|
||||
{
|
||||
char buffer[64];
|
||||
|
|
@ -89,47 +44,6 @@ std::string DebuggerDevtoolsMessageBuilder::buildEmptyMessage(const uint32_t id)
|
|||
return { buffer, static_cast<size_t>(written) };
|
||||
}
|
||||
|
||||
std::string DebuggerDevtoolsMessageBuilder::buildScriptParsedMessage(const uint8_t scriptId, const String* source, const String* srcName)
|
||||
{
|
||||
if (!source || !srcName || !source->is8Bit() || !srcName->is8Bit()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const LChar* sourceName = srcName->characters8();
|
||||
const LChar* sourceCode = source->characters8();
|
||||
size_t sourceNameLength = srcName->length();
|
||||
size_t sourceCodeLength = source->length();
|
||||
|
||||
uint32_t endLine = 0;
|
||||
uint32_t endColumn = 0;
|
||||
computeEndLocation(sourceCode, sourceCodeLength, endLine, endColumn);
|
||||
|
||||
char buffer[512];
|
||||
int written = snprintf(buffer, sizeof(buffer),
|
||||
"{\"method\":\"Debugger.scriptParsed\","
|
||||
"\"params\":{"
|
||||
"\"scriptId\":\"%u\","
|
||||
"\"url\":\"%.*s\","
|
||||
"\"startLine\":0,"
|
||||
"\"startColumn\":0,"
|
||||
"\"endLine\":%u,"
|
||||
"\"endColumn\":%u,"
|
||||
"\"executionContextId\":1"
|
||||
"}"
|
||||
"}",
|
||||
scriptId,
|
||||
static_cast<int>(sourceNameLength),
|
||||
reinterpret_cast<const char*>(sourceName),
|
||||
endLine,
|
||||
endColumn);
|
||||
|
||||
if (written < 0 || static_cast<size_t>(written) >= sizeof(buffer)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return { buffer, static_cast<size_t>(written) };
|
||||
}
|
||||
|
||||
std::string DebuggerDevtoolsMessageBuilder::buildSourceCodeMessage(const uint8_t requestId, const String* source)
|
||||
{
|
||||
if (!source || !source->is8Bit()) {
|
||||
|
|
@ -137,20 +51,23 @@ std::string DebuggerDevtoolsMessageBuilder::buildSourceCodeMessage(const uint8_t
|
|||
}
|
||||
|
||||
const LChar* sourceCode = source->characters8();
|
||||
size_t sourceCodeLength = source->length();
|
||||
// Special characters in source code must be escaped.
|
||||
std::string escapedSource = jsonEscape(sourceCode, sourceCodeLength);
|
||||
const size_t sourceCodeLength = source->length();
|
||||
|
||||
rapidjson::StringBuffer sb;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
|
||||
writer.String(reinterpret_cast<const rapidjson::Writer<rapidjson::GenericStringBuffer<rapidjson::UTF8<>>>::Ch*>(sourceCode), sourceCodeLength);
|
||||
const std::string escaped = sb.GetString();
|
||||
|
||||
// FIXME: buffer size depends on length of source.
|
||||
char buffer[4096];
|
||||
int written = snprintf(buffer, sizeof(buffer),
|
||||
"{\"id\":%u,"
|
||||
"\"result\":{"
|
||||
"\"scriptSource\":\"%s\""
|
||||
"}"
|
||||
"}",
|
||||
requestId,
|
||||
escapedSource.c_str());
|
||||
const int written = snprintf(buffer, sizeof(buffer),
|
||||
"{\"id\":%u,"
|
||||
"\"result\":{"
|
||||
"\"scriptSource\":%s"
|
||||
"}"
|
||||
"}",
|
||||
requestId,
|
||||
escaped.c_str());
|
||||
|
||||
if (written < 0 || static_cast<size_t>(written) >= sizeof(buffer)) {
|
||||
return {};
|
||||
|
|
|
|||
|
|
@ -755,23 +755,6 @@ void DebuggerEscargot::getScopeVariables(ExecutionState* state, uint32_t stateIn
|
|||
sendSubtype(ESCARGOT_MESSAGE_VARIABLE, ESCARGOT_VARIABLE_END);
|
||||
}
|
||||
|
||||
static LexicalEnvironment* getFunctionLexEnv(ExecutionState* state)
|
||||
{
|
||||
LexicalEnvironment* lexEnv = state->lexicalEnvironment();
|
||||
|
||||
while (lexEnv) {
|
||||
EnvironmentRecord* record = lexEnv->record();
|
||||
|
||||
if (record->isDeclarativeEnvironmentRecord()
|
||||
&& record->asDeclarativeEnvironmentRecord()->isFunctionEnvironmentRecord()) {
|
||||
return lexEnv;
|
||||
}
|
||||
|
||||
lexEnv = lexEnv->outerEnvironment();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool DebuggerEscargot::processEvents(ExecutionState* state, Optional<ByteCodeBlock*> byteCodeBlock, bool isBlockingRequest)
|
||||
{
|
||||
uint8_t buffer[ESCARGOT_DEBUGGER_MAX_MESSAGE_LENGTH];
|
||||
|
|
|
|||
|
|
@ -207,7 +207,6 @@ bool DebuggerTcp::send(const uint8_t type, const void* buffer, const size_t leng
|
|||
return false;
|
||||
}
|
||||
|
||||
// ESCARGOT_LOG_INFO("Sent message: %s\n", static_cast<const char*>(buffer));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3308,12 +3308,19 @@ public:
|
|||
context->m_locData->push_back(std::make_pair(start, idx));
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
const auto loc = computeNodeLOC(m_codeBlock->src(), m_codeBlock->functionStart(), idx);
|
||||
ByteCodeLOC* bytecodeLoc = &reinterpret_cast<ByteCode*>(first)->m_loc;
|
||||
bytecodeLoc->index = loc.index;
|
||||
bytecodeLoc->line = loc.line;
|
||||
bytecodeLoc->column = loc.column;
|
||||
#endif
|
||||
|
||||
m_code.resizeWithUninitializedValues(m_code.size() + sizeof(CodeType));
|
||||
for (size_t i = 0; i < sizeof(CodeType); i++) {
|
||||
m_code[start++] = *first;
|
||||
first++;
|
||||
}
|
||||
|
||||
m_requiredOperandRegisterNumber = std::max(m_requiredOperandRegisterNumber, (ByteCodeRegisterIndex)context->m_baseRegisterCount);
|
||||
|
||||
// TODO throw exception
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue