Fix eval search for function without full qualified name.
authorOleg Lekarev <o.lekarev@samsung.com>
Thu, 11 Nov 2021 09:57:48 +0000 (12:57 +0300)
committerAlexander Soldatov/Platform Lab /SRR/Staff Engineer/Samsung Electronics <soldatov.a@samsung.com>
Thu, 18 Nov 2021 15:32:12 +0000 (18:32 +0300)
src/debugger/evalstackmachine.cpp
src/debugger/evaluator.cpp
src/debugger/evaluator.h
test-suite/MITestEvaluate/Program.cs
test-suite/VSCodeTestEvaluate/Program.cs

index 4efa27aa8201e0780cc4409f57027b38aeba5e62..7052df49af512f57b49cf9ec3013ac0240866c1f 100644 (file)
@@ -1061,6 +1061,8 @@ namespace
             return E_INVALIDARG;
 
         HRESULT Status;
+        bool idsEmpty = false;
+        bool isInstance = true;
         std::vector<ToRelease<ICorDebugValue>> iCorArgs(Int);
         for (int32_t i = Int - 1; i >= 0; i--)
         {
@@ -1075,7 +1077,23 @@ namespace
         evalStack.front().identifiers.pop_back();
 
         if (!evalStack.front().iCorValue && evalStack.front().identifiers.empty())
-            evalStack.front().identifiers.emplace_back("this");
+        {
+            std::string methodClass;
+            idsEmpty = true;
+            IfFailRet(ed.pEvaluator->GetMethodClass(ed.pThread, ed.frameLevel, methodClass, isInstance));
+            if (isInstance)
+            {
+                evalStack.front().identifiers.emplace_back("this");
+            }
+            else
+            {
+                // here we add a full qualified "path" separated with dots (aka Class.Subclass.Subclass ..etc) 
+                // although <identifiers> usually contains a vector of components of the full name qualification
+                // Anyway, our added component will be correctly processed by Evaluator::ResolveIdentifiers() for
+                // that case as it seals all the qualification components into one with dots before using them.
+                evalStack.front().identifiers.emplace_back(methodClass);
+            }
+        }
 
         ToRelease<ICorDebugValue> iCorValue;
         ToRelease<ICorDebugType> iCorType;
@@ -1119,12 +1137,12 @@ namespace
         std::vector<Evaluator::ArgElementType> funcArgs(Int);
         for (int32_t i = 0; i < Int; ++i)
         {
-            ToRelease<ICorDebugValue> iCorValue;
-            IfFailRet(DereferenceAndUnboxValue(iCorArgs[i].GetPtr(), &iCorValue, nullptr));
-            IfFailRet(iCorValue->GetType(&funcArgs[i].corType));
+            ToRelease<ICorDebugValue> iCorValueArg;
+            IfFailRet(DereferenceAndUnboxValue(iCorArgs[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(iCorValue, funcArgs[i].typeName));
+                IfFailRet(TypePrinter::NameForTypeByValue(iCorValueArg, funcArgs[i].typeName));
         }
 
         ToRelease<ICorDebugFunction> iCorFunc;
@@ -1135,7 +1153,7 @@ namespace
             std::vector<Evaluator::ArgElementType> &methodArgs,
             Evaluator::GetFunctionCallback getFunction)
         {
-            if ((searchStatic && !is_static) || (!searchStatic && is_static) ||
+            if ( (searchStatic && !is_static) || (!searchStatic && is_static && !idsEmpty) ||
                 funcArgs.size() != methodArgs.size() || funcName != methodName)
                 return S_OK;
 
@@ -1147,6 +1165,7 @@ namespace
             }
 
             IfFailRet(getFunction(&iCorFunc));
+            isInstance = !is_static;
 
             return E_ABORT; // Fast exit from cycle.
         });
@@ -1155,10 +1174,10 @@ namespace
 
         evalStack.front().ResetEntry();
 
-        ULONG32 realArgsCount = Int + (searchStatic ? 0 : 1);
+        ULONG32 realArgsCount = Int + (isInstance ? 1 : 0);
         std::vector<ICorDebugValue*> iCorValueArgs;
         iCorValueArgs.reserve(realArgsCount);
-        if (!searchStatic)
+        if (isInstance)
         {
             iCorValueArgs.emplace_back(iCorValue.GetPtr());
         }
index 83c105b8d8250897fb9b307e20303912f7c4cf4f..6addfa3bc6119ce7432c99a5398330f3323a2a3a 100644 (file)
@@ -1045,6 +1045,39 @@ static HRESULT HandleSpecialThisParam(
     return S_OK;
 }
 
+HRESULT Evaluator::GetMethodClass(ICorDebugThread *pThread, FrameLevel frameLevel, std::string &methodClass, bool &haveThis)
+{
+    HRESULT Status;
+    ToRelease<ICorDebugFrame> pFrame;
+    IfFailRet(GetFrameAt(pThread, frameLevel, &pFrame));
+    if (pFrame == nullptr)
+        return E_FAIL;
+
+    ToRelease<ICorDebugFunction> pFunction;
+    IfFailRet(pFrame->GetFunction(&pFunction));
+
+    ToRelease<ICorDebugModule> pModule;
+    IfFailRet(pFunction->GetModule(&pModule));
+
+    ToRelease<IUnknown> pMDUnknown;
+    IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
+    ToRelease<IMetaDataImport> pMD;
+    IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
+
+    mdMethodDef methodDef;
+    IfFailRet(pFunction->GetToken(&methodDef));
+
+    std::string methodName;
+    TypePrinter::GetTypeAndMethod(pFrame, methodClass, methodName);
+
+    DWORD methodAttr = 0;
+    IfFailRet(pMD->GetMethodProps(methodDef, NULL, NULL, 0, NULL, &methodAttr, NULL, NULL, NULL, NULL));
+
+    haveThis = (methodAttr & mdStatic) == 0;
+
+    return S_OK;
+}
+
 HRESULT Evaluator::WalkStackVars(ICorDebugThread *pThread, FrameLevel frameLevel, WalkStackVarsCallback cb)
 {
     HRESULT Status;
index 40805c5b5efad04b3ce50b1a327e696c550d31de..ed79e9b069859fcd08c5def32a8be02cd295ae22 100644 (file)
@@ -79,6 +79,12 @@ public:
         FrameLevel frameLevel,
         WalkStackVarsCallback cb);
 
+    HRESULT Evaluator::GetMethodClass(
+        ICorDebugThread *pThread,
+        FrameLevel frameLevel,
+        std::string &methodClass,
+        bool &thisParam);
+
     HRESULT GetElement(ICorDebugValue *pInputValue, std::vector<ULONG32> &indexes, ICorDebugValue **ppResultValue);
     HRESULT WalkMethods(ICorDebugType *pInputType, WalkMethodsCallback cb);
     HRESULT WalkMethods(ICorDebugValue *pInputTypeValue, WalkMethodsCallback cb);
index 523821877141fc094c2cf7b1bec2da01a3744682..88c23ed0ac21f60897b9d87d8697510ba79ce3b1 100644 (file)
@@ -376,9 +376,11 @@ namespace MITestEvaluate
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", "3", "int", "MITestEvaluate.test_this_t.this_static_i");
 
                 // Test method calls.
-                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "Calc1()", "Error");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "15", "int", "Calc1()");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "15", "int", "test_this_t.Calc1()");
                 Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "this.Calc1()", "Error");
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", "16", "int", "Calc2()");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "test_this_t.Calc2()", "Error");
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", "16", "int", "this.Calc2()");
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"MITestEvaluate.test_this_t\\\"", "string", "ToString()");
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"MITestEvaluate.test_this_t\\\"", "string", "this.ToString()");
@@ -651,6 +653,21 @@ namespace MITestEvaluate
         int int_i = 505;
         static test_nested test_nested_static_instance;
 
