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/array-iterator.h"
18 #include "hphp/runtime/base/backtrace.h"
19 #include "hphp/runtime/base/php-globals.h"
20 #include "hphp/runtime/base/static-string-table.h"
21 #include "hphp/runtime/base/string-util.h"
22 #include "hphp/runtime/base/tv-variant.h"
23 #include "hphp/runtime/ext/vsdebug/command.h"
24 #include "hphp/runtime/ext/vsdebug/debugger.h"
25 #include "hphp/runtime/vm/runtime.h"
26 #include "hphp/runtime/vm/vm-regs.h"
31 SetVariableCommand::SetVariableCommand(
33 folly::dynamic message
34 ) : VSCommand(debugger
, message
),
37 const folly::dynamic
& args
= tryGetObject(message
, "arguments", s_emptyArgs
);
38 const int variablesReference
= tryGetInt(args
, "variablesReference", -1);
39 if (variablesReference
< 0) {
40 throw DebuggerCommandException("Invalid variablesReference specified.");
43 m_objectId
= variablesReference
;
46 SetVariableCommand::~SetVariableCommand() {
49 request_id_t
SetVariableCommand::targetThreadId(DebuggerSession
* session
) {
50 ServerObject
* obj
= session
->getServerObject(m_objectId
);
52 throw DebuggerCommandException("Invalid variablesReference specified.");
55 if (obj
->objectType() == ServerObjectType::Scope
) {
56 ScopeObject
* scope
= static_cast<ScopeObject
*>(obj
);
57 return scope
->m_requestId
;
58 } else if (obj
->objectType() == ServerObjectType::Variable
) {
59 VariableObject
* variable
= static_cast<VariableObject
*>(obj
);
60 return variable
->m_requestId
;
63 throw DebuggerCommandException("Unexpected server object type");
66 bool SetVariableCommand::executeImpl(
67 DebuggerSession
* session
,
68 folly::dynamic
* responseMsg
70 // The request thread should not re-enter the debugger while
71 // processing this command.
72 DebuggerNoBreakContext
noBreak(m_debugger
);
74 folly::dynamic body
= folly::dynamic::object
;
75 folly::dynamic variables
= folly::dynamic::array
;
76 auto& args
= tryGetObject(getMessage(), "arguments", s_emptyArgs
);
78 ServerObject
* obj
= session
->getServerObject(m_objectId
);
80 throw DebuggerCommandException("Invalid variablesReference specified.");
83 const std::string
& suppliedName
= tryGetString(args
, "name", "");
84 if (suppliedName
.empty()) {
85 throw DebuggerCommandException("Invalid variable name specified.");
88 // Remove any prefixes that we would have added to the variable name
89 // before presenting it to the front-end.
90 std::string name
= removeVariableNamePrefix(suppliedName
);
92 const std::string
& strValue
= tryGetString(args
, "value", "");
95 if (obj
->objectType() == ServerObjectType::Scope
) {
96 ScopeObject
* scope
= static_cast<ScopeObject
*>(obj
);
98 switch (scope
->m_scopeType
) {
99 case ScopeType::Locals
:
100 success
= setLocalVariable(
109 case ScopeType::ServerConstants
:
110 success
= setConstant(
119 // Superglobals and core constants are provided by the current execution
120 // context, rather than trying to overwrite them here, defer to the PHP
121 // console, which will let the runtime enforce whatever policies are
123 case ScopeType::Superglobals
:
124 m_debugger
->sendUserMessage(
125 "Could not directly set value of superglobal variable, you may "
126 "be able to set this value by running a Hack/PHP command in the "
128 DebugTransport::OutputLevelError
135 } else if (obj
->objectType() == ServerObjectType::Variable
) {
136 VariableObject
* variable
= static_cast<VariableObject
*>(obj
);
137 Variant
& variant
= variable
->m_variable
;
138 if (variant
.isArray()) {
139 success
= setArrayVariable(session
, name
, strValue
, variable
, &body
);
140 } else if (variant
.isObject()) {
141 success
= setObjectVariable(session
, name
, strValue
, variable
, &body
);
143 throw DebuggerCommandException(
144 "Failed to set variable: Unexpected variable type."
148 } catch (DebuggerCommandException
&e
) {
149 m_debugger
->sendUserMessage(
151 DebugTransport::OutputLevelError
157 throw DebuggerCommandException(
158 "Failed to set variable."
162 (*responseMsg
)["body"] = body
;
166 bool SetVariableCommand::setLocalVariable(
167 DebuggerSession
* session
,
168 const std::string
& name
,
169 const std::string
& value
,
171 folly::dynamic
* result
173 VMRegAnchor _regAnchor
;
176 g_context
->getFrameAtDepthForDebuggerUnsafe(scope
->m_frameDepth
);
180 // Can't set variable in a frame with no context.
184 const auto func
= fp
->func();
185 if (func
== nullptr) {
189 const auto localCount
= func
->numNamedLocals();
191 for (Id id
= 0; id
< localCount
; id
++) {
192 // Check for unnamed local.
193 auto const localNameSd
= func
->localVarName(id
);
194 if (!localNameSd
) continue;
196 auto const frameValue
= frame_local(fp
, id
);
197 const std::string localName
= localNameSd
->toCppString();
198 if (localName
== name
) {
214 const StaticString
s_user("user");
216 bool SetVariableCommand::setConstant(
217 DebuggerSession
* session
,
218 const std::string
& name
,
219 const std::string
& value
,
221 folly::dynamic
* result
223 const auto& constants
= lookupDefinedConstants(false);
224 for (ArrayIter
iter(constants
); iter
; ++iter
) {
225 const std::string constantName
= iter
.first().toString().toCppString();
226 if (constantName
== name
) {
227 TypedValue
* constantValue
= iter
.second().asTypedValue();
242 bool SetVariableCommand::setArrayVariable(
243 DebuggerSession
* session
,
244 const std::string
& name
,
245 const std::string
& value
,
246 VariableObject
* array
,
247 folly::dynamic
* result
249 VMRegAnchor regAnchor
;
251 Variant
& var
= array
->m_variable
;
252 assertx(var
.isArray());
254 Array arr
= var
.toArray();
255 for (ArrayIter
iter(arr
); iter
; ++iter
) {
256 std::string indexName
= iter
.first().toString().toCppString();
257 if (indexName
== name
) {
258 TypedValue
* arrayValue
= iter
.second().asTypedValue();
268 auto keyVariant
= iter
.first();
269 if (keyVariant
.isString()) {
270 HPHP::String key
= keyVariant
.toString();
271 arr
.set(key
, tvToInit(*arrayValue
));
272 } else if (keyVariant
.isInteger()) {
273 int64_t key
= keyVariant
.toInt64();
274 arr
.set(key
, tvToInit(*arrayValue
));
276 throw DebuggerCommandException("Unsupported array key type.");
285 bool SetVariableCommand::setObjectVariable(
286 DebuggerSession
* session
,
287 const std::string
& name
,
288 const std::string
& value
,
289 VariableObject
* object
,
290 folly::dynamic
* result
292 Variant
& var
= object
->m_variable
;
293 assertx(var
.isObject());
295 HPHP::String
key(name
);
296 ObjectData
* obj
= var
.getObjectData();
297 Variant currentValue
= obj
->o_get(key
, false);
298 if (!currentValue
.isInitialized()) {
299 throw DebuggerCommandException(
300 "Failed to set variable: Property not found on object."
304 TypedValue
* propValue
= currentValue
.asTypedValue();
314 obj
->o_set(key
, currentValue
);
318 bool SetVariableCommand::getBooleanValue(const std::string
& str
) {
319 // Trim leading and trailing whitespace.
320 std::string trimmed
= trimString(str
);
322 // Boolean values in PHP are not case sensitive.
323 for (char& c
: trimmed
) {
327 if (trimmed
== "TRUE") {
329 } else if (trimmed
== "FALSE") {
333 throw DebuggerCommandException(
334 "The specified value was not a valid boolean value."
338 void SetVariableCommand::setVariableValue(
339 DebuggerSession
* session
,
340 const std::string
& name
,
341 const std::string
& value
,
342 tv_lval typedVariable
,
343 request_id_t requestId
,
344 folly::dynamic
* result
346 switch (type(typedVariable
)) {
347 case KindOfBoolean
: {
348 bool boolVal
= getBooleanValue(value
);
349 val(typedVariable
).num
= boolVal
? 1 : 0;
355 val(typedVariable
).num
= std::stoi(value
, nullptr, 0);
356 } catch (std::exception
&) {
357 throw DebuggerCommandException("Invalid value specified.");
363 val(typedVariable
).dbl
= std::stod(value
);
364 } catch (std::exception
&) {
365 throw DebuggerCommandException("Invalid value specified.");
369 case KindOfPersistentString
:
371 const auto newSd
= StringData::Make(
376 if (type(typedVariable
) == KindOfString
&&
377 val(typedVariable
).pstr
!= nullptr) {
378 val(typedVariable
).pstr
->decRefCount();
381 val(typedVariable
).pstr
= newSd
;
382 type(typedVariable
) = KindOfString
;
388 // In the case of uninit and null, we don't even know how to interpret
389 // the value from the client, because the object has no known type.
392 case KindOfPersistentVec
:
394 case KindOfPersistentDict
:
396 case KindOfPersistentKeyset
:
399 // For complex types, we need to run PHP code to create a new object
400 // or determine what reference to assign, making direct assignment via
401 // reflection impractical, so we defer to the console REPL, which will use
402 // an evaluation command to run PHP.
403 // NOTE: It would be nice in the future to just run an eval command here
404 // on the user's behalf. At the moment, if we are setting a child prop
405 // on an object or array, we don't know the fully qualified name string
406 // to pass to PHP though, since we only have a reference to the container.
407 throw DebuggerCommandException(
408 "Failed to set object value. Please use the console to set the value "
409 "of complex objects."
413 throw DebuggerCommandException("Unexpected variable type");
416 Variant variable
{variant_ref
{typedVariable
}};
417 *result
= VariablesCommand::serializeVariable(