HRESULT InvocationExpression(std::list<EvalStackEntry> &evalStack, PVOID pArguments, std::string &output, EvalData &ed)
{
- // todo: static const char* extensionAttributeName = "System.Runtime.CompilerServices.ExtensionAttribute..ctor";
int32_t Int = ((FormatFI*)pArguments)->Int;
if (Int < 0)
});
if (!iCorFunc)
- return E_FAIL;
+ {
+ if(SUCCEEDED(ed.pEvaluator->LookupExtensionMethods(iCorType, funcName, funcArgs, methodGenerics, &iCorFunc)))
+ isInstance = true; // Extension methods always require "this" as their first parameter
+ else
+ return E_FAIL;
+ }
size_t typeArgsCount = evalStack.front().genericTypeCache.size();
ULONG32 realArgsCount = Int + (isInstance ? 1 : 0);
iCorValueArgs.reserve(realArgsCount);
iCorTypeArgs.reserve(typeArgsCount);
- // Place instance value ("this") if not static
+ // Place instance value ("this") if extension or not static method
if (isInstance)
{
iCorValueArgs.emplace_back(iCorValue.GetPtr());
return WalkMethods(iCorType, methodGenerics, cb);
}
+// https://github.com/dotnet/runtime/blob/57bfe474518ab5b7cfe6bf7424a79ce3af9d6657/docs/design/coreclr/profiling/davbr-blog-archive/samples/sigparse.cpp
+static const ULONG SIG_METHOD_VARARG = 0x5; // vararg calling convention
+static const ULONG SIG_METHOD_GENERIC = 0x10; // used to indicate that the method has one or more generic parameters.
+
static HRESULT InternalWalkMethods(ICorDebugType *pInputType, std::vector<Evaluator::ArgElementType> &methodGenerics, Evaluator::WalkMethodsCallback cb)
{
HRESULT Status;
}
}
- // https://github.com/dotnet/runtime/blob/57bfe474518ab5b7cfe6bf7424a79ce3af9d6657/docs/design/coreclr/profiling/davbr-blog-archive/samples/sigparse.cpp
- static const ULONG SIG_METHOD_VARARG = 0x5; // vararg calling convention
- static const ULONG SIG_METHOD_GENERIC = 0x10; // used to indicate that the method has one or more generic parameters.
-
ULONG numMethods = 0;
HCORENUM fEnum = NULL;
mdMethodDef methodDef;
inputSetterData, identifiers, ppResultValue, resultSetterData, ppResultType, evalFlags);
}
+HRESULT Evaluator::LookupExtensionMethods(ICorDebugType *pType,
+ const std::string &methodName,
+ std::vector<Evaluator::ArgElementType> &methodArgs,
+ std::vector<Evaluator::ArgElementType> &methodGenerics,
+ ICorDebugFunction** ppCorFunc)
+{
+ static const char* attributeName = "System.Runtime.CompilerServices.ExtensionAttribute..ctor";
+ HRESULT Status;
+ std::vector<Evaluator::ArgElementType> typeGenerics;
+ ToRelease<ICorDebugTypeEnum> paramTypes;
+
+ if (SUCCEEDED(pType->EnumerateTypeParameters(¶mTypes)))
+ {
+ ULONG fetched = 0;
+ ToRelease<ICorDebugType> pCurrentTypeParam;
+
+ while (SUCCEEDED(paramTypes->Next(1, &pCurrentTypeParam, &fetched)) && fetched == 1)
+ {
+ Evaluator::ArgElementType argElType;
+ pCurrentTypeParam->GetType(&argElType.corType);
+ if(argElType.corType == ELEMENT_TYPE_VALUETYPE || argElType.corType == ELEMENT_TYPE_CLASS)
+ IfFailRet(TypePrinter::NameForTypeByType(pCurrentTypeParam, argElType.typeName));
+ typeGenerics.emplace_back(argElType);
+ pCurrentTypeParam.Free();
+ }
+ }
+
+ m_sharedModules->ForEachModule([&](ICorDebugModule *pModule)->HRESULT {
+ ULONG typesCnt = 0;
+ HCORENUM fTypeEnum = NULL;
+ mdTypeDef mdType = mdTypeDefNil;
+
+ ToRelease<IUnknown> pMDUnknown;
+ IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
+ ToRelease<IMetaDataImport> pMD;
+ IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
+
+ while (SUCCEEDED(pMD->EnumTypeDefs(&fTypeEnum, &mdType, 1, &typesCnt)) && typesCnt != 0)
+ {
+ std::string typeName;
+ if (!HasAttribute(pMD, mdType, attributeName))
+ continue;
+ if(FAILED(TypePrinter::NameForToken(mdType, pMD, typeName, false, nullptr)))
+ continue;
+ HCORENUM fFuncEnum = NULL;
+ mdMethodDef mdMethod = mdMethodDefNil;
+ ULONG methodsCnt = 0;
+
+ while (SUCCEEDED(pMD->EnumMethods(&fFuncEnum, mdType, &mdMethod, 1, &methodsCnt)) && methodsCnt != 0)
+ {
+ mdTypeDef memTypeDef;
+ ULONG nameLen;
+ WCHAR szFuncName[mdNameLen] = {0};
+ PCCOR_SIGNATURE pSig = NULL;
+ ULONG cbSig = 0;
+
+ if(FAILED(pMD->GetMethodProps(mdMethod, &memTypeDef, szFuncName, _countof(szFuncName), &nameLen,
+ nullptr, &pSig, &cbSig, nullptr, nullptr)))
+ continue;
+ if (!HasAttribute(pMD, mdMethod, attributeName))
+ continue;
+ std::string fullName = to_utf8(szFuncName);
+ if (fullName != methodName)
+ continue;
+ ULONG cParams; // Count of signature parameters.
+ ULONG gParams; // count of generic parameters;
+ ULONG elementSize;
+ ULONG convFlags;
+
+ // 1. calling convention for MethodDefSig:
+ // [[HASTHIS] [EXPLICITTHIS]] (DEFAULT|VARARG|GENERIC GenParamCount)
+ elementSize = CorSigUncompressData(pSig, &convFlags);
+ pSig += elementSize;
+
+ // 2. if method has generic params, count them
+ if (convFlags & SIG_METHOD_GENERIC)
+ {
+ elementSize = CorSigUncompressData(pSig, &gParams);
+ pSig += elementSize;
+ }
+
+ // 3. count of params
+ elementSize = CorSigUncompressData(pSig, &cParams);
+ pSig += elementSize;
+
+ // 4. return type
+ Evaluator::ArgElementType returnElementType;
+ if(FAILED(ParseElementType(pMD, &pSig, returnElementType, typeGenerics, methodGenerics)))
+ continue;
+
+ // 5. get next element from method signature
+ std::vector<Evaluator::ArgElementType> argElementTypes(cParams);
+ for (ULONG i = 0; i < cParams; ++i)
+ {
+ if(FAILED(ParseElementType(pMD, &pSig, argElementTypes[i], typeGenerics, methodGenerics)))
+ break;
+ }
+
+ std::string typeName;
+ CorElementType ty;
+
+ if(FAILED(pType->GetType(&ty)))
+ continue;
+ if(FAILED(TypePrinter::NameForTypeByType(pType, typeName)))
+ continue;
+ if (ty == ELEMENT_TYPE_CLASS || ty == ELEMENT_TYPE_VALUETYPE)
+ {
+ if (typeName != argElementTypes[0].typeName)
+ {
+ // if type names don't match check implemented interfaces names
+
+ ToRelease<ICorDebugClass> iCorClass;
+ if(FAILED(pType->GetClass(&iCorClass)))
+ continue;
+
+ ToRelease<ICorDebugModule> iCorModule;
+ if(FAILED(iCorClass->GetModule(&iCorModule)))
+ continue;
+
+ mdTypeDef metaTypeDef;
+ if(FAILED(iCorClass->GetToken(&metaTypeDef)))
+ continue;
+
+ ToRelease<IUnknown> pMDUnk;
+ if(FAILED(iCorModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnk)))
+ continue;
+
+ ToRelease<IMetaDataImport> pMDI;
+ if(FAILED(pMDUnk->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMDI)))
+ continue;
+
+ HCORENUM ifEnum = NULL;
+ mdInterfaceImpl ifaceImpl;
+ ULONG pcImpls = 0;
+ while (SUCCEEDED(pMDI->EnumInterfaceImpls(&ifEnum, metaTypeDef, &ifaceImpl, 1, &pcImpls)) && pcImpls != 0)
+ {
+ mdTypeDef tkClass;
+ mdToken tkIface;
+ PCCOR_SIGNATURE pSig = NULL;
+ ULONG pcbSig;
+ Evaluator::ArgElementType ifaceElementType;
+ if(FAILED(pMDI->GetInterfaceImplProps(ifaceImpl, &tkClass, &tkIface)))
+ continue;
+ if(TypeFromToken(tkIface) == mdtTypeSpec)
+ {
+ if(FAILED(pMDI->GetTypeSpecFromToken(tkIface, &pSig, &pcbSig)))
+ continue;
+ if(FAILED(ParseElementType(pMDI, &pSig, ifaceElementType, typeGenerics, methodGenerics, false)))
+ continue;
+ }
+ else
+ {
+ if (FAILED(TypePrinter::NameForToken(tkIface, pMDI, ifaceElementType.typeName, true, nullptr)))
+ continue;
+ }
+
+ if(ifaceElementType.typeName == argElementTypes[0].typeName && methodArgs.size() + 1 == argElementTypes.size())
+ {
+ bool found = true;
+ for(unsigned int i = 0; i < methodArgs.size(); i++)
+ {
+ if(methodArgs[i].corType != argElementTypes[i+1].corType)
+ {
+ found = false;
+ break;
+ }
+ }
+ if(found)
+ {
+ pModule->GetFunctionFromToken(mdMethod, ppCorFunc);
+ pMDI->CloseEnum(ifEnum);
+ pMD->CloseEnum(fFuncEnum);
+ pMD->CloseEnum(fTypeEnum);
+ return E_ABORT;
+ }
+ }
+ }
+ pMDI->CloseEnum(ifEnum);
+ }
+ }
+ else if (ty != argElementTypes[0].corType || (methodArgs.size() + 1 != argElementTypes.size()))
+ {
+ continue;
+ }
+ else
+ {
+ bool found = true;
+ for(unsigned int i = 0; i < methodArgs.size(); i++)
+ {
+ if(methodArgs[i].corType != argElementTypes[i+1].corType)
+ {
+ found = false;
+ break;
+ }
+ }
+ if(found)
+ {
+ pModule->GetFunctionFromToken(mdMethod, ppCorFunc);
+ pMD->CloseEnum(fFuncEnum);
+ pMD->CloseEnum(fTypeEnum);
+ return E_ABORT;
+ }
+ }
+ }
+ pMD->CloseEnum(fFuncEnum);
+ }
+ pMD->CloseEnum(fTypeEnum);
+ return S_OK;
+ });
+ return S_OK;
+}
+
} // namespace netcoredbg
std::string &methodClass,
bool &thisParam);
+ HRESULT Evaluator::LookupExtensionMethods(
+ ICorDebugType *pType,
+ const std::string &methodName,
+ std::vector<Evaluator::ArgElementType> &methodArgs,
+ std::vector<Evaluator::ArgElementType> &methodGenerics,
+ ICorDebugFunction** ppCorFunc);
+
HRESULT GetElement(ICorDebugValue *pInputValue, std::vector<ULONG32> &indexes, ICorDebugValue **ppResultValue);
HRESULT WalkMethods(ICorDebugType *pInputType, std::vector<Evaluator::ArgElementType> &methodGenerics, WalkMethodsCallback cb);
HRESULT WalkMethods(ICorDebugValue *pInputTypeValue, WalkMethodsCallback cb);
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">\r
+\r
+ <ItemGroup>
+ <ProjectReference Include="..\NetcoreDbgTest\NetcoreDbgTest.csproj" />
+ </ItemGroup>\r
+\r
+ <PropertyGroup>\r
+ <OutputType>Exe</OutputType>\r
+ <TargetFramework>netcoreapp3.1</TargetFramework>\r
+ </PropertyGroup>\r
+\r
+</Project>\r
--- /dev/null
+using System;
+using System.IO;
+using System.Collections.Generic;
+using System.Linq;
+
+using NetcoreDbgTest;
+using NetcoreDbgTest.MI;
+using NetcoreDbgTest.Script;
+
+namespace NetcoreDbgTest.Script
+{
+ class Context
+ {
+ public void Prepare(string caller_trace)
+ {
+ Assert.Equal(MIResultClass.Done,
+ MIDebugger.Request("-file-exec-and-symbols " + ControlInfo.CorerunPath).Class,
+ @"__FILE__:__LINE__"+"\n"+caller_trace);
+
+ Assert.Equal(MIResultClass.Done,
+ MIDebugger.Request("-exec-arguments " + ControlInfo.TargetAssemblyPath).Class,
+ @"__FILE__:__LINE__"+"\n"+caller_trace);
+
+ Assert.Equal(MIResultClass.Running,
+ MIDebugger.Request("-exec-run").Class,
+ @"__FILE__:__LINE__"+"\n"+caller_trace);
+ }
+
+ public void EnableBreakpoint(string caller_trace, string bpName)
+ {
+ Breakpoint bp = ControlInfo.Breakpoints[bpName];
+
+ Assert.Equal(BreakpointType.Line, bp.Type, @"__FILE__:__LINE__"+"\n"+caller_trace);
+
+ var lbp = (LineBreakpoint)bp;
+
+ Assert.Equal(MIResultClass.Done,
+ MIDebugger.Request("-break-insert -f " + lbp.FileName + ":" + lbp.NumLine).Class,
+ @"__FILE__:__LINE__"+"\n"+caller_trace);
+ }
+
+ public void CalcAndCheckExpression(string caller_trace, string ExpectedResult, string expr)
+ {
+ var res = MIDebugger.Request("-var-create - * \"" + expr + "\"");
+
+ Assert.Equal(MIResultClass.Done, res.Class, @"__FILE__:__LINE__"+"\n"+caller_trace);
+ Assert.Equal(ExpectedResult, ((MIConst)res["value"]).CString, @"__FILE__:__LINE__"+"\n"+caller_trace);
+ }
+
+ public string GetAndCheckValue(string caller_trace, string ExpectedResult, string ExpectedType, string Expression)
+ {
+ var res = MIDebugger.Request(String.Format("-var-create - * \"{0}\"", Expression));
+ Assert.Equal(MIResultClass.Done, res.Class, @"__FILE__:__LINE__"+"\n"+caller_trace);
+
+ Assert.Equal(Expression, ((MIConst)res["exp"]).CString, @"__FILE__:__LINE__"+"\n"+caller_trace);
+ Assert.Equal(ExpectedType, ((MIConst)res["type"]).CString, @"__FILE__:__LINE__"+"\n"+caller_trace);
+ Assert.Equal(ExpectedResult, ((MIConst)res["value"]).CString, @"__FILE__:__LINE__"+"\n"+caller_trace);
+
+ return ((MIConst)res["name"]).CString;
+ }
+
+ public void CheckErrorAtRequest(string caller_trace, string Expression, string errMsgStart)
+ {
+ var res = MIDebugger.Request(String.Format("-var-create - * \"{0}\"", Expression));
+ Assert.Equal(MIResultClass.Error, res.Class, @"__FILE__:__LINE__"+"\n"+caller_trace);
+ Assert.True(((MIConst)res["msg"]).CString.StartsWith(errMsgStart), @"__FILE__:__LINE__"+"\n"+caller_trace);
+ }
+
+ public void GetResultAsString(string caller_trace, string expr, out string strRes)
+ {
+ var res = MIDebugger.Request("-var-create - * \"" + expr + "\"");
+ Assert.Equal(MIResultClass.Done, res.Class, @"__FILE__:__LINE__"+"\n" + caller_trace);
+ strRes = ((MIConst)res["value"]).CString;
+ }
+
+ public void WasEntryPointHit(string caller_trace)
+ {
+ Func<MIOutOfBandRecord, bool> filter = (record) => {
+ if (!IsStoppedEvent(record)) {
+ return false;
+ }
+
+ var output = ((MIAsyncRecord)record).Output;
+ var reason = (MIConst)output["reason"];
+
+ if (reason.CString != "entry-point-hit") {
+ return false;
+ }
+
+ var frame = (MITuple)output["frame"];
+ var func = (MIConst)frame["func"];
+ if (func.CString == ControlInfo.TestName + ".Program.Main()") {
+ return true;
+ }
+
+ return false;
+ };
+
+ Assert.True(MIDebugger.IsEventReceived(filter), @"__FILE__:__LINE__"+"\n"+caller_trace);
+ }
+
+ public void WasBreakpointHit(string caller_trace, string bpName)
+ {
+ var bp = (LineBreakpoint)ControlInfo.Breakpoints[bpName];
+
+ Func<MIOutOfBandRecord, bool> filter = (record) => {
+ if (!IsStoppedEvent(record)) {
+ return false;
+ }
+
+ var output = ((MIAsyncRecord)record).Output;
+ var reason = (MIConst)output["reason"];
+
+ if (reason.CString != "breakpoint-hit") {
+ return false;
+ }
+
+ var frame = (MITuple)output["frame"];
+ var fileName = (MIConst)frame["file"];
+ var line = ((MIConst)frame["line"]).Int;
+
+ if (fileName.CString == bp.FileName &&
+ line == bp.NumLine) {
+ return true;
+ }
+
+ return false;
+ };
+
+ Assert.True(MIDebugger.IsEventReceived(filter),
+ @"__FILE__:__LINE__"+"\n"+caller_trace);
+ }
+
+ public void WasExit(string caller_trace)
+ {
+ Func<MIOutOfBandRecord, bool> filter = (record) => {
+ if (!IsStoppedEvent(record)) {
+ return false;
+ }
+
+ var output = ((MIAsyncRecord)record).Output;
+ var reason = (MIConst)output["reason"];
+
+ if (reason.CString != "exited") {
+ return false;
+ }
+
+ var exitCode = (MIConst)output["exit-code"];
+
+ if (exitCode.CString == "0") {
+ return true;
+ }
+
+ return false;
+ };
+
+ Assert.True(MIDebugger.IsEventReceived(filter), @"__FILE__:__LINE__"+"\n"+caller_trace);
+ }
+
+ public void DebuggerExit(string caller_trace)
+ {
+ Assert.Equal(MIResultClass.Exit,
+ MIDebugger.Request("-gdb-exit").Class,
+ @"__FILE__:__LINE__"+"\n"+caller_trace);
+ }
+
+ bool IsStoppedEvent(MIOutOfBandRecord record)
+ {
+ if (record.Type != MIOutOfBandRecordType.Async) {
+ return false;
+ }
+
+ var asyncRecord = (MIAsyncRecord)record;
+
+ if (asyncRecord.Class != MIAsyncRecordClass.Exec ||
+ asyncRecord.Output.Class != MIAsyncOutputClass.Stopped) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public void Continue(string caller_trace)
+ {
+ Assert.Equal(MIResultClass.Running,
+ MIDebugger.Request("-exec-continue").Class,
+ @"__FILE__:__LINE__"+"\n"+caller_trace);
+ }
+
+ public Context(ControlInfo controlInfo, NetcoreDbgTestCore.DebuggerClient debuggerClient)
+ {
+ ControlInfo = controlInfo;
+ MIDebugger = new MIDebugger(debuggerClient);
+ }
+
+ ControlInfo ControlInfo;
+ MIDebugger MIDebugger;
+ }
+}
+
+
+namespace CustomExtensions
+{
+ public static class StringExtension
+ {
+ public static int WordCount(this string str)
+ {
+ return str.Split(new char[] {' ', '.', '?'}, StringSplitOptions.RemoveEmptyEntries).Length;
+ }
+
+ public static int WordCount(this string str, int i)
+ {
+ return str.Split(new char[] {' ', '.', '?'}, StringSplitOptions.RemoveEmptyEntries).Length + i;
+ }
+
+ public static int WordCount(this string str, int i, int j)
+ {
+ return str.Split(new char[] {' ', '.', '?'}, StringSplitOptions.RemoveEmptyEntries).Length + i + j;
+ }
+ }
+}
+
+namespace MITestExtensionMethods
+{
+ using CustomExtensions;
+
+ public class MyString
+ {
+ string s;
+ public MyString(string ms)
+ {
+ s = ms;
+ }
+ }
+
+ struct MyInt
+ {
+ int i;
+ public MyInt(int mi)
+ {
+ i = mi;
+ }
+ }
+
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ string s = "The quick brown fox jumped over the lazy dog.";
+ List<string> lists = new List<string>();
+ Label.Checkpoint("init", "expression_test1", (Object context) => {
+ Context Context = (Context)context;
+ Context.Prepare(@"__FILE__:__LINE__");
+ Context.WasEntryPointHit(@"__FILE__:__LINE__");
+ Context.EnableBreakpoint(@"__FILE__:__LINE__", "BREAK1");
+ Context.Continue(@"__FILE__:__LINE__");
+ });
+
+ lists.Add("null");
+ lists.Add("first");
+ lists.Add("second");
+ lists.Add("third");
+ lists.Add("fourth");
+ string res = lists.ElementAt(1); Label.Breakpoint("BREAK1");
+
+ Label.Checkpoint("expression_test1", "finish", (Object context) => {
+ Context Context = (Context)context;
+ Context.WasBreakpointHit(@"__FILE__:__LINE__", "BREAK1");
+
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", "9", "int", "s.WordCount()");
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", "10", "int", "s.WordCount(1)");
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", "11", "int", "s.WordCount(1+1)");
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", "11", "int", "s.WordCount( 1+1)");
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", "11", "int", "s.WordCount(1+1 )");
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", "11", "int", "s.WordCount( 1+1 )");
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", "11", "int", "s.WordCount( 1 + 1 )");
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", "11", "int", "s.WordCount(1,1)");
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", "13", "int", "s.WordCount(1+1,1+1)");
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", "11", "int", "s.WordCount(1,1)");
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", "11", "int", "s.WordCount(1,1)");
+ Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "s.WordCount(1,1,1)", "Error: 0x80070057");
+ Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "s.WordCount(\\\"first\\\")", "Error: 0x80070057");
+ Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "s.WordCount(1, \\\"first\\\")", "Error: 0x80070057");
+ Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "s.WordCount(\\\"first\\\", 1)", "Error: 0x80070057");
+
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"null\\\"", "string", "lists.ElementAt(0)");
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"first\\\"", "string", "lists.ElementAt(1)");
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"second\\\"", "string", "lists.ElementAt(2)");
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"third\\\"", "string", "lists.ElementAt(3)");
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"fourth\\\"", "string", "lists.ElementAt(4)");
+ Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "lists.ElemetAt()", "Error: 0x80070057");
+ Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "lists.ElementAt(1,2)", "Error: 0x80070057");
+ Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "lists.ElementAt(\\\"first\\\")", "Error: 0x80070057");
+
+ Context.Continue(@"__FILE__:__LINE__");
+ });
+
+ Label.Checkpoint("finish", "", (Object context) => {
+ Context Context = (Context)context;
+ Context.WasExit(@"__FILE__:__LINE__");
+ Context.DebuggerExit(@"__FILE__:__LINE__");
+ });
+ }
+ }
+}
--- /dev/null
+using System;
+using System.IO;
+using System.Collections.Generic;
+using System.Linq;
+
+using NetcoreDbgTest;
+using NetcoreDbgTest.VSCode;
+using NetcoreDbgTest.Script;
+
+using Newtonsoft.Json;
+
+namespace NetcoreDbgTest.Script
+{
+ class Context
+ {
+ public void PrepareStart(string caller_trace)
+ {
+ InitializeRequest initializeRequest = new InitializeRequest();
+ initializeRequest.arguments.clientID = "vscode";
+ initializeRequest.arguments.clientName = "Visual Studio Code";
+ initializeRequest.arguments.adapterID = "coreclr";
+ initializeRequest.arguments.pathFormat = "path";
+ initializeRequest.arguments.linesStartAt1 = true;
+ initializeRequest.arguments.columnsStartAt1 = true;
+ initializeRequest.arguments.supportsVariableType = true;
+ initializeRequest.arguments.supportsVariablePaging = true;
+ initializeRequest.arguments.supportsRunInTerminalRequest = true;
+ initializeRequest.arguments.locale = "en-us";
+ Assert.True(VSCodeDebugger.Request(initializeRequest).Success,
+ @"__FILE__:__LINE__"+"\n"+caller_trace);
+
+ LaunchRequest launchRequest = new LaunchRequest();
+ launchRequest.arguments.name = ".NET Core Launch (console) with pipeline";
+ launchRequest.arguments.type = "coreclr";
+ launchRequest.arguments.preLaunchTask = "build";
+ launchRequest.arguments.program = ControlInfo.TargetAssemblyPath;
+ launchRequest.arguments.cwd = "";
+ launchRequest.arguments.console = "internalConsole";
+ launchRequest.arguments.stopAtEntry = true;
+ launchRequest.arguments.internalConsoleOptions = "openOnSessionStart";
+ launchRequest.arguments.__sessionId = Guid.NewGuid().ToString();
+ Assert.True(VSCodeDebugger.Request(launchRequest).Success,
+ @"__FILE__:__LINE__"+"\n"+caller_trace);
+ }
+
+ public void PrepareEnd(string caller_trace)
+ {
+ ConfigurationDoneRequest configurationDoneRequest = new ConfigurationDoneRequest();
+ Assert.True(VSCodeDebugger.Request(configurationDoneRequest).Success,
+ @"__FILE__:__LINE__"+"\n"+caller_trace);
+ }
+
+ public void WasEntryPointHit(string caller_trace)
+ {
+ Func<string, bool> filter = (resJSON) => {
+ if (VSCodeDebugger.isResponseContainProperty(resJSON, "event", "stopped")
+ && VSCodeDebugger.isResponseContainProperty(resJSON, "reason", "entry")) {
+ threadId = Convert.ToInt32(VSCodeDebugger.GetResponsePropertyValue(resJSON, "threadId"));
+ return true;
+ }
+ return false;
+ };
+
+ Assert.True(VSCodeDebugger.IsEventReceived(filter),
+ @"__FILE__:__LINE__"+"\n"+caller_trace);
+ }
+
+ public void WasExit(string caller_trace)
+ {
+ bool wasExited = false;
+ int ?exitCode = null;
+ bool wasTerminated = false;
+
+ Func<string, bool> filter = (resJSON) => {
+ if (VSCodeDebugger.isResponseContainProperty(resJSON, "event", "exited")) {
+ wasExited = true;
+ ExitedEvent exitedEvent = JsonConvert.DeserializeObject<ExitedEvent>(resJSON);
+ exitCode = exitedEvent.body.exitCode;
+ }
+ if (VSCodeDebugger.isResponseContainProperty(resJSON, "event", "terminated")) {
+ wasTerminated = true;
+ }
+ if (wasExited && exitCode == 0 && wasTerminated)
+ return true;
+
+ return false;
+ };
+
+ Assert.True(VSCodeDebugger.IsEventReceived(filter),
+ @"__FILE__:__LINE__"+"\n"+caller_trace);
+ }
+
+ public void DebuggerExit(string caller_trace)
+ {
+ DisconnectRequest disconnectRequest = new DisconnectRequest();
+ disconnectRequest.arguments = new DisconnectArguments();
+ disconnectRequest.arguments.restart = false;
+ Assert.True(VSCodeDebugger.Request(disconnectRequest).Success,
+ @"__FILE__:__LINE__"+"\n"+caller_trace);
+ }
+
+ public void AddBreakpoint(string caller_trace, string bpName, string Condition = null)
+ {
+ Breakpoint bp = ControlInfo.Breakpoints[bpName];
+ Assert.Equal(BreakpointType.Line, bp.Type,
+ @"__FILE__:__LINE__"+"\n"+caller_trace);
+ var lbp = (LineBreakpoint)bp;
+
+ BreakpointSourceName = lbp.FileName;
+ BreakpointList.Add(new SourceBreakpoint(lbp.NumLine, Condition));
+ BreakpointLines.Add(lbp.NumLine);
+ }
+
+ public void SetBreakpoints(string caller_trace)
+ {
+ SetBreakpointsRequest setBreakpointsRequest = new SetBreakpointsRequest();
+ setBreakpointsRequest.arguments.source.name = BreakpointSourceName;
+ // NOTE this code works only with one source file
+ setBreakpointsRequest.arguments.source.path = ControlInfo.SourceFilesPath;
+ setBreakpointsRequest.arguments.lines.AddRange(BreakpointLines);
+ setBreakpointsRequest.arguments.breakpoints.AddRange(BreakpointList);
+ setBreakpointsRequest.arguments.sourceModified = false;
+ Assert.True(VSCodeDebugger.Request(setBreakpointsRequest).Success,
+ @"__FILE__:__LINE__"+"\n"+caller_trace);
+ }
+
+ public void WasBreakpointHit(string caller_trace, string bpName)
+ {
+ Func<string, bool> filter = (resJSON) => {
+ if (VSCodeDebugger.isResponseContainProperty(resJSON, "event", "stopped")
+ && VSCodeDebugger.isResponseContainProperty(resJSON, "reason", "breakpoint")) {
+ threadId = Convert.ToInt32(VSCodeDebugger.GetResponsePropertyValue(resJSON, "threadId"));
+ return true;
+ }
+ return false;
+ };
+
+ Assert.True(VSCodeDebugger.IsEventReceived(filter), @"__FILE__:__LINE__"+"\n"+caller_trace);
+
+ StackTraceRequest stackTraceRequest = new StackTraceRequest();
+ stackTraceRequest.arguments.threadId = threadId;
+ stackTraceRequest.arguments.startFrame = 0;
+ stackTraceRequest.arguments.levels = 20;
+ var ret = VSCodeDebugger.Request(stackTraceRequest);
+ Assert.True(ret.Success, @"__FILE__:__LINE__"+"\n"+caller_trace);
+
+ Breakpoint breakpoint = ControlInfo.Breakpoints[bpName];
+ Assert.Equal(BreakpointType.Line, breakpoint.Type, @"__FILE__:__LINE__"+"\n"+caller_trace);
+ var lbp = (LineBreakpoint)breakpoint;
+
+ StackTraceResponse stackTraceResponse =
+ JsonConvert.DeserializeObject<StackTraceResponse>(ret.ResponseStr);
+
+ if (stackTraceResponse.body.stackFrames[0].line == lbp.NumLine
+ && stackTraceResponse.body.stackFrames[0].source.name == lbp.FileName
+ // NOTE this code works only with one source file
+ && stackTraceResponse.body.stackFrames[0].source.path == ControlInfo.SourceFilesPath)
+ return;
+
+ throw new ResultNotSuccessException(@"__FILE__:__LINE__"+"\n"+caller_trace);
+ }
+
+ public void Continue(string caller_trace)
+ {
+ ContinueRequest continueRequest = new ContinueRequest();
+ continueRequest.arguments.threadId = threadId;
+ Assert.True(VSCodeDebugger.Request(continueRequest).Success,
+ @"__FILE__:__LINE__"+"\n"+caller_trace);
+ }
+
+ public Context(ControlInfo controlInfo, NetcoreDbgTestCore.DebuggerClient debuggerClient)
+ {
+ ControlInfo = controlInfo;
+ VSCodeDebugger = new VSCodeDebugger(debuggerClient);
+ }
+
+ public Int64 DetectFrameId(string caller_trace, string bpName)
+ {
+ StackTraceRequest stackTraceRequest = new StackTraceRequest();
+ stackTraceRequest.arguments.threadId = threadId;
+ stackTraceRequest.arguments.startFrame = 0;
+ stackTraceRequest.arguments.levels = 20;
+ var ret = VSCodeDebugger.Request(stackTraceRequest);
+ Assert.True(ret.Success, @"__FILE__:__LINE__"+"\n"+caller_trace);
+
+ Breakpoint breakpoint = ControlInfo.Breakpoints[bpName];
+ Assert.Equal(BreakpointType.Line, breakpoint.Type, @"__FILE__:__LINE__"+"\n"+caller_trace);
+ var lbp = (LineBreakpoint)breakpoint;
+
+ StackTraceResponse stackTraceResponse =
+ JsonConvert.DeserializeObject<StackTraceResponse>(ret.ResponseStr);
+
+ if (stackTraceResponse.body.stackFrames[0].line == lbp.NumLine
+ && stackTraceResponse.body.stackFrames[0].source.name == lbp.FileName
+ // NOTE this code works only with one source file
+ && stackTraceResponse.body.stackFrames[0].source.path == ControlInfo.SourceFilesPath)
+ return stackTraceResponse.body.stackFrames[0].id;
+
+ throw new ResultNotSuccessException(@"__FILE__:__LINE__"+"\n"+caller_trace);
+ }
+
+ public void GetAndCheckValue(string caller_trace, Int64 frameId, string ExpectedResult, string ExpectedType, string Expression)
+ {
+ EvaluateRequest evaluateRequest = new EvaluateRequest();
+ evaluateRequest.arguments.expression = Expression;
+ evaluateRequest.arguments.frameId = frameId;
+ var ret = VSCodeDebugger.Request(evaluateRequest);
+ Assert.True(ret.Success, @"__FILE__:__LINE__"+"\n"+caller_trace);
+
+ EvaluateResponse evaluateResponse =
+ JsonConvert.DeserializeObject<EvaluateResponse>(ret.ResponseStr);
+
+ Assert.Equal(ExpectedResult, evaluateResponse.body.result, @"__FILE__:__LINE__"+"\n"+caller_trace);
+ Assert.Equal(ExpectedType, evaluateResponse.body.type, @"__FILE__:__LINE__"+"\n"+caller_trace);
+ }
+
+ public void CheckErrorAtRequest(string caller_trace, Int64 frameId, string Expression, string errMsgStart)
+ {
+ EvaluateRequest evaluateRequest = new EvaluateRequest();
+ evaluateRequest.arguments.expression = Expression;
+ evaluateRequest.arguments.frameId = frameId;
+ var ret = VSCodeDebugger.Request(evaluateRequest);
+ Assert.False(ret.Success, @"__FILE__:__LINE__"+"\n"+caller_trace);
+
+ EvaluateResponse evaluateResponse =
+ JsonConvert.DeserializeObject<EvaluateResponse>(ret.ResponseStr);
+
+ Assert.True(evaluateResponse.message.StartsWith(errMsgStart), @"__FILE__:__LINE__"+"\n"+caller_trace);
+ }
+
+ public void GetResultAsString(string caller_trace, Int64 frameId, string expr, out string strRes)
+ {
+ EvaluateRequest evaluateRequest = new EvaluateRequest();
+ evaluateRequest.arguments.expression = expr;
+ evaluateRequest.arguments.frameId = frameId;
+ var ret = VSCodeDebugger.Request(evaluateRequest);
+ Assert.True(ret.Success, @"__FILE__:__LINE__"+"\n"+caller_trace);
+ EvaluateResponse evaluateResponse = JsonConvert.DeserializeObject<EvaluateResponse>(ret.ResponseStr);
+ strRes = evaluateResponse.body.result;
+ }
+
+ ControlInfo ControlInfo;
+ VSCodeDebugger VSCodeDebugger;
+ int threadId = -1;
+ string BreakpointSourceName;
+ List<SourceBreakpoint> BreakpointList = new List<SourceBreakpoint>();
+ List<int> BreakpointLines = new List<int>();
+ }
+}
+
+namespace CustomExtensions
+{
+ public static class StringExtension
+ {
+ public static int WordCount(this string str)
+ {
+ return str.Split(new char[] {' ', '.', '?'}, StringSplitOptions.RemoveEmptyEntries).Length;
+ }
+
+ public static int WordCount(this string str, int i)
+ {
+ return str.Split(new char[] {' ', '.', '?'}, StringSplitOptions.RemoveEmptyEntries).Length + i;
+ }
+
+ public static int WordCount(this string str, int i, int j)
+ {
+ return str.Split(new char[] {' ', '.', '?'}, StringSplitOptions.RemoveEmptyEntries).Length + i + j;
+ }
+ }
+}
+
+namespace VSCodeTestExtensionMethods
+{
+ using CustomExtensions;
+
+ public class MyString
+ {
+ string s;
+ public MyString(string ms)
+ {
+ s = ms;
+ }
+ }
+
+ struct MyInt
+ {
+ int i;
+ public MyInt(int mi)
+ {
+ i = mi;
+ }
+ }
+
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ string s = "The quick brown fox jumped over the lazy dog.";
+ string st = "first.second";
+ List<string> lists = new List<string>();
+
+ // first checkpoint (initialization) must provide "init" as id
+ Label.Checkpoint("init", "bp_test", (Object context) => {
+ Context Context = (Context)context;
+ Context.PrepareStart(@"__FILE__:__LINE__");
+ Context.AddBreakpoint(@"__FILE__:__LINE__", "BREAK1");
+ Context.SetBreakpoints(@"__FILE__:__LINE__");
+ Context.PrepareEnd(@"__FILE__:__LINE__");
+ Context.WasEntryPointHit(@"__FILE__:__LINE__");
+ Context.Continue(@"__FILE__:__LINE__");
+ });
+
+ lists.Add("null");
+ lists.Add("first");
+ lists.Add("second");
+ lists.Add("third");
+ lists.Add("fourth");
+ string res = lists.ElementAt(1); Label.Breakpoint("BREAK1");
+
+ Label.Checkpoint("bp_test", "finish", (Object context) => {
+ Context Context = (Context)context;
+ Context.WasBreakpointHit(@"__FILE__:__LINE__", "BREAK1");
+ Int64 frameId = Context.DetectFrameId(@"__FILE__:__LINE__", "BREAK1");
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "9", "int", "s.WordCount()");
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "10", "int", "s.WordCount(1)");
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "11", "int", "s.WordCount(1+1)");
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "11", "int", "s.WordCount( 1+1)");
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "11", "int", "s.WordCount(1+1 )");
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "11", "int", "s.WordCount( 1+1 )");
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "11", "int", "s.WordCount( 1 + 1 )");
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "11", "int", "s.WordCount(1,1)");
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "13", "int", "s.WordCount(1+1,1+1)");
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "11", "int", "s.WordCount(1,1)");
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "11", "int", "s.WordCount(1,1)");
+ Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "s.WordCount(1,1,1)", "error: 0x80070057");
+ Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "s.WordCount(\"first\")", "error: 0x80070057");
+ Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "s.WordCount(1, \"first\")", "error: 0x80070057");
+ Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "s.WordCount(\"first\", 1)", "error: 0x80070057");
+
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"null\"", "string", "lists.ElementAt(0)");
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"first\"", "string", "lists.ElementAt(1)");
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"second\"", "string", "lists.ElementAt(2)");
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"third\"", "string", "lists.ElementAt(3)");
+ Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"fourth\"", "string", "lists.ElementAt(4)");
+ Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "lists.ElemetAt()", "error: 0x80070057");
+ Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "lists.ElementAt(1,2)", "error: 0x80070057");
+ Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "lists.ElementAt(\"first\")", "error: 0x80070057");
+
+ Context.Continue(@"__FILE__:__LINE__");
+ });
+
+ // last checkpoint must provide "finish" as id or empty string ("") as next checkpoint id
+ Label.Checkpoint("finish", "", (Object context) => {
+ Context Context = (Context)context;
+ Context.WasExit(@"__FILE__:__LINE__");
+ Context.DebuggerExit(@"__FILE__:__LINE__");
+ });
+ }
+ }
+}
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">\r
+\r
+ <ItemGroup>
+ <ProjectReference Include="..\NetcoreDbgTest\NetcoreDbgTest.csproj" />
+ </ItemGroup>\r
+\r
+ <PropertyGroup>\r
+ <OutputType>Exe</OutputType>\r
+ <TargetFramework>netcoreapp3.1</TargetFramework>\r
+ </PropertyGroup>\r
+\r
+</Project>\r
"MITestHandshake"
"MITestTarget"
"MITestExceptionBreakpoint"
+ "MITestExtensionMethods"
"MITestExitCode"
"MITestEvalNotEnglish"
"MITest中文目录"
"VSCodeTestAsyncLambdaEvaluate"
"VSCodeTestGeneric"
"VSCodeTestEvalArraysIndexers"
+ "VSCodeTestExtensionMethods"
"VSCodeTestBreakpointWithoutStop"
)
"MITestHandshake"
"MITestTarget"
"MITestExceptionBreakpoint"
+ "MITestExtensionMethods"
"MITestExitCode"
"MITestEvalNotEnglish"
"MITest中文目录"
"VSCodeTestAsyncLambdaEvaluate"
"VSCodeTestGeneric"
"VSCodeTestEvalArraysIndexers"
+ "VSCodeTestExtensionMethods"
"VSCodeTestBreakpointWithoutStop"
)
"MITestExecInt"
"MITestHandshake"
"MITestExceptionBreakpoint"
+ "MITestExtensionMethods"
"MITestExitCode"
"MITestEvalNotEnglish"
"MITestEnum"
"VSCodeTestAsyncLambdaEvaluate"
"VSCodeTestGeneric"
"VSCodeTestEvalArraysIndexers"
+ "VSCodeTestExtensionMethods"
"VSCodeTestBreakpointWithoutStop"
)
"MITestExecInt"
"MITestHandshake"
"MITestExceptionBreakpoint"
+ "MITestExtensionMethods"
"MITestExitCode"
"MITestEvalNotEnglish"
"MITest中文目录"
"VSCodeTestAsyncLambdaEvaluate"
"VSCodeTestGeneric"
"VSCodeTestEvalArraysIndexers"
+ "VSCodeTestExtensionMethods"
"VSCodeTestBreakpointWithoutStop"
)