Fix Set Value routine in MI/GDB and VSCode protocols.
authorMikhail Kurinnoi <m.kurinnoi@samsung.com>
Fri, 12 Nov 2021 14:08:19 +0000 (06:08 -0800)
committerAlexander Soldatov/Platform Lab /SRR/Staff Engineer/Samsung Electronics <soldatov.a@samsung.com>
Thu, 18 Nov 2021 15:36:33 +0000 (18:36 +0300)
- Add VSCode protocol setExpression command implementation.
- Add MI/GDB protocol var-evaluate-expression command implementation.
- Fix MI/GDB protocol "editable/noneditable" attribute in var-create, var-show-attributes and var-list-children replies.
- Add MI/GDB and VSCode protocols tests in order to cover all changes and fixed.

16 files changed:
src/debugger/evalstackmachine.cpp
src/debugger/evalstackmachine.h
src/debugger/evaluator.cpp
src/debugger/manageddebugger.cpp
src/debugger/manageddebugger.h
src/debugger/variables.cpp
src/debugger/variables.h
src/interfaces/idebugger.h
src/interfaces/types.h
src/protocols/miprotocol.cpp
src/protocols/miprotocol.h
src/protocols/vscodeprotocol.cpp
test-suite/MITestVariables/Program.cs
test-suite/NetcoreDbgTest/VSCode/VSCodeProtocolRequest.cs
test-suite/NetcoreDbgTest/VSCode/VSCodeProtocolResponse.cs
test-suite/VSCodeTestVariables/Program.cs

index 7052df49af512f57b49cf9ec3013ac0240866c1f..b545561e70c16bda87bb509babc6eda1d9564099 100644 (file)
@@ -981,7 +981,7 @@ namespace
         HRESULT Status;
         ToRelease<ICorDebugValue> iCorValue;
         IfFailRet(GetFrontStackEntryValue(&iCorValue, evalStack, ed, output));
-        evalStack.front().ResetEntry(true); // Don't reset literal status.
+        evalStack.front().ResetEntry(EvalStackEntry::ResetLiteralStatus::No);
         ToRelease<ICorDebugValue> iCorRealValue;
         CorElementType elemType;
         IfFailRet(GetRealValueWithType(iCorValue, &iCorRealValue, &elemType));
@@ -1043,6 +1043,7 @@ namespace
 
         evalStack.emplace_front();
         evalStack.front().identifiers.emplace_back(std::move(String));
+        evalStack.front().editable = true;
         return S_OK;
     }
 
@@ -1217,7 +1218,8 @@ namespace
         ToRelease<ICorDebugValue> iCorArrayValue;
         IfFailRet(GetFrontStackEntryValue(&iCorArrayValue, evalStack, ed, output));
 
-        evalStack.front().ResetEntry();
+        evalStack.front().iCorValue.Free();
+        evalStack.front().identifiers.clear();
         return ed.pEvaluator->GetElement(iCorArrayValue, indexes, &evalStack.front().iCorValue);
     }
 
@@ -1246,7 +1248,8 @@ namespace
             return S_OK;
         }
 
-        evalStack.front().ResetEntry();
+        evalStack.front().iCorValue.Free();
+        evalStack.front().identifiers.clear();
         return ed.pEvaluator->GetElement(iCorArrayValue, indexes, &evalStack.front().iCorValue);
     }
 
