Repository navigation
Expand file tree
/
Copy pathcmd_list.cpp
More file actions
372 lines (350 loc) · 13.8 KB
/
Copy pathcmd_list.cpp
File metadata and controls
372 lines (350 loc) · 13.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-2013 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 "hphp/runtime/debugger/cmd/cmd_list.h"
#include "hphp/runtime/debugger/cmd/cmd_info.h"
#include "hphp/runtime/base/file.h"
#include "hphp/runtime/ext/ext_file.h"
namespace HPHP { namespace Eval {
///////////////////////////////////////////////////////////////////////////////
TRACE_SET_MOD(debugger);
// Always called from send and implements specific
// logic for serializing a list command to send via Thrift.
void CmdList::sendImpl(DebuggerThriftBuffer &thrift) {
DebuggerCommand::sendImpl(thrift);
thrift.write(m_file);
thrift.write(m_line1);
thrift.write(m_line2);
thrift.write(m_code);
}
// Always called from recv and implements specific
// logic for deserializing a list command received via Thrift.
void CmdList::recvImpl(DebuggerThriftBuffer &thrift) {
DebuggerCommand::recvImpl(thrift);
thrift.read(m_file);
thrift.read(m_line1);
thrift.read(m_line2);
thrift.read(m_code);
}
// Informs the client of all strings that may follow a list command.
// Used for auto completion. The client uses the prefix of the argument
// following the command to narrow down the list displayed to the user.
void CmdList::list(DebuggerClient &client) {
client.addCompletion(DebuggerClient::AutoCompleteFileNames);
}
// The text to display when the debugger client processes "help list".
void CmdList::help(DebuggerClient &client) {
client.helpTitle("List Command");
client.helpCmds(
"list", "displays current block of source code",
"list {line}", "displays code around specified line",
"list {line1}-{line2}", "displays specified block of source code",
"list {line1}-", "displays code starting with the line",
"list -{line2}", "displays code ending with the line",
"list {file}", "displays beginning lines of the file",
"list {cls}", "displays beginning lines of the class",
"list {function}", "displays beginning lines of the function",
"list {cls::method}", "displays beginning lines of the method",
"list {file}:{line}", "displays code around specified file:line",
"list {file}:{l1}-{l2}", "displays specified block in the file",
"list {file}:{l1}-", "displays specified block in the file",
"list {file}:-{l2}", "displays specified block in the file",
"list {directory}", "sets PHP source root directory",
nullptr
);
client.helpBody(
"Use list command to display PHP source code. In remote debugging, this "
"is displaying source code on server side. When server side cannot find "
"the file, it will fall back to local files.\n"
"\n"
"Hit return to display more lines of code after current display.\n"
"\n"
"When a directory name is specified, this will become a root directory "
"for resolving relative paths of PHP files. Files with absolute paths "
"will not be affected by this setting. This directory will be stored "
"in configuration file for future sessions as well."
);
}
// Retrieves the current source location (file, line).
// The current location is initially determined by the location
// where execution was interrupted to hand control back to
// the debugger client and can thereafter be modified by list
// commands and by switching the the stack frame.
//
// The lineFocus and and charFocus parameters
// are non zero only when the source location comes from a breakpoint.
// They can be used to highlight the location of the current breakpoint
// in the edit window of an attached IDE, for example.
//
// If m_line1 and m_line2 are currently 0 (because they have not been specified
// as parameters to the list command), they are updated to point to a block of
// code one line beyond the current line maintained by the client.
// This has the effect that a succession of list commands that specify no
// parameters will scroll sequentially through the source code in blocks
// of DebuggerClient::CodeBlockSize.
void CmdList::getListLocation(DebuggerClient &client, int &lineFocus0,
int &charFocus0, int &lineFocus1,
int &charFocus1) {
int currentLine = 0;
client.getListLocation(m_file, currentLine, lineFocus0, charFocus0,
lineFocus1, charFocus1);
if (m_line1 == 0 && m_line2 == 0) {
m_line1 = currentLine + 1;
m_line2 = m_line1 + DebuggerClient::CodeBlockSize;
}
}
// If there is no current file, print the desired range of eval code
// or give an error message if the debugger is not currently performing
// an eval command.
void CmdList::listEvalCode(DebuggerClient &client) {
assert(m_file.empty());
string evalCode = client.getCode();
if (evalCode.empty()) {
client.error("There is no current source file.");
} else {
client.print(highlight_php(evalCode));
}
}
// Sends this list command to the server to retrieve the source to be listed
// and then displays the source on the client. The client's current line
// is then updated to point to the last listed line.
// Returns false if the server was unable to return source for this command.
bool CmdList::listFileRange(DebuggerClient &client,
int lineFocus0, int charFocus0,
int lineFocus1, int charFocus1) {
if (m_line1 <= 0) m_line1 = 1;
if (m_line2 <= 0) m_line2 = 1;
if (m_line1 > m_line2) {
int32_t tmp = m_line1;
m_line1 = m_line2;
m_line2 = tmp;
}
CmdListPtr res = client.xend<CmdList>(this);
if (res->m_code.isString()) {
if (!client.code(res->m_code.toString(), m_line1, m_line2,
lineFocus0, charFocus0, lineFocus1, charFocus1)) {
client.info("No more lines in %s to display.", m_file.c_str());
}
client.setListLocation(m_file, m_line2, false);
return true;
}
return false;
}
const StaticString
s_methods("methods"),
s_file("file"),
s_line1("line1"),
s_line2("line2");
// Sends an Info command to the server to retrieve source location
// information for the function or class specified by the command
// argument. Then updates this command with the source information
// and sends it to the server in order to retrieve the source
// text from the server.
// Returns false if the server was unable to return the information
// needed for this command.
bool CmdList::listFunctionOrClass(DebuggerClient &client) {
assert(client.argCount() == 1);
CmdInfoPtr cmdInfo(new CmdInfo());
DebuggerCommandPtr deleter(cmdInfo);
string subsymbol;
cmdInfo->parseOneArg(client, subsymbol);
CmdInfoPtr cmd = client.xend<CmdInfo>(cmdInfo.get());
Array info = cmd->getInfo();
if (info.empty()) return false;
always_assert(info.size() == 1);
ArrayIter iter(info);
Array funcInfo = iter.second().toArray();
if (!subsymbol.empty()) {
String key = CmdInfo::FindSubSymbol(funcInfo[s_methods].toArray(),
subsymbol);
if (key.isNull()) return false;
funcInfo = funcInfo[s_methods][key].toArray();
}
String file = funcInfo[s_file].toString();
int line1 = funcInfo[s_line1].toInt32();
int line2 = funcInfo[s_line2].toInt32();
int line = line1 ? line1 : line2;
if (file.empty() || !line) return false;
client.setListLocation(file.data(), line - 1, false);
line = 0;
int charFocus0 = 0;
int lineFocus1 = 0;
int charFocus1 = 0;
m_file.clear();
m_line1 = m_line2 = 0;
getListLocation(client, line, charFocus0, lineFocus1, charFocus1);
if (m_file.empty()) {
listEvalCode(client);
return true;
}
return listFileRange(client, line, charFocus0, lineFocus1, charFocus1);
}
// Checks the command arguments, report errors and returning as appropriate.
// Then communicates with the server to retrieve source information. Also
// retrieves and updates location information stored in the client.
void CmdList::onClient(DebuggerClient &client) {
if (DebuggerCommand::displayedHelp(client)) return;
if (client.argCount() > 1) {
help(client);
return;
}
int line = 0;
m_line1 = m_line2 = 0;
if (client.argCount() == 1) {
string arg = client.argValue(1);
if (DebuggerClient::IsValidNumber(arg)) {
line = atoi(arg.c_str());
if (line <= 0) {
client.error("A line number has to be a positive integer.");
help(client);
return;
}
m_line1 = line - DebuggerClient::CodeBlockSize/2;
m_line2 = m_line1 + DebuggerClient::CodeBlockSize;
} else if (arg.find("::") != string::npos) {
if (!listFunctionOrClass(client)) {
client.error("Unable to read specified method.");
}
return;
} else {
size_t pos = arg.find(':');
if (pos != string::npos) {
m_file = arg.substr(0, pos);
if (m_file.empty()) {
client.error("File name cannot be empty.");
help(client);
return;
}
arg = arg.substr(pos + 1);
}
pos = arg.find('-');
if (pos != string::npos) {
string line1 = arg.substr(0, pos);
string line2 = arg.substr(pos + 1);
if (!DebuggerClient::IsValidNumber(line1) ||
!DebuggerClient::IsValidNumber(line2)) {
if (m_file.empty()) {
m_file = arg;
m_line1 = 1;
m_line2 = DebuggerClient::CodeBlockSize;
} else {
client.error("Line numbers have to be integers.");
help(client);
return;
}
} else {
m_line1 = atoi(line1.c_str());
m_line2 = atoi(line2.c_str());
if (line1.empty()) {
m_line1 = m_line2 - DebuggerClient::CodeBlockSize;
}
if (line2.empty()) {
m_line2 = m_line1 + DebuggerClient::CodeBlockSize;
}
if (m_line1 <= 0 || m_line2 <= 0) {
client.error("Line numbers have to be positive integers.");
help(client);
return;
}
}
} else {
if (!DebuggerClient::IsValidNumber(arg)) {
if (m_file.empty()) {
if (client.argCount() == 1 && listFunctionOrClass(client)) {
return;
}
m_file = arg;
m_line1 = 1;
m_line2 = DebuggerClient::CodeBlockSize;
} else {
client.error("A line number has to be an integer.");
help(client);
return;
}
} else {
int line = atoi(arg.c_str());
if (line <= 0) {
client.error("A line number has to be a positive integer.");
help(client);
return;
}
m_line1 = line - DebuggerClient::CodeBlockSize/2;
m_line2 = m_line1 + DebuggerClient::CodeBlockSize;
}
}
}
}
int charFocus0 = 0;
int lineFocus1 = 0;
int charFocus1 = 0;
if (m_file.empty()) {
getListLocation(client, line, charFocus0, lineFocus1, charFocus1);
if (m_file.empty()) {
listEvalCode(client);
return;
}
} else if (m_file[0] == '/') {
struct stat sb;
stat(m_file.c_str(), &sb);
if ((sb.st_mode & S_IFMT) == S_IFDIR) {
client.setSourceRoot(m_file);
client.info("PHP source root directory is set to %s", m_file.c_str());
return;
}
}
if (!listFileRange(client, line, charFocus0, lineFocus1, charFocus1)) {
client.error(
"Unable to read specified function, class or source file location.");
}
}
// Tries to read the contents of the file whose path is specified in m_file.
// If the path cannot be resolved and is relative, the path of the sandbox
// is used to qualify the relative path. If the contents cannot be retrieved
// m_code will be an empty string.
// The function returns false if the reply to the client fails during the
// sending process.
bool CmdList::onServer(DebuggerProxy &proxy) {
auto savedWarningFrequency = RuntimeOption::WarningFrequency;
RuntimeOption::WarningFrequency = 0;
m_code = f_file_get_contents(m_file.c_str());
if (!proxy.isLocal() && !m_code.toBoolean() && m_file[0] != '/') {
DSandboxInfo info = proxy.getSandbox();
if (info.m_path.empty()) {
raise_warning("path for sandbox %s is not setup, run a web request",
info.desc().c_str());
} else {
std::string full_path = info.m_path + m_file;
m_code = f_file_get_contents(full_path.c_str());
}
}
RuntimeOption::WarningFrequency = savedWarningFrequency;
if (!m_code.toBoolean() &&
m_file.find("systemlib.php") == m_file.length() - 13) {
m_code = SystemLib::s_source;
}
return proxy.sendToClient((DebuggerCommand*)this);
}
// Sends a "list file" command to the proxy attached to the given client.
// Returns false if the file does not exist or could not be read as an
// HPHP::String instance containing the contents of the file.
Variant CmdList::GetSourceFile(DebuggerClient &client,
const std::string &file) {
CmdList cmd;
cmd.m_file = file;
CmdListPtr res = client.xend<CmdList>(&cmd);
return res->m_code;
}
///////////////////////////////////////////////////////////////////////////////
}}