3abc997a4e
The specification string following a break command was parsed by means of ad-hoc string splitting and sub string finding. This scheme cannot deal with namespaces and class names that contain : (such as xhp classes). There is now a proper parser for break point specifications. Also, xhp class names are mangled before putting them in the BreakPointInfo structure, otherwise breakpoints qualified with xhp classes will not be hit. The syntax for namespaces has been changed to fit the language and if present namespaces are used to mangle function and class names, so that they match the names produced by the regular parser.
580 linhas
17 KiB
C++
580 linhas
17 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#include <runtime/eval/debugger/debugger_base.h>
|
|
#include <runtime/eval/debugger/debugger_client.h>
|
|
#include <runtime/eval/debugger/break_point.h>
|
|
#include <util/parser/scanner.h>
|
|
#include <util/util.h>
|
|
|
|
namespace HPHP { namespace Eval {
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
static const Trace::Module TRACEMOD = Trace::debugger;
|
|
|
|
const std::string &DSandboxInfo::id() const {
|
|
TRACE(2, "DSandboxInfo::id\n");
|
|
if (m_cached_id.empty() && !m_user.empty()) {
|
|
m_cached_id = m_user + "\t" + m_name;
|
|
}
|
|
return m_cached_id;
|
|
}
|
|
|
|
const std::string DSandboxInfo::desc() const {
|
|
TRACE(2, "DSandboxInfo::desc\n");
|
|
string ret = m_user + "'s " + m_name + " sandbox";
|
|
if (!m_path.empty()) {
|
|
ret += " at " + m_path;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
DSandboxInfo DSandboxInfo::CreateDummyInfo(uint64_t unique) {
|
|
TRACE(2, "DSandboxInfo::CreateDummyInfo\n");
|
|
char buf[64];
|
|
snprintf(buf, 64, "dummy\t0x%" PRIu64, unique);
|
|
return DSandboxInfo(std::string(buf));
|
|
}
|
|
|
|
void DSandboxInfo::set(const std::string &id) {
|
|
TRACE(2, "DSandboxInfo::set\n");
|
|
m_cached_id.clear();
|
|
m_user.clear();
|
|
m_name.clear();
|
|
m_path.clear();
|
|
if (!id.empty()) {
|
|
vector<string> tokens;
|
|
Util::split('\t', id.c_str(), tokens);
|
|
if (tokens.size() == 2) {
|
|
m_user = tokens[0];
|
|
m_name = tokens[1];
|
|
}
|
|
}
|
|
}
|
|
|
|
void DSandboxInfo::update(const DSandboxInfo &src) {
|
|
TRACE(2, "DSandboxInfo::update\n");
|
|
if (!src.m_path.empty() && m_path.empty()) {
|
|
m_path = src.m_path;
|
|
}
|
|
}
|
|
|
|
void DSandboxInfo::sendImpl(ThriftBuffer &thrift) {
|
|
TRACE(2, "DSandboxInfo::sendImpl\n");
|
|
thrift.write(m_user);
|
|
thrift.write(m_name);
|
|
thrift.write(m_path);
|
|
}
|
|
|
|
void DSandboxInfo::recvImpl(ThriftBuffer &thrift) {
|
|
TRACE(2, "DSandboxInfo::recvImpl\n");
|
|
thrift.read(m_user);
|
|
thrift.read(m_name);
|
|
thrift.read(m_path);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DThreadInfo::sendImpl(ThriftBuffer &thrift) {
|
|
TRACE(2, "DThreadInfo::sendImpl\n");
|
|
thrift.write(m_id);
|
|
thrift.write(m_desc);
|
|
thrift.write(m_type);
|
|
thrift.write(m_url);
|
|
}
|
|
|
|
void DThreadInfo::recvImpl(ThriftBuffer &thrift) {
|
|
TRACE(2, "DThreadInfo::recvImpl\n");
|
|
thrift.read(m_id);
|
|
thrift.read(m_desc);
|
|
thrift.read(m_type);
|
|
thrift.read(m_url);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DFunctionInfo::sendImpl(ThriftBuffer &thrift) {
|
|
TRACE(2, "DFunctionInfo::sendImpl\n");
|
|
thrift.write(m_namespace);
|
|
thrift.write(m_class);
|
|
thrift.write(m_function);
|
|
}
|
|
|
|
void DFunctionInfo::recvImpl(ThriftBuffer &thrift) {
|
|
TRACE(2, "DFunctionInfo::recvImpl\n");
|
|
thrift.read(m_namespace);
|
|
thrift.read(m_class);
|
|
thrift.read(m_function);
|
|
}
|
|
|
|
std::string DFunctionInfo::getName() const {
|
|
TRACE(2, "DFunctionInfo::getName\n");
|
|
if (m_function.empty() || m_class.empty()) {
|
|
return m_function;
|
|
} else {
|
|
return m_class + "::" + m_function;
|
|
}
|
|
}
|
|
|
|
std::string DFunctionInfo::site(std::string &preposition) const {
|
|
TRACE(2, "DFunctionInfo::site\n");
|
|
string ret;
|
|
preposition = "at ";
|
|
if (!m_class.empty()) {
|
|
if (!m_namespace.empty()) {
|
|
ret = m_namespace + "\\";
|
|
}
|
|
ret += m_class;
|
|
if (!m_function.empty()) {
|
|
ret += "::" + m_function + "()";
|
|
} else {
|
|
ret = "class " + ret;
|
|
preposition = "in ";
|
|
}
|
|
} else {
|
|
if (!m_function.empty()) {
|
|
ret = m_function + "()";
|
|
if (!m_namespace.empty()) {
|
|
ret += " in namespace " + m_namespace;
|
|
}
|
|
} else if (!m_namespace.empty()) {
|
|
ret = "namespace " + m_namespace;
|
|
preposition = "in ";
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
std::string DFunctionInfo::desc(const BreakPointInfo *bpi) const {
|
|
TRACE(2, "DFunctionInfo::desc\n");
|
|
string ret;
|
|
if (!m_class.empty()) {
|
|
string cls;
|
|
if (!m_namespace.empty()) {
|
|
cls = bpi->regex(m_namespace) + "\\";
|
|
}
|
|
cls += bpi->regex(m_class);
|
|
if (!m_function.empty()) {
|
|
ret += cls + "::" + bpi->regex(m_function) + "()";
|
|
} else {
|
|
ret += "any functions in class " + cls;
|
|
}
|
|
} else {
|
|
if (!m_function.empty()) {
|
|
ret += bpi->regex(m_function) + "()";
|
|
if (!m_namespace.empty()) {
|
|
ret += " in namespace " + bpi->regex(m_namespace);
|
|
}
|
|
} else {
|
|
ret += "any functions in namespace " + bpi->regex(m_namespace);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
std::string Macro::desc(const char *indent) {
|
|
TRACE(2, "Macro::desc\n");
|
|
string ret;
|
|
for (unsigned int i = 0; i < m_cmds.size(); i++) {
|
|
if (indent) ret += indent;
|
|
ret += m_cmds[i];
|
|
ret += "\n";
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void Macro::load(Hdf node) {
|
|
TRACE(2, "Macro::load\n");
|
|
m_name = node["name"].getString();
|
|
node["cmds"].get(m_cmds);
|
|
}
|
|
|
|
void Macro::save(Hdf node) {
|
|
TRACE(2, "Macro::save\n");
|
|
node["name"] = m_name;
|
|
for (unsigned int i = 0; i < m_cmds.size(); i++) {
|
|
node["cmds"][i] = m_cmds[i];
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
const char *PHP_KEYWORDS[] = {
|
|
"require_once",
|
|
"require",
|
|
"eval",
|
|
"include_once",
|
|
"include",
|
|
"print",
|
|
"instanceof",
|
|
"bool",
|
|
"object",
|
|
"string",
|
|
"double",
|
|
"int",
|
|
"clone",
|
|
"new",
|
|
"exit",
|
|
"if",
|
|
"elseif",
|
|
"else",
|
|
"endif",
|
|
"echo",
|
|
"do",
|
|
"while",
|
|
"endwhile",
|
|
"for",
|
|
"endfor",
|
|
"foreach",
|
|
"endforeach",
|
|
"declare",
|
|
"enddeclare",
|
|
"as",
|
|
"switch",
|
|
"endswitch",
|
|
"case",
|
|
"default",
|
|
"break",
|
|
"continue",
|
|
"function",
|
|
"const",
|
|
"return",
|
|
"try",
|
|
"catch",
|
|
"throw",
|
|
"use",
|
|
"global",
|
|
"public",
|
|
"protected",
|
|
"private",
|
|
"final",
|
|
"abstract",
|
|
"static",
|
|
"var",
|
|
"unset",
|
|
"isset",
|
|
"empty",
|
|
"halt_compiler",
|
|
"class",
|
|
"interface",
|
|
"extends",
|
|
"implements",
|
|
"list",
|
|
"array",
|
|
"__CLASS__",
|
|
"__METHOD__",
|
|
"__FUNCTION__",
|
|
"__LINE__",
|
|
"__FILE__",
|
|
"parent",
|
|
"self",
|
|
nullptr
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void get_color(int tokid, int prev, int next,
|
|
const char *&color, const char *&end,
|
|
const char **palette =
|
|
DebuggerClient::DefaultCodeColors) {
|
|
|
|
TRACE(7, "debugger_base:get_color\n");
|
|
#undef YYTOKENTYPE
|
|
#ifdef YYTOKEN_MAP
|
|
#undef YYTOKEN_MAP
|
|
#undef YYTOKEN
|
|
#endif
|
|
#define YYTOKEN(num, name) (char)(CodeColorKeyword * 2)
|
|
#define YYTOKEN_MAP static char code[] =
|
|
#include "hphp/util/parser/hphp.tab.hpp"
|
|
#undef YYTOKEN_MAP
|
|
#undef YYTOKEN
|
|
|
|
#define COLOR_ENTRY(name, type) \
|
|
code[name - YYTOKEN_MIN] = (char)(CodeColor ## type * 2)
|
|
|
|
static bool code_inited = false;
|
|
if (!code_inited) {
|
|
code_inited = true;
|
|
|
|
COLOR_ENTRY(T_SR_EQUAL, None );
|
|
COLOR_ENTRY(T_SL_EQUAL, None );
|
|
COLOR_ENTRY(T_XOR_EQUAL, None );
|
|
COLOR_ENTRY(T_OR_EQUAL, None );
|
|
COLOR_ENTRY(T_AND_EQUAL, None );
|
|
COLOR_ENTRY(T_MOD_EQUAL, None );
|
|
COLOR_ENTRY(T_CONCAT_EQUAL, None );
|
|
COLOR_ENTRY(T_DIV_EQUAL, None );
|
|
COLOR_ENTRY(T_MUL_EQUAL, None );
|
|
COLOR_ENTRY(T_MINUS_EQUAL, None );
|
|
COLOR_ENTRY(T_PLUS_EQUAL, None );
|
|
COLOR_ENTRY(T_BOOLEAN_OR, None );
|
|
COLOR_ENTRY(T_BOOLEAN_AND, None );
|
|
COLOR_ENTRY(T_IS_NOT_IDENTICAL, None );
|
|
COLOR_ENTRY(T_IS_IDENTICAL, None );
|
|
COLOR_ENTRY(T_IS_NOT_EQUAL, None );
|
|
COLOR_ENTRY(T_IS_EQUAL, None );
|
|
COLOR_ENTRY(T_IS_GREATER_OR_EQUAL, None );
|
|
COLOR_ENTRY(T_IS_SMALLER_OR_EQUAL, None );
|
|
COLOR_ENTRY(T_SR, None );
|
|
COLOR_ENTRY(T_SL, None );
|
|
COLOR_ENTRY(T_DEC, None );
|
|
COLOR_ENTRY(T_INC, None );
|
|
COLOR_ENTRY(T_LNUMBER, None );
|
|
COLOR_ENTRY(T_DNUMBER, None );
|
|
COLOR_ENTRY(T_STRING, None );
|
|
COLOR_ENTRY(T_STRING_VARNAME, Variable );
|
|
COLOR_ENTRY(T_VARIABLE, Variable );
|
|
COLOR_ENTRY(T_NUM_STRING, None );
|
|
COLOR_ENTRY(T_INLINE_HTML, Html );
|
|
COLOR_ENTRY(T_ENCAPSED_AND_WHITESPACE, String );
|
|
COLOR_ENTRY(T_CONSTANT_ENCAPSED_STRING, String );
|
|
COLOR_ENTRY(T_OBJECT_OPERATOR, None );
|
|
COLOR_ENTRY(T_DOUBLE_ARROW, None );
|
|
COLOR_ENTRY(T_CLASS_C, Constant );
|
|
COLOR_ENTRY(T_METHOD_C, Constant );
|
|
COLOR_ENTRY(T_FUNC_C, Constant );
|
|
COLOR_ENTRY(T_LINE, Constant );
|
|
COLOR_ENTRY(T_FILE, Constant );
|
|
COLOR_ENTRY(T_DIR, Constant );
|
|
COLOR_ENTRY(T_COMMENT, Comment );
|
|
COLOR_ENTRY(T_DOC_COMMENT, Comment );
|
|
COLOR_ENTRY(T_OPEN_TAG, Tag );
|
|
COLOR_ENTRY(T_OPEN_TAG_WITH_ECHO, Tag );
|
|
COLOR_ENTRY(T_CLOSE_TAG, Tag );
|
|
COLOR_ENTRY(T_WHITESPACE, None );
|
|
COLOR_ENTRY(T_DOLLAR_OPEN_CURLY_BRACES, None );
|
|
COLOR_ENTRY(T_CURLY_OPEN, None );
|
|
COLOR_ENTRY(T_PAAMAYIM_NEKUDOTAYIM, None );
|
|
}
|
|
|
|
if (tokid == T_STRING) {
|
|
int type = CodeColorConstant;
|
|
if (prev == '$') {
|
|
type = CodeColorVariable;
|
|
} else if (prev == T_FUNCTION || prev == T_CLASS || prev == T_INTERFACE) {
|
|
type = CodeColorDeclaration;
|
|
} else if (next == '(') {
|
|
type = CodeColorNone;
|
|
}
|
|
color = palette[type * 2];
|
|
end = palette[type * 2 + 1];
|
|
} else if (tokid >= YYTOKEN_MIN && tokid <= YYTOKEN_MAX) {
|
|
tokid -= YYTOKEN_MIN;
|
|
char c = code[tokid];
|
|
color = palette[(int)c];
|
|
end = palette[(int)(c+1)];
|
|
} else {
|
|
color = end = nullptr;
|
|
}
|
|
}
|
|
|
|
static void color_line_no(StringBuffer &sb, int line, int lineFocus0,
|
|
int lineFocus1, const char *color) {
|
|
TRACE(7, "debugger_base:color_line_no\n");
|
|
if (((line == lineFocus0 && lineFocus1 == 0) ||
|
|
(line >= lineFocus0 && line <= lineFocus1)) &&
|
|
DebuggerClient::HighlightBgColor) {
|
|
sb.append(add_bgcolor(DebuggerClient::HighlightForeColor,
|
|
DebuggerClient::HighlightBgColor));
|
|
} else {
|
|
sb.append(color);
|
|
}
|
|
}
|
|
|
|
static void append_line_no(StringBuffer &sb, const char *text,
|
|
int &line, const char *color, const char *end,
|
|
int lineFocus0, int charFocus0, int lineFocus1,
|
|
int charFocus1, const char **palette =
|
|
DebuggerClient::DefaultCodeColors) {
|
|
TRACE(7, "debugger_base:append_line_no\n");
|
|
const char *colorLineNo = palette[CodeColorLineNo * 2];
|
|
const char *endLineNo = palette[CodeColorLineNo * 2 + 1];
|
|
|
|
// beginning
|
|
if (line && sb.empty()) {
|
|
if (colorLineNo) color_line_no(sb, line, lineFocus0, lineFocus1,
|
|
colorLineNo);
|
|
sb.printf(DebuggerClient::LineNoFormat, line);
|
|
if (endLineNo) sb.append(endLineNo);
|
|
}
|
|
|
|
// ending
|
|
if (text == nullptr) {
|
|
if (line) {
|
|
if (colorLineNo) color_line_no(sb, line, lineFocus0, lineFocus1,
|
|
colorLineNo);
|
|
sb.append("(END)\n");
|
|
if (endLineNo) sb.append(endLineNo);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (color) sb.append(color);
|
|
|
|
if (line == 0) {
|
|
sb.append(text);
|
|
} else {
|
|
const char *begin = text;
|
|
const char *p = begin;
|
|
for (; *p; p++) {
|
|
if (*p == '\n') {
|
|
++line;
|
|
sb.append(begin, p - begin);
|
|
sb.append(ANSI_COLOR_END);
|
|
sb.append('\n');
|
|
if (colorLineNo) color_line_no(sb, line, lineFocus0, lineFocus1,
|
|
colorLineNo);
|
|
sb.printf(DebuggerClient::LineNoFormat, line);
|
|
if (endLineNo) sb.append(endLineNo);
|
|
if (color) sb.append(color);
|
|
begin = p + 1;
|
|
}
|
|
}
|
|
if (p - begin > 0) {
|
|
sb.append(begin, p - begin);
|
|
}
|
|
}
|
|
|
|
if (end) sb.append(end);
|
|
}
|
|
|
|
String highlight_code(CStrRef source, int line /* = 0 */,
|
|
int lineFocus0 /* = 0 */, int charFocus0 /* = 0 */,
|
|
int lineFocus1 /* = 0 */, int charFocus1 /* = 0 */) {
|
|
TRACE(7, "debugger_base:highlight_code\n");
|
|
String prepended = "<?php\n";
|
|
prepended += source;
|
|
String highlighted = highlight_php(prepended, line, lineFocus0, charFocus0,
|
|
lineFocus1, charFocus1);
|
|
int pos = highlighted.find("\n");
|
|
return highlighted.substr(pos + 1);
|
|
}
|
|
|
|
string check_char_highlight(int lineFocus0, int charFocus0,
|
|
int lineFocus1, int charFocus1,
|
|
Location &loc) {
|
|
TRACE(7, "debugger_base:check_char_highlight\n");
|
|
if (DebuggerClient::HighlightBgColor &&
|
|
lineFocus0 && charFocus0 && lineFocus1 && charFocus1 &&
|
|
loc.line0 * 1000 + loc.char0 >= lineFocus0 * 1000 + charFocus0 &&
|
|
loc.line1 * 1000 + loc.char1 <= lineFocus1 * 1000 + charFocus1) {
|
|
return add_bgcolor(DebuggerClient::HighlightForeColor,
|
|
DebuggerClient::HighlightBgColor);
|
|
}
|
|
return "";
|
|
}
|
|
|
|
String highlight_php(CStrRef source, int line /* = 0 */,
|
|
int lineFocus0 /* = 0 */, int charFocus0 /* = 0 */,
|
|
int lineFocus1 /* = 0 */, int charFocus1 /* = 0 */) {
|
|
TRACE(7, "debugger_base:highlight_php\n");
|
|
StringBuffer res;
|
|
Scanner scanner(source.data(), source.size(),
|
|
Scanner::AllowShortTags | Scanner::ReturnAllTokens);
|
|
ScannerToken tok1, tok2;
|
|
std::vector<std::pair<int, string> > ahead_tokens;
|
|
Location loc1, loc2;
|
|
|
|
const char *colorComment = nullptr, *endComment = nullptr;
|
|
get_color(T_COMMENT, 0, 0, colorComment, endComment);
|
|
|
|
int prev = 0;
|
|
int tokid = scanner.getNextToken(tok1, loc1);
|
|
int next = 0;
|
|
while (tokid) {
|
|
// look ahead
|
|
next = scanner.getNextToken(tok2, loc2);
|
|
while (next == T_WHITESPACE ||
|
|
next == T_COMMENT ||
|
|
next == T_DOC_COMMENT) {
|
|
|
|
string text = tok2.text();
|
|
string hcolor = check_char_highlight(lineFocus0, charFocus0,
|
|
lineFocus1, charFocus1, loc2);
|
|
if (!hcolor.empty()) {
|
|
text = hcolor + text + ANSI_COLOR_END;
|
|
}
|
|
|
|
ahead_tokens.push_back(std::pair<int, string>(next, text));
|
|
next = scanner.getNextToken(tok2, loc2);
|
|
}
|
|
|
|
string hcolor = check_char_highlight(lineFocus0, charFocus0,
|
|
lineFocus1, charFocus1, loc1);
|
|
|
|
if (tokid < 256) {
|
|
if (!hcolor.empty()) {
|
|
res.append(hcolor);
|
|
res.append((char)tokid);
|
|
res.append(ANSI_COLOR_END);
|
|
} else {
|
|
res.append((char)tokid);
|
|
}
|
|
} else {
|
|
const char *color = nullptr, *end = nullptr;
|
|
get_color(tokid, prev, next, color, end);
|
|
if (!hcolor.empty()) {
|
|
color = hcolor.c_str();
|
|
end = ANSI_COLOR_END;
|
|
}
|
|
|
|
const std::string &text = tok1.text();
|
|
int offset = 0;
|
|
if (text[0] == '$') {
|
|
if (!hcolor.empty()) {
|
|
res.append(hcolor);
|
|
res.append('$');
|
|
res.append(ANSI_COLOR_END);
|
|
} else {
|
|
res.append('$');
|
|
}
|
|
offset = 1;
|
|
}
|
|
append_line_no(res, text.c_str() + offset, line, color, end,
|
|
lineFocus0, charFocus0, lineFocus1, charFocus1);
|
|
}
|
|
|
|
if (!ahead_tokens.empty()) {
|
|
for (unsigned int i = 0; i < ahead_tokens.size(); i++) {
|
|
bool comment = ahead_tokens[i].first != T_WHITESPACE;
|
|
append_line_no(res, ahead_tokens[i].second.c_str(), line,
|
|
comment ? colorComment : nullptr,
|
|
comment ? endComment : nullptr,
|
|
lineFocus0, charFocus0, lineFocus1, charFocus1);
|
|
}
|
|
ahead_tokens.clear();
|
|
}
|
|
|
|
if (!(tokid == T_WHITESPACE || tokid == T_COMMENT ||
|
|
tokid == T_DOC_COMMENT)) {
|
|
prev = tokid;
|
|
}
|
|
tok1 = tok2;
|
|
loc1 = loc2;
|
|
tokid = next;
|
|
}
|
|
|
|
append_line_no(res, nullptr, line, nullptr, nullptr,
|
|
lineFocus0, charFocus0, lineFocus1, charFocus1);
|
|
return res.detach();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}}
|