@@ -1647,13 +1650,14 @@ namespace
     {
         evalStack.emplace_front();
         evalStack.front().identifiers.emplace_back("this");
+        evalStack.front().editable = true;
         return S_OK;
     }
 
 } // unnamed namespace
 
 HRESULT EvalStackMachine::Run(ICorDebugThread *pThread, FrameLevel frameLevel, int evalFlags, const std::string &expression,
-                              ICorDebugValue **ppResultValue, std::string &output)
+                              ICorDebugValue **ppResultValue, bool *editable, std::string &output)
 {
     static const std::vector<std::function<HRESULT(std::list<EvalStackEntry>&, PVOID, std::string&, EvalData&)>> CommandImplementation = {
         IdentifierName,
@@ -1743,6 +1747,9 @@ HRESULT EvalStackMachine::Run(ICorDebugThread *pThread, FrameLevel frameLevel, i
 
         assert(m_evalStack.size() == 1);
 
+        if (editable)
+            *editable = m_evalStack.front().editable;
+
         if (*ppResultValue == nullptr)
         {
             Status = GetFrontStackEntryValue(ppResultValue, m_evalStack, m_evalData, output);
index 78a31f126e550a5d050379293c7dabd511d9c3f9..0755d7795a24fe93bb952e6c791ad563f60362f0 100644 (file)
@@ -24,6 +24,12 @@ class EvalWaiter;
 \r
 struct EvalStackEntry\r
 {\r
+    enum class ResetLiteralStatus\r
+    {\r
+        No  = 0,\r
+        Yes = 1\r
+    };\r
+\r
     // Unresolved identifiers.\r
     // Note, in case we already have some resolved identifiers (iCorValue), unresolved identifiers must be resolved within iCorValue.\r
     std::vector<std::string> identifiers;\r
@@ -36,18 +42,21 @@ struct EvalStackEntry
     bool preventBinding;\r
     // This is literal entry (value was created from literal).\r
     bool literal;\r
+    // This entry is real variable (not literal, not result of expression calculation, not result of function call, ...).\r
+    bool editable;\r
 \r
-    EvalStackEntry() : preventBinding(false), literal(false)\r
+    EvalStackEntry() : preventBinding(false), literal(false), editable(false)\r
     {}\r
 \r
-    void ResetEntry(bool skipLiteral = false)\r
+    void ResetEntry(ResetLiteralStatus resetLiteral = ResetLiteralStatus::Yes)\r
     {\r
         identifiers.clear();\r
         iCorValue.Free();\r
         iCorValuePredefined.Free();\r
         preventBinding = false;\r
-        if (!skipLiteral)\r
+        if (resetLiteral == ResetLiteralStatus::Yes)\r
             literal = false;\r
+        editable = false;\r
     }\r
 };\r
 \r
@@ -93,7 +102,7 @@ public:
 \r
     // Run stack machine for particular expression.\r
     HRESULT Run(ICorDebugThread *pThread, FrameLevel frameLevel, int evalFlags, const std::string &expression,\r
-                ICorDebugValue **ppResultValue, std::string &output);\r
+                ICorDebugValue **ppResultValue, bool *editable, std::string &output);\r
 \r
     // Find ICorDebugClass objects for all predefined types we need for stack machine during Private.CoreLib load.\r
     // See ManagedCallback::LoadModule().\r
index 6addfa3bc6119ce7432c99a5398330f3323a2a3a..d44333088184102384906d5e10f6d3fb2546f775 100644 (file)
@@ -641,7 +641,7 @@ HRESULT Evaluator::WalkMembers(
 
             ToRelease<ICorDebugValue> iCorValue;
             IfFailRet(getValue(&iCorValue, evalFlags));
-            return m_sharedEvalStackMachine->Run(pThread, frameLevel, evalFlags, value, iCorValue.GetRef(), output);
+            return m_sharedEvalStackMachine->Run(pThread, frameLevel, evalFlags, value, iCorValue.GetRef(), nullptr, output);
         };
 
         return cb(nullptr, false, "", getValue, setValue);
@@ -681,7 +681,7 @@ HRESULT Evaluator::WalkMembers(
 
                 ToRelease<ICorDebugValue> iCorValue;
                 IfFailRet(getValue(&iCorValue, evalFlags));
-                return m_sharedEvalStackMachine->Run(pThread, frameLevel, evalFlags, value, iCorValue.GetRef(), output);
+                return m_sharedEvalStackMachine->Run(pThread, frameLevel, evalFlags, value, iCorValue.GetRef(), nullptr, output);
             };
 
             IfFailRet(cb(nullptr, false, "[" + IndiciesToStr(ind, base) + "]", getValue, setValue));
@@ -790,7 +790,7 @@ HRESULT Evaluator::WalkMembers(
 
                 ToRelease<ICorDebugValue> iCorValue;
                 IfFailRet(getValue(&iCorValue, evalFlags));
-                return m_sharedEvalStackMachine->Run(pThread, frameLevel, evalFlags, value, iCorValue.GetRef(), output);
+                return m_sharedEvalStackMachine->Run(pThread, frameLevel, evalFlags, value, iCorValue.GetRef(), nullptr, output);
             };
 
             IfFailRet(cb(pType, is_static, name, getValue, setValue));
@@ -896,7 +896,7 @@ HRESULT Evaluator::WalkMembers(
                 {
                     // FIXME investigate, why in this case we can't use ICorDebugReferenceValue::SetValue() for string in iCorValue
                     iCorValue.Free();
-                    IfFailRet(m_sharedEvalStackMachine->Run(pThread, frameLevel, evalFlags, value, &iCorValue, output));
+                    IfFailRet(m_sharedEvalStackMachine->Run(pThread, frameLevel, evalFlags, value, &iCorValue, nullptr, output));
 
                     CorElementType elemType;
                     IfFailRet(iCorValue->GetType(&elemType));
@@ -905,7 +905,7 @@ HRESULT Evaluator::WalkMembers(
                 }
                 else // Allow stack machine decide what types are supported.
                 {
-                    IfFailRet(m_sharedEvalStackMachine->Run(pThread, frameLevel, evalFlags, value, iCorValue.GetRef(), output));
+                    IfFailRet(m_sharedEvalStackMachine->Run(pThread, frameLevel, evalFlags, value, iCorValue.GetRef(), nullptr, output));
                 }
 
                 // Call setter.
index eff637e549e7355e595f2ea990cb89e5e5415344..8e996c3a5971435978cbc587961ecf608aa129ca 100644 (file)
@@ -1002,7 +1002,8 @@ HRESULT ManagedDebugger::SetVariable(const std::string &name, const std::string
 
 HRESULT ManagedDebugger::SetVariableByExpression(
     FrameId frameId,
-    const Variable &variable,
+    const std::string &evaluateName,
+    int evalFlags,
     const std::string &value,
     std::string &output)
 {
@@ -1011,8 +1012,8 @@ HRESULT ManagedDebugger::SetVariableByExpression(
     HRESULT Status;
     ToRelease<ICorDebugValue> pResultValue;
 
-    IfFailRet(m_sharedVariables->GetValueByExpression(m_iCorProcess, frameId, variable, &pResultValue));
-    return m_sharedVariables->SetVariable(m_iCorProcess, pResultValue, value, frameId, variable.evalFlags, output);
+    IfFailRet(m_sharedVariables->GetValueByExpression(m_iCorProcess, frameId, evaluateName, evalFlags, &pResultValue));
+    return m_sharedVariables->SetVariable(m_iCorProcess, pResultValue, value, frameId, evalFlags, output);
 }
 
 
index d8ee7c757b114dcfe824d7566f1fafcee8b237cd..e283dfafa1823e997baf4bdf8ff1bee0e385174b 100644 (file)
@@ -155,7 +155,7 @@ public:
     int GetNamedVariables(uint32_t variablesReference) override;
     HRESULT Evaluate(FrameId frameId, const std::string &expression, Variable &variable, std::string &output) override;
     HRESULT SetVariable(const std::string &name, const std::string &value, uint32_t ref, std::string &output) override;
-    HRESULT SetVariableByExpression(FrameId frameId, const Variable &variable, const std::string &value, std::string &output) override;
+    HRESULT SetVariableByExpression(FrameId frameId, const std::string &evaluateName, int evalFlags, const std::string &value, std::string &output) override;
     HRESULT GetExceptionInfo(ThreadId threadId, ExceptionInfo &exceptionInfo) override;
     HRESULT GetSourceFile(const std::string &sourcePath, char** fileBuf, int* fileLen) override;
     void FreeUnmanaged(PVOID mem) override;
index 46592fc66b50c22e4b54f5210b0a36bbf89c4d2e..885a37c0e2c6d7028a4321a5d80cc246e211357f 100644 (file)
@@ -418,7 +418,7 @@ HRESULT Variables::Evaluate(
 
     ToRelease<ICorDebugValue> pResultValue;
     FrameLevel frameLevel = frameId.getLevel();
-    IfFailRet(m_sharedEvalStackMachine->Run(pThread, frameLevel, variable.evalFlags, expression, &pResultValue, output));
+    IfFailRet(m_sharedEvalStackMachine->Run(pThread, frameLevel, variable.evalFlags, expression, &pResultValue, &variable.editable, output));
 
     variable.evaluateName = expression;
     IfFailRet(PrintValue(pResultValue, variable.value));
@@ -477,7 +477,7 @@ HRESULT Variables::SetStackVariable(
 
         ToRelease<ICorDebugValue> iCorValue;
         IfFailRet(getValue(&iCorValue, ref.evalFlags));
-        IfFailRet(m_sharedEvalStackMachine->Run(pThread, ref.frameId.getLevel(), ref.evalFlags, value, iCorValue.GetRef(), output));
+        IfFailRet(m_sharedEvalStackMachine->Run(pThread, ref.frameId.getLevel(), ref.evalFlags, value, iCorValue.GetRef(), nullptr, output));
         IfFailRet(PrintValue(iCorValue, output));
         return E_ABORT; // Fast exit from cycle.
     })) && Status != E_ABORT)
@@ -524,7 +524,7 @@ HRESULT Variables::SetChild(
 }
 
 HRESULT Variables::GetValueByExpression(ICorDebugProcess *pProcess, FrameId frameId,
-                                        const Variable &variable, ICorDebugValue **ppResult)
+                                        const std::string &evaluateName, int evalFlags, ICorDebugValue **ppResult)
 {
     if (pProcess == nullptr)
         return E_FAIL;
@@ -542,7 +542,7 @@ HRESULT Variables::GetValueByExpression(ICorDebugProcess *pProcess, FrameId fram
     // All "set value" code must be refactored in order to remove dependency from Roslyn.
 
     std::string output;
-    return m_sharedEvalStackMachine->Run(pThread, frameId.getLevel(), variable.evalFlags, variable.evaluateName, ppResult, output);
+    return m_sharedEvalStackMachine->Run(pThread, frameId.getLevel(), evalFlags, evaluateName, ppResult, nullptr, output);
 }
 
 HRESULT Variables::SetVariable(
@@ -565,7 +565,7 @@ HRESULT Variables::SetVariable(
     ToRelease<ICorDebugThread> pThread;
     IfFailRet(pProcess->GetThread(int(threadId), &pThread));
 
-    IfFailRet(m_sharedEvalStackMachine->Run(pThread, frameId.getLevel(), evalFlags, value, &pVariable, output));
+    IfFailRet(m_sharedEvalStackMachine->Run(pThread, frameId.getLevel(), evalFlags, value, &pVariable, nullptr, output));
     IfFailRet(PrintValue(pVariable, output));
     return S_OK;
 }
index 0f79748f52d52c647cbe0d197781e8a9fd580df4..e4aa97781de0416d6578bb6ae8fe75f045735fd9 100644 (file)
@@ -176,7 +176,8 @@ public:
     HRESULT GetValueByExpression(
         ICorDebugProcess *pProcess,
         FrameId frameId,
-        const Variable &variable,
+        const std::string &evaluateName,
+        int evalFlags,
         ICorDebugValue **ppResult);
 
     HRESULT GetExceptionVariable(
index c41b9d9e5e73eab598b96794e14444bc838dfc29..ae233681ece7a677a2ec168470f12e2ab1ecb588 100644 (file)
@@ -96,7 +96,7 @@ public:
     virtual int GetNamedVariables(uint32_t variablesReference) = 0;
     virtual HRESULT Evaluate(FrameId frameId, const std::string &expression, Variable &variable, std::string &output) = 0;
     virtual HRESULT SetVariable(const std::string &name, const std::string &value, uint32_t ref, std::string &output) = 0;
-    virtual HRESULT SetVariableByExpression(FrameId frameId, const Variable &variable, const std::string &value, std::string &output) = 0;
+    virtual HRESULT SetVariableByExpression(FrameId frameId, const std::string &evaluateName, int evalFlags, const std::string &value, std::string &output) = 0;
     virtual HRESULT GetExceptionInfo(ThreadId threadId, ExceptionInfo &exceptionInfo) = 0;
     virtual HRESULT GetSourceFile(const std::string &sourcePath, char** fileBuf, int* fileLen) = 0;
     virtual void FreeUnmanaged(PVOID mem) = 0;
index 4f24fafe4bb3e59afeab9885e3d208320463937c..051d648b0ec76d2d10f11de311f571b2ed1c9312 100644 (file)
@@ -388,8 +388,9 @@ struct Variable
     int namedVariables;
     int indexedVariables;
     int evalFlags;
+    bool editable;
 
-    Variable(int flags = defaultEvalFlags) : variablesReference(0), namedVariables(0), indexedVariables(0), evalFlags(flags) {}
+    Variable(int flags = defaultEvalFlags) : variablesReference(0), namedVariables(0), indexedVariables(0), evalFlags(flags), editable(false) {}
 };
 
 enum VariablesFilter
index 4dbd76ad9f850d4b24fda5fcebeaef60cc330330..b8dd1c677e9957fe04f1d2f754c11c5950702464 100644 (file)
@@ -198,40 +198,31 @@ static HRESULT PrintVariables(const std::vector<Variable> &variables, std::strin
     return S_OK;
 }
 
-static bool IsEditable(const std::string &type)
-{
-    static std::unordered_set<std::string> editableTypes{
-        "int", "bool", "char", "byte", "sbyte", "short", "ushort", "uint", "long", "ulong", "decimal", "string"};
-
-    return editableTypes.find(type) != editableTypes.end();
-}
-
 static void PrintVar(const std::string &varobjName, Variable &v, ThreadId threadId, int print_values, std::string &output)
 {
     std::ostringstream ss;
 
-    std::string editable;
-    if (IsEditable(v.type))
-        editable = "editable";
+    std::string attributes;
+    if (v.editable)
+        attributes = "editable";
     else
-        editable = "noneditable";
+        attributes = "noneditable";
 
     ss << "name=\"" << varobjName << "\",";
     if (print_values)
     {
         ss << "value=\"" << MIProtocol::EscapeMIValue(v.value) << "\",";
     }
-    ss << "attributes=\"" << editable << "\",";
+    ss << "attributes=\"" << attributes << "\",";
     ss << "exp=\"" << MIProtocol::EscapeMIValue(v.name.empty() ? v.evaluateName : v.name) << "\",";
     ss << "numchild=\"" << v.namedVariables << "\",";
     ss << "type=\"" << v.type << "\",";
     ss << "thread-id=\"" << int(threadId) << "\"";
-    //,has_more="0"}
 
     output = ss.str();
 }
 
-HRESULT MIProtocol::PrintNewVar(const std::string& varobjName, Variable &v, ThreadId threadId, int print_values, std::string &output)
+HRESULT MIProtocol::PrintNewVar(const std::string& varobjName, Variable &v, ThreadId threadId, FrameLevel level, int print_values, std::string &output)
 {
     if (m_vars.size() == std::numeric_limits<unsigned>::max())
         return E_FAIL;
@@ -246,7 +237,7 @@ HRESULT MIProtocol::PrintNewVar(const std::string& varobjName, Variable &v, Thre
         name = varobjName;
     }
 
-    m_vars[name] = v;
+    m_vars[name] = MIVariable{v, threadId, level};
 
     PrintVar(name, v, threadId, print_values, output);
 
@@ -262,7 +253,7 @@ HRESULT MIProtocol::CreateVar(ThreadId threadId, FrameLevel level, int evalFlags
     IfFailRet(m_sharedDebugger->Evaluate(frameId, expression, variable, output));
 
     int print_values = 1;
-    return PrintNewVar(varobjName, variable, threadId, print_values, output);
+    return PrintNewVar(varobjName, variable, threadId, level, print_values, output);
 }
 
 HRESULT MIProtocol::DeleteVar(const std::string &varobjName)
@@ -280,7 +271,7 @@ HRESULT MIProtocol::DeleteVar(const std::string &varobjName)
     return S_OK;
 }
 
-HRESULT MIProtocol::FindVar(const std::string &varobjName, Variable &variable)
+HRESULT MIProtocol::FindVar(const std::string &varobjName, MIVariable &variable)
 {
     auto it = m_vars.find(varobjName);
     if (it == m_vars.end())
@@ -299,7 +290,7 @@ void MIProtocol::Cleanup()
     m_exceptionBreakpoints.clear();
 }
 
-HRESULT MIProtocol::PrintChildren(std::vector<Variable> &children, ThreadId threadId, int print_values, bool has_more, std::string &output)
+HRESULT MIProtocol::PrintChildren(std::vector<Variable> &children, ThreadId threadId, FrameLevel level, int print_values, bool has_more, std::string &output)
 {
     HRESULT Status;
     std::ostringstream ss;
@@ -317,7 +308,7 @@ HRESULT MIProtocol::PrintChildren(std::vector<Variable> &children, ThreadId thre
     {
         std::string varout;
         std::string minus("-");
-        IfFailRet(PrintNewVar(minus, child, threadId, print_values, varout));
+        IfFailRet(PrintNewVar(minus, child, threadId, level, print_values, varout));
 
         ss << sep;
         sep = ",";
@@ -331,29 +322,24 @@ HRESULT MIProtocol::PrintChildren(std::vector<Variable> &children, ThreadId thre
     return S_OK;
 }
 
-HRESULT MIProtocol::ListChildren(ThreadId threadId, FrameLevel level, int childStart, int childEnd, const std::string &varName, int print_values, std::string &output)
+HRESULT MIProtocol::ListChildren(int childStart, int childEnd, const MIVariable &miVariable, int print_values, std::string &output)
 {
     HRESULT Status;
-
-    StackFrame stackFrame(threadId, level, "");
-
     std::vector<Variable> variables;
 
-    auto it = m_vars.find(varName);
-    if (it == m_vars.end())
-        return E_FAIL;
-
-    uint32_t variablesReference = it->second.variablesReference;
-
     bool has_more = false;
 
-    if (variablesReference > 0)
+    if (miVariable.variable.variablesReference > 0)
     {
-        IfFailRet(m_sharedDebugger->GetVariables(variablesReference, VariablesNamed, childStart, childEnd - childStart, variables));
-        has_more = childEnd < m_sharedDebugger->GetNamedVariables(variablesReference);
+        IfFailRet(m_sharedDebugger->GetVariables(miVariable.variable.variablesReference, VariablesNamed, childStart, childEnd - childStart, variables));
+        has_more = childEnd < m_sharedDebugger->GetNamedVariables(miVariable.variable.variablesReference);
+        for (auto &child : variables)
+        {
+            child.editable = miVariable.variable.editable;
+        }
     }
 
-    return PrintChildren(variables, threadId, print_values, has_more, output);
+    return PrintChildren(variables, miVariable.threadId, miVariable.level, print_values, has_more, output);
 }
 
 HRESULT MIProtocol::SetLineBreakpoint(
@@ -956,16 +942,16 @@ HRESULT MIProtocol::HandleCommand(const std::string& command, const std::vector<
             return E_FAIL;
         }
 
-        ThreadId threadId{ ProtocolUtils::GetIntArg(args, "--thread", int(m_sharedDebugger->GetLastStoppedThreadId())) };
-        FrameLevel level{ ProtocolUtils::GetIntArg(args, "--frame", 0) };
-
         int childStart = 0;
         int childEnd = INT_MAX;
         ProtocolUtils::StripArgs(args);
         ProtocolUtils::GetIndices(args, childStart, childEnd);
         std::string varName = args.at(0);
+        HRESULT Status;
+        MIVariable miVariable;
+        IfFailRet(FindVar(varName, miVariable));
 
-        return ListChildren(threadId, level, childStart, childEnd, varName, print_values, output);
+        return ListChildren(childStart, childEnd, miVariable, print_values, output);
     }},
     { "var-delete", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
         if (args.size() < 1)
@@ -1048,17 +1034,17 @@ HRESULT MIProtocol::HandleCommand(const std::string& command, const std::vector<
     }},
     { "var-show-attributes", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
         HRESULT Status;
-        Variable variable;
+        MIVariable miVariable;
         std::string varName = args.at(0);
-        std::string editable;
+        std::string attributes;
 
-        IfFailRet(FindVar(varName, variable));
-        if (IsEditable(variable.type))
-            editable = "editable";
+        IfFailRet(FindVar(varName, miVariable));
+        if (miVariable.variable.editable)
+            attributes = "editable";
         else
-            editable = "noneditable";
+            attributes = "noneditable";
 
-        output = "status=\"" + editable + "\"";
+        output = "status=\"" + attributes + "\"";
         return S_OK;
     }},
     { "var-assign", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
@@ -1076,19 +1062,36 @@ HRESULT MIProtocol::HandleCommand(const std::string& command, const std::vector<
         if (varExpr.size() >= 2 && varExpr.front() == '"' && varExpr.back() == '"')
             varExpr = varExpr.substr(1, varExpr.size() - 2);
 
-        ThreadId threadId{ ProtocolUtils::GetIntArg(args, "--thread", int(m_sharedDebugger->GetLastStoppedThreadId())) };
-        FrameLevel level{ ProtocolUtils::GetIntArg(args, "--frame", 0) };
-        FrameId frameId(threadId, level);
+        MIVariable miVariable;
+        IfFailRet(FindVar(varName, miVariable));
 
-        Variable variable;
-        IfFailRet(FindVar(varName, variable));
-
-        IfFailRet(m_sharedDebugger->SetVariableByExpression(frameId, variable, varExpr, output));
+        FrameId frameId(miVariable.threadId, miVariable.level);
+        IfFailRet(m_sharedDebugger->SetVariableByExpression(frameId, miVariable.variable.evaluateName, miVariable.variable.evalFlags, varExpr, output));
 
         output = "value=\"" + MIProtocol::EscapeMIValue(output) + "\"";
 
         return S_OK;
     }},
+    { "var-evaluate-expression", [this](const std::vector<std::string> &args, std::string &output) -> HRESULT {
+        HRESULT Status;
+
+        if (args.size() != 1)
+        {
+            output = "Command requires 1 argument";
+            return E_FAIL;
+        }
+
+        std::string varName = args.at(0);
+
+        MIVariable miVariable;
+        IfFailRet(FindVar(varName, miVariable));
+        FrameId frameId(miVariable.threadId, miVariable.level);
+        Variable variable(miVariable.variable.evalFlags);
+        IfFailRet(m_sharedDebugger->Evaluate(frameId, miVariable.variable.evaluateName, variable, output));
+
+        output = "value=\"" + MIProtocol::EscapeMIValue(variable.value) + "\"";
+        return S_OK;
+    }},
     };
 
     auto command_it = commands.find(command);
index 411fb7b2b50ccb8755654cad9e20a7ebc3837d44..bdfd4d229badd838089d9e26946b632e32b398db 100644 (file)
@@ -55,7 +55,14 @@ private:
     std::string m_fileExec;
     std::vector<std::string> m_execArgs;
 
-    std::unordered_map<std::string, Variable> m_vars;
+    struct MIVariable
+    {
+        Variable variable;
+        ThreadId threadId;
+        FrameLevel level;
+    };
+
+    std::unordered_map<std::string, MIVariable> m_vars;
     std::unordered_map<std::string, std::unordered_map<uint32_t, LineBreakpoint> > m_lineBreakpoints;
     std::unordered_map<uint32_t, FuncBreakpoint> m_funcBreakpoints;
     std::unordered_map<uint32_t, ExceptionBreakpoint> m_exceptionBreakpoints;
@@ -71,10 +78,10 @@ private:
     HRESULT PrintFrames(ThreadId threadId, std::string &output, FrameLevel lowFrame, FrameLevel highFrame);
     HRESULT CreateVar(ThreadId threadId, FrameLevel level, int evalFlags, 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);
-    HRESULT PrintChildren(std::vector<Variable> &children, ThreadId threadId, int print_values, bool has_more, std::string &output);
-    HRESULT PrintNewVar(const std::string& varobjName, Variable &v, ThreadId threadId, int print_values, std::string &output);
-    HRESULT ListChildren(ThreadId threadId, FrameLevel level, int childStart, int childEnd, const std::string &varName, int print_values, std::string &output);
+    HRESULT FindVar(const std::string &varobjName, MIVariable &variable);
+    HRESULT PrintChildren(std::vector<Variable> &children, ThreadId threadId, FrameLevel level, int print_values, bool has_more, std::string &output);
+    HRESULT PrintNewVar(const std::string& varobjName, Variable &v, ThreadId threadId, FrameLevel level, int print_values, std::string &output);
+    HRESULT ListChildren(int childStart, int childEnd, const MIVariable &miVariable, int print_values, std::string &output);
     HRESULT SetLineBreakpoint(const std::string &module, const std::string &filename, int linenum, const std::string &condition, Breakpoint &breakpoints);
     HRESULT SetFuncBreakpoint(const std::string &module, const std::string &funcname, const std::string &params, const std::string &condition, Breakpoint &breakpoint);
     HRESULT SetExceptionBreakpoints(std::vector<ExceptionBreakpoint> &excBreakpoints, std::vector<Breakpoint> &breakpoints);
index 7460e1385570b405fb84802044921240cc05e2b0..47194dbf0db8a64a5ac478eb201c839854bda77c 100644 (file)
@@ -416,6 +416,7 @@ void VSCodeProtocol::AddCapabilitiesTo(json &capabilities)
     capabilities["supportsConditionalBreakpoints"] = true;
     capabilities["supportTerminateDebuggee"] = true;
     capabilities["supportsSetVariable"] = true;
+    capabilities["supportsSetExpression"] = true;
     capabilities["supportsTerminateRequest"] = true;
 
     capabilities["supportsExceptionInfoRequest"] = true;
@@ -701,6 +702,44 @@ HRESULT VSCodeProtocol::HandleCommand(const std::string &command, const json &ar
         }
         return S_OK;
     } },
+    { "setExpression", [this](const json &arguments, json &body){
+        HRESULT Status;
+        std::string expression = arguments.at("expression");
+        std::string value = arguments.at("value");
+        FrameId frameId([&](){
+            auto frameIdIter = arguments.find("frameId");
+            if (frameIdIter == arguments.end())
+            {
+                ThreadId threadId = m_sharedDebugger->GetLastStoppedThreadId();
+                return FrameId{threadId, FrameLevel{0}};
+            }
+            else {
+                return FrameId{int(frameIdIter.value())};
+            }
+        }());
+
+        // NOTE
+        // VSCode don't support evaluation flags, we can't disable implicit function calls during evaluation.
+        // https://github.com/OmniSharp/omnisharp-vscode/issues/3173
+        std::string output;
+        Status = m_sharedDebugger->SetVariableByExpression(frameId, expression, defaultEvalFlags, value, output);
+        if (FAILED(Status))
+        {
+            if (output.empty())
+            {
+                std::stringstream stream;
+                stream << "error: 0x" << std::hex << Status;
+                body["message"] = stream.str();
+            }
+            else
+                body["message"] = output;
+
+            return Status;
+        }
+
+        body["value"] = output;
+        return S_OK;
+    } },
     { "attach", [this](const json &arguments, json &body){
         int processId;
 
index 932d459f86b4e062dd5ae43a20bb86b72691fcf4..79cf3c91c64f2d04efc44b21783f5cffb99411e4 100644 (file)
@@ -37,10 +37,18 @@ namespace NetcoreDbgTest.Script
                          @"__FILE__:__LINE__"+"\n"+caller_trace);
         }
 
+        public void CheckAttributes(string caller_trace, string variable, string expectedAttributes)
+        {
+            var res = MIDebugger.Request(String.Format("-var-create - * \"{0}\"", variable));
+            Assert.Equal(MIResultClass.Done, res.Class, @"__FILE__:__LINE__"+"\n"+caller_trace);
+            Assert.Equal(expectedAttributes, ((MIConst)res["attributes"]).CString, @"__FILE__:__LINE__"+"\n"+caller_trace);
+        }
+
         public void CreateAndAssignVar(string caller_trace, string variable, string val, bool ignoreCheck = false)
         {
             var res = MIDebugger.Request(String.Format("-var-create - * \"{0}\"", variable));
             Assert.Equal(MIResultClass.Done, res.Class, @"__FILE__:__LINE__"+"\n"+caller_trace);
+            Assert.Equal("editable", ((MIConst)res["attributes"]).CString, @"__FILE__:__LINE__"+"\n"+caller_trace);
 
             string internalName = ((MIConst)res["name"]).CString;
 
@@ -82,7 +90,20 @@ namespace NetcoreDbgTest.Script
             var res = MIDebugger.Request(String.Format("-var-create - * \"{0}\"", variable));
             Assert.Equal(MIResultClass.Done, res.Class, @"__FILE__:__LINE__"+"\n"+caller_trace);
             var curValue = ((MIConst)res["value"]).CString;
-            if (((MIConst)res["type"]).CString == "char")
+            var curType = ((MIConst)res["type"]).CString;
+            if (curType == "char")
+            {
+                int foundStr = curValue.IndexOf(" ");
+                if (foundStr >= 0)
+                    curValue = curValue.Remove(foundStr);
+            }
+            Assert.Equal(val, curValue, @"__FILE__:__LINE__"+"\n"+caller_trace);
+
+            string varName = ((MIConst)res["name"]).CString;
+            res = MIDebugger.Request(String.Format("-var-evaluate-expression {0}", varName));
+            Assert.Equal(MIResultClass.Done, res.Class, @"__FILE__:__LINE__"+"\n"+caller_trace);
+            curValue = ((MIConst)res["value"]).CString;
+            if (curType == "char")
             {
                 int foundStr = curValue.IndexOf(" ");
                 if (foundStr >= 0)
@@ -92,7 +113,7 @@ namespace NetcoreDbgTest.Script
         }
 
         public void GetAndCheckChildValue(string caller_trace, string ExpectedResult, string variable,
-                                          int childIndex, bool setEvalFlags, enum_EVALFLAGS evalFlags)
+                                          int childIndex, bool setEvalFlags, enum_EVALFLAGS evalFlags, string expectedAttributes = "editable")
         {
             var res = MIDebugger.Request(String.Format("-var-create - * \"{0}\"", variable) +
                                          (setEvalFlags ? (" --evalFlags " + (int)evalFlags) : "" ));
@@ -105,9 +126,21 @@ namespace NetcoreDbgTest.Script
             Assert.Equal(MIResultClass.Done, res.Class, @"__FILE__:__LINE__"+"\n"+caller_trace);
 
             var children = (MIList)res["children"];
-            var child =  (MITuple)((MIResult)children[childIndex]).Value;
+            var child = (MITuple)((MIResult)children[childIndex]).Value;
+            Assert.Equal(expectedAttributes, ((MIConst)child["attributes"]).CString, @"__FILE__:__LINE__"+"\n"+caller_trace);
 
             Assert.Equal(ExpectedResult, ((MIConst)child["value"]).CString, @"__FILE__:__LINE__"+"\n"+caller_trace);
+
+            string varName = ((MIConst)child["name"]).CString;
+            res = MIDebugger.Request(String.Format("-var-evaluate-expression {0}", varName));
+            if (ExpectedResult == "<error>")
+            {
+                Assert.Equal(MIResultClass.Error, res.Class, @"__FILE__:__LINE__"+"\n"+caller_trace);
+                return;
+            }
+            Assert.Equal(MIResultClass.Done, res.Class, @"__FILE__:__LINE__"+"\n"+caller_trace);
+
+            Assert.Equal(ExpectedResult, ((MIConst)res["value"]).CString, @"__FILE__:__LINE__"+"\n"+caller_trace);
         }
 
         public void WasEntryPointHit(string caller_trace)
@@ -308,6 +341,22 @@ namespace MITestVariables
         }
     }
 
+    public struct TestSetVarStruct
+    {
+        public static int static_field_i;
+        public int field_i;
+
+        public static int static_prop_i
+        { get; set; }
+        public int prop_i
+        { get; set; }
+
+        public static int static_prop_i_noset
+        { get {return 5001;} }
+        public int prop_i_noset
+        { get {return 5002;} }
+    }
+
     public struct TestStruct3
     {
         public int val1
@@ -487,6 +536,8 @@ namespace MITestVariables
                 Context.EnableBreakpoint(@"__FILE__:__LINE__", "BREAK6");
                 Context.EnableBreakpoint(@"__FILE__:__LINE__", "BREAK7");
                 Context.EnableBreakpoint(@"__FILE__:__LINE__", "BREAK_GETTER");
+                Context.EnableBreakpoint(@"__FILE__:__LINE__", "bp_func1");
+                Context.EnableBreakpoint(@"__FILE__:__LINE__", "bp_func2");
 
                 Context.Continue(@"__FILE__:__LINE__");
             });
@@ -543,6 +594,14 @@ namespace MITestVariables
             string  litString = "string";
             TestImplicitCast1 litClass = new TestImplicitCast1(212);
 
+            int[] array1 = new int[] { 1, 2, 3, 4, 5 };
+
+            TestSetVarStruct setVarStruct = new TestSetVarStruct();
+            TestSetVarStruct.static_field_i = 1001;
+            TestSetVarStruct.static_prop_i = 1002;
+            setVarStruct.field_i = 2001;
+            setVarStruct.prop_i = 2002;
+
             int dummy1 = 1;                                     Label.Breakpoint("BREAK1");
 
             Label.Checkpoint("setup_var", "test_var", (Object context) => {
@@ -963,12 +1022,50 @@ namespace MITestVariables
                 Context.CreateAndAssignVar(@"__FILE__:__LINE__", "varStruct3", "11m", true);
                 Context.CreateAndCompareVar(@"__FILE__:__LINE__", "varStruct3.ToString()", "\\\"11\\\"");
 
+                Context.CreateAndCompareVar(@"__FILE__:__LINE__", "array1[0]", "1");
+                Context.CreateAndCompareVar(@"__FILE__:__LINE__", "array1[1]", "2");
+                Context.CreateAndCompareVar(@"__FILE__:__LINE__", "array1[2]", "3");
+                Context.CreateAndCompareVar(@"__FILE__:__LINE__", "array1[3]", "4");
+                Context.CreateAndCompareVar(@"__FILE__:__LINE__", "array1[4]", "5");
+                Context.CreateAndAssignVar(@"__FILE__:__LINE__", "array1[1]", "11");
+                Context.CreateAndAssignVar(@"__FILE__:__LINE__", "array1[3]", "33");
+
+                Context.CreateAndCompareVar(@"__FILE__:__LINE__", "TestSetVarStruct.static_field_i", "1001");
+                Context.CreateAndCompareVar(@"__FILE__:__LINE__", "TestSetVarStruct.static_prop_i", "1002");
+                Context.CreateAndCompareVar(@"__FILE__:__LINE__", "TestSetVarStruct.static_prop_i_noset", "5001");
+                Context.CreateAndCompareVar(@"__FILE__:__LINE__", "setVarStruct.field_i", "2001");
+                Context.CreateAndCompareVar(@"__FILE__:__LINE__", "setVarStruct.prop_i", "2002");
+                Context.CreateAndCompareVar(@"__FILE__:__LINE__", "setVarStruct.prop_i_noset", "5002");
+                Context.CreateAndAssignVar(@"__FILE__:__LINE__", "TestSetVarStruct.static_field_i", "3001");
+                // FIXME debugger must be fixed first //Context.CreateAndAssignVar(@"__FILE__:__LINE__", "TestSetVarStruct.static_prop_i", "3002");
+                // FIXME debugger must be fixed first //Context.ErrorAtAssignVar(@"__FILE__:__LINE__", "TestSetVarStruct.static_prop_i_noset", "3003");
+                Context.CreateAndAssignVar(@"__FILE__:__LINE__", "setVarStruct.field_i", "4001");
+                // FIXME debugger must be fixed first //Context.CreateAndAssignVar(@"__FILE__:__LINE__",  "setVarStruct.prop_i", "4002");
+                // FIXME debugger must be fixed first //Context.ErrorAtAssignVar(@"__FILE__:__LINE__", "setVarStruct.prop_i_noset", "4003");
+                Context.CreateAndCompareVar(@"__FILE__:__LINE__", "TestSetVarStruct.static_field_i", "3001");
+                // FIXME debugger must be fixed first //Context.CreateAndCompareVar(@"__FILE__:__LINE__", "TestSetVarStruct.static_prop_i", "3002");
+                Context.CreateAndCompareVar(@"__FILE__:__LINE__", "setVarStruct.field_i", "4001");
+                // FIXME debugger must be fixed first //Context.CreateAndCompareVar(@"__FILE__:__LINE__", "setVarStruct.prop_i", "4002");
+                // FIXME debugger must be fixed first //Context.ErrorAtAssignVar(@"__FILE__:__LINE__", "1+1", "2");
+                // FIXME debugger must be fixed first //Context.ErrorAtAssignVar(@"__FILE__:__LINE__", "1", "1");
+                Context.ErrorAtAssignVar(@"__FILE__:__LINE__", "1.ToString()", "\"1\"");
+
+                Context.CheckAttributes(@"__FILE__:__LINE__", "array1[0]", "editable");
+                Context.CheckAttributes(@"__FILE__:__LINE__", "array1?[0]", "editable");
+                Context.CheckAttributes(@"__FILE__:__LINE__", "litClass.data", "editable");
+                Context.CheckAttributes(@"__FILE__:__LINE__", "litClass?.data", "editable");
+                Context.CheckAttributes(@"__FILE__:__LINE__", "1", "noneditable");
+                Context.CheckAttributes(@"__FILE__:__LINE__", "-1", "noneditable");
+                Context.CheckAttributes(@"__FILE__:__LINE__", "-array1[0]", "noneditable");
+                Context.CheckAttributes(@"__FILE__:__LINE__", "1+1", "noneditable");
+                Context.CheckAttributes(@"__FILE__:__LINE__", "litClass.data.ToString()", "noneditable");
+
                 Context.Continue(@"__FILE__:__LINE__");
             });
 
             int dummy2 = 2;                                     Label.Breakpoint("BREAK2");
 
-            Label.Checkpoint("test_var", "test_eval_flags", (Object context) => {
+            Label.Checkpoint("test_var", "bp_func_test", (Object context) => {
                 Context Context = (Context)context;
                 Context.WasBreakpointHit(@"__FILE__:__LINE__", "BREAK2");
 
@@ -1020,9 +1117,22 @@ namespace MITestVariables
                 Context.CreateAndCompareVar(@"__FILE__:__LINE__", "varClass2.ToString()", "\\\"120\\\"");
                 Context.CreateAndCompareVar(@"__FILE__:__LINE__", "varStruct3.ToString()", "\\\"11\\\"");
 
+                Context.CreateAndCompareVar(@"__FILE__:__LINE__", "array1[0]", "1");
+                Context.CreateAndCompareVar(@"__FILE__:__LINE__", "array1[1]", "11");
+                Context.CreateAndCompareVar(@"__FILE__:__LINE__", "array1[2]", "3");
+                Context.CreateAndCompareVar(@"__FILE__:__LINE__", "array1[3]", "33");
+                Context.CreateAndCompareVar(@"__FILE__:__LINE__", "array1[4]", "5");
+
+                Context.CreateAndCompareVar(@"__FILE__:__LINE__", "TestSetVarStruct.static_field_i", "3001");
+                // FIXME debugger must be fixed first //Context.CreateAndCompareVar(@"__FILE__:__LINE__", "TestSetVarStruct.static_prop_i", "3002");
+                Context.CreateAndCompareVar(@"__FILE__:__LINE__", "setVarStruct.field_i", "4001");
+                // FIXME debugger must be fixed first //Context.CreateAndCompareVar(@"__FILE__:__LINE__", "setVarStruct.prop_i", "4002");
+
                 Context.Continue(@"__FILE__:__LINE__");
             });
 
+            TestFunctionArgs(10, 5f, "test_string");
+
             TestStruct3 ts3 = new TestStruct3();
 
             int dummy3 = 3;                                     Label.Breakpoint("BREAK3");
@@ -1086,8 +1196,8 @@ namespace MITestVariables
                     Context.GetAndCheckChildValue(@"__FILE__:__LINE__", "<error>", "ts6", 1, false, 0);
                     Context.GetAndCheckChildValue(@"__FILE__:__LINE__", "\\\"text_123\\\"", "ts6", 2, false, 0);
                 });
-                // we have 5 seconds evaluation timeout by default, wait 20 seconds (5 seconds eval timeout * 3 eval requests + 5 seconds reserve)
-                if (!task.Wait(TimeSpan.FromSeconds(20)))
+                // we have 5 seconds evaluation timeout by default, wait 20 seconds (5 seconds eval timeout * 3 eval requests + 5 seconds reserve) * 2 (for 2 command calls)
+                if (!task.Wait(TimeSpan.FromSeconds(40)))
                     throw new DebuggerTimedOut(@"__FILE__:__LINE__");
 
                 Context.Continue(@"__FILE__:__LINE__");
@@ -1115,5 +1225,38 @@ namespace MITestVariables
                 Context.DebuggerExit(@"__FILE__:__LINE__");
             });
         }
