frames.cpp
jmc.cpp
cputil.cpp
- expr.cpp)
+ expr.cpp
+ valuewrite.cpp)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
+// Copyright (c) 2018 Samsung Electronics Co., LTD
+// Distributed under the MIT License.
+// See the LICENSE file in the project root for more information.
+
#include "cputil.h"
#include <codecvt>
virtual HRESULT GetVariables(uint32_t variablesReference, VariablesFilter filter, int start, int count, std::vector<Variable> &variables) = 0;
virtual int GetNamedVariables(uint32_t variablesReference) = 0;
virtual HRESULT Evaluate(uint64_t frameId, const std::string &expression, Variable &variable) = 0;
+ virtual HRESULT SetVariable(const std::string &name, const std::string &value, uint32_t ref) = 0;
+ virtual HRESULT SetVariableByExpression(uint64_t frameId, const std::string &name, const std::string &value) = 0;
};
class Protocol
return S_OK;
}
+
+HRESULT Evaluator::CreateString(ICorDebugThread *pThread, const std::string &value, ICorDebugValue **ppNewString)
+{
+ HRESULT Status;
+ auto value16t = to_utf16(value);
+
+ ToRelease<ICorDebugEval> pEval;
+ IfFailRet(pThread->CreateEval(&pEval));
+
+ IfFailRet(pEval->NewString(value16t.c_str()));
+
+ return WaitEvalResult(pThread, pEval, ppNewString);
+}
HRESULT WalkStackVars(ICorDebugFrame *pFrame, WalkStackVarsCallback cb);
+ HRESULT CreateString(
+ ICorDebugThread *pThread,
+ const std::string &value,
+ ICorDebugValue **ppNewString);
+
void Cleanup();
};
unsigned int &numchild,
bool static_members = false);
+ HRESULT SetStackVariable(
+ uint64_t frameId,
+ ICorDebugThread *pThread,
+ ICorDebugFrame *pFrame,
+ const std::string &name,
+ const std::string &value);
+
+ HRESULT SetChild(
+ VariableReference &ref,
+ ICorDebugThread *pThread,
+ ICorDebugFrame *pFrame,
+ const std::string &name,
+ const std::string &value);
+
public:
Variables(Evaluator &evaluator) : m_evaluator(evaluator), m_nextVariableReference(1) {}
int count,
std::vector<Variable> &variables);
+ HRESULT SetVariable(
+ ICorDebugProcess *pProcess,
+ const std::string &name,
+ const std::string &value,
+ uint32_t ref);
+
+ HRESULT SetVariable(
+ ICorDebugProcess *pProcess,
+ ICorDebugValue *pVariable,
+ const std::string &value,
+ uint64_t frameId);
+
HRESULT GetScopes(ICorDebugProcess *pProcess, uint64_t frameId, std::vector<Scope> &scopes);
HRESULT Evaluate(ICorDebugProcess *pProcess, uint64_t frameId, const std::string &expression, Variable &variable);
+ HRESULT GetValueByExpression(
+ ICorDebugProcess *pProcess,
+ uint64_t frameId,
+ const std::string &expression,
+ ICorDebugValue **ppResult);
+
void Clear() { m_variables.clear(); m_nextVariableReference = 1; }
};
HRESULT GetVariables(uint32_t variablesReference, VariablesFilter filter, int start, int count, std::vector<Variable> &variables) override;
int GetNamedVariables(uint32_t variablesReference) override;
HRESULT Evaluate(uint64_t frameId, const std::string &expression, Variable &variable) override;
+ HRESULT SetVariable(const std::string &name, const std::string &value, uint32_t ref) override;
+ HRESULT SetVariableByExpression(uint64_t frameId, const std::string &expression, const std::string &value) override;
};
return S_OK;
}
+bool MIProtocol::IsEditable(const std::string &type)
+{
+ if (type == "int"
+ || type == "bool"
+ || type == "char"
+ || type == "byte"
+ || type == "sbyte"
+ || type == "short"
+ || type == "ushort"
+ || type == "uint"
+ || type == "long"
+ || type == "ulong"
+ || type == "decimal"
+ || type == "string")
+ return true;
+
+ return false;
+}
+
void MIProtocol::PrintVar(const std::string &varobjName, Variable &v, int threadId, int print_values, std::string &output)
{
std::ostringstream ss;
- std::string editable = "noneditable";
+ std::string editable;
+ if (IsEditable(v.type))
+ editable = "editable";
+ else
+ editable = "noneditable";
ss << "name=\"" << varobjName << "\",";
if (print_values)
return m_vars.erase(varobjName) == 0 ? E_FAIL : S_OK;
}
+HRESULT MIProtocol::FindVar(const std::string &varobjName, Variable &variable)
+{
+ auto it = m_vars.find(varobjName);
+ if (it == m_vars.end())
+ return E_FAIL;
+
+ variable = it->second;
+
+ return S_OK;
+}
+
void MIProtocol::Cleanup()
{
m_vars.clear();
return S_OK;
}},
- { "var-show-attributes", [](const std::vector<std::string> &args, std::string &output) -> HRESULT {
- output = "status=\"noneditable\"";
+ { "var-show-attributes", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
+ HRESULT Status;
+ Variable variable;
+ std::string varName = args.at(0);
+ std::string editable;
+
+ IfFailRet(FindVar(varName, variable));
+ if (IsEditable(variable.type))
+ editable = "editable";
+ else
+ editable = "noneditable";
+
+ output = "status=\"" + editable + "\"";
+ return S_OK;
+ }},
+ { "var-assign", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
+ HRESULT Status;
+
+ if (args.size() < 2)
+ {
+ output = "Command requires at least 2 arguments";
+ return E_FAIL;
+ }
+
+ std::string varName = args.at(0);
+ std::string varExpr = args.at(1);
+
+ if (varExpr.size() >= 2 && varExpr.front() == '"' && varExpr.back() == '"')
+ varExpr = varExpr.substr(1, varExpr.size() - 2);
+
+ int threadId = GetIntArg(args, "--thread", m_debugger->GetLastStoppedThreadId());
+ int level = GetIntArg(args, "--frame", 0);
+ uint64_t frameId = StackFrame(threadId, level, "").id;
+
+ Variable variable;
+ IfFailRet(FindVar(varName, variable));
+
+ m_debugger->SetVariableByExpression(frameId, variable.evaluateName, varExpr);
+
+ output = "value=\"" + MIProtocol::EscapeMIValue(varExpr) + "\"";
+
return S_OK;
}},
};
return command_it->second(args, output);
}
-static std::vector<std::string> TokenizeString(const std::string &str, const char *delimiters = " \t\n\r")
+class Tokenizer
{
- enum {
- StateSpace,
- StateToken,
- StateQuotedToken,
- StateEscape
- } state = StateSpace;
+ std::string m_str;
+ std::string m_delimiters;
+ size_t m_next;
+public:
- std::vector<std::string> result;
+ Tokenizer(const std::string &str, const std::string &delimiters = " \t\n\r")
+ : m_str(str), m_delimiters(delimiters), m_next(0)
+ {
+ m_str.erase(m_str.find_last_not_of(m_delimiters) + 1);
+ }
- for (char c : str)
+ bool Next(std::string &token)
{
- switch(state)
+ token = "";
+
+ if (m_next >= m_str.size())
+ return false;
+
+ enum {
+ StateSpace,
+ StateToken,
+ StateQuotedToken,
+ StateEscape
+ } state = StateSpace;
+
+ for (; m_next < m_str.size(); m_next++)
{
- case StateSpace:
- if (strchr(delimiters, c))
- continue;
- result.emplace_back();
- state = c == '"' ? StateQuotedToken : StateToken;
- if (state != StateQuotedToken)
- result.back() +=c;
- break;
- case StateToken:
- if (strchr(delimiters, c))
- state = StateSpace;
- else
- result.back() += c;
- break;
- case StateQuotedToken:
- if (c == '\\')
- state = StateEscape;
- else if (c == '"')
- state = StateSpace;
- else
- result.back() += c;
- break;
- case StateEscape:
- result.back() += c;
- state = StateQuotedToken;
- break;
+ char c = m_str.at(m_next);
+ switch(state)
+ {
+ case StateSpace:
+ if (m_delimiters.find(c) != std::string::npos)
+ continue;
+ if (!token.empty())
+ return true;
+ state = c == '"' ? StateQuotedToken : StateToken;
+ if (state != StateQuotedToken)
+ token +=c;
+ break;
+ case StateToken:
+ if (m_delimiters.find(c) != std::string::npos)
+ state = StateSpace;
+ else
+ token += c;
+ break;
+ case StateQuotedToken:
+ if (c == '\\')
+ state = StateEscape;
+ else if (c == '"')
+ state = StateSpace;
+ else
+ token += c;
+ break;
+ case StateEscape:
+ token += c;
+ state = StateQuotedToken;
+ break;
+ }
}
+ return state != StateEscape || token.empty();
}
- return result;
-}
+
+ std::string Remain() const
+ {
+ return m_str.substr(m_next);
+ }
+};
static bool ParseLine(const std::string &str,
std::string &token,
cmd.clear();
args.clear();
- std::vector<std::string> result = TokenizeString(str);
+ Tokenizer tokenizer(str);
+ std::string result;
- if (result.empty())
+ if (!tokenizer.Next(result) || result.empty())
return false;
- auto cmd_it = result.begin();
-
- std::size_t i = cmd_it->find_first_not_of("0123456789");
+ std::size_t i = result.find_first_not_of("0123456789");
if (i == std::string::npos)
return false;
- if (cmd_it->at(i) != '-')
+ if (result.at(i) != '-')
return false;
- token = cmd_it->substr(0, i);
- cmd = cmd_it->substr(i + 1);
- result.erase(result.begin());
- args = result;
+ token = result.substr(0, i);
+ cmd = result.substr(i + 1);
+
+ if (cmd == "var-assign")
+ {
+ tokenizer.Next(result);
+ args.push_back(result);
+ args.push_back(tokenizer.Remain());
+ return true;
+ }
+
+ while (tokenizer.Next(result))
+ args.push_back(result);
return true;
}
static void Printf(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
#endif
+ static bool IsEditable(const std::string &type);
+
public:
MIProtocol() : Protocol(), m_varCounter(0) {}
HRESULT PrintVariables(const std::vector<Variable> &variables, std::string &output);
HRESULT CreateVar(int threadId, int level, const std::string &varobjName, const std::string &expression, std::string &output);
HRESULT DeleteVar(const std::string &varobjName);
+ HRESULT FindVar(const std::string &varobjName, Variable &variable);
void PrintChildren(std::vector<Variable> &children, int threadId, int print_values, bool has_more, std::string &output);
void PrintNewVar(std::string varobjName, Variable &v, int threadId, int print_values, std::string &output);
HRESULT ListChildren(int threadId, int level, int childStart, int childEnd, const std::string &varName, int print_values, std::string &output);
public:
static const int HiddenLine;
-#ifdef _MSC_VER
-#define PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop) )
-#else
-#define PACK( __Declaration__ ) __Declaration__ __attribute__((packed))
-#endif
-
- PACK(struct SequencePoint {
+ PACK_BEGIN struct SequencePoint {
int32_t startLine;
int32_t startColumn;
int32_t endLine;
int32_t endColumn;
int32_t offset;
- });
+ } PACK_END;
SymbolReader()
{
#define TO_CDADDR(taddr) ((CLRDATA_ADDRESS)(LONG_PTR)(taddr))
const int mdNameLen = 2048;
+
+#ifdef _MSC_VER
+#define PACK_BEGIN __pragma( pack(push, 1) )
+#define PACK_END __pragma( pack(pop) )
+#else
+#define PACK_BEGIN
+#define PACK_END __attribute__((packed))
+#endif
--- /dev/null
+// Copyright (c) 2018 Samsung Electronics Co., LTD
+// Distributed under the MIT License.
+// See the LICENSE file in the project root for more information.
+
+#ifndef FEATURE_PAL
+// Turn off macro definitions named max and min in <windows.h> header file
+// to avoid compile error for std::numeric_limits<uint64_t>::max().
+#define NOMINMAX
+#endif
+
+#include "valuewrite.h"
+
+#include <locale>
+#include <codecvt>
+#include <cstring>
+#include <sstream>
+#include <iomanip>
+
+#include <arrayholder.h>
+
+#include "torelease.h"
+#include "cputil.h"
+#include "typeprinter.h"
+
+
+PACK_BEGIN struct Decimal {
+ unsigned int unused : 16;
+ unsigned int exponent : 8;
+ unsigned int unused2 : 7;
+ unsigned int sign : 1;
+
+ uint32_t hi;
+ uint32_t lo;
+ uint32_t mid;
+
+ Decimal() : unused(0), exponent(0), unused2(0), sign(0), hi(0), lo(0), mid(0) {}
+ Decimal(uint32_t lo, uint32_t mid, uint32_t hi, uint8_t exp, unsigned int sign) : unused(0),
+ exponent(exp), unused2(0), sign(sign), hi(hi), lo(lo), mid(mid) {}
+ void Mul10();
+ void AddInt32(uint32_t val);
+ bool Parse(const std::string &value);
+
+private:
+ void ShiftLeft();
+ void Add(Decimal &d);
+ static bool AddCarry(uint32_t &to, uint32_t val);
+} PACK_END;
+
+bool Decimal::AddCarry(uint32_t &to, uint32_t val)
+{
+ uint32_t v = to;
+ to += val;
+ return (to < v) || (to < val);
+}
+
+void Decimal::ShiftLeft()
+{
+ uint32_t c0 = (lo & 0x80000000) != 0 ? 1u : 0u;
+ uint32_t c1 = (mid & 0x80000000) != 0 ? 1u : 0u;
+
+ lo = lo << 1;
+ mid = (mid << 1) | c0;
+ hi = (hi << 1) | c1;
+}
+
+void Decimal::Add(Decimal &d)
+{
+ if (AddCarry(lo, d.lo))
+ if (AddCarry(mid, 1))
+ AddCarry(hi, 1);
+
+ if (AddCarry(mid, d.mid))
+ AddCarry(hi, 1);
+
+ AddCarry(hi, d.hi);
+}
+
+void Decimal::Mul10()
+{
+ Decimal d(lo, mid, hi, exponent, sign);
+
+ ShiftLeft();
+ ShiftLeft();
+ Add(d);
+ ShiftLeft();
+}
+
+void Decimal::AddInt32(uint32_t val)
+{
+ if (AddCarry(lo, val))
+ if (AddCarry(mid, 1))
+ AddCarry(hi, 1);
+}
+
+
+
+
+const int DECIMAL_PRECISION = 29;
+
+bool Decimal::Parse(const std::string &value)
+{
+ std::string valueToStore(value);
+ bool is_negative = valueToStore.length() > 0 && valueToStore[0] == '-';
+
+ if (is_negative) {
+ sign = 1;
+ valueToStore.erase(0, 1);
+ }
+ else
+ {
+ sign = 0;
+ }
+
+ // Cut all meaningless first zeroes
+ const auto ix = valueToStore.find_first_not_of('0');
+ valueToStore.erase(0, ix);
+
+ const auto dotPos = valueToStore.find(".");
+ if (dotPos != std::string::npos)
+ valueToStore.erase(dotPos, 1);
+
+ if (dotPos > std::numeric_limits<int>::max())
+ return false;
+
+ int scale = static_cast<int>(dotPos);
+
+ const unsigned char* p = reinterpret_cast<const unsigned char*>(valueToStore.c_str());
+
+ if (*p == 0)
+ {
+ // To avoid risking an app-compat issue with pre 4.5 (where some app was illegally using Reflection to examine the internal scale bits), we'll only force
+ // the scale to 0 if the scale was previously positive (previously, such cases were unparsable to a bug.)
+ if (scale > 0)
+ scale = 0;
+ }
+ else
+ {
+ if (scale > DECIMAL_PRECISION)
+ return false;
+
+ while (((scale > 0) || ((*p != 0) && (scale > -(DECIMAL_PRECISION - 1)))) &&
+ ((hi < 0x19999999) || ((hi == 0x19999999) &&
+ ((mid < 0x99999999) || ((mid == 0x99999999) &&
+ ((lo < 0x99999999) || ((lo == 0x99999999) &&
+ (*p <= '5'))))))))
+ {
+ Mul10();
+
+ if (*p != 0)
+ AddInt32((uint32_t)(*p++ - '0'));
+ scale--;
+ }
+
+ if (*p++ >= '5')
+ {
+ bool round = true;
+
+ if ((*(p - 1) == '5') && ((*(p - 2) % 2) == 0))
+ {
+ // Check if previous digit is even, only if the when we are unsure whether hows to do
+ // Banker's rounding. For digits > 5 we will be rounding up anyway.
+ int count = 20; // Look at the next 20 digits to check to round
+ while ((*p == '0') && (count != 0))
+ {
+ p++;
+ count--;
+ }
+ if ((*p == '\0') || (count == 0))
+ round = false;// Do nothing
+ }
+
+ if (round)
+ {
+ AddInt32(1);
+ if ((hi | mid | lo) == 0)
+ {
+ // If we got here, the magnitude portion overflowed and wrapped back to 0 as the magnitude was already at the MaxValue point:
+ //
+ // 79,228,162,514,264,337,593,543,950,335e+X
+ //
+ // Manually force it to the correct result:
+ //
+ // 7,922,816,251,426,433,759,354,395,034e+(X+1)
+ //
+ // This code path can be reached by trying to parse the following as a Decimal:
+ //
+ // 0.792281625142643375935439503355e28
+ //
+
+ hi = 0x19999999;
+ mid = 0x99999999;
+ lo = 0x9999999A;
+ scale++;
+ }
+ }
+ }
+ }
+
+ if (scale > 0)
+ return false; // Rounding may have caused its own overflow. For example, parsing "0.792281625142643375935439503355e29" will get here.
+
+ if (scale <= -DECIMAL_PRECISION)
+ {
+ // Parsing a large scale zero can give you more precision than fits in the decimal.
+ // This should only happen for actual zeros or very small numbers that round to zero.
+ hi = 0;
+ lo = 0;
+ mid = 0;
+ exponent = DECIMAL_PRECISION - 1;
+ }
+ else
+ {
+ exponent = -scale;
+ }
+
+ return true;
+}
+
+HRESULT WriteValue(ICorDebugValue *pValue, const std::string &value, ICorDebugThread *pThread, Evaluator &evaluator)
+{
+ HRESULT Status;
+
+ ULONG32 size;
+ IfFailRet(pValue->GetSize(&size));
+
+ ArrayHolder<BYTE> valBuf = new (std::nothrow) BYTE[size];
+ if (valBuf == nullptr)
+ return E_OUTOFMEMORY;
+
+ CorElementType corType;
+ IfFailRet(pValue->GetType(&corType));
+
+ if (corType == ELEMENT_TYPE_STRING)
+ {
+ ToRelease<ICorDebugValue> pNewString;
+ IfFailRet(evaluator.CreateString(pThread, value, &pNewString));
+
+ // Switch object addresses
+ ToRelease<ICorDebugReferenceValue> pRefNew;
+ IfFailRet(pNewString->QueryInterface(IID_ICorDebugReferenceValue, (LPVOID *) &pRefNew));
+ ToRelease<ICorDebugReferenceValue> pRefOld;
+ IfFailRet(pValue->QueryInterface(IID_ICorDebugReferenceValue, (LPVOID *) &pRefOld));
+
+ CORDB_ADDRESS addr;
+ IfFailRet(pRefNew->GetValue(&addr));
+ IfFailRet(pRefOld->SetValue(addr));
+
+ return S_OK;
+ }
+
+ ToRelease<ICorDebugGenericValue> pGenValue;
+ IfFailRet(pValue->QueryInterface(IID_ICorDebugGenericValue, (LPVOID *) &pGenValue));
+
+ std::stringstream ss(value);
+
+ switch (corType)
+ {
+ case ELEMENT_TYPE_BOOLEAN:
+ if (value == "false")
+ valBuf[0] = 0;
+ else if (value == "true")
+ valBuf[0] = 1;
+ else
+ return E_INVALIDARG;
+
+ break;
+ case ELEMENT_TYPE_CHAR:
+ {
+ auto wval = to_utf16(value);
+
+ if (wval.length() > 1)
+ return E_INVALIDARG;
+
+ std::memcpy(&valBuf[0], &wval[0], sizeof(wval[0]));
+
+ break;
+ }
+ case ELEMENT_TYPE_I1:
+ {
+ int num;
+
+ ss >> num;
+ valBuf[0] = (char)num;
+ break;
+ }
+ case ELEMENT_TYPE_U1:
+ {
+ int num;
+
+ ss >> num;
+ valBuf[0] = (unsigned char)num;
+ break;
+ }
+ case ELEMENT_TYPE_I2:
+ ss >> *(short *)&valBuf[0];
+ break;
+ case ELEMENT_TYPE_U2:
+ ss >> *(unsigned short *)&valBuf[0];
+ break;
+ case ELEMENT_TYPE_I4:
+ case ELEMENT_TYPE_I:
+ ss >> *(int *)&valBuf[0];
+ break;
+ case ELEMENT_TYPE_U4:
+ case ELEMENT_TYPE_U:
+ ss >> *(unsigned int *)&valBuf[0];
+ break;
+ case ELEMENT_TYPE_I8:
+ ss >> *(__int64 *)&valBuf[0];
+ break;
+ case ELEMENT_TYPE_U8:
+ ss >> *(unsigned __int64 *)&valBuf[0];
+ break;
+ case ELEMENT_TYPE_R4:
+ ss >> std::setprecision(8) >> *(float *)&valBuf[0];
+ break;
+ case ELEMENT_TYPE_R8:
+ ss >> std::setprecision(16) >> *(double *)&valBuf[0];
+ break;
+ case ELEMENT_TYPE_VALUETYPE:
+ case ELEMENT_TYPE_CLASS:
+ {
+ std::string typeName;
+ TypePrinter::GetTypeOfValue(pValue, typeName);
+ if (typeName == "decimal")
+ {
+ Decimal d;
+ if (!d.Parse(value))
+ return E_INVALIDARG;
+ if (sizeof(Decimal) > size)
+ return E_FAIL;
+ std::memcpy(&valBuf[0], &d, size);
+ }
+ break;
+ }
+ default:
+ return S_OK;
+ }
+
+ IfFailRet(pGenValue->SetValue((LPVOID) &valBuf[0]));
+
+ return S_OK;
+}
--- /dev/null
+// Copyright (c) 2018 Samsung Electronics Co., LTD
+// Distributed under the MIT License.
+// See the LICENSE file in the project root for more information.
+
+#pragma once
+
+#include <string>
+
+#include <cor.h>
+#include <cordebug.h>
+#include "manageddebugger.h"
+
+HRESULT WriteValue(ICorDebugValue *pValue, const std::string &value, ICorDebugThread *pThread, Evaluator &evaluator);
#include <unordered_set>
#include <vector>
+#include <cstring>
+
+#include <sstream>
#include "typeprinter.h"
#include "valueprint.h"
+#include "valuewrite.h"
#include "frames.h"
return S_OK;
}
+
+HRESULT ManagedDebugger::SetVariable(const std::string &name, const std::string &value, uint32_t ref)
+{
+ return m_variables.SetVariable(m_pProcess, name, value, ref);
+}
+
+HRESULT Variables::SetVariable(
+ ICorDebugProcess *pProcess,
+ const std::string &name,
+ const std::string &value,
+ uint32_t ref)
+{
+ if (pProcess == nullptr)
+ return E_FAIL;
+
+ auto it = m_variables.find(ref);
+ if (it == m_variables.end())
+ return E_FAIL;
+
+ VariableReference &varRef = it->second;
+ HRESULT Status;
+
+ StackFrame stackFrame(varRef.frameId);
+ ToRelease<ICorDebugThread> pThread;
+ IfFailRet(pProcess->GetThread(stackFrame.GetThreadId(), &pThread));
+ ToRelease<ICorDebugFrame> pFrame;
+ IfFailRet(GetFrameAt(pThread, stackFrame.GetLevel(), &pFrame));
+
+ if (varRef.IsScope())
+ {
+ IfFailRet(SetStackVariable(varRef.frameId, pThread, pFrame, name, value));
+ }
+ else
+ {
+ IfFailRet(SetChild(varRef, pThread, pFrame, name, value));
+ }
+
+ return S_OK;
+}
+
+HRESULT Variables::SetStackVariable(
+ uint64_t frameId,
+ ICorDebugThread *pThread,
+ ICorDebugFrame *pFrame,
+ const std::string &name,
+ const std::string &value)
+{
+ HRESULT Status;
+
+ // TODO Exception?
+
+ IfFailRet(m_evaluator.WalkStackVars(pFrame, [&](
+ ICorDebugILFrame *pILFrame,
+ ICorDebugValue *pValue,
+ const std::string &varName) -> HRESULT
+ {
+ if (varName == name)
+ IfFailRet(WriteValue(pValue, value, pThread, m_evaluator));
+
+ return S_OK;
+ }));
+
+ return S_OK;
+}
+
+HRESULT Variables::SetChild(
+ VariableReference &ref,
+ ICorDebugThread *pThread,
+ ICorDebugFrame *pFrame,
+ const std::string &name,
+ const std::string &value)
+{
+ if (ref.IsScope())
+ return E_INVALIDARG;
+
+ if (!ref.value)
+ return S_OK;
+
+ HRESULT Status;
+
+ ToRelease<ICorDebugILFrame> pILFrame;
+ if (pFrame)
+ IfFailRet(pFrame->QueryInterface(IID_ICorDebugILFrame, (LPVOID*) &pILFrame));
+
+ IfFailRet(m_evaluator.WalkMembers(ref.value, pThread, pILFrame, [&](
+ mdMethodDef mdGetter,
+ ICorDebugModule *pModule,
+ ICorDebugType *pType,
+ ICorDebugValue *pValue,
+ bool is_static,
+ const std::string &varName) -> HRESULT
+ {
+ if (varName == name)
+ IfFailRet(WriteValue(pValue, value, pThread, m_evaluator));
+
+ return S_OK;
+ }));
+
+ return S_OK;
+}
+
+HRESULT ManagedDebugger::SetVariableByExpression(
+ uint64_t frameId,
+ const std::string &expression,
+ const std::string &value)
+{
+ HRESULT Status;
+ ToRelease<ICorDebugValue> pResultValue;
+
+ IfFailRet(m_variables.GetValueByExpression(m_pProcess, frameId, expression, &pResultValue));
+ return m_variables.SetVariable(m_pProcess, pResultValue, value, frameId);
+}
+
+HRESULT Variables::GetValueByExpression(ICorDebugProcess *pProcess, uint64_t frameId, const std::string &expression,
+ ICorDebugValue **ppResult)
+{
+ if (pProcess == nullptr)
+ return E_FAIL;
+
+ HRESULT Status;
+
+ StackFrame stackFrame(frameId);
+ ToRelease<ICorDebugThread> pThread;
+ IfFailRet(pProcess->GetThread(stackFrame.GetThreadId(), &pThread));
+ ToRelease<ICorDebugFrame> pFrame;
+ IfFailRet(GetFrameAt(pThread, stackFrame.GetLevel(), &pFrame));
+
+ return m_evaluator.EvalExpr(pThread, pFrame, expression, ppResult);
+}
+
+HRESULT Variables::SetVariable(
+ ICorDebugProcess *pProcess,
+ ICorDebugValue *pVariable,
+ const std::string &value,
+ uint64_t frameId)
+{
+ HRESULT Status;
+
+ if (pProcess == nullptr)
+ return E_FAIL;
+
+ StackFrame stackFrame(frameId);
+ ToRelease<ICorDebugThread> pThread;
+ IfFailRet(pProcess->GetThread(stackFrame.GetThreadId(), &pThread));
+
+ return WriteValue(pVariable, value, pThread, m_evaluator);
+}
return E_INVALIDARG;
return m_debugger->Attach(processId);
+ } },
+ { "setVariable", [this](const json &arguments, json &body) {
+ HRESULT Status;
+
+ std::string name = arguments.at("name");
+ std::string value = arguments.at("value");
+ int ref = arguments.at("variablesReference");
+
+ IfFailRet(m_debugger->SetVariable(name, value, ref));
+ body["value"] = value;
+
+ return S_OK;
} }
};
--- /dev/null
+/*\r
+Send("1-file-exec-and-symbols dotnet");\r
+Send("2-exec-arguments " + TestBin);\r
+Send("3-exec-run");\r
+\r
+var r = Expect("*stopped");\r
+Assert.Equal("entry-point-hit", r.FindString("reason"));\r
+\r
+Send(String.Format("4-break-insert -f {0}:{1}", TestSource, Lines["BREAK1"]));\r
+Expect("4^done");\r
+\r
+Send(String.Format("5-break-insert -f {0}:{1}", TestSource, Lines["BREAK2"]));\r
+Expect("5^done");\r
+\r
+Send("6-exec-continue");\r
+*/\r
+\r
+using System;\r
+\r
+namespace SetValuesTest\r
+{\r
+ public struct TestStruct1\r
+ {\r
+ public int val1;\r
+ public byte val2;\r
+\r
+ public TestStruct1(int v1, byte v2)\r
+ {\r
+ val1 = v1;\r
+ val2 = v2;\r
+ }\r
+ }\r
+\r
+ public struct TestStruct2\r
+ {\r
+ public int val1;\r
+ public TestStruct1 struct2;\r
+\r
+ public TestStruct2(int v1, int v2, byte v3)\r
+ {\r
+ val1 = v1;\r
+ struct2.val1 = v2;\r
+ struct2.val2 = v3;\r
+ }\r
+ }\r
+\r
+ class Program\r
+ {\r
+ static void Main(string[] args)\r
+ { // //@START@\r
+ TestStruct2 ts = new TestStruct2(1, 5, 10); \r
+\r
+ bool testBool = false;\r
+ char testChar = 'ㅎ';\r
+ byte testByte = (byte)10;\r
+ sbyte testSByte = (sbyte)-100;\r
+ short testShort = (short)-500;\r
+ ushort testUShort = (ushort)500;\r
+ int testInt = -999999;\r
+ uint testUInt = 999999;\r
+ long testLong = -999999999;\r
+ ulong testULong = 9999999999;\r
+\r
+ decimal b = 0000001.000000000000000000000000006M;\r
+ int[] arrs = decimal.GetBits(b);\r
+ string testString = "someNewString that I'll test with";\r
+\r
+ int dummy1 = 1; // //@BREAK1@\r
+/*\r
+r = Expect("*stopped");\r
+Assert.Equal("breakpoint-hit", r.FindString("reason"));\r
+Assert.Equal(Lines["BREAK1"], r.Find("frame").FindInt("line"));\r
+\r
+Send(String.Format("8-var-create - * \"{0}\"", "ts.struct2.val1"));\r
+r = Expect("8^done");\r
+string val1 = r.FindString("name");\r
+Send(String.Format("9-var-assign {0} \"666\"", val1));\r
+r = Expect("9^done");\r
+\r
+Send(String.Format("10-var-create - * \"{0}\"", "testBool"));\r
+r = Expect("10^done");\r
+string testBool = r.FindString("name");\r
+Send(String.Format("11-var-assign {0} \"true\"", testBool));\r
+r = Expect("11^done");\r
+\r
+Send(String.Format("12-var-create - * \"{0}\"", "testChar"));\r
+r = Expect("12^done");\r
+string testChar = r.FindString("name");\r
+Send(String.Format("13-var-assign {0} \"a\"", testChar));\r
+r = Expect("13^done");\r
+\r
+Send(String.Format("14-var-create - * \"{0}\"", "testByte"));\r
+r = Expect("14^done");\r
+string testByte = r.FindString("name");\r
+Send(String.Format("15-var-assign {0} \"200\"", testByte));\r
+r = Expect("15^done");\r
+\r
+Send(String.Format("16-var-create - * \"{0}\"", "testSByte"));\r
+r = Expect("16^done");\r
+string testSByte = r.FindString("name");\r
+Send(String.Format("17-var-assign {0} \"-1\"", testSByte));\r
+r = Expect("17^done");\r
+\r
+Send(String.Format("18-var-create - * \"{0}\"", "testShort"));\r
+r = Expect("18^done");\r
+string testShort = r.FindString("name");\r
+Send(String.Format("19-var-assign {0} \"-666\"", testShort));\r
+r = Expect("19^done");\r
+\r
+Send(String.Format("20-var-create - * \"{0}\"", "testUShort"));\r
+r = Expect("20^done");\r
+string testUShort = r.FindString("name");\r
+Send(String.Format("21-var-assign {0} \"666\"", testUShort));\r
+r = Expect("21^done");\r
+\r
+Send(String.Format("22-var-create - * \"{0}\"", "testInt"));\r
+r = Expect("22^done");\r
+string testInt = r.FindString("name");\r
+Send(String.Format("23-var-assign {0} \"666666\"", testInt));\r
+r = Expect("23^done");\r
+\r
+Send(String.Format("24-var-create - * \"{0}\"", "testUInt"));\r
+r = Expect("24^done");\r
+string testUInt = r.FindString("name");\r
+Send(String.Format("25-var-assign {0} \"666666\"", testUInt));\r
+r = Expect("25^done");\r
+\r
+Send(String.Format("26-var-create - * \"{0}\"", "testLong"));\r
+r = Expect("26^done");\r
+string testLong = r.FindString("name");\r
+Send(String.Format("27-var-assign {0} \"-666666666\"", testLong));\r
+r = Expect("27^done");\r
+\r
+Send(String.Format("28-var-create - * \"{0}\"", "testULong"));\r
+r = Expect("28^done");\r
+string testULong = r.FindString("name");\r
+Send(String.Format("29-var-assign {0} \"666666666\"", testULong));\r
+r = Expect("29^done");\r
+\r
+Send(String.Format("30-var-create - * \"{0}\"", "b"));\r
+r = Expect("30^done");\r
+string b = r.FindString("name");\r
+Send(String.Format("31-var-assign {0} \"-1.000000000000000000000017\"", b));\r
+r = Expect("31^done");\r
+\r
+Send(String.Format("32-var-create - * \"{0}\"", "testString"));\r
+r = Expect("32^done");\r
+string testString = r.FindString("name");\r
+Send(String.Format("33-var-assign {0} \"edited string\"", testString));\r
+r = Expect("33^done");\r
+Send("34-exec-continue");\r
+*/\r
+ int dummy2 = 2; // //@BREAK2@\r
+/*\r
+r = Expect("*stopped");\r
+Assert.Equal("breakpoint-hit", r.FindString("reason"));\r
+Assert.Equal(Lines["BREAK2"], r.Find("frame").FindInt("line"));\r
+\r
+Send(String.Format("35-var-create - * \"{0}\"", "ts.struct2"));\r
+r = Expect("35^done");\r
+string struct2 = r.FindString("name");\r
+Send(String.Format("36-var-list-children --simple-values \"{0}\"", struct2));\r
+r = Expect("36^done");\r
+string val = r.Find("children").Find("child").FindString("value");\r
+Assert.Equal("666", val);\r
+\r
+\r
+\r
+\r
+\r
+\r
+Send(String.Format("37-var-create - * \"{0}\"", "testBool"));\r
+r = Expect("37^done");\r
+Assert.Equal(r.FindString("value"), "true");\r
+\r
+Send(String.Format("38-var-create - * \"{0}\"", "testChar"));\r
+r = Expect("38^done");\r
+Assert.Equal(r.FindString("value"), "97 \'a\'");\r
+\r
+Send(String.Format("39-var-create - * \"{0}\"", "testByte"));\r
+r = Expect("39^done");\r
+Assert.Equal(r.FindString("value"), "200");\r
+\r
+Send(String.Format("40-var-create - * \"{0}\"", "testSByte"));\r
+r = Expect("40^done");\r
+Assert.Equal(r.FindString("value"), "-1");\r
+\r
+Send(String.Format("41-var-create - * \"{0}\"", "testShort"));\r
+r = Expect("41^done");\r
+Assert.Equal(r.FindString("value"), "-666");\r
+\r
+Send(String.Format("42-var-create - * \"{0}\"", "testUShort"));\r
+r = Expect("42^done");\r
+Assert.Equal(r.FindString("value"), "666");\r
+\r
+Send(String.Format("43-var-create - * \"{0}\"", "testInt"));\r
+r = Expect("43^done");\r
+Assert.Equal(r.FindString("value"), "666666");\r
+\r
+Send(String.Format("44-var-create - * \"{0}\"", "testUInt"));\r
+r = Expect("44^done");\r
+Assert.Equal(r.FindString("value"), "666666");\r
+\r
+Send(String.Format("45-var-create - * \"{0}\"", "testLong"));\r
+r = Expect("45^done");\r
+Assert.Equal(r.FindString("value"), "-666666666");\r
+\r
+Send(String.Format("46-var-create - * \"{0}\"", "testULong"));\r
+r = Expect("46^done");\r
+Assert.Equal(r.FindString("value"), "666666666");\r
+\r
+Send(String.Format("47-var-create - * \"{0}\"", "b"));\r
+r = Expect("47^done");\r
+Assert.Equal(r.FindString("value"), "-1.000000000000000000000017");\r
+\r
+Send(String.Format("48-var-create - * \"{0}\"", "testString"));\r
+r = Expect("48^done");\r
+Assert.Equal(r.FindString("value"), "\"edited string\"");\r
+\r
+*/\r
+ }\r
+ }\r
+}\r
+/*\r
+Send("49-exec-continue");\r
+r = Expect("*stopped");\r
+Assert.Equal("exited", r.FindString("reason"));\r
+*/\r
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">\r
+\r
+ <PropertyGroup>\r
+ <OutputType>Exe</OutputType>\r
+ <TargetFramework>netcoreapp2.0</TargetFramework>\r
+ </PropertyGroup>\r
+\r
+</Project>\r
[Fact]
public void BreakpointAddRemoveTest() => ExecuteTest();
+ [Fact]
+ public void SetValuesTest() => ExecuteTest();
+
private const int DefaultTimeoutSec = 20;
private int expectTimeoutSec;
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BreakpointAddRemoveTest", "BreakpointAddRemoveTest\BreakpointAddRemoveTest.csproj", "{48403D8A-08D8-48FC-BF1B-F0239154AD85}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SetValuesTest", "SetValuesTest\SetValuesTest.csproj", "{9E6CD7E6-0B84-4088-9097-1839A14B49DB}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
{48403D8A-08D8-48FC-BF1B-F0239154AD85}.Release|x64.Build.0 = Release|x64
{48403D8A-08D8-48FC-BF1B-F0239154AD85}.Release|x86.ActiveCfg = Release|x86
{48403D8A-08D8-48FC-BF1B-F0239154AD85}.Release|x86.Build.0 = Release|x86
+ {9E6CD7E6-0B84-4088-9097-1839A14B49DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9E6CD7E6-0B84-4088-9097-1839A14B49DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9E6CD7E6-0B84-4088-9097-1839A14B49DB}.Debug|x64.ActiveCfg = Debug|x64
+ {9E6CD7E6-0B84-4088-9097-1839A14B49DB}.Debug|x64.Build.0 = Debug|x64
+ {9E6CD7E6-0B84-4088-9097-1839A14B49DB}.Debug|x86.ActiveCfg = Debug|x86
+ {9E6CD7E6-0B84-4088-9097-1839A14B49DB}.Debug|x86.Build.0 = Debug|x86
+ {9E6CD7E6-0B84-4088-9097-1839A14B49DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9E6CD7E6-0B84-4088-9097-1839A14B49DB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9E6CD7E6-0B84-4088-9097-1839A14B49DB}.Release|x64.ActiveCfg = Release|x64
+ {9E6CD7E6-0B84-4088-9097-1839A14B49DB}.Release|x64.Build.0 = Release|x64
+ {9E6CD7E6-0B84-4088-9097-1839A14B49DB}.Release|x86.ActiveCfg = Release|x86
+ {9E6CD7E6-0B84-4088-9097-1839A14B49DB}.Release|x86.Build.0 = Release|x86
EndGlobalSection
EndGlobal