2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2017-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/runtime/base/backtrace.h"
18 #include "hphp/runtime/base/php-globals.h"
19 #include "hphp/runtime/base/static-string-table.h"
20 #include "hphp/runtime/base/string-util.h"
21 #include "hphp/runtime/base/tv-variant.h"
22 #include "hphp/runtime/ext/vsdebug/command.h"
23 #include "hphp/runtime/ext/vsdebug/debugger.h"
24 #include "hphp/runtime/vm/runtime.h"
25 #include "hphp/runtime/vm/vm-regs.h"
30 SetVariableCommand::SetVariableCommand(
32 folly::dynamic message
33 ) : VSCommand(debugger
, message
),
36 const folly::dynamic
& args
= tryGetObject(message
, "arguments", s_emptyArgs
);
37 const int variablesReference
= tryGetInt(args
, "variablesReference", -1);
38 if (variablesReference
< 0) {
39 throw DebuggerCommandException("Invalid variablesReference specified.");
42 m_objectId
= variablesReference
;
45 SetVariableCommand::~SetVariableCommand() {
48 request_id_t
SetVariableCommand::targetThreadId(DebuggerSession
* session
) {
49 ServerObject
* obj
= session
->getServerObject(m_objectId
);
51 throw DebuggerCommandException("Invalid variablesReference specified.");
54 if (obj
->objectType() == ServerObjectType::Scope
) {
55 ScopeObject
* scope
= static_cast<ScopeObject
*>(obj
);
56 return scope
->m_requestId
;
57 } else if (obj
->objectType() == ServerObjectType::Variable
) {
58 VariableObject
* variable
= static_cast<VariableObject
*>(obj
);
59 return variable
->m_requestId
;
62 throw DebuggerCommandException("Unexpected server object type");
65 bool SetVariableCommand::executeImpl(
66 DebuggerSession
* session
,
67 folly::dynamic
* responseMsg
69 // The request thread should not re-enter the debugger while
70 // processing this command.
71 DebuggerNoBreakContext
noBreak(m_debugger
);
73 folly::dynamic body
= folly::dynamic::object
;
74 folly::dynamic variables
= folly::dynamic::array
;
75 auto& args
= tryGetObject(getMessage(), "arguments", s_emptyArgs
);
77 ServerObject
* obj
= session
->getServerObject(m_objectId
);
79 throw DebuggerCommandException("Invalid variablesReference specified.");
82 const std::string
& suppliedName
= tryGetString(args
, "name", "");
83 if (suppliedName
.empty()) {
84 throw DebuggerCommandException("Invalid variable name specified.");
87 // Remove any prefixes that we would have added to the variable name
88 // before presenting it to the front-end.
89 std::string name
= removeVariableNamePrefix(suppliedName
);
91 const std::string
& strValue
= tryGetString(args
, "value", "");
94 if (obj
->objectType() == ServerObjectType::Scope
) {
95 ScopeObject
* scope
= static_cast<ScopeObject
*>(obj
);
97 switch (scope
->m_scopeType
) {
98 case ScopeType::Locals
:
99 success
= setLocalVariable(
108 case ScopeType::ServerConstants
:
109 success
= setConstant(
118 // Superglobals and core constants are provided by the current execution
119 // context, rather than trying to overwrite them here, defer to the PHP
120 // console, which will let the runtime enforce whatever policies are
122 case ScopeType::Superglobals
:
123 m_debugger
->sendUserMessage(
124 "Could not directly set value of superglobal variable, you may "
125 "be able to set this value by running a Hack/PHP command in the "
127 DebugTransport::OutputLevelError
134 } else if (obj
->objectType() == ServerObjectType::Variable
) {
135 VariableObject
* variable
= static_cast<VariableObject
*>(obj
);
136 Variant
& variant
= variable
->m_variable
;
137 if (variant
.isArray()) {
138 success
= setArrayVariable(session
, name
, strValue
, variable
, &body
);
139 } else if (variant
.isObject()) {
140 success
= setObjectVariable(session
, name
, strValue
, variable
, &body
);
142 throw DebuggerCommandException(
143 "Failed to set variable: Unexpected variable type."
147 } catch (DebuggerCommandException
&e
) {
148 m_debugger
->sendUserMessage(
150 DebugTransport::OutputLevelError
156 throw DebuggerCommandException(
157 "Failed to set variable."
161 (*responseMsg
)["body"] = body
;
165 bool SetVariableCommand::setLocalVariable(
166 DebuggerSession
* session
,
167 const std::string
& name
,
168 const std::string
& value
,
170 folly::dynamic
* result
172 VMRegAnchor _regAnchor
;
175 g_context
->getFrameAtDepthForDebuggerUnsafe(scope
->m_frameDepth
);
179 // Can't set variable in a frame with no context.
183 const auto func
= fp
->func();
184 if (func
== nullptr) {
188 const auto localCount
= func
->numNamedLocals();
190 for (Id id
= 0; id
< localCount
; id
++) {
191 TypedValue
* frameValue
= frame_local(fp
, id
);
192 const std::string localName
= func
->localVarName(id
)->toCppString();
193 if (localName
== name
) {
209 const StaticString
s_user("user");
211 bool SetVariableCommand::setConstant(
212 DebuggerSession
* session
,
213 const std::string
& name
,
214 const std::string
& value
,
216 folly::dynamic
* result
218 const auto& constants
= lookupDefinedConstants(false);
219 for (ArrayIter
iter(constants
); iter
; ++iter
) {
220 const std::string constantName
= iter
.first().toString().toCppString();
221 if (constantName
== name
) {
222 TypedValue
* constantValue
= iter
.second().asTypedValue();
237 bool SetVariableCommand::setArrayVariable(
238 DebuggerSession
* session
,
239 const std::string
& name
,
240 const std::string
& value
,
241 VariableObject
* array
,
242 folly::dynamic
* result
244 VMRegAnchor regAnchor
;
246 Variant
& var
= array
->m_variable
;
247 assertx(var
.isArray());
249 Array arr
= var
.toArray();
250 for (ArrayIter
iter(arr
); iter
; ++iter
) {
251 std::string indexName
= iter
.first().toString().toCppString();
252 if (indexName
== name
) {
253 TypedValue
* arrayValue
= iter
.second().asTypedValue();
263 auto keyVariant
= iter
.first();
264 if (keyVariant
.isString()) {
265 HPHP::String key
= keyVariant
.toString();
266 arr
.set(key
, tvToInit(*arrayValue
));
267 } else if (keyVariant
.isInteger()) {
268 int64_t key
= keyVariant
.toInt64();
269 arr
.set(key
, tvToInit(*arrayValue
));
271 throw DebuggerCommandException("Unsupported array key type.");
280 bool SetVariableCommand::setObjectVariable(
281 DebuggerSession
* session
,
282 const std::string
& name
,
283 const std::string
& value
,
284 VariableObject
* object
,
285 folly::dynamic
* result
287 Variant
& var
= object
->m_variable
;
288 assertx(var
.isObject());
290 HPHP::String
key(name
);
291 ObjectData
* obj
= var
.getObjectData();
292 Variant currentValue
= obj
->o_get(key
, false);
293 if (!currentValue
.isInitialized()) {
294 throw DebuggerCommandException(
295 "Failed to set variable: Property not found on object."
299 TypedValue
* propValue
= currentValue
.asTypedValue();
309 obj
->o_set(key
, currentValue
);
313 bool SetVariableCommand::getBooleanValue(const std::string
& str
) {
314 // Trim leading and trailing whitespace.
315 std::string trimmed
= trimString(str
);
317 // Boolean values in PHP are not case sensitive.
318 for (char& c
: trimmed
) {
322 if (trimmed
== "TRUE") {
324 } else if (trimmed
== "FALSE") {
328 throw DebuggerCommandException(
329 "The specified value was not a valid boolean value."
333 void SetVariableCommand::setVariableValue(
334 DebuggerSession
* session
,
335 const std::string
& name
,
336 const std::string
& value
,
337 TypedValue
* typedVariable
,
338 request_id_t requestId
,
339 folly::dynamic
* result
341 switch (typedVariable
->m_type
) {
342 case KindOfBoolean
: {
343 bool boolVal
= getBooleanValue(value
);
344 typedVariable
->m_data
.num
= boolVal
? 1 : 0;
350 typedVariable
->m_data
.num
= std::stoi(value
, nullptr, 0);
351 } catch (std::exception
&e
) {
352 throw DebuggerCommandException("Invalid value specified.");
358 typedVariable
->m_data
.dbl
= std::stod(value
);
359 } catch (std::exception
&e
) {
360 throw DebuggerCommandException("Invalid value specified.");
364 case KindOfPersistentString
:
366 const auto newSd
= StringData::Make(
371 if (typedVariable
->m_type
== KindOfString
&&
372 typedVariable
->m_data
.pstr
!= nullptr) {
374 typedVariable
->m_data
.pstr
->decRefCount();
377 typedVariable
->m_data
.pstr
= newSd
;
378 typedVariable
->m_type
= KindOfString
;
384 // In the case of uninit and null, we don't even know how to interpret
385 // the value from the client, because the object has no known type.
388 case KindOfPersistentVec
:
390 case KindOfPersistentDArray
:
392 case KindOfPersistentVArray
:
394 case KindOfPersistentArray
:
396 case KindOfPersistentDict
:
398 case KindOfPersistentKeyset
:
401 // For complex types, we need to run PHP code to create a new object
402 // or determine what reference to assign, making direct assignment via
403 // reflection impractical, so we defer to the console REPL, which will use
404 // an evaluation command to run PHP.
405 // NOTE: It would be nice in the future to just run an eval command here
406 // on the user's behalf. At the moment, if we are setting a child prop
407 // on an object or array, we don't know the fully qualified name string
408 // to pass to PHP though, since we only have a reference to the container.
409 throw DebuggerCommandException(
410 "Failed to set object value. Please use the console to set the value "
411 "of complex objects."
415 throw DebuggerCommandException("Unexpected variable type");
418 Variant variable
= tvAsVariant(typedVariable
);
419 *result
= VariablesCommand::serializeVariable(