+
+        static void TestFunctionArgs(int test_arg_i, float test_arg_f, string test_arg_string)
+        {
+            int dummy1 = 1;                                     Label.Breakpoint("bp_func1");
+
+            Label.Checkpoint("bp_func_test", "bp_func_test2", (Object context) => {
+                Context Context = (Context)context;
+                Context.WasBreakpointHit(@"__FILE__:__LINE__", "bp_func1");
+
+                Context.CreateAndCompareVar(@"__FILE__:__LINE__", "test_arg_i", "10");
+                Context.CreateAndCompareVar(@"__FILE__:__LINE__", "test_arg_f", "5");
+                Context.CreateAndCompareVar(@"__FILE__:__LINE__", "test_arg_string", "\\\"test_string\\\"");
+
+                Context.CreateAndAssignVar(@"__FILE__:__LINE__", "test_arg_i", "20");
+                Context.CreateAndAssignVar(@"__FILE__:__LINE__", "test_arg_f", "50");
+                Context.CreateAndAssignVar(@"__FILE__:__LINE__", "test_arg_string", "\"edited_string\"", true);
+
+                Context.Continue(@"__FILE__:__LINE__");
+            });
+
+            dummy1 = 2;                                         Label.Breakpoint("bp_func2");
+
+            Label.Checkpoint("bp_func_test2", "test_eval_flags", (Object context) => {
+                Context Context = (Context)context;
+                Context.WasBreakpointHit(@"__FILE__:__LINE__", "bp_func2");
+
+                Context.CreateAndCompareVar(@"__FILE__:__LINE__", "test_arg_i", "20");
+                Context.CreateAndCompareVar(@"__FILE__:__LINE__", "test_arg_f", "50");
+                Context.CreateAndCompareVar(@"__FILE__:__LINE__", "test_arg_string", "\\\"edited_string\\\"");
+
+                Context.Continue(@"__FILE__:__LINE__");
+            });
+        }
     }
 }
