Indexers for nullable generic classes/standard containers evaluation support added.
authorOleg Lekarev <o.lekarev@samsung.com>
Tue, 23 Aug 2022 15:28:41 +0000 (18:28 +0300)
committerAlexander Soldatov/Platform Lab /SRR/Staff Engineer/Samsung Electronics <soldatov.a@samsung.com>
Fri, 26 Aug 2022 16:25:46 +0000 (19:25 +0300)
src/debugger/evalstackmachine.cpp
test-suite/MITestEvalArraysIndexers/Program.cs
test-suite/VSCodeTestEvalArraysIndexers/Program.cs

index 4a9e447f1b6bf70643e52291707b678c095057c9..ab226917ba8e2f5e3a8dd0f0bd394ffad9784483 100644 (file)
@@ -342,27 +342,6 @@ namespace
         return Status;
     }
 
-    HRESULT GetIndexesFromStack(std::vector<ULONG32> &indexes, int dimension, std::list<EvalStackEntry> &evalStack, EvalData &ed, std::string &output)
-    {
-        HRESULT Status;
-
-        for (int32_t i = 0; i < dimension; i++)
-        {
-            ToRelease<ICorDebugValue> iCorValue;
-            IfFailRet(GetFrontStackEntryValue(&iCorValue, nullptr, evalStack, ed, output));
-            evalStack.pop_front();
-
-            // TODO implicitly convert iCorValue to int, if type not int
-            //      at this moment GetElementIndex() work with integer types only
-
-            ULONG32 result_index = 0;
-            IfFailRet(GetElementIndex(iCorValue, result_index));
-            indexes.insert(indexes.begin(), result_index);
-        }
-
-        return S_OK;
-    }
-
     HRESULT GetArgData(ICorDebugValue *pTypeValue, std::string &typeName, CorElementType &elemType)
     {
         HRESULT Status;
@@ -1311,20 +1290,25 @@ namespace
     HRESULT ElementBindingExpression(std::list<EvalStackEntry> &evalStack, PVOID pArguments, std::string &output, EvalData &ed)
     {
         int32_t Int = ((FormatFI*)pArguments)->Int;
-
         HRESULT Status;
-        std::vector<ULONG32> indexes;
-        IfFailRet(GetIndexesFromStack(indexes, Int, evalStack, ed, output));
 
+        std::vector<ToRelease<ICorDebugValue>> indexvalues(Int);
+
+        for (int32_t i = Int - 1; i >= 0; i--)
+        {
+            ToRelease<ICorDebugValue> iCorValue;
+            IfFailRet(GetFrontStackEntryValue(&indexvalues[i], nullptr, evalStack, ed, output));
+            evalStack.pop_front();
+        }
         if (evalStack.front().preventBinding)
             return S_OK;
 
-        ToRelease<ICorDebugValue> iCorArrayValue;
+        ToRelease<ICorDebugValue> iCorObjectValue;
         std::unique_ptr<Evaluator::SetterData> setterData;
-        IfFailRet(GetFrontStackEntryValue(&iCorArrayValue, &setterData, evalStack, ed, output));
+        IfFailRet(GetFrontStackEntryValue(&iCorObjectValue, &setterData, evalStack, ed, output));
 
         ToRelease<ICorDebugReferenceValue> pReferenceValue;
-        IfFailRet(iCorArrayValue->QueryInterface(IID_ICorDebugReferenceValue, (LPVOID*) &pReferenceValue));
+        IfFailRet(iCorObjectValue->QueryInterface(IID_ICorDebugReferenceValue, (LPVOID*) &pReferenceValue));
         BOOL isNull = FALSE;
         IfFailRet(pReferenceValue->IsNull(&isNull));
 
@@ -1334,10 +1318,79 @@ namespace
             return S_OK;
         }
 
-        evalStack.front().iCorValue.Free();
-        evalStack.front().identifiers.clear();
-        evalStack.front().setterData = std::move(setterData);
-        return ed.pEvaluator->GetElement(iCorArrayValue, indexes, &evalStack.front().iCorValue);
+        ToRelease<ICorDebugValue> iCorRealValue;
+        CorElementType elemType;
+        IfFailRet(GetRealValueWithType(iCorObjectValue, &iCorRealValue, &elemType));
+        std::vector<ULONG32> indexes;
+
+        if (elemType == ELEMENT_TYPE_SZARRAY || elemType == ELEMENT_TYPE_ARRAY) {
+            for (int32_t i = Int - 1; i >= 0; i--)
+            {
+                ULONG32 result_index = 0;
+                // TODO implicitly convert iCorValue to int, if type not int
+                // at this moment GetElementIndex() work with integer types only
+                IfFailRet(GetElementIndex(indexvalues[i], result_index));
+                indexes.insert(indexes.begin(), result_index);
+            }
+            evalStack.front().iCorValue.Free();
+            evalStack.front().identifiers.clear();
+            evalStack.front().setterData = std::move(setterData);
+            Status = ed.pEvaluator->GetElement(iCorObjectValue, indexes, &evalStack.front().iCorValue);
+        } else {
+            std::vector<Evaluator::ArgElementType> funcArgs(Int);
+            for (int32_t i = 0; i < Int; ++i)
+            {
+                ToRelease<ICorDebugValue> iCorValueArg;
+                IfFailRet(DereferenceAndUnboxValue(indexvalues[i].GetPtr(), &iCorValueArg, nullptr));
+                IfFailRet(iCorValueArg->GetType(&funcArgs[i].corType));
+
+                if (funcArgs[i].corType == ELEMENT_TYPE_VALUETYPE || funcArgs[i].corType == ELEMENT_TYPE_CLASS)
+                    IfFailRet(TypePrinter::NameForTypeByValue(iCorValueArg, funcArgs[i].typeName));
+            }
+
+            ToRelease<ICorDebugFunction> iCorFunc;
+            ed.pEvaluator->WalkMethods(iCorObjectValue, [&](
+                bool,
+                const std::string &methodName,
+                Evaluator::ReturnElementType& retType,
+                std::vector<Evaluator::ArgElementType> &methodArgs,
+                Evaluator::GetFunctionCallback getFunction)
+            {
+                std::string name = "get_Item";
+                std::size_t found = methodName.rfind(name);
+                if (retType.corType == ELEMENT_TYPE_VOID || found == std::string::npos || found != methodName.length() - name.length() || funcArgs.size() != methodArgs.size())
+                    return S_OK; // Return with success to continue walk.
+
+                for (size_t i = 0; i < funcArgs.size(); ++i)
+                {
+                    if (funcArgs[i].corType != methodArgs[i].corType ||
+                        funcArgs[i].typeName != methodArgs[i].typeName)
+                        return S_OK;
+                }
+                IfFailRet(getFunction(&iCorFunc));
+                return E_ABORT; // Fast exit from cycle, since we already found iCorFunc.
+            });
+            if (!iCorFunc)
+                return E_INVALIDARG;
+            evalStack.front().ResetEntry();
+            std::vector<ICorDebugValue*> iCorValueArgs;
+            iCorValueArgs.reserve(Int+1);
+
+            iCorValueArgs.emplace_back(iCorObjectValue.GetPtr());
+
+            for (int32_t i = 0; i < Int; i++)
+            {
+                iCorValueArgs.emplace_back(indexvalues[i].GetPtr());
+            }
+
+            ToRelease<ICorDebugValue2> iCorValue2;
+            IfFailRet(iCorObjectValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &iCorValue2));
+            ToRelease<ICorDebugType> iCorType;
+            IfFailRet(iCorValue2->GetExactType(&iCorType));
+
+            Status = ed.pEvalHelpers->EvalFunction(ed.pThread, iCorFunc, iCorType.GetRef(), 1, iCorValueArgs.data(), Int + 1, &evalStack.front().iCorValue, ed.evalFlags);
+        }
+        return Status;
     }
 
     HRESULT NumericLiteralExpression(std::list<EvalStackEntry> &evalStack, PVOID pArguments, std::string &output, EvalData &ed)
