7d526df945645cee4e3764a22e4805e96dd3b050
[sdk/tools/netcoredbg.git] / src / debug / netcoredbg / jmc.cpp
1 // Copyright (c) 2017 Samsung Electronics Co., LTD
2 // Distributed under the MIT License.
3 // See the LICENSE file in the project root for more information.
4
5 #include "modules.h"
6
7 #include <string>
8 #include <vector>
9 #include <list>
10 #include <unordered_set>
11
12 #include "typeprinter.h"
13 #include "platform.h"
14 #include "symbolreader.h"
15 #include "cputil.h"
16
17
18 static const char *g_nonUserCode = "System.Diagnostics.DebuggerNonUserCodeAttribute..ctor";
19 static const char *g_stepThrough = "System.Diagnostics.DebuggerStepThroughAttribute..ctor";
20 // TODO: DebuggerStepThroughAttribute also affects breakpoints when JMC is enabled
21
22 // From ECMA-335
23 static const std::unordered_set<std::string> g_operatorMethodNames
24 {
25 // Unary operators
26     "op_Decrement",                    // --
27     "op_Increment",                    // ++
28     "op_UnaryNegation",                // - (unary)
29     "op_UnaryPlus",                    // + (unary)
30     "op_LogicalNot",                   // !
31     "op_True",                         // Not defined
32     "op_False",                        // Not defined
33     "op_AddressOf",                    // & (unary)
34     "op_OnesComplement",               // ~
35     "op_PointerDereference",           // * (unary)
36 // Binary operators
37     "op_Addition",                     // + (binary)
38     "op_Subtraction",                  // - (binary)
39     "op_Multiply",                     // * (binary)
40     "op_Division",                     // /
41     "op_Modulus",                      // %
42     "op_ExclusiveOr",                  // ^
43     "op_BitwiseAnd",                   // & (binary)
44     "op_BitwiseOr",                    // |
45     "op_LogicalAnd",                   // &&
46     "op_LogicalOr",                    // ||
47     "op_Assign",                       // Not defined (= is not the same)
48     "op_LeftShift",                    // <<
49     "op_RightShift",                   // >>
50     "op_SignedRightShift",             // Not defined
51     "op_UnsignedRightShift",           // Not defined
52     "op_Equality",                     // ==
53     "op_GreaterThan",                  // >
54     "op_LessThan",                     // <
55     "op_Inequality",                   // !=
56     "op_GreaterThanOrEqual",           // >=
57     "op_LessThanOrEqual",              // <=
58     "op_UnsignedRightShiftAssignment", // Not defined
59     "op_MemberSelection",              // ->
60     "op_RightShiftAssignment",         // >>=
61     "op_MultiplicationAssignment",     // *=
62     "op_PointerToMemberSelection",     // ->*
63     "op_SubtractionAssignment",        // -=
64     "op_ExclusiveOrAssignment",        // ^=
65     "op_LeftShiftAssignment",          // <<=
66     "op_ModulusAssignment",            // %=
67     "op_AdditionAssignment",           // +=
68     "op_BitwiseAndAssignment",         // &=
69     "op_BitwiseOrAssignment",          // |=
70     "op_Comma",                        // ,
71     "op_DivisionAssignment"            // /=
72 };
73
74 bool Modules::ShouldLoadSymbolsForModule(const std::string &moduleName)
75 {
76     std::string name = GetFileName(moduleName);
77     if (name.find("System.") == 0 || name.find("SOS.") == 0)
78         return false;
79     return true;
80 }
81
82 static bool HasAttribute(IMetaDataImport *pMD, mdToken tok, const std::string &attrName)
83 {
84     bool found = false;
85
86     ULONG numAttributes = 0;
87     HCORENUM fEnum = NULL;
88     mdCustomAttribute attr;
89     while(SUCCEEDED(pMD->EnumCustomAttributes(&fEnum, tok, 0, &attr, 1, &numAttributes)) && numAttributes != 0)
90     {
91         mdToken ptkObj = mdTokenNil;
92         mdToken ptkType = mdTokenNil;
93         pMD->GetCustomAttributeProps(attr, &ptkObj, &ptkType, nullptr, nullptr);
94
95         std::string mdName;
96         std::list<std::string> emptyArgs;
97         TypePrinter::NameForToken(ptkType, pMD, mdName, true, emptyArgs);
98
99         if (mdName == attrName)
100         {
101             found = true;
102             break;
103         }
104     }
105     pMD->CloseEnum(fEnum);
106
107     return found;
108 }
109
110 static bool HasSourceLocation(SymbolReader *symbolReader, mdMethodDef methodDef)
111 {
112     HRESULT Status;
113     std::vector<SymbolReader::SequencePoint> points;
114     if (FAILED(symbolReader->GetSequencePoints(methodDef, points)))
115         return false;
116
117     for (auto &p : points)
118     {
119         if (p.startLine != 0 && p.startLine != SymbolReader::HiddenLine)
120             return true;
121     }
122     return false;
123 }
124
125 static HRESULT GetNonJMCMethodsForTypeDef(
126     IMetaDataImport *pMD,
127     SymbolReader *sr,
128     mdTypeDef typeDef,
129     std::vector<mdToken> &excludeMethods)
130 {
131     HRESULT Status;
132
133     ULONG numMethods = 0;
134     HCORENUM fEnum = NULL;
135     mdMethodDef methodDef;
136     while(SUCCEEDED(pMD->EnumMethods(&fEnum, typeDef, &methodDef, 1, &numMethods)) && numMethods != 0)
137     {
138         HRESULT hr;
139         mdTypeDef memTypeDef;
140         ULONG nameLen;
141         WCHAR szFunctionName[mdNameLen] = {0};
142
143         Status = pMD->GetMethodProps(methodDef, &memTypeDef,
144                                      szFunctionName, _countof(szFunctionName), &nameLen,
145                                      nullptr, nullptr, nullptr, nullptr, nullptr);
146
147         if (FAILED(Status))
148             continue;
149
150         if ((g_operatorMethodNames.find(to_utf8(szFunctionName)) != g_operatorMethodNames.end())
151             || HasAttribute(pMD, methodDef, g_nonUserCode)
152             || HasAttribute(pMD, methodDef, g_stepThrough)
153             || !HasSourceLocation(sr, methodDef))
154         {
155             excludeMethods.push_back(methodDef);
156         }
157     }
158     pMD->CloseEnum(fEnum);
159
160     mdProperty propertyDef;
161     ULONG numProperties = 0;
162     HCORENUM propEnum = NULL;
163     while(SUCCEEDED(pMD->EnumProperties(&propEnum, typeDef, &propertyDef, 1, &numProperties)) && numProperties != 0)
164     {
165         mdMethodDef mdSetter;
166         mdMethodDef mdGetter;
167         if (SUCCEEDED(pMD->GetPropertyProps(propertyDef,
168                                             nullptr,
169                                             nullptr,
170                                             0,
171                                             nullptr,
172                                             nullptr,
173                                             nullptr,
174                                             nullptr,
175                                             nullptr,
176                                             nullptr,
177                                             nullptr,
178                                             &mdSetter,
179                                             &mdGetter,
180                                             nullptr,
181                                             0,
182                                             nullptr)))
183         {
184             if (mdSetter != mdMethodDefNil)
185                 excludeMethods.push_back(mdSetter);
186             if (mdGetter != mdMethodDefNil)
187                 excludeMethods.push_back(mdGetter);
188         }
189     }
190     pMD->CloseEnum(propEnum);
191
192     return S_OK;
193 }
194
195 static HRESULT GetNonJMCClassesAndMethods(ICorDebugModule *pModule, SymbolReader *sr, std::vector<mdToken> &excludeTokens)
196 {
197     HRESULT Status;
198
199     ToRelease<IUnknown> pMDUnknown;
200     ToRelease<IMetaDataImport> pMD;
201     IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
202     IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
203
204     ULONG numTypedefs = 0;
205     HCORENUM fEnum = NULL;
206     mdTypeDef typeDef;
207     while(SUCCEEDED(pMD->EnumTypeDefs(&fEnum, &typeDef, 1, &numTypedefs)) && numTypedefs != 0)
208     {
209         if (HasAttribute(pMD, typeDef, g_nonUserCode))
210             excludeTokens.push_back(typeDef);
211         else
212             GetNonJMCMethodsForTypeDef(pMD, sr, typeDef, excludeTokens);
213     }
214     pMD->CloseEnum(fEnum);
215
216     return S_OK;
217 }
218
219 HRESULT Modules::SetJMCFromAttributes(ICorDebugModule *pModule, SymbolReader *symbolReader)
220 {
221     std::vector<mdToken> excludeTokens;
222
223     GetNonJMCClassesAndMethods(pModule, symbolReader, excludeTokens);
224
225     for (mdToken token : excludeTokens)
226     {
227         if (TypeFromToken(token) == mdtMethodDef)
228         {
229             ToRelease<ICorDebugFunction> pFunction;
230             ToRelease<ICorDebugFunction2> pFunction2;
231             if (FAILED(pModule->GetFunctionFromToken(token, &pFunction)))
232                 continue;
233             if (FAILED(pFunction->QueryInterface(IID_ICorDebugFunction2, (LPVOID *)&pFunction2)))
234                 continue;
235
236             pFunction2->SetJMCStatus(FALSE);
237         }
238         else if (TypeFromToken(token) == mdtTypeDef)
239         {
240             ToRelease<ICorDebugClass> pClass;
241             ToRelease<ICorDebugClass2> pClass2;
242             if (FAILED(pModule->GetClassFromToken(token, &pClass)))
243                 continue;
244             if (FAILED(pClass->QueryInterface(IID_ICorDebugClass2, (LPVOID *)&pClass2)))
245                 continue;
246
247             pClass2->SetJMCStatus(FALSE);
248         }
249     }
250
251     return S_OK;
252 }