index cd677d62baad6f29c74bbea90ed2d23f4c01fbf8..0057b9f018ce4d9fd794cc09db9dc050a7e7df9a 100644 (file)
@@ -367,4 +367,19 @@ namespace NetcoreDbgTest.VSCode
     public class ExceptionInfoArguments {
         public int threadId;
     }
+
+    public class SetExpressionRequest : Request {
+        public SetExpressionRequest()
+        {
+            command = "setExpression";
+        }
+        public SetExpressionArguments arguments = new SetExpressionArguments();
+    }
+
+    public class SetExpressionArguments {
+        public string expression;
+        public string value;
+        public int? frameId;
+        public ValueFormat? format;
+    }
 }
index 0b8f31835d8ed7c44fdd88e5fea99117d9ec1f87..fd2b7c98ac9599dc8c66a8f1f1b9f40a7680035f 100644 (file)
@@ -156,4 +156,16 @@ namespace NetcoreDbgTest.VSCode
         public List<ExceptionDetails> innerException;
     }
 
+    public class SetExpressionResponse : Response {
+        public SetExpressionResponseBody body;
+    }
+
+    public class SetExpressionResponseBody {
+        public string value;
+        public string? type;
+        public VariablePresentationHint? presentationHint;
+        public int? variablesReference;
+        public int? namedVariables;
+        public int? indexedVariables;
+    }
 }