index a2d2548eafcdf27750db4271cd5ec41da0f29d19..94d61319f0b0c6bd0d00b4cd2b99af33ef21af31 100644 (file)
@@ -325,6 +325,9 @@ namespace MITestEvalArraysIndexers
             MyString[] myStrings = new MyString[6]
                 {new MyString("zero"), new MyString("one"), new MyString("two"), new MyString("three"), new MyString("four"), new MyString("five")};
 
+            SimpleInt sinull;
+            SimpleInt? siq;
+
             int i0 = 0;
             int i1 = 1;
             int i2 = 2;
@@ -654,6 +657,14 @@ namespace MITestEvalArraysIndexers
                 Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "dictmsmi[\\\"a string\\\"]", "Error: 0x80070057");
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", "{System.Collections.Generic.KeyNotFoundException}", "System.Collections.Generic.KeyNotFoundException", "dictmsmi[myStrings[5]]");
 
+                // check nullables
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "null", "MITestEvalArraysIndexers.SimpleInt", "sinull");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "{System.NullReferenceException}", "System.NullReferenceException", "sinull[0]");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "null", "MITestEvalArraysIndexers.SimpleInt", "sinull?[0]");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "null", "MITestEvalArraysIndexers.SimpleInt", "siq");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "{System.NullReferenceException}", "System.NullReferenceException", "siq[0]");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "null", "MITestEvalArraysIndexers.SimpleInt", "siq?[0]");
+
                 Context.Continue(@"__FILE__:__LINE__");
             });
 
index 31e1e8e1158f03fdc2d3f74e3bdca2703d696c89..73c20d107a14bfb450ec2afa93f80875b7dd0085 100644 (file)
@@ -378,6 +378,9 @@ namespace VSCodeTestEvalArraysIndexers
             MyString[] myStrings = new MyString[6]
                 {new MyString("zero"), new MyString("one"), new MyString("two"), new MyString("three"), new MyString("four"), new MyString("five")};
 
+            SimpleInt sinull;
+            SimpleInt? siq;
+
             int i0 = 0;
             int i1 = 1;
             int i2 = 2;
@@ -708,6 +711,14 @@ namespace VSCodeTestEvalArraysIndexers
                 Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "dictmsmi[\"a string\"]", "error: 0x80070057");
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "{System.Collections.Generic.KeyNotFoundException}", "System.Collections.Generic.KeyNotFoundException", "dictmsmi[myStrings[5]]");
 
+                // check nullables
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "null", "VSCodeTestEvalArraysIndexers.SimpleInt", "sinull");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "{System.NullReferenceException}", "System.NullReferenceException", "sinull[0]");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "null", "VSCodeTestEvalArraysIndexers.SimpleInt", "sinull?[0]");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "null", "VSCodeTestEvalArraysIndexers.SimpleInt", "siq");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "{System.NullReferenceException}", "System.NullReferenceException", "siq[0]");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "null", "VSCodeTestEvalArraysIndexers.SimpleInt", "siq?[0]");
+
                 Context.Continue(@"__FILE__:__LINE__");
             });