+        static int stGetInt()
+        {
+            return 111;
+        }
+
+        static int stGetInt(int x)
+        {
+            return x * 2;
+        }
+
+        int getInt()
+        {
+            return 222;
+        }
+
         static void Main(string[] args)
         {
             Label.Checkpoint("init", "values_test", (Object context) => {
@@ -669,6 +686,7 @@ namespace MITestEvaluate
                 Context.EnableBreakpoint(@"__FILE__:__LINE__", "BREAK10");
                 Context.EnableBreakpoint(@"__FILE__:__LINE__", "BREAK11");
                 Context.EnableBreakpoint(@"__FILE__:__LINE__", "BREAK12");
+                Context.EnableBreakpoint(@"__FILE__:__LINE__", "BREAK13");
                 Context.Continue(@"__FILE__:__LINE__");
             });
 
@@ -1305,7 +1323,7 @@ namespace MITestEvaluate
 
             int break_line12 = 1;                                                                        Label.Breakpoint("BREAK12");
 
-            Label.Checkpoint("unary_operators_test", "finish", (Object context) => {
+            Label.Checkpoint("unary_operators_test", "function_evaluation_test", (Object context) => {
                 Context Context = (Context)context;
                 Context.WasBreakpointHit(@"__FILE__:__LINE__", "BREAK12");
 
@@ -1366,6 +1384,18 @@ namespace MITestEvaluate
                 Context.Continue(@"__FILE__:__LINE__");
             });
 
+            int break_line_13 = 13;                                                                           Label.Breakpoint("BREAK13");
+            Label.Checkpoint("function_evaluation_test", "finish", (Object context) => {
+                Context Context = (Context)context;
+                Context.WasBreakpointHit(@"__FILE__:__LINE__", "BREAK13");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "111", "int", "stGetInt()");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "666", "int", "stGetInt(333)");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "getInt()", "Error:");
+
+                Context.Continue(@"__FILE__:__LINE__");
+            });
+
             Label.Checkpoint("finish", "", (Object context) => {
                 Context Context = (Context)context;
                 Context.WasExit(@"__FILE__:__LINE__");
index 3029c04f5808a93606faa50d8378cc15e02ff721..bdd3d57ecc34b52f6bc083bbe845cc5b206a51a4 100644 (file)
@@ -494,10 +494,12 @@ namespace VSCodeTestEvaluate
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "3", "int", "VSCodeTestEvaluate.test_this_t.this_static_i");
 
                 // Test method calls.
-                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "Calc1()", "error:");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "15", "int", "Calc1()");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "15", "int", "test_this_t.Calc1()");
                 Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "this.Calc1()", "error:");
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "16", "int", "Calc2()");
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "16", "int", "this.Calc2()");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "test_this_t.Calc2()", "error:");
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"VSCodeTestEvaluate.test_this_t\"", "string", "ToString()");
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"VSCodeTestEvaluate.test_this_t\"", "string", "this.ToString()");
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "Expression has been evaluated and has no value", "void", "TestVoidReturn()");
@@ -770,6 +772,21 @@ namespace VSCodeTestEvaluate
         int int_i = 505;
         static test_nested test_nested_static_instance;
 