index 85c16bae3dbd4a0a57667a7e279a2d1dc5c6fec9..e4ee0862b400ef8eebefe8c23f4d217d6476273e 100644 (file)
@@ -333,6 +333,22 @@ namespace NetcoreDbgTest.Script
             Assert.False(VSCodeDebugger.Request(setVariableRequest).Success, @"__FILE__:__LINE__");
         }
 
+        public void SetExpression(string caller_trace, Int64 frameId, string Expression, string Value)
+        {
+            SetExpressionRequest setExpressionRequest = new SetExpressionRequest();
+            setExpressionRequest.arguments.expression = Expression;
+            setExpressionRequest.arguments.value = Value;
+            Assert.True(VSCodeDebugger.Request(setExpressionRequest).Success, @"__FILE__:__LINE__");
+        }
+
+        public void ErrorSetExpression(string caller_trace, Int64 frameId, string Expression, string Value)
+        {
+            SetExpressionRequest setExpressionRequest = new SetExpressionRequest();
+            setExpressionRequest.arguments.expression = Expression;
+            setExpressionRequest.arguments.value = Value;
+            Assert.False(VSCodeDebugger.Request(setExpressionRequest).Success, @"__FILE__:__LINE__");
+        }
+
         public void Continue(string caller_trace)
         {
             ContinueRequest continueRequest = new ContinueRequest();
@@ -416,6 +432,38 @@ namespace VSCodeTestVariables
         }
     }
 
+    public struct TestSetVarStruct
+    {
+        public static int static_field_i;
+        public int field_i;
+
+        public static int static_prop_i
+        { get; set; }
+        public int prop_i
+        { get; set; }
+
+        public static int static_prop_i_noset
+        { get {return 5001;} }
+        public int prop_i_noset
+        { get {return 5002;} }
+    }
+
+    public struct TestSetExprStruct
+    {
+        public static int static_field_i;
+        public int field_i;
+
+        public static int static_prop_i
+        { get; set; }
+        public int prop_i
+        { get; set; }
+
+        public static int static_prop_i_noset
+        { get {return 5001;} }
+        public int prop_i_noset
+        { get {return 5002;} }
+    }
+
     public struct TestStruct4
     {
         [System.Diagnostics.DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
@@ -577,7 +625,8 @@ namespace VSCodeTestVariables
                 Context.AddBreakpoint(@"__FILE__:__LINE__", "bp3");
                 Context.AddBreakpoint(@"__FILE__:__LINE__", "bp4");
                 Context.AddBreakpoint(@"__FILE__:__LINE__", "bp5");
-                Context.AddBreakpoint(@"__FILE__:__LINE__", "bp_func");
+                Context.AddBreakpoint(@"__FILE__:__LINE__", "bp_func1");
+                Context.AddBreakpoint(@"__FILE__:__LINE__", "bp_func2");
                 Context.AddBreakpoint(@"__FILE__:__LINE__", "bp_getter");
                 Context.SetBreakpoints(@"__FILE__:__LINE__");
                 Context.PrepareEnd(@"__FILE__:__LINE__");
@@ -637,6 +686,20 @@ namespace VSCodeTestVariables
             string  litString = "string";
             TestImplicitCast1 litClass = new TestImplicitCast1(212);
 
+            int[] array1 = new int[] { 1, 2, 3, 4, 5 };
+
+            TestSetVarStruct setVarStruct = new TestSetVarStruct();
+            TestSetVarStruct.static_field_i = 1001;
+            TestSetVarStruct.static_prop_i = 1002;
+            setVarStruct.field_i = 2001;
+            setVarStruct.prop_i = 2002;
+
+            TestSetExprStruct setExprStruct = new TestSetExprStruct();
+            TestSetExprStruct.static_field_i = 1001;
+            TestSetExprStruct.static_prop_i = 1002;
+            setExprStruct.field_i = 2001;
+            setExprStruct.prop_i = 2002;
+
             int dummy1 = 1;                                     Label.Breakpoint("BREAK1");
 
             Label.Checkpoint("setup_var", "test_var", (Object context) => {
@@ -1059,6 +1122,53 @@ namespace VSCodeTestVariables
                 Context.SetVariable(@"__FILE__:__LINE__", frameId, variablesReference, "varStruct3", "11m", true);
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "varStruct3.ToString()", "\"11\"");
 
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "array1[0]", "1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "array1[1]", "2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "array1[2]", "3");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "array1[3]", "4");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "array1[4]", "5");
+                int array1Reference = Context.GetChildVariablesReference(@"__FILE__:__LINE__", variablesReference, "array1");
+                Context.SetVariable(@"__FILE__:__LINE__", frameId, array1Reference, "[1]", "11");
+                Context.SetVariable(@"__FILE__:__LINE__", frameId, array1Reference, "[3]", "33");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "TestSetVarStruct.static_field_i", "1001");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "TestSetVarStruct.static_prop_i", "1002");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "TestSetVarStruct.static_prop_i_noset", "5001");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "setVarStruct.field_i", "2001");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "setVarStruct.prop_i", "2002");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "setVarStruct.prop_i_noset", "5002");
+                int setVarStructReference = Context.GetChildVariablesReference(@"__FILE__:__LINE__", variablesReference, "setVarStruct");
+                Context.SetVariable(@"__FILE__:__LINE__", frameId, setVarStructReference, "static_field_i", "3001", true);
+                Context.SetVariable(@"__FILE__:__LINE__", frameId, setVarStructReference, "static_prop_i", "3002", true);
+                Context.ErrorSetVariable(@"__FILE__:__LINE__", setVarStructReference, "static_prop_i_noset", "3003");
+                Context.SetVariable(@"__FILE__:__LINE__", frameId, setVarStructReference, "field_i", "4001", true);
+                Context.SetVariable(@"__FILE__:__LINE__", frameId, setVarStructReference, "prop_i", "4002", true);
+                Context.ErrorSetVariable(@"__FILE__:__LINE__", setVarStructReference, "prop_i_noset", "4003");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "TestSetVarStruct.static_field_i", "3001");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "TestSetVarStruct.static_prop_i", "3002");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "setVarStruct.field_i", "4001");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "setVarStruct.prop_i", "4002");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "TestSetExprStruct.static_field_i", "1001");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "TestSetExprStruct.static_prop_i", "1002");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "TestSetExprStruct.static_prop_i_noset", "5001");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "setExprStruct.field_i", "2001");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "setExprStruct.prop_i", "2002");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "setExprStruct.prop_i_noset", "5002");
+                Context.SetExpression(@"__FILE__:__LINE__", frameId, "TestSetExprStruct.static_field_i", "3001");
+                Context.SetExpression(@"__FILE__:__LINE__", frameId, "TestSetExprStruct.static_prop_i", "3002");
+                // FIXME debugger must be fixed first //Context.ErrorSetExpression(@"__FILE__:__LINE__", frameId, "TestSetExprStruct.static_prop_i_noset", "3003");
+                Context.SetExpression(@"__FILE__:__LINE__", frameId, "setExprStruct.field_i", "4001");
+                Context.SetExpression(@"__FILE__:__LINE__", frameId, "setExprStruct.prop_i", "4002");
+                // FIXME debugger must be fixed first //Context.ErrorSetExpression(@"__FILE__:__LINE__", frameId, "setExprStruct.prop_i_noset", "4003");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "TestSetExprStruct.static_field_i", "3001");
+                // FIXME debugger must be fixed first //Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "TestSetExprStruct.static_prop_i", "3002");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "setExprStruct.field_i", "4001");
+                // FIXME debugger must be fixed first //Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "setExprStruct.prop_i", "4002");
+                // FIXME debugger must be fixed first //Context.ErrorSetExpression(@"__FILE__:__LINE__", frameId, "1+1", "2");
+                // FIXME debugger must be fixed first //Context.ErrorSetExpression(@"__FILE__:__LINE__", frameId, "1", "1");
+                Context.ErrorSetExpression(@"__FILE__:__LINE__", frameId, "1.ToString()", "\"1\"");
+
                 Context.Continue(@"__FILE__:__LINE__");
             });
 