+        static int stGetInt()
+        {
+            return 111;
+        }
+
+        static int stGetInt(int x)
+        {
+            return x * 2;
+        }
+
+        int getInt()
+        {
+            return 222;
+        }
+
         static void Main(string[] args)
         {
             Label.Checkpoint("init", "values_test", (Object context) => {
@@ -787,6 +804,7 @@ namespace VSCodeTestEvaluate
                 Context.AddBreakpoint(@"__FILE__:__LINE__", "BREAK10");
                 Context.AddBreakpoint(@"__FILE__:__LINE__", "BREAK11");
                 Context.AddBreakpoint(@"__FILE__:__LINE__", "BREAK12");
+                Context.AddBreakpoint(@"__FILE__:__LINE__", "BREAK13");
                 Context.SetBreakpoints(@"__FILE__:__LINE__");
                 Context.PrepareEnd(@"__FILE__:__LINE__");
                 Context.WasEntryPointHit(@"__FILE__:__LINE__");
@@ -1427,7 +1445,7 @@ namespace VSCodeTestEvaluate
 
             int break_line12 = 1;                                                                        Label.Breakpoint("BREAK12");
 
-            Label.Checkpoint("unary_operators_test", "finish", (Object context) => {
+            Label.Checkpoint("unary_operators_test", "function_evaluation_test", (Object context) => {
                 Context Context = (Context)context;
                 Context.WasBreakpointHit(@"__FILE__:__LINE__", "BREAK12");
                 Int64 frameId = Context.DetectFrameId(@"__FILE__:__LINE__", "BREAK12");
@@ -1489,6 +1507,20 @@ namespace VSCodeTestEvaluate
                 Context.Continue(@"__FILE__:__LINE__");
             });
 
+            int break_line13 = 13;                                                                        Label.Breakpoint("BREAK13");
+
+            Label.Checkpoint("function_evaluation_test", "finish", (Object context) => {
+                Context Context = (Context)context;
+                Context.WasBreakpointHit(@"__FILE__:__LINE__", "BREAK13");
+                Int64 frameId = Context.DetectFrameId(@"__FILE__:__LINE__", "BREAK13");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "111", "int", "stGetInt()");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "222", "int", "stGetInt(111)");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "getInt()", "error");
+
+                Context.Continue(@"__FILE__:__LINE__");
+            });
+
             Label.Checkpoint("finish", "", (Object context) => {
                 Context Context = (Context)context;
                 Context.WasExit(@"__FILE__:__LINE__");