@@ -1118,10 +1228,26 @@ namespace VSCodeTestVariables
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "varClass2.ToString()", "\"120\"");
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "varStruct3.ToString()", "\"11\"");
 
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "array1[0]", "1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "array1[1]", "11");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "array1[2]", "3");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "array1[3]", "33");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "array1[4]", "5");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "TestSetVarStruct.static_field_i", "3001");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "TestSetVarStruct.static_prop_i", "3002");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "setVarStruct.field_i", "4001");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "setVarStruct.prop_i", "4002");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "TestSetExprStruct.static_field_i", "3001");
+                // FIXME debugger must be fixed first //Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "TestSetExprStruct.static_prop_i", "3002");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "setExprStruct.field_i", "4001");
+                // FIXME debugger must be fixed first //Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "setExprStruct.prop_i", "4002");
+
                 Context.Continue(@"__FILE__:__LINE__");
             });
 
-            TestFunction(10);
+            TestFunctionArgs(10, 5f, "test_string");
 
             TestStruct4 ts4 = new TestStruct4();
 
@@ -1239,18 +1365,39 @@ namespace VSCodeTestVariables
             });
         }
 
-        static void TestFunction(int t)
+        static void TestFunctionArgs(int test_arg_i, float test_arg_f, string test_arg_string)
         {
-            int f = 5;
-            Console.WriteLine("f = " + f.ToString());                     Label.Breakpoint("bp_func");
+            int dummy1 = 1;                                     Label.Breakpoint("bp_func1");
 
-            Label.Checkpoint("bp_func_test", "test_debugger_browsable_state", (Object context) => {
+            Label.Checkpoint("bp_func_test", "bp_func_test2", (Object context) => {
                 Context Context = (Context)context;
-                Context.WasBreakpointHit(@"__FILE__:__LINE__", "bp_func");
-                Int64 frameId = Context.DetectFrameId(@"__FILE__:__LINE__", "bp_func");
+                Context.WasBreakpointHit(@"__FILE__:__LINE__", "bp_func1");
+                Int64 frameId = Context.DetectFrameId(@"__FILE__:__LINE__", "bp_func1");
                 int variablesReference = Context.GetVariablesReference(@"__FILE__:__LINE__", frameId, "Locals");
-                Context.EvalVariable(@"__FILE__:__LINE__", variablesReference, "int", "t", "10");
-                Context.EvalVariable(@"__FILE__:__LINE__", variablesReference, "int", "f", "5");
+
+                Context.EvalVariable(@"__FILE__:__LINE__", variablesReference, "int", "test_arg_i", "10");
+                Context.EvalVariable(@"__FILE__:__LINE__", variablesReference, "float", "test_arg_f", "5");
+                Context.EvalVariable(@"__FILE__:__LINE__", variablesReference, "string", "test_arg_string", "\"test_string\"");
+
+                Context.SetVariable(@"__FILE__:__LINE__", frameId, variablesReference, "test_arg_i", "20", true);
+                Context.SetVariable(@"__FILE__:__LINE__", frameId, variablesReference, "test_arg_f", "50", true);
+                Context.SetVariable(@"__FILE__:__LINE__", frameId, variablesReference, "test_arg_string", "\"edited_string\"", true);
+
+                Context.Continue(@"__FILE__:__LINE__");
+            });
+
+            dummy1 = 2;                                         Label.Breakpoint("bp_func2");
+
+            Label.Checkpoint("bp_func_test2", "test_debugger_browsable_state", (Object context) => {
+                Context Context = (Context)context;
+                Context.WasBreakpointHit(@"__FILE__:__LINE__", "bp_func2");
+                Int64 frameId = Context.DetectFrameId(@"__FILE__:__LINE__", "bp_func2");
+                int variablesReference = Context.GetVariablesReference(@"__FILE__:__LINE__", frameId, "Locals");
+
+                Context.EvalVariable(@"__FILE__:__LINE__", variablesReference, "int", "test_arg_i", "20");
+                Context.EvalVariable(@"__FILE__:__LINE__", variablesReference, "float", "test_arg_f", "50");
+                Context.EvalVariable(@"__FILE__:__LINE__", variablesReference, "string", "test_arg_string", "\"edited_string\"");
+
                 Context.Continue(@"__FILE__:__LINE__");
             });
         }